From: drh Date: Tue, 18 Nov 2014 21:20:57 +0000 (+0000) Subject: Merge recent trunk enhancements, including the read-after-ROLLBACK change X-Git-Tag: version-3.13.0~148^2~96 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=04e8a5866948996e4dd1540a9a736d00609c27e8;p=thirdparty%2Fsqlite.git Merge recent trunk enhancements, including the read-after-ROLLBACK change and the addition of sqlite3_stmt_scanstatus() support, as well as various minor bug fixes. FossilOrigin-Name: f09055f3c4348264c7336f90646375f0d98b061e --- 04e8a5866948996e4dd1540a9a736d00609c27e8 diff --cc manifest index 26da220ff3,8a5349a2dd..3bf03658d6 --- a/manifest +++ b/manifest @@@ -1,9 -1,9 +1,9 @@@ - C Merge\srecent\strunk\senhancements,\sand\sin\sparticular\sthe\simprovements\sto\nthe\sb-tree\sbalancing\slogic,\sinto\sthe\ssessions\sbranch. - D 2014-10-31T14:53:32.379 -C Merge\sin\sall\sthe\sother\sROLLBACK\sfixes\sfrom\sthe\sbranch-3.8.7\sbranch.\s\s\nI\sdon't\sknow\swhy\sI\swas\sdoing\sthem\sone-by-one. -D 2014-11-18T20:49:30.759 ++C Merge\srecent\strunk\senhancements,\sincluding\sthe\sread-after-ROLLBACK\schange\nand\sthe\saddition\sof\ssqlite3_stmt_scanstatus()\ssupport,\sas\swell\sas\svarious\nminor\sbug\sfixes. ++D 2014-11-18T21:20:57.012 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f - F Makefile.in dd5f245aa8c741bc65845747203c8ce2f3fb6c83 -F Makefile.in a226317fdf3f4c895fb3cfedc355b4d0868ce1fb ++F Makefile.in e2007fafb7b679a39800a1d636dcc6662a840530 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 - F Makefile.msc 35808af7f8d999176ed5b38fb482a87a129ee3e1 -F Makefile.msc 788f1288633a0c3c3cbbe0f3e4827d033f7ba530 ++F Makefile.msc 64f7ba446298db653fc195ad7699e4b06863d9c4 F Makefile.vxworks 034289efa9d591b04b1a73598623119c306cbba0 F README.md 64f270c43c38c46de749e419c22f0ae2f4499fe8 F VERSION d846487aff892625eb8e75960234e7285f0462fe @@@ -168,7 -152,7 +169,7 @@@ F ext/userauth/userauth.c 5fa3bdb492f48 F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8 F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60 - F main.mk 7711bc77822814799b853271ee19ac79e98bfb4b -F main.mk 084976077a4aa3bd985154b5423e7aed88e4a2e9 ++F main.mk 112ccda703db78f10e0b386723acab2044fd97ed F mkopcodec.awk c2ff431854d702cdd2d779c9c0d1f58fa16fa4ea F mkopcodeh.awk c6b3fa301db6ef7ac916b14c60868aeaec1337b5 F mkso.sh fd21c06b063bb16a5d25deea1752c2da6ac3ed83 @@@ -196,9 -180,9 +197,9 @@@ F src/build.c 67bb05b1077e0cdaccb2e36bf F src/callback.c 7b44ce59674338ad48b0e84e7b72f935ea4f68b0 F src/complete.c c4ba6e0626bb94bc77a0861735f3382fcf7cc818 F src/ctime.c df19848891c8a553c80e6f5a035e768280952d1a - F src/date.c 57a7f9ba9f6b4d5268f5e411739066a611f99036 + F src/date.c 93594514aae68de117ca4a2a0d6cc63eddf26744 -F src/delete.c 0750b1eb4d96cd3fb2c798599a3a7c85e92f1417 +F src/delete.c 20a360262b62051afacb44122b3593a8bd9be131 - F src/expr.c 0391a657df4959eaf2a2fd7d77de5ebe750686ee + F src/expr.c a3ff05db5709d628c23890db862e30f3dd9dc428 F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb F src/fkey.c da985ae673efef2c712caef825a5d2edb087ead7 F src/func.c ba47c1671ab3cfdafa6e9d6ee490939ea578adee @@@ -211,8 -195,8 +212,8 @@@ F src/journal.c b4124532212b6952f42eb2c F src/legacy.c ba1863ea58c4c840335a84ec276fc2b25e22bc4e F src/lempar.c 7274c97d24bb46631e504332ccd3bd1b37841770 F src/loadext.c de741e66e5ddc1598d904d7289239696e40ed994 - F src/main.c 7a3d124561f3fd0555dbefe492f5db0a4ba9f8d8 - F src/malloc.c 3c3ac67969612493d435e14b6832793209afd2ec -F src/main.c d3310d5ed56e246bf1589e47eeaca8be582bd4b8 ++F src/main.c 46e2f592797c6167fb6964a7fa953c480ec766d8 + F src/malloc.c 740db54387204c9a2eb67c6d98e68b08e9ef4eab F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645 F src/mem1.c faf615aafd8be74a71494dfa027c113ea5c6615f F src/mem2.c f1940d9e91948dd6a908fbb9ce3835c36b5d83c3 @@@ -245,16 -229,16 +246,16 @@@ F src/random.c ba2679f80ec82c4190062d75 F src/resolve.c 4965007d6497b6a4d7a6d98751cc39712885f952 F src/rowset.c eccf6af6d620aaa4579bd3b72c1b6395d9e9fa1e F src/select.c 428165951748151e87a15295b7357221433e311b - F src/shell.c e3a0e5d94d58fbaf9a4a0a4bb2db19ed2e64be6d - F src/sqlite.h.in 322c7813901be3038a7df08cdb456ad76e880f60 -F src/shell.c bc28d5992109717c87804e2eb1a08a7c8cc7a2fd -F src/sqlite.h.in 0c5c0df7e4e436dfc5592511325bf4a96f6a638d ++F src/shell.c a67b1304a7d93f26ec611de7b4650307f8e074a7 ++F src/sqlite.h.in 6a51d3ab98e02777b894f361a81766eb9cee7413 F src/sqlite3.rc 992c9f5fb8285ae285d6be28240a7e8d3a7f2bad F src/sqlite3ext.h 17d487c3c91b0b8c584a32fbeb393f6f795eea7d - F src/sqliteInt.h 339f3f8bf633452d271a3c9c593c4a0faaa171f8 -F src/sqliteInt.h 71b0bf1a7fc55b5cb374f7579fd140e730a6e0f4 ++F src/sqliteInt.h 29e71a7464bdec315a269976b817b7add6d4edbc F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d F src/status.c 81712116e826b0089bb221b018929536b2b5406f F src/table.c f142bba7903e93ca8d113a5b8877a108ad1a27dc - F src/tclsqlite.c 684c317b85f4729de12909bfad80d3f5500357cf - F src/test1.c 63d4b1707c4052cf9c05c1cbb4a62666d70a0b48 -F src/tclsqlite.c 0a874655dd39a9875e39c5d3c464db662171d228 ++F src/tclsqlite.c 05be57620509060e85064b9495256c05d56e76b0 + F src/test1.c 6b0469b8e06c77b1de1d3e4a3834cf26edea9cc7 F src/test2.c 98049e51a17dc62606a99a9eb95ee477f9996712 F src/test3.c 1c0e5d6f080b8e33c1ce8b3078e7013fdbcd560c F src/test4.c 9b32d22f5f150abe23c1830e2057c4037c45b3df @@@ -266,8 -250,9 +267,9 @@@ F src/test9.c bea1e8cf52aa93695487baded F src/test_async.c 21e11293a2f72080eda70e1124e9102044531cd8 F src/test_autoext.c dea8a01a7153b9adc97bd26161e4226329546e12 F src/test_backup.c 3875e899222b651e18b662f86e0e50daa946344e + F src/test_blob.c 1f2e3e25255b731c4fcf15ee7990d06347cb6c09 F src/test_btree.c 2e9978eca99a9a4bfa8cae949efb00886860a64f - F src/test_config.c 6ff73defc3db475059a9d7ca8023f05f99242c8e -F src/test_config.c 035c17a173937d019b8dfc1d524f9d3fc8123504 ++F src/test_config.c d0d671ce14e41abd487036ef8c4422585157d49a F src/test_demovfs.c 69b2085076654ebc18014cbc6386f04409c959a9 F src/test_devsym.c e7498904e72ba7491d142d5c83b476c4e76993bc F src/test_fs.c ced436e3d4b8e4681328409b8081051ce614e28f @@@ -302,25 -287,25 +304,25 @@@ F src/test_wsd.c 41cadfd9d97fe8e3e4e44f F src/threads.c 6de09362b657f19ba83e5fa521ee715787ce9fee F src/tokenize.c cc9016e5007fc5e76789079616d2f26741bcc689 F src/trigger.c 25571661fdeae8c7f975ff40ffec205520a3f92f -F src/update.c 3c4ecc282accf12d39edb8d524cf089645e55a13 +F src/update.c d207deb7a031f698104bee879de0632b611e72dd F src/utf.c fc6b889ba0779b7722634cdeaa25f1930d93820c F src/util.c 3b627daa45c7308c1e36e3dbaa3f9ce7e5c7fa73 - F src/vacuum.c 59f03f92bcff57faa6a8ca256eb29ccddfb0614a - F src/vdbe.c 407f16f5991c5bb27978e00bf74e64dd0dabd692 - F src/vdbe.h d61daeffed696e21630759de9e135ee298ad9573 - F src/vdbeInt.h 18756149efd3a931803381074dccc4ce161c7063 - F src/vdbeapi.c 1a5c9248249f3e17bd0707da799d4c613970ffe1 - F src/vdbeaux.c d53c188cde088b58bd2733144c4c803e32bf2bba - F src/vdbeblob.c d83b1f9e8f3c0359adc8404b8673b7994739daf7 + F src/vacuum.c 9b30ec729337dd012ed88d4c292922c8ef9cf00c -F src/vdbe.c 5563459c06c434bc43131044fcf8164654008ebd -F src/vdbe.h 6fc69d9c5e146302c56e163cb4b31d1ee64a18c3 -F src/vdbeInt.h 9bb69ff2447c34b6ccc58b34ec35b615f86ead78 -F src/vdbeapi.c 07acb615d1e4170e71fc1b0d087f3c53a1ad8e83 -F src/vdbeaux.c 9b0a251b6dfab349dd6c6efb40062eb7386b26f5 -F src/vdbeblob.c 4af4bfb71f6df7778397b4a0ebc1879793276778 -F src/vdbemem.c 31d8eabb0cd78bfeab4e5124c7363c3e9e54db9f ++F src/vdbe.c ef0d4a46d2aa7f6aedb2c4c29c66e1e33d4c7168 ++F src/vdbe.h b434bb75fbec973d18d49225a59833ae39ee2afc ++F src/vdbeInt.h dc69f0351bef56456fdba3e09d3387ba4f1b1520 ++F src/vdbeapi.c 3d4d2a2b24055ce2cb029fa73067c56616264b51 ++F src/vdbeaux.c 19ecf5fc0524346e46c31fa035c57adb90ffa49e ++F src/vdbeblob.c cb7359c2d99df92c35cdaedc12af6d4f83854cb7 +F src/vdbemem.c 96e41193b4affd9ebc0eea2fa628879dac88c744 - F src/vdbesort.c 975aeffa99acb0991b2f288d30294756bff41438 + F src/vdbesort.c 87f3923483113d1c95d84640becb4e4946f27d9a F src/vdbetrace.c 7e4222955e07dd707a2f360c0eb73452be1cb010 F src/vtab.c 2a30791bbd7926b589401bd09c3abb33de563793 - F src/wal.c 10e7de7ce90865a68153f001a61f1d985cd17983 + F src/wal.c fa090966140602f03a621f87d82ee69e66ca63b5 F src/wal.h df01efe09c5cb8c8e391ff1715cca294f89668a4 F src/walker.c c253b95b4ee44b21c406e2a1052636c31ea27804 - F src/where.c 5665df88cbd2b38eb72b4b94c8892c8afb360181 - F src/whereInt.h 19279cd0664ce1d90b9ad3ef0108cb494acfe455 + F src/where.c 3862a1173ae2716bde12f1ab3fb649f1d85b05c2 + F src/whereInt.h d3633e9b592103241b74b0ec76185f3e5b8b62e0 F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2 F test/aggnested.test b35b4cd69fc913f90d39a575e171e1116c3a4bb7 @@@ -507,8 -497,8 +514,8 @@@ F test/fkey2.test 1db212cda86b0d3ce7271 F test/fkey3.test 76d475c80b84ee7a5d062e56ccb6ea68882e2b49 F test/fkey4.test 86446017011273aad8f9a99c1a65019e7bd9ca9d F test/fkey5.test 8a1fde4e7721ae00b05b3178888833726ca2df8d -F test/fkey6.test abb59f866c1b44926fd02d1fdd217d831fe04f48 +F test/fkey6.test 6697550baa38505c9952eff130ab26a2d156c0cc - F test/fkey7.test e31d0e71a41c1d29349a16448d6c420e2c53a8fc + F test/fkey7.test 72e915890ee4a005daaf3002cb208e8fe973ac13 F test/fkey_malloc.test 594a7ea1fbab553c036c70813cd8bd9407d63749 F test/format4.test 1f0cac8ff3895e9359ed87e41aaabee982a812eb F test/fts-9fd058691.test 78b887e30ae6816df0e1fed6259de4b5a64ad33c @@@ -896,10 -889,10 +907,10 @@@ F test/superlock.test 1cde669f68d2dd37d F test/sync.test a34cd43e98b7fb84eabbf38f7ed8f7349b3f3d85 F test/syscall.test d2fdaad713f103ac611fe7ef9b724c7b69f8149c F test/sysfault.test fa776e60bf46bdd3ae69f0b73e46ee3977a58ae6 - F test/table.test 2a1d2fa52c531de5915f28023747d9a8c27b6f31 + F test/table.test 06271d61eb13871490d38168433c1ef3dd82bb2a F test/tableapi.test 2674633fa95d80da917571ebdd759a14d9819126 F test/tableopts.test dba698ba97251017b7c80d738c198d39ab747930 -F test/tclsqlite.test 37a61c2da7e3bfe3b8c1a2867199f6b860df5d43 +F test/tclsqlite.test a7308276aad2e6c0bfb5b0414424dd0d9cc0cad7 F test/tempdb.test 19d0f66e2e3eeffd68661a11c83ba5e6ace9128c F test/temptable.test d2c9b87a54147161bcd1822e30c1d1cd891e5b30 F test/temptrigger.test 8ec228b0db5d7ebc4ee9b458fc28cb9e7873f5e1 @@@ -1228,7 -1221,7 +1239,7 @@@ F tool/vdbe_profile.tcl 67746953071a9f8 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings.sh 0abfd78ceb09b7f7c27c688c8e3fe93268a13b32 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f - P f4de9e07be3819db222317725e62ea997cd22a20 67f0d469da28c023200239a1f3d0c6cef9ef0e45 - R c6574894282e9c56b60788bbd17b6d37 -P 2896f2640ab3e102ee248d20fb68c497817524eb 945a9e687fdfee5f7103d85d131024e85d594ac3 -R 8b4c6ed5b267d5d2bc5845aacffad250 ++P 28b044a51215a3f64dafb2cf3b6cb7d2029580ef 296b0c7397790ceadbdb330959e962f6491abc3e ++R 29b28d376c9121783d6d4ac3519f50ed U drh - Z d46687ae123d3be720b18e6a3cbacd2c -Z 0fc13d2fc810c826601883122c184fdf ++Z c3d0de9dd44c494f12cc744c4f6471c6 diff --cc manifest.uuid index 374c4eafae,75b72440d4..5f7a3cf0d4 --- a/manifest.uuid +++ b/manifest.uuid @@@ -1,1 -1,1 +1,1 @@@ - 28b044a51215a3f64dafb2cf3b6cb7d2029580ef -296b0c7397790ceadbdb330959e962f6491abc3e ++f09055f3c4348264c7336f90646375f0d98b061e diff --cc src/sqlite.h.in index 400d3aae5e,4427f39d06..65fade74be --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@@ -7406,103 -7444,100 +7444,195 @@@ int sqlite3_vtab_on_conflict(sqlite3 *) /* #define SQLITE_ABORT 4 // Also an error code */ #define SQLITE_REPLACE 5 + /* + ** CAPI3REF: Prepared Statement Scan Status Opcodes + ** KEYWORDS: {scanstatus options} + ** + ** The following constants can be used for the T parameter to the + ** [sqlite3_stmt_scanstatus(S,X,T,V)] interface. Each constant designates a + ** different metric for sqlite3_stmt_scanstatus() to return. + ** + **
+ ** [[SQLITE_SCANSTAT_NLOOP]]
SQLITE_SCANSTAT_NLOOP
+ **
^The [sqlite3_int64] variable pointed to by the T parameter will be set to the + ** total number of times that the X-th loop has run.
+ ** + ** [[SQLITE_SCANSTAT_NVISIT]]
SQLITE_SCANSTAT_NVISIT
+ **
^The [sqlite3_int64] variable pointed to by the T parameter will be set to the + ** total number of rows examined by all iterations of the X-th loop.
+ ** + ** [[SQLITE_SCANSTAT_EST]]
SQLITE_SCANSTAT_EST
+ **
^The "double" variable pointed to by the T parameter will be set to the + ** query planner's estimate for the average number of rows output from each + ** iteration of the X-th loop. If the query planner's estimates was accurate, + ** then this value will approximate the quotient NVISIT/NLOOP and the + ** product of this value for all prior loops with the same SELECTID will + ** be the NLOOP value for the current loop. + ** + ** [[SQLITE_SCANSTAT_NAME]]
SQLITE_SCANSTAT_NAME
+ **
^The "const char *" variable pointed to by the T parameter will be set to + ** a zero-terminated UTF-8 string containing the name of the index or table used + ** for the X-th loop. + ** + ** [[SQLITE_SCANSTAT_EXPLAIN]]
SQLITE_SCANSTAT_EXPLAIN
+ **
^The "const char *" variable pointed to by the T parameter will be set to + ** a zero-terminated UTF-8 string containing the [EXPLAIN QUERY PLAN] description + ** for the X-th loop. + ** + ** [[SQLITE_SCANSTAT_SELECTID]]
SQLITE_SCANSTAT_SELECT
+ **
^The "int" variable pointed to by the T parameter will be set to the + ** "select-id" for the X-th loop. The select-id identifies which query or + ** subquery the loop is part of. The main query has a select-id of zero. + ** The select-id is the same value as is output in the first column + ** of an [EXPLAIN QUERY PLAN] query. + **
+ */ + #define SQLITE_SCANSTAT_NLOOP 0 + #define SQLITE_SCANSTAT_NVISIT 1 + #define SQLITE_SCANSTAT_EST 2 + #define SQLITE_SCANSTAT_NAME 3 + #define SQLITE_SCANSTAT_EXPLAIN 4 + #define SQLITE_SCANSTAT_SELECTID 5 + + /* + ** CAPI3REF: Prepared Statement Scan Status + ** + ** Return status data for a single loop within query pStmt. + ** + ** The "iScanStatusOp" parameter determines which status information to return. + ** The "iScanStatusOp" must be one of the [scanstatus options] or the behavior of + ** this interface is undefined. + ** ^The requested measurement is written into a variable pointed to by + ** the "pOut" parameter. + ** Parameter "idx" identifies the specific loop to retrieve statistics for. + ** Loops are numbered starting from zero. ^If idx is out of range - less than + ** zero or greater than or equal to the total number of loops used to implement + ** the statement - a non-zero value is returned and the variable that pOut + ** points to is unchanged. + ** + ** ^Statistics might not be available for all loops in all statements. ^In cases + ** where there exist loops with no available statistics, this function behaves + ** as if the loop did not exist - it returns non-zero and leave the variable + ** that pOut points to unchanged. + ** + ** This API is only available if the library is built with pre-processor + ** symbol [SQLITE_ENABLE_STMT_SCANSTATUS] defined. + ** + ** See also: [sqlite3_stmt_scanstatus_reset()] + */ + SQLITE_EXPERIMENTAL int sqlite3_stmt_scanstatus( + sqlite3_stmt *pStmt, /* Prepared statement for which info desired */ + int idx, /* Index of loop to report on */ + int iScanStatusOp, /* Information desired. SQLITE_SCANSTAT_* */ + void *pOut /* Result written here */ + ); + + /* + ** CAPI3REF: Zero Scan-Status Counters + ** + ** ^Zero all [sqlite3_stmt_scanstatus()] related event counters. + ** + ** This API is only available if the library is built with pre-processor + ** symbol [SQLITE_ENABLE_STMT_SCANSTATUS] defined. + */ + SQLITE_EXPERIMENTAL void sqlite3_stmt_scanstatus_reset(sqlite3_stmt*); +/* +** CAPI3REF: The pre-update hook. +** EXPERIMENTAL +** +** ^These interfaces are only available if SQLite is compiled using the +** [SQLITE_ENABLE_UPDATE_HOOK] compile-time option. +** +** ^The [sqlite3_preupdate_hook()] interface registers a callback function +** that is invoked prior to each [INSERT], [UPDATE], and [DELETE] operation. +** ^At most one preupdate hook may be registered at a time on a single +** [database connection]; each call to [sqlite3_preupdate_hook()] overrides +** the previous setting. +** ^The preupdate hook is disabled by invoking [sqlite3_preupdate_hook()] +** with a NULL pointer as the second parameter. +** ^The third parameter to [sqlite3_preupdate_hook()] is passed through as +** the first parameter to callbacks. +** +** ^The preupdate hook only fires for changes to real tables; the preupdate +** hook is not invoked for changes to virtual tables. +** +** ^The second parameter to the preupdate callback is a pointer to +** the [database connection] that registered the preupdate hook. +** ^The third parameter to the preupdate callback is one of the constants +** [SQLITE_INSERT], [SQLITE_DELETE], or [SQLITE_UPDATE] to indentify the +** kind of update operation that is about to occur. +** ^(The fourth parameter to the preupdate callback is the name of the +** database within the database connection that is being modified. This +** will be "main" for the main database or "temp" for TEMP tables or +** the name given after the AS keyword in the [ATTACH] statement for attached +** databases.)^ +** ^The fifth parameter to the preupdate callback is the name of the +** table that is being modified. +** ^The sixth parameter to the preupdate callback is the initial [rowid] of the +** row being changes for SQLITE_UPDATE and SQLITE_DELETE changes and is +** undefined for SQLITE_INSERT changes. +** ^The seventh parameter to the preupdate callback is the final [rowid] of +** the row being changed for SQLITE_UPDATE and SQLITE_INSERT changes and is +** undefined for SQLITE_DELETE changes. +** +** The [sqlite3_preupdate_old()], [sqlite3_preupdate_new()], +** [sqlite3_preupdate_count()], and [sqlite3_preupdate_depth()] interfaces +** provide additional information about a preupdate event. These routines +** may only be called from within a preupdate callback. Invoking any of +** these routines from outside of a preupdate callback or with a +** [database connection] pointer that is different from the one supplied +** to the preupdate callback results in undefined and probably undesirable +** behavior. +** +** ^The [sqlite3_preupdate_count(D)] interface returns the number of columns +** in the row that is being inserted, updated, or deleted. +** +** ^The [sqlite3_preupdate_old(D,N,P)] interface writes into P a pointer to +** a [protected sqlite3_value] that contains the value of the Nth column of +** the table row before it is updated. The N parameter must be between 0 +** and one less than the number of columns or the behavior will be +** undefined. This must only be used within SQLITE_UPDATE and SQLITE_DELETE +** preupdate callbacks; if it is used by an SQLITE_INSERT callback then the +** behavior is undefined. The [sqlite3_value] that P points to +** will be destroyed when the preupdate callback returns. +** +** ^The [sqlite3_preupdate_new(D,N,P)] interface writes into P a pointer to +** a [protected sqlite3_value] that contains the value of the Nth column of +** the table row after it is updated. The N parameter must be between 0 +** and one less than the number of columns or the behavior will be +** undefined. This must only be used within SQLITE_INSERT and SQLITE_UPDATE +** preupdate callbacks; if it is used by an SQLITE_DELETE callback then the +** behavior is undefined. The [sqlite3_value] that P points to +** will be destroyed when the preupdate callback returns. +** +** ^The [sqlite3_preupdate_depth(D)] interface returns 0 if the preupdate +** callback was invoked as a result of a direct insert, update, or delete +** operation; or 1 for inserts, updates, or deletes invoked by top-level +** triggers; or 2 for changes resulting from triggers called by top-level +** triggers; and so forth. +** +** See also: [sqlite3_update_hook()] +*/ +SQLITE_EXPERIMENTAL void *sqlite3_preupdate_hook( + sqlite3 *db, + void(*xPreUpdate)( + void *pCtx, /* Copy of third arg to preupdate_hook() */ + sqlite3 *db, /* Database handle */ + int op, /* SQLITE_UPDATE, DELETE or INSERT */ + char const *zDb, /* Database name */ + char const *zName, /* Table name */ + sqlite3_int64 iKey1, /* Rowid of row about to be deleted/updated */ + sqlite3_int64 iKey2 /* New rowid value (for a rowid UPDATE) */ + ), + void* +); +SQLITE_EXPERIMENTAL int sqlite3_preupdate_old(sqlite3 *, int, sqlite3_value **); +SQLITE_EXPERIMENTAL int sqlite3_preupdate_count(sqlite3 *); +SQLITE_EXPERIMENTAL int sqlite3_preupdate_depth(sqlite3 *); +SQLITE_EXPERIMENTAL int sqlite3_preupdate_new(sqlite3 *, int, sqlite3_value **); + /* ** Undo the hack that converts floating point types to integer for ** builds on processors without floating point support. diff --cc src/vdbeapi.c index e0b204cd1b,5744c28632..c7c14d9c31 --- a/src/vdbeapi.c +++ b/src/vdbeapi.c @@@ -1476,183 -1476,71 +1476,252 @@@ int sqlite3_stmt_status(sqlite3_stmt *p return (int)v; } +#ifdef SQLITE_ENABLE_PREUPDATE_HOOK +/* +** Allocate and populate an UnpackedRecord structure based on the serialized +** record in nKey/pKey. Return a pointer to the new UnpackedRecord structure +** if successful, or a NULL pointer if an OOM error is encountered. +*/ +static UnpackedRecord *vdbeUnpackRecord( + KeyInfo *pKeyInfo, + int nKey, + const void *pKey +){ + char *dummy; /* Dummy argument for AllocUnpackedRecord() */ + UnpackedRecord *pRet; /* Return value */ + + pRet = sqlite3VdbeAllocUnpackedRecord(pKeyInfo, 0, 0, &dummy); + if( pRet ){ + memset(pRet->aMem, 0, sizeof(Mem)*(pKeyInfo->nField+1)); + sqlite3VdbeRecordUnpack(pKeyInfo, nKey, pKey, pRet); + } + return pRet; +} + +/* +** This function is called from within a pre-update callback to retrieve +** a field of the row currently being updated or deleted. +*/ +int sqlite3_preupdate_old(sqlite3 *db, int iIdx, sqlite3_value **ppValue){ + PreUpdate *p = db->pPreUpdate; + int rc = SQLITE_OK; + + /* Test that this call is being made from within an SQLITE_DELETE or + ** SQLITE_UPDATE pre-update callback, and that iIdx is within range. */ + if( !p || p->op==SQLITE_INSERT ){ + rc = SQLITE_MISUSE_BKPT; + goto preupdate_old_out; + } + if( iIdx>=p->pCsr->nField || iIdx<0 ){ + rc = SQLITE_RANGE; + goto preupdate_old_out; + } + + /* If the old.* record has not yet been loaded into memory, do so now. */ + if( p->pUnpacked==0 ){ + u32 nRec; + u8 *aRec; + + rc = sqlite3BtreeDataSize(p->pCsr->pCursor, &nRec); + if( rc!=SQLITE_OK ) goto preupdate_old_out; + aRec = sqlite3DbMallocRaw(db, nRec); + if( !aRec ) goto preupdate_old_out; + rc = sqlite3BtreeData(p->pCsr->pCursor, 0, nRec, aRec); + if( rc==SQLITE_OK ){ + p->pUnpacked = vdbeUnpackRecord(&p->keyinfo, nRec, aRec); + if( !p->pUnpacked ) rc = SQLITE_NOMEM; + } + if( rc!=SQLITE_OK ){ + sqlite3DbFree(db, aRec); + goto preupdate_old_out; + } + p->aRecord = aRec; + } + + if( iIdx>=p->pUnpacked->nField ){ + *ppValue = (sqlite3_value *)columnNullValue(); + }else{ + *ppValue = &p->pUnpacked->aMem[iIdx]; + if( iIdx==p->iPKey ){ + sqlite3VdbeMemSetInt64(*ppValue, p->iKey1); + } + } + + preupdate_old_out: + sqlite3Error(db, rc); + return sqlite3ApiExit(db, rc); +} +#endif /* SQLITE_ENABLE_PREUPDATE_HOOK */ + +#ifdef SQLITE_ENABLE_PREUPDATE_HOOK +/* +** This function is called from within a pre-update callback to retrieve +** the number of columns in the row being updated, deleted or inserted. +*/ +int sqlite3_preupdate_count(sqlite3 *db){ + PreUpdate *p = db->pPreUpdate; + return (p ? p->keyinfo.nField : 0); +} +#endif /* SQLITE_ENABLE_PREUPDATE_HOOK */ + +#ifdef SQLITE_ENABLE_PREUPDATE_HOOK +/* +** This function is designed to be called from within a pre-update callback +** only. It returns zero if the change that caused the callback was made +** immediately by a user SQL statement. Or, if the change was made by a +** trigger program, it returns the number of trigger programs currently +** on the stack (1 for a top-level trigger, 2 for a trigger fired by a +** top-level trigger etc.). +** +** For the purposes of the previous paragraph, a foreign key CASCADE, SET NULL +** or SET DEFAULT action is considered a trigger. +*/ +int sqlite3_preupdate_depth(sqlite3 *db){ + PreUpdate *p = db->pPreUpdate; + return (p ? p->v->nFrame : 0); +} +#endif /* SQLITE_ENABLE_PREUPDATE_HOOK */ + +#ifdef SQLITE_ENABLE_PREUPDATE_HOOK +/* +** This function is called from within a pre-update callback to retrieve +** a field of the row currently being updated or inserted. +*/ +int sqlite3_preupdate_new(sqlite3 *db, int iIdx, sqlite3_value **ppValue){ + PreUpdate *p = db->pPreUpdate; + int rc = SQLITE_OK; + Mem *pMem; + + if( !p || p->op==SQLITE_DELETE ){ + rc = SQLITE_MISUSE_BKPT; + goto preupdate_new_out; + } + if( iIdx>=p->pCsr->nField || iIdx<0 ){ + rc = SQLITE_RANGE; + goto preupdate_new_out; + } + + if( p->op==SQLITE_INSERT ){ + /* For an INSERT, memory cell p->iNewReg contains the serialized record + ** that is being inserted. Deserialize it. */ + UnpackedRecord *pUnpack = p->pNewUnpacked; + if( !pUnpack ){ + Mem *pData = &p->v->aMem[p->iNewReg]; + rc = sqlite3VdbeMemExpandBlob(pData); + if( rc!=SQLITE_OK ) goto preupdate_new_out; + pUnpack = vdbeUnpackRecord(&p->keyinfo, pData->n, pData->z); + if( !pUnpack ){ + rc = SQLITE_NOMEM; + goto preupdate_new_out; + } + p->pNewUnpacked = pUnpack; + } + if( iIdx>=pUnpack->nField ){ + pMem = (sqlite3_value *)columnNullValue(); + }else{ + pMem = &pUnpack->aMem[iIdx]; + if( iIdx==p->iPKey ){ + sqlite3VdbeMemSetInt64(pMem, p->iKey2); + } + } + }else{ + /* For an UPDATE, memory cell (p->iNewReg+1+iIdx) contains the required + ** value. Make a copy of the cell contents and return a pointer to it. + ** It is not safe to return a pointer to the memory cell itself as the + ** caller may modify the value text encoding. + */ + assert( p->op==SQLITE_UPDATE ); + if( !p->aNew ){ + p->aNew = (Mem *)sqlite3DbMallocZero(db, sizeof(Mem) * p->pCsr->nField); + if( !p->aNew ){ + rc = SQLITE_NOMEM; + goto preupdate_new_out; + } + } + assert( iIdx>=0 && iIdxpCsr->nField ); + pMem = &p->aNew[iIdx]; + if( pMem->flags==0 ){ + if( iIdx==p->iPKey ){ + sqlite3VdbeMemSetInt64(pMem, p->iKey2); + }else{ + rc = sqlite3VdbeMemCopy(pMem, &p->v->aMem[p->iNewReg+1+iIdx]); + if( rc!=SQLITE_OK ) goto preupdate_new_out; + } + } + } + *ppValue = pMem; + + preupdate_new_out: + sqlite3Error(db, rc); + return sqlite3ApiExit(db, rc); +} +#endif /* SQLITE_ENABLE_PREUPDATE_HOOK */ ++ + #ifdef SQLITE_ENABLE_STMT_SCANSTATUS + /* + ** Return status data for a single loop within query pStmt. + */ + int sqlite3_stmt_scanstatus( + sqlite3_stmt *pStmt, /* Prepared statement being queried */ + int idx, /* Index of loop to report on */ + int iScanStatusOp, /* Which metric to return */ + void *pOut /* OUT: Write the answer here */ + ){ + Vdbe *p = (Vdbe*)pStmt; + ScanStatus *pScan; + if( idx<0 || idx>=p->nScan ) return 1; + pScan = &p->aScan[idx]; + switch( iScanStatusOp ){ + case SQLITE_SCANSTAT_NLOOP: { + *(sqlite3_int64*)pOut = p->anExec[pScan->addrLoop]; + break; + } + case SQLITE_SCANSTAT_NVISIT: { + *(sqlite3_int64*)pOut = p->anExec[pScan->addrVisit]; + break; + } + case SQLITE_SCANSTAT_EST: { + double r = 1.0; + LogEst x = pScan->nEst; + while( x<100 ){ + x += 10; + r *= 0.5; + } + *(double*)pOut = r*sqlite3LogEstToInt(x); + break; + } + case SQLITE_SCANSTAT_NAME: { + *(const char**)pOut = pScan->zName; + break; + } + case SQLITE_SCANSTAT_EXPLAIN: { + if( pScan->addrExplain ){ + *(const char**)pOut = p->aOp[ pScan->addrExplain ].p4.z; + }else{ + *(const char**)pOut = 0; + } + break; + } + case SQLITE_SCANSTAT_SELECTID: { + if( pScan->addrExplain ){ + *(int*)pOut = p->aOp[ pScan->addrExplain ].p1; + }else{ + *(int*)pOut = -1; + } + break; + } + default: { + return 1; + } + } + return 0; + } + + /* + ** Zero all counters associated with the sqlite3_stmt_scanstatus() data. + */ + void sqlite3_stmt_scanstatus_reset(sqlite3_stmt *pStmt){ + Vdbe *p = (Vdbe*)pStmt; + memset(p->anExec, 0, p->nOp * sizeof(i64)); + } + #endif /* SQLITE_ENABLE_STMT_SCANSTATUS */