1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-08-08 14:02:16 +03:00

Modify the Tcl interface to use sqlite3_prepare_v2(). (CVS 4542)

FossilOrigin-Name: 7bb00c7df672cb8988121f30e3bbdea9ad951603
This commit is contained in:
danielk1977
2007-11-13 10:30:24 +00:00
parent 65d3c13cad
commit 8e556520e9
9 changed files with 161 additions and 71 deletions

View File

@@ -12,7 +12,7 @@
** A TCL Interface to SQLite. Append this file to sqlite3.c and
** compile the whole thing to build a TCL-enabled version of SQLite.
**
** $Id: tclsqlite.c,v 1.205 2007/11/12 17:56:43 drh Exp $
** $Id: tclsqlite.c,v 1.206 2007/11/13 10:30:26 danielk1977 Exp $
*/
#include "tcl.h"
#include <errno.h>
@@ -886,6 +886,67 @@ static char *local_getline(char *zPrompt, FILE *in){
return zLine;
}
/*
** Figure out the column names for the data returned by the statement
** passed as the second argument.
**
** If parameter papColName is not NULL, then *papColName is set to point
** at an array allocated using Tcl_Alloc(). It is the callers responsibility
** to free this array using Tcl_Free(), and to decrement the reference
** count of each Tcl_Obj* member of the array.
**
** The return value of this function is the number of columns of data
** returned by pStmt (and hence the size of the *papColName array).
**
** If pArray is not NULL, then it contains the name of a Tcl array
** variable. The "*" member of this array is set to a list containing
** the names of the columns returned by the statement, in order from
** left to right. e.g. if the names of the returned columns are a, b and
** c, it does the equivalent of the tcl command:
**
** set ${pArray}(*) {a b c}
*/
static int
computeColumnNames(
Tcl_Interp *interp,
sqlite3_stmt *pStmt, /* SQL statement */
Tcl_Obj ***papColName, /* OUT: Array of column names */
Tcl_Obj *pArray /* Name of array variable (may be null) */
){
int nCol;
/* Compute column names */
nCol = sqlite3_column_count(pStmt);
if( papColName ){
int i;
Tcl_Obj **apColName = (Tcl_Obj**)Tcl_Alloc( sizeof(Tcl_Obj*)*nCol );
for(i=0; i<nCol; i++){
apColName[i] = dbTextToObj(sqlite3_column_name(pStmt,i));
Tcl_IncrRefCount(apColName[i]);
}
/* If results are being stored in an array variable, then create
** the array(*) entry for that array
*/
if( pArray ){
Tcl_Obj *pColList = Tcl_NewObj();
Tcl_Obj *pStar = Tcl_NewStringObj("*", -1);
Tcl_IncrRefCount(pColList);
for(i=0; i<nCol; i++){
Tcl_ListObjAppendElement(interp, pColList, apColName[i]);
}
Tcl_IncrRefCount(pStar);
Tcl_ObjSetVar2(interp, pArray, pStar, pColList,0);
Tcl_DecrRefCount(pColList);
Tcl_DecrRefCount(pStar);
}
*papColName = apColName;
}
return nCol;
}
/*
** The "sqlite" command below creates a new Tcl command for each
** connection it opens to an SQLite database. This routine is invoked
@@ -1506,7 +1567,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
while( rc==TCL_OK && zSql[0] ){
int i; /* Loop counter */
int nVar; /* Number of bind parameters in the pStmt */
int nCol; /* Number of columns in the result set */
int nCol = -1; /* Number of columns in the result set */
Tcl_Obj **apColName = 0; /* Array of column names */
int len; /* String length of zSql */
@@ -1514,13 +1575,8 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
** which matches the next sequence of SQL.
*/
pStmt = 0;
pPreStmt = pDb->stmtList;
len = strlen(zSql);
if( pPreStmt && sqlite3_expired(pPreStmt->pStmt) ){
flushStmtCache(pDb);
pPreStmt = 0;
}
for(; pPreStmt; pPreStmt=pPreStmt->pNext){
for(pPreStmt = pDb->stmtList; pPreStmt; pPreStmt=pPreStmt->pNext){
int n = pPreStmt->nSql;
if( len>=n
&& memcmp(pPreStmt->zSql, zSql, n)==0
@@ -1551,7 +1607,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
/* If no prepared statement was found. Compile the SQL text
*/
if( pStmt==0 ){
if( SQLITE_OK!=sqlite3_prepare(pDb->db, zSql, -1, &pStmt, &zLeft) ){
if( SQLITE_OK!=sqlite3_prepare_v2(pDb->db, zSql, -1, &pStmt, &zLeft) ){
Tcl_SetObjResult(interp, dbTextToObj(sqlite3_errmsg(pDb->db)));
rc = TCL_ERROR;
break;
@@ -1625,36 +1681,20 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
}
}
/* Compute column names */
nCol = sqlite3_column_count(pStmt);
if( pScript ){
apColName = (Tcl_Obj**)Tcl_Alloc( sizeof(Tcl_Obj*)*nCol );
if( apColName==0 ) break;
for(i=0; i<nCol; i++){
apColName[i] = dbTextToObj(sqlite3_column_name(pStmt,i));
Tcl_IncrRefCount(apColName[i]);
}
}
/* If results are being stored in an array variable, then create
** the array(*) entry for that array
*/
if( pArray ){
Tcl_Obj *pColList = Tcl_NewObj();
Tcl_Obj *pStar = Tcl_NewStringObj("*", -1);
Tcl_IncrRefCount(pColList);
for(i=0; i<nCol; i++){
Tcl_ListObjAppendElement(interp, pColList, apColName[i]);
}
Tcl_IncrRefCount(pStar);
Tcl_ObjSetVar2(interp, pArray, pStar, pColList,0);
Tcl_DecrRefCount(pColList);
Tcl_DecrRefCount(pStar);
}
/* Execute the SQL
*/
while( rc==TCL_OK && pStmt && SQLITE_ROW==sqlite3_step(pStmt) ){
/* Compute column names. This must be done after the first successful
** call to sqlite3_step(), in case the query is recompiled and the
** number or names of the returned columns changes.
*/
assert(!pArray||pScript);
if (nCol < 0) {
Tcl_Obj ***ap = (pScript?&apColName:0);
nCol = computeColumnNames(interp, pStmt, ap, pArray);
}
for(i=0; i<nCol; i++){
Tcl_Obj *pVal;
@@ -1727,6 +1767,14 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
/* Free the column name objects */
if( pScript ){
/* If the query returned no rows, but an array variable was
** specified, call computeColumnNames() now to populate the
** arrayname(*) variable.
*/
if (pArray && nCol < 0) {
Tcl_Obj ***ap = (pScript?&apColName:0);
nCol = computeColumnNames(interp, pStmt, ap, pArray);
}
for(i=0; i<nCol; i++){
Tcl_DecrRefCount(apColName[i]);
}
@@ -1745,15 +1793,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
** flush the statement cache and try the statement again.
*/
rc2 = sqlite3_reset(pStmt);
if( SQLITE_SCHEMA==rc2 ){
/* After a schema change, flush the cache and try to run the
** statement again
*/
flushStmtCache( pDb );
sqlite3_finalize(pStmt);
if( pPreStmt ) Tcl_Free((char*)pPreStmt);
continue;
}else if( SQLITE_OK!=rc2 ){
if( SQLITE_OK!=rc2 ){
/* If a run-time error occurs, report the error and stop reading
** the SQL
*/
@@ -1854,9 +1894,6 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
if( rc!=SQLITE_OK ){
rc = TCL_ERROR;
Tcl_SetResult(interp, (char *)sqlite3_errmsg(pDb->db), TCL_VOLATILE);
}else{
/* Must flush any cached statements */
flushStmtCache( pDb );
}
break;
}