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
#
$(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
#include <string.h>
#include <assert.h>
+#include <stdint.h>
#ifndef SQLITE_AMALGAMATION
typedef sqlite3_int64 i64;
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 */
--- /dev/null
+
+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
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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
-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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
-01c4b5b84ec7ce589e20ea66e80011f092ab32f0
\ No newline at end of file
+941a01eb868815f566539e9ab21f807d9e798e40
\ No newline at end of file
}
}
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 ){
*/
#include "sqliteInt.h"
+#ifdef SQLITE_ENABLE_SQLRR
+# include "sqlrr.h"
+#endif
/*
** Execute SQL code. Return one of the SQLITE_ success/failure
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] ){
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 ){
*/
#include "sqliteInt.h"
+#ifdef SQLITE_ENABLE_SQLRR
+# include "sqlrr.h"
+#endif
#ifdef SQLITE_ENABLE_FTS3
# include "fts3.h"
#endif
if( db->lookaside.bMalloced ){
sqlite3_free(db->lookaside.pStart);
}
+#ifdef SQLITE_ENABLE_SQLRR
+ SRRecClose(db);
+#endif
+
sqlite3_free(db);
return SQLITE_OK;
}
}
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
}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);
}
*/
#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().
sqlite3_int64 *p;
assert( nByte>0 );
nByte = ROUND8(nByte);
- p = malloc( nByte+8 );
+ p = SQLITE_MALLOC( nByte+8 );
if( p ){
p[0] = nByte;
p++;
sqlite3_int64 *p = (sqlite3_int64*)pPrior;
assert( pPrior!=0 );
p--;
- free(p);
+ SQLITE_FREE(p);
}
/*
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++;
** 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;
}
** 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;
}
sqlite3_config(SQLITE_CONFIG_MALLOC, &defaultMethods);
}
+#undef SQLITE_MALLOC
+#undef SQLITE_FREE
+#undef SQLITE_REALLOC
+
#endif /* SQLITE_SYSTEM_MALLOC */
# 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.
#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
** 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));
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.
return rc;
}
+#endif /* SQLITE_ENABLE_NFS_RANGELOCK */
/*
** Lock the file with the lock specified by parameter locktype - one
** of the following:
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;
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;
**
** 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 );
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;
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
typedef struct afpLockingContext afpLockingContext;
struct afpLockingContext {
unsigned long long sharedByte;
+ int reserved;
const char *dbPath; /* Name of the open file */
};
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;
}
}
}
+ unixLeaveMutex();
OSTRACE4("TEST WR-LOCK %d %d %d\n", pFile->h, rc, reserved);
*pResOut = reserved;
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
}
/* 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 );
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.
** 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) ){
} 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
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 */
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:
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 ){
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 ){
}
}
}
+
unixLeaveMutex();
- if( rc==SQLITE_OK ){
- pFile->locktype = locktype;
- }
+ if( rc==SQLITE_OK ) pFile->locktype = locktype;
return rc;
}
** 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);
*/
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 */
********************* 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 *****************************
*/
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);
*/
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);
*/
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 ){
)
#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
)
#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
/*
{ "hfs", &posixIoMethods },
{ "ufs", &posixIoMethods },
{ "afpfs", &afpIoMethods },
-#ifdef SQLITE_ENABLE_AFP_LOCKING_SMB
{ "smbfs", &afpIoMethods },
-#else
- { "smbfs", &flockIoMethods },
-#endif
{ "webdav", &nolockIoMethods },
{ 0, 0 }
};
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;
}
#endif
}
- if( pLockingStyle == &posixIoMethods ){
+ if( pLockingStyle == &posixIoMethods || pLockingStyle == &nfsIoMethods ){
unixEnterMutex();
rc = findLockInfo(pNew, &pNew->pLock, &pNew->pOpen);
if( rc!=SQLITE_OK ){
** 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();
}
}
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.
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;
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;
}
** 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
** 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
*/
#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
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;
#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);
}
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.
** 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
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. */
+ }
}
/*
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;
/* 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
}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;
}
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);
}
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;
}
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;
}
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;
}
#endif
UNIXVFS("unix-none", nolockIoFinder ),
UNIXVFS("unix-dotfile", dotlockIoFinder ),
- UNIXVFS("unix-wfl", posixWflIoFinder ),
#if OS_VXWORKS
UNIXVFS("unix-namedsem", semIoFinder ),
#endif
#endif
#if SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__)
UNIXVFS("unix-afp", afpIoFinder ),
+ UNIXVFS("unix-nfs", nfsIoFinder ),
UNIXVFS("unix-proxy", proxyIoFinder ),
#endif
};
/*
** 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);
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 ){
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().
*/
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);
if( ALWAYS(pBt) ){
newMax = sqlite3BtreeMaxPageCount(pBt, newMax);
}
- returnSingleInt(pParse, "max_page_count", newMax);
+ returnSingleInt(pParse, "max_page_count", &newMax);
}else
/*
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 */
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 );
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;
*/
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);
}
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,
** $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
){
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;
}
){
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;
}
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
#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 */
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;
}
*/
#include "tcl.h"
-#include <sqlite3.h>
+#include "sqlite3.h"
#include <assert.h>
/* These functions are implemented in test1.c. */
*/
#include "sqliteInt.h"
#include "vdbeInt.h"
+#ifdef SQLITE_ENABLE_SQLRR
+# include "sqlrr.h"
+#endif
#ifndef SQLITE_OMIT_DEPRECATED
/*
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;
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);
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++){
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
}
rc = sqlite3ApiExit(db, rc);
sqlite3_mutex_leave(db->mutex);
+#ifdef SQLITE_ENABLE_SQLRR
+ SRRecStepEnd(pStmt);
+#endif
}
return rc;
}
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);
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);
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);
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
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 */
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;
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);
}
} {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
}
--- /dev/null
+# 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
#
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]
} {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]
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 {
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} {
# 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
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
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
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
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";
}
} {mylittleproxy}
+ # 2 database connections can share a lock proxy file
do_test pragma-16.2 {
sqlite3 db2 test.db
execsql {
} {}
db2 close
+ # 2nd database connection should auto-name an existing lock proxy file
do_test pragma-16.2.1 {
sqlite3 db2 test.db
execsql {
} {mylittleproxy}
db2 close
+ # 2nd database connection cannot override the lock proxy file
do_test pragma-16.3 {
sqlite3 db2 test.db
execsql {
} 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
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 {
} db2
} {myotherproxy}
+ # auto-naming a new proxy should use a predictable & unique name
do_test pragma-16.6 {
db2 close
sqlite3 db2 test2.db
} {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 {
} {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 {
} {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 {
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
}
fuzz_malloc.test
in2.test
loadext.test
+ lock_proxy.test
memleak.test
misc7.test
misuse.test
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}]
}
sqliteicu.h
sqliteInt.h
sqliteLimit.h
+ sqlrr.h
vdbe.h
vdbeInt.h
} {
rtree.c
icu.c
- fts3_icu.c
+ fts3_icu.c
+ sqlrr.c
} {
copy_file tsrc/$file
}