Modify ATTACH and DETACH to execute at runtime instead of compile time. (CVS 2803)
FossilOrigin-Name: 5e04ec694add7a8331e3d6fbdfcaed51349ae7bc
This commit is contained in:
parent
7dd90a4785
commit
f744bb56a1
22
manifest
22
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
|
||||
|
@ -1 +1 @@
|
||||
f5b58163d4520fa3e7137e8445a8ef19aae3e799
|
||||
5e04ec694add7a8331e3d6fbdfcaed51349ae7bc
|
355
src/attach.c
355
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; 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;
|
||||
@ -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; 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;
|
||||
strcpy(zErr, "cannot DETACH database within transaction");
|
||||
goto detach_error;
|
||||
}
|
||||
#ifndef SQLITE_OMIT_AUTHORIZATION
|
||||
if( sqlite3AuthCheck(pParse,SQLITE_DETACH,db->aDb[i].zName,0,0)!=SQLITE_OK ){
|
||||
return;
|
||||
}
|
||||
#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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -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 <ctype.h>
|
||||
@ -1025,6 +1025,7 @@ void sqlite3RegisterBuiltinFunctions(sqlite3 *db){
|
||||
#ifndef SQLITE_OMIT_ALTERTABLE
|
||||
sqlite3AlterFunctions(db);
|
||||
#endif
|
||||
sqlite3AttachFunctions(db);
|
||||
for(i=0; i<sizeof(aAggs)/sizeof(aAggs[0]); i++){
|
||||
void *pArg = 0;
|
||||
switch( aAggs[i].argType ){
|
||||
|
17
src/parse.y
17
src/parse.y
@ -14,7 +14,7 @@
|
||||
** the parser. Lemon will also generate a header file containing
|
||||
** numeric codes for all of the tokens.
|
||||
**
|
||||
** @(#) $Id: parse.y,v 1.185 2005/11/24 22:22:30 drh Exp $
|
||||
** @(#) $Id: parse.y,v 1.186 2005/12/06 17:19:11 danielk1977 Exp $
|
||||
*/
|
||||
|
||||
// All token codes are small integers with #defines that begin with "TK_"
|
||||
@ -990,20 +990,19 @@ cmd ::= DROP TRIGGER fullname(X). {
|
||||
%endif // !SQLITE_OMIT_TRIGGER
|
||||
|
||||
//////////////////////// ATTACH DATABASE file AS name /////////////////////////
|
||||
cmd ::= ATTACH database_kw_opt ids(F) AS nm(D) key_opt(K). {
|
||||
sqlite3Attach(pParse, &F, &D, K.type, &K.key);
|
||||
cmd ::= ATTACH database_kw_opt expr(F) AS expr(D) key_opt(K). {
|
||||
sqlite3Attach(pParse, F, D, K);
|
||||
}
|
||||
%type key_opt {struct AttachKey}
|
||||
key_opt(A) ::= . { A.type = 0; }
|
||||
key_opt(A) ::= KEY ids(X). { A.type=1; A.key = X; }
|
||||
key_opt(A) ::= KEY BLOB(X). { A.type=2; A.key = X; }
|
||||
%type key_opt {Expr *}
|
||||
key_opt(A) ::= . { A = 0; }
|
||||
key_opt(A) ::= KEY expr(X). { A = X; }
|
||||
|
||||
database_kw_opt ::= DATABASE.
|
||||
database_kw_opt ::= .
|
||||
|
||||
//////////////////////// DETACH DATABASE name /////////////////////////////////
|
||||
cmd ::= DETACH database_kw_opt nm(D). {
|
||||
sqlite3Detach(pParse, &D);
|
||||
cmd ::= DETACH database_kw_opt expr(D). {
|
||||
sqlite3Detach(pParse, D);
|
||||
}
|
||||
|
||||
////////////////////////// REINDEX collation //////////////////////////////////
|
||||
|
@ -11,7 +11,7 @@
|
||||
*************************************************************************
|
||||
** Internal interface definitions for SQLite.
|
||||
**
|
||||
** @(#) $Id: sqliteInt.h,v 1.429 2005/12/06 12:52:59 danielk1977 Exp $
|
||||
** @(#) $Id: sqliteInt.h,v 1.430 2005/12/06 17:19:11 danielk1977 Exp $
|
||||
*/
|
||||
#ifndef _SQLITEINT_H_
|
||||
#define _SQLITEINT_H_
|
||||
@ -1603,8 +1603,8 @@ void sqlite3DeferForeignKey(Parse*, int);
|
||||
# define sqlite3AuthContextPush(a,b,c)
|
||||
# define sqlite3AuthContextPop(a) ((void)(a))
|
||||
#endif
|
||||
void sqlite3Attach(Parse*, Token*, Token*, int, Token*);
|
||||
void sqlite3Detach(Parse*, Token*);
|
||||
void sqlite3Attach(Parse*, Expr*, Expr*, Expr*);
|
||||
void sqlite3Detach(Parse*, Expr*);
|
||||
int sqlite3BtreeFactory(const sqlite3 *db, const char *zFilename,
|
||||
int omitJournal, int nCache, Btree **ppBtree);
|
||||
int sqlite3FixInit(DbFixer*, Parse*, int, const char*, const Token*);
|
||||
@ -1678,6 +1678,7 @@ void sqlite3RegisterLikeFunctions(sqlite3*, int);
|
||||
int sqlite3IsLikeFunction(sqlite3*,Expr*,int*,char*);
|
||||
SqliteTsd *sqlite3Tsd();
|
||||
void sqlite3ClearMallocFailed();
|
||||
void sqlite3AttachFunctions(sqlite3 *);
|
||||
|
||||
#ifdef SQLITE_SSE
|
||||
#include "sseInt.h"
|
||||
|
@ -12,7 +12,7 @@
|
||||
# focus of this script is testing the ATTACH and DETACH commands
|
||||
# and related functionality.
|
||||
#
|
||||
# $Id: attach2.test,v 1.32 2005/03/29 03:11:00 danielk1977 Exp $
|
||||
# $Id: attach2.test,v 1.33 2005/12/06 17:19:12 danielk1977 Exp $
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
@ -134,12 +134,14 @@ do_test attach2-3.1 {
|
||||
set DB [sqlite3 db test.db]
|
||||
set rc [catch {sqlite3_prepare $DB "ATTACH 'test2.db' AS t2" -1 TAIL} VM]
|
||||
if {$rc} {lappend rc $VM}
|
||||
sqlite3_step $VM
|
||||
sqlite3_finalize $VM
|
||||
set rc
|
||||
} {0}
|
||||
do_test attach2-3.2 {
|
||||
set rc [catch {sqlite3_prepare $DB "DETACH t2" -1 TAIL} VM]
|
||||
if {$rc} {lappend rc $VM}
|
||||
sqlite3_step $VM
|
||||
sqlite3_finalize $VM
|
||||
set rc
|
||||
} {0}
|
||||
|
Loading…
Reference in New Issue
Block a user