⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 attach.c

📁 sqlite-3.4.1,嵌入式数据库.是一个功能强大的开源数据库,给学习和研发以及小型公司的发展带来了全所未有的好处.
💻 C
字号:
/*** 2003 April 6**** The author disclaims copyright to this source code.  In place of** a legal notice, here is a blessing:****    May you do good and not evil.**    May you find forgiveness for yourself and forgive others.**    May you share freely, never taking more than you give.***************************************************************************** This file contains code used to implement the ATTACH and DETACH commands.**** $Id: attach.c,v 1.60 2007/05/09 20:31:30 drh Exp $*/#include "sqliteInt.h"#ifndef SQLITE_OMIT_ATTACH/*** 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.**** i.e. if the parser sees:****     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.*/static int resolveAttachExpr(NameContext *pName, Expr *pExpr){  int rc = SQLITE_OK;  if( pExpr ){    if( pExpr->op!=TK_ID ){      rc = sqlite3ExprResolveNames(pName, pExpr);      if( rc==SQLITE_OK && !sqlite3ExprIsConstant(pExpr) ){        sqlite3ErrorMsg(pName->pParse, "invalid name: \"%T\"", &pExpr->span);        return SQLITE_ERROR;      }    }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;  char zErr[128];  char *zErrDyn = 0;  zFile = (const char *)sqlite3_value_text(argv[0]);  zName = (const char *)sqlite3_value_text(argv[1]);  if( zFile==0 ) zFile = "";  if( zName==0 ) zName = "";  /* Check for the following errors:  **  **     * Too many attached databases,  **     * Transaction currently open  **     * Specified database name already being used.  */  if( db->nDb>=SQLITE_MAX_ATTACHED+2 ){    sqlite3_snprintf(      sizeof(zErr), zErr, "too many attached databases - max %d",       SQLITE_MAX_ATTACHED    );    goto attach_error;  }  if( !db->autoCommit ){    sqlite3_snprintf(sizeof(zErr), zErr,                     "cannot ATTACH database within transaction");    goto attach_error;  }  for(i=0; i<db->nDb; i++){    char *z = db->aDb[i].zName;    if( z && zName && sqlite3StrICmp(z, zName)==0 ){      sqlite3_snprintf(sizeof(zErr), 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 ){      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 ){      return;    }   }  db->aDb = aNew;  aNew = &db->aDb[db->nDb++];  memset(aNew, 0, sizeof(*aNew));  /* Open the database file. If the btree is successfully opened, use  ** it to obtain the database schema. At this point the schema may  ** or may not be initialised.  */  rc = sqlite3BtreeFactory(db, zFile, 0, SQLITE_DEFAULT_CACHE_SIZE, &aNew->pBt);  if( rc==SQLITE_OK ){    aNew->pSchema = sqlite3SchemaGet(aNew->pBt);    if( !aNew->pSchema ){      rc = SQLITE_NOMEM;    }else if( aNew->pSchema->file_format && aNew->pSchema->enc!=ENC(db) ){      sqlite3_snprintf(sizeof(zErr), zErr,         "attached databases must use the same text encoding as main database");      goto attach_error;    }    sqlite3PagerLockingMode(sqlite3BtreePager(aNew->pBt), db->dfltLockMode);  }  aNew->zName = sqliteStrDup(zName);  aNew->safety_level = 3;#if SQLITE_HAS_CODEC  {    extern int sqlite3CodecAttach(sqlite3*, int, const void*, int);    extern void sqlite3CodecGetKey(sqlite3*, int, void**, int*);    int nKey;    char *zKey;    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  /* 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 ){    sqlite3SafetyOn(db);    rc = sqlite3Init(db, &zErrDyn);    sqlite3SafetyOff(db);  }  if( rc ){    int iDb = db->nDb - 1;    assert( iDb>=2 );    if( db->aDb[iDb].pBt ){      sqlite3BtreeClose(db->aDb[iDb].pBt);      db->aDb[iDb].pBt = 0;      db->aDb[iDb].pSchema = 0;    }    sqlite3ResetInternalSchema(db, 0);    db->nDb = iDb;    if( rc==SQLITE_NOMEM ){      sqlite3FailedMalloc();      sqlite3_snprintf(sizeof(zErr),zErr, "out of memory");    }else{      sqlite3_snprintf(sizeof(zErr),zErr, "unable to open database: %s", zFile);    }    goto attach_error;  }    return;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);  }}/*** 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 x****     SELECT sqlite_detach(x)*/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;  Db *pDb = 0;  char zErr[128];  if( zName==0 ) 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 ){    sqlite3_snprintf(sizeof(zErr),zErr, "no such database: %s", zName);    goto detach_error;  }  if( i<2 ){    sqlite3_snprintf(sizeof(zErr),zErr, "cannot detach database %s", zName);    goto detach_error;  }  if( !db->autoCommit ){    sqlite3_snprintf(sizeof(zErr), zErr,                     "cannot DETACH database within transaction");    goto detach_error;  }  if( sqlite3BtreeIsInReadTrans(pDb->pBt) ){    sqlite3_snprintf(sizeof(zErr),zErr, "database %s is locked", zName);    goto detach_error;  }  sqlite3BtreeClose(pDb->pBt);  pDb->pBt = 0;  pDb->pSchema = 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  assert( sqlite3MallocFailed() || pAuthArg );  if( pAuthArg ){    char *zAuthArg = sqlite3NameFromToken(&pAuthArg->span);    if( !zAuthArg ){      goto attach_end;    }    rc = sqlite3AuthCheck(pParse, type, zAuthArg, 0, 0);    sqliteFree(zAuthArg);    if(rc!=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 || sqlite3MallocFailed() );  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);}/*** 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);}#endif /* SQLITE_OMIT_ATTACH *//*** Register the functions sqlite_attach and sqlite_detach.*/void sqlite3AttachFunctions(sqlite3 *db){#ifndef SQLITE_OMIT_ATTACH  static const int enc = SQLITE_UTF8;  sqlite3CreateFunc(db, "sqlite_attach", 3, enc, db, attachFunc, 0, 0);  sqlite3CreateFunc(db, "sqlite_detach", 1, enc, db, detachFunc, 0, 0);#endif}/*** Initialize a DbFixer structure.  This routine must be called prior** to passing the structure to one of the sqliteFixAAAA() routines below.**** The return value indicates whether or not fixation is required.  TRUE** means we do need to fix the database references, FALSE means we do not.*/int sqlite3FixInit(  DbFixer *pFix,      /* The fixer to be initialized */  Parse *pParse,      /* Error messages will be written here */  int iDb,            /* This is the database that must be used */  const char *zType,  /* "view", "trigger", or "index" */  const Token *pName  /* Name of the view, trigger, or index */){  sqlite3 *db;  if( iDb<0 || iDb==1 ) return 0;  db = pParse->db;  assert( db->nDb>iDb );  pFix->pParse = pParse;  pFix->zDb = db->aDb[iDb].zName;  pFix->zType = zType;  pFix->pName = pName;  return 1;}/*** The following set of routines walk through the parse tree and assign** a specific database to all table references where the database name** was left unspecified in the original SQL statement.  The pFix structure** must have been initialized by a prior call to sqlite3FixInit().**** These routines are used to make sure that an index, trigger, or** view in one database does not refer to objects in a different database.** (Exception: indices, triggers, and views in the TEMP database are** allowed to refer to anything.)  If a reference is explicitly made** to an object in a different database, an error message is added to** pParse->zErrMsg and these routines return non-zero.  If everything** checks out, these routines return 0.*/int sqlite3FixSrcList(  DbFixer *pFix,       /* Context of the fixation */  SrcList *pList       /* The Source list to check and modify */){  int i;  const char *zDb;  struct SrcList_item *pItem;  if( pList==0 ) return 0;  zDb = pFix->zDb;  for(i=0, pItem=pList->a; i<pList->nSrc; i++, pItem++){    if( pItem->zDatabase==0 ){      pItem->zDatabase = sqliteStrDup(zDb);    }else if( sqlite3StrICmp(pItem->zDatabase,zDb)!=0 ){      sqlite3ErrorMsg(pFix->pParse,         "%s %T cannot reference objects in database %s",         pFix->zType, pFix->pName, pItem->zDatabase);      return 1;    }#if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_TRIGGER)    if( sqlite3FixSelect(pFix, pItem->pSelect) ) return 1;    if( sqlite3FixExpr(pFix, pItem->pOn) ) return 1;#endif  }  return 0;}#if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_TRIGGER)int sqlite3FixSelect(  DbFixer *pFix,       /* Context of the fixation */  Select *pSelect      /* The SELECT statement to be fixed to one database */){  while( pSelect ){    if( sqlite3FixExprList(pFix, pSelect->pEList) ){      return 1;    }    if( sqlite3FixSrcList(pFix, pSelect->pSrc) ){      return 1;    }    if( sqlite3FixExpr(pFix, pSelect->pWhere) ){      return 1;    }    if( sqlite3FixExpr(pFix, pSelect->pHaving) ){      return 1;    }    pSelect = pSelect->pPrior;  }  return 0;}int sqlite3FixExpr(  DbFixer *pFix,     /* Context of the fixation */  Expr *pExpr        /* The expression to be fixed to one database */){  while( pExpr ){    if( sqlite3FixSelect(pFix, pExpr->pSelect) ){      return 1;    }    if( sqlite3FixExprList(pFix, pExpr->pList) ){      return 1;    }    if( sqlite3FixExpr(pFix, pExpr->pRight) ){      return 1;    }    pExpr = pExpr->pLeft;  }  return 0;}int sqlite3FixExprList(  DbFixer *pFix,     /* Context of the fixation */  ExprList *pList    /* The expression to be fixed to one database */){  int i;  struct ExprList_item *pItem;  if( pList==0 ) return 0;  for(i=0, pItem=pList->a; i<pList->nExpr; i++, pItem++){    if( sqlite3FixExpr(pFix, pItem->pExpr) ){      return 1;    }  }  return 0;}#endif#ifndef SQLITE_OMIT_TRIGGERint sqlite3FixTriggerStep(  DbFixer *pFix,     /* Context of the fixation */  TriggerStep *pStep /* The trigger step be fixed to one database */){  while( pStep ){    if( sqlite3FixSelect(pFix, pStep->pSelect) ){      return 1;    }    if( sqlite3FixExpr(pFix, pStep->pWhere) ){      return 1;    }    if( sqlite3FixExprList(pFix, pStep->pExprList) ){      return 1;    }    pStep = pStep->pNext;  }  return 0;}#endif

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -