示例#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);
            }
        }