]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Prototype implementation for a new sqlite3_result_str() API.
authordrh <>
Fri, 19 Jun 2026 17:03:21 +0000 (17:03 +0000)
committerdrh <>
Fri, 19 Jun 2026 17:03:21 +0000 (17:03 +0000)
FossilOrigin-Name: cdbc62c36a37ad687589917c6f1e92ded6d99959c54eb8d79eaaef6fa57dacd3

manifest
manifest.tags
manifest.uuid
src/func.c
src/loadext.c
src/sqlite.h.in
src/sqlite3ext.h
src/vdbeapi.c

index 40c7a07bd8bc0856916944b9613f30be9d3d5c06..4325d3e4c08ac399f0f5527fd721a88cf8ffbd32 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Fix\sthe\sformat()\sSQL\sfunction\sso\sthat\sit\sreturns\san\sempty\sstring\snot\sa\nNULL\sif\sthe\sfirst\sargument\sis\san\sempty\sstring.\n[bugs:/info/2026-06-19T16:40:04Z|Bug\s2026-06-19T16:40:04Z]
-D 2026-06-19T16:55:08.727
+C Prototype\simplementation\sfor\sa\snew\ssqlite3_result_str()\sAPI.
+D 2026-06-19T17:03:21.505
 F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
@@ -691,7 +691,7 @@ F src/delete.c 59eeca3fb88c29329afc41bb803ee568b120d9dd7470b5f38ab55cc38390b451
 F src/expr.c e97dd9f6ada4c448764e225d8963091bf630b3efb2c92e4d0762571cca2a14e5
 F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007
 F src/fkey.c 931f74cec1dc8038a0217ef340c91ce147dd1bbed08dc40c47ee0ec6edfffb08
-F src/func.c 1d16098c987cd6aafa7930989b2ccdcc88af9fe75fd6a09d8e4fd3c61192b8c0
+F src/func.c 65c7e3fb007a42e3b7e18bea6dc48d9e14504287abd8c0d96300245f10c8f77d
 F src/global.c a19e4b1ca1335f560e9560e590fc13081e21f670643367f99cb9e8f9dc7d615b
 F src/hash.c 03c8c0f4be9e8bcb6de65aa26d34a61d48a9430747084a69f9469fbb00ea52ca
 F src/hash.h 46b92795a95bfefb210f52f0c316e9d7cdbcdd7e7fcfb0d8be796d3a5767cddf
@@ -700,7 +700,7 @@ F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71
 F src/insert.c e6e9b574b4863649e2c433f2260f226183e697df1ab73fcb8da9a85d5abcd390
 F src/json.c f058c449acb9fdb1d3d1bb9f7e97b225ba773f5b6fdcec4310d3f49980125ed4
 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa
-F src/loadext.c 78d5b06f18996ffa1203129b28fea043f63a87a4117539678f1d761c30b4ff65
+F src/loadext.c 5cd4cd7ec6e1a46416806899086363fbf95eecd3bfb9a388b436c9a77243c902
 F src/main.c efd782fadd65b8e67952f439d56d7605134582346573018a614a8e082e074bd7
 F src/malloc.c 422f7e0498e1c9ef967f06283b6f2c0b16db6b905d8e06f6dbc8baaa3e4e6c5a
 F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645
@@ -739,9 +739,9 @@ F src/resolve.c d0724113da9f5c0430d2052808ce59519f51ae7c4fbb1f5ef21fe3a832956086
 F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97
 F src/select.c 80ea6935f8470b97d1212bc1b759b7fadb28351797877f493d0cf598be1fef5e
 F src/shell.c.in a4e83895cfa336065ad7f7a7dea8fc2a19d050f7ce7466621c67208acaac9e44
-F src/sqlite.h.in 740636d37edf1ed4a64378989e48ccfad169008aac250330f982e14aaf1d599b
+F src/sqlite.h.in 8c70db561507478176b942a3f88cac396e00993eafb827ed31d48c191f327408
 F src/sqlite3.rc 015537e6ac1eec6c7050e17b616c2ffe6f70fca241835a84a4f0d5937383c479
-F src/sqlite3ext.h 9788c301f95370fa30e808861f1d2e6f022a816ddbe2a4f67486784c1b31db2e
+F src/sqlite3ext.h 0efd4723bad9124ea1f581d9f1ea0254ac1c6f3e5fb29e4f3dcf36c72485a456
 F src/sqliteInt.h bda082da71e9e93cefadd169860b857c422182503d1e6122159258b17332636b
 F src/sqliteLimit.h c70656b67ab5b96741a8f1c812bdd80c81f2b1c1e443d0cc3ea8c33bb1f1a092
 F src/status.c 7565d63a79aa2f326339a24a0461a60096d0bd2bce711fefb50b5c89335f3592
@@ -807,7 +807,7 @@ F src/vacuum.c d3d35d8ae893d419ade5fa196d761a83bddcbb62137a1a157ae751ef38b26e82
 F src/vdbe.c 39658ee12b9d6bf5fc546e1ede20b307d86d9e988ae709c9b461249a8312513d
 F src/vdbe.h 70e862ac8a11b590f8c1eaac17a0078429d42bc4ea3f757a9af0f451dd966a71
 F src/vdbeInt.h c31ba4dc8d280c2b1dc89c6fcee68f2555e3813ab34279552c20b964c0e338b1
-F src/vdbeapi.c 6cdcbe5c7afa754c998e73d2d5d2805556268362914b952811bdfb9c78a37cf1
+F src/vdbeapi.c 9da3bbf81adc5d7d21c6b8c4d53f684788432e0a25ce41217e6d1b4eda4c7782
 F src/vdbeaux.c a052c43fcf342651f655e6771003c175d49b237a9473c3c4d79d44725f494dae
 F src/vdbeblob.c b3f0640db9642fbdc88bd6ebcc83d6009514cafc98f062f675f2c8d505d82692
 F src/vdbemem.c 6e7ad67507c9a8e625b46256a9c003929331d6a27b99bbe139b8f0dab636e1f2
@@ -2208,8 +2208,11 @@ F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee
 F tool/warnings.sh a554d13f6e5cf3760f041b87939e3d616ec6961859c3245e8ef701d1eafc2ca2
 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
 F tool/winmain.c 00c8fb88e365c9017db14c73d3c78af62194d9644feaf60e220ab0f411f3604c
-P d6a3272e5592bcfeecf8a06dd8330cce57ff6603b24994b533d1fdcd48b5dc66
-R 7e7c087b6061ec959347a00d450a39f7
+P ece7af98a8bf982567b489cf82a52519f46a1c9360c82a486dbe1cd0664b1555
+R 0d8b5cb26fa44abfe78394d4c5f0057a
+T *branch * sqlite3-result-str
+T *sym-sqlite3-result-str *
+T -sym-trunk *
 U drh
-Z 9591ba0273abaf6614bea46629e85033
+Z fbd653b71152122c69a15d2a079e18da
 # Remove this line to create a well-formed Fossil manifest.
index bec971799ff1b8ee641c166c7aeb22d12c785393..9a4573c4cf37be1a58233e6222b229b01e08b2f6 100644 (file)
@@ -1,2 +1,2 @@
-branch trunk
-tag trunk
+branch sqlite3-result-str
+tag sqlite3-result-str
index 88b0ccbb10c2fdd529be5f5d24c6d4b10fd1540a..66bc314e01fee14b5a5e04dcfe3668f87376c405 100644 (file)
@@ -1 +1 @@
-ece7af98a8bf982567b489cf82a52519f46a1c9360c82a486dbe1cd0664b1555
+cdbc62c36a37ad687589917c6f1e92ded6d99959c54eb8d79eaaef6fa57dacd3
index b50f35def623c97fee34f5cadda20711e2cc135f..f88e0e1d23ab5d0bf5d91dda3c36351bec6b5424 100644 (file)
@@ -320,7 +320,6 @@ static void printfFunc(
   PrintfArguments x;
   StrAccum str;
   const char *zFormat;
-  int n;
   sqlite3 *db = sqlite3_context_db_handle(context);
 
   if( argc>=1 && (zFormat = (const char*)sqlite3_value_text(argv[0]))!=0 ){
@@ -330,21 +329,7 @@ static void printfFunc(
     sqlite3StrAccumInit(&str, db, 0, 0, db->aLimit[SQLITE_LIMIT_LENGTH]);
     str.printfFlags = SQLITE_PRINTF_SQLFUNC;
     sqlite3_str_appendf(&str, zFormat, &x);
-    if( str.accError ){
-      if( str.accError==SQLITE_NOMEM ){
-        sqlite3_result_error_nomem(context);
-      }else{
-        sqlite3_result_error_toobig(context);
-      }
-      sqlite3_str_reset(&str);
-    }else if( str.nChar==0 ){
-      sqlite3_result_text(context, "", 1, SQLITE_STATIC);
-      sqlite3_str_reset(&str);
-    }else{
-      n = str.nChar;
-      sqlite3_result_text(context, sqlite3StrAccumFinish(&str), n,
-                          SQLITE_DYNAMIC);
-    }
+    sqlite3_result_str(context, &str, SQLITE_XFER);
   }
 }
 
@@ -1291,12 +1276,7 @@ static void quoteFunc(sqlite3_context *context, int argc, sqlite3_value **argv){
   UNUSED_PARAMETER(argc);
   sqlite3StrAccumInit(&str, db, 0, 0, db->aLimit[SQLITE_LIMIT_LENGTH]);
   sqlite3QuoteValue(&str,argv[0],SQLITE_PTR_TO_INT(sqlite3_user_data(context)));
-  sqlite3_result_text(context, sqlite3StrAccumFinish(&str), str.nChar,
-                      SQLITE_DYNAMIC);
-  if( str.accError!=SQLITE_OK ){
-    sqlite3_result_null(context);
-    sqlite3_result_error_code(context, str.accError);
-  }
+  sqlite3_result_str(context, &str, SQLITE_XFER);
 }
 
 /*
@@ -2339,18 +2319,8 @@ static void groupConcatFinalize(sqlite3_context *context){
 static void groupConcatValue(sqlite3_context *context){
   GroupConcatCtx *pGCC
     = (GroupConcatCtx*)sqlite3_aggregate_context(context, 0);
-  if( pGCC ){
-    StrAccum *pAccum = &pGCC->str;
-    if( pAccum->accError==SQLITE_TOOBIG ){
-      sqlite3_result_error_toobig(context);
-    }else if( pAccum->accError==SQLITE_NOMEM ){
-      sqlite3_result_error_nomem(context);
-    }else if( pGCC->nAccum>0 && pAccum->nChar==0 ){
-      sqlite3_result_text(context, "", 1, SQLITE_STATIC);
-    }else{   
-      const char *zText = sqlite3_str_value(pAccum);
-      sqlite3_result_text(context, zText, pAccum->nChar, SQLITE_TRANSIENT);
-    }
+  if( pGCC && pGCC->nAccum>0 ){
+    sqlite3_result_str(context, &pGCC->str, SQLITE_COPY);
   }
 }
 #else
@@ -3195,8 +3165,7 @@ static void filestatFunc(
         if( rc ) sqlite3_str_append(pStr, "null", 4);
       }
       sqlite3_str_append(pStr, "}", 1);
-      sqlite3_result_text(context, sqlite3_str_finish(pStr), -1,
-                          sqlite3_free);
+      sqlite3_result_str(context, pStr, SQLITE_FINISH);
     }
     sqlite3BtreeLeave(pBtree);
   }else{
@@ -3312,8 +3281,8 @@ static void parseuriFunc(
         }
       }
     }
-    sqlite3_result_text(ctx, sqlite3_str_finish(pResult), -1, sqlite3_free);
   }
+  sqlite3_result_str(ctx, pResult, SQLITE_FINISH);
   sqlite3_free_filename(zFile);
   sqlite3_free(zErr);
 }
index dd3f1493b3f82e05133a79a9b9f8f50783e38533..cf7ff56262b2c999970377dcebdd28fc52b45e1e 100644 (file)
@@ -533,7 +533,9 @@ static const sqlite3_api_routines sqlite3Apis = {
   0,
   0,
 #endif
-  sqlite3_incomplete
+  /* Version 3.54.0 and later */
+  sqlite3_incomplete,
+  sqlite3_result_str
 };
 
 /* True if x is the directory separator character
index 35a1c1a07b1d4686b248833d4400124c88548b5a..58e46f8493d22fd1d5d7fa59ae4d7e138ceb5c89 100644 (file)
@@ -3413,7 +3413,7 @@ void sqlite3_randomness(int N, void *P);
 **
 ** ^The first parameter to the authorizer callback is a copy of the third
 ** parameter to the sqlite3_set_authorizer() interface. ^The second parameter
-** to the callback is an integer [SQLITE_COPY | action code] that specifies
+** to the callback is an integer [SQLITE_READ | action code] that specifies
 ** the particular action to be authorized. ^The third through sixth parameters
 ** to the callback are either NULL pointers or zero-terminated strings
 ** that contain additional details about the action to be authorized.
@@ -3545,9 +3545,14 @@ int sqlite3_set_authorizer(
 #define SQLITE_DROP_VTABLE          30   /* Table Name      Module Name     */
 #define SQLITE_FUNCTION             31   /* NULL            Function Name   */
 #define SQLITE_SAVEPOINT            32   /* Operation       Savepoint Name  */
-#define SQLITE_COPY                  0   /* No longer used */
 #define SQLITE_RECURSIVE            33   /* NULL            NULL            */
 
+/*
+** Note:  The SQLITE_COPY macro (with value 0) used to be one of the
+** action codes above.  That macro has now been repurposed as a possible
+** value to the 3rd argument to sqlite3_result_str().
+*/
+
 /*
 ** CAPI3REF: Deprecated Tracing And Profiling Functions
 ** DEPRECATED
@@ -8798,8 +8803,8 @@ int sqlite3_keyword_check(const char*,int);
 ** <li> ^The sqlite3_str object is created using [sqlite3_str_new()].
 ** <li> ^Text is appended to the sqlite3_str object using various
 ** methods, such as [sqlite3_str_appendf()].
-** <li> ^The sqlite3_str object is destroyed and the string it created
-** is returned using the [sqlite3_str_finish()] interface.
+** <li> The sqlite3_str object is destroyed and the string it created
+** is returned using [sqlite3_str_finish()] or [sqlite3_result_str()].
 ** </ol>
 */
 typedef struct sqlite3_str sqlite3_str;
@@ -8851,6 +8856,40 @@ sqlite3_str *sqlite3_str_new(sqlite3*);
 char *sqlite3_str_finish(sqlite3_str*);
 void sqlite3_str_free(sqlite3_str*);
 
+/*
+** CAPI3REF: Return A Dynamic String From an SQL Function
+**
+** The [sqlite3_result_str(C,S,F)] interface causes the 
+** [sqlite3_str|dynamic string] S to become the return value for the
+** application-defined function or virtual table that uses
+** [sqlite3_context] C.  The F flag can be one of [SQLITE_COPY]
+** or [SQLITE_XFER] or [SQLITE_FINISH].
+**
+** If the dynamic string is invalid or incomplete due to an out-of-memory
+** or string-too-large error, then this routine transfers that error
+** over to the SQL function.
+**
+** If the F argument is SQLITE_COPY, then a copy of the dynamic string
+** content is made and the dynamic string object is unchanged.
+** If the F argument is SQLITE_XFER, then ownership of the content 
+** in the dynamic is transferred to the SQL function (via a pointer copy
+** rather than a string copy) and the dynamic string is reset to an
+** empty string.  The SQLITE_FINISH value for F works like SQLITE_RESET
+** except that it also invokes the [sqlite3_str_free(S)] destructor
+** on the dynamic string object.
+*/
+void sqlite3_result_str(sqlite3_context*, sqlite3_str*, int);
+
+/*
+** CAPI3REF: Control Flags For sqlite3_result_str()
+**
+** The following integers can be used as the third "F" argument
+** to [sqlite3_result_str(C,S,F)].
+*/
+#define SQLITE_COPY   0   /* Results copied.  Dynamic string unchanged */
+#define SQLITE_XFER   1   /* Results transfered.  Dynamic string reset */
+#define SQLITE_FINISH 2   /* Like SQLITE_XFER, plus dynamic string freed */
+
 /*
 ** CAPI3REF: Add Content To A Dynamic String
 ** METHOD: sqlite3_str
index 6c12ec88b12cc2ff5ae12c0b56a40ecc4c2585d0..47b886facabcf10f2a0d30f9f328aa2242f5a828 100644 (file)
@@ -378,6 +378,7 @@ struct sqlite3_api_routines {
   int (*carray_bind_v2)(sqlite3_stmt*,int,void*,int,int,void(*)(void*),void*);
   /* Version 3.54.0 and later */
   sqlite3_int64 (*incomplete)(const char*);
+  void (*result_str)(sqlite3_context*,sqlite3_str*,int);
 };
 
 /*
@@ -723,6 +724,7 @@ typedef int (*sqlite3_loadext_entry)(
 #define sqlite3_carray_bind_v2         sqlite3_api->carray_bind_v2
 /* Version 3.54.0 and later */
 #define sqlite3_incomplete             sqlite3_api->incomplete
+#define sqlite3_result_str             sqlite3_api->result_str
 #endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */
 
 #if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
index 9fd4715cea0a8cd42e49f425d79f06242a370eb9..3b5d007cb3f7223b38828429edd36ff96cdce9a0 100644 (file)
@@ -374,15 +374,18 @@ void sqlite3_value_free(sqlite3_value *pOld){
  
 
 /**************************** sqlite3_result_  *******************************
-** The following routines are used by user-defined functions to specify
-** the function result.
+** The following routines are used by application-defined SQL functions to
+** specify the function return value.  There are many variations on
+** sqlite3_result_xxxx() for different types of return values.
 **
-** The setStrOrError() function calls sqlite3VdbeMemSetStr() to store the
-** result as a string or blob.  Appropriate errors are set if the string/blob
-** is too big or if an OOM occurs.
+** The setStrOrError() function is a helper function that invokes
+** sqlite3VdbeMemSetStr() to store the result as a string or blob.
+** Appropriate errors are set if the string/blob is too big or if
+** an OOM occurs.
 **
-** The invokeValueDestructor(P,X) routine invokes destructor function X()
-** on value P if P is not going to be used and need to be destroyed.
+** The invokeValueDestructor(P,X) helper function invokes the destructor
+** function X() on value P if P is not going to be used and need to
+** be destroyed.
 */
 static void setResultStrOrError(
   sqlite3_context *pCtx,  /* Function context */
@@ -695,7 +698,7 @@ void sqlite3_result_error_code(sqlite3_context *pCtx, int errCode){
   }
 }
 
-/* Force an SQLITE_TOOBIG error. */
+/* Cause the SQL function to raise an SQLITE_TOOBIG error. */
 void sqlite3_result_error_toobig(sqlite3_context *pCtx){
 #ifdef SQLITE_ENABLE_API_ARMOR
   if( pCtx==0 ) return;
@@ -706,7 +709,7 @@ void sqlite3_result_error_toobig(sqlite3_context *pCtx){
                        SQLITE_UTF8, SQLITE_STATIC);
 }
 
-/* An SQLITE_NOMEM error. */
+/* Cause the SQL function to raise an SQLITE_NOMEM error. */
 void sqlite3_result_error_nomem(sqlite3_context *pCtx){
 #ifdef SQLITE_ENABLE_API_ARMOR
   if( pCtx==0 ) return;
@@ -717,6 +720,59 @@ void sqlite3_result_error_nomem(sqlite3_context *pCtx){
   sqlite3OomFault(pCtx->pOut->db);
 }
 
+/* Make the return value of the SQL function or virtual table pCtx
+** be the content of the sqlite3_str object pStr.  The eOwn flag
+** determines ownership of the sqlite3_str object and its content.
+**
+**    eOwn             Ownership transfer
+**    -------------    ------------------------------------------------
+**
+**    SQLITE_COPY      The SQL function returns a copy the sqlite3_str
+**                     content and leaves the sqlite3_str object itself
+**                     unchanged.
+**
+**    SQLITE_XFER      The content of the sqlite3_str is transferred to
+**                     the SQL function and the SQL function takes
+**                     responsibility for freeing that content when it is
+**                     no longer neede.  The sqlite3_str object is reset
+**                     to an empty string.
+**
+**    SQLITE_FINISH    Like SQLITE_XFER except that the pStr is also
+**                     freed using sqlite_str_free().
+*/
+void sqlite3_result_str(sqlite3_context *pCtx, sqlite3_str *pStr, int eOwn){
+#ifdef SQLITE_ENABLE_API_ARMOR
+  if( pCtx==0 ) return;
+  if( pStr==0 ) return;
+#endif
+  if( pStr->accError==0 ){
+    if( pStr->nChar==0 ){
+      setResultStrOrError(pCtx, "", 0, SQLITE_UTF8_ZT, SQLITE_STATIC);
+      if( eOwn ) sqlite3_str_reset(pStr);
+    }else{
+      const char *zText = sqlite3_str_value(pStr);
+      if( eOwn==SQLITE_COPY || !isMalloced(pStr) ){
+        setResultStrOrError(pCtx, zText, pStr->nChar, 
+                            SQLITE_UTF8, SQLITE_TRANSIENT);
+      }else{
+        setResultStrOrError(pCtx, zText, pStr->nChar,
+                            SQLITE_UTF8_ZT, SQLITE_DYNAMIC);
+      }
+    }
+  }else if( pStr->accError==SQLITE_NOMEM ){
+    sqlite3_result_error_nomem(pCtx);
+  }else{
+    assert( pStr->accError==SQLITE_TOOBIG );
+    sqlite3_result_error_toobig(pCtx);
+  }
+  if( eOwn ){
+    sqlite3StrAccumInit(pStr, pStr->db, 0, 0, pStr->mxAlloc);
+    if( eOwn==SQLITE_FINISH ){
+      sqlite3_str_free(pStr);
+    }
+  }
+}
+
 #ifndef SQLITE_UNTESTABLE
 /* Force the INT64 value currently stored as the result to be
 ** a MEM_IntReal value.  See the SQLITE_TESTCTRL_RESULT_INTREAL