-C Fix\san\sSQLITE_MISUSE\smemory\serror\sin\scheck-ini\s[1f940357f7bb160b].
-D 2026-05-01T17:42:15.461
+C This\sis\san\sidea\sfor\snew\sAPIs\sto\simprove\supon\ssqlite3_set_authorizer().\nAfter\simplementing\sit,\showever,\sI'm\shaving\ssecond\sthroughts.\s\sSaved\shere\nin\scase\sI\schanged\smy\smind.
+D 2026-05-01T23:56:16.497
F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F src/alter.c 7d7ddbdc189f0e0c686e32ee170abdddc95c11f2089e40df4ffcee88f5334826
F src/analyze.c 03bcfc083fc0cccaa9ded93604e1d4244ea245c17285d463ef6a60425fcb247d
F src/attach.c c58278c7d2d954785591c4fde81669ec3e4d52f348c453b028a19ae8adf4f338
-F src/auth.c ebec42df26b34a62b6750d30d9c2c03554a1c522020182476f7729a439fef04f
+F src/auth.c ca42996e133f1b7b50447b54291db6a5e2b7c8fbca015235c101dcc97e719917
F src/backup.c 6ebe22ccbedfcb92423833992130e8d65824be4e6599c3a03f540ab38fc7d13c
F src/bitvec.c e242d4496774dfc88fa278177dd23b607dce369ccafb3f61b41638eea2c9b399
F src/btmutex.c 30dada73a819a1ef5b7583786370dce1842e12e1ad941e4d05ac29695528daea
F src/json.c 5027b856cd9b621dc9ba66b211e21a440ccdc63cefdefb44c51e7d3ac550d1a4
F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa
F src/loadext.c 78d5b06f18996ffa1203129b28fea043f63a87a4117539678f1d761c30b4ff65
-F src/main.c 6180079f53ccdd784df2eddc3751f49ea7153c5959bee792b19ad9f4bdbcf437
+F src/main.c 022f66a3adb94db148c4ad9ff4357a4d4586fea0c0546f8b91f745bd54a3529c
F src/malloc.c 422f7e0498e1c9ef967f06283b6f2c0b16db6b905d8e06f6dbc8baaa3e4e6c5a
F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645
F src/mem1.c 3bb59158c38e05f6270e761a9f435bf19827a264c13d1631c58b84bdc96d73b2
F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97
F src/select.c 4c05cde130f26991b7411d8c6809e0630625e18078742c963a047b4b9cc01d49
F src/shell.c.in 3ed0902de2a82c92ee535c1bc627fb7cfd64cc3b81762f1c10b59c634597da10
-F src/sqlite.h.in 39d2e09114d2bdb7afd998f4a469c8f8cd065f8093835a7d0422f260fc78fb4f
+F src/sqlite.h.in fa079952ae30eb0921e3b5eb11e4c73741d7dee86d202b2fa7e4c688d6f9b0cf
F src/sqlite3.rc 015537e6ac1eec6c7050e17b616c2ffe6f70fca241835a84a4f0d5937383c479
F src/sqlite3ext.h 9788c301f95370fa30e808861f1d2e6f022a816ddbe2a4f67486784c1b31db2e
F src/sqliteInt.h 5071d64ad787d9922e524d834c3fe7b7a11989d29dc6ea25f7944892a43266f4
F tool/warnings.sh a554d13f6e5cf3760f041b87939e3d616ec6961859c3245e8ef701d1eafc2ca2
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
F tool/winmain.c 00c8fb88e365c9017db14c73d3c78af62194d9644feaf60e220ab0f411f3604c
-P 9190f1b9b8889e4d80c370db36b916744d5779de0ec51da0369bbe74adb7b931
-R daf617341525cb93b193d2a453da3d1c
+P fc64113e8f9aee0161e682db7c244a7849f2db86551a557b276deb1785941476
+R 5c538d610a39b9c009307badcddf6e4d
+T *branch * push-pop-authorizer
+T *sym-push-pop-authorizer *
+T -sym-trunk *
U drh
-Z 80e6eac2e96d40aca876dc4a525f888d
+Z a0686f1cab4b032dd5f99fc3c9d92233
# Remove this line to create a well-formed Fossil manifest.
}
}
+/*
+** There is a single instance of this object for each nested
+** authorizer.
+*/
+typedef struct NestedAuth {
+ int (*xPriorAuth)(void*,int,const char*,const char*,const char*,const char*);
+ void *pPriorArg;
+ int (*xThisAuth)(void*,int,const char*,const char*,const char*,const char*);
+ void *pThisArg;
+ int bOvrd;
+} NestedAuth;
+
+/*
+** This is an authorizer implementation that invokes two separate
+** authorizers and returns the most restrictive answer.
+**
+** The pArg value is a pointer to an sqlite3_auth object that contains
+** the function pointer and augument for two other (real) authorizers.
+** This routine runs both of the nested authorizers. If either returns
+** SQLITE_DENY, this routine returns SQLITE_DENY. If neither returns
+** SQLITE_DENY but either of them return SQLITE_IGNORE, then this
+** routine returns SQLITE_IGNORE. Only if both authorizers return
+** SQLITE_OK does this routine return SQLITE_OK.
+*/
+static int sqlite3NestedAuthorizer(
+ void *pArg, /* Must be an sqlite3_auth object */
+ int eOp, /* Operation to be authorized */
+ const char *z1, /* First argument */
+ const char *z2, /* Second argument */
+ const char *z3, /* Third argument */
+ const char *z4 /* Fourth argument */
+){
+ NestedAuth *p = (NestedAuth*)pArg;
+ int rc1, rc2;
+ if( p->xThisAuth==0 ){
+ rc1 = SQLITE_OK;
+ /* If the top-most authorizer is a NULL, then do no perform
+ ** any other authorizations. */
+ }else{
+ rc1 = p->xThisAuth(p->pThisArg,eOp,z1,z2,z3,z4);
+ if( rc1==SQLITE_DENY || p->bOvrd ) return rc1;
+ }
+ assert( p->xPriorAuth!=0 );
+ rc2 = p->xPriorAuth(p->pPriorArg,eOp,z1,z2,z3,z4);
+ if( rc2 ) return rc2;
+ return rc1;
+}
+
+/*
+** Push a new authorizer function xAuth with pArg. This new authorizer
+** runs first before any prior authorizer.
+*/
+int sqlite3_push_authorizer(
+ sqlite3 *db,
+ int (*xAuth)(void*,int,const char*,const char*,const char*,const char*),
+ void *pArg,
+ int bOvrd
+){
+ NestedAuth *p;
+ if( db==0 ) return SQLITE_MISUSE;
+ if( db->xAuth==0 ){
+ return sqlite3_set_authorizer(db, xAuth, pArg);
+ }
+ p = sqlite3DbMallocZero(db, sizeof(NestedAuth));
+ if( p==0 ) return SQLITE_NOMEM;
+ p->xPriorAuth = db->xAuth;
+ p->pPriorArg = db->pAuthArg;
+ p->xThisAuth = xAuth;
+ p->pThisArg = pArg;
+ p->bOvrd = bOvrd;
+ return sqlite3_set_authorizer(db, sqlite3NestedAuthorizer, p);
+}
+
+/*
+** Pop the top-most authorizer off of the authorizer stack.
+*/
+int sqlite3_pop_authorizer(sqlite3 *db){
+ if( db==0 ) return SQLITE_MISUSE;
+ if( db->xAuth==sqlite3NestedAuthorizer ){
+ NestedAuth *p = (NestedAuth*)db->pAuthArg;
+ (void)sqlite3_set_authorizer(db, p->xPriorAuth, p->pPriorArg);
+ sqlite3DbFree(db, p);
+ }else{
+ (void)sqlite3_set_authorizer(db, 0, 0);
+ }
+ return SQLITE_OK;
+}
+
#endif /* SQLITE_OMIT_AUTHORIZATION */
** METHOD: sqlite3
** KEYWORDS: {authorizer callback}
**
-** ^This routine registers an authorizer callback with a particular
-** [database connection], supplied in the first argument.
-** ^The authorizer callback is invoked as SQL statements are being compiled
-** by [sqlite3_prepare()] or its variants [sqlite3_prepare_v2()],
-** [sqlite3_prepare_v3()], [sqlite3_prepare16()], [sqlite3_prepare16_v2()],
-** and [sqlite3_prepare16_v3()]. ^At various
+** The sqlite3_set_authorizer(D,X,P) and sqlite3_push_authorizer(D,X,P,F)
+** routines registers an authorizer callback X with a particular
+** [database connection] D. The sqlite3_push_authorizer() interface
+** is preferred.
+**
+** The authorizer callback is invoked multiple times as SQL statements
+** are being compiled by [sqlite3_prepare()] or its variants
+** [sqlite3_prepare_v2()], [sqlite3_prepare_v3()], [sqlite3_prepare16()],
+** [sqlite3_prepare16_v2()], and [sqlite3_prepare16_v3()]. At various
** points during the compilation process, as logic is being created
** to perform various actions, the authorizer callback is invoked to
-** see if those actions are allowed. ^The authorizer callback should
+** see if those actions are allowed. The authorizer callback should
** return [SQLITE_OK] to allow the action, [SQLITE_IGNORE] to disallow the
** specific action but allow the SQL statement to continue to be
** compiled, or [SQLITE_DENY] to cause the entire SQL statement to be
-** rejected with an error. ^If the authorizer callback returns
+** rejected with an error. If the authorizer callback returns
** any value other than [SQLITE_IGNORE], [SQLITE_OK], or [SQLITE_DENY]
** then the [sqlite3_prepare_v2()] or equivalent call that triggered
** the authorizer will fail with an error message.
**
-** When the callback returns [SQLITE_OK], that means the operation
-** requested is ok. ^When the callback returns [SQLITE_DENY], the
-** [sqlite3_prepare_v2()] or equivalent call that triggered the
-** authorizer will fail with an error message explaining that
-** access is denied.
-**
-** ^The first parameter to the authorizer callback is a copy of the third
-** parameter to the sqlite3_set_authorizer() interface. ^The second parameter
+** The first parameter to the authorizer callback is a copy of the third
+** parameter (P) to the sqlite3_set_authorizer(D,X,P) or
+** sqlite3_push_authorizer(D,X,P,F) interface that created the
+** authorizer. The second parameter
** to the callback is an integer [SQLITE_COPY | action code] that specifies
-** the particular action to be authorized. ^The third through sixth parameters
+** the particular action to be authorized. The third through sixth parameters
** to the callback are either NULL pointers or zero-terminated strings
** that contain additional details about the action to be authorized.
** Applications must always be prepared to encounter a NULL pointer in any
** of the third through the sixth parameters of the authorization callback.
**
-** ^If the action code is [SQLITE_READ]
+** If the action code is [SQLITE_READ]
** and the callback returns [SQLITE_IGNORE] then the
** [prepared statement] statement is constructed to substitute
** a NULL value in place of the table column that would have
** been read if [SQLITE_OK] had been returned. The [SQLITE_IGNORE]
** return can be used to deny an untrusted user access to individual
** columns of a table.
-** ^When a table is referenced by a [SELECT] but no column values are
+** When a table is referenced by a [SELECT] but no column values are
** extracted from that table (for example in a query like
** "SELECT count(*) FROM tab") then the [SQLITE_READ] authorizer callback
** is invoked once for that table with a column name that is an empty string.
-** ^If the action code is [SQLITE_DELETE] and the callback returns
+** If the action code is [SQLITE_DELETE] and the callback returns
** [SQLITE_IGNORE] then the [DELETE] operation proceeds but the
** [truncate optimization] is disabled and all rows are deleted individually.
**
-** An authorizer is used when [sqlite3_prepare | preparing]
-** SQL statements from an untrusted source, to ensure that the SQL statements
-** do not try to access data they are not allowed to see, or that they do not
-** try to execute malicious statements that damage the database. For
-** example, an application may allow a user to enter arbitrary
+** The authorizer mechanism is designed for use when
+** [sqlite3_prepare | preparing] SQL statements from an untrusted source,
+** to ensure that the SQL statements do not access data they are
+** not allowed to see, or change data that they are not allowed to
+** modify. For example, an application may allow a user to enter arbitrary
** SQL queries for evaluation by a database. But the application does
** not want the user to be able to make arbitrary changes to the
** database. An authorizer could then be put in place while the
** and limiting database size using the [max_page_count] [PRAGMA]
** in addition to using an authorizer.
**
-** ^(Only a single authorizer can be in place on a database connection
-** at a time. Each call to sqlite3_set_authorizer overrides the
-** previous call.)^ ^Disable the authorizer by installing a NULL callback.
-** The authorizer is disabled by default.
-**
-** The authorizer callback must not do anything that will modify
-** the database connection that invoked the authorizer callback.
-** Note that [sqlite3_prepare_v2()] and [sqlite3_step()] both modify their
-** database connections for the meaning of "modify" in this paragraph.
-**
-** ^When [sqlite3_prepare_v2()] is used to prepare a statement, the
+** The sqlite3_set_authorizer(D,X,P) interface causes X to be the only
+** authorizer that runs. Any previously registered authorizers are
+** discarded. Older versions of SQLite (prior to version 3.54.0)
+** only support sqlite3_set_authorizer().
+**
+** The sqlite3_push_authorizer(D,X,P,F) interface pushes a new authorizer
+** onto a stack of authorizers. The sqlite3_pop_authorizer(D) pops that
+** authorizer back off the stack. If the F flag is 1, then the pushed
+** authorizer callback is the only one that is considered - previous
+** authorizers are retained so that they can be restored after the
+** next sqlite3_pop_authorizer(), but they are not used while the new
+** authorizer is still on the stack. If the F flag to
+** sqlite3_push_authorizer(D,X,P,F) is zero, that means that
+** the new authorizer runs first, but preexisting authorizers are also
+** run and the result is the most restrictive result returned by
+** any authorizer. SQLITE_DENY is more restrictive than SQLITE_IGNORE,
+** and SQLITE_IGNORE is more restrictive than SQLITE_OK.
+**
+** A call to sqlite3_push_authorizer() will return SQLITE_NOMEM if it
+** is unable to allocate memory to hold prior authorizers.
+**
+** Use sqlite3_push_authorizer(D,0,0,1) to disable all authorizers on
+** database connection D temporarily, but remember prior authorizers
+** so that they are restored at the next sqlite3_pop_authorizer(D).
+**
+** It is illegal to invoke sqlite3_set_authorizer() if there are pending
+** (unpopped) calls to sqlite3_push_authorizer(). Doing so can lead
+** to memory leaks. The use of sqlite3_set_authorizer() is discouraged.
+** New applications should use only sqlite3_push_authorizer() and
+** sqlite3_pop_authorizer().
+**
+** <h2>Important Details</h2>
+**
+** When [sqlite3_prepare_v2()] is used to prepare a statement, the
** statement might be re-prepared during [sqlite3_step()] due to a
** schema change. Hence, the application should ensure that the
** correct authorizer callback remains in place during the [sqlite3_step()].
**
-** ^Note that the authorizer callback is invoked only during
+** Note that the authorizer callback is invoked only during
** [sqlite3_prepare()] or its variants. Authorization is not
** performed during statement evaluation in [sqlite3_step()], unless
** as stated in the previous paragraph, sqlite3_step() invokes
int (*xAuth)(void*,int,const char*,const char*,const char*,const char*),
void *pUserData
);
+int sqlite3_push_authorizer(
+ sqlite3*,
+ int (*xAuth)(void*,int,const char*,const char*,const char*,const char*),
+ void *pUserData,
+ int bOvrd
+);
+int sqlite3_pop_authorizer(sqlite3*);
/*
** CAPI3REF: Authorizer Return Codes