Index: ext/fts3/fts3_tokenizer.c
==================================================================
--- ext/fts3/fts3_tokenizer.c
+++ ext/fts3/fts3_tokenizer.c
@@ -77,11 +77,11 @@
zName = sqlite3_value_text(argv[0]);
nName = sqlite3_value_bytes(argv[0])+1;
if( argc==2 ){
- if( fts3TokenizerEnabled(context) ){
+ if( fts3TokenizerEnabled(context) || sqlite3_value_frombind(argv[1]) ){
void *pOld;
int n = sqlite3_value_bytes(argv[1]);
if( zName==0 || n!=sizeof(pPtr) ){
sqlite3_result_error(context, "argument type mismatch", -1);
return;
@@ -104,11 +104,11 @@
sqlite3_result_error(context, zErr, -1);
sqlite3_free(zErr);
return;
}
}
- if( fts3TokenizerEnabled(context) ){
+ if( fts3TokenizerEnabled(context) || sqlite3_value_frombind(argv[0]) ){
sqlite3_result_blob(context, (void *)&pPtr, sizeof(pPtr), SQLITE_TRANSIENT);
}
}
int sqlite3Fts3IsIdChar(char c){
Index: src/sqlite.h.in
==================================================================
--- src/sqlite.h.in
+++ src/sqlite.h.in
@@ -4977,10 +4977,12 @@
**
sqlite3_value_frombind
+** | → | True if value originated a bound parameter
**
**
** Details:
**
** These routines extract type, size, and content information from
@@ -5037,10 +5039,15 @@
** was unchanging). ^Within an [xUpdate] method, any value for which
** sqlite3_value_nochange(X) is true will in all other respects appear
** to be a NULL value. If sqlite3_value_nochange(X) is invoked anywhere other
** than within an [xUpdate] method call for an UPDATE statement, then
** the return value is arbitrary and meaningless.
+**
+** ^The sqlite3_value_frombind(X) interface returns non-zero if the
+** value X originated from one of the [sqlite3_bind_int|sqlite3_bind()]
+** interfaces. ^If X comes from an SQL literal value, or a table column,
+** and expression, then sqlite3_value_frombind(X) returns zero.
**
** Please pay particular attention to the fact that the pointer returned
** from [sqlite3_value_blob()], [sqlite3_value_text()], or
** [sqlite3_value_text16()] can be invalidated by a subsequent call to
** [sqlite3_value_bytes()], [sqlite3_value_bytes16()], [sqlite3_value_text()],
@@ -5083,10 +5090,11 @@
int sqlite3_value_bytes(sqlite3_value*);
int sqlite3_value_bytes16(sqlite3_value*);
int sqlite3_value_type(sqlite3_value*);
int sqlite3_value_numeric_type(sqlite3_value*);
int sqlite3_value_nochange(sqlite3_value*);
+int sqlite3_value_frombind(sqlite3_value*);
/*
** CAPI3REF: Finding The Subtype Of SQL Values
** METHOD: sqlite3_value
**
Index: src/test_func.c
==================================================================
--- src/test_func.c
+++ src/test_func.c
@@ -627,10 +627,28 @@
int argc,
sqlite3_value **argv
){
sqlite3_result_int(context, (int)sqlite3_value_subtype(argv[0]));
}
+
+/* test_frombind(A,B,C,...)
+**
+** Return an integer bitmask that has a bit set for every argument
+** (up to the first 63 arguments) that originates from a bind a parameter.
+*/
+static void test_frombind(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ sqlite3_uint64 m = 0;
+ int i;
+ for(i=0; iaVar[pOp->p1 - 1];
if( sqlite3VdbeMemTooBig(pVar) ){
goto too_big;
}
pOut = &aMem[pOp->p2];
- sqlite3VdbeMemShallowCopy(pOut, pVar, MEM_Static);
+ if( VdbeMemDynamic(pOut) ) sqlite3VdbeMemSetNull(pOut);
+ memcpy(pOut, pVar, MEMCELLSIZE);
+ pOut->flags &= ~(MEM_Dyn|MEM_Ephem);
+ pOut->flags |= MEM_Static|MEM_FromBind;
UPDATE_MAX_BLOBSIZE(pOut);
break;
}
/* Opcode: Move P1 P2 P3 * *
Index: src/vdbeInt.h
==================================================================
--- src/vdbeInt.h
+++ src/vdbeInt.h
@@ -244,11 +244,11 @@
#define MEM_Str 0x0002 /* Value is a string */
#define MEM_Int 0x0004 /* Value is an integer */
#define MEM_Real 0x0008 /* Value is a real number */
#define MEM_Blob 0x0010 /* Value is a BLOB */
#define MEM_AffMask 0x001f /* Mask of affinity bits */
-/* Available 0x0020 */
+#define MEM_FromBind 0x0020 /* Value originates from sqlite3_bind() */
/* Available 0x0040 */
#define MEM_Undefined 0x0080 /* Value is undefined */
#define MEM_Cleared 0x0100 /* NULL set by OP_Null, not from data */
#define MEM_TypeMask 0xc1ff /* Mask of type bits */
Index: src/vdbeapi.c
==================================================================
--- src/vdbeapi.c
+++ src/vdbeapi.c
@@ -272,10 +272,15 @@
/* Return true if a parameter to xUpdate represents an unchanged column */
int sqlite3_value_nochange(sqlite3_value *pVal){
return (pVal->flags&(MEM_Null|MEM_Zero))==(MEM_Null|MEM_Zero);
}
+
+/* Return true if a parameter value originated from an sqlite3_bind() */
+int sqlite3_value_frombind(sqlite3_value *pVal){
+ return (pVal->flags&MEM_FromBind)!=0;
+}
/* Make a copy of an sqlite3_value object
*/
sqlite3_value *sqlite3_value_dup(const sqlite3_value *pOrig){
sqlite3_value *pNew;
Index: src/vdbemem.c
==================================================================
--- src/vdbemem.c
+++ src/vdbemem.c
@@ -55,11 +55,11 @@
assert( ((p->flags&MEM_Dyn)!=0 ? 1 : 0) +
((p->flags&MEM_Ephem)!=0 ? 1 : 0) +
((p->flags&MEM_Static)!=0 ? 1 : 0) <= 1 );
/* No other bits set */
- assert( (p->flags & ~(MEM_Null|MEM_Term|MEM_Subtype
+ assert( (p->flags & ~(MEM_Null|MEM_Term|MEM_Subtype|MEM_FromBind
|MEM_Dyn|MEM_Ephem|MEM_Static))==0 );
}else{
/* A pure NULL might have other flags, such as MEM_Static, MEM_Dyn,
** MEM_Ephem, MEM_Cleared, or MEM_Subtype */
}
Index: test/fts3atoken.test
==================================================================
--- test/fts3atoken.test
+++ test/fts3atoken.test
@@ -84,15 +84,52 @@
INSERT INTO t1(content) VALUES('That the colt from ol regret had got');
SELECT content FROM t1 WHERE content MATCH 'movement'
}
} {{There was movement at the station}}
+unset -nocomplain simple blah2name simplename
+set simplename "simple"
+set blah2name "blah2"
+set simple [db one {SELECT fts3_tokenizer('simple')}]
sqlite3_db_config db SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER 0
do_catchsql_test 1.6 {
SELECT fts3_tokenizer('blah', fts3_tokenizer('simple')) IS NULL;
} {1 {fts3tokenize disabled}}
+do_test fts3atoken-1.7 {
+ execsql {
+ SELECT fts3_tokenizer('blah2', $simple) IS NULL;
+ }
+} {1}
+
+# With ENABLE_FTS3_TOKENIZER off, the fts3_tokenzer(1) function
+# returns NULL unless the first parameter is a bound parameter.
+# If the first parameter is a bound parameter, then fts3_tokenizer(1)
+# returns the actual pointer value as a BLOB.
+#
+do_test fts3atoken-1.8 {
+ execsql {
+ SELECT fts3_tokenizer($blah2name) == fts3_tokenizer($simplename),
+ typeof(fts3_tokenizer($blah2name)),
+ typeof(fts3_tokenizer('blah2')),
+ typeof(fts3_tokenizer($simplename)),
+ typeof(fts3_tokenizer('simple'));
+ }
+} {1 blob null blob null}
+# With ENABLE_FTS3_TOKENIZER on, fts3_tokenizer() always returns
+# the BLOB pointer, regardless the parameter
+#
+sqlite3_db_config db SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER 1
+do_test fts3atoken-1.9 {
+ execsql {
+ SELECT fts3_tokenizer('blah2') == fts3_tokenizer('simple'),
+ typeof(fts3_tokenizer($blah2name)),
+ typeof(fts3_tokenizer('blah2')),
+ typeof(fts3_tokenizer($simplename)),
+ typeof(fts3_tokenizer('simple'));
+ }
+} {1 blob blob blob blob}
#--------------------------------------------------------------------------
# Test cases fts3atoken-2.* test error cases in the scalar function based
# API for getting and setting tokenizers.
#
Index: test/func.test
==================================================================
--- test/func.test
+++ test/func.test
@@ -1389,6 +1389,34 @@
# Test char().
#
do_execsql_test func-31.1 {
SELECT char(), length(char()), typeof(char())
} {{} 0 text}
+
+# sqlite3_value_frombind()
+#
+do_execsql_test func-32.100 {
+ SELECT test_frombind(1,2,3,4);
+} {0}
+do_execsql_test func-32.110 {
+ SELECT test_frombind(1,2,?,4);
+} {4}
+do_execsql_test func-32.120 {
+ SELECT test_frombind(1,(?),4,?+7);
+} {2}
+do_execsql_test func-32.130 {
+ DROP TABLE IF EXISTS t1;
+ CREATE TABLE t1(a,b,c,e,f);
+ INSERT INTO t1 VALUES(1,2.5,'xyz',x'e0c1b2a3',null);
+ SELECT test_frombind(a,b,c,e,f,$xyz) FROM t1;
+} {32}
+do_execsql_test func-32.140 {
+ SELECT test_frombind(a,b,c,e,f,$xyz+f) FROM t1;
+} {0}
+do_execsql_test func-32.150 {
+ SELECT test_frombind(x.a,y.b,x.c,:123,y.e,x.f,$xyz+y.f) FROM t1 x, t1 y;
+} {8}
+
+
+
+
finish_test
|