]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Add a single-argument form to the CARRAY table-valued function, with
authordrh <drh@noemail.net>
Tue, 17 Nov 2020 14:41:37 +0000 (14:41 +0000)
committerdrh <drh@noemail.net>
Tue, 17 Nov 2020 14:41:37 +0000 (14:41 +0000)
content bound using the sqlite3_carray_bind() interface that is included
with the extension.

FossilOrigin-Name: 7b229cb1202be203a87b8f47d284313f357deb1e6dfeb94bba7b46744c33512e

ext/misc/carray.c
ext/misc/carray.h [new file with mode: 0644]
manifest
manifest.uuid
src/test1.c
test/carray01.test [new file with mode: 0644]

index 32fec3406ea1aa10fb40a3214d14f6df169db13b..7c892b8a68e7317704c37aea1fd93f61d16203b1 100644 (file)
@@ -57,20 +57,31 @@ SQLITE_EXTENSION_INIT1
 #include <assert.h>
 #include <string.h>
 
+/* Allowed values for the mFlags parameter to sqlite3_carray_bind().
+** Must exactly match the definitions in carray.h.
+*/
+#define CARRAY_INT32     0      /* Data is 32-bit signed integers */
+#define CARRAY_INT64     1      /* Data is 64-bit signed integers */
+#define CARRAY_DOUBLE    2      /* Data is doubles */
+#define CARRAY_TEXT      3      /* Data is char* */
+
 #ifndef SQLITE_OMIT_VIRTUALTABLE
 
 /*
-** Allowed datatypes
+** Names of allowed datatypes
 */
-#define CARRAY_INT32    0
-#define CARRAY_INT64    1
-#define CARRAY_DOUBLE   2
-#define CARRAY_TEXT     3
+static const char *azType[] = { "int32", "int64", "double", "char*" };
 
 /*
-** Names of types
+** Structure used to hold the sqlite3_carray_bind() information
 */
-static const char *azType[] = { "int32", "int64", "double", "char*" };
+typedef struct carray_bind carray_bind;
+struct carray_bind {
+  void *aData;                /* The data */
+  int nData;                  /* Number of elements */
+  int mFlags;                 /* Control flags */
+  void (*xDel)(void*);        /* Destructor for aData */
+};
 
 
 /* carray_cursor is a subclass of sqlite3_vtab_cursor which will
@@ -239,28 +250,39 @@ static int carrayFilter(
   int argc, sqlite3_value **argv
 ){
   carray_cursor *pCur = (carray_cursor *)pVtabCursor;
-  if( idxNum ){
-    pCur->pPtr = sqlite3_value_pointer(argv[0], "carray");
-    pCur->iCnt = pCur->pPtr ? sqlite3_value_int64(argv[1]) : 0;
-    if( idxNum<3 ){
-      pCur->eType = CARRAY_INT32;
-    }else{
-      unsigned char i;
-      const char *zType = (const char*)sqlite3_value_text(argv[2]);
-      for(i=0; i<sizeof(azType)/sizeof(azType[0]); i++){
-        if( sqlite3_stricmp(zType, azType[i])==0 ) break;
-      }
-      if( i>=sizeof(azType)/sizeof(azType[0]) ){
-        pVtabCursor->pVtab->zErrMsg = sqlite3_mprintf(
-          "unknown datatype: %Q", zType);
-        return SQLITE_ERROR;
+  pCur->pPtr = 0;
+  pCur->iCnt = 0;
+  switch( idxNum ){
+    case 1: {
+      carray_bind *pBind = sqlite3_value_pointer(argv[0], "carray-bind");
+      if( pBind==0 ) break;
+      pCur->pPtr = pBind->aData;
+      pCur->iCnt = pBind->nData;
+      pCur->eType = pBind->mFlags & 0x03;
+      break;
+    }
+    case 2:
+    case 3: {
+      pCur->pPtr = sqlite3_value_pointer(argv[0], "carray");
+      pCur->iCnt = pCur->pPtr ? sqlite3_value_int64(argv[1]) : 0;
+      if( idxNum<3 ){
+        pCur->eType = CARRAY_INT32;
       }else{
-        pCur->eType = i;
+        unsigned char i;
+        const char *zType = (const char*)sqlite3_value_text(argv[2]);
+        for(i=0; i<sizeof(azType)/sizeof(azType[0]); i++){
+          if( sqlite3_stricmp(zType, azType[i])==0 ) break;
+        }
+        if( i>=sizeof(azType)/sizeof(azType[0]) ){
+          pVtabCursor->pVtab->zErrMsg = sqlite3_mprintf(
+            "unknown datatype: %Q", zType);
+          return SQLITE_ERROR;
+        }else{
+          pCur->eType = i;
+        }
       }
+      break;
     }
-  }else{
-    pCur->pPtr = 0;
-    pCur->iCnt = 0;
   }
   pCur->iRowid = 1;
   return SQLITE_OK;
@@ -275,9 +297,16 @@ static int carrayFilter(
 ** In this implementation idxNum is used to represent the
 ** query plan.  idxStr is unused.
 **
-** idxNum is 2 if the pointer= and count= constraints exist,
-** 3 if the ctype= constraint also exists, and is 0 otherwise.
-** If idxNum is 0, then carray becomes an empty table.
+** idxNum is:
+**
+**    1    If only the pointer= constraint exists.  In this case, the
+**         parameter must be bound using sqlite3_carray_bind().
+**
+**    2    if the pointer= and count= constraints exist.
+**
+**    3    if the ctype= constraint also exists.
+**
+** idxNum is 0 otherwise and carray becomes an empty table.
 */
 static int carrayBestIndex(
   sqlite3_vtab *tab,
@@ -305,18 +334,21 @@ static int carrayBestIndex(
         break;
     }
   }
-  if( ptrIdx>=0 && cntIdx>=0 ){
+  if( ptrIdx>=0 ){
     pIdxInfo->aConstraintUsage[ptrIdx].argvIndex = 1;
     pIdxInfo->aConstraintUsage[ptrIdx].omit = 1;
-    pIdxInfo->aConstraintUsage[cntIdx].argvIndex = 2;
-    pIdxInfo->aConstraintUsage[cntIdx].omit = 1;
     pIdxInfo->estimatedCost = (double)1;
     pIdxInfo->estimatedRows = 100;
-    pIdxInfo->idxNum = 2;
-    if( ctypeIdx>=0 ){
-      pIdxInfo->aConstraintUsage[ctypeIdx].argvIndex = 3;
-      pIdxInfo->aConstraintUsage[ctypeIdx].omit = 1;
-      pIdxInfo->idxNum = 3;
+    pIdxInfo->idxNum = 1;
+    if( cntIdx>=0 ){
+      pIdxInfo->aConstraintUsage[cntIdx].argvIndex = 2;
+      pIdxInfo->aConstraintUsage[cntIdx].omit = 1;
+      pIdxInfo->idxNum = 2;
+      if( ctypeIdx>=0 ){
+        pIdxInfo->aConstraintUsage[ctypeIdx].argvIndex = 3;
+        pIdxInfo->aConstraintUsage[ctypeIdx].omit = 1;
+        pIdxInfo->idxNum = 3;
+      }
     }
   }else{
     pIdxInfo->estimatedCost = (double)2147483647;
@@ -353,6 +385,89 @@ static sqlite3_module carrayModule = {
   0,                         /* xRename */
 };
 
+/*
+** Destructor for the carray_bind object
+*/
+static void carrayBindDel(void *pPtr){
+  carray_bind *p = (carray_bind*)pPtr;
+  if( p->xDel!=SQLITE_STATIC ){
+     p->xDel(p->aData);
+  }
+  sqlite3_free(p);
+}
+
+/*
+** Invoke this interface in order to bind to the single-argument
+** version of CARRAY().
+*/
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+int sqlite3_carray_bind(
+  sqlite3_stmt *pStmt,
+  int idx,
+  void *aData,
+  int nData,
+  int mFlags,
+  void (*xDestroy)(void*)
+){
+  carray_bind *pNew;
+  int i;
+  pNew = sqlite3_malloc64(sizeof(*pNew));
+  if( pNew==0 ){
+    if( xDestroy!=SQLITE_STATIC && xDestroy!=SQLITE_TRANSIENT ){
+      xDestroy(aData);
+    }
+    return SQLITE_NOMEM;
+  }
+  pNew->nData = nData;
+  pNew->mFlags = mFlags;
+  if( xDestroy==SQLITE_TRANSIENT ){
+    sqlite3_int64 sz = nData;
+    switch( mFlags & 0x03 ){
+      case CARRAY_INT32:   sz *= 4;              break;
+      case CARRAY_INT64:   sz *= 8;              break;
+      case CARRAY_DOUBLE:  sz *= 8;              break;
+      case CARRAY_TEXT:    sz *= sizeof(char*);  break;
+    }
+    if( (mFlags & 0x03)==CARRAY_TEXT ){
+      for(i=0; i<nData; i++){
+        const char *z = ((char**)aData)[i];
+        if( z ) sz += strlen(z) + 1;
+      }
+    } 
+    pNew->aData = sqlite3_malloc64( sz );
+    if( pNew->aData==0 ){
+      sqlite3_free(pNew);
+      return SQLITE_NOMEM;
+    }
+    if( (mFlags & 0x03)==CARRAY_TEXT ){
+      char **az = (char**)pNew->aData;
+      char *z = (char*)&az[nData];
+      for(i=0; i<nData; i++){
+        const char *zData = ((char**)aData)[i];
+        sqlite3_int64 n;
+        if( zData==0 ){
+          az[i] = 0;
+          continue;
+        }
+        az[i] = z;
+        n = strlen(zData);
+        memcpy(z, zData, n+1);
+        z += n+1;
+      }
+    }else{
+      memcpy(pNew->aData, aData, sz*nData);
+    }
+    pNew->xDel = sqlite3_free;
+  }else{
+    pNew->aData = aData;
+    pNew->xDel = xDestroy;
+  }
+  sqlite3_bind_pointer(pStmt, idx, pNew, "carray-bind", carrayBindDel);
+}
+
+
 /*
 ** For testing purpose in the TCL test harness, we need a method for
 ** setting the pointer value.  The inttoptr(X) SQL function accomplishes
diff --git a/ext/misc/carray.h b/ext/misc/carray.h
new file mode 100644 (file)
index 0000000..e490bf2
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+** Interface definitions for the CARRAY table-valued function
+** extension.
+*/
+
+/* Use this interface to bind an array to the single-argument version
+** of CARRAY().
+*/
+int sqlite3_carray_bind(
+  sqlite3_stmt *pStmt,        /* Statement to be bound */
+  int i,                      /* Parameter index */
+  void *aData,                /* Pointer to array data */
+  int nData,                  /* Number of data elements */
+  int mFlags,                 /* CARRAY flags */
+  void (*xDel)(void*)         /* Destructgor for aData*/
+);
+
+/* Allowed values for the mFlags parameter to sqlite3_carray_bind().
+*/
+#define CARRAY_INT32     0    /* Data is 32-bit signed integers */
+#define CARRAY_INT64     1    /* Data is 64-bit signed integers */
+#define CARRAY_DOUBLE    2    /* Data is doubles */
+#define CARRAY_TEXT      3    /* Data is char* */
index 8fefc6838d126b7be18108886eed67cf52c5f163..b26485fc0ed00109df13699bb9642385c9073333 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Improved\sdiagnostics\soutput\swith\s".wheretrace\s0x800".\s\sNo\schanges\sto\nnon-debug\sbuilds.
-D 2020-11-12T18:16:01.196
+C Add\sa\ssingle-argument\sform\sto\sthe\sCARRAY\stable-valued\sfunction,\swith\ncontent\sbound\susing\sthe\ssqlite3_carray_bind()\sinterface\sthat\sis\sincluded\nwith\sthe\sextension.
+D 2020-11-17T14:41:37.097
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -287,7 +287,8 @@ F ext/misc/anycollseq.c 5ffdfde9829eeac52219136ad6aa7cd9a4edb3b15f4f2532de52f4a2
 F ext/misc/appendvfs.c 55121d311d408ba9c62c3cfa367408887638f02f9522dd9859891d0ee69a7eba
 F ext/misc/blobio.c a867c4c4617f6ec223a307ebfe0eabb45e0992f74dd47722b96f3e631c0edb2a
 F ext/misc/btreeinfo.c d28ce349b40054eaa9473e835837bad7a71deec33ba13e39f963d50933bfa0f9
-F ext/misc/carray.c 91e9a7f512fda934894bed30464552fffa7d3073b5be04189ae0bd0c59f26bfd
+F ext/misc/carray.c 6b3cb5217466f687f48e3c3a87c43fb628ae80db68a95e30bbd8074d099c29a4
+F ext/misc/carray.h de74ac70b2338f416723f7d538026e8ec0b7f1d388319f8f140c9a4d7677f02e
 F ext/misc/cksumvfs.c 910848f3d9739908cf77cad66a76dd45001546f46ff5ef4ca5c20c5476e77e98
 F ext/misc/closure.c dbfd8543b2a017ae6b1a5843986b22ddf99ff126ec9634a2f4047cd14c85c243
 F ext/misc/completion.c 6dafd7f4348eecc7be9e920d4b419d1fb2af75d938cd9c59a20cfe8beb2f22b9
@@ -547,7 +548,7 @@ F src/sqliteLimit.h d7323ffea5208c6af2734574bae933ca8ed2ab728083caa117c9738581a3
 F src/status.c 4b8bc2a6905163a38b739854a35b826c737333fab5b1f8e03fa7eb9a4799c4c1
 F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1
 F src/tclsqlite.c 986b6391f02cd9b53c1d688be55899f6ffddeb8e8014cd83c1b73ff912579a71
-F src/test1.c 9e52fb797bf74fa327295df38881aa3ade0824bfb0c14abd0719e555b169fd55
+F src/test1.c 385533d17fb06529c909defc73ef47dd2712dc198eedff94fc6df5bc23687c71
 F src/test2.c 3efb99ab7f1fc8d154933e02ae1378bac9637da5
 F src/test3.c 61798bb0d38b915067a8c8e03f5a534b431181f802659a6616f9b4ff7d872644
 F src/test4.c 7c4420e01c577b5c4add2cb03119743b1a357543d347773b9e717195ea967159
@@ -740,6 +741,7 @@ F test/capi3b.test efb2b9cfd127efa84433cd7a2d72ce0454ae0dc4
 F test/capi3c.test 54e2dc0c8fd7c34ad1590d1be6864397da2438c95a9f5aee2f8fbc60c112e44b
 F test/capi3d.test aba917805573a03deed961a21f07a5a84505ad0a616f7e3fc1508844a15bccc4
 F test/capi3e.test 3d49c01ef2a1a55f41d73cba2b23b5059ec460fe
+F test/carray01.test 3f2658bbddd75a013735a296ae2178ff441aca3f00ba623cfbae00b732ede792
 F test/cast.test 336fa21989b5170ebcaf90c24266be22dd97b3e23d1fad5ecf6ad4efb04c4423
 F test/cffault.test 9d6b20606afe712374952eec4f8fd74b1a8097ef
 F test/check.test 4a2a91ed67eee84a6be16057c48d5198b6fb24849cd6da6cd855981de3fbb416
@@ -1883,7 +1885,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
 F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P 572f1ed59d29e74f810c74ef9e72ebc94c2d3e04befc03a1f88034f04a9c60a8
-R 4901674d935be395f9df3d7654b8445e
+P 772ae83c61c87a9004a614d8ec120ba843286bff1edbd20b987fd592ced84d79
+R dda1ed32688d23b76f833a4d9835bd2c
 U drh
-Z 60f97595458aa54172137f4d47d1e884
+Z 69dc23c53e350f7672399c3847d987a1
index 3d1e246f078aaa031b5861988021d808807bc3b7..25aaf41a1e05ab3f8bd5ddfcdec8733114570c1d 100644 (file)
@@ -1 +1 @@
-772ae83c61c87a9004a614d8ec120ba843286bff1edbd20b987fd592ced84d79
\ No newline at end of file
+7b229cb1202be203a87b8f47d284313f357deb1e6dfeb94bba7b46744c33512e
\ No newline at end of file
index 04b9f7a0113349ac03d7420c02feeac34e9b24cd..a7fb07cb4ed5e92e6e4b6a7781cca9101986195a 100644 (file)
@@ -3940,7 +3940,7 @@ static int SQLITE_TCLAPI test_bind_blob(
     char zBuf[200];
     sqlite3_snprintf(sizeof(zBuf), zBuf,
                      "cannot use %d blob bytes, have %d", bytes, len);
-    Tcl_AppendResult(interp, zBuf, -1);
+    Tcl_AppendResult(interp, zBuf, (char*)0);
     return TCL_ERROR;
   }
 
@@ -3953,6 +3953,193 @@ static int SQLITE_TCLAPI test_bind_blob(
   return TCL_OK;
 }
 
+
+/*
+** sqlite3_carray_bind [options...] STMT NAME VALUE ...
+**
+** Options:
+**    -transient
+**    -static
+**    -int32
+**    -int64
+**    -double
+**    -text
+**
+** Each call clears static data.  Called with no options does nothing
+** but clear static data.
+*/
+static int SQLITE_TCLAPI test_carray_bind(
+  void * clientData,
+  Tcl_Interp *interp,
+  int objc,
+  Tcl_Obj *CONST objv[]
+){
+  sqlite3_stmt *pStmt;
+  int eType = 0;   /* CARRAY_INT32 */
+  int nData = 0;
+  void *aData = 0;
+  int isTransient = 0;
+  int isStatic = 0;
+  int idx;
+  int i, j;
+  int rc;
+  void (*xDel)(void*) = sqlite3_free;
+  static void *aStaticData = 0;
+  static int nStaticData = 0;
+  static int eStaticType = 0;
+  extern int sqlite3_carray_bind(
+    sqlite3_stmt *pStmt,
+    int i,
+    void *aData,
+    int nData,
+    int mFlags,
+    void (*xDestroy)(void*)
+  );
+  
+  if( aStaticData ){
+    /* Always clear preexisting static data on every call */
+    if( eStaticType==3 ){
+      for(i=0; i<nStaticData; i++){
+        sqlite3_free(((char**)aStaticData)[i]);
+      }
+    }
+    sqlite3_free(aStaticData);
+    aStaticData = 0;
+    nStaticData = 0;
+    eStaticType = 0;
+  }
+  if( objc==1 ) return TCL_OK;
+
+  for(i=1; i<objc && Tcl_GetString(objv[i])[0]=='-'; i++){
+    const char *z = Tcl_GetString(objv[i]);
+    if( strcmp(z, "-transient")==0 ){
+      isTransient = 1;
+      xDel = SQLITE_TRANSIENT;
+    }else
+    if( strcmp(z, "-static")==0 ){
+      isStatic = 1;
+      xDel = SQLITE_STATIC;
+    }else
+    if( strcmp(z, "-int32")==0 ){
+      eType = 0;  /* CARRAY_INT32 */
+    }else
+    if( strcmp(z, "-int64")==0 ){
+      eType = 1;  /* CARRAY_INT64 */
+    }else
+    if( strcmp(z, "-double")==0 ){
+      eType = 2;  /* CARRAY_DOUBLE */
+    }else
+    if( strcmp(z, "-text")==0 ){
+      eType = 3;  /* CARRAY_TEXT */
+    }else
+    if( strcmp(z, "--")==0 ){
+      break;
+    }else
+    {
+      Tcl_AppendResult(interp, "unknown option: ", z, (char*)0);
+      return TCL_ERROR;
+    }
+  }
+  if( eType==3 && !isStatic && !isTransient ){
+    Tcl_AppendResult(interp, "text data must be either -static or -transient",
+                     (char*)0);
+    return TCL_ERROR;
+  }
+  if( isStatic && isTransient ){
+    Tcl_AppendResult(interp, "cannot be both -static and -transient",
+                     (char*)0);
+    return TCL_ERROR;
+  }
+  if( objc-i < 2 ){
+    Tcl_WrongNumArgs(interp, 1, objv, "[OPTIONS] STMT IDX VALUE ...");
+    return TCL_ERROR;
+  }
+  if( getStmtPointer(interp, Tcl_GetString(objv[i]), &pStmt) ) return TCL_ERROR;
+  i++;
+  if( Tcl_GetIntFromObj(interp, objv[i], &idx) ) return TCL_ERROR;
+  i++;
+  nData = objc - i;
+  switch( eType + 4*(nData<=0) ){
+    case 0: { /* INT32 */
+      int *a = sqlite3_malloc( sizeof(int)*nData );
+      if( a==0 ){ rc = SQLITE_NOMEM; goto carray_bind_done; }
+      for(j=0; j<nData; j++){
+        int v;
+        if( Tcl_GetIntFromObj(interp, objv[i+j], &v) ){
+          sqlite3_free(a);
+          return TCL_ERROR;
+        }
+        a[j] = v;
+      }
+      aData = a;
+      break;
+    }
+    case 1: { /* INT64 */
+      sqlite3_int64 *a = sqlite3_malloc( sizeof(sqlite3_int64)*nData );
+      if( a==0 ){ rc = SQLITE_NOMEM; goto carray_bind_done; }
+      for(j=0; j<nData; j++){
+        Tcl_WideInt v;
+        if( Tcl_GetWideIntFromObj(interp, objv[i+j], &v) ){
+          sqlite3_free(a);
+          return TCL_ERROR;
+        }
+        a[j] = v;
+      }
+      aData = a;
+      break;
+    }
+    case 2: { /* DOUBLE */
+      double *a = sqlite3_malloc( sizeof(double)*nData );
+      if( a==0 ){ rc = SQLITE_NOMEM; goto carray_bind_done; }
+      for(j=0; j<nData; j++){
+        double v;
+        if( Tcl_GetDoubleFromObj(interp, objv[i+j], &v) ){
+          sqlite3_free(a);
+          return TCL_ERROR;
+        }
+        a[j] = v;
+      }
+      aData = a;
+      break;
+    }
+    case 3: { /* TEXT */
+      char **a = sqlite3_malloc( sizeof(char*)*nData );
+      if( a==0 ){ rc = SQLITE_NOMEM; goto carray_bind_done; }
+      for(j=0; j<nData; j++){
+        const char *v = Tcl_GetString(objv[i+j]);
+        a[j] = sqlite3_mprintf("%s", v);
+      }
+      aData = a;
+      break;
+    }
+    case 4: { /* nData==0 */
+      aData = "";
+      xDel = SQLITE_STATIC;
+      isTransient = 0;
+      isStatic = 0;
+      break;
+    }
+  }
+  if( isStatic ){
+    aStaticData = aData;
+    nStaticData = nData;
+    eStaticType = eType;
+  }
+  rc = sqlite3_carray_bind(pStmt, idx, aData, nData, eType, xDel);
+  if( isTransient ){
+    if( eType==3 ){
+      for(i=0; i<nData; i++) sqlite3_free(((char**)aData)[i]);
+    }
+    sqlite3_free(aData);
+  }
+carray_bind_done:
+  if( rc ){
+    Tcl_AppendResult(interp, sqlite3_errstr(rc), (char*)0);
+    return TCL_ERROR;
+  }
+  return TCL_OK;
+}
+
 /*
 ** Usage:   sqlite3_bind_parameter_count  STMT
 **
@@ -8029,6 +8216,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
      { "sqlite3_bind_text",             test_bind_text     ,0 },
      { "sqlite3_bind_text16",           test_bind_text16   ,0 },
      { "sqlite3_bind_blob",             test_bind_blob     ,0 },
+     { "sqlite3_carray_bind",           test_carray_bind   ,0 },
      { "sqlite3_bind_parameter_count",  test_bind_parameter_count, 0},
      { "sqlite3_bind_parameter_name",   test_bind_parameter_name,  0},
      { "sqlite3_bind_parameter_index",  test_bind_parameter_index, 0},
diff --git a/test/carray01.test b/test/carray01.test
new file mode 100644 (file)
index 0000000..6db4f5e
--- /dev/null
@@ -0,0 +1,100 @@
+# 2020-11-17
+#
+# 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 tests for CARRAY extension
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set testprefix carray01
+
+ifcapable !vtab {
+  finish_test
+  return
+}
+load_static_extension db carray
+
+# Parameter $stmt must be a prepared statement created using
+# the sqlite3_prepare_v2 command and with parameters fullly bound.
+# This routine simply runs the statement, gathers the result, and
+# returns a list containing the result.
+#
+# If the optional second argument is true, then the stmt is finalized
+# after it is run.
+#
+proc run_stmt {stmt {finalizeFlag 0}} {
+  set r {}
+  while {[sqlite3_step $stmt]=="SQLITE_ROW"} {
+    for {set i 0} {$i<[sqlite3_data_count $stmt]} {incr i} {
+      lappend r [sqlite3_column_text $stmt $i]
+    }
+  }
+  if {$finalizeFlag} {
+    sqlite3_finalize $stmt
+  } else {
+    sqlite3_reset $stmt
+  }
+  return $r
+}
+
+do_test 100 {
+  set STMT [sqlite3_prepare_v2 db {SELECT 5 IN carray(?3)} -1]
+  sqlite3_carray_bind $STMT 3 1 2 3 4 5 6 7
+  run_stmt $STMT 0
+} {1}
+do_test 101 {
+  sqlite3_carray_bind -static $STMT 3 1 2 3 4 5 6 7
+  run_stmt $STMT 0
+} {1}
+do_test 110 {
+  sqlite3_carray_bind $STMT 3 1 2 3 4 6 7
+  run_stmt $STMT 0
+} {0}
+do_test 120 {
+  sqlite3_carray_bind -int64 $STMT 3 1 2 3 4 5 6 7
+  run_stmt $STMT 0
+} {1}
+do_test 130 {
+  sqlite3_carray_bind -int64 $STMT 3 1 2 3 4 6 7
+  run_stmt $STMT 0
+} {0}
+do_test 131 {
+  sqlite3_carray_bind -int64 -static $STMT 3 1 2 3 4 6 7
+  run_stmt $STMT 0
+} {0}
+do_test 140 {
+  sqlite3_carray_bind -double $STMT 3 1 2 3 4 5 6 7
+  run_stmt $STMT 0
+} {1}
+do_test 150 {
+  sqlite3_carray_bind -double $STMT 3 1 2 3 4 6 7
+  run_stmt $STMT 0
+} {0}
+do_test 160 {
+  sqlite3_carray_bind -double $STMT 3 1 2 3 4 5 6 7
+  run_stmt $STMT 0
+} {1}
+do_test 170 {
+  sqlite3_carray_bind -text -static $STMT 3 1 2 3 4 6 7
+  run_stmt $STMT 0
+} {0}
+do_test 180 {
+  sqlite3_carray_bind -text -transient $STMT 3 1 2 3 4 5 6 7
+  run_stmt $STMT 0
+} {0}
+do_test 190 {
+  sqlite3_carray_bind $STMT 3
+  run_stmt $STMT 0
+} {0}
+
+sqlite3_finalize $STMT
+
+finish_test