]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Add code to parse a rank() function specification. And a tcl interface to add auxilia...
authordan <dan@noemail.net>
Mon, 1 Dec 2014 20:05:00 +0000 (20:05 +0000)
committerdan <dan@noemail.net>
Mon, 1 Dec 2014 20:05:00 +0000 (20:05 +0000)
FossilOrigin-Name: 9c1697a2aa1f601e6eb11704abe63a73c8105447

ext/fts5/fts5.c
ext/fts5/fts5Int.h
ext/fts5/fts5_config.c
ext/fts5/fts5_tcl.c [new file with mode: 0644]
main.mk
manifest
manifest.uuid
src/tclsqlite.c
test/fts5al.test

index 83428bc1c3adfb6a6a4c79736b63356b8b8e723c..120c7e27385f05afc248d9ec66e557b413f98127 100644 (file)
@@ -898,7 +898,7 @@ static int fts5SeekCursor(Fts5Cursor *pCsr){
 ** INSERT Directives" section of the documentation. It should be updated if
 ** more commands are added to this function.
 */
-static int fts5SpecialCommand(
+static int fts5SpecialInsert(
   Fts5Table *pTab,                /* Fts5 table object */
   sqlite3_value *pCmd,            /* Value inserted into special column */
   sqlite3_value *pVal             /* Value inserted into rowid column */
@@ -911,10 +911,12 @@ static int fts5SpecialCommand(
     rc = sqlite3Fts5StorageIntegrity(pTab->pStorage);
   }else{
     rc = sqlite3Fts5ConfigSetValue(pTab->pConfig, z, pVal, &bError);
-    if( rc==SQLITE_OK && bError ){
-      rc = SQLITE_ERROR;
-    }else{
-      rc = sqlite3Fts5StorageConfigValue(pTab->pStorage, z, pVal);
+    if( rc==SQLITE_OK ){
+      if( bError ){
+        rc = SQLITE_ERROR;
+      }else{
+        rc = sqlite3Fts5StorageConfigValue(pTab->pStorage, z, pVal);
+      }
     }
   }
   return rc;
@@ -951,7 +953,7 @@ static int fts5UpdateMethod(
   assert( nArg==1 || nArg==(2 + pConfig->nCol + 2) );
 
   if( nArg>1 && SQLITE_NULL!=sqlite3_value_type(apVal[2 + pConfig->nCol]) ){
-    return fts5SpecialCommand(pTab, 
+    return fts5SpecialInsert(pTab, 
         apVal[2 + pConfig->nCol], apVal[2 + pConfig->nCol + 1]
     );
   }
@@ -1676,7 +1678,7 @@ static void fts5Fts5Func(
   char buf[8];
   assert( nArg==0 );
   assert( sizeof(buf)>=sizeof(pGlobal) );
-  memcpy(buf, pGlobal, sizeof(pGlobal));
+  memcpy(buf, (void*)&pGlobal, sizeof(pGlobal));
   sqlite3_result_blob(pCtx, buf, sizeof(pGlobal), SQLITE_TRANSIENT);
 }
 
index d2abadc36df9e2794456ae40c865abf37a452eba..c2aea79451a6177fc297b220e86ae43e02e1573c 100644 (file)
@@ -81,6 +81,8 @@ struct Fts5Config {
   int iCookie;                    /* Incremented when %_config is modified */
   int pgsz;                       /* Approximate page size used in %_data */
   int nAutomerge;                 /* 'automerge' setting */
+  char *zRank;                    /* Name of rank function */
+  char *zRankArgs;                /* Arguments to rank function */
 };
 
 int sqlite3Fts5ConfigParse(
index c7e729276bd8822b3a34b7e1c737fcf9888748f9..88a030f5b576dcbabb0dca3214511a4d23c24882 100644 (file)
@@ -230,6 +230,8 @@ void sqlite3Fts5ConfigFree(Fts5Config *pConfig){
     }
     sqlite3_free(pConfig->azCol);
     sqlite3_free(pConfig->aPrefix);
+    sqlite3_free(pConfig->zRank);
+    sqlite3_free(pConfig->zRankArgs);
     sqlite3_free(pConfig);
   }
 }
@@ -302,6 +304,190 @@ int sqlite3Fts5Tokenize(
   return pConfig->pTokApi->xTokenize(pConfig->pTok, pCtx, pText, nText, xToken);
 }
 
+/*
+** Argument pIn points to a character that is part of a nul-terminated 
+** string. Return a pointer to the first character following *pIn in 
+** the string that is not a white-space character.
+*/
+static const char *fts5ConfigSkipWhitespace(const char *pIn){
+  const char *p = pIn;
+  if( p ){
+    while( *p==' ' ){ p++; }
+  }
+  return p;
+}
+
+/*
+** Argument pIn points to a character that is part of a nul-terminated 
+** string. Return a pointer to the first character following *pIn in 
+** the string that is not a "bareword" character.
+*/
+static const char *fts5ConfigSkipBareword(const char *pIn){
+  const char *p = pIn;
+  while( *p      && *p!=' ' && *p!=':' && *p!='!' && *p!='@' 
+      && *p!='#' && *p!='$' && *p!='%' && *p!='^' && *p!='&' 
+      && *p!='*' && *p!='(' && *p!=')' 
+  ){
+    p++;
+  }
+  if( p==pIn ) p = 0;
+  return p;
+}
+
+static int fts5_isdigit(char a){
+  return (a>='0' && a<='9');
+}
+
+
+
+static const char *fts5ConfigSkipLiteral(const char *pIn){
+  const char *p = pIn;
+  if( p ){
+    switch( *p ){
+      case 'n': case 'N':
+        if( sqlite3_strnicmp("null", p, 4)==0 ){
+          p = &p[4];
+        }else{
+          p = 0;
+        }
+        break;
+        
+      case 'x': case 'X':
+        p++;
+        if( *p=='\'' ){
+          p++;
+          while( (*p>='a' && *p<='f') 
+              || (*p>='A' && *p<='F') 
+              || (*p>='0' && *p<='9') 
+          ){
+            p++;
+          }
+          if( *p=='\'' && 0==((p-pIn)%2) ){
+            p++;
+          }else{
+            p = 0;
+          }
+        }else{
+          p = 0;
+        }
+        break;
+
+      case '\'':
+        p++;
+        while( p ){
+          if( *p=='\'' ){
+            p++;
+            if( *p!='\'' ) break;
+          }
+          p++;
+          if( *p==0 ) p = 0;
+        }
+        break;
+
+      default:
+        /* maybe a number */
+        if( *p=='+' || *p=='-' ) p++;
+        while( fts5_isdigit(*p) ) p++;
+
+        /* At this point, if the literal was an integer, the parse is 
+        ** finished. Or, if it is a floating point value, it may continue
+        ** with either a decimal point or an 'E' character. */
+        if( *p=='.' && fts5_isdigit(p[1]) ){
+          p += 2;
+          while( fts5_isdigit(*p) ) p++;
+        }
+
+        break;
+    }
+  }
+
+  return p;
+}
+
+/*
+** Argument pIn points to the first character in what is expected to be
+** a comma-separated list of SQL literals followed by a ')' character.
+** If it actually is this, return a pointer to the ')'. Otherwise, return
+** NULL to indicate a parse error.
+*/
+static const char *fts5ConfigSkipArgs(const char *pIn){
+  const char *p = pIn;
+  
+  while( 1 ){
+    p = fts5ConfigSkipWhitespace(p);
+    p = fts5ConfigSkipLiteral(p);
+    p = fts5ConfigSkipWhitespace(p);
+    if( p==0 || *p==')' ) break;
+    if( *p!=',' ){
+      p = 0;
+      break;
+    }
+    p++;
+  }
+
+  return p;
+}
+
+/*
+** Parameter zIn contains a rank() function specification. The format of 
+** this is:
+**
+**   + Bareword (function name)
+**   + Open parenthesis - "("
+**   + Zero or more SQL literals in a comma separated list
+**   + Close parenthesis - ")"
+*/
+static int fts5ConfigParseRank(
+  const char *zIn,                /* Input string */
+  char **pzRank,                  /* OUT: Rank function name */
+  char **pzRankArgs               /* OUT: Rank function arguments */
+){
+  const char *p = zIn;
+  const char *pRank;
+  char *zRank = 0;
+  char *zRankArgs = 0;
+  int rc = SQLITE_OK;
+
+  *pzRank = 0;
+  *pzRankArgs = 0;
+
+  p = fts5ConfigSkipWhitespace(p);
+  pRank = p;
+  p = fts5ConfigSkipBareword(p);
+
+  if( p ){
+    zRank = sqlite3Fts5MallocZero(&rc, 1 + p - pRank);
+    if( zRank ) memcpy(zRank, pRank, p-pRank);
+  }else{
+    rc = SQLITE_ERROR;
+  }
+
+  if( rc==SQLITE_OK ){
+    p = fts5ConfigSkipWhitespace(p);
+    if( *p!='(' ) rc = SQLITE_ERROR;
+    p++;
+  }
+  if( rc==SQLITE_OK ){
+    const char *pArgs = p;
+    p = fts5ConfigSkipArgs(p);
+    if( p==0 ){
+      rc = SQLITE_ERROR;
+    }else{
+      zRankArgs = sqlite3Fts5MallocZero(&rc, 1 + p - pArgs);
+      if( zRankArgs ) memcpy(zRankArgs, pArgs, p-pArgs);
+    }
+  }
+
+  if( rc!=SQLITE_OK ){
+    sqlite3_free(zRank);
+    assert( zRankArgs==0 );
+  }else{
+    *pzRank = zRank;
+    *pzRankArgs = zRankArgs;
+  }
+  return rc;
+}
+
 int sqlite3Fts5ConfigSetValue(
   Fts5Config *pConfig, 
   const char *zKey, 
@@ -339,7 +525,19 @@ int sqlite3Fts5ConfigSetValue(
   }
 
   else if( 0==sqlite3_stricmp(zKey, "rank") ){
-    // todo
+    const char *zIn = (const char*)sqlite3_value_text(pVal);
+    char *zRank;
+    char *zRankArgs;
+    rc = fts5ConfigParseRank(zIn, &zRank, &zRankArgs);
+    if( rc==SQLITE_OK ){
+      sqlite3_free(pConfig->zRank);
+      sqlite3_free(pConfig->zRankArgs);
+      pConfig->zRank = zRank;
+      pConfig->zRankArgs = zRankArgs;
+    }else if( rc==SQLITE_ERROR ){
+      rc = SQLITE_OK;
+      if( pbBadkey ) *pbBadkey = 1;
+    }
   }else{
     if( pbBadkey ) *pbBadkey = 1;
   }
diff --git a/ext/fts5/fts5_tcl.c b/ext/fts5/fts5_tcl.c
new file mode 100644 (file)
index 0000000..28efe71
--- /dev/null
@@ -0,0 +1,341 @@
+/*
+** 2014 Dec 01
+**
+** 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 "fts5.h"
+#include <tcl.h>
+#include <string.h>
+#include <assert.h>
+
+/*************************************************************************
+** This is a copy of the first part of the SqliteDb structure in 
+** tclsqlite.c.  We need it here so that the get_sqlite_pointer routine
+** can extract the sqlite3* pointer from an existing Tcl SQLite
+** connection.
+*/
+struct SqliteDb {
+  sqlite3 *db;
+};
+
+/*
+** Decode a pointer to an sqlite3 object.
+*/
+static int f5tDbPointer(Tcl_Interp *interp, Tcl_Obj *pObj, sqlite3 **ppDb){
+  struct SqliteDb *p;
+  Tcl_CmdInfo cmdInfo;
+  char *z = Tcl_GetString(pObj);
+  if( Tcl_GetCommandInfo(interp, z, &cmdInfo) ){
+    p = (struct SqliteDb*)cmdInfo.objClientData;
+    *ppDb = p->db;
+    return TCL_OK;
+  }
+  return TCL_ERROR;
+}
+/* End of code that accesses the SqliteDb struct.
+**************************************************************************/
+
+typedef struct F5tFunction F5tFunction;
+struct F5tFunction {
+  Tcl_Interp *interp;
+  Tcl_Obj *pScript;
+};
+
+typedef struct F5tApi F5tApi;
+struct F5tApi {
+  const Fts5ExtensionApi *pApi;
+  Fts5Context *pFts;
+};
+
+/*
+**      api sub-command... 
+**
+** Description...
+*/
+static int xF5tApi(
+  void * clientData,
+  Tcl_Interp *interp,
+  int objc,
+  Tcl_Obj *CONST objv[]
+){
+  struct Sub {
+    const char *zName;
+    int nArg;
+    const char *zMsg;
+  } aSub[] = {
+    { "xRowid",      0, "" },
+    { "xInstCount",  0, "" },
+    { "xInst",       1, "IDX" },
+    { "xColumnText", 1, "COL" },
+    { "xColumnSize", 1, "COL" },
+  };
+  int rc;
+  int iSub = 0;
+  F5tApi *p = (F5tApi*)clientData;
+
+  if( objc<2 ){
+    Tcl_WrongNumArgs(interp, 1, objv, "SUB-COMMAND");
+    return TCL_ERROR;
+  }
+
+  rc = Tcl_GetIndexFromObjStruct(
+      interp, objv[1], aSub, sizeof(aSub[0]), "SUB-COMMAND", 0, &iSub
+  );
+  if( rc!=TCL_OK ) return rc;
+  if( aSub[iSub].nArg!=objc-2 ){
+    Tcl_WrongNumArgs(interp, 1, objv, aSub[iSub].zMsg);
+    return TCL_ERROR;
+  }
+
+  switch( iSub ){
+    case 0: { /* xRowid */
+      sqlite3_int64 iRowid = p->pApi->xRowid(p->pFts);
+      Tcl_SetObjResult(interp, Tcl_NewWideIntObj(iRowid));
+      break;
+    }
+
+    case 1: { /* xInstCount */
+      int nInst;
+      rc = p->pApi->xInstCount(p->pFts, &nInst);
+      if( rc==SQLITE_OK ){
+        Tcl_SetObjResult(interp, Tcl_NewIntObj(nInst));
+      }
+      break;
+    }
+
+    case 2: { /* xInst */
+      int iIdx, ip, ic, io;
+      if( Tcl_GetIntFromObj(interp, objv[2], &iIdx) ){
+        return TCL_ERROR;
+      }
+      rc = p->pApi->xInst(p->pFts, iIdx, &ip, &ic, &io);
+      if( rc==SQLITE_OK ){
+        Tcl_Obj *pList = Tcl_NewObj();
+        Tcl_ListObjAppendElement(interp, pList, Tcl_NewIntObj(ip));
+        Tcl_ListObjAppendElement(interp, pList, Tcl_NewIntObj(ic));
+        Tcl_ListObjAppendElement(interp, pList, Tcl_NewIntObj(io));
+        Tcl_SetObjResult(interp, pList);
+      }
+      break;
+    }
+
+    case 3: { /* xColumnText */
+      const char *z = 0;
+      int n = 0;
+      int iCol;
+      if( Tcl_GetIntFromObj(interp, objv[2], &iCol) ){
+        return TCL_ERROR;
+      }
+      rc = p->pApi->xColumnText(p->pFts, iCol, &z, &n);
+      if( rc==SQLITE_OK ){
+        Tcl_SetObjResult(interp, Tcl_NewStringObj(z, n));
+      }
+      break;
+    }
+
+    case 4: { /* xColumnSize */
+      int n = 0;
+      int iCol;
+      if( Tcl_GetIntFromObj(interp, objv[2], &iCol) ){
+        return TCL_ERROR;
+      }
+      rc = p->pApi->xColumnSize(p->pFts, iCol, &n);
+      if( rc==SQLITE_OK ){
+        Tcl_SetObjResult(interp, Tcl_NewIntObj(n));
+      }
+      break;
+    }
+
+    default: 
+      assert( 0 );
+      break;
+  }
+
+  if( rc!=SQLITE_OK ){
+    Tcl_AppendResult(interp, "error in api call", 0);
+    return TCL_ERROR;
+  }
+
+  return TCL_OK;
+}
+
+static void xF5tFunction(
+  const Fts5ExtensionApi *pApi,   /* API offered by current FTS version */
+  Fts5Context *pFts,              /* First arg to pass to pApi functions */
+  sqlite3_context *pCtx,          /* Context for returning result/error */
+  int nVal,                       /* Number of values in apVal[] array */
+  sqlite3_value **apVal           /* Array of trailing arguments */
+){
+  F5tFunction *p = (F5tFunction*)pApi->xUserData(pFts);
+  Tcl_Obj *pEval;                 /* Script to evaluate */
+  int i;
+  int rc;
+
+  static sqlite3_int64 iCmd = 0;
+  char zCmd[64];
+  F5tApi sApi;
+  sApi.pApi = pApi;
+  sApi.pFts = pFts;
+
+  sprintf(zCmd, "f5t_%lld", iCmd++);
+  Tcl_CreateObjCommand(p->interp, zCmd, xF5tApi, &sApi, 0);
+  pEval = Tcl_DuplicateObj(p->pScript);
+  Tcl_IncrRefCount(pEval);
+  Tcl_ListObjAppendElement(p->interp, pEval, Tcl_NewStringObj(zCmd, -1));
+
+  for(i=0; i<nVal; i++){
+    Tcl_Obj *pObj = 0;
+    switch( sqlite3_value_type(apVal[i]) ){
+      case SQLITE_TEXT:
+        pObj = Tcl_NewStringObj((const char*)sqlite3_value_text(apVal[i]), -1);
+        break;
+      case SQLITE_BLOB:
+        pObj = Tcl_NewByteArrayObj(
+            sqlite3_value_blob(apVal[i]), sqlite3_value_bytes(apVal[i])
+        );
+        break;
+      case SQLITE_INTEGER:
+        pObj = Tcl_NewWideIntObj(sqlite3_value_int64(apVal[i]));
+        break;
+      case SQLITE_FLOAT:
+        pObj = Tcl_NewDoubleObj(sqlite3_value_double(apVal[i]));
+        break;
+      default:
+        pObj = Tcl_NewObj();
+        break;
+    }
+    Tcl_ListObjAppendElement(p->interp, pEval, pObj);
+  }
+
+  rc = Tcl_EvalObjEx(p->interp, pEval, TCL_GLOBAL_ONLY);
+  Tcl_DecrRefCount(pEval);
+  Tcl_DeleteCommand(p->interp, zCmd);
+
+  if( rc!=TCL_OK ){
+    sqlite3_result_error(pCtx, Tcl_GetStringResult(p->interp), -1);
+  }else{
+    Tcl_Obj *pVar = Tcl_GetObjResult(p->interp);
+    int n;
+    const char *zType = (pVar->typePtr ? pVar->typePtr->name : "");
+    char c = zType[0];
+    if( c=='b' && strcmp(zType,"bytearray")==0 && pVar->bytes==0 ){
+      /* Only return a BLOB type if the Tcl variable is a bytearray and
+      ** has no string representation. */
+      unsigned char *data = Tcl_GetByteArrayFromObj(pVar, &n);
+      sqlite3_result_blob(pCtx, data, n, SQLITE_TRANSIENT);
+    }else if( c=='b' && strcmp(zType,"boolean")==0 ){
+      Tcl_GetIntFromObj(0, pVar, &n);
+      sqlite3_result_int(pCtx, n);
+    }else if( c=='d' && strcmp(zType,"double")==0 ){
+      double r;
+      Tcl_GetDoubleFromObj(0, pVar, &r);
+      sqlite3_result_double(pCtx, r);
+    }else if( (c=='w' && strcmp(zType,"wideInt")==0) ||
+          (c=='i' && strcmp(zType,"int")==0) ){
+      Tcl_WideInt v;
+      Tcl_GetWideIntFromObj(0, pVar, &v);
+      sqlite3_result_int64(pCtx, v);
+    }else{
+      unsigned char *data = (unsigned char *)Tcl_GetStringFromObj(pVar, &n);
+      sqlite3_result_text(pCtx, (char *)data, n, SQLITE_TRANSIENT);
+    }
+  }
+}
+
+static void xF5tDestroy(void *pCtx){
+  F5tFunction *p = (F5tFunction*)pCtx;
+  Tcl_DecrRefCount(p->pScript);
+  ckfree(p);
+}
+
+/*
+**      sqlite3_fts5_create_function DB NAME SCRIPT
+**
+** Description...
+*/
+static int f5tCreateFunction(
+  void * clientData,
+  Tcl_Interp *interp,
+  int objc,
+  Tcl_Obj *CONST objv[]
+){
+  char *zName;
+  Tcl_Obj *pScript;
+  sqlite3 *db = 0;
+  sqlite3_stmt *pStmt = 0;
+  fts5_api *pApi = 0;
+  F5tFunction *pCtx = 0;
+  int rc;
+
+  if( objc!=4 ){
+    Tcl_WrongNumArgs(interp, 1, objv, "DB NAME SCRIPT");
+    return TCL_ERROR;
+  }
+  if( f5tDbPointer(interp, objv[1], &db) ){
+    return TCL_ERROR;
+  }
+  zName = Tcl_GetString(objv[2]);
+  pScript = objv[3];
+
+  rc = sqlite3_prepare_v2(db, "SELECT fts5()", -1, &pStmt, 0);
+  if( rc!=SQLITE_OK ){
+    Tcl_AppendResult(interp, "error: ", sqlite3_errmsg(db), 0);
+    return TCL_ERROR;
+  }
+
+  if( SQLITE_ROW==sqlite3_step(pStmt) ){
+    const void *pPtr = sqlite3_column_blob(pStmt, 0);
+    memcpy((void*)&pApi, pPtr, sizeof(pApi));
+  }
+  if( sqlite3_finalize(pStmt)!=SQLITE_OK ){
+    Tcl_AppendResult(interp, "error: ", sqlite3_errmsg(db), 0);
+    return TCL_ERROR;
+  }
+
+  pCtx = (F5tFunction*)ckalloc(sizeof(F5tFunction));
+  pCtx->interp = interp;
+  pCtx->pScript = pScript;
+  Tcl_IncrRefCount(pScript);
+
+  rc = pApi->xCreateFunction(
+      pApi, zName, (void*)pCtx, xF5tFunction, xF5tDestroy
+  );
+  if( rc!=SQLITE_OK ){
+    Tcl_AppendResult(interp, "error: ", sqlite3_errmsg(db), 0);
+    return TCL_ERROR;
+  }
+
+  return TCL_OK;
+}
+
+/*
+** Entry point.
+*/
+int Fts5tcl_Init(Tcl_Interp *interp){
+  static struct Cmd {
+    char *zName;
+    Tcl_ObjCmdProc *xProc;
+    void *clientData;
+  } aCmd[] = {
+    { "sqlite3_fts5_create_function", f5tCreateFunction, 0 }
+  };
+  int i;
+
+  for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){
+    struct Cmd *p = &aCmd[i];
+    Tcl_CreateObjCommand(interp, p->zName, p->xProc, p->clientData, 0);
+  }
+
+  return TCL_OK;
+}
+
diff --git a/main.mk b/main.mk
index e30bb92c82edde7f505b57ae8320fd5a092cebae..58044218a726371c8acd1e4ca8fb9cf25229c0ea 100644 (file)
--- a/main.mk
+++ b/main.mk
@@ -312,7 +312,8 @@ TESTSRC += \
   $(TOP)/ext/misc/spellfix.c \
   $(TOP)/ext/misc/totype.c \
   $(TOP)/ext/misc/wholenumber.c \
-  $(TOP)/ext/misc/vfslog.c
+  $(TOP)/ext/misc/vfslog.c \
+  $(TOP)/ext/fts5/fts5_tcl.c 
 
 
 #TESTSRC += $(TOP)/ext/fts2/fts2_tokenizer.c
index 91938fa8ea94e9f2b7cd352227fd53cbd32ba048..27423bc7d74743b87efa84b5a29f06a18c235a61 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Add\sa\scookie\smechanism\sto\sensure\sthat\sthe\s%_config\stable\sis\sre-read\sas\srequired.
-D 2014-11-28T20:01:13.778
+C Add\scode\sto\sparse\sa\srank()\sfunction\sspecification.\sAnd\sa\stcl\sinterface\sto\sadd\sauxiliary\sfunctions\sto\sfts5.
+D 2014-12-01T20:05:00.761
 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
 F Makefile.in b03432313a3aad96c706f8164fb9f5307eaf19f5
 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@@ -104,16 +104,17 @@ F ext/fts3/unicode/CaseFolding.txt 8c678ca52ecc95e16bc7afc2dbf6fc9ffa05db8c
 F ext/fts3/unicode/UnicodeData.txt cd07314edb62d49fde34debdaf92fa2aa69011e7
 F ext/fts3/unicode/mkunicode.tcl dc6f268eb526710e2c6e496c372471d773d0c368
 F ext/fts5/extract_api_docs.tcl 6320db4a1d0722a4e2069e661381ad75e9889786
-F ext/fts5/fts5.c b3a2574be6921512133d228a922bc0bfb221c569
+F ext/fts5/fts5.c 07f81ce7ebbffdd0acdad9eb090ff506fa503a10
 F ext/fts5/fts5.h 72fc1e9995b1ddc254a487b9528614a83bd3dfb6
-F ext/fts5/fts5Int.h a466dd67c909ac05ce8330acf13c7c5bfd244e15
+F ext/fts5/fts5Int.h e16cf2213ae748ccc2c890f404fc341eb941d10b
 F ext/fts5/fts5_aux.c 0e3e5fea6bf5772805afe14c95cb5f16e03e4b3f
 F ext/fts5/fts5_buffer.c c79d67a5a611521f1f3b9d495981f22c02ef4bdb
-F ext/fts5/fts5_config.c c95d89bd3ee119681f0aeff0fa34ee9cd18fc430
+F ext/fts5/fts5_config.c bb87c2b915ae94002d94d02a6b1f81a0dac9c6db
 F ext/fts5/fts5_expr.c d317be07d70223a6865444f17982570260b690a5
 F ext/fts5/fts5_hash.c 63fa8379c5f2ac107d47c2b7d9ac04c95ef8a279
 F ext/fts5/fts5_index.c 7e7023f3a29f104b44df2ca2474b296b8dfe447c
 F ext/fts5/fts5_storage.c 0198c5976cefa5e8d3f1cfffa3587d0dd594fb2a
+F ext/fts5/fts5_tcl.c 5272224faf9be129679da5e19d788f0307afc375
 F ext/fts5/fts5_tokenize.c 8360c0d1ae0d4696f3cc13f7c67a2db6011cdc5b
 F ext/fts5/fts5parse.y 777da8e5819f75c217982c79c29d014c293acac9
 F ext/icu/README.txt d9fbbad0c2f647c3fdf715fc9fd64af53aedfc43
@@ -159,7 +160,7 @@ F ext/rtree/viewrtree.tcl eea6224b3553599ae665b239bd827e182b466024
 F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
 F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
 F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60
-F main.mk 8a02fddafc05159c4b7d65200e912cf549f978c1
+F main.mk 863a6f5cdcc3a47a9dcbedc9af37d3c0d4172935
 F mkopcodec.awk c2ff431854d702cdd2d779c9c0d1f58fa16fa4ea
 F mkopcodeh.awk c6b3fa301db6ef7ac916b14c60868aeaec1337b5
 F mkso.sh fd21c06b063bb16a5d25deea1752c2da6ac3ed83
@@ -244,7 +245,7 @@ F src/sqliteInt.h fccdc735c27b3dc12322fec7cdad8bc76be8d00b
 F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d
 F src/status.c 7ac05a5c7017d0b9f0b4bcd701228b784f987158
 F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e
-F src/tclsqlite.c e87c99e28a145943666b51b212dacae35fcea0bd
+F src/tclsqlite.c 3a274c56cfc66b1f957afef201547213fc2ccecc
 F src/test1.c 3c8bc491d2f8de5adbbf306533cefc343c733927
 F src/test2.c 98049e51a17dc62606a99a9eb95ee477f9996712
 F src/test3.c 1c0e5d6f080b8e33c1ce8b3078e7013fdbcd560c
@@ -609,7 +610,7 @@ F test/fts5ah.test 788e923e60b5e7a559f672cfbf262b8b260ea176
 F test/fts5ai.test aa2b5fd0f8d2cf59ac0211111e63cbca3b40ed7d
 F test/fts5aj.test bc3d91bd012c7ca175cdf266c2074920bb5fa5ba
 F test/fts5ak.test e55bb0f3fac1291d32bc9485a3ee55a7d76f4d5f
-F test/fts5al.test 455b2bdc9f6ffb965a38a970a60c5075ee1e23bb
+F test/fts5al.test d716a933bb88eb6986b02b985924fa42960b6eec
 F test/fts5ea.test afaf3497b43add578384dc1fd26b0342738abe87
 F test/full.test 6b3c8fb43c6beab6b95438c1675374b95fab245d
 F test/func.test ae97561957aba6ca9e3a7b8a13aac41830d701ef
@@ -1206,7 +1207,7 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1
 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
 F tool/warnings.sh 0abfd78ceb09b7f7c27c688c8e3fe93268a13b32
 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
-P 83491c56661ca78f96020ba68184bb3fb19e674f
-R 14f6d2fef178e1939a8a8ad40901ad6e
+P bb4a37b53de60da9ec8b9317eec14afa99690828
+R efa8336057fcd1502b8cbf6d797345c7
 U dan
-Z bcf001d05010ed5ade28bb9d53b64e80
+Z dc9192af5fedea55ad78c651e89e8c7b
index 2e718d56bfb7b9f944cb6a8766b01685c4a848ed..a405c3cc14fd513222ff662244bd1fa7246545fd 100644 (file)
@@ -1 +1 @@
-bb4a37b53de60da9ec8b9317eec14afa99690828
\ No newline at end of file
+9c1697a2aa1f601e6eb11704abe63a73c8105447
\ No newline at end of file
index 9b977e54aeac38c12de13c3a57bcaa7ed1eee8cd..92a107cd7735ce217ffa94cb09ae5b2cc3bf490b 100644 (file)
@@ -3698,6 +3698,7 @@ static void init_all(Tcl_Interp *interp){
     extern int Sqlitemultiplex_Init(Tcl_Interp*);
     extern int SqliteSuperlock_Init(Tcl_Interp*);
     extern int SqlitetestSyscall_Init(Tcl_Interp*);
+    extern int Fts5tcl_Init(Tcl_Interp *);
 
 #if defined(SQLITE_ENABLE_FTS3) || defined(SQLITE_ENABLE_FTS4)
     extern int Sqlitetestfts3_Init(Tcl_Interp *interp);
@@ -3740,6 +3741,7 @@ static void init_all(Tcl_Interp *interp){
     Sqlitemultiplex_Init(interp);
     SqliteSuperlock_Init(interp);
     SqlitetestSyscall_Init(interp);
+    Fts5tcl_Init(interp);
 
 #if defined(SQLITE_ENABLE_FTS3) || defined(SQLITE_ENABLE_FTS4)
     Sqlitetestfts3_Init(interp);
index 63e14a13a0e39a002bf19719c4432edae15aaea0..7739093c5617a491649683aa04f9b8a125ef904a 100644 (file)
@@ -39,5 +39,106 @@ do_execsql_test 1.3 {
   SELECT * FROM ft1_config;
 } {pgsz 64}
 
+#--------------------------------------------------------------------------
+# Test the logic for parsing the rank() function definition.
+#
+foreach {tn defn} {
+  1 "fname()"
+  2 "fname(1)"
+  3 "fname(1,2)"
+  4 "fname(null,NULL,nUlL)"
+  5 "  fname    (   null  ,   NULL  ,  nUlL  )  "
+  6 "fname('abc')"
+  7 "fname('a''bc')"
+  8 "fname('''abc')"
+  9 "fname('abc''')"
+
+  7 "fname(  'a''bc'  )"
+  8 "fname('''abc'  )"
+  9 "fname(  'abc''' )"
+
+  10 "fname(X'1234ab')"
+
+  11 "myfunc(1.2)"
+  12 "myfunc(-1.0)"
+  13 "myfunc(.01,'abc')"
+} {
+  do_execsql_test 2.1.$tn {
+    INSERT INTO ft1(ft1, rank) VALUES('rank', $defn);
+  }
+}
+
+foreach {tn defn} {
+  1 ""
+  2 "fname"
+  3 "fname(X'234ab')"
+  4 "myfunc(-1.,'abc')"
+} {
+  do_test 2.2.$tn {
+    catchsql { INSERT INTO ft1(ft1, rank) VALUES('rank', $defn) }
+  } {1 {SQL logic error or missing database}}
+}
+
+#-------------------------------------------------------------------------
+#
+
+do_execsql_test 3.1 {
+  CREATE VIRTUAL TABLE t1 USING fts5(x);
+  INSERT INTO t1 VALUES('q w e r t y');
+  INSERT INTO t1 VALUES('y t r e w q');
+}
+
+proc argtest {cmd args} { return $args }
+sqlite3_fts5_create_function db argtest argtest
+
+do_execsql_test 3.2.1 {
+  SELECT argtest(t1, 123) FROM t1 WHERE t1 MATCH 'q'
+} {123 123}
+
+do_execsql_test 3.2.2 {
+  SELECT argtest(t1, 123, 456) FROM t1 WHERE t1 MATCH 'q'
+} {{123 456} {123 456}}
+
+proc rowidtest {cmd} { $cmd xRowid }
+sqlite3_fts5_create_function db rowidtest rowidtest
+
+do_execsql_test 3.3.1 {
+  SELECT rowidtest(t1) FROM t1 WHERE t1 MATCH 'q'
+} {2 1}
+
+proc insttest {cmd} {
+  set res [list]
+  for {set i 0} {$i < [$cmd xInstCount]} {incr i} {
+    lappend res [$cmd xInst $i]
+  }
+  set res
+}
+sqlite3_fts5_create_function db insttest insttest
+
+do_execsql_test 3.4.1 {
+  SELECT insttest(t1) FROM t1 WHERE t1 MATCH 'q'
+} {
+  {{0 0 5}} 
+  {{0 0 0}}
+}
+
+do_execsql_test 3.4.2 {
+  SELECT insttest(t1) FROM t1 WHERE t1 MATCH 'r+e OR w'
+} {
+  {{0 0 2} {1 0 4}} 
+  {{1 0 1}}
+}
+
+proc coltest {cmd} {
+  list [$cmd xColumnSize 0] [$cmd xColumnText 0]
+}
+sqlite3_fts5_create_function db coltest coltest
+
+do_execsql_test 3.4.1 {
+  SELECT coltest(t1) FROM t1 WHERE t1 MATCH 'q'
+} {
+  {6 {y t r e w q}} {6 {q w e r t y}}
+}
+
 finish_test