From 9fd4cbde74cbec8b08e4474a8f2939b14fb66556 Mon Sep 17 00:00:00 2001 From: drh <> Date: Tue, 18 Aug 2026 09:17:48 +0000 Subject: [PATCH] Add SQLITE_LIMIT_TRIGGER_STEPS and -DSQLITE_MAX_TRIGGER_STEPS=n. Default maximum is 65000. FossilOrigin-Name: 6fbed57b1447725ae9d923d69b509fd5932d8c6bae6cf6ac0d1248a943752a58 --- manifest | 32 ++++++++++++++++---------------- manifest.uuid | 2 +- src/main.c | 7 ++++++- src/shell.c.in | 1 + src/sqlite.h.in | 5 +++++ src/sqliteInt.h | 2 +- src/sqliteLimit.h | 8 ++++++++ src/test1.c | 3 ++- src/test_config.c | 1 + src/trigger.c | 7 ++++++- test/sqllimits1.test | 4 ++++ test/trigger1.test | 25 +++++++++++++++++++++++++ 12 files changed, 76 insertions(+), 21 deletions(-) diff --git a/manifest b/manifest index 80fba07822..8d69662eb9 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Plaform\sportability\simprovements\sfor\sautosetup/find_tclconfig.tcl,\sfrom\sDan\sShearer\sat\sforum\spost\s[bugs:778223d22549a1a1|2026-08-13T20:55:18Z]. -D 2026-08-18T07:51:01.702 +C Add\sSQLITE_LIMIT_TRIGGER_STEPS\sand\s-DSQLITE_MAX_TRIGGER_STEPS=n.\s\sDefault\smaximum\nis\s65000. +D 2026-08-18T09:17:48.729 F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea @@ -707,7 +707,7 @@ F src/insert.c 64c22744bce3b1e9630288043c1967a60b4f02c8fc7707fdb10d13c1ec30d297 F src/json.c 3f05a035d3a747a549e75bb419afcad7137e3241a5a836d20a62335d9e90a35d F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 5cd4cd7ec6e1a46416806899086363fbf95eecd3bfb9a388b436c9a77243c902 -F src/main.c 2af1793f14ae547168e11682720aefc360d3fec75bc1dbabd9565ca09b02c8ab +F src/main.c fb8399bd4731dd01392628fdd452d16abd00216681af58aab34265018441219a F src/malloc.c 422f7e0498e1c9ef967f06283b6f2c0b16db6b905d8e06f6dbc8baaa3e4e6c5a F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645 F src/mem1.c 3bb59158c38e05f6270e761a9f435bf19827a264c13d1631c58b84bdc96d73b2 @@ -744,17 +744,17 @@ F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c F src/resolve.c 7145e41ad75e04a811b326aa5faf83c2e071480fabd9fc825055a6b889e6cb86 F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97 F src/select.c 7720e5e4ffcdf630691a2208e284a5b4eb507f51a06a380487b10ef5f6a9c8dc -F src/shell.c.in 49689bdd9228e3ff3a199c3e0a214c3601dd8448537bc6790e5b2ac2a594b6d5 -F src/sqlite.h.in 2101a2b494cbc282d0dc1c3f631c4fb83060c52876cc9d7615b091ad66d932b1 +F src/shell.c.in 7ba51656c54cb0d3ecaa70c35e20ae67c25778431d1d9355d5f6486b54c86c12 +F src/sqlite.h.in efc3f7aa00a2bb01c087435e6fa5f86103e310e6c3ea072b12cc110cf2715a75 F src/sqlite3.rc 015537e6ac1eec6c7050e17b616c2ffe6f70fca241835a84a4f0d5937383c479 F src/sqlite3ext.h 0efd4723bad9124ea1f581d9f1ea0254ac1c6f3e5fb29e4f3dcf36c72485a456 -F src/sqliteInt.h ddeeebaff5d345d5d338f823f492ac99c2918dd69616070de77fd65f1a8b67d8 -F src/sqliteLimit.h 7617f2ee673dab382046beba4552283f442cf1a181bb169ac38eb908135dd8da +F src/sqliteInt.h 95a3e8fcf076b1dd54699718c98b2157c4ed9b36020faa0dfef75b698e6d64d2 +F src/sqliteLimit.h c433b3fdb5e52f4226bf78d7e8081dff8fd8fbb0ce0cb1a5fee282aaa8976813 F src/status.c 7565d63a79aa2f326339a24a0461a60096d0bd2bce711fefb50b5c89335f3592 F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1 F src/tclsqlite.c ed8f7cd15cf56e05a31270d002598f349c1c17f5a21b9f8b55688a519f633ec6 F src/tclsqlite.h 614b3780a62522bc9f8f2b9fb22689e8009958e7aa77e572d0f3149050af348a -F src/test1.c bd9e5123ef2136c960a16c09a1abfbcb6c1498e5193deb2acee188260a31a473 +F src/test1.c 83f56d24b5de0965af7b245dc0199690389772e3813f8bca2c88f11e524982fe F src/test2.c a61ed01d9264a1db74ab42fbc981fc66165f85d5e3947e92be61a73e79ec4f2d F src/test3.c c959fe0c3b04157483f5a6fa1950c1f222e062d920fee0a8e70410c6b6d45522 F src/test4.c 0ac87fc13cdb334ab3a71823f99b6c32a6bebe5d603cd6a71d84c823d43a25a0 @@ -767,7 +767,7 @@ F src/test_backup.c a2bfd90d2ff2511b8635507bdb30fa9b605ade19c16b533066cae3077f5b F src/test_bestindex.c d75fad21369d80910238032bcf8d9ca1f2bffda13c1ceec63bfbb7f704448b15 F src/test_blob.c 3a946319c1b062a58d249c67be099c3bc1ec52f2e6ba23b1867699f0b8189ac2 F src/test_btree.c 28283787d32b8fa953eb77412ad0de2c9895260e4e5bd5a94b3c7411664f90d5 -F src/test_config.c ac3e4e5e3f5d23a3b604f64b98920a18411f9aa38d58ccd1f5be21103237b2e5 +F src/test_config.c 45b83ce7981fd01c58e85b67955bc6a344da5abfb4db1c3f2ba6dcc7adc85d83 F src/test_delete.c d0e8f6dc55cfc98a7c27c057fb88d512260564bf0b611482656c68b8f7f401ed F src/test_demovfs.c 3efa2adf4f21e10d95521721687d5ca047aea91fa62dd8cc22ac9e5a9c942383 F src/test_devsym.c 649434ed34d0b03fbd5a6b42df80f0f9a7e53f94dd1710aad5dd8831e91c4e86 @@ -804,7 +804,7 @@ F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9 F src/threads.c 85d8b9f05f78211c61e3739ab5db761d7118766d1916ae7f2764735106bc4e13 F src/tokenize.c e9d52d9f7374d82dadcd11726bea8a597d43709d6672704f3f0375bf1d726912 F src/treeview.c feaa59f14db4f7b5aacca9c5ad5aeb562c1f98262c1ffd74371f4186ade91fc5 -F src/trigger.c 74bc99aaa36dc820481960d4cde3cd7f060964525deebd527eee55ac3b6a2895 +F src/trigger.c 0ddd0ae0ed85498deb721720303d3931969c3d6d28dadd0f0c33fd20bc3cdbf2 F src/update.c 3e5e7ff66fa19ebe4d1b113d480639a24cc1175adbefabbd1a948a07f28e37cf F src/upsert.c dd9f0fcccbfb4f20e1026a21a7254ba3f2c08e9cfa92affaff5b5ec3b00ea549 F src/utf.c 7267c3fb9e2467020507601af3354c2446c61f444387e094c779dccd5ca62165 @@ -1692,7 +1692,7 @@ F test/spellfix2.test dfc8f519a3fc204cb2dfa8b4f29821ae90f6f8c3 F test/spellfix3.test 0f9efaaa502a0e0a09848028518a6fb096c8ad33 F test/spellfix4.test 7c56a09de68b95051ef0627fe235e514efbba2aa816dc2a9c5c670d98877e632 F test/sqldiff1.test 1b7ab4f312442c5cc6b3a5f299fa8ca051416d1dd173cb1126fd51bf64f2c3fb -F test/sqllimits1.test aa8b02192c0ce71bd64ad2e475f5584739119143f11283ce6764339636fdc9f5 +F test/sqllimits1.test c4545743e6e374d1c2b61f8c23546cbd7cce994c729b53e426a05f7688ac2e51 F test/sqllog.test 6af6cb0b09f4e44e1917e06ce85be7670302517a F test/starschema1.test f5388cd32527ab18d3f98f9e3402ec780f6a186e04e0d9c8531d7568ee734e11 F test/startup.c 1beb5ca66fcc0fce95c3444db9d1674f90fc605499a574ae2434dcfc10d22805 @@ -1906,7 +1906,7 @@ F test/trans.test 45f6f9ab6f66a7b5744f1caac06b558f95da62501916906cf55586a896f9f4 F test/trans2.test 62bd045bfc7a1c14c5ba83ba64d21ade31583f76 F test/trans3.test 91a100e5412b488e22a655fe423a14c26403ab94 F test/transitive1.test d6252d06ddc104d13372fce0da93af501546bf5d75cb4d2a1f83b8c27238997a -F test/trigger1.test 141bd2bbfa82fa91aed7391f50517373c9823ff8f55af35e56313dbc75112ca1 +F test/trigger1.test e8c2d09342c26ab99e2e2dbef9a0f3a789e43492608637b1614b1f6cc04ce9fc F test/trigger2.test 30fcb3a6aa6782020d47968735ee6086ed795f73a7affa9406c8d5a36e7b5265 F test/trigger3.test aa640bb2bbb03edd5ff69c055117ea088f121945 F test/trigger4.test 74700b76ebf3947b2f7a92405141eb2cf2a5d359 @@ -2219,8 +2219,8 @@ F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee F tool/warnings.sh a554d13f6e5cf3760f041b87939e3d616ec6961859c3245e8ef701d1eafc2ca2 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f F tool/winmain.c 00c8fb88e365c9017db14c73d3c78af62194d9644feaf60e220ab0f411f3604c -P e53c5fd75f3e0b7642acb4ca1cd4ef8155f256986bd6f112f372360a434cd4bf -R 0140e2988d8041ec5fb49a24826c2ee5 -U stephan -Z e045cd4b15eacb90149093e2d74f5645 +P 2df4fe72f74afb650e23c3643e61ec5bf8a3f1d163af9adbc4ce8ba4c1f895ec +R 83901b437f2bcf333fd9e8706583327f +U drh +Z 59f61ab32d908d034d9fef2583905ec5 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 75caa4e4e7..05ca087465 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -2df4fe72f74afb650e23c3643e61ec5bf8a3f1d163af9adbc4ce8ba4c1f895ec +6fbed57b1447725ae9d923d69b509fd5932d8c6bae6cf6ac0d1248a943752a58 diff --git a/src/main.c b/src/main.c index 8d7b47e8f9..e45356b47c 100644 --- a/src/main.c +++ b/src/main.c @@ -2993,6 +2993,7 @@ static const int aHardLimit[] = { SQLITE_MAX_WORKER_THREADS, SQLITE_MAX_PARSER_DEPTH, SQLITE_MAX_SCHEMA, + SQLITE_MAX_TRIGGER_STEPS, }; /* @@ -3034,6 +3035,9 @@ static const int aHardLimit[] = { #if SQLITE_MAX_WORKER_THREADS<0 || SQLITE_MAX_WORKER_THREADS>50 # error SQLITE_MAX_WORKER_THREADS must be between 0 and 50 #endif +#if SQLITE_MAX_TRIGGER_STEPS<0 || SQLITE_MAX_TRIGGER_STEPS>65535 +# error SQLITE_MAX_TRIGGER_STEPS must be between 0 and 65535 +#endif /* @@ -3076,7 +3080,8 @@ int sqlite3_limit(sqlite3 *db, int limitId, int newLimit){ assert( aHardLimit[SQLITE_LIMIT_TRIGGER_DEPTH]==SQLITE_MAX_TRIGGER_DEPTH ); assert( aHardLimit[SQLITE_LIMIT_WORKER_THREADS]==SQLITE_MAX_WORKER_THREADS ); assert( aHardLimit[SQLITE_LIMIT_SCHEMA]==SQLITE_MAX_SCHEMA ); - assert( SQLITE_LIMIT_SCHEMA==(SQLITE_N_LIMIT-1) ); + assert( aHardLimit[SQLITE_LIMIT_TRIGGER_STEPS]==SQLITE_MAX_TRIGGER_STEPS ); + assert( SQLITE_LIMIT_TRIGGER_STEPS==(SQLITE_N_LIMIT-1) ); if( limitId<0 || limitId>=SQLITE_N_LIMIT ){ diff --git a/src/shell.c.in b/src/shell.c.in index 6760ebe24a..209055fe3f 100644 --- a/src/shell.c.in +++ b/src/shell.c.in @@ -10901,6 +10901,7 @@ static int do_meta_command(const char *zLine, ShellState *p){ { "trigger_depth", SQLITE_LIMIT_TRIGGER_DEPTH }, { "worker_threads", SQLITE_LIMIT_WORKER_THREADS }, { "schema", SQLITE_LIMIT_SCHEMA }, + { "trigger_steps", SQLITE_LIMIT_TRIGGER_STEPS }, }; int i, n2; open_db(p, 0); diff --git a/src/sqlite.h.in b/src/sqlite.h.in index 047a5b4015..001ba6512e 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -4436,6 +4436,10 @@ int sqlite3_limit(sqlite3*, int id, int newVal); ** [[SQLITE_LIMIT_SCHEMA]] ^(
SQLITE_LIMIT_SCHEMA
**
The maximum number of objects (tables, indexes, triggers, and views) ** defined by the database schema.
)^ +** +** [[SQLITE_LIMIT_TRIGGER_STEPS]] ^(
SQLITE_LIMIT_TRIGGER_STEPS
+**
The maximum number of SQL statements that can be contained within +** a single trigger.
)^ ** */ #define SQLITE_LIMIT_LENGTH 0 @@ -4452,6 +4456,7 @@ int sqlite3_limit(sqlite3*, int id, int newVal); #define SQLITE_LIMIT_WORKER_THREADS 11 #define SQLITE_LIMIT_PARSER_DEPTH 12 #define SQLITE_LIMIT_SCHEMA 13 +#define SQLITE_LIMIT_TRIGGER_STEPS 14 /* ** CAPI3REF: Prepare Flags diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 6571ea5b49..1b09c76862 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -1561,7 +1561,7 @@ struct Schema { ** The number of different kinds of things that can be limited ** using the sqlite3_limit() interface. */ -#define SQLITE_N_LIMIT (SQLITE_LIMIT_SCHEMA+1) +#define SQLITE_N_LIMIT (SQLITE_LIMIT_TRIGGER_STEPS+1) /* ** Lookaside malloc is a set of fixed-size buffers that can be used diff --git a/src/sqliteLimit.h b/src/sqliteLimit.h index 5d6b1561c0..ff67a3df1e 100644 --- a/src/sqliteLimit.h +++ b/src/sqliteLimit.h @@ -294,3 +294,11 @@ #ifndef SQLITE_MAX_SCHEMA # define SQLITE_MAX_SCHEMA 10000000 #endif + +/* +** Maximum number of SQL statements allowed in the body of a single +** trigger. +*/ +#ifndef SQLITE_MAX_TRIGGER_STEPS +# define SQLITE_MAX_TRIGGER_STEPS 65000 +#endif diff --git a/src/test1.c b/src/test1.c index 779cea4342..00092729e6 100644 --- a/src/test1.c +++ b/src/test1.c @@ -7477,10 +7477,11 @@ static int SQLITE_TCLAPI test_limit( { "SQLITE_LIMIT_TRIGGER_DEPTH", SQLITE_LIMIT_TRIGGER_DEPTH }, { "SQLITE_LIMIT_WORKER_THREADS", SQLITE_LIMIT_WORKER_THREADS }, { "SQLITE_LIMIT_SCHEMA", SQLITE_LIMIT_SCHEMA }, + { "SQLITE_LIMIT_TRIGGER_STEPS", SQLITE_LIMIT_TRIGGER_STEPS }, /* Out of range test cases */ { "SQLITE_LIMIT_TOOSMALL", -1, }, - { "SQLITE_LIMIT_TOOBIG", SQLITE_LIMIT_SCHEMA+1 }, + { "SQLITE_LIMIT_TOOBIG", SQLITE_LIMIT_TRIGGER_STEPS+1 }, }; int i, id = 0; int val; diff --git a/src/test_config.c b/src/test_config.c index 6eb4b7e97f..7338622ad8 100644 --- a/src/test_config.c +++ b/src/test_config.c @@ -828,6 +828,7 @@ Tcl_SetVar2(interp, "sqlite_options", "mergesort", "1", TCL_GLOBAL_ONLY); LINKVAR( MAX_DEFAULT_PAGE_SIZE ); LINKVAR( MAX_WORKER_THREADS ); LINKVAR( MAX_SCHEMA ); + LINKVAR( MAX_TRIGGER_STEPS ); { static const int cv_TEMP_STORE = SQLITE_TEMP_STORE; diff --git a/src/trigger.c b/src/trigger.c index 0e96f3ebc0..461c9cefc1 100644 --- a/src/trigger.c +++ b/src/trigger.c @@ -332,6 +332,7 @@ void sqlite3FinishTrigger( DbFixer sFix; /* Fixer object */ int iDb; /* Database containing the trigger */ Token nameToken; /* Trigger name for error reporting */ + u64 n; /* Number of steps */ pParse->pNewTrigger = 0; if( NEVER(pParse->nErr) || !pTrig ) goto triggerfinish_cleanup; @@ -339,10 +340,14 @@ void sqlite3FinishTrigger( iDb = sqlite3SchemaToIndex(pParse->db, pTrig->pSchema); assert( iDb>=00 && iDbnDb ); pTrig->step_list = pStepList; - while( pStepList ){ + for(n=0; pStepList; n++){ pStepList->pTrig = pTrig; pStepList = pStepList->pNext; } + if( n>pParse->db->aLimit[SQLITE_LIMIT_TRIGGER_STEPS] ){ + sqlite3ErrorMsg(pParse, "trigger \"%w\" contains too many steps", zName); + goto triggerfinish_cleanup; + } sqlite3TokenInit(&nameToken, pTrig->zName); sqlite3FixInit(&sFix, pParse, iDb, "trigger", &nameToken); if( sqlite3FixTriggerStep(&sFix, pTrig->step_list) diff --git a/test/sqllimits1.test b/test/sqllimits1.test index aee87a4f53..f4fc582cfd 100644 --- a/test/sqllimits1.test +++ b/test/sqllimits1.test @@ -66,6 +66,10 @@ do_test sqllimits1-1.13 { sqlite3_limit db SQLITE_LIMIT_SCHEMA 999999999 sqlite3_limit db SQLITE_LIMIT_SCHEMA -1 } $SQLITE_MAX_SCHEMA +do_test sqllimits1-1.14 { + sqlite3_limit db SQLITE_LIMIT_TRIGGER_STEPS 999999999 + sqlite3_limit db SQLITE_LIMIT_TRIGGER_STEPS -1 +} $SQLITE_MAX_TRIGGER_STEPS # Limit parameters out of range. # diff --git a/test/trigger1.test b/test/trigger1.test index 67943677f9..43ec73d4e5 100644 --- a/test/trigger1.test +++ b/test/trigger1.test @@ -851,4 +851,29 @@ do_catchsql_test trigger1-24.2 { INSERT INTO t1 VALUES(9876); } {1 {attempt to insert 9876 where is not a power of 2}} +# 2026-08-18 Limit to the number of statements in a trigger. +# +reset_db +do_execsql_test trigger1-25.1 { + CREATE TABLE t1(x); + CREATE TABLE t2(y); +} {} +sqlite3_limit db SQLITE_LIMIT_TRIGGER_STEPS 3 +do_execsql_test trigger1-25.2 { + CREATE TRIGGER r1 AFTER INSERT ON t1 BEGIN + SELECT * FROM t2; + SELECT * FROM t2; + SELECT * FROM t2; + END; +} {} +do_catchsql_test trigger1-25.3 { + CREATE TRIGGER r2 AFTER UPDATE ON t1 BEGIN + SELECT * FROM t2; + SELECT * FROM t2; + SELECT * FROM t2; + SELECT * FROM t2; + END; +} {1 {trigger "r2" contains too many steps}} + + finish_test