diff options
author | drh <drh@noemail.net> | 2017-10-21 17:17:17 +0000 |
---|---|---|
committer | drh <drh@noemail.net> | 2017-10-21 17:17:17 +0000 |
commit | aa79e434f1cf89a3d9e47c92ebb26661b5ffae82 (patch) | |
tree | d42b470c4aad5ea7387d3a44bff7d51df7b86b8e /src | |
parent | 82cae9ffd62c1375adef56d9e0d43c099691c797 (diff) | |
parent | 6fe3733ba97193227a395c7796bb9ca0d6f82134 (diff) | |
download | sqlite-aa79e434f1cf89a3d9e47c92ebb26661b5ffae82.tar.gz sqlite-aa79e434f1cf89a3d9e47c92ebb26661b5ffae82.zip |
Merge all the enhancements and bug fixes from trunk, since none are
destablizing. Call this the second beta.
FossilOrigin-Name: fb3ee1b7cac09e4950e4f48b44c277e4f391cb6c8f069644732d2389ca653da4
Diffstat (limited to 'src')
-rw-r--r-- | src/btree.c | 13 | ||||
-rw-r--r-- | src/build.c | 2 | ||||
-rw-r--r-- | src/dbpage.c | 329 | ||||
-rw-r--r-- | src/main.c | 6 | ||||
-rw-r--r-- | src/resolve.c | 4 | ||||
-rw-r--r-- | src/select.c | 3 | ||||
-rw-r--r-- | src/shell.c.in | 20 | ||||
-rw-r--r-- | src/sqlite3ext.h | 4 | ||||
-rw-r--r-- | src/sqliteInt.h | 3 | ||||
-rw-r--r-- | src/tclsqlite.c | 842 | ||||
-rw-r--r-- | src/test_md5.c | 450 | ||||
-rw-r--r-- | src/test_tclsh.c | 198 | ||||
-rw-r--r-- | src/where.c | 21 |
13 files changed, 1122 insertions, 773 deletions
diff --git a/src/btree.c b/src/btree.c index 89222197b..ddcb6cfd3 100644 --- a/src/btree.c +++ b/src/btree.c @@ -4824,7 +4824,7 @@ static const void *fetchPayload( BtCursor *pCur, /* Cursor pointing to entry to read from */ u32 *pAmt /* Write the number of available bytes here */ ){ - u32 amt; + int amt; assert( pCur!=0 && pCur->iPage>=0 && pCur->pPage); assert( pCur->eState==CURSOR_VALID ); assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) ); @@ -4833,9 +4833,14 @@ static const void *fetchPayload( assert( pCur->info.nSize>0 ); assert( pCur->info.pPayload>pCur->pPage->aData || CORRUPT_DB ); assert( pCur->info.pPayload<pCur->pPage->aDataEnd ||CORRUPT_DB); - amt = (int)(pCur->pPage->aDataEnd - pCur->info.pPayload); - if( pCur->info.nLocal<amt ) amt = pCur->info.nLocal; - *pAmt = amt; + amt = pCur->info.nLocal; + if( amt>(int)(pCur->pPage->aDataEnd - pCur->info.pPayload) ){ + /* There is too little space on the page for the expected amount + ** of local content. Database must be corrupt. */ + assert( CORRUPT_DB ); + amt = MAX(0, (int)(pCur->pPage->aDataEnd - pCur->info.pPayload)); + } + *pAmt = (u32)amt; return (void*)pCur->info.pPayload; } diff --git a/src/build.c b/src/build.c index cb8c374d1..a9428104a 100644 --- a/src/build.c +++ b/src/build.c @@ -1063,12 +1063,10 @@ void sqlite3AddColumn(Parse *pParse, Token *pName, Token *pType){ Column *pCol; sqlite3 *db = pParse->db; if( (p = pParse->pNewTable)==0 ) return; -#if SQLITE_MAX_COLUMN if( p->nCol+1>db->aLimit[SQLITE_LIMIT_COLUMN] ){ sqlite3ErrorMsg(pParse, "too many columns on %s", p->zName); return; } -#endif z = sqlite3DbMallocRaw(db, pName->n + pType->n + 2); if( z==0 ) return; memcpy(z, pName->z, pName->n); diff --git a/src/dbpage.c b/src/dbpage.c new file mode 100644 index 000000000..d21c5b6df --- /dev/null +++ b/src/dbpage.c @@ -0,0 +1,329 @@ +/* +** 2017-10-11 +** +** 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. +** +****************************************************************************** +** +** This file contains an implementation of the "sqlite_dbpage" virtual table. +** +** The sqlite_dbpage virtual table is used to read or write whole raw +** pages of the database file. The pager interface is used so that +** uncommitted changes and changes recorded in the WAL file are correctly +** retrieved. +** +** Usage example: +** +** SELECT data FROM sqlite_dbpage('aux1') WHERE pgno=123; +** +** This is an eponymous virtual table so it does not need to be created before +** use. The optional argument to the sqlite_dbpage() table name is the +** schema for the database file that is to be read. The default schema is +** "main". +** +** The data field of sqlite_dbpage table can be updated. The new +** value must be a BLOB which is the correct page size, otherwise the +** update fails. Rows may not be deleted or inserted. +*/ + +#include "sqliteInt.h" /* Requires access to internal data structures */ +#if (defined(SQLITE_ENABLE_DBPAGE_VTAB) || defined(SQLITE_TEST)) \ + && !defined(SQLITE_OMIT_VIRTUALTABLE) + +typedef struct DbpageTable DbpageTable; +typedef struct DbpageCursor DbpageCursor; + +struct DbpageCursor { + sqlite3_vtab_cursor base; /* Base class. Must be first */ + int pgno; /* Current page number */ + int mxPgno; /* Last page to visit on this scan */ +}; + +struct DbpageTable { + sqlite3_vtab base; /* Base class. Must be first */ + sqlite3 *db; /* The database */ + Pager *pPager; /* Pager being read/written */ + int iDb; /* Index of database to analyze */ + int szPage; /* Size of each page in bytes */ + int nPage; /* Number of pages in the file */ +}; + +/* +** Connect to or create a dbpagevfs virtual table. +*/ +static int dbpageConnect( + sqlite3 *db, + void *pAux, + int argc, const char *const*argv, + sqlite3_vtab **ppVtab, + char **pzErr +){ + DbpageTable *pTab = 0; + int rc = SQLITE_OK; + int iDb; + + if( argc>=4 ){ + Token nm; + sqlite3TokenInit(&nm, (char*)argv[3]); + iDb = sqlite3FindDb(db, &nm); + if( iDb<0 ){ + *pzErr = sqlite3_mprintf("no such schema: %s", argv[3]); + return SQLITE_ERROR; + } + }else{ + iDb = 0; + } + rc = sqlite3_declare_vtab(db, + "CREATE TABLE x(pgno INTEGER PRIMARY KEY, data BLOB, schema HIDDEN)"); + if( rc==SQLITE_OK ){ + pTab = (DbpageTable *)sqlite3_malloc64(sizeof(DbpageTable)); + if( pTab==0 ) rc = SQLITE_NOMEM_BKPT; + } + + assert( rc==SQLITE_OK || pTab==0 ); + if( rc==SQLITE_OK ){ + Btree *pBt = db->aDb[iDb].pBt; + memset(pTab, 0, sizeof(DbpageTable)); + pTab->db = db; + pTab->iDb = iDb; + pTab->pPager = pBt ? sqlite3BtreePager(pBt) : 0; + } + + *ppVtab = (sqlite3_vtab*)pTab; + return rc; +} + +/* +** Disconnect from or destroy a dbpagevfs virtual table. +*/ +static int dbpageDisconnect(sqlite3_vtab *pVtab){ + sqlite3_free(pVtab); + return SQLITE_OK; +} + +/* +** idxNum: +** +** 0 full table scan +** 1 pgno=?1 +*/ +static int dbpageBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ + int i; + pIdxInfo->estimatedCost = 1.0e6; /* Initial cost estimate */ + for(i=0; i<pIdxInfo->nConstraint; i++){ + struct sqlite3_index_constraint *p = &pIdxInfo->aConstraint[i]; + if( p->usable && p->iColumn<=0 && p->op==SQLITE_INDEX_CONSTRAINT_EQ ){ + pIdxInfo->estimatedRows = 1; + pIdxInfo->idxFlags = SQLITE_INDEX_SCAN_UNIQUE; + pIdxInfo->estimatedCost = 1.0; + pIdxInfo->idxNum = 1; + pIdxInfo->aConstraintUsage[i].argvIndex = 1; + pIdxInfo->aConstraintUsage[i].omit = 1; + break; + } + } + if( pIdxInfo->nOrderBy>=1 + && pIdxInfo->aOrderBy[0].iColumn<=0 + && pIdxInfo->aOrderBy[0].desc==0 + ){ + pIdxInfo->orderByConsumed = 1; + } + return SQLITE_OK; +} + +/* +** Open a new dbpagevfs cursor. +*/ +static int dbpageOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){ + DbpageCursor *pCsr; + + pCsr = (DbpageCursor *)sqlite3_malloc64(sizeof(DbpageCursor)); + if( pCsr==0 ){ + return SQLITE_NOMEM_BKPT; + }else{ + memset(pCsr, 0, sizeof(DbpageCursor)); + pCsr->base.pVtab = pVTab; + pCsr->pgno = -1; + } + + *ppCursor = (sqlite3_vtab_cursor *)pCsr; + return SQLITE_OK; +} + +/* +** Close a dbpagevfs cursor. +*/ +static int dbpageClose(sqlite3_vtab_cursor *pCursor){ + DbpageCursor *pCsr = (DbpageCursor *)pCursor; + sqlite3_free(pCsr); + return SQLITE_OK; +} + +/* +** Move a dbpagevfs cursor to the next entry in the file. +*/ +static int dbpageNext(sqlite3_vtab_cursor *pCursor){ + int rc = SQLITE_OK; + DbpageCursor *pCsr = (DbpageCursor *)pCursor; + pCsr->pgno++; + return rc; +} + +static int dbpageEof(sqlite3_vtab_cursor *pCursor){ + DbpageCursor *pCsr = (DbpageCursor *)pCursor; + return pCsr->pgno > pCsr->mxPgno; +} + +static int dbpageFilter( + sqlite3_vtab_cursor *pCursor, + int idxNum, const char *idxStr, + int argc, sqlite3_value **argv +){ + DbpageCursor *pCsr = (DbpageCursor *)pCursor; + DbpageTable *pTab = (DbpageTable *)pCursor->pVtab; + int rc = SQLITE_OK; + Btree *pBt = pTab->db->aDb[pTab->iDb].pBt; + + pTab->szPage = sqlite3BtreeGetPageSize(pBt); + pTab->nPage = sqlite3BtreeLastPage(pBt); + if( idxNum==1 ){ + pCsr->pgno = sqlite3_value_int(argv[0]); + if( pCsr->pgno<1 || pCsr->pgno>pTab->nPage ){ + pCsr->pgno = 1; + pCsr->mxPgno = 0; + }else{ + pCsr->mxPgno = pCsr->pgno; + } + }else{ + pCsr->pgno = 1; + pCsr->mxPgno = pTab->nPage; + } + return rc; +} + +static int dbpageColumn( + sqlite3_vtab_cursor *pCursor, + sqlite3_context *ctx, + int i +){ + DbpageCursor *pCsr = (DbpageCursor *)pCursor; + DbpageTable *pTab = (DbpageTable *)pCursor->pVtab; + int rc = SQLITE_OK; + switch( i ){ + case 0: { /* pgno */ + sqlite3_result_int(ctx, pCsr->pgno); + break; + } + case 1: { /* data */ + DbPage *pDbPage = 0; + rc = sqlite3PagerGet(pTab->pPager, pCsr->pgno, (DbPage**)&pDbPage, 0); + if( rc==SQLITE_OK ){ + sqlite3_result_blob(ctx, sqlite3PagerGetData(pDbPage), pTab->szPage, + SQLITE_TRANSIENT); + } + sqlite3PagerUnref(pDbPage); + break; + } + default: { /* schema */ + sqlite3 *db = sqlite3_context_db_handle(ctx); + sqlite3_result_text(ctx, db->aDb[pTab->iDb].zDbSName, -1, SQLITE_STATIC); + break; + } + } + return SQLITE_OK; +} + +static int dbpageRowid(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){ + DbpageCursor *pCsr = (DbpageCursor *)pCursor; + *pRowid = pCsr->pgno; + return SQLITE_OK; +} + +static int dbpageUpdate( + sqlite3_vtab *pVtab, + int argc, + sqlite3_value **argv, + sqlite_int64 *pRowid +){ + DbpageTable *pTab = (DbpageTable *)pVtab; + int pgno; + DbPage *pDbPage = 0; + int rc = SQLITE_OK; + char *zErr = 0; + + if( argc==1 ){ + zErr = "cannot delete"; + goto update_fail; + } + pgno = sqlite3_value_int(argv[0]); + if( pgno<1 || pgno>pTab->nPage ){ + zErr = "bad page number"; + goto update_fail; + } + if( sqlite3_value_int(argv[1])!=pgno ){ + zErr = "cannot insert"; + goto update_fail; + } + if( sqlite3_value_type(argv[3])!=SQLITE_BLOB + || sqlite3_value_bytes(argv[3])!=pTab->szPage + ){ + zErr = "bad page value"; + goto update_fail; + } + rc = sqlite3PagerGet(pTab->pPager, pgno, (DbPage**)&pDbPage, 0); + if( rc==SQLITE_OK ){ + rc = sqlite3PagerWrite(pDbPage); + if( rc==SQLITE_OK ){ + memcpy(sqlite3PagerGetData(pDbPage), + sqlite3_value_blob(argv[3]), + pTab->szPage); + } + } + sqlite3PagerUnref(pDbPage); + return rc; + +update_fail: + sqlite3_free(pVtab->zErrMsg); + pVtab->zErrMsg = sqlite3_mprintf("%s", zErr); + return SQLITE_ERROR; +} + +/* +** Invoke this routine to register the "dbpage" virtual table module +*/ +int sqlite3DbpageRegister(sqlite3 *db){ + static sqlite3_module dbpage_module = { + 0, /* iVersion */ + dbpageConnect, /* xCreate */ + dbpageConnect, /* xConnect */ + dbpageBestIndex, /* xBestIndex */ + dbpageDisconnect, /* xDisconnect */ + dbpageDisconnect, /* xDestroy */ + dbpageOpen, /* xOpen - open a cursor */ + dbpageClose, /* xClose - close a cursor */ + dbpageFilter, /* xFilter - configure scan constraints */ + dbpageNext, /* xNext - advance a cursor */ + dbpageEof, /* xEof - check for end of scan */ + dbpageColumn, /* xColumn - read data */ + dbpageRowid, /* xRowid - read data */ + dbpageUpdate, /* xUpdate */ + 0, /* xBegin */ + 0, /* xSync */ + 0, /* xCommit */ + 0, /* xRollback */ + 0, /* xFindMethod */ + 0, /* xRename */ + 0, /* xSavepoint */ + 0, /* xRelease */ + 0, /* xRollbackTo */ + }; + return sqlite3_create_module(db, "sqlite_dbpage", &dbpage_module, 0); +} +#elif defined(SQLITE_ENABLE_DBPAGE_VTAB) +int sqlite3DbpageRegister(sqlite3 *db){ return SQLITE_OK; } +#endif /* SQLITE_ENABLE_DBSTAT_VTAB */ diff --git a/src/main.c b/src/main.c index 3d7609ce5..49613f6c7 100644 --- a/src/main.c +++ b/src/main.c @@ -3054,6 +3054,12 @@ static int openDatabase( } #endif +#ifdef SQLITE_ENABLE_DBPAGE_VTAB + if( !db->mallocFailed && rc==SQLITE_OK){ + rc = sqlite3DbpageRegister(db); + } +#endif + #ifdef SQLITE_ENABLE_DBSTAT_VTAB if( !db->mallocFailed && rc==SQLITE_OK){ rc = sqlite3DbstatRegister(db); diff --git a/src/resolve.c b/src/resolve.c index 78f37512a..4a1e8284c 100644 --- a/src/resolve.c +++ b/src/resolve.c @@ -959,12 +959,10 @@ static int resolveCompoundOrderBy( pOrderBy = pSelect->pOrderBy; if( pOrderBy==0 ) return 0; db = pParse->db; -#if SQLITE_MAX_COLUMN if( pOrderBy->nExpr>db->aLimit[SQLITE_LIMIT_COLUMN] ){ sqlite3ErrorMsg(pParse, "too many terms in ORDER BY clause"); return 1; } -#endif for(i=0; i<pOrderBy->nExpr; i++){ pOrderBy->a[i].done = 0; } @@ -1056,12 +1054,10 @@ int sqlite3ResolveOrderGroupBy( struct ExprList_item *pItem; if( pOrderBy==0 || pParse->db->mallocFailed ) return 0; -#if SQLITE_MAX_COLUMN if( pOrderBy->nExpr>db->aLimit[SQLITE_LIMIT_COLUMN] ){ sqlite3ErrorMsg(pParse, "too many terms in %s BY clause", zType); return 1; } -#endif pEList = pSelect->pEList; assert( pEList!=0 ); /* sqlite3SelectNew() guarantees this */ for(i=0, pItem=pOrderBy->a; i<pOrderBy->nExpr; i++, pItem++){ diff --git a/src/select.c b/src/select.c index 6f524fa2e..0e2328120 100644 --- a/src/select.c +++ b/src/select.c @@ -1689,6 +1689,7 @@ int sqlite3ColumnsFromExprList( nCol = pEList->nExpr; aCol = sqlite3DbMallocZero(db, sizeof(aCol[0])*nCol); testcase( aCol==0 ); + if( nCol>32767 ) nCol = 32767; }else{ nCol = 0; aCol = 0; @@ -4596,12 +4597,10 @@ static int selectExpander(Walker *pWalker, Select *p){ sqlite3ExprListDelete(db, pEList); p->pEList = pNew; } -#if SQLITE_MAX_COLUMN if( p->pEList && p->pEList->nExpr>db->aLimit[SQLITE_LIMIT_COLUMN] ){ sqlite3ErrorMsg(pParse, "too many columns in result set"); return WRC_Abort; } -#endif return WRC_Continue; } diff --git a/src/shell.c.in b/src/shell.c.in index 896d475ed..8f5ed59e9 100644 --- a/src/shell.c.in +++ b/src/shell.c.in @@ -3611,20 +3611,24 @@ static int shell_dbinfo_command(ShellState *p, int nArg, char **azArg){ { "schema size:", "SELECT total(length(sql)) FROM %s" }, }; - sqlite3_file *pFile = 0; int i; char *zSchemaTab; char *zDb = nArg>=2 ? azArg[1] : "main"; + sqlite3_stmt *pStmt = 0; unsigned char aHdr[100]; open_db(p, 0); if( p->db==0 ) return 1; - sqlite3_file_control(p->db, zDb, SQLITE_FCNTL_FILE_POINTER, &pFile); - if( pFile==0 || pFile->pMethods==0 || pFile->pMethods->xRead==0 ){ - return 1; - } - i = pFile->pMethods->xRead(pFile, aHdr, 100, 0); - if( i!=SQLITE_OK ){ + sqlite3_prepare_v2(p->db,"SELECT data FROM sqlite_dbpage(?1) WHERE pgno=1", + -1, &pStmt, 0); + sqlite3_bind_text(pStmt, 1, zDb, -1, SQLITE_STATIC); + if( sqlite3_step(pStmt)==SQLITE_ROW + && sqlite3_column_bytes(pStmt,0)>100 + ){ + memcpy(aHdr, sqlite3_column_blob(pStmt,0), 100); + sqlite3_finalize(pStmt); + }else{ raw_printf(stderr, "unable to read database header\n"); + sqlite3_finalize(pStmt); return 1; } i = get2byteInt(aHdr+16); @@ -4244,7 +4248,7 @@ static int do_meta_command(char *zLine, ShellState *p){ utf8_printf(stderr, "testcase-%s FAILED\n Expected: [%s]\n Got: [%s]\n", p->zTestcase, azArg[1], zRes); - rc = 2; + rc = 1; }else{ utf8_printf(stdout, "testcase-%s ok\n", p->zTestcase); p->nCheck++; diff --git a/src/sqlite3ext.h b/src/sqlite3ext.h index 4aade278b..d1d2c574a 100644 --- a/src/sqlite3ext.h +++ b/src/sqlite3ext.h @@ -134,7 +134,7 @@ struct sqlite3_api_routines { int (*set_authorizer)(sqlite3*,int(*)(void*,int,const char*,const char*, const char*,const char*),void*); void (*set_auxdata)(sqlite3_context*,int,void*,void (*)(void*)); - char * (*snprintf)(int,char*,const char*,...); + char * (*xsnprintf)(int,char*,const char*,...); int (*step)(sqlite3_stmt*); int (*table_column_metadata)(sqlite3*,const char*,const char*,const char*, char const**,char const**,int*,int*,int*); @@ -418,7 +418,7 @@ typedef int (*sqlite3_loadext_entry)( #define sqlite3_rollback_hook sqlite3_api->rollback_hook #define sqlite3_set_authorizer sqlite3_api->set_authorizer #define sqlite3_set_auxdata sqlite3_api->set_auxdata -#define sqlite3_snprintf sqlite3_api->snprintf +#define sqlite3_snprintf sqlite3_api->xsnprintf #define sqlite3_step sqlite3_api->step #define sqlite3_table_column_metadata sqlite3_api->table_column_metadata #define sqlite3_thread_cleanup sqlite3_api->thread_cleanup diff --git a/src/sqliteInt.h b/src/sqliteInt.h index a8f1bed51..0cc435d7b 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -4400,6 +4400,9 @@ int sqlite3ThreadCreate(SQLiteThread**,void*(*)(void*),void*); int sqlite3ThreadJoin(SQLiteThread*, void**); #endif +#if defined(SQLITE_ENABLE_DBPAGE_VTAB) || defined(SQLITE_TEST) +int sqlite3DbpageRegister(sqlite3*); +#endif #if defined(SQLITE_ENABLE_DBSTAT_VTAB) || defined(SQLITE_TEST) int sqlite3DbstatRegister(sqlite3*); #endif diff --git a/src/tclsqlite.c b/src/tclsqlite.c index 1b9f91405..eed86eee3 100644 --- a/src/tclsqlite.c +++ b/src/tclsqlite.c @@ -14,17 +14,19 @@ ** ** Compile-time options: ** -** -DTCLSH=1 Add a "main()" routine that works as a tclsh. +** -DTCLSH Add a "main()" routine that works as a tclsh. ** -** -DSQLITE_TCLMD5 When used in conjuction with -DTCLSH=1, add -** four new commands to the TCL interpreter for -** generating MD5 checksums: md5, md5file, -** md5-10x8, and md5file-10x8. +** -DTCLSH_INIT_PROC=name ** -** -DSQLITE_TEST When used in conjuction with -DTCLSH=1, add -** hundreds of new commands used for testing -** SQLite. This option implies -DSQLITE_TCLMD5. -*/ +** Invoke name(interp) to initialize the Tcl interpreter. +** If name(interp) returns a non-NULL string, then run +** that string as a Tcl script to launch the application. +** If name(interp) returns NULL, then run the regular +** tclsh-emulator code. +*/ +#ifdef TCLSH_INIT_PROC +# define TCLSH 1 +#endif /* ** If requested, include the SQLite compiler options file for MSVC. @@ -3286,7 +3288,42 @@ static int SQLITE_TCLAPI DbObjCmd( ** Return the version string for this database. */ case DB_VERSION: { - Tcl_SetResult(interp, (char *)sqlite3_libversion(), TCL_STATIC); + int i; + for(i=2; i<objc; i++){ + const char *zArg = Tcl_GetString(objv[i]); + /* Optional arguments to $db version are used for testing purpose */ +#ifdef SQLITE_TEST + /* $db version -use-legacy-prepare BOOLEAN + ** + ** Turn the use of legacy sqlite3_prepare() on or off. + */ + if( strcmp(zArg, "-use-legacy-prepare")==0 && i+1<objc ){ + i++; + if( Tcl_GetBooleanFromObj(interp, objv[i], &pDb->bLegacyPrepare) ){ + return TCL_ERROR; + } + }else + + /* $db version -last-stmt-ptr + ** + ** Return a string which is a hex encoding of the pointer to the + ** most recent sqlite3_stmt in the statement cache. + */ + if( strcmp(zArg, "-last-stmt-ptr")==0 ){ + char zBuf[100]; + sqlite3_snprintf(sizeof(zBuf), zBuf, "%p", + pDb->stmtList ? pDb->stmtList->pStmt: 0); + Tcl_SetResult(interp, zBuf, TCL_VOLATILE); + }else +#endif /* SQLITE_TEST */ + { + Tcl_AppendResult(interp, "unknown argument: ", zArg, (char*)0); + return TCL_ERROR; + } + } + if( i==2 ){ + Tcl_SetResult(interp, (char *)sqlite3_libversion(), TCL_STATIC); + } break; } @@ -3546,721 +3583,56 @@ int Sqlite_Unload(Tcl_Interp *interp, int flags){ return TCL_OK; } int Tclsqlite_Unload(Tcl_Interp *interp, int flags){ return TCL_OK; } #endif -#ifdef TCLSH -/***************************************************************************** -** All of the code that follows is used to build standalone TCL interpreters -** that are statically linked with SQLite. Enable these by compiling -** with -DTCLSH=n where n can be 1 or 2. An n of 1 generates a standard -** tclsh but with SQLite built in. An n of 2 generates the SQLite space -** analysis program. -*/ - -#if defined(SQLITE_TEST) || defined(SQLITE_TCLMD5) -/* - * This code implements the MD5 message-digest algorithm. - * The algorithm is due to Ron Rivest. This code was - * written by Colin Plumb in 1993, no copyright is claimed. - * This code is in the public domain; do with it what you wish. - * - * Equivalent code is available from RSA Data Security, Inc. - * This code has been tested against that, and is equivalent, - * except that you don't need to include two pages of legalese - * with every copy. - * - * To compute the message digest of a chunk of bytes, declare an - * MD5Context structure, pass it to MD5Init, call MD5Update as - * needed on buffers full of bytes, and then call MD5Final, which - * will fill a supplied 16-byte array with the digest. - */ - -/* - * If compiled on a machine that doesn't have a 32-bit integer, - * you just set "uint32" to the appropriate datatype for an - * unsigned 32-bit integer. For example: - * - * cc -Duint32='unsigned long' md5.c - * - */ -#ifndef uint32 -# define uint32 unsigned int -#endif - -struct MD5Context { - int isInit; - uint32 buf[4]; - uint32 bits[2]; - unsigned char in[64]; -}; -typedef struct MD5Context MD5Context; - -/* - * Note: this code is harmless on little-endian machines. - */ -static void byteReverse (unsigned char *buf, unsigned longs){ - uint32 t; - do { - t = (uint32)((unsigned)buf[3]<<8 | buf[2]) << 16 | - ((unsigned)buf[1]<<8 | buf[0]); - *(uint32 *)buf = t; - buf += 4; - } while (--longs); -} -/* The four core functions - F1 is optimized somewhat */ - -/* #define F1(x, y, z) (x & y | ~x & z) */ -#define F1(x, y, z) (z ^ (x & (y ^ z))) -#define F2(x, y, z) F1(z, x, y) -#define F3(x, y, z) (x ^ y ^ z) -#define F4(x, y, z) (y ^ (x | ~z)) - -/* This is the central step in the MD5 algorithm. */ -#define MD5STEP(f, w, x, y, z, data, s) \ - ( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x ) - -/* - * The core of the MD5 algorithm, this alters an existing MD5 hash to - * reflect the addition of 16 longwords of new data. MD5Update blocks - * the data and converts bytes into longwords for this routine. - */ -static void MD5Transform(uint32 buf[4], const uint32 in[16]){ - register uint32 a, b, c, d; - - a = buf[0]; - b = buf[1]; - c = buf[2]; - d = buf[3]; - - MD5STEP(F1, a, b, c, d, in[ 0]+0xd76aa478, 7); - MD5STEP(F1, d, a, b, c, in[ 1]+0xe8c7b756, 12); - MD5STEP(F1, c, d, a, b, in[ 2]+0x242070db, 17); - MD5STEP(F1, b, c, d, a, in[ 3]+0xc1bdceee, 22); - MD5STEP(F1, a, b, c, d, in[ 4]+0xf57c0faf, 7); - MD5STEP(F1, d, a, b, c, in[ 5]+0x4787c62a, 12); - MD5STEP(F1, c, d, a, b, in[ 6]+0xa8304613, 17); - MD5STEP(F1, b, c, d, a, in[ 7]+0xfd469501, 22); - MD5STEP(F1, a, b, c, d, in[ 8]+0x698098d8, 7); - MD5STEP(F1, d, a, b, c, in[ 9]+0x8b44f7af, 12); - MD5STEP(F1, c, d, a, b, in[10]+0xffff5bb1, 17); - MD5STEP(F1, b, c, d, a, in[11]+0x895cd7be, 22); - MD5STEP(F1, a, b, c, d, in[12]+0x6b901122, 7); - MD5STEP(F1, d, a, b, c, in[13]+0xfd987193, 12); - MD5STEP(F1, c, d, a, b, in[14]+0xa679438e, 17); - MD5STEP(F1, b, c, d, a, in[15]+0x49b40821, 22); - - MD5STEP(F2, a, b, c, d, in[ 1]+0xf61e2562, 5); - MD5STEP(F2, d, a, b, c, in[ 6]+0xc040b340, 9); - MD5STEP(F2, c, d, a, b, in[11]+0x265e5a51, 14); - MD5STEP(F2, b, c, d, a, in[ 0]+0xe9b6c7aa, 20); - MD5STEP(F2, a, b, c, d, in[ 5]+0xd62f105d, 5); - MD5STEP(F2, d, a, b, c, in[10]+0x02441453, 9); - MD5STEP(F2, c, d, a, b, in[15]+0xd8a1e681, 14); - MD5STEP(F2, b, c, d, a, in[ 4]+0xe7d3fbc8, 20); - MD5STEP(F2, a, b, c, d, in[ 9]+0x21e1cde6, 5); - MD5STEP(F2, d, a, b, c, in[14]+0xc33707d6, 9); - MD5STEP(F2, c, d, a, b, in[ 3]+0xf4d50d87, 14); - MD5STEP(F2, b, c, d, a, in[ 8]+0x455a14ed, 20); - MD5STEP(F2, a, b, c, d, in[13]+0xa9e3e905, 5); - MD5STEP(F2, d, a, b, c, in[ 2]+0xfcefa3f8, 9); - MD5STEP(F2, c, d, a, b, in[ 7]+0x676f02d9, 14); - MD5STEP(F2, b, c, d, a, in[12]+0x8d2a4c8a, 20); - - MD5STEP(F3, a, b, c, d, in[ 5]+0xfffa3942, 4); - MD5STEP(F3, d, a, b, c, in[ 8]+0x8771f681, 11); - MD5STEP(F3, c, d, a, b, in[11]+0x6d9d6122, 16); - MD5STEP(F3, b, c, d, a, in[14]+0xfde5380c, 23); - MD5STEP(F3, a, b, c, d, in[ 1]+0xa4beea44, 4); - MD5STEP(F3, d, a, b, c, in[ 4]+0x4bdecfa9, 11); - MD5STEP(F3, c, d, a, b, in[ 7]+0xf6bb4b60, 16); - MD5STEP(F3, b, c, d, a, in[10]+0xbebfbc70, 23); - MD5STEP(F3, a, b, c, d, in[13]+0x289b7ec6, 4); - MD5STEP(F3, d, a, b, c, in[ 0]+0xeaa127fa, 11); - MD5STEP(F3, c, d, a, b, in[ 3]+0xd4ef3085, 16); - MD5STEP(F3, b, c, d, a, in[ 6]+0x04881d05, 23); - MD5STEP(F3, a, b, c, d, in[ 9]+0xd9d4d039, 4); - MD5STEP(F3, d, a, b, c, in[12]+0xe6db99e5, 11); - MD5STEP(F3, c, d, a, b, in[15]+0x1fa27cf8, 16); - MD5STEP(F3, b, c, d, a, in[ 2]+0xc4ac5665, 23); - - MD5STEP(F4, a, b, c, d, in[ 0]+0xf4292244, 6); - MD5STEP(F4, d, a, b, c, in[ 7]+0x432aff97, 10); - MD5STEP(F4, c, d, a, b, in[14]+0xab9423a7, 15); - MD5STEP(F4, b, c, d, a, in[ 5]+0xfc93a039, 21); - MD5STEP(F4, a, b, c, d, in[12]+0x655b59c3, 6); - MD5STEP(F4, d, a, b, c, in[ 3]+0x8f0ccc92, 10); - MD5STEP(F4, c, d, a, b, in[10]+0xffeff47d, 15); - MD5STEP(F4, b, c, d, a, in[ 1]+0x85845dd1, 21); - MD5STEP(F4, a, b, c, d, in[ 8]+0x6fa87e4f, 6); - MD5STEP(F4, d, a, b, c, in[15]+0xfe2ce6e0, 10); - MD5STEP(F4, c, d, a, b, in[ 6]+0xa3014314, 15); - MD5STEP(F4, b, c, d, a, in[13]+0x4e0811a1, 21); - MD5STEP(F4, a, b, c, d, in[ 4]+0xf7537e82, 6); - MD5STEP(F4, d, a, b, c, in[11]+0xbd3af235, 10); - MD5STEP(F4, c, d, a, b, in[ 2]+0x2ad7d2bb, 15); - MD5STEP(F4, b, c, d, a, in[ 9]+0xeb86d391, 21); - - buf[0] += a; - buf[1] += b; - buf[2] += c; - buf[3] += d; -} - -/* - * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious - * initialization constants. - */ -static void MD5Init(MD5Context *ctx){ - ctx->isInit = 1; - ctx->buf[0] = 0x67452301; - ctx->buf[1] = 0xefcdab89; - ctx->buf[2] = 0x98badcfe; - ctx->buf[3] = 0x10325476; - ctx->bits[0] = 0; - ctx->bits[1] = 0; -} - -/* - * Update context to reflect the concatenation of another buffer full - * of bytes. - */ -static -void MD5Update(MD5Context *ctx, const unsigned char *buf, unsigned int len){ - uint32 t; - - /* Update bitcount */ - - t = ctx->bits[0]; - if ((ctx->bits[0] = t + ((uint32)len << 3)) < t) - ctx->bits[1]++; /* Carry from low to high */ - ctx->bits[1] += len >> 29; - - t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */ - - /* Handle any leading odd-sized chunks */ - - if ( t ) { - unsigned char *p = (unsigned char *)ctx->in + t; - - t = 64-t; - if (len < t) { - memcpy(p, buf, len); - return; - } - memcpy(p, buf, t); - byteReverse(ctx->in, 16); - MD5Transform(ctx->buf, (uint32 *)ctx->in); - buf += t; - len -= t; - } - - /* Process data in 64-byte chunks */ - - while (len >= 64) { - memcpy(ctx->in, buf, 64); - byteReverse(ctx->in, 16); - MD5Transform(ctx->buf, (uint32 *)ctx->in); - buf += 64; - len -= 64; - } - - /* Handle any remaining bytes of data. */ - - memcpy(ctx->in, buf, len); -} - -/* - * Final wrapup - pad to 64-byte boundary with the bit pattern - * 1 0* (64-bit count of bits processed, MSB-first) - */ -static void MD5Final(unsigned char digest[16], MD5Context *ctx){ - unsigned count; - unsigned char *p; - - /* Compute number of bytes mod 64 */ - count = (ctx->bits[0] >> 3) & 0x3F; - - /* Set the first char of padding to 0x80. This is safe since there is - always at least one byte free */ - p = ctx->in + count; - *p++ = 0x80; - - /* Bytes of padding needed to make 64 bytes */ - count = 64 - 1 - count; - - /* Pad out to 56 mod 64 */ - if (count < 8) { - /* Two lots of padding: Pad the first block to 64 bytes */ - memset(p, 0, count); - byteReverse(ctx->in, 16); - MD5Transform(ctx->buf, (uint32 *)ctx->in); - - /* Now fill the next block with 56 bytes */ - memset(ctx->in, 0, 56); - } else { - /* Pad block to 56 bytes */ - memset(p, 0, count-8); - } - byteReverse(ctx->in, 14); - - /* Append length in bits and transform */ - memcpy(ctx->in + 14*4, ctx->bits, 8); - - MD5Transform(ctx->buf, (uint32 *)ctx->in); - byteReverse((unsigned char *)ctx->buf, 4); - memcpy(digest, ctx->buf, 16); -} - /* -** Convert a 128-bit MD5 digest into a 32-digit base-16 number. +** If the TCLSH macro is defined, add code to make a stand-alone program. */ -static void MD5DigestToBase16(unsigned char *digest, char *zBuf){ - static char const zEncode[] = "0123456789abcdef"; - int i, j; - - for(j=i=0; i<16; i++){ - int a = digest[i]; - zBuf[j++] = zEncode[(a>>4)&0xf]; - zBuf[j++] = zEncode[a & 0xf]; - } - zBuf[j] = 0; -} - +#if defined(TCLSH) -/* -** Convert a 128-bit MD5 digest into sequency of eight 5-digit integers -** each representing 16 bits of the digest and separated from each -** other by a "-" character. +/* This is the main routine for an ordinary TCL shell. If there are +** are arguments, run the first argument as a script. Otherwise, +** read TCL commands from standard input */ -static void MD5DigestToBase10x8(unsigned char digest[16], char zDigest[50]){ - int i, j; - unsigned int x; - for(i=j=0; i<16; i+=2){ - x = digest[i]*256 + digest[i+1]; - if( i>0 ) zDigest[j++] = '-'; - sqlite3_snprintf(50-j, &zDigest[j], "%05u", x); - j += 5; - } - zDigest[j] = 0; -} - -/* -** A TCL command for md5. The argument is the text to be hashed. The -** Result is the hash in base64. -*/ -static int SQLITE_TCLAPI md5_cmd( - void*cd, - Tcl_Interp *interp, - int argc, - const char **argv -){ - MD5Context ctx; - unsigned char digest[16]; - char zBuf[50]; - void (*converter)(unsigned char*, char*); - - if( argc!=2 ){ - Tcl_AppendResult(interp,"wrong # args: should be \"", argv[0], - " TEXT\"", (char*)0); - return TCL_ERROR; - } - MD5Init(&ctx); - MD5Update(&ctx, (unsigned char*)argv[1], (unsigned)strlen(argv[1])); - MD5Final(digest, &ctx); - converter = (void(*)(unsigned char*,char*))cd; - converter(digest, zBuf); - Tcl_AppendResult(interp, zBuf, (char*)0); - return TCL_OK; -} - -/* -** A TCL command to take the md5 hash of a file. The argument is the -** name of the file. -*/ -static int SQLITE_TCLAPI md5file_cmd( - void*cd, - Tcl_Interp *interp, - int argc, - const char **argv -){ - FILE *in; - int ofst; - int amt; - MD5Context ctx; - void (*converter)(unsigned char*, char*); - unsigned char digest[16]; - char zBuf[10240]; - - if( argc!=2 && argc!=4 ){ - Tcl_AppendResult(interp,"wrong # args: should be \"", argv[0], - " FILENAME [OFFSET AMT]\"", (char*)0); - return TCL_ERROR; - } - if( argc==4 ){ - ofst = atoi(argv[2]); - amt = atoi(argv[3]); - }else{ - ofst = 0; - amt = 2147483647; - } - in = fopen(argv[1],"rb"); - if( in==0 ){ - Tcl_AppendResult(interp,"unable to open file \"", argv[1], - "\" for reading", (char*)0); - return TCL_ERROR; - } - fseek(in, ofst, SEEK_SET); - MD5Init(&ctx); - while( amt>0 ){ - int n; - n = (int)fread(zBuf, 1, sizeof(zBuf)<=amt ? sizeof(zBuf) : amt, in); - if( n<=0 ) break; - MD5Update(&ctx, (unsigned char*)zBuf, (unsigned)n); - amt -= n; - } - fclose(in); - MD5Final(digest, &ctx); - converter = (void(*)(unsigned char*,char*))cd; - converter(digest, zBuf); - Tcl_AppendResult(interp, zBuf, (char*)0); - return TCL_OK; -} - -/* -** Register the four new TCL commands for generating MD5 checksums -** with the TCL interpreter. -*/ -int Md5_Init(Tcl_Interp *interp){ - Tcl_CreateCommand(interp, "md5", (Tcl_CmdProc*)md5_cmd, - MD5DigestToBase16, 0); - Tcl_CreateCommand(interp, "md5-10x8", (Tcl_CmdProc*)md5_cmd, - MD5DigestToBase10x8, 0); - Tcl_CreateCommand(interp, "md5file", (Tcl_CmdProc*)md5file_cmd, - MD5DigestToBase16, 0); - Tcl_CreateCommand(interp, "md5file-10x8", (Tcl_CmdProc*)md5file_cmd, - MD5DigestToBase10x8, 0); - return TCL_OK; -} -#endif /* defined(SQLITE_TEST) || defined(SQLITE_TCLMD5) */ - -#if defined(SQLITE_TEST) -/* -** During testing, the special md5sum() aggregate function is available. -** inside SQLite. The following routines implement that function. -*/ -static void md5step(sqlite3_context *context, int argc, sqlite3_value **argv){ - MD5Context *p; - int i; - if( argc<1 ) return; - p = sqlite3_aggregate_context(context, sizeof(*p)); - if( p==0 ) return; - if( !p->isInit ){ - MD5Init(p); - } - for(i=0; i<argc; i++){ - const char *zData = (char*)sqlite3_value_text(argv[i]); - if( zData ){ - MD5Update(p, (unsigned char*)zData, (int)strlen(zData)); - } - } -} -static void md5finalize(sqlite3_context *context){ - MD5Context *p; - unsigned char digest[16]; - char zBuf[33]; - p = sqlite3_aggregate_context(context, sizeof(*p)); - MD5Final(digest,p); - MD5DigestToBase16(digest, zBuf); - sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT); -} -int Md5_Register( - sqlite3 *db, - char **pzErrMsg, - const sqlite3_api_routines *pThunk -){ - int rc = sqlite3_create_function(db, "md5sum", -1, SQLITE_UTF8, 0, 0, - md5step, md5finalize); - sqlite3_overload_function(db, "md5sum", -1); /* To exercise this API */ - return rc; -} -#endif /* defined(SQLITE_TEST) */ - - -/* -** If the macro TCLSH is one, then put in code this for the -** "main" routine that will initialize Tcl and take input from -** standard input, or if a file is named on the command line -** the TCL interpreter reads and evaluates that file. -*/ -#if TCLSH==1 static const char *tclsh_main_loop(void){ static const char zMainloop[] = - "set line {}\n" - "while {![eof stdin]} {\n" - "if {$line!=\"\"} {\n" - "puts -nonewline \"> \"\n" - "} else {\n" - "puts -nonewline \"% \"\n" - "}\n" - "flush stdout\n" - "append line [gets stdin]\n" - "if {[info complete $line]} {\n" - "if {[catch {uplevel #0 $line} result]} {\n" - "puts stderr \"Error: $result\"\n" - "} elseif {$result!=\"\"} {\n" - "puts $result\n" + "if {[llength $argv]>=1} {\n" + "set argv0 [lindex $argv 0]\n" + "set argv [lrange $argv 1 end]\n" + "source $argv0\n" + "} else {\n" + "set line {}\n" + "while {![eof stdin]} {\n" + "if {$line!=\"\"} {\n" + "puts -nonewline \"> \"\n" + "} else {\n" + "puts -nonewline \"% \"\n" + "}\n" + "flush stdout\n" + "append line [gets stdin]\n" + "if {[info complete $line]} {\n" + "if {[catch {uplevel #0 $line} result]} {\n" + "puts stderr \"Error: $result\"\n" + "} elseif {$result!=\"\"} {\n" + "puts $result\n" + "}\n" + "set line {}\n" + "} else {\n" + "append line \\n\n" "}\n" - "set line {}\n" - "} else {\n" - "append line \\n\n" "}\n" "}\n" ; return zMainloop; } -#endif -#if TCLSH==2 -static const char *tclsh_main_loop(void); -#endif - -#ifdef SQLITE_TEST -static void init_all(Tcl_Interp *); -static int SQLITE_TCLAPI init_all_cmd( - ClientData cd, - Tcl_Interp *interp, - int objc, - Tcl_Obj *CONST objv[] -){ - - Tcl_Interp *slave; - if( objc!=2 ){ - Tcl_WrongNumArgs(interp, 1, objv, "SLAVE"); - return TCL_ERROR; - } - - slave = Tcl_GetSlave(interp, Tcl_GetString(objv[1])); - if( !slave ){ - return TCL_ERROR; - } - - init_all(slave); - return TCL_OK; -} - -/* -** Tclcmd: db_use_legacy_prepare DB BOOLEAN -** -** The first argument to this command must be a database command created by -** [sqlite3]. If the second argument is true, then the handle is configured -** to use the sqlite3_prepare_v2() function to prepare statements. If it -** is false, sqlite3_prepare(). -*/ -static int SQLITE_TCLAPI db_use_legacy_prepare_cmd( - ClientData cd, - Tcl_Interp *interp, - int objc, - Tcl_Obj *CONST objv[] -){ - Tcl_CmdInfo cmdInfo; - SqliteDb *pDb; - int bPrepare; - - if( objc!=3 ){ - Tcl_WrongNumArgs(interp, 1, objv, "DB BOOLEAN"); - return TCL_ERROR; - } - - if( !Tcl_GetCommandInfo(interp, Tcl_GetString(objv[1]), &cmdInfo) ){ - Tcl_AppendResult(interp, "no such db: ", Tcl_GetString(objv[1]), (char*)0); - return TCL_ERROR; - } - pDb = (SqliteDb*)cmdInfo.objClientData; - if( Tcl_GetBooleanFromObj(interp, objv[2], &bPrepare) ){ - return TCL_ERROR; - } - - pDb->bLegacyPrepare = bPrepare; - - Tcl_ResetResult(interp); - return TCL_OK; -} - -/* -** Tclcmd: db_last_stmt_ptr DB -** -** If the statement cache associated with database DB is not empty, -** return the text representation of the most recently used statement -** handle. -*/ -static int SQLITE_TCLAPI db_last_stmt_ptr( - ClientData cd, - Tcl_Interp *interp, - int objc, - Tcl_Obj *CONST objv[] -){ - extern int sqlite3TestMakePointerStr(Tcl_Interp*, char*, void*); - Tcl_CmdInfo cmdInfo; - SqliteDb *pDb; - sqlite3_stmt *pStmt = 0; - char zBuf[100]; - - if( objc!=2 ){ - Tcl_WrongNumArgs(interp, 1, objv, "DB"); - return TCL_ERROR; - } - - if( !Tcl_GetCommandInfo(interp, Tcl_GetString(objv[1]), &cmdInfo) ){ - Tcl_AppendResult(interp, "no such db: ", Tcl_GetString(objv[1]), (char*)0); - return TCL_ERROR; - } - pDb = (SqliteDb*)cmdInfo.objClientData; - - if( pDb->stmtList ) pStmt = pDb->stmtList->pStmt; - if( sqlite3TestMakePointerStr(interp, zBuf, pStmt) ){ - return TCL_ERROR; - } - Tcl_SetResult(interp, zBuf, TCL_VOLATILE); - - return TCL_OK; -} -#endif /* SQLITE_TEST */ - -/* -** Configure the interpreter passed as the first argument to have access -** to the commands and linked variables that make up: -** -** * the [sqlite3] extension itself, -** -** * If SQLITE_TCLMD5 or SQLITE_TEST is defined, the Md5 commands, and -** -** * If SQLITE_TEST is set, the various test interfaces used by the Tcl -** test suite. -*/ -static void init_all(Tcl_Interp *interp){ - Sqlite3_Init(interp); - -#if defined(SQLITE_TEST) || defined(SQLITE_TCLMD5) - Md5_Init(interp); -#endif - -#ifdef SQLITE_TEST - { - extern int Sqliteconfig_Init(Tcl_Interp*); - extern int Sqlitetest1_Init(Tcl_Interp*); - extern int Sqlitetest2_Init(Tcl_Interp*); - extern int Sqlitetest3_Init(Tcl_Interp*); - extern int Sqlitetest4_Init(Tcl_Interp*); - extern int Sqlitetest5_Init(Tcl_Interp*); - extern int Sqlitetest6_Init(Tcl_Interp*); - extern int Sqlitetest7_Init(Tcl_Interp*); - extern int Sqlitetest8_Init(Tcl_Interp*); - extern int Sqlitetest9_Init(Tcl_Interp*); - extern int Sqlitetestasync_Init(Tcl_Interp*); - extern int Sqlitetest_autoext_Init(Tcl_Interp*); - extern int Sqlitetest_blob_Init(Tcl_Interp*); - extern int Sqlitetest_demovfs_Init(Tcl_Interp *); - extern int Sqlitetest_func_Init(Tcl_Interp*); - extern int Sqlitetest_hexio_Init(Tcl_Interp*); - extern int Sqlitetest_init_Init(Tcl_Interp*); - extern int Sqlitetest_malloc_Init(Tcl_Interp*); - extern int Sqlitetest_mutex_Init(Tcl_Interp*); - extern int Sqlitetestschema_Init(Tcl_Interp*); - extern int Sqlitetestsse_Init(Tcl_Interp*); - extern int Sqlitetesttclvar_Init(Tcl_Interp*); - extern int Sqlitetestfs_Init(Tcl_Interp*); - extern int SqlitetestThread_Init(Tcl_Interp*); - extern int SqlitetestOnefile_Init(); - extern int SqlitetestOsinst_Init(Tcl_Interp*); - extern int Sqlitetestbackup_Init(Tcl_Interp*); - extern int Sqlitetestintarray_Init(Tcl_Interp*); - extern int Sqlitetestvfs_Init(Tcl_Interp *); - extern int Sqlitetestrtree_Init(Tcl_Interp*); - extern int Sqlitequota_Init(Tcl_Interp*); - extern int Sqlitemultiplex_Init(Tcl_Interp*); - extern int SqliteSuperlock_Init(Tcl_Interp*); - extern int SqlitetestSyscall_Init(Tcl_Interp*); -#if defined(SQLITE_ENABLE_SESSION) && defined(SQLITE_ENABLE_PREUPDATE_HOOK) - extern int TestSession_Init(Tcl_Interp*); -#endif - extern int Fts5tcl_Init(Tcl_Interp *); - extern int SqliteRbu_Init(Tcl_Interp*); - extern int Sqlitetesttcl_Init(Tcl_Interp*); -#if defined(SQLITE_ENABLE_FTS3) || defined(SQLITE_ENABLE_FTS4) - extern int Sqlitetestfts3_Init(Tcl_Interp *interp); -#endif - -#ifdef SQLITE_ENABLE_ZIPVFS - extern int Zipvfs_Init(Tcl_Interp*); - Zipvfs_Init(interp); -#endif - - Sqliteconfig_Init(interp); - Sqlitetest1_Init(interp); - Sqlitetest2_Init(interp); - Sqlitetest3_Init(interp); - Sqlitetest4_Init(interp); - Sqlitetest5_Init(interp); - Sqlitetest6_Init(interp); - Sqlitetest7_Init(interp); - Sqlitetest8_Init(interp); - Sqlitetest9_Init(interp); - Sqlitetestasync_Init(interp); - Sqlitetest_autoext_Init(interp); - Sqlitetest_blob_Init(interp); - Sqlitetest_demovfs_Init(interp); - Sqlitetest_func_Init(interp); - Sqlitetest_hexio_Init(interp); - Sqlitetest_init_Init(interp); - Sqlitetest_malloc_Init(interp); - Sqlitetest_mutex_Init(interp); - Sqlitetestschema_Init(interp); - Sqlitetesttclvar_Init(interp); - Sqlitetestfs_Init(interp); - SqlitetestThread_Init(interp); - SqlitetestOnefile_Init(); - SqlitetestOsinst_Init(interp); - Sqlitetestbackup_Init(interp); - Sqlitetestintarray_Init(interp); - Sqlitetestvfs_Init(interp); - Sqlitetestrtree_Init(interp); - Sqlitequota_Init(interp); - Sqlitemultiplex_Init(interp); - SqliteSuperlock_Init(interp); - SqlitetestSyscall_Init(interp); -#if defined(SQLITE_ENABLE_SESSION) && defined(SQLITE_ENABLE_PREUPDATE_HOOK) - TestSession_Init(interp); -#endif - Fts5tcl_Init(interp); - SqliteRbu_Init(interp); - Sqlitetesttcl_Init(interp); - -#if defined(SQLITE_ENABLE_FTS3) || defined(SQLITE_ENABLE_FTS4) - Sqlitetestfts3_Init(interp); -#endif - - Tcl_CreateObjCommand( - interp, "load_testfixture_extensions", init_all_cmd, 0, 0 - ); - Tcl_CreateObjCommand( - interp, "db_use_legacy_prepare", db_use_legacy_prepare_cmd, 0, 0 - ); - Tcl_CreateObjCommand( - interp, "db_last_stmt_ptr", db_last_stmt_ptr, 0, 0 - ); - -#ifdef SQLITE_SSE - Sqlitetestsse_Init(interp); -#endif - } -#endif -} - -/* Needed for the setrlimit() system call on unix */ -#if defined(unix) -#include <sys/resource.h> -#endif #define TCLSH_MAIN main /* Needed to fake out mktclapp */ int SQLITE_CDECL TCLSH_MAIN(int argc, char **argv){ Tcl_Interp *interp; + int i; + const char *zScript = 0; + char zArgc[32]; +#if defined(TCLSH_INIT_PROC) + extern const char *TCLSH_INIT_PROC(Tcl_Interp*); +#endif #if !defined(_WIN32_WCE) if( getenv("BREAK") ){ @@ -4271,17 +3643,6 @@ int SQLITE_CDECL TCLSH_MAIN(int argc, char **argv){ } #endif - /* Since the primary use case for this binary is testing of SQLite, - ** be sure to generate core files if we crash */ -#if defined(SQLITE_TEST) && defined(unix) - { struct rlimit x; - getrlimit(RLIMIT_CORE, &x); - x.rlim_cur = x.rlim_max; - setrlimit(RLIMIT_CORE, &x); - } -#endif /* SQLITE_TEST && unix */ - - /* Call sqlite3_shutdown() once before doing anything else. This is to ** test that sqlite3_shutdown() can be safely called by a process before ** sqlite3_initialize() is. */ @@ -4290,32 +3651,27 @@ int SQLITE_CDECL TCLSH_MAIN(int argc, char **argv){ Tcl_FindExecutable(argv[0]); Tcl_SetSystemEncoding(NULL, "utf-8"); interp = Tcl_CreateInterp(); + Sqlite3_Init(interp); -#if TCLSH==2 - sqlite3_config(SQLITE_CONFIG_SINGLETHREAD); + sqlite3_snprintf(sizeof(zArgc), zArgc, "%d", argc-1); + Tcl_SetVar(interp,"argc", zArgc, TCL_GLOBAL_ONLY); + Tcl_SetVar(interp,"argv0",argv[0],TCL_GLOBAL_ONLY); + Tcl_SetVar(interp,"argv", "", TCL_GLOBAL_ONLY); + for(i=1; i<argc; i++){ + Tcl_SetVar(interp, "argv", argv[i], + TCL_GLOBAL_ONLY | TCL_LIST_ELEMENT | TCL_APPEND_VALUE); + } +#if defined(TCLSH_INIT_PROC) + zScript = TCLSH_INIT_PROC(interp); #endif - - init_all(interp); - if( argc>=2 ){ - int i; - char zArgc[32]; - sqlite3_snprintf(sizeof(zArgc), zArgc, "%d", argc-(3-TCLSH)); - Tcl_SetVar(interp,"argc", zArgc, TCL_GLOBAL_ONLY); - Tcl_SetVar(interp,"argv0",argv[1],TCL_GLOBAL_ONLY); - Tcl_SetVar(interp,"argv", "", TCL_GLOBAL_ONLY); - for(i=3-TCLSH; i<argc; i++){ - Tcl_SetVar(interp, "argv", argv[i], - TCL_GLOBAL_ONLY | TCL_LIST_ELEMENT | TCL_APPEND_VALUE); - } - if( TCLSH==1 && Tcl_EvalFile(interp, argv[1])!=TCL_OK ){ - const char *zInfo = Tcl_GetVar(interp, "errorInfo", TCL_GLOBAL_ONLY); - if( zInfo==0 ) zInfo = Tcl_GetStringResult(interp); - fprintf(stderr,"%s: %s\n", *argv, zInfo); - return 1; - } + if( zScript==0 ){ + zScript = tclsh_main_loop(); } - if( TCLSH==2 || argc<=1 ){ - Tcl_GlobalEval(interp, tclsh_main_loop()); + if( Tcl_GlobalEval(interp, zScript)!=TCL_OK ){ + const char *zInfo = Tcl_GetVar(interp, "errorInfo", TCL_GLOBAL_ONLY); + if( zInfo==0 ) zInfo = Tcl_GetStringResult(interp); + fprintf(stderr,"%s: %s\n", *argv, zInfo); + return 1; } return 0; } diff --git a/src/test_md5.c b/src/test_md5.c new file mode 100644 index 000000000..b67002686 --- /dev/null +++ b/src/test_md5.c @@ -0,0 +1,450 @@ +/* +** 2017-10-13 +** +** 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. +** +************************************************************************* +** +** This file contains code to implement an MD5 extension to TCL. +*/ +#include "sqlite3.h" +#include <stdlib.h> +#include <string.h> +#include "sqlite3.h" +#if defined(INCLUDE_SQLITE_TCL_H) +# include "sqlite_tcl.h" +#else +# include "tcl.h" +# ifndef SQLITE_TCLAPI +# define SQLITE_TCLAPI +# endif +#endif + +/* + * This code implements the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * + * To compute the message digest of a chunk of bytes, declare an + * MD5Context structure, pass it to MD5Init, call MD5Update as + * needed on buffers full of bytes, and then call MD5Final, which + * will fill a supplied 16-byte array with the digest. + */ + +/* + * If compiled on a machine that doesn't have a 32-bit integer, + * you just set "uint32" to the appropriate datatype for an + * unsigned 32-bit integer. For example: + * + * cc -Duint32='unsigned long' md5.c + * + */ +#ifndef uint32 +# define uint32 unsigned int +#endif + +struct MD5Context { + int isInit; + uint32 buf[4]; + uint32 bits[2]; + unsigned char in[64]; +}; +typedef struct MD5Context MD5Context; + +/* + * Note: this code is harmless on little-endian machines. + */ +static void byteReverse (unsigned char *buf, unsigned longs){ + uint32 t; + do { + t = (uint32)((unsigned)buf[3]<<8 | buf[2]) << 16 | + ((unsigned)buf[1]<<8 | buf[0]); + *(uint32 *)buf = t; + buf += 4; + } while (--longs); +} +/* The four core functions - F1 is optimized somewhat */ + +/* #define F1(x, y, z) (x & y | ~x & z) */ +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) + +/* This is the central step in the MD5 algorithm. */ +#define MD5STEP(f, w, x, y, z, data, s) \ + ( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x ) + +/* + * The core of the MD5 algorithm, this alters an existing MD5 hash to + * reflect the addition of 16 longwords of new data. MD5Update blocks + * the data and converts bytes into longwords for this routine. + */ +static void MD5Transform(uint32 buf[4], const uint32 in[16]){ + register uint32 a, b, c, d; + + a = buf[0]; + b = buf[1]; + c = buf[2]; + d = buf[3]; + + MD5STEP(F1, a, b, c, d, in[ 0]+0xd76aa478, 7); + MD5STEP(F1, d, a, b, c, in[ 1]+0xe8c7b756, 12); + MD5STEP(F1, c, d, a, b, in[ 2]+0x242070db, 17); + MD5STEP(F1, b, c, d, a, in[ 3]+0xc1bdceee, 22); + MD5STEP(F1, a, b, c, d, in[ 4]+0xf57c0faf, 7); + MD5STEP(F1, d, a, b, c, in[ 5]+0x4787c62a, 12); + MD5STEP(F1, c, d, a, b, in[ 6]+0xa8304613, 17); + MD5STEP(F1, b, c, d, a, in[ 7]+0xfd469501, 22); + MD5STEP(F1, a, b, c, d, in[ 8]+0x698098d8, 7); + MD5STEP(F1, d, a, b, c, in[ 9]+0x8b44f7af, 12); + MD5STEP(F1, c, d, a, b, in[10]+0xffff5bb1, 17); + MD5STEP(F1, b, c, d, a, in[11]+0x895cd7be, 22); + MD5STEP(F1, a, b, c, d, in[12]+0x6b901122, 7); + MD5STEP(F1, d, a, b, c, in[13]+0xfd987193, 12); + MD5STEP(F1, c, d, a, b, in[14]+0xa679438e, 17); + MD5STEP(F1, b, c, d, a, in[15]+0x49b40821, 22); + + MD5STEP(F2, a, b, c, d, in[ 1]+0xf61e2562, 5); + MD5STEP(F2, d, a, b, c, in[ 6]+0xc040b340, 9); + MD5STEP(F2, c, d, a, b, in[11]+0x265e5a51, 14); + MD5STEP(F2, b, c, d, a, in[ 0]+0xe9b6c7aa, 20); + MD5STEP(F2, a, b, c, d, in[ 5]+0xd62f105d, 5); + MD5STEP(F2, d, a, b, c, in[10]+0x02441453, 9); + MD5STEP(F2, c, d, a, b, in[15]+0xd8a1e681, 14); + MD5STEP(F2, b, c, d, a, in[ 4]+0xe7d3fbc8, 20); + MD5STEP(F2, a, b, c, d, in[ 9]+0x21e1cde6, 5); + MD5STEP(F2, d, a, b, c, in[14]+0xc33707d6, 9); + MD5STEP(F2, c, d, a, b, in[ 3]+0xf4d50d87, 14); + MD5STEP(F2, b, c, d, a, in[ 8]+0x455a14ed, 20); + MD5STEP(F2, a, b, c, d, in[13]+0xa9e3e905, 5); + MD5STEP(F2, d, a, b, c, in[ 2]+0xfcefa3f8, 9); + MD5STEP(F2, c, d, a, b, in[ 7]+0x676f02d9, 14); + MD5STEP(F2, b, c, d, a, in[12]+0x8d2a4c8a, 20); + + MD5STEP(F3, a, b, c, d, in[ 5]+0xfffa3942, 4); + MD5STEP(F3, d, a, b, c, in[ 8]+0x8771f681, 11); + MD5STEP(F3, c, d, a, b, in[11]+0x6d9d6122, 16); + MD5STEP(F3, b, c, d, a, in[14]+0xfde5380c, 23); + MD5STEP(F3, a, b, c, d, in[ 1]+0xa4beea44, 4); + MD5STEP(F3, d, a, b, c, in[ 4]+0x4bdecfa9, 11); + MD5STEP(F3, c, d, a, b, in[ 7]+0xf6bb4b60, 16); + MD5STEP(F3, b, c, d, a, in[10]+0xbebfbc70, 23); + MD5STEP(F3, a, b, c, d, in[13]+0x289b7ec6, 4); + MD5STEP(F3, d, a, b, c, in[ 0]+0xeaa127fa, 11); + MD5STEP(F3, c, d, a, b, in[ 3]+0xd4ef3085, 16); + MD5STEP(F3, b, c, d, a, in[ 6]+0x04881d05, 23); + MD5STEP(F3, a, b, c, d, in[ 9]+0xd9d4d039, 4); + MD5STEP(F3, d, a, b, c, in[12]+0xe6db99e5, 11); + MD5STEP(F3, c, d, a, b, in[15]+0x1fa27cf8, 16); + MD5STEP(F3, b, c, d, a, in[ 2]+0xc4ac5665, 23); + + MD5STEP(F4, a, b, c, d, in[ 0]+0xf4292244, 6); + MD5STEP(F4, d, a, b, c, in[ 7]+0x432aff97, 10); + MD5STEP(F4, c, d, a, b, in[14]+0xab9423a7, 15); + MD5STEP(F4, b, c, d, a, in[ 5]+0xfc93a039, 21); + MD5STEP(F4, a, b, c, d, in[12]+0x655b59c3, 6); + MD5STEP(F4, d, a, b, c, in[ 3]+0x8f0ccc92, 10); + MD5STEP(F4, c, d, a, b, in[10]+0xffeff47d, 15); + MD5STEP(F4, b, c, d, a, in[ 1]+0x85845dd1, 21); + MD5STEP(F4, a, b, c, d, in[ 8]+0x6fa87e4f, 6); + MD5STEP(F4, d, a, b, c, in[15]+0xfe2ce6e0, 10); + MD5STEP(F4, c, d, a, b, in[ 6]+0xa3014314, 15); + MD5STEP(F4, b, c, d, a, in[13]+0x4e0811a1, 21); + MD5STEP(F4, a, b, c, d, in[ 4]+0xf7537e82, 6); + MD5STEP(F4, d, a, b, c, in[11]+0xbd3af235, 10); + MD5STEP(F4, c, d, a, b, in[ 2]+0x2ad7d2bb, 15); + MD5STEP(F4, b, c, d, a, in[ 9]+0xeb86d391, 21); + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} + +/* + * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious + * initialization constants. + */ +static void MD5Init(MD5Context *ctx){ + ctx->isInit = 1; + ctx->buf[0] = 0x67452301; + ctx->buf[1] = 0xefcdab89; + ctx->buf[2] = 0x98badcfe; + ctx->buf[3] = 0x10325476; + ctx->bits[0] = 0; + ctx->bits[1] = 0; +} + +/* + * Update context to reflect the concatenation of another buffer full + * of bytes. + */ +static +void MD5Update(MD5Context *ctx, const unsigned char *buf, unsigned int len){ + uint32 t; + + /* Update bitcount */ + + t = ctx->bits[0]; + if ((ctx->bits[0] = t + ((uint32)len << 3)) < t) + ctx->bits[1]++; /* Carry from low to high */ + ctx->bits[1] += len >> 29; + + t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */ + + /* Handle any leading odd-sized chunks */ + + if ( t ) { + unsigned char *p = (unsigned char *)ctx->in + t; + + t = 64-t; + if (len < t) { + memcpy(p, buf, len); + return; + } + memcpy(p, buf, t); + byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, (uint32 *)ctx->in); + buf += t; + len -= t; + } + + /* Process data in 64-byte chunks */ + + while (len >= 64) { + memcpy(ctx->in, buf, 64); + byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, (uint32 *)ctx->in); + buf += 64; + len -= 64; + } + + /* Handle any remaining bytes of data. */ + + memcpy(ctx->in, buf, len); +} + +/* + * Final wrapup - pad to 64-byte boundary with the bit pattern + * 1 0* (64-bit count of bits processed, MSB-first) + */ +static void MD5Final(unsigned char digest[16], MD5Context *ctx){ + unsigned count; + unsigned char *p; + + /* Compute number of bytes mod 64 */ + count = (ctx->bits[0] >> 3) & 0x3F; + + /* Set the first char of padding to 0x80. This is safe since there is + always at least one byte free */ + p = ctx->in + count; + *p++ = 0x80; + + /* Bytes of padding needed to make 64 bytes */ + count = 64 - 1 - count; + + /* Pad out to 56 mod 64 */ + if (count < 8) { + /* Two lots of padding: Pad the first block to 64 bytes */ + memset(p, 0, count); + byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, (uint32 *)ctx->in); + + /* Now fill the next block with 56 bytes */ + memset(ctx->in, 0, 56); + } else { + /* Pad block to 56 bytes */ + memset(p, 0, count-8); + } + byteReverse(ctx->in, 14); + + /* Append length in bits and transform */ + memcpy(ctx->in + 14*4, ctx->bits, 8); + + MD5Transform(ctx->buf, (uint32 *)ctx->in); + byteReverse((unsigned char *)ctx->buf, 4); + memcpy(digest, ctx->buf, 16); +} + +/* +** Convert a 128-bit MD5 digest into a 32-digit base-16 number. +*/ +static void MD5DigestToBase16(unsigned char *digest, char *zBuf){ + static char const zEncode[] = "0123456789abcdef"; + int i, j; + + for(j=i=0; i<16; i++){ + int a = digest[i]; + zBuf[j++] = zEncode[(a>>4)&0xf]; + zBuf[j++] = zEncode[a & 0xf]; + } + zBuf[j] = 0; +} + + +/* +** Convert a 128-bit MD5 digest into sequency of eight 5-digit integers +** each representing 16 bits of the digest and separated from each +** other by a "-" character. +*/ +static void MD5DigestToBase10x8(unsigned char digest[16], char zDigest[50]){ + int i, j; + unsigned int x; + for(i=j=0; i<16; i+=2){ + x = digest[i]*256 + digest[i+1]; + if( i>0 ) zDigest[j++] = '-'; + sqlite3_snprintf(50-j, &zDigest[j], "%05u", x); + j += 5; + } + zDigest[j] = 0; +} + +/* +** A TCL command for md5. The argument is the text to be hashed. The +** Result is the hash in base64. +*/ +static int SQLITE_TCLAPI md5_cmd( + void*cd, + Tcl_Interp *interp, + int argc, + const char **argv +){ + MD5Context ctx; + unsigned char digest[16]; + char zBuf[50]; + void (*converter)(unsigned char*, char*); + + if( argc!=2 ){ + Tcl_AppendResult(interp,"wrong # args: should be \"", argv[0], + " TEXT\"", (char*)0); + return TCL_ERROR; + } + MD5Init(&ctx); + MD5Update(&ctx, (unsigned char*)argv[1], (unsigned)strlen(argv[1])); + MD5Final(digest, &ctx); + converter = (void(*)(unsigned char*,char*))cd; + converter(digest, zBuf); + Tcl_AppendResult(interp, zBuf, (char*)0); + return TCL_OK; +} + +/* +** A TCL command to take the md5 hash of a file. The argument is the +** name of the file. +*/ +static int SQLITE_TCLAPI md5file_cmd( + void*cd, + Tcl_Interp *interp, + int argc, + const char **argv +){ + FILE *in; + int ofst; + int amt; + MD5Context ctx; + void (*converter)(unsigned char*, char*); + unsigned char digest[16]; + char zBuf[10240]; + + if( argc!=2 && argc!=4 ){ + Tcl_AppendResult(interp,"wrong # args: should be \"", argv[0], + " FILENAME [OFFSET AMT]\"", (char*)0); + return TCL_ERROR; + } + if( argc==4 ){ + ofst = atoi(argv[2]); + amt = atoi(argv[3]); + }else{ + ofst = 0; + amt = 2147483647; + } + in = fopen(argv[1],"rb"); + if( in==0 ){ + Tcl_AppendResult(interp,"unable to open file \"", argv[1], + "\" for reading", (char*)0); + return TCL_ERROR; + } + fseek(in, ofst, SEEK_SET); + MD5Init(&ctx); + while( amt>0 ){ + int n; + n = (int)fread(zBuf, 1, sizeof(zBuf)<=amt ? sizeof(zBuf) : amt, in); + if( n<=0 ) break; + MD5Update(&ctx, (unsigned char*)zBuf, (unsigned)n); + amt -= n; + } + fclose(in); + MD5Final(digest, &ctx); + converter = (void(*)(unsigned char*,char*))cd; + converter(digest, zBuf); + Tcl_AppendResult(interp, zBuf, (char*)0); + return TCL_OK; +} + +/* +** Register the four new TCL commands for generating MD5 checksums +** with the TCL interpreter. +*/ +int Md5_Init(Tcl_Interp *interp){ + Tcl_CreateCommand(interp, "md5", (Tcl_CmdProc*)md5_cmd, + MD5DigestToBase16, 0); + Tcl_CreateCommand(interp, "md5-10x8", (Tcl_CmdProc*)md5_cmd, + MD5DigestToBase10x8, 0); + Tcl_CreateCommand(interp, "md5file", (Tcl_CmdProc*)md5file_cmd, + MD5DigestToBase16, 0); + Tcl_CreateCommand(interp, "md5file-10x8", (Tcl_CmdProc*)md5file_cmd, + MD5DigestToBase10x8, 0); + return TCL_OK; +} + +/* +** During testing, the special md5sum() aggregate function is available. +** inside SQLite. The following routines implement that function. +*/ +static void md5step(sqlite3_context *context, int argc, sqlite3_value **argv){ + MD5Context *p; + int i; + if( argc<1 ) return; + p = sqlite3_aggregate_context(context, sizeof(*p)); + if( p==0 ) return; + if( !p->isInit ){ + MD5Init(p); + } + for(i=0; i<argc; i++){ + const char *zData = (char*)sqlite3_value_text(argv[i]); + if( zData ){ + MD5Update(p, (unsigned char*)zData, (int)strlen(zData)); + } + } +} +static void md5finalize(sqlite3_context *context){ + MD5Context *p; + unsigned char digest[16]; + char zBuf[33]; + p = sqlite3_aggregate_context(context, sizeof(*p)); + MD5Final(digest,p); + MD5DigestToBase16(digest, zBuf); + sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT); +} +int Md5_Register( + sqlite3 *db, + char **pzErrMsg, + const sqlite3_api_routines *pThunk +){ + int rc = sqlite3_create_function(db, "md5sum", -1, SQLITE_UTF8, 0, 0, + md5step, md5finalize); + sqlite3_overload_function(db, "md5sum", -1); /* To exercise this API */ + return rc; +} diff --git a/src/test_tclsh.c b/src/test_tclsh.c new file mode 100644 index 000000000..976f7cb24 --- /dev/null +++ b/src/test_tclsh.c @@ -0,0 +1,198 @@ +/* +** 2017-10-13 +** +** 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. +** +************************************************************************* +** +** This file contains extensions to the the "tclsqlite.c" module used for +** testing. Basically, all of the other "test_*.c" modules are linked +** into the enhanced tclsh used for testing (and named "testfixture" or +** "testfixture.exe") using logic encoded by this file. +** +** The code in this file used to be found in tclsqlite3.c, contained within +** #if SQLITE_TEST ... #endif. It is factored out into this separate module +** in an effort to keep the tclsqlite.c file pure. +*/ +#include "sqlite3.h" +#if defined(INCLUDE_SQLITE_TCL_H) +# include "sqlite_tcl.h" +#else +# include "tcl.h" +# ifndef SQLITE_TCLAPI +# define SQLITE_TCLAPI +# endif +#endif + +/* Needed for the setrlimit() system call on unix */ +#if defined(unix) +#include <sys/resource.h> +#endif + +/* Forward declaration */ +static int SQLITE_TCLAPI load_testfixture_extensions( + ClientData cd, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +); + +/* +** This routine is the primary export of this file. +** +** Configure the interpreter passed as the first argument to have access +** to the commands and linked variables that make up: +** +** * the [sqlite3] extension itself, +** +** * If SQLITE_TCLMD5 or SQLITE_TEST is defined, the Md5 commands, and +** +** * If SQLITE_TEST is set, the various test interfaces used by the Tcl +** test suite. +*/ +const char *sqlite3TestInit(Tcl_Interp *interp){ + extern int Sqlite3_Init(Tcl_Interp*); + extern int Sqliteconfig_Init(Tcl_Interp*); + extern int Sqlitetest1_Init(Tcl_Interp*); + extern int Sqlitetest2_Init(Tcl_Interp*); + extern int Sqlitetest3_Init(Tcl_Interp*); + extern int Sqlitetest4_Init(Tcl_Interp*); + extern int Sqlitetest5_Init(Tcl_Interp*); + extern int Sqlitetest6_Init(Tcl_Interp*); + extern int Sqlitetest7_Init(Tcl_Interp*); + extern int Sqlitetest8_Init(Tcl_Interp*); + extern int Sqlitetest9_Init(Tcl_Interp*); + extern int Sqlitetestasync_Init(Tcl_Interp*); + extern int Sqlitetest_autoext_Init(Tcl_Interp*); + extern int Sqlitetest_blob_Init(Tcl_Interp*); + extern int Sqlitetest_demovfs_Init(Tcl_Interp *); + extern int Sqlitetest_func_Init(Tcl_Interp*); + extern int Sqlitetest_hexio_Init(Tcl_Interp*); + extern int Sqlitetest_init_Init(Tcl_Interp*); + extern int Sqlitetest_malloc_Init(Tcl_Interp*); + extern int Sqlitetest_mutex_Init(Tcl_Interp*); + extern int Sqlitetestschema_Init(Tcl_Interp*); + extern int Sqlitetestsse_Init(Tcl_Interp*); + extern int Sqlitetesttclvar_Init(Tcl_Interp*); + extern int Sqlitetestfs_Init(Tcl_Interp*); + extern int SqlitetestThread_Init(Tcl_Interp*); + extern int SqlitetestOnefile_Init(); + extern int SqlitetestOsinst_Init(Tcl_Interp*); + extern int Sqlitetestbackup_Init(Tcl_Interp*); + extern int Sqlitetestintarray_Init(Tcl_Interp*); + extern int Sqlitetestvfs_Init(Tcl_Interp *); + extern int Sqlitetestrtree_Init(Tcl_Interp*); + extern int Sqlitequota_Init(Tcl_Interp*); + extern int Sqlitemultiplex_Init(Tcl_Interp*); + extern int SqliteSuperlock_Init(Tcl_Interp*); + extern int SqlitetestSyscall_Init(Tcl_Interp*); +#if defined(SQLITE_ENABLE_SESSION) && defined(SQLITE_ENABLE_PREUPDATE_HOOK) + extern int TestSession_Init(Tcl_Interp*); +#endif + extern int Md5_Init(Tcl_Interp*); + extern int Fts5tcl_Init(Tcl_Interp *); + extern int SqliteRbu_Init(Tcl_Interp*); + extern int Sqlitetesttcl_Init(Tcl_Interp*); +#if defined(SQLITE_ENABLE_FTS3) || defined(SQLITE_ENABLE_FTS4) + extern int Sqlitetestfts3_Init(Tcl_Interp *interp); +#endif +#ifdef SQLITE_ENABLE_ZIPVFS + extern int Zipvfs_Init(Tcl_Interp*); +#endif + Tcl_CmdInfo cmdInfo; + + /* Since the primary use case for this binary is testing of SQLite, + ** be sure to generate core files if we crash */ +#if defined(unix) + { struct rlimit x; + getrlimit(RLIMIT_CORE, &x); + x.rlim_cur = x.rlim_max; + setrlimit(RLIMIT_CORE, &x); + } +#endif /* unix */ + + if( Tcl_GetCommandInfo(interp, "sqlite3", &cmdInfo)==0 ){ + Sqlite3_Init(interp); + } +#ifdef SQLITE_ENABLE_ZIPVFS + Zipvfs_Init(interp); +#endif + Md5_Init(interp); + Sqliteconfig_Init(interp); + Sqlitetest1_Init(interp); + Sqlitetest2_Init(interp); + Sqlitetest3_Init(interp); + Sqlitetest4_Init(interp); + Sqlitetest5_Init(interp); + Sqlitetest6_Init(interp); + Sqlitetest7_Init(interp); + Sqlitetest8_Init(interp); + Sqlitetest9_Init(interp); + Sqlitetestasync_Init(interp); + Sqlitetest_autoext_Init(interp); + Sqlitetest_blob_Init(interp); + Sqlitetest_demovfs_Init(interp); + Sqlitetest_func_Init(interp); + Sqlitetest_hexio_Init(interp); + Sqlitetest_init_Init(interp); + Sqlitetest_malloc_Init(interp); + Sqlitetest_mutex_Init(interp); + Sqlitetestschema_Init(interp); + Sqlitetesttclvar_Init(interp); + Sqlitetestfs_Init(interp); + SqlitetestThread_Init(interp); + SqlitetestOnefile_Init(); + SqlitetestOsinst_Init(interp); + Sqlitetestbackup_Init(interp); + Sqlitetestintarray_Init(interp); + Sqlitetestvfs_Init(interp); + Sqlitetestrtree_Init(interp); + Sqlitequota_Init(interp); + Sqlitemultiplex_Init(interp); + SqliteSuperlock_Init(interp); + SqlitetestSyscall_Init(interp); +#if defined(SQLITE_ENABLE_SESSION) && defined(SQLITE_ENABLE_PREUPDATE_HOOK) + TestSession_Init(interp); +#endif + Fts5tcl_Init(interp); + SqliteRbu_Init(interp); + Sqlitetesttcl_Init(interp); + +#if defined(SQLITE_ENABLE_FTS3) || defined(SQLITE_ENABLE_FTS4) + Sqlitetestfts3_Init(interp); +#endif + + Tcl_CreateObjCommand( + interp, "load_testfixture_extensions", load_testfixture_extensions,0,0 + ); + return 0; +} + +/* tclcmd: load_testfixture_extensions +*/ +static int SQLITE_TCLAPI load_testfixture_extensions( + ClientData cd, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + + Tcl_Interp *slave; + if( objc!=2 ){ + Tcl_WrongNumArgs(interp, 1, objv, "SLAVE"); + return TCL_ERROR; + } + + slave = Tcl_GetSlave(interp, Tcl_GetString(objv[1])); + if( !slave ){ + return TCL_ERROR; + } + + (void)sqlite3TestInit(slave); + return TCL_OK; +} diff --git a/src/where.c b/src/where.c index 5a3d44f9e..6021edf47 100644 --- a/src/where.c +++ b/src/where.c @@ -1885,18 +1885,19 @@ static void whereInfoFree(sqlite3 *db, WhereInfo *pWInfo){ ** Return TRUE if all of the following are true: ** ** (1) X has the same or lower cost that Y -** (2) X is a proper subset of Y -** (3) X skips at least as many columns as Y -** -** By "proper subset" we mean that X uses fewer WHERE clause terms -** than Y and that every WHERE clause term used by X is also used -** by Y. +** (2) X uses fewer WHERE clause terms than Y +** (3) Every WHERE clause term used by X is also used by Y +** (4) X skips at least as many columns as Y +** (5) If X is a covering index, than Y is too ** +** Conditions (2) and (3) mean that X is a "proper subset" of Y. ** If X is a proper subset of Y then Y is a better choice and ought ** to have a lower cost. This routine returns TRUE when that cost -** relationship is inverted and needs to be adjusted. The third rule +** relationship is inverted and needs to be adjusted. Constraint (4) ** was added because if X uses skip-scan less than Y it still might -** deserve a lower cost even if it is a proper subset of Y. +** deserve a lower cost even if it is a proper subset of Y. Constraint (5) +** was added because a covering index probably deserves to have a lower cost +** than a non-covering index even if it is a proper subset. */ static int whereLoopCheaperProperSubset( const WhereLoop *pX, /* First WhereLoop to compare */ @@ -1918,6 +1919,10 @@ static int whereLoopCheaperProperSubset( } if( j<0 ) return 0; /* X not a subset of Y since term X[i] not used by Y */ } + if( (pX->wsFlags&WHERE_IDX_ONLY)!=0 + && (pY->wsFlags&WHERE_IDX_ONLY)==0 ){ + return 0; /* Constraint (5) */ + } return 1; /* All conditions meet */ } |