mirror of
https://github.com/sqlite/sqlite.git
synced 2025-08-01 06:27:03 +03:00
Merge latest trunk changes with this branch.
FossilOrigin-Name: 4b3651677e7132c4c45605bc1f216fc08ef31198
This commit is contained in:
@ -3116,6 +3116,7 @@ static int fts3FilterMethod(
|
||||
/* In case the cursor has been used before, clear it now. */
|
||||
sqlite3_finalize(pCsr->pStmt);
|
||||
sqlite3_free(pCsr->aDoclist);
|
||||
sqlite3_free(pCsr->aMatchinfo);
|
||||
sqlite3Fts3ExprFree(pCsr->pExpr);
|
||||
memset(&pCursor[1], 0, sizeof(Fts3Cursor)-sizeof(sqlite3_vtab_cursor));
|
||||
|
||||
@ -3750,7 +3751,7 @@ static void hashDestroy(void *p){
|
||||
*/
|
||||
void sqlite3Fts3SimpleTokenizerModule(sqlite3_tokenizer_module const**ppModule);
|
||||
void sqlite3Fts3PorterTokenizerModule(sqlite3_tokenizer_module const**ppModule);
|
||||
#ifdef SQLITE_ENABLE_FTS4_UNICODE61
|
||||
#ifndef SQLITE_DISABLE_FTS3_UNICODE
|
||||
void sqlite3Fts3UnicodeTokenizer(sqlite3_tokenizer_module const**ppModule);
|
||||
#endif
|
||||
#ifdef SQLITE_ENABLE_ICU
|
||||
@ -3768,7 +3769,7 @@ int sqlite3Fts3Init(sqlite3 *db){
|
||||
Fts3Hash *pHash = 0;
|
||||
const sqlite3_tokenizer_module *pSimple = 0;
|
||||
const sqlite3_tokenizer_module *pPorter = 0;
|
||||
#ifdef SQLITE_ENABLE_FTS4_UNICODE61
|
||||
#ifndef SQLITE_DISABLE_FTS3_UNICODE
|
||||
const sqlite3_tokenizer_module *pUnicode = 0;
|
||||
#endif
|
||||
|
||||
@ -3777,7 +3778,7 @@ int sqlite3Fts3Init(sqlite3 *db){
|
||||
sqlite3Fts3IcuTokenizerModule(&pIcu);
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_ENABLE_FTS4_UNICODE61
|
||||
#ifndef SQLITE_DISABLE_FTS3_UNICODE
|
||||
sqlite3Fts3UnicodeTokenizer(&pUnicode);
|
||||
#endif
|
||||
|
||||
@ -3805,7 +3806,7 @@ int sqlite3Fts3Init(sqlite3 *db){
|
||||
if( sqlite3Fts3HashInsert(pHash, "simple", 7, (void *)pSimple)
|
||||
|| sqlite3Fts3HashInsert(pHash, "porter", 7, (void *)pPorter)
|
||||
|
||||
#ifdef SQLITE_ENABLE_FTS4_UNICODE61
|
||||
#ifndef SQLITE_DISABLE_FTS3_UNICODE
|
||||
|| sqlite3Fts3HashInsert(pHash, "unicode61", 10, (void *)pUnicode)
|
||||
#endif
|
||||
#ifdef SQLITE_ENABLE_ICU
|
||||
@ -4426,7 +4427,7 @@ static int fts3EvalIncrPhraseNext(
|
||||
bMaxSet = 1;
|
||||
}
|
||||
}
|
||||
assert( rc!=SQLITE_OK || a[p->nToken-1].bIgnore==0 );
|
||||
assert( rc!=SQLITE_OK || (p->nToken>=1 && a[p->nToken-1].bIgnore==0) );
|
||||
assert( rc!=SQLITE_OK || bMaxSet );
|
||||
|
||||
/* Keep advancing iterators until they all point to the same document */
|
||||
|
@ -585,7 +585,7 @@ int sqlite3Fts3MsrIncrRestart(Fts3MultiSegReader *pCsr);
|
||||
int sqlite3Fts3InitTok(sqlite3*, Fts3Hash *);
|
||||
|
||||
/* fts3_unicode2.c (functions generated by parsing unicode text files) */
|
||||
#ifdef SQLITE_ENABLE_FTS4_UNICODE61
|
||||
#ifndef SQLITE_DISABLE_FTS3_UNICODE
|
||||
int sqlite3FtsUnicodeFold(int, int);
|
||||
int sqlite3FtsUnicodeIsalnum(int);
|
||||
int sqlite3FtsUnicodeIsdiacritic(int);
|
||||
|
@ -190,7 +190,7 @@ static int getNextToken(
|
||||
/* Set variable i to the maximum number of bytes of input to tokenize. */
|
||||
for(i=0; i<n; i++){
|
||||
if( sqlite3_fts3_enable_parentheses && (z[i]=='(' || z[i]==')') ) break;
|
||||
if( z[i]=='*' || z[i]=='"' ) break;
|
||||
if( z[i]=='"' ) break;
|
||||
}
|
||||
|
||||
*pnConsumed = i;
|
||||
|
@ -183,7 +183,7 @@ static int isVowel(const char *z){
|
||||
** by a consonant.
|
||||
**
|
||||
** In this routine z[] is in reverse order. So we are really looking
|
||||
** for an instance of of a consonant followed by a vowel.
|
||||
** for an instance of a consonant followed by a vowel.
|
||||
*/
|
||||
static int m_gt_0(const char *z){
|
||||
while( isVowel(z) ){ z++; }
|
||||
|
@ -13,7 +13,7 @@
|
||||
** Implementation of the "unicode" full-text-search tokenizer.
|
||||
*/
|
||||
|
||||
#ifdef SQLITE_ENABLE_FTS4_UNICODE61
|
||||
#ifndef SQLITE_DISABLE_FTS3_UNICODE
|
||||
|
||||
#include "fts3Int.h"
|
||||
#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3)
|
||||
@ -231,7 +231,7 @@ static int unicodeCreate(
|
||||
|
||||
for(i=0; rc==SQLITE_OK && i<nArg; i++){
|
||||
const char *z = azArg[i];
|
||||
int n = strlen(z);
|
||||
int n = (int)strlen(z);
|
||||
|
||||
if( n==19 && memcmp("remove_diacritics=1", z, 19)==0 ){
|
||||
pNew->bRemoveDiacritic = 1;
|
||||
@ -318,7 +318,7 @@ static int unicodeNext(
|
||||
){
|
||||
unicode_cursor *pCsr = (unicode_cursor *)pC;
|
||||
unicode_tokenizer *p = ((unicode_tokenizer *)pCsr->base.pTokenizer);
|
||||
int iCode;
|
||||
int iCode = 0;
|
||||
char *zOut;
|
||||
const unsigned char *z = &pCsr->aInput[pCsr->iOff];
|
||||
const unsigned char *zStart = z;
|
||||
@ -363,11 +363,11 @@ static int unicodeNext(
|
||||
);
|
||||
|
||||
/* Set the output variables and return. */
|
||||
pCsr->iOff = (z - pCsr->aInput);
|
||||
pCsr->iOff = (int)(z - pCsr->aInput);
|
||||
*paToken = pCsr->zToken;
|
||||
*pnToken = zOut - pCsr->zToken;
|
||||
*piStart = (zStart - pCsr->aInput);
|
||||
*piEnd = (zEnd - pCsr->aInput);
|
||||
*pnToken = (int)(zOut - pCsr->zToken);
|
||||
*piStart = (int)(zStart - pCsr->aInput);
|
||||
*piEnd = (int)(zEnd - pCsr->aInput);
|
||||
*piPos = pCsr->iToken++;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
@ -390,4 +390,4 @@ void sqlite3Fts3UnicodeTokenizer(sqlite3_tokenizer_module const **ppModule){
|
||||
}
|
||||
|
||||
#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) */
|
||||
#endif /* ifndef SQLITE_ENABLE_FTS4_UNICODE61 */
|
||||
#endif /* ifndef SQLITE_DISABLE_FTS3_UNICODE */
|
||||
|
@ -15,7 +15,7 @@
|
||||
** DO NOT EDIT THIS MACHINE GENERATED FILE.
|
||||
*/
|
||||
|
||||
#if defined(SQLITE_ENABLE_FTS4_UNICODE61)
|
||||
#ifndef SQLITE_DISABLE_FTS3_UNICODE
|
||||
#if defined(SQLITE_ENABLE_FTS3) || defined(SQLITE_ENABLE_FTS4)
|
||||
|
||||
#include <assert.h>
|
||||
@ -39,7 +39,7 @@ int sqlite3FtsUnicodeIsalnum(int c){
|
||||
** C. It is not possible to represent a range larger than 1023 codepoints
|
||||
** using this format.
|
||||
*/
|
||||
const static unsigned int aEntry[] = {
|
||||
static const unsigned int aEntry[] = {
|
||||
0x00000030, 0x0000E807, 0x00016C06, 0x0001EC2F, 0x0002AC07,
|
||||
0x0002D001, 0x0002D803, 0x0002EC01, 0x0002FC01, 0x00035C01,
|
||||
0x0003DC01, 0x000B0804, 0x000B480E, 0x000B9407, 0x000BB401,
|
||||
@ -131,7 +131,7 @@ int sqlite3FtsUnicodeIsalnum(int c){
|
||||
return ( (aAscii[c >> 5] & (1 << (c & 0x001F)))==0 );
|
||||
}else if( c<(1<<22) ){
|
||||
unsigned int key = (((unsigned int)c)<<10) | 0x000003FF;
|
||||
int iRes;
|
||||
int iRes = 0;
|
||||
int iHi = sizeof(aEntry)/sizeof(aEntry[0]) - 1;
|
||||
int iLo = 0;
|
||||
while( iHi>=iLo ){
|
||||
@ -202,7 +202,7 @@ static int remove_diacritic(int c){
|
||||
}
|
||||
assert( key>=aDia[iRes] );
|
||||
return ((c > (aDia[iRes]>>3) + (aDia[iRes]&0x07)) ? c : (int)aChar[iRes]);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
@ -362,4 +362,4 @@ int sqlite3FtsUnicodeFold(int c, int bRemoveDiacritic){
|
||||
return ret;
|
||||
}
|
||||
#endif /* defined(SQLITE_ENABLE_FTS3) || defined(SQLITE_ENABLE_FTS4) */
|
||||
#endif /* !defined(SQLITE_ENABLE_FTS4_UNICODE61) */
|
||||
#endif /* !defined(SQLITE_DISABLE_FTS3_UNICODE) */
|
||||
|
@ -376,7 +376,7 @@ static void showSegmentStats(sqlite3 *db, const char *zTab){
|
||||
sqlite3_finalize(pStmt);
|
||||
nLeaf = nSeg - nIdx;
|
||||
printf("Leaf segments larger than %5d bytes.... %9d %5.2f%%\n",
|
||||
pgsz-45, n, n*100.0/nLeaf);
|
||||
pgsz-45, n, nLeaf>0 ? n*100.0/nLeaf : 0.0);
|
||||
|
||||
pStmt = prepare(db, "SELECT max(level%%1024) FROM '%q_segdir'", zTab);
|
||||
mxLevel = 0;
|
||||
@ -554,7 +554,7 @@ static void decodeSegment(
|
||||
sqlite3_int64 n;
|
||||
sqlite3_int64 iDocsz;
|
||||
int iHeight;
|
||||
int i = 0;
|
||||
sqlite3_int64 i = 0;
|
||||
int cnt = 0;
|
||||
char zTerm[1000];
|
||||
|
||||
@ -576,12 +576,12 @@ static void decodeSegment(
|
||||
fprintf(stderr, "term to long\n");
|
||||
exit(1);
|
||||
}
|
||||
memcpy(zTerm+iPrefix, aData+i, nTerm);
|
||||
memcpy(zTerm+iPrefix, aData+i, (size_t)nTerm);
|
||||
zTerm[iPrefix+nTerm] = 0;
|
||||
i += nTerm;
|
||||
if( iHeight==0 ){
|
||||
i += getVarint(aData+i, &iDocsz);
|
||||
printf("term: %-25s doclist %7lld bytes offset %d\n", zTerm, iDocsz, i);
|
||||
printf("term: %-25s doclist %7lld bytes offset %lld\n", zTerm, iDocsz, i);
|
||||
i += iDocsz;
|
||||
}else{
|
||||
printf("term: %-25s child %lld\n", zTerm, ++iChild);
|
||||
@ -749,18 +749,19 @@ static void decodeDoclist(
|
||||
*/
|
||||
static void showDoclist(sqlite3 *db, const char *zTab){
|
||||
const unsigned char *aData;
|
||||
sqlite3_int64 offset, nData;
|
||||
sqlite3_int64 offset;
|
||||
int nData;
|
||||
sqlite3_stmt *pStmt;
|
||||
|
||||
offset = atoi64(azExtra[1]);
|
||||
nData = atoi64(azExtra[2]);
|
||||
nData = atoi(azExtra[2]);
|
||||
pStmt = prepareToGetSegment(db, zTab, azExtra[0]);
|
||||
if( sqlite3_step(pStmt)!=SQLITE_ROW ){
|
||||
sqlite3_finalize(pStmt);
|
||||
return;
|
||||
}
|
||||
aData = sqlite3_column_blob(pStmt, 0);
|
||||
printf("Doclist at %s offset %lld of size %lld bytes:\n",
|
||||
printf("Doclist at %s offset %lld of size %d bytes:\n",
|
||||
azExtra[0], offset, nData);
|
||||
if( findOption("raw", 0, 0)!=0 ){
|
||||
printBlob(aData+offset, nData);
|
||||
|
@ -160,7 +160,7 @@ proc print_rd {map} {
|
||||
}
|
||||
assert( key>=aDia[iRes] );
|
||||
return ((c > (aDia[iRes]>>3) + (aDia[iRes]&0x07)) ? c : (int)aChar[iRes]);}
|
||||
puts "\};"
|
||||
puts "\}"
|
||||
}
|
||||
|
||||
proc print_isdiacritic {zFunc map} {
|
||||
@ -298,7 +298,7 @@ proc an_print_range_array {lRange} {
|
||||
** using this format.
|
||||
*/
|
||||
}]
|
||||
puts -nonewline " const static unsigned int aEntry\[\] = \{"
|
||||
puts -nonewline " static const unsigned int aEntry\[\] = \{"
|
||||
set i 0
|
||||
foreach range $lRange {
|
||||
foreach {iFirst nRange} $range {}
|
||||
@ -349,7 +349,7 @@ proc print_isalnum {zFunc lRange} {
|
||||
return ( (aAscii[c >> 5] & (1 << (c & 0x001F)))==0 );
|
||||
}else if( c<(1<<22) ){
|
||||
unsigned int key = (((unsigned int)c)<<10) | 0x000003FF;
|
||||
int iRes;
|
||||
int iRes = 0;
|
||||
int iHi = sizeof(aEntry)/sizeof(aEntry[0]) - 1;
|
||||
int iLo = 0;
|
||||
while( iHi>=iLo ){
|
||||
@ -735,7 +735,7 @@ proc print_fileheader {} {
|
||||
if {$::generate_fts5_code} {
|
||||
puts "#if defined(SQLITE_ENABLE_FTS5)"
|
||||
} else {
|
||||
puts "#if defined(SQLITE_ENABLE_FTS4_UNICODE61)"
|
||||
puts "#ifndef SQLITE_DISABLE_FTS3_UNICODE"
|
||||
puts "#if defined(SQLITE_ENABLE_FTS3) || defined(SQLITE_ENABLE_FTS4)"
|
||||
}
|
||||
puts ""
|
||||
@ -831,5 +831,5 @@ if {$generate_fts5_code} {
|
||||
puts "#endif /* defined(SQLITE_ENABLE_FTS5) */"
|
||||
} else {
|
||||
puts "#endif /* defined(SQLITE_ENABLE_FTS3) || defined(SQLITE_ENABLE_FTS4) */"
|
||||
puts "#endif /* !defined(SQLITE_ENABLE_FTS4_UNICODE61) */"
|
||||
puts "#endif /* !defined(SQLITE_DISABLE_FTS3_UNICODE) */"
|
||||
}
|
||||
|
122
ext/misc/eval.c
Normal file
122
ext/misc/eval.c
Normal file
@ -0,0 +1,122 @@
|
||||
/*
|
||||
** 2014-11-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.
|
||||
**
|
||||
******************************************************************************
|
||||
**
|
||||
** This SQLite extension implements SQL function eval() which runs
|
||||
** SQL statements recursively.
|
||||
*/
|
||||
#include "sqlite3ext.h"
|
||||
SQLITE_EXTENSION_INIT1
|
||||
#include <string.h>
|
||||
|
||||
/*
|
||||
** Structure used to accumulate the output
|
||||
*/
|
||||
struct EvalResult {
|
||||
char *z; /* Accumulated output */
|
||||
const char *zSep; /* Separator */
|
||||
int szSep; /* Size of the separator string */
|
||||
sqlite3_int64 nAlloc; /* Number of bytes allocated for z[] */
|
||||
sqlite3_int64 nUsed; /* Number of bytes of z[] actually used */
|
||||
};
|
||||
|
||||
/*
|
||||
** Callback from sqlite_exec() for the eval() function.
|
||||
*/
|
||||
static int callback(void *pCtx, int argc, char **argv, char **colnames){
|
||||
struct EvalResult *p = (struct EvalResult*)pCtx;
|
||||
int i;
|
||||
for(i=0; i<argc; i++){
|
||||
const char *z = argv[i] ? argv[i] : "";
|
||||
size_t sz = strlen(z);
|
||||
if( (sqlite3_int64)sz+p->nUsed+p->szSep+1 > p->nAlloc ){
|
||||
char *zNew;
|
||||
p->nAlloc = p->nAlloc*2 + sz + p->szSep + 1;
|
||||
/* Using sqlite3_realloc64() would be better, but it is a recent
|
||||
** addition and will cause a segfault if loaded by an older version
|
||||
** of SQLite. */
|
||||
zNew = p->nAlloc<=0x7fffffff ? sqlite3_realloc(p->z, (int)p->nAlloc) : 0;
|
||||
if( zNew==0 ){
|
||||
sqlite3_free(p->z);
|
||||
memset(p, 0, sizeof(*p));
|
||||
return 1;
|
||||
}
|
||||
p->z = zNew;
|
||||
}
|
||||
if( p->nUsed>0 ){
|
||||
memcpy(&p->z[p->nUsed], p->zSep, p->szSep);
|
||||
p->nUsed += p->szSep;
|
||||
}
|
||||
memcpy(&p->z[p->nUsed], z, sz);
|
||||
p->nUsed += sz;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Implementation of the eval(X) and eval(X,Y) SQL functions.
|
||||
**
|
||||
** Evaluate the SQL text in X. Return the results, using string
|
||||
** Y as the separator. If Y is omitted, use a single space character.
|
||||
*/
|
||||
static void sqlEvalFunc(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
const char *zSql;
|
||||
sqlite3 *db;
|
||||
char *zErr = 0;
|
||||
int rc;
|
||||
struct EvalResult x;
|
||||
|
||||
memset(&x, 0, sizeof(x));
|
||||
x.zSep = " ";
|
||||
zSql = (const char*)sqlite3_value_text(argv[0]);
|
||||
if( zSql==0 ) return;
|
||||
if( argc>1 ){
|
||||
x.zSep = (const char*)sqlite3_value_text(argv[1]);
|
||||
if( x.zSep==0 ) return;
|
||||
}
|
||||
x.szSep = (int)strlen(x.zSep);
|
||||
db = sqlite3_context_db_handle(context);
|
||||
rc = sqlite3_exec(db, zSql, callback, &x, &zErr);
|
||||
if( rc!=SQLITE_OK ){
|
||||
sqlite3_result_error(context, zErr, -1);
|
||||
sqlite3_free(zErr);
|
||||
}else if( x.zSep==0 ){
|
||||
sqlite3_result_error_nomem(context);
|
||||
sqlite3_free(x.z);
|
||||
}else{
|
||||
sqlite3_result_text(context, x.z, (int)x.nUsed, sqlite3_free);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
__declspec(dllexport)
|
||||
#endif
|
||||
int sqlite3_eval_init(
|
||||
sqlite3 *db,
|
||||
char **pzErrMsg,
|
||||
const sqlite3_api_routines *pApi
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
SQLITE_EXTENSION_INIT2(pApi);
|
||||
(void)pzErrMsg; /* Unused parameter */
|
||||
rc = sqlite3_create_function(db, "eval", 1, SQLITE_UTF8, 0,
|
||||
sqlEvalFunc, 0, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_create_function(db, "eval", 2, SQLITE_UTF8, 0,
|
||||
sqlEvalFunc, 0, 0);
|
||||
}
|
||||
return rc;
|
||||
}
|
@ -61,7 +61,6 @@ static void writefileFunc(
|
||||
){
|
||||
FILE *out;
|
||||
const char *z;
|
||||
int n;
|
||||
sqlite3_int64 rc;
|
||||
const char *zFile;
|
||||
|
||||
@ -71,11 +70,9 @@ static void writefileFunc(
|
||||
if( out==0 ) return;
|
||||
z = (const char*)sqlite3_value_blob(argv[1]);
|
||||
if( z==0 ){
|
||||
n = 0;
|
||||
rc = 0;
|
||||
}else{
|
||||
n = sqlite3_value_bytes(argv[1]);
|
||||
rc = fwrite(z, 1, n, out);
|
||||
rc = fwrite(z, 1, sqlite3_value_bytes(argv[1]), out);
|
||||
}
|
||||
fclose(out);
|
||||
sqlite3_result_int64(context, rc);
|
||||
|
103
ext/misc/showauth.c
Normal file
103
ext/misc/showauth.c
Normal file
@ -0,0 +1,103 @@
|
||||
/*
|
||||
** 2014-09-21
|
||||
**
|
||||
** 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 SQLite extension adds a debug "authorizer" callback to the database
|
||||
** connection. The callback merely writes the authorization request to
|
||||
** standard output and returns SQLITE_OK.
|
||||
**
|
||||
** This extension can be used (for example) in the command-line shell to
|
||||
** trace the operation of the authorizer.
|
||||
*/
|
||||
#include "sqlite3ext.h"
|
||||
SQLITE_EXTENSION_INIT1
|
||||
#include <stdio.h>
|
||||
|
||||
/*
|
||||
** Display the authorization request
|
||||
*/
|
||||
static int authCallback(
|
||||
void *pClientData,
|
||||
int op,
|
||||
const char *z1,
|
||||
const char *z2,
|
||||
const char *z3,
|
||||
const char *z4
|
||||
){
|
||||
const char *zOp;
|
||||
char zOpSpace[50];
|
||||
switch( op ){
|
||||
case SQLITE_CREATE_INDEX: zOp = "CREATE_INDEX"; break;
|
||||
case SQLITE_CREATE_TABLE: zOp = "CREATE_TABLE"; break;
|
||||
case SQLITE_CREATE_TEMP_INDEX: zOp = "CREATE_TEMP_INDEX"; break;
|
||||
case SQLITE_CREATE_TEMP_TABLE: zOp = "CREATE_TEMP_TABLE"; break;
|
||||
case SQLITE_CREATE_TEMP_TRIGGER: zOp = "CREATE_TEMP_TRIGGER"; break;
|
||||
case SQLITE_CREATE_TEMP_VIEW: zOp = "CREATE_TEMP_VIEW"; break;
|
||||
case SQLITE_CREATE_TRIGGER: zOp = "CREATE_TRIGGER"; break;
|
||||
case SQLITE_CREATE_VIEW: zOp = "CREATE_VIEW"; break;
|
||||
case SQLITE_DELETE: zOp = "DELETE"; break;
|
||||
case SQLITE_DROP_INDEX: zOp = "DROP_INDEX"; break;
|
||||
case SQLITE_DROP_TABLE: zOp = "DROP_TABLE"; break;
|
||||
case SQLITE_DROP_TEMP_INDEX: zOp = "DROP_TEMP_INDEX"; break;
|
||||
case SQLITE_DROP_TEMP_TABLE: zOp = "DROP_TEMP_TABLE"; break;
|
||||
case SQLITE_DROP_TEMP_TRIGGER: zOp = "DROP_TEMP_TRIGGER"; break;
|
||||
case SQLITE_DROP_TEMP_VIEW: zOp = "DROP_TEMP_VIEW"; break;
|
||||
case SQLITE_DROP_TRIGGER: zOp = "DROP_TRIGGER"; break;
|
||||
case SQLITE_DROP_VIEW: zOp = "DROP_VIEW"; break;
|
||||
case SQLITE_INSERT: zOp = "INSERT"; break;
|
||||
case SQLITE_PRAGMA: zOp = "PRAGMA"; break;
|
||||
case SQLITE_READ: zOp = "READ"; break;
|
||||
case SQLITE_SELECT: zOp = "SELECT"; break;
|
||||
case SQLITE_TRANSACTION: zOp = "TRANSACTION"; break;
|
||||
case SQLITE_UPDATE: zOp = "UPDATE"; break;
|
||||
case SQLITE_ATTACH: zOp = "ATTACH"; break;
|
||||
case SQLITE_DETACH: zOp = "DETACH"; break;
|
||||
case SQLITE_ALTER_TABLE: zOp = "ALTER_TABLE"; break;
|
||||
case SQLITE_REINDEX: zOp = "REINDEX"; break;
|
||||
case SQLITE_ANALYZE: zOp = "ANALYZE"; break;
|
||||
case SQLITE_CREATE_VTABLE: zOp = "CREATE_VTABLE"; break;
|
||||
case SQLITE_DROP_VTABLE: zOp = "DROP_VTABLE"; break;
|
||||
case SQLITE_FUNCTION: zOp = "FUNCTION"; break;
|
||||
case SQLITE_SAVEPOINT: zOp = "SAVEPOINT"; break;
|
||||
case SQLITE_COPY: zOp = "COPY"; break;
|
||||
case SQLITE_RECURSIVE: zOp = "RECURSIVE"; break;
|
||||
|
||||
|
||||
default: {
|
||||
sqlite3_snprintf(sizeof(zOpSpace), zOpSpace, "%d", op);
|
||||
zOp = zOpSpace;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( z1==0 ) z1 = "NULL";
|
||||
if( z2==0 ) z2 = "NULL";
|
||||
if( z3==0 ) z3 = "NULL";
|
||||
if( z4==0 ) z4 = "NULL";
|
||||
printf("AUTH: %s,%s,%s,%s,%s\n", zOp, z1, z2, z3, z4);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
__declspec(dllexport)
|
||||
#endif
|
||||
int sqlite3_showauth_init(
|
||||
sqlite3 *db,
|
||||
char **pzErrMsg,
|
||||
const sqlite3_api_routines *pApi
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
SQLITE_EXTENSION_INIT2(pApi);
|
||||
(void)pzErrMsg; /* Unused parameter */
|
||||
rc = sqlite3_set_authorizer(db, authCallback, 0);
|
||||
return rc;
|
||||
}
|
@ -1893,7 +1893,7 @@ static int spellfix1Init(
|
||||
char **pzErr
|
||||
){
|
||||
spellfix1_vtab *pNew = 0;
|
||||
const char *zModule = argv[0];
|
||||
/* const char *zModule = argv[0]; // not used */
|
||||
const char *zDbName = argv[1];
|
||||
const char *zTableName = argv[2];
|
||||
int nDbName;
|
||||
@ -1947,7 +1947,7 @@ static int spellfix1Init(
|
||||
spellfix1DbExec(&rc, db,
|
||||
"CREATE INDEX IF NOT EXISTS \"%w\".\"%w_vocab_index_langid_k2\" "
|
||||
"ON \"%w_vocab\"(langid,k2);",
|
||||
zDbName, zModule, zTableName
|
||||
zDbName, zTableName, zTableName
|
||||
);
|
||||
}
|
||||
for(i=3; rc==SQLITE_OK && i<argc; i++){
|
||||
@ -2736,12 +2736,22 @@ static int spellfix1Update(
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
if( sqlite3_value_type(argv[0])==SQLITE_NULL ){
|
||||
spellfix1DbExec(&rc, db,
|
||||
"INSERT INTO \"%w\".\"%w_vocab\"(rank,langid,word,k1,k2) "
|
||||
"VALUES(%d,%d,%Q,%Q,%Q)",
|
||||
p->zDbName, p->zTableName,
|
||||
iRank, iLang, zWord, zK1, zK2
|
||||
);
|
||||
if( sqlite3_value_type(argv[1])==SQLITE_NULL ){
|
||||
spellfix1DbExec(&rc, db,
|
||||
"INSERT INTO \"%w\".\"%w_vocab\"(rank,langid,word,k1,k2) "
|
||||
"VALUES(%d,%d,%Q,%Q,%Q)",
|
||||
p->zDbName, p->zTableName,
|
||||
iRank, iLang, zWord, zK1, zK2
|
||||
);
|
||||
}else{
|
||||
newRowid = sqlite3_value_int64(argv[1]);
|
||||
spellfix1DbExec(&rc, db,
|
||||
"INSERT INTO \"%w\".\"%w_vocab\"(id,rank,langid,word,k1,k2) "
|
||||
"VALUES(%lld,%d,%d,%Q,%Q,%Q)",
|
||||
p->zDbName, p->zTableName,
|
||||
newRowid, iRank, iLang, zWord, zK1, zK2
|
||||
);
|
||||
}
|
||||
*pRowid = sqlite3_last_insert_rowid(db);
|
||||
}else{
|
||||
rowid = sqlite3_value_int64(argv[0]);
|
||||
|
@ -1533,9 +1533,13 @@ static int rtreeFilter(
|
||||
|
||||
rtreeReference(pRtree);
|
||||
|
||||
/* Reset the cursor to the same state as rtreeOpen() leaves it in. */
|
||||
freeCursorConstraints(pCsr);
|
||||
pCsr->iStrategy = idxNum;
|
||||
sqlite3_free(pCsr->aPoint);
|
||||
memset(pCsr, 0, sizeof(RtreeCursor));
|
||||
pCsr->base.pVtab = (sqlite3_vtab*)pRtree;
|
||||
|
||||
pCsr->iStrategy = idxNum;
|
||||
if( idxNum==1 ){
|
||||
/* Special case - lookup by rowid. */
|
||||
RtreeNode *pLeaf; /* Leaf on which the required cell resides */
|
||||
|
@ -33,6 +33,7 @@ set testprefix rtree1
|
||||
# rtree-8.*: Test constrained scans of r-tree data.
|
||||
#
|
||||
# rtree-12.*: Test that on-conflict clauses are supported.
|
||||
# rtree-13.*: Test that bug [d2889096e7bdeac6d] has been fixed.
|
||||
#
|
||||
|
||||
ifcapable !rtree {
|
||||
@ -513,4 +514,25 @@ foreach {tn sql_template testdata} {
|
||||
db close
|
||||
}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test that bug [d2889096e7bdeac6d] has been fixed.
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 13.1 {
|
||||
CREATE VIRTUAL TABLE t9 USING rtree(id, xmin, xmax);
|
||||
INSERT INTO t9 VALUES(1,0,0);
|
||||
INSERT INTO t9 VALUES(2,0,0);
|
||||
SELECT * FROM t9 WHERE id IN (1, 2);
|
||||
} {1 0.0 0.0 2 0.0 0.0}
|
||||
|
||||
do_execsql_test 13.2 {
|
||||
WITH r(x) AS (
|
||||
SELECT 1 UNION ALL
|
||||
SELECT 2 UNION ALL
|
||||
SELECT 3
|
||||
)
|
||||
SELECT * FROM r CROSS JOIN t9 WHERE id=x;
|
||||
} {1 1 0.0 0.0 2 2 0.0 0.0}
|
||||
|
||||
finish_test
|
||||
|
@ -102,7 +102,7 @@ do_eqp_test rtree6.2.4.2 {
|
||||
SELECT * FROM t1,t2 WHERE v=10 and x1<10 and x2>10
|
||||
} {
|
||||
0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 2:C0E1}
|
||||
0 1 1 {SEARCH TABLE t2 USING AUTOMATIC COVERING INDEX (v=?)}
|
||||
0 1 1 {SEARCH TABLE t2 USING AUTOMATIC PARTIAL COVERING INDEX (v=?)}
|
||||
}
|
||||
|
||||
do_eqp_test rtree6.2.5 {
|
||||
|
81
ext/rtree/rtreeF.test
Normal file
81
ext/rtree/rtreeF.test
Normal file
@ -0,0 +1,81 @@
|
||||
# 2014-08-21
|
||||
#
|
||||
# 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 contains tests for the r-tree module.
|
||||
#
|
||||
# This file contains test cases for the ticket
|
||||
# [369d57fb8e5ccdff06f197a37147a88f9de95cda] (2014-08-21)
|
||||
#
|
||||
# The following SQL causes an assertion fault while running
|
||||
# sqlite3_prepare() on the DELETE statement:
|
||||
#
|
||||
# CREATE TABLE t1(x);
|
||||
# CREATE TABLE t2(y);
|
||||
# CREATE VIRTUAL TABLE t3 USING rtree(a,b,c);
|
||||
# CREATE TRIGGER t2del AFTER DELETE ON t2 WHEN (SELECT 1 from t1) BEGIN
|
||||
# DELETE FROM t3 WHERE a=old.y;
|
||||
# END;
|
||||
# DELETE FROM t2 WHERE y=1;
|
||||
#
|
||||
|
||||
if {![info exists testdir]} {
|
||||
set testdir [file join [file dirname [info script]] .. .. test]
|
||||
}
|
||||
source $testdir/tester.tcl
|
||||
ifcapable !rtree { finish_test ; return }
|
||||
|
||||
do_execsql_test rtreeF-1.1 {
|
||||
CREATE TABLE t1(x);
|
||||
CREATE TABLE t2(y);
|
||||
CREATE VIRTUAL TABLE t3 USING rtree(a,b,c);
|
||||
CREATE TRIGGER t2dwl AFTER DELETE ON t2 WHEN (SELECT 1 from t1) BEGIN
|
||||
DELETE FROM t3 WHERE a=old.y;
|
||||
END;
|
||||
|
||||
INSERT INTO t1(x) VALUES(999);
|
||||
INSERT INTO t2(y) VALUES(1),(2),(3),(4),(5);
|
||||
INSERT INTO t3(a,b,c) VALUES(1,2,3),(2,3,4),(3,4,5),(4,5,6),(5,6,7);
|
||||
|
||||
SELECT a FROM t3 ORDER BY a;
|
||||
SELECT '|';
|
||||
SELECT y FROM t2 ORDER BY y;
|
||||
} {1 2 3 4 5 | 1 2 3 4 5}
|
||||
do_execsql_test rtreeF-1.2 {
|
||||
DELETE FROM t2 WHERE y=3;
|
||||
|
||||
SELECT a FROM t3 ORDER BY a;
|
||||
SELECT '|';
|
||||
SELECT y FROM t2 ORDER BY y;
|
||||
} {1 2 4 5 | 1 2 4 5}
|
||||
do_execsql_test rtreeF-1.3 {
|
||||
DELETE FROM t1;
|
||||
DELETE FROM t2 WHERE y=5;
|
||||
|
||||
SELECT a FROM t3 ORDER BY a;
|
||||
SELECT '|';
|
||||
SELECT y FROM t2 ORDER BY y;
|
||||
} {1 2 4 5 | 1 2 4}
|
||||
do_execsql_test rtreeF-1.4 {
|
||||
INSERT INTO t1 DEFAULT VALUES;
|
||||
DELETE FROM t2 WHERE y=5;
|
||||
|
||||
SELECT a FROM t3 ORDER BY a;
|
||||
SELECT '|';
|
||||
SELECT y FROM t2 ORDER BY y;
|
||||
} {1 2 4 5 | 1 2 4}
|
||||
do_execsql_test rtreeF-1.5 {
|
||||
DELETE FROM t2 WHERE y=2;
|
||||
|
||||
SELECT a FROM t3 ORDER BY a;
|
||||
SELECT '|';
|
||||
SELECT y FROM t2 ORDER BY y;
|
||||
} {1 4 5 | 1 4}
|
||||
|
||||
finish_test
|
88
ext/userauth/sqlite3userauth.h
Normal file
88
ext/userauth/sqlite3userauth.h
Normal file
@ -0,0 +1,88 @@
|
||||
/*
|
||||
** 2014-09-08
|
||||
**
|
||||
** 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 contains the application interface definitions for the
|
||||
** user-authentication extension feature.
|
||||
**
|
||||
** To compile with the user-authentication feature, append this file to
|
||||
** end of an SQLite amalgamation header file ("sqlite3.h"), then add
|
||||
** the SQLITE_USER_AUTHENTICATION compile-time option. See the
|
||||
** user-auth.txt file in the same source directory as this file for
|
||||
** additional information.
|
||||
*/
|
||||
#ifdef SQLITE_USER_AUTHENTICATION
|
||||
|
||||
/*
|
||||
** If a database contains the SQLITE_USER table, then the
|
||||
** sqlite3_user_authenticate() interface must be invoked with an
|
||||
** appropriate username and password prior to enable read and write
|
||||
** access to the database.
|
||||
**
|
||||
** Return SQLITE_OK on success or SQLITE_ERROR if the username/password
|
||||
** combination is incorrect or unknown.
|
||||
**
|
||||
** If the SQLITE_USER table is not present in the database file, then
|
||||
** this interface is a harmless no-op returnning SQLITE_OK.
|
||||
*/
|
||||
int sqlite3_user_authenticate(
|
||||
sqlite3 *db, /* The database connection */
|
||||
const char *zUsername, /* Username */
|
||||
const char *aPW, /* Password or credentials */
|
||||
int nPW /* Number of bytes in aPW[] */
|
||||
);
|
||||
|
||||
/*
|
||||
** The sqlite3_user_add() interface can be used (by an admin user only)
|
||||
** to create a new user. When called on a no-authentication-required
|
||||
** database, this routine converts the database into an authentication-
|
||||
** required database, automatically makes the added user an
|
||||
** administrator, and logs in the current connection as that user.
|
||||
** The sqlite3_user_add() interface only works for the "main" database, not
|
||||
** for any ATTACH-ed databases. Any call to sqlite3_user_add() by a
|
||||
** non-admin user results in an error.
|
||||
*/
|
||||
int sqlite3_user_add(
|
||||
sqlite3 *db, /* Database connection */
|
||||
const char *zUsername, /* Username to be added */
|
||||
const char *aPW, /* Password or credentials */
|
||||
int nPW, /* Number of bytes in aPW[] */
|
||||
int isAdmin /* True to give new user admin privilege */
|
||||
);
|
||||
|
||||
/*
|
||||
** The sqlite3_user_change() interface can be used to change a users
|
||||
** login credentials or admin privilege. Any user can change their own
|
||||
** login credentials. Only an admin user can change another users login
|
||||
** credentials or admin privilege setting. No user may change their own
|
||||
** admin privilege setting.
|
||||
*/
|
||||
int sqlite3_user_change(
|
||||
sqlite3 *db, /* Database connection */
|
||||
const char *zUsername, /* Username to change */
|
||||
const char *aPW, /* New password or credentials */
|
||||
int nPW, /* Number of bytes in aPW[] */
|
||||
int isAdmin /* Modified admin privilege for the user */
|
||||
);
|
||||
|
||||
/*
|
||||
** The sqlite3_user_delete() interface can be used (by an admin user only)
|
||||
** to delete a user. The currently logged-in user cannot be deleted,
|
||||
** which guarantees that there is always an admin user and hence that
|
||||
** the database cannot be converted into a no-authentication-required
|
||||
** database.
|
||||
*/
|
||||
int sqlite3_user_delete(
|
||||
sqlite3 *db, /* Database connection */
|
||||
const char *zUsername /* Username to remove */
|
||||
);
|
||||
|
||||
#endif /* SQLITE_USER_AUTHENTICATION */
|
164
ext/userauth/user-auth.txt
Normal file
164
ext/userauth/user-auth.txt
Normal file
@ -0,0 +1,164 @@
|
||||
Activate the user authentication logic by including the
|
||||
ext/userauth/userauth.c source code file in the build and
|
||||
adding the -DSQLITE_USER_AUTHENTICATION compile-time option.
|
||||
The ext/userauth/sqlite3userauth.h header file is available to
|
||||
applications to define the interface.
|
||||
|
||||
When using the SQLite amalgamation, it is sufficient to append
|
||||
the ext/userauth/userauth.c source file onto the end of the
|
||||
amalgamation.
|
||||
|
||||
The following new APIs are available when user authentication is
|
||||
activated:
|
||||
|
||||
int sqlite3_user_authenticate(
|
||||
sqlite3 *db, /* The database connection */
|
||||
const char *zUsername, /* Username */
|
||||
const char *aPW, /* Password or credentials */
|
||||
int nPW /* Number of bytes in aPW[] */
|
||||
);
|
||||
|
||||
int sqlite3_user_add(
|
||||
sqlite3 *db, /* Database connection */
|
||||
const char *zUsername, /* Username to be added */
|
||||
const char *aPW, /* Password or credentials */
|
||||
int nPW, /* Number of bytes in aPW[] */
|
||||
int isAdmin /* True to give new user admin privilege */
|
||||
);
|
||||
|
||||
int sqlite3_user_change(
|
||||
sqlite3 *db, /* Database connection */
|
||||
const char *zUsername, /* Username to change */
|
||||
const void *aPW, /* Modified password or credentials */
|
||||
int nPW, /* Number of bytes in aPW[] */
|
||||
int isAdmin /* Modified admin privilege for the user */
|
||||
);
|
||||
|
||||
int sqlite3_user_delete(
|
||||
sqlite3 *db, /* Database connection */
|
||||
const char *zUsername /* Username to remove */
|
||||
);
|
||||
|
||||
With this extension, a database can be marked as requiring authentication.
|
||||
By default a database does not require authentication.
|
||||
|
||||
The sqlite3_open(), sqlite3_open16(), and sqlite3_open_v2() interfaces
|
||||
work as before: they open a new database connection. However, if the
|
||||
database being opened requires authentication, then attempts to read
|
||||
or write from the database will fail with an SQLITE_AUTH error until
|
||||
after sqlite3_user_authenticate() has been called successfully. The
|
||||
sqlite3_user_authenticate() call will return SQLITE_OK if the
|
||||
authentication credentials are accepted and SQLITE_ERROR if not.
|
||||
|
||||
Calling sqlite3_user_authenticate() on a no-authentication-required
|
||||
database connection is a harmless no-op.
|
||||
|
||||
If the database is encrypted, then sqlite3_key_v2() must be called first,
|
||||
with the correct decryption key, prior to invoking sqlite3_user_authenticate().
|
||||
|
||||
To recapitulate: When opening an existing unencrypted authentication-
|
||||
required database, the call sequence is:
|
||||
|
||||
sqlite3_open_v2()
|
||||
sqlite3_user_authenticate();
|
||||
/* Database is now usable */
|
||||
|
||||
To open an existing, encrypted, authentication-required database, the
|
||||
call sequence is:
|
||||
|
||||
sqlite3_open_v2();
|
||||
sqlite3_key_v2();
|
||||
sqlite3_user_authenticate();
|
||||
/* Database is now usable */
|
||||
|
||||
When opening a no-authentication-required database, the database
|
||||
connection is treated as if it was authenticated as an admin user.
|
||||
|
||||
When ATTACH-ing new database files to a connection, each newly attached
|
||||
database that is an authentication-required database is checked using
|
||||
the same username and password as supplied to the main database. If that
|
||||
check fails, then the ATTACH command fails with an SQLITE_AUTH error.
|
||||
|
||||
The sqlite3_user_add() interface can be used (by an admin user only)
|
||||
to create a new user. When called on a no-authentication-required
|
||||
database and when A is true, the sqlite3_user_add(D,U,P,N,A) routine
|
||||
converts the database into an authentication-required database and
|
||||
logs in the database connection D as user U with password P,N.
|
||||
To convert a no-authentication-required database into an authentication-
|
||||
required database, the isAdmin parameter must be true. If
|
||||
sqlite3_user_add(D,U,P,N,A) is called on a no-authentication-required
|
||||
database and A is false, then the call fails with an SQLITE_AUTH error.
|
||||
|
||||
Any call to sqlite3_user_add() by a non-admin user results in an error.
|
||||
|
||||
Hence, to create a new, unencrypted, authentication-required database,
|
||||
the call sequence is:
|
||||
|
||||
sqlite3_open_v2();
|
||||
sqlite3_user_add();
|
||||
|
||||
And to create a new, encrypted, authentication-required database, the call
|
||||
sequence is:
|
||||
|
||||
sqlite3_open_v2();
|
||||
sqlite3_key_v2();
|
||||
sqlite3_user_add();
|
||||
|
||||
The sqlite3_user_delete() interface can be used (by an admin user only)
|
||||
to delete a user. The currently logged-in user cannot be deleted,
|
||||
which guarantees that there is always an admin user and hence that
|
||||
the database cannot be converted into a no-authentication-required
|
||||
database.
|
||||
|
||||
The sqlite3_user_change() interface can be used to change a users
|
||||
login credentials or admin privilege. Any user can change their own
|
||||
password. Only an admin user can change another users login
|
||||
credentials or admin privilege setting. No user may change their own
|
||||
admin privilege setting.
|
||||
|
||||
The sqlite3_set_authorizer() callback is modified to take a 7th parameter
|
||||
which is the username of the currently logged in user, or NULL for a
|
||||
no-authentication-required database.
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
Implementation notes:
|
||||
|
||||
An authentication-required database is identified by the presence of a
|
||||
new table:
|
||||
|
||||
CREATE TABLE sqlite_user(
|
||||
uname TEXT PRIMARY KEY,
|
||||
isAdmin BOOLEAN,
|
||||
pw BLOB
|
||||
) WITHOUT ROWID;
|
||||
|
||||
The sqlite_user table is inaccessible (unreadable and unwriteable) to
|
||||
non-admin users and is read-only for admin users. However, if the same
|
||||
database file is opened by a version of SQLite that omits
|
||||
the -DSQLITE_USER_AUTHENTICATION compile-time option, then the sqlite_user
|
||||
table will be readable by anybody and writeable by anybody if
|
||||
the "PRAGMA writable_schema=ON" statement is run first.
|
||||
|
||||
The sqlite_user.pw field is encoded by a built-in SQL function
|
||||
"sqlite_crypt(X,Y)". The two arguments are both BLOBs. The first argument
|
||||
is the plaintext password supplied to the sqlite3_user_authenticate()
|
||||
interface. The second argument is the sqlite_user.pw value and is supplied
|
||||
so that the function can extract the "salt" used by the password encoder.
|
||||
The result of sqlite_crypt(X,Y) is another blob which is the value that
|
||||
ends up being stored in sqlite_user.pw. To verify credentials X supplied
|
||||
by the sqlite3_user_authenticate() routine, SQLite runs:
|
||||
|
||||
sqlite_user.pw == sqlite_crypt(X, sqlite_user.pw)
|
||||
|
||||
To compute an appropriate sqlite_user.pw value from a new or modified
|
||||
password X, sqlite_crypt(X,NULL) is run. A new random salt is selected
|
||||
when the second argument is NULL.
|
||||
|
||||
The built-in version of of sqlite_crypt() uses a simple Ceasar-cypher
|
||||
which prevents passwords from being revealed by searching the raw database
|
||||
for ASCII text, but is otherwise trivally broken. For better password
|
||||
security, the database should be encrypted using the SQLite Encryption
|
||||
Extension or similar technology. Or, the application can use the
|
||||
sqlite3_create_function() interface to provide an alternative
|
||||
implementation of sqlite_crypt() that computes a stronger password hash,
|
||||
perhaps using a cryptographic hash function like SHA1.
|
355
ext/userauth/userauth.c
Normal file
355
ext/userauth/userauth.c
Normal file
@ -0,0 +1,355 @@
|
||||
/*
|
||||
** 2014-09-08
|
||||
**
|
||||
** 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 contains the bulk of the implementation of the
|
||||
** user-authentication extension feature. Some parts of the user-
|
||||
** authentication code are contained within the SQLite core (in the
|
||||
** src/ subdirectory of the main source code tree) but those parts
|
||||
** that could reasonable be separated out are moved into this file.
|
||||
**
|
||||
** To compile with the user-authentication feature, append this file to
|
||||
** end of an SQLite amalgamation, then add the SQLITE_USER_AUTHENTICATION
|
||||
** compile-time option. See the user-auth.txt file in the same source
|
||||
** directory as this file for additional information.
|
||||
*/
|
||||
#ifdef SQLITE_USER_AUTHENTICATION
|
||||
#ifndef _SQLITEINT_H_
|
||||
# include "sqliteInt.h"
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Prepare an SQL statement for use by the user authentication logic.
|
||||
** Return a pointer to the prepared statement on success. Return a
|
||||
** NULL pointer if there is an error of any kind.
|
||||
*/
|
||||
static sqlite3_stmt *sqlite3UserAuthPrepare(
|
||||
sqlite3 *db,
|
||||
const char *zFormat,
|
||||
...
|
||||
){
|
||||
sqlite3_stmt *pStmt;
|
||||
char *zSql;
|
||||
int rc;
|
||||
va_list ap;
|
||||
int savedFlags = db->flags;
|
||||
|
||||
va_start(ap, zFormat);
|
||||
zSql = sqlite3_vmprintf(zFormat, ap);
|
||||
va_end(ap);
|
||||
if( zSql==0 ) return 0;
|
||||
db->flags |= SQLITE_WriteSchema;
|
||||
rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
|
||||
db->flags = savedFlags;
|
||||
sqlite3_free(zSql);
|
||||
if( rc ){
|
||||
sqlite3_finalize(pStmt);
|
||||
pStmt = 0;
|
||||
}
|
||||
return pStmt;
|
||||
}
|
||||
|
||||
/*
|
||||
** Check to see if the sqlite_user table exists in database zDb.
|
||||
*/
|
||||
static int userTableExists(sqlite3 *db, const char *zDb){
|
||||
int rc;
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
sqlite3BtreeEnterAll(db);
|
||||
if( db->init.busy==0 ){
|
||||
char *zErr = 0;
|
||||
sqlite3Init(db, &zErr);
|
||||
sqlite3DbFree(db, zErr);
|
||||
}
|
||||
rc = sqlite3FindTable(db, "sqlite_user", zDb)!=0;
|
||||
sqlite3BtreeLeaveAll(db);
|
||||
sqlite3_mutex_leave(db->mutex);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Check to see if database zDb has a "sqlite_user" table and if it does
|
||||
** whether that table can authenticate zUser with nPw,zPw. Write one of
|
||||
** the UAUTH_* user authorization level codes into *peAuth and return a
|
||||
** result code.
|
||||
*/
|
||||
static int userAuthCheckLogin(
|
||||
sqlite3 *db, /* The database connection to check */
|
||||
const char *zDb, /* Name of specific database to check */
|
||||
u8 *peAuth /* OUT: One of UAUTH_* constants */
|
||||
){
|
||||
sqlite3_stmt *pStmt;
|
||||
int rc;
|
||||
|
||||
*peAuth = UAUTH_Unknown;
|
||||
if( !userTableExists(db, "main") ){
|
||||
*peAuth = UAUTH_Admin; /* No sqlite_user table. Everybody is admin. */
|
||||
return SQLITE_OK;
|
||||
}
|
||||
if( db->auth.zAuthUser==0 ){
|
||||
*peAuth = UAUTH_Fail;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
pStmt = sqlite3UserAuthPrepare(db,
|
||||
"SELECT pw=sqlite_crypt(?1,pw), isAdmin FROM \"%w\".sqlite_user"
|
||||
" WHERE uname=?2", zDb);
|
||||
if( pStmt==0 ) return SQLITE_NOMEM;
|
||||
sqlite3_bind_blob(pStmt, 1, db->auth.zAuthPW, db->auth.nAuthPW,SQLITE_STATIC);
|
||||
sqlite3_bind_text(pStmt, 2, db->auth.zAuthUser, -1, SQLITE_STATIC);
|
||||
rc = sqlite3_step(pStmt);
|
||||
if( rc==SQLITE_ROW && sqlite3_column_int(pStmt,0) ){
|
||||
*peAuth = sqlite3_column_int(pStmt, 1) + UAUTH_User;
|
||||
}else{
|
||||
*peAuth = UAUTH_Fail;
|
||||
}
|
||||
return sqlite3_finalize(pStmt);
|
||||
}
|
||||
int sqlite3UserAuthCheckLogin(
|
||||
sqlite3 *db, /* The database connection to check */
|
||||
const char *zDb, /* Name of specific database to check */
|
||||
u8 *peAuth /* OUT: One of UAUTH_* constants */
|
||||
){
|
||||
int rc;
|
||||
u8 savedAuthLevel;
|
||||
assert( zDb!=0 );
|
||||
assert( peAuth!=0 );
|
||||
savedAuthLevel = db->auth.authLevel;
|
||||
db->auth.authLevel = UAUTH_Admin;
|
||||
rc = userAuthCheckLogin(db, zDb, peAuth);
|
||||
db->auth.authLevel = savedAuthLevel;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** If the current authLevel is UAUTH_Unknown, the take actions to figure
|
||||
** out what authLevel should be
|
||||
*/
|
||||
void sqlite3UserAuthInit(sqlite3 *db){
|
||||
if( db->auth.authLevel==UAUTH_Unknown ){
|
||||
u8 authLevel = UAUTH_Fail;
|
||||
sqlite3UserAuthCheckLogin(db, "main", &authLevel);
|
||||
db->auth.authLevel = authLevel;
|
||||
if( authLevel<UAUTH_Admin ) db->flags &= ~SQLITE_WriteSchema;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Implementation of the sqlite_crypt(X,Y) function.
|
||||
**
|
||||
** If Y is NULL then generate a new hash for password X and return that
|
||||
** hash. If Y is not null, then generate a hash for password X using the
|
||||
** same salt as the previous hash Y and return the new hash.
|
||||
*/
|
||||
void sqlite3CryptFunc(
|
||||
sqlite3_context *context,
|
||||
int NotUsed,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
const char *zIn;
|
||||
int nIn, ii;
|
||||
u8 *zOut;
|
||||
char zSalt[8];
|
||||
zIn = sqlite3_value_blob(argv[0]);
|
||||
nIn = sqlite3_value_bytes(argv[0]);
|
||||
if( sqlite3_value_type(argv[1])==SQLITE_BLOB
|
||||
&& sqlite3_value_bytes(argv[1])==nIn+sizeof(zSalt)
|
||||
){
|
||||
memcpy(zSalt, sqlite3_value_blob(argv[1]), sizeof(zSalt));
|
||||
}else{
|
||||
sqlite3_randomness(sizeof(zSalt), zSalt);
|
||||
}
|
||||
zOut = sqlite3_malloc( nIn+sizeof(zSalt) );
|
||||
if( zOut==0 ){
|
||||
sqlite3_result_error_nomem(context);
|
||||
}else{
|
||||
memcpy(zOut, zSalt, sizeof(zSalt));
|
||||
for(ii=0; ii<nIn; ii++){
|
||||
zOut[ii+sizeof(zSalt)] = zIn[ii]^zSalt[ii&0x7];
|
||||
}
|
||||
sqlite3_result_blob(context, zOut, nIn+sizeof(zSalt), sqlite3_free);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** If a database contains the SQLITE_USER table, then the
|
||||
** sqlite3_user_authenticate() interface must be invoked with an
|
||||
** appropriate username and password prior to enable read and write
|
||||
** access to the database.
|
||||
**
|
||||
** Return SQLITE_OK on success or SQLITE_ERROR if the username/password
|
||||
** combination is incorrect or unknown.
|
||||
**
|
||||
** If the SQLITE_USER table is not present in the database file, then
|
||||
** this interface is a harmless no-op returnning SQLITE_OK.
|
||||
*/
|
||||
int sqlite3_user_authenticate(
|
||||
sqlite3 *db, /* The database connection */
|
||||
const char *zUsername, /* Username */
|
||||
const char *zPW, /* Password or credentials */
|
||||
int nPW /* Number of bytes in aPW[] */
|
||||
){
|
||||
int rc;
|
||||
u8 authLevel = UAUTH_Fail;
|
||||
db->auth.authLevel = UAUTH_Unknown;
|
||||
sqlite3_free(db->auth.zAuthUser);
|
||||
sqlite3_free(db->auth.zAuthPW);
|
||||
memset(&db->auth, 0, sizeof(db->auth));
|
||||
db->auth.zAuthUser = sqlite3_mprintf("%s", zUsername);
|
||||
if( db->auth.zAuthUser==0 ) return SQLITE_NOMEM;
|
||||
db->auth.zAuthPW = sqlite3_malloc( nPW+1 );
|
||||
if( db->auth.zAuthPW==0 ) return SQLITE_NOMEM;
|
||||
memcpy(db->auth.zAuthPW,zPW,nPW);
|
||||
db->auth.nAuthPW = nPW;
|
||||
rc = sqlite3UserAuthCheckLogin(db, "main", &authLevel);
|
||||
db->auth.authLevel = authLevel;
|
||||
sqlite3ExpirePreparedStatements(db);
|
||||
if( rc ){
|
||||
return rc; /* OOM error, I/O error, etc. */
|
||||
}
|
||||
if( authLevel<UAUTH_User ){
|
||||
return SQLITE_AUTH; /* Incorrect username and/or password */
|
||||
}
|
||||
return SQLITE_OK; /* Successful login */
|
||||
}
|
||||
|
||||
/*
|
||||
** The sqlite3_user_add() interface can be used (by an admin user only)
|
||||
** to create a new user. When called on a no-authentication-required
|
||||
** database, this routine converts the database into an authentication-
|
||||
** required database, automatically makes the added user an
|
||||
** administrator, and logs in the current connection as that user.
|
||||
** The sqlite3_user_add() interface only works for the "main" database, not
|
||||
** for any ATTACH-ed databases. Any call to sqlite3_user_add() by a
|
||||
** non-admin user results in an error.
|
||||
*/
|
||||
int sqlite3_user_add(
|
||||
sqlite3 *db, /* Database connection */
|
||||
const char *zUsername, /* Username to be added */
|
||||
const char *aPW, /* Password or credentials */
|
||||
int nPW, /* Number of bytes in aPW[] */
|
||||
int isAdmin /* True to give new user admin privilege */
|
||||
){
|
||||
sqlite3_stmt *pStmt;
|
||||
int rc;
|
||||
sqlite3UserAuthInit(db);
|
||||
if( db->auth.authLevel<UAUTH_Admin ) return SQLITE_AUTH;
|
||||
if( !userTableExists(db, "main") ){
|
||||
if( !isAdmin ) return SQLITE_AUTH;
|
||||
pStmt = sqlite3UserAuthPrepare(db,
|
||||
"CREATE TABLE sqlite_user(\n"
|
||||
" uname TEXT PRIMARY KEY,\n"
|
||||
" isAdmin BOOLEAN,\n"
|
||||
" pw BLOB\n"
|
||||
") WITHOUT ROWID;");
|
||||
if( pStmt==0 ) return SQLITE_NOMEM;
|
||||
sqlite3_step(pStmt);
|
||||
rc = sqlite3_finalize(pStmt);
|
||||
if( rc ) return rc;
|
||||
}
|
||||
pStmt = sqlite3UserAuthPrepare(db,
|
||||
"INSERT INTO sqlite_user(uname,isAdmin,pw)"
|
||||
" VALUES(%Q,%d,sqlite_crypt(?1,NULL))",
|
||||
zUsername, isAdmin!=0);
|
||||
if( pStmt==0 ) return SQLITE_NOMEM;
|
||||
sqlite3_bind_blob(pStmt, 1, aPW, nPW, SQLITE_STATIC);
|
||||
sqlite3_step(pStmt);
|
||||
rc = sqlite3_finalize(pStmt);
|
||||
if( rc ) return rc;
|
||||
if( db->auth.zAuthUser==0 ){
|
||||
assert( isAdmin!=0 );
|
||||
sqlite3_user_authenticate(db, zUsername, aPW, nPW);
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** The sqlite3_user_change() interface can be used to change a users
|
||||
** login credentials or admin privilege. Any user can change their own
|
||||
** login credentials. Only an admin user can change another users login
|
||||
** credentials or admin privilege setting. No user may change their own
|
||||
** admin privilege setting.
|
||||
*/
|
||||
int sqlite3_user_change(
|
||||
sqlite3 *db, /* Database connection */
|
||||
const char *zUsername, /* Username to change */
|
||||
const char *aPW, /* Modified password or credentials */
|
||||
int nPW, /* Number of bytes in aPW[] */
|
||||
int isAdmin /* Modified admin privilege for the user */
|
||||
){
|
||||
sqlite3_stmt *pStmt;
|
||||
int rc;
|
||||
u8 authLevel;
|
||||
|
||||
authLevel = db->auth.authLevel;
|
||||
if( authLevel<UAUTH_User ){
|
||||
/* Must be logged in to make a change */
|
||||
return SQLITE_AUTH;
|
||||
}
|
||||
if( strcmp(db->auth.zAuthUser, zUsername)!=0 ){
|
||||
if( db->auth.authLevel<UAUTH_Admin ){
|
||||
/* Must be an administrator to change a different user */
|
||||
return SQLITE_AUTH;
|
||||
}
|
||||
}else if( isAdmin!=(authLevel==UAUTH_Admin) ){
|
||||
/* Cannot change the isAdmin setting for self */
|
||||
return SQLITE_AUTH;
|
||||
}
|
||||
db->auth.authLevel = UAUTH_Admin;
|
||||
if( !userTableExists(db, "main") ){
|
||||
/* This routine is a no-op if the user to be modified does not exist */
|
||||
}else{
|
||||
pStmt = sqlite3UserAuthPrepare(db,
|
||||
"UPDATE sqlite_user SET isAdmin=%d, pw=sqlite_crypt(?1,NULL)"
|
||||
" WHERE uname=%Q", isAdmin, zUsername);
|
||||
if( pStmt==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
sqlite3_bind_blob(pStmt, 1, aPW, nPW, SQLITE_STATIC);
|
||||
sqlite3_step(pStmt);
|
||||
rc = sqlite3_finalize(pStmt);
|
||||
}
|
||||
}
|
||||
db->auth.authLevel = authLevel;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** The sqlite3_user_delete() interface can be used (by an admin user only)
|
||||
** to delete a user. The currently logged-in user cannot be deleted,
|
||||
** which guarantees that there is always an admin user and hence that
|
||||
** the database cannot be converted into a no-authentication-required
|
||||
** database.
|
||||
*/
|
||||
int sqlite3_user_delete(
|
||||
sqlite3 *db, /* Database connection */
|
||||
const char *zUsername /* Username to remove */
|
||||
){
|
||||
sqlite3_stmt *pStmt;
|
||||
if( db->auth.authLevel<UAUTH_Admin ){
|
||||
/* Must be an administrator to delete a user */
|
||||
return SQLITE_AUTH;
|
||||
}
|
||||
if( strcmp(db->auth.zAuthUser, zUsername)==0 ){
|
||||
/* Cannot delete self */
|
||||
return SQLITE_AUTH;
|
||||
}
|
||||
if( !userTableExists(db, "main") ){
|
||||
/* This routine is a no-op if the user to be deleted does not exist */
|
||||
return SQLITE_OK;
|
||||
}
|
||||
pStmt = sqlite3UserAuthPrepare(db,
|
||||
"DELETE FROM sqlite_user WHERE uname=%Q", zUsername);
|
||||
if( pStmt==0 ) return SQLITE_NOMEM;
|
||||
sqlite3_step(pStmt);
|
||||
return sqlite3_finalize(pStmt);
|
||||
}
|
||||
|
||||
#endif /* SQLITE_USER_AUTHENTICATION */
|
Reference in New Issue
Block a user