blob: fecb7fe95e052143390ea43a85c69f9c8310fdc2 [file] [log] [blame] [edit]
/*
** 2018-02-10
**
** 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.
**
******************************************************************************
*/
#include "sqlite3ext.h"
SQLITE_EXTENSION_INIT1
#ifndef SQLITE_OMIT_VIRTUALTABLE
#ifndef SQLITE_AMALGAMATION
typedef sqlite3_int64 i64;
typedef sqlite3_uint64 u64;
typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned long u32;
#define MIN(a,b) ((a)<(b) ? (a) : (b))
#if defined(SQLITE_COVERAGE_TEST) || defined(SQLITE_MUTATION_TEST)
# define ALWAYS(X) (1)
# define NEVER(X) (0)
#elif !defined(NDEBUG)
# define ALWAYS(X) ((X)?1:(assert(0),0))
# define NEVER(X) ((X)?(assert(0),1):0)
#else
# define ALWAYS(X) (X)
# define NEVER(X) (X)
#endif
#endif /* SQLITE_AMALGAMATION */
#define ZONEFILE_MAGIC_NUMBER 0x464B3138
#define ZONEFILE_SZ_HEADER 32
#define ZONEFILE_SZ_KEYOFFSETS_ENTRY 20
#define ZONEFILE_DEFAULT_MAXAUTOFRAMESIZE (64*1024)
#define ZONEFILE_DEFAULT_ENCRYPTION 0
#define ZONEFILE_DEFAULT_COMPRESSION 0
#define ZONEFILE_SCHEMA \
"CREATE TABLE z1(" \
" k INTEGER PRIMARY KEY," \
" v BLOB," \
" fileid INTEGER," \
" frame INTEGER," \
" ofst INTEGER," \
" sz INTEGER" \
")"
#define ZONEFILE_FILES_SCHEMA \
"CREATE TABLE z2(" \
" filename TEXT," \
" ekey BLOB," \
" header JSON HIDDEN" \
")"
#include <stdio.h>
#include <string.h>
#include <assert.h>
/*
** A structure to store the parameters for a single zonefile_write()
** invocation.
*/
typedef struct ZonefileWrite ZonefileWrite;
struct ZonefileWrite {
int compressionTypeIndexData;
int compressionTypeContent;
int encryptionType;
int maxAutoFrameSize;
};
/*
** A structure to store a deserialized zonefile header in.
*/
typedef struct ZonefileHeader ZonefileHeader;
struct ZonefileHeader {
u32 magicNumber;
u8 compressionTypeIndexData;
u8 compressionTypeContent;
u32 byteOffsetDictionary;
u32 byteOffsetFrames;
u32 numFrames;
u32 numKeys;
u8 encryptionType;
u8 encryptionKeyIdx;
u8 extendedHeaderVersion;
u8 extendedHeaderSize;
};
/*
** Buffer structure used by the zonefile_write() implementation.
*/
typedef struct ZonefileBuffer ZonefileBuffer;
struct ZonefileBuffer {
u8 *a;
int n;
int nAlloc;
};
/*
** Set the error message contained in context ctx to the results of
** vprintf(zFmt, ...).
*/
static void zonefileCtxError(sqlite3_context *ctx, const char *zFmt, ...){
char *zMsg = 0;
va_list ap;
va_start(ap, zFmt);
zMsg = sqlite3_vmprintf(zFmt, ap);
sqlite3_result_error(ctx, zMsg, -1);
sqlite3_free(zMsg);
va_end(ap);
}
static void zonefileTransferError(sqlite3_context *pCtx){
sqlite3 *db = sqlite3_context_db_handle(pCtx);
const char *zErr = sqlite3_errmsg(db);
sqlite3_result_error(pCtx, zErr, -1);
}
static int zonefilePrepare(
sqlite3 *db,
sqlite3_stmt **ppStmt,
char **pzErr,
const char *zFmt,
...
){
int rc;
va_list ap;
char *zSql;
va_start(ap, zFmt);
zSql = sqlite3_vmprintf(zFmt, ap);
*ppStmt = 0;
if( zSql ){
rc = sqlite3_prepare(db, zSql, -1, ppStmt, 0);
if( rc!=SQLITE_OK ){
*pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(db));
}
sqlite3_free(zSql);
}else{
rc = SQLITE_NOMEM;
}
return rc;
}
static sqlite3_stmt *zonefileCtxPrepare(
sqlite3_context *pCtx,
const char *zFmt,
...
){
sqlite3_stmt *pRet = 0;
va_list ap;
char *zSql;
va_start(ap, zFmt);
zSql = sqlite3_vmprintf(zFmt, ap);
if( zSql ){
sqlite3 *db = sqlite3_context_db_handle(pCtx);
int rc = sqlite3_prepare(db, zSql, -1, &pRet, 0);
if( rc!=SQLITE_OK ){
zonefileTransferError(pCtx);
}
sqlite3_free(zSql);
}else{
sqlite3_result_error_nomem(pCtx);
}
return pRet;
}
/*
** Return zero if the two SQL values passed as arguments are equal, or
** non-zero otherwise. Values with different types are considered unequal,
** even if they both contain the same numeric value (e.g. 2 and 2.0).
*/
static int zonefileCompareValue(sqlite3_value *p1, sqlite3_value *p2){
int eType;
assert( p1 );
if( p2==0 ) return 1;
eType = sqlite3_value_type(p1);
if( sqlite3_value_type(p2)!=eType ) return 1;
switch( eType ){
case SQLITE_INTEGER:
return sqlite3_value_int64(p1)!=sqlite3_value_int64(p2);
case SQLITE_FLOAT:
return sqlite3_value_double(p1)!=sqlite3_value_double(p2);
case SQLITE_TEXT:
case SQLITE_BLOB: {
int n1 = sqlite3_value_bytes(p1);
int n2 = sqlite3_value_bytes(p2);
if( n1!=n2 ) return 1;
return memcmp(sqlite3_value_blob(p1), sqlite3_value_blob(p2), n1);
}
default:
assert( eType==SQLITE_NULL);
}
return 0;
}
int zonefileIsAutoFrame(sqlite3_value *pFrame){
return (
sqlite3_value_type(pFrame)==SQLITE_INTEGER
&& sqlite3_value_int64(pFrame)==-1
);
}
static int zonefileGetParams(
sqlite3_context *pCtx, /* Leave any error message here */
const char *zJson, /* JSON configuration parameter (or NULL) */
ZonefileWrite *p /* Populate this object before returning */
){
sqlite3 *db = sqlite3_context_db_handle(pCtx);
sqlite3_stmt *pStmt = 0;
char *zErr = 0;
int rc = SQLITE_OK;
int rc2;
memset(p, 0, sizeof(ZonefileWrite));
p->maxAutoFrameSize = ZONEFILE_DEFAULT_MAXAUTOFRAMESIZE;
rc = zonefilePrepare(db, &pStmt, &zErr,"SELECT key, value FROM json_each(?)");
if( rc==SQLITE_OK ){
sqlite3_bind_text(pStmt, 1, zJson, -1, SQLITE_STATIC);
}
while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){
const char *zKey = (const char*)sqlite3_column_text(pStmt, 0);
int iVal = sqlite3_column_int(pStmt, 1);
if( sqlite3_stricmp("maxAutoFrameSize", zKey)==0 ){
p->maxAutoFrameSize = iVal;
}else
if( sqlite3_stricmp("compressionTypeIndexData", zKey)==0 ){
p->compressionTypeIndexData = iVal;
}else
if( sqlite3_stricmp("compressionTypeContent", zKey)==0 ){
p->compressionTypeContent = iVal;
}else
if( sqlite3_stricmp("encryptionType", zKey)==0 ){
p->encryptionType = iVal;
}else{
rc = SQLITE_ERROR;
zErr = sqlite3_mprintf("unknown parameter name: \"%s\"", zKey);
}
}
rc2 = sqlite3_finalize(pStmt);
if( rc==SQLITE_OK ) rc = rc2;
if( zErr ){
sqlite3_result_error(pCtx, zErr, -1);
sqlite3_free(zErr);
}
return rc;
}
/*
** Check that there is room in buffer pBuf for at least nByte bytes more
** data. If not, attempt to allocate more space. If the allocation attempt
** fails, leave an error message in context pCtx and return SQLITE_ERROR.
**
** If no error occurs, SQLITE_OK is returned.
*/
static int zonefileBufferGrow(
sqlite3_context *pCtx,
ZonefileBuffer *pBuf,
int nByte
){
int nReq = pBuf->n + nByte;
if( nReq>pBuf->nAlloc ){
u8 *aNew;
int nNew = pBuf->nAlloc ? pBuf->nAlloc*2 : 128;
while( nNew<nReq ) nNew = nNew*2;
aNew = sqlite3_realloc(pBuf->a, nNew);
if( aNew==0 ){
sqlite3_result_error_nomem(pCtx);
return SQLITE_ERROR;
}
pBuf->a = aNew;
pBuf->nAlloc = nNew;
}
return SQLITE_OK;
}
static void zonefileBufferFree(ZonefileBuffer *pBuf){
sqlite3_free(pBuf->a);
memset(pBuf, 0, sizeof(ZonefileBuffer));
}
static void zonefilePut32(u8 *aBuf, u32 v){
aBuf[0] = (v >> 24) & 0xFF;
aBuf[1] = (v >> 16) & 0xFF;
aBuf[2] = (v >> 8) & 0xFF;
aBuf[3] = v & 0xFF;
}
static u32 zonefileGet32(u8 *aBuf){
return (((u32)aBuf[0]) << 24)
+ (((u32)aBuf[1]) << 16)
+ (((u32)aBuf[2]) << 8)
+ (((u32)aBuf[3]) << 0);
}
static u64 zonefileGet64(u8 *aBuf){
return (((u64)aBuf[0]) << 56)
+ (((u64)aBuf[1]) << 48)
+ (((u64)aBuf[2]) << 40)
+ (((u64)aBuf[3]) << 32)
+ (((u64)aBuf[4]) << 24)
+ (((u64)aBuf[5]) << 16)
+ (((u64)aBuf[6]) << 8)
+ (((u64)aBuf[7]) << 0);
}
static void zonefileAppend32(ZonefileBuffer *pBuf, u32 v){
zonefilePut32(&pBuf->a[pBuf->n], v);
pBuf->n += 4;
}
static void zonefileAppend64(ZonefileBuffer *pBuf, u64 v){
zonefileAppend32(pBuf, v>>32);
zonefileAppend32(pBuf, v & 0xFFFFFFFF);
}
static void zonefileAppendBlob(ZonefileBuffer *pBuf, const u8 *p, int n){
memcpy(&pBuf->a[pBuf->n], p, n);
pBuf->n += n;
}
static int zonefileFileWrite(FILE *pFd, const u8 *aBuf, int nBuf){
size_t res = fwrite(aBuf, 1, nBuf, pFd);
return res!=nBuf ? SQLITE_ERROR : SQLITE_OK;
}
static int zonefileFileRead(FILE *pFd, u8 *aBuf, int nBuf, i64 iOff){
int rc = fseek(pFd, iOff, SEEK_SET);
if( rc==0 ){
rc = fread(aBuf, 1, nBuf, pFd);
rc = (rc==nBuf) ? SQLITE_OK : SQLITE_ERROR;
}
return rc;
}
static FILE *zonefileFileOpen(const char *zFile, int bWrite, char **pzErr){
FILE *pFd = fopen(zFile, bWrite ? "wb" : "rb");
if( pFd==0 ){
*pzErr = sqlite3_mprintf("failed to open file \"%s\" for %s",
zFile, bWrite ? "writing" : "reading"
);
}
return pFd;
}
static void zonefileFileClose(FILE *pFd){
if( pFd ) fclose(pFd);
}
/*
** Function: zonefile_write(F,T[,J])
*/
static void zonefileWriteFunc(
sqlite3_context *pCtx, /* Context object */
int objc, /* Number of SQL arguments */
sqlite3_value **objv /* Array of SQL arguments */
){
const char *zFile = 0; /* File to write to */
const char *zTbl = 0; /* Database object to read from */
const char *zJson = 0; /* JSON configuration parameters */
ZonefileWrite sWrite; /* Decoded JSON parameters */
int nKey = 0; /* Number of keys in new zonefile */
int nFrame = 0; /* Number of frames in new zonefile */
int szFrame = 0; /* Size of current frame */
sqlite3_stmt *pStmt = 0; /* SQL used to read data from source table */
FILE *pFd = 0;
int rc;
sqlite3_value *pPrev = 0;
char *zErr = 0;
ZonefileBuffer sFrameIdx = {0, 0, 0};
ZonefileBuffer sKeyIdx = {0, 0, 0};
ZonefileBuffer sFrames = {0, 0, 0};
u8 aHdr[ZONEFILE_SZ_HEADER]; /* Space to assemble zonefile header */
assert( objc==2 || objc==3 );
zFile = (const char*)sqlite3_value_text(objv[0]);
zTbl = (const char*)sqlite3_value_text(objv[1]);
if( objc==3 ){
zJson = (const char*)sqlite3_value_text(objv[2]);
}
if( zonefileGetParams(pCtx, zJson, &sWrite) ) return;
/* Prepare the SQL statement used to read data from the source table. This
** also serves to verify the suitability of the source table schema. */
pStmt = zonefileCtxPrepare(pCtx,
"SELECT k, frame, v FROM %Q ORDER BY frame, idx, k", zTbl
);
if( pStmt==0 ) return;
/* Open a file-handle used to write out the zonefile */
pFd = zonefileFileOpen(zFile, 1, &zErr);
if( pFd==0 ){
sqlite3_result_error(pCtx, zErr, -1);
sqlite3_finalize(pStmt);
sqlite3_free(zErr);
return;
}
while( SQLITE_ROW==sqlite3_step(pStmt) ){
sqlite3_int64 k = sqlite3_column_int64(pStmt, 0);
sqlite3_value *pFrame = sqlite3_column_value(pStmt, 1);
int nBlob = sqlite3_column_bytes(pStmt, 2);
const u8 *pBlob = (const u8*)sqlite3_column_blob(pStmt, 2);
int bAuto = zonefileIsAutoFrame(pFrame);
if( zonefileCompareValue(pFrame, pPrev)
|| (bAuto && szFrame && (szFrame+nBlob)>sWrite.maxAutoFrameSize)
){
/* Add new entry to sFrameIdx */
szFrame = 0;
if( zonefileBufferGrow(pCtx, &sFrameIdx, 4) ) goto zone_write_out;
zonefileAppend32(&sFrameIdx, sFrames.n);
sqlite3_value_free(pPrev);
pPrev = sqlite3_value_dup(pFrame);
if( pPrev==0 ){
sqlite3_result_error_nomem(pCtx);
goto zone_write_out;
}
nFrame++;
}
/* Add new entry to sKeyIdx */
if( zonefileBufferGrow(pCtx, &sKeyIdx, ZONEFILE_SZ_KEYOFFSETS_ENTRY) ){
goto zone_write_out;
}
zonefileAppend64(&sKeyIdx, k);
zonefileAppend32(&sKeyIdx, nFrame-1);
zonefileAppend32(&sKeyIdx, szFrame);
zonefileAppend32(&sKeyIdx, nBlob);
/* Add data for new entry to sFrames */
if( zonefileBufferGrow(pCtx, &sFrames, nBlob) ) goto zone_write_out;
zonefileAppendBlob(&sFrames, pBlob, nBlob);
szFrame += nBlob;
nKey++;
}
sqlite3_value_free(pPrev);
pPrev = 0;
/* Create the zonefile header in the in-memory buffer */
memset(aHdr, 0, ZONEFILE_SZ_HEADER);
zonefilePut32(&aHdr[0], ZONEFILE_MAGIC_NUMBER);
aHdr[4] = sWrite.compressionTypeIndexData;
aHdr[5] = sWrite.compressionTypeContent;
zonefilePut32(&aHdr[6], 0); /* Compression dictionary byte offset */
zonefilePut32(&aHdr[10], ZONEFILE_SZ_HEADER + sFrameIdx.n + sKeyIdx.n);
zonefilePut32(&aHdr[14], nFrame);
zonefilePut32(&aHdr[18], nKey);
aHdr[22] = sWrite.encryptionType;
aHdr[23] = 0; /* Encryption key index */
aHdr[24] = 0; /* extended header version */
aHdr[25] = 0; /* extended header size */
assert( ZONEFILE_SZ_HEADER>=26 );
rc = zonefileFileWrite(pFd, aHdr, ZONEFILE_SZ_HEADER);
if( rc==SQLITE_OK ) rc = zonefileFileWrite(pFd, sFrameIdx.a, sFrameIdx.n);
if( rc==SQLITE_OK ) rc = zonefileFileWrite(pFd, sKeyIdx.a, sKeyIdx.n);
if( rc==SQLITE_OK ) rc = zonefileFileWrite(pFd, sFrames.a, sFrames.n);
if( rc ){
zonefileCtxError(pCtx, "error writing file \"%s\" (fwrite())", zFile);
goto zone_write_out;
}
if( fclose(pFd) ){
zonefileCtxError(pCtx, "error writing file \"%s\" (fclose())", zFile);
}
pFd = 0;
zone_write_out:
if( pFd ) fclose(pFd);
sqlite3_finalize(pStmt);
zonefileBufferFree(&sFrameIdx);
zonefileBufferFree(&sKeyIdx);
zonefileBufferFree(&sFrames);
}
typedef struct ZonefileFilesTab ZonefileFilesTab;
struct ZonefileFilesTab {
sqlite3_vtab base; /* Base class - must be first */
sqlite3 *db;
char *zBase; /* Name of this table */
char *zDb; /* Database containing this table */
sqlite3_stmt *pInsert; /* Insert into the %_shadow_file table */
sqlite3_stmt *pInsertIdx; /* Insert into the %_shadow_idx table */
sqlite3_stmt *pDeleteIdx; /* Delete by fileid from %_shadow_idx table */
sqlite3_stmt *pDelete; /* Delete by rowid from %_shadow_file table */
};
typedef struct ZonefileFilesCsr ZonefileFilesCsr;
struct ZonefileFilesCsr {
sqlite3_vtab_cursor base; /* Base class - must be first */
sqlite3_stmt *pSelect;
};
/*
** This function does the work of xCreate (if bCreate!=0) or xConnect
** (if bCreate==0) for the zonefile_files module.
*/
static int zffCreateConnect(
int bCreate,
sqlite3 *db,
int argc, const char *const*argv,
sqlite3_vtab **ppVtab,
char **pzErr
){
ZonefileFilesTab *p;
const char *zName = argv[2];
const char *zDb = argv[1];
int nName = strlen(zName);
int nDb = strlen(zDb);
int rc = SQLITE_OK;
if( nName<6 || memcmp(&zName[nName-6], "_files", 6)!=0 ){
*pzErr = sqlite3_mprintf("do not create zonefile_files tables directly!");
*ppVtab = 0;
return SQLITE_ERROR;
}
p = (ZonefileFilesTab*)sqlite3_malloc(sizeof(ZonefileFilesTab)+nName+1+nDb+1);
if( !p ){
rc = SQLITE_NOMEM;
}else{
memset(p, 0, sizeof(ZonefileFilesTab));
p->zBase = (char*)&p[1];
memcpy(p->zBase, zName, nName-6);
p->zBase[nName-6] = '\0';
p->zDb = &p->zBase[nName+1];
memcpy(p->zDb, zDb, nDb+1);
p->db = db;
rc = sqlite3_declare_vtab(db, ZONEFILE_FILES_SCHEMA);
}
if( rc!=SQLITE_OK ){
sqlite3_free(p);
p = 0;
}
*ppVtab = (sqlite3_vtab*)p;
return rc;
}
/*
** zonefile_files virtual table module xCreate method.
*/
static int zffCreate(
sqlite3 *db,
void *pAux,
int argc, const char *const*argv,
sqlite3_vtab **ppVtab,
char **pzErr
){
return zffCreateConnect(1, db, argc, argv, ppVtab, pzErr);
}
/*
** zonefile_files virtual table module xConnect method.
*/
static int zffConnect(
sqlite3 *db,
void *pAux,
int argc, const char *const*argv,
sqlite3_vtab **ppVtab,
char **pzErr
){
return zffCreateConnect(0, db, argc, argv, ppVtab, pzErr);
}
/*
** zonefile_files virtual table module xDisconnect method.
*/
static int zffDisconnect(sqlite3_vtab *pVtab){
ZonefileFilesTab *pTab = (ZonefileFilesTab*)pVtab;
sqlite3_finalize(pTab->pInsert);
sqlite3_finalize(pTab->pDelete);
sqlite3_finalize(pTab->pInsertIdx);
sqlite3_finalize(pTab->pDeleteIdx);
sqlite3_free(pTab);
return SQLITE_OK;
}
/*
** zonefile_files virtual table module xBestIndex method.
*/
static int zffBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pInfo){
return SQLITE_OK;
}
/*
** zonefile_files virtual table module xOpen method.
*/
static int zffOpen(sqlite3_vtab *pVtab, sqlite3_vtab_cursor **ppCursor){
ZonefileFilesCsr *pCsr;
pCsr = (ZonefileFilesCsr*)sqlite3_malloc(sizeof(ZonefileFilesCsr));
if( pCsr==0 ){
return SQLITE_NOMEM;
}
memset(pCsr, 0, sizeof(ZonefileFilesCsr));
*ppCursor = (sqlite3_vtab_cursor*)pCsr;
return SQLITE_OK;
}
/*
** Reset a ZonefileFilesCsr object to the state it is in immediately after
** it is allocated by zffOpen().
*/
static void zffCursorReset(ZonefileFilesCsr *pCsr){
sqlite3_finalize(pCsr->pSelect);
pCsr->pSelect = 0;
}
/*
** zonefile_files virtual table module xClose method.
*/
static int zffClose(sqlite3_vtab_cursor *cur){
ZonefileFilesCsr *pCsr = (ZonefileFilesCsr*)cur;
zffCursorReset(pCsr);
sqlite3_free(pCsr);
return SQLITE_OK;
}
/*
** zonefile_files virtual table module xNext method.
*/
static int zffNext(sqlite3_vtab_cursor *cur){
ZonefileFilesCsr *pCsr = (ZonefileFilesCsr*)cur;
int rc;
if( SQLITE_ROW==sqlite3_step(pCsr->pSelect) ){
rc = SQLITE_OK;
}else{
rc = sqlite3_finalize(pCsr->pSelect);
pCsr->pSelect = 0;
}
return rc;
}
/*
** zonefile_files virtual table module xFilter method.
*/
static int zffFilter(
sqlite3_vtab_cursor *cur,
int idxNum, const char *idxStr,
int argc, sqlite3_value **argv
){
ZonefileFilesCsr *pCsr = (ZonefileFilesCsr*)cur;
ZonefileFilesTab *pTab = (ZonefileFilesTab*)(pCsr->base.pVtab);
int rc;
zffCursorReset(pCsr);
rc = zonefilePrepare(pTab->db, &pCsr->pSelect, &pTab->base.zErrMsg,
"SELECT filename, fileid FROM %Q.'%q_shadow_file'",
pTab->zDb, pTab->zBase
);
if( rc==SQLITE_OK ){
rc = zffNext(cur);
}
return rc;
}
/*
** zonefile_files virtual table module xEof method.
*/
static int zffEof(sqlite3_vtab_cursor *cur){
ZonefileFilesCsr *pCsr = (ZonefileFilesCsr*)cur;
return pCsr->pSelect==0;
}
static void zonefileHeaderDeserialize(u8 *aBuf, ZonefileHeader *pHdr){
pHdr->magicNumber = zonefileGet32(&aBuf[0]);
pHdr->compressionTypeIndexData = aBuf[4];
pHdr->compressionTypeContent = aBuf[5];
pHdr->byteOffsetDictionary = zonefileGet32(&aBuf[6]);
pHdr->byteOffsetFrames = zonefileGet32(&aBuf[10]);
pHdr->numFrames = zonefileGet32(&aBuf[14]);
pHdr->numKeys = zonefileGet32(&aBuf[18]);
pHdr->encryptionType = aBuf[22];
pHdr->encryptionKeyIdx = aBuf[23];
pHdr->extendedHeaderVersion = aBuf[24];
pHdr->extendedHeaderSize = aBuf[25];
}
static void zonefileJsonHeader(sqlite3_context *pCtx, const char *zFile){
char *zErr = 0;
FILE *pFd = zonefileFileOpen(zFile, 0, &zErr);
if( pFd ){
int rc;
ZonefileHeader hdr;
u8 aBuf[ZONEFILE_SZ_HEADER];
rc = zonefileFileRead(pFd, aBuf, ZONEFILE_SZ_HEADER, 0);
if( rc==SQLITE_OK ){
zonefileHeaderDeserialize(aBuf, &hdr);
}
if( rc!=SQLITE_OK ){
zonefileCtxError(pCtx, "failed to read header from file: \"%s\"", zFile);
}else{
char *zJson = sqlite3_mprintf("{"
"\"magicNumber\":%u,"
"\"compressionTypeIndexData\":%u,"
"\"compressionTypeContent\":%u,"
"\"byteOffsetDictionary\":%u,"
"\"byteOffsetFrames\":%u,"
"\"numFrames\":%u,"
"\"numKeys\":%u,"
"\"encryptionType\":%u,"
"\"encryptionKeyIdx\":%u,"
"\"extendedHeaderVersion\":%u,"
"\"extendedHeaderSize\":%u}",
(u32)hdr.magicNumber,
(u32)hdr.compressionTypeIndexData,
(u32)hdr.compressionTypeContent,
(u32)hdr.byteOffsetDictionary,
(u32)hdr.byteOffsetFrames,
(u32)hdr.numFrames,
(u32)hdr.numKeys,
(u32)hdr.encryptionType,
(u32)hdr.encryptionKeyIdx,
(u32)hdr.extendedHeaderVersion,
(u32)hdr.extendedHeaderSize
);
if( zJson ){
sqlite3_result_text(pCtx, zJson, -1, SQLITE_TRANSIENT);
sqlite3_free(zJson);
}else{
sqlite3_result_error_nomem(pCtx);
}
}
fclose(pFd);
}else{
sqlite3_result_error(pCtx, zErr, -1);
sqlite3_free(zErr);
}
}
/*
** zonefile_files virtual table module xColumn method.
*/
static int zffColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){
ZonefileFilesCsr *pCsr = (ZonefileFilesCsr*)cur;
switch( i ){
case 0: /* filename */
sqlite3_result_value(ctx, sqlite3_column_value(pCsr->pSelect, 0));
break;
case 1: /* ekey */
break;
case 2: { /* header */
const char *zFile = (const char*)sqlite3_column_text(pCsr->pSelect, 0);
zonefileJsonHeader(ctx, zFile);
break;
}
}
return SQLITE_OK;
}
/*
** zonefile_files virtual table module xRowid method.
*/
static int zffRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
ZonefileFilesCsr *pCsr = (ZonefileFilesCsr*)cur;
*pRowid = sqlite3_column_int64(pCsr->pSelect, 1);
return SQLITE_OK;
}
/*
** Read and decode a Zonefile header from the start of the file opened
** by file-handle pFd. If successful, populate object (*pHdr) before
** returning SQLITE_OK. Otherwise, if an error occurs, set output
** parameter (*pzErr) to point to an English language error message and
** return an SQLite error code.
**
** It is the responsibility of the caller to eventually free any error
** message returned via (*pzErr) using sqlite3_free().
*/
static int zonefileReadHeader(
FILE *pFd, /* File to read from */
const char *zFile, /* Name of file opened by pFd */
ZonefileHeader *pHdr, /* Populate this object before returning */
char **pzErr /* OUT: Error message */
){
u8 aBuf[ZONEFILE_SZ_HEADER];
int rc = zonefileFileRead(pFd, aBuf, ZONEFILE_SZ_HEADER, 0);
if( rc==SQLITE_OK ){
zonefileHeaderDeserialize(aBuf, pHdr);
if( pHdr->magicNumber!=ZONEFILE_MAGIC_NUMBER ){
rc = SQLITE_ERROR;
}
}
if( rc!=SQLITE_OK ){
*pzErr = sqlite3_mprintf(
"failed to read zonefile header from file \"%s\"", zFile
);
}
return rc;
}
static int zonefilePopulateIndex(
ZonefileFilesTab *pTab,
const char *zFile,
i64 iFileid
){
ZonefileHeader hdr;
int rc;
FILE *pFd = zonefileFileOpen(zFile, 0, &pTab->base.zErrMsg);
if( pFd==0 ){
rc = SQLITE_ERROR;
}else{
rc = zonefileReadHeader(pFd, zFile, &hdr, &pTab->base.zErrMsg);
}
if( rc==SQLITE_OK && hdr.numKeys>0 ){
/* TODO: Deal with encrypted and compressed ZonefileIndex objects */
i64 iOff; /* Offset of keyOffsets array */
u8 *aKey; /* Entire KeyOffsets array */
int nKey; /* Size of buffer aKey[] in bytes */
int i;
assert( hdr.encryptionType==0 && hdr.compressionTypeIndexData==0 );
iOff = ZONEFILE_SZ_HEADER + (hdr.numFrames * 4);
nKey = ZONEFILE_SZ_KEYOFFSETS_ENTRY * hdr.numKeys;
aKey = (u8*)sqlite3_malloc(nKey);
if( aKey==0 ){
rc = SQLITE_NOMEM;
}else{
rc = zonefileFileRead(pFd, aKey, nKey, iOff);
}
if( rc==SQLITE_OK && pTab->pInsertIdx==0 ){
rc = zonefilePrepare(pTab->db, &pTab->pInsertIdx, &pTab->base.zErrMsg,
"INSERT INTO %Q.'%q_shadow_idx'(k, fileid, frame, ofst, sz)"
"VALUES(?,?,?,?,?)",
pTab->zDb, pTab->zBase
);
}
for(i=0; i<hdr.numKeys && rc==SQLITE_OK; i++){
u8 *aEntry = &aKey[ZONEFILE_SZ_KEYOFFSETS_ENTRY * i];
sqlite3_bind_int64(pTab->pInsertIdx, 1, (i64)zonefileGet64(&aEntry[0]));
sqlite3_bind_int64(pTab->pInsertIdx, 2, iFileid);
sqlite3_bind_int64(pTab->pInsertIdx, 3, (i64)zonefileGet32(&aEntry[8]));
sqlite3_bind_int64(pTab->pInsertIdx, 4, (i64)zonefileGet32(&aEntry[12]));
sqlite3_bind_int64(pTab->pInsertIdx, 5, (i64)zonefileGet32(&aEntry[16]));
sqlite3_step(pTab->pInsertIdx);
rc = sqlite3_reset(pTab->pInsertIdx);
if( rc!=SQLITE_OK ){
pTab->base.zErrMsg = sqlite3_mprintf("%s", sqlite3_errmsg(pTab->db));
}
}
sqlite3_free(aKey);
}
zonefileFileClose(pFd);
return rc;
}
/*
** zonefile_files virtual table module xUpdate method.
**
** A delete specifies a single argument - the rowid of the row to remove.
**
** Update and insert operations pass:
**
** 1. The "old" rowid, or NULL.
** 2. The "new" rowid.
** 3. Values for each of the 3 columns: (filename,ekey,header)
*/
static int zffUpdate(
sqlite3_vtab *pVtab,
int nVal,
sqlite3_value **apVal,
sqlite_int64 *pRowid
){
int rc = SQLITE_OK;
ZonefileFilesTab *pTab = (ZonefileFilesTab*)pVtab;
if( sqlite3_value_type(apVal[0])==SQLITE_INTEGER ){
if( pTab->pDelete==0 ){
rc = zonefilePrepare(pTab->db, &pTab->pDelete, &pVtab->zErrMsg,
"DELETE FROM %Q.'%q_shadow_file' WHERE fileid=?",
pTab->zDb, pTab->zBase
);
}
if( rc==SQLITE_OK && pTab->pDeleteIdx==0 ){
rc = zonefilePrepare(pTab->db, &pTab->pDeleteIdx, &pVtab->zErrMsg,
"DELETE FROM %Q.'%q_shadow_idx' WHERE fileid=?",
pTab->zDb, pTab->zBase
);
}
if( rc==SQLITE_OK ){
sqlite3_bind_value(pTab->pDelete, 1, apVal[0]);
sqlite3_step(pTab->pDelete);
rc = sqlite3_reset(pTab->pDelete);
}
if( rc==SQLITE_OK ){
sqlite3_bind_value(pTab->pDeleteIdx, 1, apVal[0]);
sqlite3_step(pTab->pDeleteIdx);
rc = sqlite3_reset(pTab->pDeleteIdx);
}
}
if( nVal>1 ){
const char *zFile = (const char*)sqlite3_value_text(apVal[2]);
if( pTab->pInsert==0 ){
rc = zonefilePrepare(pTab->db, &pTab->pInsert, &pVtab->zErrMsg,
"INSERT INTO %Q.'%q_shadow_file'(filename) VALUES(?)",
pTab->zDb, pTab->zBase
);
}
/* Add the new entry to the %_shadow_file table. */
if( rc==SQLITE_OK ){
sqlite3_bind_text(pTab->pInsert, 1, zFile, -1, SQLITE_TRANSIENT);
sqlite3_step(pTab->pInsert);
rc = sqlite3_reset(pTab->pInsert);
}
/* Populate the %_shadow_idx table with entries for all keys in
** the zonefile just added to %_shadow_file. */
if( rc==SQLITE_OK ){
i64 iFileid = sqlite3_last_insert_rowid(pTab->db);
rc = zonefilePopulateIndex(pTab, zFile, iFileid);
}
}
return rc;
}
typedef struct ZonefileTab ZonefileTab;
struct ZonefileTab {
sqlite3_vtab base; /* Base class - must be first */
sqlite3 *db;
sqlite3_stmt *pIdToName; /* Translate fileid to filename */
char *zName; /* Name of this table */
char *zDb; /* Name of db containing this table */
};
typedef struct ZonefileCsr ZonefileCsr;
struct ZonefileCsr {
sqlite3_vtab_cursor base; /* Base class - must be first */
sqlite3_stmt *pSelect; /* SELECT on %_shadow_idx table */
};
/*
** This function does the work of xCreate (if bCreate!=0) or xConnect
** (if bCreate==0) for the zonefile module.
**
** argv[0] -> module name ("zonefile")
** argv[1] -> database name
** argv[2] -> table name
*/
static int zonefileCreateConnect(
int bCreate,
sqlite3 *db,
int argc, const char *const*argv,
sqlite3_vtab **ppVtab,
char **pzErr
){
ZonefileTab *p;
const char *zName = argv[2];
const char *zDb = argv[1];
int nName = strlen(zName);
int nDb = strlen(zDb);
int rc = SQLITE_OK;
p = (ZonefileTab*)sqlite3_malloc(sizeof(ZonefileTab) + nName+1 + nDb+1);
if( !p ){
rc = SQLITE_NOMEM;
}else{
memset(p, 0, sizeof(ZonefileTab));
p->zName = (char*)&p[1];
memcpy(p->zName, zName, nName+1);
p->zDb = &p->zName[nName+1];
memcpy(p->zDb, zDb, nDb+1);
p->db = db;
if( bCreate ){
char *zSql = sqlite3_mprintf(
"CREATE TABLE %Q.'%q_shadow_idx'("
" k INTEGER PRIMARY KEY,"
" fileid INTEGER,"
" frame INTEGER,"
" ofst INTEGER,"
" sz INTEGER"
");"
"CREATE TABLE %Q.'%q_shadow_file'("
" filename TEXT,"
" fileid INTEGER PRIMARY KEY"
");"
"CREATE VIRTUAL TABLE %Q.'%q_files' USING zonefile_files;",
zDb, zName, zDb, zName, zDb, zName
);
if( zSql==0 ){
rc = SQLITE_NOMEM;
}else{
rc = sqlite3_exec(db, zSql, 0, 0, pzErr);
sqlite3_free(zSql);
}
}
if( rc==SQLITE_OK ){
rc = sqlite3_declare_vtab(db, ZONEFILE_SCHEMA);
}
}
if( rc!=SQLITE_OK ){
sqlite3_free(p);
p = 0;
}
*ppVtab = (sqlite3_vtab*)p;
return rc;
}
/*
** zonefile virtual table module xCreate method.
*/
static int zonefileCreate(
sqlite3 *db,
void *pAux,
int argc, const char *const*argv,
sqlite3_vtab **ppVtab,
char **pzErr
){
return zonefileCreateConnect(1, db, argc, argv, ppVtab, pzErr);
}
/*
** zonefile virtual table module xConnect method.
*/
static int zonefileConnect(
sqlite3 *db,
void *pAux,
int argc, const char *const*argv,
sqlite3_vtab **ppVtab,
char **pzErr
){
return zonefileCreateConnect(0, db, argc, argv, ppVtab, pzErr);
}
/*
** zonefile virtual table module xDisconnect method.
*/
static int zonefileDisconnect(sqlite3_vtab *pVtab){
ZonefileTab *pTab = (ZonefileTab*)pVtab;
sqlite3_finalize(pTab->pIdToName);
sqlite3_free(pTab);
return SQLITE_OK;
}
/*
** Zonefile virtual table module xBestIndex method.
**
** Equality and range constraints on either the rowid or column "k" (which
** are the same thing) are processed. Bits in the idxNum parameter are
** set to indicate the constraints present:
**
** 0x01: k == ?
** 0x02: k < ?
** 0x04: k <= ?
** 0x08: k > ?
** 0x10: k >= ?
**
** Only some combinations are valid:
**
** * If an == constraint is found, no other bits are set.
** * If a < constraint is present, any <= is ignored.
** * If a > constraint is present, any >= is ignored.
*/
static int zonefileBestIndex(sqlite3_vtab *pVtab, sqlite3_index_info *pInfo){
int iEq = -1;
int iLt = -1;
int iLe = -1;
int iGt = -1;
int iGe = -1;
int i;
int idxNum = 0;
double cost = 1000000000.0;
for(i=0; i<pInfo->nConstraint; i++){
struct sqlite3_index_constraint *p = &pInfo->aConstraint[i];
if( p->usable && p->iColumn<=0 ){
switch( p->op ){
case SQLITE_INDEX_CONSTRAINT_EQ: iEq = i; break;
case SQLITE_INDEX_CONSTRAINT_LT: iLt = i; break;
case SQLITE_INDEX_CONSTRAINT_LE: iLe = i; break;
case SQLITE_INDEX_CONSTRAINT_GT: iGt = i; break;
case SQLITE_INDEX_CONSTRAINT_GE: iGe = i; break;
}
}
}
if( iEq>=0 ){
cost = 10.0;
idxNum = 0x01;
pInfo->aConstraintUsage[iEq].argvIndex = 1;
}else{
int iIdx = 1;
if( iLt>=0 ){
pInfo->aConstraintUsage[iLt].argvIndex = iIdx++;
idxNum |= 0x02;
}else if( iLe>=0 ){
pInfo->aConstraintUsage[iLe].argvIndex = iIdx++;
idxNum |= 0x04;
}
if( iGt>=0 ){
pInfo->aConstraintUsage[iGt].argvIndex = iIdx++;
idxNum |= 0x08;
}else if( iGe>=0 ){
pInfo->aConstraintUsage[iGe].argvIndex = iIdx++;
idxNum |= 0x10;
}
if( iIdx==2 ) cost = 10000.0;
if( iIdx==3 ) cost = 100.0;
}
pInfo->idxNum = idxNum;
pInfo->estimatedCost = cost;
return SQLITE_OK;
}
/*
** zonefile virtual table module xDestroy method.
*/
static int zonefileDestroy(sqlite3_vtab *pVtab){
ZonefileTab *pTab = (ZonefileTab*)pVtab;
int rc = SQLITE_OK;
char *zSql = sqlite3_mprintf(
"DROP TABLE IF EXISTS %Q.'%q_shadow_idx';"
"DROP TABLE IF EXISTS %Q.'%q_shadow_file';"
"DROP TABLE IF EXISTS %Q.'%q_files';",
pTab->zDb, pTab->zName, pTab->zDb, pTab->zName, pTab->zDb, pTab->zName
);
if( zSql==0 ){
rc = SQLITE_NOMEM;
}else{
rc = sqlite3_exec(pTab->db, zSql, 0, 0, 0);
sqlite3_free(zSql);
}
if( rc==SQLITE_OK ){
zonefileDisconnect(pVtab);
}
return rc;
}
/*
** zonefile virtual table module xOpen method.
*/
static int zonefileOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
ZonefileCsr *pCsr;
pCsr = (ZonefileCsr*)sqlite3_malloc(sizeof(ZonefileCsr));
if( pCsr==0 ){
return SQLITE_NOMEM;
}
memset(pCsr, 0, sizeof(ZonefileCsr));
*ppCursor = (sqlite3_vtab_cursor*)pCsr;
return SQLITE_OK;
}
/*
** Reset a ZonefileCsr object to the state it is in immediately after
** it is allocated by zffOpen().
*/
static void zonefileCursorReset(ZonefileCsr *pCsr){
sqlite3_finalize(pCsr->pSelect);
pCsr->pSelect = 0;
}
/*
** zonefile virtual table module xClose method.
*/
static int zonefileClose(sqlite3_vtab_cursor *cur){
ZonefileCsr *pCsr = (ZonefileCsr*)cur;
zonefileCursorReset(pCsr);
sqlite3_free(pCsr);
return SQLITE_OK;
}
/*
** zonefile virtual table module xNext method.
*/
static int zonefileNext(sqlite3_vtab_cursor *cur){
ZonefileCsr *pCsr = (ZonefileCsr*)cur;
int rc = SQLITE_OK;
if( SQLITE_ROW!=sqlite3_step(pCsr->pSelect) ){
rc = sqlite3_finalize(pCsr->pSelect);
if( rc!=SQLITE_OK ){
ZonefileTab *pTab = (ZonefileTab*)pCsr->base.pVtab;
pTab->base.zErrMsg = sqlite3_mprintf("%s", sqlite3_errmsg(pTab->db));
}
pCsr->pSelect = 0;
}
return rc;
}
/*
** zonefile virtual table module xFilter method.
*/
static int zonefileFilter(
sqlite3_vtab_cursor *cur,
int idxNum, const char *idxStr,
int argc, sqlite3_value **argv
){
ZonefileCsr *pCsr = (ZonefileCsr*)cur;
ZonefileTab *pTab = (ZonefileTab*)pCsr->base.pVtab;
int rc;
const char *z1 = 0;
const char *z2 = 0;
if( idxNum & 0x01 ){
z1 = "k = ?";
}else{
if( idxNum & 0x02 ) { z1 = "k < ?"; }
if( idxNum & 0x04 ) { z1 = "k <= ?"; }
if( idxNum & 0x08 ) { if( z1 ) z2 = "k > ?"; else z1 = "k > ?"; }
if( idxNum & 0x10 ) { if( z1 ) z2 = "k <= ?"; else z1 = "k <= ?"; }
}
rc = zonefilePrepare(pTab->db, &pCsr->pSelect, &pTab->base.zErrMsg,
"SELECT k, fileid, frame, ofst, sz FROM %Q.'%q_shadow_idx'%s%s%s%s",
pTab->zDb, pTab->zName,
z1 ? " WHERE " : "", z1,
z2 ? " AND " : "", z2
);
if( z1 ) sqlite3_bind_value(pCsr->pSelect, 1, argv[0]);
if( z2 ) sqlite3_bind_value(pCsr->pSelect, 2, argv[1]);
if( rc==SQLITE_OK ){
rc = zonefileNext(cur);
}
return rc;
}
/*
** zonefile virtual table module xEof method.
*/
static int zonefileEof(sqlite3_vtab_cursor *cur){
ZonefileCsr *pCsr = (ZonefileCsr*)cur;
return (pCsr->pSelect==0);
}
static void zonefileFree(void *p){
sqlite3_free(p);
}
static int zonefileGetValue(sqlite3_context *pCtx, ZonefileCsr *pCsr){
ZonefileTab *pTab = (ZonefileTab*)pCsr->base.pVtab;
const char *zFile = 0;
char *zErr = 0;
FILE *pFd = 0;
int rc = SQLITE_OK;
u32 iOff = 0; /* Offset of frame in file */
ZonefileHeader hdr;
if( pTab->pIdToName==0 ){
rc = zonefilePrepare(pTab->db, &pTab->pIdToName, &pTab->base.zErrMsg,
"SELECT filename FROM %Q.'%q_shadow_file' WHERE fileid=?",
pTab->zDb, pTab->zName
);
if( rc!=SQLITE_OK ){
zonefileTransferError(pCtx);
return SQLITE_ERROR;
}
}
/* Open the file to read the blob from */
sqlite3_bind_int64(pTab->pIdToName, 1, sqlite3_column_int64(pCsr->pSelect,1));
if( SQLITE_ROW==sqlite3_step(pTab->pIdToName) ){
zFile = (const char*)sqlite3_column_text(pTab->pIdToName, 0);
pFd = zonefileFileOpen(zFile, 0, &zErr);
}
if( zFile==0 ){
rc = sqlite3_reset(pTab->pIdToName);
if( rc!=SQLITE_OK ){
zonefileTransferError(pCtx);
}else{
rc = SQLITE_CORRUPT_VTAB;
}
}else if( pFd==0 ){
rc = SQLITE_ERROR;
}
/* Read the zonefile header */
if( rc==SQLITE_OK ){
rc = zonefileReadHeader(pFd, zFile, &hdr, &zErr);
}
/* Calculate the offset of the frame to read the blob from */
if( rc==SQLITE_OK ){
u8 aOff[4] = {0,0,0,0};
int iFrame = sqlite3_column_int(pCsr->pSelect, 2);
rc = zonefileFileRead(pFd, aOff, 4, ZONEFILE_SZ_HEADER + 4 * iFrame);
iOff = zonefileGet32(aOff);
}
/* Read the blob */
if( rc==SQLITE_OK ){
int sz = sqlite3_column_int(pCsr->pSelect, 4);
int ofst = sqlite3_column_int(pCsr->pSelect, 3);
u8 *aBuf = sqlite3_malloc(sz);
if( aBuf==0 ){
rc = SQLITE_NOMEM;
}else{
rc = zonefileFileRead(pFd, aBuf, sz, hdr.byteOffsetFrames + iOff + ofst);
if( rc==SQLITE_OK ){
sqlite3_result_blob(pCtx, aBuf, sz, zonefileFree);
}else{
zErr = sqlite3_mprintf(
"failed to read %d bytes from file \"%s\"", sz, zFile
);
}
}
}
sqlite3_reset(pTab->pIdToName);
if( zErr ){
assert( rc!=SQLITE_OK );
sqlite3_result_error(pCtx, zErr, -1);
sqlite3_free(zErr);
}
zonefileFileClose(pFd);
return rc;
}
/*
** zonefile virtual table module xColumn method.
*/
static int zonefileColumn(
sqlite3_vtab_cursor *cur,
sqlite3_context *pCtx,
int i
){
ZonefileCsr *pCsr = (ZonefileCsr*)cur;
int rc = SQLITE_OK;
switch( i ){
case 0: /* k */
sqlite3_result_value(pCtx, sqlite3_column_value(pCsr->pSelect, 0));
break;
case 1: /* v */
rc = zonefileGetValue(pCtx, pCsr);
break;
case 2: /* fileid */
sqlite3_result_value(pCtx, sqlite3_column_value(pCsr->pSelect, 1));
break;
case 3: /* frame */
sqlite3_result_value(pCtx, sqlite3_column_value(pCsr->pSelect, 2));
break;
case 4: /* ofst */
sqlite3_result_value(pCtx, sqlite3_column_value(pCsr->pSelect, 3));
break;
default: /* sz */
sqlite3_result_value(pCtx, sqlite3_column_value(pCsr->pSelect, 4));
break;
}
return rc;
}
/*
** zonefile virtual table module xRowid method.
*/
static int zonefileRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
ZonefileCsr *pCsr = (ZonefileCsr*)cur;
*pRowid = sqlite3_column_int64(pCsr->pSelect, 0);
return SQLITE_OK;
}
/*
** Register the "zonefile" extensions.
*/
static int zonefileRegister(sqlite3 *db){
static sqlite3_module filesModule = {
0, /* iVersion */
zffCreate, /* xCreate - create a table */
zffConnect, /* xConnect - connect to an existing table */
zffBestIndex, /* xBestIndex - Determine search strategy */
zffDisconnect, /* xDisconnect - Disconnect from a table */
zffDisconnect, /* xDestroy - Drop a table */
zffOpen, /* xOpen - open a cursor */
zffClose, /* xClose - close a cursor */
zffFilter, /* xFilter - configure scan constraints */
zffNext, /* xNext - advance a cursor */
zffEof, /* xEof */
zffColumn, /* xColumn - read data */
zffRowid, /* xRowid - read data */
zffUpdate, /* xUpdate - write data */
0, /* xBegin - begin transaction */
0, /* xSync - sync transaction */
0, /* xCommit - commit transaction */
0, /* xRollback - rollback transaction */
0, /* xFindFunction - function overloading */
0, /* xRename - rename the table */
0, /* xSavepoint */
0, /* xRelease */
0 /* xRollbackTo */
};
static sqlite3_module zonefileModule = {
0, /* iVersion */
zonefileCreate, /* xCreate - create a table */
zonefileConnect, /* xConnect - connect to an existing table */
zonefileBestIndex, /* xBestIndex - Determine search strategy */
zonefileDisconnect, /* xDisconnect - Disconnect from a table */
zonefileDestroy, /* xDestroy - Drop a table */
zonefileOpen, /* xOpen - open a cursor */
zonefileClose, /* xClose - close a cursor */
zonefileFilter, /* xFilter - configure scan constraints */
zonefileNext, /* xNext - advance a cursor */
zonefileEof, /* xEof */
zonefileColumn, /* xColumn - read data */
zonefileRowid, /* xRowid - read data */
0, /* xUpdate - write data */
0, /* xBegin - begin transaction */
0, /* xSync - sync transaction */
0, /* xCommit - commit transaction */
0, /* xRollback - rollback transaction */
0, /* xFindFunction - function overloading */
0, /* xRename - rename the table */
0, /* xSavepoint */
0, /* xRelease */
0 /* xRollbackTo */
};
struct Func {
const char *z;
int n;
void (*x)(sqlite3_context*,int,sqlite3_value**);
} aFunc[] = {
{ "zonefile_write", 2, zonefileWriteFunc },
{ "zonefile_write", 3, zonefileWriteFunc },
};
int rc = SQLITE_OK;
int i;
for(i=0; rc==SQLITE_OK && i<sizeof(aFunc)/sizeof(aFunc[0]); i++){
struct Func *p = &aFunc[i];
rc = sqlite3_create_function(db, p->z, p->n, SQLITE_ANY, 0, p->x, 0, 0);
}
if( rc==SQLITE_OK ){
rc = sqlite3_create_module(db, "zonefile_files", &filesModule, 0);
}
if( rc==SQLITE_OK ){
rc = sqlite3_create_module(db, "zonefile", &zonefileModule, 0);
}
return rc;
}
#else /* SQLITE_OMIT_VIRTUALTABLE */
# define zonefileRegister(x) SQLITE_OK
#endif
#ifdef _WIN32
__declspec(dllexport)
#endif
int sqlite3_zonefile_init(
sqlite3 *db,
char **pzErrMsg,
const sqlite3_api_routines *pApi
){
SQLITE_EXTENSION_INIT2(pApi);
(void)pzErrMsg; /* Unused parameter */
return zonefileRegister(db);
}