Upgrade bundled library to 2.8.14 + misc fixes

(http://www.sqlite.org/cvstrac/chngview?cn=1742)
This commit is contained in:
Wez Furlong 2004-07-10 12:27:51 +00:00
parent cd732f1a3f
commit e563b4eafa
43 changed files with 5953 additions and 5559 deletions

View file

@ -33,8 +33,9 @@ typedef struct {
** Fill the InitData structure with an error message that indicates
** that the database is corrupt.
*/
static void corruptSchema(InitData *pData){
sqliteSetString(pData->pzErrMsg, "malformed database schema", (char*)0);
static void corruptSchema(InitData *pData, const char *zExtra){
sqliteSetString(pData->pzErrMsg, "malformed database schema",
zExtra!=0 && zExtra[0]!=0 ? " - " : (char*)0, zExtra, (char*)0);
}
/*
@ -54,36 +55,39 @@ static void corruptSchema(InitData *pData){
static
int sqliteInitCallback(void *pInit, int argc, char **argv, char **azColName){
InitData *pData = (InitData*)pInit;
Parse sParse;
int nErr = 0;
assert( argc==5 );
if( argv==0 ) return 0; /* Might happen if EMPTY_RESULT_CALLBACKS are on */
if( argv[0]==0 ){
corruptSchema(pData);
corruptSchema(pData, 0);
return 1;
}
switch( argv[0][0] ){
case 'v':
case 'i':
case 't': { /* CREATE TABLE, CREATE INDEX, or CREATE VIEW statements */
sqlite *db = pData->db;
if( argv[2]==0 || argv[4]==0 ){
corruptSchema(pData);
corruptSchema(pData, 0);
return 1;
}
if( argv[3] && argv[3][0] ){
/* Call the parser to process a CREATE TABLE, INDEX or VIEW.
** But because sParse.initFlag is set to 1, no VDBE code is generated
** But because db->init.busy is set to 1, no VDBE code is generated
** or executed. All the parser does is build the internal data
** structures that describe the table, index, or view.
*/
memset(&sParse, 0, sizeof(sParse));
sParse.db = pData->db;
sParse.initFlag = 1;
sParse.iDb = atoi(argv[4]);
sParse.newTnum = atoi(argv[2]);
sParse.useCallback = 1;
sqliteRunParser(&sParse, argv[3], pData->pzErrMsg);
char *zErr;
assert( db->init.busy );
db->init.iDb = atoi(argv[4]);
assert( db->init.iDb>=0 && db->init.iDb<db->nDb );
db->init.newTnum = atoi(argv[2]);
if( sqlite_exec(db, argv[3], 0, 0, &zErr) ){
corruptSchema(pData, zErr);
sqlite_freemem(zErr);
}
db->init.iDb = 0;
}else{
/* If the SQL column is blank it means this is an index that
** was created to be the PRIMARY KEY or to fulfill a UNIQUE
@ -95,8 +99,8 @@ int sqliteInitCallback(void *pInit, int argc, char **argv, char **azColName){
Index *pIndex;
iDb = atoi(argv[4]);
assert( iDb>=0 && iDb<pData->db->nDb );
pIndex = sqliteFindIndex(pData->db, argv[1], pData->db->aDb[iDb].zName);
assert( iDb>=0 && iDb<db->nDb );
pIndex = sqliteFindIndex(db, argv[1], db->aDb[iDb].zName);
if( pIndex==0 || pIndex->tnum!=0 ){
/* This can occur if there exists an index on a TEMP table which
** has the same name as another index on a permanent index. Since
@ -127,6 +131,9 @@ int sqliteInitCallback(void *pInit, int argc, char **argv, char **azColName){
** format version 1 or 2 to version 3. The correct operation of
** this routine relys on the fact that no indices are used when
** copying a table out to a temporary file.
**
** The change from version 2 to version 3 occurred between SQLite
** version 2.5.6 and 2.6.0 on 2002-July-18.
*/
static
int upgrade_3_callback(void *pInit, int argc, char **argv, char **NotUsed){
@ -150,8 +157,8 @@ int upgrade_3_callback(void *pInit, int argc, char **argv, char **NotUsed){
"DROP TABLE sqlite_x;",
0, 0, &zErr, argv[0], argv[0], argv[0]);
if( zErr ){
sqliteSetString(pData->pzErrMsg, zErr, (char*)0);
sqlite_freemem(zErr);
if( *pData->pzErrMsg ) sqlite_freemem(*pData->pzErrMsg);
*pData->pzErrMsg = zErr;
}
/* If an error occurred in the SQL above, then the transaction will
@ -185,7 +192,6 @@ static int sqliteInitOne(sqlite *db, int iDb, char **pzErrMsg){
char *azArg[6];
char zDbNum[30];
int meta[SQLITE_N_BTREE_META];
Parse sParse;
InitData initData;
/*
@ -242,6 +248,7 @@ static int sqliteInitOne(sqlite *db, int iDb, char **pzErrMsg){
/* Construct the schema tables: sqlite_master and sqlite_temp_master
*/
sqliteSafetyOff(db);
azArg[0] = "table";
azArg[1] = MASTER_NAME;
azArg[2] = "2";
@ -266,6 +273,7 @@ static int sqliteInitOne(sqlite *db, int iDb, char **pzErrMsg){
pTab->readOnly = 1;
}
}
sqliteSafetyOn(db);
/* Create a cursor to hold the database open
*/
@ -292,6 +300,9 @@ static int sqliteInitOne(sqlite *db, int iDb, char **pzErrMsg){
if( size==0 ){ size = MAX_PAGES; }
db->cache_size = size;
db->safety_level = meta[4];
if( meta[6]>0 && meta[6]<=2 && db->temp_store==0 ){
db->temp_store = meta[6];
}
if( db->safety_level==0 ) db->safety_level = 2;
/*
@ -327,31 +338,28 @@ static int sqliteInitOne(sqlite *db, int iDb, char **pzErrMsg){
/* Read the schema information out of the schema tables
*/
memset(&sParse, 0, sizeof(sParse));
sParse.db = db;
sParse.xCallback = sqliteInitCallback;
sParse.pArg = (void*)&initData;
sParse.initFlag = 1;
sParse.useCallback = 1;
assert( db->init.busy );
sqliteSafetyOff(db);
if( iDb==0 ){
sqliteRunParser(&sParse,
rc = sqlite_exec(db,
db->file_format>=2 ? init_script : older_init_script,
pzErrMsg);
sqliteInitCallback, &initData, 0);
}else{
char *zSql = 0;
sqliteSetString(&zSql,
"SELECT type, name, rootpage, sql, ", zDbNum, " FROM \"",
db->aDb[iDb].zName, "\".sqlite_master", (char*)0);
sqliteRunParser(&sParse, zSql, pzErrMsg);
rc = sqlite_exec(db, zSql, sqliteInitCallback, &initData, 0);
sqliteFree(zSql);
}
sqliteSafetyOn(db);
sqliteBtreeCloseCursor(curMain);
if( sqlite_malloc_failed ){
sqliteSetString(pzErrMsg, "out of memory", (char*)0);
sParse.rc = SQLITE_NOMEM;
rc = SQLITE_NOMEM;
sqliteResetInternalSchema(db, 0);
}
if( sParse.rc==SQLITE_OK ){
if( rc==SQLITE_OK ){
DbSetProperty(db, iDb, DB_SchemaLoaded);
if( iDb==0 ){
DbSetProperty(db, 1, DB_SchemaLoaded);
@ -359,7 +367,7 @@ static int sqliteInitOne(sqlite *db, int iDb, char **pzErrMsg){
}else{
sqliteResetInternalSchema(db, iDb);
}
return sParse.rc;
return rc;
}
/*
@ -378,17 +386,58 @@ static int sqliteInitOne(sqlite *db, int iDb, char **pzErrMsg){
int sqliteInit(sqlite *db, char **pzErrMsg){
int i, rc;
if( db->init.busy ) return SQLITE_OK;
assert( (db->flags & SQLITE_Initialized)==0 );
rc = SQLITE_OK;
db->init.busy = 1;
for(i=0; rc==SQLITE_OK && i<db->nDb; i++){
if( DbHasProperty(db, i, DB_SchemaLoaded) ) continue;
assert( i!=1 ); /* Should have been initialized together with 0 */
rc = sqliteInitOne(db, i, pzErrMsg);
if( rc ){
sqliteResetInternalSchema(db, i);
}
}
db->init.busy = 0;
if( rc==SQLITE_OK ){
db->flags |= SQLITE_Initialized;
sqliteCommitInternalChanges(db);
}else{
}
/* If the database is in formats 1 or 2, then upgrade it to
** version 3. This will reconstruct all indices. If the
** upgrade fails for any reason (ex: out of disk space, database
** is read only, interrupt received, etc.) then fail the init.
*/
if( rc==SQLITE_OK && db->file_format<3 ){
char *zErr = 0;
InitData initData;
int meta[SQLITE_N_BTREE_META];
db->magic = SQLITE_MAGIC_OPEN;
initData.db = db;
initData.pzErrMsg = &zErr;
db->file_format = 3;
rc = sqlite_exec(db,
"BEGIN; SELECT name FROM sqlite_master WHERE type='table';",
upgrade_3_callback,
&initData,
&zErr);
if( rc==SQLITE_OK ){
sqliteBtreeGetMeta(db->aDb[0].pBt, meta);
meta[2] = 4;
sqliteBtreeUpdateMeta(db->aDb[0].pBt, meta);
sqlite_exec(db, "COMMIT", 0, 0, 0);
}
if( rc!=SQLITE_OK ){
sqliteSetString(pzErrMsg,
"unable to upgrade database to the version 2.6 format",
zErr ? ": " : 0, zErr, (char*)0);
}
sqlite_freemem(zErr);
}
if( rc!=SQLITE_OK ){
db->flags &= ~SQLITE_Initialized;
}
return rc;
@ -432,6 +481,7 @@ sqlite *sqlite_open(const char *zFilename, int mode, char **pzErrMsg){
db->magic = SQLITE_MAGIC_BUSY;
db->nDb = 2;
db->aDb = db->aDbStatic;
/* db->flags |= SQLITE_ShortColNames; */
sqliteHashInit(&db->aFunc, SQLITE_HASH_STRING, 1);
for(i=0; i<db->nDb; i++){
sqliteHashInit(&db->aDb[i].tblHash, SQLITE_HASH_STRING, 0);
@ -475,42 +525,6 @@ sqlite *sqlite_open(const char *zFilename, int mode, char **pzErrMsg){
*pzErrMsg = 0;
}
/* If the database is in formats 1 or 2, then upgrade it to
** version 3. This will reconstruct all indices. If the
** upgrade fails for any reason (ex: out of disk space, database
** is read only, interrupt received, etc.) then refuse to open.
*/
if( rc==SQLITE_OK && db->file_format<3 ){
char *zErr = 0;
InitData initData;
int meta[SQLITE_N_BTREE_META];
initData.db = db;
initData.pzErrMsg = &zErr;
db->file_format = 3;
rc = sqlite_exec(db,
"BEGIN; SELECT name FROM sqlite_master WHERE type='table';",
upgrade_3_callback,
&initData,
&zErr);
if( rc==SQLITE_OK ){
sqliteBtreeGetMeta(db->aDb[0].pBt, meta);
meta[2] = 4;
sqliteBtreeUpdateMeta(db->aDb[0].pBt, meta);
sqlite_exec(db, "COMMIT", 0, 0, 0);
}
if( rc!=SQLITE_OK ){
sqliteSetString(pzErrMsg,
"unable to upgrade database to the version 2.6 format",
zErr ? ": " : 0, zErr, (char*)0);
sqlite_freemem(zErr);
sqliteStrRealloc(pzErrMsg);
sqlite_close(db);
return 0;
}
sqlite_freemem(zErr);
}
/* Return a pointer to the newly opened database structure */
return db;
@ -534,6 +548,16 @@ int sqlite_changes(sqlite *db){
return db->nChange;
}
/*
** Return the number of changes produced by the last INSERT, UPDATE, or
** DELETE statement to complete execution. The count does not include
** changes due to SQL statements executed in trigger programs that were
** triggered by that statement
*/
int sqlite_last_statement_changes(sqlite *db){
return db->lsChange;
}
/*
** Close an existing SQLite database
*/
@ -547,13 +571,10 @@ void sqlite_close(sqlite *db){
}
db->magic = SQLITE_MAGIC_CLOSED;
for(j=0; j<db->nDb; j++){
if( db->aDb[j].pBt ){
sqliteBtreeClose(db->aDb[j].pBt);
db->aDb[j].pBt = 0;
}
if( j>=2 ){
sqliteFree(db->aDb[j].zName);
db->aDb[j].zName = 0;
struct Db *pDb = &db->aDb[j];
if( pDb->pBt ){
sqliteBtreeClose(pDb->pBt);
pDb->pBt = 0;
}
}
sqliteResetInternalSchema(db, 0);
@ -581,84 +602,8 @@ void sqliteRollbackAll(sqlite *db){
db->aDb[i].inTrans = 0;
}
}
sqliteRollbackInternalChanges(db);
}
/*
** This routine does the work of either sqlite_exec() or sqlite_compile().
** It works like sqlite_exec() if pVm==NULL and it works like sqlite_compile()
** otherwise.
*/
static int sqliteMain(
sqlite *db, /* The database on which the SQL executes */
const char *zSql, /* The SQL to be executed */
sqlite_callback xCallback, /* Invoke this callback routine */
void *pArg, /* First argument to xCallback() */
const char **pzTail, /* OUT: Next statement after the first */
sqlite_vm **ppVm, /* OUT: The virtual machine */
char **pzErrMsg /* OUT: Write error messages here */
){
Parse sParse;
if( pzErrMsg ) *pzErrMsg = 0;
if( sqliteSafetyOn(db) ) goto exec_misuse;
if( (db->flags & SQLITE_Initialized)==0 ){
int rc, cnt = 1;
while( (rc = sqliteInit(db, pzErrMsg))==SQLITE_BUSY
&& db->xBusyCallback && db->xBusyCallback(db->pBusyArg, "", cnt++)!=0 ){}
if( rc!=SQLITE_OK ){
sqliteStrRealloc(pzErrMsg);
sqliteSafetyOff(db);
return rc;
}
if( pzErrMsg ){
sqliteFree(*pzErrMsg);
*pzErrMsg = 0;
}
}
if( db->file_format<3 ){
sqliteSafetyOff(db);
sqliteSetString(pzErrMsg, "obsolete database file format", (char*)0);
return SQLITE_ERROR;
}
if( db->pVdbe==0 ){ db->nChange = 0; }
memset(&sParse, 0, sizeof(sParse));
sParse.db = db;
sParse.xCallback = xCallback;
sParse.pArg = pArg;
sParse.useCallback = ppVm==0;
if( db->xTrace ) db->xTrace(db->pTraceArg, zSql);
sqliteRunParser(&sParse, zSql, pzErrMsg);
if( sqlite_malloc_failed ){
sqliteSetString(pzErrMsg, "out of memory", (char*)0);
sParse.rc = SQLITE_NOMEM;
sqliteRollbackAll(db);
sqliteResetInternalSchema(db, 0);
db->flags &= ~SQLITE_InTrans;
}
if( sParse.rc==SQLITE_DONE ) sParse.rc = SQLITE_OK;
if( sParse.rc!=SQLITE_OK && pzErrMsg && *pzErrMsg==0 ){
sqliteSetString(pzErrMsg, sqlite_error_string(sParse.rc), (char*)0);
}
sqliteStrRealloc(pzErrMsg);
if( sParse.rc==SQLITE_SCHEMA ){
sqliteResetInternalSchema(db, 0);
}
if( sParse.useCallback==0 ){
assert( ppVm );
*ppVm = (sqlite_vm*)sParse.pVdbe;
if( pzTail ) *pzTail = sParse.zTail;
}
if( sqliteSafetyOff(db) ) goto exec_misuse;
return sParse.rc;
exec_misuse:
if( pzErrMsg ){
*pzErrMsg = 0;
sqliteSetString(pzErrMsg, sqlite_error_string(SQLITE_MISUSE), (char*)0);
sqliteStrRealloc(pzErrMsg);
}
return SQLITE_MISUSE;
sqliteResetInternalSchema(db, 0);
/* sqliteRollbackInternalChanges(db); */
}
/*
@ -678,9 +623,62 @@ int sqlite_exec(
void *pArg, /* First argument to xCallback() */
char **pzErrMsg /* Write error messages here */
){
return sqliteMain(db, zSql, xCallback, pArg, 0, 0, pzErrMsg);
int rc = SQLITE_OK;
const char *zLeftover;
sqlite_vm *pVm;
int nRetry = 0;
int nChange = 0;
int nCallback;
if( zSql==0 ) return SQLITE_OK;
while( rc==SQLITE_OK && zSql[0] ){
pVm = 0;
rc = sqlite_compile(db, zSql, &zLeftover, &pVm, pzErrMsg);
if( rc!=SQLITE_OK ){
assert( pVm==0 || sqlite_malloc_failed );
return rc;
}
if( pVm==0 ){
/* This happens if the zSql input contained only whitespace */
break;
}
db->nChange += nChange;
nCallback = 0;
while(1){
int nArg;
char **azArg, **azCol;
rc = sqlite_step(pVm, &nArg, (const char***)&azArg,(const char***)&azCol);
if( rc==SQLITE_ROW ){
if( xCallback!=0 && xCallback(pArg, nArg, azArg, azCol) ){
sqlite_finalize(pVm, 0);
return SQLITE_ABORT;
}
nCallback++;
}else{
if( rc==SQLITE_DONE && nCallback==0
&& (db->flags & SQLITE_NullCallback)!=0 && xCallback!=0 ){
xCallback(pArg, nArg, azArg, azCol);
}
rc = sqlite_finalize(pVm, pzErrMsg);
if( rc==SQLITE_SCHEMA && nRetry<2 ){
nRetry++;
rc = SQLITE_OK;
break;
}
if( db->pVdbe==0 ){
nChange = db->nChange;
}
nRetry = 0;
zSql = zLeftover;
while( isspace(zSql[0]) ) zSql++;
break;
}
}
}
return rc;
}
/*
** Compile a single statement of SQL into a virtual machine. Return one
** of the SQLITE_ success/failure codes. Also write an error message into
@ -693,7 +691,88 @@ int sqlite_compile(
sqlite_vm **ppVm, /* OUT: The virtual machine */
char **pzErrMsg /* OUT: Write error messages here */
){
return sqliteMain(db, zSql, 0, 0, pzTail, ppVm, pzErrMsg);
Parse sParse;
if( pzErrMsg ) *pzErrMsg = 0;
if( sqliteSafetyOn(db) ) goto exec_misuse;
if( !db->init.busy ){
if( (db->flags & SQLITE_Initialized)==0 ){
int rc, cnt = 1;
while( (rc = sqliteInit(db, pzErrMsg))==SQLITE_BUSY
&& db->xBusyCallback
&& db->xBusyCallback(db->pBusyArg, "", cnt++)!=0 ){}
if( rc!=SQLITE_OK ){
sqliteStrRealloc(pzErrMsg);
sqliteSafetyOff(db);
return rc;
}
if( pzErrMsg ){
sqliteFree(*pzErrMsg);
*pzErrMsg = 0;
}
}
if( db->file_format<3 ){
sqliteSafetyOff(db);
sqliteSetString(pzErrMsg, "obsolete database file format", (char*)0);
return SQLITE_ERROR;
}
}
assert( (db->flags & SQLITE_Initialized)!=0 || db->init.busy );
if( db->pVdbe==0 ){ db->nChange = 0; }
memset(&sParse, 0, sizeof(sParse));
sParse.db = db;
sqliteRunParser(&sParse, zSql, pzErrMsg);
if( db->xTrace && !db->init.busy ){
/* Trace only the statment that was compiled.
** Make a copy of that part of the SQL string since zSQL is const
** and we must pass a zero terminated string to the trace function
** The copy is unnecessary if the tail pointer is pointing at the
** beginnig or end of the SQL string.
*/
if( sParse.zTail && sParse.zTail!=zSql && *sParse.zTail ){
char *tmpSql = sqliteStrNDup(zSql, sParse.zTail - zSql);
if( tmpSql ){
db->xTrace(db->pTraceArg, tmpSql);
free(tmpSql);
}else{
/* If a memory error occurred during the copy,
** trace entire SQL string and fall through to the
** sqlite_malloc_failed test to report the error.
*/
db->xTrace(db->pTraceArg, zSql);
}
}else{
db->xTrace(db->pTraceArg, zSql);
}
}
if( sqlite_malloc_failed ){
sqliteSetString(pzErrMsg, "out of memory", (char*)0);
sParse.rc = SQLITE_NOMEM;
sqliteRollbackAll(db);
sqliteResetInternalSchema(db, 0);
db->flags &= ~SQLITE_InTrans;
}
if( sParse.rc==SQLITE_DONE ) sParse.rc = SQLITE_OK;
if( sParse.rc!=SQLITE_OK && pzErrMsg && *pzErrMsg==0 ){
sqliteSetString(pzErrMsg, sqlite_error_string(sParse.rc), (char*)0);
}
sqliteStrRealloc(pzErrMsg);
if( sParse.rc==SQLITE_SCHEMA ){
sqliteResetInternalSchema(db, 0);
}
assert( ppVm );
*ppVm = (sqlite_vm*)sParse.pVdbe;
if( pzTail ) *pzTail = sParse.zTail;
if( sqliteSafetyOff(db) ) goto exec_misuse;
return sParse.rc;
exec_misuse:
if( pzErrMsg ){
*pzErrMsg = 0;
sqliteSetString(pzErrMsg, sqlite_error_string(SQLITE_MISUSE), (char*)0);
sqliteStrRealloc(pzErrMsg);
}
return SQLITE_MISUSE;
}
@ -729,7 +808,7 @@ int sqlite_reset(
char **pzErrMsg /* OUT: Write error messages here */
){
int rc = sqliteVdbeReset((Vdbe*)pVm, pzErrMsg);
sqliteVdbeMakeReady((Vdbe*)pVm, -1, 0, 0, 0);
sqliteVdbeMakeReady((Vdbe*)pVm, -1, 0);
sqliteStrRealloc(pzErrMsg);
return rc;
}
@ -767,6 +846,7 @@ const char *sqlite_error_string(int rc){
case SQLITE_AUTH: z = "authorization denied"; break;
case SQLITE_FORMAT: z = "auxiliary database format error"; break;
case SQLITE_RANGE: z = "bind index out of range"; break;
case SQLITE_NOTADB: z = "file is encrypted or is not a database";break;
default: z = "unknown error"; break;
}
return z;
@ -784,22 +864,23 @@ static int sqliteDefaultBusyCallback(
int count /* Number of times table has been busy */
){
#if SQLITE_MIN_SLEEP_MS==1
int delay = 10;
int prior_delay = 0;
static const char delays[] =
{ 1, 2, 5, 10, 15, 20, 25, 25, 25, 50, 50, 50, 100};
static const short int totals[] =
{ 0, 1, 3, 8, 18, 33, 53, 78, 103, 128, 178, 228, 287};
# define NDELAY (sizeof(delays)/sizeof(delays[0]))
int timeout = (int)(long)Timeout;
int i;
int delay, prior;
for(i=1; i<count; i++){
prior_delay += delay;
delay = delay*2;
if( delay>=1000 ){
delay = 1000;
prior_delay += 1000*(count - i - 1);
break;
}
if( count <= NDELAY ){
delay = delays[count-1];
prior = totals[count-1];
}else{
delay = delays[NDELAY-1];
prior = totals[NDELAY-1] + delay*(count-NDELAY-1);
}
if( prior_delay + delay > timeout ){
delay = timeout - prior_delay;
if( prior + delay > timeout ){
delay = timeout - prior;
if( delay<=0 ) return 0;
}
sqliteOsSleep(delay);
@ -856,9 +937,9 @@ void sqlite_progress_handler(
** This routine installs a default busy handler that waits for the
** specified number of milliseconds before returning 0.
*/
void sqlite_busy_timeout(sqlite *db, long ms){
void sqlite_busy_timeout(sqlite *db, int ms){
if( ms>0 ){
sqlite_busy_handler(db, sqliteDefaultBusyCallback, (void*)ms);
sqlite_busy_handler(db, sqliteDefaultBusyCallback, (void*)(long)ms);
}else{
sqlite_busy_handler(db, 0, 0);
}
@ -903,7 +984,7 @@ const char *sqlite_libencoding(void){ return sqlite_encoding; }
** sqlite_create_aggregate(), and vice versa.
**
** If nArg is -1 it means that this function will accept any number
** of arguments, including 0.
** of arguments, including 0. The maximum allowed value of nArg is 127.
*/
int sqlite_create_function(
sqlite *db, /* Add the function to this database connection */
@ -915,6 +996,7 @@ int sqlite_create_function(
FuncDef *p;
int nName;
if( db==0 || zName==0 || sqliteSafetyCheck(db) ) return 1;
if( nArg<-1 || nArg>127 ) return 1;
nName = strlen(zName);
if( nName>255 ) return 1;
p = sqliteFindFunction(db, zName, nName, nArg, 1);
@ -936,6 +1018,7 @@ int sqlite_create_aggregate(
FuncDef *p;
int nName;
if( db==0 || zName==0 || sqliteSafetyCheck(db) ) return 1;
if( nArg<-1 || nArg>127 ) return 1;
nName = strlen(zName);
if( nName>255 ) return 1;
p = sqliteFindFunction(db, zName, nName, nArg, 1);
@ -976,6 +1059,24 @@ void *sqlite_trace(sqlite *db, void (*xTrace)(void*,const char*), void *pArg){
return pOld;
}
/*** EXPERIMENTAL ***
**
** Register a function to be invoked when a transaction comments.
** If either function returns non-zero, then the commit becomes a
** rollback.
*/
void *sqlite_commit_hook(
sqlite *db, /* Attach the hook to this database */
int (*xCallback)(void*), /* Function to invoke on each commit */
void *pArg /* Argument to the function */
){
void *pOld = db->pCommitArg;
db->xCommitCallback = xCallback;
db->pCommitArg = pArg;
return pOld;
}
/*
** This routine is called to create a connection to a database BTree
** driver. If zFilename is the name of a file, then that file is