static void AttachFunc_(FuncContext fctx, int notUsed1, Mem[] argv) { Context ctx = sqlite3_context_db_handle(fctx); string file = Mem_Text(argv[0]); string name = Mem_Text(argv[1]); if (file == null) { file = ""; } if (name == null) { name = ""; } // Check for the following errors: // * Too many attached databases, // * Transaction currently open // * Specified database name already being used. RC rc = 0; string errDyn = null; if (ctx.DBs.length >= ctx.Limits[(int)LIMIT.ATTACHED] + 2) { errDyn = SysEx.Mprintf(ctx, "too many attached databases - max %d", ctx.Limits[(int)LIMIT.ATTACHED]); goto attach_error; } if (!ctx.AutoCommit) { errDyn = SysEx.Mprintf(ctx, "cannot ATTACH database within transaction"); goto attach_error; } for (int i = 0; i < ctx.DBs.length; i++) { string z = ctx.DBs[i].Name; Debug.Assert(z != null && name != null); if (z.Equals(name, StringComparison.OrdinalIgnoreCase)) { errDyn = SysEx.Mprintf(ctx, "database %s is already in use", name); goto attach_error; } } // Allocate the new entry in the ctx->aDb[] array and initialize the schema hash tables. //: Realloc if (ctx.DBs.data.Length <= ctx.DBs.length) { Array.Resize(ref ctx.DBs.data, ctx.DBs.length + 1); } if (ctx.DBs.data == null) { return; } ctx.DBs[ctx.DBs.length] = new Context.DB(); Context.DB newDB = ctx.DBs[ctx.DBs.length]; //: _memset(newDB, 0, sizeof(*newDB)); // 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 initialized. VSystem.OPEN flags = ctx.OpenFlags; VSystem vfs = null; string path = string.Empty; string err = string.Empty; rc = sqlite3ParseUri(ctx.Vfs.Name, file, ref flags, ref vfs, ref path, ref err); if (rc != RC.OK) { if (rc == RC.NOMEM) { ctx.MallocFailed = true; } sqlite3_result_error(fctx, err, -1); C._free(ref err); return; } Debug.Assert(vfs != null); flags |= VSystem.OPEN.MAIN_DB; rc = Btree.Open(vfs, path, ctx, ref newDB.Bt, 0, flags); C._free(ref path); ctx.DBs.length++; if (rc == RC.CONSTRAINT) { rc = RC.ERROR; errDyn = SysEx.Mprintf(ctx, "database is already attached"); } else if (rc == RC.OK) { newDB.Schema = sqlite3SchemaGet(ctx, newDB.Bt); if (newDB.Schema == null) { rc = RC.NOMEM; } else if (newDB.Schema.FileFormat != 0 && newDB.Schema.Encode != E.CTXENCODE(ctx)) { errDyn = SysEx.Mprintf(ctx, "attached databases must use the same text encoding as main database"); rc = RC.ERROR; } Pager pager = newDB.Bt.get_Pager(); pager.LockingMode(ctx.DefaultLockMode); newDB.Bt.SecureDelete(ctx.DBs[0].Bt.SecureDelete(true)); } newDB.SafetyLevel = 3; newDB.Name = name; if (rc == RC.OK && newDB.Name == null) { rc = RC.NOMEM; } #if HAS_CODEC //extern int sqlite3CodecAttach(sqlite3*, int, const void*, int); //extern void sqlite3CodecGetKey(sqlite3*, int, void**, int*); if (rc == RC.OK) { int keyLength; string key; TYPE t = sqlite3_value_type(argv[2]); switch (t) { case TYPE.INTEGER: case TYPE.FLOAT: errDyn = "Invalid key value"; rc = RC.ERROR; break; case TYPE.TEXT: case TYPE.BLOB: keyLength = Mem.Bytes(argv[2]); key = sqlite3_value_blob(argv[2]).ToString(); rc = sqlite3CodecAttach(ctx, ctx.DBs.length - 1, key, keyLength); break; case TYPE.NULL: // No key specified. Use the key from the main database sqlite3CodecGetKey(ctx, 0, out key, out keyLength); if (keyLength > 0 || ctx.DBs[0].Bt.GetReserve() > 0) { rc = sqlite3CodecAttach(ctx, ctx.DBs.length - 1, key, keyLength); } 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 ctx->aDb[] array. i.e. put everything back the way we found it. if (rc == RC.OK) { Btree.EnterAll(ctx); rc = sqlite3Init(ctx, ref errDyn); Btree.LeaveAll(ctx); } if (rc != 0) { int db = ctx.DBs.length - 1; Debug.Assert(db >= 2); if (ctx.DBs[db].Bt != null) { ctx.DBs[db].Bt.Close(); ctx.DBs[db].Bt = null; ctx.DBs[db].Schema = null; } sqlite3ResetInternalSchema(ctx, -1); ctx.DBs.length = db; if (rc == RC.NOMEM || rc == RC.IOERR_NOMEM) { ctx.MallocFailed = true; C._tagfree(ctx, ref errDyn); errDyn = SysEx.Mprintf(ctx, "out of memory"); } else if (errDyn == null) { errDyn = SysEx.Mprintf(ctx, "unable to open database: %s", file); } goto attach_error; } return; attach_error: // Return an error if we get here if (errDyn != null) { sqlite3_result_error(fctx, errDyn, -1); C._tagfree(ctx, ref errDyn); } if (rc != 0) { sqlite3_result_error_code(fctx, rc); } }