e6c3c72c1b
A ext/sqlrr/sqlrr.[ch] -- add sql recording/replay support (very experimental extension -- pre alpha) M ext/rtree/rtree.c -- fix cast compiler warning M src/func.c -- substring backward compatiblity <rdar://problem/6778339> M src/legacy.c -- replay recorder calls M src/main.c -- if SQLITE_ENABLE_AUTO_PROFILE, call sqlite3_profile when env(SQLITE_AUTO_PROFILE) -- replay recorder calls M src/mem1.c -- use custom malloc zone on single core systems M src/os_unix.c -- hostid via gethostuuid -- read-only file system support for proxy locking -- improved handling of simulated shared lock on AFP/SMB on 64 bit systems -- disable whole-file-lock NFS file locking -- fix for downgrading from exclusive to shared lock on NFS -- proxy lock breaking & recreating local lock files -- support SQLITE_OPEN_AUTOPROXY flag -- only write 1st byte into new zero-length files if fs is DOS -- cache fs type info in unixFile struct -- force use of fsync() even if fdatasync() is available -- remove flock locking style support for SMB -- replace strcpy with strlcpy M src/prepare.c -- replay recorder calls M src/sqlite.h.in -- added SQLITE_OPEN_AUTOPROXY M src/sqliteInt.h -- include replay recorder header M src/vdbeapi.c -- checking for NULL statement for <rdar://6646331> -- replay recorder calls FossilOrigin-Name: 941a01eb868815f566539e9ab21f807d9e798e40
582 lines
19 KiB
C
582 lines
19 KiB
C
/*
|
|
* 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 */
|