mirror of
https://github.com/tursodatabase/libsql.git
synced 2024-12-16 18:29:20 +00:00
322 lines
9.0 KiB
C
322 lines
9.0 KiB
C
/*
|
|
** 2018-04-19
|
|
**
|
|
** 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 a table-valued function:
|
|
**
|
|
** prefixes('abcdefg')
|
|
**
|
|
** The function has a single (non-HIDDEN) column named prefix that takes
|
|
** on all prefixes of the string in its argument, including an empty string
|
|
** and the input string itself. The order of prefixes is from longest
|
|
** to shortest.
|
|
*/
|
|
#if !defined(SQLITE_CORE) || !defined(SQLITE_OMIT_VIRTUALTABLE)
|
|
#if !defined(SQLITEINT_H)
|
|
#include "sqlite3ext.h"
|
|
#endif
|
|
SQLITE_EXTENSION_INIT1
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
|
|
/* prefixes_vtab is a subclass of sqlite3_vtab which is
|
|
** underlying representation of the virtual table
|
|
*/
|
|
typedef struct prefixes_vtab prefixes_vtab;
|
|
struct prefixes_vtab {
|
|
sqlite3_vtab base; /* Base class - must be first */
|
|
/* No additional fields are necessary */
|
|
};
|
|
|
|
/* prefixes_cursor is a subclass of sqlite3_vtab_cursor which will
|
|
** serve as the underlying representation of a cursor that scans
|
|
** over rows of the result
|
|
*/
|
|
typedef struct prefixes_cursor prefixes_cursor;
|
|
struct prefixes_cursor {
|
|
sqlite3_vtab_cursor base; /* Base class - must be first */
|
|
sqlite3_int64 iRowid; /* The rowid */
|
|
char *zStr; /* Original string to be prefixed */
|
|
int nStr; /* Length of the string in bytes */
|
|
};
|
|
|
|
/*
|
|
** The prefixesConnect() method is invoked to create a new
|
|
** template virtual table.
|
|
**
|
|
** Think of this routine as the constructor for prefixes_vtab objects.
|
|
**
|
|
** All this routine needs to do is:
|
|
**
|
|
** (1) Allocate the prefixes_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 prefixesConnect(
|
|
sqlite3 *db,
|
|
void *pAux,
|
|
int argc, const char *const*argv,
|
|
sqlite3_vtab **ppVtab,
|
|
char **pzErr
|
|
){
|
|
prefixes_vtab *pNew;
|
|
int rc;
|
|
|
|
rc = sqlite3_declare_vtab(db,
|
|
"CREATE TABLE prefixes(prefix TEXT, original_string TEXT HIDDEN)"
|
|
);
|
|
if( rc==SQLITE_OK ){
|
|
pNew = sqlite3_malloc( sizeof(*pNew) );
|
|
*ppVtab = (sqlite3_vtab*)pNew;
|
|
if( pNew==0 ) return SQLITE_NOMEM;
|
|
memset(pNew, 0, sizeof(*pNew));
|
|
sqlite3_vtab_config(db, SQLITE_VTAB_INNOCUOUS);
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
** This method is the destructor for prefixes_vtab objects.
|
|
*/
|
|
static int prefixesDisconnect(sqlite3_vtab *pVtab){
|
|
prefixes_vtab *p = (prefixes_vtab*)pVtab;
|
|
sqlite3_free(p);
|
|
return SQLITE_OK;
|
|
}
|
|
|
|
/*
|
|
** Constructor for a new prefixes_cursor object.
|
|
*/
|
|
static int prefixesOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
|
|
prefixes_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 prefixes_cursor.
|
|
*/
|
|
static int prefixesClose(sqlite3_vtab_cursor *cur){
|
|
prefixes_cursor *pCur = (prefixes_cursor*)cur;
|
|
sqlite3_free(pCur->zStr);
|
|
sqlite3_free(pCur);
|
|
return SQLITE_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
** Advance a prefixes_cursor to its next row of output.
|
|
*/
|
|
static int prefixesNext(sqlite3_vtab_cursor *cur){
|
|
prefixes_cursor *pCur = (prefixes_cursor*)cur;
|
|
pCur->iRowid++;
|
|
return SQLITE_OK;
|
|
}
|
|
|
|
/*
|
|
** Return values of columns for the row at which the prefixes_cursor
|
|
** is currently pointing.
|
|
*/
|
|
static int prefixesColumn(
|
|
sqlite3_vtab_cursor *cur, /* The cursor */
|
|
sqlite3_context *ctx, /* First argument to sqlite3_result_...() */
|
|
int i /* Which column to return */
|
|
){
|
|
prefixes_cursor *pCur = (prefixes_cursor*)cur;
|
|
switch( i ){
|
|
case 0:
|
|
sqlite3_result_text(ctx, pCur->zStr, pCur->nStr - (int)pCur->iRowid,
|
|
0);
|
|
break;
|
|
default:
|
|
sqlite3_result_text(ctx, pCur->zStr, pCur->nStr, 0);
|
|
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 prefixesRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
|
|
prefixes_cursor *pCur = (prefixes_cursor*)cur;
|
|
*pRowid = pCur->iRowid;
|
|
return SQLITE_OK;
|
|
}
|
|
|
|
/*
|
|
** Return TRUE if the cursor has been moved off of the last
|
|
** row of output.
|
|
*/
|
|
static int prefixesEof(sqlite3_vtab_cursor *cur){
|
|
prefixes_cursor *pCur = (prefixes_cursor*)cur;
|
|
return pCur->iRowid>pCur->nStr;
|
|
}
|
|
|
|
/*
|
|
** This method is called to "rewind" the prefixes_cursor object back
|
|
** to the first row of output. This method is always called at least
|
|
** once prior to any call to prefixesColumn() or prefixesRowid() or
|
|
** prefixesEof().
|
|
*/
|
|
static int prefixesFilter(
|
|
sqlite3_vtab_cursor *pVtabCursor,
|
|
int idxNum, const char *idxStr,
|
|
int argc, sqlite3_value **argv
|
|
){
|
|
prefixes_cursor *pCur = (prefixes_cursor *)pVtabCursor;
|
|
sqlite3_free(pCur->zStr);
|
|
if( argc>0 ){
|
|
pCur->zStr = sqlite3_mprintf("%s", sqlite3_value_text(argv[0]));
|
|
pCur->nStr = pCur->zStr ? (int)strlen(pCur->zStr) : 0;
|
|
}else{
|
|
pCur->zStr = 0;
|
|
pCur->nStr = 0;
|
|
}
|
|
pCur->iRowid = 0;
|
|
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 prefixesBestIndex(
|
|
sqlite3_vtab *tab,
|
|
sqlite3_index_info *pIdxInfo
|
|
){
|
|
/* Search for a usable equality constraint against column 1
|
|
** (original_string) and use it if at all possible */
|
|
int i;
|
|
const struct sqlite3_index_constraint *p;
|
|
|
|
for(i=0, p=pIdxInfo->aConstraint; i<pIdxInfo->nConstraint; i++, p++){
|
|
if( p->iColumn!=1 ) continue;
|
|
if( p->op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue;
|
|
if( !p->usable ) continue;
|
|
pIdxInfo->aConstraintUsage[i].argvIndex = 1;
|
|
pIdxInfo->aConstraintUsage[i].omit = 1;
|
|
pIdxInfo->estimatedCost = (double)10;
|
|
pIdxInfo->estimatedRows = 10;
|
|
return SQLITE_OK;
|
|
}
|
|
pIdxInfo->estimatedCost = (double)1000000000;
|
|
pIdxInfo->estimatedRows = 1000000000;
|
|
return SQLITE_OK;
|
|
}
|
|
|
|
/*
|
|
** This following structure defines all the methods for the
|
|
** virtual table.
|
|
*/
|
|
static sqlite3_module prefixesModule = {
|
|
/* iVersion */ 0,
|
|
/* xCreate */ 0,
|
|
/* xConnect */ prefixesConnect,
|
|
/* xBestIndex */ prefixesBestIndex,
|
|
/* xDisconnect */ prefixesDisconnect,
|
|
/* xDestroy */ 0,
|
|
/* xOpen */ prefixesOpen,
|
|
/* xClose */ prefixesClose,
|
|
/* xFilter */ prefixesFilter,
|
|
/* xNext */ prefixesNext,
|
|
/* xEof */ prefixesEof,
|
|
/* xColumn */ prefixesColumn,
|
|
/* xRowid */ prefixesRowid,
|
|
/* xUpdate */ 0,
|
|
/* xBegin */ 0,
|
|
/* xSync */ 0,
|
|
/* xCommit */ 0,
|
|
/* xRollback */ 0,
|
|
/* xFindMethod */ 0,
|
|
/* xRename */ 0,
|
|
/* xSavepoint */ 0,
|
|
/* xRelease */ 0,
|
|
/* xRollbackTo */ 0,
|
|
/* xShadowName */ 0,
|
|
/* xIntegrity */ 0
|
|
};
|
|
|
|
/*
|
|
** This is a copy of the SQLITE_SKIP_UTF8(zIn) macro in sqliteInt.h.
|
|
**
|
|
** Assuming zIn points to the first byte of a UTF-8 character,
|
|
** advance zIn to point to the first byte of the next UTF-8 character.
|
|
*/
|
|
#define PREFIX_SKIP_UTF8(zIn) { \
|
|
if( (*(zIn++))>=0xc0 ){ \
|
|
while( (*zIn & 0xc0)==0x80 ){ zIn++; } \
|
|
} \
|
|
}
|
|
|
|
/*
|
|
** Implementation of function prefix_length(). This function accepts two
|
|
** strings as arguments and returns the length in characters (not bytes),
|
|
** of the longest prefix shared by the two strings. For example:
|
|
**
|
|
** prefix_length('abcdxxx', 'abcyy') == 3
|
|
** prefix_length('abcdxxx', 'bcyyy') == 0
|
|
** prefix_length('abcdxxx', 'ab') == 2
|
|
** prefix_length('ab', 'abcd') == 2
|
|
**
|
|
** This function assumes the input is well-formed utf-8. If it is not,
|
|
** it is possible for this function to return -1.
|
|
*/
|
|
static void prefixLengthFunc(
|
|
sqlite3_context *ctx,
|
|
int nVal,
|
|
sqlite3_value **apVal
|
|
){
|
|
int nByte; /* Number of bytes to compare */
|
|
int nRet = 0; /* Return value */
|
|
const unsigned char *zL = sqlite3_value_text(apVal[0]);
|
|
const unsigned char *zR = sqlite3_value_text(apVal[1]);
|
|
int nL = sqlite3_value_bytes(apVal[0]);
|
|
int nR = sqlite3_value_bytes(apVal[1]);
|
|
int i;
|
|
|
|
nByte = (nL > nR ? nL : nR);
|
|
for(i=0; i<nByte; i++){
|
|
if( zL[i]!=zR[i] ) break;
|
|
if( (zL[i] & 0xC0)!=0x80 ) nRet++;
|
|
}
|
|
|
|
if( (zL[i] & 0xC0)==0x80 ) nRet--;
|
|
sqlite3_result_int(ctx, nRet);
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
__declspec(dllexport)
|
|
#endif
|
|
int sqlite3_prefixes_init(
|
|
sqlite3 *db,
|
|
char **pzErrMsg,
|
|
const sqlite3_api_routines *pApi
|
|
){
|
|
int rc = SQLITE_OK;
|
|
SQLITE_EXTENSION_INIT2(pApi);
|
|
rc = sqlite3_create_module(db, "prefixes", &prefixesModule, 0);
|
|
if( rc==SQLITE_OK ){
|
|
rc = sqlite3_create_function(
|
|
db, "prefix_length", 2, SQLITE_UTF8, 0, prefixLengthFunc, 0, 0
|
|
);
|
|
}
|
|
return rc;
|
|
}
|
|
#endif /* !defined(SQLITE_CORE) || !defined(SQLITE_OMIT_VIRTUALTABLE) */
|