Diff
Not logged in

Differences From:

File src/db.c part of check-in [29900f98f6] - Print the current status (including the current branch) after moving to the end branch in the "open" command. by drh on 2009-01-29 03:36:00. [view]

To:

File src/db.c part of check-in [7a2c37063a] - merge trunk into creole branch by bob on 2009-09-22 07:49:39. Also file src/db.c part of check-in [fe57da72ab] - Make sure foreign key constraints are disabled when a new database connection is created. by drh on 2009-09-19 17:58:56. [view]

@@ -621,33 +621,63 @@
   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");
+#ifdef __MINGW32__
+  zDbName = mbcsToUtf8(zDbName);
+#endif
+  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 = sqlite3_open(zDbName, &g.db);
-    if( rc!=SQLITE_OK ){
-      db_err(sqlite3_errmsg(g.db));
-    }
-    sqlite3_busy_timeout(g.db, 5000);
+    g.db = openDatabase(zDbName);
     db_connection_init();
   }else{
+#ifdef __MINGW32__
+    zDbName = mbcsToUtf8(zDbName);
+#endif
     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__
@@ -657,14 +687,19 @@
     if( zHome==0 ){
       zHome = getenv("HOMEPATH");
     }
   }
+  if( zHome==0 ){
+    db_err("cannot locate home directory - "
+           "please set the HOMEPATH environment variable");
+  }
 #else
   zHome = getenv("HOME");
-#endif
   if( zHome==0 ){
-    db_err("cannot local home directory");
-  }
+    db_err("cannot locate home directory - "
+           "please set the HOME environment variable");
+  }
+#endif
 #ifdef __MINGW32__
   /* . filenames give some window systems problems and many apps problems */
   zDbName = mprintf("%//_fossil", zHome);
 #else
@@ -672,9 +707,15 @@
 #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;
 }
 
 /*
@@ -690,9 +731,9 @@
   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
@@ -785,9 +826,9 @@
     }
   }
   if( access(zDbName, R_OK) || file_size(zDbName)<1024 ){
     if( access(zDbName, 0) ){
-      fossil_panic("repository does not exists or"
+      fossil_panic("repository does not exist or"
                    " is in an unreadable directory: %s", zDbName);
     }else if( access(zDbName, R_OK) ){
       fossil_panic("read permission denied for repository %s", zDbName);
     }else{
@@ -892,13 +933,15 @@
   );
   if( !setupUserOnly ){
     db_multi_exec(
        "INSERT INTO user(login,pw,cap,info)"
-       "   VALUES('anonymous','anonymous','ghknw','Anon');"
+       "   VALUES('anonymous',hex(randomblob(8)),'ghmncz','Anon');"
        "INSERT INTO user(login,pw,cap,info)"
        "   VALUES('nobody','','jor','Nobody');"
        "INSERT INTO user(login,pw,cap,info)"
-       "   VALUES('developer','','deipt','Dev');"
+       "   VALUES('developer','','dei','Dev');"
+       "INSERT INTO user(login,pw,cap,info)"
+       "   VALUES('reader','','kptw','Reader');"
     );
   }
 }
 
@@ -907,13 +950,14 @@
 ** repository. This function is shared between 'create_repository_cmd'
 ** ('new') and 'reconstruct_cmd' ('reconstruct'), both of which create
 ** new repositories.
 **
-** The makeInitialVersion flag determines whether or not an initial
-** manifest is created.  The makeServerCodes flag determines whether or
+** The zInitialDate parameter determines the date of the initial check-in
+** that is automatically created.  If zInitialDate is 0 then no initial
+** check-in is created. The makeServerCodes flag determines whether or
 ** not server and project codes are invented for this repository.
 */
-void db_initial_setup (int makeInitialVersion, int makeServerCodes){
+void db_initial_setup (const char *zInitialDate, int makeServerCodes){
   char *zDate;
   Blob hash;
   Blob manifest;
 
@@ -931,13 +975,13 @@
   if( !db_is_global("localauth") ) db_set_int("localauth", 0, 0);
   db_create_default_users(0);
   user_select();
 
-  if (makeInitialVersion){
+  if( zInitialDate ){
     int rid;
     blob_zero(&manifest);
     blob_appendf(&manifest, "C initial\\sempty\\scheck-in\n");
-    zDate = db_text(0, "SELECT datetime('now')");
+    zDate = db_text(0, "SELECT datetime(%Q)", zInitialDate);
     zDate[10]='T';
     blob_appendf(&manifest, "D %s\n", zDate);
     blob_appendf(&manifest, "P\n");
     md5sum_init();
@@ -963,16 +1007,20 @@
 ** a copy of an existing project.  This command starts a new project.
 */
 void create_repository_cmd(void){
   char *zPassword;
+  const char *zDate;          /* Date of the initial check-in */
+
+  zDate = find_option("date-override",0,1);
+  if( zDate==0 ) zDate = "now";
   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(1, 1);
+  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));
   zPassword = db_text(0, "SELECT pw FROM user WHERE login=%Q", g.zLogin);
@@ -1096,8 +1144,9 @@
 */
 LOCAL void db_connection_init(void){
   static int once = 1;
   if( once ){
+    sqlite3_exec(g.db, "PRAGMA foreign_keys=OFF;", 0, 0, 0);
     sqlite3_create_function(g.db, "print", -1, SQLITE_UTF8, 0,db_sql_print,0,0);
     sqlite3_create_function(
       g.db, "file_is_selected", 1, SQLITE_UTF8, 0, file_is_selected,0,0
     );
@@ -1128,8 +1177,25 @@
   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){
@@ -1137,9 +1203,11 @@
   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;
   }
@@ -1146,30 +1214,44 @@
   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 FROM global_config WHERE name=%Q", zName);
+    db_swap_connections();
+  }else{
+    db_multi_exec("DELETE FROM 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;
@@ -1184,20 +1266,27 @@
   }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;
@@ -1233,32 +1322,40 @@
     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
 **
-** Usage: %fossil open FILENAME
+** Usage: %fossil open FILENAME ?VERSION? ?--keep?
 **
 ** Open a connection to the local repository in FILENAME.  A checkout
 ** for the repository is created with its root at the working directory.
+** If VERSION is specified then that version is checked out.  Otherwise
+** the latest version is checked out.  No files other than "manifest"
+** and "manifest.uuid" are modified if the --keep option is present.
+**
 ** See also the "close" command.
 */
 void cmd_open(void){
   Blob path;
   int vid;
-  static char *azNewArgv[] = { 0, "update", "--latest", 0 };
+  int keepFlag;
+  static char *azNewArgv[] = { 0, "checkout", "--latest", 0, 0, 0 };
   url_proxy_options();
-  if( g.argc!=3 ){
-    usage("REPOSITORY-FILENAME");
+  keepFlag = find_option("keep",0,0)!=0;
+  if( g.argc!=3 && g.argc!=4 ){
+    usage("REPOSITORY-FILENAME ?VERSION?");
   }
   if( db_open_local() ){
     fossil_panic("already within an open tree rooted at %s", g.zLocalRoot);
   }
@@ -1272,12 +1369,21 @@
                   " WHERE NOT EXISTS(SELECT 1 FROM plink x WHERE x.cid=y.pid)");
   if( vid==0 ){
     db_lset_int("checkout", 1);
   }else{
+    char **oldArgv = g.argv;
+    int oldArgc = g.argc;
     db_lset_int("checkout", vid);
+    azNewArgv[0] = g.argv[0];
     g.argv = azNewArgv;
     g.argc = 3;
-    update_cmd();
+    if( oldArgc==4 ){
+      azNewArgv[g.argc-1] = oldArgv[3];
+    }
+    if( keepFlag ){
+      azNewArgv[g.argc++] = "--keep";
+    }
+    checkout_cmd();
     g.argc = 2;
     info_cmd();
   }
 }
@@ -1328,8 +1434,11 @@
 **
 **    diff-command     External command to run when performing a diff.
 **                     If undefined, the internal text diff will be used.
 **
+**    dont-push        Prevent this repository from pushing from client to
+**                     server.  Useful when setting up a private branch.
+**
 **    editor           Text editor command used for check-in comments.
 **
 **    http-port        The TCP/IP port number to use by the "server"
 **                     and "ui" commands.  Default: 8080
@@ -1365,8 +1474,9 @@
 void setting_cmd(void){
   static const char *azName[] = {
     "autosync",
     "diff-command",
+    "dont-push",
     "editor",
     "gdiff-command",
     "http-port",
     "localauth",
@@ -1378,13 +1488,13 @@
   };
   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 ){
@@ -1409,168 +1519,5 @@
     }
   }else{
     usage("?PROPERTY? ?VALUE?");
   }
-}
-
-/*
-** SQL function to render a UUID as a hyperlink to a page describing
-** that UUID.
-*/
-static void hyperlinkUuidFunc(
-  sqlite3_context *pCxt,     /* function context */
-  int argc,                  /* number of arguments to the function */
-  sqlite3_value **argv       /* values of all function arguments */
-){
-  const char *zUuid;         /* The UUID to render */
-  char *z;                   /* Rendered HTML text */
-
-  zUuid = (const char*)sqlite3_value_text(argv[0]);
-  if( g.okHistory && zUuid && strlen(zUuid)>=10 ){
-    z = mprintf("<tt><a href='%s/info/%t'><span style='font-size:1.5em'>"
-                "%#h</span>%h</a></tt>",
-                g.zBaseURL, zUuid, 10, zUuid, &zUuid[10]);
-    sqlite3_result_text(pCxt, z, -1, free);
-  }else{
-    sqlite3_result_text(pCxt, zUuid, -1, SQLITE_TRANSIENT);
-  }
-}
-
-/*
-** SQL function to render a TAGID as a hyperlink to a page describing
-** that tag.
-*/
-static void hyperlinkTagidFunc(
-  sqlite3_context *pCxt,     /* function context */
-  int argc,                  /* number of arguments to the function */
-  sqlite3_value **argv       /* values of all function arguments */
-){
-  int tagid;                 /* The tagid to render */
-  char *z;                   /* rendered html text */
-
-  tagid = sqlite3_value_int(argv[0]);
-  if( g.okHistory ){
-    z = mprintf("<a href='%s/tagview?tagid=%d'>%d</a>",
-                  g.zBaseURL, tagid, tagid);
-  }else{
-    z = mprintf("%d", tagid);
-  }
-  sqlite3_result_text(pCxt, z, -1, free);
-}
-
-/*
-** SQL function to render a TAGNAME as a hyperlink to a page describing
-** that tag.
-*/
-static void hyperlinkTagnameFunc(
-  sqlite3_context *pCxt,     /* function context */
-  int argc,                  /* number of arguments to the function */
-  sqlite3_value **argv       /* values of all function arguments */
-){
-  const char *zTag;          /* The tag to render */
-  char *z;                   /* rendered html text */
-
-  zTag = (const char*)sqlite3_value_text(argv[0]);
-  if( g.okHistory ){
-    z = mprintf("<a href='%s/tagview?name=%T&raw=y'>%h</a>",
-                  g.zBaseURL, zTag, zTag);
-  }else{
-    z = mprintf("%h", zTag);
-  }
-  sqlite3_result_text(pCxt, z, -1, free);
-}
-
-/*
-** SQL function to escape all characters in a string that have special
-** meaning to HTML.
-*/
-static void htmlizeFunc(
-  sqlite3_context *pCxt,     /* function context */
-  int argc,                  /* number of arguments to the function */
-  sqlite3_value **argv       /* values of all function arguments */
-){
-  const char *zText;         /* Text to be htmlized */
-  char *z;                   /* rendered html text */
-
-  zText = (const char*)sqlite3_value_text(argv[0]);
-  z = htmlize(zText, -1);
-  sqlite3_result_text(pCxt, z, -1, free);
-}
-
-/*
-** This routine is a helper to run an SQL query and table-ize the
-** results.
-**
-** The zSql parameter should be a single, complete SQL statement.
-** Tableized output of the SQL statement is rendered back to the client.
-**
-** The isSafe flag is true if all query results have been processed
-** by routines such as
-**
-**        linkuuid()
-**        linktagid()
-**        linktagname()
-**        htmlize()
-**
-** and are therefore safe for direct rendering.  If isSafe is false,
-** then all characters in the query result that have special meaning
-** to HTML are escaped.
-**
-** Returns SQLITE_OK on success and any other value on error.
-*/
-int db_generic_query_view(const char *zSql, int isSafe){
-  sqlite3_stmt *pStmt;
-  int rc;
-  int nCol, i;
-  int nRow;
-  const char *zRow;
-  static int once = 1;
-
-  /* Install the special functions on the first call to this routine */
-  if( once ){
-    once = 0;
-    sqlite3_create_function(g.db, "linkuuid", 1, SQLITE_UTF8, 0,
-                            hyperlinkUuidFunc, 0, 0);
-    sqlite3_create_function(g.db, "linktagid", 1, SQLITE_UTF8, 0,
-                            hyperlinkTagidFunc, 0, 0);
-    sqlite3_create_function(g.db, "linktagname", 1, SQLITE_UTF8, 0,
-                            hyperlinkTagnameFunc, 0, 0);
-    sqlite3_create_function(g.db, "htmlize", 1, SQLITE_UTF8, 0,
-                            htmlizeFunc, 0, 0);
-  }
-
-  /*
-  ** Use sqlite3_stmt directly rather than going through db_prepare(),
-  ** so that we can treat errors a non-fatal.
-  */
-  rc = sqlite3_prepare(g.db, zSql, -1, &pStmt, 0);
-  if( SQLITE_OK != rc ){
-    @ <span style='color:red'>db_generic_query_view() SQL error:
-    @ %h(sqlite3_errmsg(g.db))</span>
-    return rc;
-  }
-  nCol = sqlite3_column_count(pStmt);
-  @ <table class='fossil_db_generic_query_view'><tbody>
-  @ <tr class='header'>
-  for(i=0; i<nCol; ++i){
-    @ <td>%h(sqlite3_column_name(pStmt,i))</td>
-  }
-  @ </tr>
-
-  nRow = 0;
-  while( SQLITE_ROW==sqlite3_step(pStmt) ){
-    const char *azClass[] = { "even", "odd" };
-    @ <tr class='%s(azClass[(nRow++)&1])'>
-      for(i=0; i<nCol; i++){
-        zRow = (char const*)sqlite3_column_text(pStmt,i);
-        if( isSafe ){
-          @ <td>%s(zRow)</td>
-        }else{
-          @ <td>%h(zRow)</td>
-        }
-      }
-    @ </tr>
-  }
-  @ </tbody></table>
-  sqlite3_finalize(pStmt);
-  return SQLITE_OK;
 }