Sorry, the attachment got scrubbed I think. Here it is again:
diff -U 3 -H -d -r -N -- sqlite-3.6.17/src/attach.c sqlite-3.6.17-mod/src/attach.c --- sqlite-3.6.17/src/attach.c 2009-11-03 10:47:25.000000000 +0100 +++ sqlite-3.6.17-mod/src/attach.c 2009-11-03 10:47:25.000000000 +0100 @@ -55,12 +55,14 @@ ** 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 +** ATTACH [READONLY] DATABASE x AS y KEY z ** -** SELECT sqlite_attach(x, y, z) +** SELECT sqlite_attach(readonly, x, y, z) +** +** "readonly" is 0 or 1. ** ** If the optional "KEY z" syntax is omitted, an SQL NULL is passed as the -** third argument. +** third argument. Note: "z" must be last for detachFunc to work correctly. */ static void attachFunc( sqlite3_context *context, @@ -72,13 +74,17 @@ sqlite3 *db = sqlite3_context_db_handle(context); const char *zName; const char *zFile; + int fReadOnly; + int fflags; Db *aNew; char *zErrDyn = 0; UNUSED_PARAMETER(NotUsed); - zFile = (const char *)sqlite3_value_text(argv[0]); - zName = (const char *)sqlite3_value_text(argv[1]); + fReadOnly = sqlite3_value_int(argv[0]); + zFile = (const char *)sqlite3_value_text(argv[1]); + zName = (const char *)sqlite3_value_text(argv[2]); + if( zFile==0 ) zFile = ""; if( zName==0 ) zName = ""; @@ -126,8 +132,12 @@ ** it to obtain the database schema. At this point the schema may ** or may not be initialised. */ + + fflags = db->openFlags; + if (fReadOnly) fflags = (fflags & ~(SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE)) | SQLITE_OPEN_READONLY; + rc = sqlite3BtreeFactory(db, zFile, 0, SQLITE_DEFAULT_CACHE_SIZE, - db->openFlags | SQLITE_OPEN_MAIN_DB, + fflags | SQLITE_OPEN_MAIN_DB, &aNew->pBt); db->nDb++; if( rc==SQLITE_CONSTRAINT ){ @@ -156,7 +166,7 @@ extern void sqlite3CodecGetKey(sqlite3*, int, void**, int*); int nKey; char *zKey; - int t = sqlite3_value_type(argv[2]); + int t = sqlite3_value_type(argv[3]); switch( t ){ case SQLITE_INTEGER: case SQLITE_FLOAT: @@ -166,8 +176,8 @@ case SQLITE_TEXT: case SQLITE_BLOB: - nKey = sqlite3_value_bytes(argv[2]); - zKey = (char *)sqlite3_value_blob(argv[2]); + nKey = sqlite3_value_bytes(argv[3]); + zKey = (char *)sqlite3_value_blob(argv[3]); sqlite3CodecAttach(db, db->nDb-1, zKey, nKey); break; @@ -290,7 +300,8 @@ 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 */ + Expr *pKey, /* Database key for encryption extension */ + int readonly /* attach readonly flag */ ){ int rc; NameContext sName; @@ -325,14 +336,15 @@ v = sqlite3GetVdbe(pParse); - regArgs = sqlite3GetTempRange(pParse, 4); - sqlite3ExprCode(pParse, pFilename, regArgs); - sqlite3ExprCode(pParse, pDbname, regArgs+1); - sqlite3ExprCode(pParse, pKey, regArgs+2); + regArgs = sqlite3GetTempRange(pParse, 5); + sqlite3ExprCode(pParse, pFilename, regArgs+1); + sqlite3ExprCode(pParse, pDbname, regArgs+2); + sqlite3ExprCode(pParse, pKey, regArgs+3); assert( v || db->mallocFailed ); if( v ){ - sqlite3VdbeAddOp3(v, OP_Function, 0, regArgs+3-pFunc->nArg, regArgs+3); + sqlite3VdbeAddOp2(v, OP_Integer, readonly, regArgs); + sqlite3VdbeAddOp3(v, OP_Function, 0, regArgs+4-pFunc->nArg, regArgs+4); assert( pFunc->nArg==-1 || (pFunc->nArg&0xff)==pFunc->nArg ); sqlite3VdbeChangeP5(v, (u8)(pFunc->nArg)); sqlite3VdbeChangeP4(v, -1, (char *)pFunc, P4_FUNCDEF); @@ -368,7 +380,7 @@ "sqlite_detach", /* zName */ 0 /* pHash */ }; - codeAttach(pParse, SQLITE_DETACH, &detach_func, pDbname, 0, 0, pDbname); + codeAttach(pParse, SQLITE_DETACH, &detach_func, pDbname, 0, 0, pDbname, 0); } /* @@ -376,9 +388,9 @@ ** ** ATTACH p AS pDbname KEY pKey */ -void sqlite3Attach(Parse *pParse, Expr *p, Expr *pDbname, Expr *pKey){ +void sqlite3Attach(Parse *pParse, Expr *p, Expr *pDbname, Expr *pKey, int readonly){ static FuncDef attach_func = { - 3, /* nArg */ + 4, /* nArg */ SQLITE_UTF8, /* iPrefEnc */ 0, /* flags */ 0, /* pUserData */ @@ -389,7 +401,7 @@ "sqlite_attach", /* zName */ 0 /* pHash */ }; - codeAttach(pParse, SQLITE_ATTACH, &attach_func, p, p, pDbname, pKey); + codeAttach(pParse, SQLITE_ATTACH, &attach_func, p, p, pDbname, pKey, readonly); } #endif /* SQLITE_OMIT_ATTACH */ diff -U 3 -H -d -r -N -- sqlite-3.6.17/src/parse.y sqlite-3.6.17-mod/src/parse.y --- sqlite-3.6.17/src/parse.y 2009-11-03 10:47:25.000000000 +0100 +++ sqlite-3.6.17-mod/src/parse.y 2009-11-03 10:47:25.000000000 +0100 @@ -1266,13 +1266,17 @@ //////////////////////// ATTACH DATABASE file AS name ///////////////////////// %ifndef SQLITE_OMIT_ATTACH -cmd ::= ATTACH database_kw_opt expr(F) AS expr(D) key_opt(K). { - sqlite3Attach(pParse, F.pExpr, D.pExpr, K); +cmd ::= ATTACH readonly_opt(R) database_kw_opt expr(F) AS expr(D) key_opt(K). { + sqlite3Attach(pParse, F.pExpr, D.pExpr, K, R); } cmd ::= DETACH database_kw_opt expr(D). { sqlite3Detach(pParse, D.pExpr); } +%type readonly_opt {int} +readonly_opt(A) ::= . { A = 0; } +readonly_opt(A) ::= READONLY. { A = 1; } + %type key_opt {Expr*} %destructor key_opt {sqlite3ExprDelete(pParse->db, $$);} key_opt(A) ::= . { A = 0; } diff -U 3 -H -d -r -N -- sqlite-3.6.17/src/sqliteInt.h sqlite-3.6.17-mod/src/sqliteInt.h --- sqlite-3.6.17/src/sqliteInt.h 2009-11-03 10:47:25.000000000 +0100 +++ sqlite-3.6.17-mod/src/sqliteInt.h 2009-11-03 10:47:25.000000000 +0100 @@ -2702,7 +2702,7 @@ # define sqlite3AuthContextPush(a,b,c) # define sqlite3AuthContextPop(a) ((void)(a)) #endif -void sqlite3Attach(Parse*, Expr*, Expr*, Expr*); +void sqlite3Attach(Parse*, Expr*, Expr*, Expr*, int); void sqlite3Detach(Parse*, Expr*); int sqlite3BtreeFactory(const sqlite3 *db, const char *zFilename, int omitJournal, int nCache, int flags, Btree **ppBtree); diff -U 3 -H -d -r -N -- sqlite-3.6.17/tool/mkkeywordhash.c sqlite-3.6.17-mod/tool/mkkeywordhash.c --- sqlite-3.6.17/tool/mkkeywordhash.c 2009-11-03 10:47:25.000000000 +0100 +++ sqlite-3.6.17-mod/tool/mkkeywordhash.c 2009-11-03 10:47:25.000000000 +0100 @@ -232,6 +232,7 @@ { "PRIMARY", "TK_PRIMARY", ALWAYS }, { "QUERY", "TK_QUERY", EXPLAIN }, { "RAISE", "TK_RAISE", TRIGGER }, + { "READONLY", "TK_READONLY", ATTACH }, { "REFERENCES", "TK_REFERENCES", FKEY }, { "REGEXP", "TK_LIKE_KW", ALWAYS }, { "REINDEX", "TK_REINDEX", REINDEX }, _______________________________________________ sqlite-users mailing list sqlite-users@sqlite.org http://sqlite.org:8080/cgi-bin/mailman/listinfo/sqlite-users