/*
** Copyright (c) 2019 D. Richard Hipp
**
** This program is free software; you can redistribute it and/or
** modify it under the terms of the Simplified BSD License (also
** known as the "2-Clause License" or "FreeBSD License".)
**
** This program is distributed in the hope that it will be useful,
** but without any warranty; without even the implied warranty of
** merchantability or fitness for a particular purpose.
**
** Author contact information:
** drh@hwaci.com
** http://www.hwaci.com/drh/
**
*******************************************************************************
**
** This module implements SQL interfaces to the delta logic. The code
** here is adapted from the ext/misc/fossildelta.c extension in SQLite.
*/
#include "config.h"
#include "deltafunc.h"
/*
** SQL functions: delta_create(X,Y)
**
** Return a delta that will transform X into Y.
*/
static void deltaCreateFunc(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
const char *aOrig; int nOrig; /* old blob */
const char *aNew; int nNew; /* new blob */
char *aOut; int nOut; /* output delta */
assert( argc==2 );
if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return;
if( sqlite3_value_type(argv[1])==SQLITE_NULL ) return;
nOrig = sqlite3_value_bytes(argv[0]);
aOrig = (const char*)sqlite3_value_blob(argv[0]);
nNew = sqlite3_value_bytes(argv[1]);
aNew = (const char*)sqlite3_value_blob(argv[1]);
aOut = sqlite3_malloc64(nNew+70);
if( aOut==0 ){
sqlite3_result_error_nomem(context);
}else{
nOut = delta_create(aOrig, nOrig, aNew, nNew, aOut);
if( nOut<0 ){
sqlite3_free(aOut);
sqlite3_result_error(context, "cannot create fossil delta", -1);
}else{
sqlite3_result_blob(context, aOut, nOut, sqlite3_free);
}
}
}
/*
** SQL functions: delta_apply(X,D)
**
** Return the result of applying delta D to input X.
*/
static void deltaApplyFunc(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
const char *aOrig; int nOrig; /* The X input */
const char *aDelta; int nDelta; /* The input delta (D) */
char *aOut; int nOut, nOut2; /* The output */
assert( argc==2 );
if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return;
if( sqlite3_value_type(argv[1])==SQLITE_NULL ) return;
nOrig = sqlite3_value_bytes(argv[0]);
aOrig = (const char*)sqlite3_value_blob(argv[0]);
nDelta = sqlite3_value_bytes(argv[1]);
aDelta = (const char*)sqlite3_value_blob(argv[1]);
/* Figure out the size of the output */
nOut = delta_output_size(aDelta, nDelta);
if( nOut<0 ){
sqlite3_result_error(context, "corrupt fossil delta", -1);
return;
}
aOut = sqlite3_malloc64((sqlite3_int64)nOut+1);
if( aOut==0 ){
sqlite3_result_error_nomem(context);
}else{
nOut2 = delta_apply(aOrig, nOrig, aDelta, nDelta, aOut);
if( nOut2!=nOut ){
sqlite3_free(aOut);
sqlite3_result_error(context, "corrupt fossil delta", -1);
}else{
sqlite3_result_blob(context, aOut, nOut, sqlite3_free);
}
}
}
/*
** SQL functions: delta_output_size(D)
**
** Return the size of the output that results from applying delta D.
*/
static void deltaOutputSizeFunc(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
const char *aDelta; int nDelta; /* The input delta (D) */
int nOut; /* Size of output */
assert( argc==1 );
if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return;
nDelta = sqlite3_value_bytes(argv[0]);
aDelta = (const char*)sqlite3_value_blob(argv[0]);
/* Figure out the size of the output */
nOut = delta_output_size(aDelta, nDelta);
if( nOut<0 ){
sqlite3_result_error(context, "corrupt fossil delta", -1);
return;
}else{
sqlite3_result_int(context, nOut);
}
}
/*****************************************************************************
** Table-valued SQL function: delta_parse(DELTA)
**
** Schema:
**
** CREATE TABLE delta_parse(
** op TEXT,
** a1 INT,
** a2 ANY,
** delta HIDDEN BLOB
** );
**
** Given an input DELTA, this function parses the delta and returns
** rows for each entry in the delta. The op column has one of the
** values SIZE, COPY, INSERT, CHECKSUM, ERROR.
**
** Assuming no errors, the first row has op='SIZE'. a1 is the size of
** the output in bytes and a2 is NULL.
**
** After the initial SIZE row, there are zero or more 'COPY' and/or 'INSERT'
** rows. A COPY row means content is copied from the source into the
** output. Column a1 is the number of bytes to copy and a2 is the offset
** into source from which to begin copying. An INSERT row means to
** insert text into the output stream. Column a1 is the number of bytes
** to insert and column is a BLOB that contains the text to be inserted.
**
** The last row of a well-formed delta will have an op value of 'CHECKSUM'.
** The a1 column will be the value of the checksum and a2 will be NULL.
**
** If the input delta is not well-formed, then a row with an op value
** of 'ERROR' is returned. The a1 value of the ERROR row is the offset
** into the delta where the error was encountered and a2 is NULL.
*/
typedef struct deltaparsevtab_vtab deltaparsevtab_vtab;
typedef struct deltaparsevtab_cursor deltaparsevtab_cursor;
struct deltaparsevtab_vtab {
sqlite3_vtab base; /* Base class - must be first */
/* No additional information needed */
};
struct deltaparsevtab_cursor {
sqlite3_vtab_cursor base; /* Base class - must be first */
char *aDelta; /* The delta being parsed */
int nDelta; /* Number of bytes in the delta */
int iCursor; /* Current cursor location */
int eOp; /* Name of current operator */
unsigned int a1, a2; /* Arguments to current operator */
int iNext; /* Next cursor value */
};
/* Operator names:
*/
static const char *const azOp[] = {
"SIZE", "COPY", "INSERT", "CHECKSUM", "ERROR", "EOF"
};
#define DELTAPARSE_OP_SIZE 0
#define DELTAPARSE_OP_COPY 1
#define DELTAPARSE_OP_INSERT 2
#define DELTAPARSE_OP_CHECKSUM 3
#define DELTAPARSE_OP_ERROR 4
#define DELTAPARSE_OP_EOF 5
/*
** Read bytes from *pz and convert them into a positive integer. When
** finished, leave *pz pointing to the first character past the end of
** the integer. The *pLen parameter holds the length of the string
** in *pz and is decremented once for each character in the integer.
*/
static unsigned int deltaGetInt(const char **pz, int *pLen){
static const signed char zValue[] = {
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
-1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, 36,
-1, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, -1, -1, -1, 63, -1,
};
unsigned int v = 0;
int c;
unsigned char *z = (unsigned char*)*pz;
unsigned char *zStart = z;
while( (c = zValue[0x7f&*(z++)])>=0 ){
v = (v<<6) + c;
}
z--;
*pLen -= z - zStart;
*pz = (char*)z;
return v;
}
/*
** The deltaparsevtabConnect() method is invoked to create a new
** deltaparse virtual table.
**
** Think of this routine as the constructor for deltaparsevtab_vtab objects.
**
** All this routine needs to do is:
**
** (1) Allocate the deltaparsevtab_vtab object and initialize all fields.
**
** (2) Tell SQLite (via the sqlite3_declare_vtab() interface) what the
** result set of queries against the virtual table will look like.
*/
static int deltaparsevtabConnect(
sqlite3 *db,
void *pAux,
int argc, const char *const*argv,
sqlite3_vtab **ppVtab,
char **pzErr
){
deltaparsevtab_vtab *pNew;
int rc;
rc = sqlite3_declare_vtab(db,
"CREATE TABLE x(op,a1,a2,delta HIDDEN)"
);
/* For convenience, define symbolic names for the index to each column. */
#define DELTAPARSEVTAB_OP 0
#define DELTAPARSEVTAB_A1 1
#define DELTAPARSEVTAB_A2 2
#define DELTAPARSEVTAB_DELTA 3
if( rc==SQLITE_OK ){
pNew = sqlite3_malloc64( sizeof(*pNew) );
*ppVtab = (sqlite3_vtab*)pNew;
if( pNew==0 ) return SQLITE_NOMEM;
memset(pNew, 0, sizeof(*pNew));
}
return rc;
}
/*
** This method is the destructor for deltaparsevtab_vtab objects.
*/
static int deltaparsevtabDisconnect(sqlite3_vtab *pVtab){
deltaparsevtab_vtab *p = (deltaparsevtab_vtab*)pVtab;
sqlite3_free(p);
return SQLITE_OK;
}
/*
** Constructor for a new deltaparsevtab_cursor object.
*/
static int deltaparsevtabOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
deltaparsevtab_cursor *pCur;
pCur = sqlite3_malloc( sizeof(*pCur) );
if( pCur==0 ) return SQLITE_NOMEM;
memset(pCur, 0, sizeof(*pCur));
*ppCursor = &pCur->base;
return SQLITE_OK;
}
/*
** Destructor for a deltaparsevtab_cursor.
*/
static int deltaparsevtabClose(sqlite3_vtab_cursor *cur){
deltaparsevtab_cursor *pCur = (deltaparsevtab_cursor*)cur;
sqlite3_free(pCur->aDelta);
sqlite3_free(pCur);
return SQLITE_OK;
}
/*
** Advance a deltaparsevtab_cursor to its next row of output.
*/
static int deltaparsevtabNext(sqlite3_vtab_cursor *cur){
deltaparsevtab_cursor *pCur = (deltaparsevtab_cursor*)cur;
const char *z;
int i = 0;
pCur->iCursor = pCur->iNext;
z = pCur->aDelta + pCur->iCursor;
pCur->a1 = deltaGetInt(&z, &i);
switch( z[0] ){
case '@': {
z++;
pCur->a2 = deltaGetInt(&z, &i);
pCur->eOp = DELTAPARSE_OP_COPY;
pCur->iNext = (int)(&z[1] - pCur->aDelta);
break;
}
case ':': {
z++;
pCur->a2 = (unsigned int)(z - pCur->aDelta);
pCur->eOp = DELTAPARSE_OP_INSERT;
pCur->iNext = (int)(&z[pCur->a1] - pCur->aDelta);
break;
}
case ';': {
pCur->eOp = DELTAPARSE_OP_CHECKSUM;
pCur->iNext = pCur->nDelta;
break;
}
default: {
if( pCur->iNext==pCur->nDelta ){
pCur->eOp = DELTAPARSE_OP_EOF;
}else{
pCur->eOp = DELTAPARSE_OP_ERROR;
pCur->iNext = pCur->nDelta;
}
break;
}
}
return SQLITE_OK;
}
/*
** Return values of columns for the row at which the deltaparsevtab_cursor
** is currently pointing.
*/
static int deltaparsevtabColumn(
sqlite3_vtab_cursor *cur, /* The cursor */
sqlite3_context *ctx, /* First argument to sqlite3_result_...() */
int i /* Which column to return */
){
deltaparsevtab_cursor *pCur = (deltaparsevtab_cursor*)cur;
switch( i ){
case DELTAPARSEVTAB_OP: {
sqlite3_result_text(ctx, azOp[pCur->eOp], -1, SQLITE_STATIC);
break;
}
case DELTAPARSEVTAB_A1: {
sqlite3_result_int(ctx, pCur->a1);
break;
}
case DELTAPARSEVTAB_A2: {
if( pCur->eOp==DELTAPARSE_OP_COPY ){
sqlite3_result_int(ctx, pCur->a2);
}else if( pCur->eOp==DELTAPARSE_OP_INSERT ){
sqlite3_result_blob(ctx, pCur->aDelta+pCur->a2, pCur->a1,
SQLITE_TRANSIENT);
}
break;
}
case DELTAPARSEVTAB_DELTA: {
sqlite3_result_blob(ctx, pCur->aDelta, pCur->nDelta, SQLITE_TRANSIENT);
break;
}
}
return SQLITE_OK;
}
/*
** Return the rowid for the current row. In this implementation, the
** rowid is the same as the output value.
*/
static int deltaparsevtabRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
deltaparsevtab_cursor *pCur = (deltaparsevtab_cursor*)cur;
*pRowid = pCur->iCursor;
return SQLITE_OK;
}
/*
** Return TRUE if the cursor has been moved off of the last
** row of output.
*/
static int deltaparsevtabEof(sqlite3_vtab_cursor *cur){
deltaparsevtab_cursor *pCur = (deltaparsevtab_cursor*)cur;
return pCur->eOp==DELTAPARSE_OP_EOF;
}
/*
** This method is called to "rewind" the deltaparsevtab_cursor object back
** to the first row of output. This method is always called at least
** once prior to any call to deltaparsevtabColumn() or deltaparsevtabRowid() or
** deltaparsevtabEof().
*/
static int deltaparsevtabFilter(
sqlite3_vtab_cursor *pVtabCursor,
int idxNum, const char *idxStr,
int argc, sqlite3_value **argv
){
deltaparsevtab_cursor *pCur = (deltaparsevtab_cursor *)pVtabCursor;
const char *a;
int i = 0;
pCur->eOp = DELTAPARSE_OP_ERROR;
if( idxNum!=1 ){
return SQLITE_OK;
}
pCur->nDelta = sqlite3_value_bytes(argv[0]);
a = (const char*)sqlite3_value_blob(argv[0]);
if( pCur->nDelta==0 || a==0 ){
return SQLITE_OK;
}
pCur->aDelta = sqlite3_malloc64( pCur->nDelta+1 );
if( pCur->aDelta==0 ){
pCur->nDelta = 0;
return SQLITE_NOMEM;
}
memcpy(pCur->aDelta, a, pCur->nDelta);
pCur->aDelta[pCur->nDelta] = 0;
a = pCur->aDelta;
pCur->eOp = DELTAPARSE_OP_SIZE;
pCur->a1 = deltaGetInt(&a, &i);
if( a[0]!='\n' ){
pCur->eOp = DELTAPARSE_OP_ERROR;
pCur->a1 = pCur->a2 = 0;
pCur->iNext = pCur->nDelta;
return SQLITE_OK;
}
a++;
pCur->iNext = (unsigned int)(a - pCur->aDelta);
return SQLITE_OK;
}
/*
** SQLite will invoke this method one or more times while planning a query
** that uses the virtual table. This routine needs to create
** a query plan for each invocation and compute an estimated cost for that
** plan.
*/
static int deltaparsevtabBestIndex(
sqlite3_vtab *tab,
sqlite3_index_info *pIdxInfo
){
int i;
for(i=0; i<pIdxInfo->nConstraint; i++){
if( pIdxInfo->aConstraint[i].iColumn != DELTAPARSEVTAB_DELTA ) continue;
if( pIdxInfo->aConstraint[i].usable==0 ) continue;
if( pIdxInfo->aConstraint[i].op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue;
pIdxInfo->aConstraintUsage[i].argvIndex = 1;
pIdxInfo->aConstraintUsage[i].omit = 1;
pIdxInfo->estimatedCost = (double)1;
pIdxInfo->estimatedRows = 10;
pIdxInfo->idxNum = 1;
return SQLITE_OK;
}
pIdxInfo->idxNum = 0;
pIdxInfo->estimatedCost = (double)0x7fffffff;
pIdxInfo->estimatedRows = 0x7fffffff;
return SQLITE_CONSTRAINT;
}
/*
** This following structure defines all the methods for the
** virtual table.
*/
static sqlite3_module deltaparsevtabModule = {
/* iVersion */ 0,
/* xCreate */ 0,
/* xConnect */ deltaparsevtabConnect,
/* xBestIndex */ deltaparsevtabBestIndex,
/* xDisconnect */ deltaparsevtabDisconnect,
/* xDestroy */ 0,
/* xOpen */ deltaparsevtabOpen,
/* xClose */ deltaparsevtabClose,
/* xFilter */ deltaparsevtabFilter,
/* xNext */ deltaparsevtabNext,
/* xEof */ deltaparsevtabEof,
/* xColumn */ deltaparsevtabColumn,
/* xRowid */ deltaparsevtabRowid,
/* xUpdate */ 0,
/* xBegin */ 0,
/* xSync */ 0,
/* xCommit */ 0,
/* xRollback */ 0,
/* xFindMethod */ 0,
/* xRename */ 0,
/* xSavepoint */ 0,
/* xRelease */ 0,
/* xRollbackTo */ 0,
/* xShadowName */ 0,
/* xIntegrity */ 0
};
/*
** Invoke this routine to register the various delta functions.
*/
int deltafunc_init(sqlite3 *db){
int rc = SQLITE_OK;
rc = sqlite3_create_function(db, "delta_create", 2, SQLITE_UTF8, 0,
deltaCreateFunc, 0, 0);
if( rc==SQLITE_OK ){
rc = sqlite3_create_function(db, "delta_apply", 2, SQLITE_UTF8, 0,
deltaApplyFunc, 0, 0);
}
if( rc==SQLITE_OK ){
rc = sqlite3_create_function(db, "delta_output_size", 1, SQLITE_UTF8, 0,
deltaOutputSizeFunc, 0, 0);
}
if( rc==SQLITE_OK ){
rc = sqlite3_create_module(db, "delta_parse", &deltaparsevtabModule, 0);
}
return rc;
}