Check-in [00ac7945a9]
Not logged in
Overview

SHA1 Hash:00ac7945a9ead7200d6ad4404ca432c683b9c4c7
Date: 2009-08-13 14:27:24
User: drh
Comment:Disconnect the global configuration database in ~/.fossil from the respository database in most cases. This allows multiple "sync" or "commit" operations to be running on different repositories at the same time.
Timelines: ancestors | descendants | both | trunk
Other Links: files | ZIP archive | manifest

Tags And Properties
Changes
[hide diffs]

Modified src/allrepo.c from [24723f7eda] to [d3d32a9331].

@@ -89,11 +89,11 @@
 
   if( g.argc<3 ){
     usage("list|ls|pull|push|rebuild|sync");
   }
   n = strlen(g.argv[2]);
-  db_open_config();
+  db_open_config(1);
   zCmd = g.argv[2];
   if( strncmp(zCmd, "list", n)==0 ){
     zCmd = "list";
   }else if( strncmp(zCmd, "ls", n)==0 ){ /* alias for "list" above */
     zCmd = "list";

Modified src/cgi.c from [1b01c427a4] to [8b6978d8ea].

@@ -678,11 +678,11 @@
   if( z ){
     z = mprintf("%s",z);
     add_param_list(z, '&');
   }
 
-  z = P("REMOTE_ADDR");
+  z = (char*)P("REMOTE_ADDR");
   if( z ) g.zIpAddr = mprintf("%s", z);
 
   len = atoi(PD("CONTENT_LENGTH", "0"));
   g.zContentType = zType = P("CONTENT_TYPE");
   if( len>0 && zType ){

Modified src/clone.c from [89ab820dce] to [f0402d199e].

@@ -41,20 +41,19 @@
   char *zPassword;
   url_proxy_options();
   if( g.argc!=4 ){
     usage("FILE-OR-URL NEW-REPOSITORY");
   }
-  db_open_config();
+  db_open_config(0);
   if( file_size(g.argv[3])>0 ){
     fossil_panic("file already exists: %s", g.argv[3]);
   }
   url_parse(g.argv[2]);
   if( g.urlIsFile ){
     file_copy(g.urlName, g.argv[3]);
     db_close();
     db_open_repository(g.argv[3]);
-    db_open_config();
     db_record_repository_filename(g.argv[3]);
     db_multi_exec(
       "REPLACE INTO config(name,value)"
       " VALUES('server-code', lower(hex(randomblob(20))));"
       "REPLACE INTO config(name,value)"

Modified src/construct.c from [bf4879b005] to [763e35ebbe].

@@ -137,11 +137,11 @@
   }
 
   /* Create the foundation */
   db_create_repository(zRepository);
   db_open_repository(zRepository);
-  db_open_config();
+  db_open_config(0);
   db_begin_transaction();
 
   db_initial_setup(0, 1);
 
   printf("project-id: %s\n", db_get("project-code", 0));

Modified src/db.c from [503381e0ca] to [b0740e5748].

@@ -620,43 +620,62 @@
   sqlite3_exec(db, "COMMIT", 0, 0, 0);
   sqlite3_close(db);
 }
 
 /*
+** Open a database file.  Return a pointer to the new database
+** connection.  An error results in process abort.
+*/
+static sqlite3 *openDatabase(const char *zDbName){
+  int rc;
+  const char *zVfs;
+  sqlite3 *db;
+
+  zVfs = getenv("FOSSIL_VFS");
+  rc = sqlite3_open_v2(
+       zDbName, &db,
+       SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
+       zVfs
+  );
+  if( rc!=SQLITE_OK ){
+    db_err(sqlite3_errmsg(db));
+  }
+  sqlite3_busy_timeout(db, 5000);
+  return db;
+}
+
+
+/*
 ** zDbName is the name of a database file.  If no other database
 ** file is open, then open this one.  If another database file is
 ** already open, then attach zDbName using the name zLabel.
 */
 void db_open_or_attach(const char *zDbName, const char *zLabel){
 #ifdef __MINGW32__
   zDbName = mbcsToUtf8(zDbName);
 #endif
   if( !g.db ){
-    int rc;
-    const char *zVfs;
-
-    zVfs = getenv("FOSSIL_VFS");
-    rc = sqlite3_open_v2(
-         zDbName, &g.db,
-         SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
-         zVfs
-    );
-    if( rc!=SQLITE_OK ){
-      db_err(sqlite3_errmsg(g.db));
-    }
-    sqlite3_busy_timeout(g.db, 5000);
+    g.db = openDatabase(zDbName);
     db_connection_init();
   }else{
     db_multi_exec("ATTACH DATABASE %Q AS %s", zDbName, zLabel);
   }
 }
 
 /*
 ** Open the user database in "~/.fossil".  Create the database anew if
 ** it does not already exist.
-*/
-void db_open_config(void){
+**
+** If the useAttach flag is 0 (the usual case) then the user database is
+** opened on a separate database connection g.dbConfig.  This prevents
+** the ~/.fossil database from becoming locked on long check-in or sync
+** operations which hold an exclusive transaction.  In a few cases, though,
+** it is convenient for the ~/.fossil to be attached to the main database
+** connection so that we can join between the various databases.  In that
+** case, invoke this routine with useAttach as 1.
+*/
+void db_open_config(int useAttach){
   char *zDbName;
   const char *zHome;
   if( g.configOpen ) return;
 #ifdef __MINGW32__
   zHome = getenv("LOCALAPPDATA");
@@ -684,11 +703,17 @@
   zDbName = mprintf("%s/.fossil", zHome);
 #endif
   if( file_size(zDbName)<1024*3 ){
     db_init_database(zDbName, zConfigSchema, (char*)0);
   }
-  db_open_or_attach(zDbName, "configdb");
+  g.useAttach = useAttach;
+  if( useAttach ){
+    db_open_or_attach(zDbName, "configdb");
+    g.dbConfig = 0;
+  }else{
+    g.dbConfig = openDatabase(zDbName);
+  }
   g.configOpen = 1;
 }
 
 /*
 ** If zDbName is a valid local database file, open it and return
@@ -702,11 +727,11 @@
   if( access(zDbName, F_OK) ) return 0;
   lsize = file_size(zDbName);
   if( lsize%1024!=0 || lsize<4096 ) return 0;
   db_open_or_attach(zDbName, "localdb");
   g.localOpen = 1;
-  db_open_config();
+  db_open_config(0);
   db_open_repository(0);
 
   /* If the "mtime" column is missing from the vfile table, then
   ** add it now.   This code added on 2008-12-06.  After all users have
   ** upgraded, this code can be safely deleted.
@@ -987,11 +1012,11 @@
   if( g.argc!=3 ){
     usage("REPOSITORY-NAME");
   }
   db_create_repository(g.argv[2]);
   db_open_repository(g.argv[2]);
-  db_open_config();
+  db_open_config(0);
   db_begin_transaction();
   db_initial_setup(zDate, 1);
   db_end_transaction(0);
   printf("project-id: %s\n", db_get("project-code", 0));
   printf("server-id:  %s\n", db_get("server-code", 0));
@@ -1147,50 +1172,83 @@
   }
   return 0;
 }
 
 /*
+** Swap the g.db and g.dbConfig connections so that the various db_* routines
+** work on the ~/.fossil database instead of on the repository database.
+** Be sure to swap them back after doing the operation.
+**
+** If g.useAttach that means the ~/.fossil database was opened with
+** the useAttach flag set to 1.  In that case no connection swap is required
+** so this routine is a no-op.
+*/
+void db_swap_connections(void){
+  if( !g.useAttach ){
+    sqlite3 *dbTemp = g.db;
+    g.db = g.dbConfig;
+    g.dbConfig = dbTemp;
+  }
+}
+
+/*
 ** Get and set values from the CONFIG, GLOBAL_CONFIG and VVAR table in the
 ** repository and local databases.
 */
 char *db_get(const char *zName, char *zDefault){
   char *z = 0;
   if( g.repositoryOpen ){
     z = db_text(0, "SELECT value FROM config WHERE name=%Q", zName);
   }
   if( z==0 && g.configOpen ){
+    db_swap_connections();
     z = db_text(0, "SELECT value FROM global_config WHERE name=%Q", zName);
+    db_swap_connections();
   }
   if( z==0 ){
     z = zDefault;
   }
   return z;
 }
 void db_set(const char *zName, const char *zValue, int globalFlag){
   db_begin_transaction();
-  db_multi_exec("REPLACE INTO %sconfig(name,value) VALUES(%Q,%Q)",
-                 globalFlag ? "global_" : "", zName, zValue);
+  if( globalFlag ){
+    db_swap_connections();
+    db_multi_exec("REPLACE INTO global_config(name,value) VALUES(%Q,%Q)",
+                   zName, zValue);
+    db_swap_connections();
+  }else{
+    db_multi_exec("REPLACE INTO config(name,value) VALUES(%Q,%Q)",
+                   zName, zValue);
+  }
   if( globalFlag && g.repositoryOpen ){
     db_multi_exec("DELETE FROM config WHERE name=%Q", zName);
   }
   db_end_transaction(0);
 }
 void db_unset(const char *zName, int globalFlag){
   db_begin_transaction();
-  db_multi_exec("DELETE FROM %sconfig WHERE name=%Q",
-                 globalFlag ? "global_" : "", zName);
+  if( globalFlag ){
+    db_swap_connections();
+    db_multi_exec("DELETE INTO global_config WHERE name=%Q", zName);
+    db_swap_connections();
+  }else{
+    db_multi_exec("DELETE INTO config WHERE name=%Q", zName);
+  }
   if( globalFlag && g.repositoryOpen ){
     db_multi_exec("DELETE FROM config WHERE name=%Q", zName);
   }
   db_end_transaction(0);
 }
 int db_is_global(const char *zName){
+  int rc = 0;
   if( g.configOpen ){
-    return db_exists("SELECT 1 FROM global_config WHERE name=%Q", zName);
-  }else{
-    return 0;
-  }
+    db_swap_connections();
+    rc = db_exists("SELECT 1 FROM global_config WHERE name=%Q", zName);
+    db_swap_connections();
+  }
+  return rc;
 }
 int db_get_int(const char *zName, int dflt){
   int v = dflt;
   int rc;
   if( g.repositoryOpen ){
@@ -1203,22 +1261,29 @@
     db_finalize(&q);
   }else{
     rc = SQLITE_DONE;
   }
   if( rc==SQLITE_DONE && g.configOpen ){
+    db_swap_connections();
     v = db_int(dflt, "SELECT value FROM global_config WHERE name=%Q", zName);
+    db_swap_connections();
   }
   return v;
 }
 void db_set_int(const char *zName, int value, int globalFlag){
-  db_begin_transaction();
-  db_multi_exec("REPLACE INTO %sconfig(name,value) VALUES(%Q,%d)",
-                globalFlag ? "global_" : "", zName, value);
+  if( globalFlag ){
+    db_swap_connections();
+    db_multi_exec("REPLACE INTO global_config(name,value) VALUES(%Q,%d)",
+                  zName, value);
+    db_swap_connections();
+  }else{
+    db_multi_exec("REPLACE INTO config(name,value) VALUES(%Q,%d)",
+                  zName, value);
+  }
   if( globalFlag && g.repositoryOpen ){
     db_multi_exec("DELETE FROM config WHERE name=%Q", zName);
   }
-  db_end_transaction(0);
 }
 int db_get_boolean(const char *zName, int dflt){
   char *zVal = db_get(zName, dflt ? "on" : "off");
   if( is_truth(zVal) ) return 1;
   if( is_false(zVal) ) return 0;
@@ -1252,15 +1317,17 @@
   if( zName==0 ){
     if( !g.localOpen ) return;
     zName = db_lget("repository", 0);
   }
   file_canonical_name(zName, &full);
+  db_swap_connections();
   db_multi_exec(
      "INSERT OR IGNORE INTO global_config(name,value)"
      "VALUES('repo:%q',1)",
      blob_str(&full)
   );
+  db_swap_connections();
   blob_reset(&full);
 }
 
 /*
 ** COMMAND: open
@@ -1412,15 +1479,15 @@
     "web-browser",
   };
   int i;
   int globalFlag = find_option("global","g",0)!=0;
   int unsetFlag = g.argv[1][0]=='u';
+  db_open_config(1);
   db_find_and_open_repository(0);
   if( !g.repositoryOpen ){
     globalFlag = 1;
   }
-  db_open_config();
   if( unsetFlag && g.argc!=3 ){
     usage("PROPERTY ?-global?");
   }
   if( g.argc==2 ){
     for(i=0; i<sizeof(azName)/sizeof(azName[0]); i++){

Modified src/info.c from [cb5129afc5] to [ce7fc1bbb9].

@@ -97,11 +97,11 @@
   i64 fsize;
   if( g.argc!=2 && g.argc!=3 ){
     usage("?FILENAME|ARTIFACT-ID?");
   }
   if( g.argc==3 && (fsize = file_size(g.argv[2]))>0 && (fsize&0x1ff)==0 ){
-    db_open_config();
+    db_open_config(0);
     db_record_repository_filename(g.argv[2]);
     db_open_repository(g.argv[2]);
     printf("project-code: %s\n", db_get("project-code", "<none>"));
     printf("project-name: %s\n", db_get("project-name", "<unnamed>"));
     printf("server-code:  %s\n", db_get("server-code", "<none>"));

Modified src/main.c from [9b1a2a10f7] to [0dbd180aa0].

@@ -52,10 +52,12 @@
 */
 struct Global {
   int argc; char **argv;  /* Command-line arguments to the program */
   int isConst;            /* True if the output is unchanging */
   sqlite3 *db;            /* The connection to the databases */
+  sqlite3 *dbConfig;      /* Separate connection for global_config table */
+  int useAttach;          /* True if global_config is attached to repository */
   int configOpen;         /* True if the config database is open */
   long long int now;      /* Seconds since 1970 */
   int repositoryOpen;     /* True if the main repository database is open */
   char *zRepositoryName;  /* Name of the repository database */
   int localOpen;          /* True if the local database is open */