From: danielk1977 Date: Tue, 6 Dec 2005 17:19:11 +0000 (+0000) Subject: Modify ATTACH and DETACH to execute at runtime instead of compile time. (CVS 2803) X-Git-Tag: version-3.6.10~3357 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=f744bb56a10ad6c0d7af953168ad8a8d47763299;p=thirdparty%2Fsqlite.git Modify ATTACH and DETACH to execute at runtime instead of compile time. (CVS 2803) FossilOrigin-Name: 5e04ec694add7a8331e3d6fbdfcaed51349ae7bc --- diff --git a/manifest b/manifest index 5f85343a91..49f3cec97d 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Report\serrors\sout\sof\ssqlite3_open16().\s(CVS\s2802) -D 2005-12-06T13:19:08 +C Modify\sATTACH\sand\sDETACH\sto\sexecute\sat\sruntime\sinstead\sof\scompile\stime.\s(CVS\s2803) +D 2005-12-06T17:19:11 F Makefile.in e3c6b3a38d734d41574c04f2fc90d18de2b87102 F Makefile.linux-gcc aee18d8a05546dcf1888bd4547e442008a49a092 F README 9c4e2d6706bdcc3efdd773ce752a8cdab4f90028 @@ -32,7 +32,7 @@ F sqlite3.def c413e514217736884254739a105c8c942fdf0c2f F sqlite3.pc.in 985b9bf34192a549d7d370e0f0b6b34a4f61369a F src/alter.c 7ed4b794c2e3a8ad8c1effe50202eaef42cedc23 F src/analyze.c ea42005eed52c382fcc7ef66969e7f1858597633 -F src/attach.c 8c3e09452be967e005a016299a110ed8ee33606a +F src/attach.c 39e678033d0be197dea4e9d5eeac2b2df51d6c9f F src/auth.c 31e2304bef67f44d635655f44234387ea7d21454 F src/btree.c aa88194f460becf8fff6196996d6e38f1b37286e F src/btree.h 1ed561263ca0e335bc3e81d761c9d5ff8c22f61e @@ -43,7 +43,7 @@ F src/date.c 8bc8d084a17d19c44d9cbf357b5f656db6706ce1 F src/delete.c 6010a081edda9871895260def092e852f0bb60a0 F src/experimental.c 50c1e3b34f752f4ac10c36f287db095c2b61766d F src/expr.c 540ed7eb44b79e5603c3656466bf1d7381abcfc7 -F src/func.c 7d81dccd9c440c6c4e761056333e629192814af0 +F src/func.c f5171a1bd0bc3eae91d37bd42724784570b203cd F src/hash.c 8747cf51d12de46512880dfcf1b68b4e24072863 F src/hash.h 1b0c445e1c89ff2aaad9b4605ba61375af001e84 F src/insert.c 5393479164f317ea0aeec954c6500cafa097ef33 @@ -61,7 +61,7 @@ F src/os_win.c d962ac2dd0e482847e42b846d46cd044f97d1c32 F src/os_win.h 41a946bea10f61c158ce8645e7646b29d44f122b F src/pager.c 893cb2106261a4f77d84c1fa0d10a083e889b23b F src/pager.h e7b41ce8e7b5f629d456708b7ad9a8c8ede37140 -F src/parse.y e4d57c2fd5cc02f19822ec41f6dc2bfc9bc85609 +F src/parse.y 87080d89439925de19f16189310d3dbc7f9ab3f6 F src/pragma.c 2793699ab0d73fa730fa8c1c7521e9436604f024 F src/prepare.c e93967011379051316728d316755f533a9bb438c F src/printf.c 3ea3a17d25d7ac498efc18007c70371a42c968f8 @@ -69,7 +69,7 @@ F src/random.c ff5e9a8cad790e2a51cd4d2e7737dc8540e09d1d F src/select.c 0e4d3627fec4a445b45f6cb471f68aab9c97a8b3 F src/shell.c 3596c1e559b82663057940d19ba533ad421c7dd3 F src/sqlite.h.in 8e648e1f386e4509f2f96c09ded7c07b0df0c9a2 -F src/sqliteInt.h 71dc0f753e9a646d9c9b3b6b5bb9d2fcc2dad9e5 +F src/sqliteInt.h bbc310a83a32476aa960055a166af4a0ef503a79 F src/table.c 486dcfce532685b53b5a2b5da8bba0ded6fb2316 F src/tclsqlite.c a497c3adfd2c85da6a934331ec0041e47884fbcb F src/test1.c 0b4bf8ab9afb37f34a83c46f81de54adf519b23d @@ -100,7 +100,7 @@ F test/alter3.test d4eecd8dbd008d0e66f1c201fa6dc2edca853c38 F test/altermalloc.test 6e1f404ec021eb2ba6582e3c77b0a35cf206b7af F test/analyze.test 2f55535aa335785db1a2f97d3f3831c16c09f8b0 F test/attach.test dae07fa1554b618b9cc4c7bc349b3bc1a532180e -F test/attach2.test 3396c012a39ddf7ba6b528d80bd79554168aa115 +F test/attach2.test 4c31484096fd24b7b98487f9c6d04d9f3f156c6c F test/attach3.test 63013383adc4380af69779f34f4af19bd49f7cbe F test/attachmalloc.test cdb26c42850f04698377ccec05f5fa89d987837c F test/auth.test 973ae7274eae32c4453fbbcbd0ec2b80c5b1eeb3 @@ -325,7 +325,7 @@ F www/tclsqlite.tcl ddcf912ea48695603c8ed7efb29f0812ef8d1b49 F www/vdbe.tcl 87a31ace769f20d3627a64fa1fade7fed47b90d0 F www/version3.tcl a99cf5f6d8bd4d5537584a2b342f0fb9fa601d8b F www/whentouse.tcl 97e2b5cd296f7d8057e11f44427dea8a4c2db513 -P 0b82f9623cf25b1cb02f70628c992903a6e8ca1c -R ad7e2ffd6d5056830e431f0a65615391 -U drh -Z 553bce70d7b9b8480be2fd03ddd473e4 +P f5b58163d4520fa3e7137e8445a8ef19aae3e799 +R 5b292cae1efee3561e8e46d85db25e2a +U danielk1977 +Z adc67f11e55d6ddbfd0953632e9d61d6 diff --git a/manifest.uuid b/manifest.uuid index bba8a407d3..382185bb8d 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -f5b58163d4520fa3e7137e8445a8ef19aae3e799 \ No newline at end of file +5e04ec694add7a8331e3d6fbdfcaed51349ae7bc \ No newline at end of file diff --git a/src/attach.c b/src/attach.c index d7d9ef824e..0088417502 100644 --- a/src/attach.c +++ b/src/attach.c @@ -11,84 +11,106 @@ ************************************************************************* ** This file contains code used to implement the ATTACH and DETACH commands. ** -** $Id: attach.c,v 1.35 2005/12/06 12:52:59 danielk1977 Exp $ +** $Id: attach.c,v 1.36 2005/12/06 17:19:11 danielk1977 Exp $ */ #include "sqliteInt.h" /* -** This routine is called by the parser to process an ATTACH statement: +** Resolve an expression that was part of an ATTACH or DETACH statement. This +** is slightly different from resolving a normal SQL expression, because simple +** identifiers are treated as strings, not possible column names or aliases. ** -** ATTACH DATABASE filename AS dbname +** i.e. if the parser sees: ** -** The pFilename and pDbname arguments are the tokens that define the -** filename and dbname in the ATTACH statement. +** ATTACH DATABASE abc AS def +** +** it treats the two expressions as literal strings 'abc' and 'def' instead of +** looking for columns of the same name. +** +** This only applies to the root node of pExpr, so the statement: +** +** ATTACH DATABASE abc||def AS 'db2' +** +** will fail because neither abc or def can be resolved. */ -void sqlite3Attach( - Parse *pParse, /* The parser context */ - Token *pFilename, /* Name of database file */ - Token *pDbname, /* Name of the database to use internally */ - int keyType, /* 0: no key. 1: TEXT, 2: BLOB */ - Token *pKey /* Text of the key for keytype 1 and 2 */ +int resolveAttachExpr(NameContext *pName, Expr *pExpr) +{ + int rc = SQLITE_OK; + if( pExpr ){ + if( pExpr->op!=TK_ID ){ + rc = sqlite3ExprResolveNames(pName, pExpr); + }else{ + pExpr->op = TK_STRING; + } + } + return rc; +} + +/* +** An SQL user-function registered to do the work of an ATTACH statement. The +** three arguments to the function come directly from an attach statement: +** +** ATTACH DATABASE x AS y KEY z +** +** SELECT sqlite_attach(x, y, z) +** +** If the optional "KEY z" syntax is omitted, an SQL NULL is passed as the +** third argument. +*/ +static void attachFunc( + sqlite3_context *context, + int argc, + sqlite3_value **argv ){ + int i; + int rc = 0; + sqlite3 *db = sqlite3_user_data(context); + const char *zName; + const char *zFile; Db *aNew; - int rc, i; - char *zFile = 0; - char *zName = 0; - sqlite3 *db; - Vdbe *v; + char zErr[128]; + char *zErrDyn = 0; - v = sqlite3GetVdbe(pParse); - if( !v ) return; - sqlite3VdbeAddOp(v, OP_Expire, 1, 0); - sqlite3VdbeAddOp(v, OP_Halt, 0, 0); - if( pParse->explain ) return; - db = pParse->db; + zFile = (const char *)sqlite3_value_text(argv[0]); + zName = (const char *)sqlite3_value_text(argv[1]); + + /* Check for the following errors: + ** + ** * Too many attached databases, + ** * Transaction currently open + ** * Specified database name already being used. + */ if( db->nDb>=MAX_ATTACHED+2 ){ - sqlite3ErrorMsg(pParse, "too many attached databases - max %d", - MAX_ATTACHED); - pParse->rc = SQLITE_ERROR; - return; + sqlite3_snprintf( + 127, zErr, "too many attached databases - max %d", MAX_ATTACHED + ); + goto attach_error; } - if( !db->autoCommit ){ - sqlite3ErrorMsg(pParse, "cannot ATTACH database within transaction"); - pParse->rc = SQLITE_ERROR; - return; - } - - zFile = sqlite3NameFromToken(pFilename); - if( zFile==0 ){ - goto attach_end; - } -#ifndef SQLITE_OMIT_AUTHORIZATION - if( sqlite3AuthCheck(pParse, SQLITE_ATTACH, zFile, 0, 0)!=SQLITE_OK ){ - goto attach_end; - } -#endif /* SQLITE_OMIT_AUTHORIZATION */ - - zName = sqlite3NameFromToken(pDbname); - if( zName==0 ){ - goto attach_end; + strcpy(zErr, "cannot ATTACH database within transaction"); + goto attach_error; } for(i=0; inDb; i++){ char *z = db->aDb[i].zName; if( z && sqlite3StrICmp(z, zName)==0 ){ - sqlite3ErrorMsg(pParse, "database %s is already in use", zName); - pParse->rc = SQLITE_ERROR; - goto attach_end; + sqlite3_snprintf(127, zErr, "database %s is already in use", zName); + goto attach_error; } } + /* Allocate the new entry in the db->aDb[] array and initialise the schema + ** hash tables. + */ if( db->aDb==db->aDbStatic ){ aNew = sqliteMalloc( sizeof(db->aDb[0])*3 ); if( aNew==0 ){ - goto attach_end; + return; } memcpy(aNew, db->aDb, sizeof(db->aDb[0])*2); }else{ aNew = sqliteRealloc(db->aDb, sizeof(db->aDb[0])*(db->nDb+1) ); if( aNew==0 ){ - goto attach_end; + return; } } db->aDb = aNew; @@ -98,45 +120,51 @@ void sqlite3Attach( sqlite3HashInit(&aNew->idxHash, SQLITE_HASH_STRING, 0); sqlite3HashInit(&aNew->trigHash, SQLITE_HASH_STRING, 0); sqlite3HashInit(&aNew->aFKey, SQLITE_HASH_STRING, 1); - aNew->zName = zName; - zName = 0; + aNew->zName = sqliteStrDup(zName); aNew->safety_level = 3; + + /* Open the database file */ rc = sqlite3BtreeFactory(db, zFile, 0, MAX_PAGES, &aNew->pBt); - if( rc ){ - sqlite3ErrorMsg(pParse, "unable to open database: %s", zFile); - } + #if SQLITE_HAS_CODEC { extern int sqlite3CodecAttach(sqlite3*, int, void*, int); - char *zKey; - int nKey; - if( keyType==0 ){ - /* No key specified. Use the key from the main database */ - extern void sqlite3CodecGetKey(sqlite3*, int, void**, int*); - sqlite3CodecGetKey(db, 0, (void**)&zKey, &nKey); - }else if( keyType==1 ){ - /* Key specified as text */ - zKey = sqlite3NameFromToken(pKey); - nKey = strlen(zKey); - }else{ - /* Key specified as a BLOB */ - char *zTemp; - assert( keyType==2 ); - pKey->z++; - pKey->n--; - zTemp = sqlite3NameFromToken(pKey); - zKey = sqlite3HexToBlob(zTemp); - sqliteFree(zTemp); - } - sqlite3CodecAttach(db, db->nDb-1, zKey, nKey); - if( keyType ){ - sqliteFree(zKey); + extern void sqlite3CodecGetKey(sqlite3*, int, void**, int*); + + int t = sqlite3_value_type(argv[2]); + switch( t ){ + case SQLITE_INTEGER: + case SQLITE_FLOAT: + zErrDyn = sqliteStrDup("Invalid key value"); + rc = SQLITE_ERROR; + break; + + case SQLITE_TEXT: + case SQLITE_BLOB: + nKey = sqlite3_value_bytes(argv[2]); + zKey = (char *)sqlite3_value_blob(argv[2]); + sqlite3CodecAttach(db, db->nDb-1, zKey, nKey); + break; + + case SQLITE_NULL: + /* No key specified. Use the key from the main database */ + sqlite3CodecGetKey(db, 0, (void**)&zKey, &nKey); + sqlite3CodecAttach(db, db->nDb-1, zKey, nKey); + break; } } #endif - db->flags &= ~SQLITE_Initialized; - if( pParse->nErr==0 && rc==SQLITE_OK ){ - rc = sqlite3ReadSchema(pParse); + + /* If the file was opened successfully, read the schema for the new database. + ** If this fails, or if opening the file failed, then close the file and + ** remove the entry from the db->aDb[] array. i.e. put everything back the way + ** we found it. + */ + if( rc==SQLITE_OK ){ + db->flags &= ~SQLITE_Initialized; + sqlite3SafetyOn(db); + rc = sqlite3Init(db, &zErrDyn); + sqlite3SafetyOff(db); } if( rc ){ int i = db->nDb - 1; @@ -146,67 +174,162 @@ void sqlite3Attach( db->aDb[i].pBt = 0; } sqlite3ResetInternalSchema(db, 0); - assert( pParse->nErr>0 ); /* Always set by sqlite3ReadSchema() */ - if( pParse->rc==SQLITE_OK ){ - pParse->rc = SQLITE_ERROR; - } db->nDb = i; + sqlite3_snprintf(127, zErr, "unable to open database: %s", zFile); + goto attach_error; } + + return; -attach_end: - sqliteFree(zFile); - sqliteFree(zName); +attach_error: + /* Return an error if we get here */ + if( zErrDyn ){ + sqlite3_result_error(context, zErrDyn, -1); + sqliteFree(zErrDyn); + }else{ + zErr[sizeof(zErr)-1] = 0; + sqlite3_result_error(context, zErr, -1); + } } /* -** This routine is called by the parser to process a DETACH statement: +** An SQL user-function registered to do the work of an DETACH statement. The +** three arguments to the function come directly from a detach statement: ** -** DETACH DATABASE dbname +** DETACH DATABASE x ** -** The pDbname argument is the name of the database in the DETACH statement. +** SELECT sqlite_detach(x) */ -void sqlite3Detach(Parse *pParse, Token *pDbname){ +static void detachFunc( + sqlite3_context *context, + int argc, + sqlite3_value **argv +){ + const char *zName = (const char *)sqlite3_value_text(argv[0]); + sqlite3 *db = sqlite3_user_data(context); int i; - sqlite3 *db; - Vdbe *v; Db *pDb = 0; - char *zName; + char zErr[128]; - v = sqlite3GetVdbe(pParse); - if( !v ) return; - sqlite3VdbeAddOp(v, OP_Expire, 0, 0); - sqlite3VdbeAddOp(v, OP_Halt, 0, 0); - if( pParse->explain ) return; - db = pParse->db; - zName = sqlite3NameFromToken(pDbname); - if( zName==0 ) return; + assert(zName); for(i=0; inDb; i++){ pDb = &db->aDb[i]; if( pDb->pBt==0 ) continue; if( sqlite3StrICmp(pDb->zName, zName)==0 ) break; } + if( i>=db->nDb ){ - sqlite3ErrorMsg(pParse, "no such database: %z", zName); - return; + sqlite3_snprintf(sizeof(zErr), zErr, "no such database: %s", zName); + goto detach_error; } if( i<2 ){ - sqlite3ErrorMsg(pParse, "cannot detach database %z", zName); - return; + sqlite3_snprintf(sizeof(zErr), zErr, "cannot detach database %s", zName); + goto detach_error; } - sqliteFree(zName); if( !db->autoCommit ){ - sqlite3ErrorMsg(pParse, "cannot DETACH database within transaction"); - pParse->rc = SQLITE_ERROR; - return; - } -#ifndef SQLITE_OMIT_AUTHORIZATION - if( sqlite3AuthCheck(pParse,SQLITE_DETACH,db->aDb[i].zName,0,0)!=SQLITE_OK ){ - return; + strcpy(zErr, "cannot DETACH database within transaction"); + goto detach_error; } -#endif /* SQLITE_OMIT_AUTHORIZATION */ + sqlite3BtreeClose(pDb->pBt); pDb->pBt = 0; sqlite3ResetInternalSchema(db, 0); + return; + +detach_error: + sqlite3_result_error(context, zErr, -1); +} + +/* +** This procedure generates VDBE code for a single invocation of either the +** sqlite_detach() or sqlite_attach() SQL user functions. +*/ +static void codeAttach( + Parse *pParse, /* The parser context */ + int type, /* Either SQLITE_ATTACH or SQLITE_DETACH */ + const char *zFunc, /* Either "sqlite_attach" or "sqlite_detach */ + int nFunc, /* Number of args to pass to zFunc */ + Expr *pAuthArg, /* Expression to pass to authorization callback */ + Expr *pFilename, /* Name of database file */ + Expr *pDbname, /* Name of the database to use internally */ + Expr *pKey /* Database key for encryption extension */ +){ + int rc; + NameContext sName; + Vdbe *v; + FuncDef *pFunc; + sqlite3* db = pParse->db; + +#ifndef SQLITE_OMIT_AUTHORIZATION + char *zAuthArg = sqlite3NameFromToken(&pAuthArg->span); + if( !zAuthArg ){ + goto attach_end; + } + if( sqlite3AuthCheck(pParse, type, zAuthArg, 0, 0)!=SQLITE_OK ){ + goto attach_end; + } +#endif /* SQLITE_OMIT_AUTHORIZATION */ + + memset(&sName, 0, sizeof(NameContext)); + sName.pParse = pParse; + + if( + SQLITE_OK!=(rc = resolveAttachExpr(&sName, pFilename)) || + SQLITE_OK!=(rc = resolveAttachExpr(&sName, pDbname)) || + SQLITE_OK!=(rc = resolveAttachExpr(&sName, pKey)) + ){ + pParse->nErr++; + goto attach_end; + } + + v = sqlite3GetVdbe(pParse); + sqlite3ExprCode(pParse, pFilename); + sqlite3ExprCode(pParse, pDbname); + sqlite3ExprCode(pParse, pKey); + + assert(v || sqlite3Tsd()->mallocFailed); + if( v ){ + sqlite3VdbeAddOp(v, OP_Function, 0, nFunc); + pFunc = sqlite3FindFunction(db, zFunc, strlen(zFunc), nFunc, SQLITE_UTF8,0); + sqlite3VdbeChangeP3(v, -1, (char *)pFunc, P3_FUNCDEF); + + /* Code an OP_Expire. For an ATTACH statement, set P1 to true (expire this + ** statement only). For DETACH, set it to false (expire all existing + ** statements). + */ + sqlite3VdbeAddOp(v, OP_Expire, (type==SQLITE_ATTACH), 0); + } + +attach_end: + sqlite3ExprDelete(pFilename); + sqlite3ExprDelete(pDbname); + sqlite3ExprDelete(pKey); + sqliteFree(zAuthArg); +} + +/* +** Called by the parser to compile a DETACH statement. +** +** DETACH pDbname +*/ +void sqlite3Detach(Parse *pParse, Expr *pDbname){ + codeAttach(pParse, SQLITE_DETACH, "sqlite_detach", 1, pDbname, 0, 0, pDbname); +} + +/* +** Called by the parser to compile an ATTACH statement. +** +** ATTACH p AS pDbname KEY pKey +*/ +void sqlite3Attach(Parse *pParse, Expr *p, Expr *pDbname, Expr *pKey){ + codeAttach(pParse, SQLITE_ATTACH, "sqlite_attach", 3, p, p, pDbname, pKey); +} + +void sqlite3AttachFunctions(sqlite3 *db) +{ + static const int enc = SQLITE_UTF8; + sqlite3_create_function(db, "sqlite_attach", 3, enc, db, attachFunc, 0, 0); + sqlite3_create_function(db, "sqlite_detach", 1, enc, db, detachFunc, 0, 0); } /* diff --git a/src/func.c b/src/func.c index 6071aa452b..c0038904be 100644 --- a/src/func.c +++ b/src/func.c @@ -16,7 +16,7 @@ ** sqliteRegisterBuildinFunctions() found at the bottom of the file. ** All other code has file scope. ** -** $Id: func.c,v 1.111 2005/10/13 02:09:50 drh Exp $ +** $Id: func.c,v 1.112 2005/12/06 17:19:11 danielk1977 Exp $ */ #include "sqliteInt.h" #include @@ -1025,6 +1025,7 @@ void sqlite3RegisterBuiltinFunctions(sqlite3 *db){ #ifndef SQLITE_OMIT_ALTERTABLE sqlite3AlterFunctions(db); #endif + sqlite3AttachFunctions(db); for(i=0; i