]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Merging in OSX customizations:
authoradam <adam@noemail.net>
Tue, 3 Nov 2009 22:34:35 +0000 (22:34 +0000)
committeradam <adam@noemail.net>
Tue, 3 Nov 2009 22:34:35 +0000 (22:34 +0000)
A ext/sqlrr/sqlrr.[ch]
-- add sql recording/replay support (very experimental extension -- pre alpha)

M ext/rtree/rtree.c
-- fix cast compiler warning

M src/func.c
-- substring backward compatiblity <rdar://problem/6778339>

M src/legacy.c
-- replay recorder calls

M src/main.c
-- if SQLITE_ENABLE_AUTO_PROFILE, call sqlite3_profile when env(SQLITE_AUTO_PROFILE)
-- replay recorder calls

M src/mem1.c
-- use custom malloc zone on single core systems

M src/os_unix.c
-- hostid via gethostuuid
-- read-only file system support for proxy locking
-- improved handling of simulated shared lock on AFP/SMB on 64 bit systems
-- disable whole-file-lock NFS file locking
-- fix for downgrading from exclusive to shared lock on NFS
-- proxy lock breaking & recreating local lock files
-- support SQLITE_OPEN_AUTOPROXY flag
-- only write 1st byte into new zero-length files if fs is DOS
-- cache fs type info in unixFile struct
-- force use of fsync() even if fdatasync() is available
-- remove flock locking style support for SMB
-- replace strcpy with strlcpy

M src/prepare.c
-- replay recorder calls

M src/sqlite.h.in
-- added SQLITE_OPEN_AUTOPROXY

M src/sqliteInt.h
-- include replay recorder header

M src/vdbeapi.c
-- checking for NULL statement for <rdar://6646331>
-- replay recorder calls

FossilOrigin-Name: 941a01eb868815f566539e9ab21f807d9e798e40

28 files changed:
Makefile.in
ext/rtree/rtree.c
ext/sqlrr/README.txt [new file with mode: 0644]
ext/sqlrr/sqlrr.c [new file with mode: 0644]
ext/sqlrr/sqlrr.h [new file with mode: 0644]
manifest
manifest.uuid
src/func.c
src/legacy.c
src/main.c
src/mem1.c
src/os_unix.c
src/pragma.c
src/prepare.c
src/sqlite.h.in
src/test1.c
src/test_backup.c
src/vdbeapi.c
test/attach.test
test/lock_proxy.test [new file with mode: 0644]
test/main.test
test/malloc_common.tcl
test/memdb.test
test/memsubsys1.test
test/pragma.test
test/quick.test
test/tester.tcl
tool/mksqlite3c.tcl

index 27687f927cf752b0086f78028168d30c024be7e5..e54834d81acfb5959b204edd36362c7e1292cbe5 100644 (file)
@@ -326,6 +326,9 @@ SRC += \
 SRC += \
   $(TOP)/ext/rtree/rtree.h \
   $(TOP)/ext/rtree/rtree.c
+SRC += \
+  $(TOP)/ext/sqlrr/sqlrr.h \
+  $(TOP)/ext/sqlrr/sqlrr.c
 
 # Source code to the library files needed by the test fixture
 #
@@ -434,7 +437,9 @@ HDR += \
   $(TOP)/ext/rtree/rtree.h
 HDR += \
   $(TOP)/ext/icu/sqliteicu.h
-
+HDR += \
+  $(TOP)/ext/sqlrr/sqlrr.h
+  
 # If using the amalgamation, use sqlite3.c directly to build the test
 # fixture.  Otherwise link against libsqlite3.la.  (This distinction is
 # necessary because the test fixture requires non-API symbols which are
index f4d74bf0e73027a30ebc47f809ce9d4c191c70ac..8b4cd43294541df5f10ca2a462f21a377695e1c5 100644 (file)
@@ -65,6 +65,7 @@
 
 #include <string.h>
 #include <assert.h>
+#include <stdint.h>
 
 #ifndef SQLITE_AMALGAMATION
 typedef sqlite3_int64 i64;
@@ -2670,7 +2671,7 @@ static int rtreeInit(
   Rtree *pRtree;
   int nDb;              /* Length of string argv[1] */
   int nName;            /* Length of string argv[2] */
-  int eCoordType = (int)pAux;
+  int eCoordType = (int)(intptr_t)pAux;
 
   const char *aErrMsg[] = {
     0,                                                    /* 0 */
diff --git a/ext/sqlrr/README.txt b/ext/sqlrr/README.txt
new file mode 100644 (file)
index 0000000..6d39063
--- /dev/null
@@ -0,0 +1,54 @@
+
+sqlrr - 10/19/2009
+
+SQL Replay Recording
+
+SUMMARY
+-------------------------------------------------------
+This extension enables recording sqlite API calls that access the database so that they can be replayed or examined.
+
+USAGE
+------------------------------------------------------
+Recording is enabled by compiling sqlite with symbolic constant SQLITE_ENABLE_SQLRR defined.  
+By default logs are written to /tmp/<databasename>_<pid>_<connection_number>.sqlrr, to choose another directory, set the environment variable SQLITE_REPLAY_RECORD_DIR to that path.
+
+FILE FORMAT
+-----------------------------------------------------
+ file:                 <header>[<sql-command>]*
+ header:                       <signature><format-version>
+   signature:          SQLRR (5 bytes)
+   format-version:     n (1 byte)
+ sql-command:          <timestamp><type><arg-data>
+   timestamp:       n (16 bytes)
+   type:                       n (1 byte)
+        open           0
+        close          1
+        exec           8
+        bind-text      16
+        bind-double    17
+        bind-int       18
+        bind-null      19
+        bind-value     20
+        bind-clear     21
+        prep           32
+        step           33
+        reset          34
+        finalize       35
+
+  open-arg-data:               <connection><path><flags>
+  close-arg-data:              <connection>
+  exec-arg-data:               <connection><len><statement-text>
+  bind-text-arg-data:  <statement-ref><index><len><data>
+  bind-double-arg-data:        <statement-ref><index><data>
+  bind-int-arg-data:   <statement-ref><index><data>
+  bind-null-arg-data:  <statement-ref><index>
+  bind-value-arg-data: <statement-ref><index><len><data> ???
+  bind-clear-arg-data: <statement-ref>
+  prep-arg-data:               <connection><len><statement-text>
+  step-arg-data:               <statement-ref>
+  reset-arg-data:              <statement-ref>
+  finalize-arg-data:   <statement-ref>
+
+NOTES
diff --git a/ext/sqlrr/sqlrr.c b/ext/sqlrr/sqlrr.c
new file mode 100644 (file)
index 0000000..7550a64
--- /dev/null
@@ -0,0 +1,581 @@
+/*
+ *  sqlrr.c
+ */
+
+#include "sqlrr.h"
+
+#if defined(SQLITE_ENABLE_SQLRR)
+
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+#include <sys/param.h>
+#include <errno.h>
+#include <pthread.h>
+#include <libkern/OSAtomic.h>
+
+#include "sqliteInt.h"
+#include "vdbeInt.h"
+
+#define LOGSUFFIXLEN 48
+
+/* 
+ * Data types 
+ */
+typedef struct SRRLogRef SRRLogRef;
+struct SRRLogRef {
+    int fd;
+    sqlite3 *db;
+    const char *dbPath;
+    char *logPath;
+    int connection;
+    int depth;
+    SRRLogRef *nextRef;
+};
+
+/* 
+ * Globals 
+ */
+SRRLogRef *logRefHead = NULL;
+int dbLogCount = 0;
+static int srr_enabled = 1;
+pthread_mutex_t srr_log_mutex;
+static volatile int32_t srr_initialized = 0;
+
+/* 
+ * Log management 
+ */
+extern void SRRecInitialize() {
+    int go = OSAtomicCompareAndSwap32Barrier(0, 1, &srr_initialized); 
+    if( go ){
+        pthread_mutex_init(&srr_log_mutex, NULL);
+    }
+}
+
+static SRRLogRef *createLog(sqlite3 *db, const char *dbPath) {
+    SRRLogRef *ref = NULL;
+    char *baseDir = getenv("SQLITE_REPLAY_RECORD_DIR");
+    char logPath[MAXPATHLEN] = "";
+    char suffix[LOGSUFFIXLEN] = "";
+    const char *dbName = dbPath;
+    int len = 0;
+    int index = 0;
+    int fd = -1;
+    size_t out;
+    unsigned char version = SRR_FILE_VERSION;
+    
+    SRRecInitialize();
+
+    /* construct the path for the log file 
+     * ${SQLITE_REPLAY_DIR}/<dbname>_<pid>_<connection_number>.sqlrr
+     */
+    if (baseDir == NULL) {
+        baseDir = "/tmp"; /* getenv(TMPDIR) */
+    }
+    len = strlen(baseDir);
+    strlcat(logPath, baseDir, MAXPATHLEN);
+    if ((len>0) && (baseDir[len-1] != '/')) {
+        strlcat(logPath, "/", MAXPATHLEN);
+    }
+    len = strlen(dbPath);
+    for (index = len-2; index >= 0; index --){
+        if (dbPath[index] == '/') {
+            dbName = &dbPath[index+1];
+            break;
+        }
+    }
+    strlcat(logPath, dbName, MAXPATHLEN);
+    int cNum = ++dbLogCount;
+    snprintf(suffix, sizeof(suffix), "_%d_%d_XXXX.sqlrr", getpid(), cNum);
+    len = strlcat(logPath, suffix, MAXPATHLEN);
+    /* make it unique if we have the space */
+    if ((len + 1) < MAXPATHLEN) {
+        fd = mkstemps(logPath, 6);
+    } else {
+        fprintf(stderr, "Failed to create sqlite replay log path for %s [%s]\n", dbPath, logPath);
+        return NULL;
+    }
+    if (fd == -1) {
+        fprintf(stderr, "Failed to create sqlite replay log file for %s with path %s [%s]\n", dbPath, logPath, strerror(errno));
+        return NULL;
+    }
+    fprintf(stdout, "Writing sqlite replay log file %s\n", logPath);
+    out = write(fd, SRR_FILE_SIGNATURE, SRR_FILE_SIGNATURE_LEN);
+    if (out!=-1) {
+        out = write(fd, &version, 1);
+    }
+    if (out == -1){
+        fprintf(stderr, "Write failure on log [%s]: %s\n", logPath, strerror(errno));
+        close(fd);
+        return NULL;
+    }
+
+    len = strlen(logPath) + 1;
+    ref = (SRRLogRef *)malloc(sizeof(SRRLogRef));
+
+    ref->db = db;
+    ref->dbPath = dbPath;
+    ref->logPath = (char *)malloc(len * sizeof(char));
+    strlcpy(ref->logPath, logPath, len);
+    ref->fd = fd;
+    ref->connection = cNum;
+    ref->depth = 0;
+    
+    pthread_mutex_lock(&srr_log_mutex);
+    ref->nextRef = logRefHead;
+    logRefHead = ref;
+    pthread_mutex_unlock(&srr_log_mutex);
+    return ref;
+}
+
+static void closeLog(sqlite3 *db) {
+    SRRLogRef *ref = NULL;
+    SRRLogRef *lastRef = NULL;
+
+    pthread_mutex_lock(&srr_log_mutex);
+    for (ref = logRefHead; ref != NULL; ref = ref->nextRef) {
+        if (ref->db == db) {
+            if (lastRef == NULL) {
+                logRefHead = ref->nextRef;
+            } else {
+                lastRef->nextRef = ref->nextRef;
+            }
+        }
+    }
+    pthread_mutex_unlock(&srr_log_mutex);
+
+    if (ref != NULL) {
+        fprintf(stdout, "Closing sqlite replay log file %s\n", ref->logPath);
+        close(ref->fd);
+        free(ref->logPath);
+        free(ref);
+    }
+}
+
+static SRRLogRef *getLog(sqlite3 *db) {
+    pthread_mutex_lock(&srr_log_mutex);
+    SRRLogRef *ref = logRefHead;
+    for (ref = logRefHead; ref != NULL; ref = ref->nextRef) {
+        if (ref->db == db) {
+            pthread_mutex_unlock(&srr_log_mutex);
+            return ref;
+        }
+    }
+    pthread_mutex_unlock(&srr_log_mutex);
+    return NULL;
+}
+
+
+/*
+ * SQLite recording API
+ */
+void SQLiteReplayRecorder(int flag) {
+    srr_enabled = flag;
+}
+
+// open-arg-data:              <connection><len><path><flags>
+void _SRRecOpen(sqlite3 *db, const char *path, int flags) {
+    if (!srr_enabled) return;
+    if (db) {
+        SRRLogRef *ref = createLog(db, path);
+        if (ref) {
+            SRRCommand code = SRROpen;
+            int len = strlen(path);
+            struct timeval tv;
+            size_t out;
+            
+            gettimeofday(&tv, NULL);
+            out = write(ref->fd, &tv, sizeof(tv));
+            if (out!=-1) { out=write(ref->fd, &code, sizeof(SRRCommand)); }
+            if (out!=-1) { out=write(ref->fd, &(ref->connection), sizeof(ref->connection)); }
+            if (out!=-1) { out=write(ref->fd, &len, sizeof(len)); }
+            if (out!=-1) { out=write(ref->fd, path, len); }
+            if (out!=-1) { out=write(ref->fd, &flags, sizeof(flags)); }
+            if (out==-1) {
+                fprintf(stderr, "Error writing open to log file [%s]: %s\n", ref->logPath, strerror(errno));
+                closeLog(db);
+            }
+        }
+    }
+}
+
+//close-arg-data:              <connection>
+void SRRecClose(sqlite3 *db) {
+    if (!srr_enabled) return;
+    if (db) {
+        SRRLogRef *ref = getLog(db);
+        if (ref) {
+            SRRCommand code = SRRClose;
+            struct timeval tv;
+            size_t out;
+            
+            gettimeofday(&tv, NULL);
+            out = write(ref->fd, &tv, sizeof(tv));
+            if (out!=-1) { out = write(ref->fd, &code, sizeof(SRRCommand)); }
+            if (out!=-1) { out = write(ref->fd, &(ref->connection), sizeof(ref->connection)); }
+            if (out==-1) {
+                fprintf(stderr, "Error writing close to log file [%s]: %s\n", ref->logPath, strerror(errno));
+            }
+            closeLog(db);
+        }
+    }
+}
+
+// exec-arg-data:              <connection><len><statement-text>
+void SRRecExec(sqlite3 *db, const char *sql) {
+    if (!srr_enabled) return;
+    if (db) {
+        SRRLogRef *ref = getLog(db);
+        if (ref) {
+            if (ref->depth == 0) {
+                SRRCommand code = SRRExec;
+                int len = strlen(sql);
+                struct timeval tv;
+                size_t out;
+                
+                ref->depth = 1;
+                gettimeofday(&tv, NULL);
+                out = write(ref->fd, &tv, sizeof(tv));
+                if (out!=-1) { out = write(ref->fd, &code, sizeof(SRRCommand)); }
+                if (out!=-1) { out = write(ref->fd, &(ref->connection), sizeof(ref->connection)); }
+                if (out!=-1) { out = write(ref->fd, &len, sizeof(len)); }
+                if (out!=-1) { out = write(ref->fd, sql, len); }
+                if (out==-1) {
+                    fprintf(stderr, "Error writing exec to log file [%s]: %s\n", ref->logPath, strerror(errno));
+                    closeLog(db);
+                }
+            } else {
+                ref->depth ++;
+            }
+        }
+    }
+}
+
+void SRRecExecEnd(sqlite3 *db) {
+    if (!srr_enabled) return;
+    if (db) {
+        SRRLogRef *ref = getLog(db);
+        if (ref) {
+            ref->depth --;
+        }
+    }
+}
+            
+// prep-arg-data:              <connection><len><statement-text><savesql><statement-ref>
+void _SRRecPrepare(sqlite3 *db, const char *sql, int nBytes, int saveSql, sqlite3_stmt *pStmt) {
+    if (!srr_enabled) return;
+    if ((db!=NULL)&&(pStmt!=NULL)) {
+        SRRLogRef *ref = getLog(db);
+        if (ref && (ref->depth == 0)) {
+            SRRCommand code = SRRPrepare;
+            struct timeval tv;
+            size_t out;
+            int sqlLen = nBytes;
+
+            if (sqlLen == -1) {
+                sqlLen = strlen(sql);
+            }
+            
+            gettimeofday(&tv, NULL);
+            out = write(ref->fd, &tv, sizeof(tv));
+            if (out!=-1) { out = write(ref->fd, &code, sizeof(SRRCommand)); }
+            if (out!=-1) { out = write(ref->fd, &(ref->connection), sizeof(ref->connection)); }
+            if (out!=-1) { out = write(ref->fd, &sqlLen, sizeof(sqlLen)); }
+            if (out!=-1) { out = write(ref->fd, sql, sqlLen); }
+            if (out!=-1) { out = write(ref->fd, &saveSql, sizeof(saveSql)); }
+            if (out!=-1) { 
+                int64_t stmtInt = (int64_t)((intptr_t)(pStmt));
+                out = write(ref->fd, &stmtInt, sizeof(int64_t)); 
+            }
+            if (out==-1) {
+                fprintf(stderr, "Error writing prepare to log file [%s]: %s\n", ref->logPath, strerror(errno));
+                closeLog(db);
+            }
+        }
+    }
+}
+
+//step-arg-data:               <statement-ref>
+void SRRecStep(sqlite3_stmt *pStmt) {
+    if (!srr_enabled) return;
+    if(pStmt!=NULL) {
+        Vdbe *v = (Vdbe *)pStmt;
+        SRRLogRef *ref = getLog(v->db);
+        if (ref) {
+            if (ref->depth == 0) {
+                SRRCommand code = SRRStep;
+                struct timeval tv;
+                size_t out;
+                
+                ref->depth = 1;
+                gettimeofday(&tv, NULL);
+                out = write(ref->fd, &tv, sizeof(tv));
+                if (out!=-1) { out = write(ref->fd, &code, sizeof(SRRCommand)); }
+                if (out!=-1) { 
+                    int64_t stmtInt = (int64_t)((intptr_t)(pStmt));
+                    out = write(ref->fd, &stmtInt, sizeof(int64_t)); 
+                }
+                if (out==-1) {
+                    fprintf(stderr, "Error writing step to log file [%s]: %s\n", ref->logPath, strerror(errno));
+                    closeLog(ref->db);
+                }
+            } else {
+                ref->depth ++;
+            }            
+        }
+    }
+}
+
+void SRRecStepEnd(sqlite3_stmt *pStmt) {
+    if (!srr_enabled) return;
+    if(pStmt!=NULL) {
+        Vdbe *v = (Vdbe *)pStmt;
+        SRRLogRef *ref = getLog(v->db);
+        if (ref) {
+            ref->depth --;
+        }
+    }
+}
+
+// reset-arg-data:             <statement-ref>
+void SRRecReset(sqlite3_stmt *pStmt) {
+    if (!srr_enabled) return;
+    if(pStmt!=NULL) {
+        Vdbe *v = (Vdbe *)pStmt;
+        SRRLogRef *ref = getLog(v->db);
+        if (ref && (ref->depth == 0)) {
+            SRRCommand code = SRRReset;
+            struct timeval tv;
+            size_t out;
+            
+            gettimeofday(&tv, NULL);
+            out = write(ref->fd, &tv, sizeof(tv));
+            if (out!=-1) { out = write(ref->fd, &code, sizeof(SRRCommand)); }
+            if (out!=-1) { 
+                int64_t stmtInt = (int64_t)((intptr_t)(pStmt));
+                out = write(ref->fd, &stmtInt, sizeof(int64_t)); 
+            }
+            if (out==-1) {
+                fprintf(stderr, "Error writing reset to log file [%s]: %s\n", ref->logPath, strerror(errno));
+                closeLog(ref->db);
+            }
+        }
+    }
+}
+
+// finalize-arg-data:  <statement-ref>
+void SRRecFinalize(sqlite3_stmt *pStmt) {
+    if (!srr_enabled) return;
+    if(pStmt!=NULL) {
+        Vdbe *v = (Vdbe *)pStmt;
+        SRRLogRef *ref = getLog(v->db);
+        if (ref && (ref->depth == 0)) {
+            SRRCommand code = SRRFinalize;
+            struct timeval tv;
+            size_t out;
+            
+            gettimeofday(&tv, NULL);
+            out = write(ref->fd, &tv, sizeof(tv));
+            if (out!=-1) { out = write(ref->fd, &code, sizeof(SRRCommand)); }
+            if (out!=-1) { 
+                int64_t stmtInt = (int64_t)((intptr_t)(pStmt));
+                out = write(ref->fd, &stmtInt, sizeof(int64_t)); 
+            }
+            if (out==-1) {
+                fprintf(stderr, "Error writing finalize to log file [%s]: %s\n", ref->logPath, strerror(errno));
+                closeLog(ref->db);
+            }
+        }
+    }
+}
+
+// bind-text-arg-data: <statement-ref><index><len><data>
+void SRRecBindText(sqlite3_stmt *pStmt, int i, const char *zData, int64_t nData) {
+    if (!srr_enabled) return;
+    if(pStmt!=NULL) {
+        Vdbe *v = (Vdbe *)pStmt;
+        SRRLogRef *ref = getLog(v->db);
+        if (ref && (ref->depth == 0)) {
+            SRRCommand code = SRRBindText;
+            struct timeval tv;
+            size_t out;
+            int64_t textLen = nData;
+            if (textLen == -1) {
+                textLen = strlen(zData);
+            }
+            
+            gettimeofday(&tv, NULL);
+            out = write(ref->fd, &tv, sizeof(tv));
+            if (out!=-1) { out = write(ref->fd, &code, sizeof(SRRCommand)); }
+            if (out!=-1) { 
+                int64_t stmtInt = (int64_t)((intptr_t)(pStmt));
+                out = write(ref->fd, &stmtInt, sizeof(int64_t)); 
+            }
+            if (out!=-1) { out = write(ref->fd, &i, sizeof(i)); }
+            if (out!=-1) { out = write(ref->fd, &textLen, sizeof(textLen)); }
+            if (out!=-1) { out = write(ref->fd, zData, textLen); }
+            if (out==-1) {
+                fprintf(stderr, "Error writing bind text to log file [%s]: %s\n", ref->logPath, strerror(errno));
+                closeLog(ref->db);
+            }
+        }
+    }
+}
+
+// bind-blob-arg-data: <statement-ref><index><len>[<data>]
+void SRRecBindBlob(sqlite3_stmt *pStmt, int i, const char *zData, int64_t nData) {
+    if (!srr_enabled) return;
+    if(pStmt!=NULL) {
+        Vdbe *v = (Vdbe *)pStmt;
+        SRRLogRef *ref = getLog(v->db);
+        if (ref && (ref->depth == 0)) {
+            SRRCommand code = SRRBindBlob;
+            struct timeval tv;
+            size_t out;
+            
+            gettimeofday(&tv, NULL);
+            out = write(ref->fd, &tv, sizeof(tv));
+            if (out!=-1) { out = write(ref->fd, &code, sizeof(SRRCommand)); }
+            if (out!=-1) { 
+                int64_t stmtInt = (int64_t)((intptr_t)(pStmt));
+                out = write(ref->fd, &stmtInt, sizeof(int64_t)); 
+            }
+            if (out!=-1) { out = write(ref->fd, &i, sizeof(i)); }
+            if (zData == NULL) {
+                int64_t negNData = -nData;
+                if (out!=-1) { out = write(ref->fd, &negNData, sizeof(negNData)); }
+            } else {
+                if (out!=-1) { out = write(ref->fd, &nData, sizeof(nData)); }
+                if (out!=-1) { out = write(ref->fd, zData, nData); }
+            }
+            if (out==-1) {
+                fprintf(stderr, "Error writing bind blob to log file [%s]: %s\n", ref->logPath, strerror(errno));
+                closeLog(ref->db);
+            }
+        }
+    }
+}
+
+// bind-double-arg-data:       <statement-ref><index><data>
+void SRRecBindDouble(sqlite3_stmt *pStmt, int i, double value) {
+    if (!srr_enabled) return;
+    if(pStmt!=NULL) {
+        Vdbe *v = (Vdbe *)pStmt;
+        SRRLogRef *ref = getLog(v->db);
+        if (ref && (ref->depth == 0)) {
+            SRRCommand code = SRRBindDouble;
+            struct timeval tv;
+            size_t out;
+            
+            gettimeofday(&tv, NULL);
+            out = write(ref->fd, &tv, sizeof(tv));
+            if (out!=-1) { out = write(ref->fd, &code, sizeof(SRRCommand)); }
+            if (out!=-1) { 
+                int64_t stmtInt = (int64_t)((intptr_t)(pStmt));
+                out = write(ref->fd, &stmtInt, sizeof(int64_t)); 
+            }
+            if (out!=-1) { out = write(ref->fd, &i, sizeof(i)); }
+            if (out!=-1) { out = write(ref->fd, &value, sizeof(value)); }
+            if (out==-1) {
+                fprintf(stderr, "Error writing bind double to log file [%s]: %s\n", ref->logPath, strerror(errno));
+                closeLog(ref->db);
+            }
+        }
+    }
+}
+
+// bind-int-arg-data:  <statement-ref><index><data>
+void SRRecBindInt64(sqlite3_stmt *pStmt, int i, int64_t value) {
+    if (!srr_enabled) return;
+    if(pStmt!=NULL) {
+        Vdbe *v = (Vdbe *)pStmt;
+        SRRLogRef *ref = getLog(v->db);
+        if (ref && (ref->depth == 0)) {
+            SRRCommand code = SRRBindInt;
+            struct timeval tv;
+            size_t out;
+            
+            gettimeofday(&tv, NULL);
+            out = write(ref->fd, &tv, sizeof(tv));
+            if (out!=-1) { out = write(ref->fd, &code, sizeof(SRRCommand)); }
+            if (out!=-1) { 
+                int64_t stmtInt = (int64_t)((intptr_t)(pStmt));
+                out = write(ref->fd, &stmtInt, sizeof(int64_t)); 
+            }
+            if (out!=-1) { out = write(ref->fd, &i, sizeof(i)); }
+            if (out!=-1) { out = write(ref->fd, &value, sizeof(value)); }
+            if (out==-1) {
+                fprintf(stderr, "Error writing bind int to log file [%s]: %s\n", ref->logPath, strerror(errno));
+                closeLog(ref->db);
+            }
+        }
+    }
+}
+
+// bind-null-arg-data: <statement-ref><index>
+void SRRecBindNull(sqlite3_stmt *pStmt, int i) {
+    if (!srr_enabled) return;
+    if(pStmt!=NULL) {
+        Vdbe *v = (Vdbe *)pStmt;
+        SRRLogRef *ref = getLog(v->db);
+        if (ref && (ref->depth == 0)) {
+            SRRCommand code = SRRBindNull;
+            struct timeval tv;
+            size_t out;
+            
+            gettimeofday(&tv, NULL);
+            out = write(ref->fd, &tv, sizeof(tv));
+            if (out!=-1) { out = write(ref->fd, &code, sizeof(SRRCommand)); }
+            if (out!=-1) { 
+                int64_t stmtInt = (int64_t)((intptr_t)(pStmt));
+                out = write(ref->fd, &stmtInt, sizeof(int64_t)); 
+            }
+            if (out!=-1) { out = write(ref->fd, &i, sizeof(i)); }
+            if (out==-1) {
+                fprintf(stderr, "Error writing bind null to log file [%s]: %s\n", ref->logPath, strerror(errno));
+                closeLog(ref->db);
+            }
+        }
+    }
+}
+
+// bind-value-arg-data:        <statement-ref><index><len><data> ???
+void SRRecBindValue(sqlite3_stmt *pStmt, int i, const sqlite3_value *value) {
+    if (!srr_enabled) return;
+    if(pStmt!=NULL) {
+        Vdbe *v = (Vdbe *)pStmt;
+        SRRLogRef *ref = getLog(v->db);
+        if (ref && (ref->depth == 0)) {
+            fprintf(stderr, "SRRecBindValue(sqlite3_bind_value) is not yet supported, closing [%s]: %s\n", ref->logPath, strerror(errno));
+            closeLog(ref->db);
+        }
+    }
+}
+
+// bind-clear-arg-data:        <statement-ref>
+void SRRecClearBindings(sqlite3_stmt *pStmt) {
+    if (!srr_enabled) return;
+    if(pStmt!=NULL) {
+        Vdbe *v = (Vdbe *)pStmt;
+        SRRLogRef *ref = getLog(v->db);
+        if (ref && (ref->depth == 0)) {
+            SRRCommand code = SRRBindClear;
+            struct timeval tv;
+            size_t out;
+            
+            gettimeofday(&tv, NULL);
+            out = write(ref->fd, &tv, sizeof(tv));
+            if (out!=-1) { out = write(ref->fd, &code, sizeof(SRRCommand)); }
+            if (out!=-1) { 
+                int64_t stmtInt = (int64_t)((intptr_t)(pStmt));
+                out = write(ref->fd, &stmtInt, sizeof(int64_t)); 
+            }
+            if (out==-1) {
+                fprintf(stderr, "Error writing clear bindings to log file [%s]: %s\n", ref->logPath, strerror(errno));
+                closeLog(ref->db);
+            }
+        }
+    }
+}
+
+#endif /* SQLITE_ENABLE_SQLRR */
diff --git a/ext/sqlrr/sqlrr.h b/ext/sqlrr/sqlrr.h
new file mode 100644 (file)
index 0000000..2dfbe07
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ *  sqlrr.h
+ */
+
+#ifndef _SQLRR_H_
+#define _SQLRR_H_
+
+/*
+** Header constants
+*/
+#define SRR_FILE_SIGNATURE       "SQLRR"
+#define SRR_FILE_SIGNATURE_LEN   5
+#define SRR_FILE_VERSION         0x1 
+#define SRR_FILE_VERSION_LEN     1
+
+#if defined(SQLITE_ENABLE_SQLRR)
+
+#include "sqlite3.h"
+#include <sys/types.h>
+
+#define SRRecOpen(A,B,C)        if(!rc){_SRRecOpen(A,B,C);}
+#define SRRecPrepare(A,B,C,D,E) if(!rc){_SRRecPrepare(A,B,C,D,E);}
+
+typedef enum {
+    SRROpen = 0,
+    SRRClose = 1,
+    SRRExec = 8,
+    SRRBindText = 16,
+    SRRBindBlob = 17,
+    SRRBindDouble = 18,
+    SRRBindInt = 19,
+    SRRBindNull = 20,
+    SRRBindValue = 21,
+    SRRBindClear = 22,
+    SRRPrepare = 32,
+    SRRStep = 33,
+    SRRReset = 34,
+    SRRFinalize = 35
+} SRRCommand;
+
+extern void SQLiteReplayRecorder(int flag);
+extern void _SRRecOpen(sqlite3 *db, const char *path, int flags);
+extern void SRRecClose(sqlite3 *db);
+extern void SRRecExec(sqlite3 *db, const char *sql);
+extern void SRRecExecEnd(sqlite3 *db);
+extern void _SRRecPrepare(sqlite3 *db, const char *sql, int nBytes, int saveSql, sqlite3_stmt *stmt);
+extern void SRRecStep(sqlite3_stmt *pStmt);
+extern void SRRecStepEnd(sqlite3_stmt *pStmt);
+extern void SRRecReset(sqlite3_stmt *pStmt);
+extern void SRRecFinalize(sqlite3_stmt *pStmt);
+extern void SRRecBindText(sqlite3_stmt *pStmt, int i, const char *zData, int64_t nData);
+extern void SRRecBindBlob(sqlite3_stmt *pStmt, int i, const char *zData, int64_t nData);
+extern void SRRecBindDouble(sqlite3_stmt *pStmt, int i, double value);
+extern void SRRecBindInt64(sqlite3_stmt *pStmt, int i, int64_t value);
+extern void SRRecBindNull(sqlite3_stmt *pStmt, int i);
+extern void SRRecBindValue(sqlite3_stmt *pStmt, int i, const sqlite3_value *value);
+extern void SRRecClearBindings(sqlite3_stmt *pStmt);
+
+#endif /* defined(SQLITE_ENABLE_SQLRR) */
+
+#endif /* _SQLRR_H_ */
\ No newline at end of file
index b37336fceed5ecf415b76a296bde3096a8f63348..f916af6f5b8a920ebe92a44be344942d126d9500 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,7 +1,7 @@
-C Fix\scompiler\swarnings\son\sMSVC\sbuild.
-D 2009-11-03T19:42:31
+C Merging\sin\sOSX\scustomizations:\n\nA\sext/sqlrr/sqlrr.[ch]\n--\sadd\ssql\srecording/replay\ssupport\s(very\sexperimental\sextension\s--\spre\salpha)\n\nM\sext/rtree/rtree.c\n--\sfix\scast\scompiler\swarning\n\nM\ssrc/func.c\n--\ssubstring\sbackward\scompatiblity\s<rdar://problem/6778339>\s\n\nM\ssrc/legacy.c\n--\sreplay\srecorder\scalls\n\nM\ssrc/main.c\n--\sif\sSQLITE_ENABLE_AUTO_PROFILE,\scall\ssqlite3_profile\swhen\senv(SQLITE_AUTO_PROFILE)\s\s\s\n--\sreplay\srecorder\scalls\n\nM\ssrc/mem1.c\n--\suse\scustom\smalloc\szone\son\ssingle\score\ssystems\n\nM\ssrc/os_unix.c\n--\shostid\svia\sgethostuuid\n--\sread-only\sfile\ssystem\ssupport\sfor\sproxy\slocking\n--\simproved\shandling\sof\ssimulated\sshared\slock\son\sAFP/SMB\son\s64\sbit\ssystems\n--\sdisable\swhole-file-lock\sNFS\sfile\slocking\n--\sfix\sfor\sdowngrading\sfrom\sexclusive\sto\sshared\slock\son\sNFS\n--\sproxy\slock\sbreaking\s&\srecreating\slocal\slock\sfiles\n--\ssupport\sSQLITE_OPEN_AUTOPROXY\sflag\n--\sonly\swrite\s1st\sbyte\sinto\snew\szero-length\sfiles\sif\sfs\sis\sDOS\n--\scache\sfs\stype\sinfo\sin\sunixFile\sstruct\n--\sforce\suse\sof\sfsync()\seven\sif\sfdatasync()\sis\savailable\n--\sremove\sflock\slocking\sstyle\ssupport\sfor\sSMB\n--\sreplace\sstrcpy\swith\sstrlcpy\n\nM\ssrc/prepare.c\n--\sreplay\srecorder\scalls\n\nM\ssrc/sqlite.h.in\n--\sadded\sSQLITE_OPEN_AUTOPROXY\n\nM\ssrc/sqliteInt.h\n--\sinclude\sreplay\srecorder\sheader\n\nM\ssrc/vdbeapi.c\n--\schecking\sfor\sNULL\sstatement\sfor\s<rdar://6646331>\s\n--\sreplay\srecorder\scalls
+D 2009-11-03T22:34:36
 F Makefile.arm-wince-mingw32ce-gcc fcd5e9cd67fe88836360bb4f9ef4cb7f8e2fb5a0
-F Makefile.in a77dfde96ad86aafd3f71651a4333a104debe86a
+F Makefile.in 3400e494a10756968f8fcd8c3d553d279a063436
 F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654
 F Makefile.vxworks 10010ddbf52e2503c7c49c7c0b7c7a096f8638a6
 F README cd04a36fbc7ea56932a4052d7d0b7f09f27c33d6
@@ -72,7 +72,7 @@ F ext/icu/README.txt 3b130aa66e7a681136f6add198b076a2f90d1e33
 F ext/icu/icu.c 12e763d288d23b5a49de37caa30737b971a2f1e2
 F ext/icu/sqliteicu.h 728867a802baa5a96de7495e9689a8e01715ef37
 F ext/rtree/README 6315c0d73ebf0ec40dedb5aa0e942bc8b54e3761
-F ext/rtree/rtree.c 5275d8f851c366f4a01e8a0c63aa0af492567f28
+F ext/rtree/rtree.c 375a762b17e307d1f9574b2563a0144ced58b021
 F ext/rtree/rtree.h 834dbcb82dc85b2481cde6a07cdadfddc99e9b9e
 F ext/rtree/rtree1.test 207041aba07fdcdd93aa797a745839d305181857
 F ext/rtree/rtree2.test 9ac9d28fa948779df66916c67a5dcf9704c3cb74
@@ -84,6 +84,9 @@ F ext/rtree/rtree_perf.tcl 0fabb6d5c48cb8024e042ce5d4bb88998b6ec1cb
 F ext/rtree/rtree_util.tcl ee0a0311eb12175319d78bfb37302320496cee6e
 F ext/rtree/tkt3363.test 6662237ea75bb431cd5d262dfc9535e1023315fc
 F ext/rtree/viewrtree.tcl 09526398dae87a5a87c5aac2b3854dbaf8376869
+F ext/sqlrr/README.txt 4239030e73023e72a2e727808cd433577d5bf730
+F ext/sqlrr/sqlrr.c 8d1e6571cd6a6beabdb5bcdfe3a0e723b914db41
+F ext/sqlrr/sqlrr.h 09e4f8929ad9bc2638732c0cc0db5eef8c417824
 F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895
 F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
 F main.mk d588eab5d51b0bfe924a1cccdfdd2cbb4cbe40b4
@@ -117,20 +120,20 @@ F src/delete.c 308e300d599d2d11b838687e2cf7309d42f29a1a
 F src/expr.c 501269f7598cd7f39664c2ed6c360a6d48956396
 F src/fault.c dc88c821842157460750d2d61a8a8b4197d047ff
 F src/fkey.c 41219cba186bcf0a053e42327dfa23aaba4f834a
-F src/func.c e536218d193b8d326aab91120bc4c6f28aa2b606
+F src/func.c 1ddc1c93a0bb350977361e60e6067b3827ea9e81
 F src/global.c 271952d199a8cc59d4ce840b3bbbfd2f30c8ba32
 F src/hash.c ebcaa921ffd9d86f7ea5ae16a0a29d1c871130a7
 F src/hash.h 35b216c13343d0b4f87d9f21969ac55ad72174e1
 F src/hwtime.h 4a1d45f4cae1f402ea19686acf24acf4f0cb53cb
 F src/insert.c 2fe2ef7bd03d6e0120e4525727c4ae7de5a2d571
 F src/journal.c e00df0c0da8413ab6e1bb7d7cab5665d4a9000d0
-F src/legacy.c 303b4ffcf1ae652fcf5ef635846c563c254564f6
+F src/legacy.c 3ec1880541904c6135fcca548a272a6301bdc951
 F src/lempar.c 7f026423f4d71d989e719a743f98a1cbd4e6d99e
 F src/loadext.c 0e88a335665db0b2fb4cece3e49dcb65d832635a
-F src/main.c 5e12af46a6e6149b755830f9c035be6eb77995a6
+F src/main.c a1c50e3bf1c07f02542de6390f75638e2621d8be
 F src/malloc.c 685561d2f1602042e349aea5d7a88c3f10a63454
 F src/mem0.c f2f84062d1f35814d6535c9f9e33de3bfb3b132c
-F src/mem1.c e6d5c23941288df8191b8a98c28e3f57771e2270
+F src/mem1.c b46d8dab40d9fdcde5048d62753f6787acebb806
 F src/mem2.c d02bd6a5b34f2d59012a852615621939d9c09548
 F src/mem3.c 805ab642adfafa171781a5d8ab112119dfaef118
 F src/mem5.c 4837b795ebdecc0cfe1522cd0c8b2c5d84ea490d
@@ -146,7 +149,7 @@ F src/os.c 8d62d8d98ad7909cb0dd294c1e5f3835c887ccb6
 F src/os.h 00a1334a4eecee7f7bef79ac606b88d325119f21
 F src/os_common.h 8c61457df58f1a4bd5f5adc3e90e01b37bf7afbc
 F src/os_os2.c bed77dc26e3a95ce4a204936b9a1ca6fe612fcc5
-F src/os_unix.c a4b4ea928ce31ed34cb8f90ed36a35df19312fad
+F src/os_unix.c cb7a1418fb41307a9fc97b9e4e772e36d4623a9f
 F src/os_win.c 5ffab20249a61e0625f869efe157fa009747039b
 F src/pager.c 729f73feeb33355ae1f0982a74f112ce190c74aa
 F src/pager.h 11852d044c86cf5a9d6e34171fb0c4fcf1f6265f
@@ -154,22 +157,22 @@ F src/parse.y 0204f0dfe8974dc2a0d46eb9ab98a433a1f963d6
 F src/pcache.c c92ffd4f3e1279b3766854c6d18b5bf4aac0d1fa
 F src/pcache.h 435ef324197f79391f9c92b71d7f92b548ad7a36
 F src/pcache1.c 211295a9ff6a5b30f1ca50516731a5cf3e9bf82c
-F src/pragma.c c25d0d15dd0bbc5ec34e9760629353358705a447
-F src/prepare.c 665d52303135833c53b9be03e68533e249e1de54
+F src/pragma.c 840143b9e0cadfbd744a24b0b918a057f0aba7ff
+F src/prepare.c 6ea9df8369f12f50b3ca92fdd0bd8f2037c48115
 F src/printf.c 508a1c59433353552b6553cba175eaa7331f8fc1
 F src/random.c 676b9d7ac820fe81e6fb2394ac8c10cff7f38628
 F src/resolve.c 1166b41dd6b0859bf31a26a8855bcddc819e6c46
 F src/rowset.c c64dafba1f9fd876836c8db8682966b9d197eb1f
 F src/select.c cbe366a0ce114856e66f5daf0f848d7c48a88298
 F src/shell.c f66531a57fff927f95c98d99c28237d88e400c86
-F src/sqlite.h.in 9106176cf206c36f01f8b761ba62671818bbe6ff
+F src/sqlite.h.in b000e2bc16eb429b4cd8f47faf0047094967a0e9
 F src/sqlite3ext.h 1db7d63ab5de4b3e6b83dd03d1a4e64fef6d2a17
 F src/sqliteInt.h b0661039d644645e5c5e60e179cb9c3f502805a0
 F src/sqliteLimit.h 38b2fffcd01faeaeaadea71b2b47695a81580c8b
 F src/status.c 237b193efae0cf6ac3f0817a208de6c6c6ef6d76
 F src/table.c cc86ad3d6ad54df7c63a3e807b5783c90411a08d
 F src/tclsqlite.c c2c4047177213baf485d4401c7dbb30a3c2ba322
-F src/test1.c 2232a39540a6b72f3be8f84b34d1ca0714f92aee
+F src/test1.c 9a4ee438435acfcf6418991a33e08bfe99e602bf
 F src/test2.c 0de743ec8890ca4f09e0bce5d6d5a681f5957fec
 F src/test3.c 2445c2beb5e7a0c91fd8136dc1339ec369a24898
 F src/test4.c b5fd530f02a6a0dbffb23be202168a690985dedd
@@ -180,7 +183,7 @@ F src/test8.c 34719910286a0a6ca233f10ba66558be938494dd
 F src/test9.c 963d380922f25c1c323712d05db01b19197ee6f7
 F src/test_async.c 731d23f953ece5bf40ce87810cfb7607218953c5
 F src/test_autoext.c f53b0cdf7bf5f08100009572a5d65cdb540bd0ad
-F src/test_backup.c 1384a18985a5a2d275c2662e48473bf1542ebd08
+F src/test_backup.c c775b759a7aedd48e9e6651b44b16e54ad11218c
 F src/test_btree.c 5adbba9b138988a3cf4d3b5424dbc7c85651da02
 F src/test_config.c 4ac1e6257dcf926a71b7934410b71c5c326e68f2
 F src/test_devsym.c 9f4bc2551e267ce7aeda195f3897d0f30c5228f4
@@ -208,7 +211,7 @@ F src/vacuum.c 48e1282bbd5eac4b461587c51658378658c00770
 F src/vdbe.c a435ffcf6bfc7f14eb40998062ccbd7dfa482319
 F src/vdbe.h 449323a21c02226790acb6189dae78af17b92b78
 F src/vdbeInt.h aa08465efa812288688a72613a2584079d294c62
-F src/vdbeapi.c 44b5f387459d5faa158aa8d3a26967f0c8596efd
+F src/vdbeapi.c de488c6311de23a68c1c4499eb76144f4b2ec8dd
 F src/vdbeaux.c 7cb0daeb128fff205183ce3efb10a94ed75ae705
 F src/vdbeblob.c 9bfaeab22e261a6a7b6df04e7faaf7d6dfdbef5a
 F src/vdbemem.c 7055a2941a7802094f4704cedc7a28cc88a23749
@@ -231,7 +234,7 @@ F test/async2.test bf5e2ca2c96763b4cba3d016249ad7259a5603b6
 F test/async3.test 93edaa9122f498e56ea98c36c72abc407f4fb11e
 F test/async4.test aafa6328c559d3e4bb587de770cbdecfca06f0da
 F test/async5.test f3592d79c84d6e83a5f50d3fd500445f7d97dfdf
-F test/attach.test 1d1be27b9e4c654f9bb14d011a4a87753c0b197a
+F test/attach.test 5519fbeb99b77d342642e9b990a644149c3ae9d1
 F test/attach2.test a295d2d7061adcee5884ef4a93c7c96a82765437
 F test/attach3.test bd9830bc3a0d22ed1310c9bff6896927937017dc
 F test/attachmalloc.test cf8cf17d183de357b1147a9baacbdfc85b940b61
@@ -453,8 +456,9 @@ F test/lock4.test f4f36271aa5ae1da449646bf43c7341f6b2b4c4e
 F test/lock5.test 6b1f78f09ad1522843dad571b76b321e6f439bf7
 F test/lock6.test 862aa71e97b288d6b3f92ba3313f51bd0b003776
 F test/lock7.test 64006c84c1c616657e237c7ad6532b765611cf64
+F test/lock_proxy.test 364b6c6220c023b57fc37eb52ec97c1f5c682c47
 F test/lookaside.test 1dd350dc6dff015c47c07fcc5a727a72fc5bae02
-F test/main.test 347ab987f16167858781383427476b33dc69fdb7
+F test/main.test 0fdc4b9b1510d522f2ff39d02569bffb2a8132aa
 F test/make-where7.tcl 05c16b5d4f5d6512881dfec560cb793915932ef9
 F test/malloc.test d23580e15c33ee0353717129421b077541e910dc
 F test/malloc3.test 4bc57f850b212f706f3e1b37c4eced1d5a727cd1
@@ -476,11 +480,11 @@ F test/mallocH.test 79b65aed612c9b3ed2dcdaa727c85895fd1bfbdb
 F test/mallocI.test e3ea401904d010cb7c1e4b2ee8803f4a9f5b999d
 F test/mallocJ.test b5d1839da331d96223e5f458856f8ffe1366f62e
 F test/mallocK.test d79968641d1b70d88f6c01bdb9a7eb4a55582cc9
-F test/malloc_common.tcl 984baeb6c6b185e798827d1187d426acc2bc4962
+F test/malloc_common.tcl a0b9f24aff8987f965a0d3e91cecdbb8b67e7916
 F test/manydb.test b3d3bc4c25657e7f68d157f031eb4db7b3df0d3c
-F test/memdb.test 0825155b2290e900264daaaf0334b6dfe69ea498
+F test/memdb.test f773146f66ee2c635854a8264317f39a6cc3e18c
 F test/memleak.test d2d2a1ff7105d32dc3fdf691458cf6cba58c7217
-F test/memsubsys1.test fd8a33046b6e758e3eb93747dc4eec21fe56bf64
+F test/memsubsys1.test d7d3266a56e69bb3e6b8dd64822fcaa1c4f3e399
 F test/memsubsys2.test 72a731225997ad5e8df89fdbeae9224616b6aecc
 F test/minmax.test 722d80816f7e096bf2c04f4111f1a6c1ba65453d
 F test/minmax2.test 33504c01a03bd99226144e4b03f7631a274d66e0
@@ -506,12 +510,12 @@ F test/pagesize.test 0d9ff3fedfce6e5ffe8fa7aca9b6d3433a2e843b
 F test/pcache.test eebc4420b37cb07733ae9b6e99c9da7c40dd6d58
 F test/pcache2.test 0d85f2ab6963aee28c671d4c71bec038c00a1d16
 F test/permutations.test 1ce2874df8fec876d0b963c7a3ef61c4e9df8827
-F test/pragma.test 5aeb48a442dba3c3e8e38773b121371814ab3b17
+F test/pragma.test 648a1760925d7d39003b3fd29e1b050c983e7553
 F test/pragma2.test 5364893491b9231dd170e3459bfc2e2342658b47
 F test/printf.test 47e9e5bbec8509023479d54ceb71c9d05a95308a
 F test/progress.test 5b075c3c790c7b2a61419bc199db87aaf48b8301
 F test/ptrchng.test ef1aa72d6cf35a2bbd0869a649b744e9d84977fc
-F test/quick.test 12fdc7656b4d20a537a686fb223eb99b5fe54483
+F test/quick.test 5ac8be9487553d9e38433ad70d6906b87d558de3
 F test/quote.test 215897dbe8de1a6f701265836d6601cc6ed103e6
 F test/randexpr1.tcl 40dec52119ed3a2b8b2a773bce24b63a3a746459
 F test/randexpr1.test 1084050991e9ba22c1c10edd8d84673b501cc25a
@@ -575,7 +579,7 @@ F test/tclsqlite.test bf4227eb236a4c097aa7974a2bf7d3225acf34be
 F test/tempdb.test 1bf52da28a9c24e29717362a87722dff08feb72b
 F test/temptable.test f42121a0d29a62f00f93274464164177ab1cc24a
 F test/temptrigger.test b0273db072ce5f37cf19140ceb1f0d524bbe9f05
-F test/tester.tcl 2caf7980d7dbb99dab9507ae0646802bc4d12c79
+F test/tester.tcl 0fbba238815429131181774ba2b169e198838150
 F test/thread001.test a3e6a7254d1cb057836cb3145b60c10bf5b7e60f
 F test/thread002.test afd20095e6e845b405df4f2c920cb93301ca69db
 F test/thread003.test b824d4f52b870ae39fc5bae4d8070eca73085dca
@@ -745,7 +749,7 @@ F tool/lempar.c 01ca97f87610d1dac6d8cd96ab109ab1130e76dc
 F tool/mkkeywordhash.c 9216336085e7a7c226a35c0bd780239968f8304f
 F tool/mkopts.tcl 66ac10d240cc6e86abd37dc908d50382f84ff46e
 F tool/mkspeedsql.tcl a1a334d288f7adfe6e996f2e712becf076745c97
-F tool/mksqlite3c.tcl a7e87ce780cbf30782bca3fd1197e86f92ae6f24
+F tool/mksqlite3c.tcl 1ac077b7bf1cca4e4922b5e72a5e898274fb174b
 F tool/mksqlite3h.tcl eb100dce83f24b501b325b340f8b5eb8e5106b3b
 F tool/mksqlite3internalh.tcl 7b43894e21bcb1bb39e11547ce7e38a063357e87
 F tool/omittest.tcl 27d6f6e3b1e95aeb26a1c140e6eb57771c6d794a
@@ -761,7 +765,10 @@ F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
 F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224
 F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e
 F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f
-P 27d8e684db4651155c4bcb8bb44bf1c692b8c48b
-R 87d07c1d512d6da4f67b4891197ec4ad
-U shane
-Z 269e8905230698d3c4544fc066424d81
+P 01c4b5b84ec7ce589e20ea66e80011f092ab32f0
+R 320cf17d3a0d32e6b3087d1f5155c40f
+T *branch * apple-osx
+T *sym-apple-osx *
+T -sym-trunk *
+U adam
+Z 409690f7ddbce583c414b998978aa931
index 7904d571676ce52782144c9f78c963fa22307fa1..f3c6cd9c49ce5bdd8cfc49e341a25ad1bf7e9969 100644 (file)
@@ -1 +1 @@
-01c4b5b84ec7ce589e20ea66e80011f092ab32f0
\ No newline at end of file
+941a01eb868815f566539e9ab21f807d9e798e40
\ No newline at end of file
index 2766fa3b1b8638d23d15783187684d8abc9df712..0d93b1a78120f986567c65b95de95b41f0f583a4 100644 (file)
@@ -191,6 +191,9 @@ static void substrFunc(
     }
   }
   p1 = sqlite3_value_int(argv[1]);
+#ifdef SQLITE_SUBSTR_COMPATIBILITY
+  if( p1==0 ) p1 = 1; /* <rdar://problem/6778339> */
+#endif
   if( argc==3 ){
     p2 = sqlite3_value_int(argv[2]);
     if( p2<0 ){
index 73421032cb88620fa7cb5b3e5e895dceff75619d..c28ee95b5834066244b1b3a5333d50ad2dfba69b 100644 (file)
@@ -18,6 +18,9 @@
 */
 
 #include "sqliteInt.h"
+#ifdef SQLITE_ENABLE_SQLRR
+# include "sqlrr.h"
+#endif
 
 /*
 ** Execute SQL code.  Return one of the SQLITE_ success/failure
@@ -44,7 +47,9 @@ int sqlite3_exec(
   int callbackIsInit;         /* True if callback data is initialized */
 
   if( zSql==0 ) zSql = "";
-
+#ifdef SQLITE_ENABLE_SQLRR
+  SRRecExec(db, zSql);
+#endif  
   sqlite3_mutex_enter(db->mutex);
   sqlite3Error(db, SQLITE_OK, 0);
   while( (rc==SQLITE_OK || (rc==SQLITE_SCHEMA && (++nRetry)<2)) && zSql[0] ){
@@ -125,6 +130,9 @@ int sqlite3_exec(
 exec_out:
   if( pStmt ) sqlite3VdbeFinalize((Vdbe *)pStmt);
   sqlite3DbFree(db, azCols);
+#ifdef SQLITE_ENABLE_SQLRR
+  SRRecExecEnd(db);
+#endif
 
   rc = sqlite3ApiExit(db, rc);
   if( rc!=SQLITE_OK && ALWAYS(rc==sqlite3_errcode(db)) && pzErrMsg ){
index a0e765db867c1645c89856d7e67955411eef7701..042a59a2f9bdf030cfe42259e0957d8d2a54311a 100644 (file)
@@ -16,6 +16,9 @@
 */
 #include "sqliteInt.h"
 
+#ifdef SQLITE_ENABLE_SQLRR
+# include "sqlrr.h"
+#endif 
 #ifdef SQLITE_ENABLE_FTS3
 # include "fts3.h"
 #endif
@@ -702,6 +705,10 @@ int sqlite3_close(sqlite3 *db){
   if( db->lookaside.bMalloced ){
     sqlite3_free(db->lookaside.pStart);
   }
+#ifdef SQLITE_ENABLE_SQLRR
+  SRRecClose(db);
+#endif
+  
   sqlite3_free(db);
   return SQLITE_OK;
 }
@@ -1513,6 +1520,12 @@ int sqlite3_limit(sqlite3 *db, int limitId, int newLimit){
   }
   return oldLimit;
 }
+#if defined(SQLITE_ENABLE_AUTO_PROFILE)
+static void profile_sql(void *aux, const char *sql, uint64_t ns) {
+#pragma unused(aux)
+       fprintf(stderr, "Query: %s\n Execution Time: %llu ms\n", sql, ns / 1000000);
+}
+#endif
 
 /*
 ** This routine does the work of opening a database on behalf of
@@ -1743,7 +1756,19 @@ opendb_out:
   }else if( rc!=SQLITE_OK ){
     db->magic = SQLITE_MAGIC_SICK;
   }
+#if defined(SQLITE_ENABLE_AUTO_PROFILE)
+  if( db && !rc ){
+    char *envprofile = getenv("SQLITE_AUTO_PROFILE");
+    
+    if( envprofile!=NULL ){
+      sqlite3_profile(db, profile_sql, NULL);
+    }
+  }
+#endif
   *ppDb = db;
+#ifdef SQLITE_ENABLE_SQLRR
+  SRRecOpen(db, zFilename, flags);
+#endif
   return sqlite3ApiExit(0, rc);
 }
 
index e3b886341c21a69096858df392645364b5aa97f0..14866165c49cc984acf6423b57d2a744ec33d27e 100644 (file)
 */
 #ifdef SQLITE_SYSTEM_MALLOC
 
+#if (!defined(__APPLE__))
+
+#define SQLITE_MALLOC(x) malloc(x)
+#define SQLITE_FREE(x) free(x)
+#define SQLITE_REALLOC(x,y) realloc((x),(y))
+
+
+#else
+
+
+#include <sys/sysctl.h>
+#include <malloc/malloc.h>
+#include <libkern/OSAtomic.h>
+
+static malloc_zone_t* _sqliteZone_;
+
+#define SQLITE_MALLOC(x) malloc_zone_malloc(_sqliteZone_, (x))
+#define SQLITE_FREE(x) malloc_zone_free(_sqliteZone_, (x));
+#define SQLITE_REALLOC(x,y) malloc_zone_realloc(_sqliteZone_, (x), (y))
+
+#endif
+
 /*
 ** Like malloc(), but remember the size of the allocation
 ** so that we can find it later using sqlite3MemSize().
@@ -40,7 +62,7 @@ static void *sqlite3MemMalloc(int nByte){
   sqlite3_int64 *p;
   assert( nByte>0 );
   nByte = ROUND8(nByte);
-  p = malloc( nByte+8 );
+  p = SQLITE_MALLOC( nByte+8 );
   if( p ){
     p[0] = nByte;
     p++;
@@ -60,7 +82,7 @@ static void sqlite3MemFree(void *pPrior){
   sqlite3_int64 *p = (sqlite3_int64*)pPrior;
   assert( pPrior!=0 );
   p--;
-  free(p);
+  SQLITE_FREE(p);
 }
 
 /*
@@ -79,7 +101,7 @@ static void *sqlite3MemRealloc(void *pPrior, int nByte){
   nByte = ROUND8(nByte);
   p = (sqlite3_int64*)pPrior;
   p--;
-  p = realloc(p, nByte+8 );
+  p = SQLITE_REALLOC(p, nByte+8 );
   if( p ){
     p[0] = nByte;
     p++;
@@ -110,6 +132,37 @@ static int sqlite3MemRoundup(int n){
 ** Initialize this module.
 */
 static int sqlite3MemInit(void *NotUsed){
+#if defined(__APPLE__)
+       if (_sqliteZone_) {
+               return SQLITE_OK;
+       }
+       int cpuCount;
+    size_t len;
+    
+    len = sizeof(cpuCount);
+    sysctlbyname("hw.ncpu", &cpuCount, &len, NULL, 0);  //  one almost always wants to use "hw.activecpu" for MT decisions, but not here.
+       
+       if (cpuCount > 1) {
+               // defer MT decisions to system malloc
+               _sqliteZone_ = malloc_default_zone();
+       } else {
+               // only 1 core, use our own zone to contention over global locks, 
+               // e.g. we have our own dedicated locks
+               malloc_zone_t* newzone = malloc_create_zone(4096, 0);
+               malloc_set_zone_name(newzone, "Sqlite_Heap");
+
+               bool success;           
+               do {
+                       success = OSAtomicCompareAndSwapPtrBarrier(NULL, newzone, (void * volatile *)&_sqliteZone_);
+               } while (!_sqliteZone_);
+               
+               if (!success) {
+                       // somebody registered a zone first
+                       malloc_destroy_zone(newzone);
+               }
+       }
+#endif
+
   UNUSED_PARAMETER(NotUsed);
   return SQLITE_OK;
 }
@@ -118,6 +171,17 @@ static int sqlite3MemInit(void *NotUsed){
 ** Deinitialize this module.
 */
 static void sqlite3MemShutdown(void *NotUsed){
+#if (0 && defined(__APPLE__))
+       if (_sqliteZone_ && (_sqliteZone_ != malloc_default_zone())) {
+               malloc_zone_t* oldzone = _sqliteZone_;
+
+               bool success = OSAtomicCompareAndSwapPtrBarrier(oldzone, NULL, (void * volatile *)&_sqliteZone_);
+               if (success) {
+                       malloc_destroy_zone(oldzone);
+               }
+       }
+#endif
+       
   UNUSED_PARAMETER(NotUsed);
   return;
 }
@@ -142,4 +206,8 @@ void sqlite3MemSetDefault(void){
   sqlite3_config(SQLITE_CONFIG_MALLOC, &defaultMethods);
 }
 
+#undef SQLITE_MALLOC
+#undef SQLITE_FREE
+#undef SQLITE_REALLOC
+
 #endif /* SQLITE_SYSTEM_MALLOC */
index b31fbab2154e18043028a212ae5ca87ec01523b2..6ba26a6e91f751daef953a7ef1a680ab1ae72992 100644 (file)
 # endif
 #endif /* SQLITE_ENABLE_LOCKING_STYLE */
 
+#define SQLITE_FSFLAGS_IS_READONLY  0x1
+#define SQLITE_FSFLAGS_IS_LOCAL     0x2
+#define SQLITE_FSFLAGS_IS_HFSPLUS   0x4
+#define SQLITE_FSFLAGS_IS_MSDOS     0x8
+#define SQLITE_FSFLAGS_INITIALIZED     0x10000
+
+
 /*
 ** If we are to be thread-safe, include the pthreads header and define
 ** the SQLITE_UNIX_THREADS macro.
@@ -199,6 +206,9 @@ struct unixFile {
 #if SQLITE_ENABLE_LOCKING_STYLE
   int openFlags;                   /* The flags specified at open() */
 #endif
+#if SQLITE_ENABLE_LOCKING_STYLE || defined(__APPLE__)
+  unsigned fsFlags;                 /* cached details from statfs() */
+#endif
 #if SQLITE_THREADSAFE && defined(__linux__)
   pthread_t tid;                   /* The thread that "owns" this unixFile */
 #endif
@@ -990,17 +1000,18 @@ static int findLockInfo(
   ** is a race condition such that another thread has already populated
   ** the first page of the database, no damage is done.
   */
-  if( statbuf.st_size==0 ){
-    rc = write(fd, "S", 1);
-    if( rc!=1 ){
-      return SQLITE_IOERR;
-    }
-    rc = fstat(fd, &statbuf);
-    if( rc!=0 ){
-      pFile->lastErrno = errno;
-      return SQLITE_IOERR;
+  if (( statbuf.st_size==0 ) && (0 != (pFile->fsFlags & SQLITE_FSFLAGS_IS_MSDOS))) {
+      rc = write(fd, "S", 1);
+      if( rc!=1 ){
+        pFile->lastErrno = errno;
+        return SQLITE_IOERR;
+      }
+      rc = fstat(fd, &statbuf);
+      if( rc!=0 ){
+        pFile->lastErrno = errno;
+        return SQLITE_IOERR;
+      }
     }
-  }
 #endif
 
   memset(&lockKey, 0, sizeof(lockKey));
@@ -1165,6 +1176,7 @@ static int unixCheckReservedLock(sqlite3_file *id, int *pResOut){
   return rc;
 }
 
+#ifdef SQLITE_ENABLE_NFS_RANGELOCK
 /*
 ** Perform a file locking operation on a range of bytes in a file.
 ** The "op" parameter should be one of F_RDLCK, F_WRLCK, or F_UNLCK.
@@ -1221,6 +1233,7 @@ static int rangeLock(unixFile *pFile, int op, int *pErrcode){
   return rc;
 }
 
+#endif /* SQLITE_ENABLE_NFS_RANGELOCK */
 /*
 ** Lock the file with the lock specified by parameter locktype - one
 ** of the following:
@@ -1385,8 +1398,11 @@ static int unixLock(sqlite3_file *id, int locktype){
     assert( pLock->locktype==0 );
 
     /* Now get the read-lock */
-    s = rangeLock(pFile, F_RDLCK, &tErrno);
-
+    lock.l_start = SHARED_FIRST;
+    lock.l_len = SHARED_SIZE;
+    if( (s = fcntl(pFile->h, F_SETLK, &lock))==(-1) ){
+      tErrno = errno;
+    }
     /* Drop the temporary PENDING lock */
     lock.l_start = PENDING_BYTE;
     lock.l_len = 1L;
@@ -1426,16 +1442,17 @@ static int unixLock(sqlite3_file *id, int locktype){
     switch( locktype ){
       case RESERVED_LOCK:
         lock.l_start = RESERVED_BYTE;
-        s = fcntl(pFile->h, F_SETLK, &lock);
-        tErrno = errno;
         break;
       case EXCLUSIVE_LOCK:
-        s = rangeLock(pFile, F_WRLCK, &tErrno);
+        lock.l_start = SHARED_FIRST;
+        lock.l_len = SHARED_SIZE;
         break;
       default:
         assert(0);
     }
+    s = fcntl(pFile->h, F_SETLK, &lock);
     if( s==(-1) ){
+      tErrno = errno;
       rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK);
       if( IS_LOCK_ERROR(rc) ){
         pFile->lastErrno = tErrno;
@@ -1525,13 +1542,19 @@ static void setPendingFd(unixFile *pFile){
 **
 ** If the locking level of the file descriptor is already at or below
 ** the requested locking level, this routine is a no-op.
+** 
+** If handleNFSUnlock is true, then on downgrading an EXCLUSIVE_LOCK to SHARED
+** the byte range is divided into 2 parts and the first part is unlocked then
+** set to a read lock, then the other part is simply unlocked.  This works 
+** around a bug in BSD NFS lockd (also seen on MacOSX 10.3+) that fails to 
+** remove the write lock on a region when a read lock is set.
 */
-static int unixUnlock(sqlite3_file *id, int locktype){
-  unixFile *pFile = (unixFile*)id; /* The open file */
-  struct unixLockInfo *pLock;      /* Structure describing current lock state */
-  struct flock lock;               /* Information passed into fcntl() */
-  int rc = SQLITE_OK;              /* Return code from this interface */
-  int h;                           /* The underlying file descriptor */
+static int _posixUnlock(sqlite3_file *id, int locktype, int handleNFSUnlock){
+  unixFile *pFile = (unixFile*)id;
+  struct unixLockInfo *pLock;
+  struct flock lock;
+  int rc = SQLITE_OK;
+  int h;
   int tErrno;                      /* Error code from system call errors */
 
   assert( pFile );
@@ -1570,14 +1593,68 @@ static int unixUnlock(sqlite3_file *id, int locktype){
     pFile->inNormalWrite = 0;
 #endif
 
-
+    /* downgrading to a shared lock on NFS involves clearing the write lock
+     ** before establishing the readlock - to avoid a race condition we downgrade
+     ** the lock in 2 blocks, so that part of the range will be covered by a 
+     ** write lock until the rest is covered by a read lock:
+     **  1:   [WWWWW]
+     **  2:   [....W]
+     **  3:   [RRRRW]
+     **  4:   [RRRR.]
+     */
     if( locktype==SHARED_LOCK ){
-      if( rangeLock(pFile, F_RDLCK, &tErrno)==(-1) ){
-        rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_RDLOCK);
-        if( IS_LOCK_ERROR(rc) ){
-          pFile->lastErrno = tErrno;
+      if( handleNFSUnlock ){
+        off_t divSize = SHARED_SIZE - 1;
+        
+        lock.l_type = F_UNLCK;
+        lock.l_whence = SEEK_SET;
+        lock.l_start = SHARED_FIRST;
+        lock.l_len = divSize;
+        if( fcntl(h, F_SETLK, &lock)==(-1) ){
+          int tErrno = errno;
+          rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_UNLOCK);
+          if( IS_LOCK_ERROR(rc) ){
+            pFile->lastErrno = tErrno;
+          }
+          goto end_unlock;
+        }
+        lock.l_type = F_RDLCK;
+        lock.l_whence = SEEK_SET;
+        lock.l_start = SHARED_FIRST;
+        lock.l_len = divSize;
+        if( fcntl(h, F_SETLK, &lock)==(-1) ){
+          int tErrno = errno;
+          rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_RDLOCK);
+          if( IS_LOCK_ERROR(rc) ){
+            pFile->lastErrno = tErrno;
+          }
+          goto end_unlock;
+        }
+        lock.l_type = F_UNLCK;
+        lock.l_whence = SEEK_SET;
+        lock.l_start = SHARED_FIRST+divSize;
+        lock.l_len = SHARED_SIZE-divSize;
+        if( fcntl(h, F_SETLK, &lock)==(-1) ){
+          int tErrno = errno;
+          rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_UNLOCK);
+          if( IS_LOCK_ERROR(rc) ){
+            pFile->lastErrno = tErrno;
+          }
+          goto end_unlock;
+        }
+      }else{
+        lock.l_type = F_RDLCK;
+        lock.l_whence = SEEK_SET;
+        lock.l_start = SHARED_FIRST;
+        lock.l_len = SHARED_SIZE;
+        if( fcntl(h, F_SETLK, &lock)==(-1) ){
+          int tErrno = errno;
+          rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_RDLOCK);
+          if( IS_LOCK_ERROR(rc) ){
+            pFile->lastErrno = tErrno;
+          }
+          goto end_unlock;
         }
-        goto end_unlock;
       }
     }
     lock.l_type = F_UNLCK;
@@ -1644,6 +1721,17 @@ end_unlock:
   return rc;
 }
 
+/*
+** Lower the locking level on file descriptor pFile to locktype.  locktype
+** must be either NO_LOCK or SHARED_LOCK.
+**
+** If the locking level of the file descriptor is already at or below
+** the requested locking level, this routine is a no-op.
+*/
+static int unixUnlock(sqlite3_file *id, int locktype){
+  return _posixUnlock(id, locktype, 0);
+}
+
 /*
 ** This function performs the parts of the "close file" operation 
 ** common to all locking schemes. It closes the directory and file
@@ -2356,6 +2444,7 @@ static int semClose(sqlite3_file *id) {
 typedef struct afpLockingContext afpLockingContext;
 struct afpLockingContext {
   unsigned long long sharedByte;
+  int reserved;
   const char *dbPath;             /* Name of the open file */
 };
 
@@ -2432,9 +2521,14 @@ static int afpCheckReservedLock(sqlite3_file *id, int *pResOut){
   
   assert( pFile );
   afpLockingContext *context = (afpLockingContext *) pFile->lockingContext;
+  if( context->reserved ){
+    *pResOut = 1;
+    return SQLITE_OK;
+  }
+  unixEnterMutex(); /* Because pFile->pLock is shared across threads */
   
   /* Check if a thread in this process holds such a lock */
-  if( pFile->locktype>SHARED_LOCK ){
+  if( pFile->pLock->locktype>SHARED_LOCK ){
     reserved = 1;
   }
   
@@ -2456,6 +2550,7 @@ static int afpCheckReservedLock(sqlite3_file *id, int *pResOut){
     }
   }
   
+  unixLeaveMutex();
   OSTRACE4("TEST WR-LOCK %d %d %d\n", pFile->h, rc, reserved);
   
   *pResOut = reserved;
@@ -2489,11 +2584,13 @@ static int afpCheckReservedLock(sqlite3_file *id, int *pResOut){
 static int afpLock(sqlite3_file *id, int locktype){
   int rc = SQLITE_OK;
   unixFile *pFile = (unixFile*)id;
+  struct unixLockInfo *pLock = pFile->pLock;
   afpLockingContext *context = (afpLockingContext *) pFile->lockingContext;
   
   assert( pFile );
-  OSTRACE5("LOCK    %d %s was %s pid=%d\n", pFile->h,
-         locktypeName(locktype), locktypeName(pFile->locktype), getpid());
+  OSTRACE7("LOCK    %d %s was %s(%s,%d) pid=%d\n", pFile->h,
+           locktypeName(locktype), locktypeName(pFile->locktype),
+           locktypeName(pLock->locktype), pLock->cnt , getpid());
 
   /* If there is already a lock of this type or more restrictive on the
   ** unixFile, do nothing. Don't use the afp_end_lock: exit path, as
@@ -2506,6 +2603,9 @@ static int afpLock(sqlite3_file *id, int locktype){
   }
 
   /* Make sure the locking sequence is correct
+  **  (1) We never move from unlocked to anything higher than shared lock.
+  **  (2) SQLite never explicitly requests a pendig lock.
+  **  (3) A shared lock is always held when a reserve lock is requested.
   */
   assert( pFile->locktype!=NO_LOCK || locktype==SHARED_LOCK );
   assert( locktype!=PENDING_LOCK );
@@ -2522,7 +2622,33 @@ static int afpLock(sqlite3_file *id, int locktype){
     unixLeaveMutex();
     return rc;
   }
-    
+  pLock = pFile->pLock;
+
+  /* If some thread using this PID has a lock via a different unixFile*
+  ** handle that precludes the requested lock, return BUSY.
+  */
+  if( (pFile->locktype!=pLock->locktype && 
+       (pLock->locktype>=PENDING_LOCK || locktype>SHARED_LOCK))
+     ){
+    rc = SQLITE_BUSY;
+    goto afp_end_lock;
+  }
+  
+  /* If a SHARED lock is requested, and some thread using this PID already
+  ** has a SHARED or RESERVED lock, then increment reference counts and
+  ** return SQLITE_OK.
+  */
+  if( locktype==SHARED_LOCK && 
+     (pLock->locktype==SHARED_LOCK || pLock->locktype==RESERVED_LOCK) ){
+    assert( locktype==SHARED_LOCK );
+    assert( pFile->locktype==0 );
+    assert( pLock->cnt>0 );
+    pFile->locktype = SHARED_LOCK;
+    pLock->cnt++;
+    pFile->pOpen->nLock++;
+    goto afp_end_lock;
+  }
+  
   /* A PENDING lock is needed before acquiring a SHARED lock and before
   ** acquiring an EXCLUSIVE lock.  For the SHARED lock, the PENDING will
   ** be released.
@@ -2542,13 +2668,17 @@ static int afpLock(sqlite3_file *id, int locktype){
   ** operating system calls for the specified lock.
   */
   if( locktype==SHARED_LOCK ){
-    int lk, lrc1, lrc2;
-    int lrc1Errno = 0;
+    int lrc1, lrc2, lrc1Errno;
+    long lk, mask;
     
+    assert( pLock->cnt==0 );
+    assert( pLock->locktype==0 );
+        
+    mask = (sizeof(long)==8) ? LARGEST_INT64 : 0x7fffffff;
     /* Now get the read-lock SHARED_LOCK */
     /* note that the quality of the randomness doesn't matter that much */
-    lk = random(); 
-    context->sharedByte = (lk & 0x7fffffff)%(SHARED_SIZE - 1);
+    lk = random();
+    context->sharedByte = (lk & mask)%(SHARED_SIZE - 1);
     lrc1 = afpSetLock(context->dbPath, pFile, 
           SHARED_FIRST+context->sharedByte, 1, 1);
     if( IS_LOCK_ERROR(lrc1) ){
@@ -2569,7 +2699,12 @@ static int afpLock(sqlite3_file *id, int locktype){
     } else {
       pFile->locktype = SHARED_LOCK;
       pFile->pOpen->nLock++;
+      pLock->cnt = 1;
     }
+  }else if( locktype==EXCLUSIVE_LOCK && pLock->cnt>1 ){
+    /* We are trying for an exclusive lock but another thread in this
+     ** same process is still holding a shared lock. */
+    rc = SQLITE_BUSY;
   }else{
     /* The request was for a RESERVED or EXCLUSIVE lock.  It is
     ** assumed that there is a SHARED or greater lock on the file
@@ -2580,6 +2715,9 @@ static int afpLock(sqlite3_file *id, int locktype){
     if (locktype >= RESERVED_LOCK && pFile->locktype < RESERVED_LOCK) {
         /* Acquire a RESERVED lock */
         failed = afpSetLock(context->dbPath, pFile, RESERVED_BYTE, 1,1);
+      if( !failed ){
+        context->reserved = 1;
+      }
     }
     if (!failed && locktype == EXCLUSIVE_LOCK) {
       /* Acquire an EXCLUSIVE lock */
@@ -2613,8 +2751,10 @@ static int afpLock(sqlite3_file *id, int locktype){
   
   if( rc==SQLITE_OK ){
     pFile->locktype = locktype;
+    pLock->locktype = locktype;
   }else if( locktype==EXCLUSIVE_LOCK ){
     pFile->locktype = PENDING_LOCK;
+    pLock->locktype = PENDING_LOCK;
   }
   
 afp_end_lock:
@@ -2634,11 +2774,15 @@ afp_end_lock:
 static int afpUnlock(sqlite3_file *id, int locktype) {
   int rc = SQLITE_OK;
   unixFile *pFile = (unixFile*)id;
-  afpLockingContext *pCtx = (afpLockingContext *) pFile->lockingContext;
-
+  struct unixLockInfo *pLock;
+  afpLockingContext *context = (afpLockingContext *) pFile->lockingContext;
+#ifdef SQLITE_TEST
+  int h = pFile->h;
+#endif
+  
   assert( pFile );
-  OSTRACE5("UNLOCK  %d %d was %d pid=%d\n", pFile->h, locktype,
-         pFile->locktype, getpid());
+  OSTRACE7("UNLOCK  %d %d was %d(%d,%d) pid=%d\n", pFile->h, locktype,
+           pFile->locktype, pFile->pLock->locktype, pFile->pLock->cnt, getpid());
 
   assert( locktype<=SHARED_LOCK );
   if( pFile->locktype<=locktype ){
@@ -2648,31 +2792,73 @@ static int afpUnlock(sqlite3_file *id, int locktype) {
     return SQLITE_MISUSE;
   }
   unixEnterMutex();
+  pLock = pFile->pLock;
+  assert( pLock->cnt!=0 );
   if( pFile->locktype>SHARED_LOCK ){
+    assert( pLock->locktype==pFile->locktype );
+    SimulateIOErrorBenign(1);
+    SimulateIOError( h=(-1) )
+    SimulateIOErrorBenign(0);
+    
+#ifndef NDEBUG
+    /* When reducing a lock such that other processes can start
+     ** reading the database file again, make sure that the
+     ** transaction counter was updated if any part of the database
+     ** file changed.  If the transaction counter is not updated,
+     ** other connections to the same file might not realize that
+     ** the file has changed and hence might not know to flush their
+     ** cache.  The use of a stale cache can lead to database corruption.
+     */
+    assert( pFile->inNormalWrite==0
+           || pFile->dbUpdate==0
+           || pFile->transCntrChng==1 );
+    pFile->inNormalWrite = 0;
+#endif
     
     if( pFile->locktype==EXCLUSIVE_LOCK ){
-      rc = afpSetLock(pCtx->dbPath, pFile, SHARED_FIRST, SHARED_SIZE, 0);
+      rc = afpSetLock(context->dbPath, pFile, SHARED_FIRST, SHARED_SIZE, 0);
       if( rc==SQLITE_OK && locktype==SHARED_LOCK ){
         /* only re-establish the shared lock if necessary */
-        int sharedLockByte = SHARED_FIRST+pCtx->sharedByte;
-        rc = afpSetLock(pCtx->dbPath, pFile, sharedLockByte, 1, 1);
+        int sharedLockByte = SHARED_FIRST+context->sharedByte;
+        rc = afpSetLock(context->dbPath, pFile, sharedLockByte, 1, 1);
       }
     }
     if( rc==SQLITE_OK && pFile->locktype>=PENDING_LOCK ){
-      rc = afpSetLock(pCtx->dbPath, pFile, PENDING_BYTE, 1, 0);
+      rc = afpSetLock(context->dbPath, pFile, PENDING_BYTE, 1, 0);
     } 
-    if( rc==SQLITE_OK && pFile->locktype>=RESERVED_LOCK ){
-      rc = afpSetLock(pCtx->dbPath, pFile, RESERVED_BYTE, 1, 0);
+    if( rc==SQLITE_OK && pFile->locktype>=RESERVED_LOCK && context->reserved ){
+      rc = afpSetLock(context->dbPath, pFile, RESERVED_BYTE, 1, 0);
+      if( !rc ){ 
+        context->reserved = 0; 
+      }
+    }
+    if( rc==SQLITE_OK && locktype==SHARED_LOCK){
+      pLock->locktype = SHARED_LOCK;
     }
   }else if( locktype==NO_LOCK ){
-    /* clear the shared lock */
-    int sharedLockByte = SHARED_FIRST+pCtx->sharedByte;
-    rc = afpSetLock(pCtx->dbPath, pFile, sharedLockByte, 1, 0);
+
+    /* Decrement the shared lock counter.  Release the lock using an
+     ** OS call only when all threads in this same process have released
+     ** the lock.
+     */
+    int sharedLockByte = SHARED_FIRST+context->sharedByte;
+    pLock->cnt--;
+    if( pLock->cnt==0 ){
+      SimulateIOErrorBenign(1);
+      SimulateIOError( h=(-1) )
+      SimulateIOErrorBenign(0);
+      rc = afpSetLock(context->dbPath, pFile, sharedLockByte, 1, 0);
+      pLock->locktype = NO_LOCK;
+      if( !rc ){
+        pFile->locktype = NO_LOCK;
+      }
+    }
   }
 
   if( rc==SQLITE_OK ){
     if( locktype==NO_LOCK ){
       struct unixOpenCnt *pOpen = pFile->pOpen;
+        
       pOpen->nLock--;
       assert( pOpen->nLock>=0 );
       if( pOpen->nLock==0 ){
@@ -2680,10 +2866,9 @@ static int afpUnlock(sqlite3_file *id, int locktype) {
       }
     }
   }
+  
   unixLeaveMutex();
-  if( rc==SQLITE_OK ){
-    pFile->locktype = locktype;
-  }
+  if( rc==SQLITE_OK ) pFile->locktype = locktype;
   return rc;
 }
 
@@ -2691,6 +2876,7 @@ static int afpUnlock(sqlite3_file *id, int locktype) {
 ** Close a file & cleanup AFP specific locking context 
 */
 static int afpClose(sqlite3_file *id) {
+  int rc = SQLITE_OK;
   if( id ){
     unixFile *pFile = (unixFile*)id;
     afpUnlock(id, NO_LOCK);
@@ -2703,12 +2889,13 @@ static int afpClose(sqlite3_file *id) {
       */
       setPendingFd(pFile);
     }
+    releaseLockInfo(pFile->pLock);
     releaseOpenCnt(pFile->pOpen);
     sqlite3_free(pFile->lockingContext);
-    closeUnixFile(id);
+    rc = closeUnixFile(id);
     unixLeaveMutex();
   }
-  return SQLITE_OK;
+  return rc;
 }
 
 #endif /* defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE */
@@ -2721,6 +2908,29 @@ static int afpClose(sqlite3_file *id) {
 ********************* End of the AFP lock implementation **********************
 ******************************************************************************/
 
+/******************************************************************************
+*************************** Begin NFS Locking ********************************/
+
+#if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE
+/*
+ ** Lower the locking level on file descriptor pFile to locktype.  locktype
+ ** must be either NO_LOCK or SHARED_LOCK.
+ **
+ ** If the locking level of the file descriptor is already at or below
+ ** the requested locking level, this routine is a no-op.
+ */
+static int nfsUnlock(sqlite3_file *id, int locktype){
+  return _posixUnlock(id, locktype, 1);
+}
+
+#endif /* defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE */
+/*
+** The code above is the NFS lock implementation.  The code is specific
+** to MacOSX and does not work on other unix platforms.  No alternative
+** is available.  
+**
+********************* End of the NFS lock implementation **********************
+******************************************************************************/
 
 /******************************************************************************
 **************** Non-locking sqlite3_file methods *****************************
@@ -2747,7 +2957,9 @@ static int afpClose(sqlite3_file *id) {
 */
 static int seekAndRead(unixFile *id, sqlite3_int64 offset, void *pBuf, int cnt){
   int got;
+#if (!defined(USE_PREAD) && !defined(USE_PREAD64))
   i64 newOffset;
+#endif
   TIMER_START;
 #if defined(USE_PREAD)
   got = pread(id->h, pBuf, cnt, offset);
@@ -2821,7 +3033,9 @@ static int unixRead(
 */
 static int seekAndWrite(unixFile *id, i64 offset, const void *pBuf, int cnt){
   int got;
+#if (!defined(USE_PREAD) && !defined(USE_PREAD64))
   i64 newOffset;
+#endif
   TIMER_START;
 #if defined(USE_PREAD)
   got = pwrite(id->h, pBuf, cnt, offset);
@@ -3015,7 +3229,11 @@ static int full_fsync(int fd, int fullSync, int dataOnly){
   */
   if( rc ) rc = fsync(fd);
 
-#else 
+#elif defined(__APPLE__)
+  // fdatasync() on HFS+ doesn't yet flush the file size if it changed correctly
+       // so currently we default to the macro that redefines fdatasync to fsync
+  rc = fsync(fd);
+#else
   rc = fdatasync(fd);
 #if OS_VXWORKS
   if( rc==-1 && errno==ENOTSUP ){
@@ -3336,23 +3554,6 @@ IOMETHODS(
 )
 #endif
 
-/*
-** The "Whole File Locking" finder returns the same set of methods as
-** the posix locking finder.  But it also sets the SQLITE_WHOLE_FILE_LOCKING
-** flag to force the posix advisory locks to cover the whole file instead
-** of just a small span of bytes near the 1GiB boundary.  Whole File Locking
-** is useful on NFS-mounted files since it helps NFS to maintain cache
-** coherency.  But it is a detriment to other filesystems since it runs
-** slower.
-*/
-static const sqlite3_io_methods *posixWflIoFinderImpl(const char*z, unixFile*p){
-  UNUSED_PARAMETER(z);
-  p->fileFlags = SQLITE_WHOLE_FILE_LOCKING;
-  return &posixIoMethods;
-}
-static const sqlite3_io_methods 
-  *(*const posixWflIoFinder)(const char*,unixFile *p) = posixWflIoFinderImpl;
-
 /*
 ** The proxy locking method is a "super-method" in the sense that it
 ** opens secondary file descriptors for the conch and lock files and
@@ -3377,6 +3578,17 @@ IOMETHODS(
 )
 #endif
 
+/* nfs lockd on OSX 10.3+ doesn't clear write locks when a read lock is set */
+#if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE
+IOMETHODS(
+  nfsIoFinder,               /* Finder function name */
+  nfsIoMethods,              /* sqlite3_io_methods object name */
+  unixClose,                 /* xClose method */
+  unixLock,                  /* xLock method */
+  nfsUnlock,                 /* xUnlock method */
+  unixCheckReservedLock      /* xCheckReservedLock method */
+)
+#endif
 
 #if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE
 /* 
@@ -3397,11 +3609,7 @@ static const sqlite3_io_methods *autolockIoFinderImpl(
     { "hfs",    &posixIoMethods },
     { "ufs",    &posixIoMethods },
     { "afpfs",  &afpIoMethods },
-#ifdef SQLITE_ENABLE_AFP_LOCKING_SMB
     { "smbfs",  &afpIoMethods },
-#else
-    { "smbfs",  &flockIoMethods },
-#endif
     { "webdav", &nolockIoMethods },
     { 0, 0 }
   };
@@ -3434,8 +3642,11 @@ static const sqlite3_io_methods *autolockIoFinderImpl(
   lockInfo.l_whence = SEEK_SET;
   lockInfo.l_type = F_RDLCK;
   if( fcntl(pNew->h, F_GETLK, &lockInfo)!=-1 ) {
-    pNew->fileFlags = SQLITE_WHOLE_FILE_LOCKING;
-    return &posixIoMethods;
+    if( strcmp(fsInfo.f_fstypename, "nfs")==0 ){
+      return &nfsIoMethods;
+    } else {
+      return &posixIoMethods;
+    }
   }else{
     return &dotlockIoMethods;
   }
@@ -3546,7 +3757,7 @@ static int fillInUnixFile(
 #endif
   }
 
-  if( pLockingStyle == &posixIoMethods ){
+  if( pLockingStyle == &posixIoMethods || pLockingStyle == &nfsIoMethods ){
     unixEnterMutex();
     rc = findLockInfo(pNew, &pNew->pLock, &pNew->pOpen);
     if( rc!=SQLITE_OK ){
@@ -3588,9 +3799,15 @@ static int fillInUnixFile(
       ** according to requirement F11141.  So we do not need to make a
       ** copy of the filename. */
       pCtx->dbPath = zFilename;
+      pCtx->reserved = 0;
       srandomdev();
       unixEnterMutex();
-      rc = findLockInfo(pNew, NULL, &pNew->pOpen);
+      rc = findLockInfo(pNew, &pNew->pLock, &pNew->pOpen);
+      if( rc!=SQLITE_OK ){
+        sqlite3_free(pNew->lockingContext);
+        close(h);
+        h = -1;
+      }
       unixLeaveMutex();        
     }
   }
@@ -3854,7 +4071,8 @@ static int unixOpen(
   int isCreate     = (flags & SQLITE_OPEN_CREATE);
   int isReadonly   = (flags & SQLITE_OPEN_READONLY);
   int isReadWrite  = (flags & SQLITE_OPEN_READWRITE);
-
+  int isAutoProxy  = (flags & SQLITE_OPEN_AUTOPROXY);
+  
   /* If creating a master or main-file journal, this function will open
   ** a file-descriptor on the directory too. The first time unixSync()
   ** is called the directory file descriptor will be fsync()ed and close()d.
@@ -3988,7 +4206,36 @@ static int unixOpen(
   noLock = eType!=SQLITE_OPEN_MAIN_DB;
 
 #if SQLITE_PREFER_PROXY_LOCKING
-  if( zPath!=NULL && !noLock && pVfs->xOpen ){
+  isAutoProxy = 1;
+#endif
+  
+#if defined(__APPLE__) || SQLITE_ENABLE_LOCKING_STYLE
+
+  struct statfs fsInfo;
+  if( fstatfs(fd, &fsInfo) == -1 ){
+    ((unixFile*)pFile)->lastErrno = errno;
+    if( dirfd>=0 ) close(dirfd); /* silently leak if fail, in error */
+    close(fd); /* silently leak if fail, in error */
+    return SQLITE_IOERR_ACCESS;
+  }
+  
+  ((unixFile*)pFile)->fsFlags = SQLITE_FSFLAGS_INITIALIZED;
+  
+  if (fsInfo.f_flags & MNT_RDONLY) {
+    ((unixFile*)pFile)->fsFlags |= SQLITE_FSFLAGS_IS_READONLY;
+  }
+  if (fsInfo.f_flags & MNT_LOCAL) {
+    ((unixFile*)pFile)->fsFlags |= SQLITE_FSFLAGS_IS_LOCAL;
+  }
+  if (0 == strncmp("hfs", fsInfo.f_fstypename, 3)) {
+    ((unixFile*)pFile)->fsFlags |= SQLITE_FSFLAGS_IS_HFSPLUS;
+  } else if (0 == strncmp("msdos", fsInfo.f_fstypename, 5)) {
+    ((unixFile*)pFile)->fsFlags |= SQLITE_FSFLAGS_IS_MSDOS;
+  }
+#endif
+  
+#if SQLITE_ENABLE_LOCKING_STYLE
+  if( isAutoProxy && (zPath!=NULL) && (!noLock) && pVfs->xOpen ){
     char *envforce = getenv("SQLITE_FORCE_PROXY_LOCKING");
     int useProxy = 0;
 
@@ -4020,6 +4267,14 @@ static int unixOpen(
       rc = fillInUnixFile(pVfs, fd, dirfd, pFile, zPath, noLock, isDelete);
       if( rc==SQLITE_OK ){
         rc = proxyTransformUnixFile((unixFile*)pFile, ":auto:");
+        if( rc!=SQLITE_OK ){
+          /* Use unixClose to clean up the resources added in fillInUnixFile 
+          ** and clear all the structure's references.  Specifically, 
+          ** pFile->pMethods will be NULL so sqlite3OsClose will be a no-op 
+          */
+          unixClose(pFile);
+          return rc;
+        }
       }
       goto open_finished;
     }
@@ -4447,11 +4702,6 @@ static int unixGetLastError(sqlite3_vfs *NotUsed, int NotUsed2, char *NotUsed3){
 ** of the database file for multiple readers and writers on the same
 ** host (the conch ensures that they all use the same local lock file).
 **
-** There is a third file - the host ID file - used as a persistent record
-** of a unique identifier for the host, a 128-byte unique host id file
-** in the path defined by the HOSTIDPATH macro (default value is
-** /Library/Caches/.com.apple.sqliteConchHostId).
-**
 ** Requesting the lock proxy does not immediately take the conch, it is
 ** only taken when the first request to lock database file is made.  
 ** This matches the semantics of the traditional locking behavior, where
@@ -4477,10 +4727,6 @@ static int unixGetLastError(sqlite3_vfs *NotUsed, int NotUsed2, char *NotUsed3){
 **       Enables the logging of error messages during host id file
 **       retrieval and creation
 **
-**  HOSTIDPATH
-**
-**       Overrides the default host ID file path location
-**
 **  LOCKPROXYDIR
 **
 **       Overrides the default directory used for lock proxy files that
@@ -4505,11 +4751,6 @@ static int unixGetLastError(sqlite3_vfs *NotUsed, int NotUsed2, char *NotUsed3){
 */
 #if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE
 
-#ifdef SQLITE_TEST
-/* simulate multiple hosts by creating unique hostid file paths */
-int sqlite3_hostid_num = 0;
-#endif
-
 /*
 ** The proxyLockingContext has the path and file structures for the remote 
 ** and local proxy files in it
@@ -4521,134 +4762,16 @@ struct proxyLockingContext {
   unixFile *lockProxy;         /* Open proxy lock file */
   char *lockProxyPath;         /* Name of the proxy lock file */
   char *dbPath;                /* Name of the open file */
-  int conchHeld;               /* True if the conch is currently held */
+  int conchHeld;               /* 1 if the conch is held, -1 if lockless */
   void *oldLockingContext;     /* Original lockingcontext to restore on close */
   sqlite3_io_methods const *pOldMethod;     /* Original I/O methods for close */
 };
 
-/* HOSTIDLEN and CONCHLEN both include space for the string 
-** terminating nul 
-*/
-#define HOSTIDLEN         128
-#define CONCHLEN          (MAXPATHLEN+HOSTIDLEN+1)
-#ifndef HOSTIDPATH
-# define HOSTIDPATH       "/Library/Caches/.com.apple.sqliteConchHostId"
-#endif
-
-/* basically a copy of unixRandomness with different
-** test behavior built in */
-static int proxyGenerateHostID(char *pHostID){
-  int pid, fd, len;
-  unsigned char *key = (unsigned char *)pHostID;
-  
-  memset(key, 0, HOSTIDLEN);
-  len = 0;
-  fd = open("/dev/urandom", O_RDONLY);
-  if( fd>=0 ){
-    len = read(fd, key, HOSTIDLEN);
-    close(fd); /* silently leak the fd if it fails */
-  }
-  if( len < HOSTIDLEN ){
-    time_t t;
-    time(&t);
-    memcpy(key, &t, sizeof(t));
-    pid = getpid();
-    memcpy(&key[sizeof(t)], &pid, sizeof(pid));
-  }
-  
-#ifdef MAKE_PRETTY_HOSTID
-  {
-    int i;
-    /* filter the bytes into printable ascii characters and NUL terminate */
-    key[(HOSTIDLEN-1)] = 0x00;
-    for( i=0; i<(HOSTIDLEN-1); i++ ){
-      unsigned char pa = key[i]&0x7F;
-      if( pa<0x20 ){
-        key[i] = (key[i]&0x80 == 0x80) ? pa+0x40 : pa+0x20;
-      }else if( pa==0x7F ){
-        key[i] = (key[i]&0x80 == 0x80) ? pa=0x20 : pa+0x7E;
-      }
-    }
-  }
-#endif
-  return SQLITE_OK;
-}
-
-/* writes the host id path to path, path should be an pre-allocated buffer
-** with enough space for a path 
-*/
-static void proxyGetHostIDPath(char *path, size_t len){
-  strlcpy(path, HOSTIDPATH, len);
-#ifdef SQLITE_TEST
-  if( sqlite3_hostid_num>0 ){
-    char suffix[2] = "1";
-    suffix[0] = suffix[0] + sqlite3_hostid_num;
-    strlcat(path, suffix, len);
-  }
-#endif
-  OSTRACE3("GETHOSTIDPATH  %s pid=%d\n", path, getpid());
-}
-
-/* get the host ID from a sqlite hostid file stored in the 
-** user-specific tmp directory, create the ID if it's not there already 
+/* 
+** The proxy lock file path for the database at dbPath is written into lPath, 
+** which must point to valid, writable memory large enough for a maxLen length
+** file path. 
 */
-static int proxyGetHostID(char *pHostID, int *pError){
-  int fd;
-  char path[MAXPATHLEN]; 
-  size_t len;
-  int rc=SQLITE_OK;
-
-  proxyGetHostIDPath(path, MAXPATHLEN);
-  /* try to create the host ID file, if it already exists read the contents */
-  fd = open(path, O_CREAT|O_WRONLY|O_EXCL, 0644);
-  if( fd<0 ){
-    int err=errno;
-               
-    if( err!=EEXIST ){
-#ifdef SQLITE_PROXY_DEBUG /* set the sqlite error message instead */
-      fprintf(stderr, "sqlite error creating host ID file %s: %s\n",
-              path, strerror(err));
-#endif
-      return SQLITE_PERM;
-    }
-    /* couldn't create the file, read it instead */
-    fd = open(path, O_RDONLY|O_EXCL);
-    if( fd<0 ){
-#ifdef SQLITE_PROXY_DEBUG /* set the sqlite error message instead */
-      int err = errno;
-      fprintf(stderr, "sqlite error opening host ID file %s: %s\n",
-              path, strerror(err));
-#endif
-      return SQLITE_PERM;
-    }
-    len = pread(fd, pHostID, HOSTIDLEN, 0);
-    if( len<0 ){
-      *pError = errno;
-      rc = SQLITE_IOERR_READ;
-    }else if( len<HOSTIDLEN ){
-      *pError = 0;
-      rc = SQLITE_IOERR_SHORT_READ;
-    }
-    close(fd); /* silently leak the fd if it fails */
-    OSTRACE3("GETHOSTID  read %s pid=%d\n", pHostID, getpid());
-    return rc;
-  }else{
-    /* we're creating the host ID file (use a random string of bytes) */
-    proxyGenerateHostID(pHostID);
-    len = pwrite(fd, pHostID, HOSTIDLEN, 0);
-    if( len<0 ){
-      *pError = errno;
-      rc = SQLITE_IOERR_WRITE;
-    }else if( len<HOSTIDLEN ){
-      *pError = 0;
-      rc = SQLITE_IOERR_WRITE;
-    }
-    close(fd); /* silently leak the fd if it fails */
-    OSTRACE3("GETHOSTID  wrote %s pid=%d\n", pHostID, getpid());
-    return rc;
-  }
-}
-
 static int proxyGetLockPath(const char *dbPath, char *lPath, size_t maxLen){
   int len;
   int dbLen;
@@ -4659,21 +4782,11 @@ static int proxyGetLockPath(const char *dbPath, char *lPath, size_t maxLen){
 #else
 # ifdef _CS_DARWIN_USER_TEMP_DIR
   {
-    confstr(_CS_DARWIN_USER_TEMP_DIR, lPath, maxLen);
-    len = strlcat(lPath, "sqliteplocks", maxLen);
-    if( mkdir(lPath, SQLITE_DEFAULT_PROXYDIR_PERMISSIONS) ){
-      /* if mkdir fails, handle as lock file creation failure */
-#  ifdef SQLITE_DEBUG
-      int err = errno;
-      if( err!=EEXIST ){
-        fprintf(stderr, "proxyGetLockPath: mkdir(%s,0%o) error %d %s\n", lPath,
-                SQLITE_DEFAULT_PROXYDIR_PERMISSIONS, err, strerror(err));
-      }
-#  endif
-    }else{
-      OSTRACE3("GETLOCKPATH  mkdir %s pid=%d\n", lPath, getpid());
+    if( !confstr(_CS_DARWIN_USER_TEMP_DIR, lPath, maxLen) ){
+      OSTRACE4("GETLOCKPATH  failed %s errno=%d pid=%d\n", lPath, errno, getpid());
+      return SQLITE_IOERR_LOCK;
     }
-    
+    len = strlcat(lPath, "sqliteplocks", maxLen);    
   }
 # else
   len = strlcpy(lPath, "/tmp/", maxLen);
@@ -4692,9 +4805,44 @@ static int proxyGetLockPath(const char *dbPath, char *lPath, size_t maxLen){
   }
   lPath[i+len]='\0';
   strlcat(lPath, ":auto:", maxLen);
+  OSTRACE3("GETLOCKPATH  proxy lock path=%s pid=%d\n", lPath, getpid());
   return SQLITE_OK;
 }
 
+/* 
+ ** Creates the lock file and any missing directories in lockPath
+ */
+static int proxyCreateLockPath(const char *lockPath){
+  int i, len;
+  char buf[MAXPATHLEN];
+  int start = 0;
+  
+  assert(lockPath!=NULL);
+  /* try to create all the intermediate directories */
+  len = (int)strlen(lockPath);
+  buf[0] = lockPath[0];
+  for( i=1; i<len; i++ ){
+    if( lockPath[i] == '/' && (i - start > 0) ){
+      /* only mkdir if leaf dir != "." or "/" or ".." */
+      if( i-start>2 || (i-start==1 && buf[start] != '.' && buf[start] != '/') 
+         || (i-start==2 && buf[start] != '.' && buf[start+1] != '.') ){
+        buf[i]='\0';
+        if( mkdir(buf, SQLITE_DEFAULT_PROXYDIR_PERMISSIONS) ){
+          int err=errno;
+          if( err!=EEXIST ) {
+            OSTRACE5("CREATELOCKPATH  FAILED creating %s, '%s' proxy lock path=%s pid=%d\n", buf, strerror(err), lockPath, getpid());
+            return err;
+          }
+        }
+      }
+      start=i+1;
+    }
+    buf[i] = lockPath[i];
+  }
+  OSTRACE3("CREATELOCKPATH  proxy lock path=%s pid=%d\n", lockPath, getpid());
+  return 0;
+}
+
 /*
 ** Create a new VFS file descriptor (stored in memory obtained from
 ** sqlite3_malloc) and open the file named "path" in the file descriptor.
@@ -4702,48 +4850,263 @@ static int proxyGetLockPath(const char *dbPath, char *lPath, size_t maxLen){
 ** The caller is responsible not only for closing the file descriptor
 ** but also for freeing the memory associated with the file descriptor.
 */
-static int proxyCreateUnixFile(const char *path, unixFile **ppFile) {
+static int proxyCreateUnixFile(
+    const char *path,        /* path for the new unixFile */
+    unixFile **ppFile,       /* unixFile created and returned by ref */
+    int islockfile           /* if non zero missing dirs will be created */
+) {
+  int fd = -1;
+  int dirfd = -1;
   unixFile *pNew;
-  int flags = SQLITE_OPEN_MAIN_DB|SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE;
   int rc = SQLITE_OK;
+  int openFlags = O_RDWR | O_CREAT;
   sqlite3_vfs dummyVfs;
-
-  pNew = (unixFile *)sqlite3_malloc(sizeof(unixFile));
-  if( !pNew ){
-    return SQLITE_NOMEM;
+  int terrno = 0;
+  UnixUnusedFd *pUnused = NULL;
+
+  /* 1. first try to open/create the file
+  ** 2. if that fails, and this is a lock file (not-conch), try creating
+  ** the parent directories and then try again.
+  ** 3. if that fails, try to open the file read-only
+  ** otherwise return BUSY (if lock file) or CANTOPEN for the conch file
+  */
+  pUnused = findReusableFd(path, openFlags);
+  if( pUnused ){
+    fd = pUnused->fd;
+  }else{
+    pUnused = sqlite3_malloc(sizeof(*pUnused));
+    if( !pUnused ){
+      return SQLITE_NOMEM;
+    }
+  }
+  if( fd<0 ){
+    fd = open(path, openFlags, SQLITE_DEFAULT_FILE_PERMISSIONS);
+    terrno = errno;
+    if( fd<0 && errno==ENOENT && islockfile ){
+      if( proxyCreateLockPath(path) == SQLITE_OK ){
+        fd = open(path, openFlags, SQLITE_DEFAULT_FILE_PERMISSIONS);
+      }
+    }
+  }
+  if( fd<0 ){
+    openFlags = O_RDONLY;
+    fd = open(path, openFlags, SQLITE_DEFAULT_FILE_PERMISSIONS);
+    terrno = errno;
+  }
+  if( fd<0 ){
+    if( islockfile ){
+      return SQLITE_BUSY;
+    }
+    switch (terrno) {
+      case EACCES:
+        return SQLITE_PERM;
+      case EIO: 
+        return SQLITE_IOERR_LOCK; /* even though it is the conch */
+      default:
+        return SQLITE_CANTOPEN;
+    }
+  }
+  
+  pNew = (unixFile *)sqlite3_malloc(sizeof(*pNew));
+  if( pNew==NULL ){
+    rc = SQLITE_NOMEM;
+    goto end_create_proxy;
   }
   memset(pNew, 0, sizeof(unixFile));
-
-  /* Call unixOpen() to open the proxy file. The flags passed to unixOpen()
-  ** suggest that the file being opened is a "main database". This is
-  ** necessary as other file types do not necessarily support locking. It
-  ** is better to use unixOpen() instead of opening the file directly with
-  ** open(), as unixOpen() sets up the various mechanisms required to
-  ** make sure a call to close() does not cause the system to discard
-  ** POSIX locks prematurely.
-  **
-  ** It is important that the xOpen member of the VFS object passed to 
-  ** unixOpen() is NULL. This tells unixOpen() may try to open a proxy-file 
-  ** for the proxy-file (creating a potential infinite loop).
-  */
+  pNew->openFlags = openFlags;
   dummyVfs.pAppData = (void*)&autolockIoFinder;
-  dummyVfs.xOpen = 0;
-  rc = unixOpen(&dummyVfs, path, (sqlite3_file *)pNew, flags, &flags);
-  if( rc==SQLITE_OK && (flags&SQLITE_OPEN_READONLY) ){
-    pNew->pMethod->xClose((sqlite3_file *)pNew);
-    rc = SQLITE_CANTOPEN;
+  pUnused->fd = fd;
+  pUnused->flags = openFlags;
+  pNew->pUnused = pUnused;
+  
+  rc = fillInUnixFile(&dummyVfs, fd, dirfd, (sqlite3_file*)pNew, path, 0, 0);
+  if( rc==SQLITE_OK ){
+    *ppFile = pNew;
+    return SQLITE_OK;
   }
+end_create_proxy:    
+  close(fd); /* silently leak fd if error, we're already in error */
+  sqlite3_free(pNew);
+  sqlite3_free(pUnused);
+  return rc;
+}
 
-  if( rc!=SQLITE_OK ){
-    sqlite3_free(pNew);
-    pNew = 0;
+#ifdef SQLITE_TEST
+/* simulate multiple hosts by creating unique hostid file paths */
+int sqlite3_hostid_num = 0;
+#endif
+
+#define PROXY_HOSTIDLEN    16  /* conch file host id length */
+
+/* get the host ID via gethostuuid(), pHostID must point to PROXY_HOSTIDLEN 
+** bytes of writable memory.
+*/
+static int proxyGetHostID(unsigned char *pHostID, int *pError){
+  struct timespec timeout = {1, 0}; /* 1 sec timeout */
+  
+  assert(PROXY_HOSTIDLEN == sizeof(uuid_t));
+  memset(pHostID, 0, PROXY_HOSTIDLEN);
+  if( gethostuuid(pHostID, &timeout) ){
+    int err = errno;
+    if( pError ){
+      *pError = err;
+    }
+    return SQLITE_IOERR;
+  }
+#ifdef SQLITE_TEST
+  /* simulate multiple hosts by creating unique hostid file paths */
+  if( sqlite3_hostid_num != 0){
+    pHostID[0] = (char)(pHostID[0] + (char)(sqlite3_hostid_num & 0xFF));
+  }
+#endif
+  
+  return SQLITE_OK;
+}
+
+/* The conch file contains the header, host id and lock file path
+ */
+#define PROXY_CONCHVERSION 2   /* 1-byte header, 16-byte host id, path */
+#define PROXY_HEADERLEN    1   /* conch file header length */
+#define PROXY_PATHINDEX    (PROXY_HEADERLEN+PROXY_HOSTIDLEN)
+#define PROXY_MAXCONCHLEN  (PROXY_HEADERLEN+PROXY_HOSTIDLEN+MAXPATHLEN)
+
+/* 
+** Takes an open conch file, copies the contents to a new path and then moves 
+** it back.  The newly created file's file descriptor is assigned to the
+** conch file structure and finally the original conch file descriptor is 
+** closed.  Returns zero if successful.
+*/
+static int proxyBreakConchLock(unixFile *pFile, uuid_t myHostID){
+  proxyLockingContext *pCtx = (proxyLockingContext *)pFile->lockingContext; 
+  unixFile *conchFile = pCtx->conchFile;
+  char tPath[MAXPATHLEN];
+  char buf[PROXY_MAXCONCHLEN];
+  char *cPath = pCtx->conchFilePath;
+  size_t readLen = 0;
+  size_t pathLen = 0;
+  char errmsg[64] = "";
+  int fd = -1;
+  int rc = -1;
+
+  /* create a new path by replace the trailing '-conch' with '-break' */
+  pathLen = strlcpy(tPath, cPath, MAXPATHLEN);
+  if( pathLen>MAXPATHLEN || pathLen<6 || 
+     (strlcpy(&tPath[pathLen-5], "break", 6) != 5) ){
+    sprintf(errmsg, "path error (len %d)", (int)pathLen);
+    goto end_breaklock;
+  }
+  /* read the conch content */
+  readLen = pread(conchFile->h, buf, PROXY_MAXCONCHLEN, 0);
+  if( readLen<PROXY_PATHINDEX ){
+    sprintf(errmsg, "read error (len %d)", (int)readLen);
+    goto end_breaklock;
+  }
+  /* write it out to the temporary break file */
+  fd = open(tPath, (O_RDWR|O_CREAT|O_EXCL), SQLITE_DEFAULT_FILE_PERMISSIONS);
+  if( fd<0 ){
+    sprintf(errmsg, "create failed (%d)", errno);
+    goto end_breaklock;
+  }
+  if( pwrite(fd, buf, readLen, 0) != readLen ){
+    sprintf(errmsg, "write failed (%d)", errno);
+    goto end_breaklock;
   }
+  if( rename(tPath, cPath) ){
+    sprintf(errmsg, "rename failed (%d)", errno);
+    goto end_breaklock;
+  }
+  rc = 0;
+  fprintf(stderr, "broke stale lock on %s\n", cPath);
+  close(conchFile->h);
+  conchFile->h = fd;
+  conchFile->openFlags = O_RDWR | O_CREAT;
 
-  *ppFile = pNew;
+end_breaklock:
+  if( rc ){
+    if( fd>=0 ){
+      unlink(tPath);
+      close(fd);
+    }
+    fprintf(stderr, "failed to break stale lock on %s, %s\n", cPath, errmsg);
+  }
   return rc;
 }
 
-/* takes the conch by taking a shared lock and read the contents conch, if 
+/* Take the requested lock on the conch file and break a stale lock if the 
+** host id matches.
+*/
+static int proxyConchLock(unixFile *pFile, uuid_t myHostID, int lockType){
+  proxyLockingContext *pCtx = (proxyLockingContext *)pFile->lockingContext; 
+  unixFile *conchFile = pCtx->conchFile;
+  int rc = SQLITE_OK;
+  int nTries = 0;
+  struct timespec conchModTime;
+  
+  do {
+    rc = conchFile->pMethod->xLock((sqlite3_file*)conchFile, lockType);
+    nTries ++;
+    if( rc==SQLITE_BUSY ){
+      /* If the lock failed (busy):
+       * 1st try: get the mod time of the conch, wait 0.5s and try again. 
+       * 2nd try: fail if the mod time changed or host id is different, wait 
+       *           10 sec and try again
+       * 3rd try: break the lock unless the mod time has changed.
+       */
+      struct stat buf;
+      if( fstat(conchFile->h, &buf) ){
+        pFile->lastErrno = errno;
+        return SQLITE_IOERR_LOCK;
+      }
+      
+      if( nTries==1 ){
+        conchModTime = buf.st_mtimespec;
+        usleep(500000); /* wait 0.5 sec and try the lock again*/
+        continue;  
+      }
+
+      assert( nTries>1 );
+      if( conchModTime.tv_sec != buf.st_mtimespec.tv_sec || 
+         conchModTime.tv_nsec != buf.st_mtimespec.tv_nsec ){
+        return SQLITE_BUSY;
+      }
+      
+      if( nTries==2 ){  
+        char tBuf[PROXY_MAXCONCHLEN];
+        int len = pread(conchFile->h, tBuf, PROXY_MAXCONCHLEN, 0);
+        if( len<0 ){
+          pFile->lastErrno = errno;
+          return SQLITE_IOERR_LOCK;
+        }
+        if( len>PROXY_PATHINDEX && tBuf[0]==(char)PROXY_CONCHVERSION){
+          /* don't break the lock if the host id doesn't match */
+          if( 0!=memcmp(&tBuf[PROXY_HEADERLEN], myHostID, PROXY_HOSTIDLEN) ){
+            return SQLITE_BUSY;
+          }
+        }else{
+          /* don't break the lock on short read or a version mismatch */
+          return SQLITE_BUSY;
+        }
+        usleep(10000000); /* wait 10 sec and try the lock again */
+        continue; 
+      }
+      
+      assert( nTries==3 );
+      if( 0==proxyBreakConchLock(pFile, myHostID) ){
+        rc = SQLITE_OK;
+        if( lockType==EXCLUSIVE_LOCK ){
+          rc = conchFile->pMethod->xLock((sqlite3_file*)conchFile, SHARED_LOCK);          
+        }
+        if( !rc ){
+          rc = conchFile->pMethod->xLock((sqlite3_file*)conchFile, lockType);
+        }
+      }
+    }
+  } while( rc==SQLITE_BUSY && nTries<3 );
+  
+  return rc;
+}
+
+/* Takes the conch by taking a shared lock and read the contents conch, if 
 ** lockPath is non-NULL, the host ID and lock file path must match.  A NULL 
 ** lockPath means that the lockPath in the conch file will be used if the 
 ** host IDs match, or a new lock path will be generated automatically 
@@ -4752,150 +5115,218 @@ static int proxyCreateUnixFile(const char *path, unixFile **ppFile) {
 static int proxyTakeConch(unixFile *pFile){
   proxyLockingContext *pCtx = (proxyLockingContext *)pFile->lockingContext; 
   
-  if( pCtx->conchHeld>0 ){
+  if( pCtx->conchHeld!=0 ){
     return SQLITE_OK;
   }else{
     unixFile *conchFile = pCtx->conchFile;
-    char testValue[CONCHLEN];
-    char conchValue[CONCHLEN];
+    uuid_t myHostID;
+    int pError = 0;
+    char readBuf[PROXY_MAXCONCHLEN];
     char lockPath[MAXPATHLEN];
-    char *tLockPath = NULL;
+    char *tempLockPath = NULL;
     int rc = SQLITE_OK;
-    int readRc = SQLITE_OK;
-    int syncPerms = 0;
-
+    int createConch = 0;
+    int hostIdMatch = 0;
+    int readLen = 0;
+    int tryOldLockPath = 0;
+    int forceNewLockPath = 0;
+    
     OSTRACE4("TAKECONCH  %d for %s pid=%d\n", conchFile->h,
              (pCtx->lockProxyPath ? pCtx->lockProxyPath : ":auto:"), getpid());
 
-    rc = conchFile->pMethod->xLock((sqlite3_file*)conchFile, SHARED_LOCK);
-    if( rc==SQLITE_OK ){
-      int pError = 0;
-      memset(testValue, 0, CONCHLEN); /* conch is fixed size */
-      rc = proxyGetHostID(testValue, &pError);
-      if( (rc&0xff)==SQLITE_IOERR ){
-        pFile->lastErrno = pError;
-      }
-      if( pCtx->lockProxyPath ){
-        strlcpy(&testValue[HOSTIDLEN], pCtx->lockProxyPath, MAXPATHLEN);
-      }
+    rc = proxyGetHostID(myHostID, &pError);
+    if( (rc&0xff)==SQLITE_IOERR ){
+      pFile->lastErrno = pError;
+      goto end_takeconch;
     }
+    rc = proxyConchLock(pFile, myHostID, SHARED_LOCK);
     if( rc!=SQLITE_OK ){
       goto end_takeconch;
     }
-    
-    readRc = unixRead((sqlite3_file *)conchFile, conchValue, CONCHLEN, 0);
-    if( readRc!=SQLITE_IOERR_SHORT_READ ){
-      if( readRc!=SQLITE_OK ){
-        if( (rc&0xff)==SQLITE_IOERR ){
-          pFile->lastErrno = conchFile->lastErrno;
+    /* read the existing conch file */
+    readLen = seekAndRead((unixFile*)conchFile, 0, readBuf, PROXY_MAXCONCHLEN);
+    if( readLen<0 ){
+      /* I/O error: lastErrno set by seekAndRead */
+      pFile->lastErrno = conchFile->lastErrno;
+      rc = SQLITE_IOERR_READ;
+      goto end_takeconch;
+    }else if( readLen<=(PROXY_HEADERLEN+PROXY_HOSTIDLEN) || 
+             readBuf[0]!=(char)PROXY_CONCHVERSION ){
+      /* a short read or version format mismatch means we need to create a new 
+      ** conch file. 
+      */
+      createConch = 1;
+    }
+    /* if the host id matches and the lock path already exists in the conch
+    ** we'll try to use the path there, if we can't open that path, we'll 
+    ** retry with a new auto-generated path 
+    */
+    do { /* in case we need to try again for an :auto: named lock file */
+
+      if( !createConch && !forceNewLockPath ){
+        hostIdMatch = !memcmp(&readBuf[PROXY_HEADERLEN], myHostID, 
+                                  PROXY_HOSTIDLEN);
+        /* if the conch has data compare the contents */
+        if( !pCtx->lockProxyPath ){
+          /* for auto-named local lock file, just check the host ID and we'll
+           ** use the local lock file path that's already in there
+           */
+          if( hostIdMatch ){
+            size_t pathLen = (readLen - PROXY_PATHINDEX);
+            
+            if( pathLen>=MAXPATHLEN ){
+              pathLen=MAXPATHLEN-1;
+            }
+            memcpy(lockPath, &readBuf[PROXY_PATHINDEX], pathLen);
+            lockPath[pathLen] = 0;
+            tempLockPath = lockPath;
+            tryOldLockPath = 1;
+            /* create a copy of the lock path if the conch is taken */
+            goto end_takeconch;
+          }
+        }else if( hostIdMatch && !strncmp(pCtx->lockProxyPath, 
+                                          &readBuf[PROXY_PATHINDEX], readLen-PROXY_PATHINDEX) ){
+          /* conch host and lock path match */
+          goto end_takeconch; 
         }
-        rc = readRc;
+      }
+      
+      /* if the conch isn't writable and doesn't match, we can't take it */
+      if( (conchFile->openFlags&O_RDWR) == 0 ){
+        rc = SQLITE_BUSY;
         goto end_takeconch;
       }
-      /* if the conch has data compare the contents */
+      
+      /* either the conch didn't match or we need to create a new one */
       if( !pCtx->lockProxyPath ){
-        /* for auto-named local lock file, just check the host ID and we'll
-         ** use the local lock file path that's already in there */
-        if( !memcmp(testValue, conchValue, HOSTIDLEN) ){
-          tLockPath = (char *)&conchValue[HOSTIDLEN];
-          goto end_takeconch;
+        proxyGetLockPath(pCtx->dbPath, lockPath, MAXPATHLEN);
+        tempLockPath = lockPath;
+        /* create a copy of the lock path _only_ if the conch is taken */
+      }
+      
+      /* update conch with host and path (this will fail if other process
+      ** has a shared lock already), if the host id matches, use the big
+      ** stick.
+      */
+      futimes(conchFile->h, NULL);
+      if( hostIdMatch && !createConch ){
+        if( conchFile->pLock && conchFile->pLock->cnt>1 ){
+          /* We are trying for an exclusive lock but another thread in this
+           ** same process is still holding a shared lock. */
+          rc = SQLITE_BUSY;
+        } else {          
+          rc = proxyConchLock(pFile, myHostID, EXCLUSIVE_LOCK);
         }
       }else{
-        /* we've got the conch if conchValue matches our path and host ID */
-        if( !memcmp(testValue, conchValue, CONCHLEN) ){
-          goto end_takeconch;
-        }
+        rc = conchFile->pMethod->xLock((sqlite3_file*)conchFile, EXCLUSIVE_LOCK);
       }
-    }else{
-      /* a short read means we're "creating" the conch (even though it could 
-      ** have been user-intervention), if we acquire the exclusive lock,
-      ** we'll try to match the current on-disk permissions of the database
-      */
-      syncPerms = 1;
-    }
-    
-    /* either conch was emtpy or didn't match */
-    if( !pCtx->lockProxyPath ){
-      proxyGetLockPath(pCtx->dbPath, lockPath, MAXPATHLEN);
-      tLockPath = lockPath;
-      strlcpy(&testValue[HOSTIDLEN], lockPath, MAXPATHLEN);
-    }
-    
-    /* update conch with host and path (this will fail if other process
-     ** has a shared lock already) */
-    rc = conchFile->pMethod->xLock((sqlite3_file*)conchFile, EXCLUSIVE_LOCK);
-    if( rc==SQLITE_OK ){
-      rc = unixWrite((sqlite3_file *)conchFile, testValue, CONCHLEN, 0);
-      if( rc==SQLITE_OK && syncPerms ){
-        struct stat buf;
-        int err = fstat(pFile->h, &buf);
-        if( err==0 ){
-          /* try to match the database file permissions, ignore failure */
+      if( rc==SQLITE_OK ){
+        char writeBuffer[PROXY_MAXCONCHLEN];
+        int writeSize = 0;
+        
+        writeBuffer[0] = (char)PROXY_CONCHVERSION;
+        memcpy(&writeBuffer[PROXY_HEADERLEN], myHostID, PROXY_HOSTIDLEN);
+        if( pCtx->lockProxyPath!=NULL ){
+          strlcpy(&writeBuffer[PROXY_PATHINDEX], pCtx->lockProxyPath, MAXPATHLEN);
+        }else{
+          strlcpy(&writeBuffer[PROXY_PATHINDEX], tempLockPath, MAXPATHLEN);
+        }
+        writeSize = PROXY_PATHINDEX + strlen(&writeBuffer[PROXY_PATHINDEX]);
+        ftruncate(conchFile->h, writeSize);
+        rc = unixWrite((sqlite3_file *)conchFile, writeBuffer, writeSize, 0);
+        fsync(conchFile->h);
+        /* If we created a new conch file (not just updated the contents of a 
+         ** valid conch file), try to match the permissions of the database 
+         */
+        if( rc==SQLITE_OK && createConch ){
+          struct stat buf;
+          int err = fstat(pFile->h, &buf);
+          if( err==0 ){
+            mode_t cmode = buf.st_mode&(S_IRUSR|S_IWUSR | S_IRGRP|S_IWGRP |
+                                        S_IROTH|S_IWOTH);
+            /* try to match the database file R/W permissions, ignore failure */
 #ifndef SQLITE_PROXY_DEBUG
-          fchmod(conchFile->h, buf.st_mode);
+            fchmod(conchFile->h, cmode);
 #else
-          if( fchmod(conchFile->h, buf.st_mode)!=0 ){
+            if( fchmod(conchFile->h, cmode)!=0 ){
+              int code = errno;
+              fprintf(stderr, "fchmod %o FAILED with %d %s\n",
+                      cmode, code, strerror(code));
+            } else {
+              fprintf(stderr, "fchmod %o SUCCEDED\n",cmode);
+            }
+          }else{
             int code = errno;
-            fprintf(stderr, "fchmod %o FAILED with %d %s\n",
-                             buf.st_mode, code, strerror(code));
-          } else {
-            fprintf(stderr, "fchmod %o SUCCEDED\n",buf.st_mode);
-          }
-        }else{
-          int code = errno;
-          fprintf(stderr, "STAT FAILED[%d] with %d %s\n", 
-                          err, code, strerror(code));
+            fprintf(stderr, "STAT FAILED[%d] with %d %s\n", 
+                    err, code, strerror(code));
 #endif
+          }
         }
       }
-    }
-    conchFile->pMethod->xUnlock((sqlite3_file*)conchFile, SHARED_LOCK);
-  
-end_takeconch:
-    OSTRACE2("TRANSPROXY: CLOSE  %d\n", pFile->h);
-    if( rc==SQLITE_OK && pFile->openFlags ){
-      if( pFile->h>=0 ){
+      conchFile->pMethod->xUnlock((sqlite3_file*)conchFile, SHARED_LOCK);
+      
+    end_takeconch:
+      OSTRACE2("TRANSPROXY: CLOSE  %d\n", pFile->h);
+      if( rc==SQLITE_OK && pFile->openFlags ){
+        if( pFile->h>=0 ){
 #ifdef STRICT_CLOSE_ERROR
-        if( close(pFile->h) ){
-          pFile->lastErrno = errno;
-          return SQLITE_IOERR_CLOSE;
-        }
+          if( close(pFile->h) ){
+            pFile->lastErrno = errno;
+            return SQLITE_IOERR_CLOSE;
+          }
 #else
-        close(pFile->h); /* silently leak fd if fail */
+          close(pFile->h); /* silently leak fd if fail */
 #endif
+        }
+        pFile->h = -1;
+        int fd = open(pCtx->dbPath, pFile->openFlags,
+                      SQLITE_DEFAULT_FILE_PERMISSIONS);
+        OSTRACE2("TRANSPROXY: OPEN  %d\n", fd);
+        if( fd>=0 ){
+          pFile->h = fd;
+        }else{
+          rc=SQLITE_CANTOPEN; /* SQLITE_BUSY? proxyTakeConch called
+           during locking */
+        }
       }
-      pFile->h = -1;
-      int fd = open(pCtx->dbPath, pFile->openFlags,
-                    SQLITE_DEFAULT_FILE_PERMISSIONS);
-      OSTRACE2("TRANSPROXY: OPEN  %d\n", fd);
-      if( fd>=0 ){
-        pFile->h = fd;
-      }else{
-        rc=SQLITE_CANTOPEN; /* SQLITE_BUSY? proxyTakeConch called
-                               during locking */
+      if( rc==SQLITE_OK && !pCtx->lockProxy ){
+        char *path = tempLockPath ? tempLockPath : pCtx->lockProxyPath;
+        rc = proxyCreateUnixFile(path, &pCtx->lockProxy, 1);
+        if( rc!=SQLITE_OK && rc!=SQLITE_NOMEM && tryOldLockPath ){
+          /* we couldn't create the proxy lock file with the old lock file path
+           ** so try again via auto-naming 
+           */
+          forceNewLockPath = 1;
+          tryOldLockPath = 0;
+          continue; // go back to the do {} while start point, try again
+        }
       }
-    }
-    if( rc==SQLITE_OK && !pCtx->lockProxy ){
-      char *path = tLockPath ? tLockPath : pCtx->lockProxyPath;
-      /* ACS: Need to make a copy of path sometimes */
-      rc = proxyCreateUnixFile(path, &pCtx->lockProxy);
-    }
-    if( rc==SQLITE_OK ){
-      pCtx->conchHeld = 1;
-
-      if( tLockPath ){
-        pCtx->lockProxyPath = sqlite3DbStrDup(0, tLockPath);
+      if( rc==SQLITE_OK ){
+        /* Need to make a copy of path if we extracted the value
+         ** from the conch file or the path was allocated on the stack
+         */
+        if( tempLockPath ){
+          pCtx->lockProxyPath = sqlite3DbStrDup(0, tempLockPath);
+          if( !pCtx->lockProxyPath ){
+            rc = SQLITE_NOMEM;
+          }
+        }
+      }
+      if( rc==SQLITE_OK ){
+        pCtx->conchHeld = 1;
+        
         if( pCtx->lockProxy->pMethod == &afpIoMethods ){
-          ((afpLockingContext *)pCtx->lockProxy->lockingContext)->dbPath =
-                     pCtx->lockProxyPath;
+          afpLockingContext *afpCtx;
+          afpCtx = (afpLockingContext *)pCtx->lockProxy->lockingContext;
+          afpCtx->dbPath = pCtx->lockProxyPath;
         }
+      } else {
+        conchFile->pMethod->xUnlock((sqlite3_file*)conchFile, NO_LOCK);
       }
-    } else {
-      conchFile->pMethod->xUnlock((sqlite3_file*)conchFile, NO_LOCK);
-    }
-    OSTRACE3("TAKECONCH  %d %s\n", conchFile->h, rc==SQLITE_OK?"ok":"failed");
-    return rc;
-  }
+      OSTRACE3("TAKECONCH  %d %s\n", conchFile->h, rc==SQLITE_OK?"ok":"failed");
+      return rc;
+    } while (1); /* in case we need to retry the :auto: lock file - we should never get here except via the 'continue' call. */
+  } 
 }
 
 /*
@@ -4911,8 +5342,10 @@ static int proxyReleaseConch(unixFile *pFile){
   OSTRACE4("RELEASECONCH  %d for %s pid=%d\n", conchFile->h,
            (pCtx->lockProxyPath ? pCtx->lockProxyPath : ":auto:"), 
            getpid());
+  if( pCtx->conchHeld>0 ){
+    rc = conchFile->pMethod->xUnlock((sqlite3_file*)conchFile, NO_LOCK);
+  }
   pCtx->conchHeld = 0;
-  rc = conchFile->pMethod->xUnlock((sqlite3_file*)conchFile, NO_LOCK);
   OSTRACE3("RELEASECONCH  %d %s\n", conchFile->h,
            (rc==SQLITE_OK ? "ok" : "failed"));
   return rc;
@@ -5008,8 +5441,8 @@ static int proxyGetDbPathForUnixFile(unixFile *pFile, char *dbPath){
     /* afp style keeps a reference to the db path in the filePath field 
     ** of the struct */
     assert( (int)strlen((char*)pFile->lockingContext)<=MAXPATHLEN );
-    strcpy(dbPath, ((afpLockingContext *)pFile->lockingContext)->dbPath);
-  }else
+    strlcpy(dbPath, ((afpLockingContext *)pFile->lockingContext)->dbPath, MAXPATHLEN);
+  } else
 #endif
   if( pFile->pMethod == &dotlockIoMethods ){
     /* dot lock style uses the locking context to store the dot lock
@@ -5019,7 +5452,7 @@ static int proxyGetDbPathForUnixFile(unixFile *pFile, char *dbPath){
   }else{
     /* all other styles use the locking context to store the db file path */
     assert( strlen((char*)pFile->lockingContext)<=MAXPATHLEN );
-    strcpy(dbPath, (char *)pFile->lockingContext);
+    strlcpy(dbPath, (char *)pFile->lockingContext, MAXPATHLEN);
   }
   return SQLITE_OK;
 }
@@ -5059,27 +5492,53 @@ static int proxyTransformUnixFile(unixFile *pFile, const char *path) {
 
   rc = proxyCreateConchPathname(dbPath, &pCtx->conchFilePath);
   if( rc==SQLITE_OK ){
-    rc = proxyCreateUnixFile(pCtx->conchFilePath, &pCtx->conchFile);
-  }  
+    rc = proxyCreateUnixFile(pCtx->conchFilePath, &pCtx->conchFile, 0);
+    if( rc==SQLITE_CANTOPEN && ((pFile->openFlags&O_RDWR) == 0) ){
+      /* if (a) the open flags are not O_RDWR, (b) the conch isn't there, and
+      ** (c) the file system is read-only, then enable no-locking access.
+      ** Ugh, since O_RDONLY==0x0000 we test for !O_RDWR since unixOpen asserts
+      ** that openFlags will have only one of O_RDONLY or O_RDWR.
+      */
+      struct statfs fsInfo;
+      struct stat conchInfo;
+      int goLockless = 0;
+
+      if( stat(pCtx->conchFilePath, &conchInfo) == -1 ) {
+        int err = errno;
+        if( (err==ENOENT) && (statfs(dbPath, &fsInfo) != -1) ){
+          goLockless = (fsInfo.f_flags&MNT_RDONLY) == MNT_RDONLY;
+        }
+      }
+      if( goLockless ){
+        pCtx->conchHeld = -1; /* read only FS/ lockless */
+        rc = SQLITE_OK;
+      }
+    }
+  }
   if( rc==SQLITE_OK && lockPath ){
     pCtx->lockProxyPath = sqlite3DbStrDup(0, lockPath);
   }
 
+  if( rc==SQLITE_OK ){
+    pCtx->dbPath = sqlite3DbStrDup(0, dbPath);
+    if( pCtx->dbPath==NULL ){
+      rc = SQLITE_NOMEM;
+    }
+  }
   if( rc==SQLITE_OK ){
     /* all memory is allocated, proxys are created and assigned, 
     ** switch the locking context and pMethod then return.
     */
-    pCtx->dbPath = sqlite3DbStrDup(0, dbPath);
     pCtx->oldLockingContext = pFile->lockingContext;
     pFile->lockingContext = pCtx;
     pCtx->pOldMethod = pFile->pMethod;
     pFile->pMethod = &proxyIoMethods;
   }else{
     if( pCtx->conchFile ){ 
-      rc = pCtx->conchFile->pMethod->xClose((sqlite3_file *)pCtx->conchFile);
-      if( rc ) return rc;
+      pCtx->conchFile->pMethod->xClose((sqlite3_file *)pCtx->conchFile);
       sqlite3_free(pCtx->conchFile);
     }
+    sqlite3_free(pCtx->lockProxyPath);
     sqlite3_free(pCtx->conchFilePath); 
     sqlite3_free(pCtx);
   }
@@ -5168,8 +5627,12 @@ static int proxyCheckReservedLock(sqlite3_file *id, int *pResOut) {
   int rc = proxyTakeConch(pFile);
   if( rc==SQLITE_OK ){
     proxyLockingContext *pCtx = (proxyLockingContext *)pFile->lockingContext;
-    unixFile *proxy = pCtx->lockProxy;
-    return proxy->pMethod->xCheckReservedLock((sqlite3_file*)proxy, pResOut);
+    if( pCtx->conchHeld>0 ){
+      unixFile *proxy = pCtx->lockProxy;
+      return proxy->pMethod->xCheckReservedLock((sqlite3_file*)proxy, pResOut);
+    }else{ /* conchHeld < 0 is lockless */
+      pResOut=0;
+    }
   }
   return rc;
 }
@@ -5203,9 +5666,13 @@ static int proxyLock(sqlite3_file *id, int locktype) {
   int rc = proxyTakeConch(pFile);
   if( rc==SQLITE_OK ){
     proxyLockingContext *pCtx = (proxyLockingContext *)pFile->lockingContext;
-    unixFile *proxy = pCtx->lockProxy;
-    rc = proxy->pMethod->xLock((sqlite3_file*)proxy, locktype);
-    pFile->locktype = proxy->locktype;
+    if( pCtx->conchHeld>0 ){
+      unixFile *proxy = pCtx->lockProxy;
+      rc = proxy->pMethod->xLock((sqlite3_file*)proxy, locktype);
+      pFile->locktype = proxy->locktype;
+    }else{
+      /* conchHeld < 0 is lockless */
+    }
   }
   return rc;
 }
@@ -5223,9 +5690,13 @@ static int proxyUnlock(sqlite3_file *id, int locktype) {
   int rc = proxyTakeConch(pFile);
   if( rc==SQLITE_OK ){
     proxyLockingContext *pCtx = (proxyLockingContext *)pFile->lockingContext;
-    unixFile *proxy = pCtx->lockProxy;
-    rc = proxy->pMethod->xUnlock((sqlite3_file*)proxy, locktype);
-    pFile->locktype = proxy->locktype;
+    if( pCtx->conchHeld>0 ){
+      unixFile *proxy = pCtx->lockProxy;
+      rc = proxy->pMethod->xUnlock((sqlite3_file*)proxy, locktype);
+      pFile->locktype = proxy->locktype;
+    }else{
+      /* conchHeld < 0 is lockless */
+    }
   }
   return rc;
 }
@@ -5352,7 +5823,6 @@ int sqlite3_os_init(void){
 #endif
     UNIXVFS("unix-none",     nolockIoFinder ),
     UNIXVFS("unix-dotfile",  dotlockIoFinder ),
-    UNIXVFS("unix-wfl",      posixWflIoFinder ),
 #if OS_VXWORKS
     UNIXVFS("unix-namedsem", semIoFinder ),
 #endif
@@ -5364,6 +5834,7 @@ int sqlite3_os_init(void){
 #endif
 #if SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__)
     UNIXVFS("unix-afp",      afpIoFinder ),
+    UNIXVFS("unix-nfs",      nfsIoFinder ),
     UNIXVFS("unix-proxy",    proxyIoFinder ),
 #endif
   };
index 3d779a75fa984ee2b7b5dbfc6eec0ba40576384e..14abc544cde4889b95816d3f935415d0004453d3 100644 (file)
@@ -144,12 +144,12 @@ static int changeTempStorage(Parse *pParse, const char *zStorageType){
 /*
 ** Generate code to return a single integer value.
 */
-static void returnSingleInt(Parse *pParse, const char *zLabel, i64 value){
+static void returnSingleInt(Parse *pParse, const char *zLabel, i64 *pValue){
   Vdbe *v = sqlite3GetVdbe(pParse);
   int mem = ++pParse->nMem;
-  i64 *pI64 = sqlite3DbMallocRaw(pParse->db, sizeof(value));
+  i64 *pI64 = sqlite3DbMallocRaw(pParse->db, sizeof(*pValue));
   if( pI64 ){
-    memcpy(pI64, &value, sizeof(value));
+    memcpy(pI64, pValue, sizeof(*pValue));
   }
   sqlite3VdbeAddOp4(v, OP_Int64, 0, mem, 0, (char*)pI64, P4_INT64);
   sqlite3VdbeSetNumCols(v, 1);
@@ -208,7 +208,8 @@ static int flagPragma(Parse *pParse, const char *zLeft, const char *zRight){
       assert( v!=0 );  /* Already allocated by sqlite3Pragma() */
       if( ALWAYS(v) ){
         if( zRight==0 ){
-          returnSingleInt(pParse, p->zName, (db->flags & p->mask)!=0 );
+          i64 value = ((db->flags & p->mask)!=0 ? 1 : 0);
+          returnSingleInt(pParse, p->zName, &value);
         }else{
           int mask = p->mask;          /* Mask of bits to set or clear. */
           if( db->autoCommit==0 ){
@@ -383,8 +384,8 @@ void sqlite3Pragma(
     Btree *pBt = pDb->pBt;
     assert( pBt!=0 );
     if( !zRight ){
-      int size = ALWAYS(pBt) ? sqlite3BtreeGetPageSize(pBt) : 0;
-      returnSingleInt(pParse, "page_size", size);
+      i64 size = ALWAYS(pBt) ? sqlite3BtreeGetPageSize(pBt) : 0;
+      returnSingleInt(pParse, "page_size", &size);
     }else{
       /* Malloc may fail when setting the page-size, as there is an internal
       ** buffer that the pager module resizes using sqlite3_realloc().
@@ -407,7 +408,7 @@ void sqlite3Pragma(
   */
   if( sqlite3StrICmp(zLeft,"max_page_count")==0 ){
     Btree *pBt = pDb->pBt;
-    int newMax = 0;
+    i64 newMax = 0;
     assert( pBt!=0 );
     if( zRight ){
       newMax = atoi(zRight);
@@ -415,7 +416,7 @@ void sqlite3Pragma(
     if( ALWAYS(pBt) ){
       newMax = sqlite3BtreeMaxPageCount(pBt, newMax);
     }
-    returnSingleInt(pParse, "max_page_count", newMax);
+    returnSingleInt(pParse, "max_page_count", &newMax);
   }else
 
   /*
@@ -556,7 +557,7 @@ void sqlite3Pragma(
       if( iLimit<-1 ) iLimit = -1;
     }
     iLimit = sqlite3PagerJournalSizeLimit(pPager, iLimit);
-    returnSingleInt(pParse, "journal_size_limit", iLimit);
+    returnSingleInt(pParse, "journal_size_limit", &iLimit);
   }else
 
 #endif /* SQLITE_OMIT_PAGER_PRAGMAS */
@@ -576,13 +577,13 @@ void sqlite3Pragma(
       goto pragma_out;
     }
     if( !zRight ){
-      int auto_vacuum;
+      i64 auto_vacuum;
       if( ALWAYS(pBt) ){
          auto_vacuum = sqlite3BtreeGetAutoVacuum(pBt);
       }else{
          auto_vacuum = SQLITE_DEFAULT_AUTOVACUUM;
       }
-      returnSingleInt(pParse, "auto_vacuum", auto_vacuum);
+      returnSingleInt(pParse, "auto_vacuum", &auto_vacuum);
     }else{
       int eAuto = getAutoVacuum(zRight);
       assert( eAuto>=0 && eAuto<=2 );
@@ -664,7 +665,8 @@ void sqlite3Pragma(
   if( sqlite3StrICmp(zLeft,"cache_size")==0 ){
     if( sqlite3ReadSchema(pParse) ) goto pragma_out;
     if( !zRight ){
-      returnSingleInt(pParse, "cache_size", pDb->pSchema->cache_size);
+      i64 cacheSize = pDb->pSchema->cache_size;
+      returnSingleInt(pParse, "cache_size", &cacheSize);
     }else{
       int size = atoi(zRight);
       if( size<0 ) size = -size;
@@ -686,7 +688,8 @@ void sqlite3Pragma(
   */
   if( sqlite3StrICmp(zLeft, "temp_store")==0 ){
     if( !zRight ){
-      returnSingleInt(pParse, "temp_store", db->temp_store);
+      i64 tempStore = db->temp_store;
+      returnSingleInt(pParse, "temp_store", &tempStore);
     }else{
       changeTempStorage(pParse, zRight);
     }
@@ -800,7 +803,8 @@ void sqlite3Pragma(
   if( sqlite3StrICmp(zLeft,"synchronous")==0 ){
     if( sqlite3ReadSchema(pParse) ) goto pragma_out;
     if( !zRight ){
-      returnSingleInt(pParse, "synchronous", pDb->safety_level-1);
+      i64 safetyLevel = pDb->safety_level-1;
+      returnSingleInt(pParse, "synchronous", &safetyLevel);
     }else{
       if( !db->autoCommit ){
         sqlite3ErrorMsg(pParse, 
index 5970b6fcc91c3d83b39e4f4713fde41e88b8a851..ee963dd2bcea86171d113178a2b9d14905037155 100644 (file)
@@ -16,6 +16,9 @@
 ** $Id: prepare.c,v 1.131 2009/08/06 17:43:31 drh Exp $
 */
 #include "sqliteInt.h"
+#ifdef SQLITE_ENABLE_SQLRR
+# include "sqlrr.h"
+#endif
 
 /*
 ** Fill the InitData structure with an error message that indicates
@@ -774,6 +777,9 @@ int sqlite3_prepare(
 ){
   int rc;
   rc = sqlite3LockAndPrepare(db,zSql,nBytes,0,0,ppStmt,pzTail);
+#ifdef SQLITE_ENABLE_SQLRR
+  SRRecPrepare(db, zSql, nBytes, 0, *ppStmt);
+#endif
   assert( rc==SQLITE_OK || ppStmt==0 || *ppStmt==0 );  /* VERIFY: F13021 */
   return rc;
 }
@@ -786,6 +792,9 @@ int sqlite3_prepare_v2(
 ){
   int rc;
   rc = sqlite3LockAndPrepare(db,zSql,nBytes,1,0,ppStmt,pzTail);
+#ifdef SQLITE_ENABLE_SQLRR
+  SRRecPrepare(db, zSql, nBytes, 1, *ppStmt);
+#endif
   assert( rc==SQLITE_OK || ppStmt==0 || *ppStmt==0 );  /* VERIFY: F13021 */
   return rc;
 }
@@ -821,7 +830,9 @@ static int sqlite3Prepare16(
   if( zSql8 ){
     rc = sqlite3LockAndPrepare(db, zSql8, -1, saveSqlFlag, 0, ppStmt, &zTail8);
   }
-
+#ifdef SQLITE_ENABLE_SQLRR
+  SRRecPrepare(db, zSql8, -1, 1, *ppStmt);
+#endif
   if( zTail8 && pzTail ){
     /* If sqlite3_prepare returns a tail pointer, we calculate the
     ** equivalent pointer into the UTF-16 string by counting the unicode
index 967986e4bf9802da1eb52366c939d09892fae56b..10724e28b1fe1352e7308e94dec0e141fb4c2af8 100644 (file)
@@ -415,6 +415,7 @@ int sqlite3_exec(
 #define SQLITE_OPEN_CREATE           0x00000004  /* Ok for sqlite3_open_v2() */
 #define SQLITE_OPEN_DELETEONCLOSE    0x00000008  /* VFS only */
 #define SQLITE_OPEN_EXCLUSIVE        0x00000010  /* VFS only */
+#define SQLITE_OPEN_AUTOPROXY        0x00000020  /* VFS only */
 #define SQLITE_OPEN_MAIN_DB          0x00000100  /* VFS only */
 #define SQLITE_OPEN_TEMP_DB          0x00000200  /* VFS only */
 #define SQLITE_OPEN_TRANSIENT_DB     0x00000400  /* VFS only */
index 6ea48e6713e4327170fcf31d8e1c704b248ae681..0ce69b88ea3a7f9828753688a12f5b675b433607 100644 (file)
@@ -3128,6 +3128,7 @@ static int test_clear_bindings(
     return TCL_ERROR;
   }
   if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR;
+  sqlite3_clear_bindings(0); /* test for handling NULL <rdar://problem/6646331> */
   Tcl_SetObjResult(interp, Tcl_NewIntObj(sqlite3_clear_bindings(pStmt)));
   return TCL_OK;
 }
index 7c561afb5989952661bc78a792fe21bf4cd1ec97..8ae3e71d815315b9f055e7b2ecd23c631a33e5ee 100644 (file)
@@ -14,7 +14,7 @@
 */
 
 #include "tcl.h"
-#include <sqlite3.h>
+#include "sqlite3.h"
 #include <assert.h>
 
 /* These functions are implemented in test1.c. */
index b9ee52b6ce2c75f246b25b6735ef94790845af00..cb03e1cd0dea03d7439189f8444790542a0f4f2b 100644 (file)
@@ -17,6 +17,9 @@
 */
 #include "sqliteInt.h"
 #include "vdbeInt.h"
+#ifdef SQLITE_ENABLE_SQLRR
+# include "sqlrr.h"
+#endif 
 
 #ifndef SQLITE_OMIT_DEPRECATED
 /*
@@ -48,6 +51,9 @@ int sqlite3_finalize(sqlite3_stmt *pStmt){
     rc = SQLITE_OK;
   }else{
     Vdbe *v = (Vdbe*)pStmt;
+#ifdef SQLITE_ENABLE_SQLRR
+    SRRecFinalize(pStmt);
+#endif
     sqlite3 *db = v->db;
 #if SQLITE_THREADSAFE
     sqlite3_mutex *mutex = v->db->mutex;
@@ -74,6 +80,9 @@ int sqlite3_reset(sqlite3_stmt *pStmt){
     rc = SQLITE_OK;
   }else{
     Vdbe *v = (Vdbe*)pStmt;
+#ifdef SQLITE_ENABLE_SQLRR
+    SRRecReset(pStmt);
+#endif
     sqlite3_mutex_enter(v->db->mutex);
     rc = sqlite3VdbeReset(v);
     sqlite3VdbeMakeReady(v, -1, 0, 0, 0, 0, 0);
@@ -92,7 +101,14 @@ int sqlite3_clear_bindings(sqlite3_stmt *pStmt){
   int rc = SQLITE_OK;
   Vdbe *p = (Vdbe*)pStmt;
 #if SQLITE_THREADSAFE
-  sqlite3_mutex *mutex = ((Vdbe*)pStmt)->db->mutex;
+  sqlite3_mutex *mutex=NULL;
+#endif
+  if( NULL==pStmt ){ return SQLITE_OK; } /* <rdar://problem/6646331> */
+#ifdef SQLITE_ENABLE_SQLRR
+  SRRecClearBindings(pStmt);  
+#endif
+#if SQLITE_THREADSAFE
+  mutex = ((Vdbe*)pStmt)->db->mutex;
 #endif
   sqlite3_mutex_enter(mutex);
   for(i=0; i<p->nVar; i++){
@@ -404,6 +420,10 @@ int sqlite3_step(sqlite3_stmt *pStmt){
     int cnt = 0;
     Vdbe *v = (Vdbe*)pStmt;
     sqlite3 *db = v->db;
+#ifdef SQLITE_ENABLE_SQLRR
+    SRRecStep(pStmt);
+#endif
+    
     sqlite3_mutex_enter(db->mutex);
     while( (rc = sqlite3Step(v))==SQLITE_SCHEMA
            && cnt++ < 5
@@ -431,6 +451,9 @@ int sqlite3_step(sqlite3_stmt *pStmt){
     }
     rc = sqlite3ApiExit(db, rc);
     sqlite3_mutex_leave(db->mutex);
+#ifdef SQLITE_ENABLE_SQLRR
+    SRRecStepEnd(pStmt);
+#endif
   }
   return rc;
 }
@@ -971,11 +994,17 @@ int sqlite3_bind_blob(
   int nData, 
   void (*xDel)(void*)
 ){
+#ifdef SQLITE_ENABLE_SQLRR
+  SRRecBindBlob(pStmt, i, zData, nData);
+#endif
   return bindText(pStmt, i, zData, nData, xDel, 0);
 }
 int sqlite3_bind_double(sqlite3_stmt *pStmt, int i, double rValue){
   int rc;
   Vdbe *p = (Vdbe *)pStmt;
+#ifdef SQLITE_ENABLE_SQLRR
+  SRRecBindDouble(pStmt, i, rValue);
+#endif
   rc = vdbeUnbind(p, i);
   if( rc==SQLITE_OK ){
     sqlite3VdbeMemSetDouble(&p->aVar[i-1], rValue);
@@ -984,11 +1013,17 @@ int sqlite3_bind_double(sqlite3_stmt *pStmt, int i, double rValue){
   return rc;
 }
 int sqlite3_bind_int(sqlite3_stmt *p, int i, int iValue){
+#ifdef SQLITE_ENABLE_SQLRR
+  SRRecBindInt64(p, i, (i64)iValue);
+#endif
   return sqlite3_bind_int64(p, i, (i64)iValue);
 }
 int sqlite3_bind_int64(sqlite3_stmt *pStmt, int i, sqlite_int64 iValue){
   int rc;
   Vdbe *p = (Vdbe *)pStmt;
+#ifdef SQLITE_ENABLE_SQLRR
+  SRRecBindInt64(pStmt, i, iValue);
+#endif
   rc = vdbeUnbind(p, i);
   if( rc==SQLITE_OK ){
     sqlite3VdbeMemSetInt64(&p->aVar[i-1], iValue);
@@ -999,6 +1034,9 @@ int sqlite3_bind_int64(sqlite3_stmt *pStmt, int i, sqlite_int64 iValue){
 int sqlite3_bind_null(sqlite3_stmt *pStmt, int i){
   int rc;
   Vdbe *p = (Vdbe*)pStmt;
+#ifdef SQLITE_ENABLE_SQLRR
+  SRRecBindNull(pStmt, i);
+#endif
   rc = vdbeUnbind(p, i);
   if( rc==SQLITE_OK ){
     sqlite3_mutex_leave(p->db->mutex);
@@ -1012,6 +1050,9 @@ int sqlite3_bind_text(
   int nData, 
   void (*xDel)(void*)
 ){
+#ifdef SQLITE_ENABLE_SQLRR
+  SRRecBindText(pStmt, i, zData, nData);
+#endif
   return bindText(pStmt, i, zData, nData, xDel, SQLITE_UTF8);
 }
 #ifndef SQLITE_OMIT_UTF16
@@ -1022,6 +1063,9 @@ int sqlite3_bind_text16(
   int nData, 
   void (*xDel)(void*)
 ){
+#ifdef SQLITE_ENABLE_SQLRR
+  SRRecBindText(pStmt, i, zData, nData);
+#endif
   return bindText(pStmt, i, zData, nData, xDel, SQLITE_UTF16NATIVE);
 }
 #endif /* SQLITE_OMIT_UTF16 */
@@ -1045,6 +1089,9 @@ int sqlite3_bind_value(sqlite3_stmt *pStmt, int i, const sqlite3_value *pValue){
       break;
     }
     case SQLITE_TEXT: {
+#ifdef SQLITE_ENABLE_SQLRR
+      SRRecBindText(pStmt, i, zData, nData);
+#endif
       rc = bindText(pStmt,i,  pValue->z, pValue->n, SQLITE_TRANSIENT,
                               pValue->enc);
       break;
@@ -1059,6 +1106,9 @@ int sqlite3_bind_value(sqlite3_stmt *pStmt, int i, const sqlite3_value *pValue){
 int sqlite3_bind_zeroblob(sqlite3_stmt *pStmt, int i, int n){
   int rc;
   Vdbe *p = (Vdbe *)pStmt;
+#ifdef SQLITE_ENABLE_SQLRR
+  SRRecBindBlob(pStmt, i, NULL, n);
+#endif
   rc = vdbeUnbind(p, i);
   if( rc==SQLITE_OK ){
     sqlite3VdbeMemSetZeroBlob(&p->aVar[i-1], n);
index 7ee659749c37da6ce88df3da9ea41c52d1b1e35a..d91a28313d4a05a7ded5ac2e8e6f9aa4d2fe3d53 100644 (file)
@@ -714,23 +714,24 @@ do_test attach-6.1 {
   }
 } {0 {}}
 if {$tcl_platform(platform)=="unix"} {
-  do_test attach-6.2 {
-    sqlite3 dbx cannot-read
-    dbx eval {CREATE TABLE t1(a,b,c)}
-    dbx close
-    file attributes cannot-read -permission 0000
-    if {[file writable cannot-read]} {
-      puts "\n**** Tests do not work when run as root ****"
-      file delete -force cannot-read
-      exit 1
-    }
-    catchsql {
-      ATTACH DATABASE 'cannot-read' AS noread;
-    }
-  } {1 {unable to open database: cannot-read}}
-  do_test attach-6.2.2 {
-    db errorcode
-  } {14}
+  sqlite3 dbx cannot-read
+  dbx eval {CREATE TABLE t1(a,b,c)}
+  dbx close
+  file attributes cannot-read -permission 0000
+  if {[file writable cannot-read]} {
+    #puts "\n**** Tests do not work when run as root ****"
+    file delete -force cannot-read
+    #exit 1
+  } else {
+    do_test attach-6.2 {
+      catchsql {
+        ATTACH DATABASE 'cannot-read' AS noread;
+      }
+    } {1 {unable to open database: cannot-read}}
+    do_test attach-6.2.2 {
+      db errorcode
+    } {14}
+  }
   file delete -force cannot-read
 }
 
diff --git a/test/lock_proxy.test b/test/lock_proxy.test
new file mode 100644 (file)
index 0000000..60ea95b
--- /dev/null
@@ -0,0 +1,120 @@
+# 2008 June 28
+#
+# The author disclaims copyright to this source code.  In place of
+# a legal notice, here is a blessing:
+#
+#    May you do good and not evil.
+#    May you find forgiveness for yourself and forgive others.
+#    May you share freely, never taking more than you give.
+#
+#***********************************************************************
+# This file implements regression tests for SQLite library.  The
+# focus of this script is database proxy locks.
+#
+# $Id$
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# This file is only run if using the unix backend compiled with the
+# SQLITE_ENABLE_LOCKING_STYLE macro.
+db close
+if {[catch {sqlite3 db test.db -vfs unix-none} msg]} {
+  finish_test
+  return
+}
+db close
+file delete -force test.db.lock
+
+ifcapable lock_proxy_pragmas {
+  set ::using_proxy 0
+  foreach {name value} [array get env SQLITE_FORCE_PROXY_LOCKING] {
+    set ::using_proxy $value
+  }
+  # Disable the proxy locking for these tests
+  set env(SQLITE_FORCE_PROXY_LOCKING) "0"
+}
+
+
+#####################################################################
+
+# test proxy locking readonly file system handling
+#
+ifcapable lock_proxy_pragmas {
+  if {[file exists /usr/bin/hdiutil]} {
+
+    puts "Creating readonly file system for proxy locking tests..."
+    if {[file exists /Volumes/readonly]} {
+      exec hdiutil detach /Volumes/readonly
+    }
+    if {[file exists readonly.dmg]} {
+      file delete readonly.dmg
+    }
+    exec hdiutil create -megabytes 1 -fs HFS+ readonly.dmg -volname readonly
+    exec hdiutil attach readonly.dmg
+    set sqlite_hostid_num 4
+    set env(SQLITE_FORCE_PROXY_LOCKING) "1"
+
+    sqlite3 db2 /Volumes/readonly/test1.db 
+    execsql {
+      create table x(y);
+    } db2
+    db2 close
+
+    set sqlite_hostid_num 5
+    sqlite3 db2 /Volumes/readonly/test2.db 
+    execsql {
+      create table x(y);
+    } db2
+    db2 close
+
+    set env(SQLITE_FORCE_PROXY_LOCKING) "0"
+    sqlite3 db2 /Volumes/readonly/test3.db 
+    execsql {
+      create table x(y);
+    } db2
+    db2 close
+    set env(SQLITE_FORCE_PROXY_LOCKING) "1"
+    exec hdiutil detach /Volumes/readonly
+    exec hdiutil attach -readonly readonly.dmg
+
+    do_test lock_proxy2.0 {
+      sqlite3 db2 /Volumes/readonly/test1.db
+      catchsql {
+        select * from sqlite_master;
+      } db2
+    } {1 {database is locked}}
+    catch { db2 close }
+    
+    do_test lock_proxy2.1 {
+      sqlite3 db2 /Volumes/readonly/test2.db
+      catchsql {
+        select * from sqlite_master;
+      } db2
+    } {0 {table x x 2 {CREATE TABLE x(y)}}}
+    catch { db2 close }
+
+    do_test lock_proxy2.2 {
+      sqlite3 db2 /Volumes/readonly/test3.db
+      catchsql {
+        select * from sqlite_master;
+      } db2
+    } {0 {table x x 2 {CREATE TABLE x(y)}}}
+    catch { db2 close }
+
+    exec hdiutil detach /Volumes/readonly
+    file delete readonly.dmg
+  }
+  set env(SQLITE_FORCE_PROXY_LOCKING) "0"
+  set sqlite_hostid_num 0
+}
+
+#####################################################################
+
+file delete -force test.db
+
+ifcapable lock_proxy_pragmas {
+  set env(SQLITE_FORCE_PROXY_LOCKING) $::using_proxy
+}
+
+finish_test
index 12262c1c5132555b22d34b9affc4dc058a5d35b9..759403fc02305225abbb9ce248829443a51c26b9 100644 (file)
@@ -293,7 +293,7 @@ do_test main-2.0 {
 #
 do_test main-3.1 {
   catch {db close}
-  foreach f [glob -nocomplain testdb/*] {file delete -force $f}
+  catch {foreach f [glob -nocomplain testdb/*] {file delete -force $f}}
   file delete -force testdb
   sqlite3 db testdb
   set v [catch {execsql {SELECT * from T1 where x!!5}} msg]
@@ -301,7 +301,7 @@ do_test main-3.1 {
 } {1 {unrecognized token: "!!"}}
 do_test main-3.2 {
   catch {db close}
-  foreach f [glob -nocomplain testdb/*] {file delete -force $f}
+  catch {foreach f [glob -nocomplain testdb/*] {file delete -force $f}}
   file delete -force testdb
   sqlite3 db testdb
   set v [catch {execsql {SELECT * from T1 where ^x}} msg]
@@ -421,7 +421,7 @@ do_test main-3.2.30 {
 
 do_test main-3.3 {
   catch {db close}
-  foreach f [glob -nocomplain testdb/*] {file delete -force $f}
+  catch {foreach f [glob -nocomplain testdb/*] {file delete -force $f}}
   file delete -force testdb
   sqlite3 db testdb
   execsql {
index 90f6b06726a850383645e77cb6cfa098383d6c85..c90632b0fbd56a5e8a2ee340b6f7e9f6566c3ef4 100644 (file)
@@ -78,7 +78,7 @@ proc do_malloc_test {tn args} {
       set zRepeat "transient"
       if {$::iRepeat} {set zRepeat "persistent"}
       restore_prng_state
-      foreach file [glob -nocomplain test.db-mj*] {file delete -force $file}
+      catch {foreach file [glob -nocomplain test.db-mj*] {file delete -force $file}}
 
       do_test ${tn}.${zRepeat}.${::n} {
   
index d987d21e05ce81144aa6c0f1f32169f1fc4650bf..7b553432e58cf4f425926cc74fc6e91992794229 100644 (file)
@@ -413,24 +413,27 @@ do_test memdb-8.2 {
 
 # Test that auto-vacuum works with in-memory databases.
 # 
-ifcapable autovacuum {
-  do_test memdb-9.1 {
-    db close
-    sqlite3 db test.db
-    db cache size 0
-    execsql {
-      PRAGMA auto_vacuum = full;
-      CREATE TABLE t1(a);
-      INSERT INTO t1 VALUES(randstr(1000,1000));
-      INSERT INTO t1 VALUES(randstr(1000,1000));
-      INSERT INTO t1 VALUES(randstr(1000,1000));
-    }
-    set memused [lindex [sqlite3_status SQLITE_STATUS_MEMORY_USED 0] 1]
-    set pgovfl [lindex [sqlite3_status SQLITE_STATUS_PAGECACHE_OVERFLOW 0] 1]
-    execsql { DELETE FROM t1 }
-    set memused2 [lindex [sqlite3_status SQLITE_STATUS_MEMORY_USED 0] 1]
-    expr {($memused2 + 2048 < $memused) || $pgovfl==0}
-  } {1}
+set msize [sqlite3_status SQLITE_STATUS_MALLOC_SIZE 0]
+if {[lindex $msize 2]!=0} {
+  ifcapable autovacuum {
+    do_test memdb-9.1 {
+      db close
+      sqlite3 db test.db
+      db cache size 0
+      execsql {
+        PRAGMA auto_vacuum = full;
+        CREATE TABLE t1(a);
+        INSERT INTO t1 VALUES(randstr(1000,1000));
+        INSERT INTO t1 VALUES(randstr(1000,1000));
+        INSERT INTO t1 VALUES(randstr(1000,1000));
+      }
+      set memused [lindex [sqlite3_status SQLITE_STATUS_MEMORY_USED 0] 1]
+      set pgovfl [lindex [sqlite3_status SQLITE_STATUS_PAGECACHE_OVERFLOW 0] 1]
+      execsql { DELETE FROM t1 }
+      set memused2 [lindex [sqlite3_status SQLITE_STATUS_MEMORY_USED 0] 1]
+      expr {($memused2 + 2048 < $memused) || $pgovfl==0}
+    } {1}
+  }
 }
 
 } ;# ifcapable memorydb
index 8524fdeb9b39ee33ab352b0663e485c16f66ea95..c717e94905443dc2f28cedad904db2375a2f9aaa 100644 (file)
@@ -182,17 +182,20 @@ build_test_db memsubsys1-5 {PRAGMA page_size=4096}
 do_test memsubsys1-5.3 {
   set pg_used [lindex [sqlite3_status SQLITE_STATUS_PAGECACHE_USED 0] 2]
 } 23
-do_test memsubsys1-5.4 {
-  set maxreq [lindex [sqlite3_status SQLITE_STATUS_MALLOC_SIZE 0] 2]
-  expr {$maxreq>4096}
-} 1
-do_test memsubsys1-5.5 {
-  set s_used [lindex [sqlite3_status SQLITE_STATUS_SCRATCH_USED 0] 2]
-} 0
-do_test memsubsys1-5.6 {
-  set s_ovfl [lindex [sqlite3_status SQLITE_STATUS_SCRATCH_OVERFLOW 0] 2]
-  expr {$s_ovfl>6000}
-} 1
+set msize [sqlite3_status SQLITE_STATUS_MALLOC_SIZE 0]
+if {[lindex $msize 2]!=0} {
+  do_test memsubsys1-5.4 {
+    set maxreq [lindex [sqlite3_status SQLITE_STATUS_MALLOC_SIZE 0] 2]
+    expr {$maxreq>4096}
+  } 1
+  do_test memsubsys1-5.5 {
+    set s_used [lindex [sqlite3_status SQLITE_STATUS_SCRATCH_USED 0] 2]
+  } 0
+  do_test memsubsys1-5.6 {
+    set s_ovfl [lindex [sqlite3_status SQLITE_STATUS_SCRATCH_OVERFLOW 0] 2]
+    expr {$s_ovfl>6000}
+  } 1
+}
 
 # Test 6:  Activate both PAGECACHE and SCRATCH with a 4k page size.
 # Make it so that SCRATCH is large enough
@@ -208,10 +211,13 @@ build_test_db memsubsys1-6 {PRAGMA page_size=4096}
 do_test memsubsys1-6.3 {
   set pg_used [lindex [sqlite3_status SQLITE_STATUS_PAGECACHE_USED 0] 2]
 } 23
-do_test memsubsys1-6.4 {
-  set maxreq [lindex [sqlite3_status SQLITE_STATUS_MALLOC_SIZE 0] 2]
-  expr {$maxreq>4096 && $maxreq<=(4096+$xtra_size)}
-} 1
+set msize [sqlite3_status SQLITE_STATUS_MALLOC_SIZE 0]
+if {[lindex $msize 2]!=0} {
+  do_test memsubsys1-6.4 {
+    set maxreq [lindex [sqlite3_status SQLITE_STATUS_MALLOC_SIZE 0] 2]
+    expr {$maxreq>4096 && $maxreq<=(4096+$xtra_size)}
+  } 1
+}
 do_test memsubsys1-6.5 {
   set s_used [lindex [sqlite3_status SQLITE_STATUS_SCRATCH_USED 0] 2]
 } 1
@@ -268,19 +274,21 @@ do_test memsubsys1-8.1 {
 do_test memsubsys1-8.2 {
   set s_ovfl [lindex [sqlite3_status SQLITE_STATUS_SCRATCH_OVERFLOW 0] 2]
 } 0
-do_test memsubsys1-8.3 {
-  sqlite3 db :memory:
-  db eval {
-    CREATE TABLE t1(x);
-    INSERT INTO t1 VALUES(zeroblob(400));
-    INSERT INTO t1 VALUES(zeroblob(400));
-    INSERT INTO t1 SELECT * FROM t1;
-    INSERT INTO t1 SELECT * FROM t1;
-    INSERT INTO t1 SELECT * FROM t1;
-  }
-  expr {[lindex [sqlite3_status SQLITE_STATUS_SCRATCH_OVERFLOW 0] 2]>0}
-} 1
-db close
+if {[lindex $msize 2]!=0} {
+  do_test memsubsys1-8.3 {
+    sqlite3 db :memory:
+    db eval {
+      CREATE TABLE t1(x);
+      INSERT INTO t1 VALUES(zeroblob(400));
+      INSERT INTO t1 VALUES(zeroblob(400));
+      INSERT INTO t1 SELECT * FROM t1;
+      INSERT INTO t1 SELECT * FROM t1;
+      INSERT INTO t1 SELECT * FROM t1;
+    }
+    expr {[lindex [sqlite3_status SQLITE_STATUS_SCRATCH_OVERFLOW 0] 2]>0}
+  } 1
+  db close
+}
 sqlite3_shutdown
 sqlite3_config_memstatus 0
 sqlite3_initialize
index 46f7d6d516d26207a9774afe087641f6a3c95086..8eddbe019e8fe567f71a751f2268d4e259aa7317 100644 (file)
@@ -1298,6 +1298,7 @@ ifcapable lock_proxy_pragmas&&prefer_proxy_locking {
   set env(SQLITE_FORCE_PROXY_LOCKING) "0"
 
   sqlite3 db test.db
+  # set lock proxy name and then query it via pragma interface
   do_test pragma-16.1 {
     execsql {
       PRAGMA lock_proxy_file="mylittleproxy";
@@ -1308,6 +1309,7 @@ ifcapable lock_proxy_pragmas&&prefer_proxy_locking {
     } 
   } {mylittleproxy}
 
+  # 2 database connections can share a lock proxy file
   do_test pragma-16.2 {
     sqlite3 db2 test.db
     execsql {
@@ -1316,6 +1318,7 @@ ifcapable lock_proxy_pragmas&&prefer_proxy_locking {
   } {}
 
   db2 close
+  # 2nd database connection should auto-name an existing lock proxy file
   do_test pragma-16.2.1 {
     sqlite3 db2 test.db
     execsql {
@@ -1328,6 +1331,7 @@ ifcapable lock_proxy_pragmas&&prefer_proxy_locking {
   } {mylittleproxy}
 
   db2 close
+  # 2nd database connection cannot override the lock proxy file
   do_test pragma-16.3 {
     sqlite3 db2 test.db
     execsql {
@@ -1338,6 +1342,7 @@ ifcapable lock_proxy_pragmas&&prefer_proxy_locking {
     } db2
   } {1 {database is locked}}
 
+  # lock proxy file can be renamed if no other connections are active
   do_test pragma-16.4 {
     db2 close
     db close
@@ -1351,6 +1356,7 @@ ifcapable lock_proxy_pragmas&&prefer_proxy_locking {
 
   db2 close
   set env(SQLITE_FORCE_PROXY_LOCKING) "1"
+  # auto-naming should reuse the last proxy name when available
   do_test pragma-16.5 {
     sqlite3 db2 test.db
     execsql {
@@ -1359,6 +1365,7 @@ ifcapable lock_proxy_pragmas&&prefer_proxy_locking {
     } db2
   } {myotherproxy}
   
+  # auto-naming a new proxy should use a predictable & unique name
   do_test pragma-16.6 {
     db2 close
     sqlite3 db2 test2.db
@@ -1370,6 +1377,7 @@ ifcapable lock_proxy_pragmas&&prefer_proxy_locking {
   } {1}
   
   set sqlite_hostid_num 2
+  # db access should be limited to one host at a time (simulate 2nd host id)
   do_test pragma-16.7 {
     sqlite3 db test2.db
     execsql {
@@ -1381,6 +1389,7 @@ ifcapable lock_proxy_pragmas&&prefer_proxy_locking {
   } {1 {database is locked}}
   db close
   
+  # default to using proxy locking (simulate network file system detection)
   do_test pragma-16.8 {
     sqlite3 db test2.db
     catchsql {
@@ -1389,19 +1398,26 @@ ifcapable lock_proxy_pragmas&&prefer_proxy_locking {
   } {1 {database is locked}}
 
   db2 close
+  # check that db is unlocked after first host connection closes 
   do_test pragma-16.8.1 {
     execsql {
       PRAGMA lock_proxy_file="yetanotherproxy";
+      select * from sqlite_master;
+    }
+    execsql {
       PRAGMA lock_proxy_file;
     } 
   } {yetanotherproxy}
   do_test pragma-16.8.2 {
     execsql {
-      create table mine(x);
+      create table if not exists mine(x);
+      insert into mine values (1);
     } 
   } {}
 
   db close
+  file delete -force proxytest.db
+  file delete -force .proxytest.db-conch
   do_test pragma-16.9 {
     sqlite3 db proxytest.db
     set lockpath2 [execsql {
@@ -1411,6 +1427,96 @@ ifcapable lock_proxy_pragmas&&prefer_proxy_locking {
     string match "*proxytest.db:auto:" $lockpath2
   } {1}
 
+  # ensure creating directories for a lock proxy file works
+  db close
+  file delete -force proxytest
+  do_test pragma-16.10.1 {
+    sqlite3 db proxytest.db
+    set lockpath2 [execsql {
+      PRAGMA lock_proxy_file="./proxytest/sub/dir/lock";
+      PRAGMA lock_proxy_file;
+    } db]
+    string match "*proxytest/sub/dir/lock" $lockpath2
+  } {1}
+
+  # ensure that after deleting the path, setting ":auto:" works correctly
+  db close
+  file delete -force proxytest
+  do_test pragma-16.10.2 {
+    sqlite3 db proxytest.db
+    set lockpath2 [execsql {
+      PRAGMA lock_proxy_file=":auto:";
+      create table if not exists pt(y);
+      PRAGMA lock_proxy_file;
+    } db]
+    string match "*proxytest/sub/dir/lock" $lockpath2
+  } {1}
+
+  # ensure that if the path can not be created (file instead of dir)
+  # setting :auto: deals with it by creating a new autonamed lock file
+  db close
+  file delete -force proxytest
+  close [open "proxytest" a]
+  do_test pragma-16.10.3 {
+    sqlite3 db proxytest.db
+    set lockpath2 [execsql {
+      PRAGMA lock_proxy_file=":auto:";
+      create table if not exists zz(y);
+      PRAGMA lock_proxy_file;
+    } db]
+    string match "*proxytest.db:auto:" $lockpath2
+  } {1}
+
+  # make sure we can deal with ugly file paths correctly
+  db close
+  file delete -force proxytest
+  do_test pragma-16.10.4 {
+    sqlite3 db proxytest.db
+    set lockpath2 [execsql {
+      PRAGMA lock_proxy_file="./././////./proxytest/../proxytest/sub/dir/lock";
+      create table if not exists aa(bb);
+      PRAGMA lock_proxy_file;
+    } db]
+    string match "*proxytest/sub/dir/lock" $lockpath2
+  } {1}
+
+  # ensure that if the path can not be created (perm), setting :auto: deals
+  db close
+  file delete -force proxytest
+  do_test pragma-16.10.5 {
+    sqlite3 db proxytest.db
+    execsql {
+      PRAGMA lock_proxy_file="./proxytest/sub/dir/lock";
+      create table if not exists bb(bb);
+    }
+    db close
+    file delete -force proxytest
+    file mkdir proxytest
+    file attributes proxytest -permission 0000
+    sqlite3 db proxytest.db
+    set lockpath2 [execsql {
+      PRAGMA lock_proxy_file=":auto:";
+      create table if not exists cc(bb);
+      PRAGMA lock_proxy_file;
+    } db]
+    string match "*proxytest.db:auto:" $lockpath2
+  } {1}
+
+  # ensure that if the path can not be created, locking fails
+  db close
+  do_test pragma-16.10.6 {
+    sqlite3 db proxytest.db
+    catchsql {
+      PRAGMA lock_proxy_file="./proxytest/sub/dir/lock";
+      create table if not exists faily(y);
+      PRAGMA lock_proxy_file;
+    } db
+  } {1 {database is locked}}
+  db close
+
+  file attributes proxytest -permission 0777
+  file delete -force proxytest
+
   set env(SQLITE_FORCE_PROXY_LOCKING) $using_proxy
   set sqlite_hostid_num 0
 }
index a56455a1416ce865ab7631ee244e75c42ffb2702..25c7ab7f3d51557b38604fdcaf9f4bca94f7e924 100644 (file)
@@ -64,6 +64,7 @@ set EXCLUDE {
   fuzz_malloc.test
   in2.test
   loadext.test
+  lock_proxy.test
   memleak.test
   misc7.test
   misuse.test
index 7651017a258ae90aca0c1951a80b8248d857d7b7..f39d781c05af268d6775386c2a8fe2e35548b04f 100644 (file)
@@ -361,11 +361,15 @@ proc finalize_testing {} {
       memdebug_log_sql leaks.sql
     }
   }
-  foreach f [glob -nocomplain test.db-*-journal] {
-    file delete -force $f
+  catch {
+    foreach f [glob -nocomplain test.db-*-journal] {
+      file delete -force $f
+    }
   }
-  foreach f [glob -nocomplain test.db-mj*] {
-    file delete -force $f
+  catch {
+    foreach f [glob -nocomplain test.db-mj*] {
+      file delete -force $f
+    }
   }
   exit [expr {$nErr>0}]
 }
index 99dc0d31249a64ba9a1ea377e9ff7e72a08f1457..85fc49b9071fdbc1c14cd77521d1f20f4dfef761 100644 (file)
@@ -107,6 +107,7 @@ foreach hdr {
    sqliteicu.h
    sqliteInt.h
    sqliteLimit.h
+   sqlrr.h
    vdbe.h
    vdbeInt.h
 } {
@@ -298,7 +299,8 @@ foreach file {
 
    rtree.c
    icu.c
-   fts3_icu.c
+   fts3_icu.c   
+   sqlrr.c
 } {
   copy_file tsrc/$file
 }