1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-10-21 11:13:54 +03:00
Files
sqlite/ext/misc/resfmt.c
drh 6e334ef616 More capabilities being added. Incremental check-in.
FossilOrigin-Name: 9adaf791f88875c5afeba7e7aa72efb59df42c6052898f8d7e2f83aede00a044
2025-10-20 19:55:26 +00:00

238 lines
6.5 KiB
C

/*
** 2025-10-20
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
*************************************************************************
** Implementation of the Result-Format or "resfmt" utility library for SQLite.
** See the resfmt.md documentation for additional information.
*/
#include "resfmt.h"
#include <string.h>
/*
** Private state information. Subject to change from one release to the
** next.
*/
struct sqlite3_resfmt {
sqlite3_stmt *pStmt; /* The statement whose output is to be rendered */
sqlite3 *db; /* The corresponding database connection */
sqlite3_str *pErr; /* Error message, or NULL */
sqlite3_str *pOut; /* Accumulated output */
int iErr; /* Error code */
int nCol; /* Number of output columns */
sqlite3_int64 nRow; /* Number of rows handled so far */
sqlite3_resfmt_spec spec; /* Copy of the original spec */
};
/*
** Free memory associated with pResfmt
*/
static void resfmtFree(sqlite3_resfmt *p){
if( p->pErr ) sqlite3_free(sqlite3_str_finish(p->pErr));
if( p->pOut ) sqlite3_free(sqlite3_str_finish(p->pOut));
sqlite3_free(p);
}
/*
** If xWrite is defined, send all content of pOut to xWrite and
** reset pOut.
*/
static void resfmtWrite(sqlite3_resfmt *p){
int n;
if( p->spec.xWrite && (n = sqlite3_str_length(p->pOut))>0 ){
p->spec.xWrite(p->spec.pWriteArg,
(const unsigned char*)sqlite3_str_value(p->pOut),
(size_t)n);
sqlite3_str_reset(p->pOut);
}
}
/*
** Encode text appropriately and append it to p->pOut.
*/
static void resfmtEncodeText(sqlite3_resfmt *p, const char *zTxt){
if( p->spec.bQuote ){
sqlite3_str_appendf(p->pOut, "%Q", zTxt);
}else{
sqlite3_str_appendall(p->pOut, zTxt);
}
}
/*
** Render value pVal into p->pOut
*/
static void resfmtRenderValue(sqlite3_resfmt *p, int iCol){
if( p->spec.xRender ){
sqlite3_value *pVal;
char *z;
pVal = sqlite3_value_dup(sqlite3_column_value(p->pStmt,iCol));
z = p->spec.xRender(p->spec.pRenderArg, pVal);
sqlite3_value_free(pVal);
if( z ){
sqlite3_str_appendall(p->pOut, z);
sqlite3_free(z);
return;
}
}
switch( sqlite3_column_type(p->pStmt,iCol) ){
case SQLITE_INTEGER: {
sqlite3_str_appendf(p->pOut, "%lld", sqlite3_column_int64(p->pStmt,iCol));
break;
}
case SQLITE_FLOAT: {
if( p->spec.zFloatFmt ){
double r = sqlite3_column_double(p->pStmt,iCol);
sqlite3_str_appendf(p->pOut, p->spec.zFloatFmt, r);
}else{
const char *zTxt = (const char*)sqlite3_column_text(p->pStmt,iCol);
sqlite3_str_appendall(p->pOut, zTxt);
}
break;
}
case SQLITE_BLOB: {
if( p->spec.bQuote ){
int iStart = sqlite3_str_length(p->pOut);
int nBlob = sqlite3_column_bytes(p->pStmt,iCol);
int i, j;
char *zVal;
const unsigned char *a = sqlite3_column_blob(p->pStmt,iCol);
sqlite3_str_append(p->pOut, "x'", 2);
sqlite3_str_appendchar(p->pOut, nBlob, ' ');
sqlite3_str_appendchar(p->pOut, nBlob, ' ');
sqlite3_str_appendchar(p->pOut, 1, '\'');
if( sqlite3_str_errcode(p->pOut) ) return;
zVal = sqlite3_str_value(p->pOut);
for(i=0, j=iStart+2; i<nBlob; i++, j+=2){
unsigned char c = a[i];
zVal[j] = "0123456789abcdef"[(c>>4)&0xf];
zVal[j+1] = "0123456789abcdef"[(c)&0xf];
}
}else{
const char *zTxt = (const char*)sqlite3_column_text(p->pStmt,iCol);
sqlite3_str_appendall(p->pOut, zTxt);
}
break;
}
case SQLITE_NULL: {
sqlite3_str_appendall(p->pOut, p->spec.zNull);
break;
}
case SQLITE_TEXT: {
const char *zTxt = (const char*)sqlite3_column_text(p->pStmt,iCol);
resfmtEncodeText(p, zTxt);
break;
}
}
}
/*
** Create a new rendering object
*/
sqlite3_resfmt *sqlite3_resfmt_begin(
sqlite3_stmt *pStmt,
sqlite3_resfmt_spec *pSpec
){
sqlite3_resfmt *p; /* The new sqlite3_resfmt being created */
size_t sz; /* Size of pSpec[], based on pSpec->iVersion */
if( pStmt==0 ) return 0;
if( pSpec==0 ) return 0;
if( pSpec->iVersion!=1 ) return 0;
p = sqlite3_malloc64( sizeof(*p) );
if( p==0 ) return 0;
p->pStmt = pStmt;
p->db = sqlite3_db_handle(pStmt);
p->pErr = 0;
p->pOut = sqlite3_str_new(p->db);
if( p->pOut==0 ){
resfmtFree(p);
return 0;
}
p->iErr = 0;
p->nCol = sqlite3_column_count(p->pStmt);
p->nRow = 0;
sz = sizeof(sqlite3_resfmt_spec);
memcpy(&p->spec, pSpec, sz);
if( p->spec.zNull==0 ) p->spec.zNull = "";
switch( p->spec.eFormat ){
case RESFMT_List: {
if( p->spec.zColumnSep==0 ) p->spec.zColumnSep = "|";
if( p->spec.zRowSep==0 ) p->spec.zRowSep = "\n";
break;
}
}
return p;
}
/*
** Render a single row of output.
*/
int sqlite3_resfmt_row(sqlite3_resfmt *p){
int rc = SQLITE_OK;
int i;
if( p==0 ) return SQLITE_DONE;
switch( p->spec.eFormat ){
case RESFMT_Off:
case RESFMT_Count: {
/* No-op */
break;
}
default: { /* RESFMT_List */
if( p->nRow==0 && p->spec.bShowCNames ){
for(i=0; i<p->nCol; i++){
const char *zCName = sqlite3_column_name(p->pStmt, i);
if( i>0 ) sqlite3_str_appendall(p->pOut, p->spec.zColumnSep);
resfmtEncodeText(p, zCName);
}
sqlite3_str_appendall(p->pOut, p->spec.zRowSep);
resfmtWrite(p);
}
for(i=0; i<p->nCol; i++){
if( i>0 ) sqlite3_str_appendall(p->pOut, p->spec.zColumnSep);
resfmtRenderValue(p, i);
}
sqlite3_str_appendall(p->pOut, p->spec.zRowSep);
resfmtWrite(p);
break;
}
}
p->nRow++;
return rc;
}
/*
** Finish rendering the results
*/
int sqlite3_resfmt_finish(sqlite3_resfmt *p, int *piErr, char **pzErrMsg){
if( p==0 ){
return SQLITE_OK;
}
switch( p->spec.eFormat ){
case RESFMT_Count: {
sqlite3_str_appendf(p->pOut, "%lld\n", p->nRow);
resfmtWrite(p);
break;
}
}
if( p->spec.pzOutput ){
*p->spec.pzOutput = sqlite3_str_finish(p->pOut);
p->pOut = 0;
}
if( piErr ){
*piErr = p->iErr;
}
if( pzErrMsg ){
*pzErrMsg = sqlite3_str_finish(p->pErr);
p->pErr = 0;
}
resfmtFree(p);
return SQLITE_OK;
}