Compare commits

..

58 Commits

Author SHA1 Message Date
drh 68b8f0a1ef Version 1.0.17 (CVS 486)
FossilOrigin-Name: bee0c818590ccdbb96e0a4cb2998bc077564b2ae
2000-12-10 18:40:00 +00:00
drh acf4575752 speed tweaks and documentation updates (CVS 170)
FossilOrigin-Name: b46f9a317b06bd490bda987e11e24f2c27162370
2000-12-10 18:35:20 +00:00
drh 144f1d64b5 speed tweaks and documentation updates (CVS 169)
FossilOrigin-Name: 00c3dfd025f5e66587e23170eddb33112b5e1133
2000-12-10 18:23:51 +00:00
drh 78e6f71fcc branches: 1.16.2;
speed tweaks and documentation updates (CVS 168)

FossilOrigin-Name: af9673d1bff613b93b25fb95dc4cc409314da9f2
2000-12-10 18:23:51 +00:00
drh 8c82b3506e speed tweaks and documentation updates (CVS 167)
FossilOrigin-Name: ced90c3f93e5e0cf73ea77b62a1308cd8dd5f3b3
2000-12-10 18:23:50 +00:00
drh af90b19472 Version 1.0.16 (CVS 487)
FossilOrigin-Name: 8c36b248fd57dfaf3233a365f13f150a07affb3c
2000-11-28 21:00:00 +00:00
drh 66b89c8f68 Comment changes only (CVS 166)
FossilOrigin-Name: 5518e012bf35821f65fe2ca7cbafca6a97e7a4ac
2000-11-28 20:47:17 +00:00
drh d9a4698e5f Comment changes only (CVS 165)
FossilOrigin-Name: 97339efdf3f3b02a6a4fe57d2cb01d19cec3c749
2000-11-28 20:46:38 +00:00
drh 52d17876d1 Version 1.0.15 (CVS 488)
FossilOrigin-Name: d2ad3d2b4ee868621fb1155c64216536d576e456
2000-10-23 13:19:59 +00:00
drh 8c3052c49b documentation and speed updates (CVS 164)
FossilOrigin-Name: 356cdd64860b714f52529159fada799dca7bb1c0
2000-10-23 13:16:31 +00:00
drh 76634487b6 remove unnecessary code when NDEBUG is defined (CVS 163)
FossilOrigin-Name: 738e3e49f6d45e4393e35f7f5f65a41d3c2080c9
2000-10-23 01:07:59 +00:00
drh ccb2a3daf6 fix a debugging issue (CVS 162)
FossilOrigin-Name: f0a5255d2657ddcba24353d6d94b0d9c579d8dec
2000-10-22 20:39:58 +00:00
drh ed5423d5ae fix an HPUX bug (CVS 1709)
FossilOrigin-Name: 9e91b729f0a708b08060979a4998be890b6d268d
2000-10-19 15:28:40 +00:00
drh 549cfc6c80 Version 1.0.14 (CVS 489)
FossilOrigin-Name: 4788dc32a5c9fc3fcd08cbc43b13671c132f0c3e
2000-10-19 15:00:00 +00:00
drh 3cebbde37e remove the Et_AppInit() function from tclsqlite (CVS 161)
FossilOrigin-Name: 8eee153078b27b56c6da7d0b7a3df25985ed051f
2000-10-19 14:59:27 +00:00
drh efa4e17240 make it a little faster (CVS 160)
FossilOrigin-Name: 757668bd641134a6f7479c8df1c8b06a24d51ee4
2000-10-19 14:42:04 +00:00
drh 01d01eeee0 add the sqlite_test_prefixes control file (CVS 159)
FossilOrigin-Name: 4ccd9103c3e7236084283a7311b6e746037d12aa
2000-10-19 14:21:42 +00:00
drh 42f04222c0 fix a memory leadk (CVS 1708)
FossilOrigin-Name: 948f5a749fdc7f40f8a8e5a4e5be8139f9e4c66d
2000-10-19 14:18:34 +00:00
drh 99e86e6b0b Added the "memory:" driver (CVS 1707)
FossilOrigin-Name: e9236833d95060509bdcba008d0e15e61fd7b6d7
2000-10-19 14:10:09 +00:00
drh 767c2001c6 Added the "memory:" driver (CVS 158)
FossilOrigin-Name: 54d60c68dc83410e911b828a680772541c44e9df
2000-10-19 14:10:08 +00:00
drh e907bfc2ac Version 1.0.13 (CVS 490)
FossilOrigin-Name: b9c84fa57912d7d777b4ebefc12627d80a2fc378
2000-10-19 02:00:00 +00:00
drh bb0b679c02 Break out DBBE drivers into separate files. (CVS 157)
FossilOrigin-Name: 979ef5d5d64b8e38cc15fef0d2d507ca2fe6842c
2000-10-19 01:49:02 +00:00
drh 58c2976fa9 Version 1.0.12 (CVS 491)
FossilOrigin-Name: 7330218a912ea788b0c83fa7c7350fd6df88b18a
2000-10-17 01:34:59 +00:00
drh 9b1eb48b7d Fix an off-by-one error in printf.c (CVS 156)
FossilOrigin-Name: 17fcd3b01568e95224425b982fb77abd8b12191a
2000-10-17 01:31:22 +00:00
drh 1afad7c98a :-) (CVS 155)
FossilOrigin-Name: 3dc7913dfc4751a00cc5d14f3aa4a3a25d7531bf
2000-10-16 22:11:57 +00:00
drh 611ad21d8b :-) (CVS 154)
FossilOrigin-Name: bae2050f3f63ff0b56ac38fabe220f150cc2d35a
2000-10-16 22:09:38 +00:00
drh 773ab7ae4c Added an interrupt capability (CVS 1706)
FossilOrigin-Name: fb015479b1d0d7c8074e92907044449d56dd6ad8
2000-10-16 22:06:43 +00:00
drh 4c5043910b Added an interrupt capability (CVS 153)
FossilOrigin-Name: f7ea08b931b9b40831bfe73cd7afea17f92112cf
2000-10-16 22:06:40 +00:00
drh b2b415b97f Removed dlmalloc.c (CVS 1705)
FossilOrigin-Name: d67884f6278a130c81bc75f472170eb6c799ddfa
2000-10-12 13:29:49 +00:00
drh 3e85251e81 Version 1.0.10 (CVS 492)
FossilOrigin-Name: e0c9e80bdbaf672c72ead7827946bb9ac6b7ec32
2000-10-11 19:30:00 +00:00
drh fe79b5325e Misc cleanup. Notes on compiling for Win95. (CVS 1704)
FossilOrigin-Name: 217a8af6d0f4396b755b7d21f94ae6fe002728a1
2000-10-11 19:28:52 +00:00
drh 7c68d60b6f Misc cleanup. Notes on compiling for Win95. (CVS 152)
FossilOrigin-Name: 3f0f1fa1fce794d1661c845f1a63a8d744892c25
2000-10-11 19:28:51 +00:00
drh d42b00f654 Version 1.0.9 (CVS 493)
FossilOrigin-Name: ebbb9e4a660e833efddb7ffea737135fff3bf2f5
2000-10-09 22:30:00 +00:00
drh 98699b54bb Documentation changes only (CVS 151)
FossilOrigin-Name: 15340d2bb34c4d7ab629f9fa2231c7f34a248db9
2000-10-09 12:57:00 +00:00
drh a18c5681e9 Added the _printf() interface. (CVS 150)
FossilOrigin-Name: f9372072a6d6caa15fa14ec722523944470fe155
2000-10-08 22:20:57 +00:00
drh 35d5d9e212 Version 1.0.8 (CVS 494)
FossilOrigin-Name: 384909e50f4ae2e84534f9a6505cfb5cc9f4d6ca
2000-09-30 23:00:00 +00:00
drh 9b0d0a8bc5 Documenting the TCL interface (CVS 149)
FossilOrigin-Name: 7e0bacedf928095b29e7166eacd2356e8169d6dd
2000-09-30 22:46:05 +00:00
drh 05a1c8d08c Fix a problem with the configure script (CVS 1703)
FossilOrigin-Name: ed5f5404ad804aec21ad971d59af834929381b5b
2000-09-29 15:15:52 +00:00
drh fdc99e9235 Fix a problem with the configure script (CVS 148)
FossilOrigin-Name: 2aeb82db3ef6fc13eafa4fcf614e8f1dfd247abf
2000-09-29 15:15:51 +00:00
drh e371033ddf :-) (CVS 147)
FossilOrigin-Name: e11f7527f9187e8902db84069baad0d3b7e17ad0
2000-09-29 13:30:53 +00:00
drh 6d31316cf3 Obj-ify tclsqlite (CVS 146)
FossilOrigin-Name: 85a4254ef0998fac06ddc285decd79345968fee1
2000-09-21 13:01:35 +00:00
drh 6562e5af1f Version 1.0.5 (CVS 495)
FossilOrigin-Name: 84839d8764ecdfac4d8c56618d9c107c8bbf65ab
2000-09-14 01:25:00 +00:00
drh a5c2ad06e6 better handling of exponential notation (CVS 145)
FossilOrigin-Name: ea0e32828f5643eeb00b216a287d14f4826ddf2d
2000-09-14 01:21:10 +00:00
drh e09efa30c4 Version 1.0.4 (CVS 496)
FossilOrigin-Name: 92346e003eba759251d23c296f97a7ad6299f5de
2000-08-28 16:25:00 +00:00
drh ff6e9116e5 adding length() and substr() tests. fix shell.c bug (CVS 144)
FossilOrigin-Name: b8cec9b938b4be1ccf230588d8e1564fb3ac4316
2000-08-28 16:21:58 +00:00
drh 6ec2733b44 Added length() and substr() functions (CVS 143)
FossilOrigin-Name: 0eef538f3de66fede7c88f8be8c3458d84107c3f
2000-08-28 15:51:43 +00:00
drh 3d441f8082 Version 1.0.3 (CVS 497)
FossilOrigin-Name: d35a1f8b37333e2df86c22c2dbd766132b4931b2
2000-08-22 18:30:00 +00:00
drh 63e2741e97 bug fix (CVS 142)
FossilOrigin-Name: f255ea6d4bb085a869485c14b4c96af13ca826fa
2000-08-22 18:29:33 +00:00
drh 64bde80a16 :-) (CVS 141)
FossilOrigin-Name: 7ecc9c447bd9259c52e82c8a75830c7fc3381af7
2000-08-22 13:47:56 +00:00
drh 4b1fb13436 :-) (CVS 140)
FossilOrigin-Name: 9d72ceb44d325963ffdf5d247961519dc2cb72ad
2000-08-22 13:43:28 +00:00
drh 3d0b5598c0 :-) (CVS 139)
FossilOrigin-Name: 67e2da38c30ca73707ad48064a23794cfcff7888
2000-08-22 13:40:51 +00:00
drh b217a57eb9 :-) (CVS 138)
FossilOrigin-Name: c6b71d9b4b09243401072eaa278fd1b9757a9c3d
2000-08-22 13:40:18 +00:00
drh 80afdca0d8 :-) (CVS 137)
FossilOrigin-Name: 5f6559ee7b82eb18078ddbb14d815d0ae0c7ff50
2000-08-22 13:27:22 +00:00
drh 61bc272814 bug fix in lemon (CVS 136)
FossilOrigin-Name: 6f4c080739333dbbc3556b5cf96f341f62167646
2000-08-20 11:42:46 +00:00
drh f4b283d086 Version 1.0.1 (CVS 498)
FossilOrigin-Name: e8521fc10dcfa02fc4ae35fa83f5bae4864a3d28
2000-08-18 09:59:59 +00:00
drh ea1fea5a50 :-) (CVS 135)
FossilOrigin-Name: 0a0576e2f9e5404dff68683ef6fdf04d3a350b1a
2000-08-18 09:58:51 +00:00
drh d317676578 configure script bug (CVS 134)
FossilOrigin-Name: 862b649204f27b4d5e8fa16b1140b945c17d0e14
2000-08-18 09:34:19 +00:00
drh bbd4a757a7 configure script bug (CVS 133)
FossilOrigin-Name: c773a449b1f152a83c93a26e169f60c4f30d50a2
2000-08-18 09:33:39 +00:00
46 changed files with 5064 additions and 1505 deletions
+35 -6
View File
@@ -47,25 +47,30 @@ LIBREADLINE = @TARGET_READLINE_LIBS@
# Object files for the SQLite library.
#
LIBOBJ = build.o dbbe.o delete.o expr.o insert.o \
main.o parse.o select.o tokenize.o update.o \
util.o vdbe.o where.o
LIBOBJ = build.o dbbe.o dbbegdbm.o dbbemem.o delete.o expr.o insert.o \
main.o parse.o printf.o select.o table.o tokenize.o update.o \
util.o vdbe.o where.o tclsqlite.o
# All of the source code files.
#
SRC = \
$(TOP)/src/build.c \
$(TOP)/src/dbbe.c \
$(TOP)/src/dbbegdbm.c \
$(TOP)/src/dbbemem.c \
$(TOP)/src/dbbe.h \
$(TOP)/src/dbbemem.c \
$(TOP)/src/delete.c \
$(TOP)/src/expr.c \
$(TOP)/src/insert.c \
$(TOP)/src/main.c \
$(TOP)/src/parse.y \
$(TOP)/src/printf.c \
$(TOP)/src/select.c \
$(TOP)/src/shell.c \
$(TOP)/src/sqlite.h.in \
$(TOP)/src/sqliteInt.h \
$(TOP)/src/table.c \
$(TOP)/src/tclsqlite.c \
$(TOP)/src/tokenize.c \
$(TOP)/src/update.c \
@@ -115,6 +120,12 @@ build.o: $(TOP)/src/build.c $(HDR)
dbbe.o: $(TOP)/src/dbbe.c $(HDR)
$(TCC) $(GDBM_FLAGS) -c $(TOP)/src/dbbe.c
dbbegdbm.o: $(TOP)/src/dbbegdbm.c $(HDR)
$(TCC) $(GDBM_FLAGS) -c $(TOP)/src/dbbegdbm.c
dbbemem.o: $(TOP)/src/dbbemem.c $(HDR)
$(TCC) $(GDBM_FLAGS) -c $(TOP)/src/dbbemem.c
main.o: $(TOP)/src/main.c $(HDR)
$(TCC) $(GDBM_FLAGS) -c $(TOP)/src/main.c
@@ -127,8 +138,8 @@ parse.c: $(TOP)/src/parse.y lemon
cp $(TOP)/src/parse.y .
./lemon parse.y
sqlite.h: $(TOP)/src/sqlite.h.in
sed s/--VERS--/`cat ${TOP}/VERSION`/ $(TOP)/src/sqlite.h.in >sqlite.h
sqlite.h: $(TOP)/src/sqlite.h.in
sed -e s/--VERS--/`cat ${TOP}/VERSION`/ $(TOP)/src/sqlite.h.in >sqlite.h
tokenize.o: $(TOP)/src/tokenize.c $(HDR)
$(TCC) $(GDBM_FLAGS) -c $(TOP)/src/tokenize.c
@@ -154,9 +165,18 @@ insert.o: $(TOP)/src/insert.c $(HDR)
select.o: $(TOP)/src/select.c $(HDR)
$(TCC) $(GDBM_FLAGS) -c $(TOP)/src/select.c
table.o: $(TOP)/src/table.c $(HDR)
$(TCC) $(GDBM_FLAGS) -c $(TOP)/src/table.c
update.o: $(TOP)/src/update.c $(HDR)
$(TCC) $(GDBM_FLAGS) -c $(TOP)/src/update.c
tclsqlite.o: $(TOP)/src/tclsqlite.c $(HDR)
$(TCC) $(GDBM_FLAGS) $(TCL_FLAGS) -c $(TOP)/src/tclsqlite.c
printf.o: $(TOP)/src/printf.c $(HDR)
$(TCC) $(GDBM_FLAGS) $(TCL_FLAGS) -c $(TOP)/src/printf.c
gdbmdump: $(TOP)/tool/gdbmdump.c
$(TCC) $(GDBM_FLAGS) -o gdbmdump $(TOP)/tool/gdbmdump.c $(LIBGDBM)
@@ -206,6 +226,9 @@ crosscompile.html: $(TOP)/www/crosscompile.tcl
mingw.html: $(TOP)/www/mingw.tcl
tclsh $(TOP)/www/mingw.tcl >mingw.html
tclsqlite.html: $(TOP)/www/tclsqlite.tcl
tclsh $(TOP)/www/tclsqlite.tcl >tclsqlite.html
# Files to be published on the website.
#
@@ -222,7 +245,8 @@ PUBLISH = \
vdbe.html \
c_interface.html \
crosscompile.html \
mingw.html
mingw.html \
tclsqlite.html
website: $(PUBLISH)
@@ -230,6 +254,11 @@ publish: $(PUBLISH)
chmod 0644 $(PUBLISH)
scp $(PUBLISH) hwaci@oak.he.net:public_html/sw/sqlite
install: sqlite libsqlite.a sqlite.h
mv sqlite /usr/bin
mv libsqlite.a /usr/lib
mv sqlite.h /usr/include
clean:
rm -f *.o sqlite libsqlite.a sqlite.h
rm -f lemon lempar.c parse.* sqlite.tar.gz
+1 -1
View File
@@ -1 +1 @@
1.0
1.0.17
Vendored
+30 -27
View File
@@ -453,7 +453,7 @@ echo > confdefs.h
# A filename unique to this package, relative to the directory that
# configure is in, which we can look for to find out if srcdir is correct.
ac_unique_file=src/sqlite.h
ac_unique_file=src/sqlite.h.in
# Find the source files, if location was not specified.
if test -z "$srcdir"; then
@@ -525,7 +525,7 @@ fi
# The following RCS revision string applies to configure.in
# $Revision: 1.5 $
# $Revision: 1.8 $
#########
# Make sure we are not building in a subdirectory of the source tree.
@@ -1342,7 +1342,7 @@ fi
fi
if test "$found" = "no"; then
for dir in /usr/local /usr/X11* /usr/pkg /usr/contrib /usr; do
for dir in /usr/local /usr/X11 /usr/X11R6 /usr/pkg /usr/contrib /usr; do
ac_safe=`echo "$dir/include/tcl.h" | sed 'y%./+-%__p_%'`
echo $ac_n "checking for $dir/include/tcl.h""... $ac_c" 1>&6
@@ -1374,6 +1374,9 @@ fi
fi
done
fi
if test "$found" = "no"; then
TARGET_TCL_INC="-DNO_TCL=1"
fi
##########
@@ -1387,14 +1390,14 @@ else
LIBS=""
echo $ac_n "checking for library containing gdbm_open""... $ac_c" 1>&6
echo "configure:1391: checking for library containing gdbm_open" >&5
echo "configure:1394: checking for library containing gdbm_open" >&5
if eval "test \"`echo '$''{'ac_cv_search_gdbm_open'+set}'`\" = set"; then
echo $ac_n "(cached) $ac_c" 1>&6
else
ac_func_search_save_LIBS="$LIBS"
ac_cv_search_gdbm_open="no"
cat > conftest.$ac_ext <<EOF
#line 1398 "configure"
#line 1401 "configure"
#include "confdefs.h"
/* Override any gcc2 internal prototype to avoid an error. */
/* We use char because int might match the return type of a gcc2
@@ -1405,7 +1408,7 @@ int main() {
gdbm_open()
; return 0; }
EOF
if { (eval echo configure:1409: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
if { (eval echo configure:1412: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
rm -rf conftest*
ac_cv_search_gdbm_open="none required"
else
@@ -1416,7 +1419,7 @@ rm -f conftest*
test "$ac_cv_search_gdbm_open" = "no" && for i in gdbm; do
LIBS="-l$i $ac_func_search_save_LIBS"
cat > conftest.$ac_ext <<EOF
#line 1420 "configure"
#line 1423 "configure"
#include "confdefs.h"
/* Override any gcc2 internal prototype to avoid an error. */
/* We use char because int might match the return type of a gcc2
@@ -1427,7 +1430,7 @@ int main() {
gdbm_open()
; return 0; }
EOF
if { (eval echo configure:1431: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
if { (eval echo configure:1434: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
rm -rf conftest*
ac_cv_search_gdbm_open="-l$i"
break
@@ -1455,7 +1458,7 @@ fi
# Figure out where to get the GDBM header files.
#
echo $ac_n "checking GDBM header files""... $ac_c" 1>&6
echo "configure:1459: checking GDBM header files" >&5
echo "configure:1462: checking GDBM header files" >&5
found=no
if test "$config_TARGET_GDBM_INC" != ""; then
TARGET_GDBM_INC=$config_TARGET_GDBM_INC
@@ -1467,17 +1470,17 @@ else
echo "$ac_t""not specified: still searching..." 1>&6
ac_safe=`echo "gdbm.h" | sed 'y%./+-%__p_%'`
echo $ac_n "checking for gdbm.h""... $ac_c" 1>&6
echo "configure:1471: checking for gdbm.h" >&5
echo "configure:1474: checking for gdbm.h" >&5
if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
echo $ac_n "(cached) $ac_c" 1>&6
else
cat > conftest.$ac_ext <<EOF
#line 1476 "configure"
#line 1479 "configure"
#include "confdefs.h"
#include <gdbm.h>
EOF
ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
{ (eval echo configure:1481: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
{ (eval echo configure:1484: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
if test -z "$ac_err"; then
rm -rf conftest*
@@ -1504,7 +1507,7 @@ if test "$found" = "no"; then
ac_safe=`echo "$dir/include/gdbm.h" | sed 'y%./+-%__p_%'`
echo $ac_n "checking for $dir/include/gdbm.h""... $ac_c" 1>&6
echo "configure:1508: checking for $dir/include/gdbm.h" >&5
echo "configure:1511: checking for $dir/include/gdbm.h" >&5
if eval "test \"`echo '$''{'ac_cv_file_$ac_safe'+set}'`\" = set"; then
echo $ac_n "(cached) $ac_c" 1>&6
else
@@ -1545,14 +1548,14 @@ else
LIBS=""
echo $ac_n "checking for library containing readline""... $ac_c" 1>&6
echo "configure:1549: checking for library containing readline" >&5
echo "configure:1552: checking for library containing readline" >&5
if eval "test \"`echo '$''{'ac_cv_search_readline'+set}'`\" = set"; then
echo $ac_n "(cached) $ac_c" 1>&6
else
ac_func_search_save_LIBS="$LIBS"
ac_cv_search_readline="no"
cat > conftest.$ac_ext <<EOF
#line 1556 "configure"
#line 1559 "configure"
#include "confdefs.h"
/* Override any gcc2 internal prototype to avoid an error. */
/* We use char because int might match the return type of a gcc2
@@ -1563,7 +1566,7 @@ int main() {
readline()
; return 0; }
EOF
if { (eval echo configure:1567: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
if { (eval echo configure:1570: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
rm -rf conftest*
ac_cv_search_readline="none required"
else
@@ -1574,7 +1577,7 @@ rm -f conftest*
test "$ac_cv_search_readline" = "no" && for i in readline; do
LIBS="-l$i $ac_func_search_save_LIBS"
cat > conftest.$ac_ext <<EOF
#line 1578 "configure"
#line 1581 "configure"
#include "confdefs.h"
/* Override any gcc2 internal prototype to avoid an error. */
/* We use char because int might match the return type of a gcc2
@@ -1585,7 +1588,7 @@ int main() {
readline()
; return 0; }
EOF
if { (eval echo configure:1589: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
if { (eval echo configure:1592: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
rm -rf conftest*
ac_cv_search_readline="-l$i"
break
@@ -1613,7 +1616,7 @@ fi
# Figure out where to get the READLINE header files.
#
echo $ac_n "checking readline header files""... $ac_c" 1>&6
echo "configure:1617: checking readline header files" >&5
echo "configure:1620: checking readline header files" >&5
found=no
if test "$config_TARGET_READLINE_INC" != ""; then
TARGET_READLINE_INC=$config_TARGET_READLINE_INC
@@ -1625,17 +1628,17 @@ else
echo "$ac_t""not specified: still searching..." 1>&6
ac_safe=`echo "readline.h" | sed 'y%./+-%__p_%'`
echo $ac_n "checking for readline.h""... $ac_c" 1>&6
echo "configure:1629: checking for readline.h" >&5
echo "configure:1632: checking for readline.h" >&5
if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
echo $ac_n "(cached) $ac_c" 1>&6
else
cat > conftest.$ac_ext <<EOF
#line 1634 "configure"
#line 1637 "configure"
#include "confdefs.h"
#include <readline.h>
EOF
ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
{ (eval echo configure:1639: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
{ (eval echo configure:1642: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
if test -z "$ac_err"; then
rm -rf conftest*
@@ -1662,7 +1665,7 @@ if test "$found" = "no"; then
ac_safe=`echo "$dir/include/readline.h" | sed 'y%./+-%__p_%'`
echo $ac_n "checking for $dir/include/readline.h""... $ac_c" 1>&6
echo "configure:1666: checking for $dir/include/readline.h" >&5
echo "configure:1669: checking for $dir/include/readline.h" >&5
if eval "test \"`echo '$''{'ac_cv_file_$ac_safe'+set}'`\" = set"; then
echo $ac_n "(cached) $ac_c" 1>&6
else
@@ -1691,7 +1694,7 @@ fi
ac_safe=`echo "$dir/include/readline/readline.h" | sed 'y%./+-%__p_%'`
echo $ac_n "checking for $dir/include/readline/readline.h""... $ac_c" 1>&6
echo "configure:1695: checking for $dir/include/readline/readline.h" >&5
echo "configure:1698: checking for $dir/include/readline/readline.h" >&5
if eval "test \"`echo '$''{'ac_cv_file_$ac_safe'+set}'`\" = set"; then
echo $ac_n "(cached) $ac_c" 1>&6
else
@@ -1735,12 +1738,12 @@ fi
# Figure out whether or not we have a "usleep()" function.
#
echo $ac_n "checking for usleep""... $ac_c" 1>&6
echo "configure:1739: checking for usleep" >&5
echo "configure:1742: checking for usleep" >&5
if eval "test \"`echo '$''{'ac_cv_func_usleep'+set}'`\" = set"; then
echo $ac_n "(cached) $ac_c" 1>&6
else
cat > conftest.$ac_ext <<EOF
#line 1744 "configure"
#line 1747 "configure"
#include "confdefs.h"
/* System header to define __stub macros and hopefully few prototypes,
which can conflict with char usleep(); below. */
@@ -1763,7 +1766,7 @@ usleep();
; return 0; }
EOF
if { (eval echo configure:1767: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
if { (eval echo configure:1770: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
rm -rf conftest*
eval "ac_cv_func_usleep=yes"
else
+6 -3
View File
@@ -146,12 +146,12 @@
# you don't need (for example BLT) by erasing or commenting out
# the corresponding code.
#
AC_INIT(src/sqlite.h)
AC_INIT(src/sqlite.h.in)
dnl Put the RCS revision string after AC_INIT so that it will also
dnl show in in configure.
# The following RCS revision string applies to configure.in
# $Revision: 1.5 $
# $Revision: 1.8 $
#########
# Make sure we are not building in a subdirectory of the source tree.
@@ -408,7 +408,7 @@ else
AC_CHECK_HEADER(tcl.h, [found=yes])
fi
if test "$found" = "no"; then
for dir in /usr/local /usr/X11* /usr/pkg /usr/contrib /usr; do
for dir in /usr/local /usr/X11 /usr/X11R6 /usr/pkg /usr/contrib /usr; do
AC_CHECK_FILE($dir/include/tcl.h, found=yes)
if test "$found" = "yes"; then
TARGET_TCL_INC="-I$dir/include"
@@ -416,6 +416,9 @@ if test "$found" = "no"; then
fi
done
fi
if test "$found" = "no"; then
TARGET_TCL_INC="-DNO_TCL=1"
fi
AC_SUBST(TARGET_TCL_INC)
##########
+49 -43
View File
@@ -1,76 +1,82 @@
C Version\s1.0\s(CVS\s499)
D 2000-08-17T10:25:00
C Version\s1.0.17\s(CVS\s486)
D 2000-12-10T18:40:00
F COPYRIGHT 74a8a6531a42e124df07ab5599aad63870fa0bd4
F Makefile.in 8a4cc1bdcbb870f76d0c769136cf973af07586a4
F Makefile.in 0b1fdafa55e1bf4d3a4f5213544130e66ef32052
F README 51f6a4e7408b34afa5bc1c0485f61b6a4efb6958
F VERSION 61652cd1568dcf2614df833eba241755eee34e89
F configure 51063d594190fa085f909cefc9427241088bec4f x
F configure.in a04f02ba61ed09a00e862b4f78b91b06a559e0b5
F VERSION 2436af0010b224c49639a6ce276551a862a08115
F configure 3dc1edb9dcf60215e31ff72b447935ab62211442 x
F configure.in d892ca33db7e88a055519ce2f36dcb11020e8fff
F doc/lemon.html e233a3e97a779c7a87e1bc4528c664a58e49dd47
F src/build.c 4d90e9e94750ca80249fc7958c617021d8bb7a50
F src/dbbe.c 226daaf8c095ceb4aff48cad188dad90643f9867
F src/dbbe.h 6337132f904e72ecb28b07390021c241397e4cbf
F src/build.c e2ceba852dc45ca899e68a042b29c3daab011575
F src/dbbe.c 7e01384320075bf1d3e7fb54984df73435908809
F src/dbbe.h d175a04b35ea75078274e059dcbcbf7c1262d42a
F src/dbbegdbm.c 4ac7222afff0cf91014803f8791740b6da825a2b
F src/dbbemem.c b55faed2e4719db47ade3483e85f7cb4b5fad39a
F src/delete.c 4d491eaf61b515516749c7ed68fa3b2ee8a09065
F src/expr.c 2fa63f086707176d09092e71832f9bbdc6a8ac85
F src/expr.c 7d7646afd52d1448237e5f517454cfb2d3d394d6
F src/insert.c f146f149ad2422a1dc3bfa7a1651a25940f98958
F src/main.c ba16b81890d962821bb90f0a4de9a29b0e495eb2
F src/main.c 52355458131977c1ac40a006ce2fe2035f39069c
F src/parse.y 5d199034de5d29ebedb42c1c51f34db4df40cbe5
F src/select.c d382e96c2221d08367cc87976f2b574537c9de97
F src/shell.c 0aabf9e5e89fc8ac7a56de7f05255ed6864170f2
F src/printf.c 1efb6b3e7f28a93be57132de3f8f400d2ac1460e
F src/select.c c1de8ac34131324fa05664b06b0ae1ee9c02905d
F src/shell.c 146149928c7dca23ceebd6aa53026e9b02d3b4e4
F src/shell.tcl 27ecbd63dd88396ad16d81ab44f73e6c0ea9d20e
F src/sqlite.h.in fe04454ae51f9f3717179ffe5a10a25874643ced
F src/sqliteInt.h f6d1e139b3bfa4ceff2136684e19d76b53178ec0
F src/tclsqlite.c 89dc4ba2b521f3e919d6d7aaa4cc1c2aba8e16f3
F src/tokenize.c 097bec5843d4a0fb4509e036fee93bac080c5e73
F src/sqlite.h.in fd86903777f1ee7bd0465243224a0fd2100eedc8
F src/sqliteInt.h ed06214fc7054e43182ac351ebde913ede708639
F src/table.c 5be76051a8ed6f6bfa641f4adc52529efa34fbf9
F src/tclsqlite.c 178adf318eab2ff480c288a87541d4ab1c37d985
F src/tokenize.c 6843f1d7a5d2ee08ceb10bdecfcc8684131ffcf7
F src/update.c 51b9ef7434b15e31096155da920302e9db0d27fc
F src/util.c b75b33e6bd5d47898bb7ed9fdd0dea4fe7c19b00
F src/vdbe.c bdedf21230581f0cf73a2dcd8fe23f30cf30ebe6
F src/vdbe.h 6c5653241633c583549c2d8097394ab52550eb63
F src/where.c 420f666a38b405cd58bd7af832ed99f1dbc7d336
F test/all.test 0950c135cab7e60c07bd745ccfad1476211e5bd7
F src/util.c 0298100e6427a4b644f767ede12276fa7170fbb6
F src/vdbe.c 3ddd7e3d17c954fc50afd5e73898371da09ad1c6
F src/vdbe.h 140cdec3c56f70483e169f8ae657bd90f9fd6e98
F src/where.c 3dfad2ffd0aa994d5eceac88852f7189c8d1d3c8
F test/all.test 15cac2f6b2d4c55bf896212aff3cc9d6597b0490
F test/copy.test b77a1214bd7756f2849d5c4fa6e715c0ff0c34eb
F test/dbbe.test c6079572516aeb2739a35fd272b105c45c30cc76
F test/dbbe.test bd2cd9fe84c6d69b6ae42ac5f55b1e940bdca886
F test/delete.test 402ee3ccb6e544582d24c573ef70b34d09583ae7
F test/expr.test 09b55ccf81cb8cc2f9cd83d592a2ba187ee48ba8
F test/expr.test 48273bf48a15d226c35829f702af4254c0ff6795
F test/func.test 02aed8845b98bde1043dda97455de1d37238ebb3
F test/in.test 2c560c0f55fb777029fd9bb5378f2997582aa603
F test/index.test 950be6116122c6e2db7c2c345eabcdb854ced1d0
F test/index.test ee060ef8912be47ba616e50cce7985259a68d58a
F test/insert.test 66f4c3bd600fec8eb1e733b928cbe6fa885eff0c
F test/insert2.test 732405e30331635af8d159fccabe835eea5cd0c6
F test/lock.test 72f3b11be44f29739cb270b14faea2687dedde7d
F test/main.test b7366cc6f3690915a11834bc1090deeff08acaf9
F test/lock.test f56cf41d29d2c4cbaa6239424b5b0ee844c273a0
F test/main.test 83cfa58b0b9b69924cc915d5c50b12f0ac9e40e7
F test/select1.test 68ff778c24fc8982e63dda37acb5b0396913adf7
F test/select2.test 45c28211702b5c82b06dd624a112ea17417f343c
F test/select2.test 0c24b9bb8825ebb96e6cc65f1eb61bace0e02aa0
F test/select3.test a9234b8424b6c6d71de534f43b91ade9be68e9cc
F test/select4.test cb5374d7c87680e294ac749307459a5cc547609d
F test/select5.test e2b9d51d88cbd6c307c2c05b0ef55fe7ba811ac2
F test/sort.test d582086c4bb7df3fbf50aa72e69d7e235e9f8e31
F test/subselect.test bf8b251a92fb091973c1c469ce499dc9648a41d5
F test/table.test 620cd72a6c29da3b9153d15c9e94abbbb282373b
F test/tester.tcl 95b286791e6256bb6db0165f9342c70fff549a62
F test/table.test eaa25951c0f18615763cd3dc248ea4bc38739c05
F test/tester.tcl 446b88283b12efb12691479a403cde15d64fbb82
F test/update.test 62f6ce99ff31756aab0ca832ff6d34c5a87b6250
F test/vacuum.test 8becf5cfeb897108b35cdd996793e7f1df2f28fd
F test/vacuum.test 2127748ff4ddb409212efbb6d9fb9c469ea1b49c
F test/where.test bbab5a308055fb6087dc23d600b4ad2b72797397
F tool/gdbmdump.c 529e67c78d920606ba196326ea55b57b75fcc82b
F tool/lemon.c 1f0e96515c12e9e413f5b8fdebc79ddcf18ddc9d
F tool/lemon.c b13a31798574af881753d38f4da7d505929259c3
F tool/lempar.c a1eec94d6eacc12332368660ec65f3b248853833
F tool/memleak.awk a0a11dd84bf4582acc81c3c61271021ae49b3f15
F tool/opNames.awk 2bd9071a138e4e2be13dc98fe066398a61219e1e
F tool/opcodeDoc.awk b3a2a3d5d3075b8bd90b7afe24283efdd586659c
F tool/renumberOps.awk 6d067177ad5f8d711b79577b462da9b3634bd0a9
F www/arch.fig 4e26e9dca3c49724fc8f554c695ddea9f2413156
F www/arch.png c4d908b79065a72e7dcf19317f36d1324c550e87
F www/arch.tcl 4f6a9afecc099a27bba17b4f8cc9561abc15dc40
F www/c_interface.tcl 29593cf77025bab137b7ba64b9459eb5eb6b4873
F www/changes.tcl 32a1a09297095ffa3e49473732e4b60fc6cdc7c1
F www/crosscompile.tcl 19734ce7f18b16ff2ed8479412abf8aca56e1dcc
F www/arch.fig 4f246003b7da23bd63b8b0af0618afb4ee3055c8
F www/arch.png 8dae0766d42ed3de9ed013c1341a5792bcf633e6
F www/arch.tcl a40380c1fe0080c43e6cc5c20ed70731511b06be
F www/c_interface.tcl 11be2d5826eb7d6efd629751d3b483c1ed78ba14
F www/changes.tcl 3b0abdf91bef39602876788bbf0e1c4585784363
F www/crosscompile.tcl bee79c34f6c3f162ec1c6f5294e79f73651d27ee
F www/fileformat.tcl cfb7fba80b7275555281ba2f256c00734bcdd1c9
F www/index.tcl 6dae7e07d110ce06c327223365dc1b475b4fb6ee
F www/index.tcl b19418d506f90968deef972bf1b427d98bdf13e0
F www/lang.tcl 9192e114b19987e630a41e879585b87006eb84a1
F www/mingw.tcl fc5f4ba9d336b6e8c97347cc6496d6162461ef60
F www/opcode.tcl cb3a1abf8b7b9be9f3a228d097d6bf8b742c2b6f
F www/sqlite.tcl cb0d23d8f061a80543928755ec7775da6e4f362f
F www/vdbe.tcl bcbfc33bcdd0ebad95eab31286adb9e1bc289520
P 5ec2b094781bb3c32b0ef0284aa4dc2b26d9e873
R 55d9f580b0fd85533f8ccc00b91b305d
F www/tclsqlite.tcl 06f81c401f79a04f2c5ebfb97e7c176225c0aef2
F www/vdbe.tcl 0c8aaa529dd216ccbf7daaabd80985e413d5f9ad
P b46f9a317b06bd490bda987e11e24f2c27162370
R c7a71bdb40c0db1def459d2ec0493e1b
U drh
Z f346b6602fac1a26cb3e2b4625824e0f
Z 76f9c173ea2160a2a9011f55704b6843
+1 -1
View File
@@ -1 +1 @@
f37dd18e3fc6314eda2af786d74e0a445b9b2287
bee0c818590ccdbb96e0a4cb2998bc077564b2ae
+8 -6
View File
@@ -33,7 +33,7 @@
** COPY
** VACUUM
**
** $Id: build.c,v 1.23 2000/08/03 15:09:20 drh Exp $
** $Id: build.c,v 1.24 2000/10/16 22:06:42 drh Exp $
*/
#include "sqliteInt.h"
@@ -48,20 +48,22 @@
** no VDBE code was generated.
*/
void sqliteExec(Parse *pParse){
int rc = SQLITE_OK;
if( pParse->pVdbe ){
if( pParse->explain ){
sqliteVdbeList(pParse->pVdbe, pParse->xCallback, pParse->pArg,
&pParse->zErrMsg);
rc = sqliteVdbeList(pParse->pVdbe, pParse->xCallback, pParse->pArg,
&pParse->zErrMsg);
}else{
FILE *trace = (pParse->db->flags & SQLITE_VdbeTrace)!=0 ? stderr : 0;
sqliteVdbeTrace(pParse->pVdbe, trace);
sqliteVdbeExec(pParse->pVdbe, pParse->xCallback, pParse->pArg,
&pParse->zErrMsg, pParse->db->pBusyArg,
pParse->db->xBusyCallback);
rc = sqliteVdbeExec(pParse->pVdbe, pParse->xCallback, pParse->pArg,
&pParse->zErrMsg, pParse->db->pBusyArg,
pParse->db->xBusyCallback);
}
sqliteVdbeDelete(pParse->pVdbe);
pParse->pVdbe = 0;
pParse->colNamesSet = 0;
pParse->rc = rc;
}
}
+14 -690
View File
@@ -30,124 +30,17 @@
** relatively simple to convert to a different database such
** as NDBM, SDBM, or BerkeleyDB.
**
** $Id: dbbe.c,v 1.19 2000/08/17 09:50:00 drh Exp $
** $Id: dbbe.c,v 1.21 2000/10/19 14:10:09 drh Exp $
*/
#include "sqliteInt.h"
#include <gdbm.h>
#include <sys/stat.h>
#include <unistd.h>
#include <ctype.h>
#include <time.h>
/*
** Information about each open disk file is an instance of this
** structure. There will only be one such structure for each
** disk file. If the VDBE opens the same file twice (as will happen
** for a self-join, for example) then two DbbeCursor structures are
** created but there is only a single BeFile structure with an
** nRef of 2.
*/
typedef struct BeFile BeFile;
struct BeFile {
char *zName; /* Name of the file */
GDBM_FILE dbf; /* The file itself */
int nRef; /* Number of references */
int delOnClose; /* Delete when closing */
int writeable; /* Opened for writing */
BeFile *pNext, *pPrev; /* Next and previous on list of open files */
};
/*
** The following structure holds the current state of the RC4 algorithm.
** We use RC4 as a random number generator. Each call to RC4 gives
** a random 8-bit number.
**
** Nothing in this file or anywhere else in SQLite does any kind of
** encryption. The RC4 algorithm is being used as a PRNG (pseudo-random
** number generator) not as an encryption device.
*/
struct rc4 {
int i, j;
int s[256];
};
/*
** The complete database is an instance of the following structure.
*/
struct Dbbe {
char *zDir; /* The directory containing the database */
int write; /* True for write permission */
BeFile *pOpen; /* List of open files */
int nTemp; /* Number of temporary files created */
FILE **apTemp; /* Space to hold temporary file pointers */
char **azTemp; /* Names of the temporary files */
struct rc4 rc4; /* The random number generator */
};
/*
** An cursor into a database file is an instance of the following structure.
** There can only be a single BeFile structure for each disk file, but
** there can be multiple DbbeCursor structures. Each DbbeCursor represents
** a cursor pointing to a particular part of the open BeFile. The
** BeFile.nRef field hold a count of the number of DbbeCursor structures
** associated with the same disk file.
*/
struct DbbeCursor {
Dbbe *pBe; /* The database of which this record is a part */
BeFile *pFile; /* The database file for this table */
datum key; /* Most recently used key */
datum data; /* Most recent data */
int needRewind; /* Next key should be the first */
int readPending; /* The fetch hasn't actually been done yet */
};
/*
** Initialize the RC4 PRNG. "seed" is a pointer to some random
** data used to initialize the PRNG.
*/
static void rc4init(struct rc4 *p, char *seed, int seedlen){
int i;
char k[256];
p->j = 0;
p->i = 0;
for(i=0; i<256; i++){
p->s[i] = i;
k[i] = seed[i%seedlen];
}
for(i=0; i<256; i++){
int t;
p->j = (p->j + p->s[i] + k[i]) & 0xff;
t = p->s[p->j];
p->s[p->j] = p->s[i];
p->s[i] = t;
}
}
/*
** Get a single 8-bit random value from the RC4 PRNG.
*/
static int rc4byte(struct rc4 *p){
int t;
p->i = (p->i + 1) & 0xff;
p->j = (p->j + p->s[p->i]) & 0xff;
t = p->s[p->i];
p->s[p->i] = p->s[p->j];
p->s[p->j] = t;
t = p->s[p->i] + p->s[p->j];
return t & 0xff;
}
/*
** The "mkdir()" function only takes one argument under Windows.
*/
#if OS_WIN
# define mkdir(A,B) mkdir(A)
#endif
/*
** This routine opens a new database. For the GDBM driver
** implemented here, the database name is the name of the directory
** containing all the files of the database.
** This routine opens a new database. It looks at the first
** few characters of the database name to try to determine what
** kind of database to open. If the first characters are "gdbm:",
** then it uses the GDBM driver. If the first few characters are
** "memory:" then it uses the in-memory driver. If there is no
** match, the default to the GDBM driver.
**
** If successful, a pointer to the Dbbe structure is returned.
** If there are errors, an appropriate error message is left
@@ -159,582 +52,13 @@ Dbbe *sqliteDbbeOpen(
int createFlag, /* True to create database if it doesn't exist */
char **pzErrMsg /* Write error messages (if any) here */
){
Dbbe *pNew;
struct stat statbuf;
char *zMaster;
if( !writeFlag ) createFlag = 0;
if( stat(zName, &statbuf)!=0 ){
if( createFlag ) mkdir(zName, 0750);
if( stat(zName, &statbuf)!=0 ){
sqliteSetString(pzErrMsg, createFlag ?
"can't find or create directory \"" : "can't find directory \"",
zName, "\"", 0);
return 0;
}
extern Dbbe *sqliteGdbmOpen(const char*,int,int,char**);
if( strncmp(zName, "gdbm:", 5)==0 ){
return sqliteGdbmOpen(&zName[5], writeFlag, createFlag, pzErrMsg);
}
if( !S_ISDIR(statbuf.st_mode) ){
sqliteSetString(pzErrMsg, "not a directory: \"", zName, "\"", 0);
return 0;
if( strncmp(zName, "memory:", 7)==0 ){
extern Dbbe *sqliteMemOpen(const char*,int,int,char**);
return sqliteMemOpen(&zName[7], writeFlag, createFlag, pzErrMsg);
}
if( access(zName, writeFlag ? (X_OK|W_OK|R_OK) : (X_OK|R_OK)) ){
sqliteSetString(pzErrMsg, "access permission denied", 0);
return 0;
}
zMaster = 0;
sqliteSetString(&zMaster, zName, "/" MASTER_NAME ".tbl", 0);
if( stat(zMaster, &statbuf)==0
&& access(zMaster, writeFlag ? (W_OK|R_OK) : R_OK)!=0 ){
sqliteSetString(pzErrMsg, "access permission denied for ", zMaster, 0);
sqliteFree(zMaster);
return 0;
}
sqliteFree(zMaster);
pNew = sqliteMalloc(sizeof(Dbbe) + strlen(zName) + 1);
if( pNew==0 ){
sqliteSetString(pzErrMsg, "out of memory", 0);
return 0;
}
pNew->zDir = (char*)&pNew[1];
strcpy(pNew->zDir, zName);
pNew->write = writeFlag;
pNew->pOpen = 0;
time(&statbuf.st_ctime);
rc4init(&pNew->rc4, (char*)&statbuf, sizeof(statbuf));
return pNew;
}
/*
** Completely shutdown the given database. Close all files. Free all memory.
*/
void sqliteDbbeClose(Dbbe *pBe){
BeFile *pFile, *pNext;
int i;
for(pFile=pBe->pOpen; pFile; pFile=pNext){
pNext = pFile->pNext;
gdbm_close(pFile->dbf);
memset(pFile, 0, sizeof(*pFile));
sqliteFree(pFile);
}
for(i=0; i<pBe->nTemp; i++){
if( pBe->apTemp[i]!=0 ){
unlink(pBe->azTemp[i]);
fclose(pBe->apTemp[i]);
sqliteFree(pBe->azTemp[i]);
pBe->apTemp[i] = 0;
pBe->azTemp[i] = 0;
break;
}
}
sqliteFree(pBe->azTemp);
sqliteFree(pBe->apTemp);
memset(pBe, 0, sizeof(*pBe));
sqliteFree(pBe);
}
/*
** Translate the name of an SQL table (or index) into the name
** of a file that holds the key/data pairs for that table or
** index. Space to hold the filename is obtained from
** sqliteMalloc() and must be freed by the calling function.
*/
static char *sqliteFileOfTable(Dbbe *pBe, const char *zTable){
char *zFile = 0;
int i;
sqliteSetString(&zFile, pBe->zDir, "/", zTable, ".tbl", 0);
if( zFile==0 ) return 0;
for(i=strlen(pBe->zDir)+1; zFile[i]; i++){
int c = zFile[i];
if( isupper(c) ){
zFile[i] = tolower(c);
}else if( !isalnum(c) && c!='-' && c!='_' && c!='.' ){
zFile[i] = '+';
}
}
return zFile;
}
/*
** Generate a random filename with the given prefix. The new filename
** is written into zBuf[]. The calling function must insure that
** zBuf[] is big enough to hold the prefix plus 20 or so extra
** characters.
**
** Very random names are chosen so that the chance of a
** collision with an existing filename is very very small.
*/
static void randomName(struct rc4 *pRc4, char *zBuf, char *zPrefix){
int i, j;
static const char zRandomChars[] = "abcdefghijklmnopqrstuvwxyz0123456789";
strcpy(zBuf, zPrefix);
j = strlen(zBuf);
for(i=0; i<15; i++){
int c = rc4byte(pRc4) % (sizeof(zRandomChars) - 1);
zBuf[j++] = zRandomChars[c];
}
zBuf[j] = 0;
}
/*
** Open a new table cursor. Write a pointer to the corresponding
** DbbeCursor structure into *ppCursr. Return an integer success
** code:
**
** SQLITE_OK It worked!
**
** SQLITE_NOMEM sqliteMalloc() failed
**
** SQLITE_PERM Attempt to access a file for which file
** access permission is denied
**
** SQLITE_BUSY Another thread or process is already using
** the corresponding file and has that file locked.
**
** SQLITE_READONLY The current thread already has this file open
** readonly but you are trying to open for writing.
** (This can happen if a SELECT callback tries to
** do an UPDATE or DELETE.)
**
** If zTable is 0 or "", then a temporary database file is created and
** a cursor to that temporary file is opened. The temporary file
** will be deleted from the disk when it is closed.
*/
int sqliteDbbeOpenCursor(
Dbbe *pBe, /* The database the table belongs to */
const char *zTable, /* The SQL name of the file to be opened */
int writeable, /* True to open for writing */
DbbeCursor **ppCursr /* Write the resulting table pointer here */
){
char *zFile; /* Name of the table file */
DbbeCursor *pCursr; /* The new table cursor */
BeFile *pFile; /* The underlying data file for this table */
int rc = SQLITE_OK; /* Return value */
int rw_mask; /* Permissions mask for opening a table */
int mode; /* Mode for opening a table */
*ppCursr = 0;
pCursr = sqliteMalloc( sizeof(*pCursr) );
if( pCursr==0 ) return SQLITE_NOMEM;
if( zTable ){
zFile = sqliteFileOfTable(pBe, zTable);
for(pFile=pBe->pOpen; pFile; pFile=pFile->pNext){
if( strcmp(pFile->zName,zFile)==0 ) break;
}
}else{
pFile = 0;
zFile = 0;
}
if( pFile==0 ){
if( writeable ){
rw_mask = GDBM_WRCREAT | GDBM_FAST;
mode = 0640;
}else{
rw_mask = GDBM_READER;
mode = 0640;
}
pFile = sqliteMalloc( sizeof(*pFile) );
if( pFile==0 ){
sqliteFree(zFile);
return SQLITE_NOMEM;
}
if( zFile ){
if( !writeable || pBe->write ){
pFile->dbf = gdbm_open(zFile, 0, rw_mask, mode, 0);
}else{
pFile->dbf = 0;
}
}else{
int limit;
struct rc4 *pRc4;
char zRandom[50];
pRc4 = &pBe->rc4;
zFile = 0;
limit = 5;
do {
randomName(&pBe->rc4, zRandom, "_temp_table_");
sqliteFree(zFile);
zFile = sqliteFileOfTable(pBe, zRandom);
pFile->dbf = gdbm_open(zFile, 0, rw_mask, mode, 0);
}while( pFile->dbf==0 && limit-- >= 0);
pFile->delOnClose = 1;
}
pFile->writeable = writeable;
pFile->zName = zFile;
pFile->nRef = 1;
pFile->pPrev = 0;
if( pBe->pOpen ){
pBe->pOpen->pPrev = pFile;
}
pFile->pNext = pBe->pOpen;
pBe->pOpen = pFile;
if( pFile->dbf==0 ){
if( !writeable && access(zFile,0) ){
/* Trying to read a non-existant file. This is OK. All the
** reads will return empty, which is what we want. */
rc = SQLITE_OK;
}else if( pBe->write==0 ){
rc = SQLITE_READONLY;
}else if( access(zFile,W_OK|R_OK) ){
rc = SQLITE_PERM;
}else{
rc = SQLITE_BUSY;
}
}
}else{
sqliteFree(zFile);
pFile->nRef++;
if( writeable && !pFile->writeable ){
rc = SQLITE_READONLY;
}
}
pCursr->pBe = pBe;
pCursr->pFile = pFile;
pCursr->readPending = 0;
pCursr->needRewind = 1;
if( rc!=SQLITE_OK ){
sqliteDbbeCloseCursor(pCursr);
*ppCursr = 0;
}else{
*ppCursr = pCursr;
}
return rc;
}
/*
** Drop a table from the database. The file on the disk that corresponds
** to this table is deleted.
*/
void sqliteDbbeDropTable(Dbbe *pBe, const char *zTable){
char *zFile; /* Name of the table file */
zFile = sqliteFileOfTable(pBe, zTable);
unlink(zFile);
sqliteFree(zFile);
}
/*
** Reorganize a table to reduce search times and disk usage.
*/
int sqliteDbbeReorganizeTable(Dbbe *pBe, const char *zTable){
DbbeCursor *pCrsr;
int rc;
rc = sqliteDbbeOpenCursor(pBe, zTable, 1, &pCrsr);
if( rc!=SQLITE_OK ){
return rc;
}
if( pCrsr && pCrsr->pFile && pCrsr->pFile->dbf ){
gdbm_reorganize(pCrsr->pFile->dbf);
}
if( pCrsr ){
sqliteDbbeCloseCursor(pCrsr);
}
return SQLITE_OK;
}
/*
** Close a cursor previously opened by sqliteDbbeOpenCursor().
**
** There can be multiple cursors pointing to the same open file.
** The underlying file is not closed until all cursors have been
** closed. This routine decrements the BeFile.nref field of the
** underlying file and closes the file when nref reaches 0.
*/
void sqliteDbbeCloseCursor(DbbeCursor *pCursr){
BeFile *pFile;
Dbbe *pBe;
if( pCursr==0 ) return;
pFile = pCursr->pFile;
pBe = pCursr->pBe;
pFile->nRef--;
if( pFile->dbf!=NULL ){
gdbm_sync(pFile->dbf);
}
if( pFile->nRef<=0 ){
if( pFile->dbf!=NULL ){
gdbm_close(pFile->dbf);
}
if( pFile->pPrev ){
pFile->pPrev->pNext = pFile->pNext;
}else{
pBe->pOpen = pFile->pNext;
}
if( pFile->pNext ){
pFile->pNext->pPrev = pFile->pPrev;
}
if( pFile->delOnClose ){
unlink(pFile->zName);
}
sqliteFree(pFile->zName);
memset(pFile, 0, sizeof(*pFile));
sqliteFree(pFile);
}
if( pCursr->key.dptr ) free(pCursr->key.dptr);
if( pCursr->data.dptr ) free(pCursr->data.dptr);
memset(pCursr, 0, sizeof(*pCursr));
sqliteFree(pCursr);
}
/*
** Clear the given datum
*/
static void datumClear(datum *p){
if( p->dptr ) free(p->dptr);
p->dptr = 0;
p->dsize = 0;
}
/*
** Fetch a single record from an open cursor. Return 1 on success
** and 0 on failure.
*/
int sqliteDbbeFetch(DbbeCursor *pCursr, int nKey, char *pKey){
datum key;
key.dsize = nKey;
key.dptr = pKey;
datumClear(&pCursr->key);
datumClear(&pCursr->data);
if( pCursr->pFile && pCursr->pFile->dbf ){
pCursr->data = gdbm_fetch(pCursr->pFile->dbf, key);
}
return pCursr->data.dptr!=0;
}
/*
** Return 1 if the given key is already in the table. Return 0
** if it is not.
*/
int sqliteDbbeTest(DbbeCursor *pCursr, int nKey, char *pKey){
datum key;
int result = 0;
key.dsize = nKey;
key.dptr = pKey;
if( pCursr->pFile && pCursr->pFile->dbf ){
result = gdbm_exists(pCursr->pFile->dbf, key);
}
return result;
}
/*
** Copy bytes from the current key or data into a buffer supplied by
** the calling function. Return the number of bytes copied.
*/
int sqliteDbbeCopyKey(DbbeCursor *pCursr, int offset, int size, char *zBuf){
int n;
if( offset>=pCursr->key.dsize ) return 0;
if( offset+size>pCursr->key.dsize ){
n = pCursr->key.dsize - offset;
}else{
n = size;
}
memcpy(zBuf, &pCursr->key.dptr[offset], n);
return n;
}
int sqliteDbbeCopyData(DbbeCursor *pCursr, int offset, int size, char *zBuf){
int n;
if( pCursr->readPending && pCursr->pFile && pCursr->pFile->dbf ){
pCursr->data = gdbm_fetch(pCursr->pFile->dbf, pCursr->key);
pCursr->readPending = 0;
}
if( offset>=pCursr->data.dsize ) return 0;
if( offset+size>pCursr->data.dsize ){
n = pCursr->data.dsize - offset;
}else{
n = size;
}
memcpy(zBuf, &pCursr->data.dptr[offset], n);
return n;
}
/*
** Return a pointer to bytes from the key or data. The data returned
** is ephemeral.
*/
char *sqliteDbbeReadKey(DbbeCursor *pCursr, int offset){
if( offset<0 || offset>=pCursr->key.dsize ) return "";
return &pCursr->key.dptr[offset];
}
char *sqliteDbbeReadData(DbbeCursor *pCursr, int offset){
if( pCursr->readPending && pCursr->pFile && pCursr->pFile->dbf ){
pCursr->data = gdbm_fetch(pCursr->pFile->dbf, pCursr->key);
pCursr->readPending = 0;
}
if( offset<0 || offset>=pCursr->data.dsize ) return "";
return &pCursr->data.dptr[offset];
}
/*
** Return the total number of bytes in either data or key.
*/
int sqliteDbbeKeyLength(DbbeCursor *pCursr){
return pCursr->key.dsize;
}
int sqliteDbbeDataLength(DbbeCursor *pCursr){
if( pCursr->readPending && pCursr->pFile && pCursr->pFile->dbf ){
pCursr->data = gdbm_fetch(pCursr->pFile->dbf, pCursr->key);
pCursr->readPending = 0;
}
return pCursr->data.dsize;
}
/*
** Make is so that the next call to sqliteNextKey() finds the first
** key of the table.
*/
int sqliteDbbeRewind(DbbeCursor *pCursr){
pCursr->needRewind = 1;
return SQLITE_OK;
}
/*
** Read the next key from the table. Return 1 on success. Return
** 0 if there are no more keys.
*/
int sqliteDbbeNextKey(DbbeCursor *pCursr){
datum nextkey;
int rc;
if( pCursr==0 || pCursr->pFile==0 || pCursr->pFile->dbf==0 ){
pCursr->readPending = 0;
return 0;
}
if( pCursr->needRewind ){
nextkey = gdbm_firstkey(pCursr->pFile->dbf);
pCursr->needRewind = 0;
}else{
nextkey = gdbm_nextkey(pCursr->pFile->dbf, pCursr->key);
}
datumClear(&pCursr->key);
datumClear(&pCursr->data);
pCursr->key = nextkey;
if( pCursr->key.dptr ){
pCursr->readPending = 1;
rc = 1;
}else{
pCursr->needRewind = 1;
pCursr->readPending = 0;
rc = 0;
}
return rc;
}
/*
** Get a new integer key.
*/
int sqliteDbbeNew(DbbeCursor *pCursr){
int iKey;
datum key;
int go = 1;
int i;
struct rc4 *pRc4;
if( pCursr->pFile==0 || pCursr->pFile->dbf==0 ) return 1;
pRc4 = &pCursr->pBe->rc4;
while( go ){
iKey = 0;
for(i=0; i<4; i++){
iKey = (iKey<<8) + rc4byte(pRc4);
}
if( iKey==0 ) continue;
key.dptr = (char*)&iKey;
key.dsize = 4;
go = gdbm_exists(pCursr->pFile->dbf, key);
}
return iKey;
}
/*
** Write an entry into the table. Overwrite any prior entry with the
** same key.
*/
int sqliteDbbePut(DbbeCursor *pCursr, int nKey,char *pKey,int nData,char *pData){
datum data, key;
int rc;
if( pCursr->pFile==0 || pCursr->pFile->dbf==0 ) return SQLITE_ERROR;
data.dsize = nData;
data.dptr = pData;
key.dsize = nKey;
key.dptr = pKey;
rc = gdbm_store(pCursr->pFile->dbf, key, data, GDBM_REPLACE);
if( rc ) rc = SQLITE_ERROR;
datumClear(&pCursr->key);
datumClear(&pCursr->data);
return rc;
}
/*
** Remove an entry from a table, if the entry exists.
*/
int sqliteDbbeDelete(DbbeCursor *pCursr, int nKey, char *pKey){
datum key;
int rc;
datumClear(&pCursr->key);
datumClear(&pCursr->data);
if( pCursr->pFile==0 || pCursr->pFile->dbf==0 ) return SQLITE_ERROR;
key.dsize = nKey;
key.dptr = pKey;
rc = gdbm_delete(pCursr->pFile->dbf, key);
if( rc ) rc = SQLITE_ERROR;
return rc;
}
/*
** Open a temporary file. The file should be deleted when closed.
**
** Note that we can't use the old Unix trick of opening the file
** and then immediately unlinking the file. That works great
** under Unix, but fails when we try to port to Windows.
*/
int sqliteDbbeOpenTempFile(Dbbe *pBe, FILE **ppFile){
char *zFile; /* Full name of the temporary file */
char zBuf[50]; /* Base name of the temporary file */
int i; /* Loop counter */
int limit; /* Prevent an infinite loop */
int rc = SQLITE_OK; /* Value returned by this function */
for(i=0; i<pBe->nTemp; i++){
if( pBe->apTemp[i]==0 ) break;
}
if( i>=pBe->nTemp ){
pBe->nTemp++;
pBe->apTemp = sqliteRealloc(pBe->apTemp, pBe->nTemp*sizeof(FILE*) );
pBe->azTemp = sqliteRealloc(pBe->azTemp, pBe->nTemp*sizeof(char*) );
}
if( pBe->apTemp==0 ){
*ppFile = 0;
return SQLITE_NOMEM;
}
limit = 4;
zFile = 0;
do{
randomName(&pBe->rc4, zBuf, "/_temp_file_");
sqliteFree(zFile);
zFile = 0;
sqliteSetString(&zFile, pBe->zDir, zBuf, 0);
}while( access(zFile,0)==0 && limit-- >= 0 );
*ppFile = pBe->apTemp[i] = fopen(zFile, "w+");
if( pBe->apTemp[i]==0 ){
rc = SQLITE_ERROR;
sqliteFree(zFile);
pBe->azTemp[i] = 0;
}else{
pBe->azTemp[i] = zFile;
}
return rc;
}
/*
** Close a temporary file opened using sqliteDbbeOpenTempFile()
*/
void sqliteDbbeCloseTempFile(Dbbe *pBe, FILE *f){
int i;
for(i=0; i<pBe->nTemp; i++){
if( pBe->apTemp[i]==f ){
unlink(pBe->azTemp[i]);
sqliteFree(pBe->azTemp[i]);
pBe->apTemp[i] = 0;
pBe->azTemp[i] = 0;
break;
}
}
fclose(f);
return sqliteGdbmOpen(zName, writeFlag, createFlag, pzErrMsg);
}
+76 -67
View File
@@ -28,7 +28,7 @@
** This library was originally designed to support the following
** backends: GDBM, NDBM, SDBM, Berkeley DB.
**
** $Id: dbbe.h,v 1.7 2000/08/17 09:50:00 drh Exp $
** $Id: dbbe.h,v 1.8 2000/10/19 01:49:02 drh Exp $
*/
#ifndef _SQLITE_DBBE_H_
#define _SQLITE_DBBE_H_
@@ -54,90 +54,99 @@
typedef struct Dbbe Dbbe;
typedef struct DbbeCursor DbbeCursor;
/*
** The 18 interface routines.
*/
/* Open a complete database */
/*
** Open a complete database.
**
** If the database name begins with "gdbm:" the GDBM driver is used.
** If the name begins with "memory:" the in-memory driver is used.
** The default driver is GDBM.
*/
Dbbe *sqliteDbbeOpen(const char *zName, int write, int create, char **pzErr);
/* Close the whole database. */
void sqliteDbbeClose(Dbbe*);
/* Open a cursor into particular file of a previously opened database.
** Create the file if it doesn't already exist and writeable!=0. zName
** is the base name of the file to be opened. This routine will add
** an appropriate path and extension to the filename to locate the
** actual file.
**
** If zName is 0 or "", then a temporary file is created that
** will be deleted when closed.
/*
** This is the structure returned by sqliteDbbeOpen(). It contains pointers
** to all access routines for the database backend.
*/
int sqliteDbbeOpenCursor(Dbbe*, const char *zName, int writeable, DbbeCursor**);
struct Dbbe {
/* Close the whole database. */
void (*Close)(Dbbe*);
/* Delete a table from the database */
void sqliteDbbeDropTable(Dbbe*, const char *zTableName);
/* Open a cursor into particular file of a previously opened database.
** Create the file if it doesn't already exist and writeable!=0. zName
** is the base name of the file to be opened. This routine will add
** an appropriate path and extension to the filename to locate the
** actual file.
**
** If zName is 0 or "", then a temporary file is created that
** will be deleted when closed.
*/
int (*OpenCursor)(Dbbe*, const char *zName, int writeable, DbbeCursor**);
/* Reorganize a table to speed access or reduce its disk usage */
int sqliteDbbeReorganizeTable(Dbbe*, const char *zTableName);
/* Delete a table from the database */
void (*DropTable)(Dbbe*, const char *zTableName);
/* Close a cursor */
void sqliteDbbeCloseCursor(DbbeCursor*);
/* Reorganize a table to speed access or reduce its disk usage */
int (*ReorganizeTable)(Dbbe*, const char *zTableName);
/* Fetch an entry from a table with the given key. Return 1 if
** successful and 0 if no such entry exists.
*/
int sqliteDbbeFetch(DbbeCursor*, int nKey, char *pKey);
/* Close a cursor */
void (*CloseCursor)(DbbeCursor*);
/* Return 1 if the given key is already in the table. Return 0
** if it is not.
*/
int sqliteDbbeTest(DbbeCursor*, int nKey, char *pKey);
/* Fetch an entry from a table with the given key. Return 1 if
** successful and 0 if no such entry exists.
*/
int (*Fetch)(DbbeCursor*, int nKey, char *pKey);
/* Retrieve the key or data used for the last fetch. Only size
** bytes are read beginning with the offset-th byte. The return
** value is the actual number of bytes read.
*/
int sqliteDbbeCopyKey(DbbeCursor*, int offset, int size, char *zBuf);
int sqliteDbbeCopyData(DbbeCursor*, int offset, int size, char *zBuf);
/* Return 1 if the given key is already in the table. Return 0
** if it is not.
*/
int (*Test)(DbbeCursor*, int nKey, char *pKey);
/* Retrieve the key or data. The result is ephemeral. In other words,
** the result is stored in a buffer that might be overwritten on the next
** call to any DBBE routine. If the results are needed for longer than
** that, you must make a copy.
*/
char *sqliteDbbeReadKey(DbbeCursor*, int offset);
char *sqliteDbbeReadData(DbbeCursor*, int offset);
/* Retrieve the key or data used for the last fetch. Only size
** bytes are read beginning with the offset-th byte. The return
** value is the actual number of bytes read.
*/
int (*CopyKey)(DbbeCursor*, int offset, int size, char *zBuf);
int (*CopyData)(DbbeCursor*, int offset, int size, char *zBuf);
/* Return the length of the most recently fetched key or data. */
int sqliteDbbeKeyLength(DbbeCursor*);
int sqliteDbbeDataLength(DbbeCursor*);
/* Retrieve the key or data. The result is ephemeral. In other words,
** the result is stored in a buffer that might be overwritten on the next
** call to any DBBE routine. If the results are needed for longer than
** that, you must make a copy.
*/
char *(*ReadKey)(DbbeCursor*, int offset);
char *(*ReadData)(DbbeCursor*, int offset);
/* Retrieve the next entry in the table. The first key is retrieved
** the first time this routine is called, or after a call to
** sqliteDbbeRewind(). The return value is 1 if there is another
** entry, or 0 if there are no more entries. */
int sqliteDbbeNextKey(DbbeCursor*);
/* Return the length of the most recently fetched key or data. */
int (*KeyLength)(DbbeCursor*);
int (*DataLength)(DbbeCursor*);
/* Make it so that the next call to sqliteDbbeNextKey() returns
** the first entry of the table. */
int sqliteDbbeRewind(DbbeCursor*);
/* Retrieve the next entry in the table. The first key is retrieved
** the first time this routine is called, or after a call to
** Dbbe.Rewind(). The return value is 1 if there is another
** entry, or 0 if there are no more entries. */
int (*NextKey)(DbbeCursor*);
/* Get a new integer key for this table. */
int sqliteDbbeNew(DbbeCursor*);
/* Make it so that the next call to Dbbe.NextKey() returns
** the first entry of the table. */
int (*Rewind)(DbbeCursor*);
/* Write an entry into a table. If another entry already exists with
** the same key, the old entry is discarded first.
*/
int sqliteDbbePut(DbbeCursor*, int nKey, char *pKey, int nData, char *pData);
/* Get a new integer key for this table. */
int (*New)(DbbeCursor*);
/* Remove an entry from the table */
int sqliteDbbeDelete(DbbeCursor*, int nKey, char *pKey);
/* Write an entry into a table. If another entry already exists with
** the same key, the old entry is discarded first.
*/
int (*Put)(DbbeCursor*, int nKey, char *pKey, int nData, char *pData);
/* Open a file suitable for temporary storage */
int sqliteDbbeOpenTempFile(Dbbe*, FILE**);
/* Remove an entry from the table */
int (*Delete)(DbbeCursor*, int nKey, char *pKey);
/* Close a temporary file */
void sqliteDbbeCloseTempFile(Dbbe *, FILE *);
/* Open a file suitable for temporary storage */
int (*OpenTempFile)(Dbbe*, FILE**);
/* Close a temporary file */
void (*CloseTempFile)(Dbbe *, FILE *);
};
#endif /* defined(_SQLITE_DBBE_H_) */
+776
View File
@@ -0,0 +1,776 @@
/*
** Copyright (c) 2000 D. Richard Hipp
**
** This program is free software; you can redistribute it and/or
** modify it under the terms of the GNU General Public
** License as published by the Free Software Foundation; either
** version 2 of the License, or (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
** General Public License for more details.
**
** You should have received a copy of the GNU General Public
** License along with this library; if not, write to the
** Free Software Foundation, Inc., 59 Temple Place - Suite 330,
** Boston, MA 02111-1307, USA.
**
** Author contact information:
** drh@hwaci.com
** http://www.hwaci.com/drh/
**
*************************************************************************
** This file contains code to implement the database backend (DBBE)
** for sqlite. The database backend is the interface between
** sqlite and the code that does the actually reading and writing
** of information to the disk.
**
** This file uses GDBM as the database backend. It should be
** relatively simple to convert to a different database such
** as NDBM, SDBM, or BerkeleyDB.
**
** $Id: dbbegdbm.c,v 1.1 2000/10/19 01:49:02 drh Exp $
*/
#include "sqliteInt.h"
#include <gdbm.h>
#include <sys/stat.h>
#include <unistd.h>
#include <ctype.h>
#include <time.h>
/*
** Information about each open disk file is an instance of this
** structure. There will only be one such structure for each
** disk file. If the VDBE opens the same file twice (as will happen
** for a self-join, for example) then two DbbeCursor structures are
** created but there is only a single BeFile structure with an
** nRef of 2.
*/
typedef struct BeFile BeFile;
struct BeFile {
char *zName; /* Name of the file */
GDBM_FILE dbf; /* The file itself */
int nRef; /* Number of references */
int delOnClose; /* Delete when closing */
int writeable; /* Opened for writing */
BeFile *pNext, *pPrev; /* Next and previous on list of open files */
};
/*
** The following structure holds the current state of the RC4 algorithm.
** We use RC4 as a random number generator. Each call to RC4 gives
** a random 8-bit number.
**
** Nothing in this file or anywhere else in SQLite does any kind of
** encryption. The RC4 algorithm is being used as a PRNG (pseudo-random
** number generator) not as an encryption device.
*/
struct rc4 {
int i, j;
int s[256];
};
/*
** The following structure contains all information used by GDBM
** database driver. This is a subclass of the Dbbe structure.
*/
typedef struct Dbbex Dbbex;
struct Dbbex {
Dbbe dbbe; /* The base class */
char *zDir; /* The directory containing the database */
int write; /* True for write permission */
BeFile *pOpen; /* List of open files */
int nTemp; /* Number of temporary files created */
FILE **apTemp; /* Space to hold temporary file pointers */
char **azTemp; /* Names of the temporary files */
struct rc4 rc4; /* The random number generator */
};
/*
** An cursor into a database file is an instance of the following structure.
** There can only be a single BeFile structure for each disk file, but
** there can be multiple DbbeCursor structures. Each DbbeCursor represents
** a cursor pointing to a particular part of the open BeFile. The
** BeFile.nRef field hold a count of the number of DbbeCursor structures
** associated with the same disk file.
*/
struct DbbeCursor {
Dbbex *pBe; /* The database of which this record is a part */
BeFile *pFile; /* The database file for this table */
datum key; /* Most recently used key */
datum data; /* Most recent data */
int needRewind; /* Next key should be the first */
int readPending; /* The fetch hasn't actually been done yet */
};
/*
** Initialize the RC4 PRNG. "seed" is a pointer to some random
** data used to initialize the PRNG.
*/
static void rc4init(struct rc4 *p, char *seed, int seedlen){
int i;
char k[256];
p->j = 0;
p->i = 0;
for(i=0; i<256; i++){
p->s[i] = i;
k[i] = seed[i%seedlen];
}
for(i=0; i<256; i++){
int t;
p->j = (p->j + p->s[i] + k[i]) & 0xff;
t = p->s[p->j];
p->s[p->j] = p->s[i];
p->s[i] = t;
}
}
/*
** Get a single 8-bit random value from the RC4 PRNG.
*/
static int rc4byte(struct rc4 *p){
int t;
p->i = (p->i + 1) & 0xff;
p->j = (p->j + p->s[p->i]) & 0xff;
t = p->s[p->i];
p->s[p->i] = p->s[p->j];
p->s[p->j] = t;
t = p->s[p->i] + p->s[p->j];
return t & 0xff;
}
/*
** The "mkdir()" function only takes one argument under Windows.
*/
#if OS_WIN
# define mkdir(A,B) mkdir(A)
#endif
/*
** Forward declaration
*/
static void sqliteGdbmCloseCursor(DbbeCursor *pCursr);
/*
** Completely shutdown the given database. Close all files. Free all memory.
*/
static void sqliteGdbmClose(Dbbe *pDbbe){
Dbbex *pBe = (Dbbex*)pDbbe;
BeFile *pFile, *pNext;
int i;
for(pFile=pBe->pOpen; pFile; pFile=pNext){
pNext = pFile->pNext;
gdbm_close(pFile->dbf);
memset(pFile, 0, sizeof(*pFile));
sqliteFree(pFile);
}
for(i=0; i<pBe->nTemp; i++){
if( pBe->apTemp[i]!=0 ){
unlink(pBe->azTemp[i]);
fclose(pBe->apTemp[i]);
sqliteFree(pBe->azTemp[i]);
pBe->apTemp[i] = 0;
pBe->azTemp[i] = 0;
break;
}
}
sqliteFree(pBe->azTemp);
sqliteFree(pBe->apTemp);
memset(pBe, 0, sizeof(*pBe));
sqliteFree(pBe);
}
/*
** Translate the name of an SQL table (or index) into the name
** of a file that holds the key/data pairs for that table or
** index. Space to hold the filename is obtained from
** sqliteMalloc() and must be freed by the calling function.
*/
static char *sqliteFileOfTable(Dbbex *pBe, const char *zTable){
char *zFile = 0;
int i;
sqliteSetString(&zFile, pBe->zDir, "/", zTable, ".tbl", 0);
if( zFile==0 ) return 0;
for(i=strlen(pBe->zDir)+1; zFile[i]; i++){
int c = zFile[i];
if( isupper(c) ){
zFile[i] = tolower(c);
}else if( !isalnum(c) && c!='-' && c!='_' && c!='.' ){
zFile[i] = '+';
}
}
return zFile;
}
/*
** Generate a random filename with the given prefix. The new filename
** is written into zBuf[]. The calling function must insure that
** zBuf[] is big enough to hold the prefix plus 20 or so extra
** characters.
**
** Very random names are chosen so that the chance of a
** collision with an existing filename is very very small.
*/
static void randomName(struct rc4 *pRc4, char *zBuf, char *zPrefix){
int i, j;
static const char zRandomChars[] = "abcdefghijklmnopqrstuvwxyz0123456789";
strcpy(zBuf, zPrefix);
j = strlen(zBuf);
for(i=0; i<15; i++){
int c = rc4byte(pRc4) % (sizeof(zRandomChars) - 1);
zBuf[j++] = zRandomChars[c];
}
zBuf[j] = 0;
}
/*
** Open a new table cursor. Write a pointer to the corresponding
** DbbeCursor structure into *ppCursr. Return an integer success
** code:
**
** SQLITE_OK It worked!
**
** SQLITE_NOMEM sqliteMalloc() failed
**
** SQLITE_PERM Attempt to access a file for which file
** access permission is denied
**
** SQLITE_BUSY Another thread or process is already using
** the corresponding file and has that file locked.
**
** SQLITE_READONLY The current thread already has this file open
** readonly but you are trying to open for writing.
** (This can happen if a SELECT callback tries to
** do an UPDATE or DELETE.)
**
** If zTable is 0 or "", then a temporary database file is created and
** a cursor to that temporary file is opened. The temporary file
** will be deleted from the disk when it is closed.
*/
static int sqliteGdbmOpenCursor(
Dbbe *pDbbe, /* The database the table belongs to */
const char *zTable, /* The SQL name of the file to be opened */
int writeable, /* True to open for writing */
DbbeCursor **ppCursr /* Write the resulting table pointer here */
){
char *zFile; /* Name of the table file */
DbbeCursor *pCursr; /* The new table cursor */
BeFile *pFile; /* The underlying data file for this table */
int rc = SQLITE_OK; /* Return value */
int rw_mask; /* Permissions mask for opening a table */
int mode; /* Mode for opening a table */
Dbbex *pBe = (Dbbex*)pDbbe;
*ppCursr = 0;
pCursr = sqliteMalloc( sizeof(*pCursr) );
if( pCursr==0 ) return SQLITE_NOMEM;
if( zTable ){
zFile = sqliteFileOfTable(pBe, zTable);
for(pFile=pBe->pOpen; pFile; pFile=pFile->pNext){
if( strcmp(pFile->zName,zFile)==0 ) break;
}
}else{
pFile = 0;
zFile = 0;
}
if( pFile==0 ){
if( writeable ){
rw_mask = GDBM_WRCREAT | GDBM_FAST;
mode = 0640;
}else{
rw_mask = GDBM_READER;
mode = 0640;
}
pFile = sqliteMalloc( sizeof(*pFile) );
if( pFile==0 ){
sqliteFree(zFile);
return SQLITE_NOMEM;
}
if( zFile ){
if( !writeable || pBe->write ){
pFile->dbf = gdbm_open(zFile, 0, rw_mask, mode, 0);
}else{
pFile->dbf = 0;
}
}else{
int limit;
struct rc4 *pRc4;
char zRandom[50];
pRc4 = &pBe->rc4;
zFile = 0;
limit = 5;
do {
randomName(&pBe->rc4, zRandom, "_temp_table_");
sqliteFree(zFile);
zFile = sqliteFileOfTable(pBe, zRandom);
pFile->dbf = gdbm_open(zFile, 0, rw_mask, mode, 0);
}while( pFile->dbf==0 && limit-- >= 0);
pFile->delOnClose = 1;
}
pFile->writeable = writeable;
pFile->zName = zFile;
pFile->nRef = 1;
pFile->pPrev = 0;
if( pBe->pOpen ){
pBe->pOpen->pPrev = pFile;
}
pFile->pNext = pBe->pOpen;
pBe->pOpen = pFile;
if( pFile->dbf==0 ){
if( !writeable && access(zFile,0) ){
/* Trying to read a non-existant file. This is OK. All the
** reads will return empty, which is what we want. */
rc = SQLITE_OK;
}else if( pBe->write==0 ){
rc = SQLITE_READONLY;
}else if( access(zFile,W_OK|R_OK) ){
rc = SQLITE_PERM;
}else{
rc = SQLITE_BUSY;
}
}
}else{
sqliteFree(zFile);
pFile->nRef++;
if( writeable && !pFile->writeable ){
rc = SQLITE_READONLY;
}
}
pCursr->pBe = pBe;
pCursr->pFile = pFile;
pCursr->readPending = 0;
pCursr->needRewind = 1;
if( rc!=SQLITE_OK ){
sqliteGdbmCloseCursor(pCursr);
*ppCursr = 0;
}else{
*ppCursr = pCursr;
}
return rc;
}
/*
** Drop a table from the database. The file on the disk that corresponds
** to this table is deleted.
*/
static void sqliteGdbmDropTable(Dbbe *pBe, const char *zTable){
char *zFile; /* Name of the table file */
zFile = sqliteFileOfTable((Dbbex*)pBe, zTable);
unlink(zFile);
sqliteFree(zFile);
}
/*
** Close a cursor previously opened by sqliteGdbmOpenCursor().
**
** There can be multiple cursors pointing to the same open file.
** The underlying file is not closed until all cursors have been
** closed. This routine decrements the BeFile.nref field of the
** underlying file and closes the file when nref reaches 0.
*/
static void sqliteGdbmCloseCursor(DbbeCursor *pCursr){
BeFile *pFile;
Dbbex *pBe;
if( pCursr==0 ) return;
pFile = pCursr->pFile;
pBe = pCursr->pBe;
pFile->nRef--;
if( pFile->dbf!=NULL ){
gdbm_sync(pFile->dbf);
}
if( pFile->nRef<=0 ){
if( pFile->dbf!=NULL ){
gdbm_close(pFile->dbf);
}
if( pFile->pPrev ){
pFile->pPrev->pNext = pFile->pNext;
}else{
pBe->pOpen = pFile->pNext;
}
if( pFile->pNext ){
pFile->pNext->pPrev = pFile->pPrev;
}
if( pFile->delOnClose ){
unlink(pFile->zName);
}
sqliteFree(pFile->zName);
memset(pFile, 0, sizeof(*pFile));
sqliteFree(pFile);
}
if( pCursr->key.dptr ) free(pCursr->key.dptr);
if( pCursr->data.dptr ) free(pCursr->data.dptr);
memset(pCursr, 0, sizeof(*pCursr));
sqliteFree(pCursr);
}
/*
** Reorganize a table to reduce search times and disk usage.
*/
static int sqliteGdbmReorganizeTable(Dbbe *pBe, const char *zTable){
DbbeCursor *pCrsr;
int rc;
rc = sqliteGdbmOpenCursor(pBe, zTable, 1, &pCrsr);
if( rc!=SQLITE_OK ){
return rc;
}
if( pCrsr && pCrsr->pFile && pCrsr->pFile->dbf ){
gdbm_reorganize(pCrsr->pFile->dbf);
}
if( pCrsr ){
sqliteGdbmCloseCursor(pCrsr);
}
return SQLITE_OK;
}
/*
** Clear the given datum
*/
static void datumClear(datum *p){
if( p->dptr ) free(p->dptr);
p->dptr = 0;
p->dsize = 0;
}
/*
** Fetch a single record from an open cursor. Return 1 on success
** and 0 on failure.
*/
static int sqliteGdbmFetch(DbbeCursor *pCursr, int nKey, char *pKey){
datum key;
key.dsize = nKey;
key.dptr = pKey;
datumClear(&pCursr->key);
datumClear(&pCursr->data);
if( pCursr->pFile && pCursr->pFile->dbf ){
pCursr->data = gdbm_fetch(pCursr->pFile->dbf, key);
}
return pCursr->data.dptr!=0;
}
/*
** Return 1 if the given key is already in the table. Return 0
** if it is not.
*/
static int sqliteGdbmTest(DbbeCursor *pCursr, int nKey, char *pKey){
datum key;
int result = 0;
key.dsize = nKey;
key.dptr = pKey;
if( pCursr->pFile && pCursr->pFile->dbf ){
result = gdbm_exists(pCursr->pFile->dbf, key);
}
return result;
}
/*
** Copy bytes from the current key or data into a buffer supplied by
** the calling function. Return the number of bytes copied.
*/
static
int sqliteGdbmCopyKey(DbbeCursor *pCursr, int offset, int size, char *zBuf){
int n;
if( offset>=pCursr->key.dsize ) return 0;
if( offset+size>pCursr->key.dsize ){
n = pCursr->key.dsize - offset;
}else{
n = size;
}
memcpy(zBuf, &pCursr->key.dptr[offset], n);
return n;
}
static
int sqliteGdbmCopyData(DbbeCursor *pCursr, int offset, int size, char *zBuf){
int n;
if( pCursr->readPending && pCursr->pFile && pCursr->pFile->dbf ){
pCursr->data = gdbm_fetch(pCursr->pFile->dbf, pCursr->key);
pCursr->readPending = 0;
}
if( offset>=pCursr->data.dsize ) return 0;
if( offset+size>pCursr->data.dsize ){
n = pCursr->data.dsize - offset;
}else{
n = size;
}
memcpy(zBuf, &pCursr->data.dptr[offset], n);
return n;
}
/*
** Return a pointer to bytes from the key or data. The data returned
** is ephemeral.
*/
static char *sqliteGdbmReadKey(DbbeCursor *pCursr, int offset){
if( offset<0 || offset>=pCursr->key.dsize ) return "";
return &pCursr->key.dptr[offset];
}
static char *sqliteGdbmReadData(DbbeCursor *pCursr, int offset){
if( pCursr->readPending && pCursr->pFile && pCursr->pFile->dbf ){
pCursr->data = gdbm_fetch(pCursr->pFile->dbf, pCursr->key);
pCursr->readPending = 0;
}
if( offset<0 || offset>=pCursr->data.dsize ) return "";
return &pCursr->data.dptr[offset];
}
/*
** Return the total number of bytes in either data or key.
*/
static int sqliteGdbmKeyLength(DbbeCursor *pCursr){
return pCursr->key.dsize;
}
static int sqliteGdbmDataLength(DbbeCursor *pCursr){
if( pCursr->readPending && pCursr->pFile && pCursr->pFile->dbf ){
pCursr->data = gdbm_fetch(pCursr->pFile->dbf, pCursr->key);
pCursr->readPending = 0;
}
return pCursr->data.dsize;
}
/*
** Make is so that the next call to sqliteNextKey() finds the first
** key of the table.
*/
static int sqliteGdbmRewind(DbbeCursor *pCursr){
pCursr->needRewind = 1;
return SQLITE_OK;
}
/*
** Read the next key from the table. Return 1 on success. Return
** 0 if there are no more keys.
*/
static int sqliteGdbmNextKey(DbbeCursor *pCursr){
datum nextkey;
int rc;
if( pCursr==0 || pCursr->pFile==0 || pCursr->pFile->dbf==0 ){
pCursr->readPending = 0;
return 0;
}
if( pCursr->needRewind ){
nextkey = gdbm_firstkey(pCursr->pFile->dbf);
pCursr->needRewind = 0;
}else{
nextkey = gdbm_nextkey(pCursr->pFile->dbf, pCursr->key);
}
datumClear(&pCursr->key);
datumClear(&pCursr->data);
pCursr->key = nextkey;
if( pCursr->key.dptr ){
pCursr->readPending = 1;
rc = 1;
}else{
pCursr->needRewind = 1;
pCursr->readPending = 0;
rc = 0;
}
return rc;
}
/*
** Get a new integer key.
*/
static int sqliteGdbmNew(DbbeCursor *pCursr){
int iKey;
datum key;
int go = 1;
int i;
struct rc4 *pRc4;
if( pCursr->pFile==0 || pCursr->pFile->dbf==0 ) return 1;
pRc4 = &pCursr->pBe->rc4;
while( go ){
iKey = 0;
for(i=0; i<4; i++){
iKey = (iKey<<8) + rc4byte(pRc4);
}
if( iKey==0 ) continue;
key.dptr = (char*)&iKey;
key.dsize = 4;
go = gdbm_exists(pCursr->pFile->dbf, key);
}
return iKey;
}
/*
** Write an entry into the table. Overwrite any prior entry with the
** same key.
*/
static int
sqliteGdbmPut(DbbeCursor *pCursr, int nKey,char *pKey,int nData,char *pData){
datum data, key;
int rc;
if( pCursr->pFile==0 || pCursr->pFile->dbf==0 ) return SQLITE_ERROR;
data.dsize = nData;
data.dptr = pData;
key.dsize = nKey;
key.dptr = pKey;
rc = gdbm_store(pCursr->pFile->dbf, key, data, GDBM_REPLACE);
if( rc ) rc = SQLITE_ERROR;
datumClear(&pCursr->key);
datumClear(&pCursr->data);
return rc;
}
/*
** Remove an entry from a table, if the entry exists.
*/
static int sqliteGdbmDelete(DbbeCursor *pCursr, int nKey, char *pKey){
datum key;
int rc;
datumClear(&pCursr->key);
datumClear(&pCursr->data);
if( pCursr->pFile==0 || pCursr->pFile->dbf==0 ) return SQLITE_ERROR;
key.dsize = nKey;
key.dptr = pKey;
rc = gdbm_delete(pCursr->pFile->dbf, key);
if( rc ) rc = SQLITE_ERROR;
return rc;
}
/*
** Open a temporary file. The file should be deleted when closed.
**
** Note that we can't use the old Unix trick of opening the file
** and then immediately unlinking the file. That works great
** under Unix, but fails when we try to port to Windows.
*/
static int sqliteGdbmOpenTempFile(Dbbe *pDbbe, FILE **ppFile){
char *zFile; /* Full name of the temporary file */
char zBuf[50]; /* Base name of the temporary file */
int i; /* Loop counter */
int limit; /* Prevent an infinite loop */
int rc = SQLITE_OK; /* Value returned by this function */
Dbbex *pBe = (Dbbex*)pDbbe;
for(i=0; i<pBe->nTemp; i++){
if( pBe->apTemp[i]==0 ) break;
}
if( i>=pBe->nTemp ){
pBe->nTemp++;
pBe->apTemp = sqliteRealloc(pBe->apTemp, pBe->nTemp*sizeof(FILE*) );
pBe->azTemp = sqliteRealloc(pBe->azTemp, pBe->nTemp*sizeof(char*) );
}
if( pBe->apTemp==0 ){
*ppFile = 0;
return SQLITE_NOMEM;
}
limit = 4;
zFile = 0;
do{
randomName(&pBe->rc4, zBuf, "/_temp_file_");
sqliteFree(zFile);
zFile = 0;
sqliteSetString(&zFile, pBe->zDir, zBuf, 0);
}while( access(zFile,0)==0 && limit-- >= 0 );
*ppFile = pBe->apTemp[i] = fopen(zFile, "w+");
if( pBe->apTemp[i]==0 ){
rc = SQLITE_ERROR;
sqliteFree(zFile);
pBe->azTemp[i] = 0;
}else{
pBe->azTemp[i] = zFile;
}
return rc;
}
/*
** Close a temporary file opened using sqliteGdbmOpenTempFile()
*/
static void sqliteGdbmCloseTempFile(Dbbe *pDbbe, FILE *f){
int i;
Dbbex *pBe = (Dbbex*)pDbbe;
for(i=0; i<pBe->nTemp; i++){
if( pBe->apTemp[i]==f ){
unlink(pBe->azTemp[i]);
sqliteFree(pBe->azTemp[i]);
pBe->apTemp[i] = 0;
pBe->azTemp[i] = 0;
break;
}
}
fclose(f);
}
/*
** This routine opens a new database. For the GDBM driver
** implemented here, the database name is the name of the directory
** containing all the files of the database.
**
** If successful, a pointer to the Dbbe structure is returned.
** If there are errors, an appropriate error message is left
** in *pzErrMsg and NULL is returned.
*/
Dbbe *sqliteGdbmOpen(
const char *zName, /* The name of the database */
int writeFlag, /* True if we will be writing to the database */
int createFlag, /* True to create database if it doesn't exist */
char **pzErrMsg /* Write error messages (if any) here */
){
Dbbex *pNew;
struct stat statbuf;
char *zMaster;
if( !writeFlag ) createFlag = 0;
if( stat(zName, &statbuf)!=0 ){
if( createFlag ) mkdir(zName, 0750);
if( stat(zName, &statbuf)!=0 ){
sqliteSetString(pzErrMsg, createFlag ?
"can't find or create directory \"" : "can't find directory \"",
zName, "\"", 0);
return 0;
}
}
if( !S_ISDIR(statbuf.st_mode) ){
sqliteSetString(pzErrMsg, "not a directory: \"", zName, "\"", 0);
return 0;
}
if( access(zName, writeFlag ? (X_OK|W_OK|R_OK) : (X_OK|R_OK)) ){
sqliteSetString(pzErrMsg, "access permission denied", 0);
return 0;
}
zMaster = 0;
sqliteSetString(&zMaster, zName, "/" MASTER_NAME ".tbl", 0);
if( stat(zMaster, &statbuf)==0
&& access(zMaster, writeFlag ? (W_OK|R_OK) : R_OK)!=0 ){
sqliteSetString(pzErrMsg, "access permission denied for ", zMaster, 0);
sqliteFree(zMaster);
return 0;
}
sqliteFree(zMaster);
pNew = sqliteMalloc(sizeof(Dbbex) + strlen(zName) + 1);
if( pNew==0 ){
sqliteSetString(pzErrMsg, "out of memory", 0);
return 0;
}
pNew->dbbe.Close = sqliteGdbmClose;
pNew->dbbe.OpenCursor = sqliteGdbmOpenCursor;
pNew->dbbe.DropTable = sqliteGdbmDropTable;
pNew->dbbe.ReorganizeTable = sqliteGdbmReorganizeTable;
pNew->dbbe.CloseCursor = sqliteGdbmCloseCursor;
pNew->dbbe.Fetch = sqliteGdbmFetch;
pNew->dbbe.Test = sqliteGdbmTest;
pNew->dbbe.CopyKey = sqliteGdbmCopyKey;
pNew->dbbe.CopyData = sqliteGdbmCopyData;
pNew->dbbe.ReadKey = sqliteGdbmReadKey;
pNew->dbbe.ReadData = sqliteGdbmReadData;
pNew->dbbe.KeyLength = sqliteGdbmKeyLength;
pNew->dbbe.DataLength = sqliteGdbmDataLength;
pNew->dbbe.NextKey = sqliteGdbmNextKey;
pNew->dbbe.Rewind = sqliteGdbmRewind;
pNew->dbbe.New = sqliteGdbmNew;
pNew->dbbe.Put = sqliteGdbmPut;
pNew->dbbe.Delete = sqliteGdbmDelete;
pNew->dbbe.OpenTempFile = sqliteGdbmOpenTempFile;
pNew->dbbe.CloseTempFile = sqliteGdbmCloseTempFile;
pNew->zDir = (char*)&pNew[1];
strcpy(pNew->zDir, zName);
pNew->write = writeFlag;
pNew->pOpen = 0;
time(&statbuf.st_ctime);
rc4init(&pNew->rc4, (char*)&statbuf, sizeof(statbuf));
return &pNew->dbbe;
}
+918
View File
@@ -0,0 +1,918 @@
/*
** Copyright (c) 2000 D. Richard Hipp
**
** This program is free software; you can redistribute it and/or
** modify it under the terms of the GNU General Public
** License as published by the Free Software Foundation; either
** version 2 of the License, or (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
** General Public License for more details.
**
** You should have received a copy of the GNU General Public
** License along with this library; if not, write to the
** Free Software Foundation, Inc., 59 Temple Place - Suite 330,
** Boston, MA 02111-1307, USA.
**
** Author contact information:
** drh@hwaci.com
** http://www.hwaci.com/drh/
**
*************************************************************************
** This file contains code to implement the database backend (DBBE)
** for sqlite. The database backend is the interface between
** sqlite and the code that does the actually reading and writing
** of information to the disk.
**
** This file uses an in-memory hash table as the database backend.
**
** $Id: dbbemem.c,v 1.5 2000/12/10 18:23:50 drh Exp $
*/
#include "sqliteInt.h"
#include <sys/stat.h>
#include <unistd.h>
#include <ctype.h>
#include <time.h>
typedef struct Array Array;
typedef struct ArrayElem ArrayElem;
typedef struct Datum Datum;
/* A complete associative array is an instance of the following structure.
** The internals of this structure are intended to be opaque -- client
** code should not attempt to access or modify the fields of this structure
** directly. Change this structure only by using the routines below.
** However, many of the "procedures" and "functions" for modifying and
** accessing this structure are really macros, so we can't really make
** this structure opaque.
*/
struct Array {
int count; /* Number of entries in the array */
ArrayElem *first; /* The first element of the array */
int htsize; /* Number of buckets in the hash table */
struct _Array_ht { /* the hash table */
int count; /* Number of entries with this hash */
ArrayElem *chain; /* Pointer to first entry with this hash */
} *ht;
};
/*
** An instance of the following structure stores a single key or
** data element.
*/
struct Datum {
int n;
void *p;
};
/* Each element in the associative array is an instance of the following
** structure. All elements are stored on a single doubly-linked list.
**
** Again, this structure is intended to be opaque, but it can't really
** be opaque because it is used by macros.
*/
struct ArrayElem {
ArrayElem *next, *prev; /* Next and previous elements in the array */
Datum key, data; /* Key and data for this element */
};
/* Some routines are so simple that they can be implemented as macros
** These are given first. */
/* Return the number of entries in the array */
#define ArrayCount(X) ((X)->count)
/* Return a pointer to the first element of the array */
#define ArrayFirst(X) ((X)->first)
/* Return a pointer to the next (or previous) element of the array */
#define ArrayNext(X) ((X)->next)
#define ArrayPrev(X) ((X)->prev)
/* Return TRUE if the element given is the last element in the array */
#define ArrayIsLast(X) ((X)->next==0)
#define ArrayIsFirst(X) ((X)->prev==0)
/* Return the data or key for an element of the array */
#define ArrayData(X) ((X)->data.p)
#define ArrayDataSize(X) ((X)->data.n)
#define ArrayKey(X) ((X)->key.p)
#define ArrayKeySize(X) ((X)->key.n)
/* Turn bulk memory into an associative array object by initializing the
** fields of the Array structure.
*/
static void ArrayInit(Array *new){
new->first = 0;
new->count = 0;
new->htsize = 0;
new->ht = 0;
}
/* Remove all entries from an associative array. Reclaim all memory.
** This is the opposite of ArrayInit().
*/
static void ArrayClear(Array *array){
ArrayElem *elem; /* For looping over all elements of the array */
elem = array->first;
array->first = 0;
array->count = 0;
if( array->ht ) sqliteFree(array->ht);
array->ht = 0;
array->htsize = 0;
while( elem ){
ArrayElem *next_elem = elem->next;
sqliteFree(elem);
elem = next_elem;
}
}
/*
** Generate a hash from an N-byte key
*/
static int ArrayHash(Datum d){
int h = 0;
while( d.n-- > 0 ){
/* The funky case "*(char**)&d.p" is to work around a bug the
** c89 compiler of HPUX. */
h = (h<<9) ^ (h<<3) ^ h ^ *((*(char**)&d.p)++);
}
if( h<0 ) h = -h;
return h;
}
/* Resize the hash table for a Array array
*/
static void ArrayRehash(Array *array, int new_size){
struct _Array_ht *new_ht; /* The new hash table */
ArrayElem *elem, *next_elem; /* For looping over existing elements */
int i; /* Loop counter */
ArrayElem *x; /* Element being copied to new hash table */
new_ht = sqliteMalloc( new_size*sizeof(struct _Array_ht) );
if( array->ht ) sqliteFree(array->ht);
array->ht = new_ht;
array->htsize = new_size;
for(i=new_size-1; i>=0; i--){
new_ht[i].count = 0;
new_ht[i].chain = 0;
}
for(elem=array->first, array->first=0; elem; elem = next_elem){
int h = ArrayHash(elem->key) & (new_size-1);
next_elem = elem->next;
x = new_ht[h].chain;
if( x ){
elem->next = x;
elem->prev = x->prev;
if( x->prev ) x->prev->next = elem;
else array->first = elem;
x->prev = elem;
}else{
elem->next = array->first;
if( array->first ) array->first->prev = elem;
elem->prev = 0;
array->first = elem;
}
new_ht[h].chain = elem;
new_ht[h].count++;
}
}
/* This function (for internal use only) locates an element in an
** array that matches the given key. The hash for this key has
** already been computed and is passed as the 3rd parameter.
*/
static ArrayElem *ArrayFindElementGivenHash(
const Array *array, /* The array to be searched */
const Datum key, /* The key we are searching for */
int h /* The hash for this key. */
){
ArrayElem *elem; /* Used to loop thru the element list */
int count; /* Number of elements left to test */
if( array->count ){
elem = array->ht[h].chain;
count = array->ht[h].count;
while( count-- && elem ){
if( elem->key.n==key.n && memcmp(elem->key.p,key.p,key.n)==0 ){
return elem;
}
elem = elem->next;
}
}
return 0;
}
/* Attempt to locate an element of the associative array with a key
** that matches "key". Return the ArrayElement if found and NULL if
** if no match.
*/
static ArrayElem *ArrayFindElement(const Array *array, Datum key){
int h; /* A hash on key */
if( array->count==0 ) return 0;
h = ArrayHash(key);
return ArrayFindElementGivenHash(array, key, h & (array->htsize-1));
}
/* Remove a single entry from the array given a pointer to that
** element and a hash on the element's key.
*/
static void ArrayRemoveElementGivenHash(
Array *array, /* The array containing "elem" */
ArrayElem* elem, /* The element to be removed from the array */
int h /* Hash value for the element */
){
if( elem->prev ){
elem->prev->next = elem->next;
}else{
array->first = elem->next;
}
if( elem->next ){
elem->next->prev = elem->prev;
}
if( array->ht[h].chain==elem ){
array->ht[h].chain = elem->next;
}
array->ht[h].count--;
if( array->ht[h].count<=0 ){
array->ht[h].chain = 0;
}
sqliteFree( elem );
array->count--;
}
/* Attempt to locate an element of the associative array with a key
** that matches "key". Return the data for this element if it is
** found, or NULL if no match is found.
*/
static Datum ArrayFind(const Array *array, Datum key){
int h; /* A hash on key */
ArrayElem *elem; /* The element that matches key */
static Datum nil = {0, 0};
if( array->count==0 ) return nil;
h = ArrayHash(key);
elem = ArrayFindElementGivenHash(array, key, h & (array->htsize-1));
return elem ? elem->data : nil;
}
/* Insert an element into the array. The key will be "key" and
** the data will be "data".
**
** If no array element exists with a matching key, then a new
** array element is created. The key is copied into the new element.
** But only a pointer to the data is stored. NULL is returned.
**
** If another element already exists with the same key, then the
** new data replaces the old data and the old data is returned.
** The key is not copied in this instance.
**
** If the "data" parameter to this function is NULL, then the
** element corresponding to "key" is removed from the array.
*/
static Datum ArrayInsert(Array *array, Datum key, Datum data){
int hraw; /* Raw hash value of the key */
int h; /* the hash of the key modulo hash table size */
ArrayElem *elem; /* Used to loop thru the element list */
ArrayElem *new_elem; /* New element added to the array */
Datum rv; /* Return value */
static Datum nil = {0, 0};
hraw = ArrayHash(key);
h = hraw & (array->htsize-1);
elem = ArrayFindElementGivenHash(array,key,h);
if( elem ){
Datum old_data = elem->data;
if( data.p==0 ){
ArrayRemoveElementGivenHash(array,elem,h);
}else{
elem->data = data;
}
return old_data;
}
if( data.p==0 ) return nil;
new_elem = (ArrayElem*)sqliteMalloc( sizeof(ArrayElem) + key.n );
if( new_elem==0 ) return nil;
new_elem->key.n = key.n;
new_elem->key.p = (void*)&new_elem[1];
memcpy(new_elem->key.p, key.p, key.n);
array->count++;
if( array->htsize==0 ) ArrayRehash(array,4);
if( array->count > array->htsize ){
ArrayRehash(array,array->htsize*2);
}
h = hraw & (array->htsize-1);
elem = array->ht[h].chain;
if( elem ){
new_elem->next = elem;
new_elem->prev = elem->prev;
if( elem->prev ){ elem->prev->next = new_elem; }
else { array->first = new_elem; }
elem->prev = new_elem;
}else{
new_elem->next = array->first;
new_elem->prev = 0;
if( array->first ){ array->first->prev = new_elem; }
array->first = new_elem;
}
array->ht[h].count++;
array->ht[h].chain = new_elem;
new_elem->data = data;
rv.p = 0;
rv.n = 0;
return rv;
}
/*
** Information about each open database table is an instance of this
** structure. There will only be one such structure for each
** table. If the VDBE opens the same table twice (as will happen
** for a self-join, for example) then two DbbeCursor structures are
** created but there is only a single MTable structure.
*/
typedef struct MTable MTable;
struct MTable {
char *zName; /* Name of the table */
int delOnClose; /* Delete when closing */
Array data; /* The data in this stable */
};
/*
** The following structure holds the current state of the RC4 algorithm.
** We use RC4 as a random number generator. Each call to RC4 gives
** a random 8-bit number.
**
** Nothing in this file or anywhere else in SQLite does any kind of
** encryption. The RC4 algorithm is being used as a PRNG (pseudo-random
** number generator) not as an encryption device.
*/
struct rc4 {
int i, j;
int s[256];
};
/*
** The following structure contains all information used by GDBM
** database driver. This is a subclass of the Dbbe structure.
*/
typedef struct Dbbex Dbbex;
struct Dbbex {
Dbbe dbbe; /* The base class */
Array tables; /* All tables of the database */
int nTemp; /* Number of temporary files created */
FILE **apTemp; /* Space to hold temporary file pointers */
char **azTemp; /* Names of the temporary files */
struct rc4 rc4; /* The random number generator */
};
/*
** An cursor into a database file is an instance of the following structure.
** There can only be a single MTable structure for each disk file, but
** there can be multiple DbbeCursor structures. Each DbbeCursor represents
** a cursor pointing to a particular part of the open MTable. The
** MTable.nRef field hold a count of the number of DbbeCursor structures
** associated with the same disk file.
*/
struct DbbeCursor {
Dbbex *pBe; /* The database of which this record is a part */
MTable *pTble; /* The database file for this table */
ArrayElem *elem; /* Most recently accessed record */
int needRewind; /* Next key should be the first */
};
/*
** Initialize the RC4 PRNG. "seed" is a pointer to some random
** data used to initialize the PRNG.
*/
static void rc4init(struct rc4 *p, char *seed, int seedlen){
int i;
char k[256];
p->j = 0;
p->i = 0;
for(i=0; i<256; i++){
p->s[i] = i;
k[i] = seed[i%seedlen];
}
for(i=0; i<256; i++){
int t;
p->j = (p->j + p->s[i] + k[i]) & 0xff;
t = p->s[p->j];
p->s[p->j] = p->s[i];
p->s[i] = t;
}
}
/*
** Get a single 8-bit random value from the RC4 PRNG.
*/
static int rc4byte(struct rc4 *p){
int t;
p->i = (p->i + 1) & 0xff;
p->j = (p->j + p->s[p->i]) & 0xff;
t = p->s[p->i];
p->s[p->i] = p->s[p->j];
p->s[p->j] = t;
t = p->s[p->i] + p->s[p->j];
return t & 0xff;
}
/*
** Forward declaration
*/
static void sqliteMemCloseCursor(DbbeCursor *pCursr);
/*
** Erase all the memory of an MTable
*/
static void deleteMTable(MTable *p){
ArrayElem *i;
for(i=ArrayFirst(&p->data); i; i=ArrayNext(i)){
void *data = ArrayData(i);
sqliteFree(data);
}
ArrayClear(&p->data);
sqliteFree(p->zName);
sqliteFree(p);
}
/*
** Completely shutdown the given database. Close all files. Free all memory.
*/
static void sqliteMemClose(Dbbe *pDbbe){
Dbbex *pBe = (Dbbex*)pDbbe;
MTable *pTble, *pNext;
int i;
ArrayElem *j, *k;
for(j=ArrayFirst(&pBe->tables); j; j=ArrayNext(j)){
pTble = ArrayData(j);
deleteMTable(pTble);
}
ArrayClear(&pBe->tables);
for(i=0; i<pBe->nTemp; i++){
if( pBe->apTemp[i]!=0 ){
unlink(pBe->azTemp[i]);
fclose(pBe->apTemp[i]);
sqliteFree(pBe->azTemp[i]);
pBe->apTemp[i] = 0;
pBe->azTemp[i] = 0;
break;
}
}
sqliteFree(pBe->azTemp);
sqliteFree(pBe->apTemp);
memset(pBe, 0, sizeof(*pBe));
sqliteFree(pBe);
}
/*
** Generate a random filename with the given prefix. The new filename
** is written into zBuf[]. The calling function must insure that
** zBuf[] is big enough to hold the prefix plus 20 or so extra
** characters.
**
** Very random names are chosen so that the chance of a
** collision with an existing filename is very very small.
*/
static void randomName(struct rc4 *pRc4, char *zBuf, char *zPrefix){
int i, j;
static const char zRandomChars[] = "abcdefghijklmnopqrstuvwxyz0123456789";
strcpy(zBuf, zPrefix);
j = strlen(zBuf);
for(i=0; i<15; i++){
int c = rc4byte(pRc4) % (sizeof(zRandomChars) - 1);
zBuf[j++] = zRandomChars[c];
}
zBuf[j] = 0;
}
/*
** Translate the name of an SQL table (or index) into its
** canonical name.
**
** Space to hold the canonical name is obtained from
** sqliteMalloc() and must be freed by the calling function.
*/
static char *sqliteNameOfTable(const char *zTable){
char *zNew = 0;
int i, c;
sqliteSetString(&zNew, zTable, 0);
if( zNew==0 ) return 0;
for(i=0; (c = zNew[i])!=0; i++){
if( isupper(c) ){
zNew[i] = tolower(c);
}
}
return zNew;
}
/*
** Open a new table cursor. Write a pointer to the corresponding
** DbbeCursor structure into *ppCursr. Return an integer success
** code:
**
** SQLITE_OK It worked!
**
** SQLITE_NOMEM sqliteMalloc() failed
**
** SQLITE_PERM Attempt to access a file for which file
** access permission is denied
**
** SQLITE_BUSY Another thread or process is already using
** the corresponding file and has that file locked.
**
** SQLITE_READONLY The current thread already has this file open
** readonly but you are trying to open for writing.
** (This can happen if a SELECT callback tries to
** do an UPDATE or DELETE.)
**
** If zTable is 0 or "", then a temporary database file is created and
** a cursor to that temporary file is opened. The temporary file
** will be deleted from the disk when it is closed.
*/
static int sqliteMemOpenCursor(
Dbbe *pDbbe, /* The database the table belongs to */
const char *zTable, /* The SQL name of the file to be opened */
int writeable, /* True to open for writing */
DbbeCursor **ppCursr /* Write the resulting table pointer here */
){
DbbeCursor *pCursr; /* The new table cursor */
char *zName; /* Canonical table name */
MTable *pTble; /* The underlying data file for this table */
int rc = SQLITE_OK; /* Return value */
int rw_mask; /* Permissions mask for opening a table */
int mode; /* Mode for opening a table */
Dbbex *pBe = (Dbbex*)pDbbe;
*ppCursr = 0;
pCursr = sqliteMalloc( sizeof(*pCursr) );
if( pCursr==0 ) return SQLITE_NOMEM;
if( zTable ){
Datum key;
zName = sqliteNameOfTable(zTable);
key.p = zName;
key.n = strlen(zName);
pTble = ArrayFind(&pBe->tables, key).p;
}else{
zName = 0;
pTble = 0;
}
if( pTble==0 ){
pTble = sqliteMalloc( sizeof(*pTble) );
if( pTble==0 ){
sqliteFree(zName);
return SQLITE_NOMEM;
}
if( zName ){
Datum ins_key, ins_data;
pTble->zName = zName;
pTble->delOnClose = 0;
ins_data.p = pTble;
ins_data.n = sizeof( *pTble );
ins_key.p = zName;
ins_key.n = strlen(zName);
ArrayInsert(&pBe->tables, ins_key, ins_data);
}else{
pTble->zName = 0;
pTble->delOnClose = 1;
}
ArrayInit(&pTble->data);
}else{
sqliteFree(zName);
}
pCursr->pBe = pBe;
pCursr->pTble = pTble;
pCursr->needRewind = 1;
*ppCursr = pCursr;
return rc;
}
/*
** Drop a table from the database. The file on the disk that corresponds
** to this table is deleted.
*/
static void sqliteMemDropTable(Dbbe *pDbbe, const char *zTable){
char *zName; /* Name of the table file */
Datum key, data;
MTable *pTble;
ArrayElem *i;
Dbbex *pBe = (Dbbex*)pDbbe;
zName = sqliteNameOfTable(zTable);
key.p = zName;
key.n = strlen(zName);
pTble = ArrayFind(&pBe->tables, key).p;
if( pTble ){
data.p = 0;
data.n = 0;
ArrayInsert(&pBe->tables, key, data);
deleteMTable(pTble);
}
sqliteFree(zName);
}
/*
** Close a cursor previously opened by sqliteMemOpenCursor().
**
** There can be multiple cursors pointing to the same open file.
** The underlying file is not closed until all cursors have been
** closed. This routine decrements the MTable.nref field of the
** underlying file and closes the file when nref reaches 0.
*/
static void sqliteMemCloseCursor(DbbeCursor *pCursr){
MTable *pTble;
Dbbex *pBe;
if( pCursr==0 ) return;
pTble = pCursr->pTble;
pBe = pCursr->pBe;
if( pTble->delOnClose ){
deleteMTable(pTble);
}
sqliteFree(pCursr);
}
/*
** Reorganize a table to reduce search times and disk usage.
*/
static int sqliteMemReorganizeTable(Dbbe *pBe, const char *zTable){
/* Do nothing */
return SQLITE_OK;
}
/*
** Fetch a single record from an open cursor. Return 1 on success
** and 0 on failure.
*/
static int sqliteMemFetch(DbbeCursor *pCursr, int nKey, char *pKey){
Datum key;
key.n = nKey;
key.p = pKey;
pCursr->elem = ArrayFindElement(&pCursr->pTble->data, key);
return pCursr->elem!=0;
}
/*
** Return 1 if the given key is already in the table. Return 0
** if it is not.
*/
static int sqliteMemTest(DbbeCursor *pCursr, int nKey, char *pKey){
return sqliteMemFetch(pCursr, nKey, pKey);
}
/*
** Copy bytes from the current key or data into a buffer supplied by
** the calling function. Return the number of bytes copied.
*/
static
int sqliteMemCopyKey(DbbeCursor *pCursr, int offset, int size, char *zBuf){
int n;
if( pCursr->elem==0 ) return 0;
if( offset>=ArrayKeySize(pCursr->elem) ) return 0;
if( offset+size>ArrayKeySize(pCursr->elem) ){
n = ArrayKeySize(pCursr->elem) - offset;
}else{
n = size;
}
memcpy(zBuf, &((char*)ArrayKey(pCursr->elem))[offset], n);
return n;
}
static
int sqliteMemCopyData(DbbeCursor *pCursr, int offset, int size, char *zBuf){
int n;
if( pCursr->elem==0 ) return 0;
if( offset>=ArrayDataSize(pCursr->elem) ) return 0;
if( offset+size>ArrayDataSize(pCursr->elem) ){
n = ArrayDataSize(pCursr->elem) - offset;
}else{
n = size;
}
memcpy(zBuf, &((char*)ArrayData(pCursr->elem))[offset], n);
return n;
}
/*
** Return a pointer to bytes from the key or data. The data returned
** is ephemeral.
*/
static char *sqliteMemReadKey(DbbeCursor *pCursr, int offset){
if( pCursr->elem==0 || offset<0 || offset>=ArrayKeySize(pCursr->elem) ){
return "";
}
return &((char*)ArrayKey(pCursr->elem))[offset];
}
static char *sqliteMemReadData(DbbeCursor *pCursr, int offset){
if( pCursr->elem==0 || offset<0 || offset>=ArrayDataSize(pCursr->elem) ){
return "";
}
return &((char*)ArrayData(pCursr->elem))[offset];
}
/*
** Return the total number of bytes in either data or key.
*/
static int sqliteMemKeyLength(DbbeCursor *pCursr){
return pCursr->elem ? ArrayKeySize(pCursr->elem) : 0;
}
static int sqliteMemDataLength(DbbeCursor *pCursr){
return pCursr->elem ? ArrayDataSize(pCursr->elem) : 0;
}
/*
** Make is so that the next call to sqliteNextKey() finds the first
** key of the table.
*/
static int sqliteMemRewind(DbbeCursor *pCursr){
pCursr->needRewind = 1;
return SQLITE_OK;
}
/*
** Read the next key from the table. Return 1 on success. Return
** 0 if there are no more keys.
*/
static int sqliteMemNextKey(DbbeCursor *pCursr){
if( pCursr->needRewind || pCursr->elem==0 ){
pCursr->elem = ArrayFirst(&pCursr->pTble->data);
pCursr->needRewind = 0;
}else{
pCursr->elem = ArrayNext(pCursr->elem);
}
return pCursr->elem!=0;
}
/*
** Get a new integer key.
*/
static int sqliteMemNew(DbbeCursor *pCursr){
int iKey;
Datum key;
int go = 1;
int i;
struct rc4 *pRc4;
pRc4 = &pCursr->pBe->rc4;
while( go ){
iKey = 0;
for(i=0; i<4; i++){
iKey = (iKey<<8) + rc4byte(pRc4);
}
if( iKey==0 ) continue;
key.p = (char*)&iKey;
key.n = 4;
go = ArrayFindElement(&pCursr->pTble->data, key)!=0;
}
return iKey;
}
/*
** Write an entry into the table. Overwrite any prior entry with the
** same key.
*/
static int
sqliteMemPut(DbbeCursor *pCursr, int nKey,char *pKey, int nData, char *pData){
Datum data, key;
data.n = nData;
data.p = sqliteMalloc( data.n );
memcpy(data.p, pData, data.n);
key.n = nKey;
key.p = pKey;
data = ArrayInsert(&pCursr->pTble->data, key, data);
if( data.p ){
sqliteFree(data.p);
}
return SQLITE_OK;
}
/*
** Remove an entry from a table, if the entry exists.
*/
static int sqliteMemDelete(DbbeCursor *pCursr, int nKey, char *pKey){
Datum key, data;
key.n = nKey;
key.p = pKey;
data.p = 0;
data.n = 0;
data = ArrayInsert(&pCursr->pTble->data, key, data);
if( data.p ){
sqliteFree(data.p);
}
return SQLITE_OK;
}
/*
** Open a temporary file. The file should be deleted when closed.
**
** Note that we can't use the old Unix trick of opening the file
** and then immediately unlinking the file. That works great
** under Unix, but fails when we try to port to Windows.
*/
static int sqliteMemOpenTempFile(Dbbe *pDbbe, FILE **ppTble){
char *zName; /* Full name of the temporary file */
char zBuf[50]; /* Base name of the temporary file */
int i; /* Loop counter */
int limit; /* Prevent an infinite loop */
int rc = SQLITE_OK; /* Value returned by this function */
Dbbex *pBe = (Dbbex*)pDbbe;
for(i=0; i<pBe->nTemp; i++){
if( pBe->apTemp[i]==0 ) break;
}
if( i>=pBe->nTemp ){
pBe->nTemp++;
pBe->apTemp = sqliteRealloc(pBe->apTemp, pBe->nTemp*sizeof(FILE*) );
pBe->azTemp = sqliteRealloc(pBe->azTemp, pBe->nTemp*sizeof(char*) );
}
if( pBe->apTemp==0 ){
*ppTble = 0;
return SQLITE_NOMEM;
}
limit = 4;
zName = 0;
do{
randomName(&pBe->rc4, zBuf, "/tmp/_temp_file_");
sqliteFree(zName);
zName = 0;
sqliteSetString(&zName, zBuf, 0);
}while( access(zName,0)==0 && limit-- >= 0 );
*ppTble = pBe->apTemp[i] = fopen(zName, "w+");
if( pBe->apTemp[i]==0 ){
rc = SQLITE_ERROR;
sqliteFree(zName);
pBe->azTemp[i] = 0;
}else{
pBe->azTemp[i] = zName;
}
return rc;
}
/*
** Close a temporary file opened using sqliteMemOpenTempFile()
*/
static void sqliteMemCloseTempFile(Dbbe *pDbbe, FILE *f){
int i;
Dbbex *pBe = (Dbbex*)pDbbe;
for(i=0; i<pBe->nTemp; i++){
if( pBe->apTemp[i]==f ){
unlink(pBe->azTemp[i]);
sqliteFree(pBe->azTemp[i]);
pBe->apTemp[i] = 0;
pBe->azTemp[i] = 0;
break;
}
}
fclose(f);
}
/*
** This routine opens a new database. For the GDBM driver
** implemented here, the database name is the name of the directory
** containing all the files of the database.
**
** If successful, a pointer to the Dbbe structure is returned.
** If there are errors, an appropriate error message is left
** in *pzErrMsg and NULL is returned.
*/
Dbbe *sqliteMemOpen(
const char *zName, /* The name of the database */
int writeFlag, /* True if we will be writing to the database */
int createFlag, /* True to create database if it doesn't exist */
char **pzErrMsg /* Write error messages (if any) here */
){
Dbbex *pNew;
long now;
pNew = sqliteMalloc( sizeof(*pNew) );
if( pNew==0 ){
sqliteSetString(pzErrMsg, "out of memory", 0);
return 0;
}
ArrayInit(&pNew->tables);
pNew->dbbe.Close = sqliteMemClose;
pNew->dbbe.OpenCursor = sqliteMemOpenCursor;
pNew->dbbe.DropTable = sqliteMemDropTable;
pNew->dbbe.ReorganizeTable = sqliteMemReorganizeTable;
pNew->dbbe.CloseCursor = sqliteMemCloseCursor;
pNew->dbbe.Fetch = sqliteMemFetch;
pNew->dbbe.Test = sqliteMemTest;
pNew->dbbe.CopyKey = sqliteMemCopyKey;
pNew->dbbe.CopyData = sqliteMemCopyData;
pNew->dbbe.ReadKey = sqliteMemReadKey;
pNew->dbbe.ReadData = sqliteMemReadData;
pNew->dbbe.KeyLength = sqliteMemKeyLength;
pNew->dbbe.DataLength = sqliteMemDataLength;
pNew->dbbe.NextKey = sqliteMemNextKey;
pNew->dbbe.Rewind = sqliteMemRewind;
pNew->dbbe.New = sqliteMemNew;
pNew->dbbe.Put = sqliteMemPut;
pNew->dbbe.Delete = sqliteMemDelete;
pNew->dbbe.OpenTempFile = sqliteMemOpenTempFile;
pNew->dbbe.CloseTempFile = sqliteMemCloseTempFile;
time(&now);
rc4init(&pNew->rc4, (char*)&now, sizeof(now));
return &pNew->dbbe;
}
+46 -11
View File
@@ -24,7 +24,7 @@
** This file contains routines used for analyzing expressions and
** for generating VDBE code that evaluates expressions.
**
** $Id: expr.c,v 1.18 2000/06/21 13:59:11 drh Exp $
** $Id: expr.c,v 1.20 2000/11/28 20:47:18 drh Exp $
*/
#include "sqliteInt.h"
@@ -323,6 +323,8 @@ int sqliteFuncId(Token *pToken){
{ "sum", 3, FN_Sum },
{ "avg", 3, FN_Avg },
{ "fcnt", 4, FN_Fcnt }, /* Used for testing only */
{ "length", 6, FN_Length},
{ "substr", 6, FN_Substr},
};
int i;
for(i=0; i<ArraySize(aFunc); i++){
@@ -381,6 +383,16 @@ int sqliteExprCheck(Parse *pParse, Expr *pExpr, int allowAgg, int *pIsAgg){
is_agg = 1;
break;
}
case FN_Length: {
too_few_args = n<1;
too_many_args = n>1;
break;
}
case FN_Substr: {
too_few_args = n<3;
too_many_args = n>3;
break;
}
/* The "fcnt(*)" function always returns the number of fetch
** operations that have occurred so far while processing the
** SQL statement. This information can be used by test procedures
@@ -392,6 +404,7 @@ int sqliteExprCheck(Parse *pParse, Expr *pExpr, int allowAgg, int *pIsAgg){
n = 0;
break;
}
default: break;
}
if( no_such_func ){
@@ -574,15 +587,37 @@ void sqliteExprCode(Parse *pParse, Expr *pExpr){
int op;
int i;
ExprList *pList = pExpr->pList;
if( id==FN_Fcnt ){
sqliteVdbeAddOp(v, OP_Fcnt, 0, 0, 0, 0);
break;
}
op = id==FN_Min ? OP_Min : OP_Max;
for(i=0; i<pList->nExpr; i++){
sqliteExprCode(pParse, pList->a[i].pExpr);
if( i>0 ){
sqliteVdbeAddOp(v, op, 0, 0, 0, 0);
switch( id ){
case FN_Fcnt: {
sqliteVdbeAddOp(v, OP_Fcnt, 0, 0, 0, 0);
break;
}
case FN_Min:
case FN_Max: {
op = id==FN_Min ? OP_Min : OP_Max;
for(i=0; i<pList->nExpr; i++){
sqliteExprCode(pParse, pList->a[i].pExpr);
if( i>0 ){
sqliteVdbeAddOp(v, op, 0, 0, 0, 0);
}
}
break;
}
case FN_Length: {
sqliteExprCode(pParse, pList->a[0].pExpr);
sqliteVdbeAddOp(v, OP_Strlen, 0, 0, 0, 0);
break;
}
case FN_Substr: {
for(i=0; i<pList->nExpr; i++){
sqliteExprCode(pParse, pList->a[i].pExpr);
}
sqliteVdbeAddOp(v, OP_Substr, 0, 0, 0, 0);
break;
}
default: {
/* Can't happen! */
break;
}
}
break;
@@ -703,7 +738,7 @@ void sqliteExprIfTrue(Parse *pParse, Expr *pExpr, int dest){
}
/*
** Generate code for boolean expression such that a jump is made
** Generate code for a boolean expression such that a jump is made
** to the label "dest" if the expression is false but execution
** continues straight thru if the expression is true.
*/
+57 -26
View File
@@ -26,7 +26,7 @@
** other files are for internal use by SQLite and should not be
** accessed by users of the library.
**
** $Id: main.c,v 1.16 2000/08/02 13:47:42 drh Exp $
** $Id: main.c,v 1.22 2000/12/10 18:23:50 drh Exp $
*/
#include "sqliteInt.h"
@@ -154,7 +154,7 @@ static int sqliteInit(sqlite *db, char **pzErrMsg){
/* Create a virtual machine to run the initialization program. Run
** the program. The delete the virtual machine.
*/
vdbe = sqliteVdbeCreate(db->pBe);
vdbe = sqliteVdbeCreate(db);
if( vdbe==0 ){
sqliteSetString(pzErrMsg, "out of memory",0);
return 1;
@@ -184,6 +184,11 @@ static int sqliteInit(sqlite *db, char **pzErrMsg){
return rc;
}
/*
** The version of the library
*/
const char sqlite_version[] = SQLITE_VERSION;
/*
** Open a new SQLite database. Construct an "sqlite" structure to define
** the state of this database and return a pointer to that structure.
@@ -222,7 +227,7 @@ sqlite *sqlite_open(const char *zFilename, int mode, char **pzErrMsg){
if( rc!=SQLITE_OK && rc!=SQLITE_BUSY ){
sqlite_close(db);
return 0;
}else{
}else /* if( pzErrMsg ) */{
free(*pzErrMsg);
*pzErrMsg = 0;
}
@@ -234,7 +239,7 @@ sqlite *sqlite_open(const char *zFilename, int mode, char **pzErrMsg){
*/
void sqlite_close(sqlite *db){
int i;
sqliteDbbeClose(db->pBe);
db->pBe->Close(db->pBe);
for(i=0; i<N_HASH; i++){
Table *pNext, *pList = db->apTblHash[i];
db->apTblHash[i] = 0;
@@ -252,29 +257,50 @@ void sqlite_close(sqlite *db){
** Return TRUE if the given SQL string ends in a semicolon.
*/
int sqlite_complete(const char *zSql){
int i;
int lastWasSemi = 0;
i = 0;
while( i>=0 && zSql[i]!=0 ){
int tokenType;
int n;
n = sqliteGetToken(&zSql[i], &tokenType);
switch( tokenType ){
case TK_SPACE:
case TK_COMMENT:
int isComplete = 0;
while( *zSql ){
switch( *zSql ){
case ';': {
isComplete = 1;
break;
case TK_SEMI:
lastWasSemi = 1;
}
case ' ':
case '\t':
case '\n':
case '\f': {
break;
default:
lastWasSemi = 0;
}
case '\'': {
isComplete = 0;
zSql++;
while( *zSql && *zSql!='\'' ){ zSql++; }
if( *zSql==0 ) return 0;
break;
}
case '"': {
isComplete = 0;
zSql++;
while( *zSql && *zSql!='"' ){ zSql++; }
if( *zSql==0 ) return 0;
break;
}
case '-': {
if( zSql[1]!='-' ){
isComplete = 0;
break;
}
while( *zSql && *zSql!='\n' ){ zSql++; }
if( *zSql==0 ) return isComplete;
break;
}
default: {
isComplete = 0;
break;
}
}
i += n;
zSql++;
}
return lastWasSemi;
return isComplete;
}
/*
@@ -306,9 +332,8 @@ int sqlite_exec(
sParse.db = db;
sParse.xCallback = xCallback;
sParse.pArg = pArg;
rc = sqliteRunParser(&sParse, zSql, pzErrMsg);
sqliteStrRealloc(pzErrMsg);
return rc;
sqliteRunParser(&sParse, zSql, pzErrMsg);
return sParse.rc;
}
/*
@@ -322,7 +347,6 @@ static int sqlite_default_busy_callback(
const char *NotUsed, /* The name of the table that is busy */
int count /* Number of times table has been busy */
){
int rc;
#if defined(HAVE_USLEEP) && HAVE_USLEEP
int delay = 10000;
int prior_delay = 0;
@@ -378,3 +402,10 @@ void sqlite_busy_timeout(sqlite *db, int ms){
sqlite_busy_handler(db, 0, 0);
}
}
/*
** Cause any pending operation to stop at its earliest opportunity.
*/
void sqlite_interrupt(sqlite *db){
db->flags |= SQLITE_Interrupt;
}
+790
View File
@@ -0,0 +1,790 @@
/*
** The "printf" code that follows dates from the 1980's. It is in
** the public domain. The original comments are included here for
** completeness. They are slightly out-of-date.
**
** The following modules is an enhanced replacement for the "printf" programs
** found in the standard library. The following enhancements are
** supported:
**
** + Additional functions. The standard set of "printf" functions
** includes printf, fprintf, sprintf, vprintf, vfprintf, and
** vsprintf. This module adds the following:
**
** * snprintf -- Works like sprintf, but has an extra argument
** which is the size of the buffer written to.
**
** * mprintf -- Similar to sprintf. Writes output to memory
** obtained from malloc.
**
** * xprintf -- Calls a function to dispose of output.
**
** * nprintf -- No output, but returns the number of characters
** that would have been output by printf.
**
** * A v- version (ex: vsnprintf) of every function is also
** supplied.
**
** + A few extensions to the formatting notation are supported:
**
** * The "=" flag (similar to "-") causes the output to be
** be centered in the appropriately sized field.
**
** * The %b field outputs an integer in binary notation.
**
** * The %c field now accepts a precision. The character output
** is repeated by the number of times the precision specifies.
**
** * The %' field works like %c, but takes as its character the
** next character of the format string, instead of the next
** argument. For example, printf("%.78'-") prints 78 minus
** signs, the same as printf("%.78c",'-').
**
** + When compiled using GCC on a SPARC, this version of printf is
** faster than the library printf for SUN OS 4.1.
**
** + All functions are fully reentrant.
**
*/
#include <ctype.h>
#include "sqliteInt.h"
/*
** Undefine COMPATIBILITY to make some slight changes in the way things
** work. I think the changes are an improvement, but they are not
** backwards compatible.
*/
/* #define COMPATIBILITY / * Compatible with SUN OS 4.1 */
/*
** Conversion types fall into various categories as defined by the
** following enumeration.
*/
enum et_type { /* The type of the format field */
etRADIX, /* Integer types. %d, %x, %o, and so forth */
etFLOAT, /* Floating point. %f */
etEXP, /* Exponentional notation. %e and %E */
etGENERIC, /* Floating or exponential, depending on exponent. %g */
etSIZE, /* Return number of characters processed so far. %n */
etSTRING, /* Strings. %s */
etPERCENT, /* Percent symbol. %% */
etCHARX, /* Characters. %c */
etERROR, /* Used to indicate no such conversion type */
/* The rest are extensions, not normally found in printf() */
etCHARLIT, /* Literal characters. %' */
etSQLESCAPE, /* Strings with '\'' doubled. %q */
etORDINAL /* 1st, 2nd, 3rd and so forth */
};
/*
** Each builtin conversion character (ex: the 'd' in "%d") is described
** by an instance of the following structure
*/
typedef struct et_info { /* Information about each format field */
int fmttype; /* The format field code letter */
int base; /* The base for radix conversion */
char *charset; /* The character set for conversion */
int flag_signed; /* Is the quantity signed? */
char *prefix; /* Prefix on non-zero values in alt format */
enum et_type type; /* Conversion paradigm */
} et_info;
/*
** The following table is searched linearly, so it is good to put the
** most frequently used conversion types first.
*/
static et_info fmtinfo[] = {
{ 'd', 10, "0123456789", 1, 0, etRADIX, },
{ 's', 0, 0, 0, 0, etSTRING, },
{ 'q', 0, 0, 0, 0, etSQLESCAPE, },
{ 'c', 0, 0, 0, 0, etCHARX, },
{ 'o', 8, "01234567", 0, "0", etRADIX, },
{ 'u', 10, "0123456789", 0, 0, etRADIX, },
{ 'x', 16, "0123456789abcdef", 0, "x0", etRADIX, },
{ 'X', 16, "0123456789ABCDEF", 0, "X0", etRADIX, },
{ 'r', 10, "0123456789", 0, 0, etORDINAL, },
{ 'f', 0, 0, 1, 0, etFLOAT, },
{ 'e', 0, "e", 1, 0, etEXP, },
{ 'E', 0, "E", 1, 0, etEXP, },
{ 'g', 0, "e", 1, 0, etGENERIC, },
{ 'G', 0, "E", 1, 0, etGENERIC, },
{ 'i', 10, "0123456789", 1, 0, etRADIX, },
{ 'n', 0, 0, 0, 0, etSIZE, },
{ '%', 0, 0, 0, 0, etPERCENT, },
{ 'b', 2, "01", 0, "b0", etRADIX, }, /* Binary */
{ 'p', 10, "0123456789", 0, 0, etRADIX, }, /* Pointers */
{ '\'', 0, 0, 0, 0, etCHARLIT, }, /* Literal char */
};
#define etNINFO (sizeof(fmtinfo)/sizeof(fmtinfo[0]))
/*
** If NOFLOATINGPOINT is defined, then none of the floating point
** conversions will work.
*/
#ifndef etNOFLOATINGPOINT
/*
** "*val" is a double such that 0.1 <= *val < 10.0
** Return the ascii code for the leading digit of *val, then
** multiply "*val" by 10.0 to renormalize.
**
** Example:
** input: *val = 3.14159
** output: *val = 1.4159 function return = '3'
**
** The counter *cnt is incremented each time. After counter exceeds
** 16 (the number of significant digits in a 64-bit float) '0' is
** always returned.
*/
static int et_getdigit(double *val, int *cnt){
int digit;
double d;
if( (*cnt)++ >= 16 ) return '0';
digit = (int)*val;
d = digit;
digit += '0';
*val = (*val - d)*10.0;
return digit;
}
#endif
#define etBUFSIZE 1000 /* Size of the output buffer */
/*
** The root program. All variations call this core.
**
** INPUTS:
** func This is a pointer to a function taking three arguments
** 1. A pointer to anything. Same as the "arg" parameter.
** 2. A pointer to the list of characters to be output
** (Note, this list is NOT null terminated.)
** 3. An integer number of characters to be output.
** (Note: This number might be zero.)
**
** arg This is the pointer to anything which will be passed as the
** first argument to "func". Use it for whatever you like.
**
** fmt This is the format string, as in the usual print.
**
** ap This is a pointer to a list of arguments. Same as in
** vfprint.
**
** OUTPUTS:
** The return value is the total number of characters sent to
** the function "func". Returns -1 on a error.
**
** Note that the order in which automatic variables are declared below
** seems to make a big difference in determining how fast this beast
** will run.
*/
static int vxprintf(
void (*func)(void*,char*,int),
void *arg,
const char *format,
va_list ap
){
register const char *fmt; /* The format string. */
register int c; /* Next character in the format string */
register char *bufpt; /* Pointer to the conversion buffer */
register int precision; /* Precision of the current field */
register int length; /* Length of the field */
register int idx; /* A general purpose loop counter */
int count; /* Total number of characters output */
int width; /* Width of the current field */
int flag_leftjustify; /* True if "-" flag is present */
int flag_plussign; /* True if "+" flag is present */
int flag_blanksign; /* True if " " flag is present */
int flag_alternateform; /* True if "#" flag is present */
int flag_zeropad; /* True if field width constant starts with zero */
int flag_long; /* True if "l" flag is present */
int flag_center; /* True if "=" flag is present */
unsigned long longvalue; /* Value for integer types */
double realvalue; /* Value for real types */
et_info *infop; /* Pointer to the appropriate info structure */
char buf[etBUFSIZE]; /* Conversion buffer */
char prefix; /* Prefix character. "+" or "-" or " " or '\0'. */
int errorflag = 0; /* True if an error is encountered */
enum et_type xtype; /* Conversion paradigm */
char *zMem; /* String to be freed */
char *zExtra; /* Extra memory used for etTCLESCAPE conversions */
static char spaces[] = " "
" ";
#define etSPACESIZE (sizeof(spaces)-1)
#ifndef etNOFLOATINGPOINT
int exp; /* exponent of real numbers */
double rounder; /* Used for rounding floating point values */
int flag_dp; /* True if decimal point should be shown */
int flag_rtz; /* True if trailing zeros should be removed */
int flag_exp; /* True to force display of the exponent */
int nsd; /* Number of significant digits returned */
#endif
fmt = format; /* Put in a register for speed */
count = length = 0;
bufpt = 0;
for(; (c=(*fmt))!=0; ++fmt){
if( c!='%' ){
register int amt;
bufpt = (char *)fmt;
amt = 1;
while( (c=(*++fmt))!='%' && c!=0 ) amt++;
(*func)(arg,bufpt,amt);
count += amt;
if( c==0 ) break;
}
if( (c=(*++fmt))==0 ){
errorflag = 1;
(*func)(arg,"%",1);
count++;
break;
}
/* Find out what flags are present */
flag_leftjustify = flag_plussign = flag_blanksign =
flag_alternateform = flag_zeropad = flag_center = 0;
do{
switch( c ){
case '-': flag_leftjustify = 1; c = 0; break;
case '+': flag_plussign = 1; c = 0; break;
case ' ': flag_blanksign = 1; c = 0; break;
case '#': flag_alternateform = 1; c = 0; break;
case '0': flag_zeropad = 1; c = 0; break;
case '=': flag_center = 1; c = 0; break;
default: break;
}
}while( c==0 && (c=(*++fmt))!=0 );
if( flag_center ) flag_leftjustify = 0;
/* Get the field width */
width = 0;
if( c=='*' ){
width = va_arg(ap,int);
if( width<0 ){
flag_leftjustify = 1;
width = -width;
}
c = *++fmt;
}else{
while( isdigit(c) ){
width = width*10 + c - '0';
c = *++fmt;
}
}
if( width > etBUFSIZE-10 ){
width = etBUFSIZE-10;
}
/* Get the precision */
if( c=='.' ){
precision = 0;
c = *++fmt;
if( c=='*' ){
precision = va_arg(ap,int);
#ifndef etCOMPATIBILITY
/* This is sensible, but SUN OS 4.1 doesn't do it. */
if( precision<0 ) precision = -precision;
#endif
c = *++fmt;
}else{
while( isdigit(c) ){
precision = precision*10 + c - '0';
c = *++fmt;
}
}
/* Limit the precision to prevent overflowing buf[] during conversion */
if( precision>etBUFSIZE-40 ) precision = etBUFSIZE-40;
}else{
precision = -1;
}
/* Get the conversion type modifier */
if( c=='l' ){
flag_long = 1;
c = *++fmt;
}else{
flag_long = 0;
}
/* Fetch the info entry for the field */
infop = 0;
for(idx=0; idx<etNINFO; idx++){
if( c==fmtinfo[idx].fmttype ){
infop = &fmtinfo[idx];
break;
}
}
/* No info entry found. It must be an error. */
if( infop==0 ){
xtype = etERROR;
}else{
xtype = infop->type;
}
zExtra = 0;
/*
** At this point, variables are initialized as follows:
**
** flag_alternateform TRUE if a '#' is present.
** flag_plussign TRUE if a '+' is present.
** flag_leftjustify TRUE if a '-' is present or if the
** field width was negative.
** flag_zeropad TRUE if the width began with 0.
** flag_long TRUE if the letter 'l' (ell) prefixed
** the conversion character.
** flag_blanksign TRUE if a ' ' is present.
** width The specified field width. This is
** always non-negative. Zero is the default.
** precision The specified precision. The default
** is -1.
** xtype The class of the conversion.
** infop Pointer to the appropriate info struct.
*/
switch( xtype ){
case etORDINAL:
case etRADIX:
if( flag_long ) longvalue = va_arg(ap,long);
else longvalue = va_arg(ap,int);
#ifdef etCOMPATIBILITY
/* For the format %#x, the value zero is printed "0" not "0x0".
** I think this is stupid. */
if( longvalue==0 ) flag_alternateform = 0;
#else
/* More sensible: turn off the prefix for octal (to prevent "00"),
** but leave the prefix for hex. */
if( longvalue==0 && infop->base==8 ) flag_alternateform = 0;
#endif
if( infop->flag_signed ){
if( *(long*)&longvalue<0 ){
longvalue = -*(long*)&longvalue;
prefix = '-';
}else if( flag_plussign ) prefix = '+';
else if( flag_blanksign ) prefix = ' ';
else prefix = 0;
}else prefix = 0;
if( flag_zeropad && precision<width-(prefix!=0) ){
precision = width-(prefix!=0);
}
bufpt = &buf[etBUFSIZE];
if( xtype==etORDINAL ){
long a,b;
a = longvalue%10;
b = longvalue%100;
bufpt -= 2;
if( a==0 || a>3 || (b>10 && b<14) ){
bufpt[0] = 't';
bufpt[1] = 'h';
}else if( a==1 ){
bufpt[0] = 's';
bufpt[1] = 't';
}else if( a==2 ){
bufpt[0] = 'n';
bufpt[1] = 'd';
}else if( a==3 ){
bufpt[0] = 'r';
bufpt[1] = 'd';
}
}
{
register char *cset; /* Use registers for speed */
register int base;
cset = infop->charset;
base = infop->base;
do{ /* Convert to ascii */
*(--bufpt) = cset[longvalue%base];
longvalue = longvalue/base;
}while( longvalue>0 );
}
length = (long)&buf[etBUFSIZE]-(long)bufpt;
for(idx=precision-length; idx>0; idx--){
*(--bufpt) = '0'; /* Zero pad */
}
if( prefix ) *(--bufpt) = prefix; /* Add sign */
if( flag_alternateform && infop->prefix ){ /* Add "0" or "0x" */
char *pre, x;
pre = infop->prefix;
if( *bufpt!=pre[0] ){
for(pre=infop->prefix; (x=(*pre))!=0; pre++) *(--bufpt) = x;
}
}
length = (long)&buf[etBUFSIZE]-(long)bufpt;
break;
case etFLOAT:
case etEXP:
case etGENERIC:
realvalue = va_arg(ap,double);
#ifndef etNOFLOATINGPOINT
if( precision<0 ) precision = 6; /* Set default precision */
if( precision>etBUFSIZE-10 ) precision = etBUFSIZE-10;
if( realvalue<0.0 ){
realvalue = -realvalue;
prefix = '-';
}else{
if( flag_plussign ) prefix = '+';
else if( flag_blanksign ) prefix = ' ';
else prefix = 0;
}
if( infop->type==etGENERIC && precision>0 ) precision--;
rounder = 0.0;
#ifdef COMPATIBILITY
/* Rounding works like BSD when the constant 0.4999 is used. Wierd! */
for(idx=precision, rounder=0.4999; idx>0; idx--, rounder*=0.1);
#else
/* It makes more sense to use 0.5 */
for(idx=precision, rounder=0.5; idx>0; idx--, rounder*=0.1);
#endif
if( infop->type==etFLOAT ) realvalue += rounder;
/* Normalize realvalue to within 10.0 > realvalue >= 1.0 */
exp = 0;
if( realvalue>0.0 ){
int k = 0;
while( realvalue>=1e8 && k++<100 ){ realvalue *= 1e-8; exp+=8; }
while( realvalue>=10.0 && k++<100 ){ realvalue *= 0.1; exp++; }
while( realvalue<1e-8 && k++<100 ){ realvalue *= 1e8; exp-=8; }
while( realvalue<1.0 && k++<100 ){ realvalue *= 10.0; exp--; }
if( k>=100 ){
bufpt = "NaN";
length = 3;
break;
}
}
bufpt = buf;
/*
** If the field type is etGENERIC, then convert to either etEXP
** or etFLOAT, as appropriate.
*/
flag_exp = xtype==etEXP;
if( xtype!=etFLOAT ){
realvalue += rounder;
if( realvalue>=10.0 ){ realvalue *= 0.1; exp++; }
}
if( xtype==etGENERIC ){
flag_rtz = !flag_alternateform;
if( exp<-4 || exp>precision ){
xtype = etEXP;
}else{
precision = precision - exp;
xtype = etFLOAT;
}
}else{
flag_rtz = 0;
}
/*
** The "exp+precision" test causes output to be of type etEXP if
** the precision is too large to fit in buf[].
*/
nsd = 0;
if( xtype==etFLOAT && exp+precision<etBUFSIZE-30 ){
flag_dp = (precision>0 || flag_alternateform);
if( prefix ) *(bufpt++) = prefix; /* Sign */
if( exp<0 ) *(bufpt++) = '0'; /* Digits before "." */
else for(; exp>=0; exp--) *(bufpt++) = et_getdigit(&realvalue,&nsd);
if( flag_dp ) *(bufpt++) = '.'; /* The decimal point */
for(exp++; exp<0 && precision>0; precision--, exp++){
*(bufpt++) = '0';
}
while( (precision--)>0 ) *(bufpt++) = et_getdigit(&realvalue,&nsd);
*(bufpt--) = 0; /* Null terminate */
if( flag_rtz && flag_dp ){ /* Remove trailing zeros and "." */
while( bufpt>=buf && *bufpt=='0' ) *(bufpt--) = 0;
if( bufpt>=buf && *bufpt=='.' ) *(bufpt--) = 0;
}
bufpt++; /* point to next free slot */
}else{ /* etEXP or etGENERIC */
flag_dp = (precision>0 || flag_alternateform);
if( prefix ) *(bufpt++) = prefix; /* Sign */
*(bufpt++) = et_getdigit(&realvalue,&nsd); /* First digit */
if( flag_dp ) *(bufpt++) = '.'; /* Decimal point */
while( (precision--)>0 ) *(bufpt++) = et_getdigit(&realvalue,&nsd);
bufpt--; /* point to last digit */
if( flag_rtz && flag_dp ){ /* Remove tail zeros */
while( bufpt>=buf && *bufpt=='0' ) *(bufpt--) = 0;
if( bufpt>=buf && *bufpt=='.' ) *(bufpt--) = 0;
}
bufpt++; /* point to next free slot */
if( exp || flag_exp ){
*(bufpt++) = infop->charset[0];
if( exp<0 ){ *(bufpt++) = '-'; exp = -exp; } /* sign of exp */
else { *(bufpt++) = '+'; }
if( exp>=100 ){
*(bufpt++) = (exp/100)+'0'; /* 100's digit */
exp %= 100;
}
*(bufpt++) = exp/10+'0'; /* 10's digit */
*(bufpt++) = exp%10+'0'; /* 1's digit */
}
}
/* The converted number is in buf[] and zero terminated. Output it.
** Note that the number is in the usual order, not reversed as with
** integer conversions. */
length = (long)bufpt-(long)buf;
bufpt = buf;
/* Special case: Add leading zeros if the flag_zeropad flag is
** set and we are not left justified */
if( flag_zeropad && !flag_leftjustify && length < width){
int i;
int nPad = width - length;
for(i=width; i>=nPad; i--){
bufpt[i] = bufpt[i-nPad];
}
i = prefix!=0;
while( nPad-- ) bufpt[i++] = '0';
length = width;
}
#endif
break;
case etSIZE:
*(va_arg(ap,int*)) = count;
length = width = 0;
break;
case etPERCENT:
buf[0] = '%';
bufpt = buf;
length = 1;
break;
case etCHARLIT:
case etCHARX:
c = buf[0] = (xtype==etCHARX ? va_arg(ap,int) : *++fmt);
if( precision>=0 ){
for(idx=1; idx<precision; idx++) buf[idx] = c;
length = precision;
}else{
length =1;
}
bufpt = buf;
break;
case etSTRING:
zMem = bufpt = va_arg(ap,char*);
if( bufpt==0 ) bufpt = "(null)";
length = strlen(bufpt);
if( precision>=0 && precision<length ) length = precision;
break;
case etSQLESCAPE:
{
int i, j, n, c;
char *arg = va_arg(ap,char*);
if( arg==0 ) arg = "(NULL)";
for(i=n=0; (c=arg[i])!=0; i++){
if( c=='\'' ) n++;
}
n += i + 1;
if( n>etBUFSIZE ){
bufpt = zExtra = sqliteMalloc( n );
}else{
bufpt = buf;
}
for(i=j=0; (c=arg[i])!=0; i++){
bufpt[j++] = c;
if( c=='\'' ) bufpt[j++] = c;
}
bufpt[j] = 0;
length = j;
if( precision>=0 && precision<length ) length = precision;
}
break;
case etERROR:
buf[0] = '%';
buf[1] = c;
errorflag = 0;
idx = 1+(c!=0);
(*func)(arg,"%",idx);
count += idx;
if( c==0 ) fmt--;
break;
}/* End switch over the format type */
/*
** The text of the conversion is pointed to by "bufpt" and is
** "length" characters long. The field width is "width". Do
** the output.
*/
if( !flag_leftjustify ){
register int nspace;
nspace = width-length;
if( nspace>0 ){
if( flag_center ){
nspace = nspace/2;
width -= nspace;
flag_leftjustify = 1;
}
count += nspace;
while( nspace>=etSPACESIZE ){
(*func)(arg,spaces,etSPACESIZE);
nspace -= etSPACESIZE;
}
if( nspace>0 ) (*func)(arg,spaces,nspace);
}
}
if( length>0 ){
(*func)(arg,bufpt,length);
count += length;
}
if( flag_leftjustify ){
register int nspace;
nspace = width-length;
if( nspace>0 ){
count += nspace;
while( nspace>=etSPACESIZE ){
(*func)(arg,spaces,etSPACESIZE);
nspace -= etSPACESIZE;
}
if( nspace>0 ) (*func)(arg,spaces,nspace);
}
}
if( zExtra ){
sqliteFree(zExtra);
}
}/* End for loop over the format string */
return errorflag ? -1 : count;
} /* End of function */
/* This structure is used to store state information about the
** write to memory that is currently in progress.
*/
struct sgMprintf {
char *zBase; /* A base allocation */
char *zText; /* The string collected so far */
int nChar; /* Length of the string so far */
int nAlloc; /* Amount of space allocated in zText */
};
/*
** This function implements the callback from vxprintf.
**
** This routine add nNewChar characters of text in zNewText to
** the sgMprintf structure pointed to by "arg".
*/
static void mout(void *arg, char *zNewText, int nNewChar){
struct sgMprintf *pM = (struct sgMprintf*)arg;
if( pM->nChar + nNewChar + 1 > pM->nAlloc ){
pM->nAlloc = pM->nChar + nNewChar*2 + 1;
if( pM->zText==pM->zBase ){
pM->zText = sqliteMalloc(pM->nAlloc);
if( pM->zText && pM->nChar ) memcpy(pM->zText,pM->zBase,pM->nChar);
}else{
pM->zText = sqliteRealloc(pM->zText, pM->nAlloc);
}
}
if( pM->zText ){
memcpy(&pM->zText[pM->nChar], zNewText, nNewChar);
pM->nChar += nNewChar;
pM->zText[pM->nChar] = 0;
}
}
/*
** sqlite_mprintf() works like printf(), but allocations memory to hold the
** resulting string and returns a pointer to the allocated memory. Use
** sqliteFree() to release the memory allocated.
*/
char *sqlite_mprintf(const char *zFormat, ...){
va_list ap;
struct sgMprintf sMprintf;
char *zNew;
char zBuf[200];
sMprintf.nChar = 0;
sMprintf.nAlloc = sizeof(zBuf);
sMprintf.zText = zBuf;
sMprintf.zBase = zBuf;
va_start(ap,zFormat);
vxprintf(mout,&sMprintf,zFormat,ap);
va_end(ap);
sMprintf.zText[sMprintf.nChar] = 0;
if( sMprintf.zText==sMprintf.zBase ){
zNew = sqliteMalloc( sMprintf.nChar+1 );
if( zNew ) strcpy(zNew,zBuf);
}else{
zNew = sqliteRealloc(sMprintf.zText,sMprintf.nChar+1);
}
return zNew;
}
/* This is the varargs version of sqlite_mprintf.
*/
char *sqlite_vmprintf(const char *zFormat, va_list ap){
struct sgMprintf sMprintf;
char zBuf[200];
sMprintf.nChar = 0;
sMprintf.zText = zBuf;
sMprintf.nAlloc = sizeof(zBuf);
sMprintf.zBase = zBuf;
vxprintf(mout,&sMprintf,zFormat,ap);
sMprintf.zText[sMprintf.nChar] = 0;
if( sMprintf.zText==sMprintf.zBase ){
sMprintf.zText = sqliteMalloc( strlen(zBuf)+1 );
if( sMprintf.zText ) strcpy(sMprintf.zText,zBuf);
}else{
sMprintf.zText = sqliteRealloc(sMprintf.zText,sMprintf.nChar+1);
}
return sMprintf.zText;
}
/*
** The following four routines implement the varargs versions of the
** sqlite_exec() and sqlite_get_table() interfaces. See the sqlite.h
** header files for a more detailed description of how these interfaces
** work.
**
** These routines are all just simple wrappers.
*/
int sqlite_exec_printf(
sqlite *db, /* An open database */
char *sqlFormat, /* printf-style format string for the SQL */
sqlite_callback xCallback, /* Callback function */
void *pArg, /* 1st argument to callback function */
char **errmsg, /* Error msg written here */
... /* Arguments to the format string. */
){
va_list ap;
int rc;
va_start(ap, errmsg);
rc = sqlite_exec_vprintf(db, sqlFormat, xCallback, pArg, errmsg, ap);
va_end(ap);
return rc;
}
int sqlite_exec_vprintf(
sqlite *db, /* An open database */
char *sqlFormat, /* printf-style format string for the SQL */
sqlite_callback xCallback, /* Callback function */
void *pArg, /* 1st argument to callback function */
char **errmsg, /* Error msg written here */
va_list ap /* Arguments to the format string. */
){
char *zSql;
int rc;
zSql = sqlite_vmprintf(sqlFormat, ap);
rc = sqlite_exec(db, zSql, xCallback, pArg, errmsg);
sqliteFree(zSql);
return rc;
}
int sqlite_get_table_printf(
sqlite *db, /* An open database */
char *sqlFormat, /* printf-style format string for the SQL */
char ***resultp, /* Result written to a char *[] that this points to */
int *nrow, /* Number of result rows written here */
int *ncol, /* Number of result columns written here */
char **errmsg, /* Error msg written here */
... /* Arguments to the format string */
){
va_list ap;
int rc;
va_start(ap, errmsg);
rc = sqlite_get_table_vprintf(db, sqlFormat, resultp, nrow, ncol, errmsg, ap);
va_end(ap);
return rc;
}
int sqlite_get_table_vprintf(
sqlite *db, /* An open database */
char *sqlFormat, /* printf-style format string for the SQL */
char ***resultp, /* Result written to a char *[] that this points to */
int *nrow, /* Number of result rows written here */
int *ncolumn, /* Number of result columns written here */
char **errmsg, /* Error msg written here */
va_list ap /* Arguments to the format string */
){
char *zSql;
int rc;
zSql = sqlite_vmprintf(sqlFormat, ap);
rc = sqlite_get_table(db, zSql, resultp, nrow, ncolumn, errmsg);
sqliteFree(zSql);
return rc;
}
+3 -3
View File
@@ -24,7 +24,7 @@
** This file contains C code routines that are called by the parser
** to handle SELECT statements.
**
** $Id: select.c,v 1.26 2000/07/29 13:06:59 drh Exp $
** $Id: select.c,v 1.27 2000/10/16 22:06:42 drh Exp $
*/
#include "sqliteInt.h"
@@ -424,7 +424,7 @@ static int matchOrderbyToColumn(
Vdbe *sqliteGetVdbe(Parse *pParse){
Vdbe *v = pParse->pVdbe;
if( v==0 ){
v = pParse->pVdbe = sqliteVdbeCreate(pParse->db->pBe);
v = pParse->pVdbe = sqliteVdbeCreate(pParse->db);
}
if( v==0 ){
sqliteSetString(&pParse->zErrMsg, "out of memory", 0);
@@ -818,7 +818,7 @@ int sqliteSelect(
*/
v = pParse->pVdbe;
if( v==0 ){
v = pParse->pVdbe = sqliteVdbeCreate(pParse->db->pBe);
v = pParse->pVdbe = sqliteVdbeCreate(pParse->db);
}
if( v==0 ){
sqliteSetString(&pParse->zErrMsg, "out of memory", 0);
+126 -51
View File
@@ -24,7 +24,7 @@
** This file contains code to implement the "sqlite" command line
** utility for accessing SQLite databases.
**
** $Id: shell.c,v 1.21 2000/08/17 09:50:00 drh Exp $
** $Id: shell.c,v 1.27 2000/10/16 22:06:42 drh Exp $
*/
#include <stdlib.h>
#include <string.h>
@@ -32,6 +32,9 @@
#include "sqlite.h"
#include <unistd.h>
#include <ctype.h>
#ifdef OS_UNIX
# include <signal.h>
#endif
#if defined(HAVE_READLINE) && HAVE_READLINE==1
# include <readline/readline.h>
@@ -41,6 +44,13 @@
# define add_history(X)
#endif
/*
** The following is the open SQLite database. We make a pointer
** to this database a static variable so that it can be accessed
** by the SIGINT handler to interrupt database processing.
*/
static sqlite *db = 0;
/*
** This routine reads a line of text from standard input, stores
** the text in memory obtained from malloc() and returns a pointer
@@ -140,8 +150,9 @@ struct callback_data {
#define MODE_Line 0 /* One column per line. Blank line between records */
#define MODE_Column 1 /* One record per line in neat columns */
#define MODE_List 2 /* One record per line with a separator */
#define MODE_Html 3 /* Generate an XHTML table */
#define MODE_Insert 4 /* Generate SQL "insert" statements */
#define MODE_Semi 3 /* Same as MODE_List but append ";" to each line */
#define MODE_Html 4 /* Generate an XHTML table */
#define MODE_Insert 5 /* Generate SQL "insert" statements */
/*
** Number of elements in an array
@@ -228,6 +239,13 @@ static void output_html_string(FILE *out, const char *z){
}
}
/*
** This routine runs when the user presses Ctrl-C
*/
static void interrupt_handler(int NotUsed){
if( db ) sqlite_interrupt(db);
}
/*
** This is the callback routine that the SQLite library
** invokes for each row of a query result.
@@ -237,9 +255,14 @@ static int callback(void *pArg, int nArg, char **azArg, char **azCol){
struct callback_data *p = (struct callback_data*)pArg;
switch( p->mode ){
case MODE_Line: {
int w = 5;
for(i=0; i<nArg; i++){
int len = strlen(azCol[i]);
if( len>w ) w = len;
}
if( p->cnt++>0 ) fprintf(p->out,"\n");
for(i=0; i<nArg; i++){
fprintf(p->out,"%s = %s\n", azCol[i], azArg[i] ? azArg[i] : 0);
fprintf(p->out,"%*s = %s\n", w, azCol[i], azArg[i] ? azArg[i] : 0);
}
break;
}
@@ -253,9 +276,9 @@ static int callback(void *pArg, int nArg, char **azArg, char **azCol){
w = 0;
}
if( w<=0 ){
w = strlen(azCol[i]);
w = strlen(azCol[i] ? azCol[i] : "");
if( w<10 ) w = 10;
n = strlen(azArg[i]);
n = strlen(azArg[i] ? azArg[i] : "");
if( w<n ) w = n;
}
if( i<ArraySize(p->actualWidth) ){
@@ -291,6 +314,7 @@ static int callback(void *pArg, int nArg, char **azArg, char **azCol){
}
break;
}
case MODE_Semi:
case MODE_List: {
if( p->cnt++==0 && p->showHeader ){
for(i=0; i<nArg; i++){
@@ -312,7 +336,13 @@ static int callback(void *pArg, int nArg, char **azArg, char **azCol){
}
z += j;
}
fprintf(p->out, "%s", i==nArg-1 ? "\n" : p->separator);
if( i<nArg-1 ){
fprintf(p->out, "%s", p->separator);
}else if( p->mode==MODE_Semi ){
fprintf(p->out, ";\n");
}else{
fprintf(p->out, "\n");
}
}
break;
}
@@ -364,14 +394,15 @@ static int dump_callback(void *pArg, int nArg, char **azArg, char **azCol){
fprintf(pData->out, "%s;\n", azArg[2]);
if( strcmp(azArg[1],"table")==0 ){
struct callback_data d2;
char zSql[1000];
d2 = *pData;
d2.mode = MODE_List;
d2.escape = '\t';
strcpy(d2.separator,"\t");
fprintf(pData->out, "COPY '%s' FROM STDIN;\n", azArg[0]);
sprintf(zSql, "SELECT * FROM '%s'", azArg[0]);
sqlite_exec(pData->db, zSql, callback, &d2, 0);
sqlite_exec_printf(pData->db,
"SELECT * FROM '%q'",
callback, &d2, 0, azArg[0]
);
fprintf(pData->out, "\\.\n");
}
fprintf(pData->out, "VACUUM '%s';\n", azArg[0]);
@@ -388,8 +419,8 @@ static char zHelp[] =
".header ON|OFF Turn display of headers on or off\n"
".help Show this message\n"
".indices TABLE Show names of all indices on TABLE\n"
".mode MODE Set mode to one of \"line\", \"column\", "
"\"list\", or \"html\"\n"
".mode MODE Set mode to one of \"line\", \"column\", \n"
" \"insert\", \"list\", or \"html\"\n"
".mode insert TABLE Generate SQL insert statements for TABLE\n"
".output FILENAME Send output to FILENAME\n"
".output stdout Send output to the screen\n"
@@ -433,22 +464,24 @@ static void do_meta_command(char *zLine, sqlite *db, struct callback_data *p){
if( nArg==0 ) return;
n = strlen(azArg[0]);
c = azArg[0][0];
if( c=='d' && strncmp(azArg[0], "dump", n)==0 ){
char *zErrMsg = 0;
char zSql[1000];
if( nArg==1 ){
sprintf(zSql, "SELECT name, type, sql FROM sqlite_master "
"WHERE type!='meta' "
"ORDER BY tbl_name, type DESC, name");
sqlite_exec(db, zSql, dump_callback, p, &zErrMsg);
sqlite_exec(db,
"SELECT name, type, sql FROM sqlite_master "
"WHERE type!='meta' "
"ORDER BY tbl_name, type DESC, name",
dump_callback, p, &zErrMsg
);
}else{
int i;
for(i=1; i<nArg && zErrMsg==0; i++){
sprintf(zSql, "SELECT name, type, sql FROM sqlite_master "
"WHERE tbl_name LIKE '%.800s' AND type!='meta' "
"ORDER BY type DESC, name", azArg[i]);
sqlite_exec(db, zSql, dump_callback, p, &zErrMsg);
sqlite_exec_printf(db,
"SELECT name, type, sql FROM sqlite_master "
"WHERE tbl_name LIKE '%q' AND type!='meta' "
"ORDER BY type DESC, name",
dump_callback, p, &zErrMsg, azArg[i]
);
}
}
@@ -494,14 +527,15 @@ static void do_meta_command(char *zLine, sqlite *db, struct callback_data *p){
if( c=='i' && strncmp(azArg[0], "indices", n)==0 && nArg>1 ){
struct callback_data data;
char *zErrMsg = 0;
char zSql[1000];
memcpy(&data, p, sizeof(data));
data.showHeader = 0;
data.mode = MODE_List;
sprintf(zSql, "SELECT name FROM sqlite_master "
"WHERE type='index' AND tbl_name LIKE '%.800s' "
"ORDER BY name", azArg[1]);
sqlite_exec(db, zSql, callback, &data, &zErrMsg);
sqlite_exec_printf(db,
"SELECT name FROM sqlite_master "
"WHERE type='index' AND tbl_name LIKE '%q' "
"ORDER BY name",
callback, &data, &zErrMsg, azArg[1]
);
if( zErrMsg ){
fprintf(stderr,"Error: %s\n", zErrMsg);
free(zErrMsg);
@@ -546,21 +580,37 @@ static void do_meta_command(char *zLine, sqlite *db, struct callback_data *p){
if( c=='s' && strncmp(azArg[0], "schema", n)==0 ){
struct callback_data data;
char *zErrMsg = 0;
char zSql[1000];
memcpy(&data, p, sizeof(data));
data.showHeader = 0;
data.mode = MODE_List;
data.mode = MODE_Semi;
if( nArg>1 ){
sprintf(zSql, "SELECT sql FROM sqlite_master "
"WHERE tbl_name LIKE '%.800s' AND type!='meta'"
"ORDER BY type DESC, name",
azArg[1]);
if( sqliteStrICmp(azArg[1],"sqlite_master")==0 ){
char *new_argv[2], *new_colv[2];
new_argv[0] = "CREATE TABLE sqlite_master (\n"
" type text,\n"
" name text,\n"
" tbl_name text,\n"
" sql text\n"
")";
new_argv[1] = 0;
new_colv[0] = "sql";
new_colv[1] = 0;
callback(&data, 1, new_argv, new_colv);
}else{
sqlite_exec_printf(db,
"SELECT sql FROM sqlite_master "
"WHERE tbl_name LIKE '%q' AND type!='meta'"
"ORDER BY type DESC, name",
callback, &data, &zErrMsg, azArg[1]);
}
}else{
sprintf(zSql, "SELECT sql FROM sqlite_master "
sqlite_exec(db,
"SELECT sql FROM sqlite_master "
"WHERE type!='meta' "
"ORDER BY tbl_name, type DESC, name");
"ORDER BY tbl_name, type DESC, name",
callback, &data, &zErrMsg
);
}
sqlite_exec(db, zSql, callback, &data, &zErrMsg);
if( zErrMsg ){
fprintf(stderr,"Error: %s\n", zErrMsg);
free(zErrMsg);
@@ -572,28 +622,49 @@ static void do_meta_command(char *zLine, sqlite *db, struct callback_data *p){
}else
if( c=='t' && n>1 && strncmp(azArg[0], "tables", n)==0 ){
struct callback_data data;
char *zErrMsg = 0;
char zSql[1000];
memcpy(&data, p, sizeof(data));
data.showHeader = 0;
data.mode = MODE_List;
char **azResult;
int nRow, rc;
char *zErrMsg;
if( nArg==1 ){
sprintf(zSql,
rc = sqlite_get_table(db,
"SELECT name FROM sqlite_master "
"WHERE type='table' "
"ORDER BY name");
"ORDER BY name",
&azResult, &nRow, 0, &zErrMsg
);
}else{
sprintf(zSql,
rc = sqlite_get_table_printf(db,
"SELECT name FROM sqlite_master "
"WHERE type='table' AND name LIKE '%%%.100s%%' "
"ORDER BY name", azArg[1]);
"WHERE type='table' AND name LIKE '%%%q%%' "
"ORDER BY name",
&azResult, &nRow, 0, &zErrMsg, azArg[1]
);
}
sqlite_exec(db, zSql, callback, &data, &zErrMsg);
if( zErrMsg ){
fprintf(stderr,"Error: %s\n", zErrMsg);
free(zErrMsg);
}
if( rc==SQLITE_OK ){
int len, maxlen = 0;
int i, j;
int nPrintCol, nPrintRow;
for(i=1; i<=nRow; i++){
if( azResult[i]==0 ) continue;
len = strlen(azResult[i]);
if( len>maxlen ) maxlen = len;
}
nPrintCol = 80/(maxlen+2);
if( nPrintCol<1 ) nPrintCol = 1;
nPrintRow = (nRow + nPrintCol - 1)/nPrintCol;
for(i=0; i<nPrintRow; i++){
for(j=i+1; j<=nRow; j+=nPrintRow){
char *zSp = j<=nPrintRow ? "" : " ";
printf("%s%-*s", zSp, maxlen, azResult[j] ? azResult[j] : "");
}
printf("\n");
}
}
sqlite_free_table(azResult);
}else
if( c=='t' && n>1 && strncmp(azArg[0], "timeout", n)==0 && nArg>=2 ){
@@ -614,7 +685,6 @@ static void do_meta_command(char *zLine, sqlite *db, struct callback_data *p){
}
int main(int argc, char **argv){
sqlite *db;
char *zErrMsg = 0;
char *argv0 = argv[0];
struct callback_data data;
@@ -623,6 +693,9 @@ int main(int argc, char **argv){
data.mode = MODE_List;
strcpy(data.separator,"|");
data.showHeader = 0;
#ifdef SIGINT
signal(SIGINT, interrupt_handler);
#endif
while( argc>=2 && argv[1][0]=='-' ){
if( strcmp(argv[1],"-html")==0 ){
data.mode = MODE_Html;
@@ -668,7 +741,7 @@ int main(int argc, char **argv){
}
exit(1);
}else{
printf("Database \"%s\" opened READ ONLY!\n", argv[1]);
fprintf(stderr,"Database \"%s\" opened READ ONLY!\n", argv[1]);
}
}
data.out = stdout;
@@ -684,7 +757,9 @@ int main(int argc, char **argv){
int istty = isatty(0);
if( istty ){
printf(
"Enter \".help\" for instructions\n"
"SQLite version %s\n"
"Enter \".help\" for instructions\n",
sqlite_version
);
}
while( (zLine = one_input_line(zSql, istty))!=0 ){
+153 -7
View File
@@ -24,16 +24,23 @@
** This header file defines the interface that the sqlite library
** presents to client programs.
**
** @(#) $Id: sqlite.h.in,v 1.1 2000/08/17 10:22:34 drh Exp $
** @(#) $Id: sqlite.h.in,v 1.7 2000/11/28 20:47:20 drh Exp $
*/
#ifndef _SQLITE_H_
#define _SQLITE_H_
#include <stdarg.h> /* Needed for the definition of va_list */
/*
** The version of the SQLite library
** The version of the SQLite library.
*/
#define SQLITE_VERSION --VERS--
#define SQLITE_VERSION_STRING "--VERS--"
#define SQLITE_VERSION "--VERS--"
/*
** The version string is also compiled into the library so that a program
** can check to make sure that the lib*.a file and the *.h file are from
** the same version.
*/
extern const char sqlite_version[];
/*
** Each open sqlite database is represented by an instance of the
@@ -120,16 +127,26 @@ int sqlite_exec(
);
/*
** Return values fro sqlite_exec()
** Return values for sqlite_exec()
*/
#define SQLITE_OK 0 /* Successful result */
#define SQLITE_INTERNAL 1 /* An internal logic error in SQLite */
#define SQLITE_ERROR 2 /* SQL error or missing database */
#define SQLITE_ERROR 1 /* SQL error or missing database */
#define SQLITE_INTERNAL 2 /* An internal logic error in SQLite */
#define SQLITE_PERM 3 /* Access permission denied */
#define SQLITE_ABORT 4 /* Callback routine requested an abort */
#define SQLITE_BUSY 5 /* One or more database files are locked */
#define SQLITE_NOMEM 6 /* A malloc() failed */
#define SQLITE_READONLY 7 /* Attempt to write a readonly database */
#define SQLITE_INTERRUPT 8 /* Operation terminated by sqlite_interrupt() */
/* This function causes any pending database operation to abort and
** return at its earliest opportunity. This routine is typically
** called in response to a user action such as pressing "Cancel"
** or Ctrl-C where the user wants a long query operation to halt
** immediately.
*/
void sqlite_interrupt(sqlite*);
/* This function returns true if the given input string comprises
** one or more complete SQL statements.
@@ -176,4 +193,133 @@ void sqlite_busy_handler(sqlite*, int(*)(void*,const char*,int), void*);
*/
void sqlite_busy_timeout(sqlite*, int ms);
/*
** This next routine is really just a wrapper around sqlite_exec().
** Instead of invoking a user-supplied callback for each row of the
** result, this routine remembers each row of the result in memory
** obtained from malloc(), then returns all of the result after the
** query has finished.
**
** As an example, suppose the query result where this table:
**
** Name | Age
** -----------------------
** Alice | 43
** Bob | 28
** Cindy | 21
**
** If the 3rd argument were &azResult then after the function returns
** azResult will contain the following data:
**
** azResult[0] = "Name";
** azResult[1] = "Age";
** azResult[2] = "Alice";
** azResult[3] = "43";
** azResult[4] = "Bob";
** azResult[5] = "28";
** azResult[6] = "Cindy";
** azResult[7] = "21";
**
** Notice that there is an extra row of data containing the column
** headers. But the *nrow return value is still 3. *ncolumn is
** set to 2. In general, the number of values inserted into azResult
** will be ((*nrow) + 1)*(*ncolumn).
**
** After the calling function has finished using the result, it should
** pass the result data pointer to sqlite_free_table() in order to
** release the memory that was malloc-ed. Because of the way the
** malloc() happens, the calling function must not try to call
** malloc() directly. Only sqlite_free_table() is able to release
** the memory properly and safely.
**
** The return value of this routine is the same as from sqlite_exec().
*/
int sqlite_get_table(
sqlite*, /* An open database */
char *sql, /* SQL to be executed */
char ***resultp, /* Result written to a char *[] that this points to */
int *nrow, /* Number of result rows written here */
int *ncolumn, /* Number of result columns written here */
char **errmsg /* Error msg written here */
);
/*
** Call this routine to free the memory that sqlite_get_table() allocated.
*/
void sqlite_free_table(char **result);
/*
** The following routines are wrappers around sqlite_exec() and
** sqlite_get_table(). The only difference between the routines that
** follow and the originals is that the second argument to the
** routines that follow is really a printf()-style format
** string describing the SQL to be executed. Arguments to the format
** string appear at the end of the argument list.
**
** All of the usual printf formatting options apply. In addition, there
** is a "%q" option. %q works like %s in that it substitutes a null-terminated
** string from the argument list. But %q also doubles every '\'' character.
** %q is designed for use inside a string literal. By doubling each '\''
** character it escapes that character and allows it to be inserted into
** the string.
**
** For example, so some string variable contains text as follows:
**
** char *zText = "It's a happy day!";
**
** We can use this text in an SQL statement as follows:
**
** sqlite_exec_printf(db, "INSERT INTO table VALUES('%q')",
** callback1, 0, 0, zText);
**
** Because the %q format string is used, the '\'' character in zText
** is escaped and the SQL generated is as follows:
**
** INSERT INTO table1 VALUES('It''s a happy day!')
**
** This is correct. Had we used %s instead of %q, the generated SQL
** would have looked like this:
**
** INSERT INTO table1 VALUES('It's a happy day!');
**
** This second example is an SQL syntax error. As a general rule you
** should always use %q instead of %s when inserting text into a string
** literal.
*/
int sqlite_exec_printf(
sqlite*, /* An open database */
char *sqlFormat, /* printf-style format string for the SQL */
sqlite_callback, /* Callback function */
void *, /* 1st argument to callback function */
char **errmsg, /* Error msg written here */
... /* Arguments to the format string. */
);
int sqlite_exec_vprintf(
sqlite*, /* An open database */
char *sqlFormat, /* printf-style format string for the SQL */
sqlite_callback, /* Callback function */
void *, /* 1st argument to callback function */
char **errmsg, /* Error msg written here */
va_list ap /* Arguments to the format string. */
);
int sqlite_get_table_printf(
sqlite*, /* An open database */
char *sqlFormat, /* printf-style format string for the SQL */
char ***resultp, /* Result written to a char *[] that this points to */
int *nrow, /* Number of result rows written here */
int *ncolumn, /* Number of result columns written here */
char **errmsg, /* Error msg written here */
... /* Arguments to the format string */
);
int sqlite_get_table_vprintf(
sqlite*, /* An open database */
char *sqlFormat, /* printf-style format string for the SQL */
char ***resultp, /* Result written to a char *[] that this points to */
int *nrow, /* Number of result rows written here */
int *ncolumn, /* Number of result columns written here */
char **errmsg, /* Error msg written here */
va_list ap /* Arguments to the format string */
);
#endif /* _SQLITE_H_ */
+16 -11
View File
@@ -23,7 +23,7 @@
*************************************************************************
** Internal interface definitions for SQLite.
**
** @(#) $Id: sqliteInt.h,v 1.29 2000/08/02 13:47:42 drh Exp $
** @(#) $Id: sqliteInt.h,v 1.33 2000/12/10 18:23:51 drh Exp $
*/
#include "sqlite.h"
#include "dbbe.h"
@@ -62,17 +62,18 @@
/*
** The following global variables are used for testing and debugging
** only. Thy only work if MEMORY_DEBUG is defined.
** only. They only work if MEMORY_DEBUG is defined.
*/
#ifdef MEMORY_DEBUG
int sqlite_nMalloc; /* Number of sqliteMalloc() calls */
int sqlite_nFree; /* Number of sqliteFree() calls */
int sqlite_iMallocFail; /* Fail sqliteMalloc() after this many calls */
extern int sqlite_nMalloc; /* Number of sqliteMalloc() calls */
extern int sqlite_nFree; /* Number of sqliteFree() calls */
extern int sqlite_iMallocFail; /* Fail sqliteMalloc() after this many calls */
#endif
/*
** The number of entries in the in-memory hash array holding the
** database schema.
** database schema. (Collision resolution is by chaining, so the
** table will hold more than this many entries.)
*/
#define N_HASH 51
@@ -99,6 +100,8 @@ int sqlite_iMallocFail; /* Fail sqliteMalloc() after this many calls */
#define FN_Sum 4
#define FN_Avg 5
#define FN_Fcnt 6
#define FN_Length 7
#define FN_Substr 8
/*
** Forward references to structures
@@ -121,7 +124,7 @@ typedef struct AggExpr AggExpr;
*/
struct sqlite {
Dbbe *pBe; /* The backend driver */
int flags; /* Miscellanous flags */
int flags; /* Miscellanous flags. See below */
int file_format; /* What file format version is this database? */
int nTable; /* Number of tables in the database */
void *pBusyArg; /* 1st Argument to the busy callback */
@@ -133,8 +136,9 @@ struct sqlite {
/*
** Possible values for the sqlite.flags.
*/
#define SQLITE_VdbeTrace 0x00000001
#define SQLITE_Initialized 0x00000002
#define SQLITE_VdbeTrace 0x00000001 /* True to trace VDBE execution */
#define SQLITE_Initialized 0x00000002 /* True after initialization */
#define SQLITE_Interrupt 0x00000004 /* Cancel current operation */
/*
** Current file format version
@@ -165,7 +169,7 @@ struct Table {
};
/*
** Each SQL index is represented in memory by and
** Each SQL index is represented in memory by an
** instance of the following structure.
**
** The columns of the table that are to be indexed are described
@@ -198,7 +202,7 @@ struct Index {
** this structure.
*/
struct Token {
char *z; /* Text of the token */
char *z; /* Text of the token. Not NULL-terminated! */
int n; /* Number of characters in this token */
};
@@ -323,6 +327,7 @@ struct AggExpr {
*/
struct Parse {
sqlite *db; /* The main database structure */
int rc; /* Return code from execution */
sqlite_callback xCallback; /* The callback function */
void *pArg; /* First argument to the callback function */
char *zErrMsg; /* An error message */
+185
View File
@@ -0,0 +1,185 @@
/*
** Copyright (c) 2000 D. Richard Hipp
**
** This program is free software; you can redistribute it and/or
** modify it under the terms of the GNU General Public
** License as published by the Free Software Foundation; either
** version 2 of the License, or (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
** General Public License for more details.
**
** You should have received a copy of the GNU General Public
** License along with this library; if not, write to the
** Free Software Foundation, Inc., 59 Temple Place - Suite 330,
** Boston, MA 02111-1307, USA.
**
** Author contact information:
** drh@hwaci.com
** http://www.hwaci.com/drh/
**
*************************************************************************
** This file contains the sqlite_get_table() and sqlite_free_table()
** interface routines. These are just wrappers around the main
** interface routine of sqlite_exec().
**
** These routines are in a separate files so that they will not be linked
** if they are not used.
*/
#include <stdlib.h>
#include "sqlite.h"
/*
** This structure is used to pass data from sqlite_get_table() through
** to the callback function is uses to build the result.
*/
typedef struct TabResult {
char **azResult;
int nResult;
int nAlloc;
int nRow;
int nColumn;
int nData;
int rc;
} TabResult;
/*
** This routine is called once for each row in the result table. Its job
** is to fill in the TabResult structure appropriately, allocating new
** memory as necessary.
*/
static int sqlite_get_table_cb(void *pArg, int nCol, char **argv, char **colv){
TabResult *p = (TabResult*)pArg;
int need;
int i;
char *z;
/* Make sure there is enough space in p->azResult to hold everything
** we need to remember from this invocation of the callback.
*/
if( p->nRow==0 ){
p->nColumn = nCol;
need = nCol*2;
}else{
need = nCol;
}
if( p->nData + need >= p->nAlloc ){
p->nAlloc = p->nAlloc*2 + need + 1;
p->azResult = realloc( p->azResult, sizeof(char*)*p->nAlloc );
if( p->azResult==0 ){
p->rc = SQLITE_NOMEM;
return 1;
}
}
/* If this is the first row, then generate an extra row containing
** the names of all columns.
*/
if( p->nRow==0 ){
for(i=0; i<nCol; i++){
if( colv[i]==0 ){
z = 0;
}else{
z = malloc( strlen(colv[i])+1 );
if( z==0 ){
p->rc = SQLITE_NOMEM;
return 1;
}
strcpy(z, colv[i]);
}
p->azResult[p->nData++] = z;
}
}
/* Copy over the row data
*/
for(i=0; i<nCol; i++){
if( argv[i]==0 ){
z = 0;
}else{
z = malloc( strlen(argv[i])+1 );
if( z==0 ){
p->rc = SQLITE_NOMEM;
return 1;
}
strcpy(z, argv[i]);
}
p->azResult[p->nData++] = z;
}
p->nRow++;
return 0;
}
/*
** Query the database. But instead of invoking a callback for each row,
** malloc() for space to hold the result and return the entire results
** at the conclusion of the call.
**
** The result that is written to ***pazResult is held in memory obtained
** from malloc(). But the caller cannot free this memory directly.
** Instead, the entire table should be passed to sqlite_free_table() when
** the calling procedure is finished using it.
*/
int sqlite_get_table(
sqlite *db, /* The database on which the SQL executes */
char *zSql, /* The SQL to be executed */
char ***pazResult, /* Write the result table here */
int *pnRow, /* Write the number of rows in the result here */
int *pnColumn, /* Write the number of columns of result here */
char **pzErrMsg /* Write error messages here */
){
int rc;
TabResult res;
if( pazResult==0 ){ return SQLITE_ERROR; }
*pazResult = 0;
if( pnColumn ) *pnColumn = 0;
if( pnRow ) *pnRow = 0;
res.nResult = 0;
res.nRow = 0;
res.nColumn = 0;
res.nData = 1;
res.nAlloc = 200;
res.rc = SQLITE_OK;
res.azResult = malloc( sizeof(char*)*res.nAlloc );
if( res.azResult==0 ){
return SQLITE_NOMEM;
}
res.azResult[0] = 0;
rc = sqlite_exec(db, zSql, sqlite_get_table_cb, &res, pzErrMsg);
if( res.azResult ){
res.azResult[0] = (char*)res.nData;
}
if( rc==SQLITE_ABORT ){
sqlite_free_table(&res.azResult[1]);
return res.rc;
}
if( rc!=SQLITE_OK ){
sqlite_free_table(&res.azResult[1]);
return rc;
}
if( res.nAlloc>res.nData ){
res.azResult = realloc( res.azResult, sizeof(char*)*(res.nData+1) );
if( res.azResult==0 ) return SQLITE_NOMEM;
}
*pazResult = &res.azResult[1];
if( pnColumn ) *pnColumn = res.nColumn;
if( pnRow ) *pnRow = res.nRow;
return rc;
}
/*
** This routine frees the space the sqlite_get_table() malloced.
*/
void sqlite_free_table(
char **azResult /* Result returned from from sqlite_get_table() */
){
if( azResult ){
int i, n;
azResult--;
n = (int)azResult[0];
for(i=1; i<n; i++){ if( azResult[i] ) free(azResult[i]); }
free(azResult);
}
}
+110 -62
View File
@@ -23,8 +23,10 @@
*************************************************************************
** A TCL Interface to SQLite
**
** $Id: tclsqlite.c,v 1.8 2000/08/17 09:50:00 drh Exp $
** $Id: tclsqlite.c,v 1.12 2000/10/19 14:59:27 drh Exp $
*/
#ifndef NO_TCL /* Omit this whole file if TCL is unavailable */
#include "sqlite.h"
#include <tcl.h>
#include <stdlib.h>
@@ -38,7 +40,7 @@ typedef struct SqliteDb SqliteDb;
struct SqliteDb {
sqlite *db; /* The "real" database structure */
Tcl_Interp *interp; /* The interpreter used for this database */
char *zBusy; /* The name of the busy callback routine */
char *zBusy; /* The busy callback routine */
};
/*
@@ -49,7 +51,7 @@ typedef struct CallbackData CallbackData;
struct CallbackData {
Tcl_Interp *interp; /* The TCL interpreter */
char *zArray; /* The array into which data is written */
char *zCode; /* The code to execute for each row */
Tcl_Obj *pCode; /* The code to execute for each row */
int once; /* Set only for the first invocation of callback */
};
@@ -66,6 +68,7 @@ static int DbEvalCallback(
int i, rc;
if( cbData->zArray[0] ){
if( cbData->once ){
Tcl_SetVar2(cbData->interp, cbData->zArray, "*", "", 0);
for(i=0; i<nCol; i++){
Tcl_SetVar2(cbData->interp, cbData->zArray, "*", azN[i],
TCL_LIST_ELEMENT|TCL_APPEND_VALUE);
@@ -84,8 +87,34 @@ static int DbEvalCallback(
}
}
cbData->once = 0;
rc = Tcl_Eval(cbData->interp, cbData->zCode);
return rc;
rc = Tcl_EvalObj(cbData->interp, cbData->pCode);
return rc!=TCL_OK && rc!=TCL_CONTINUE;
}
/*
** This is an alternative callback for database queries. Instead
** of invoking a TCL script to handle the result, this callback just
** appends each column of the result to a list. After the query
** is complete, the list is returned.
*/
static int DbEvalCallback2(
void *clientData, /* An instance of CallbackData */
int nCol, /* Number of columns in the result */
char ** azCol, /* Data for each column */
char ** azN /* Name for each column */
){
Tcl_Obj *pList = (Tcl_Obj*)clientData;
int i;
for(i=0; i<nCol; i++){
Tcl_Obj *pElem;
if( azCol[i] && *azCol[i] ){
pElem = Tcl_NewStringObj(azCol[i], -1);
}else{
pElem = Tcl_NewObj();
}
Tcl_ListObjAppendElement(0, pList, pElem);
}
return 0;
}
/*
@@ -109,7 +138,6 @@ static int DbBusyHandler(void *cd, const char *zTable, int nTries){
int rc;
char zVal[30];
char *zCmd;
char *zResult;
Tcl_DString cmd;
Tcl_DStringInit(&cmd);
@@ -139,58 +167,70 @@ static int DbBusyHandler(void *cd, const char *zTable, int nTries){
** and calls that connection "db1". The second command causes this
** subroutine to be invoked.
*/
static int DbCmd(void *cd, Tcl_Interp *interp, int argc, char **argv){
char *z;
int n, c;
static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
SqliteDb *pDb = (SqliteDb*)cd;
if( argc<2 ){
Tcl_AppendResult(interp,"wrong # args: should be \"", argv[0],
" SUBCOMMAND ...\"", 0);
int choice;
static char *DB_optStrs[] = {
"busy", "close", "complete", "eval", "timeout"
};
enum DB_opts {
DB_BUSY, DB_CLOSE, DB_COMPLETE, DB_EVAL, DB_TIMEOUT
};
if( objc<2 ){
Tcl_WrongNumArgs(interp, 1, objv, "SUBCOMMAND ...");
return TCL_ERROR;
}
z = argv[1];
n = strlen(z);
c = z[0];
if( Tcl_GetIndexFromObj(interp, objv[1], DB_optStrs, "option", 0, &choice) ){
return TCL_ERROR;
}
switch( (enum DB_opts)choice ){
/* $db busy ?CALLBACK?
**
** Invoke the given callback if an SQL statement attempts to open
** a locked database file.
*/
if( c=='b' && strncmp(z,"busy",n)==0 ){
if( argc>3 ){
Tcl_AppendResult(interp,"wrong # args: should be \"",
argv[0], " busy ?CALLBACK?", 0);
case DB_BUSY: {
if( objc>3 ){
Tcl_WrongNumArgs(interp, 2, objv, "CALLBACK");
return TCL_ERROR;
}else if( argc==2 ){
}else if( objc==2 ){
if( pDb->zBusy ){
Tcl_AppendResult(interp, pDb->zBusy, 0);
}
}else{
char *zBusy;
int len;
if( pDb->zBusy ){
Tcl_Free(pDb->zBusy);
pDb->zBusy = 0;
}
if( argv[2][0] ){
pDb->zBusy = Tcl_Alloc( strlen(argv[2]) + 1 );
if( pDb->zBusy ){
strcpy(pDb->zBusy, argv[2]);
}
zBusy = Tcl_GetStringFromObj(objv[2], &len);
if( zBusy && len>0 ){
pDb->zBusy = Tcl_Alloc( len + 1 );
strcpy(pDb->zBusy, zBusy);
}else{
pDb->zBusy = 0;
}
if( pDb->zBusy ){
pDb->interp = interp;
sqlite_busy_handler(pDb->db, DbBusyHandler, pDb);
}else{
sqlite_busy_handler(pDb->db, 0, 0);
}
}
}else
break;
}
/* $db close
**
** Shutdown the database
*/
if( c=='c' && n>=2 && strncmp(z,"close",n)==0 ){
Tcl_DeleteCommand(interp, argv[0]);
}else
case DB_CLOSE: {
Tcl_DeleteCommand(interp, Tcl_GetStringFromObj(objv[0], 0));
break;
}
/* $db complete SQL
**
@@ -198,16 +238,18 @@ static int DbCmd(void *cd, Tcl_Interp *interp, int argc, char **argv){
** additional lines of input are needed. This is similar to the
** built-in "info complete" command of Tcl.
*/
if( c=='c' && n>=2 && strncmp(z,"complete",n)==0 ){
char *zRes;
if( argc!=3 ){
Tcl_AppendResult(interp,"wrong # args: should be \"", argv[0],
" complete SQL\"", 0);
case DB_COMPLETE: {
Tcl_Obj *pResult;
int isComplete;
if( objc!=3 ){
Tcl_WrongNumArgs(interp, 2, objv, "SQL");
return TCL_ERROR;
}
zRes = sqlite_complete(argv[2]) ? "1" : "0";
Tcl_SetResult(interp, zRes, TCL_VOLATILE);
}else
isComplete = sqlite_complete( Tcl_GetStringFromObj(objv[2], 0) );
pResult = Tcl_GetObjResult(interp);
Tcl_SetBooleanObj(pResult, isComplete);
break;
}
/*
** $db eval $sql ?array { ...code... }?
@@ -218,57 +260,59 @@ static int DbCmd(void *cd, Tcl_Interp *interp, int argc, char **argv){
** If "array" is an empty string, then the values are placed in variables
** that have the same name as the fields extracted by the query.
*/
if( c=='e' && strncmp(z,"eval",n)==0 ){
case DB_EVAL: {
CallbackData cbData;
char *zErrMsg;
char *zSql;
int rc;
if( argc!=5 && argc!=3 ){
Tcl_AppendResult(interp,"wrong # args: should be \"", argv[0],
" eval SQL ?ARRAY-NAME CODE?", 0);
if( objc!=5 && objc!=3 ){
Tcl_WrongNumArgs(interp, 2, objv, "SQL ?ARRAY-NAME CODE?");
return TCL_ERROR;
}
pDb->interp = interp;
if( argc==5 ){
zSql = Tcl_GetStringFromObj(objv[2], 0);
Tcl_IncrRefCount(objv[2]);
if( objc==5 ){
cbData.interp = interp;
cbData.once = 1;
cbData.zArray = argv[3];
cbData.zCode = argv[4];
cbData.zArray = Tcl_GetStringFromObj(objv[3], 0);
cbData.pCode = objv[4];
zErrMsg = 0;
rc = sqlite_exec(pDb->db, argv[2], DbEvalCallback, &cbData, &zErrMsg);
Tcl_IncrRefCount(objv[3]);
Tcl_IncrRefCount(objv[4]);
rc = sqlite_exec(pDb->db, zSql, DbEvalCallback, &cbData, &zErrMsg);
Tcl_DecrRefCount(objv[4]);
Tcl_DecrRefCount(objv[3]);
}else{
rc = sqlite_exec(pDb->db, argv[2], 0, 0, &zErrMsg);
Tcl_Obj *pList = Tcl_NewObj();
rc = sqlite_exec(pDb->db, zSql, DbEvalCallback2, pList, &zErrMsg);
Tcl_SetObjResult(interp, pList);
}
if( zErrMsg ){
Tcl_SetResult(interp, zErrMsg, TCL_VOLATILE);
free(zErrMsg);
}
Tcl_DecrRefCount(objv[2]);
return rc;
}else
}
/*
** $db timeout MILLESECONDS
**
** Delay for the number of milliseconds specified when a file is locked.
*/
if( c=='t' && strncmp(z,"timeout",n)==0 ){
case DB_TIMEOUT: {
int ms;
if( argc!=3 ){
Tcl_AppendResult(interp,"wrong # args: should be \"", argv[0],
" timeout MILLISECONDS", 0);
if( objc!=3 ){
Tcl_WrongNumArgs(interp, 2, objv, "MILLISECONDS");
return TCL_ERROR;
}
if( Tcl_GetInt(interp, argv[2], &ms) ) return TCL_ERROR;
if( Tcl_GetIntFromObj(interp, objv[2], &ms) ) return TCL_ERROR;
sqlite_busy_timeout(pDb->db, ms);
}else
/* The default
*/
{
Tcl_AppendResult(interp,"unknown subcommand \"", z,
"\" - should be one of: close complete eval", 0);
return TCL_ERROR;
break;
}
} /* End of the SWITCH statement */
return TCL_OK;
}
@@ -314,7 +358,7 @@ static int DbMain(void *cd, Tcl_Interp *interp, int argc, char **argv){
free(zErrMsg);
return TCL_ERROR;
}
Tcl_CreateCommand(interp, argv[1], DbCmd, (char*)p, DbDeleteCmd);
Tcl_CreateObjCommand(interp, argv[1], DbObjCmd, (char*)p, DbDeleteCmd);
return TCL_OK;
}
@@ -336,6 +380,7 @@ int Sqlite_SafeInit(Tcl_Interp *interp){
return TCL_OK;
}
#if 0
/*
** If compiled using mktclapp, this routine runs to initialize
** everything.
@@ -343,6 +388,7 @@ int Sqlite_SafeInit(Tcl_Interp *interp){
int Et_AppInit(Tcl_Interp *interp){
return Sqlite_Init(interp);
}
#endif
/*
** If the macro TCLSH is defined and is one, then put in code for the
@@ -397,3 +443,5 @@ int TCLSH_MAIN(int argc, char **argv){
return 0;
}
#endif /* TCLSH */
#endif /* !defined(NO_TCL) */
+17 -4
View File
@@ -27,7 +27,7 @@
** individual tokens and sends those tokens one-by-one over to the
** parser for analysis.
**
** $Id: tokenize.c,v 1.13 2000/08/09 17:17:25 drh Exp $
** $Id: tokenize.c,v 1.16 2000/12/10 18:23:51 drh Exp $
*/
#include "sqliteInt.h"
#include <ctype.h>
@@ -102,7 +102,7 @@ static Keyword aKeywordTable[] = {
/*
** This is the hash table
*/
#define KEY_HASH_SIZE 37
#define KEY_HASH_SIZE 69
static Keyword *apHashTable[KEY_HASH_SIZE];
@@ -298,7 +298,7 @@ int sqliteGetToken(const char *z, int *tokenType){
/*
** Run the parser on the given SQL string. The parser structure is
** passed in. Return the number of errors.
** passed in. An SQLITE_ status code.
*/
int sqliteRunParser(Parse *pParse, char *zSql, char **pzErrMsg){
int nErr = 0;
@@ -311,6 +311,8 @@ int sqliteRunParser(Parse *pParse, char *zSql, char **pzErrMsg){
extern int sqliteParser(void*, int, ...);
extern void sqliteParserTrace(FILE*, char *);
pParse->db->flags &= ~SQLITE_Interrupt;
pParse->rc = SQLITE_OK;
i = 0;
sqliteParseInfoReset(pParse);
pEngine = sqliteParserAlloc((void*(*)(int))malloc);
@@ -318,10 +320,17 @@ int sqliteRunParser(Parse *pParse, char *zSql, char **pzErrMsg){
sqliteSetString(pzErrMsg, "out of memory", 0);
return 1;
}
#ifndef NDEBUG
sqliteParserTrace(trace, "parser: ");
#endif
while( nErr==0 && i>=0 && zSql[i]!=0 ){
int tokenType;
if( (pParse->db->flags & SQLITE_Interrupt)!=0 ){
pParse->rc = SQLITE_INTERRUPT;
sqliteSetString(pzErrMsg, "interrupt", 0);
break;
}
pParse->sLastToken.z = &zSql[i];
pParse->sLastToken.n = sqliteGetToken(&zSql[i], &tokenType);
i += pParse->sLastToken.n;
@@ -391,7 +400,7 @@ int sqliteRunParser(Parse *pParse, char *zSql, char **pzErrMsg){
break;
}
}
if( nErr==0 ){
if( nErr==0 && (pParse->db->flags & SQLITE_Interrupt)==0 ){
sqliteParser(pEngine, 0, pParse->sLastToken, pParse);
if( pParse->zErrMsg && pParse->sErrToken.z ){
sqliteSetNString(pzErrMsg, "near \"", -1,
@@ -423,5 +432,9 @@ int sqliteRunParser(Parse *pParse, char *zSql, char **pzErrMsg){
pParse->pNewTable = 0;
}
sqliteParseInfoReset(pParse);
sqliteStrRealloc(pzErrMsg);
if( nErr>0 && pParse->rc==SQLITE_OK ){
pParse->rc = SQLITE_ERROR;
}
return nErr;
}
+97 -9
View File
@@ -26,14 +26,26 @@
** This file contains functions for allocating memory, comparing
** strings, and stuff like that.
**
** $Id: util.c,v 1.14 2000/07/31 11:57:37 drh Exp $
** $Id: util.c,v 1.17 2000/12/10 18:23:51 drh Exp $
*/
#include "sqliteInt.h"
#include <stdarg.h>
#include <ctype.h>
/*
** If MEMORY_DEBUG is defined, then use versions of malloc() and
** free() that track memory usage and check for buffer overruns.
*/
#ifdef MEMORY_DEBUG
/*
** For keeping track of the number of mallocs and frees. This
** is used to check for memory leaks.
*/
int sqlite_nMalloc; /* Number of sqliteMalloc() calls */
int sqlite_nFree; /* Number of sqliteFree() calls */
int sqlite_iMallocFail; /* Fail sqliteMalloc() after this many calls */
/*
** Allocate new memory and set it to zero. Return NULL if
@@ -164,9 +176,14 @@ char *sqliteStrNDup_(const char *z, int n, char *zFile, int line){
}
return zNew;
}
#endif /* MEMORY_DEBUG */
/*
** The following versions of malloc() and free() are for use in a
** normal build.
*/
#if !defined(MEMORY_DEBUG)
#else /* !defined(MEMORY_DEBUG) */
/*
** Allocate new memory and set it to zero. Return NULL if
** no memory is available.
@@ -219,7 +236,7 @@ char *sqliteStrNDup(const char *z, int n){
}
return zNew;
}
#endif /* MEMORY_DEBUG */
#endif /* !defined(MEMORY_DEBUG) */
/*
** Create a string from the 2nd and subsequent arguments (up to the
@@ -353,7 +370,7 @@ int sqliteHashNoCase(const char *z, int n){
int c;
if( n<=0 ) n = strlen(z);
while( n-- > 0 && (c = *z++)!=0 ){
h = h<<3 ^ h ^ UpperToLower[c];
h = (h<<3) ^ h ^ UpperToLower[c];
}
if( h<0 ) h = -h;
return h;
@@ -550,6 +567,41 @@ static int privateStrCmp(const char *atext, const char *btext, int useCase){
return result;
}
/*
** Do a comparison of pure numerics. If either string is not a pure
** numeric, then return 0. Otherwise return 1 and set *pResult to be
** negative, zero or positive if the first string are numerially less than
** equal to, or greater than the second.
*/
static int privateCompareNum(const char *a, const char *b, int *pResult){
char *endPtr;
double rA, rB;
int isNumA, isNumB;
if( isdigit(*a) || ((*a=='-' || *a=='+') && isdigit(a[1])) ){
rA = strtod(a, &endPtr);
isNumA = *endPtr==0;
}else{
isNumA = 0;
}
if( isdigit(*b) || ((*b=='-' || *b=='+') && isdigit(b[1])) ){
rB = strtod(b, &endPtr);
isNumB = *endPtr==0;
}else{
isNumB = 0;
}
if( isNumB==0 && isNumA==0 ) return 0;
if( isNumA!=isNumB ){
*pResult = isNumA - isNumB;
}else if( rA<rB ){
*pResult = -1;
}else if( rA>rB ){
*pResult = 1;
}else{
*pResult = 0;
}
return 1;
}
/* This comparison routine is what we use for comparison operations
** in an SQL expression. (Ex: name<'Hello' or value<5). Compare two
** strings. Use case only as a tie-breaker. Numbers compare in
@@ -557,8 +609,10 @@ static int privateStrCmp(const char *atext, const char *btext, int useCase){
*/
int sqliteCompare(const char *atext, const char *btext){
int result;
result = privateStrCmp(atext, btext, 0);
if( result==0 ) result = privateStrCmp(atext, btext, 1);
if( !privateCompareNum(atext, btext, &result) || result==0 ){
result = privateStrCmp(atext, btext, 0);
if( result==0 ) result = privateStrCmp(atext, btext, 1);
}
return result;
}
@@ -573,12 +627,13 @@ int sortCmp(const char **a, const char **b){
return sqliteCompare(*a, *b);
}
int main(int argc, char **argv){
int i, j, k, n;
int i, j, k, n, cnt;
static char *azStr[] = {
"abc", "aBc", "abcd", "aBcd",
"123", "124", "1234", "-123", "-124", "-1234",
"123", "124", "1234", "-123", "-124", "-1234", "+124",
"123.45", "123.456", "123.46", "-123.45", "-123.46", "-123.456",
"x9", "x10", "x-9", "x-10", "X9", "X10",
"1.234e+02", "+123", "1.23E2", "1.2345e+2", "-1.2345e2", "+w"
};
n = sizeof(azStr)/sizeof(azStr[0]);
qsort(azStr, n, sizeof(azStr[0]), sortCmp);
@@ -587,6 +642,7 @@ int main(int argc, char **argv){
}
printf("Sanity1...");
fflush(stdout);
cnt = 0;
for(i=0; i<n-1; i++){
char *a = azStr[i];
for(j=i+1; j<n; j++){
@@ -595,10 +651,42 @@ int main(int argc, char **argv){
printf("Failed! \"%s\" vs \"%s\"\n", a, b);
i = j = n;
}
cnt++;
}
}
if( i<n ){
printf(" OK\n");
printf(" OK (%d)\n", cnt);
}
printf("Sanity2...");
fflush(stdout);
cnt = 0;
for(i=0; i<n; i++){
char *a = azStr[i];
for(j=0; j<n; j++){
char *b = azStr[j];
for(k=0; k<n; k++){
char *c = azStr[k];
int x1, x2, x3, success;
x1 = sqliteCompare(a,b);
x2 = sqliteCompare(b,c);
x3 = sqliteCompare(a,c);
if( x1==0 ){
success = x2==x3;
}else if( x1<0 ){
success = (x2<=0 && x3<=0) || x2>0;
}else{
success = (x2>=0 && x3>=0) || x2<0;
}
if( !success ){
printf("Failed! \"%s\" vs \"%s\" vs \"%s\"\n", a, b, c);
i = j = k = n+1;
}
cnt++;
}
}
}
if( i<n+1 ){
printf(" OK (%d)\n", cnt);
}
return 0;
}
+535 -378
View File
File diff suppressed because it is too large Load Diff
+7 -4
View File
@@ -27,7 +27,7 @@
** or VDBE. The VDBE implements an abstract machine that runs a
** simple program to access and modify the underlying database.
**
** $Id: vdbe.h,v 1.11 2000/07/28 14:32:50 drh Exp $
** $Id: vdbe.h,v 1.14 2000/10/16 22:06:43 drh Exp $
*/
#ifndef _SQLITE_VDBE_H_
#define _SQLITE_VDBE_H_
@@ -173,13 +173,16 @@ typedef struct VdbeOp VdbeOp;
#define OP_Concat 87
#define OP_Noop 88
#define OP_MAX 88
#define OP_Strlen 89
#define OP_Substr 90
#define OP_MAX 90
/*
** Prototypes for the VDBE interface. See comments on the implementation
** for a description of what each of these routines does.
*/
Vdbe *sqliteVdbeCreate(Dbbe*);
Vdbe *sqliteVdbeCreate(sqlite*);
int sqliteVdbeAddOp(Vdbe*,int,int,int,const char*,int);
int sqliteVdbeAddOpList(Vdbe*, int nOp, VdbeOp const *aOp);
void sqliteVdbeChangeP3(Vdbe*, int addr, const char *zP1, int N);
@@ -193,6 +196,6 @@ int sqliteVdbeList(Vdbe*,sqlite_callback,void*,char**);
void sqliteVdbeResolveLabel(Vdbe*, int);
int sqliteVdbeCurrentAddr(Vdbe*);
void sqliteVdbeTrace(Vdbe*,FILE*);
void sqliteVdbeCompressSpace(Vdbe*,int);
#endif
+2 -2
View File
@@ -25,7 +25,7 @@
** the WHERE clause of SQL statements. Also found here are subroutines
** to generate VDBE code to evaluate expressions.
**
** $Id: where.c,v 1.8 2000/06/21 13:59:13 drh Exp $
** $Id: where.c,v 1.9 2000/08/22 18:29:34 drh Exp $
*/
#include "sqliteInt.h"
@@ -324,7 +324,7 @@ WhereInfo *sqliteWhereBegin(
if( i==pTabList->nId-1 && pushKey ){
haveKey = 1;
}else{
sqliteVdbeAddOp(v, OP_Fetch, idx, 0, 0, 0);
sqliteVdbeAddOp(v, OP_Fetch, base+idx, 0, 0, 0);
haveKey = 0;
}
}
+43 -6
View File
@@ -22,24 +22,61 @@
#***********************************************************************
# This file runs all tests.
#
# $Id: all.test,v 1.2 2000/06/02 14:27:23 drh Exp $
# $Id: all.test,v 1.5 2000/12/10 18:23:51 drh Exp $
set testdir [file dirname $argv0]
source $testdir/tester.tcl
rename finish_test really_finish_test
proc finish_test {} {}
proc finish_test {} {memleak_check}
if {[file exists ./sqlite_test_count]} {
set COUNT [exec cat ./sqlite_test_count]
} else {
set COUNT 1
set COUNT 4
}
if {[file exists ./sqlite_test_prefixes]} {
set PREFIXES [exec cat ./sqlite_test_prefixes]
} else {
set PREFIXES {memory: gdbm:}
}
# LeakList will hold a list of the number of unfreed mallocs after
# each round of the test. This number should be constant. If it
# grows, it may mean there is a memory leak in the library.
#
set LeakList {}
for {set Counter 0} {$Counter<$COUNT} {incr Counter} {
foreach testfile [lsort -dictionary [glob $testdir/*.test]] {
if {[file tail $testfile]=="all.test"} continue
source $testfile
foreach p $PREFIXES {
set dbprefix $p
foreach testfile [lsort -dictionary [glob $testdir/*.test]] {
if {[file tail $testfile]=="all.test"} continue
source $testfile
}
}
if {[info exists Leak]} {
lappend LeakList $Leak
}
}
# Do one last test to look for a memory leak in the library. This will
# only work if SQLite is compiled with the -DMEMORY_DEBUG=1 flag.
#
if {$LeakList!=""} {
puts -nonewline memory-leak-test...
incr ::nTest
foreach x $LeakList {
if {$x!=[lindex $LeakList 0]} {
puts " failed!"
puts "Expected: all values to be the same"
puts " Got: $LeakList"
incr ::nErr
break
}
}
puts " Ok"
}
really_finish_test
+2 -2
View File
@@ -23,7 +23,7 @@
# This file implements regression tests for SQLite library. The
# focus of this file is exercising the code in dbbe.c.
#
# $Id: dbbe.test,v 1.3 2000/08/17 09:50:00 drh Exp $
# $Id: dbbe.test,v 1.4 2000/10/16 22:06:43 drh Exp $
set testdir [file dirname $argv0]
source $testdir/tester.tcl
@@ -130,7 +130,7 @@ do_test dbbe-3.1 {
sqlite db testdb 0444
set v [catch {execsql {INSERT INTO t1 VALUES(1)}} msg]
lappend v $msg
} {1 {table t1 is readonly}}
} {7 {table t1 is readonly}}
finish_test
+3 -3
View File
@@ -23,7 +23,7 @@
# This file implements regression tests for SQLite library. The
# focus of this file is testing expressions.
#
# $Id: expr.test,v 1.8 2000/06/16 20:51:26 drh Exp $
# $Id: expr.test,v 1.9 2000/09/14 01:21:11 drh Exp $
set testdir [file dirname $argv0]
source $testdir/tester.tcl
@@ -80,8 +80,8 @@ test_expr expr-1.37 {i1=1, i2=NULL} {not i2} {1}
test_expr expr-2.1 {r1=1.23, r2=2.34} {r1+r2} 3.57
test_expr expr-2.2 {r1=1.23, r2=2.34} {r1-r2} -1.11
test_expr expr-2.3 {r1=1.23, r2=2.34} {r1*r2} 2.8782
test_expr expr-2.4 {r1=1.23, r2=2.34} {r1/r2} 0.525641
test_expr expr-2.5 {r1=1.23, r2=2.34} {r2/r1} 1.90244
test_expr expr-2.4 {r1=1.23, r2=2.34} {r1/r2} 0.525641025641026
test_expr expr-2.5 {r1=1.23, r2=2.34} {r2/r1} 1.90243902439024
test_expr expr-2.6 {r1=1.23, r2=2.34} {r2<r1} 0
test_expr expr-2.7 {r1=1.23, r2=2.34} {r2<=r1} 0
test_expr expr-2.8 {r1=1.23, r2=2.34} {r2>r1} 1
+92
View File
@@ -0,0 +1,92 @@
# Copyright (c) 1999, 2000 D. Richard Hipp
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public
# License along with this library; if not, write to the
# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
# Boston, MA 02111-1307, USA.
#
# Author contact information:
# drh@hwaci.com
# http://www.hwaci.com/drh/
#
#***********************************************************************
# This file implements regression tests for SQLite library. The
# focus of this file is testing built-in functions.
#
# $Id: func.test,v 1.1 2000/08/28 16:22:00 drh Exp $
set testdir [file dirname $argv0]
source $testdir/tester.tcl
# Create a table to work with.
#
execsql {CREATE TABLE tbl1(t1 text)}
foreach word {this program is free software} {
execsql "INSERT INTO tbl1 VALUES('$word')"
}
# Make sure the table was created properly.
#
do_test func-0.0 {
execsql {SELECT t1 FROM tbl1 ORDER BY t1}
} {free is program software this}
# Check out the length() function
#
do_test func-1.0 {
execsql {SELECT length(t1) FROM tbl1 ORDER BY t1}
} {4 2 7 8 4}
do_test func-1.1 {
set r [catch {execsql {SELECT length(*) FROM tbl1 ORDER BY t1}} msg]
lappend r $msg
} {1 {too few arguments to function length()}}
do_test func-1.2 {
set r [catch {execsql {SELECT length(t1,5) FROM tbl1 ORDER BY t1}} msg]
lappend r $msg
} {1 {too many arguments to function length()}}
do_test func-1.3 {
execsql {SELECT length(t1), count(*) FROM tbl1 GROUP BY length(t1)
ORDER BY length(t1)}
} {2 1 4 2 7 1 8 1}
# Check out the substr() function
#
do_test func-2.0 {
execsql {SELECT substr(t1,1,2) FROM tbl1 ORDER BY t1}
} {fr is pr so th}
do_test func-2.1 {
execsql {SELECT substr(t1,2,1) FROM tbl1 ORDER BY t1}
} {r s r o h}
do_test func-2.2 {
execsql {SELECT substr(t1,3,3) FROM tbl1 ORDER BY t1}
} {ee {} ogr ftw is}
do_test func-2.3 {
execsql {SELECT substr(t1,-1,1) FROM tbl1 ORDER BY t1}
} {e s m e s}
do_test func-2.4 {
execsql {SELECT substr(t1,-1,2) FROM tbl1 ORDER BY t1}
} {e s m e s}
do_test func-2.5 {
execsql {SELECT substr(t1,-2,1) FROM tbl1 ORDER BY t1}
} {e i a r i}
do_test func-2.6 {
execsql {SELECT substr(t1,-2,2) FROM tbl1 ORDER BY t1}
} {ee is am re is}
do_test func-2.7 {
execsql {SELECT substr(t1,-4,2) FROM tbl1 ORDER BY t1}
} {fr {} gr wa th}
do_test func-2.8 {
execsql {SELECT t1 FROM tbl1 ORDER BY substr(t1,2,20)}
} {this software free program is}
finish_test
+8 -1
View File
@@ -23,7 +23,7 @@
# This file implements regression tests for SQLite library. The
# focus of this file is testing the CREATE INDEX statement.
#
# $Id: index.test,v 1.7 2000/08/02 13:47:42 drh Exp $
# $Id: index.test,v 1.8 2000/10/19 14:10:09 drh Exp $
set testdir [file dirname $argv0]
source $testdir/tester.tcl
@@ -39,12 +39,14 @@ do_test index-1.1b {
execsql {SELECT name, sql, tbl_name, type FROM sqlite_master
WHERE name='index1'}
} {index1 {CREATE INDEX index1 ON test1(f1)} test1 index}
skipif memory:
do_test index-1.1c {
db close
sqlite db testdb
execsql {SELECT name, sql, tbl_name, type FROM sqlite_master
WHERE name='index1'}
} {index1 {CREATE INDEX index1 ON test1(f1)} test1 index}
skipif memory:
do_test index-1.1d {
db close
sqlite db testdb
@@ -106,6 +108,7 @@ set r {}
for {set i 1} {$i<100} {incr i} {
lappend r testdb/index$i.tbl
}
skipif memory:
do_test index-3.2 {
execsql {INSERT INTO test1 VALUES(1,2,3,4,5)}
lsort -dictionary [glob testdb/index*.tbl]
@@ -223,6 +226,10 @@ do_test index-7.1 {
for {set i 1} {$i<20} {incr i} {
execsql "INSERT INTO test1 VALUES($i,[expr {int(pow(2,$i))}])"
}
execsql {SELECT count(*) FROM test1}
} {19}
skipif memory:
do_test index-7.1b {
lsort -dictionary [glob testdb/test1*.tbl]
} {testdb/test1.tbl testdb/test1__primary_key.tbl}
do_test index-7.2 {
+6 -2
View File
@@ -23,7 +23,9 @@
# This file implements regression tests for SQLite library. The
# focus of this script is database locks.
#
# $Id: lock.test,v 1.2 2000/08/04 13:51:11 drh Exp $
# $Id: lock.test,v 1.4 2000/10/19 14:10:09 drh Exp $
if {$dbprefix=="gdbm:"} {
set testdir [file dirname $argv0]
source $testdir/tester.tcl
@@ -59,7 +61,7 @@ do_test lock-1.2 {
#
set v [catch {execsql {UPDATE big SET f2='xyz' WHERE f1=11}} msg]
lappend v $msg
} {1 {table big is locked}}
} {5 {table big is locked}}
do_test lock-1.3 {
# Try to update the database in a separate process
@@ -99,3 +101,5 @@ catch {exec kill -HUP $::lock_pid}
catch {exec kill -9 $::lock_pid}
finish_test
}
+29 -1
View File
@@ -23,7 +23,7 @@
# This file implements regression tests for SQLite library. The
# focus of this file is exercising the code in main.c.
#
# $Id: main.test,v 1.3 2000/06/16 20:51:26 drh Exp $
# $Id: main.test,v 1.4 2000/12/10 18:23:52 drh Exp $
set testdir [file dirname $argv0]
source $testdir/tester.tcl
@@ -48,6 +48,34 @@ do_test main-1.4 {
;
}
} {1}
do_test main-1.5 {
db complete {DROP TABLE 'xyz;}
} {0}
do_test main-1.6 {
db complete {DROP TABLE 'xyz';}
} {1}
do_test main-1.7 {
db complete {DROP TABLE "xyz;}
} {0}
do_test main-1.8 {
db complete {DROP TABLE "xyz';}
} {0}
do_test main-1.9 {
db complete {DROP TABLE "xyz";}
} {1}
do_test main-1.10 {
db complete {DROP TABLE xyz; hi}
} {0}
do_test main-1.11 {
db complete {DROP TABLE xyz; }
} {1}
do_test main-1.12 {
db complete {DROP TABLE xyz; -- hi }
} {1}
do_test main-1.13 {
db complete {DROP TABLE xyz; -- hi
}
} {1}
# Try to open a database with a corrupt master file.
#
+12 -2
View File
@@ -23,7 +23,7 @@
# This file implements regression tests for SQLite library. The
# focus of this file is testing the SELECT statement.
#
# $Id: select2.test,v 1.8 2000/07/29 13:07:00 drh Exp $
# $Id: select2.test,v 1.9 2000/10/19 14:10:09 drh Exp $
set testdir [file dirname $argv0]
source $testdir/tester.tcl
@@ -104,9 +104,16 @@ do_test select2-3.2c {
execsql {SELECT f1 FROM tbl2 WHERE f2=1000}
} {500}
do_test select2-3.2d {
execsql {SELECT fcnt() FROM tbl2 WHERE 1000=f2}
} {2}
do_test select2-3.2e {
execsql {SELECT fcnt() FROM tbl2 WHERE f2=1000}
} {2}
testif gdbm:
do_test select2-3.2f {
set t1 [lindex [time {execsql {SELECT f1 FROM tbl2 WHERE 1000=f2}} 1] 0]
set t2 [lindex [time {execsql {SELECT f1 FROM tbl2 WHERE f2=1000}} 1] 0]
expr {$t1*0.8<$t2 && $t2*0.8<$t1}
expr {$t1*0.7<$t2 && $t2*0.7<$t1}
} {1}
# Make sure queries run faster with an index than without
@@ -117,5 +124,8 @@ do_test select2-3.3 {
set t2 [lindex [time {execsql {SELECT f1 FROM tbl2 WHERE f2==2000}} 1] 0]
expr {$t1*10 < $t2}
} {1}
do_test select2-3.4 {
expr {[execsql {SELECT fcnt() FROM tbl2 WHERE f2==2000}]>10}
} {1}
finish_test
+12 -1
View File
@@ -23,7 +23,7 @@
# This file implements regression tests for SQLite library. The
# focus of this file is testing the CREATE TABLE statement.
#
# $Id: table.test,v 1.6 2000/08/02 13:47:43 drh Exp $
# $Id: table.test,v 1.7 2000/10/19 14:10:09 drh Exp $
set testdir [file dirname $argv0]
source $testdir/tester.tcl
@@ -49,6 +49,9 @@ do_test table-1.1 {
#
do_test table-1.2 {
execsql {INSERT INTO test1 VALUES('hi', 'y''all')}
} {}
testif gdbm:
do_test table-1.2b {
lsort [glob -nocomplain testdb/*.tbl]
} {testdb/sqlite_master.tbl testdb/test1.tbl}
@@ -61,6 +64,7 @@ do_test table-1.3 {
# Close and reopen the database. Verify that everything is
# still the same.
#
skipif memory:
do_test table-1.4 {
db close
sqlite db testdb
@@ -76,6 +80,7 @@ do_test table-1.5 {
# Verify that the file associated with the database is gone.
#
testif gdbm:
do_test table-1.5 {
lsort [glob -nocomplain testdb/*.tbl]
} {testdb/sqlite_master.tbl}
@@ -83,6 +88,7 @@ do_test table-1.5 {
# Close and reopen the database. Verify that the table is
# still gone.
#
skipif memory:
do_test table-1.6 {
db close
sqlite db testdb
@@ -121,6 +127,7 @@ do_test table-2.1b {
set v [catch {execsql {CREATE TABLE sqlite_master(two text)}} msg]
lappend v $msg
} {1 {table sqlite_master already exists}}
skipif memory:
do_test table-2.1c {
db close
sqlite db testdb
@@ -138,6 +145,7 @@ do_test table-2.2a {
set v [catch {execsql {CREATE TABLE test3(two text)}} msg]
lappend v $msg
} {1 {there is already an index named test3}}
skipif memory:
do_test table-2.2b {
db close
sqlite db testdb
@@ -198,6 +206,7 @@ do_test table-3.4 {
set v [catch {execsql {CREATE TABLE bIg(xyz foo)}} msg]
lappend v $msg
} {1 {table bIg already exists}}
skipif memory:
do_test table-3.5 {
db close
sqlite db testdb
@@ -226,6 +235,7 @@ do_test table-4.1 {
}
execsql {SELECT name FROM sqlite_master WHERE type!='meta' ORDER BY name}
} $r
skipif memory:
do_test table-4.1b {
db close
sqlite db testdb
@@ -287,6 +297,7 @@ do_test table-5.4 {
# Create a table with a goofy name
#
testif gdbm:
do_test table-6.1 {
execsql {CREATE TABLE 'Spaces In This Name!'(x int)}
execsql {INSERT INTO 'spaces in this name!' VALUES(1)}
+65 -14
View File
@@ -23,13 +23,27 @@
# This file implements some common TCL routines used for regression
# testing the SQLite library
#
# $Id: tester.tcl,v 1.5 2000/06/08 13:36:41 drh Exp $
# $Id: tester.tcl,v 1.8 2000/12/10 18:23:52 drh Exp $
# Create a test database
#
file delete -force testdb
file mkdir testdb
sqlite db testdb
if {![info exists dbprefix]} {
if {[info exists env(SQLITE_PREFIX)]} {
set dbprefix $env(SQLITE_PREFIX):
} else {
set dbprefix "gdbm:"
}
}
switch $dbprefix {
gdbm: {
file delete -force testdb
file mkdir testdb
}
memory: {
# do nothing
}
}
sqlite db ${dbprefix}testdb
# Abort early if this script has been run before.
#
@@ -39,12 +53,17 @@ if {[info exists nTest]} return
#
set nErr 0
set nTest 0
set skip_test 0
# Invoke the do_test procedure to run a single test
#
proc do_test {name cmd expected} {
global argv nErr nTest
if {[llength $argv]==0} {
global argv nErr nTest skip_test
if {$skip_test} {
set skip_test 0
return
}
if {[llength $argv]==0} {
set go 1
} else {
set go 0
@@ -57,7 +76,7 @@ proc do_test {name cmd expected} {
}
if {!$go} return
incr nTest
puts -nonewline $name...
puts -nonewline $::dbprefix$name...
flush stdout
if {[catch {uplevel #0 "$cmd;\n"} result]} {
puts "\nError: $result"
@@ -70,10 +89,48 @@ proc do_test {name cmd expected} {
}
}
# Skip a test based on the dbprefix
#
proc skipif {args} {
foreach a $args {
if {$::dbprefix==$a} {
set ::skip_test 1
return
}
}
}
# Run the next test only if the dbprefix is among the listed arguments
#
proc testif {args} {
foreach a $args {
if {$::dbprefix==$a} {
set ::skip_test 0
return
}
}
set ::skip_test 1
}
# The procedure uses the special "--malloc-stats--" macro of SQLite
# (which is only available if SQLite is compiled with -DMEMORY_DEBUG=1)
# to see how many malloc()s have not been free()ed. The number
# of surplus malloc()s is stored in the global variable $::Leak.
# If the value in $::Leak grows, it may mean there is a memory leak
# in the library.
#
proc memleak_check {} {
set r [execsql {--malloc-stats--}]
if {$r==""} return
set ::Leak [expr {[lindex $r 0]-[lindex $r 1]}]
# puts "*** $::Leak mallocs have not been freed ***"
}
# Run this routine last
#
proc finish_test {} {
global nTest nErr
memleak_check
catch {db close}
puts "$nErr errors out of $nTest tests"
exit $nErr
@@ -82,13 +139,7 @@ proc finish_test {} {
# A procedure to execute SQL
#
proc execsql {sql} {
set result {}
db eval $sql data {
foreach f $data(*) {
lappend result $data($f)
}
}
return $result
return [db eval $sql]
}
# Another procedure to execute SQL. This one includes the field
+3 -1
View File
@@ -23,7 +23,7 @@
# This file implements regression tests for SQLite library. The
# focus of this file is testing the VACUUM statement.
#
# $Id: vacuum.test,v 1.1 2000/06/03 18:06:54 drh Exp $
# $Id: vacuum.test,v 1.2 2000/10/19 14:10:10 drh Exp $
set testdir [file dirname $argv0]
source $testdir/tester.tcl
@@ -53,6 +53,7 @@ execsql {INSERT INTO test1 VALUES(2)}
execsql {INSERT INTO test1 VALUES(3)}
execsql {INSERT INTO test2 VALUES(4)}
testif gdbm:
do_test vacuum-1.3 {
set b1 [file mtime testdb/test1.tbl]
set b2 [file mtime testdb/test2.tbl]
@@ -64,6 +65,7 @@ do_test vacuum-1.3 {
set a3 [file mtime testdb/index1.tbl]
expr {$a1>$b1 && $a2==$b2 && $a3==$b3}
} {1}
testif gdbm:
do_test vacuum-1.4 {
set b1 [file mtime testdb/test1.tbl]
set b2 [file mtime testdb/test2.tbl]
+2 -1
View File
@@ -375,7 +375,8 @@ struct action *ap2;
rc = ap1->sp->index - ap2->sp->index;
if( rc==0 ) rc = (int)ap1->type - (int)ap2->type;
if( rc==0 ){
assert( ap1->type==REDUCE && ap2->type==REDUCE );
assert( ap1->type==REDUCE || ap1->type==RD_RESOLVED || ap1->type==CONFLICT);
assert( ap2->type==REDUCE || ap2->type==RD_RESOLVED || ap2->type==CONFLICT);
rc = ap1->x.rp->index - ap2->x.rp->index;
}
return rc;
+1 -1
View File
@@ -40,7 +40,6 @@ Single
4 1 0 100 0 0 20 0.0000 4 195 1140 3675 2550 Tokenizer\001
4 1 0 100 0 0 20 0.0000 4 195 1020 3675 750 Interface\001
4 1 0 100 0 0 20 0.0000 4 195 990 3675 9825 Backend\001
4 1 0 100 0 0 14 0.0000 4 150 570 3675 1575 main.c\001
4 1 0 100 0 0 14 0.0000 4 195 1860 3675 6525 build.c delete.c expr.c\001
4 1 0 100 0 0 14 0.0000 4 195 2115 3675 6750 insert.c select.c update.c\001
4 1 0 100 0 0 14 0.0000 4 150 705 3675 6975 where.c\001
@@ -48,3 +47,4 @@ Single
4 1 0 100 0 0 14 0.0000 4 150 870 3675 3375 tokenize.c\001
4 1 0 100 0 0 14 0.0000 4 150 570 3675 8775 vdbe.c\001
4 1 0 100 0 0 14 0.0000 4 150 570 3675 10500 dbbe.c\001
4 1 0 100 0 0 14 0.0000 4 195 2040 3675 1575 main.c table.c tclsqlite.c\001
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

+5 -3
View File
@@ -1,7 +1,7 @@
#
# Run this Tcl script to generate the sqlite.html file.
#
set rcsid {$Id: arch.tcl,v 1.2 2000/06/09 14:14:34 drh Exp $}
set rcsid {$Id: arch.tcl,v 1.3 2000/09/29 13:30:55 drh Exp $}
puts {<html>
<head>
@@ -30,8 +30,10 @@ follows will provide a quick overview of each of these components.
<h2>Interface</h2>
<p>The public interface to the SQLite library is implemented by
four functions found in the <b>main.c</b> source file. Additional
<p>Most of the public interface to the SQLite library is implemented by
four functions found in the <b>main.c</b> source file. The
<b>sqlite_get_table()</b> routine is implemented in <b>table.c</b>.
The Tcl interface is implemented by <b>tclsqlite.c</b>. More
information on the C interface to SQLite is
<a href="c_interface.html">available separately</a>.<p>
+243 -17
View File
@@ -1,7 +1,7 @@
#
# Run this Tcl script to generate the sqlite.html file.
#
set rcsid {$Id: c_interface.tcl,v 1.6 2000/07/28 14:32:51 drh Exp $}
set rcsid {$Id: c_interface.tcl,v 1.13 2000/11/28 20:46:39 drh Exp $}
puts {<html>
<head>
@@ -22,15 +22,14 @@ programming interface.</p>
<h2>The API</h2>
<p>The interface to the SQLite library consists of six functions
(only three of which are required),
one opaque data structure, and some constants used as return
values from sqlite_exec():</p>
<p>The interface to the SQLite library consists of three core functions,
one opaque data structure, and some constants used as return values.
The core interface is as follows:</p>
<blockquote><pre>
typedef struct sqlite sqlite;
sqlite *sqlite_open(const char *filename, int mode, char **errmsg);
sqlite *sqlite_open(const char *dbname, int mode, char **errmsg);
void sqlite_close(sqlite*);
@@ -42,12 +41,6 @@ int sqlite_exec(
char **errmsg
);
int sqlite_complete(const char *sql);
void sqlite_busy_handler(sqlite*, int (*)(void*,const char*,int), void*);
void sqlite_busy_timeout(sqlite*, int ms);
#define SQLITE_OK 0 /* Successful result */
#define SQLITE_INTERNAL 1 /* An internal logic error in SQLite */
#define SQLITE_ERROR 2 /* SQL error or missing database */
@@ -58,6 +51,73 @@ void sqlite_busy_timeout(sqlite*, int ms);
#define SQLITE_READONLY 7 /* Attempt to write a readonly database */
</pre></blockquote>
<p>Only the three core routines shown above are required to use
SQLite. But there are many other functions that provide
useful interfaces. These extended routines are as follows:
</p>
<blockquote><pre>
int sqlite_get_table(
sqlite*,
char *sql,
char ***result,
int *nrow,
int *ncolumn,
char **errmsg
);
void sqlite_free_table(char**);
void sqlite_interrupt(sqlite*);
int sqlite_complete(const char *sql);
void sqlite_busy_handler(sqlite*, int (*)(void*,const char*,int), void*);
void sqlite_busy_timeout(sqlite*, int ms);
const char sqlite_version[];
int sqlite_exec_printf(
sqlite*,
char *sql,
int (*)(void*,int,char**,char**),
void*,
char **errmsg,
...
);
int sqlite_exec_vprintf(
sqlite*,
char *sql,
int (*)(void*,int,char**,char**),
void*,
char **errmsg,
va_list
);
int sqlite_get_table_printf(
sqlite*,
char *sql,
char ***result,
int *nrow,
int *ncolumn,
char **errmsg,
...
);
int sqlite_get_table_vprintf(
sqlite*,
char *sql,
char ***result,
int *nrow,
int *ncolumn,
char **errmsg,
va_list
);
</pre></blockquote>
<p>All of the above definitions are included in the "sqlite.h"
header file that comes in the source tree.</p>
@@ -74,8 +134,9 @@ written to memory obtained from malloc() and *errmsg will be made
to point to this error message. The calling function is responsible
for freeing the memory when it has finished with it.</p>
<p>An SQLite database is just a directory containing a collection of
GDBM files. There is one GDBM file for each table and index in the
<p>The name of an SQLite database is normally the name of a directory
that contains a collection of GDBM files that comprise the database.
There is one GDBM file for each table and index in the
database. All GDBM files end with the ".tbl" suffix. Every SQLite
database also contains a special database table named <b>sqlite_master</b>
stored in its own GDBM file. This special table records the database
@@ -83,7 +144,20 @@ schema.</p>
<p>To create a new SQLite database, all you have to do is call
<b>sqlite_open()</b> with the first parameter set to the name of
an empty directory and the second parameter set to 0666.</p>
an empty directory and the second parameter set to 0666. The
directory is created automatically if it does not already exist.</p>
<p>Beginning with SQLite version 1.0.14, SQLite supports database
backends other than GDBM. The only backends currently supported
are the default GDBM driver and an in-memory hash table database.
You may anticipate additional backends in future versions of SQLite.</p>
<p>An alternative database backend is specified by prepending the
backend name and a colon to the database name argument of the
<b>sqlite_open()</b> function. For the GDBM backend, you can
prepend "<b>gdbm:</b>" to the directory name. To select the in-memory
hash table backend, prepend "<b>memory:</b>" to the database name.
Future database drivers will be selected by a similar mechanism.</p>
<p>The return value of the <b>sqlite_open()</b> function is a
pointer to an opaque <b>sqlite</b> structure. This pointer will
@@ -205,12 +279,75 @@ a database file that was originally opened for reading only. This can
happen if the callback from a query attempts to update the table
being queried.
</p></dd>
<dt>SQLITE_INTERRUPT</dt>
<dd><p>This value is returned if a call to <b>sqlite_interrupt()</b>
interrupts a database operation in progress.
</p></dd>
</dl>
</blockquote>
<h2>Querying without using a callback function</h2>
<p>The <b>sqlite_get_table()</b> function is a wrapper around
<b>sqlite_exec()</b> that collects all the information from successive
callbacks and write it into memory obtained from malloc(). This
is a convenience function that allows the application to get the
entire result of a database query with a single function call.</p>
<p>The main result from <b>sqlite_get_table()</b> is an array of pointers
to strings. There is one element in this array for each column of
each row in the result. NULL results are represented by a NULL
pointer. In addition to the regular data, there is an added row at the
beginning of the array that contains the names of each column of the
result.</p>
<p>As an example, consider the following query:</p>
<blockquote>
SELECT employee_name, login, host FROM users WHERE logic LIKE 'd%';
</blockquote>
<p>This query will return the name, login and host computer name
for every employee whose login begins with the letter "d". If this
query is submitted to <b>sqlite_get_table()</b> the result might
look like this:</p>
<blockquote>
nrow = 2<br>
ncolumn = 3<br>
result[0] = "employee_name"<br>
result[1] = "login"<br>
result[2] = "host"<br>
result[3] = "dummy"<br>
result[4] = "No such user"<br>
result[5] = 0<br>
result[6] = "D. Richard Hipp"<br>
result[7] = "drh"<br>
result[8] = "zadok"
</blockquote>
<p>Notice that the "host" value for the "dummy" record is NULL so
the result[] array contains a NULL pointer at that slot.</p>
<p>Memory to hold the information returned by <b>sqlite_get_table()</b>
is obtained from malloc(). But the calling function should not try
to free this information directly. Instead, pass the complete table
to <b>sqlite_free_table()</b> when the table is no longer needed.</p>
<p>The <b>sqlite_get_table()</b> routine returns the same integer
result code as <b>sqlite_exec()</b>.</p>
<h2>Interrupting an SQLite operation</h2>
<p>The <b>sqlite_interrupt()</b> function can be called from a
different thread or from a signal handler to the current database
operation to exit at its first opportunity. When this happens,
the <b>sqlite_exec()</b> routine (or the equivalent) that started
the database operation will return SQLITE_INTERRUPT.</p>
<h2>Testing for a complete SQL statement</h2>
<p>The last interface routine to SQLite is a convenience function used
<p>The next interface routine to SQLite is a convenience function used
to test whether or not a string forms a complete SQL statement.
If the <b>sqlite_complete()</b> function returns true when its input
is a string, then the argument forms a complete SQL statement.
@@ -230,7 +367,17 @@ then <b>sqlite_exec()</b> is called and the input buffer is reset. If
the continuation prompt and another line of text is read and added to
the input buffer.</p>
<h2>Changing the libraries reponse to locked files</h2>
<h2>Library version string</h2>
<p>The SQLite library exports the string constant named
<b>sqlite_version</b> which contains the version number of the
library. The header file contains a macro SQLITE_VERSION
with the same information. If desired, a program can compare
the SQLITE_VERSION macro against the <b>sqlite_version</b>
string constant to verify that the version number of the
header file and the library match.</p>
<h2>Changing the libraries response to locked files</h2>
<p>The GDBM library supports database locks at the file level.
If a GDBM database file is opened for reading, then that same
@@ -271,6 +418,85 @@ will wait for the lock to clear for at least the number of milliseconds
specified before it returns SQLITE_BUSY. Specifying zero milliseconds for
the timeout restores the default behavior.</p>
<h2>Using the <tt>_printf()</tt> wrapper functions</h2>
<p>The four utility functions</p>
<p>
<ul>
<li><b>sqlite_exec_printf()</b></li>
<li><b>sqlite_exec_vprintf()</b></li>
<li><b>sqlite_get_table_printf()</b></li>
<li><b>sqlite_get_table_vprintf()</b></li>
</ul>
</p>
<p>implement the same query functionality as <b>sqlite_exec()</b>
and <b>sqlite_get_table()</b>. But instead of taking a complete
SQL statement as their second argument, the four <b>_printf</b>
routines take a printf-style format string. The SQL statement to
be executed is generated from this format string and from whatever
additional arguments are attached to the end of the function call.</p>
<p>There are two advantages to using the SQLite printf
functions instead of <b>sprintf()</b>. First of all, with the
SQLite printf routines, there is never a danger of overflowing a
static buffer as there is with <b>sprintf()</b>. The SQLite
printf routines automatically allocate (and later free)
as much memory as is
necessary to hold the SQL statements generated.</p>
<p>The second advantage the SQLite printf routines have over
<b>sprintf()</b> is a new formatting option specifically designed
to support string literals in SQL. Within the format string,
the %q formatting option works very much like %s in that it
reads a null-terminated string from the argument list and inserts
it into the result. But %q translates the inserted string by
making two copies of every single-quote (') character in the
substituted string. This has the effect of escaping the end-of-string
meaning of single-quote within a string literal.
</p>
<p>Consider an example. Suppose you are trying to insert a string
value into a database table where the string value was obtained from
user input. Suppose the string to be inserted is stored in a variable
named zString. The code to do the insertion might look like this:</p>
<blockquote><pre>
sqlite_exec_printf(db,
"INSERT INTO table1 VALUES('%s')",
0, 0, 0, zString);
</pre></blockquote>
<p>If the zString variable holds text like "Hello", then this statement
will work just fine. But suppose the user enters a string like
"Hi y'all!". The SQL statement generated reads as follows:
<blockquote><pre>
INSERT INTO table1 VALUES('Hi y'all')
</pre></blockquote>
<p>This is not valid SQL because of the apostrophy in the word "y'all".
But if the %q formatting option is used instead of %s, like this:</p>
<blockquote><pre>
sqlite_exec_printf(db,
"INSERT INTO table1 VALUES('%q')",
0, 0, 0, zString);
</pre></blockquote>
<p>Then the generated SQL will look like the following:</p>
<blockquote><pre>
INSERT INTO table1 VALUES('Hi y''all')
</pre></blockquote>
<p>Here the apostrophy has been escaped and the SQL statement is well-formed.
When generating SQL on-the-fly from data that might contain a
single-quote character ('), it is always a good idea to use the
SQLite printf routines and the %q formatting option instead of <b>sprintf</b>.
</p>
<h2>Usage Examples</h2>
<p>For examples of how the SQLite C/C++ interface can be used,
+104
View File
@@ -17,6 +17,110 @@ proc chng {date desc} {
puts "<DD><P><UL>$desc</UL></P></DD>"
}
chng {2000 Dec 10 (1.0.17)} {
<li>Rewrote <b>sqlite_complete()</b> to make it faster.</li>
<li>Minor tweaks to other code to make it run a little faster.</li>
<li>Added new tests for <b>sqlite_complete()</b> and for memory leaks.</li>
}
chng {2000 Dec 4 (1.0.16)} {
<li>Documentation updates. Mostly fixing of typos and spelling errors.</li>
}
chng {2000 Oct 23 (1.0.15)} {
<li>Documentation updates</li>
<li>Some sanity checking code was removed from the inner loop of vdbe.c
to help the library to run a little faster. The code is only
removed if you compile with -DNDEBUG.</li>
}
chng {2000 Oct 19 (1.0.14)} {
<li>Added a "memory:" backend driver that stores its database in an
in-memory hash table.</li>
}
chng {2000 Oct 18 (1.0.13)} {
<li>Break out the GDBM driver into a separate file in anticipation
to added new drivers.</li>
<li>Allow the name of a database to be prefixed by the driver type.
For now, the only driver type is "gdbm:".</li>
}
chng {2000 Oct 16 (1.0.12)} {
<li>Fixed an off-by-one error that was causing a coredump in
the '%q' format directive of the new
<b>sqlite_..._printf()</b> routines.</li>
<li>Added the <b>sqlite_interrupt()</b> interface.</li>
<li>In the shell, <b>sqlite_interrupt()</b> is invoked when the
user presses Control-C</li>
<li>Fixed some instances where <b>sqlite_exec()</b> was
returning the wrong error code.</li>
}
chng {2000 Oct 11 (1.0.10)} {
<li>Added notes on how to compile for Windows95/98.</li>
<li>Removed a few variables that were not being used. Etc.</li>
}
chng {2000 Oct 8 (1.0.9)} {
<li>Added the <b>sqlite_..._printf()</b> interface routines.</li>
<li>Modified the <b>sqlite</b> shell program to use the new interface
routines.</li>
<li>Modified the <b>sqlite</b> shell program to print the schema for
the built-in SQLITE_MASTER table, if explicitly requested.</li>
}
chng {2000 Sep 30 (1.0.8)} {
<li>Begin writing documentation on the TCL interface.</li>
}
chng {2000 Sep 29 (Not Released)} {
<li>Added the <b>sqlite_get_table()</b> API</li>
<li>Updated the documentation for due to the above change.</li>
<li>Modified the <b>sqlite</b> shell to make use of the new
sqlite_get_table() API in order to print a list of tables
in multiple columns, similar to the way "ls" prints filenames.</li>
<li>Modified the <b>sqlite</b> shell to print a semicolon at the
end of each CREATE statement in the output of the ".schema" command.</li>
}
chng {2000 Sep 21 (Not Released)} {
<li>Change the tclsqlite "eval" method to return a list of results if
no callback script is specified.</li>
<li>Change tclsqlite.c to use the Tcl_Obj interface</li>
<li>Add tclsqlite.c to the libsqlite.a library</li>
}
chng {2000 Sep 13 (Version 1.0.5)} {
<li>Changed the print format for floating point values from "%g" to "%.15g".
</li>
<li>Changed the comparison function so that numbers in exponential notation
(ex: 1.234e+05) sort in numerical order.</li>
}
chng {2000 Aug 28 (Version 1.0.4)} {
<li>Added functions <b>length()</b> and <b>substr()</b>.</li>
<li>Fix a bug in the <b>sqlite</b> shell program that was causing
a coredump when the output mode was "column" and the first row
of data contained a NULL.</li>
}
chng {2000 Aug 22 (Version 1.0.3)} {
<li>In the sqlite shell, print the "Database opened READ ONLY" message
to stderr instead of stdout.</li>
<li>In the sqlite shell, now print the version number on initial startup.</li>
<li>Add the <b>sqlite_version[]</b> string constant to the library</li>
<li>Makefile updates</li>
<li>Bug fix: incorrect VDBE code was being generated for the following
circumstance: a query on an indexed table containing a WHERE clause with
an IN operator that had a subquery on its right-hand side.</li>
}
chng {2000 Aug 18 (Version 1.0.1)} {
<li>Fix a bug in the configure script.</li>
<li>Minor revisions to the website.</li>
}
chng {2000 Aug 17 (Version 1.0)} {
<li>Change the <b>sqlite</b> program so that it can read
databases for which it lacks write permission. (It used to
+62 -2
View File
@@ -1,7 +1,7 @@
#
# Run this Tcl script to generate the crosscompile.html file.
#
set rcsid {$Id: crosscompile.tcl,v 1.2 2000/07/31 19:16:32 drh Exp $}
set rcsid {$Id: crosscompile.tcl,v 1.3 2000/10/11 19:28:53 drh Exp $}
puts {<html>
<head>
@@ -23,7 +23,9 @@ binaries. See the website for details.</p>
<p>This page describes how you can use MinGW configured as a
cross-compiler running under RedHat 6.0 Linux to generate a
binary for SQLite that runs under WinNT.</p>
binary for SQLite that runs under WinNT.
Some additional steps (described <a href="#win95">below</a>)
are needed to target Win95/98.</p>
}
proc Code {body} {
@@ -123,6 +125,64 @@ make
</pre></blockquote>
</li>
</ol>
<a name="win95">
<h2>Targetting Windows95/98 instead of WindowsNT</h2>
<p>A small amount of additional work is needed to get SQLite running
under Windows95/98. The first problem is that the LockFile() and
UnlockFile() API that the Roth GDBM port uses does not work under
Windows95. The easiest workaround is to just disable file locking
in the GDBM library. You can do so by appending a few lines of code
to the end of one of the GDBM source files and compiling the library
using a special command-line option. Replace step (4) above as
follows:</p>
<ol>
<li value="4"><p>
Append text to the <b>systems.h</b> source file as follows:</p>
<blockquote><pre>
cat &gt;&gt;systems.h &lt;&lt;\END
#ifdef NO_LOCKS
#undef UNLOCK_FILE
#define UNLOCK_FILE(x)
#undef READLOCK_FILE
#define READLOCK_FILE(x)
#undef WRITELOCK_FILE
#define WRITELOCK_FILE(x)
#endif
END
</pre></blockquote>
<p>Then manually build the GDBM library with the extra
"NO_LOCKS" define as follows:</p>
<blockquote><pre>
i386-mingw32-gcc -DWIN32=1 -DNO_LOCKS -O2 -c *.c
i386-mingw32-ar cr libgdbm.a *.o
i386-mingw32-ranlib libgdbm.a
cd ..
</pre></blockquote>
</p></li>
</ol>
<p>Note that the locking problem has been reported and may actually
be fixed in the Roth GDBM distribution by the time you read this.
You should probably check before you make the above changes.</p>
<p>The above is all you have to do to get SQLite to work on Windows95.
But one more step is required to get it to work <i>well</i>. It
turns out that SQLite make heavy use of <b>malloc()</b> and
<b>free()</b> and the implementation of this functions
on Windows95 is particular poor. Large database queries will run
more than 10 times faster if you substitute a better memory allocator
such as the one by
<a href="http://g.oswego.edu/dl/html/malloc.html">Doug Lea</a>.
A copy of Doug's allocator is included in the <b>contrib</b>
directory of the source tree. Speed improvements are also reported
on WindowsNT when alternative memory allocators are used, though
the speedup is not as dramatic as it is with WIndows95.</p>
}
puts {
<p><hr /></p>
+34 -30
View File
@@ -1,12 +1,12 @@
#
# Run this TCL script to generate HTML for the index.html file.
#
set rcsid {$Id: index.tcl,v 1.27 2000/08/17 10:22:34 drh Exp $}
set rcsid {$Id: index.tcl,v 1.29 2000/09/30 22:46:07 drh Exp $}
puts {<html>
<head><title>SQLite: An SQL Database Engine Built Atop GDBM</title></head>
<head><title>SQLite: An SQL Database Library Built Atop GDBM</title></head>
<body bgcolor=white>
<h1 align=center>SQLite: An SQL Database Engine Built Atop
<h1 align=center>SQLite: An SQL Database Library Built Atop
<a href="http://www.gnu.org/software/gdbm/gdbm.html">GDBM</a></h1>
<p align=center>}
puts "This page was last modified on [lrange $rcsid 3 4] GMT<br>"
@@ -17,33 +17,17 @@ puts {</p>}
puts {<h2>Introduction</h2>
<p>SQLite is an SQL database engine built on top of the
<a href="http://www.gnu.org/software/gdbm/gdbm.html">GDBM library</a>.
SQLite includes a standalone command-line
access program (<a href="sqlite.html">sqlite</a>)
and a C library (<a href="c_interface.html">libsqlite.a</a>)
that can be linked
with a C/C++ program to provide SQL database access without
a separate RDBMS.</p>
<p>SQLite is an SQL database library
(<a href="c_interface.html">libsqlite.a</a>) that uses
<a href="http://www.gnu.org/software/gdbm/gdbm.html">GDBM</a>
as its underlying file storage mechanism.
Programs that link the SQLite library can have SQL database
access without running a separate RDBMS process.
The distribution comes with a standalone command-line
access program (<a href="sqlite.html">sqlite</a>) that can
be used to administer an SQLite database and which serves as
an example of how to use the SQLite library.</p>
<h2>Important News Flash!</h2>
<p>
The SQLite file format was changed in an incompatible way on
Aug 2, 2000. If you are updated the library and have databases
built using the old version of the library, you should save your
old databases into an ASCII file then reimport those
database using the new library. For example, if you change the
name of the old <b>sqlite</b> utility to "old-sqlite" and
change the name of the old database directory to "old-db", then
you can reconstruct the database as follows:</p>
<blockquote><pre>
echo .dump | old-sqlite old-db | sqlite db
</pre></blockquote>
<p>This file format change was made to work around a potential
inefficiency in GDBM that comes up when large indices are created
on tables where many entries in the table have the same index key.</p>
<h2>Features</h2>
@@ -56,7 +40,7 @@ on tables where many entries in the table have the same index key.</p>
<li>Very simple
<a href="c_interface.html">C/C++ interface</a> requires the use of only
three functions and one opaque structure.</li>
<li>A <a href="http://dev.scriptics.com/">Tcl</a> interface is
<li>A <a href="tclsqlite.html">Tcl</a> interface is
included.</li>
<li>Command-line access program <a href="sqlite.html">sqlite</a> uses
the <a href="http://www.google.com/search?q=gnu+readline+library">GNU
@@ -88,6 +72,25 @@ Among the SQL features that SQLite does not currently implement are:</p>
</ul>
</p>
<h2>Important News Flash!</h2>
<p>
The SQLite file format was changed in an incompatible way on
Aug 2, 2000. If you are updated the library and have databases
built using the old version of the library, you should save your
old databases into an ASCII file then reimport the
database using the new library. For example, if you change the
name of the old <b>sqlite</b> utility to "old-sqlite" and
change the name of the old database directory to "old-db", then
you can reconstruct the database as follows:</p>
<blockquote><pre>
echo .dump | old-sqlite old-db | sqlite db
</pre></blockquote>
<p>This file format change was made to work around a potential
inefficiency in GDBM that comes up when large indices are created
on tables where many entries in the table have the same index key.</p>
<h2>Documentation</h2>
<p>The following documentation is currently available:</p>
@@ -97,6 +100,7 @@ Among the SQL features that SQLite does not currently implement are:</p>
command-line utility.</li>
<li>The <a href="lang.html">SQL Language</a> subset understood by SQLite.</li>
<li>The <a href="c_interface.html">C/C++ Interface</a>.</li>
<li>The <a href="tclsqlite.html">Tcl Interface</a>.</li>
<li>The <a href="fileformat.html">file format</a> used by SQLite databases.</li>
<li>The <a href="arch.html">Architecture of the SQLite Library</a> describes
how the library is put together.</li>
+274
View File
@@ -0,0 +1,274 @@
#
# Run this Tcl script to generate the tclsqlite.html file.
#
set rcsid {$Id: tclsqlite.tcl,v 1.3 2000/10/23 13:16:33 drh Exp $}
puts {<html>
<head>
<title>The Tcl interface to the SQLite library</title>
</head>
<body bgcolor=white>
<h1 align=center>
The Tcl interface to the SQLite library
</h1>}
puts "<p align=center>
(This page was last modified on [lrange $rcsid 3 4] GMT)
</p>"
puts {
<p>The SQLite library is designed to be very easy to use from
a Tcl or Tcl/Tk script. This document gives an overview of the Tcl
programming interface.</p>
<h2>The API</h2>
<p>The interface to the SQLite library consists of single
tcl command named <b>sqlite</b>. Because there is only this
one interface command, the interface is not placed in a separate
namespace.</p>
<p>The <b>sqlite</b> command is used as follows:</p>
<blockquote>
<b>sqlite</b>&nbsp;&nbsp;<i>dbcmd&nbsp;&nbsp;database-name</i>
</blockquote>
<p>
The <b>sqlite</b> command opens the database named in the second
argument. If the database does not already exist, it is
automatically created.
The <b>sqlite</b> command also creates a new Tcl
command to control the database. The name of the new Tcl command
is given by the first argument. This approach is similar to the
way widgets are created in Tk.
</p>
<p>
The name of the database is usually either the name of a directory
that will contain the GDBM files that comprise the database, or it is the
name of the directory prefaced by "<b>gdbm:</b>". The second form
of the name is a new feature beginning in SQLite version 1.0.14 that
allows you to select alternative database backends. The default
backend is GDBM. But you can also select to store the database in
a hash table in memory by using the prefix "<b>memory:</b>".
Other backends may be added in the future.
</p>
<p>
Every time you open an SQLite database with the <b>memory:</b> prefix
on the database name, you get a new in-memory database. This is true
even if you open two databases with the same name. Furthermore,
an in-memory database is automatically deleted when the database is
closed and so is not useful for persistant storage like a normal
database. But the use of an in-memory SQL database does give Tcl/Tk
a powerful new data storage mechanism that can do things that are
difficult to do with only Tcl array variables. In fact, the
hash-table backend for SQLite was created for the sole purpose of
providing better data structure support to the Tcl language.
</p>
<p>
Once an SQLite database is open, it can be controlled using
methods of the <i>dbcmd</i>. There are currently 5 methods
defined:</p>
<p>
<ul>
<li> busy
<li> close
<li> complete
<li> eval
<li> timeout
</ul>
</p>
<p>We will explain all of these methods, though not in that order.
We will be begin with the "close" method.</p>
<h2>The "close" method</h2>
<p>
As its name suggests, the "close" method to an SQLite database just
closes the database. This has the side-effect of deleting the
<i>dbcmd</i> Tcl command. Here is an example of opening and then
immediately closing a database:
</p>
<blockquote>
<b>sqlite db1 ./testdb<br>
db1 close</b>
</blockquote>
<p>
If you delete the <i>dbcmd</i> directly, that has the same effect
as invoking the "close" method. So the following code is equivalent
to the previous:</p>
<blockquote>
<b>sqlite db1 ./testdb<br>
rename db1 {}</b>
</blockquote>
<h2>The "eval" method</h2>
<p>
The most useful <i>dbcmd</i> method is "eval". The eval method is used
to execute SQL on the database. The syntax of the eval method looks
like this:</p>
<blockquote>
<i>dbcmd</i>&nbsp;&nbsp;<b>eval</b>&nbsp;&nbsp;<i>sql</i>
&nbsp;&nbsp;?<i>array-name&nbsp;&nbsp;script</i>?
</blockquote>
<p>
The job of the eval method is to execute the SQL statement or statements
given in the second argument. For example, to create a new table in
a database, you can do this:</p>
<blockquote>
<b>sqlite db1 ./testdb<br>
db1 eval {CREATE TABLE t1(a int, b text)}</b>
</blockquote>
<p>The above code creates a new table named <b>t1</b> with columns
<b>a</b> and <b>b</b>. What could be simpler?</p>
<p>Query results are returned as a list of column values. If a
query requests 2 columns and there are 3 rows matching the query,
then the returned list will contain 6 elements. For example:</p>
<blockquote>
<b>db1 eval {INSERT INTO t1 VALUES(1,'hello')}<br>
db1 eval {INSERT INTO t1 VALUES(2,'goodbye')}<br>
db1 eval {INSERT INTO t1 VALUES(3,'howdy!')}<br>
set x [db1 eval {SELECT * FROM t1 ORDER BY a}]</b>
</blockquote>
<p>The variable <b>$x</b> is set by the above code to</p>
<blockquote>
<b>1 hello 2 goodbye 3 howdy!</b>
</blockquote>
<p>You can also process the results of a query one row at a time
by specifying the name of an array variable and a script following
the SQL code. For each row of the query result, the value of each
column will be inserted into the array variable and the script will
be executed. For instance:</p>
<blockquote>
<b>db1 eval {SELECT * FROM t1 ORDER BY a} values {<br>
&nbsp;&nbsp;&nbsp;&nbsp;parray values<br>
&nbsp;&nbsp;&nbsp;&nbsp;puts ""<br>
}</b>
</blockquote>
<p>This last code will give the following output:</p>
<blockquote><b>
values(*) = a b<br>
values(a) = 1<br>
values(b) = hello<p>
values(*) = a b<br>
values(a) = 2<br>
values(b) = goodbye<p>
values(*) = a b<br>
values(a) = 3<br>
values(b) = howdy!</b>
</blockquote>
<p>
For each column in a row of the result, the name of that column
is used as an index in to array. The value of the column is stored
in the corresponding array entry. The special array index * is
used to store a list of column names in the order that they appear.
</p>
<p>
If the array variable name is the empty string, then the value of
each column is stored in a variable with the same name as the column
itself. For example:
</p>
<blockquote>
<b>db1 eval {SELECT * FROM t1 ORDER BY a} {} {<br>
&nbsp;&nbsp;&nbsp;&nbsp;puts "a=$a b=$b"<br>
}</b>
</blockquote>
<p>
From this we get the following output
</p>
<blockquote><b>
a=1 b=hello<br>
a=2 b=goodbye<br>
a=3 b=howdy!</b>
</blockquote>
<h2>The "complete" method</h2>
<p>
The "complete" method takes a string of supposed SQL as its only argument.
It returns TRUE if the string is a complete statement of SQL and FALSE if
there is more to be entered.</p>
<p>The "complete" method is useful when building interactive applications
in order to know when the user has finished entering a line of SQL code.
This is really just an interface to the <b>sqlite_complete()</b> C
function. Refer to the <a href="c_interface.html">C/C++ interface</a>
specification for additional information.</p>
<h2>The "timeout" method</h2>
<p>The "timeout" method is used to control how long the SQLite library
will wait for locks to clear before giving up on a database transaction.
The default timeout is 0 millisecond. (In other words, the default behavior
is not to wait at all.)</p>
<p>The GDBM backend allows multiple simultaneous
readers or a single writer but not both. If any process is writing to
the database no other process is allows to read or write. If any process
is reading the database other processes are allowed to read but not write.
Each GDBM file is locked separately. Because each SQL table is stored as
a separate file, it is possible for different processes to write to different
database tables at the same time, just not the same table.</p>
<p>When SQLite tries to open a GDBM file and finds that it is locked, it
can optionally delay for a short while and try to open the file again.
This process repeats until the query times out and SQLite returns a
failure. The timeout is adjustable. It is set to 0 by default so that
if a GDBM file is locked, the SQL statement fails immediately. But you
can use the "timeout" method to change the timeout value to a positive
number. For example:</p>
<blockquote><b>db1 timeout 2000</b></blockquote>
<p>The argument to the timeout method is the maximum number of milliseconds
to wait for the lock to clear. So in the example above, the maximum delay
would be 2 seconds.</p>
<h2>The "busy" method</h2>
<p>The "busy" method, like "timeout", only comes into play when a GDBM
file is locked. But the "busy" method gives the programmer much more
control over what action to take. The "busy" method specifies a callback
Tcl procedure that is invoked whenever SQLite tries to open a locked
GDBM file. This callback can do whatever is desired. Presumably, the
callback will do some other useful work for a short while then return
so that the lock can be tried again. The callback procedure should
return "0" if it wants SQLite to try again to open the GDBM file and
should return "1" if it wants SQLite to abandon the current operation.
}
puts {
<p><hr /></p>
<p><a href="index.html"><img src="/goback.jpg" border=0 />
Back to the SQLite Home Page</a>
</p>
</body></html>}
+6 -6
View File
@@ -1,7 +1,7 @@
#
# Run this Tcl script to generate the vdbe.html file.
#
set rcsid {$Id: vdbe.tcl,v 1.5 2000/07/30 20:04:43 drh Exp $}
set rcsid {$Id: vdbe.tcl,v 1.6 2000/11/28 20:46:41 drh Exp $}
puts {<html>
<head>
@@ -220,7 +220,7 @@ cursor to the "examp" file.</p>
preprocessor macro, then
there is a special SQL comment that will cause the
the VDBE to traces the execution of programs.
Though this features was originally intended for testing
Though this feature was originally intended for testing
and debugging, it might also be useful in learning about
how the VDBE operates.
Use the "<tt>--vdbe-trace-on--</tt>" comment to
@@ -306,7 +306,7 @@ int Callback(void *pUserData, int nColumn, char *azData[], char *azColumnName[])
<p>The SQLite library supplies the VDBE with a pointer to the callback function
and the <b>pUserData</b> pointer. (Both the callback and the user data were
originally passed in as argument to the <b>sqlite_exec()</b> API function.)
originally passed in as arguments to the <b>sqlite_exec()</b> API function.)
The job of the VDBE is to
come up with values for <b>nColumn</b>, <b>azData[]</b>,
and <b>azColumnName[]</b>.
@@ -769,7 +769,7 @@ a small percentage of the rows end up in the result. This can
take a long time on a big table. To speed things up, SQLite
can use an index.</p>
<p>An GDBM file associates a key with some data. For a SQLite
<p>A GDBM file associates a key with some data. For a SQLite
table, the GDBM file is set up so that the key is a integer
and the data is the information for one row of the table.
Indices in SQLite reverse this arrangement. The GDBM key
@@ -1172,7 +1172,7 @@ with that key is brought into focus. A new bucket is created with
the key if one did not previously exist. Once the bucket is in
focus, the memory locations of the bucket are used to accumulate
the values of the various aggregate functions. After the query
loop terminates, the each bucket is visited once to generate a
loop terminates, each bucket is visited once to generate a
single row of the results.</p>
<p>An example will help to clarify this concept. Consider the
@@ -1476,7 +1476,7 @@ VDBE halts.</p>
All this code does is make an entry in the temporary table for each
row of the examp2 table. The key for each temporary table entry
is the "three" column of examp2 and the data
entries is an empty string since it is never used.</p>
is an empty string since it is never used.</p>
<p>The outer SELECT is implemented by instructions 8 through 19. In
particular, the WHERE clause containing the IN operator is implemented