当前位置: 首页 > news >正文

sqlite blob 数据检索(基于sqlite3_get_table的优化)

sqlite blob 数据检索(基于sqlite3_get_table的优化

  • sqlite数据库blob数据插入与检索
    • blob数据的插入
    • blob数据的检索
  • sqlite_get_table 检索blob数据存在的问题
  • sqlite3_get_table的优化

sqlite数据库blob数据插入与检索

sqlite数据库共有 SQLITE_INTEGER SQLITE_FLOAT SQLITE_BLOB SQLITE_NULL SQLITE_TEXT 五中数据类型,分别为整形、浮点、块类型、空类型和文本。Blob类型是一种块状的二进制数据,在实际应用时会保存一些我们用到的二进制数据。比如有一个

struct student{int score;char name[64];
};

结构体数据需要保存,在实际应用时会将该保存该结构体数据的内存地址和结构体长度传给数据库接口,调用数据库接口时会将内存中的数据拷贝进数据库文件中。

blob数据的插入

sqlite数据库往表中插入一条数据的sql语句如下:

INSERT INTO TABLE_NAME [(column1, column2, column3,...columnN)] VALUES (value1, value2, value3,...valueN);

其中 TABLE_NAME是表名称,column1column2,… ,columnN 是表中需要插入的列名称,value1value2,… ,valueN是插入的值。如果要插入bolb数据,则不能用简单的insert语句。

#include <sqlite3.h>
#include <stdio.h>int main() {sqlite3 *db;sqlite3_stmt *stmt;int rc;rc = sqlite3_open("example.db", &db);if (rc != SQLITE_OK) {fprintf(stderr, "Cannot open database: %s\n", sqlite3_errmsg(db));sqlite3_close(db);return 1;}const char *sql = "INSERT INTO blob_table (blob_column) VALUES (?)";rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);if (rc != SQLITE_OK) {fprintf(stderr, "Failed to prepare statement: %s\n", sqlite3_errmsg(db));sqlite3_close(db);return 1;}// 假设我们有一个名为data和size的BLOB数据和大小// 你需要替换为实际的数据和大小unsigned char *data = ...;int size = ...;rc = sqlite3_bind_blob(stmt, 1, data, size, SQLITE_TRANSIENT);if (rc != SQLITE_OK) {fprintf(stderr, "Failed to bind blob: %s\n", sqlite3_errmsg(db));sqlite3_close(db);return 1;}rc = sqlite3_step(stmt);if (rc != SQLITE_DONE) {fprintf(stderr, "Failed to execute statement: %s\n", sqlite3_errmsg(db));sqlite3_close(db);return 1;}sqlite3_finalize(stmt);sqlite3_close(db);return 0;
}

blob 数据的插入需要 sqlite3_prepare_v2sqlite3_bind_blobsqlite3_stepsqlite3_finalize 等函数配合使用

blob数据的检索

sqlite获取检索结果采用 sqlite3_get_table 函数:

int sqlite3_get_table(sqlite3 *db,          /* An open database */const char *zSql,     /* SQL to be evaluated */char ***pazResult,    /* Results of the query */int *pnRow,           /* Number of result rows written here */int *pnColumn,        /* Number of result columns written here */char **pzErrmsg       /* Error msg written here */
);
void sqlite3_free_table(char **result);

调用 sqlite3_get_table 函数时,会从数据库 db 中执行 zSql 语句,检索结果放在一个动态申请的二维字符串数组中,字符串数组地址通过 pazResult 参数返回。在检索结果使用完之后,需要用 sqlite3_free_table 函数释放 pazResult 所指向的二维数组。

sqlite_get_table 检索blob数据存在的问题

sqlite_get_table 函数返回结果是二维字符串数组,对于整形、浮点、字符串类型数据,可以将其打印成字符串到字符串数组中,空类型可以打印空字符串或者 NULL 字符串。blob数据是二进制对象数据,并没有合适的将其转换成字符串的方法。在 sqlite_get_table 函数返回的结果数组中,blob数据的首地址将返回,但是返回结果的内容长度却与真实blob数据长度不符合。

SQLITE_API int sqlite3_get_table(sqlite3 *db,                /* The database on which the SQL executes */const char *zSql,           /* The SQL to be executed */char ***pazResult,          /* Write the result table here */int *pnRow,                 /* Write the number of rows in the result here */int *pnColumn,              /* Write the number of columns of result here */char **pzErrMsg             /* Write error messages here */
){int rc;TabResult res;#ifdef SQLITE_ENABLE_API_ARMORif( !sqlite3SafetyCheckOk(db) || pazResult==0 ) return SQLITE_MISUSE_BKPT;
#endif*pazResult = 0;if( pnColumn ) *pnColumn = 0;if( pnRow ) *pnRow = 0;if( pzErrMsg ) *pzErrMsg = 0;res.zErrMsg = 0;res.nRow = 0;res.nColumn = 0;res.nData = 1;res.nAlloc = 20;res.rc = SQLITE_OK;res.azResult = sqlite3_malloc64(sizeof(char*)*res.nAlloc );if( res.azResult==0 ){db->errCode = SQLITE_NOMEM;return SQLITE_NOMEM_BKPT;}res.azResult[0] = 0;rc = sqlite3_exec(db, zSql, sqlite3_get_table_cb, &res, pzErrMsg);assert( sizeof(res.azResult[0])>= sizeof(res.nData) );res.azResult[0] = SQLITE_INT_TO_PTR(res.nData);if( (rc&0xff)==SQLITE_ABORT ){sqlite3_free_table(&res.azResult[1]);if( res.zErrMsg ){if( pzErrMsg ){sqlite3_free(*pzErrMsg);*pzErrMsg = sqlite3_mprintf("%s",res.zErrMsg);}sqlite3_free(res.zErrMsg);}db->errCode = res.rc;  /* Assume 32-bit assignment is atomic */return res.rc;}sqlite3_free(res.zErrMsg);if( rc!=SQLITE_OK ){sqlite3_free_table(&res.azResult[1]);return rc;}if( res.nAlloc>res.nData ){char **azNew;azNew = sqlite3Realloc( res.azResult, sizeof(char*)*res.nData );if( azNew==0 ){sqlite3_free_table(&res.azResult[1]);db->errCode = SQLITE_NOMEM;return SQLITE_NOMEM_BKPT;}res.azResult = azNew;}*pazResult = &res.azResult[1];if( pnColumn ) *pnColumn = res.nColumn;if( pnRow ) *pnRow = res.nRow;return rc;
}

sqlite3_get_table 函数代码如上,其主要实现手段是 sqlite3_exec 执行sql语句,执行过程中通过传入的 sqlite3_get_table_cb 回调函数对检索数据进行处理。

static int sqlite3_get_table_cb(void *pArg, int nCol, char **argv, char **colv){TabResult *p = (TabResult*)pArg;  /* Result accumulator */int need;                         /* Slots needed in p->azResult[] */int i;                            /* Loop counter */char *z;                          /* A single column of result *//* Make sure there is enough space in p->azResult to hold everything** we need to remember from this invocation of the callback.*/if( p->nRow==0 && argv!=0 ){need = nCol*2;}else{need = nCol;}if( p->nData + need > p->nAlloc ){char **azNew;p->nAlloc = p->nAlloc*2 + need;azNew = sqlite3Realloc( p->azResult, sizeof(char*)*p->nAlloc );if( azNew==0 ) goto malloc_failed;p->azResult = azNew;}/* If this is the first row, then generate an extra row containing** the names of all columns.*/if( p->nRow==0 ){p->nColumn = nCol;for(i=0; i<nCol; i++){z = sqlite3_mprintf("%s", colv[i]);if( z==0 ) goto malloc_failed;p->azResult[p->nData++] = z;}}else if( (int)p->nColumn!=nCol ){sqlite3_free(p->zErrMsg);p->zErrMsg = sqlite3_mprintf("sqlite3_get_table() called with two or more incompatible queries");p->rc = SQLITE_ERROR;return 1;}/* Copy over the row data*/if( argv!=0 ){for(i=0; i<nCol; i++){if( argv[i]==0 ){z = 0;}else{int n = sqlite3Strlen30(argv[i])+1;z = sqlite3_malloc64( n );if( z==0 ) goto malloc_failed;memcpy(z, argv[i], n);}p->azResult[p->nData++] = z;}p->nRow++;}return 0;malloc_failed:p->rc = SQLITE_NOMEM_BKPT;return 1;
}

在回调函数 sqlite3_get_table_cb 中,Copy over the row data 时,通过 sqlite3Strlen30 判断该单元数据的长度;然后通过sqlite3_malloc64分配内存。

SQLITE_PRIVATE int sqlite3Strlen30(const char *z){if( z==0 ) return 0;return 0x3fffffff & (int)strlen(z);
}

由此可知,长度是通过 strlen 获取的。
回到 sqlite3_exec 函数,其代码实现

SQLITE_API int sqlite3_exec(sqlite3 *db,                /* The database on which the SQL executes */const char *zSql,           /* The SQL to be executed */sqlite3_callback xCallback, /* Invoke this callback routine */void *pArg,                 /* First argument to xCallback() */char **pzErrMsg             /* Write error messages here */
){int rc = SQLITE_OK;         /* Return code */const char *zLeftover;      /* Tail of unprocessed SQL */sqlite3_stmt *pStmt = 0;    /* The current SQL statement */char **azCols = 0;          /* Names of result columns */int callbackIsInit;         /* True if callback data is initialized */if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT;if( zSql==0 ) zSql = "";sqlite3_mutex_enter(db->mutex);sqlite3Error(db, SQLITE_OK);while( rc==SQLITE_OK && zSql[0] ){int nCol = 0;char **azVals = 0;pStmt = 0;rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, &zLeftover);assert( rc==SQLITE_OK || pStmt==0 );if( rc!=SQLITE_OK ){continue;}if( !pStmt ){/* this happens for a comment or white-space */zSql = zLeftover;continue;}callbackIsInit = 0;while( 1 ){int i;rc = sqlite3_step(pStmt);/* Invoke the callback function if required */if( xCallback && (SQLITE_ROW==rc ||(SQLITE_DONE==rc && !callbackIsInit&& db->flags&SQLITE_NullCallback)) ){if( !callbackIsInit ){nCol = sqlite3_column_count(pStmt);azCols = sqlite3DbMallocRaw(db, (2*nCol+1)*sizeof(const char*));if( azCols==0 ){goto exec_out;}for(i=0; i<nCol; i++){azCols[i] = (char *)sqlite3_column_name(pStmt, i);/* sqlite3VdbeSetColName() installs column names as UTF8** strings so there is no way for sqlite3_column_name() to fail. */assert( azCols[i]!=0 );}callbackIsInit = 1;}if( rc==SQLITE_ROW ){azVals = &azCols[nCol];for(i=0; i<nCol; i++){azVals[i] = (char *)sqlite3_column_text(pStmt, i);if( !azVals[i] && sqlite3_column_type(pStmt, i)!=SQLITE_NULL ){sqlite3OomFault(db);goto exec_out;}}azVals[i] = 0;}if( xCallback(pArg, nCol, azVals, azCols) ){/* EVIDENCE-OF: R-38229-40159 If the callback function to** sqlite3_exec() returns non-zero, then sqlite3_exec() will** return SQLITE_ABORT. */rc = SQLITE_ABORT;sqlite3VdbeFinalize((Vdbe *)pStmt);pStmt = 0;sqlite3Error(db, SQLITE_ABORT);goto exec_out;}}if( rc!=SQLITE_ROW ){rc = sqlite3VdbeFinalize((Vdbe *)pStmt);pStmt = 0;zSql = zLeftover;while( sqlite3Isspace(zSql[0]) ) zSql++;break;}}sqlite3DbFree(db, azCols);azCols = 0;}exec_out:if( pStmt ) sqlite3VdbeFinalize((Vdbe *)pStmt);sqlite3DbFree(db, azCols);rc = sqlite3ApiExit(db, rc);if( rc!=SQLITE_OK && pzErrMsg ){*pzErrMsg = sqlite3DbStrDup(0, sqlite3_errmsg(db));if( *pzErrMsg==0 ){rc = SQLITE_NOMEM_BKPT;sqlite3Error(db, SQLITE_NOMEM);}}else if( pzErrMsg ){*pzErrMsg = 0;}assert( (rc&db->errMask)==rc );sqlite3_mutex_leave(db->mutex);return rc;
}

sqlite3_exec 函数主要是通过 sqlite3_prepare_v2sqlite3_stepxCallback回调函数入参来实现的,在每一次查询到结果时,调用sqlite3_get_table_cb 回调函数对检索数据进行处理。数据拿取和回调处理的代码如下:

if( rc==SQLITE_ROW ){azVals = &azCols[nCol];for(i=0; i<nCol; i++){azVals[i] = (char *)sqlite3_column_text(pStmt, i);if( !azVals[i] && sqlite3_column_type(pStmt, i)!=SQLITE_NULL ){sqlite3OomFault(db);goto exec_out;}}azVals[i] = 0;}if( xCallback(pArg, nCol, azVals, azCols) ){/* EVIDENCE-OF: R-38229-40159 If the callback function to** sqlite3_exec() returns non-zero, then sqlite3_exec() will** return SQLITE_ABORT. */rc = SQLITE_ABORT;sqlite3VdbeFinalize((Vdbe *)pStmt);pStmt = 0;sqlite3Error(db, SQLITE_ABORT);goto exec_out;}

sqlite3_column_text 代码如下

struct sqlite3_value {union MemValue {double r;           /* Real value used when MEM_Real is set in flags */i64 i;              /* Integer value used when MEM_Int is set in flags */int nZero;          /* Extra zero bytes when MEM_Zero and MEM_Blob set */const char *zPType; /* Pointer type when MEM_Term|MEM_Subtype|MEM_Null */FuncDef *pDef;      /* Used only when flags==MEM_Agg */} u;char *z;            /* String or BLOB value */int n;              /* Number of characters in string value, excluding '\0' */u16 flags;          /* Some combination of MEM_Null, MEM_Str, MEM_Dyn, etc. */u8  enc;            /* SQLITE_UTF8, SQLITE_UTF16BE, SQLITE_UTF16LE */u8  eSubtype;       /* Subtype for this value *//* ShallowCopy only needs to copy the information above */sqlite3 *db;        /* The associated database connection */int szMalloc;       /* Size of the zMalloc allocation */u32 uTemp;          /* Transient storage for serial_type in OP_MakeRecord */char *zMalloc;      /* Space to hold MEM_Str or MEM_Blob if szMalloc>0 */void (*xDel)(void*);/* Destructor for Mem.z - only valid if MEM_Dyn */
#ifdef SQLITE_DEBUGMem *pScopyFrom;    /* This Mem is a shallow copy of pScopyFrom */u16 mScopyFlags;    /* flags value immediately after the shallow copy */
#endif
};
static SQLITE_NOINLINE const void *valueToText(sqlite3_value* pVal, u8 enc){assert( pVal!=0 );assert( pVal->db==0 || sqlite3_mutex_held(pVal->db->mutex) );assert( (enc&3)==(enc&~SQLITE_UTF16_ALIGNED) );assert( !sqlite3VdbeMemIsRowSet(pVal) );assert( (pVal->flags & (MEM_Null))==0 );if( pVal->flags & (MEM_Blob|MEM_Str) ){if( ExpandBlob(pVal) ) return 0;pVal->flags |= MEM_Str;if( pVal->enc != (enc & ~SQLITE_UTF16_ALIGNED) ){sqlite3VdbeChangeEncoding(pVal, enc & ~SQLITE_UTF16_ALIGNED);}if( (enc & SQLITE_UTF16_ALIGNED)!=0 && 1==(1&SQLITE_PTR_TO_INT(pVal->z)) ){assert( (pVal->flags & (MEM_Ephem|MEM_Static))!=0 );if( sqlite3VdbeMemMakeWriteable(pVal)!=SQLITE_OK ){return 0;}}sqlite3VdbeMemNulTerminate(pVal); /* IMP: R-31275-44060 */}else{sqlite3VdbeMemStringify(pVal, enc, 0);assert( 0==(1&SQLITE_PTR_TO_INT(pVal->z)) );}assert(pVal->enc==(enc & ~SQLITE_UTF16_ALIGNED) || pVal->db==0|| pVal->db->mallocFailed );if( pVal->enc==(enc & ~SQLITE_UTF16_ALIGNED) ){assert( sqlite3VdbeMemValidStrRep(pVal) );return pVal->z;}else{return 0;}
}SQLITE_PRIVATE const void *sqlite3ValueText(sqlite3_value* pVal, u8 enc){if( !pVal ) return 0;assert( pVal->db==0 || sqlite3_mutex_held(pVal->db->mutex) );assert( (enc&3)==(enc&~SQLITE_UTF16_ALIGNED) );assert( !sqlite3VdbeMemIsRowSet(pVal) );if( (pVal->flags&(MEM_Str|MEM_Term))==(MEM_Str|MEM_Term) && pVal->enc==enc ){assert( sqlite3VdbeMemValidStrRep(pVal) );return pVal->z;}if( pVal->flags&MEM_Null ){return 0;}return valueToText(pVal, enc);
}SQLITE_API const unsigned char *sqlite3_value_text(sqlite3_value *pVal){return (const unsigned char *)sqlite3ValueText(pVal, SQLITE_UTF8);
}SQLITE_API const unsigned char *sqlite3_column_text(sqlite3_stmt *pStmt, int i){const unsigned char *val = sqlite3_value_text( columnMem(pStmt,i) );columnMallocFailure(pStmt);return val;
}

由上可知 blob数据字符串数据在查询时获取到数据是一样的,都通过 sqlite3_value 结构体中的 z指针指向。但是在执行 sqlite_exec 在执行查询返回表格时,分配给两种数据的buf长度却是不一样的,通过strlen能正确获取字符串数据的长度,却不能获取blob数据长度。比如

struct A{char a;char b;int c;
}
struct A blob;
blob.a = 1;
blob b = 0;
blob c = 1;
strlen(&blob); //此处返回1,实际blob数据的长度至少是6

因此上层接口 sqlite3_get_table获取到的结果不适用于blob数据,blob数据可能是被截断的,直接按照完整的数据访问可能导致内存越界。

sqlite3_get_table的优化

解决上述问题,可以修改sqlite 源码,改造 sqlite3_get_table 函数。要想能正确拷贝blob数据,1)需要知道表格中该列数据是否为blob类型,2)需要知道表格中该blob数据的长度是多少。在sqlite3_exec 执行语句时,sqlite3_column_typesqlite3_column_bytes 可以知道当前列的数据类型和数据长度。因此,可以根据上述信息修改 sqlite3_execsqlite3_get_table_cb
如下所示 sqlite3_exec_v3sqlite3_get_table_cb_v3 可以实现blob数据的正确拷贝,代码中注释 //新增代码//修改代码 是相对于原有代码修改的部分。sqlite3_get_table 内部几乎没有改动,只需要将 sqlite3_execsqlite3_get_table_cb 替换掉即可。

SQLITE_API int sqlite3_exec(sqlite3 *db,                /* The database on which the SQL executes */const char *zSql,           /* The SQL to be executed */sqlite3_callback xCallback, /* Invoke this callback routine */void *pArg,                 /* First argument to xCallback() */char **pzErrMsg             /* Write error messages here */
){int rc = SQLITE_OK;         /* Return code */const char *zLeftover;      /* Tail of unprocessed SQL */sqlite3_stmt *pStmt = 0;    /* The current SQL statement */char **azCols = 0;          /* Names of result columns */int * izTypes = 0;	//新增代码,列类型数据int * izLens = 0;		//新增代码,数据长度int callbackIsInit;         /* True if callback data is initialized */if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT;if( zSql==0 ) zSql = "";sqlite3_mutex_enter(db->mutex);sqlite3Error(db, SQLITE_OK);while( rc==SQLITE_OK && zSql[0] ){int nCol = 0;char **azVals = 0;pStmt = 0;rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, &zLeftover);assert( rc==SQLITE_OK || pStmt==0 );if( rc!=SQLITE_OK ){continue;}if( !pStmt ){/* this happens for a comment or white-space */zSql = zLeftover;continue;}callbackIsInit = 0;while( 1 ){int i;rc = sqlite3_step(pStmt);/* Invoke the callback function if required */if( xCallback && (SQLITE_ROW==rc ||(SQLITE_DONE==rc && !callbackIsInit&& db->flags&SQLITE_NullCallback)) ){if( !callbackIsInit ){nCol = sqlite3_column_count(pStmt);azCols = sqlite3DbMallocRaw(db, (2*nCol+1)*sizeof(const char*));izTypes = sqlite3DbMallocRaw(db, nCol*sizeof(int)); //新增代码izLens = sqlite3DbMallocRaw(db, nCol*sizeof(int));  //新增代码if( azCols==0 || 0==izTypes || 0==izLens){	//新增代码goto exec_out;}for(i=0; i<nCol; i++){azCols[i] = (char *)sqlite3_column_name(pStmt, i);izTypes[i] = sqlite3_column_type(pStmt, i);	//新增代码izLens[i] = sqlite3_column_bytes(pStmt, i); //新增代码/* sqlite3VdbeSetColName() installs column names as UTF8** strings so there is no way for sqlite3_column_name() to fail. */assert( azCols[i]!=0 );}callbackIsInit = 1;}if( rc==SQLITE_ROW ){azVals = &azCols[nCol];for(i=0; i<nCol; i++){azVals[i] = (char *)sqlite3_column_text(pStmt, i);if( !azVals[i] && sqlite3_column_type(pStmt, i)!=SQLITE_NULL ){sqlite3OomFault(db);goto exec_out;}}azVals[i] = 0;}if( xCallback(pArg, nCol, azVals, azCols, izTypes, izLens) ){  //修改代码,回调函数新增入参/* EVIDENCE-OF: R-38229-40159 If the callback function to** sqlite3_exec() returns non-zero, then sqlite3_exec() will** return SQLITE_ABORT. */rc = SQLITE_ABORT;sqlite3VdbeFinalize((Vdbe *)pStmt);pStmt = 0;sqlite3Error(db, SQLITE_ABORT);goto exec_out;}}if( rc!=SQLITE_ROW ){rc = sqlite3VdbeFinalize((Vdbe *)pStmt);pStmt = 0;zSql = zLeftover;while( sqlite3Isspace(zSql[0]) ) zSql++;break;}}sqlite3DbFree(db, azCols);azCols = 0;sqlite3DbFree(db, izTypes);	//新增代码sqlite3DbFree(db, izLens);	//新增代码izTypes = 0;	//新增代码izLens = 0;		//新增代码}exec_out:if( pStmt ) sqlite3VdbeFinalize((Vdbe *)pStmt);sqlite3DbFree(db, azCols);rc = sqlite3ApiExit(db, rc);if( rc!=SQLITE_OK && pzErrMsg ){*pzErrMsg = sqlite3DbStrDup(0, sqlite3_errmsg(db));if( *pzErrMsg==0 ){rc = SQLITE_NOMEM_BKPT;sqlite3Error(db, SQLITE_NOMEM);}}else if( pzErrMsg ){*pzErrMsg = 0;}assert( (rc&db->errMask)==rc );sqlite3_mutex_leave(db->mutex);return rc;
}//修改回调函数,增加入参 izTypes izLens
static int sqlite3_get_table_cb_v3(void *pArg, int nCol, char **argv, char **colv, int * izTypes, int * izLens){TabResult *p = (TabResult*)pArg;  /* Result accumulator */int need;                         /* Slots needed in p->azResult[] */int i;                            /* Loop counter */char *z;                          /* A single column of result *//* Make sure there is enough space in p->azResult to hold everything** we need to remember from this invocation of the callback.*/if( p->nRow==0 && argv!=0 ){need = nCol*2;}else{need = nCol;}if( p->nData + need > p->nAlloc ){char **azNew;p->nAlloc = p->nAlloc*2 + need;azNew = sqlite3Realloc( p->azResult, sizeof(char*)*p->nAlloc );if( azNew==0 ) goto malloc_failed;p->azResult = azNew;}/* If this is the first row, then generate an extra row containing** the names of all columns.*/if( p->nRow==0 ){p->nColumn = nCol;for(i=0; i<nCol; i++){z = sqlite3_mprintf("%s", colv[i]);if( z==0 ) goto malloc_failed;p->azResult[p->nData++] = z;}}else if( (int)p->nColumn!=nCol ){sqlite3_free(p->zErrMsg);p->zErrMsg = sqlite3_mprintf("sqlite3_get_table() called with two or more incompatible queries");p->rc = SQLITE_ERROR;return 1;}/* Copy over the row data*/if( argv!=0 ){for(i=0; i<nCol; i++){if( argv[i]==0 ){z = 0;}else{int n = sqlite3Strlen30(argv[i])+1;if(SQLITE_BLOB == izTypes[i])	//新增代码n = izLens[i];				//新增代码z = sqlite3_malloc64( n );if( z==0 ) goto malloc_failed;memcpy(z, argv[i], n);}p->azResult[p->nData++] = z;}p->nRow++;}return 0;malloc_failed:p->rc = SQLITE_NOMEM_BKPT;return 1;
}

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 如何使用Gitee管理自己的项目
  • 【自由能系列(初级)】自由能原理——神经科学的“能量守恒”方程
  • 惠海H6900B升压恒流调光IC芯片3.7V7.4V12V升压18V24V36V 48V 植物灯/电解水
  • 娱乐社交、游戏等行业共探合规前提下,实现产品可持续的增长与营收 | 网易数智x华为云泛娱乐行业沙龙-杭州站邀您前来!
  • 学苑教育杂志社学苑教育编辑部学苑教育杂志2024年第23期目录
  • 解决有向图中节点出度和入度计算问题
  • 万字详解Spring框架基础(Java开发社区最受欢迎的框架之一)
  • python 和C通过共享内存通信
  • Redis 实现哨兵模式
  • uniapp,uview:inputnumber或者input,当type为number的时候,在ios里输入不了小数的问题
  • 歌曲转换成mp3格式免费秘籍,安利6款音频转换软件(简单实用)
  • JVM 性能分析 —— CMS 老年代并发 GC 触发条件与压缩式 GC (升级为 Full GC)触发条件
  • 数据库命令与语句
  • 深度学习入门-10
  • 金融知识普及月答题活动
  • ES6指北【2】—— 箭头函数
  • [原]深入对比数据科学工具箱:Python和R 非结构化数据的结构化
  • Apache的80端口被占用以及访问时报错403
  • ComponentOne 2017 V2版本正式发布
  • cookie和session
  • Java方法详解
  • js 实现textarea输入字数提示
  • Js实现点击查看全文(类似今日头条、知乎日报效果)
  • leetcode讲解--894. All Possible Full Binary Trees
  • PHP 7 修改了什么呢 -- 2
  • react-core-image-upload 一款轻量级图片上传裁剪插件
  • socket.io+express实现聊天室的思考(三)
  • Spring Cloud Alibaba迁移指南(一):一行代码从 Hystrix 迁移到 Sentinel
  • Spring Cloud(3) - 服务治理: Spring Cloud Eureka
  • Vue2.0 实现互斥
  • 阿里云ubuntu14.04 Nginx反向代理Nodejs
  • 给自己的博客网站加上酷炫的初音未来音乐游戏?
  • 机器学习 vs. 深度学习
  • 深度解析利用ES6进行Promise封装总结
  • 一文看透浏览器架构
  • 异步
  • # 手柄编程_北通阿修罗3动手评:一款兼具功能、操控性的电竞手柄
  • #ubuntu# #git# repository git config --global --add safe.directory
  • #每日一题合集#牛客JZ23-JZ33
  • $.each()与$(selector).each()
  • (android 地图实战开发)3 在地图上显示当前位置和自定义银行位置
  • (C语言)逆序输出字符串
  • (ZT) 理解系统底层的概念是多么重要(by趋势科技邹飞)
  • (读书笔记)Javascript高级程序设计---ECMAScript基础
  • (二刷)代码随想录第16天|104.二叉树的最大深度 559.n叉树的最大深度● 111.二叉树的最小深度● 222.完全二叉树的节点个数
  • (附源码)ssm教师工作量核算统计系统 毕业设计 162307
  • (附源码)小程序儿童艺术培训机构教育管理小程序 毕业设计 201740
  • (教学思路 C#之类三)方法参数类型(ref、out、parmas)
  • (一)项目实践-利用Appdesigner制作目标跟踪仿真软件
  • (转)用.Net的File控件上传文件的解决方案
  • (转载)虚幻引擎3--【UnrealScript教程】章节一:20.location和rotation
  • *算法训练(leetcode)第四十七天 | 并查集理论基础、107. 寻找存在的路径
  • .“空心村”成因分析及解决对策122344
  • .MSSQLSERVER 导入导出 命令集--堪称经典,值得借鉴!
  • .NET CLR基本术语