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); } }
static RC LoadStat3(Context ctx, string dbName) { Debug.Assert(!ctx.Lookaside.Enabled); if (sqlite3FindTable(ctx, "sqlite_stat3", dbName) == null) { return(RC.OK); } string sql = SysEx.Mprintf(ctx, "SELECT idx,count(*) FROM %Q.sqlite_stat3 GROUP BY idx", dbName); // Text of the SQL statement if (!sql) { return(RC_NOMEM); } Vdbe stmt = null; // An SQL statement being run RC rc = Vdbe.Prepare(ctx, sql, -1, stmt, 0); // Result codes from subroutines C._tagfree(ctx, sql); if (rc) { return(rc); } while (stmt.Step() == RC.ROW) { string indexName = (string)Vdbe.Column_Text(stmt, 0); // Index name if (!indexName) { continue; } int samplesLength = Vdbe.Column_Int(stmt, 1); // Number of samples Index idx = sqlite3FindIndex(ctx, indexName, dbName); // Pointer to the index object if (!idx) { continue; } _assert(idx->Samples.length == 0); idx.Samples.length = samplesLength; idx.Samples.data = new IndexSample[samplesLength]; idx.AvgEq = idx.RowEsts[1]; if (!idx->Samples.data) { ctx->MallocFailed = true; Vdbe.Finalize(stmt); return(RC.NOMEM); } } rc = Vdbe.Finalize(stmt); if (rc) { return(rc); } sql = C._mtagprintf(ctx, "SELECT idx,neq,nlt,ndlt,sample FROM %Q.sqlite_stat3", dbName); if (!sql) { return(RC.NOMEM); } rc = Vdbe.Prepare(ctx, sql, -1, &stmt, 0); C._tagfree(ctx, sql); if (rc) { return(rc); } Index prevIdx = null; // Previous index in the loop int idxId = 0; // slot in pIdx->aSample[] for next sample while (stmt.Step() == RC.ROW) { string indexName = (string)Vdbe.Column_Text(stmt, 0); // Index name if (indexName == null) { continue; } Index idx = sqlite3FindIndex(ctx, indexName, dbName); // Pointer to the index object if (idx == null) { continue; } if (idx == prevIdx) { idxId++; } else { prevIdx = idx; idxId = 0; } Debug.Assert(idxId < idx.Samples.length); IndexSample sample = idx.Samples[idxId]; // A slot in pIdx->aSample[] sample.Eq = (tRowcnt)Vdbe.Column_Int64(stmt, 1); sample.Lt = (tRowcnt)Vdbe.Column_Int64(stmt, 2); sample.DLt = (tRowcnt)Vdbe.Column_Int64(stmt, 3); if (idxId == idx.Samples.length - 1) { tRowcnt sumEq; // Sum of the nEq values if (sample.DLt > 0) { for (int i = 0, sumEq = 0; i <= idxId - 1; i++) { sumEq += idx.Samples[i].Eq; } idx.AvgEq = (sample.Lt - sumEq) / sample.DLt; } if (idx.AvgEq <= 0) { idx.AvgEq = 1; } } TYPE type = Vdbe.Column_Type(stmt, 4); // Datatype of a sample sample.Type = type; switch (type) { case TYPE.INTEGER: { sample.u.I = Vdbe.Column_Int64(stmt, 4); break; } case TYPE.FLOAT: { sample.u.R = Vdbe.Column_Double(stmt, 4); break; } case TYPE.NULL: { break; } default: Debug.Assert(type == TYPE.TEXT || type == TYPE.BLOB); { string z = (string)(type == TYPE_BLOB ? Vdbe.Column_Blob(stmt, 4) : Vdbe.Column_Text(stmt, 4)); int n = (z ? Vdbe.Column_Bytes(stmt, 4) : 0); sample.Bytes = n; if (n < 1) { sample.u.Z = null; } else { sample.u.Z = C._tagalloc(ctx, n); if (sample->u.Z == null) { ctx.MallocFailed = true; Vdbe.Finalize(stmt); return(RC.NOMEM); } Buffer.BlockCopy(sample.u.Z, z, n); } } } } return(Vdbe.Finalize(stmt)); }