Compare commits

...

26 Commits

Author SHA1 Message Date
drh 60ea3720f2 Version 2.4.9 (CVS 542)
FossilOrigin-Name: 0691720a4b94141635734ab0a8c4072cab189a33
2002-04-22 00:35:06 +00:00
drh 85b988c803 Update the version number and changes log for 2.4.9 (CVS 541)
FossilOrigin-Name: a91da82f8ae2b375bf9f5e064f16d6a5770e20b6
2002-04-22 00:33:23 +00:00
drh e98d4fafe4 Fix for ticket #21 (I think): Do not return an "out of memory" error if we
can not find the users home directory. Instead, just report that we could
not find the home directory. (CVS 540)

FossilOrigin-Name: 8a50c57cc3342de9c6eca6c2567d3aa42b407f10
2002-04-21 19:06:22 +00:00
drh 77155c480a Version 2.4.8 (CVS 538)
FossilOrigin-Name: d703a2c5c4fa3e792e203fd9895b59570b5fdfd1
2002-04-20 14:45:31 +00:00
drh 57809db9c5 Update the change log for the 2.4.8 release. (CVS 539)
FossilOrigin-Name: c01bc2d19e8592fe3b1a5202926be649af96c783
2002-04-20 14:44:01 +00:00
drh 0ac6589202 Fix for ticket #1: Implement the GLOB and LIKE operators as functions that
can be overridden.  This way, a developer can change the LIKE operator to
be case sensitive, for example. (CVS 537)

FossilOrigin-Name: 51572bf71774d7631c7083be90b806e621bc9bee
2002-04-20 14:24:41 +00:00
drh 67505e78c6 Add support for saving the sqlite shell command-line history across sessions. (CVS 536)
FossilOrigin-Name: ca4abf3fe1f0e66802f9f98a20e0c8b82a6459aa
2002-04-19 12:34:06 +00:00
persicom c8cacb3704 Added rights release for Matthew O. Persico (CVS 535)
FossilOrigin-Name: 6c32c07e8218caffebd4503e7d8a90226ac81cdc
2002-04-19 01:00:12 +00:00
drh dd45df8a5b Change shell.c so that it will compile under windows. Shorten the help
command somewhat.  Add the state of ".header" to the output of ".show". (CVS 534)

FossilOrigin-Name: 0582168b8b853559b484f4a024d28c67192160c4
2002-04-18 12:39:03 +00:00
persicom 45698a3a03 Updated to match new shell.c functionality. (CVS 533)
FossilOrigin-Name: ff67ad40106a20fb85797cca4c495dc34ad5519b
2002-04-18 02:53:54 +00:00
persicom 1d0b872d43 Mistake in help text. Spurious trailing whitespace removed. (CVS 532)
FossilOrigin-Name: 4bdd040e4810565c91bcbb5f065580520c5e3c45
2002-04-18 02:53:04 +00:00
persicom 7e2dfdd726 General:
o Added global static chars mainPrompt and continuePrompt.
o Moved Argv0 declaration to head of file. Needed in do_meta_command,
  previously found below that.
o Added struct previous_mode_data to support new .explain toggle
  functionality.
o Added nullvalue, explainPrev and outfile members to
  struct callback_data.
o Added modeDescr array for number/text translation ofdisplay modes.
o Modified zHelp to match new functionality.

callback():
o Added support for .nullvalue

do_meta_command():
o Output filename is now saved to callback struct. If using stdout,
  then the string "stdout" is saved.
o Explain is now a toggle. When it is turned on, the current values of
  mode, header and colWidth are saved if not already in explain mode.
  When turned off, those values are restored.
o Allow .mode plurals columns and lines and dot command plural
  .headers.
o Added processing for new keywords .quit, .nullvalue, .show, .prompt.

main():
o Added -init as an option to override .sqliterc.
o Added -nullvalue as a command line option.
o Processes .sqliterc.

main_init():
o Genesis. Moved some initialization code here from inside main() so
  that it can be called initially by main and again if -init is
  specified.

one_input_line():
o Now takes prompts from settable values.

process_sqliterc():
o Genesis. Read .sqliterc from user's home directory and pass it to
  process_input(). File should contain meta commands for setups. (CVS 531)

FossilOrigin-Name: e751338c468cdad79efcc5cd5b924eb2c1eb3c3a
2002-04-18 02:46:52 +00:00
drh a168045f7d Fix for ticket #19: Do not call sqliteOsSync() if the only changes were
changes to TEMP tables. (CVS 530)

FossilOrigin-Name: 33da20b9c1a8eef16ad7ab5929bb8937c75090f2
2002-04-18 01:56:57 +00:00
drh fc6cdfe9dc When doing a ".dump" command in the command-line shell, make sure VIEWs
are created after TABLEs. (CVS 529)

FossilOrigin-Name: 7edd13468e24d79939f0fa1e58f3b686422ca826
2002-04-13 23:42:24 +00:00
drh 6b0e620fda Version 2.4.7 (CVS 528)
FossilOrigin-Name: 977abbaebe5433c66516d0376a1c229e45b5ab1f
2002-04-12 13:12:25 +00:00
drh adbe353fc6 Add tests to prevent a recurrance of bug #16. Also: put a hyperlink to the new
ticket screen on the main page of the website. (CVS 527)

FossilOrigin-Name: cdab6dad323dd1e95ec8b7684a7c4b06c75905e0
2002-04-12 13:11:52 +00:00
drh c8d30ac109 Fix for bug #15: Add the sqlite_changes() API function for retrieving the
number of rows that changed in the previous operation. (CVS 526)

FossilOrigin-Name: 6e71493b9dc77d508c3ce90562766789e87e6d80
2002-04-12 10:08:59 +00:00
drh b04a5d8768 Fix for bug #16: Check for invalid functions in the VALUES clause of an
INSERT statement. (CVS 525)

FossilOrigin-Name: 43a77f019d34e1a6b3f502ad0ec31a00c8fdbe6e
2002-04-12 03:55:15 +00:00
drh feeb1394ee Fix for bug #11: Output the correct row count when and INSERT does an
IGNORE action. (CVS 524)

FossilOrigin-Name: bb83642e9a6c1c9ade861618496933c9f922a8f8
2002-04-09 03:28:01 +00:00
drh fe1a1773a8 Fix for bug #10: Pop the stack by the right amount on an IGNORE so that the
stack does not grow without bound. (CVS 523)

FossilOrigin-Name: f46acfc3b828620e4e97b09f9aff119b9313e5d7
2002-04-09 03:15:06 +00:00
drh 8b32e17d26 Add a -column option to the sqlite command-line utility.
Patch from Matthew O. Persico. (CVS 522)

FossilOrigin-Name: 760bf568c882d7b28746b1e004309ef08d2ff4c0
2002-04-08 02:42:57 +00:00
drh 6ed41ad735 Added the last_insert_rowid() SQL function. (CVS 521)
FossilOrigin-Name: 6aca3f86bc08849e9d806fdd490f98e4daf71025
2002-04-06 14:10:47 +00:00
drh bf3a4fa4cb Make the FROM clause on a SELECT optional. If omitted, the result of
the SELECT is a single row consisting of the values in the expression list. (CVS 520)

FossilOrigin-Name: 28ce42f7872e2660faa22e66b508db9b1f046af0
2002-04-06 13:57:42 +00:00
drh 41e941dda6 Add an fflush() call to shell.c to insure that all output has been written
before we prompt for a new line of input. (CVS 519)

FossilOrigin-Name: 932274187e045ce331177b1a640ed62da4a22d94
2002-04-04 15:10:12 +00:00
drh 5447322939 Fix for bug #2: Add support for TABLE.* in SELECT statements. (CVS 518)
FossilOrigin-Name: c2320eabfe44d6eb05c02b76547e5bd48a29943c
2002-04-04 02:10:55 +00:00
drh f238f700c2 Added a hyperlink to the SQLite-PHP project. (CVS 517)
FossilOrigin-Name: ffdeec3087e168acaa04a6331e91369e2e0f8345
2002-04-03 20:50:21 +00:00
30 changed files with 1052 additions and 303 deletions
+1 -1
View File
@@ -1 +1 @@
2.4.6
2.4.9
+33 -33
View File
@@ -1,9 +1,9 @@
C Version\s2.4.6\s(CVS\s516)
D 2002-04-02T13:27:43
C Version\s2.4.9\s(CVS\s542)
D 2002-04-22T00:35:06
F Makefile.in 50f1b3351df109b5774771350d8c1b8d3640130d
F Makefile.template 89e373b2dad0321df00400fa968dc14b61a03296
F README a4c0ba11354ef6ba0776b400d057c59da47a4cc0
F VERSION ed0299de5b86deca846fdb0f8b99aaae85f27f47
F VERSION 23cded544ac22813b39f410aac0a8735ae991d64
F aclocal.m4 11faa843caa38fd451bc6aeb43e248d1723a269d
F config.guess f38b1e93d1e0fa6f5a6913e9e7b12774b9232588
F config.log 6a73d03433669b10a3f0c221198c3f26b9413914
@@ -18,33 +18,33 @@ F libtool c56e618713c9510a103bda6b95f3ea3900dcacd6
F ltmain.sh e9ed72eb1d690f447c13945eaf69e28af531eda1
F publish.sh 0b9da5898bf5997f30d6f80de4880072db7e5718
F spec.template 238f7db425a78dc1bb7682e56e3834c7270a3f5e
F sqlite.1 2e2bb0529ef468ade9e4322bd609d0695fb9ded9
F sqlite.1 6b339cb747b45f8e7ea0335b06291b6d52067b31
F src/TODO af7f3cab0228e34149cf98e073aa83d45878e7e6
F src/btree.c 7dd7ddc66459982dd0cb9800958c1f8d65a32d9f
F src/btree.h 8abeabfe6e0b1a990b64fa457592a6482f6674f3
F src/build.c d01b81f41481e733e27ab2fa8e1bfcc64f24257d
F src/delete.c 577da499162291c1855f0b304b211bffcf9da945
F src/expr.c e7a1e22bc2ebcd789f0f8c0db544cf16ad664054
F src/func.c 87516e7dc37190c24af77593931a5d09d797520a
F src/delete.c 6a6b8192cdff5e4b083da3bc63de099f3790d01f
F src/expr.c cf8d2ea17e419fc83b23e080195b2952e0be4164
F src/func.c a31dcba85bc2ecb9b752980289cf7e6cd0cafbce
F src/hash.c cc259475e358baaf299b00a2c7370f2b03dda892
F src/hash.h dca065dda89d4575f3176e75e9a3dc0f4b4fb8b9
F src/insert.c 903ff262c90586c99eafd43934517bc3aed5f77e
F src/main.c b21019084b93fe685a8a25217d01f6958584ae9b
F src/insert.c 31233f44fc79edbb43523a830e54736a8e222ff4
F src/main.c df43fe585d2bfb925c837b6822783c0ee3dd6e1c
F src/md5.c b2b1a34fce66ceca97f4e0dabc20be8be7933c92
F src/os.c 5ab8b6b4590d0c1ab8e96c67996c170e4462e0fc
F src/os.h 4a361fccfbc4e7609b3e1557f604f94c1e96ad10
F src/pager.c f136f5ba82c896d500a10b6a2e5caea62abf716b
F src/pager.c ba5740104cc27b342cd43eebfdc44d60f64a3ded
F src/pager.h 6fddfddd3b73aa8abc081b973886320e3c614f0e
F src/parse.y a0943e2774c7b0cab608640df1126f6c3ae39e90
F src/parse.y 0ce56f1c751657f01e18a4b4ac1aa4d29525ac20
F src/printf.c 300a90554345751f26e1fc0c0333b90a66110a1d
F src/random.c 19e8e00fe0df32a742f115773f57651be327cabe
F src/select.c 9323800e2937e84b52c198fffc51995d822b1779
F src/shell.c b3454229599246b944cdb5b95753af3fca5d8bb0
F src/select.c 92aef3f69e90dc065d680d88b1f075409e9249bb
F src/shell.c 5acbe59e137d60d8efd975c683dbea74ab626530
F src/shell.tcl 27ecbd63dd88396ad16d81ab44f73e6c0ea9d20e
F src/sqlite.h.in 1dae50411aee9439860d7fbe315183c582d27197
F src/sqliteInt.h 73e7d0f39a3c11d395fc422986959261bd193eba
F src/sqlite.h.in ffcacf73b5ed1a4939205d29a704a185758fa6a6
F src/sqliteInt.h e47ca9267a4c4a98e9f8d90c2df994a18f23d699
F src/table.c eed2098c9b577aa17f8abe89313a9c4413f57d63
F src/tclsqlite.c df847b71b28277f1cfa1ee1e3e51452ffe5a9a26
F src/tclsqlite.c c9e9039762d9866eae70bf782237d0206a13f57e
F src/test1.c d46ab7a82a9c16a3b1ee363cb4c0f98c5ff65743
F src/test2.c d410dbd8a90faa466c3ab694fa0aa57f5a773aa6
F src/test3.c 4e52fff8b01f08bd202f7633feda5639b7ba2b5e
@@ -52,23 +52,23 @@ F src/threadtest.c 81f0598e0f031c1bd506af337fdc1b7e8dff263f
F src/tokenize.c 5624d342601f616157ba266abccc1368a5afee70
F src/update.c 7dd714a6a7fa47f849ebb36b6d915974d6c6accb
F src/util.c b34cd91387bbfdc79319ea451a7d120cef478120
F src/vdbe.c 3cebe9f77cac311a3c537587258946d04a09c58f
F src/vdbe.h f9be1f6e9a336c3ff4d14ea7489ee976e07460cc
F src/vdbe.c 0ed88fff32273c103a24396d491b71bc69b4b596
F src/vdbe.h 67840a462e1daedb958cca0ccc97db140d3d9152
F src/where.c 9d36f6c9fea4af71501770c13089f824cb9b033c
F test/all.test 6aa106eee4d7127afa5cee97c51a783a79694ead
F test/bigrow.test 8ab252dba108f12ad64e337b0f2ff31a807ac578
F test/btree.test bf326f546a666617367a7033fa2c07451bd4f8e1
F test/btree2.test e3b81ec33dc2f89b3e6087436dfe605b870c9080
F test/btree3.test 9caa9e22491dd8cd8aa36d7ac3b48b089817c895
F test/conflict.test c794c6c8f6e59918107dbab2d201ae454bb47db8
F test/conflict.test 5149646703d3930c9111068b5cda7e2e938476e3
F test/copy.test b3cefcb520c64d7e7dfedbab06b4d4c31fa5b99a
F test/delete.test c904a62129fe102b314a96111a8417f10249e4d8
F test/expr.test 846795016b5993a7411f772eebe82ab67bd7230a
F test/func.test 4359344586067e79abf4c710c4737d67ed3cf963
F test/func.test d34e461f0acb0cf2978a4b3a3e098460f2ea8fbc
F test/in.test c09312672e3f0709fa02c8e2e9cd8fb4bd6269aa
F test/index.test c8a471243bbf878974b99baf5badd59407237cf3
F test/insert.test c36d534a4ab58c2cd452a273e51b2b0dd1ede1f9
F test/insert2.test 2f02b1e0dbfba3e8c76496209be5f4010b584181
F test/insert.test 58d44c19b3557f67f4aeb5110ed9ef02038c3684
F test/insert2.test eb8481878a7f52ccb4e3346f87550f5afdd77f76
F test/intpkey.test 31b5f28b2c44273e6695cf36ab2e4133aee7753c
F test/ioerr.test 57d9bffaca18b34f9e976f786eadc2591d6efc6a
F test/limit.test a930f3eba2a7691c8397ccab33710b931589566a
@@ -84,7 +84,7 @@ F test/printf.test 3cb415073754cb8ff076f26173143c3cd293a9da
F test/quick.test 6f023c7a73fc413e6d65b7a1879c79764038dc05
F test/quote.test 286db944717afa9a9bf829dd85e59185c65d5435
F test/rowid.test 4c55943300cddf73dd0f88d40a268cab14c83274
F test/select1.test 572d53f7b28c35a1efdce207f7579523358ba637
F test/select1.test 734b9f45a85b4f8d594390b1ed3cbff0b8c76c84
F test/select2.test aceea74fd895b9d007512f72499db589735bd8e4
F test/select3.test 9469c332250a75a0ef1771fb5da62dc04ec77f18
F test/select4.test 29a2ffb187f3d8b6ca42a0a6b619e9cabe12e228
@@ -94,7 +94,7 @@ F test/sort.test 3b996ce7ca385f9cd559944ac0f4027a23aa546b
F test/subselect.test 335d3dad8d585726c447dfee8d9c4f7383c76b78
F test/table.test 17b0b6eafa3faaee5545b7a94e6c1ff73f0880f3
F test/tableapi.test 3c80421a889e1d106df16e5800fa787f0d2914a6
F test/tclsqlite.test ca8dd89b02ab68bd4540163c24551756a69f6783
F test/tclsqlite.test 79deeffd7cd637ca0f06c5dbbf2f44d272079533
F test/temptable.test 0e9934283259a5e637eec756a7eefd6964c0f79b
F test/tester.tcl dc1b56bd628b487e4d75bfd1e7480b5ed8810ac6
F test/trans.test ae0b9a82d5d34122c3a3108781eb8d078091ccee
@@ -110,28 +110,28 @@ F tool/opNames.awk 5ba1f48aa854ee3b7c3d2b54233665bc3e649ea2
F tool/opcodeDoc.awk b3a2a3d5d3075b8bd90b7afe24283efdd586659c
F tool/renumberOps.awk 6d067177ad5f8d711b79577b462da9b3634bd0a9
F tool/report1.txt 9eae07f26a8fc53889b45fc833a66a33daa22816
F tool/speedtest.tcl 6d89431651f2eb800fb0998a8de44b84168e08b9
F tool/speedtest.tcl 8287851340fd4a9500a69e9b10fb5ed9e6b0d0c3
F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
F www/arch.fig d5f9752a4dbf242e9cfffffd3f5762b6c63b3bcf
F www/arch.png 82ef36db1143828a7abc88b1e308a5f55d4336f4
F www/arch.tcl 72a0c80e9054cc7025a50928d28d9c75c02c2b8b
F www/c_interface.tcl 567cda531aac9d68a61ef02e26c6b202bd856db2
F www/changes.tcl 563071eacf1c0fc03521bf874438274d83445c78
F www/c_interface.tcl 79f63ffa8c6e7adb9e3449ff325d093cfb0ce3e3
F www/changes.tcl c4bc9b9e57bf67f272e6d420de4d99175f4d9e82
F www/conflict.tcl 81dd21f9a679e60aae049e9dd8ab53d59570cda2
F www/crosscompile.tcl 3622ebbe518927a3854a12de51344673eb2dd060
F www/download.tcl 29aa6679ca29621d10613f60ebbbda18f4b91c49
F www/dynload.tcl 02eb8273aa78cfa9070dd4501dca937fb22b466c
F www/faq.tcl fb1e92e2f604546694f83a36d969492f52fb685d
F www/formatchng.tcl 2ce21ff30663fad6618198fe747ce675df577590
F www/index.tcl e6d59816ad6f90b03b0a92eccd45d6a4a928438a
F www/lang.tcl e6c277480c4e31877c08509af7a16f6c69bb1046
F www/index.tcl d0c52fbf031d0a3ee6d9d77aa669d5a4b24b6130
F www/lang.tcl 2d4654255ad1ec7f58d02dc41b59528c0ee6ea44
F www/mingw.tcl f1c7c0a7f53387dd9bb4f8c7e8571b7561510ebc
F www/opcode.tcl bdec8ef9f100dbd87bbef8976c54b88e43fd8ccc
F www/speed.tcl da8afcc1d3ccc5696cfb388a68982bc3d9f7f00f
F www/sqlite.tcl 8b5884354cb615049aed83039f8dfe1552a44279
F www/tclsqlite.tcl 829b393d1ab187fd7a5e978631b3429318885c49
F www/tclsqlite.tcl 1db15abeb446aad0caf0b95b8b9579720e4ea331
F www/vdbe.tcl 2013852c27a02a091d39a766bc87cff329f21218
P abb12259a09418eb6e3cf573ea718ac58c91ac7b
R 1e8462f1dea7fa55f9cf92098e6e2f92
P a91da82f8ae2b375bf9f5e064f16d6a5770e20b6
R 472bee23aa57568ebf6afa84b40ded15
U drh
Z c767d9da34c060a9378c5b233c151832
Z 4019f966a724267cde26980ddbf6ddea
+1 -1
View File
@@ -1 +1 @@
5ae7efd87f1a8e2f3e927bf373fbcb5c692d1a02
0691720a4b94141635734ab0a8c4072cab189a33
+76 -7
View File
@@ -2,7 +2,7 @@
.\" First parameter, NAME, should be all caps
.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection
.\" other parameters are allowed: see man(7), man(1)
.TH SQLITE SECTION "January 2, 2002"
.TH SQLITE 1 "Mon Apr 15 23:49:17 2002"
.\" Please adjust this date whenever revising the manpage.
.\"
.\" Some roff macros, for reference:
@@ -73,19 +73,39 @@ any time. For example:
.nf
.cc |
sqlite> .help
.dump Dump database in a text format
.dump ?TABLE? ... Dump the database in an text format
.echo ON|OFF Turn command echo on or off
.exit Exit this program
.explain Set output mode suitable for EXPLAIN
.header ON|OFF Turn display of headers on or off
.explain ON|OFF Turn output mode suitable for EXPLAIN on or off.
"off" will revert to the output mode that was
previously in effect
.header(s) ON|OFF Turn display of headers on or off
.help Show this message
.indices TABLE Show names of all indices on TABLE
.mode MODE Set mode to one of "line", "column", "list", or "html"
.mode MODE Set mode to one of "line(s)", "column(s)",
"insert", "list", or "html"
.mode insert TABLE Generate SQL insert statements for TABLE
.nullvalue STRING Print STRING instead of nothing for NULL data
.output FILENAME Send output to FILENAME
.output stdout Send output to the screen
.prompt MAIN CONTINUE Replace the standard prompts
"sqlite > " and " ...> "
with the strings MAIN and CONTINUE
CONTINUE is optional.
.quit Exit this program
.read FILENAME Execute SQL in FILENAME
.reindex ?TABLE? Rebuild indices
.schema ?TABLE? Show the CREATE statements
.separator STRING Change separator string for "list" mode
.tables List names all tables in the database
.show Show the current values for the following:
.echo
.explain
.mode
.nullvalue
.output
.separator
.width
.tables ?PATTERN? List names of tables matching a pattern
.timeout MS Try opening locked tables for MS milliseconds
.width NUM NUM ... Set column widths for "column" mode
sqlite>
@@ -96,6 +116,10 @@ sqlite>
.SH OPTIONS
The program has the following options:
.TP
.BI \-init\ file
Read in and process 'file', which contains "dot commands".
You can use this file to initialize display settings.
.TP
.B \-html
Set output mode to HTML.
.TP
@@ -105,9 +129,22 @@ Set output mode to 'list'.
.B \-line
Set output mode to 'line'.
.TP
.B \-column
Set output mode to 'column'.
.TP
.BI \-seperator\ seperator
Specify which output field seperator for 'list' mode to use.
Specify which output field seperator for 'list' mode to use.
Default is '|'.
.TP
.BI \-nullvalue\ string
When a null is encountered, print 'string'. Default is no string.
.TP
.B \-[no]header
Turn headers on or off. Default is off.
.TP
.B \-echo
Print commands before execution.
.SH OUTPUT MODE
The SQLite program has different output modes, which define the way
@@ -124,6 +161,38 @@ In HTML mode, an XHTML table is generated.
In 'column' mode, one record per line is output, aligned neatly in colums.
.SH INIT FILE
sqlite can be initialized using resource files. These can be combined with
command line arguments to set up sqlite exactly the way you want it.
Initialization proceeds as follows:
o The defaults of
.sp
.nf
.cc |
mode = LIST
separator = "|"
main prompt = "sqlite> "
continue prompt = " ...> "
|cc .
.sp
.fi
are established.
o If a file .sqliterc can be found in the user's home directory, it is
read and processed. It should only contain "dot commands". If the
file is not found or cannot be read, processing continues without
notification.
o If a file is specified on the command line with the -init option, it
is processed in the same manner as .sqliterc
o All other command line options are processed
o The database is opened and you are now ready to begin.
.SH SEE ALSO
http://www.hwaci.com/sw/sqlite/
.br
+5 -4
View File
@@ -12,7 +12,7 @@
** This file contains C code routines that are called by the parser
** to handle DELETE FROM statements.
**
** $Id: delete.c,v 1.29 2002/03/03 18:59:40 drh Exp $
** $Id: delete.c,v 1.30 2002/04/12 10:08:59 drh Exp $
*/
#include "sqliteInt.h"
@@ -183,7 +183,7 @@ void sqliteDeleteFrom(
}
end = sqliteVdbeMakeLabel(v);
addr = sqliteVdbeAddOp(v, OP_ListRead, 0, end);
sqliteGenerateRowDelete(v, pTab, base);
sqliteGenerateRowDelete(v, pTab, base, 1);
sqliteVdbeAddOp(v, OP_Goto, 0, addr);
sqliteVdbeResolveLabel(v, end);
sqliteVdbeAddOp(v, OP_ListReset, 0, 0);
@@ -229,11 +229,12 @@ delete_from_cleanup:
void sqliteGenerateRowDelete(
Vdbe *v, /* Generate code into this VDBE */
Table *pTab, /* Table containing the row to be deleted */
int base /* Cursor number for the table */
int base, /* Cursor number for the table */
int count /* Increment the row change counter */
){
sqliteVdbeAddOp(v, OP_MoveTo, base, 0);
sqliteGenerateRowIndexDelete(v, pTab, base, 0);
sqliteVdbeAddOp(v, OP_Delete, base, 0);
sqliteVdbeAddOp(v, OP_Delete, base, count);
}
/*
+3 -20
View File
@@ -12,7 +12,7 @@
** This file contains routines used for analyzing expressions and
** for generating VDBE code that evaluates expressions in SQLite.
**
** $Id: expr.c,v 1.57 2002/03/24 13:13:29 drh Exp $
** $Id: expr.c,v 1.58 2002/04/20 14:24:42 drh Exp $
*/
#include "sqliteInt.h"
@@ -705,8 +705,6 @@ void sqliteExprCode(Parse *pParse, Expr *pExpr){
case TK_GE: op = OP_Ge; break;
case TK_NE: op = OP_Ne; break;
case TK_EQ: op = OP_Eq; break;
case TK_LIKE: op = OP_Like; break;
case TK_GLOB: op = OP_Glob; break;
case TK_ISNULL: op = OP_IsNull; break;
case TK_NOTNULL: op = OP_NotNull; break;
case TK_NOT: op = OP_Not; break;
@@ -784,9 +782,7 @@ void sqliteExprCode(Parse *pParse, Expr *pExpr){
case TK_GT:
case TK_GE:
case TK_NE:
case TK_EQ:
case TK_LIKE:
case TK_GLOB: {
case TK_EQ: {
int dest;
sqliteVdbeAddOp(v, OP_Integer, 1, 0);
sqliteExprCode(pParse, pExpr->pLeft);
@@ -938,8 +934,6 @@ void sqliteExprIfTrue(Parse *pParse, Expr *pExpr, int dest){
case TK_GE: op = OP_Ge; break;
case TK_NE: op = OP_Ne; break;
case TK_EQ: op = OP_Eq; break;
case TK_LIKE: op = OP_Like; break;
case TK_GLOB: op = OP_Glob; break;
case TK_ISNULL: op = OP_IsNull; break;
case TK_NOTNULL: op = OP_NotNull; break;
default: break;
@@ -966,9 +960,7 @@ void sqliteExprIfTrue(Parse *pParse, Expr *pExpr, int dest){
case TK_GT:
case TK_GE:
case TK_NE:
case TK_EQ:
case TK_LIKE:
case TK_GLOB: {
case TK_EQ: {
sqliteExprCode(pParse, pExpr->pLeft);
sqliteExprCode(pParse, pExpr->pRight);
sqliteVdbeAddOp(v, op, 0, dest);
@@ -1026,8 +1018,6 @@ void sqliteExprIfFalse(Parse *pParse, Expr *pExpr, int dest){
case TK_GE: op = OP_Lt; break;
case TK_NE: op = OP_Eq; break;
case TK_EQ: op = OP_Ne; break;
case TK_LIKE: op = OP_Like; break;
case TK_GLOB: op = OP_Glob; break;
case TK_ISNULL: op = OP_NotNull; break;
case TK_NOTNULL: op = OP_IsNull; break;
default: break;
@@ -1060,13 +1050,6 @@ void sqliteExprIfFalse(Parse *pParse, Expr *pExpr, int dest){
sqliteVdbeAddOp(v, op, 0, dest);
break;
}
case TK_LIKE:
case TK_GLOB: {
sqliteExprCode(pParse, pExpr->pLeft);
sqliteExprCode(pParse, pExpr->pRight);
sqliteVdbeAddOp(v, op, 1, dest);
break;
}
case TK_ISNULL:
case TK_NOTNULL: {
sqliteExprCode(pParse, pExpr->pLeft);
+42 -1
View File
@@ -16,7 +16,7 @@
** sqliteRegisterBuildinFunctions() found at the bottom of the file.
** All other code has file scope.
**
** $Id: func.c,v 1.14 2002/03/04 02:26:16 drh Exp $
** $Id: func.c,v 1.16 2002/04/20 14:24:42 drh Exp $
*/
#include <ctype.h>
#include <math.h>
@@ -196,6 +196,43 @@ static void randomFunc(sqlite_func *context, int argc, const char **argv){
sqlite_set_result_int(context, sqliteRandomInteger());
}
/*
** Implementation of the last_insert_rowid() SQL function. The return
** value is the same as the sqlite_last_insert_rowid() API function.
*/
static void last_insert_rowid(sqlite_func *context, int arg, const char **argv){
sqlite *db = sqlite_user_data(context);
sqlite_set_result_int(context, sqlite_last_insert_rowid(db));
}
/*
** Implementation of the like() SQL function. This function implements
** the build-in LIKE operator. The first argument to the function is the
** string and the second argument is the pattern. So, the SQL statements:
**
** A LIKE B
**
** is implemented as like(A,B).
*/
static void likeFunc(sqlite_func *context, int arg, const char **argv){
sqlite_set_result_int(context,
sqliteLikeCompare(argv[0] ? argv[0] : "",argv[1] ? argv[1] : ""));
}
/*
** Implementation of the glob() SQL function. This function implements
** the build-in GLOB operator. The first argument to the function is the
** string and the second argument is the pattern. So, the SQL statements:
**
** A GLOB B
**
** is implemented as glob(A,B).
*/
static void globFunc(sqlite_func *context, int arg, const char **argv){
sqlite_set_result_int(context,
sqliteGlobCompare(argv[0] ? argv[0] : "",argv[1] ? argv[1] : ""));
}
/*
** An instance of the following structure holds the context of a
** sum() or avg() aggregate computation.
@@ -385,6 +422,8 @@ void sqliteRegisterBuildinFunctions(sqlite *db){
{ "coalesce", 0, 0 },
{ "coalesce", 1, 0 },
{ "random", -1, randomFunc },
{ "like", 2, likeFunc },
{ "glob", 2, globFunc },
};
static struct {
char *zName;
@@ -408,6 +447,8 @@ void sqliteRegisterBuildinFunctions(sqlite *db){
sqlite_create_function(db, aFuncs[i].zName,
aFuncs[i].nArg, aFuncs[i].xFunc, 0);
}
sqlite_create_function(db, "last_insert_rowid", 0,
last_insert_rowid, db);
for(i=0; i<sizeof(aAggs)/sizeof(aAggs[0]); i++){
sqlite_create_aggregate(db, aAggs[i].zName,
aAggs[i].nArg, aAggs[i].xStep, aAggs[i].xFinalize, 0);
+15 -13
View File
@@ -12,7 +12,7 @@
** This file contains C code routines that are called by the parser
** to handle INSERT statements in SQLite.
**
** $Id: insert.c,v 1.48 2002/03/31 18:29:03 drh Exp $
** $Id: insert.c,v 1.52 2002/04/12 10:08:59 drh Exp $
*/
#include "sqliteInt.h"
@@ -100,6 +100,9 @@ void sqliteInsert(
if( sqliteExprResolveIds(pParse, 0, &dummy, 0, pList->a[i].pExpr) ){
goto insert_cleanup;
}
if( sqliteExprCheck(pParse, pList->a[i].pExpr, 0, 0) ){
goto insert_cleanup;
}
}
}
@@ -183,15 +186,18 @@ void sqliteInsert(
}
pParse->nTab += idx;
/* Initialize the count of rows to be inserted
*/
if( db->flags & SQLITE_CountRows ){
sqliteVdbeAddOp(v, OP_Integer, 0, 0); /* Initialize the row count */
}
/* If the data source is a SELECT statement, then we have to create
** a loop because there might be multiple rows of data. If the data
** source is an expression list, then exactly one row will be inserted
** and the loop is not used.
*/
if( srcTab>=0 ){
if( db->flags & SQLITE_CountRows ){
sqliteVdbeAddOp(v, OP_Integer, 0, 0); /* Initialize the row count */
}
iBreak = sqliteVdbeMakeLabel(v);
sqliteVdbeAddOp(v, OP_Rewind, srcTab, iBreak);
iCont = sqliteVdbeCurrentAddr(v);
@@ -258,10 +264,9 @@ void sqliteInsert(
sqliteGenerateConstraintChecks(pParse, pTab, base, 0,0,0, onError, endOfLoop);
sqliteCompleteInsertion(pParse, pTab, base, 0,0,0);
/* If inserting from a SELECT, keep a count of the number of
** rows inserted.
/* Update the count of rows that are inserted
*/
if( srcTab>=0 && (db->flags & SQLITE_CountRows)!=0 ){
if( (db->flags & SQLITE_CountRows)!=0 ){
sqliteVdbeAddOp(v, OP_AddImm, 1, 0);
}
@@ -286,9 +291,6 @@ void sqliteInsert(
sqliteVdbeAddOp(v, OP_ColumnCount, 1, 0);
sqliteVdbeAddOp(v, OP_ColumnName, 0, 0);
sqliteVdbeChangeP3(v, -1, "rows inserted", P3_STATIC);
if( srcTab<0 ){
sqliteVdbeAddOp(v, OP_Integer, 1, 0);
}
sqliteVdbeAddOp(v, OP_Callback, 1, 0);
}
@@ -519,12 +521,12 @@ void sqliteGenerateConstraintChecks(
}
case OE_Ignore: {
assert( seenReplace==0 );
sqliteVdbeAddOp(v, OP_Pop, nCol+extra+2+hasTwoRecnos, 0);
sqliteVdbeAddOp(v, OP_Pop, nCol+extra+3+hasTwoRecnos, 0);
sqliteVdbeAddOp(v, OP_Goto, 0, ignoreDest);
break;
}
case OE_Replace: {
sqliteGenerateRowDelete(v, pTab, base);
sqliteGenerateRowDelete(v, pTab, base, 0);
if( isUpdate ){
sqliteVdbeAddOp(v, OP_Dup, nCol+extra+1+hasTwoRecnos, 1);
sqliteVdbeAddOp(v, OP_MoveTo, base, 0);
@@ -571,7 +573,7 @@ void sqliteCompleteInsertion(
sqliteVdbeAddOp(v, OP_IdxPut, base+i+1, 0);
}
sqliteVdbeAddOp(v, OP_MakeRecord, pTab->nCol, 0);
sqliteVdbeAddOp(v, OP_PutIntKey, base, 0);
sqliteVdbeAddOp(v, OP_PutIntKey, base, 1);
if( isUpdate && recnoChng ){
sqliteVdbeAddOp(v, OP_Pop, 1, 0);
}
+11 -1
View File
@@ -14,7 +14,7 @@
** other files are for internal use by SQLite and should not be
** accessed by users of the library.
**
** $Id: main.c,v 1.68 2002/03/06 22:01:36 drh Exp $
** $Id: main.c,v 1.69 2002/04/12 10:08:59 drh Exp $
*/
#include "sqliteInt.h"
#include "os.h"
@@ -421,6 +421,13 @@ int sqlite_last_insert_rowid(sqlite *db){
return db->lastRowid;
}
/*
** Return the number of changes in the most recent call to sqlite_exec().
*/
int sqlite_changes(sqlite *db){
return db->nChange;
}
/*
** Close an existing SQLite database
*/
@@ -526,6 +533,8 @@ int sqlite_exec(
return rc;
}
}
if( db->recursionDepth==0 ){ db->nChange = 0; }
db->recursionDepth++;
memset(&sParse, 0, sizeof(sParse));
sParse.db = db;
sParse.pBe = db->pBe;
@@ -544,6 +553,7 @@ int sqlite_exec(
if( sParse.rc==SQLITE_SCHEMA ){
clearHashTable(db, 1);
}
db->recursionDepth--;
return sParse.rc;
}
+15 -3
View File
@@ -18,7 +18,7 @@
** file simultaneously, or one process from reading the database while
** another is writing.
**
** @(#) $Id: pager.c,v 1.44 2002/03/06 22:01:36 drh Exp $
** @(#) $Id: pager.c,v 1.45 2002/04/18 01:56:58 drh Exp $
*/
#include "sqliteInt.h"
#include "pager.h"
@@ -120,6 +120,7 @@ struct Pager {
u8 tempFile; /* zFilename is a temporary file */
u8 readOnly; /* True for a read-only database */
u8 needSync; /* True if an fsync() is needed on the journal */
u8 dirtyFile; /* True if database file has changed in any way */
u8 *aInJournal; /* One bit for each page in the database file */
u8 *aInCkpt; /* One bit for each page in the database */
PgHdr *pFirst, *pLast; /* List of free pages */
@@ -444,7 +445,7 @@ end_ckpt_playback:
*/
void sqlitepager_set_cachesize(Pager *pPager, int mxPage){
if( mxPage>=0 ){
pPager->noSync = 0;
pPager->noSync = pPager->tempFile;
}else{
pPager->noSync = 1;
mxPage = -mxPage;
@@ -538,6 +539,7 @@ int sqlitepager_open(
pPager->tempFile = tempFile;
pPager->readOnly = readOnly;
pPager->needSync = 0;
pPager->noSync = pPager->tempFile;
pPager->pFirst = 0;
pPager->pLast = 0;
pPager->nExtra = nExtra;
@@ -1036,7 +1038,8 @@ int sqlitepager_begin(void *pData){
return SQLITE_CANTOPEN;
}
pPager->journalOpen = 1;
pPager->needSync = !pPager->noSync;
pPager->needSync = 0;
pPager->dirtyFile = 0;
pPager->state = SQLITE_WRITELOCK;
sqlitepager_pagecount(pPager);
pPager->origDbSize = pPager->dbSize;
@@ -1088,6 +1091,7 @@ int sqlitepager_write(void *pData){
*/
pPg->dirty = 1;
if( pPg->inJournal && (pPg->inCkpt || pPager->ckptOpen==0) ){
pPager->dirtyFile = 1;
return SQLITE_OK;
}
@@ -1100,6 +1104,7 @@ int sqlitepager_write(void *pData){
*/
assert( pPager->state!=SQLITE_UNLOCK );
rc = sqlitepager_begin(pData);
pPager->dirtyFile = 1;
if( rc!=SQLITE_OK ) return rc;
assert( pPager->state==SQLITE_WRITELOCK );
assert( pPager->journalOpen );
@@ -1238,6 +1243,13 @@ int sqlitepager_commit(Pager *pPager){
return SQLITE_ERROR;
}
assert( pPager->journalOpen );
if( pPager->dirtyFile==0 ){
/* Exit early (without doing the time-consuming sqliteOsSync() calls)
** if there have been no changes to the database file. */
rc = pager_unwritelock(pPager);
pPager->dbSize = -1;
return rc;
}
if( pPager->needSync && sqliteOsSync(&pPager->jfd)!=SQLITE_OK ){
goto commit_abort;
}
+19 -10
View File
@@ -14,7 +14,7 @@
** the parser. Lemon will also generate a header file containing
** numeric codes for all of the tokens.
**
** @(#) $Id: parse.y,v 1.59 2002/03/30 15:26:51 drh Exp $
** @(#) $Id: parse.y,v 1.62 2002/04/20 14:24:42 drh Exp $
*/
%token_prefix TK_
%token_type {Token}
@@ -255,6 +255,11 @@ selcollist(A) ::= sclp(P) expr(X) as ids(Y). {A = sqliteExprListAppend(P,X,&Y);}
selcollist(A) ::= sclp(P) STAR. {
A = sqliteExprListAppend(P, sqliteExpr(TK_ALL, 0, 0, 0), 0);
}
selcollist(A) ::= sclp(P) ids(X) DOT STAR. {
Expr *pRight = sqliteExpr(TK_ALL, 0, 0, 0);
Expr *pLeft = sqliteExpr(TK_ID, 0, 0, &X);
A = sqliteExprListAppend(P, sqliteExpr(TK_DOT, pLeft, pRight, 0), 0);
}
as ::= .
as ::= AS.
@@ -266,6 +271,7 @@ as ::= AS.
%type from {IdList*}
%destructor from {sqliteIdListDelete($$);}
from(A) ::= . {A = sqliteMalloc(sizeof(*A));}
from(A) ::= FROM seltablist(X). {A = X;}
stl_prefix(A) ::= seltablist(X) COMMA. {A = X;}
stl_prefix(A) ::= . {A = 0;}
@@ -425,18 +431,21 @@ expr(A) ::= expr(X) BITAND expr(Y). {A = sqliteExpr(TK_BITAND, X, Y, 0);}
expr(A) ::= expr(X) BITOR expr(Y). {A = sqliteExpr(TK_BITOR, X, Y, 0);}
expr(A) ::= expr(X) LSHIFT expr(Y). {A = sqliteExpr(TK_LSHIFT, X, Y, 0);}
expr(A) ::= expr(X) RSHIFT expr(Y). {A = sqliteExpr(TK_RSHIFT, X, Y, 0);}
expr(A) ::= expr(X) LIKE expr(Y). {A = sqliteExpr(TK_LIKE, X, Y, 0);}
expr(A) ::= expr(X) NOT LIKE expr(Y). {
A = sqliteExpr(TK_LIKE, X, Y, 0);
A = sqliteExpr(TK_NOT, A, 0, 0);
sqliteExprSpan(A,&X->span,&Y->span);
}
expr(A) ::= expr(X) GLOB expr(Y). {A = sqliteExpr(TK_GLOB,X,Y,0);}
expr(A) ::= expr(X) NOT GLOB expr(Y). {
A = sqliteExpr(TK_GLOB, X, Y, 0);
expr(A) ::= expr(X) likeop(OP) expr(Y). [LIKE] {
ExprList *pList = sqliteExprListAppend(0, Y, 0);
pList = sqliteExprListAppend(pList, X, 0);
A = sqliteExprFunction(pList, &OP);
sqliteExprSpan(A, &X->span, &Y->span);
}
expr(A) ::= expr(X) NOT likeop(OP) expr(Y). [LIKE] {
ExprList *pList = sqliteExprListAppend(0, Y, 0);
pList = sqliteExprListAppend(pList, X, 0);
A = sqliteExprFunction(pList, &OP);
A = sqliteExpr(TK_NOT, A, 0, 0);
sqliteExprSpan(A,&X->span,&Y->span);
}
likeop(A) ::= LIKE(X). {A = X;}
likeop(A) ::= GLOB(X). {A = X;}
expr(A) ::= expr(X) PLUS expr(Y). {A = sqliteExpr(TK_PLUS, X, Y, 0);}
expr(A) ::= expr(X) MINUS expr(Y). {A = sqliteExpr(TK_MINUS, X, Y, 0);}
expr(A) ::= expr(X) STAR expr(Y). {A = sqliteExpr(TK_STAR, X, Y, 0);}
+54 -18
View File
@@ -12,7 +12,7 @@
** This file contains C code routines that are called by the parser
** to handle SELECT statements in SQLite.
**
** $Id: select.c,v 1.77 2002/03/23 00:31:29 drh Exp $
** $Id: select.c,v 1.78 2002/04/04 02:10:57 drh Exp $
*/
#include "sqliteInt.h"
@@ -355,16 +355,16 @@ Table *sqliteResultSetOfSelect(Parse *pParse, char *zTabName, Select *pSelect){
** (1) Fill in the pTabList->a[].pTab fields in the IdList that
** defines the set of tables that should be scanned.
**
** (2) If the columns to be extracted variable (pEList) is NULL
** (meaning that a "*" was used in the SQL statement) then
** create a fake pEList containing the names of all columns
** of all tables.
** (2) Scan the list of columns in the result set (pEList) looking
** for instances of the "*" operator or the TABLE.* operator.
** If found, expand each "*" to be every column in every table
** and TABLE.* to be every column in TABLE.
**
** Return 0 on success. If there are problems, leave an error message
** in pParse and return non-zero.
*/
static int fillInColumnList(Parse *pParse, Select *p){
int i, j, k;
int i, j, k, rc;
IdList *pTabList;
ExprList *pEList;
Table *pTab;
@@ -410,42 +410,72 @@ static int fillInColumnList(Parse *pParse, Select *p){
}
/* For every "*" that occurs in the column list, insert the names of
** all columns in all tables. The parser inserted a special expression
** all columns in all tables. And for every TABLE.* insert the names
** of all columns in TABLE. The parser inserted a special expression
** with the TK_ALL operator for each "*" that it found in the column list.
** The following code just has to locate the TK_ALL expressions and expand
** each one to the list of all columns in all tables.
**
** The first loop just checks to see if there are any "*" operators
** that need expanding.
*/
for(k=0; k<pEList->nExpr; k++){
if( pEList->a[k].pExpr->op==TK_ALL ) break;
Expr *pE = pEList->a[k].pExpr;
if( pE->op==TK_ALL ) break;
if( pE->op==TK_DOT && pE->pRight && pE->pRight->op==TK_ALL
&& pE->pLeft && pE->pLeft->op==TK_ID ) break;
}
rc = 0;
if( k<pEList->nExpr ){
/*
** If we get here it means the result set contains one or more "*"
** operators that need to be expanded. Loop through each expression
** in the result set and expand them one by one.
*/
struct ExprList_item *a = pEList->a;
ExprList *pNew = 0;
for(k=0; k<pEList->nExpr; k++){
if( a[k].pExpr->op!=TK_ALL ){
Expr *pE = a[k].pExpr;
if( pE->op!=TK_ALL &&
(pE->op!=TK_DOT || pE->pRight==0 || pE->pRight->op!=TK_ALL) ){
/* This particular expression does not need to be expanded.
*/
pNew = sqliteExprListAppend(pNew, a[k].pExpr, 0);
pNew->a[pNew->nExpr-1].zName = a[k].zName;
a[k].pExpr = 0;
a[k].zName = 0;
}else{
/* This expression is a "*" or a "TABLE.*" and needs to be
** expanded. */
int tableSeen = 0; /* Set to 1 when TABLE matches */
Token *pName; /* text of name of TABLE */
if( pE->op==TK_DOT && pE->pLeft ){
pName = &pE->pLeft->token;
}else{
pName = 0;
}
for(i=0; i<pTabList->nId; i++){
Table *pTab = pTabList->a[i].pTab;
char *zTabName = pTabList->a[i].zAlias;
if( zTabName==0 || zTabName[0]==0 ){
zTabName = pTab->zName;
}
if( pName && (zTabName==0 || zTabName[0]==0 ||
sqliteStrNICmp(pName->z, zTabName, pName->n)!=0) ){
continue;
}
tableSeen = 1;
for(j=0; j<pTab->nCol; j++){
Expr *pExpr, *pLeft, *pRight;
pRight = sqliteExpr(TK_ID, 0, 0, 0);
if( pRight==0 ) break;
pRight->token.z = pTab->aCol[j].zName;
pRight->token.n = strlen(pTab->aCol[j].zName);
if( pTab->zName ){
if( zTabName ){
pLeft = sqliteExpr(TK_ID, 0, 0, 0);
if( pLeft==0 ) break;
if( pTabList->a[i].zAlias && pTabList->a[i].zAlias[0] ){
pLeft->token.z = pTabList->a[i].zAlias;
pLeft->token.n = strlen(pTabList->a[i].zAlias);
}else{
pLeft->token.z = pTab->zName;
pLeft->token.n = strlen(pTab->zName);
}
pLeft->token.z = zTabName;
pLeft->token.n = strlen(zTabName);
pExpr = sqliteExpr(TK_DOT, pLeft, pRight, 0);
if( pExpr==0 ) break;
}else{
@@ -455,12 +485,18 @@ static int fillInColumnList(Parse *pParse, Select *p){
pNew = sqliteExprListAppend(pNew, pExpr, 0);
}
}
if( !tableSeen ){
assert( pName!=0 );
sqliteSetNString(&pParse->zErrMsg, "no such table: ", -1,
pName->z, pName->n, 0);
rc = 1;
}
}
}
sqliteExprListDelete(pEList);
p->pEList = pNew;
}
return 0;
return rc;
}
/*
+319 -49
View File
@@ -12,15 +12,19 @@
** This file contains code to implement the "sqlite" command line
** utility for accessing SQLite databases.
**
** $Id: shell.c,v 1.47 2002/03/03 23:06:01 drh Exp $
** $Id: shell.c,v 1.56 2002/04/21 19:06:22 drh Exp $
*/
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "sqlite.h"
#include <ctype.h>
#if !defined(_WIN32) && !defined(WIN32)
# include <signal.h>
# include <pwd.h>
# include <unistd.h>
# include <sys/types.h>
#endif
#if defined(HAVE_READLINE) && HAVE_READLINE==1
@@ -28,7 +32,10 @@
# include <readline/history.h>
#else
# define readline(p) getline(p,stdin)
# define add_history(X)
# define add_history(X)
# define read_history(X)
# define write_history(X)
# define stifle_history(X)
#endif
/*
@@ -38,6 +45,24 @@
*/
static sqlite *db = 0;
/*
** True if an interrupt (Control-C) has been received.
*/
static int seenInterrupt = 0;
/*
** This is the name of our program. It is set in main(), used
** in a number of other places, mostly for error messages.
*/
static char *Argv0;
/*
** Prompt strings. Initialized in main. Settable with
** .prompt main continue
*/
static char mainPrompt[20]; /* First line prompt. default: "sqlite> "*/
static char continuePrompt[20]; /* Continuation prompt. default: " ...> " */
/*
** This routine reads a line of text from standard input, stores
** the text in memory obtained from malloc() and returns a pointer
@@ -104,15 +129,21 @@ static char *one_input_line(const char *zPrior, FILE *in){
return getline(0, in);
}
if( zPrior && zPrior[0] ){
zPrompt = " ...> ";
zPrompt = continuePrompt;
}else{
zPrompt = "sqlite> ";
zPrompt = mainPrompt;
}
zResult = readline(zPrompt);
if( zResult ) add_history(zResult);
return zResult;
}
struct previous_mode_data {
int valid; /* Is there legit data in here? */
int mode;
int showHeader;
int colWidth[100];
};
/*
** An pointer to an instance of this structure is passed from
** the main program to the callback. This is used to communicate
@@ -129,6 +160,11 @@ struct callback_data {
char separator[20]; /* Separator character for MODE_List */
int colWidth[100]; /* Requested width of each column when in column mode*/
int actualWidth[100]; /* Actual width of each column */
char nullvalue[20]; /* The text to print when a NULL comes back from the database */
struct previous_mode_data explainPrev;
/* Holds the mode information just before .explain ON */
char outfile[FILENAME_MAX];
/* Filename for *out */
};
/*
@@ -140,6 +176,16 @@ struct callback_data {
#define MODE_Semi 3 /* Same as MODE_List but append ";" to each line */
#define MODE_Html 4 /* Generate an XHTML table */
#define MODE_Insert 5 /* Generate SQL "insert" statements */
#define MODE_NUM_OF 6
char *modeDescr[MODE_NUM_OF] = {
"line",
"column",
"list",
"semi",
"html",
"insert"
};
/*
** Number of elements in an array
@@ -154,7 +200,7 @@ static int is_numeric(const char *z){
if( *z=='-' || *z=='+' ){
z++;
}
while( isdigit(*z) ){
while( isdigit(*z) ){
seen_digit = 1;
z++;
}
@@ -231,6 +277,7 @@ 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){
seenInterrupt = 1;
if( db ) sqlite_interrupt(db);
}
@@ -251,7 +298,7 @@ static int callback(void *pArg, int nArg, char **azArg, char **azCol){
}
if( p->cnt++>0 ) fprintf(p->out,"\n");
for(i=0; i<nArg; i++){
fprintf(p->out,"%*s = %s\n", w, azCol[i], azArg[i] ? azArg[i] : 0);
fprintf(p->out,"%*s = %s\n", w, azCol[i], azArg[i] ? azArg[i] : p->nullvalue);
}
break;
}
@@ -267,11 +314,11 @@ static int callback(void *pArg, int nArg, char **azArg, char **azCol){
if( w<=0 ){
w = strlen(azCol[i] ? azCol[i] : "");
if( w<10 ) w = 10;
n = strlen(azArg && azArg[i] ? azArg[i] : "");
n = strlen(azArg && azArg[i] ? azArg[i] : p->nullvalue);
if( w<n ) w = n;
}
if( i<ArraySize(p->actualWidth) ){
p->actualWidth[i] = w;
p->actualWidth[i] = w;
}
if( p->showHeader ){
fprintf(p->out,"%-*.*s%s",w,w,azCol[i], i==nArg-1 ? "\n": " ");
@@ -300,7 +347,7 @@ static int callback(void *pArg, int nArg, char **azArg, char **azCol){
w = 10;
}
fprintf(p->out,"%-*.*s%s",w,w,
azArg[i] ? azArg[i] : "", i==nArg-1 ? "\n": " ");
azArg[i] ? azArg[i] : p->nullvalue, i==nArg-1 ? "\n": " ");
}
break;
}
@@ -314,7 +361,7 @@ static int callback(void *pArg, int nArg, char **azArg, char **azCol){
if( azArg==0 ) break;
for(i=0; i<nArg; i++){
char *z = azArg[i];
if( z==0 ) z = "";
if( z==0 ) z = p->nullvalue;
fprintf(p->out, "%s", z);
if( i<nArg-1 ){
fprintf(p->out, "%s", p->separator);
@@ -338,7 +385,7 @@ static int callback(void *pArg, int nArg, char **azArg, char **azCol){
fprintf(p->out,"<TR>");
for(i=0; i<nArg; i++){
fprintf(p->out,"<TD>");
output_html_string(p->out, azArg[i] ? azArg[i] : "");
output_html_string(p->out, azArg[i] ? azArg[i] : p->nullvalue);
fprintf(p->out,"</TD>\n");
}
fprintf(p->out,"</TD></TR>\n");
@@ -361,7 +408,7 @@ static int callback(void *pArg, int nArg, char **azArg, char **azCol){
fprintf(p->out,");\n");
break;
}
}
}
return 0;
}
@@ -419,7 +466,7 @@ static int dump_callback(void *pArg, int nArg, char **azArg, char **azCol){
d2.mode = MODE_Insert;
d2.zDestTable = 0;
set_table_name(&d2, azArg[0]);
sqlite_exec_printf(p->db,
sqlite_exec_printf(p->db,
"SELECT * FROM '%q'",
callback, &d2, 0, azArg[0]
);
@@ -431,24 +478,28 @@ static int dump_callback(void *pArg, int nArg, char **azArg, char **azCol){
/*
** Text of a help message
*/
static char zHelp[] =
static char zHelp[] =
".dump ?TABLE? ... Dump the database in an text format\n"
".echo ON|OFF Turn command echo on or off\n"
".exit Exit this program\n"
".explain Set output mode suitable for EXPLAIN\n"
".header ON|OFF Turn display of headers on or off\n"
".explain ON|OFF Turn output mode suitable for EXPLAIN on or off.\n"
".header(s) ON|OFF Turn display of headers on or off\n"
".help Show this message\n"
".indices TABLE Show names of all indices on TABLE\n"
".mode MODE Set mode to one of \"line\", \"column\", \n"
".mode MODE Set mode to one of \"line(s)\", \"column(s)\", \n"
" \"insert\", \"list\", or \"html\"\n"
".mode insert TABLE Generate SQL insert statements for TABLE\n"
".nullvalue STRING Print STRING instead of nothing for NULL data\n"
".output FILENAME Send output to FILENAME\n"
".output stdout Send output to the screen\n"
".prompt MAIN CONTINUE Replace the standard prompts\n"
".quit Exit this program\n"
".read FILENAME Execute SQL in FILENAME\n"
".reindex ?TABLE? Rebuild indices\n"
/* ".rename OLD NEW Change the name of a table or index\n" */
".schema ?TABLE? Show the CREATE statements\n"
".separator STRING Change separator string for \"list\" mode\n"
".show Show the current values for various settings\n"
".tables ?PATTERN? List names of tables matching a pattern\n"
".timeout MS Try opening locked tables for MS milliseconds\n"
".width NUM NUM ... Set column widths for \"column\" mode\n"
@@ -460,11 +511,14 @@ static void process_input(struct callback_data *p, FILE *in);
/*
** If an input line begins with "." then invoke this routine to
** process that line.
**
** Return 1 to exit and 0 to continue.
*/
static void do_meta_command(char *zLine, sqlite *db, struct callback_data *p){
static int do_meta_command(char *zLine, sqlite *db, struct callback_data *p){
int i = 1;
int nArg = 0;
int n, c;
int rc = 0;
char *azArg[50];
/* Parse the input line into tokens.
@@ -487,7 +541,7 @@ static void do_meta_command(char *zLine, sqlite *db, struct callback_data *p){
/* Process the input line.
*/
if( nArg==0 ) return;
if( nArg==0 ) return rc;
n = strlen(azArg[0]);
c = azArg[0][0];
if( c=='d' && strncmp(azArg[0], "dump", n)==0 ){
@@ -497,19 +551,18 @@ static void do_meta_command(char *zLine, sqlite *db, struct callback_data *p){
sqlite_exec(db,
"SELECT name, type, sql FROM sqlite_master "
"WHERE type!='meta' AND sql NOT NULL "
"ORDER BY tbl_name, type DESC, name",
"ORDER BY substr(type,2,1), name",
dump_callback, p, &zErrMsg
);
}else{
int i;
for(i=1; i<nArg && zErrMsg==0; i++){
sqlite_exec_printf(db,
sqlite_exec_printf(db,
"SELECT name, type, sql FROM sqlite_master "
"WHERE tbl_name LIKE '%q' AND type!='meta' AND sql NOT NULL "
"ORDER BY type DESC, name",
"ORDER BY substr(type,2,1), name",
dump_callback, p, &zErrMsg, azArg[i]
);
}
}
if( zErrMsg ){
@@ -531,26 +584,59 @@ static void do_meta_command(char *zLine, sqlite *db, struct callback_data *p){
val = 1;
}else if( strcmp(z,"yes")==0 ){
val = 1;
}
}
p->echoOn = val;
}else
if( c=='e' && strncmp(azArg[0], "exit", n)==0 ){
sqlite_close(db);
exit(0);
rc = 1;
}else
if( c=='e' && strncmp(azArg[0], "explain", n)==0 ){
p->mode = MODE_Column;
p->showHeader = 1;
p->colWidth[0] = 4;
p->colWidth[1] = 12;
p->colWidth[2] = 10;
p->colWidth[3] = 10;
p->colWidth[4] = 35;
int j;
char *z = nArg>=2 ? azArg[1] : "1";
int val = atoi(z);
for(j=0; z[j]; j++){
if( isupper(z[j]) ) z[j] = tolower(z[j]);
}
if( strcmp(z,"on")==0 ){
val = 1;
}else if( strcmp(z,"yes")==0 ){
val = 1;
}
if(val == 1) {
if(!p->explainPrev.valid) {
p->explainPrev.valid = 1;
p->explainPrev.mode = p->mode;
p->explainPrev.showHeader = p->showHeader;
memcpy(p->explainPrev.colWidth,p->colWidth,sizeof(p->colWidth));
}
/* We could put this code under the !p->explainValid
** condition so that it does not execute if we are already in
** explain mode. However, always executing it allows us an easy
** was to reset to explain mode in case the user previously
** did an .explain followed by a .width, .mode or .header
** command.
*/
p->mode = MODE_Column;
p->showHeader = 1;
memset(p->colWidth,0,ArraySize(p->colWidth));
p->colWidth[0] = 4;
p->colWidth[1] = 12;
p->colWidth[2] = 10;
p->colWidth[3] = 10;
p->colWidth[4] = 35;
}else if (p->explainPrev.valid) {
p->explainPrev.valid = 0;
p->mode = p->explainPrev.mode;
p->showHeader = p->explainPrev.showHeader;
memcpy(p->colWidth,p->explainPrev.colWidth,sizeof(p->colWidth));
}
}else
if( c=='h' && strncmp(azArg[0], "header", n)==0 && nArg>1 ){
if( c=='h' && (strncmp(azArg[0], "header", n)==0
||
strncmp(azArg[0], "headers", n)==0 )&& nArg>1 ){
int j;
char *z = azArg[1];
int val = atoi(azArg[1]);
@@ -561,7 +647,7 @@ static void do_meta_command(char *zLine, sqlite *db, struct callback_data *p){
val = 1;
}else if( strcmp(z,"yes")==0 ){
val = 1;
}
}
p->showHeader = val;
}else
@@ -575,7 +661,7 @@ static void do_meta_command(char *zLine, sqlite *db, struct callback_data *p){
memcpy(&data, p, sizeof(data));
data.showHeader = 0;
data.mode = MODE_List;
sqlite_exec_printf(db,
sqlite_exec_printf(db,
"SELECT name FROM sqlite_master "
"WHERE type='index' AND tbl_name LIKE '%q' "
"ORDER BY name",
@@ -589,9 +675,13 @@ static void do_meta_command(char *zLine, sqlite *db, struct callback_data *p){
if( c=='m' && strncmp(azArg[0], "mode", n)==0 && nArg>=2 ){
int n2 = strlen(azArg[1]);
if( strncmp(azArg[1],"line",n2)==0 ){
if( strncmp(azArg[1],"line",n2)==0
||
strncmp(azArg[1],"lines",n2)==0 ){
p->mode = MODE_Line;
}else if( strncmp(azArg[1],"column",n2)==0 ){
}else if( strncmp(azArg[1],"column",n2)==0
||
strncmp(azArg[1],"columns",n2)==0 ){
p->mode = MODE_Column;
}else if( strncmp(azArg[1],"list",n2)==0 ){
p->mode = MODE_List;
@@ -609,21 +699,42 @@ static void do_meta_command(char *zLine, sqlite *db, struct callback_data *p){
}
}else
if( c=='n' && strncmp(azArg[0], "nullvalue", n)==0 && nArg==2 ) {
sprintf(p->nullvalue, "%.*s", (int)ArraySize(p->nullvalue)-1, azArg[1]);
}else
if( c=='o' && strncmp(azArg[0], "output", n)==0 && nArg==2 ){
if( p->out!=stdout ){
fclose(p->out);
}
if( strcmp(azArg[1],"stdout")==0 ){
p->out = stdout;
strcpy(p->outfile,"stdout");
}else{
p->out = fopen(azArg[1], "w");
if( p->out==0 ){
fprintf(stderr,"can't write to \"%s\"\n", azArg[1]);
p->out = stdout;
} else {
strcpy(p->outfile,azArg[1]);
}
}
}else
if( c=='p' && strncmp(azArg[0], "prompt", n)==0 && (nArg==2 || nArg==3)){
if( nArg >= 2) {
strncpy(mainPrompt,azArg[1],(int)ArraySize(mainPrompt)-1);
}
if( nArg >= 3) {
strncpy(continuePrompt,azArg[2],(int)ArraySize(continuePrompt)-1);
}
}else
if( c=='q' && strncmp(azArg[0], "quit", n)==0 ){
sqlite_close(db);
exit(0);
}else
if( c=='r' && strncmp(azArg[0], "read", n)==0 && nArg==2 ){
FILE *alt = fopen(azArg[1], "r");
if( alt==0 ){
@@ -714,6 +825,23 @@ static void do_meta_command(char *zLine, sqlite *db, struct callback_data *p){
sprintf(p->separator, "%.*s", (int)ArraySize(p->separator)-1, azArg[1]);
}else
if( c=='s' && strncmp(azArg[0], "show", n)==0){
int i;
fprintf(p->out,"%9.9s: %s\n","echo", p->echoOn ? "on" : "off");
fprintf(p->out,"%9.9s: %s\n","explain", p->explainPrev.valid ? "on" :"off");
fprintf(p->out,"%9.9s: %s\n","headers", p->showHeader ? "on" : "off");
fprintf(p->out,"%9.9s: %s\n","mode", modeDescr[p->mode]);
fprintf(p->out,"%9.9s: %s\n","nullvalue", p->nullvalue);
fprintf(p->out,"%9.9s: %s\n","output",
strlen(p->outfile) ? p->outfile : "stdout");
fprintf(p->out,"%9.9s: %s\n","separator", p->separator);
fprintf(p->out,"%9.9s: ","width");
for (i=0;i<(int)ArraySize(p->colWidth) && p->colWidth[i] != 0;i++) {
fprintf(p->out,"%d ",p->colWidth[i]);
}
fprintf(p->out,"\n\n");
}else
if( c=='t' && n>1 && strncmp(azArg[0], "tables", n)==0 ){
char **azResult;
int nRow, rc;
@@ -772,22 +900,35 @@ static void do_meta_command(char *zLine, sqlite *db, struct callback_data *p){
}else
{
fprintf(stderr, "unknown command: \"%s\". Enter \".help\" for help\n",
azArg[0]);
fprintf(stderr, "unknown command or invalid arguments: "
" \"%s\". Enter \".help\" for help\n", azArg[0]);
}
return rc;
}
static char *Argv0;
/*
** Read input from *in and process it. If *in==0 then input
** is interactive - the user is typing it it. Otherwise, input
** is coming from a file or device. A prompt is issued and history
** is saved only if input is interactive. An interrupt signal will
** cause this routine to exit immediately, unless input is interactive.
*/
static void process_input(struct callback_data *p, FILE *in){
char *zLine;
char *zSql = 0;
int nSql = 0;
char *zErrMsg;
while( (zLine = one_input_line(zSql, in))!=0 ){
while( fflush(p->out), (zLine = one_input_line(zSql, in))!=0 ){
if( seenInterrupt ){
if( in!=0 ) break;
seenInterrupt = 0;
}
if( p->echoOn ) printf("%s\n", zLine);
if( zLine && zLine[0]=='.' && nSql==0 ){
do_meta_command(zLine, db, p);
int rc = do_meta_command(zLine, db, p);
free(zLine);
if( rc ) break;
continue;
}
if( zSql==0 ){
@@ -812,7 +953,7 @@ static void process_input(struct callback_data *p, FILE *in){
free(zLine);
if( zSql && sqlite_complete(zSql) ){
p->cnt = 0;
if( sqlite_exec(db, zSql, callback, p, &zErrMsg)!=0
if( sqlite_exec(db, zSql, callback, p, &zErrMsg)!=0
&& zErrMsg!=0 ){
if( in!=0 && !p->echoOn ) printf("%s\n",zSql);
printf("SQL error: %s\n", zErrMsg);
@@ -830,20 +971,130 @@ static void process_input(struct callback_data *p, FILE *in){
}
}
/*
** Return a pathname which is the user's home directory. A
** 0 return indicates an error of some kind. Space to hold the
** resulting string is obtained from malloc(). The calling
** function should free the result.
*/
static char *find_home_dir(void){
char *home_dir = NULL;
#if !defined(_WIN32) && !defined(WIN32)
struct passwd *pwent;
uid_t uid = getuid();
while( (pwent=getpwent()) != NULL) {
if(pwent->pw_uid == uid) {
home_dir = pwent->pw_dir;
break;
}
}
#endif
if (!home_dir) {
home_dir = getenv("HOME");
if (!home_dir) {
home_dir = getenv("HOMEPATH"); /* Windows? */
}
}
#if defined(_WIN32) || defined(WIN32)
if (!home_dir) {
home_dir = "c:";
}
#endif
if( home_dir ){
char *z = malloc( strlen(home_dir)+1 );
if( z ) strcpy(z, home_dir);
home_dir = z;
}
return home_dir;
}
/*
** Read input from the file given by sqliterc_override. Or if that
** parameter is NULL, take input from ~/.sqliterc
*/
static void process_sqliterc(struct callback_data *p, char *sqliterc_override){
char *home_dir = NULL;
char *sqliterc = sqliterc_override;
FILE *in = NULL;
if (sqliterc == NULL) {
home_dir = find_home_dir();
if( home_dir==0 ){
fprintf(stderr,"%s: cannot locate your home directory!\n", Argv0);
return;
}
sqliterc = malloc(strlen(home_dir) + 15);
if( sqliterc==0 ){
fprintf(stderr,"%s: out of memory!\n", Argv0);
exit(1);
}
sprintf(sqliterc,"%s/.sqliterc",home_dir);
free(home_dir);
}
in = fopen(sqliterc,"r");
if(in) {
printf("Loading resources from %s\n",sqliterc);
process_input(p,in);
fclose(in);
}
return;
}
/*
** Initialize the state information in data
*/
void main_init(struct callback_data *data) {
memset(data, 0, sizeof(*data));
data->mode = MODE_List;
strcpy(data->separator,"|");
data->showHeader = 0;
strcpy(mainPrompt,"sqlite> ");
strcpy(continuePrompt," ...> ");
}
int main(int argc, char **argv){
char *zErrMsg = 0;
struct callback_data data;
int origArgc = argc;
char **origArgv = argv;
Argv0 = argv[0];
memset(&data, 0, sizeof(data));
data.mode = MODE_List;
strcpy(data.separator,"|");
data.showHeader = 0;
main_init(&data);
process_sqliterc(&data,NULL);
#ifdef SIGINT
signal(SIGINT, interrupt_handler);
#endif
while( argc>=2 && argv[1][0]=='-' ){
if( strcmp(argv[1],"-html")==0 ){
if( argc>=3 && strcmp(argv[1],"-init")==0 ){
/* If we get a -init to do, we have to pretend that
** it replaced the .sqliterc file. Soooo, in order to
** do that we need to start from scratch...*/
main_init(&data);
/* treat this file as the sqliterc... */
process_sqliterc(&data,argv[2]);
/* fix up the command line so we do not re-read
** the option next time around... */
{
int i = 1;
for(i=1;i<=argc-2;i++) {
argv[i] = argv[i+2];
}
}
origArgc-=2;
/* and reset the command line options to be re-read.*/
argv = origArgv;
argc = origArgc;
}else if( strcmp(argv[1],"-html")==0 ){
data.mode = MODE_Html;
argc--;
argv++;
@@ -855,10 +1106,18 @@ int main(int argc, char **argv){
data.mode = MODE_Line;
argc--;
argv++;
}else if( strcmp(argv[1],"-column")==0 ){
data.mode = MODE_Column;
argc--;
argv++;
}else if( argc>=3 && strcmp(argv[1],"-separator")==0 ){
sprintf(data.separator,"%.*s",(int)sizeof(data.separator)-1,argv[2]);
argc -= 2;
argv += 2;
}else if( argc>=3 && strcmp(argv[1],"-nullvalue")==0 ){
sprintf(data.nullvalue,"%.*s",(int)sizeof(data.nullvalue)-1,argv[2]);
argc -= 2;
argv += 2;
}else if( strcmp(argv[1],"-header")==0 ){
data.showHeader = 1;
argc--;
@@ -910,12 +1169,23 @@ int main(int argc, char **argv){
}else{
extern int isatty();
if( isatty(0) ){
char *zHome;
char *zHistory = 0;
printf(
"SQLite version %s\n"
"Enter \".help\" for instructions\n",
sqlite_version
);
zHome = find_home_dir();
if( zHome && (zHistory = malloc(strlen(zHome)+20))!=0 ){
sprintf(zHistory,"%s/.sqlite_history", zHome);
}
if( zHistory ) read_history(zHistory);
process_input(&data, 0);
if( zHistory ){
stifle_history(100);
write_history(zHistory);
}
}else{
process_input(&data, stdin);
}
+23 -1
View File
@@ -12,7 +12,7 @@
** This header file defines the interface that the SQLite library
** presents to client programs.
**
** @(#) $Id: sqlite.h.in,v 1.29 2002/03/08 02:12:00 drh Exp $
** @(#) $Id: sqlite.h.in,v 1.30 2002/04/12 10:08:59 drh Exp $
*/
#ifndef _SQLITE_H_
#define _SQLITE_H_
@@ -173,6 +173,28 @@ int sqlite_exec(
*/
int sqlite_last_insert_rowid(sqlite*);
/*
** This function returns the number of database rows that were changed
** (or inserted or deleted) by the most recent called sqlite_exec().
**
** All changes are counted, even if they were later undone by a
** ROLLBACK or ABORT. Except, changes associated with creating and
** dropping tables are not counted.
**
** If a callback invokes sqlite_exec() recursively, then the changes
** in the inner, recursive call are counted together with the changes
** in the outer call.
**
** SQLite implements the command "DELETE FROM table" without a WHERE clause
** by dropping and recreating the table. (This is much faster than going
** through and deleting individual elements form the table.) Because of
** this optimization, the change count for "DELETE FROM table" will be
** zero regardless of the number of elements that were originally in the
** table. To get an accurate count of the number of rows deleted, use
** "DELETE FROM table WHERE 1" instead.
*/
int sqlite_changes(sqlite*);
/* If the parameter to this routine is one of the return value constants
** defined above, then this routine returns a constant text string which
** descripts (in English) the meaning of the return value.
+4 -2
View File
@@ -11,7 +11,7 @@
*************************************************************************
** Internal interface definitions for SQLite.
**
** @(#) $Id: sqliteInt.h,v 1.104 2002/03/12 23:10:05 drh Exp $
** @(#) $Id: sqliteInt.h,v 1.105 2002/04/12 10:08:59 drh Exp $
*/
#include "sqlite.h"
#include "hash.h"
@@ -167,6 +167,8 @@ struct sqlite {
int lastRowid; /* ROWID of most recent insert */
int priorNewRowid; /* Last randomly generated ROWID */
int onError; /* Default conflict algorithm */
int nChange; /* Number of rows changed */
int recursionDepth; /* Number of nested calls to sqlite_exec() */
};
/*
@@ -630,7 +632,7 @@ void sqliteCommitTransaction(Parse*);
void sqliteRollbackTransaction(Parse*);
char *sqlite_mprintf(const char *, ...);
int sqliteExprIsConstant(Expr*);
void sqliteGenerateRowDelete(Vdbe*, Table*, int);
void sqliteGenerateRowDelete(Vdbe*, Table*, int, int);
void sqliteGenerateRowIndexDelete(Vdbe*, Table*, int, char*);
void sqliteGenerateConstraintChecks(Parse*,Table*,int,char*,int,int,int,int);
void sqliteCompleteInsertion(Parse*, Table*, int, char*, int, int);
+24 -3
View File
@@ -11,7 +11,7 @@
*************************************************************************
** A TCL Interface to SQLite
**
** $Id: tclsqlite.c,v 1.30 2002/03/11 02:06:13 drh Exp $
** $Id: tclsqlite.c,v 1.31 2002/04/12 10:08:59 drh Exp $
*/
#ifndef NO_TCL /* Omit this whole file if TCL is unavailable */
@@ -268,10 +268,12 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
SqliteDb *pDb = (SqliteDb*)cd;
int choice;
static char *DB_optStrs[] = {
"busy", "close", "complete", "eval", "last_insert_rowid", "timeout", 0
"busy", "changes", "close", "complete",
"eval", "last_insert_rowid", "timeout", 0
};
enum DB_opts {
DB_BUSY, DB_CLOSE, DB_COMPLETE, DB_EVAL, DB_LAST_INSERT_ROWID, DB_TIMEOUT
DB_BUSY, DB_CHANGES, DB_CLOSE, DB_COMPLETE,
DB_EVAL, DB_LAST_INSERT_ROWID, DB_TIMEOUT
};
if( objc<2 ){
@@ -320,6 +322,25 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
break;
}
/*
** $db changes
**
** Return the number of rows that were modified, inserted, or deleted by
** the most recent "eval".
*/
case DB_CHANGES: {
Tcl_Obj *pResult;
int nChange;
if( objc!=2 ){
Tcl_WrongNumArgs(interp, 2, objv, "");
return TCL_ERROR;
}
nChange = sqlite_changes(pDb->db);
pResult = Tcl_GetObjResult(interp);
Tcl_SetIntObj(pResult, nChange);
break;
}
/* $db close
**
** Shutdown the database
+16 -86
View File
@@ -30,7 +30,7 @@
** But other routines are also provided to help in building up
** a program instruction by instruction.
**
** $Id: vdbe.c,v 1.136 2002/04/02 01:44:51 drh Exp $
** $Id: vdbe.c,v 1.139 2002/04/20 14:24:42 drh Exp $
*/
#include "sqliteInt.h"
#include <ctype.h>
@@ -1058,11 +1058,11 @@ static char *zOpName[] = { 0,
"MustBeInt", "Add", "AddImm", "Subtract",
"Multiply", "Divide", "Remainder", "BitAnd",
"BitOr", "BitNot", "ShiftLeft", "ShiftRight",
"AbsValue", "Like", "Glob", "Eq",
"Ne", "Lt", "Le", "Gt",
"Ge", "IsNull", "NotNull", "Negative",
"And", "Or", "Not", "Concat",
"Noop", "Function", "Limit",
"AbsValue", "Eq", "Ne", "Lt",
"Le", "Gt", "Ge", "IsNull",
"NotNull", "Negative", "And", "Or",
"Not", "Concat", "Noop", "Function",
"Limit",
};
/*
@@ -2011,69 +2011,6 @@ case OP_Ge: {
break;
}
/* Opcode: Like P1 P2 *
**
** Pop the top two elements from the stack. The top-most is a
** "like" pattern -- the right operand of the SQL "LIKE" operator.
** The lower element is the string to compare against the like
** pattern. Jump to P2 if the two compare, and fall through without
** jumping if they do not. The '%' in the top-most element matches
** any sequence of zero or more characters in the lower element. The
** '_' character in the topmost matches any single character of the
** lower element. Case is ignored for this comparison.
**
** If P1 is not zero, the sense of the test is inverted and we
** have a "NOT LIKE" operator. The jump is made if the two values
** are different.
*/
case OP_Like: {
int tos = p->tos;
int nos = tos - 1;
int c;
VERIFY( if( nos<0 ) goto not_enough_stack; )
if( Stringify(p, tos) || Stringify(p, nos) ) goto no_mem;
c = sqliteLikeCompare((unsigned char*)zStack[tos],
(unsigned char*)zStack[nos]);
POPSTACK;
POPSTACK;
if( pOp->p1 ) c = !c;
if( c ) pc = pOp->p2-1;
break;
}
/* Opcode: Glob P1 P2 *
**
** Pop the top two elements from the stack. The top-most is a
** "glob" pattern. The lower element is the string to compare
** against the glob pattern.
**
** Jump to P2 if the two compare, and fall through without
** jumping if they do not. The '*' in the top-most element matches
** any sequence of zero or more characters in the lower element. The
** '?' character in the topmost matches any single character of the
** lower element. [...] matches a range of characters. [^...]
** matches any character not in the range. Case is significant
** for globs.
**
** If P1 is not zero, the sense of the test is inverted and we
** have a "NOT GLOB" operator. The jump is made if the two values
** are different.
*/
case OP_Glob: {
int tos = p->tos;
int nos = tos - 1;
int c;
VERIFY( if( nos<0 ) goto not_enough_stack; )
if( Stringify(p, tos) || Stringify(p, nos) ) goto no_mem;
c = sqliteGlobCompare((unsigned char*)zStack[tos],
(unsigned char*)zStack[nos]);
POPSTACK;
POPSTACK;
if( pOp->p1 ) c = !c;
if( c ) pc = pOp->p2-1;
break;
}
/* Opcode: And * * *
**
** Pop two values off the stack. Take the logical AND of the
@@ -3155,19 +3092,16 @@ case OP_NewRecno: {
** stack. The key is the next value down on the stack. The key must
** be an integer. The stack is popped twice by this instruction.
**
** If P2==1 then overwriting is prohibited. If a prior entry with
** the same key exists, an SQLITE_CONSTRAINT exception is raised.
** If P2==1 then the row change count is incremented. If P2==0 the
** row change count is unmodified.
*/
/* Opcode: PutStrKey P1 P2 *
/* Opcode: PutStrKey P1 * *
**
** Write an entry into the database file P1. A new entry is
** created if it doesn't already exist or the data for an existing
** entry is overwritten. The data is the value on the top of the
** stack. The key is the next value down on the stack. The key must
** be a string. The stack is popped twice by this instruction.
**
** If P2==1 then overwriting is prohibited. If a prior entry with
** the same key exists, an SQLITE_CONSTRAINT exception is raised.
*/
case OP_PutIntKey:
case OP_PutStrKey: {
@@ -3188,16 +3122,7 @@ case OP_PutStrKey: {
iKey = intToKey(aStack[nos].i);
zKey = (char*)&iKey;
db->lastRowid = aStack[nos].i;
}
if( pOp->p2 ){
int res;
rc = sqliteBtreeMoveto(p->aCsr[i].pCursor, zKey, nKey, &res);
if( res==0 && rc==SQLITE_OK ){
rc = SQLITE_CONSTRAINT;
}
if( rc!=SQLITE_OK ){
goto abort_due_to_error;
}
if( pOp->p2 ) db->nChange++;
}
rc = sqliteBtreeInsert(p->aCsr[i].pCursor, zKey, nKey,
zStack[tos], aStack[tos].n);
@@ -3208,7 +3133,7 @@ case OP_PutStrKey: {
break;
}
/* Opcode: Delete P1 * *
/* Opcode: Delete P1 P2 *
**
** Delete the record at which the P1 cursor is currently pointing.
**
@@ -3216,12 +3141,16 @@ case OP_PutStrKey: {
** record in the table. If it is left pointing at the next record, then
** the next Next instruction will be a no-op. Hence it is OK to delete
** a record from within an Next loop.
**
** The row change counter is incremented if P2==1 and is unmodified
** if P2==0.
*/
case OP_Delete: {
int i = pOp->p1;
if( VERIFY( i>=0 && i<p->nCursor && ) p->aCsr[i].pCursor!=0 ){
rc = sqliteBtreeDelete(p->aCsr[i].pCursor);
}
if( pOp->p2 ) db->nChange++;
break;
}
@@ -4715,6 +4644,7 @@ cleanup:
}
sqliteBtreeCommitCkpt(pBt);
if( db->pBeTemp ) sqliteBtreeCommitCkpt(db->pBeTemp);
assert( p->tos<pc );
return rc;
/* Jump to here if a malloc() fails. It's hard to get a malloc()
+18 -20
View File
@@ -15,7 +15,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.49 2002/02/28 03:31:12 drh Exp $
** $Id: vdbe.h,v 1.50 2002/04/20 14:24:43 drh Exp $
*/
#ifndef _SQLITE_VDBE_H_
#define _SQLITE_VDBE_H_
@@ -180,27 +180,25 @@ typedef struct VdbeOp VdbeOp;
#define OP_ShiftLeft 95
#define OP_ShiftRight 96
#define OP_AbsValue 97
#define OP_Like 98
#define OP_Glob 99
#define OP_Eq 100
#define OP_Ne 101
#define OP_Lt 102
#define OP_Le 103
#define OP_Gt 104
#define OP_Ge 105
#define OP_IsNull 106
#define OP_NotNull 107
#define OP_Negative 108
#define OP_And 109
#define OP_Or 110
#define OP_Not 111
#define OP_Concat 112
#define OP_Noop 113
#define OP_Function 114
#define OP_Eq 98
#define OP_Ne 99
#define OP_Lt 100
#define OP_Le 101
#define OP_Gt 102
#define OP_Ge 103
#define OP_IsNull 104
#define OP_NotNull 105
#define OP_Negative 106
#define OP_And 107
#define OP_Or 108
#define OP_Not 109
#define OP_Concat 110
#define OP_Noop 111
#define OP_Function 112
#define OP_Limit 115
#define OP_Limit 113
#define OP_MAX 115
#define OP_MAX 113
/*
** Prototypes for the VDBE interface. See comments on the implementation
+115 -1
View File
@@ -13,7 +13,7 @@
# This file implements tests for the conflict resolution extension
# to SQLite.
#
# $Id: conflict.test,v 1.8 2002/02/19 13:39:23 drh Exp $
# $Id: conflict.test,v 1.11 2002/04/12 10:09:00 drh Exp $
set testdir [file dirname $argv0]
source $testdir/tester.tcl
@@ -377,6 +377,120 @@ foreach {i conf1 conf2 cmd t0 t1 t2} {
} [list $t0 $t1 $t2]
}
# Test to make sure a lot of IGNOREs don't cause a stack overflow
#
do_test conflict-7.1 {
execsql {
DROP TABLE t1;
DROP TABLE t2;
DROP TABLE t3;
CREATE TABLE t1(a unique, b);
}
for {set i 1} {$i<=50} {incr i} {
execsql "INSERT into t1 values($i,[expr {$i+1}]);"
}
execsql {
SELECT count(*), min(a), max(b) FROM t1;
}
} {50 1 51}
do_test conflict-7.2 {
execsql {
PRAGMA count_changes=on;
UPDATE OR IGNORE t1 SET a=1000;
}
} {1}
do_test conflict-7.2.1 {
db changes
} {1}
do_test conflict-7.3 {
execsql {
SELECT b FROM t1 WHERE a=1000;
}
} {2}
do_test conflict-7.4 {
execsql {
SELECT count(*) FROM t1;
}
} {50}
do_test conflict-7.5 {
execsql {
PRAGMA count_changes=on;
UPDATE OR REPLACE t1 SET a=1001;
}
} {50}
do_test conflict-7.5.1 {
db changes
} {50}
do_test conflict-7.6 {
execsql {
SELECT b FROM t1 WHERE a=1001;
}
} {51}
do_test conflict-7.7 {
execsql {
SELECT count(*) FROM t1;
}
} {1}
do_test conflict-7.7.1 {
db changes
} {0}
# Make sure the row count is right for rows that are ignored on
# an insert.
#
do_test conflict-8.1 {
execsql {
DELETE FROM t1;
INSERT INTO t1 VALUES(1,2);
}
execsql {
INSERT OR IGNORE INTO t1 VALUES(2,3);
}
} {1}
do_test conflict-8.1.1 {
db changes
} {1}
do_test conflict-8.2 {
execsql {
INSERT OR IGNORE INTO t1 VALUES(2,4);
}
} {0}
do_test conflict-8.2.1 {
db changes
} {0}
do_test conflict-8.3 {
execsql {
INSERT OR REPLACE INTO t1 VALUES(2,4);
}
} {1}
do_test conflict-8.3.1 {
db changes
} {1}
do_test conflict-8.4 {
execsql {
INSERT OR IGNORE INTO t1 SELECT * FROM t1;
}
} {0}
do_test conflict-8.4.1 {
db changes
} {0}
do_test conflict-8.5 {
execsql {
INSERT OR IGNORE INTO t1 SELECT a+2,b+2 FROM t1;
}
} {2}
do_test conflict-8.5.1 {
db changes
} {2}
do_test conflict-8.6 {
execsql {
INSERT OR IGNORE INTO t1 SELECT a+3,b+3 FROM t1;
}
} {3}
do_test conflict-8.6.1 {
db changes
} {3}
do_test insert-99.1 {
set x [execsql {PRAGMA integrity_check}]
if {$x==""} {set x ok}
+7 -1
View File
@@ -11,7 +11,7 @@
# This file implements regression tests for SQLite library. The
# focus of this file is testing built-in functions.
#
# $Id: func.test,v 1.9 2002/03/02 17:04:09 drh Exp $
# $Id: func.test,v 1.10 2002/04/06 14:10:47 drh Exp $
set testdir [file dirname $argv0]
source $testdir/tester.tcl
@@ -230,5 +230,11 @@ do_test func-6.2 {
execsql {SELECT coalesce(upper(a),'nil') FROM t2}
} {1 nil 345 nil 67890}
# Test the last_insert_rowid() function
#
do_test func-7.1 {
execsql {SELECT last_insert_rowid()}
} [db last_insert_rowid]
finish_test
+16 -1
View File
@@ -11,7 +11,7 @@
# This file implements regression tests for SQLite library. The
# focus of this file is testing the INSERT statement.
#
# $Id: insert.test,v 1.9 2002/02/19 13:39:23 drh Exp $
# $Id: insert.test,v 1.10 2002/04/12 13:11:53 drh Exp $
set testdir [file dirname $argv0]
source $testdir/tester.tcl
@@ -162,6 +162,8 @@ do_test insert-3.5 {
set x
} {ok}
# Test of expressions in the VALUES clause
#
do_test insert-4.1 {
execsql {
CREATE TABLE t3(a,b,c);
@@ -192,5 +194,18 @@ do_test insert-4.5 {
SELECT b,c FROM t3 WHERE a IS NULL;
}
} {6 7}
do_test insert-4.6 {
catchsql {
INSERT INTO t3 VALUES(notafunc(2,3),2,3);
}
} {1 {no such function: notafunc}}
do_test insert-4.7 {
execsql {
INSERT INTO t3 VALUES(min(1,2,3),max(1,2,3),99);
SELECT * FROM t3 WHERE c=99;
}
} {1 3 99}
# Test
finish_test
+12 -1
View File
@@ -12,7 +12,7 @@
# focus of this file is testing the INSERT statement that takes is
# result from a SELECT.
#
# $Id: insert2.test,v 1.8 2002/03/03 02:49:52 drh Exp $
# $Id: insert2.test,v 1.9 2002/04/12 10:09:00 drh Exp $
set testdir [file dirname $argv0]
source $testdir/tester.tcl
@@ -38,6 +38,9 @@ do_test insert2-1.1.1 {
}
} {6}
do_test insert2-1.1.2 {
db changes
} {6}
do_test insert2-1.1.3 {
execsql {SELECT * FROM t1 ORDER BY log}
} {0 1 1 1 2 2 3 4 4 8 5 4}
@@ -138,6 +141,11 @@ do_test insert2-3.2 {
INSERT INTO t4 VALUES(9,18);
INSERT INTO t4 VALUES(10,20);
COMMIT;
}
db changes
} {9}
do_test insert2-3.2.1 {
execsql {
SELECT count(*) FROM t4;
}
} {10}
@@ -182,5 +190,8 @@ do_test insert2-3.7 {
ROLLBACK;
}
} {1}
do_test insert2-3.8 {
db changes
} {159}
finish_test
+122 -1
View File
@@ -11,7 +11,7 @@
# This file implements regression tests for SQLite library. The
# focus of this file is testing the SELECT statement.
#
# $Id: select1.test,v 1.22 2002/03/02 17:04:09 drh Exp $
# $Id: select1.test,v 1.24 2002/04/06 13:57:43 drh Exp $
set testdir [file dirname $argv0]
source $testdir/tester.tcl
@@ -544,5 +544,126 @@ do_test select1-10.6 {
}
} {11 22}
# Check the ability to specify "TABLE.*" in the result set of a SELECT
#
do_test select1-11.1 {
execsql {
DELETE FROM t3;
DELETE FROM t4;
INSERT INTO t3 VALUES(1,2);
INSERT INTO t4 VALUES(3,4);
SELECT * FROM t3, t4;
}
} {1 2 3 4}
do_test select1-11.2 {
execsql2 {
SELECT * FROM t3, t4;
}
} {t3.a 1 t3.b 2 t4.a 3 t4.b 4}
do_test select1-11.3 {
execsql2 {
SELECT * FROM t3 AS x, t4 AS y;
}
} {x.a 1 x.b 2 y.a 3 y.b 4}
do_test select1-11.4 {
execsql {
SELECT t3.*, t4.b FROM t3, t4;
}
} {1 2 4}
do_test select1-11.5 {
execsql2 {
SELECT t3.*, t4.b FROM t3, t4;
}
} {t3.a 1 t3.b 2 t4.b 4}
do_test select1-11.6 {
execsql2 {
SELECT x.*, y.b FROM t3 AS x, t4 AS y;
}
} {x.a 1 x.b 2 y.b 4}
do_test select1-11.7 {
execsql {
SELECT t3.b, t4.* FROM t3, t4;
}
} {2 3 4}
do_test select1-11.8 {
execsql2 {
SELECT t3.b, t4.* FROM t3, t4;
}
} {t3.b 2 t4.a 3 t4.b 4}
do_test select1-11.9 {
execsql2 {
SELECT x.b, y.* FROM t3 AS x, t4 AS y;
}
} {x.b 2 y.a 3 y.b 4}
do_test select1-11.10 {
catchsql {
SELECT t5.* FROM t3, t4;
}
} {1 {no such table: t5}}
do_test select1-11.11 {
catchsql {
SELECT t3.* FROM t3 AS x, t4;
}
} {1 {no such table: t3}}
do_test select1-11.12 {
execsql2 {
SELECT t3.* FROM t3, (SELECT max(a), max(b) FROM t4)
}
} {t3.a 1 t3.b 2}
do_test select1-11.13 {
execsql2 {
SELECT t3.* FROM (SELECT max(a), max(b) FROM t4), t3
}
} {t3.a 1 t3.b 2}
do_test select1-11.14 {
execsql2 {
SELECT * FROM t3, (SELECT max(a), max(b) FROM t4)
}
} {t3.a 1 t3.b 2 max(a) 3 max(b) 4}
do_test select1-11.15 {
execsql2 {
SELECT y.*, t3.* FROM t3, (SELECT max(a), max(b) FROM t4) AS y
}
} {y.max(a) 3 y.max(b) 4 t3.a 1 t3.b 2}
# Tests of SELECT statements without a FROM clause.
#
do_test select1-12.1 {
execsql2 {
SELECT 1+2+3
}
} {1+2+3 6}
do_test select1-12.2 {
execsql2 {
SELECT 1,'hello',2
}
} {1 1 'hello' hello 2 2}
do_test select1-12.3 {
execsql2 {
SELECT 1 AS 'a','hello' AS 'b',2 AS 'c'
}
} {a 1 b hello c 2}
do_test select1-12.4 {
execsql {
DELETE FROM t3;
INSERT INTO t3 VALUES(1,2);
SELECT * FROM t3 UNION SELECT 3 AS 'a', 4 ORDER BY a;
}
} {1 2 3 4}
do_test select1-12.5 {
execsql {
SELECT 3, 4 UNION SELECT * FROM t3;
}
} {1 2 3 4}
do_test select1-12.6 {
execsql {
SELECT * FROM t3 WHERE a=(SELECT 1);
}
} {1 2}
do_test select1-12.7 {
execsql {
SELECT * FROM t3 WHERE a=(SELECT 2);
}
} {}
finish_test
+2 -2
View File
@@ -15,7 +15,7 @@
# interface is pretty well tested. This file contains some addition
# tests for fringe issues that the main test suite does not cover.
#
# $Id: tclsqlite.test,v 1.5 2002/01/16 21:00:28 drh Exp $
# $Id: tclsqlite.test,v 1.6 2002/04/12 10:09:00 drh Exp $
set testdir [file dirname $argv0]
source $testdir/tester.tcl
@@ -29,7 +29,7 @@ do_test tcl-1.1 {
do_test tcl-1.2 {
set v [catch {db bogus} msg]
lappend v $msg
} {1 {bad option "bogus": must be busy, close, complete, eval, last_insert_rowid, or timeout}}
} {1 {bad option "bogus": must be busy, changes, close, complete, eval, last_insert_rowid, or timeout}}
do_test tcl-1.3 {
execsql {CREATE TABLE t1(a int, b int)}
execsql {INSERT INTO t1 VALUES(10,20)}
+8 -8
View File
@@ -32,10 +32,10 @@ proc runtest {title} {
puts "</blockquote><table border=0 cellpadding=0 cellspacing=0>"
set format {<tr><td>%s</td><td align="right">&nbsp;&nbsp;&nbsp;%.3f</td></tr>}
set delay 1000
exec sync; after $delay;
set t [time "exec psql drh <$sqlfile" 1]
set t [expr {[lindex $t 0]/1000000.0}]
puts [format $format PostgreSQL: $t]
# exec sync; after $delay;
# set t [time "exec psql drh <$sqlfile" 1]
# set t [expr {[lindex $t 0]/1000000.0}]
# puts [format $format PostgreSQL: $t]
exec sync; after $delay;
set t [time "exec mysql -f drh <$sqlfile" 1]
set t [expr {[lindex $t 0]/1000000.0}]
@@ -47,11 +47,11 @@ proc runtest {title} {
# set t [expr {[lindex $t 0]/1000000.0}]
# puts [format $format {SQLite 2.4 (cache=100):} $t]
exec sync; after $delay;
set t [time "exec ./sqlite240 s2k.db <$sqlfile" 1]
set t [time "exec ./sqlite248 s2k.db <$sqlfile" 1]
set t [expr {[lindex $t 0]/1000000.0}]
puts [format $format {SQLite 2.4:} $t]
exec sync; after $delay;
set t [time "exec ./sqlite240 sns.db <$sqlfile" 1]
set t [time "exec ./sqlite248 sns.db <$sqlfile" 1]
set t [expr {[lindex $t 0]/1000000.0}]
puts [format $format {SQLite 2.4 (nosync):} $t]
# set t [time "exec ./sqlite-t1 st1.db <$sqlfile" 1]
@@ -78,14 +78,14 @@ puts $fd {
PRAGMA default_synchronous=on;
}
close $fd
exec ./sqlite240 s2k.db <2kinit.sql
exec ./sqlite248 s2k.db <2kinit.sql
set fd [open nosync-init.sql w]
puts $fd {
PRAGMA default_cache_size=2000;
PRAGMA default_synchronous=off;
}
close $fd
exec ./sqlite240 sns.db <nosync-init.sql
exec ./sqlite248 sns.db <nosync-init.sql
set ones {zero one two three four five six seven eight nine
ten eleven twelve thirteen fourteen fifteen sixteen seventeen
eighteen nineteen}
+19 -1
View File
@@ -1,7 +1,7 @@
#
# Run this Tcl script to generate the sqlite.html file.
#
set rcsid {$Id: c_interface.tcl,v 1.25 2002/03/04 02:26:17 drh Exp $}
set rcsid {$Id: c_interface.tcl,v 1.26 2002/04/12 10:09:00 drh Exp $}
puts {<html>
<head>
@@ -307,6 +307,8 @@ useful interfaces. These extended routines are as follows:
<blockquote><pre>
int sqlite_last_insert_rowid(sqlite*);
int sqlite_changes(sqlite*);
int sqlite_get_table(
sqlite*,
char *sql,
@@ -389,6 +391,22 @@ the key is automatically generated. You can find the value of the key
for the most recent INSERT statement using the
<b>sqlite_last_insert_rowid()</b> API function.</p>
<h2>The number of rows that changed</h2>
<p>The <b>sqlite_changes()</b> API function returns the number of rows
that were inserted, deleted, or modified during the most recent
<b>sqlite_exec()</b> call. The number reported includes any changes
that were later undo by a ROLLBACK or ABORT. But rows that are
deleted because of a DROP TABLE are <em>not</em> counted.</p>
<p>SQLite implements the command "<b>DELETE FROM table</b>" (without
a WHERE clause) by dropping the table then recreating it.
This is much faster than deleting the elements of the table individually.
But it also means that the value returned from <b>sqlite_changes()</b>
will be zero regardless of the number of elements that were originally
in the table. If an accurate count of the number of elements deleted
is necessary, use "<b>DELETE FROM table WHERE 1</b>" instead.</p>
<h2>Querying without using a callback function</h2>
<p>The <b>sqlite_get_table()</b> function is a wrapper around
+28
View File
@@ -25,6 +25,34 @@ proc chng {date desc} {
puts "<DD><P><UL>$desc</UL></P></DD>"
}
chng {2002 Apr 21 (2.4.9)} {
<li>Fix a bug that was causing the precompiled binary of SQLITE.EXE to
report "out of memory" under Windows 98.</li>
}
chng {2002 Apr 20 (2.4.8)} {
<li>Make sure VIEWs are created after their corresponding TABLEs in the
output of the <b>.dump</b> command in the shell.</li>
<li>Speed improvements: Do not do synchronous updates on TEMP tables.</li>
<li>Many improvements and enhancements to the shell.</li>
<li>Make the GLOB and LIKE operators functions that can be overridden
by a programmer. This allows, for example, the LIKE operator to
be changed to be case sensitive.</li>
}
chng {2002 Apr 06 (2.4.7)} {
<li>Add the ability to put TABLE.* in the column list of a
SELECT statement.</li>
<li>Permit SELECT statements without a FROM clause.</li>
<li>Added the <b>last_insert_rowid()</b> SQL function.</li>
<li>Do not count rows where the IGNORE conflict resultion occurs in
the row count.</li>
<li>Make sure functions expressions in the VALUES clause of an INSERT
are correct.</li>
<li>Added the <b>sqlite_changes()</b> API function to return the number
of row that changed in the most recent operation.</li>
}
chng {2002 Apr 02 (2.4.6)} {
<li>Bug fix: Correctly handle terms in the WHERE clause of a join that
do not contain a comparison operator.</li>
+22 -10
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.57 2002/04/01 12:15:02 drh Exp $}
set rcsid {$Id: index.tcl,v 1.59 2002/04/12 13:11:53 drh Exp $}
puts {<html>
<head><title>SQLite: An SQL Database Engine In A C Library</title></head>
@@ -29,13 +29,20 @@ big database server. SQLite <b>is</b> the server. The SQLite
library reads and writes directly to and from the database files
on disk.</p>}
puts {<table align="right"hspace="10">
<tr><td align="center" bgcolor="#8ee5ee">
<table border="2"><tr><td align="center">
<a href="download.html"><big><b>Download<br>SQLite
puts {
<table align="right" hspace="10" cellpadding=0 cellspacing=0 broder=0>
<tr><td align="right" bgcolor="#cacae4">
<table border="2" width="100%" cellspacing=0 cellpadding=5><tr><td align="left">
Quick Links:
<ul>
<li><a href="download.html">Download</a></li>
<li><a href="http://cvs.hwaci.com:2080/sqlite/timeline">Change Log</a></li>
<li><a href="http://cvs.hwaci.com:2080/sqlite/tktnew">Report a bug</a></li>
</ul>
</td></tr></table>
</td></tr>
</table>}
</table>
}
puts {<h2>Features</h2>
@@ -63,10 +70,11 @@ puts {<h2>Current Status</h2>
<p>A <a href="changes.html">Change Summary</a> is available on this
website. You can also access a detailed
<a href="http://cvs.hwaci.com:2080/sqlite/timeline">change history</a>
and a list of
<a href="http://cvs.hwaci.com:2080/sqlite/rptview?rn=2">open bugs</a>
from the
<a href="http://cvs.hwaci.com:2080/sqlite/timeline">change history</a>,
<a href="http://cvs.hwaci.com:2080/sqlite/rptview?rn=2">view open bugs</a>,
or
<a href="http://cvs.hwaci.com:2080/sqlite/tktnew">report new bugs</a>
at the
<a href="http://cvs.hwaci.com:2080/sqlite/">CVS server</a>.</p>
<p>Complete source code and precompiled binaries for the latest release are
@@ -174,6 +182,10 @@ puts {<h2>Related Sites</h2>
<li><p>An ODBC driver for SQLite can be found at
<a href="http://www.ch-werner.de/sqliteodbc/">
http://www.ch-werner.de/sqliteodbc/</a>.</p></li>
<li><p>A PHP module for SQLite can be found at
<a href="http://freshmeat.net/projects/sqlite-php">
http://freshmeat.net/projects/sqlite-php</a></li>
</ul>}
puts {
+13 -2
View File
@@ -1,7 +1,7 @@
#
# Run this Tcl script to generate the sqlite.html file.
#
set rcsid {$Id: lang.tcl,v 1.29 2002/03/28 14:20:08 drh Exp $}
set rcsid {$Id: lang.tcl,v 1.32 2002/04/12 10:09:00 drh Exp $}
puts {<html>
<head>
@@ -566,6 +566,13 @@ their result across all rows of the result set.</p>
all arguments are NULL then NULL is returned.</td>
</tr>
<tr>
<td valign="top" align="right">last_insert_rowid()</td>
<td valign="top">Return the ROWID of the last row insert from this
connection to the database. This is the same value that would be returned
from the <b>sqlite_last_insert_rowid()</b> API function.</td>
</tr>
<tr>
<td valign="top" align="right">length(<i>X</i>)</td>
<td valign="top">Return the string length of <i>X</i> in characters.
@@ -836,6 +843,8 @@ with caution.</p>
<p>When on, the COUNT_CHANGES pragma causes the callback function to
be invoked once for each DELETE, INSERT, or UPDATE operation. The
argument is the number of rows that were changed.</p>
<p>This pragma may be removed from future versions of SQLite.
Consider using the <b>sqlite_changes()</b> API function instead.</p></li>
<li><p><b>PRAGMA default_cache_size;
<br>PRAGMA default_cache_size = </b><i>Number-of-pages</i><b>;</b></p>
@@ -955,7 +964,7 @@ information.</p>
Section SELECT select
Syntax {sql-statement} {
SELECT <result> FROM <table-list>
SELECT <result> [FROM <table-list>]
[WHERE <expression>]
[GROUP BY <expr-list>]
[HAVING <expression>]
@@ -993,6 +1002,8 @@ puts {for that one expression.</p>
the FROM keyword. If more than one table is specified, then the
query is against the (inner) join of the various tables. A sub-query
in parentheses may be substituted for any table name in the FROM clause.
The entire FROM clause may be omitted, in which case the result is a
single row consisting of the values of the expression list.
</p>
<p>The WHERE clause can be used to limit the number of rows over
+9 -2
View File
@@ -1,7 +1,7 @@
#
# Run this Tcl script to generate the tclsqlite.html file.
#
set rcsid {$Id: tclsqlite.tcl,v 1.6 2002/01/16 21:00:28 drh Exp $}
set rcsid {$Id: tclsqlite.tcl,v 1.7 2002/04/12 10:09:00 drh Exp $}
puts {<html>
<head>
@@ -50,12 +50,13 @@ the database is stored.
<p>
Once an SQLite database is open, it can be controlled using
methods of the <i>dbcmd</i>. There are currently 6 methods
methods of the <i>dbcmd</i>. There are currently 7 methods
defined:</p>
<p>
<ul>
<li> busy
<li> changes
<li> close
<li> complete
<li> eval
@@ -248,6 +249,12 @@ should return "1" if it wants SQLite to abandon the current operation.
<p>The "last_insert_rowid" method returns an integer which is the ROWID
of the most recently inserted database row.</p>
<h2>The "changes" method</h2>
<p>The "changes" method returns an integer which is the number of rows
in the database that were inserted, deleted, and/or modified by the most
recent "eval" method.</p>
}
puts {