-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
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
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
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
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
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.
-branch trunk
-tag trunk
+branch sqlite3-result-str
+tag sqlite3-result-str
-ece7af98a8bf982567b489cf82a52519f46a1c9360c82a486dbe1cd0664b1555
+cdbc62c36a37ad687589917c6f1e92ded6d99959c54eb8d79eaaef6fa57dacd3
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 ){
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);
}
}
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);
}
/*
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
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{
}
}
}
- 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);
}
0,
0,
#endif
- sqlite3_incomplete
+ /* Version 3.54.0 and later */
+ sqlite3_incomplete,
+ sqlite3_result_str
};
/* True if x is the directory separator character
**
** ^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.
#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
** <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;
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
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);
};
/*
#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)
/**************************** 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 */
}
}
-/* 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;
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;
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