1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-08-05 15:55:57 +03:00

Add the "-returntype" option to the "db function" Tcl method.

FossilOrigin-Name: 789a492b68c353e2b763d67d399722b7ab61bfe09b472466df2821f65cab1be9
This commit is contained in:
dan
2019-02-27 16:38:19 +00:00
parent f26b145385
commit 89d249364e
4 changed files with 160 additions and 31 deletions

View File

@@ -93,6 +93,14 @@ typedef struct SqliteDb SqliteDb;
/*
** New SQL functions can be created as TCL scripts. Each such function
** is described by an instance of the following structure.
**
** Variable eType may be set to SQLITE_INTEGER, SQLITE_FLOAT, SQLITE_TEXT,
** SQLITE_BLOB or SQLITE_NULL. If it is SQLITE_NULL, then the implementation
** attempts to determine the type of the result based on the Tcl object.
** If it is SQLITE_TEXT or SQLITE_BLOB, then a text (sqlite3_result_text())
** or blob (sqlite3_result_blob()) is returned. If it is SQLITE_INTEGER
** or SQLITE_FLOAT, then an attempt is made to return an integer or float
** value, falling back to float and then text if this is not possible.
*/
typedef struct SqlFunc SqlFunc;
struct SqlFunc {
@@ -100,6 +108,7 @@ struct SqlFunc {
Tcl_Obj *pScript; /* The Tcl_Obj representation of the script */
SqliteDb *pDb; /* Database connection that owns this function */
int useEvalObjv; /* True if it is safe to use Tcl_EvalObjv */
int eType; /* Type of value to return */
char *zName; /* Name of this function */
SqlFunc *pNext; /* Next function on the list of them all */
};
@@ -995,27 +1004,54 @@ static void tclSqlFunc(sqlite3_context *context, int argc, sqlite3_value**argv){
u8 *data;
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. */
data = Tcl_GetByteArrayFromObj(pVar, &n);
sqlite3_result_blob(context, data, n, SQLITE_TRANSIENT);
}else if( c=='b' && strcmp(zType,"boolean")==0 ){
Tcl_GetIntFromObj(0, pVar, &n);
sqlite3_result_int(context, n);
}else if( c=='d' && strcmp(zType,"double")==0 ){
double r;
Tcl_GetDoubleFromObj(0, pVar, &r);
sqlite3_result_double(context, 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(context, v);
}else{
data = (unsigned char *)Tcl_GetStringFromObj(pVar, &n);
sqlite3_result_text(context, (char *)data, n, SQLITE_TRANSIENT);
int eType = p->eType;
if( eType==SQLITE_NULL ){
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. */
eType = SQLITE_BLOB;
}else if( (c=='b' && strcmp(zType,"boolean")==0)
|| (c=='w' && strcmp(zType,"wideInt")==0)
|| (c=='i' && strcmp(zType,"int")==0)
){
eType = SQLITE_INTEGER;
}else if( c=='d' && strcmp(zType,"double")==0 ){
eType = SQLITE_FLOAT;
}else{
eType = SQLITE_TEXT;
}
}
switch( eType ){
case SQLITE_BLOB: {
data = Tcl_GetByteArrayFromObj(pVar, &n);
sqlite3_result_blob(context, data, n, SQLITE_TRANSIENT);
break;
}
case SQLITE_INTEGER: {
Tcl_WideInt v;
if( TCL_OK==Tcl_GetWideIntFromObj(0, pVar, &v) ){
sqlite3_result_int64(context, v);
break;
}
/* fall-through */
}
case SQLITE_FLOAT: {
double r;
if( TCL_OK==Tcl_GetDoubleFromObj(0, pVar, &r) ){
sqlite3_result_double(context, r);
break;
}
/* fall-through */
}
default: {
data = (unsigned char *)Tcl_GetStringFromObj(pVar, &n);
sqlite3_result_text(context, (char *)data, n, SQLITE_TRANSIENT);
break;
}
}
}
}
@@ -2646,6 +2682,7 @@ deserialize_error:
char *zName;
int nArg = -1;
int i;
int eType = SQLITE_NULL;
if( objc<4 ){
Tcl_WrongNumArgs(interp, 2, objv, "NAME ?SWITCHES? SCRIPT");
return TCL_ERROR;
@@ -2653,7 +2690,7 @@ deserialize_error:
for(i=3; i<(objc-1); i++){
const char *z = Tcl_GetString(objv[i]);
int n = strlen30(z);
if( n>2 && strncmp(z, "-argcount",n)==0 ){
if( n>1 && strncmp(z, "-argcount",n)==0 ){
if( i==(objc-2) ){
Tcl_AppendResult(interp, "option requires an argument: ", z,(char*)0);
return TCL_ERROR;
@@ -2666,11 +2703,25 @@ deserialize_error:
}
i++;
}else
if( n>2 && strncmp(z, "-deterministic",n)==0 ){
if( n>1 && strncmp(z, "-deterministic",n)==0 ){
flags |= SQLITE_DETERMINISTIC;
}else
if( n>1 && strncmp(z, "-returntype", n)==0 ){
const char *azType[] = {"integer", "real", "text", "blob", "any", 0};
assert( SQLITE_INTEGER==1 && SQLITE_FLOAT==2 && SQLITE_TEXT==3 );
assert( SQLITE_BLOB==4 && SQLITE_NULL==5 );
if( i==(objc-2) ){
Tcl_AppendResult(interp, "option requires an argument: ", z,(char*)0);
return TCL_ERROR;
}
i++;
if( Tcl_GetIndexFromObj(interp, objv[i], azType, "type", 0, &eType) ){
return TCL_ERROR;
}
eType++;
}else{
Tcl_AppendResult(interp, "bad option \"", z,
"\": must be -argcount or -deterministic", (char*)0
"\": must be -argcount, -deterministic or -returntype", (char*)0
);
return TCL_ERROR;
}
@@ -2686,6 +2737,7 @@ deserialize_error:
pFunc->pScript = pScript;
Tcl_IncrRefCount(pScript);
pFunc->useEvalObjv = safeToUseEvalObjv(interp, pScript);
pFunc->eType = eType;
rc = sqlite3_create_function(pDb->db, zName, nArg, flags,
pFunc, tclSqlFunc, 0, 0);
if( rc!=SQLITE_OK ){