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); } }
public OpenMode(string z, VSystem.OPEN mode) { Z = z; Mode = mode; }
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); }
// 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); }