Example #1
0
        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);
            }
        }
Example #2
0
        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));
        }