Compare commits

...

1 Commits

Author SHA1 Message Date
drh 676f17a570 Proof of concept for a new sqlite3_function_needed() interface.
FossilOrigin-Name: cd67edc032983b5111ffcf6c73ca1308ef67cf2f16baa25788121827b3038d2f
2023-07-07 16:58:20 +00:00
8 changed files with 169 additions and 32 deletions
+14 -11
View File
@@ -1,5 +1,5 @@
C For\sthe\s"Valgrind"\stest\sscenario,\sset\sLONGDOUBLE_TYPE=double\sas\svalgrind\ndoes\snot\scorrectly\semulated\sextended\sprecision\son\sx64.
D 2023-07-07T12:18:26.936
C Proof\sof\sconcept\sfor\sa\snew\ssqlite3_function_needed()\sinterface.
D 2023-07-07T16:58:20.565
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -580,7 +580,7 @@ F src/btree.c c0c93b6cb4dc133b528c1290bb4ad0f2414452f9a5758ff2b106af718874f39e
F src/btree.h aa354b9bad4120af71e214666b35132712b8f2ec11869cb2315c52c81fad45cc
F src/btreeInt.h 3b4eff7155c0cea6971dc51f62e3529934a15a6640ec607dd42a767e379cb3a9
F src/build.c a8ae3b32d9aa9bbd2c0e97d7c0dd80def9fbca408425de1608f57ee6f47f45f4
F src/callback.c db3a45e376deff6a16c0058163fe0ae2b73a2945f3f408ca32cf74960b28d490
F src/callback.c 3520e686ab1d427f33cffb46d554d0a0a2a588866a195c69d1a17103b67dde18
F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e
F src/ctime.c 20507cc0b0a6c19cd882fcd0eaeda32ae6a4229fb4b024cfdf3183043d9b703d
F src/date.c f73f203b3877cef866c60ab402aec2bf89597219b60635cf50cbe3c5e4533e94
@@ -600,7 +600,7 @@ F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276
F src/json.c 14c474fb1249a46eb44e878e2361f36abfe686b134039b0d1883d93d61505b4a
F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa
F src/loadext.c 176d6b2cb18a6ad73b133db17f6fc351c4d9a2d510deebdb76c22bde9cfd1465
F src/main.c 512b1d45bc556edf4471a845afb7ba79e64bd5b832ab222dc195c469534cd002
F src/main.c d869b4032a2717b24ad0fd82266ea4d79304a25d942a4b332ff307009f6f10e3
F src/malloc.c 47b82c5daad557d9b963e3873e99c22570fb470719082c6658bf64e3012f7d23
F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645
F src/mem1.c 3bb59158c38e05f6270e761a9f435bf19827a264c13d1631c58b84bdc96d73b2
@@ -638,11 +638,11 @@ F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c
F src/resolve.c 37953a5f36c60bea413c3c04efcd433b6177009f508ef2ace0494728912fe2e9
F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97
F src/select.c 3ab1186290a311a8ceed1286c0e286209f7fe97b2d02c7593258004ce295dd88
F src/shell.c.in d320d8a13636de06d777cc1eab981caca304e175464e98183cf4ea68d93db818
F src/sqlite.h.in f999ef3642f381d69679b2516b430dbcb6c5a2a951b7f5e43dc4751b474a5774
F src/shell.c.in d6c9ac6ab2c56eac0009ad383f6fd31a410d4cfa04ddf5875496207773fc60b1
F src/sqlite.h.in 287bce997bc287c251558b41ffb4ec025b98e7d0c74e3bf22c4919d2edb6d44c
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
F src/sqlite3ext.h da473ce2b3d0ae407a6300c4a164589b9a6bfdbec9462688a8593ff16f3bb6e4
F src/sqliteInt.h f6c5470b7db42318a3de1115757e94b76570ad7697ac547823e01f1166756f1d
F src/sqlite3ext.h 51ee0eff1f843bd233c3441d220812964d2166f3c5e5714e7a3d847a778310c4
F src/sqliteInt.h 3323b237e3f951a4e0297ce797fb7e65bade820f04aa90876a3cf7c1eee029eb
F src/sqliteLimit.h 33b1c9baba578d34efe7dfdb43193b366111cdf41476b1e82699e14c11ee1fb6
F src/status.c 160c445d7d28c984a0eae38c144f6419311ed3eace59b44ac6dafc20db4af749
F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1
@@ -2043,8 +2043,11 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
P 64e6bd1c25d8e1dbfe511cba1921ff052c0fa4fe410fc9ce4435700a70cb88b1
R 938acc948a8d28aa6e5bbb237d21a380
P d3532eaed1cc671d8149b1bd34ead2a88fb83502a2898a5f60aea0a7daf18958
R d4ed9da3aa6365c836a379cf03c29a8d
T *branch * need-function
T *sym-need-function *
T -sym-trunk *
U drh
Z 95c647057214d80d38fbb5e9ad4c9cbc
Z e80e56952de95f04df4da782eb785cf5
# Remove this line to create a well-formed Fossil manifest.
+1 -1
View File
@@ -1 +1 @@
d3532eaed1cc671d8149b1bd34ead2a88fb83502a2898a5f60aea0a7daf18958
cd67edc032983b5111ffcf6c73ca1308ef67cf2f16baa25788121827b3038d2f
+21
View File
@@ -474,7 +474,28 @@ FuncDef *sqlite3FindFunction(
if( pBest && (pBest->xSFunc || createFlag) ){
return pBest;
}else if( db->xFuncNeeded!=0 ){
int rc;
int (*xFN)(void*,sqlite3*,const char*,int,int) = db->xFuncNeeded;
db->xFuncNeeded = 0;
rc = xFN(db->pFuncNeededArg,db,zName,nArg,enc);
db->xFuncNeeded = xFN;
pBest = 0;
bestScore = 0;
if( rc==SQLITE_OK ){
p = (FuncDef*)sqlite3HashFind(&db->aFunc,zName);
while( p ){
int score = matchQuality(p, nArg, enc);
if( score>bestScore ){
pBest = p;
bestScore = score;
}
p = p->pNext;
}
return pBest;
}
}
return 0;
}
+25
View File
@@ -1219,6 +1219,9 @@ static int sqlite3Close(sqlite3 *db, int forceZombie){
db->trace.xV2(SQLITE_TRACE_CLOSE, db->pTraceArg, db, 0);
}
/* Free the function-needed payload pointer */
if( db->xFuncNeededFree ) db->xFuncNeededFree(db->pFuncNeededArg);
/* Force xDisconnect calls on all virtual tables */
disconnectAllVtab(db);
@@ -3705,6 +3708,28 @@ int sqlite3_collation_needed16(
}
#endif /* SQLITE_OMIT_UTF16 */
/*
** Register a collation sequence factory callback with the database handle
** db. Replace any previously installed collation sequence factory.
*/
int sqlite3_function_needed(
sqlite3 *db,
void *pFuncNeededArg,
int (*xFuncNeeded)(void*,sqlite3*,const char*,int,int),
void (*xFuncNeededFree)(void*)
){
#ifdef SQLITE_ENABLE_API_ARMOR
if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT;
#endif
sqlite3_mutex_enter(db->mutex);
if( db->xFuncNeededFree ) db->xFuncNeededFree(db->pFuncNeededArg);
db->pFuncNeededArg = pFuncNeededArg;
db->xFuncNeeded = xFuncNeeded;
db->xFuncNeededFree = xFuncNeededFree;
sqlite3_mutex_leave(db->mutex);
return SQLITE_OK;
}
#ifndef SQLITE_OMIT_DEPRECATED
/*
** This function is now an anachronism. It used to be used to recover from a
+53 -20
View File
@@ -5383,6 +5383,57 @@ static void shellUSleepFunc(
#define OPEN_DB_KEEPALIVE 0x001 /* Return after error if true */
#define OPEN_DB_ZIPFILE 0x002 /* Open as ZIP if name matches *.zip */
/*
** Unknown function callback
*/
static int shellUnknownFunction(
void *pUserData,
sqlite3 *db,
const char *zFunc,
int nArg,
int enc
){
ShellState *p = (ShellState*)pUserData;
(void)enc;
(void)nArg;
if( sqlite3_stricmp(zFunc,"strtod")==0 ){
sqlite3_create_function(db, "strtod", 1, SQLITE_UTF8, 0,
shellStrtod, 0, 0);
}else
if( sqlite3_stricmp(zFunc,"dtostr")==0 ){
sqlite3_create_function(db, "dtostr", 1, SQLITE_UTF8, 0,
shellDtostr, 0, 0);
sqlite3_create_function(db, "dtostr", 2, SQLITE_UTF8, 0,
shellDtostr, 0, 0);
}else
if( sqlite3_stricmp(zFunc,"shell_add_schema")==0 ){
sqlite3_create_function(db, "shell_add_schema", 3, SQLITE_UTF8, 0,
shellAddSchemaName, 0, 0);
}else
if( sqlite3_stricmp(zFunc,"shell_module_schema")==0 ){
sqlite3_create_function(db, "shell_module_schema", 1, SQLITE_UTF8, 0,
shellModuleSchema, 0, 0);
}else
if( sqlite3_stricmp(zFunc,"shell_putsnl")==0 ){
sqlite3_create_function(db, "shell_putsnl", 1, SQLITE_UTF8, p,
shellPutsFunc, 0, 0);
}else
if( sqlite3_stricmp(zFunc,"usleep")==0 ){
sqlite3_create_function(db, "usleep",1,SQLITE_UTF8,0,
shellUSleepFunc, 0, 0);
}
#ifndef SQLITE_NOHAVE_SYSTEM
else
if( sqlite3_stricmp(zFunc,"edit")==0 ){
sqlite3_create_function(db, "edit", 1, SQLITE_UTF8, 0,
editFunc, 0, 0);
sqlite3_create_function(db, "edit", 2, SQLITE_UTF8, 0,
editFunc, 0, 0);
}
#endif
return SQLITE_OK;
}
/*
** Make sure the database is open. If it is not, then open it. If
** the database fails to open, print an error message and exit.
@@ -5503,27 +5554,9 @@ static void open_db(ShellState *p, int openFlags){
}
#endif
sqlite3_create_function(p->db, "strtod", 1, SQLITE_UTF8, 0,
shellStrtod, 0, 0);
sqlite3_create_function(p->db, "dtostr", 1, SQLITE_UTF8, 0,
shellDtostr, 0, 0);
sqlite3_create_function(p->db, "dtostr", 2, SQLITE_UTF8, 0,
shellDtostr, 0, 0);
sqlite3_create_function(p->db, "shell_add_schema", 3, SQLITE_UTF8, 0,
shellAddSchemaName, 0, 0);
sqlite3_create_function(p->db, "shell_module_schema", 1, SQLITE_UTF8, 0,
shellModuleSchema, 0, 0);
sqlite3_create_function(p->db, "shell_putsnl", 1, SQLITE_UTF8, p,
shellPutsFunc, 0, 0);
sqlite3_create_function(p->db, "usleep",1,SQLITE_UTF8,0,
shellUSleepFunc, 0, 0);
#ifndef SQLITE_NOHAVE_SYSTEM
sqlite3_create_function(p->db, "edit", 1, SQLITE_UTF8, 0,
editFunc, 0, 0);
sqlite3_create_function(p->db, "edit", 2, SQLITE_UTF8, 0,
editFunc, 0, 0);
#endif
sqlite3_function_needed(p->db, p, shellUnknownFunction, 0);
if( p->openMode==SHELL_OPEN_ZIPFILE ){
char *zSql = sqlite3_mprintf(
"CREATE VIRTUAL TABLE zip USING zipfile(%Q);", zDbFilename);
+48
View File
@@ -6206,6 +6206,54 @@ int sqlite3_create_collation16(
int(*xCompare)(void*,int,const void*,int,const void*)
);
/*
** CAPI3REF: Function Needed Callbacks
** METHOD: sqlite3
**
** The sqlite3_function_needed(D,P,C,X) interface registers a callback
** function C which is invoked whenever the SQL parser encounters an
** unknown SQL function. The callback may optionally register a suitable
** implementation for that unknown function (using [sqlite3_create_function()]
** or similar), in which case parsing will continue normally. In this way,
** applications with hundreds or thousands of seldom-used application defined
** SQL functions can avoid having register them at start-up and instead
** register each function if and only if it it actually gets used.
**
** ^If the callback function C has been registered using the
** sqlite3_function_needed(D,P,C,X), then whenever the SQL parser encounters
** an unknown SQL function, it will invoke the C callback with 5 arguments:
** P, D, F, N, E. P is a copy of the generic pointer P parameter to
** the original sqlite3_function_needed() call that registered the
** callback. D is the database connection. F is the name of the unknown
** function (UTF-8 encoded and zero-terminated but in an arbitrary case).
** N is the number of arguments on the F function.
** E is the preferred text encoding.
**
** If the C callback registers a new function with the correct
** name and number of arguments and if the callback returns SQLITE_OK, then
** the newly registered function is used and parsing continues. If an
** appropriate function does not get registered, or if the callback returns
** anything other than SQLITE_OK, then an "unknown function" error is generated
** as normal.
**
** Only a single function-needed callback is supported per database connection.
** Each call to sqlite3_function_needed() replaces the callback identified
** by the previous call to sqlite3_function_needed(). The function-needed
** callback mechanism may be disabled by invoking sqlite3_function_needed(D,P,C,X)
** using a NULL pointer for the C parameter.
**
** The X argument to sqlite3_function_needed(D,P,C,X) is an optional
** destructor for generic pointer parameter P. If X and C are both not NULL,
** then X(P) is invoked when the database connection closes or at the next
** call to sqlite3_function_needed().
*/
int sqlite3_function_needed(
sqlite3*,
void*,
int(*)(void*,sqlite3*,const char*,int,int),
void(*)(void*)
);
/*
** CAPI3REF: Collation Needed Callbacks
** METHOD: sqlite3
+4
View File
@@ -361,6 +361,8 @@ struct sqlite3_api_routines {
int (*value_encoding)(sqlite3_value*);
/* Version 3.41.0 and later */
int (*is_interrupted)(sqlite3*);
/* Version 3.43.0 and later */
int (*function_needed)(sqlite3*,void*,const char*,int,int);
};
/*
@@ -689,6 +691,8 @@ typedef int (*sqlite3_loadext_entry)(
#define sqlite3_value_encoding sqlite3_api->value_encoding
/* Version 3.41.0 and later */
#define sqlite3_is_interrupted sqlite3_api->is_interrupted
/* Version 3.43.0 and later */
#define sqlite3_function_needed sqlite3_api->function_needed
#endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */
#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
+3
View File
@@ -1674,6 +1674,9 @@ struct sqlite3 {
int (*xWalCallback)(void *, sqlite3 *, const char *, int);
void *pWalArg;
#endif
int(*xFuncNeeded)(void*,sqlite3*,const char*,int,int);
void *pFuncNeededArg;
void(*xFuncNeededFree)(void*);
void(*xCollNeeded)(void*,sqlite3*,int eTextRep,const char*);
void(*xCollNeeded16)(void*,sqlite3*,int eTextRep,const void*);
void *pCollNeededArg;