Compare commits

...

63 Commits

Author SHA1 Message Date
drh d6b1cf5a7e Version 1.0.24 (CVS 479)
FossilOrigin-Name: 34b17e6ce15dd24dc2ca609ef9c136fe4dc1b4b5
2001-03-14 12:59:59 +00:00
drh b8525d9dcf :-) (CVS 191)
FossilOrigin-Name: 8f79ca2acaf765ecb3274c353284c55b6790f657
2001-03-14 12:54:11 +00:00
drh b072950e24 Fix a bug on systems where malloc(0) return NULL.
Add code to detect this bug even on systems were it used not to
occur. (CVS 190)

FossilOrigin-Name: bda83e2b75843b9a078c2f4e64fcd4d2145a716b
2001-03-14 12:35:57 +00:00
drh 65cd7c02e7 Documentation updates only (CVS 189)
FossilOrigin-Name: d44abe54eb4a0c5eedf8f3ff59704ddb8dd68faf
2001-02-20 13:06:30 +00:00
drh 3c9ebf144e Version 1.0.23 (CVS 480)
FossilOrigin-Name: cbfa44c32365a1c9d10c9fcfeebad2fea139aafb
2001-02-20 00:00:00 +00:00
drh e53dea15d1 Fix a temp file problem on memory: (CVS 188)
FossilOrigin-Name: 88447d45c8ac316868a8a3e3cbb0b0cdf279d89c
2001-02-19 23:48:16 +00:00
drh 7d822f292b Version 1.0.22 (CVS 481)
FossilOrigin-Name: ec861066e3e6d3bfee83997fa7018d0291e22d90
2001-02-19 23:29:59 +00:00
drh a6e982a256 Repairs to the previous fix (CVS 187)
FossilOrigin-Name: b91854a67f5e3835aafbbb1f9c528f4011ffc3f9
2001-02-19 23:28:36 +00:00
drh 573bd2754f Repairs to the previous fix (CVS 186)
FossilOrigin-Name: 4fb52b4824cbc6d5fa23449bfb998ec9985f1336
2001-02-19 23:23:38 +00:00
drh 827e656177 Version 1.0.21 (CVS 482)
FossilOrigin-Name: 7a1147ff52df5f78b1a2e3502c1b78e4d3906968
2001-02-19 18:30:00 +00:00
drh 4f3c63e475 fix the UPDATE bug (CVS 185)
FossilOrigin-Name: 99aae9aaa9f53d1be6600f8598f66594692aae39
2001-02-19 18:24:21 +00:00
drh 4e0dec47d7 Version 1.0.20 (CVS 484)
FossilOrigin-Name: eb0a523c49df65ef3454b8d7e05a6a852b24fc8b
2001-02-11 17:00:00 +00:00
drh 03e7e9516f Perpare to fork SQLite2.0 develop into a separate tree (CVS 184)
FossilOrigin-Name: 4f00e27f17a15504543c5bbb8765c88bacd7257e
2001-02-11 16:58:21 +00:00
drh 338ea13c0d Perpare to fork SQLite2.0 develop into a separate tree (CVS 183)
FossilOrigin-Name: 6adb6078871114ba19ab601bb94d43ff9e03e43f
2001-02-11 16:56:24 +00:00
drh 2e7051b3ec hi (CVS 1716)
FossilOrigin-Name: 5128135c7e12f763722981ad39ecb7835db49142
2001-02-06 14:10:26 +00:00
drh 98a0b0834d :-) (CVS 1715)
FossilOrigin-Name: 29305c3c9eb805a1f5beb82ad50cba1d3bbd3465
2001-02-06 14:01:23 +00:00
drh c58011e862 Version 1.0.19 (CVS 483)
FossilOrigin-Name: 08e179b076b53a2792aba7541f614ad39fa45194
2001-02-06 14:00:00 +00:00
drh b5730347ad Working better with Win95.
Continued work on the new db.c backend. (CVS 181)

FossilOrigin-Name: f9c55a900032ed6babd86170dcb5257e33da950f
2001-01-31 13:28:08 +00:00
drh 17a6893463 Working better with Win95.
Continued work on the new db.c backend. (CVS 1714)

FossilOrigin-Name: df0ff304855bd18c7a3517c500e891b6d006be6a
2001-01-31 13:28:08 +00:00
drh d1881e6165 :-) (CVS 1713)
FossilOrigin-Name: 3259a53111bd19417e78160174bd67c69098494a
2001-01-29 01:27:19 +00:00
drh b5b452cb58 :-) (CVS 1712)
FossilOrigin-Name: edb01b1275c3de7c398522b5826c898917811247
2001-01-25 01:45:40 +00:00
drh acbcdc49c6 :-) (CVS 180)
FossilOrigin-Name: 98da825312fd4bb8a20ff33293131c02beb3ae63
2001-01-22 00:31:53 +00:00
drh d9e5075ff6 :-) (CVS 1711)
FossilOrigin-Name: 0529c979fd17995aff82e21b91b5cc833f23d8ef
2001-01-21 22:03:30 +00:00
drh 2702114089 :-) (CVS 179)
FossilOrigin-Name: d5f2a668978c0d108045237f19b0a7efa07678f2
2001-01-21 00:58:07 +00:00
drh 41a2b48bd0 :-) (CVS 178)
FossilOrigin-Name: 1662063dfb0925bd439e3e2e49bff82705e20fd0
2001-01-20 19:52:49 +00:00
drh e5080e4e9e continued progress toward version 2.0 (CVS 1710)
FossilOrigin-Name: a60af40a3e2e2903d0caa8b81b7e5a4cdc67cd52
2001-01-15 22:51:09 +00:00
drh 345fda3ef5 continued progress toward version 2.0 (CVS 177)
FossilOrigin-Name: c6ffb7ec6acb596907ead8992dfad94e18e83866
2001-01-15 22:51:08 +00:00
drh ae85dc8b0b Changes to the DBBE. Moving toward having many more
backend driver choices. (CVS 176)

FossilOrigin-Name: c0730217a04323a1a73d125e3e7da32bcc8d58fc
2001-01-13 14:34:05 +00:00
drh 993c2a7c8f Version 1.0.18 (CVS 485)
FossilOrigin-Name: 46b86abb1cc8e550acddba24e510d36eaf8ac6b9
2001-01-04 14:29:59 +00:00
drh 2fad28e310 branches: 1.55.2;
:-) (CVS 175)

FossilOrigin-Name: 81815a9c30ffc803cc03aad98936cba641674cd8
2001-01-04 14:27:07 +00:00
drh 660f68d6a2 :-) (CVS 174)
FossilOrigin-Name: 5ddeeb477148295b66fdd78d102827386852fed3
2001-01-04 14:27:07 +00:00
drh 4b2d64a9c9 :-) (CVS 173)
FossilOrigin-Name: e54b0c5ea845111d09529b57b2372ea2e797651a
2001-01-04 14:20:18 +00:00
drh 41adf6c327 branches: 1.17.2;
:-) (CVS 172)

FossilOrigin-Name: 64900e42cdb92b44e0fb3a17f0b94fc62ce733d5
2001-01-04 14:20:18 +00:00
drh a2e1bb5a58 :-) (CVS 171)
FossilOrigin-Name: d49e10fb5e29318f1f777664562b68dd054f7535
2001-01-04 14:20:18 +00:00
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
60 changed files with 7257 additions and 5277 deletions
+15 -3
View File
@@ -47,15 +47,17 @@ 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 printf.o select.o table.o tokenize.o update.o \
util.o vdbe.o where.o tclsqlite.o
LIBOBJ = build.o dbbe.o dbbegdbm.o dbbemem.o delete.o expr.o insert.o \
main.o parse.o printf.o random.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 \
@@ -64,6 +66,7 @@ SRC = \
$(TOP)/src/main.c \
$(TOP)/src/parse.y \
$(TOP)/src/printf.c \
$(TOP)/src/random.c \
$(TOP)/src/select.c \
$(TOP)/src/shell.c \
$(TOP)/src/sqlite.h.in \
@@ -118,6 +121,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
@@ -154,6 +163,9 @@ expr.o: $(TOP)/src/expr.c $(HDR)
insert.o: $(TOP)/src/insert.c $(HDR)
$(TCC) $(GDBM_FLAGS) -c $(TOP)/src/insert.c
random.o: $(TOP)/src/random.c $(HDR)
$(TCC) $(GDBM_FLAGS) -c $(TOP)/src/random.c
select.o: $(TOP)/src/select.c $(HDR)
$(TCC) $(GDBM_FLAGS) -c $(TOP)/src/select.c
+1 -1
View File
@@ -1 +1 @@
1.0.10
1.0.24
-3276
View File
File diff suppressed because it is too large Load Diff
+48
View File
@@ -0,0 +1,48 @@
The SQL database used for ACD contains 113 tables and indices implemented
in GDBM. The following are statistics on the sizes of keys and data
within these tables and indices.
Entries: 962080
Size: 45573853
Avg Size: 48
Key Size: 11045299
Avg Key Size: 12
Max Key Size: 99
0..8 266 0%
9..12 5485 0%
13..16 73633 8%
17..24 180918 27%
25..32 209823 48%
33..40 148995 64%
41..48 76304 72%
49..56 14346 73%
57..64 15725 75%
65..80 44916 80%
81..96 127815 93%
97..112 34769 96%
113..128 13314 98%
129..144 8098 99%
145..160 3355 99%
161..176 1159 99%
177..192 629 99%
193..208 221 99%
209..224 210 99%
225..240 129 99%
241..256 57 99%
257..288 496 99%
289..320 60 99%
321..352 37 99%
353..384 46 99%
385..416 22 99%
417..448 24 99%
449..480 26 99%
481..512 27 99%
513..1024 471 99%
1025..2048 389 99%
2049..4096 182 99%
4097..8192 74 99%
8193..16384 34 99%
16385..32768 17 99%
32769..65536 5 99%
65537..131073 3 100%
+62 -47
View File
@@ -1,82 +1,97 @@
C Version\s1.0.10\s(CVS\s492)
D 2000-10-11T19:30:00
C Version\s1.0.24\s(CVS\s479)
D 2001-03-14T13:00:00
F COPYRIGHT 74a8a6531a42e124df07ab5599aad63870fa0bd4
F Makefile.in faecea9b6419cec25030b4818c9b3f7f4163b3c1
F Makefile.in 7efa81e2985b45ba73db27d55b70cc927f5abfd7
F README 51f6a4e7408b34afa5bc1c0485f61b6a4efb6958
F VERSION 0302755bd219af2c39c53cc097caccb858798223
F VERSION 404ea684e576d2b596e185903cea48b3e984c7fd
F configure 3dc1edb9dcf60215e31ff72b447935ab62211442 x
F configure.in d892ca33db7e88a055519ce2f36dcb11020e8fff
F contrib/dlmalloc.c 754f43cd3b225728017b2516a58103f4bc3f05ee
F doc/lemon.html e233a3e97a779c7a87e1bc4528c664a58e49dd47
F src/build.c 4d90e9e94750ca80249fc7958c617021d8bb7a50
F src/dbbe.c 226daaf8c095ceb4aff48cad188dad90643f9867
F src/dbbe.h 6337132f904e72ecb28b07390021c241397e4cbf
F src/dbbemem.c eb3d79be7105bd80069815ee499c8e8682876378
F src/delete.c 4d491eaf61b515516749c7ed68fa3b2ee8a09065
F src/expr.c e8e350d7baa33bd9ed8701c159eaba5e912e0adb
F src/insert.c f146f149ad2422a1dc3bfa7a1651a25940f98958
F src/main.c 03ca6d7601b40a3c6de95d7f8c6c817e8cca9fdf
F src/parse.y 5d199034de5d29ebedb42c1c51f34db4df40cbe5
F src/printf.c 6fc343f0c3537e2530a11ac164ce64ba171aebf8
F src/select.c d382e96c2221d08367cc87976f2b574537c9de97
F src/shell.c ef5d12129c824cb98238763e9e86ca1847e0c7bd
F doc/report1.txt ad0a41513479f1be0355d1f3f074e66779ff2282
F src/TODO 38a68a489e56e9fd4a96263e0ff9404a47368ad4
F src/build.c 7aa5879bf58ea6bbff22c26c59d1130021fa6ca4
F src/dbbe.c 162d29b09ac379f160892c5795efc14099dcc8eb
F src/dbbe.h 0435a36906a839cce062608f51bd9d3e79878fec
F src/dbbegdbm.c 5bfcb1b4ee47a98c5eae83041e9716cd3233fd0e
F src/dbbemem.c 576a0a0ad675063a1dd2c4aae68233232bd2deac
F src/delete.c b83dc815f83220a82df13f1f1f479187d305fe6a
F src/ex/README b745b00acce2d892f60c40111dacdfc48e0c1c7a
F src/ex/db.c f1419ae6c93e40b5ac6e39fe7efd95d868e6f9d7
F src/ex/db.h 3f2933ee20c147fe494835786e4c6f3a562def4e
F src/ex/dbbebdb1.c 61ed414307f829478614def33de640bbe5b2f770
F src/ex/dbbemird.c b00aef85656fa0a101dac2c32e12922ad106715a
F src/ex/pg.c 2bbf6a94f37226d06337868b6bf4d7affc60197f
F src/ex/pg.h 23a4ac807b0546ec2bb6239ec8bd3e06926572cd
F src/ex/sizes.tcl f54bad4a2ac567624be59131a6ee42d71b41a3d7
F src/expr.c 49bc261fdc4f4fb91c74cd668a9a952c00e85931
F src/insert.c 4bc1cab84f7805d560a1417734a532843e30b762
F src/main.c 5afe29c425b875acede20f609485866eb5b276f6
F src/parse.y 25ee4d8efccc4b247c32fe4ab194e3dd8fd5a4ee
F src/printf.c af0dc65c293427272e1949c7807b1d88f10004fd
F src/random.c b36c3f57dc80c8f354e6bfbf39cf1e1de021d54a
F src/select.c faac634ef0c717bc82ca112a4531a257886f2c7a
F src/shell.c 441e20913cde0bb71281f4027623c623530241cd
F src/shell.tcl 27ecbd63dd88396ad16d81ab44f73e6c0ea9d20e
F src/sqlite.h.in 7a11f9d131e9657d11dee5c23dfe458da91de8e2
F src/sqliteInt.h b65fdecac7281aafb4c9ff3e79ea1b5546478385
F src/table.c f08189678c806d8a74a70c156d7c27083760e028
F src/tclsqlite.c 44b08b47612a668caaf7c4ec32133b69d73ff78e
F src/tokenize.c 097bec5843d4a0fb4509e036fee93bac080c5e73
F src/update.c 51b9ef7434b15e31096155da920302e9db0d27fc
F src/util.c 811e0ad47f842c16555aaf361b26dab7221c1a6c
F src/vdbe.c b8b0bc26d6543d5d33820809122bf0e7659e4663
F src/vdbe.h 8ca755f77eeaa93886d25188fd66129febfa16cf
F src/where.c 3dfad2ffd0aa994d5eceac88852f7189c8d1d3c8
F test/all.test 0950c135cab7e60c07bd745ccfad1476211e5bd7
F src/sqlite.h.in 7c1a53f020418d89d13ed2fe9c477ff54540755d
F src/sqliteInt.h fd513fa6b7ac94919f85ebfa183aaa194284ce16
F src/table.c 5be76051a8ed6f6bfa641f4adc52529efa34fbf9
F src/tclsqlite.c 2a925e1835c348f07dd17c87d95ae9a577833407
F src/test.file 55ca6286e3e4f4fba5d0448333fa99fc5a404a73
F src/tokenize.c c7ad428f38e56342eb2025320480b5ae9ece1b90
F src/update.c 8365b3922ea098330d1e20862d6e64911e4e03d0
F src/util.c f4573201fc2b581dbf601c53787349310b7da150
F src/vdbe.c e8254ae39d2f42a3f85b168b67a493ae206f4bf4
F src/vdbe.h 031b7dd7d6f94c51dc37cdf26efe43d1619bb672
F src/where.c 478fde7c930969ca428de2d80b137959d25ee2fb
F test/all.test 15cac2f6b2d4c55bf896212aff3cc9d6597b0490
F test/copy.test b77a1214bd7756f2849d5c4fa6e715c0ff0c34eb
F test/dbbe.test c6079572516aeb2739a35fd272b105c45c30cc76
F test/dbbe.test 27deeebf2a01da97cabaab8dc7f34ca3b51a0123
F test/delete.test 402ee3ccb6e544582d24c573ef70b34d09583ae7
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 bf6d3f8a0a91c6673ff5e357b7a1397e241f7f14
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 ad57d7e8114b0691eb5e943b9dabbd68119b8e2c
F test/update.test 62f6ce99ff31756aab0ca832ff6d34c5a87b6250
F test/vacuum.test 8becf5cfeb897108b35cdd996793e7f1df2f28fd
F test/table.test eaa25951c0f18615763cd3dc248ea4bc38739c05
F test/tester.tcl e24caef6d07c58c16b24e7afc967464b288a4065
F test/update.test 72c0c93310483b86dc904a992220c5b84c7ce100
F test/vacuum.test 2127748ff4ddb409212efbb6d9fb9c469ea1b49c
F test/where.test bbab5a308055fb6087dc23d600b4ad2b72797397
F tool/gdbmdump.c 529e67c78d920606ba196326ea55b57b75fcc82b
F tool/gdbmstat.c 56a9033531e5f5a48413f6ec436d5fb0341632c1
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 tool/report1.txt 9eae07f26a8fc53889b45fc833a66a33daa22816
F www/arch.fig 4f246003b7da23bd63b8b0af0618afb4ee3055c8
F www/arch.png 8dae0766d42ed3de9ed013c1341a5792bcf633e6
F www/arch.tcl a40380c1fe0080c43e6cc5c20ed70731511b06be
F www/c_interface.tcl 03f5fe8f2da9713aa1748f7e4ce34a0c35926ac9
F www/changes.tcl fcd35239062ac03d6169506ae8ef19189979c8f1
F www/crosscompile.tcl bee79c34f6c3f162ec1c6f5294e79f73651d27ee
F www/c_interface.tcl 11be2d5826eb7d6efd629751d3b483c1ed78ba14
F www/changes.tcl a8cbb3496ae35184fabc5f7b47242bf33a5637d1
F www/crosscompile.tcl c99efacb3aefaa550c6e80d91b240f55eb9fd33e
F www/dynload.tcl 02eb8273aa78cfa9070dd4501dca937fb22b466c
F www/fileformat.tcl cfb7fba80b7275555281ba2f256c00734bcdd1c9
F www/index.tcl b19418d506f90968deef972bf1b427d98bdf13e0
F www/lang.tcl 9192e114b19987e630a41e879585b87006eb84a1
F www/index.tcl 0ca6421e6e82b17ed0c1779d46463211498f9d12
F www/lang.tcl e3905bec9f0d0fd47d9838e991cab7d6f7aff47d
F www/mingw.tcl fc5f4ba9d336b6e8c97347cc6496d6162461ef60
F www/opcode.tcl cb3a1abf8b7b9be9f3a228d097d6bf8b742c2b6f
F www/sqlite.tcl cb0d23d8f061a80543928755ec7775da6e4f362f
F www/tclsqlite.tcl ae101d5f7c07dcc59770e2a84aae09025fab2dad
F www/vdbe.tcl bcbfc33bcdd0ebad95eab31286adb9e1bc289520
P 217a8af6d0f4396b755b7d21f94ae6fe002728a1
R 0e3d2b823401c2dde691e21fb25d6652
F www/tclsqlite.tcl 06f81c401f79a04f2c5ebfb97e7c176225c0aef2
F www/vdbe.tcl 0c8aaa529dd216ccbf7daaabd80985e413d5f9ad
P 8f79ca2acaf765ecb3274c353284c55b6790f657
R 360be56ced6760c85770c34d6c105ac8
U drh
Z eae7458cfefbc493be1d4a350c873f88
Z 7f6063d380c9dc73dddd8b75f41104c4
+1 -1
View File
@@ -1 +1 @@
e0c9e80bdbaf672c72ead7827946bb9ac6b7ec32
34b17e6ce15dd24dc2ca609ef9c136fe4dc1b4b5
+35
View File
@@ -0,0 +1,35 @@
* Finish fleshing out the db.c file.
- sqliteDbReadOvfl
- sqliteDbSpreadLoad
- sqliteDbSplit
- sqliteDbNextIndexLevel
- fix sqliteDbCursorNext to work right after sqliteDbCursorDelete
* Compile db.c with -Wall and get no errors.
* Make a pass over pg.c and db.c looking for errors.
- correct handling of I/O errors, malloc failures, etc.
- page leaks (not calling sqlitePgUnref)
* Write a test interface for db.c.
* Compile and link against the db test interface.
* Generate tests for the db interface.
* Add read/write locks to pg.c
* Add an sqliteDbReorganize() function.
* Integrate db into vdbe.
* Modify code generation to take advantage of the new db interface.
- Able to delete without disturbing scan order
- Now keeps a count of number of table entries
+ Special processing for count(*)
+ Better selection of indices on a select
- Transactions
* Modify sqlite_master to store the table number.
* Add a cache in DbCursor to speed up the sqliteDbReadOvfl() routine.
* Add cache information to speed up sqliteDbCursorMoveTo().
Longer term:
* Document all the changes and release Sqlite 2.0.
* Techniques for optimizing querys by grouping data with similar
indices.
* "OPTIMIZE select" statement to automatically create and/or tune
indices.
* "CREATE INDEX FOR select" to automatically generate needed indices.
* "VACUUM table USING index".
* Parse and use constraints.
+16 -14
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.25 2001/01/15 22:51:09 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;
}
}
@@ -339,7 +341,7 @@ void sqliteEndTable(Parse *pParse, Token *pEnd){
*/
if( !pParse->initFlag ){
static VdbeOp addTable[] = {
{ OP_Open, 0, 1, MASTER_NAME },
{ OP_OpenTbl, 0, 1, MASTER_NAME },
{ OP_New, 0, 0, 0},
{ OP_String, 0, 0, "table" },
{ OP_String, 0, 0, 0}, /* 3 */
@@ -414,7 +416,7 @@ void sqliteDropTable(Parse *pParse, Token *pName){
v = sqliteGetVdbe(pParse);
if( v ){
static VdbeOp dropTable[] = {
{ OP_Open, 0, 1, MASTER_NAME },
{ OP_OpenTbl, 0, 1, MASTER_NAME },
{ OP_ListOpen, 0, 0, 0},
{ OP_String, 0, 0, 0}, /* 2 */
{ OP_Next, 0, ADDR(10), 0}, /* 3 */
@@ -591,7 +593,7 @@ void sqliteCreateIndex(
*/
if( pParse->initFlag==0 ){
static VdbeOp addTable[] = {
{ OP_Open, 2, 1, MASTER_NAME},
{ OP_OpenTbl, 2, 1, MASTER_NAME},
{ OP_New, 2, 0, 0},
{ OP_String, 0, 0, "index"},
{ OP_String, 0, 0, 0}, /* 3 */
@@ -608,8 +610,8 @@ void sqliteCreateIndex(
v = sqliteGetVdbe(pParse);
if( v==0 ) goto exit_create_index;
sqliteVdbeAddOp(v, OP_Open, 0, 0, pTab->zName, 0);
sqliteVdbeAddOp(v, OP_Open, 1, 1, pIndex->zName, 0);
sqliteVdbeAddOp(v, OP_OpenTbl, 0, 0, pTab->zName, 0);
sqliteVdbeAddOp(v, OP_OpenIdx, 1, 1, pIndex->zName, 0);
if( pStart && pEnd ){
int base;
n = (int)pEnd->z - (int)pStart->z + 1;
@@ -668,7 +670,7 @@ void sqliteDropIndex(Parse *pParse, Token *pName){
v = sqliteGetVdbe(pParse);
if( v ){
static VdbeOp dropIndex[] = {
{ OP_Open, 0, 1, MASTER_NAME},
{ OP_OpenTbl, 0, 1, MASTER_NAME},
{ OP_ListOpen, 0, 0, 0},
{ OP_String, 0, 0, 0}, /* 2 */
{ OP_Next, 0, ADDR(9), 0}, /* 3 */
@@ -841,9 +843,9 @@ void sqliteCopy(
addr = sqliteVdbeAddOp(v, OP_FileOpen, 0, 0, 0, 0);
sqliteVdbeChangeP3(v, addr, pFilename->z, pFilename->n);
sqliteVdbeDequoteP3(v, addr);
sqliteVdbeAddOp(v, OP_Open, 0, 1, pTab->zName, 0);
sqliteVdbeAddOp(v, OP_OpenTbl, 0, 1, pTab->zName, 0);
for(i=1, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){
sqliteVdbeAddOp(v, OP_Open, i, 1, pIdx->zName, 0);
sqliteVdbeAddOp(v, OP_OpenIdx, i, 1, pIdx->zName, 0);
}
end = sqliteVdbeMakeLabel(v);
addr = sqliteVdbeAddOp(v, OP_FileRead, pTab->nCol, end, 0, 0);
+89 -631
View File
@@ -30,124 +30,18 @@
** 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.23 2001/01/15 22:51:10 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,522 +53,15 @@ 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;
return sqliteGdbmOpen(zName, writeFlag, createFlag, pzErrMsg);
}
/*
@@ -684,7 +71,7 @@ int sqliteDbbeDelete(DbbeCursor *pCursr, int nKey, char *pKey){
** 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){
int sqliteDbbeOpenTempFile(const char *zDir, 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 */
@@ -706,10 +93,10 @@ int sqliteDbbeOpenTempFile(Dbbe *pBe, FILE **ppFile){
limit = 4;
zFile = 0;
do{
randomName(&pBe->rc4, zBuf, "/_temp_file_");
sqliteRandomName(zBuf, "/_temp_file_");
sqliteFree(zFile);
zFile = 0;
sqliteSetString(&zFile, pBe->zDir, zBuf, 0);
sqliteSetString(&zFile, zDir, zBuf, 0);
}while( access(zFile,0)==0 && limit-- >= 0 );
*ppFile = pBe->apTemp[i] = fopen(zFile, "w+");
if( pBe->apTemp[i]==0 ){
@@ -723,7 +110,7 @@ int sqliteDbbeOpenTempFile(Dbbe *pBe, FILE **ppFile){
}
/*
** Close a temporary file opened using sqliteDbbeOpenTempFile()
** Close a temporary file opened using sqliteGdbmOpenTempFile()
*/
void sqliteDbbeCloseTempFile(Dbbe *pBe, FILE *f){
int i;
@@ -738,3 +125,74 @@ void sqliteDbbeCloseTempFile(Dbbe *pBe, FILE *f){
}
fclose(f);
}
/*
** Close all temporary files that happen to still be open.
** This routine is called when the database is being closed.
*/
void sqliteDbbeCloseAllTempFiles(Dbbe *pBe){
int i;
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);
}
/*
** 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.
**
** zDir is the name of the directory in which the file should
** be located. zSuffix is the filename extension to use for
** the file.
*/
char *sqliteDbbeNameToFile(
const char *zDir, /* Directory containing the file */
const char *zTable, /* Name of the SQL table that the file contains */
const char *zSuffix /* Suffix for the file. Includes the "." */
){
char *zFile = 0;
int i, k, c;
int nChar = 0;
for(i=0; (c = zTable[i])!=0; i++){
if( !isalnum(c) && c!='_' ){
nChar += 3;
}else{
nChar ++;
}
}
nChar += strlen(zDir) + strlen(zSuffix) + 2;
zFile = sqliteMalloc( nChar );
if( zFile==0 ) return 0;
for(i=0; (c = zDir[i])!=0; i++){
zFile[i] = c;
}
zFile[i++] = '/';
for(k=0; (c = zTable[k])!=0; k++){
if( isupper(c) ){
zFile[i++] = tolower(c);
}else if( isalnum(c) || c=='_' ){
zFile[i++] = c;
}else{
zFile[i++] = '~';
zFile[i++] = "0123456789abcdef"[c & 0xf];
zFile[i++] = "0123456789abcdef"[(c>>8)&0xf];
}
}
for(k=0; (c = zSuffix[k])!=0; k++){
zFile[i++] = c;
}
zFile[i] = 0;
return zFile;
}
+114 -79
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.10 2001/01/15 22:51:10 drh Exp $
*/
#ifndef _SQLITE_DBBE_H_
#define _SQLITE_DBBE_H_
@@ -53,91 +53,126 @@
*/
typedef struct Dbbe Dbbe;
typedef struct DbbeCursor DbbeCursor;
typedef struct DbbeMethods DbbeMethods;
/*
** The 18 interface routines.
** 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.
*/
/* Open a complete database */
Dbbe *sqliteDbbeOpen(const char *zName, int write, int create, char **pzErr);
/* Close the whole database. */
void sqliteDbbeClose(Dbbe*);
/*
** Each of the various SQLite backends defines a set of methods for
** accessing the database. Pointers to the methods are contained in
** an instance of the following structure. A pointer to a static instance
** of this structure is assigned to the Dbbe structure that sqlileDbbeOpen
** returns.
*/
struct DbbeMethods {
/* Close the whole database. */
void (*Close)(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.
/* 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.
**
** The keyType parameter is TRUE if this table will only be accessed
** using integer keys. This parameter allows the database backend to
** use a faster algorithm for the special case of integer keys, if it
** wants to.
**
** 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,
int intKeyOnly, DbbeCursor**);
/* Delete a table from the database */
void (*DropTable)(Dbbe*, const char *zTableName);
/* Reorganize a table to speed access or reduce its disk usage */
int (*ReorganizeTable)(Dbbe*, const char *zTableName);
/* Close a cursor */
void (*CloseCursor)(DbbeCursor*);
/* 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);
/* 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 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);
/* 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);
/* Return the length of the most recently fetched key or data. */
int (*KeyLength)(DbbeCursor*);
int (*DataLength)(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*);
/* Make it so that the next call to Dbbe.NextKey() returns
** the first entry of the table. */
int (*Rewind)(DbbeCursor*);
/* Get a new integer key for this table. */
int (*New)(DbbeCursor*);
/* 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);
/* Remove an entry from the table */
int (*Delete)(DbbeCursor*, int nKey, char *pKey);
/* Open a file suitable for temporary storage */
int (*OpenTempFile)(Dbbe*, FILE**);
/* Close a temporary file */
void (*CloseTempFile)(Dbbe *, FILE *);
};
/*
** This is the structure returned by sqliteDbbeOpen(). It contains
** information common to all the different backend drivers.
**
** If zName is 0 or "", then a temporary file is created that
** will be deleted when closed.
** The information in this structure (with the exception the method
** pointers in the Dbbe.x field) is intended to be visible to
** the backend drivers only. Users should not access or modify
** this structure in any way other than the read the method pointers
** in Dbbe.x.
*/
int sqliteDbbeOpenCursor(Dbbe*, const char *zName, int writeable, DbbeCursor**);
/* Delete a table from the database */
void sqliteDbbeDropTable(Dbbe*, const char *zTableName);
/* Reorganize a table to speed access or reduce its disk usage */
int sqliteDbbeReorganizeTable(Dbbe*, const char *zTableName);
/* Close a cursor */
void sqliteDbbeCloseCursor(DbbeCursor*);
/* 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);
/* Return 1 if the given key is already in the table. Return 0
** if it is not.
*/
int sqliteDbbeTest(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);
/* 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);
/* Return the length of the most recently fetched key or data. */
int sqliteDbbeKeyLength(DbbeCursor*);
int sqliteDbbeDataLength(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
** sqliteDbbeRewind(). The return value is 1 if there is another
** entry, or 0 if there are no more entries. */
int sqliteDbbeNextKey(DbbeCursor*);
/* Make it so that the next call to sqliteDbbeNextKey() returns
** the first entry of the table. */
int sqliteDbbeRewind(DbbeCursor*);
/* Get a new integer key for this table. */
int sqliteDbbeNew(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);
/* Remove an entry from the table */
int sqliteDbbeDelete(DbbeCursor*, int nKey, char *pKey);
/* Open a file suitable for temporary storage */
int sqliteDbbeOpenTempFile(Dbbe*, FILE**);
/* Close a temporary file */
void sqliteDbbeCloseTempFile(Dbbe *, FILE *);
struct Dbbe {
struct DbbeMethods *x; /* Backend-specific methods for database access */
int nTemp; /* Number of temporary files created */
FILE **apTemp; /* Space to hold temporary file pointers */
char **azTemp; /* Names of the temporary files */
};
#endif /* defined(_SQLITE_DBBE_H_) */
+638
View File
@@ -0,0 +1,638 @@
/*
** 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.3 2001/01/15 22:51:10 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 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 */
int write; /* True for write permission */
BeFile *pOpen; /* List of open files */
char *zDir; /* Directory hold the database */
};
/*
** 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 */
};
/*
** 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;
for(pFile=pBe->pOpen; pFile; pFile=pNext){
pNext = pFile->pNext;
gdbm_close(pFile->dbf);
memset(pFile, 0, sizeof(*pFile));
sqliteFree(pFile);
}
sqliteDbbeCloseAllTempFiles(pDbbe);
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;
}
/*
** 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 */
int intKeyOnly, /* True if only integer keys are used */
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;
char zRandom[50];
zFile = 0;
limit = 5;
do {
sqliteRandomName(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, 0, &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;
if( pCursr->pFile==0 || pCursr->pFile->dbf==0 ) return 1;
while( go ){
iKey = sqliteRandomInteger();
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, /* Write to the database associated with this cursor */
int nKey, /* Number of bytes in the key */
char *pKey, /* The data for the key */
int nData, /* Number of bytes of data */
char *pData /* The data */
){
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 is located in the same directory
** as the rest of the database.
*/
static int sqliteGdbmOpenTempFile(Dbbe *pDbbe, FILE **ppFile){
Dbbex *pBe = (Dbbex*)pDbbe;
return sqliteDbbeOpenTempFile(pBe->zDir, pDbbe, ppFile);
}
/*
** This variable contains pointers to all of the access methods
** used to implement the GDBM backend.
*/
static struct DbbeMethods gdbmMethods = {
/* n Close */ sqliteGdbmClose,
/* OpenCursor */ sqliteGdbmOpenCursor,
/* DropTable */ sqliteGdbmDropTable,
/* ReorganizeTable */ sqliteGdbmReorganizeTable,
/* CloseCursor */ sqliteGdbmCloseCursor,
/* Fetch */ sqliteGdbmFetch,
/* Test */ sqliteGdbmTest,
/* CopyKey */ sqliteGdbmCopyKey,
/* CopyData */ sqliteGdbmCopyData,
/* ReadKey */ sqliteGdbmReadKey,
/* ReadData */ sqliteGdbmReadData,
/* KeyLength */ sqliteGdbmKeyLength,
/* DataLength */ sqliteGdbmDataLength,
/* NextKey */ sqliteGdbmNextKey,
/* Rewind */ sqliteGdbmRewind,
/* New */ sqliteGdbmNew,
/* Put */ sqliteGdbmPut,
/* Delete */ sqliteGdbmDelete,
/* OpenTempFile */ sqliteGdbmOpenTempFile,
/* CloseTempFile */ sqliteDbbeCloseTempFile
};
/*
** 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.x = &gdbmMethods;
pNew->zDir = (char*)&pNew[1];
strcpy(pNew->zDir, zName);
pNew->write = writeFlag;
pNew->pOpen = 0;
return &pNew->dbbe;
}
+553 -528
View File
File diff suppressed because it is too large Load Diff
+3 -3
View File
@@ -24,7 +24,7 @@
** This file contains C code routines that are called by the parser
** to handle DELETE FROM statements.
**
** $Id: delete.c,v 1.6 2000/06/21 13:59:11 drh Exp $
** $Id: delete.c,v 1.7 2001/01/15 22:51:10 drh Exp $
*/
#include "sqliteInt.h"
@@ -105,9 +105,9 @@ void sqliteDeleteFrom(
*/
base = pParse->nTab;
sqliteVdbeAddOp(v, OP_ListRewind, 0, 0, 0, 0);
sqliteVdbeAddOp(v, OP_Open, base, 1, pTab->zName, 0);
sqliteVdbeAddOp(v, OP_OpenTbl, base, 1, pTab->zName, 0);
for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){
sqliteVdbeAddOp(v, OP_Open, base+i, 1, pIdx->zName, 0);
sqliteVdbeAddOp(v, OP_OpenIdx, base+i, 1, pIdx->zName, 0);
}
end = sqliteVdbeMakeLabel(v);
addr = sqliteVdbeAddOp(v, OP_ListRead, 0, end, 0, 0);
+9
View File
@@ -0,0 +1,9 @@
This directory is intended to hold "experimental" files.
The code in this directory does not necessary work. It may
or may not be added to future SQLite releases. We just need
a place to put ideas and works-in-progress and so this
directory was created.
If you are interested in the production versions of SQLite,
you can safely ignore this directory.
+1495
View File
File diff suppressed because it is too large Load Diff
+51
View File
@@ -0,0 +1,51 @@
/*
** Copyright (c) 2001 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/
**
*************************************************************************
** $Id: db.h,v 1.1 2001/02/11 16:56:24 drh Exp $
*/
typedef struct Db Db;
typedef struct DbCursor DbCursor;
int sqliteDbOpen(const char *filename, Db**);
int sqliteDbClose(Db*);
int sqliteDbBeginTransaction(Db*);
int sqliteDbCommit(Db*);
int sqliteDbRollback(Db*);
int sqliteDbCreateTable(Db*, int *pTblno);
int sqliteDbDropTable(Db*, int tblno);
int sqliteDbCursorOpen(Db*, int tblno, DbCursor**);
int sqliteDbCursorClose(DbCursor*);
int sqliteDbCursorFirst(DbCursor*);
int sqliteDbCursorNext(DbCursor*);
int sqliteDbCursorDatasize(DbCursor*);
int sqliteDbCursorKeysize(DbCursor*);
int sqliteDbCursorRead(DbCursor*, int amt, int offset, void *buf);
int sqliteDbCursorReadKey(DbCursor*, int amt, int offset, void *buf);
int sqliteDbCursorWrite(DbCursor*, int amt, int offset, const void *buf);
int sqliteDbCursorFind(DbCursor*, int nKey, const void *pKey, int createFlag);
int sqliteDbCursorResize(DbCursor*, int nData);
+618
View File
@@ -0,0 +1,618 @@
/*
** 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 Berkeley Database version 1.85 as the database backend.
**
** $Id: dbbebdb1.c,v 1.1 2001/02/11 16:56:24 drh Exp $
*/
#ifdef USE_BDB2
#include "sqliteInt.h"
#include <sys/types.h>
#include <limits.h>
#include <db.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 */
DB dbf; /* The file itself */
int nRef; /* Number of references */
int delOnClose; /* Delete when closing */
int writeable; /* Opened for writing */
DbbeCursor *pCursor; /* Which of several DbbeCursors has the file cursor */
BeFile *pNext, *pPrev; /* Next and previous on list of open files */
};
/*
** The following structure contains all information used by BDB2
** database driver. This is a subclass of the Dbbe structure.
*/
typedef struct Dbbex Dbbex;
struct Dbbex {
Dbbe dbbe; /* The base class */
int write; /* True for write permission */
BeFile *pOpen; /* List of open files */
char *zDir; /* Directory hold the database */
};
/*
** 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 */
DBT key; /* Most recently used key */
DBT data; /* Most recent data */
int needRewind; /* Next key should be the first */
int readPending; /* The fetch hasn't actually been done yet */
};
/*
** The "mkdir()" function only takes one argument under Windows.
*/
#if OS_WIN
# define mkdir(A,B) mkdir(A)
#endif
/*
** Forward declaration
*/
static void sqliteBdb1CloseCursor(DbbeCursor *pCursr);
/*
** Completely shutdown the given database. Close all files. Free all memory.
*/
static void sqliteBdb1Close(Dbbe *pDbbe){
Dbbex *pBe = (Dbbex*)pDbbe;
BeFile *pFile, *pNext;
for(pFile=pBe->pOpen; pFile; pFile=pNext){
pNext = pFile->pNext;
(*pFile->dbf)(pFile->dbf);
memset(pFile, 0, sizeof(*pFile));
sqliteFree(pFile);
}
sqliteDbbeCloseAllTempFiles(pDbbe);
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){
return sqliteDbbeNameToFile(pBe->zDir, zTable, ".tbl");
}
/*
** 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 sqliteBdb1OpenCursor(
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 */
int intKeyOnly, /* True if only integer keys are used */
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 open_flags; /* Flags passed to dbopen() */
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 ){
open_flags = O_RDWR|O_CREAT
}else{
open_flags = O_RDONLY;
}
pFile = sqliteMalloc( sizeof(*pFile) );
if( pFile==0 ){
sqliteFree(zFile);
return SQLITE_NOMEM;
}
if( zFile ){
if( !writeable || pBe->write ){
pFile->dbf = dbopen(zFile, open_flags, DB_HASH, 0);
}else{
pFile->dbf = 0;
}
}else{
int limit;
char zRandom[50];
zFile = 0;
limit = 5;
do {
sqliteRandomName(zRandom, "_temp_table_");
sqliteFree(zFile);
zFile = sqliteFileOfTable(pBe, zRandom);
pFile->dbf = dbopen(zFile, open_flags, DB_HASH, 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->pCursor = 0;
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 ){
sqliteBdb1CloseCursor(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 sqliteBdb1DropTable(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 sqliteBdb1OpenCursor().
**
** 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 sqliteBdb1CloseCursor(DbbeCursor *pCursr){
BeFile *pFile;
Dbbex *pBe;
if( pCursr==0 ) return;
pFile = pCursr->pFile;
pBe = pCursr->pBe;
if( pFile->pCursor==pCursr ){
pFile->pCursor = 0;
}
pFile->nRef--;
if( pFile->dbf!=NULL ){
(*pFile->dbf->sync)(pFile->dbf, 0);
}
if( pFile->nRef<=0 ){
if( pFile->dbf!=NULL ){
(*pFile->dbf->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 sqliteBdb1ReorganizeTable(Dbbe *pBe, const char *zTable){
/* No-op */
return SQLITE_OK;
}
/*
** Clear the given datum
*/
static void datumClear(datum *p){
if( p->dptr ) free(p->dptr); ########
p->data = 0;
p->size = 0;
}
/*
** Fetch a single record from an open cursor. Return 1 on success
** and 0 on failure.
*/
static int sqliteBdb1Fetch(DbbeCursor *pCursr, int nKey, char *pKey){
DBT key;
key.size = nKey;
key.data = 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 sqliteBdb1Test(DbbeCursor *pCursr, int nKey, char *pKey){
DBT 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 sqliteBdb1CopyKey(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 sqliteBdb1CopyData(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 *sqliteBdb1ReadKey(DbbeCursor *pCursr, int offset){
if( offset<0 || offset>=pCursr->key.dsize ) return "";
return &pCursr->key.dptr[offset];
}
static char *sqliteBdb1ReadData(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 sqliteBdb1KeyLength(DbbeCursor *pCursr){
return pCursr->key.dsize;
}
static int sqliteBdb1DataLength(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 sqliteBdb1Rewind(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 sqliteBdb1NextKey(DbbeCursor *pCursr){
DBT 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 sqliteBdb1New(DbbeCursor *pCursr){
int iKey;
DBT key;
int go = 1;
if( pCursr->pFile==0 || pCursr->pFile->dbf==0 ) return 1;
while( go ){
iKey = sqliteRandomInteger();
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 sqliteBdb1Put(
DbbeCursor *pCursr, /* Write to the database associated with this cursor */
int nKey, /* Number of bytes in the key */
char *pKey, /* The data for the key */
int nData, /* Number of bytes of data */
char *pData /* The data */
){
DBT 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 sqliteBdb1Delete(DbbeCursor *pCursr, int nKey, char *pKey){
DBT 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 is located in the same directory
** as the rest of the database.
*/
static int sqliteBdb1OpenTempFile(Dbbe *pDbbe, FILE **ppFile){
Dbbex *pBe = (Dbbex*)pDbbe;
return sqliteDbbeOpenTempFile(pBe->zDir, pDbbe, ppFile);
}
/*
** This variable contains pointers to all of the access methods
** used to implement the GDBM backend.
*/
static struct DbbeMethods gdbmMethods = {
/* n Close */ sqliteBdb1Close,
/* OpenCursor */ sqliteBdb1OpenCursor,
/* DropTable */ sqliteBdb1DropTable,
/* ReorganizeTable */ sqliteBdb1ReorganizeTable,
/* CloseCursor */ sqliteBdb1CloseCursor,
/* Fetch */ sqliteBdb1Fetch,
/* Test */ sqliteBdb1Test,
/* CopyKey */ sqliteBdb1CopyKey,
/* CopyData */ sqliteBdb1CopyData,
/* ReadKey */ sqliteBdb1ReadKey,
/* ReadData */ sqliteBdb1ReadData,
/* KeyLength */ sqliteBdb1KeyLength,
/* DataLength */ sqliteBdb1DataLength,
/* NextKey */ sqliteBdb1NextKey,
/* Rewind */ sqliteBdb1Rewind,
/* New */ sqliteBdb1New,
/* Put */ sqliteBdb1Put,
/* Delete */ sqliteBdb1Delete,
/* OpenTempFile */ sqliteBdb1OpenTempFile,
/* CloseTempFile */ sqliteDbbeCloseTempFile
};
/*
** 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 *sqliteBdb1Open(
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.x = &gdbmMethods;
pNew->zDir = (char*)&pNew[1];
strcpy(pNew->zDir, zName);
pNew->write = writeFlag;
pNew->pOpen = 0;
return &pNew->dbbe;
}
+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: dbbemird.c,v 1.1 2001/02/11 16:56:24 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;
}
+708
View File
@@ -0,0 +1,708 @@
/*
** Copyright (c) 2001 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/
**
*************************************************************************
** $Id: pg.c,v 1.1 2001/02/11 16:56:24 drh Exp $
*/
#include <assert.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include "sqliteInt.h"
#include "pg.h"
/*
** Uncomment the following for a debug trace
*/
#if 1
# define TRACE(X) printf X; fflush(stdout);
#endif
/*
** Hash table sizes
*/
#define J_HASH_SIZE 127 /* Size of the journal page hash table */
#define PG_HASH_SIZE 349 /* Size of the database page hash table */
/*
** Forward declaration of structure
*/
typedef struct Pghdr Pghdr;
/*
** All information about a single paging file is contained in an
** instance of the following structure.
*/
struct Pgr {
int fdMain; /* The main database file */
char *zMain; /* Name of the database file */
int fdJournal; /* The journal file */
char *zJournal; /* Name of the journal file */
int nMemPg; /* Number of memory-resident pages */
int nJPg; /* Number of pages in the journal */
int nDbPg; /* Number of pages in the database */
int nRefPg; /* Number of pages currently in use */
Pghdr *pLru, *pMru; /* Least and most recently used mem-page */
Pghdr *pJidx; /* List of journal index pages */
Pghdr *pAll; /* All pages, except journal index pages */
u32 aJHash[J_HASH_SIZE]; /* Journal page hash table */
Pghdr *aPgHash[PG_HASH_SIZE]; /* Mem-page hash table */
};
/*
** Each memory-resident page of the paging file has a header which
** is an instance of the following structure.
*/
struct Pghdr {
Pgr *p; /* Pointer back to the Pgr structure */
int nRef; /* Number of references to this page */
int isDirty; /* TRUE if needs to be written to disk */
u32 dbpgno; /* Page number in the database file */
u32 jpgno; /* Page number in the journal file */
Pghdr *pNx; /* Next page on a list of them all */
Pghdr *pLru; /* Less recently used pages */
Pghdr *pMru; /* More recently used pages */
Pghdr *pNxHash; /* Next with same dbpgno hash */
Pghdr *pPvHash; /* Previous with the same dbpgno hash */
};
/*
** For a memory-resident page, the page data comes immediately after
** the page header. The following macros can be used to change a
** pointer to a page header into a pointer to the data, or vice
** versa.
*/
#define PG_TO_DATA(X) ((void*)&(X)[1])
#define DATA_TO_PG(X) (&((Pghdr*)(X))[-1])
/*
** The number of in-memory pages that we accumulate before trying
** to reuse older pages when new ones are requested.
*/
#define MX_MEM_PAGE 100
/*
** The number of journal data pages that come between consecutive
** journal index pages.
*/
#define N_J_DATAPAGE (SQLITE_PAGE_SIZE/(2*sizeof(u32)))
/*
** An index page in the journal consists of an array of N_J_DATAPAGE
** of the following structures. There is one instance of the following
** structure for each of the N_J_DATAPAGE data pages that follow the
** index.
**
** Let the journal page number that a JidxEntry describes be J. Then
** the JidxEntry.dbpgno field is the page of the database file that
** corresponds to the J page in the journal. The JidxEntry.next_jpgno
** field hold the number of another journal page that contains
** a database file page with the same hash as JidxEntry.dbpgno.
**
** All information is written to the journal index in big-endian
** notation.
*/
typedef struct JidxEntry JidxEntry;
struct JidxEntry {
char dbpgno[sizeof(u32)]; /* Database page number for this entry */
char next_jpgno[sizeof(u32)]; /* Next entry with same hash on dbpgno */
};
/*
** Read a page from a file into memory. Return SQLITE_OK if successful.
** The "pgno" parameter tells where in the file to read the page.
** The first page is 1. Files do not contain a page 0 since a page
** number of 0 is used to indicate "no such page".
*/
static int sqlitePgRead(int fd, char *zBuf, u32 pgno){
int got = 0;
int amt;
assert( pgno>0 );
assert( fd>=0 );
lseek(fd, SEEK_SET, (pgno-1)*SQLITE_PAGE_SIZE);
while( got<SQLITE_PAGE_SIZE ){
amt = read(fd, &zBuf[got], SQLITE_PAGE_SIZE - got);
if( amt<=0 ){
memset(&zBuf[got], 0, SQLITE_PAGE_SIZE - got);
return amt==0 ? SQLITE_OK : SQLITE_IOERR;
}
got += amt;
}
return SQLITE_OK;
}
/*
** Read a page from a file into memory. Return SQLITE_OK if successful.
** The "pgno" parameter tells where in the file to write the page.
** The first page is 1. Files do not contain a page 0 since a page
** number of 0 is used to indicate "no such page".
*/
static int sqlitePgWrite(int fd, char *zBuf, u32 pgno){
int done = 0;
int amt;
assert( pgno>0 );
assert( fd>=0 );
lseek(fd, SEEK_SET, (pgno-1)*SQLITE_PAGE_SIZE);
while( done<SQLITE_PAGE_SIZE ){
amt = write(fd, &zBuf[done], SQLITE_PAGE_SIZE - done);
if( amt<=0 ) return SQLITE_IOERR;
done += amt;
}
return SQLITE_OK;
}
/*
** Turn four bytes into an integer. The first byte is always the
** most significant 8 bits.
*/
static u32 sqlitePgGetInt(const char *p){
return ((p[0]&0xff)<<24) | ((p[1]&0xff)<<16) | ((p[2]&0xff)<<8) | (p[3]&0xff);
}
/*
** Turn an integer into 4 bytes. The first byte is always the
** most significant 8 bits.
*/
static void sqlitePgPutInt(u32 v, char *p){
p[3] = v & 0xff;
v >>= 8;
p[2] = v & 0xff;
v >>= 8;
p[1] = v & 0xff;
v >>= 8;
p[0] = v & 0xff;
}
/*
** Check the hash table for an in-memory page. Return a pointer to
** the page header if found. Return NULL if the page is not in memory.
*/
static Pghdr *sqlitePgFind(Pgr *p, u32 pgno){
int h;
Pghdr *pPg;
if( pgno==0 ) return 0;
h = pgno % PG_HASH_SIZE;
for(pPg = p->aPgHash[h]; pPg; pPg=pPg->pNxHash){
if( pPg->dbpgno==pgno ) return pPg;
}
TRACE(("PG: data page %u is %#x\n", pgno, (u32)pPg));
return 0;
}
/*
** Locate and return an index page from the journal.
**
** The first page of a journal is the primary index. Additional
** index pages are called secondary indices. Index pages appear
** in the journal as often as needed. (If SQLITE_PAGE_SIZE==1024,
** then there are 1024/sizeof(int)*2 = 128 database between each
** pair of index pages.) Journal index pages are not hashed and
** do no appear on the Pgr.pAll list. Index pages are on the
** Pgr.pJidx list only. Index pages have Pghdr.dbpgno==0.
**
** If the requested index page is not already in memory, then a
** new memory page is created to hold the index.
**
** This routine will return a NULL pointer if we run out of memory.
*/
static Pghdr *sqlitePgFindJidx(Pgr *p, u32 pgno){
Pghdr *pPg;
assert( pgno % (N_J_DATAPAGE+1) == 1 );
for(pPg=p->pJidx; pPg; pPg=pPg->pNx){
if( pPg->jpgno==pgno ){
TRACE(("PG: found j-index %u at %#x\n", pgno, (u32)pPg));
return pPg;
}
}
pPg = sqliteMalloc( sizeof(Pghdr)+SQLITE_PAGE_SIZE );
if( pPg==0 ) return 0;
pPg->jpgno = pgno;
pPg->pNx = p->pJidx;
p->pJidx = pPg;
sqlitePgRead(p->fdJournal, PG_TO_DATA(pPg), pgno);
TRACE(("PG: create j-index %u at %#x\n", pgno, (u32)pPg));
return pPg;
}
/*
** Look in the journal to see if the given database page is stored
** in the journal. If it is, return its journal page number. If
** not, return 0.
*/
static u32 sqlitePgJournalPageNumber(Pgr *p, u32 dbpgno){
u32 jpgno;
if( dbpgno==0 ) return 0;
jpgno = p->aJHash[dbpgno % J_HASH_SIZE];
while( jpgno!=0 ){
int idx_num; /* Which journal index describes page jpgno */
int ipgno; /* Page number for the journal index */
int idx_slot; /* Which entry in index idx_num describes jpgno */
Pghdr *pIdxPg; /* The index page for jpgno */
JidxEntry *aIdx; /* The data for the index page */
idx_num = (jpgno - 1)/(N_J_DATAPAGE + 1);
idx_slot = (jpgno - 1) % (N_J_DATAPAGE + 1) - 2;
ipgno = idx_num * (N_J_DATAPAGE + 1) + 1;
if( ipgno>p->nJPg ){
jpgno = 0;
break;
}
pIdxPg = sqlitePgFindJidx(p, ipgno);
assert( pIdxPg!=0 );
aIdx = PG_TO_DATA(pIdxPg);
if( dbpgno==sqlitePgGetInt(aIdx[idx_slot].dbpgno) ){
break;
}
jpgno = sqlitePgGetInt(aIdx[idx_slot].next_jpgno);
}
return jpgno;
}
/*
** Make a page not dirty by writing it to the journal.
*/
static int sqlitePgMakeClean(Pghdr *pPg){
Pgr *p = pPg->p;
int rc;
assert( pPg->isDirty );
assert( p->fdJournal>=0 );
if( pPg->jpgno==0 ){
int jpgno; /* A newly allocate page in the journal */
int idx_num; /* Which journal index describes page jpgno */
int idx_slot; /* Which entry in index idx_num describes jpgno */
Pghdr *pIdxPg; /* The index page for jpgno */
JidxEntry *aIdx; /* The data for the index page */
int h; /* The hash value for pPg->dbpgno */
jpgno = p->nJPg + 1;
if( jpgno % (N_J_DATAPAGE + 1) == 1 ){
jpgno++;
}
idx_num = (jpgno - 1)/(N_J_DATAPAGE + 1);
idx_slot = (jpgno - 1) % (N_J_DATAPAGE + 1) - 2;
pIdxPg = sqlitePgFindJidx(p, idx_num * (N_J_DATAPAGE + 1) + 1);
assert( pIdxPg!=0 );
aIdx = PG_TO_DATA(pIdxPg);
sqlitePgPutInt(pPg->dbpgno, aIdx[idx_slot].dbpgno);
h = pPg->dbpgno % J_HASH_SIZE;
sqlitePgPutInt(p->aJHash[h], aIdx[idx_slot].next_jpgno);
p->aJHash[h] = jpgno;
p->nJPg = jpgno;
pPg->jpgno = jpgno;
TRACE(("PG: assign d-page %u to j-page %u\n", jpgno, pPg->dbpgno));
}
rc = sqlitePgWrite(p->fdJournal, PG_TO_DATA(pPg), pPg->jpgno);
if( rc==SQLITE_OK ){
pPg->isDirty = 0;
}
return rc;
}
/*
** Find the number of pages in the given file by measuring the size
** of the file. Return 0 if there is any problem.
*/
static int sqlitePgPageCount(int fd){
struct stat statbuf;
if( fstat(fd, &statbuf)!=0 ) return 0;
return statbuf.st_size/SQLITE_PAGE_SIZE;
}
/*
** This routine reads the journal and transfers pages from the
** journal to the database.
*/
static int sqlitePgJournalPlayback(Pgr *p){
Pghdr *pPg;
JidxEntry *aIdx;
int nJpg;
int jpgno = 1;
int i;
int dbpgno;
int rc;
char idx[SQLITE_PAGE_SIZE];
char pgbuf[SQLITE_PAGE_SIZE];
assert( p->fdJournal>=0 );
nJpg = sqlitePgPageCount(p->fdJournal);
while( jpgno<=nJpg ){
if( !sqlitePgRead(p->fdJournal, idx, jpgno++) ) break;
aIdx = (JidxEntry*)idx;
for(i=0; i<N_J_DATAPAGE; i++){
dbpgno = sqlitePgGetInt(&idx[i]);
if( dbpgno==0 ){
jpgno = nJpg+1;
break;
}
pPg = sqlitePgFind(p, dbpgno);
if( pPg ){
rc = sqlitePgWrite(p->fdMain, PG_TO_DATA(pPg), dbpgno);
TRACE(("PG: commit j-page %u to d-page %u from memory\n",jpgno,dbpgno));
}else{
rc = sqlitePgRead(p->fdJournal, pgbuf, jpgno);
if( rc!=SQLITE_OK ){
return rc;
}
rc = sqlitePgWrite(p->fdMain, pgbuf, dbpgno);
TRACE(("PG: commit j-page %u to d-page %u from disk\n",jpgno,dbpgno));
}
jpgno++;
if( rc!=SQLITE_OK ){
return rc;
}
}
}
TRACE(("PG: commit complete. deleting the journal.\n"));
fsync(p->fdMain);
close(p->fdJournal);
p->fdJournal = -1;
unlink(p->zJournal);
for(pPg=p->pAll; pPg; pPg=pPg->pNx){
pPg->isDirty = 0;
pPg->jpgno = 0;
}
while( (pPg = p->pJidx)!=0 ){
p->pAll = pPg->pNx;
sqliteFree(pPg);
}
return SQLITE_OK;
}
/*
** Remove the given page from the LRU list.
*/
static void sqlitePgUnlinkLru(Pghdr *pPg){
Pgr *p = pPg->p;
if( pPg->pLru ){
pPg->pLru->pMru = pPg->pLru;
}
if( pPg->pMru ){
pPg->pMru->pLru = pPg->pMru;
}
if( p->pLru==pPg ){
p->pLru = pPg->pLru;
}
if( p->pMru==pPg ){
p->pMru = pPg->pMru;
}
pPg->pLru = pPg->pMru = 0;
}
/*
** Open the database file and make *ppPgr pointer to a structure describing it.
** Return SQLITE_OK on success or an error code if there is a failure.
**
** If there was an unfinished commit, complete it before returnning.
*/
int sqlitePgOpen(const char *zFilename, Pgr **ppPgr){
Pgr *p;
int n;
n = strlen(zFilename);
p = sqliteMalloc( sizeof(*p) + n*2 + 4 );
if( p==0 ){
*ppPgr = 0;
return SQLITE_NOMEM;
}
p->zMain = (char*)&p[1];
strcpy(p->zMain, zFilename);
p->zJournal = &p->zMain[n+1];
strcpy(p->zJournal, p->zMain);
p->zJournal[n] = '~';
p->zJournal[n+1] = 0;
p->fdJournal = -1;
p->fdMain = open(p->zMain, O_CREAT|O_RDWR, 0600);
if( p->fdMain<0 ){
*ppPgr = 0;
sqliteFree(p);
return SQLITE_PERM;
}
p->nDbPg = sqlitePgPageCount(p->fdMain);
if( access(p->zJournal, R_OK)==0 ){
sqlitePgJournalPlayback(p);
}
*ppPgr = p;
return SQLITE_OK;
}
/*
** Close the database file. Any outstanding transactions are abandoned.
*/
int sqlitePgClose(Pgr *p){
Pghdr *pPg;
if( p->fdMain ) close(p->fdMain);
if( p->fdJournal ) close(p->fdJournal);
unlink(p->zJournal);
while( (pPg = p->pAll)!=0 ){
p->pAll = pPg->pNx;
sqliteFree(pPg);
}
while( (pPg = p->pJidx)!=0 ){
p->pAll = pPg->pNx;
sqliteFree(pPg);
}
sqliteFree(p);
return SQLITE_OK;
}
/*
** Begin a new transaction. Return SQLITE_OK on success or an error
** code if something goes wrong.
*/
int sqlitePgBeginTransaction(Pgr *p){
assert( p->fdJournal<0 );
if( p->nRefPg>0 ){
/* release the read lock */
}
/* write lock the database */
p->fdJournal = open(p->zJournal, O_CREAT|O_EXCL|O_RDWR, 0600);
if( p->fdJournal<0 ){
return SQLITE_PERM;
}
p->nJPg = 0;
TRACE(("PG: begin transaction\n"));
return SQLITE_OK;
}
/*
** Commit the current transaction. Return SQLITE_OK or an error code.
*/
int sqlitePgCommit(Pgr *p){
Pghdr *pPrimaryIdx = 0;
Pghdr *pPg;
int rc;
for(pPg=p->pAll; pPg; pPg=pPg->pNx){
if( pPg->isDirty ){
rc = sqlitePgMakeClean(pPg);
if( rc!=SQLITE_OK ){
return rc;
}
}
}
for(pPg=p->pJidx; pPg; pPg=pPg->pNx){
if( pPg->jpgno==1 ){
pPrimaryIdx = pPg;
}else{
TRACE(("PG: writing j-index %u\n", pPg->jpgno));
rc = sqlitePgMakeClean(pPg);
if( rc!=SQLITE_OK ){
return rc;
}
}
}
assert( pPrimaryIdx!=0 );
fsync(p->fdJournal);
TRACE(("PG: writing j-index %u\n", pPrimaryIdx->jpgno));
rc = sqlitePgMakeClean(pPrimaryIdx);
if( rc!=SQLITE_OK ){
return rc;
}
fsync(p->fdJournal);
rc = sqlitePgJournalPlayback(p);
if( rc!=SQLITE_OK ){
return rc;
}
/* remove write lock from database */
if( p->nRefPg>0 ){
/* acquire read lock on database */
}
return SQLITE_OK;
}
/*
** Abandon the current transaction.
*/
int sqlitePgRollback(Pgr *p){
Pghdr *pPg;
TRACE(("PG: begin rollback\n"));
for(pPg=p->pAll; pPg; pPg=pPg->pNx){
if( pPg->isDirty || pPg->jpgno!=0 ){
pPg->isDirty = 0;
pPg->jpgno = 0;
if( pPg->nRef>0 ){
TRACE(("PG: reloading d-page %u\n", pPg->dbpgno));
sqlitePgRead(p->fdMain, PG_TO_DATA(pPg), pPg->dbpgno);
}else{
sqlitePgUnlinkLru(pPg);
}
}
}
close(p->fdJournal);
p->fdJournal = -1;
unlink(p->zJournal);
while( (pPg = p->pJidx)!=0 ){
p->pAll = pPg->pNx;
sqliteFree(pPg);
}
p->nDbPg = sqlitePgPageCount(p->fdMain);
/* remove write lock from database */
if( p->nRefPg>0 ){
/* acquire read lock on database */
}
return SQLITE_OK;
}
/*
** Get a page from the database. Return a pointer to the data for that
** page.
**
** A NULL pointer will be returned if we run out of memory.
*/
int sqlitePgGet(Pgr *p, u32 pgno, void **ppData){
Pghdr *pPg;
int h;
pPg = sqlitePgFind(p, pgno);
if( pPg ){
pPg->nRef++;
if( pPg->nRef==1 ){
sqlitePgUnlinkLru(pPg);
TRACE(("PG: d-page %u pulled from cache\n", pgno));
}
p->nRefPg++;
if( p->nRefPg==1 ){
/* Acquire a read lock */
}
*ppData = PG_TO_DATA(pPg);
return SQLITE_OK;
}
if( p->nMemPg<MX_MEM_PAGE || p->pLru==0 ){
pPg = sqliteMalloc( sizeof(Pghdr) + SQLITE_PAGE_SIZE );
if( pPg==0 ) return SQLITE_NOMEM;
p->nMemPg++;
pPg->pNx = p->pAll;
p->pAll = pPg;
pPg->p = p;
TRACE(("PG: new page %d created.\n", p->nMemPg));
}else{
int rc;
pPg = p->pLru;
if( pPg->isDirty ){
rc = sqlitePgMakeClean(pPg);
if( rc!=SQLITE_OK ) return rc;
}
sqlitePgUnlinkLru(pPg);
h = pPg->dbpgno % PG_HASH_SIZE;
if( pPg->pPvHash ){
pPg->pPvHash->pNxHash = pPg->pNxHash;
}else{
assert( p->aPgHash[h]==pPg );
p->aPgHash[h] = pPg->pNxHash;
}
if( pPg->pNxHash ){
pPg->pNxHash->pPvHash = pPg->pPvHash;
}
TRACE(("PG: recycling d-page %u to d-page %u\n", pPg->dbpgno, pgno));
}
pPg->dbpgno = pgno;
if( pgno>p->nDbPg ){
p->nDbPg = pgno;
}
h = pgno % PG_HASH_SIZE;
pPg->pPvHash = 0;
pPg->pNxHash = p->aPgHash[h];
if( pPg->pNxHash ){
pPg->pNxHash->pPvHash = pPg;
}
p->aPgHash[h] = pPg;
pPg->jpgno = sqlitePgJournalPageNumber(p, pgno);
if( pPg->jpgno!=0 ){
TRACE(("PG: reading d-page %u content from j-page %u\n", pgno, pPg->jpgno));
sqlitePgRead(p->fdJournal, PG_TO_DATA(pPg), pPg->jpgno);
}else if( pPg->dbpgno!=0 ){
TRACE(("PG: reading d-page %u from database\n", pgno));
sqlitePgRead(p->fdMain, PG_TO_DATA(pPg), pPg->dbpgno);
}else{
TRACE(("PG: reading zero page\n");
memset(PG_TO_DATA(pPg), 0, SQLITE_PAGE_SIZE);
}
pPg->isDirty = 0;
pPg->nRef = 1;
p->nRefPg++;
if( p->nRefPg==1 ){
/* Acquire a read lock */
}
*ppData = PG_TO_DATA(pPg);
return SQLITE_OK;
}
/*
** Release a reference to a database data page.
*/
int sqlitePgUnref(void *pData){
Pghdr *pPg = DATA_TO_PG(pData);
pPg->nRef--;
assert( pPg->nRef>=0 );
if( pPg->nRef==0 ){
Pgr *p = pPg->p;
pPg->pMru = 0;
pPg->pLru = p->pLru;
p->pLru = pPg;
TRACE(("PG: d-page %u is unused\n", pPg->dbpgno));
p->nRefPg--;
if( p->nRefPg==0 ){
/* Release the read lock */
}
}
return SQLITE_OK;
}
/*
** The database page in the argument has been modified. Write it back
** to the database file on the next commit.
*/
int sqlitePgTouch(void *pD){
Pghdr *pPg = DATA_TO_PG(pD);
assert( pPg->p->fdJournal>=0 );
if( pPg->isDirty==0 ){
pPg->isDirty = 1;
TRACE(("PG: d-page %u is dirty\n", pPg->dbpgno));
}
return SQLITE_OK;
}
/*
** Return the number of the first unused page at the end of the
** database file.
*/
int sqlitePgCount(Pgr *p, u32 *pPgno){
*pPgno = p->nDbPg;
return SQLITE_OK;
}
/*
** Return the page number associated with the given page.
*/
u32 sqlitePgNum(void *pD){
Pghdr *pPg = DATA_TO_PG(pD);
return pPg->dbpgno;
}
+40
View File
@@ -0,0 +1,40 @@
/*
** Copyright (c) 2001 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/
**
*************************************************************************
** $Id: pg.h,v 1.1 2001/02/11 16:56:24 drh Exp $
*/
typedef struct Pgr Pgr;
#define SQLITE_PAGE_SIZE 1024
int sqlitePgOpen(const char *filename, Pgr **pp);
int sqlitePgClose(Pgr*);
int sqlitePgBeginTransaction(Pgr*);
int sqlitePgCommit(Pgr*);
int sqlitePgRollback(Pgr*);
int sqlitePgGet(Pgr*, u32 pgno, void **);
int sqlitePgUnref(void*);
int sqlitePgTouch(void*);
int sqlitePgCount(Pgr*, u32*);
u32 sqlitePgNum(void*);
+41
View File
@@ -0,0 +1,41 @@
set sizes {1024 2048 4096 8192 16384 32768}
set fmt { %-8s}
puts -nonewline "page size: "
foreach s $sizes {
puts -nonewline [format $fmt $s]
}
puts ""
puts -nonewline "on leaf: "
foreach s $sizes {
set x [expr {$s - 18*4}]
set p($s) $x
puts -nonewline [format $fmt $x]
}
puts ""
puts -nonewline "direct: "
foreach s $sizes {
set x [expr {$p($s) + 10*$s}]
set p($s) $x
puts -nonewline [format $fmt $x]
}
puts ""
puts -nonewline "indirect: "
foreach s $sizes {
set x [expr {$p($s) + ($s/4.0)*$s}]
set p($s) $x
puts -nonewline [format $fmt $x]
}
puts ""
puts -nonewline "dbl indir: "
foreach s $sizes {
set x [expr {$p($s) + ($s/4.0)*($s/4)*$s}]
set p($s) $x
puts -nonewline [format $fmt $x]
}
puts ""
+3 -3
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.19 2000/08/28 15:51:44 drh Exp $
** $Id: expr.c,v 1.21 2001/01/15 22:51:10 drh Exp $
*/
#include "sqliteInt.h"
@@ -216,7 +216,7 @@ int sqliteExprResolveIds(Parse *pParse, IdList *pTabList, Expr *pExpr){
** table. The cursor number of the temporary table has already
** been put in iTable by sqliteExprResolveInSelect().
*/
sqliteVdbeAddOp(v, OP_Open, pExpr->iTable, 1, 0, 0);
sqliteVdbeAddOp(v, OP_OpenIdx, pExpr->iTable, 1, 0, 0);
if( sqliteSelect(pParse, pExpr->pSelect, SRT_Set, pExpr->iTable) );
}else if( pExpr->pList ){
/* Case 2: expr IN (exprlist)
@@ -738,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.
*/
+4 -4
View File
@@ -24,7 +24,7 @@
** This file contains C code routines that are called by the parser
** to handle INSERT statements.
**
** $Id: insert.c,v 1.11 2000/06/21 13:59:12 drh Exp $
** $Id: insert.c,v 1.12 2001/01/15 22:51:11 drh Exp $
*/
#include "sqliteInt.h"
@@ -92,7 +92,7 @@ void sqliteInsert(
if( pSelect ){
int rc;
srcTab = pParse->nTab++;
sqliteVdbeAddOp(v, OP_Open, srcTab, 1, 0, 0);
sqliteVdbeAddOp(v, OP_OpenTbl, srcTab, 1, 0, 0);
rc = sqliteSelect(pParse, pSelect, SRT_Table, srcTab);
if( rc ) goto insert_cleanup;
assert( pSelect->pEList );
@@ -156,9 +156,9 @@ void sqliteInsert(
** all indices of that table.
*/
base = pParse->nTab;
sqliteVdbeAddOp(v, OP_Open, base, 1, pTab->zName, 0);
sqliteVdbeAddOp(v, OP_OpenTbl, base, 1, pTab->zName, 0);
for(idx=1, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, idx++){
sqliteVdbeAddOp(v, OP_Open, idx+base, 1, pIdx->zName, 0);
sqliteVdbeAddOp(v, OP_OpenIdx, idx+base, 1, pIdx->zName, 0);
}
/* If the data source is a SELECT statement, then we have to create
+54 -27
View File
@@ -26,9 +26,10 @@
** other files are for internal use by SQLite and should not be
** accessed by users of the library.
**
** $Id: main.c,v 1.19 2000/10/11 19:28:52 drh Exp $
** $Id: main.c,v 1.25 2001/02/11 16:56:24 drh Exp $
*/
#include "sqliteInt.h"
#include <unistd.h>
/*
** This is the callback routine for the code that initializes the
@@ -123,7 +124,7 @@ static int sqliteInit(sqlite *db, char **pzErrMsg){
** database scheme.
*/
static VdbeOp initProg[] = {
{ OP_Open, 0, 0, MASTER_NAME},
{ OP_OpenTbl, 0, 0, MASTER_NAME},
{ OP_Next, 0, 9, 0}, /* 1 */
{ OP_Field, 0, 0, 0},
{ OP_String, 0, 0, "meta"},
@@ -154,7 +155,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;
@@ -227,7 +228,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;
}
@@ -239,7 +240,7 @@ sqlite *sqlite_open(const char *zFilename, int mode, char **pzErrMsg){
*/
void sqlite_close(sqlite *db){
int i;
sqliteDbbeClose(db->pBe);
db->pBe->x->Close(db->pBe);
for(i=0; i<N_HASH; i++){
Table *pNext, *pList = db->apTblHash[i];
db->apTblHash[i] = 0;
@@ -257,29 +258,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;
}
/*
@@ -300,7 +322,6 @@ int sqlite_exec(
char **pzErrMsg /* Write error messages here */
){
Parse sParse;
int rc;
if( pzErrMsg ) *pzErrMsg = 0;
if( (db->flags & SQLITE_Initialized)==0 ){
@@ -311,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;
}
/*
@@ -382,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;
}
+6 -6
View File
@@ -26,7 +26,7 @@
** the parser. Lemon will also generate a header file containing
** numeric codes for all of the tokens.
**
** @(#) $Id: parse.y,v 1.25 2000/08/01 09:56:27 drh Exp $
** @(#) $Id: parse.y,v 1.26 2001/01/04 14:20:18 drh Exp $
*/
%token_prefix TK_
%token_type {Token}
@@ -114,12 +114,12 @@ ccons ::= CHECK LP expr RP.
conslist_opt ::= .
conslist_opt ::= COMMA conslist.
conslist ::= conslist COMMA tcons.
conslist ::= conslist tcons.
conslist ::= tcons.
tcons ::= CONSTRAINT id tcons2.
tcons ::= tcons2.
tcons2 ::= PRIMARY KEY LP idxlist(X) RP. {sqliteCreateIndex(pParse,0,0,X,0,0);}
tcons2 ::= UNIQUE LP idlist RP.
tcons2 ::= CHECK expr.
tcons ::= CONSTRAINT id.
tcons ::= PRIMARY KEY LP idxlist(X) RP. {sqliteCreateIndex(pParse,0,0,X,0,0);}
tcons ::= UNIQUE LP idlist RP.
tcons ::= CHECK expr.
idlist ::= idlist COMMA id.
idlist ::= id.
+3 -4
View File
@@ -46,7 +46,6 @@
** + All functions are fully reentrant.
**
*/
#include <ctype.h>
#include "sqliteInt.h"
/*
@@ -262,7 +261,7 @@ static int vxprintf(
}
c = *++fmt;
}else{
while( isdigit(c) ){
while( c>='0' && c<='9' ){
width = width*10 + c - '0';
c = *++fmt;
}
@@ -282,7 +281,7 @@ static int vxprintf(
#endif
c = *++fmt;
}else{
while( isdigit(c) ){
while( c>='0' && c<='9' ){
precision = precision*10 + c - '0';
c = *++fmt;
}
@@ -561,7 +560,7 @@ static int vxprintf(
for(i=n=0; (c=arg[i])!=0; i++){
if( c=='\'' ) n++;
}
n += i;
n += i + 1;
if( n>etBUFSIZE ){
bufpt = zExtra = sqliteMalloc( n );
}else{
+127
View File
@@ -0,0 +1,127 @@
/*
** 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 a pseudo-random number
** generator (PRNG) for SQLite.
**
** Random numbers are used by some of the database backends in order
** to generate random integer keys for tables or random filenames.
**
** $Id: random.c,v 1.2 2001/01/31 13:28:09 drh Exp $
*/
#include "sqliteInt.h"
#include <time.h>
/*
** Get a single 8-bit random value from the RC4 PRNG.
*/
int sqliteRandomByte(void){
int t;
/*
** 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.
*/
static struct {
int isInit;
int i, j;
int s[256];
} prng_state;
/* Initialize the state of the random number generator once,
** the first time this routine is called. The seed value does
** not need to contain a lot of randomness since we are not
** trying to do secure encryption or anything like that...
*/
if( !prng_state.isInit ){
int i;
static char seed[] = " sqlite random seed";
char k[256];
time((time_t*)seed);
prng_state.j = 0;
prng_state.i = 0;
for(i=0; i<256; i++){
prng_state.s[i] = i;
k[i] = seed[i%sizeof(seed)];
}
for(i=0; i<256; i++){
int t;
prng_state.j = (prng_state.j + prng_state.s[i] + k[i]) & 0xff;
t = prng_state.s[prng_state.j];
prng_state.s[prng_state.j] = prng_state.s[i];
prng_state.s[i] = t;
}
prng_state.isInit = 1;
}
/* Generate and return single random byte
*/
prng_state.i = (prng_state.i + 1) & 0xff;
prng_state.j = (prng_state.j + prng_state.s[prng_state.i]) & 0xff;
t = prng_state.s[prng_state.i];
prng_state.s[prng_state.i] = prng_state.s[prng_state.j];
prng_state.s[prng_state.j] = t;
t = prng_state.s[prng_state.i] + prng_state.s[prng_state.j];
return t & 0xff;
}
/*
** Return a random 32-bit integer. The integer is generated by making
** 4 calls to sqliteRandomByte().
*/
int sqliteRandomInteger(void){
int r;
int i;
r = sqliteRandomByte();
for(i=1; i<4; i++){
r = (r<<8) + sqliteRandomByte();
}
return r;
}
/*
** 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.
*/
void sqliteRandomName(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 = sqliteRandomByte() % (sizeof(zRandomChars) - 1);
zBuf[j++] = zRandomChars[c];
}
zBuf[j] = 0;
}
+10 -8
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.29 2001/02/19 23:23:38 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);
@@ -489,9 +489,11 @@ static int multiSelect(Parse *pParse, Select *p, int eDest, int iParm){
&& matchOrderbyToColumn(pParse, p, p->pOrderBy, unionTab, 1) ){
return 1;
}
sqliteVdbeAddOp(v, OP_Open, unionTab, 1, 0, 0);
if( p->op!=TK_ALL ){
sqliteVdbeAddOp(v, OP_OpenIdx, unionTab, 1, 0, 0);
sqliteVdbeAddOp(v, OP_KeyAsData, unionTab, 1, 0, 0);
}else{
sqliteVdbeAddOp(v, OP_OpenTbl, unionTab, 1, 0, 0);
}
}
@@ -549,7 +551,7 @@ static int multiSelect(Parse *pParse, Select *p, int eDest, int iParm){
if( p->pOrderBy && matchOrderbyToColumn(pParse,p,p->pOrderBy,tab1,1) ){
return 1;
}
sqliteVdbeAddOp(v, OP_Open, tab1, 1, 0, 0);
sqliteVdbeAddOp(v, OP_OpenIdx, tab1, 1, 0, 0);
sqliteVdbeAddOp(v, OP_KeyAsData, tab1, 1, 0, 0);
/* Code the SELECTs to our left into temporary table "tab1".
@@ -559,7 +561,7 @@ static int multiSelect(Parse *pParse, Select *p, int eDest, int iParm){
/* Code the current SELECT into temporary table "tab2"
*/
sqliteVdbeAddOp(v, OP_Open, tab2, 1, 0, 0);
sqliteVdbeAddOp(v, OP_OpenIdx, tab2, 1, 0, 0);
sqliteVdbeAddOp(v, OP_KeyAsData, tab2, 1, 0, 0);
p->pPrior = 0;
rc = sqliteSelect(pParse, p, SRT_Union, tab2);
@@ -576,7 +578,7 @@ static int multiSelect(Parse *pParse, Select *p, int eDest, int iParm){
}
iBreak = sqliteVdbeMakeLabel(v);
iCont = sqliteVdbeAddOp(v, OP_Next, tab1, iBreak, 0, 0);
sqliteVdbeAddOp(v, OP_Key, tab1, 0, 0, 0);
sqliteVdbeAddOp(v, OP_FullKey, tab1, 0, 0, 0);
sqliteVdbeAddOp(v, OP_NotFound, tab2, iCont, 0, 0);
rc = selectInnerLoop(pParse, 0, tab1, p->pEList->nExpr,
p->pOrderBy, -1, eDest, iParm,
@@ -818,7 +820,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);
@@ -852,7 +854,7 @@ int sqliteSelect(
/* Begin the database scan
*/
if( isDistinct ){
sqliteVdbeAddOp(v, OP_Open, distinct, 1, 0, 0);
sqliteVdbeAddOp(v, OP_OpenIdx, distinct, 1, 0, 0);
}
pWInfo = sqliteWhereBegin(pParse, pTabList, pWhere, 0);
if( pWInfo==0 ) return 1;
+36 -7
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.26 2000/10/08 22:20:58 drh Exp $
** $Id: shell.c,v 1.29 2001/01/04 14:27:08 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
@@ -229,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.
@@ -447,7 +464,6 @@ 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;
if( nArg==1 ){
@@ -669,15 +685,18 @@ 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;
int echo = 0;
memset(&data, 0, sizeof(data));
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;
@@ -703,6 +722,10 @@ int main(int argc, char **argv){
data.showHeader = 0;
argc--;
argv++;
}else if( strcmp(argv[1],"-echo")==0 ){
echo = 1;
argc--;
argv++;
}else{
fprintf(stderr,"%s: unknown option: %s\n", argv0, argv[1]);
return 1;
@@ -745,15 +768,20 @@ int main(int argc, char **argv){
);
}
while( (zLine = one_input_line(zSql, istty))!=0 ){
if( echo ) printf("%s\n", zLine);
if( zLine && zLine[0]=='.' ){
do_meta_command(zLine, db, &data);
free(zLine);
continue;
}
if( zSql==0 ){
nSql = strlen(zLine);
zSql = malloc( nSql+1 );
strcpy(zSql, zLine);
int i;
for(i=0; zLine[i] && isspace(zLine[i]); i++){}
if( zLine[i]!=0 ){
nSql = strlen(zLine);
zSql = malloc( nSql+1 );
strcpy(zSql, zLine);
}
}else{
int len = strlen(zLine);
zSql = realloc( zSql, nSql + len + 2 );
@@ -766,10 +794,11 @@ int main(int argc, char **argv){
nSql += len;
}
free(zLine);
if( sqlite_complete(zSql) ){
if( zSql && sqlite_complete(zSql) ){
data.cnt = 0;
if( sqlite_exec(db, zSql, callback, &data, &zErrMsg)!=0
&& zErrMsg!=0 ){
if( !istty && !echo ) printf("%s\n",zSql);
printf("SQL error: %s\n", zErrMsg);
free(zErrMsg);
zErrMsg = 0;
+19 -5
View File
@@ -24,7 +24,7 @@
** This header file defines the interface that the sqlite library
** presents to client programs.
**
** @(#) $Id: sqlite.h.in,v 1.5 2000/10/09 12:57:01 drh Exp $
** @(#) $Id: sqlite.h.in,v 1.9 2001/01/20 19:52:49 drh Exp $
*/
#ifndef _SQLITE_H_
#define _SQLITE_H_
@@ -130,13 +130,27 @@ int 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() */
#define SQLITE_IOERR 9 /* Disk full or other I/O error */
#define SQLITE_CORRUPT 10 /* The database disk image is malformed */
#define SQLITE_NOTFOUND 11 /* Table or record not found */
#define SQLITE_FULL 12 /* Insertion failed because database is full */
/* 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.
@@ -248,9 +262,9 @@ void sqlite_free_table(char **result);
**
** 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 double every '\'' character.
** string from the argument list. But %q also doubles every '\'' character.
** %q is designed for use inside a string literal. By doubling each '\''
** character is escapes that character and allows it to be inserted into
** character it escapes that character and allows it to be inserted into
** the string.
**
** For example, so some string variable contains text as follows:
+26 -11
View File
@@ -23,7 +23,7 @@
*************************************************************************
** Internal interface definitions for SQLite.
**
** @(#) $Id: sqliteInt.h,v 1.30 2000/08/28 15:51:44 drh Exp $
** @(#) $Id: sqliteInt.h,v 1.36 2001/01/20 19:52:50 drh Exp $
*/
#include "sqlite.h"
#include "dbbe.h"
@@ -35,6 +35,11 @@
#include <string.h>
#include <assert.h>
/*
** The paging system deals with 32-bit integers.
*/
typedef unsigned int u32;
/*
** If memory allocation problems are found, recompile with
**
@@ -62,17 +67,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
@@ -123,7 +129,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 */
@@ -135,8 +141,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
@@ -167,7 +174,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
@@ -200,7 +207,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 */
};
@@ -325,6 +332,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 */
@@ -416,3 +424,10 @@ void sqliteExprResolveInSelect(Parse*, Expr*);
int sqliteExprAnalyzeAggregates(Parse*, Expr*);
void sqliteParseInfoReset(Parse*);
Vdbe *sqliteGetVdbe(Parse*);
int sqliteRandomByte(void);
int sqliteRandomInteger(void);
void sqliteRandomName(char*,char*);
int sqliteDbbeOpenTempFile(const char*, Dbbe*, FILE**);
void sqliteDbbeCloseTempFile(Dbbe*, FILE*);
void sqliteDbbeCloseAllTempFiles(Dbbe*);
char *sqliteDbbeNameToFile(const char*,const char*,const char*);
+23 -1
View File
@@ -1,9 +1,31 @@
/*
** 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().
**
** This routines are in a separate files to that they will not be linked
** These routines are in a separate files so that they will not be linked
** if they are not used.
*/
#include <stdlib.h>
+4 -2
View File
@@ -23,12 +23,12 @@
*************************************************************************
** A TCL Interface to SQLite
**
** $Id: tclsqlite.c,v 1.11 2000/10/11 19:28:52 drh Exp $
** $Id: tclsqlite.c,v 1.13 2001/01/31 13:28:09 drh Exp $
*/
#ifndef NO_TCL /* Omit this whole file if TCL is unavailable */
#include "sqlite.h"
#include <tcl.h>
#include "tcl.h"
#include <stdlib.h>
#include <string.h>
@@ -380,6 +380,7 @@ int Sqlite_SafeInit(Tcl_Interp *interp){
return TCL_OK;
}
#if 0
/*
** If compiled using mktclapp, this routine runs to initialize
** everything.
@@ -387,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
+1
View File
@@ -0,0 +1 @@
hi
+18 -5
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.17 2001/02/11 16:56:24 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;
@@ -308,9 +308,11 @@ int sqliteRunParser(Parse *pParse, char *zSql, char **pzErrMsg){
static FILE *trace = 0;
extern void *sqliteParserAlloc(void*(*)(int));
extern void sqliteParserFree(void*, void(*)(void*));
extern int sqliteParser(void*, int, ...);
extern int sqliteParser(void*, int, Token, Parse*);
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;
}
+7 -5
View File
@@ -24,7 +24,7 @@
** This file contains C code routines that are called by the parser
** to handle UPDATE statements.
**
** $Id: update.c,v 1.8 2000/06/21 13:59:12 drh Exp $
** $Id: update.c,v 1.10 2001/03/14 12:35:57 drh Exp $
*/
#include "sqliteInt.h"
@@ -126,8 +126,10 @@ void sqliteUpdate(
}
if( i<pIdx->nColumn ) nIdx++;
}
apIdx = sqliteMalloc( sizeof(Index*) * nIdx );
if( apIdx==0 ) goto update_cleanup;
if( nIdx>0 ){
apIdx = sqliteMalloc( sizeof(Index*) * nIdx );
if( apIdx==0 ) goto update_cleanup;
}
for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
for(i=0; i<pIdx->nColumn; i++){
if( aXRef[pIdx->aiColumn[i]]>=0 ) break;
@@ -159,9 +161,9 @@ void sqliteUpdate(
*/
sqliteVdbeAddOp(v, OP_ListRewind, 0, 0, 0, 0);
base = pParse->nTab;
sqliteVdbeAddOp(v, OP_Open, base, 1, pTab->zName, 0);
sqliteVdbeAddOp(v, OP_OpenTbl, base, 1, pTab->zName, 0);
for(i=0; i<nIdx; i++){
sqliteVdbeAddOp(v, OP_Open, base+i+1, 1, apIdx[i]->zName, 0);
sqliteVdbeAddOp(v, OP_OpenIdx, base+i+1, 1, apIdx[i]->zName, 0);
}
/* Loop over every record that needs updating. We have to load
+12 -2
View File
@@ -26,7 +26,7 @@
** This file contains functions for allocating memory, comparing
** strings, and stuff like that.
**
** $Id: util.c,v 1.16 2000/10/11 19:28:52 drh Exp $
** $Id: util.c,v 1.18 2001/03/14 12:35:57 drh Exp $
*/
#include "sqliteInt.h"
#include <stdarg.h>
@@ -38,6 +38,15 @@
*/
#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
** no memory is available.
@@ -51,6 +60,7 @@ void *sqliteMalloc_(int n, char *zFile, int line){
sqlite_iMallocFail--;
if( sqlite_iMallocFail==0 ) return 0;
}
if( n==0 ) return 0;
k = (n+sizeof(int)-1)/sizeof(int);
pi = malloc( (3+k)*sizeof(int));
if( pi==0 ) return 0;
@@ -361,7 +371,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;
+540 -422
View File
File diff suppressed because it is too large Load Diff
+95 -93
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.13 2000/10/11 19:28:53 drh Exp $
** $Id: vdbe.h,v 1.16 2001/02/19 23:23:39 drh Exp $
*/
#ifndef _SQLITE_VDBE_H_
#define _SQLITE_VDBE_H_
@@ -71,118 +71,120 @@ typedef struct VdbeOp VdbeOp;
** The source tree contains an AWK script named renumberOps.awk that
** can be used to renumber these opcodes when new opcodes are inserted.
*/
#define OP_Open 1
#define OP_Close 2
#define OP_Fetch 3
#define OP_Fcnt 4
#define OP_New 5
#define OP_Put 6
#define OP_Distinct 7
#define OP_Found 8
#define OP_NotFound 9
#define OP_Delete 10
#define OP_Field 11
#define OP_KeyAsData 12
#define OP_Key 13
#define OP_Rewind 14
#define OP_Next 15
#define OP_OpenIdx 1
#define OP_OpenTbl 2
#define OP_Close 3
#define OP_Fetch 4
#define OP_Fcnt 5
#define OP_New 6
#define OP_Put 7
#define OP_Distinct 8
#define OP_Found 9
#define OP_NotFound 10
#define OP_Delete 11
#define OP_Field 12
#define OP_KeyAsData 13
#define OP_Key 14
#define OP_FullKey 15
#define OP_Rewind 16
#define OP_Next 17
#define OP_Destroy 16
#define OP_Reorganize 17
#define OP_Destroy 18
#define OP_Reorganize 19
#define OP_ResetIdx 18
#define OP_NextIdx 19
#define OP_PutIdx 20
#define OP_DeleteIdx 21
#define OP_ResetIdx 20
#define OP_NextIdx 21
#define OP_PutIdx 22
#define OP_DeleteIdx 23
#define OP_MemLoad 22
#define OP_MemStore 23
#define OP_MemLoad 24
#define OP_MemStore 25
#define OP_ListOpen 24
#define OP_ListWrite 25
#define OP_ListRewind 26
#define OP_ListRead 27
#define OP_ListClose 28
#define OP_ListOpen 26
#define OP_ListWrite 27
#define OP_ListRewind 28
#define OP_ListRead 29
#define OP_ListClose 30
#define OP_SortOpen 29
#define OP_SortPut 30
#define OP_SortMakeRec 31
#define OP_SortMakeKey 32
#define OP_Sort 33
#define OP_SortNext 34
#define OP_SortKey 35
#define OP_SortCallback 36
#define OP_SortClose 37
#define OP_SortOpen 31
#define OP_SortPut 32
#define OP_SortMakeRec 33
#define OP_SortMakeKey 34
#define OP_Sort 35
#define OP_SortNext 36
#define OP_SortKey 37
#define OP_SortCallback 38
#define OP_SortClose 39
#define OP_FileOpen 38
#define OP_FileRead 39
#define OP_FileField 40
#define OP_FileClose 41
#define OP_FileOpen 40
#define OP_FileRead 41
#define OP_FileField 42
#define OP_FileClose 43
#define OP_AggReset 42
#define OP_AggFocus 43
#define OP_AggIncr 44
#define OP_AggNext 45
#define OP_AggSet 46
#define OP_AggGet 47
#define OP_AggReset 44
#define OP_AggFocus 45
#define OP_AggIncr 46
#define OP_AggNext 47
#define OP_AggSet 48
#define OP_AggGet 49
#define OP_SetInsert 48
#define OP_SetFound 49
#define OP_SetNotFound 50
#define OP_SetClear 51
#define OP_SetInsert 50
#define OP_SetFound 51
#define OP_SetNotFound 52
#define OP_SetClear 53
#define OP_MakeRecord 52
#define OP_MakeKey 53
#define OP_MakeRecord 54
#define OP_MakeKey 55
#define OP_Goto 54
#define OP_If 55
#define OP_Halt 56
#define OP_Goto 56
#define OP_If 57
#define OP_Halt 58
#define OP_ColumnCount 57
#define OP_ColumnName 58
#define OP_Callback 59
#define OP_ColumnCount 59
#define OP_ColumnName 60
#define OP_Callback 61
#define OP_Integer 60
#define OP_String 61
#define OP_Null 62
#define OP_Pop 63
#define OP_Dup 64
#define OP_Pull 65
#define OP_Integer 62
#define OP_String 63
#define OP_Null 64
#define OP_Pop 65
#define OP_Dup 66
#define OP_Pull 67
#define OP_Add 66
#define OP_AddImm 67
#define OP_Subtract 68
#define OP_Multiply 69
#define OP_Divide 70
#define OP_Min 71
#define OP_Max 72
#define OP_Like 73
#define OP_Glob 74
#define OP_Eq 75
#define OP_Ne 76
#define OP_Lt 77
#define OP_Le 78
#define OP_Gt 79
#define OP_Ge 80
#define OP_IsNull 81
#define OP_NotNull 82
#define OP_Negative 83
#define OP_And 84
#define OP_Or 85
#define OP_Not 86
#define OP_Concat 87
#define OP_Noop 88
#define OP_Add 68
#define OP_AddImm 69
#define OP_Subtract 70
#define OP_Multiply 71
#define OP_Divide 72
#define OP_Min 73
#define OP_Max 74
#define OP_Like 75
#define OP_Glob 76
#define OP_Eq 77
#define OP_Ne 78
#define OP_Lt 79
#define OP_Le 80
#define OP_Gt 81
#define OP_Ge 82
#define OP_IsNull 83
#define OP_NotNull 84
#define OP_Negative 85
#define OP_And 86
#define OP_Or 87
#define OP_Not 88
#define OP_Concat 89
#define OP_Noop 90
#define OP_Strlen 89
#define OP_Substr 90
#define OP_Strlen 91
#define OP_Substr 92
#define OP_MAX 90
#define OP_MAX 92
/*
** 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);
+4 -4
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.9 2000/08/22 18:29:34 drh Exp $
** $Id: where.c,v 1.12 2001/02/19 23:23:39 drh Exp $
*/
#include "sqliteInt.h"
@@ -271,9 +271,9 @@ WhereInfo *sqliteWhereBegin(
/* Open all tables in the pTabList and all indices in aIdx[].
*/
for(i=0; i<pTabList->nId; i++){
sqliteVdbeAddOp(v, OP_Open, base+i, 0, pTabList->a[i].pTab->zName, 0);
sqliteVdbeAddOp(v, OP_OpenTbl, base+i, 0, pTabList->a[i].pTab->zName, 0);
if( i<ARRAYSIZE(aIdx) && aIdx[i]!=0 ){
sqliteVdbeAddOp(v, OP_Open, base+pTabList->nId+i, 0, aIdx[i]->zName, 0);
sqliteVdbeAddOp(v, OP_OpenIdx, base+pTabList->nId+i, 0, aIdx[i]->zName,0);
}
}
memcpy(pWInfo->aIdx, aIdx, sizeof(aIdx));
@@ -338,8 +338,8 @@ WhereInfo *sqliteWhereBegin(
if( (aExpr[j].prereqRight & loopMask)!=aExpr[j].prereqRight ) continue;
if( (aExpr[j].prereqLeft & loopMask)!=aExpr[j].prereqLeft ) continue;
if( haveKey ){
sqliteVdbeAddOp(v, OP_Fetch, base+idx, 0, 0, 0);
haveKey = 0;
sqliteVdbeAddOp(v, OP_Fetch, base+idx, 0, 0, 0);
}
sqliteExprIfFalse(pParse, aExpr[j].p, cont);
aExpr[j].p = 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
+11 -11
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.5 2001/01/31 13:28:09 drh Exp $
set testdir [file dirname $argv0]
source $testdir/tester.tcl
@@ -32,13 +32,13 @@ source $testdir/tester.tcl
#
do_test dbbe-1.1 {
catch {db close}
file delete -force testdb
forcedelete testdb
set v [catch {sqlite db testdb 0444} msg]
lappend v $msg
} {1 {can't find directory "testdb"}}
do_test dbbe-1.2 {
catch {db close}
file delete -force testdb
forcedelete testdb
set v [catch {sqlite db testdb/dummy 0666} msg]
lappend v $msg
} {1 {can't find or create directory "testdb/dummy"}}
@@ -49,7 +49,7 @@ do_test dbbe-1.2 {
#
do_test dbbe-1.3 {
catch {db close}
file delete -force testdb
forcedelete testdb
set v [catch {sqlite db testdb 0666} msg]
lappend v $msg
} {0 {}}
@@ -58,7 +58,7 @@ do_test dbbe-1.3 {
#
do_test dbbe-1.4 {
catch {db close}
file delete -force testdb
forcedelete testdb
set fd [open testdb w]
puts $fd hi!
close $fd
@@ -70,7 +70,7 @@ do_test dbbe-1.4 {
#
do_test dbbe-1.5 {
catch {db close}
file delete -force testdb
forcedelete testdb
file mkdir testdb
file attributes testdb -permissions 0
set v [catch {sqlite db testdb 0666} msg]
@@ -81,7 +81,7 @@ do_test dbbe-1.5 {
#
do_test dbbe-1.6 {
catch {db close}
file delete -force testdb
forcedelete testdb
sqlite db testdb 0666
execsql {CREATE TABLE t1(x int)}
db close
@@ -91,7 +91,7 @@ do_test dbbe-1.6 {
} {1 {access permission denied for testdb/sqlite_master.tbl}}
do_test dbbe-1.6b {
catch {db close}
file delete -force testdb
forcedelete testdb
sqlite db testdb 0666
execsql {CREATE TABLE t1(x int)}
db close
@@ -105,7 +105,7 @@ do_test dbbe-1.6b {
#
do_test dbbe-2.1 {
catch {db close}
file delete -force testdb
forcedelete testdb
sqlite db testdb 0666
execsql {
CREATE TABLE t1(x int);
@@ -123,14 +123,14 @@ do_test dbbe-2.1 {
#
do_test dbbe-3.1 {
catch {db close}
file delete -force testdb
forcedelete testdb
sqlite db testdb 0666
execsql {CREATE TABLE t1(x int)}
db close
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
+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 {
+8 -4
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.5 2001/01/04 14:20:18 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
@@ -70,7 +72,7 @@ do_test lock-1.3 {
puts $f "SELECT f2 FROM big WHERE f1=11;"
close $f
exec ./sqlite testdb <update.sql
} "SQL error: table big is locked\n22"
} "UPDATE big SET f2='xyz' WHERE f1=11;\nSQL error: table big is locked\n22"
do_test lock-1.4 {
# Try to update the database using a timeout
@@ -81,7 +83,7 @@ do_test lock-1.4 {
puts $f "SELECT f2 FROM big WHERE f1=11;"
close $f
exec ./sqlite testdb <update.sql
} "SQL error: table big is locked\n22"
} "UPDATE big SET f2='xyz' WHERE f1=11;\nSQL error: table big is locked\n22"
do_test lock-1.5 {
# Try to update the database using a timeout
@@ -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)}
+75 -7
View File
@@ -23,13 +23,29 @@
# This file implements some common TCL routines used for regression
# testing the SQLite library
#
# $Id: tester.tcl,v 1.6 2000/09/21 13:01:37 drh Exp $
# $Id: tester.tcl,v 1.10 2001/01/31 13:28:09 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: {
if {[catch {file delete -force testdb}]} {
exec rm -rf testdb
}
file mkdir testdb
}
memory: {
# do nothing
}
}
sqlite db ${dbprefix}testdb
# Abort early if this script has been run before.
#
@@ -39,12 +55,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 +78,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 +91,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,6 +141,7 @@ proc finish_test {} {
# A procedure to execute SQL
#
proc execsql {sql} {
# puts "SQL = $sql"
return [db eval $sql]
}
@@ -97,3 +157,11 @@ proc execsql2 {sql} {
}
return $result
}
# Delete a file or directory
#
proc forcedelete {filename} {
if {[catch {file delete -force $filename}]} {
exec rm -rf $filename
}
}
+277 -23
View File
@@ -23,7 +23,7 @@
# This file implements regression tests for SQLite library. The
# focus of this file is testing the UPDATE statement.
#
# $Id: update.test,v 1.4 2000/06/21 13:59:14 drh Exp $
# $Id: update.test,v 1.5 2001/02/19 18:24:22 drh Exp $
set testdir [file dirname $argv0]
source $testdir/tester.tcl
@@ -90,47 +90,301 @@ do_test update-3.8 {
execsql {UPDATE test1 SET F2=f1, F1=f2}
execsql {SELECT * FROM test1 ORDER BY F1}
} {2 1 4 2 8 3 16 4 32 5 64 6 128 7 256 8 512 9 1024 10}
# Create an index and make sure updating works with an index.
#
do_test update-3.9 {
execsql {CREATE INDEX index1 ON test1(f1)}
execsql {CREATE INDEX index2 ON test1(f1)}
execsql {UPDATE test1 SET f1=1025 WHERE f2==10}
execsql {SELECT * FROM test1 ORDER BY f1}
} {2 1 4 2 8 3 16 4 32 5 64 6 128 7 256 8 512 9 1025 10}
do_test update-3.10 {
execsql {SELECT * FROM test1 WHERE f1=1025}
} {1025 10}
do_test update-3.11 {
execsql {UPDATE test1 SET f2=11 WHERE f1==1025}
execsql {SELECT * FROM test1 ORDER BY f1}
} {2 1 4 2 8 3 16 4 32 5 64 6 128 7 256 8 512 9 1025 11}
do_test update-3.12 {
execsql {SELECT * FROM test1 WHERE f1=1025}
} {1025 11}
execsql {UPDATE test1 SET F2=f1, F1=f2}
execsql {SELECT * FROM test1 ORDER BY F1}
} {1 2 2 4 3 8 4 16 5 32 6 64 7 128 8 256 9 512 10 1024}
# Create duplicate entries and make sure updating still
# works.
#
do_test update-4.0 {
execsql {
DELETE FROM test1 WHERE f1<=5;
INSERT INTO test1(f1,f2) VALUES(8,88);
INSERT INTO test1(f1,f2) VALUES(8,888);
INSERT INTO test1(f1,f2) VALUES(77,128);
INSERT INTO test1(f1,f2) VALUES(777,128);
}
execsql {SELECT * FROM test1 ORDER BY f1,f2}
} {6 64 7 128 8 88 8 256 8 888 9 512 10 1024 77 128 777 128}
do_test update-4.1 {
execsql {UPDATE test1 SET f2=f2+1 WHERE f1==8}
execsql {SELECT * FROM test1 ORDER BY f1,f2}
} {6 64 7 128 8 89 8 257 8 889 9 512 10 1024 77 128 777 128}
do_test update-4.2 {
execsql {UPDATE test1 SET f2=f2-1 WHERE f1==8 and f2>800}
execsql {SELECT * FROM test1 ORDER BY f1,f2}
} {6 64 7 128 8 89 8 257 8 888 9 512 10 1024 77 128 777 128}
do_test update-4.3 {
execsql {UPDATE test1 SET f2=f2-1 WHERE f1==8 and f2<800}
execsql {SELECT * FROM test1 ORDER BY f1,f2}
} {6 64 7 128 8 88 8 256 8 888 9 512 10 1024 77 128 777 128}
do_test update-4.4 {
execsql {UPDATE test1 SET f1=f1+1 WHERE f2==128}
execsql {SELECT * FROM test1 ORDER BY f1,f2}
} {6 64 8 88 8 128 8 256 8 888 9 512 10 1024 78 128 778 128}
do_test update-4.5 {
execsql {UPDATE test1 SET f1=f1-1 WHERE f1>100 and f2==128}
execsql {SELECT * FROM test1 ORDER BY f1,f2}
} {6 64 8 88 8 128 8 256 8 888 9 512 10 1024 78 128 777 128}
do_test update-4.6 {
execsql {UPDATE test1 SET f1=f1-1 WHERE f1<=100 and f2==128}
execsql {SELECT * FROM test1 ORDER BY f1,f2}
} {6 64 7 128 8 88 8 256 8 888 9 512 10 1024 77 128 777 128}
# Repeat the previous sequence of tests with an index.
#
do_test update-5.0 {
execsql {CREATE INDEX idx1 ON test1(f1)}
execsql {SELECT * FROM test1 ORDER BY f1,f2}
} {6 64 7 128 8 88 8 256 8 888 9 512 10 1024 77 128 777 128}
do_test update-5.1 {
execsql {UPDATE test1 SET f2=f2+1 WHERE f1==8}
execsql {SELECT * FROM test1 ORDER BY f1,f2}
} {6 64 7 128 8 89 8 257 8 889 9 512 10 1024 77 128 777 128}
do_test update-5.2 {
execsql {UPDATE test1 SET f2=f2-1 WHERE f1==8 and f2>800}
execsql {SELECT * FROM test1 ORDER BY f1,f2}
} {6 64 7 128 8 89 8 257 8 888 9 512 10 1024 77 128 777 128}
do_test update-5.3 {
execsql {UPDATE test1 SET f2=f2-1 WHERE f1==8 and f2<800}
execsql {SELECT * FROM test1 ORDER BY f1,f2}
} {6 64 7 128 8 88 8 256 8 888 9 512 10 1024 77 128 777 128}
do_test update-5.4 {
execsql {UPDATE test1 SET f1=f1+1 WHERE f2==128}
execsql {SELECT * FROM test1 ORDER BY f1,f2}
} {6 64 8 88 8 128 8 256 8 888 9 512 10 1024 78 128 778 128}
do_test update-5.4.1 {
execsql {SELECT * FROM test1 WHERE f1==78 ORDER BY f1,f2}
} {78 128}
do_test update-5.4.2 {
execsql {SELECT * FROM test1 WHERE f1==778 ORDER BY f1,f2}
} {778 128}
do_test update-5.4.3 {
execsql {SELECT * FROM test1 WHERE f1==8 ORDER BY f1,f2}
} {8 88 8 128 8 256 8 888}
do_test update-5.5 {
execsql {UPDATE test1 SET f1=f1-1 WHERE f1>100 and f2==128}
execsql {SELECT * FROM test1 ORDER BY f1,f2}
} {6 64 8 88 8 128 8 256 8 888 9 512 10 1024 78 128 777 128}
do_test update-5.5.1 {
execsql {SELECT * FROM test1 WHERE f1==78 ORDER BY f1,f2}
} {78 128}
do_test update-5.5.2 {
execsql {SELECT * FROM test1 WHERE f1==778 ORDER BY f1,f2}
} {}
do_test update-5.5.3 {
execsql {SELECT * FROM test1 WHERE f1==777 ORDER BY f1,f2}
} {777 128}
do_test update-5.5.4 {
execsql {SELECT * FROM test1 WHERE f1==8 ORDER BY f1,f2}
} {8 88 8 128 8 256 8 888}
do_test update-5.6 {
execsql {UPDATE test1 SET f1=f1-1 WHERE f1<=100 and f2==128}
execsql {SELECT * FROM test1 ORDER BY f1,f2}
} {6 64 7 128 8 88 8 256 8 888 9 512 10 1024 77 128 777 128}
do_test update-5.6.1 {
execsql {SELECT * FROM test1 WHERE f1==77 ORDER BY f1,f2}
} {77 128}
do_test update-5.6.2 {
execsql {SELECT * FROM test1 WHERE f1==778 ORDER BY f1,f2}
} {}
do_test update-5.6.3 {
execsql {SELECT * FROM test1 WHERE f1==777 ORDER BY f1,f2}
} {777 128}
do_test update-5.6.4 {
execsql {SELECT * FROM test1 WHERE f1==8 ORDER BY f1,f2}
} {8 88 8 256 8 888}
# Repeat the previous sequence of tests with a different index.
#
do_test update-6.0 {
execsql {DROP INDEX idx1}
execsql {CREATE INDEX idx1 ON test1(f2)}
execsql {SELECT * FROM test1 ORDER BY f1,f2}
} {6 64 7 128 8 88 8 256 8 888 9 512 10 1024 77 128 777 128}
do_test update-6.1 {
execsql {UPDATE test1 SET f2=f2+1 WHERE f1==8}
execsql {SELECT * FROM test1 ORDER BY f1,f2}
} {6 64 7 128 8 89 8 257 8 889 9 512 10 1024 77 128 777 128}
do_test update-6.1.1 {
execsql {SELECT * FROM test1 WHERE f1==8 ORDER BY f1,f2}
} {8 89 8 257 8 889}
do_test update-6.1.2 {
execsql {SELECT * FROM test1 WHERE f2==89 ORDER BY f1,f2}
} {8 89}
do_test update-6.1.3 {
execsql {SELECT * FROM test1 WHERE f1==88 ORDER BY f1,f2}
} {}
do_test update-6.2 {
execsql {UPDATE test1 SET f2=f2-1 WHERE f1==8 and f2>800}
execsql {SELECT * FROM test1 ORDER BY f1,f2}
} {6 64 7 128 8 89 8 257 8 888 9 512 10 1024 77 128 777 128}
do_test update-6.3 {
execsql {UPDATE test1 SET f2=f2-1 WHERE f1==8 and f2<800}
execsql {SELECT * FROM test1 ORDER BY f1,f2}
} {6 64 7 128 8 88 8 256 8 888 9 512 10 1024 77 128 777 128}
do_test update-6.3.1 {
execsql {SELECT * FROM test1 WHERE f1==8 ORDER BY f1,f2}
} {8 88 8 256 8 888}
do_test update-6.3.2 {
execsql {SELECT * FROM test1 WHERE f2==89 ORDER BY f1,f2}
} {}
do_test update-6.3.3 {
execsql {SELECT * FROM test1 WHERE f2==88 ORDER BY f1,f2}
} {8 88}
do_test update-6.4 {
execsql {UPDATE test1 SET f1=f1+1 WHERE f2==128}
execsql {SELECT * FROM test1 ORDER BY f1,f2}
} {6 64 8 88 8 128 8 256 8 888 9 512 10 1024 78 128 778 128}
do_test update-6.4.1 {
execsql {SELECT * FROM test1 WHERE f1==78 ORDER BY f1,f2}
} {78 128}
do_test update-6.4.2 {
execsql {SELECT * FROM test1 WHERE f1==778 ORDER BY f1,f2}
} {778 128}
do_test update-6.4.3 {
execsql {SELECT * FROM test1 WHERE f1==8 ORDER BY f1,f2}
} {8 88 8 128 8 256 8 888}
do_test update-6.5 {
execsql {UPDATE test1 SET f1=f1-1 WHERE f1>100 and f2==128}
execsql {SELECT * FROM test1 ORDER BY f1,f2}
} {6 64 8 88 8 128 8 256 8 888 9 512 10 1024 78 128 777 128}
do_test update-6.5.1 {
execsql {SELECT * FROM test1 WHERE f1==78 ORDER BY f1,f2}
} {78 128}
do_test update-6.5.2 {
execsql {SELECT * FROM test1 WHERE f1==778 ORDER BY f1,f2}
} {}
do_test update-6.5.3 {
execsql {SELECT * FROM test1 WHERE f1==777 ORDER BY f1,f2}
} {777 128}
do_test update-6.5.4 {
execsql {SELECT * FROM test1 WHERE f1==8 ORDER BY f1,f2}
} {8 88 8 128 8 256 8 888}
do_test update-6.6 {
execsql {UPDATE test1 SET f1=f1-1 WHERE f1<=100 and f2==128}
execsql {SELECT * FROM test1 ORDER BY f1,f2}
} {6 64 7 128 8 88 8 256 8 888 9 512 10 1024 77 128 777 128}
do_test update-6.6.1 {
execsql {SELECT * FROM test1 WHERE f1==77 ORDER BY f1,f2}
} {77 128}
do_test update-6.6.2 {
execsql {SELECT * FROM test1 WHERE f1==778 ORDER BY f1,f2}
} {}
do_test update-6.6.3 {
execsql {SELECT * FROM test1 WHERE f1==777 ORDER BY f1,f2}
} {777 128}
do_test update-6.6.4 {
execsql {SELECT * FROM test1 WHERE f1==8 ORDER BY f1,f2}
} {8 88 8 256 8 888}
# Repeat the previous sequence of tests with multiple
# indices
#
do_test update-7.0 {
execsql {CREATE INDEX idx2 ON test1(f2)}
execsql {CREATE INDEX idx3 ON test1(f1,f2)}
execsql {SELECT * FROM test1 ORDER BY f1,f2}
} {6 64 7 128 8 88 8 256 8 888 9 512 10 1024 77 128 777 128}
do_test update-7.1 {
execsql {UPDATE test1 SET f2=f2+1 WHERE f1==8}
execsql {SELECT * FROM test1 ORDER BY f1,f2}
} {6 64 7 128 8 89 8 257 8 889 9 512 10 1024 77 128 777 128}
do_test update-7.1.1 {
execsql {SELECT * FROM test1 WHERE f1==8 ORDER BY f1,f2}
} {8 89 8 257 8 889}
do_test update-7.1.2 {
execsql {SELECT * FROM test1 WHERE f2==89 ORDER BY f1,f2}
} {8 89}
do_test update-7.1.3 {
execsql {SELECT * FROM test1 WHERE f1==88 ORDER BY f1,f2}
} {}
do_test update-7.2 {
execsql {UPDATE test1 SET f2=f2-1 WHERE f1==8 and f2>800}
execsql {SELECT * FROM test1 ORDER BY f1,f2}
} {6 64 7 128 8 89 8 257 8 888 9 512 10 1024 77 128 777 128}
do_test update-7.3 {
execsql {UPDATE test1 SET f2=f2-1 WHERE f1==8 and f2<800}
execsql {SELECT * FROM test1 ORDER BY f1,f2}
} {6 64 7 128 8 88 8 256 8 888 9 512 10 1024 77 128 777 128}
do_test update-7.3.1 {
execsql {SELECT * FROM test1 WHERE f1==8 ORDER BY f1,f2}
} {8 88 8 256 8 888}
do_test update-7.3.2 {
execsql {SELECT * FROM test1 WHERE f2==89 ORDER BY f1,f2}
} {}
do_test update-7.3.3 {
execsql {SELECT * FROM test1 WHERE f2==88 ORDER BY f1,f2}
} {8 88}
do_test update-7.4 {
execsql {UPDATE test1 SET f1=f1+1 WHERE f2==128}
execsql {SELECT * FROM test1 ORDER BY f1,f2}
} {6 64 8 88 8 128 8 256 8 888 9 512 10 1024 78 128 778 128}
do_test update-7.4.1 {
execsql {SELECT * FROM test1 WHERE f1==78 ORDER BY f1,f2}
} {78 128}
do_test update-7.4.2 {
execsql {SELECT * FROM test1 WHERE f1==778 ORDER BY f1,f2}
} {778 128}
do_test update-7.4.3 {
execsql {SELECT * FROM test1 WHERE f1==8 ORDER BY f1,f2}
} {8 88 8 128 8 256 8 888}
do_test update-7.5 {
execsql {UPDATE test1 SET f1=f1-1 WHERE f1>100 and f2==128}
execsql {SELECT * FROM test1 ORDER BY f1,f2}
} {6 64 8 88 8 128 8 256 8 888 9 512 10 1024 78 128 777 128}
do_test update-7.5.1 {
execsql {SELECT * FROM test1 WHERE f1==78 ORDER BY f1,f2}
} {78 128}
do_test update-7.5.2 {
execsql {SELECT * FROM test1 WHERE f1==778 ORDER BY f1,f2}
} {}
do_test update-7.5.3 {
execsql {SELECT * FROM test1 WHERE f1==777 ORDER BY f1,f2}
} {777 128}
do_test update-7.5.4 {
execsql {SELECT * FROM test1 WHERE f1==8 ORDER BY f1,f2}
} {8 88 8 128 8 256 8 888}
do_test update-7.6 {
execsql {UPDATE test1 SET f1=f1-1 WHERE f1<=100 and f2==128}
execsql {SELECT * FROM test1 ORDER BY f1,f2}
} {6 64 7 128 8 88 8 256 8 888 9 512 10 1024 77 128 777 128}
do_test update-7.6.1 {
execsql {SELECT * FROM test1 WHERE f1==77 ORDER BY f1,f2}
} {77 128}
do_test update-7.6.2 {
execsql {SELECT * FROM test1 WHERE f1==778 ORDER BY f1,f2}
} {}
do_test update-7.6.3 {
execsql {SELECT * FROM test1 WHERE f1==777 ORDER BY f1,f2}
} {777 128}
do_test update-7.6.4 {
execsql {SELECT * FROM test1 WHERE f1==8 ORDER BY f1,f2}
} {8 88 8 256 8 888}
# Error messages
#
do_test update-4.1 {
do_test update-9.1 {
set v [catch {execsql {
UPDATE test1 SET x=11 WHERE f1=1025
}} msg]
lappend v $msg
} {1 {no such column: x}}
do_test update-4.2 {
do_test update-9.2 {
set v [catch {execsql {
UPDATE test1 SET f1=x(11) WHERE f1=1025
}} msg]
lappend v $msg
} {1 {no such function: x}}
do_test update-4.3 {
do_test update-9.3 {
set v [catch {execsql {
UPDATE test1 SET f1=11 WHERE x=1025
}} msg]
lappend v $msg
} {1 {no such column: x}}
do_test update-4.4 {
do_test update-9.4 {
set v [catch {execsql {
UPDATE test1 SET f1=11 WHERE x(f1)=1025
}} msg]
+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]
+144
View File
@@ -0,0 +1,144 @@
/*
** 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/
**
*************************************************************************
** A utility print statistics about the content of a GDBM database.
*/
#include <stdio.h>
#include <ctype.h>
#include <gdbm.h>
#include <stdlib.h>
static int bins[] = {
4, 8, 12, 16, 24,
32, 40, 48, 56, 64,
80, 96, 112, 128, 144, 160, 176, 192, 208, 224, 240, 256,
288, 320, 352, 384, 416, 448, 480, 512, 1024, 2048, 4096, 8192,
16384, 32768, 65536,
};
#define NBIN (sizeof(bins)/sizeof(bins[0])+1)
static int gAllSize[NBIN];
static int gCount;
static int gTotal;
static int gMax;
static int gKey;
static int gMaxKey;
static int gdbm_stat(char *zFilename){
GDBM_FILE p;
datum data, key, next;
int nEntry = 0;
int keyTotal = 0;
int dataTotal = 0;
int allTotal = 0;
int lMax = 0;
int keySize[NBIN], dataSize[NBIN], allSize[NBIN];
int i, priorSize;
p = gdbm_open(zFilename, 0, GDBM_READER, 0, 0);
if( p==0 ){
fprintf(stderr,"can't open file \"%s\"\n", zFilename);
return 1;
}
for(i=0; i<NBIN; i++){
keySize[i] = 0;
dataSize[i] = 0;
allSize[i] = 0;
}
key = gdbm_firstkey(p);
while( key.dptr ){
int all;
nEntry++;
gCount++;
keyTotal += key.dsize;
for(i=0; i<NBIN-1 && key.dsize>bins[i]; i++){}
keySize[i]++;
gKey += key.dsize;
if( key.dsize>gMaxKey ) gMaxKey = key.dsize;
data = gdbm_fetch(p, key);
if( data.dptr==0 ) data.dsize = 0;
dataTotal += data.dsize;
for(i=0; i<NBIN-1 && data.dsize>bins[i]; i++){}
dataSize[i]++;
all = key.dsize + data.dsize;
allTotal += all;
gTotal += all;
if( all>gMax ) gMax = all;
if( all>lMax ) lMax = all;
for(i=0; i<NBIN-1 && all>bins[i]; i++){}
allSize[i]++;
gAllSize[i]++;
next = gdbm_nextkey(p, key);
free( key.dptr );
key = next;
}
gdbm_close(p);
printf("%s:\n", zFilename);
printf(" entries: %d\n", nEntry);
printf(" keysize: %d (%d per entry)\n",
keyTotal, nEntry>0 ? (keyTotal+nEntry-1)/nEntry : 0);
printf(" datasize: %d (%d per entry)\n",
dataTotal, nEntry>0 ? (dataTotal+nEntry-1)/nEntry : 0);
printf(" size: %d (%d per entry)\n",
allTotal, nEntry>0 ? (allTotal+nEntry-1)/nEntry : 0);
priorSize = 0;
for(i=0; i<NBIN-1; i++){
if( keySize[i]==0 && dataSize[i]==0 ) continue;
printf("%5d..%-5d %7d %7d %7d\n", priorSize, bins[i], keySize[i],
dataSize[i], allSize[i]);
priorSize = bins[i]+1;
}
if( keySize[NBIN-1]>0 || dataSize[NBIN-1]>0 ){
printf("%5d..%-5d %7d %7d %7d\n", priorSize, lMax,
keySize[NBIN-1], dataSize[NBIN-1], allSize[NBIN-1]);
}
return 0;
}
int main(int argc, char **argv){
int i, ps, sum;
int nErr = 0;
for(i=1; i<argc; i++){
nErr += gdbm_stat(argv[i]);
}
printf("*****************************************************************\n");
printf("Entries: %d\n", gCount);
printf("Size: %d\n", gTotal);
printf("Avg Size: %d\n", gCount>0 ? (gTotal + gCount - 1)/gCount : 0);
printf("Key Size: %d\n", gKey);
printf("Avg Key Size: %d\n", gCount>0 ? (gKey + gCount - 1)/gCount : 0);
printf("Max Key Size: %d\n\n", gMaxKey);
ps = 0;
sum = 0;
for(i=0; i<NBIN-1; i++){
if( gAllSize[i]==0 ) continue;
sum += gAllSize[i];
printf("%5d..%-5d %8d %3d%%\n",
ps, bins[i], gAllSize[i], sum*100/gCount);
ps = bins[i]+1;
}
if( gAllSize[NBIN-1]>0 ){
printf("%5d..%-5d %8d 100%%\n", ps, gMax, gAllSize[NBIN-1]);
}
return nErr;
}
+66
View File
@@ -0,0 +1,66 @@
The SQL database used for ACD contains 113 tables and indices implemented
in GDBM. The following are statistics on the sizes of keys and data
within these tables and indices.
Entries: 962080
Size: 45573853
Avg Size: 48
Key Size: 11045299
Avg Key Size: 12
Max Key Size: 99
Size of key Cummulative
and data Instances Percentage
------------ ---------- -----------
0..8 266 0%
9..12 5485 0%
13..16 73633 8%
17..24 180918 27%
25..32 209823 48%
33..40 148995 64%
41..48 76304 72%
49..56 14346 73%
57..64 15725 75%
65..80 44916 80%
81..96 127815 93%
97..112 34769 96%
113..128 13314 98%
129..144 8098 99%
145..160 3355 99%
161..176 1159 99%
177..192 629 99%
193..208 221 99%
209..224 210 99%
225..240 129 99%
241..256 57 99%
257..288 496 99%
289..320 60 99%
321..352 37 99%
353..384 46 99%
385..416 22 99%
417..448 24 99%
449..480 26 99%
481..512 27 99%
513..1024 471 99%
1025..2048 389 99%
2049..4096 182 99%
4097..8192 74 99%
8193..16384 34 99%
16385..32768 17 99%
32769..65536 5 99%
65537..131073 3 100%
This information is gathered to help design the new built-in
backend for sqlite 2.0. Note in particular that 99% of all
database entries have a combined key and data size of less than
144 bytes. So if a leaf node in the new database is able to
store 144 bytes of combined key and data, only 1% of the leaves
will require overflow pages. Furthermore, note that no key
is larger than 99 bytes, so if the key will never be on an
overflow page.
The average combined size of key+data is 48. Add in 16 bytes of
overhead for a total of 64. That means that a 1K page will
store (on average) about 16 entries.
+34 -6
View File
@@ -1,7 +1,7 @@
#
# Run this Tcl script to generate the sqlite.html file.
#
set rcsid {$Id: c_interface.tcl,v 1.10 2000/10/09 12:57:01 drh Exp $}
set rcsid {$Id: c_interface.tcl,v 1.13 2000/11/28 20:46:39 drh Exp $}
puts {<html>
<head>
@@ -29,7 +29,7 @@ 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*);
@@ -68,6 +68,8 @@ int sqlite_get_table(
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*);
@@ -132,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
@@ -141,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
@@ -263,6 +279,10 @@ 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>
@@ -317,6 +337,14 @@ 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 next interface routine to SQLite is a convenience function used
@@ -430,7 +458,7 @@ meaning of single-quote within a string literal.
</p>
<p>Consider an example. Suppose you are trying to insert a string
values into a database table where the string value was obtained from
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>
+85 -2
View File
@@ -17,9 +17,92 @@ proc chng {date desc} {
puts "<DD><P><UL>$desc</UL></P></DD>"
}
chng {2000 Oct 11 (Not Released)} {
chng {2001 Mar 14 19 (1.0.24)} {
<li>Fix a bug which was causing
the UPDATE command to fail on systems where "malloc(0)" returns
NULL. The problem does not appear Windows, Linux, or HPUX but does
cause the library to fail on QNX.
</li>
}
chng {2001 Feb 19 (1.0.23)} {
<li>An unrelated (and minor) bug from Mark Muranwski fixed. The algorithm
for figuring out where to put temporary files for a "memory:" database
was not working quite right.
</li>
}
chng {2001 Feb 19 (1.0.22)} {
<li>The previous fix was not quite right. This one seems to work better.
</li>
}
chng {2001 Feb 19 (1.0.21)} {
<li>The UPDATE statement was not working when the WHERE clause contained
some terms that could be satisfied using indices and other terms that
could not. Fixed.</li>
}
chng {2001 Feb 11 (1.0.20)} {
<li>Merge development changes into the main trunk. Future work toward
using a BTree file structure will use a separate CVS source tree. This
CVS tree will continue to support the GDBM version of SQLite only.</li>
}
chng {2001 Feb 6 (1.0.19)} {
<li>Fix a strange (but valid) C declaration that was causing problems
for QNX. No logical changes.</li>
}
chng {2001 Jan 4 (1.0.18)} {
<li>Print the offending SQL statement when an error occurs.</li>
<li>Do not require commas between constraints in CREATE TABLE statements.</li>
<li>Added the "-echo" option to the shell.</li>
<li>Changes to comments.</li>
}
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>Add Doug Lea's memory allocator to the distribution, for completeness.</li>
<li>Removed a few variables that were not being used. Etc.</li>
}
+3 -3
View File
@@ -1,7 +1,7 @@
#
# Run this Tcl script to generate the crosscompile.html file.
#
set rcsid {$Id: crosscompile.tcl,v 1.3 2000/10/11 19:28:53 drh Exp $}
set rcsid {$Id: crosscompile.tcl,v 1.4 2001/01/31 13:28:09 drh Exp $}
puts {<html>
<head>
@@ -148,9 +148,9 @@ cat &gt;&gt;systems.h &lt;&lt;\END
#undef UNLOCK_FILE
#define UNLOCK_FILE(x)
#undef READLOCK_FILE
#define READLOCK_FILE(x)
#define READLOCK_FILE(x) lock_val=0;
#undef WRITELOCK_FILE
#define WRITELOCK_FILE(x)
#define WRITELOCK_FILE(x) lock_val=0;
#endif
END
</pre></blockquote>
+70
View File
@@ -0,0 +1,70 @@
#
# Run this Tcl script to generate the dynload.html file.
#
set rcsid {$Id: dynload.tcl,v 1.1 2001/02/11 16:58:22 drh Exp $}
puts {<html>
<head>
<title>How to build a dynamically loaded Tcl extension for SQLite</title>
</head>
<body bgcolor=white>
<h1 align=center>
How To Build A Dynamically Loaded Tcl Extension
</h1>}
puts {<p>
<i>This note was contributed by
<a href="bsaunder@tampabay.rr.com.nospam">Bill Saunders</a>. Thanks, Bill!</i>
<p>
To compile the SQLite Tcl extension into a dynamically loaded module
I did the following:
</p>
<ol>
<li><p>Do a standard compile
(I had a dir called bld at the same level as sqlite ie
/root/bld
/root/sqlite
I followed the directions and did a standard build in the bld
directory)</p></li>
<li><p>
Now do the following in the bld directory
<blockquote><pre>
gcc -shared -I. -lgdbm ../sqlite/src/tclsqlite.c libsqlite.a -o sqlite.so
</pre></blockquote></p></li>
<li><p>
This should produce the file sqlite.so in the bld directory</p></li>
<li><p>
Create a pkgIndex.tcl file that contains this line
<blockquote><pre>
package ifneeded sqlite 1.0 [list load [file join $dir sqlite.so]]
</pre></blockquote></p></li>
<li><p>
To use this put sqlite.so and pkgIndex.tcl in the same directory</p></li>
<li><p>
From that directory start wish</p></li>
<li><p>
Execute the following tcl command (tells tcl where to fine loadable
modules)
<blockquote><pre>
lappend auto_path [exec pwd]
</pre></blockquote></p></li>
<li><p>
Load the package
<blockquote><pre>
package require sqlite
</pre></blockquote></p></li>
<li><p>
Have fun....</p></li>
</ul>
</body></html>}
+9 -2
View File
@@ -1,7 +1,7 @@
#
# Run this TCL script to generate HTML for the index.html file.
#
set rcsid {$Id: index.tcl,v 1.29 2000/09/30 22:46:07 drh Exp $}
set rcsid {$Id: index.tcl,v 1.31 2001/02/19 23:28:37 drh Exp $}
puts {<html>
<head><title>SQLite: An SQL Database Library Built Atop GDBM</title></head>
@@ -61,6 +61,12 @@ all code except for a few areas which are unreachable or which are
only reached when <tt>malloc()</tt> fails. The code has been tested
for memory leaks and is found to be clean.</p>
<p><b>Important Note:</b> A bug was found in the processing of UPDATE
statements when the WHERE clause contained some terms that could be
satisfied using indices and other terms which could not. The problem
was fixed in version 1.0.22. Users of prior versions of SQLite should
consider upgrading.</p>
<p>
Among the SQL features that SQLite does not currently implement are:</p>
@@ -75,7 +81,8 @@ Among the SQL features that SQLite does not currently implement are:</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
Aug 2, 2000 (prior to version 1.0, when SQLite was still in Beta).
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
+26 -2
View File
@@ -1,7 +1,7 @@
#
# Run this Tcl script to generate the sqlite.html file.
#
set rcsid {$Id: lang.tcl,v 1.5 2000/08/04 13:49:03 drh Exp $}
set rcsid {$Id: lang.tcl,v 1.6 2001/02/20 13:06:31 drh Exp $}
puts {<html>
<head>
@@ -361,6 +361,30 @@ operand of an IN operator, then the first row of the result of the
SELECT becomes the value used in the expression. If the SELECT yields
more than one result row, all rows after the first are ignored. If
the SELECT yeilds no rows, then the value of the SELECT is NULL.</p>
<p>The expression syntax currently supports the following
functions:</p>
<blockquote><pre>
<font color="#2c2cf0"><big>count min max sum
avg length substr</big></font>
</pre></blockquote>
<p>
The functions <b>count</b>, <b>sum</b>, and <b>avg</b> and the functions
<b>min</b> and <b>max</b> used with only one argument are all aggregate
functions. This means that they are computed across all rows of the result.
The functions <b>min</b> and <b>max</b>
with two or more arguments and the
functions <b>length</b> and <b>substr</b>
are non-aggregates. Non-aggregate functions
are computed separately for each row of the result.
</p>
<p>
The "<b>count(*)</b>" syntax is supported but
"<b>count(distinct</b> <i>COLUMN-NAME</i><b>)</b>" is not.
</p>
}
Section INSERT insert
@@ -492,7 +516,7 @@ puts {
command found in PostgreSQL. If VACUUM is invoked with the name of a
table or index, then the <b>gdbm_reorganize()</b> function is called
on the corresponding GDBM file. If VACUUM is invoked with no arguments,
then <b>gdbm_reorganize()</b> is call on every GDBM file in the database.</p>
then <b>gdbm_reorganize()</b> is called for every GDBM file in the database.</p>
<p>It is a good idea to run VACUUM after creating large indices,
especially indices where a single index value refers to many
+31 -7
View File
@@ -1,7 +1,7 @@
#
# Run this Tcl script to generate the tclsqlite.html file.
#
set rcsid {$Id: tclsqlite.tcl,v 1.2 2000/10/08 22:20:58 drh Exp $}
set rcsid {$Id: tclsqlite.tcl,v 1.3 2000/10/23 13:16:33 drh Exp $}
puts {<html>
<head>
@@ -30,19 +30,43 @@ namespace.</p>
<p>The <b>sqlite</b> command is used as follows:</p>
<blockquote>
<b>sqlite</b>&nbsp;&nbsp;<i>dbcmd&nbsp;&nbsp;database-directory-name</i>
<b>sqlite</b>&nbsp;&nbsp;<i>dbcmd&nbsp;&nbsp;database-name</i>
</blockquote>
<p>
The <b>sqlite</b> command opens the SQLite database located in the
directory named by the second argument. If the database or directory
does not exist, it is created. The <b>sqlite</b> command
also creates a new Tcl
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
@@ -205,7 +229,7 @@ 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 library the underlies SQLite allows multiple simultaneous
<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.
+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