-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
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
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
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
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
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
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
*************************************************************************
** 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; i<db->nDb; 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;
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;
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; i<db->nDb; 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);
}
/*