@@ -949,7 +949,7 @@ class InnerDatabaseFile : public SQLiteVFS::File {
949949 public:
950950 InnerDatabaseFile (std::unique_ptr<SQLite::Database> &&outer_db,
951951 const std::string &inner_db_tablename_prefix, bool read_only, size_t threads,
952- bool noprefetch)
952+ bool noprefetch, bool web )
953953 : outer_db_(std::move(outer_db)),
954954 inner_db_pages_table_ (inner_db_tablename_prefix + " pages" ), read_only_(read_only),
955955 // MAX(pageno) instead of COUNT(pageno) because the latter would trigger table scan
@@ -962,14 +962,34 @@ class InnerDatabaseFile : public SQLiteVFS::File {
962962 assert (threads);
963963 fetch_jobs_.reserve (MAX_FETCH_CURSORS); // important! ensure fetch_jobs_.data() never moves
964964 methods_.iVersion = 1 ;
965- assert (outer_db_->execAndGet (" PRAGMA quick_check" ).getString () == " ok" );
965+ assert (web || outer_db_->execAndGet (" PRAGMA quick_check" ).getString () == " ok" );
966966 }
967967}; // namespace SQLiteNested
968968
969969// issue when write performance is prioritized over transaction safety / possible corruption
970970const char *UNSAFE_PRAGMAS =
971971 " PRAGMA journal_mode=OFF; PRAGMA synchronous=OFF; PRAGMA locking_mode=EXCLUSIVE" ;
972972
973+ // originally found: http://codepad.org/lCypTglt
974+ std::string urlencode (const std::string &s, bool keep_slash = false ) {
975+ // RFC 3986 section 2.3 Unreserved Characters (January 2005)
976+ static const std::string unreserved =
977+ " ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.~" ;
978+
979+ std::string escaped = " " ;
980+ for (size_t i = 0 ; i < s.length (); i++) {
981+ if (unreserved.find_first_of (s[i]) != std::string::npos || keep_slash && s[i] == ' /' ) {
982+ escaped.push_back (s[i]);
983+ } else {
984+ escaped.append (" %" );
985+ char buf[3 ];
986+ sprintf (buf, " %.2X" , s[i]);
987+ escaped.append (buf);
988+ }
989+ }
990+ return escaped;
991+ }
992+
973993class VFS : public SQLiteVFS ::Wrapper {
974994 protected:
975995 // subclass may override inner_db_tablename_prefix_ with something encoding-specific, to
@@ -1004,41 +1024,53 @@ class VFS : public SQLiteVFS::Wrapper {
10041024
10051025 virtual std::unique_ptr<SQLiteVFS::File>
10061026 NewInnerDatabaseFile (const char *zName, std::unique_ptr<SQLite::Database> &&outer_db,
1007- bool read_only, size_t threads, bool noprefetch) {
1027+ bool read_only, size_t threads, bool noprefetch, bool web ) {
10081028 return std::unique_ptr<SQLiteVFS::File>(new InnerDatabaseFile (
1009- std::move (outer_db), inner_db_tablename_prefix_, read_only, threads, noprefetch));
1029+ std::move (outer_db), inner_db_tablename_prefix_, read_only, threads, noprefetch, web ));
10101030 }
10111031
10121032 int Open (const char *zName, sqlite3_file *pFile, int flags, int *pOutFlags) override {
10131033 if (zName && zName[0 ]) {
10141034 std::string sName (zName);
10151035 if (flags & SQLITE_OPEN_MAIN_DB) {
10161036 // strip inner_db_filename_suffix_ to get filename of outer database
1017- std::string outer_db_filename =
1018- sName .size () > inner_db_filename_suffix_.size ()
1019- ? sName .substr (0 , sName .size () - inner_db_filename_suffix_.size ())
1020- : " " ;
1021- if (outer_db_filename.empty () ||
1022- sName .substr (outer_db_filename.size ()) != inner_db_filename_suffix_) {
1023- last_error_ = " inner database filename unexpectedly missing suffix " +
1024- inner_db_filename_suffix_;
1025- return SQLITE_CANTOPEN_FULLPATH;
1037+ std::string outer_db_filename = sName ;
1038+ bool web = sName == " /__web__" ;
1039+ if (!web) {
1040+ if (sName .size () > inner_db_filename_suffix_.size ()) {
1041+ outer_db_filename =
1042+ sName .substr (0 , sName .size () - inner_db_filename_suffix_.size ());
1043+ } else {
1044+ outer_db_filename.clear ();
1045+ }
1046+ if (outer_db_filename.empty () ||
1047+ sName .substr (outer_db_filename.size ()) != inner_db_filename_suffix_) {
1048+ last_error_ = " inner database filename unexpectedly missing suffix " +
1049+ inner_db_filename_suffix_;
1050+ return SQLITE_CANTOPEN_FULLPATH;
1051+ }
10261052 }
10271053
1028- // TODO: URI-encode outer_db_filename
1029- std::string outer_db_uri = " file:" + outer_db_filename;
1054+ std::string vfs = outer_vfs_;
1055+ std::string outer_db_uri = " file:" + urlencode ( outer_db_filename, true ) ;
10301056 bool unsafe = sqlite3_uri_boolean (zName, " outer_unsafe" , 0 );
1031- if (unsafe) {
1057+ if (web) {
1058+ outer_db_uri += " ?immutable=1&web_url=" ;
1059+ outer_db_uri += urlencode (sqlite3_uri_parameter (zName, " web_url" ));
1060+ flags &= ~(SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE);
1061+ flags |= SQLITE_OPEN_READONLY;
1062+ vfs = " web" ;
1063+ } else if (unsafe) {
10321064 outer_db_uri += " ?nolock=1&psow=1" ;
10331065 } else if (sqlite3_uri_boolean (zName, " immutable" , 0 )) {
10341066 outer_db_uri += " ?immutable=1" ;
10351067 }
1068+ _DBG << outer_db_uri << _EOL;
10361069
10371070 try {
10381071 // open outer database
10391072 std::unique_ptr<SQLite::Database> outer_db (new SQLite::Database (
1040- outer_db_uri, flags | SQLITE_OPEN_NOMUTEX | SQLITE_OPEN_URI, 0 ,
1041- outer_vfs_));
1073+ outer_db_uri, flags | SQLITE_OPEN_NOMUTEX | SQLITE_OPEN_URI, 0 , vfs));
10421074 // see comment in Lock() about possibe future relaxation of exclusive
10431075 // locking
10441076 outer_db->exec (" PRAGMA locking_mode=EXCLUSIVE" );
@@ -1082,7 +1114,7 @@ class VFS : public SQLiteVFS::Wrapper {
10821114
10831115 auto idbf = NewInnerDatabaseFile (zName, std::move (outer_db),
10841116 (flags & SQLITE_OPEN_READONLY),
1085- (size_t )threads, noprefetch);
1117+ (size_t )threads, noprefetch, web );
10861118 idbf->InitHandle (pFile);
10871119 assert (pFile->pMethods );
10881120 idbf.release ();
@@ -1118,6 +1150,10 @@ class VFS : public SQLiteVFS::Wrapper {
11181150 // filesystem, but xOpen() will recognize).
11191151 int FullPathname (const char *zName, int nPathOut, char *zPathOut) override {
11201152 std::string zName2 (zName);
1153+ if (zName2 == " /__web__" ) {
1154+ strncpy (zPathOut, zName, nPathOut);
1155+ return SQLITE_OK;
1156+ }
11211157 if (!zName2.empty () && zName2[0 ] != ' /' ) {
11221158 if (getcwd (zPathOut, nPathOut) && !strcmp (zPathOut, " /" )) {
11231159 // evading bug in sqlite3 os_unix.c unixFullPathname, when given a relative path
@@ -1127,7 +1163,7 @@ class VFS : public SQLiteVFS::Wrapper {
11271163 }
11281164 int rc = SQLiteVFS::Wrapper::FullPathname (zName2.c_str (), nPathOut, zPathOut);
11291165 if (rc != SQLITE_OK && rc != SQLITE_OK_SYMLINK) {
1130- _DBG << " FullPathNameE " << rc << " " << sqlite3_errstr (rc) << _EOL;
1166+ _DBG << " FullPathName " << rc << " " << sqlite3_errstr (rc) << _EOL;
11311167 return rc;
11321168 }
11331169 std::string outer_db_filename (zPathOut);
0 commit comments