/* ** 2008 March 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. ** ************************************************************************* ** Code for testing all sorts of SQLite interfaces. This code ** implements new SQL functions used by the test scripts. */ #include "sqlite3.h" #if defined(INCLUDE_SQLITE_TCL_H) # include "sqlite_tcl.h" #else # include "tcl.h" #endif #include #include #include #include "sqliteInt.h" #include "vdbeInt.h" /* ** Allocate nByte bytes of space using sqlite3_malloc(). If the ** allocation fails, call sqlite3_result_error_nomem() to notify ** the database handle that malloc() has failed. */ static void *testContextMalloc(sqlite3_context *context, int nByte){ char *z = sqlite3_malloc(nByte); if( !z && nByte>0 ){ sqlite3_result_error_nomem(context); } return z; } /* ** This function generates a string of random characters. Used for ** generating test data. */ static void randStr(sqlite3_context *context, int argc, sqlite3_value **argv){ static const unsigned char zSrc[] = "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "0123456789" ".-!,:*^+=_|?/<> "; int iMin, iMax, n, r, i; unsigned char zBuf[1000]; /* It used to be possible to call randstr() with any number of arguments, ** but now it is registered with SQLite as requiring exactly 2. */ assert(argc==2); iMin = sqlite3_value_int(argv[0]); if( iMin<0 ) iMin = 0; if( iMin>=sizeof(zBuf) ) iMin = sizeof(zBuf)-1; iMax = sqlite3_value_int(argv[1]); if( iMax=sizeof(zBuf) ) iMax = sizeof(zBuf)-1; n = iMin; if( iMax>iMin ){ sqlite3_randomness(sizeof(r), &r); r &= 0x7fffffff; n += r%(iMax + 1 - iMin); } assert( n='0' && c<='9' ){ return c - '0'; }else if( c>='a' && c<='f' ){ return c - 'a' + 10; }else if( c>='A' && c<='F' ){ return c - 'A' + 10; } return 0; } /* ** Convert hex to binary. */ static void testHexToBin(const char *zIn, char *zOut){ while( zIn[0] && zIn[1] ){ *(zOut++) = (testHexChar(zIn[0])<<4) + testHexChar(zIn[1]); zIn += 2; } } /* ** hex_to_utf16be(HEX) ** ** Convert the input string from HEX into binary. Then return the ** result using sqlite3_result_text16le(). */ #ifndef SQLITE_OMIT_UTF16 static void testHexToUtf16be( sqlite3_context *pCtx, int nArg, sqlite3_value **argv ){ int n; const char *zIn; char *zOut; assert( nArg==1 ); n = sqlite3_value_bytes(argv[0]); zIn = (const char*)sqlite3_value_text(argv[0]); zOut = sqlite3_malloc( n/2 ); if( zOut==0 ){ sqlite3_result_error_nomem(pCtx); }else{ testHexToBin(zIn, zOut); sqlite3_result_text16be(pCtx, zOut, n/2, sqlite3_free); } } #endif /* ** hex_to_utf8(HEX) ** ** Convert the input string from HEX into binary. Then return the ** result using sqlite3_result_text16le(). */ static void testHexToUtf8( sqlite3_context *pCtx, int nArg, sqlite3_value **argv ){ int n; const char *zIn; char *zOut; assert( nArg==1 ); n = sqlite3_value_bytes(argv[0]); zIn = (const char*)sqlite3_value_text(argv[0]); zOut = sqlite3_malloc( n/2 ); if( zOut==0 ){ sqlite3_result_error_nomem(pCtx); }else{ testHexToBin(zIn, zOut); sqlite3_result_text(pCtx, zOut, n/2, sqlite3_free); } } /* ** hex_to_utf16le(HEX) ** ** Convert the input string from HEX into binary. Then return the ** result using sqlite3_result_text16le(). */ #ifndef SQLITE_OMIT_UTF16 static void testHexToUtf16le( sqlite3_context *pCtx, int nArg, sqlite3_value **argv ){ int n; const char *zIn; char *zOut; assert( nArg==1 ); n = sqlite3_value_bytes(argv[0]); zIn = (const char*)sqlite3_value_text(argv[0]); zOut = sqlite3_malloc( n/2 ); if( zOut==0 ){ sqlite3_result_error_nomem(pCtx); }else{ testHexToBin(zIn, zOut); sqlite3_result_text16le(pCtx, zOut, n/2, sqlite3_free); } } #endif /* ** SQL function: real2hex(X) ** ** If argument X is a real number, then convert it into a string which is ** the big-endian hexadecimal representation of the ieee754 encoding of ** that number. If X is not a real number, return NULL. */ static void real2hex( sqlite3_context *context, int argc, sqlite3_value **argv ){ union { sqlite3_uint64 i; double r; unsigned char x[8]; } v; char zOut[20]; int i; int bigEndian; v.i = 1; bigEndian = v.x[0]==0; v.r = sqlite3_value_double(argv[0]); for(i=0; i<8; i++){ if( bigEndian ){ zOut[i*2] = "0123456789abcdef"[v.x[i]>>4]; zOut[i*2+1] = "0123456789abcdef"[v.x[i]&0xf]; }else{ zOut[14-i*2] = "0123456789abcdef"[v.x[i]>>4]; zOut[14-i*2+1] = "0123456789abcdef"[v.x[i]&0xf]; } } zOut[16] = 0; sqlite3_result_text(context, zOut, -1, SQLITE_TRANSIENT); } /* ** test_extract(record, field) ** ** This function implements an SQL user-function that accepts a blob ** containing a formatted database record as the first argument. The ** second argument is the index of the field within that record to ** extract and return. */ static void test_extract( sqlite3_context *context, int argc, sqlite3_value **argv ){ sqlite3 *db = sqlite3_context_db_handle(context); u8 *pRec; u8 *pEndHdr; /* Points to one byte past record header */ u8 *pHdr; /* Current point in record header */ u8 *pBody; /* Current point in record data */ u64 nHdr; /* Bytes in record header */ int iIdx; /* Required field */ int iCurrent = 0; /* Current field */ assert( argc==2 ); pRec = (u8*)sqlite3_value_blob(argv[0]); iIdx = sqlite3_value_int(argv[1]); pHdr = pRec + sqlite3GetVarint(pRec, &nHdr); pBody = pEndHdr = &pRec[nHdr]; for(iCurrent=0; pHdr> 4) & 0x0F)]; hex[1] = hexdigit[(z[i] & 0x0F)]; hex[2] = '\0'; Tcl_AppendStringsToObj(pVal, hex, 0); } Tcl_AppendStringsToObj(pVal, "'", 0); break; } case SQLITE_FLOAT: pVal = Tcl_NewDoubleObj(sqlite3_value_double(&mem)); break; case SQLITE_INTEGER: pVal = Tcl_NewWideIntObj(sqlite3_value_int64(&mem)); break; case SQLITE_NULL: pVal = Tcl_NewStringObj("NULL", -1); break; default: assert( 0 ); } Tcl_ListObjAppendElement(0, pRet, pVal); if( mem.szMalloc ){ sqlite3DbFree(db, mem.zMalloc); } } sqlite3_result_text(context, Tcl_GetString(pRet), -1, SQLITE_TRANSIENT); Tcl_DecrRefCount(pRet); } /* ** test_zeroblob(N) ** ** The implementation of scalar SQL function "test_zeroblob()". This is ** similar to the built-in zeroblob() function, except that it does not ** check that the integer parameter is within range before passing it ** to sqlite3_result_zeroblob(). */ static void test_zeroblob( sqlite3_context *context, int argc, sqlite3_value **argv ){ int nZero = sqlite3_value_int(argv[0]); sqlite3_result_zeroblob(context, nZero); } /* test_getsubtype(V) ** ** Return the subtype for value V. */ static void test_getsubtype( sqlite3_context *context, int argc, sqlite3_value **argv ){ sqlite3_result_int(context, (int)sqlite3_value_subtype(argv[0])); } /* test_setsubtype(V, T) ** ** Return the value V with its subtype changed to T */ static void test_setsubtype( sqlite3_context *context, int argc, sqlite3_value **argv ){ sqlite3_result_value(context, argv[0]); sqlite3_result_subtype(context, (unsigned int)sqlite3_value_int(argv[1])); } static int registerTestFunctions( sqlite3 *db, char **pzErrMsg, const sqlite3_api_routines *pThunk ){ static const struct { char *zName; signed char nArg; unsigned int eTextRep; /* 1: UTF-16. 0: UTF-8 */ void (*xFunc)(sqlite3_context*,int,sqlite3_value **); } aFuncs[] = { { "randstr", 2, SQLITE_UTF8, randStr }, { "test_destructor", 1, SQLITE_UTF8, test_destructor}, #ifndef SQLITE_OMIT_UTF16 { "test_destructor16", 1, SQLITE_UTF8, test_destructor16}, { "hex_to_utf16be", 1, SQLITE_UTF8, testHexToUtf16be}, { "hex_to_utf16le", 1, SQLITE_UTF8, testHexToUtf16le}, #endif { "hex_to_utf8", 1, SQLITE_UTF8, testHexToUtf8}, { "test_destructor_count", 0, SQLITE_UTF8, test_destructor_count}, { "test_auxdata", -1, SQLITE_UTF8, test_auxdata}, { "test_error", 1, SQLITE_UTF8, test_error}, { "test_error", 2, SQLITE_UTF8, test_error}, { "test_eval", 1, SQLITE_UTF8, test_eval}, { "test_isolation", 2, SQLITE_UTF8, test_isolation}, { "test_counter", 1, SQLITE_UTF8, counterFunc}, { "real2hex", 1, SQLITE_UTF8, real2hex}, { "test_decode", 1, SQLITE_UTF8, test_decode}, { "test_extract", 2, SQLITE_UTF8, test_extract}, { "test_zeroblob", 1, SQLITE_UTF8|SQLITE_DETERMINISTIC, test_zeroblob}, { "test_getsubtype", 1, SQLITE_UTF8, test_getsubtype}, { "test_setsubtype", 2, SQLITE_UTF8, test_setsubtype}, }; int i; for(i=0; i / ) * ** ** where is the number of instances of the phrase in the ** column value of the current row and is the number ** of instances of the phrase in the same column of all rows in the FTS ** table. The is a weighting factor assigned to each ** column by the caller (see below). ** ** The first argument to this function must be the return value of the FTS ** matchinfo() function. Following this must be one argument for each column ** of the FTS table containing a numeric weight factor for the corresponding ** column. Example: ** ** CREATE VIRTUAL TABLE documents USING fts3(title, content) ** ** The following query returns the docids of documents that match the full-text ** query sorted from most to least relevant. When calculating ** relevance, query term instances in the 'title' column are given twice the ** weighting of those in the 'content' column. ** ** SELECT docid FROM documents ** WHERE documents MATCH ** ORDER BY rank(matchinfo(documents), 1.0, 0.5) DESC */ static void rankfunc(sqlite3_context *pCtx, int nVal, sqlite3_value **apVal){ int *aMatchinfo; /* Return value of matchinfo() */ int nMatchinfo; /* Number of elements in aMatchinfo[] */ int nCol = 0; /* Number of columns in the table */ int nPhrase = 0; /* Number of phrases in the query */ int iPhrase; /* Current phrase */ double score = 0.0; /* Value to return */ assert( sizeof(int)==4 ); /* Check that the number of arguments passed to this function is correct. ** If not, jump to wrong_number_args. Set aMatchinfo to point to the array ** of unsigned integer values returned by FTS function matchinfo. Set ** nPhrase to contain the number of reportable phrases in the users full-text ** query, and nCol to the number of columns in the table. Then check that the ** size of the matchinfo blob is as expected. Return an error if it is not. */ if( nVal<1 ) goto wrong_number_args; aMatchinfo = (int*)sqlite3_value_blob(apVal[0]); nMatchinfo = sqlite3_value_bytes(apVal[0]) / sizeof(int); if( nMatchinfo>=2 ){ nPhrase = aMatchinfo[0]; nCol = aMatchinfo[1]; } if( nMatchinfo!=(2+3*nCol*nPhrase) ){ sqlite3_result_error(pCtx, "invalid matchinfo blob passed to function rank()", -1); return; } if( nVal!=(1+nCol) ) goto wrong_number_args; /* Iterate through each phrase in the users query. */ for(iPhrase=0; iPhrase / ) * ** ** aPhraseinfo[] points to the start of the data for phrase iPhrase. So ** the hit count and global hit counts for each column are found in ** aPhraseinfo[iCol*3] and aPhraseinfo[iCol*3+1], respectively. */ int *aPhraseinfo = &aMatchinfo[2 + iPhrase*nCol*3]; for(iCol=0; iCol0 ){ score += ((double)nHitCount / (double)nGlobalHitCount) * weight; } } } sqlite3_result_double(pCtx, score); return; /* Jump here if the wrong number of arguments are passed to this function */ wrong_number_args: sqlite3_result_error(pCtx, "wrong number of arguments to function rank()", -1); } static int SQLITE_TCLAPI install_fts3_rank_function( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ extern int getDbPointer(Tcl_Interp*, const char*, sqlite3**); sqlite3 *db; if( objc!=2 ){ Tcl_WrongNumArgs(interp, 1, objv, "DB"); return TCL_ERROR; } if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; sqlite3_create_function(db, "rank", -1, SQLITE_UTF8, 0, rankfunc, 0, 0); return TCL_OK; } /* ** Register commands with the TCL interpreter. */ int Sqlitetest_func_Init(Tcl_Interp *interp){ static struct { char *zName; Tcl_ObjCmdProc *xProc; } aObjCmd[] = { { "autoinstall_test_functions", autoinstall_test_funcs }, { "abuse_create_function", abuse_create_function }, { "install_fts3_rank_function", install_fts3_rank_function }, }; int i; extern int Md5_Register(sqlite3 *, char **, const sqlite3_api_routines *); for(i=0; i