Beispiel #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);
            }
        }
Beispiel #2
0
 public OpenMode(string z, VSystem.OPEN mode)
 {
     Z    = z;
     Mode = mode;
 }
Beispiel #3
0
        public static RC ParseUri(string defaultVfsName, string uri, ref VSystem.OPEN flagsRef, out VSystem vfsOut, out string fileNameOut, out string errMsgOut)
        {
            vfsOut      = null;
            fileNameOut = null;
            errMsgOut   = null;

            VSystem.OPEN flags     = flagsRef;
            string       vfsName   = defaultVfsName;
            int          uriLength = uri.Length;

            RC            rc       = RC.OK;
            StringBuilder fileName = null;

            if (((flags & VSystem.OPEN.URI) != 0 || SysEx._GlobalStatics.OpenUri) && uriLength >= 5 && uri.StartsWith("file:"))
            {
                // Make sure the SQLITE_OPEN_URI flag is set to indicate to the VFS xOpen method that there may be extra parameters following the file-name.
                flags |= VSystem.OPEN.URI;

                int bytes = uriLength + 2; // Bytes of space to allocate
                int uriIdx;                // Input character index
                for (uriIdx = 0; uriIdx < uriLength; uriIdx++)
                {
                    bytes += (uri[uriIdx] == '&' ? 1 : 0);
                }
                fileName = new StringBuilder(bytes);
                if (fileName == null)
                {
                    return(RC.NOMEM);
                }

                // Discard the scheme and authority segments of the URI.
                if (uri[5] == '/' && uri[6] == '/')
                {
                    uriIdx = 7;
                    while (uriIdx < uriLength && uri[uriIdx] != '/')
                    {
                        uriIdx++;
                    }
                    if (uriIdx != 7 && (uriIdx != 16 || !string.Equals("localhost", uri.Substring(7, 9), StringComparison.InvariantCultureIgnoreCase)))
                    {
                        errMsgOut = C._mprintf("invalid uri authority: %.*s", uriIdx - 7, uri.Substring(7));
                        rc        = RC.ERROR;
                        goto parse_uri_out;
                    }
                }
                else
                {
                    uriIdx = 5;
                }

                // Copy the filename and any query parameters into the zFile buffer. Decode %HH escape codes along the way.
                //
                // Within this loop, variable eState may be set to 0, 1 or 2, depending on the parsing context. As follows:
                //
                //   0: Parsing file-name.
                //   1: Parsing name section of a name=value query parameter.
                //   2: Parsing value section of a name=value query parameter.
                int  state = 0; // Parser state when parsing URI
                char c;
                //int fileNameIdx = 0; // Output character index
                while (uriIdx < uriLength && (c = uri[uriIdx]) != 0 && c != '#')
                {
                    uriIdx++;

                    if (c == '%' && C._isxdigit(uri[uriIdx]) && C._isxdigit(uri[uriIdx + 1]))
                    {
                        int octet = (C._hextobyte(uri[uriIdx++]) << 4);
                        octet += C._hextobyte(uri[uriIdx++]);
                        Debug.Assert(octet >= 0 && octet < 256);
                        if (octet == 0)
                        {
                            // This branch is taken when "%00" appears within the URI. In this case we ignore all text in the remainder of the path, name or
                            // value currently being parsed. So ignore the current character and skip to the next "?", "=" or "&", as appropriate.
                            while (uriIdx < uriLength && (c = uri[uriIdx]) != 0 && c != '#' &&
                                   (state != 0 || c != '?') &&
                                   (state != 1 || (c != '=' && c != '&')) &&
                                   (state != 2 || c != '&'))
                            {
                                uriIdx++;
                            }
                            continue;
                        }
                        c = (char)octet;
                    }
                    else if (state == 1 && (c == '&' || c == '='))
                    {
                        if (fileName[fileName.Length - 1] == '\0')
                        {
                            // An empty option name. Ignore this option altogether.
                            while (uri[uriIdx] != '\0' && uri[uriIdx] != '#' && uri[uriIdx - 1] != '&')
                            {
                                uriIdx++;
                            }
                            continue;
                        }
                        if (c == '&')
                        {
                            fileName.Append('\0');
                        }
                        else
                        {
                            state = 2;
                        }
                        c = '\0';
                    }
                    else if ((state == 0 && c == '?') || (state == 2 && c == '&'))
                    {
                        c     = '\0';
                        state = 1;
                    }
                    fileName.Append(c);
                }
                if (state == 1)
                {
                    fileName.Append('\0');
                }
                fileName.Append('\0');
                fileName.Append('\0');

                // Check if there were any options specified that should be interpreted here. Options that are interpreted here include "vfs" and those that
                // correspond to flags that may be passed to the sqlite3_open_v2() method.
                string opt = fileName.ToString().Substring(fileName.Length + 1);
                while (opt.Length > 0)
                {
                    int    optLength = opt.Length;
                    string val       = opt.Substring(optLength);
                    int    valLength = val.Length;
                    if (optLength == 3 && opt.StartsWith("vfs"))
                    {
                        vfsName = val;
                    }
                    else
                    {
                        OpenMode[]   modes    = null;
                        string       modeType = null;
                        VSystem.OPEN mask     = (VSystem.OPEN) 0;
                        VSystem.OPEN limit    = (VSystem.OPEN) 0;
                        if (optLength == 5 && opt.StartsWith("cache"))
                        {
                            mask     = VSystem.OPEN.SHAREDCACHE | VSystem.OPEN.PRIVATECACHE;
                            modes    = _cacheModes;
                            limit    = mask;
                            modeType = "cache";
                        }
                        if (optLength == 4 && opt.StartsWith("mode"))
                        {
                            mask     = VSystem.OPEN.READONLY | VSystem.OPEN.READWRITE | VSystem.OPEN.CREATE;
                            modes    = _openModes;
                            limit    = mask & flags;
                            modeType = "access";
                        }
                        if (modes != null)
                        {
                            VSystem.OPEN mode = 0;
                            for (int i = 0; modes[i].Z != null; i++)
                            {
                                string z = modes[i].Z;
                                if (valLength == z.Length && z.StartsWith(val))
                                {
                                    mode = modes[i].Mode;
                                    break;
                                }
                            }
                            if (mode == 0)
                            {
                                errMsgOut = C._mprintf("no such %s mode: %s", modeType, val);
                                rc        = RC.ERROR;
                                goto parse_uri_out;
                            }
                            if (mode > limit)
                            {
                                errMsgOut = C._mprintf("%s mode not allowed: %s", modeType, val);
                                rc        = RC.PERM;
                                goto parse_uri_out;
                            }
                            flags = ((flags & ~mask) | mode);
                        }
                    }
                    opt = val.Substring(valLength + 1);
                }
            }
            else
            {
                fileName = (uri == null ? new StringBuilder() : new StringBuilder(uri.Substring(0, uriLength)));
                if (fileName == null)
                {
                    return(RC.NOMEM);
                }
                fileName.Append('\0');
                fileName.Append('\0');
            }

            vfsOut = FindVfs(vfsName);
            if (vfsOut == null)
            {
                errMsgOut = C._mprintf("no such vfs: %s", vfsName);
                rc        = RC.ERROR;
            }
parse_uri_out:
            if (rc != RC.OK)
            {
                C._free(ref fileName);
                fileName = null;
            }
            flagsRef    = flags;
            fileNameOut = (fileName == null ? null : fileName.ToString().Substring(0, fileName.Length));
            return(rc);
        }
Beispiel #4
0
        // extensions
#if ENABLE_ATOMIC_WRITE
        internal static RC JournalVFileOpen(VSystem vfs, string name, ref VFile file, VSystem.OPEN flags, int bufferLength)
        {
            var p = new JournalVFile();

            if (bufferLength > 0)
            {
                p.Buffer = C._allocZero(bufferLength);
            }
            else
            {
                VSystem.OPEN dummy;
                return(vfs.Open(name, p, flags, out dummy));
            }
            p.Type         = 2;
            p.BufferLength = bufferLength;
            p.Flags        = flags;
            p.Journal      = name;
            p.Vfs          = vfs;
            file           = p;
            return(RC.OK);
        }