mirror of
https://github.com/signalwire/freeswitch.git
synced 2026-07-04 19:31:56 +00:00
sync up our in tree sqlite with the 3.3.13 official release. Commit to follow to finish this process on the windows build.
git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@4351 d0543943-73ff-0310-b7d9-9358b9ac24b2
This commit is contained in:
@@ -0,0 +1 @@
|
||||
Thu Feb 22 16:53:55 EST 2007
|
||||
@@ -116,9 +116,7 @@ NAWK = @AWK@
|
||||
|
||||
# You should not have to change anything below this line
|
||||
###############################################################################
|
||||
OPTS =
|
||||
OPTS += -DSQLITE_OMIT_CURSOR # Cursors do not work at this time
|
||||
TCC += -DSQLITE_OMIT_CURSOR
|
||||
TCC += -DSQLITE_OMIT_LOAD_EXTENSION=1
|
||||
|
||||
# Object files for the SQLite library.
|
||||
#
|
||||
@@ -305,7 +303,7 @@ target_source: $(SRC) parse.c opcodes.c keywordhash.h $(VDBEHDR)
|
||||
# Rules to build the LEMON compiler generator
|
||||
#
|
||||
lemon$(BEXE): $(TOP)/tool/lemon.c $(TOP)/tool/lempar.c
|
||||
$(BCC) -o lemon $(TOP)/tool/lemon.c
|
||||
$(BCC) -o lemon$(BEXE) $(TOP)/tool/lemon.c
|
||||
cp $(TOP)/tool/lempar.c .
|
||||
|
||||
|
||||
@@ -393,7 +391,7 @@ parse.h: parse.c
|
||||
|
||||
parse.c: $(TOP)/src/parse.y lemon$(BEXE) $(TOP)/addopcodes.awk
|
||||
cp $(TOP)/src/parse.y .
|
||||
./lemon $(OPTS) parse.y
|
||||
./lemon$(BEXE) $(OPTS) parse.y
|
||||
mv parse.h parse.h.temp
|
||||
awk -f $(TOP)/addopcodes.awk parse.h.temp >parse.h
|
||||
|
||||
@@ -667,6 +665,7 @@ install: sqlite3 libsqlite3.la sqlite3.h ${HAVE_TCL:1=tcl_install}
|
||||
$(LTINSTALL) sqlite3 $(DESTDIR)$(exec_prefix)/bin
|
||||
$(INSTALL) -d $(DESTDIR)$(prefix)/include
|
||||
$(INSTALL) -m 0644 sqlite3.h $(DESTDIR)$(prefix)/include
|
||||
$(INSTALL) -m 0644 $(TOP)/src/sqlite3ext.h $(DESTDIR)$(prefix)/include
|
||||
$(INSTALL) -d $(DESTDIR)$(libdir)/pkgconfig;
|
||||
$(INSTALL) -m 0644 sqlite3.pc $(DESTDIR)$(libdir)/pkgconfig;
|
||||
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
3.3.8
|
||||
3.3.13
|
||||
|
||||
+56
-37
@@ -50,14 +50,14 @@ typedef struct StringBuffer {
|
||||
char *s; /* Content of the string */
|
||||
} StringBuffer;
|
||||
|
||||
void initStringBuffer(StringBuffer *sb){
|
||||
static void initStringBuffer(StringBuffer *sb){
|
||||
sb->len = 0;
|
||||
sb->alloced = 100;
|
||||
sb->s = malloc(100);
|
||||
sb->s[0] = '\0';
|
||||
}
|
||||
|
||||
void nappend(StringBuffer *sb, const char *zFrom, int nFrom){
|
||||
static void nappend(StringBuffer *sb, const char *zFrom, int nFrom){
|
||||
if( sb->len + nFrom >= sb->alloced ){
|
||||
sb->alloced = sb->len + nFrom + 100;
|
||||
sb->s = realloc(sb->s, sb->alloced+1);
|
||||
@@ -70,7 +70,7 @@ void nappend(StringBuffer *sb, const char *zFrom, int nFrom){
|
||||
sb->len += nFrom;
|
||||
sb->s[sb->len] = 0;
|
||||
}
|
||||
void append(StringBuffer *sb, const char *zFrom){
|
||||
static void append(StringBuffer *sb, const char *zFrom){
|
||||
nappend(sb, zFrom, strlen(zFrom));
|
||||
}
|
||||
|
||||
@@ -847,25 +847,31 @@ static char *string_dup(const char *s){
|
||||
}
|
||||
|
||||
/* Format a string, replacing each occurrence of the % character with
|
||||
* zName. This may be more convenient than sqlite_mprintf()
|
||||
* zDb.zName. This may be more convenient than sqlite_mprintf()
|
||||
* when one string is used repeatedly in a format string.
|
||||
* The caller must free() the returned string. */
|
||||
static char *string_format(const char *zFormat, const char *zName){
|
||||
static char *string_format(const char *zFormat,
|
||||
const char *zDb, const char *zName){
|
||||
const char *p;
|
||||
size_t len = 0;
|
||||
size_t nDb = strlen(zDb);
|
||||
size_t nName = strlen(zName);
|
||||
size_t nFullTableName = nDb+1+nName;
|
||||
char *result;
|
||||
char *r;
|
||||
|
||||
/* first compute length needed */
|
||||
for(p = zFormat ; *p ; ++p){
|
||||
len += (*p=='%' ? nName : 1);
|
||||
len += (*p=='%' ? nFullTableName : 1);
|
||||
}
|
||||
len += 1; /* for null terminator */
|
||||
|
||||
r = result = malloc(len);
|
||||
for(p = zFormat; *p; ++p){
|
||||
if( *p=='%' ){
|
||||
memcpy(r, zDb, nDb);
|
||||
r += nDb;
|
||||
*r++ = '.';
|
||||
memcpy(r, zName, nName);
|
||||
r += nName;
|
||||
} else {
|
||||
@@ -877,8 +883,9 @@ static char *string_format(const char *zFormat, const char *zName){
|
||||
return result;
|
||||
}
|
||||
|
||||
static int sql_exec(sqlite3 *db, const char *zName, const char *zFormat){
|
||||
char *zCommand = string_format(zFormat, zName);
|
||||
static int sql_exec(sqlite3 *db, const char *zDb, const char *zName,
|
||||
const char *zFormat){
|
||||
char *zCommand = string_format(zFormat, zDb, zName);
|
||||
int rc;
|
||||
TRACE(("FTS1 sql: %s\n", zCommand));
|
||||
rc = sqlite3_exec(db, zCommand, NULL, 0, NULL);
|
||||
@@ -886,9 +893,9 @@ static int sql_exec(sqlite3 *db, const char *zName, const char *zFormat){
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int sql_prepare(sqlite3 *db, const char *zName, sqlite3_stmt **ppStmt,
|
||||
const char *zFormat){
|
||||
char *zCommand = string_format(zFormat, zName);
|
||||
static int sql_prepare(sqlite3 *db, const char *zDb, const char *zName,
|
||||
sqlite3_stmt **ppStmt, const char *zFormat){
|
||||
char *zCommand = string_format(zFormat, zDb, zName);
|
||||
int rc;
|
||||
TRACE(("FTS1 prepare: %s\n", zCommand));
|
||||
rc = sqlite3_prepare(db, zCommand, -1, ppStmt, NULL);
|
||||
@@ -1040,6 +1047,7 @@ static const char *const fulltext_zStatement[MAX_STMT] = {
|
||||
struct fulltext_vtab {
|
||||
sqlite3_vtab base; /* Base class used by SQLite core */
|
||||
sqlite3 *db; /* The database connection */
|
||||
const char *zDb; /* logical database name */
|
||||
const char *zName; /* virtual table name */
|
||||
int nColumn; /* number of columns in virtual table */
|
||||
char **azColumn; /* column names. malloced */
|
||||
@@ -1139,7 +1147,7 @@ static int sql_get_statement(fulltext_vtab *v, fulltext_statement iStmt,
|
||||
default:
|
||||
zStmt = fulltext_zStatement[iStmt];
|
||||
}
|
||||
rc = sql_prepare(v->db, v->zName, &v->pFulltextStatements[iStmt],
|
||||
rc = sql_prepare(v->db, v->zDb, v->zName, &v->pFulltextStatements[iStmt],
|
||||
zStmt);
|
||||
if( zStmt != fulltext_zStatement[iStmt]) free((void *) zStmt);
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
@@ -1242,7 +1250,7 @@ static int content_update(fulltext_vtab *v, sqlite3_value **pValues,
|
||||
return sql_single_step_statement(v, CONTENT_UPDATE_STMT, &s);
|
||||
}
|
||||
|
||||
void freeStringArray(int nString, const char **pString){
|
||||
static void freeStringArray(int nString, const char **pString){
|
||||
int i;
|
||||
|
||||
for (i=0 ; i < nString ; ++i) {
|
||||
@@ -1634,7 +1642,7 @@ static char **tokenizeString(const char *z, int *pnToken){
|
||||
** [pqr] becomes pqr
|
||||
** `mno` becomes mno
|
||||
*/
|
||||
void dequoteString(char *z){
|
||||
static void dequoteString(char *z){
|
||||
int quote;
|
||||
int i, j;
|
||||
if( z==0 ) return;
|
||||
@@ -1676,7 +1684,7 @@ void dequoteString(char *z){
|
||||
** input: delimiters ( '[' , ']' , '...' )
|
||||
** output: [ ] ...
|
||||
*/
|
||||
void tokenListToIdList(char **azIn){
|
||||
static void tokenListToIdList(char **azIn){
|
||||
int i, j;
|
||||
if( azIn ){
|
||||
for(i=0, j=-1; azIn[i]; i++){
|
||||
@@ -1699,8 +1707,7 @@ void tokenListToIdList(char **azIn){
|
||||
** the result.
|
||||
*/
|
||||
static char *firstToken(char *zIn, char **pzTail){
|
||||
int i, n, ttype;
|
||||
i = 0;
|
||||
int n, ttype;
|
||||
while(1){
|
||||
n = getToken(zIn, &ttype);
|
||||
if( ttype==TOKEN_SPACE ){
|
||||
@@ -1743,6 +1750,7 @@ static int startsWith(const char *s, const char *t){
|
||||
** and use by fulltextConnect and fulltextCreate.
|
||||
*/
|
||||
typedef struct TableSpec {
|
||||
const char *zDb; /* Logical database name */
|
||||
const char *zName; /* Name of the full-text index */
|
||||
int nColumn; /* Number of columns to be indexed */
|
||||
char **azColumn; /* Original names of columns to be indexed */
|
||||
@@ -1753,7 +1761,7 @@ typedef struct TableSpec {
|
||||
/*
|
||||
** Reclaim all of the memory used by a TableSpec
|
||||
*/
|
||||
void clearTableSpec(TableSpec *p) {
|
||||
static void clearTableSpec(TableSpec *p) {
|
||||
free(p->azColumn);
|
||||
free(p->azContentColumn);
|
||||
free(p->azTokenizer);
|
||||
@@ -1767,8 +1775,9 @@ void clearTableSpec(TableSpec *p) {
|
||||
* We return parsed information in a TableSpec structure.
|
||||
*
|
||||
*/
|
||||
int parseSpec(TableSpec *pSpec, int argc, const char *const*argv, char**pzErr){
|
||||
int i, j, n;
|
||||
static int parseSpec(TableSpec *pSpec, int argc, const char *const*argv,
|
||||
char**pzErr){
|
||||
int i, n;
|
||||
char *z, *zDummy;
|
||||
char **azArg;
|
||||
const char *zTokenizer = 0; /* argv[] entry describing the tokenizer */
|
||||
@@ -1804,11 +1813,12 @@ int parseSpec(TableSpec *pSpec, int argc, const char *const*argv, char**pzErr){
|
||||
/* Identify the column names and the tokenizer and delimiter arguments
|
||||
** in the argv[][] array.
|
||||
*/
|
||||
pSpec->zDb = azArg[1];
|
||||
pSpec->zName = azArg[2];
|
||||
pSpec->nColumn = 0;
|
||||
pSpec->azColumn = azArg;
|
||||
zTokenizer = "tokenize simple";
|
||||
for(i=3, j=0; i<argc; ++i){
|
||||
for(i=3; i<argc; ++i){
|
||||
if( startsWith(azArg[i],"tokenize") ){
|
||||
zTokenizer = azArg[i];
|
||||
}else{
|
||||
@@ -1904,6 +1914,7 @@ static int constructVtab(
|
||||
memset(v, 0, sizeof(*v));
|
||||
/* sqlite will initialize v->base */
|
||||
v->db = db;
|
||||
v->zDb = spec->zDb; /* Freed when azColumn is freed */
|
||||
v->zName = spec->zName; /* Freed when azColumn is freed */
|
||||
v->nColumn = spec->nColumn;
|
||||
v->azContentColumn = spec->azContentColumn;
|
||||
@@ -2020,11 +2031,11 @@ static int fulltextCreate(sqlite3 *db, void *pAux,
|
||||
append(&schema, "CREATE TABLE %_content(");
|
||||
appendList(&schema, spec.nColumn, spec.azContentColumn);
|
||||
append(&schema, ")");
|
||||
rc = sql_exec(db, spec.zName, schema.s);
|
||||
rc = sql_exec(db, spec.zDb, spec.zName, schema.s);
|
||||
free(schema.s);
|
||||
if( rc!=SQLITE_OK ) goto out;
|
||||
|
||||
rc = sql_exec(db, spec.zName,
|
||||
rc = sql_exec(db, spec.zDb, spec.zName,
|
||||
"create table %_term(term text, segment integer, doclist blob, "
|
||||
"primary key(term, segment));");
|
||||
if( rc!=SQLITE_OK ) goto out;
|
||||
@@ -2039,6 +2050,7 @@ out:
|
||||
/* Decide how to handle an SQL query. */
|
||||
static int fulltextBestIndex(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){
|
||||
int i;
|
||||
TRACE(("FTS1 BestIndex\n"));
|
||||
|
||||
for(i=0; i<pInfo->nConstraint; ++i){
|
||||
const struct sqlite3_index_constraint *pConstraint;
|
||||
@@ -2047,10 +2059,12 @@ static int fulltextBestIndex(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){
|
||||
if( pConstraint->iColumn==-1 &&
|
||||
pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ ){
|
||||
pInfo->idxNum = QUERY_ROWID; /* lookup by rowid */
|
||||
TRACE(("FTS1 QUERY_ROWID\n"));
|
||||
} else if( pConstraint->iColumn>=0 &&
|
||||
pConstraint->op==SQLITE_INDEX_CONSTRAINT_MATCH ){
|
||||
/* full-text search */
|
||||
pInfo->idxNum = QUERY_FULLTEXT + pConstraint->iColumn;
|
||||
TRACE(("FTS1 QUERY_FULLTEXT %d\n", pConstraint->iColumn));
|
||||
} else continue;
|
||||
|
||||
pInfo->aConstraintUsage[i].argvIndex = 1;
|
||||
@@ -2065,7 +2079,6 @@ static int fulltextBestIndex(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){
|
||||
}
|
||||
}
|
||||
pInfo->idxNum = QUERY_GENERIC;
|
||||
TRACE(("FTS1 BestIndex\n"));
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
@@ -2080,8 +2093,10 @@ static int fulltextDestroy(sqlite3_vtab *pVTab){
|
||||
int rc;
|
||||
|
||||
TRACE(("FTS1 Destroy %p\n", pVTab));
|
||||
rc = sql_exec(v->db, v->zName,
|
||||
"drop table %_content; drop table %_term");
|
||||
rc = sql_exec(v->db, v->zDb, v->zName,
|
||||
"drop table if exists %_content;"
|
||||
"drop table if exists %_term;"
|
||||
);
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
|
||||
fulltext_vtab_destroy((fulltext_vtab *)pVTab);
|
||||
@@ -2815,6 +2830,11 @@ static int fulltextQuery(
|
||||
** number idxNum-QUERY_FULLTEXT, 0 indexed. argv[0] is the right-hand
|
||||
** side of the MATCH operator.
|
||||
*/
|
||||
/* TODO(shess) Upgrade the cursor initialization and destruction to
|
||||
** account for fulltextFilter() being called multiple times on the
|
||||
** same cursor. The current solution is very fragile. Apply fix to
|
||||
** fts2 as appropriate.
|
||||
*/
|
||||
static int fulltextFilter(
|
||||
sqlite3_vtab_cursor *pCursor, /* The cursor used for this query */
|
||||
int idxNum, const char *idxStr, /* Which indexing scheme to use */
|
||||
@@ -2829,9 +2849,10 @@ static int fulltextFilter(
|
||||
|
||||
zSql = sqlite3_mprintf("select rowid, * from %%_content %s",
|
||||
idxNum==QUERY_GENERIC ? "" : "where rowid=?");
|
||||
rc = sql_prepare(v->db, v->zName, &c->pStmt, zSql);
|
||||
sqlite3_finalize(c->pStmt);
|
||||
rc = sql_prepare(v->db, v->zDb, v->zName, &c->pStmt, zSql);
|
||||
sqlite3_free(zSql);
|
||||
if( rc!=SQLITE_OK ) goto out;
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
|
||||
c->iCursorType = idxNum;
|
||||
switch( idxNum ){
|
||||
@@ -2840,7 +2861,7 @@ static int fulltextFilter(
|
||||
|
||||
case QUERY_ROWID:
|
||||
rc = sqlite3_bind_int64(c->pStmt, 1, sqlite3_value_int64(argv[0]));
|
||||
if( rc!=SQLITE_OK ) goto out;
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
break;
|
||||
|
||||
default: /* full-text search */
|
||||
@@ -2851,16 +2872,14 @@ static int fulltextFilter(
|
||||
assert( argc==1 );
|
||||
queryClear(&c->q);
|
||||
rc = fulltextQuery(v, idxNum-QUERY_FULLTEXT, zQuery, -1, &pResult, &c->q);
|
||||
if( rc!=SQLITE_OK ) goto out;
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
if( c->result.pDoclist!=NULL ) docListDelete(c->result.pDoclist);
|
||||
readerInit(&c->result, pResult);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
rc = fulltextNext(pCursor);
|
||||
|
||||
out:
|
||||
return rc;
|
||||
return fulltextNext(pCursor);
|
||||
}
|
||||
|
||||
/* This is the xEof method of the virtual table. The SQLite core
|
||||
@@ -3081,11 +3100,11 @@ static int index_update(fulltext_vtab *v, sqlite_int64 iRow,
|
||||
int rc = deleteTerms(v, pTerms, iRow);
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
|
||||
/* Now add positions for terms which appear in the updated row. */
|
||||
rc = insertTerms(v, pTerms, iRow, pValues);
|
||||
rc = content_update(v, pValues, iRow); /* execute an SQL UPDATE */
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
|
||||
return content_update(v, pValues, iRow); /* execute an SQL UPDATE */
|
||||
/* Now add positions for terms which appear in the updated row. */
|
||||
return insertTerms(v, pTerms, iRow, pValues);
|
||||
}
|
||||
|
||||
/* This function implements the xUpdate callback; it's the top-level entry
|
||||
|
||||
@@ -70,9 +70,6 @@ static int porterCreate(
|
||||
sqlite3_tokenizer **ppTokenizer
|
||||
){
|
||||
porter_tokenizer *t;
|
||||
int i;
|
||||
|
||||
for(i=0; i<argc; i++) printf("argv[%d] = %s\n", i, argv[i]);
|
||||
t = (porter_tokenizer *) calloc(sizeof(porter_tokenizer), 1);
|
||||
*ppTokenizer = &t->base;
|
||||
return SQLITE_OK;
|
||||
@@ -563,7 +560,7 @@ static void porter_stemmer(const char *zIn, int nIn, char *zOut, int *pnOut){
|
||||
** part of a token. In other words, delimiters all must have
|
||||
** values of 0x7f or lower.
|
||||
*/
|
||||
const char isIdChar[] = {
|
||||
static const char isIdChar[] = {
|
||||
/* x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC xD xE xF */
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, /* 3x */
|
||||
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 4x */
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
This folder contains source code to the second full-text search
|
||||
extension for SQLite. While the API is the same, this version uses a
|
||||
substantially different storage schema from fts1, so tables will need
|
||||
to be rebuilt.
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,11 @@
|
||||
#include "sqlite3.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
int sqlite3Fts2Init(sqlite3 *db);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif /* __cplusplus */
|
||||
@@ -0,0 +1,369 @@
|
||||
/*
|
||||
** 2001 September 22
|
||||
**
|
||||
** 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 is the implementation of generic hash-tables used in SQLite.
|
||||
** We've modified it slightly to serve as a standalone hash table
|
||||
** implementation for the full-text indexing module.
|
||||
*/
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/*
|
||||
** The code in this file is only compiled if:
|
||||
**
|
||||
** * The FTS2 module is being built as an extension
|
||||
** (in which case SQLITE_CORE is not defined), or
|
||||
**
|
||||
** * The FTS2 module is being built into the core of
|
||||
** SQLite (in which case SQLITE_ENABLE_FTS2 is defined).
|
||||
*/
|
||||
#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS2)
|
||||
|
||||
|
||||
#include "fts2_hash.h"
|
||||
|
||||
static void *malloc_and_zero(int n){
|
||||
void *p = malloc(n);
|
||||
if( p ){
|
||||
memset(p, 0, n);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
/* Turn bulk memory into a hash table object by initializing the
|
||||
** fields of the Hash structure.
|
||||
**
|
||||
** "pNew" is a pointer to the hash table that is to be initialized.
|
||||
** keyClass is one of the constants
|
||||
** FTS2_HASH_BINARY or FTS2_HASH_STRING. The value of keyClass
|
||||
** determines what kind of key the hash table will use. "copyKey" is
|
||||
** true if the hash table should make its own private copy of keys and
|
||||
** false if it should just use the supplied pointer.
|
||||
*/
|
||||
void sqlite3Fts2HashInit(fts2Hash *pNew, int keyClass, int copyKey){
|
||||
assert( pNew!=0 );
|
||||
assert( keyClass>=FTS2_HASH_STRING && keyClass<=FTS2_HASH_BINARY );
|
||||
pNew->keyClass = keyClass;
|
||||
pNew->copyKey = copyKey;
|
||||
pNew->first = 0;
|
||||
pNew->count = 0;
|
||||
pNew->htsize = 0;
|
||||
pNew->ht = 0;
|
||||
pNew->xMalloc = malloc_and_zero;
|
||||
pNew->xFree = free;
|
||||
}
|
||||
|
||||
/* Remove all entries from a hash table. Reclaim all memory.
|
||||
** Call this routine to delete a hash table or to reset a hash table
|
||||
** to the empty state.
|
||||
*/
|
||||
void sqlite3Fts2HashClear(fts2Hash *pH){
|
||||
fts2HashElem *elem; /* For looping over all elements of the table */
|
||||
|
||||
assert( pH!=0 );
|
||||
elem = pH->first;
|
||||
pH->first = 0;
|
||||
if( pH->ht ) pH->xFree(pH->ht);
|
||||
pH->ht = 0;
|
||||
pH->htsize = 0;
|
||||
while( elem ){
|
||||
fts2HashElem *next_elem = elem->next;
|
||||
if( pH->copyKey && elem->pKey ){
|
||||
pH->xFree(elem->pKey);
|
||||
}
|
||||
pH->xFree(elem);
|
||||
elem = next_elem;
|
||||
}
|
||||
pH->count = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Hash and comparison functions when the mode is FTS2_HASH_STRING
|
||||
*/
|
||||
static int strHash(const void *pKey, int nKey){
|
||||
const char *z = (const char *)pKey;
|
||||
int h = 0;
|
||||
if( nKey<=0 ) nKey = (int) strlen(z);
|
||||
while( nKey > 0 ){
|
||||
h = (h<<3) ^ h ^ *z++;
|
||||
nKey--;
|
||||
}
|
||||
return h & 0x7fffffff;
|
||||
}
|
||||
static int strCompare(const void *pKey1, int n1, const void *pKey2, int n2){
|
||||
if( n1!=n2 ) return 1;
|
||||
return strncmp((const char*)pKey1,(const char*)pKey2,n1);
|
||||
}
|
||||
|
||||
/*
|
||||
** Hash and comparison functions when the mode is FTS2_HASH_BINARY
|
||||
*/
|
||||
static int binHash(const void *pKey, int nKey){
|
||||
int h = 0;
|
||||
const char *z = (const char *)pKey;
|
||||
while( nKey-- > 0 ){
|
||||
h = (h<<3) ^ h ^ *(z++);
|
||||
}
|
||||
return h & 0x7fffffff;
|
||||
}
|
||||
static int binCompare(const void *pKey1, int n1, const void *pKey2, int n2){
|
||||
if( n1!=n2 ) return 1;
|
||||
return memcmp(pKey1,pKey2,n1);
|
||||
}
|
||||
|
||||
/*
|
||||
** Return a pointer to the appropriate hash function given the key class.
|
||||
**
|
||||
** The C syntax in this function definition may be unfamilar to some
|
||||
** programmers, so we provide the following additional explanation:
|
||||
**
|
||||
** The name of the function is "hashFunction". The function takes a
|
||||
** single parameter "keyClass". The return value of hashFunction()
|
||||
** is a pointer to another function. Specifically, the return value
|
||||
** of hashFunction() is a pointer to a function that takes two parameters
|
||||
** with types "const void*" and "int" and returns an "int".
|
||||
*/
|
||||
static int (*hashFunction(int keyClass))(const void*,int){
|
||||
if( keyClass==FTS2_HASH_STRING ){
|
||||
return &strHash;
|
||||
}else{
|
||||
assert( keyClass==FTS2_HASH_BINARY );
|
||||
return &binHash;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Return a pointer to the appropriate hash function given the key class.
|
||||
**
|
||||
** For help in interpreted the obscure C code in the function definition,
|
||||
** see the header comment on the previous function.
|
||||
*/
|
||||
static int (*compareFunction(int keyClass))(const void*,int,const void*,int){
|
||||
if( keyClass==FTS2_HASH_STRING ){
|
||||
return &strCompare;
|
||||
}else{
|
||||
assert( keyClass==FTS2_HASH_BINARY );
|
||||
return &binCompare;
|
||||
}
|
||||
}
|
||||
|
||||
/* Link an element into the hash table
|
||||
*/
|
||||
static void insertElement(
|
||||
fts2Hash *pH, /* The complete hash table */
|
||||
struct _fts2ht *pEntry, /* The entry into which pNew is inserted */
|
||||
fts2HashElem *pNew /* The element to be inserted */
|
||||
){
|
||||
fts2HashElem *pHead; /* First element already in pEntry */
|
||||
pHead = pEntry->chain;
|
||||
if( pHead ){
|
||||
pNew->next = pHead;
|
||||
pNew->prev = pHead->prev;
|
||||
if( pHead->prev ){ pHead->prev->next = pNew; }
|
||||
else { pH->first = pNew; }
|
||||
pHead->prev = pNew;
|
||||
}else{
|
||||
pNew->next = pH->first;
|
||||
if( pH->first ){ pH->first->prev = pNew; }
|
||||
pNew->prev = 0;
|
||||
pH->first = pNew;
|
||||
}
|
||||
pEntry->count++;
|
||||
pEntry->chain = pNew;
|
||||
}
|
||||
|
||||
|
||||
/* Resize the hash table so that it cantains "new_size" buckets.
|
||||
** "new_size" must be a power of 2. The hash table might fail
|
||||
** to resize if sqliteMalloc() fails.
|
||||
*/
|
||||
static void rehash(fts2Hash *pH, int new_size){
|
||||
struct _fts2ht *new_ht; /* The new hash table */
|
||||
fts2HashElem *elem, *next_elem; /* For looping over existing elements */
|
||||
int (*xHash)(const void*,int); /* The hash function */
|
||||
|
||||
assert( (new_size & (new_size-1))==0 );
|
||||
new_ht = (struct _fts2ht *)pH->xMalloc( new_size*sizeof(struct _fts2ht) );
|
||||
if( new_ht==0 ) return;
|
||||
if( pH->ht ) pH->xFree(pH->ht);
|
||||
pH->ht = new_ht;
|
||||
pH->htsize = new_size;
|
||||
xHash = hashFunction(pH->keyClass);
|
||||
for(elem=pH->first, pH->first=0; elem; elem = next_elem){
|
||||
int h = (*xHash)(elem->pKey, elem->nKey) & (new_size-1);
|
||||
next_elem = elem->next;
|
||||
insertElement(pH, &new_ht[h], elem);
|
||||
}
|
||||
}
|
||||
|
||||
/* This function (for internal use only) locates an element in an
|
||||
** hash table that matches the given key. The hash for this key has
|
||||
** already been computed and is passed as the 4th parameter.
|
||||
*/
|
||||
static fts2HashElem *findElementGivenHash(
|
||||
const fts2Hash *pH, /* The pH to be searched */
|
||||
const void *pKey, /* The key we are searching for */
|
||||
int nKey,
|
||||
int h /* The hash for this key. */
|
||||
){
|
||||
fts2HashElem *elem; /* Used to loop thru the element list */
|
||||
int count; /* Number of elements left to test */
|
||||
int (*xCompare)(const void*,int,const void*,int); /* comparison function */
|
||||
|
||||
if( pH->ht ){
|
||||
struct _fts2ht *pEntry = &pH->ht[h];
|
||||
elem = pEntry->chain;
|
||||
count = pEntry->count;
|
||||
xCompare = compareFunction(pH->keyClass);
|
||||
while( count-- && elem ){
|
||||
if( (*xCompare)(elem->pKey,elem->nKey,pKey,nKey)==0 ){
|
||||
return elem;
|
||||
}
|
||||
elem = elem->next;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Remove a single entry from the hash table given a pointer to that
|
||||
** element and a hash on the element's key.
|
||||
*/
|
||||
static void removeElementGivenHash(
|
||||
fts2Hash *pH, /* The pH containing "elem" */
|
||||
fts2HashElem* elem, /* The element to be removed from the pH */
|
||||
int h /* Hash value for the element */
|
||||
){
|
||||
struct _fts2ht *pEntry;
|
||||
if( elem->prev ){
|
||||
elem->prev->next = elem->next;
|
||||
}else{
|
||||
pH->first = elem->next;
|
||||
}
|
||||
if( elem->next ){
|
||||
elem->next->prev = elem->prev;
|
||||
}
|
||||
pEntry = &pH->ht[h];
|
||||
if( pEntry->chain==elem ){
|
||||
pEntry->chain = elem->next;
|
||||
}
|
||||
pEntry->count--;
|
||||
if( pEntry->count<=0 ){
|
||||
pEntry->chain = 0;
|
||||
}
|
||||
if( pH->copyKey && elem->pKey ){
|
||||
pH->xFree(elem->pKey);
|
||||
}
|
||||
pH->xFree( elem );
|
||||
pH->count--;
|
||||
if( pH->count<=0 ){
|
||||
assert( pH->first==0 );
|
||||
assert( pH->count==0 );
|
||||
fts2HashClear(pH);
|
||||
}
|
||||
}
|
||||
|
||||
/* Attempt to locate an element of the hash table pH with a key
|
||||
** that matches pKey,nKey. Return the data for this element if it is
|
||||
** found, or NULL if there is no match.
|
||||
*/
|
||||
void *sqlite3Fts2HashFind(const fts2Hash *pH, const void *pKey, int nKey){
|
||||
int h; /* A hash on key */
|
||||
fts2HashElem *elem; /* The element that matches key */
|
||||
int (*xHash)(const void*,int); /* The hash function */
|
||||
|
||||
if( pH==0 || pH->ht==0 ) return 0;
|
||||
xHash = hashFunction(pH->keyClass);
|
||||
assert( xHash!=0 );
|
||||
h = (*xHash)(pKey,nKey);
|
||||
assert( (pH->htsize & (pH->htsize-1))==0 );
|
||||
elem = findElementGivenHash(pH,pKey,nKey, h & (pH->htsize-1));
|
||||
return elem ? elem->data : 0;
|
||||
}
|
||||
|
||||
/* Insert an element into the hash table pH. The key is pKey,nKey
|
||||
** and the data is "data".
|
||||
**
|
||||
** If no element exists with a matching key, then a new
|
||||
** element is created. A copy of the key is made if the copyKey
|
||||
** flag is set. NULL is returned.
|
||||
**
|
||||
** If another element already exists with the same key, then the
|
||||
** new data replaces the old data and the old data is returned.
|
||||
** The key is not copied in this instance. If a malloc fails, then
|
||||
** the new data is returned and the hash table is unchanged.
|
||||
**
|
||||
** If the "data" parameter to this function is NULL, then the
|
||||
** element corresponding to "key" is removed from the hash table.
|
||||
*/
|
||||
void *sqlite3Fts2HashInsert(
|
||||
fts2Hash *pH, /* The hash table to insert into */
|
||||
const void *pKey, /* The key */
|
||||
int nKey, /* Number of bytes in the key */
|
||||
void *data /* The data */
|
||||
){
|
||||
int hraw; /* Raw hash value of the key */
|
||||
int h; /* the hash of the key modulo hash table size */
|
||||
fts2HashElem *elem; /* Used to loop thru the element list */
|
||||
fts2HashElem *new_elem; /* New element added to the pH */
|
||||
int (*xHash)(const void*,int); /* The hash function */
|
||||
|
||||
assert( pH!=0 );
|
||||
xHash = hashFunction(pH->keyClass);
|
||||
assert( xHash!=0 );
|
||||
hraw = (*xHash)(pKey, nKey);
|
||||
assert( (pH->htsize & (pH->htsize-1))==0 );
|
||||
h = hraw & (pH->htsize-1);
|
||||
elem = findElementGivenHash(pH,pKey,nKey,h);
|
||||
if( elem ){
|
||||
void *old_data = elem->data;
|
||||
if( data==0 ){
|
||||
removeElementGivenHash(pH,elem,h);
|
||||
}else{
|
||||
elem->data = data;
|
||||
}
|
||||
return old_data;
|
||||
}
|
||||
if( data==0 ) return 0;
|
||||
new_elem = (fts2HashElem*)pH->xMalloc( sizeof(fts2HashElem) );
|
||||
if( new_elem==0 ) return data;
|
||||
if( pH->copyKey && pKey!=0 ){
|
||||
new_elem->pKey = pH->xMalloc( nKey );
|
||||
if( new_elem->pKey==0 ){
|
||||
pH->xFree(new_elem);
|
||||
return data;
|
||||
}
|
||||
memcpy((void*)new_elem->pKey, pKey, nKey);
|
||||
}else{
|
||||
new_elem->pKey = (void*)pKey;
|
||||
}
|
||||
new_elem->nKey = nKey;
|
||||
pH->count++;
|
||||
if( pH->htsize==0 ){
|
||||
rehash(pH,8);
|
||||
if( pH->htsize==0 ){
|
||||
pH->count = 0;
|
||||
pH->xFree(new_elem);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
if( pH->count > pH->htsize ){
|
||||
rehash(pH,pH->htsize*2);
|
||||
}
|
||||
assert( pH->htsize>0 );
|
||||
assert( (pH->htsize & (pH->htsize-1))==0 );
|
||||
h = hraw & (pH->htsize-1);
|
||||
insertElement(pH, &pH->ht[h], new_elem);
|
||||
new_elem->data = data;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS2) */
|
||||
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
** 2001 September 22
|
||||
**
|
||||
** 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 is the header file for the generic hash-table implemenation
|
||||
** used in SQLite. We've modified it slightly to serve as a standalone
|
||||
** hash table implementation for the full-text indexing module.
|
||||
**
|
||||
*/
|
||||
#ifndef _FTS2_HASH_H_
|
||||
#define _FTS2_HASH_H_
|
||||
|
||||
/* Forward declarations of structures. */
|
||||
typedef struct fts2Hash fts2Hash;
|
||||
typedef struct fts2HashElem fts2HashElem;
|
||||
|
||||
/* A complete hash table is an instance of the following structure.
|
||||
** The internals of this structure are intended to be opaque -- client
|
||||
** code should not attempt to access or modify the fields of this structure
|
||||
** directly. Change this structure only by using the routines below.
|
||||
** However, many of the "procedures" and "functions" for modifying and
|
||||
** accessing this structure are really macros, so we can't really make
|
||||
** this structure opaque.
|
||||
*/
|
||||
struct fts2Hash {
|
||||
char keyClass; /* HASH_INT, _POINTER, _STRING, _BINARY */
|
||||
char copyKey; /* True if copy of key made on insert */
|
||||
int count; /* Number of entries in this table */
|
||||
fts2HashElem *first; /* The first element of the array */
|
||||
void *(*xMalloc)(int); /* malloc() function to use */
|
||||
void (*xFree)(void *); /* free() function to use */
|
||||
int htsize; /* Number of buckets in the hash table */
|
||||
struct _fts2ht { /* the hash table */
|
||||
int count; /* Number of entries with this hash */
|
||||
fts2HashElem *chain; /* Pointer to first entry with this hash */
|
||||
} *ht;
|
||||
};
|
||||
|
||||
/* Each element in the hash table is an instance of the following
|
||||
** structure. All elements are stored on a single doubly-linked list.
|
||||
**
|
||||
** Again, this structure is intended to be opaque, but it can't really
|
||||
** be opaque because it is used by macros.
|
||||
*/
|
||||
struct fts2HashElem {
|
||||
fts2HashElem *next, *prev; /* Next and previous elements in the table */
|
||||
void *data; /* Data associated with this element */
|
||||
void *pKey; int nKey; /* Key associated with this element */
|
||||
};
|
||||
|
||||
/*
|
||||
** There are 2 different modes of operation for a hash table:
|
||||
**
|
||||
** FTS2_HASH_STRING pKey points to a string that is nKey bytes long
|
||||
** (including the null-terminator, if any). Case
|
||||
** is respected in comparisons.
|
||||
**
|
||||
** FTS2_HASH_BINARY pKey points to binary data nKey bytes long.
|
||||
** memcmp() is used to compare keys.
|
||||
**
|
||||
** A copy of the key is made if the copyKey parameter to fts2HashInit is 1.
|
||||
*/
|
||||
#define FTS2_HASH_STRING 1
|
||||
#define FTS2_HASH_BINARY 2
|
||||
|
||||
/*
|
||||
** Access routines. To delete, insert a NULL pointer.
|
||||
*/
|
||||
void sqlite3Fts2HashInit(fts2Hash*, int keytype, int copyKey);
|
||||
void *sqlite3Fts2HashInsert(fts2Hash*, const void *pKey, int nKey, void *pData);
|
||||
void *sqlite3Fts2HashFind(const fts2Hash*, const void *pKey, int nKey);
|
||||
void sqlite3Fts2HashClear(fts2Hash*);
|
||||
|
||||
/*
|
||||
** Shorthand for the functions above
|
||||
*/
|
||||
#define fts2HashInit sqlite3Fts2HashInit
|
||||
#define fts2HashInsert sqlite3Fts2HashInsert
|
||||
#define fts2HashFind sqlite3Fts2HashFind
|
||||
#define fts2HashClear sqlite3Fts2HashClear
|
||||
|
||||
/*
|
||||
** Macros for looping over all elements of a hash table. The idiom is
|
||||
** like this:
|
||||
**
|
||||
** fts2Hash h;
|
||||
** fts2HashElem *p;
|
||||
** ...
|
||||
** for(p=fts2HashFirst(&h); p; p=fts2HashNext(p)){
|
||||
** SomeStructure *pData = fts2HashData(p);
|
||||
** // do something with pData
|
||||
** }
|
||||
*/
|
||||
#define fts2HashFirst(H) ((H)->first)
|
||||
#define fts2HashNext(E) ((E)->next)
|
||||
#define fts2HashData(E) ((E)->data)
|
||||
#define fts2HashKey(E) ((E)->pKey)
|
||||
#define fts2HashKeysize(E) ((E)->nKey)
|
||||
|
||||
/*
|
||||
** Number of entries in a hash table
|
||||
*/
|
||||
#define fts2HashCount(H) ((H)->count)
|
||||
|
||||
#endif /* _FTS2_HASH_H_ */
|
||||
@@ -0,0 +1,642 @@
|
||||
/*
|
||||
** 2006 September 30
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** Implementation of the full-text-search tokenizer that implements
|
||||
** a Porter stemmer.
|
||||
*/
|
||||
|
||||
/*
|
||||
** The code in this file is only compiled if:
|
||||
**
|
||||
** * The FTS2 module is being built as an extension
|
||||
** (in which case SQLITE_CORE is not defined), or
|
||||
**
|
||||
** * The FTS2 module is being built into the core of
|
||||
** SQLite (in which case SQLITE_ENABLE_FTS2 is defined).
|
||||
*/
|
||||
#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS2)
|
||||
|
||||
|
||||
#include <assert.h>
|
||||
#if !defined(__APPLE__)
|
||||
#include <malloc.h>
|
||||
#else
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "fts2_tokenizer.h"
|
||||
|
||||
/*
|
||||
** Class derived from sqlite3_tokenizer
|
||||
*/
|
||||
typedef struct porter_tokenizer {
|
||||
sqlite3_tokenizer base; /* Base class */
|
||||
} porter_tokenizer;
|
||||
|
||||
/*
|
||||
** Class derived from sqlit3_tokenizer_cursor
|
||||
*/
|
||||
typedef struct porter_tokenizer_cursor {
|
||||
sqlite3_tokenizer_cursor base;
|
||||
const char *zInput; /* input we are tokenizing */
|
||||
int nInput; /* size of the input */
|
||||
int iOffset; /* current position in zInput */
|
||||
int iToken; /* index of next token to be returned */
|
||||
char *zToken; /* storage for current token */
|
||||
int nAllocated; /* space allocated to zToken buffer */
|
||||
} porter_tokenizer_cursor;
|
||||
|
||||
|
||||
/* Forward declaration */
|
||||
static const sqlite3_tokenizer_module porterTokenizerModule;
|
||||
|
||||
|
||||
/*
|
||||
** Create a new tokenizer instance.
|
||||
*/
|
||||
static int porterCreate(
|
||||
int argc, const char * const *argv,
|
||||
sqlite3_tokenizer **ppTokenizer
|
||||
){
|
||||
porter_tokenizer *t;
|
||||
t = (porter_tokenizer *) calloc(sizeof(porter_tokenizer), 1);
|
||||
*ppTokenizer = &t->base;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Destroy a tokenizer
|
||||
*/
|
||||
static int porterDestroy(sqlite3_tokenizer *pTokenizer){
|
||||
free(pTokenizer);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Prepare to begin tokenizing a particular string. The input
|
||||
** string to be tokenized is zInput[0..nInput-1]. A cursor
|
||||
** used to incrementally tokenize this string is returned in
|
||||
** *ppCursor.
|
||||
*/
|
||||
static int porterOpen(
|
||||
sqlite3_tokenizer *pTokenizer, /* The tokenizer */
|
||||
const char *zInput, int nInput, /* String to be tokenized */
|
||||
sqlite3_tokenizer_cursor **ppCursor /* OUT: Tokenization cursor */
|
||||
){
|
||||
porter_tokenizer_cursor *c;
|
||||
|
||||
c = (porter_tokenizer_cursor *) malloc(sizeof(porter_tokenizer_cursor));
|
||||
c->zInput = zInput;
|
||||
if( zInput==0 ){
|
||||
c->nInput = 0;
|
||||
}else if( nInput<0 ){
|
||||
c->nInput = (int)strlen(zInput);
|
||||
}else{
|
||||
c->nInput = nInput;
|
||||
}
|
||||
c->iOffset = 0; /* start tokenizing at the beginning */
|
||||
c->iToken = 0;
|
||||
c->zToken = NULL; /* no space allocated, yet. */
|
||||
c->nAllocated = 0;
|
||||
|
||||
*ppCursor = &c->base;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Close a tokenization cursor previously opened by a call to
|
||||
** porterOpen() above.
|
||||
*/
|
||||
static int porterClose(sqlite3_tokenizer_cursor *pCursor){
|
||||
porter_tokenizer_cursor *c = (porter_tokenizer_cursor *) pCursor;
|
||||
free(c->zToken);
|
||||
free(c);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
/*
|
||||
** Vowel or consonant
|
||||
*/
|
||||
static const char cType[] = {
|
||||
0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0,
|
||||
1, 1, 1, 2, 1
|
||||
};
|
||||
|
||||
/*
|
||||
** isConsonant() and isVowel() determine if their first character in
|
||||
** the string they point to is a consonant or a vowel, according
|
||||
** to Porter ruls.
|
||||
**
|
||||
** A consonate is any letter other than 'a', 'e', 'i', 'o', or 'u'.
|
||||
** 'Y' is a consonant unless it follows another consonant,
|
||||
** in which case it is a vowel.
|
||||
**
|
||||
** In these routine, the letters are in reverse order. So the 'y' rule
|
||||
** is that 'y' is a consonant unless it is followed by another
|
||||
** consonent.
|
||||
*/
|
||||
static int isVowel(const char*);
|
||||
static int isConsonant(const char *z){
|
||||
int j;
|
||||
char x = *z;
|
||||
if( x==0 ) return 0;
|
||||
assert( x>='a' && x<='z' );
|
||||
j = cType[x-'a'];
|
||||
if( j<2 ) return j;
|
||||
return z[1]==0 || isVowel(z + 1);
|
||||
}
|
||||
static int isVowel(const char *z){
|
||||
int j;
|
||||
char x = *z;
|
||||
if( x==0 ) return 0;
|
||||
assert( x>='a' && x<='z' );
|
||||
j = cType[x-'a'];
|
||||
if( j<2 ) return 1-j;
|
||||
return isConsonant(z + 1);
|
||||
}
|
||||
|
||||
/*
|
||||
** Let any sequence of one or more vowels be represented by V and let
|
||||
** C be sequence of one or more consonants. Then every word can be
|
||||
** represented as:
|
||||
**
|
||||
** [C] (VC){m} [V]
|
||||
**
|
||||
** In prose: A word is an optional consonant followed by zero or
|
||||
** vowel-consonant pairs followed by an optional vowel. "m" is the
|
||||
** number of vowel consonant pairs. This routine computes the value
|
||||
** of m for the first i bytes of a word.
|
||||
**
|
||||
** Return true if the m-value for z is 1 or more. In other words,
|
||||
** return true if z contains at least one vowel that is followed
|
||||
** by a consonant.
|
||||
**
|
||||
** In this routine z[] is in reverse order. So we are really looking
|
||||
** for an instance of of a consonant followed by a vowel.
|
||||
*/
|
||||
static int m_gt_0(const char *z){
|
||||
while( isVowel(z) ){ z++; }
|
||||
if( *z==0 ) return 0;
|
||||
while( isConsonant(z) ){ z++; }
|
||||
return *z!=0;
|
||||
}
|
||||
|
||||
/* Like mgt0 above except we are looking for a value of m which is
|
||||
** exactly 1
|
||||
*/
|
||||
static int m_eq_1(const char *z){
|
||||
while( isVowel(z) ){ z++; }
|
||||
if( *z==0 ) return 0;
|
||||
while( isConsonant(z) ){ z++; }
|
||||
if( *z==0 ) return 0;
|
||||
while( isVowel(z) ){ z++; }
|
||||
if( *z==0 ) return 1;
|
||||
while( isConsonant(z) ){ z++; }
|
||||
return *z==0;
|
||||
}
|
||||
|
||||
/* Like mgt0 above except we are looking for a value of m>1 instead
|
||||
** or m>0
|
||||
*/
|
||||
static int m_gt_1(const char *z){
|
||||
while( isVowel(z) ){ z++; }
|
||||
if( *z==0 ) return 0;
|
||||
while( isConsonant(z) ){ z++; }
|
||||
if( *z==0 ) return 0;
|
||||
while( isVowel(z) ){ z++; }
|
||||
if( *z==0 ) return 0;
|
||||
while( isConsonant(z) ){ z++; }
|
||||
return *z!=0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return TRUE if there is a vowel anywhere within z[0..n-1]
|
||||
*/
|
||||
static int hasVowel(const char *z){
|
||||
while( isConsonant(z) ){ z++; }
|
||||
return *z!=0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return TRUE if the word ends in a double consonant.
|
||||
**
|
||||
** The text is reversed here. So we are really looking at
|
||||
** the first two characters of z[].
|
||||
*/
|
||||
static int doubleConsonant(const char *z){
|
||||
return isConsonant(z) && z[0]==z[1] && isConsonant(z+1);
|
||||
}
|
||||
|
||||
/*
|
||||
** Return TRUE if the word ends with three letters which
|
||||
** are consonant-vowel-consonent and where the final consonant
|
||||
** is not 'w', 'x', or 'y'.
|
||||
**
|
||||
** The word is reversed here. So we are really checking the
|
||||
** first three letters and the first one cannot be in [wxy].
|
||||
*/
|
||||
static int star_oh(const char *z){
|
||||
return
|
||||
z[0]!=0 && isConsonant(z) &&
|
||||
z[0]!='w' && z[0]!='x' && z[0]!='y' &&
|
||||
z[1]!=0 && isVowel(z+1) &&
|
||||
z[2]!=0 && isConsonant(z+2);
|
||||
}
|
||||
|
||||
/*
|
||||
** If the word ends with zFrom and xCond() is true for the stem
|
||||
** of the word that preceeds the zFrom ending, then change the
|
||||
** ending to zTo.
|
||||
**
|
||||
** The input word *pz and zFrom are both in reverse order. zTo
|
||||
** is in normal order.
|
||||
**
|
||||
** Return TRUE if zFrom matches. Return FALSE if zFrom does not
|
||||
** match. Not that TRUE is returned even if xCond() fails and
|
||||
** no substitution occurs.
|
||||
*/
|
||||
static int stem(
|
||||
char **pz, /* The word being stemmed (Reversed) */
|
||||
const char *zFrom, /* If the ending matches this... (Reversed) */
|
||||
const char *zTo, /* ... change the ending to this (not reversed) */
|
||||
int (*xCond)(const char*) /* Condition that must be true */
|
||||
){
|
||||
char *z = *pz;
|
||||
while( *zFrom && *zFrom==*z ){ z++; zFrom++; }
|
||||
if( *zFrom!=0 ) return 0;
|
||||
if( xCond && !xCond(z) ) return 1;
|
||||
while( *zTo ){
|
||||
*(--z) = *(zTo++);
|
||||
}
|
||||
*pz = z;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
** This is the fallback stemmer used when the porter stemmer is
|
||||
** inappropriate. The input word is copied into the output with
|
||||
** US-ASCII case folding. If the input word is too long (more
|
||||
** than 20 bytes if it contains no digits or more than 6 bytes if
|
||||
** it contains digits) then word is truncated to 20 or 6 bytes
|
||||
** by taking 10 or 3 bytes from the beginning and end.
|
||||
*/
|
||||
static void copy_stemmer(const char *zIn, int nIn, char *zOut, int *pnOut){
|
||||
int i, mx, j;
|
||||
int hasDigit = 0;
|
||||
for(i=0; i<nIn; i++){
|
||||
int c = zIn[i];
|
||||
if( c>='A' && c<='Z' ){
|
||||
zOut[i] = c - 'A' + 'a';
|
||||
}else{
|
||||
if( c>='0' && c<='9' ) hasDigit = 1;
|
||||
zOut[i] = c;
|
||||
}
|
||||
}
|
||||
mx = hasDigit ? 3 : 10;
|
||||
if( nIn>mx*2 ){
|
||||
for(j=mx, i=nIn-mx; i<nIn; i++, j++){
|
||||
zOut[j] = zOut[i];
|
||||
}
|
||||
i = j;
|
||||
}
|
||||
zOut[i] = 0;
|
||||
*pnOut = i;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Stem the input word zIn[0..nIn-1]. Store the output in zOut.
|
||||
** zOut is at least big enough to hold nIn bytes. Write the actual
|
||||
** size of the output word (exclusive of the '\0' terminator) into *pnOut.
|
||||
**
|
||||
** Any upper-case characters in the US-ASCII character set ([A-Z])
|
||||
** are converted to lower case. Upper-case UTF characters are
|
||||
** unchanged.
|
||||
**
|
||||
** Words that are longer than about 20 bytes are stemmed by retaining
|
||||
** a few bytes from the beginning and the end of the word. If the
|
||||
** word contains digits, 3 bytes are taken from the beginning and
|
||||
** 3 bytes from the end. For long words without digits, 10 bytes
|
||||
** are taken from each end. US-ASCII case folding still applies.
|
||||
**
|
||||
** If the input word contains not digits but does characters not
|
||||
** in [a-zA-Z] then no stemming is attempted and this routine just
|
||||
** copies the input into the input into the output with US-ASCII
|
||||
** case folding.
|
||||
**
|
||||
** Stemming never increases the length of the word. So there is
|
||||
** no chance of overflowing the zOut buffer.
|
||||
*/
|
||||
static void porter_stemmer(const char *zIn, int nIn, char *zOut, int *pnOut){
|
||||
int i, j, c;
|
||||
char zReverse[28];
|
||||
char *z, *z2;
|
||||
if( nIn<3 || nIn>=sizeof(zReverse)-7 ){
|
||||
/* The word is too big or too small for the porter stemmer.
|
||||
** Fallback to the copy stemmer */
|
||||
copy_stemmer(zIn, nIn, zOut, pnOut);
|
||||
return;
|
||||
}
|
||||
for(i=0, j=sizeof(zReverse)-6; i<nIn; i++, j--){
|
||||
c = zIn[i];
|
||||
if( c>='A' && c<='Z' ){
|
||||
zReverse[j] = c + 'a' - 'A';
|
||||
}else if( c>='a' && c<='z' ){
|
||||
zReverse[j] = c;
|
||||
}else{
|
||||
/* The use of a character not in [a-zA-Z] means that we fallback
|
||||
** to the copy stemmer */
|
||||
copy_stemmer(zIn, nIn, zOut, pnOut);
|
||||
return;
|
||||
}
|
||||
}
|
||||
memset(&zReverse[sizeof(zReverse)-5], 0, 5);
|
||||
z = &zReverse[j+1];
|
||||
|
||||
|
||||
/* Step 1a */
|
||||
if( z[0]=='s' ){
|
||||
if(
|
||||
!stem(&z, "sess", "ss", 0) &&
|
||||
!stem(&z, "sei", "i", 0) &&
|
||||
!stem(&z, "ss", "ss", 0)
|
||||
){
|
||||
z++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Step 1b */
|
||||
z2 = z;
|
||||
if( stem(&z, "dee", "ee", m_gt_0) ){
|
||||
/* Do nothing. The work was all in the test */
|
||||
}else if(
|
||||
(stem(&z, "gni", "", hasVowel) || stem(&z, "de", "", hasVowel))
|
||||
&& z!=z2
|
||||
){
|
||||
if( stem(&z, "ta", "ate", 0) ||
|
||||
stem(&z, "lb", "ble", 0) ||
|
||||
stem(&z, "zi", "ize", 0) ){
|
||||
/* Do nothing. The work was all in the test */
|
||||
}else if( doubleConsonant(z) && (*z!='l' && *z!='s' && *z!='z') ){
|
||||
z++;
|
||||
}else if( m_eq_1(z) && star_oh(z) ){
|
||||
*(--z) = 'e';
|
||||
}
|
||||
}
|
||||
|
||||
/* Step 1c */
|
||||
if( z[0]=='y' && hasVowel(z+1) ){
|
||||
z[0] = 'i';
|
||||
}
|
||||
|
||||
/* Step 2 */
|
||||
switch( z[1] ){
|
||||
case 'a':
|
||||
stem(&z, "lanoita", "ate", m_gt_0) ||
|
||||
stem(&z, "lanoit", "tion", m_gt_0);
|
||||
break;
|
||||
case 'c':
|
||||
stem(&z, "icne", "ence", m_gt_0) ||
|
||||
stem(&z, "icna", "ance", m_gt_0);
|
||||
break;
|
||||
case 'e':
|
||||
stem(&z, "rezi", "ize", m_gt_0);
|
||||
break;
|
||||
case 'g':
|
||||
stem(&z, "igol", "log", m_gt_0);
|
||||
break;
|
||||
case 'l':
|
||||
stem(&z, "ilb", "ble", m_gt_0) ||
|
||||
stem(&z, "illa", "al", m_gt_0) ||
|
||||
stem(&z, "iltne", "ent", m_gt_0) ||
|
||||
stem(&z, "ile", "e", m_gt_0) ||
|
||||
stem(&z, "ilsuo", "ous", m_gt_0);
|
||||
break;
|
||||
case 'o':
|
||||
stem(&z, "noitazi", "ize", m_gt_0) ||
|
||||
stem(&z, "noita", "ate", m_gt_0) ||
|
||||
stem(&z, "rota", "ate", m_gt_0);
|
||||
break;
|
||||
case 's':
|
||||
stem(&z, "msila", "al", m_gt_0) ||
|
||||
stem(&z, "ssenevi", "ive", m_gt_0) ||
|
||||
stem(&z, "ssenluf", "ful", m_gt_0) ||
|
||||
stem(&z, "ssensuo", "ous", m_gt_0);
|
||||
break;
|
||||
case 't':
|
||||
stem(&z, "itila", "al", m_gt_0) ||
|
||||
stem(&z, "itivi", "ive", m_gt_0) ||
|
||||
stem(&z, "itilib", "ble", m_gt_0);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Step 3 */
|
||||
switch( z[0] ){
|
||||
case 'e':
|
||||
stem(&z, "etaci", "ic", m_gt_0) ||
|
||||
stem(&z, "evita", "", m_gt_0) ||
|
||||
stem(&z, "ezila", "al", m_gt_0);
|
||||
break;
|
||||
case 'i':
|
||||
stem(&z, "itici", "ic", m_gt_0);
|
||||
break;
|
||||
case 'l':
|
||||
stem(&z, "laci", "ic", m_gt_0) ||
|
||||
stem(&z, "luf", "", m_gt_0);
|
||||
break;
|
||||
case 's':
|
||||
stem(&z, "ssen", "", m_gt_0);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Step 4 */
|
||||
switch( z[1] ){
|
||||
case 'a':
|
||||
if( z[0]=='l' && m_gt_1(z+2) ){
|
||||
z += 2;
|
||||
}
|
||||
break;
|
||||
case 'c':
|
||||
if( z[0]=='e' && z[2]=='n' && (z[3]=='a' || z[3]=='e') && m_gt_1(z+4) ){
|
||||
z += 4;
|
||||
}
|
||||
break;
|
||||
case 'e':
|
||||
if( z[0]=='r' && m_gt_1(z+2) ){
|
||||
z += 2;
|
||||
}
|
||||
break;
|
||||
case 'i':
|
||||
if( z[0]=='c' && m_gt_1(z+2) ){
|
||||
z += 2;
|
||||
}
|
||||
break;
|
||||
case 'l':
|
||||
if( z[0]=='e' && z[2]=='b' && (z[3]=='a' || z[3]=='i') && m_gt_1(z+4) ){
|
||||
z += 4;
|
||||
}
|
||||
break;
|
||||
case 'n':
|
||||
if( z[0]=='t' ){
|
||||
if( z[2]=='a' ){
|
||||
if( m_gt_1(z+3) ){
|
||||
z += 3;
|
||||
}
|
||||
}else if( z[2]=='e' ){
|
||||
stem(&z, "tneme", "", m_gt_1) ||
|
||||
stem(&z, "tnem", "", m_gt_1) ||
|
||||
stem(&z, "tne", "", m_gt_1);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'o':
|
||||
if( z[0]=='u' ){
|
||||
if( m_gt_1(z+2) ){
|
||||
z += 2;
|
||||
}
|
||||
}else if( z[3]=='s' || z[3]=='t' ){
|
||||
stem(&z, "noi", "", m_gt_1);
|
||||
}
|
||||
break;
|
||||
case 's':
|
||||
if( z[0]=='m' && z[2]=='i' && m_gt_1(z+3) ){
|
||||
z += 3;
|
||||
}
|
||||
break;
|
||||
case 't':
|
||||
stem(&z, "eta", "", m_gt_1) ||
|
||||
stem(&z, "iti", "", m_gt_1);
|
||||
break;
|
||||
case 'u':
|
||||
if( z[0]=='s' && z[2]=='o' && m_gt_1(z+3) ){
|
||||
z += 3;
|
||||
}
|
||||
break;
|
||||
case 'v':
|
||||
case 'z':
|
||||
if( z[0]=='e' && z[2]=='i' && m_gt_1(z+3) ){
|
||||
z += 3;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/* Step 5a */
|
||||
if( z[0]=='e' ){
|
||||
if( m_gt_1(z+1) ){
|
||||
z++;
|
||||
}else if( m_eq_1(z+1) && !star_oh(z+1) ){
|
||||
z++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Step 5b */
|
||||
if( m_gt_1(z) && z[0]=='l' && z[1]=='l' ){
|
||||
z++;
|
||||
}
|
||||
|
||||
/* z[] is now the stemmed word in reverse order. Flip it back
|
||||
** around into forward order and return.
|
||||
*/
|
||||
*pnOut = i = strlen(z);
|
||||
zOut[i] = 0;
|
||||
while( *z ){
|
||||
zOut[--i] = *(z++);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Characters that can be part of a token. We assume any character
|
||||
** whose value is greater than 0x80 (any UTF character) can be
|
||||
** part of a token. In other words, delimiters all must have
|
||||
** values of 0x7f or lower.
|
||||
*/
|
||||
static const char isIdChar[] = {
|
||||
/* x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC xD xE xF */
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, /* 3x */
|
||||
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 4x */
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* 5x */
|
||||
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 6x */
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 7x */
|
||||
};
|
||||
#define idChar(C) (((ch=C)&0x80)!=0 || (ch>0x2f && isIdChar[ch-0x30]))
|
||||
#define isDelim(C) (((ch=C)&0x80)==0 && (ch<0x30 || !isIdChar[ch-0x30]))
|
||||
|
||||
/*
|
||||
** Extract the next token from a tokenization cursor. The cursor must
|
||||
** have been opened by a prior call to porterOpen().
|
||||
*/
|
||||
static int porterNext(
|
||||
sqlite3_tokenizer_cursor *pCursor, /* Cursor returned by porterOpen */
|
||||
const char **pzToken, /* OUT: *pzToken is the token text */
|
||||
int *pnBytes, /* OUT: Number of bytes in token */
|
||||
int *piStartOffset, /* OUT: Starting offset of token */
|
||||
int *piEndOffset, /* OUT: Ending offset of token */
|
||||
int *piPosition /* OUT: Position integer of token */
|
||||
){
|
||||
porter_tokenizer_cursor *c = (porter_tokenizer_cursor *) pCursor;
|
||||
const char *z = c->zInput;
|
||||
|
||||
while( c->iOffset<c->nInput ){
|
||||
int iStartOffset, ch;
|
||||
|
||||
/* Scan past delimiter characters */
|
||||
while( c->iOffset<c->nInput && isDelim(z[c->iOffset]) ){
|
||||
c->iOffset++;
|
||||
}
|
||||
|
||||
/* Count non-delimiter characters. */
|
||||
iStartOffset = c->iOffset;
|
||||
while( c->iOffset<c->nInput && !isDelim(z[c->iOffset]) ){
|
||||
c->iOffset++;
|
||||
}
|
||||
|
||||
if( c->iOffset>iStartOffset ){
|
||||
int n = c->iOffset-iStartOffset;
|
||||
if( n>c->nAllocated ){
|
||||
c->nAllocated = n+20;
|
||||
c->zToken = realloc(c->zToken, c->nAllocated);
|
||||
}
|
||||
porter_stemmer(&z[iStartOffset], n, c->zToken, pnBytes);
|
||||
*pzToken = c->zToken;
|
||||
*piStartOffset = iStartOffset;
|
||||
*piEndOffset = c->iOffset;
|
||||
*piPosition = c->iToken++;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
}
|
||||
return SQLITE_DONE;
|
||||
}
|
||||
|
||||
/*
|
||||
** The set of routines that implement the porter-stemmer tokenizer
|
||||
*/
|
||||
static const sqlite3_tokenizer_module porterTokenizerModule = {
|
||||
0,
|
||||
porterCreate,
|
||||
porterDestroy,
|
||||
porterOpen,
|
||||
porterClose,
|
||||
porterNext,
|
||||
};
|
||||
|
||||
/*
|
||||
** Allocate a new porter tokenizer. Return a pointer to the new
|
||||
** tokenizer in *ppModule
|
||||
*/
|
||||
void sqlite3Fts2PorterTokenizerModule(
|
||||
sqlite3_tokenizer_module const**ppModule
|
||||
){
|
||||
*ppModule = &porterTokenizerModule;
|
||||
}
|
||||
|
||||
#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS2) */
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
** 2006 July 10
|
||||
**
|
||||
** The author disclaims copyright to this source code.
|
||||
**
|
||||
*************************************************************************
|
||||
** Defines the interface to tokenizers used by fulltext-search. There
|
||||
** are three basic components:
|
||||
**
|
||||
** sqlite3_tokenizer_module is a singleton defining the tokenizer
|
||||
** interface functions. This is essentially the class structure for
|
||||
** tokenizers.
|
||||
**
|
||||
** sqlite3_tokenizer is used to define a particular tokenizer, perhaps
|
||||
** including customization information defined at creation time.
|
||||
**
|
||||
** sqlite3_tokenizer_cursor is generated by a tokenizer to generate
|
||||
** tokens from a particular input.
|
||||
*/
|
||||
#ifndef _FTS2_TOKENIZER_H_
|
||||
#define _FTS2_TOKENIZER_H_
|
||||
|
||||
/* TODO(shess) Only used for SQLITE_OK and SQLITE_DONE at this time.
|
||||
** If tokenizers are to be allowed to call sqlite3_*() functions, then
|
||||
** we will need a way to register the API consistently.
|
||||
*/
|
||||
#include "sqlite3.h"
|
||||
|
||||
/*
|
||||
** Structures used by the tokenizer interface.
|
||||
*/
|
||||
typedef struct sqlite3_tokenizer sqlite3_tokenizer;
|
||||
typedef struct sqlite3_tokenizer_cursor sqlite3_tokenizer_cursor;
|
||||
typedef struct sqlite3_tokenizer_module sqlite3_tokenizer_module;
|
||||
|
||||
struct sqlite3_tokenizer_module {
|
||||
int iVersion; /* currently 0 */
|
||||
|
||||
/*
|
||||
** Create and destroy a tokenizer. argc/argv are passed down from
|
||||
** the fulltext virtual table creation to allow customization.
|
||||
*/
|
||||
int (*xCreate)(int argc, const char *const*argv,
|
||||
sqlite3_tokenizer **ppTokenizer);
|
||||
int (*xDestroy)(sqlite3_tokenizer *pTokenizer);
|
||||
|
||||
/*
|
||||
** Tokenize a particular input. Call xOpen() to prepare to
|
||||
** tokenize, xNext() repeatedly until it returns SQLITE_DONE, then
|
||||
** xClose() to free any internal state. The pInput passed to
|
||||
** xOpen() must exist until the cursor is closed. The ppToken
|
||||
** result from xNext() is only valid until the next call to xNext()
|
||||
** or until xClose() is called.
|
||||
*/
|
||||
/* TODO(shess) current implementation requires pInput to be
|
||||
** nul-terminated. This should either be fixed, or pInput/nBytes
|
||||
** should be converted to zInput.
|
||||
*/
|
||||
int (*xOpen)(sqlite3_tokenizer *pTokenizer,
|
||||
const char *pInput, int nBytes,
|
||||
sqlite3_tokenizer_cursor **ppCursor);
|
||||
int (*xClose)(sqlite3_tokenizer_cursor *pCursor);
|
||||
int (*xNext)(sqlite3_tokenizer_cursor *pCursor,
|
||||
const char **ppToken, int *pnBytes,
|
||||
int *piStartOffset, int *piEndOffset, int *piPosition);
|
||||
};
|
||||
|
||||
struct sqlite3_tokenizer {
|
||||
const sqlite3_tokenizer_module *pModule; /* The module for this tokenizer */
|
||||
/* Tokenizer implementations will typically add additional fields */
|
||||
};
|
||||
|
||||
struct sqlite3_tokenizer_cursor {
|
||||
sqlite3_tokenizer *pTokenizer; /* Tokenizer for this cursor. */
|
||||
/* Tokenizer implementations will typically add additional fields */
|
||||
};
|
||||
|
||||
/*
|
||||
** Get the module for a tokenizer which generates tokens based on a
|
||||
** set of non-token characters. The default is to break tokens at any
|
||||
** non-alnum character, though the set of delimiters can also be
|
||||
** specified by the first argv argument to xCreate().
|
||||
*/
|
||||
/* TODO(shess) This doesn't belong here. Need some sort of
|
||||
** registration process.
|
||||
*/
|
||||
void sqlite3Fts2SimpleTokenizerModule(sqlite3_tokenizer_module const**ppModule);
|
||||
void sqlite3Fts2PorterTokenizerModule(sqlite3_tokenizer_module const**ppModule);
|
||||
|
||||
#endif /* _FTS2_TOKENIZER_H_ */
|
||||
@@ -0,0 +1,220 @@
|
||||
/*
|
||||
** The author disclaims copyright to this source code.
|
||||
**
|
||||
*************************************************************************
|
||||
** Implementation of the "simple" full-text-search tokenizer.
|
||||
*/
|
||||
|
||||
/*
|
||||
** The code in this file is only compiled if:
|
||||
**
|
||||
** * The FTS2 module is being built as an extension
|
||||
** (in which case SQLITE_CORE is not defined), or
|
||||
**
|
||||
** * The FTS2 module is being built into the core of
|
||||
** SQLite (in which case SQLITE_ENABLE_FTS2 is defined).
|
||||
*/
|
||||
#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS2)
|
||||
|
||||
|
||||
#include <assert.h>
|
||||
#if !defined(__APPLE__)
|
||||
#include <malloc.h>
|
||||
#else
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "fts2_tokenizer.h"
|
||||
|
||||
typedef struct simple_tokenizer {
|
||||
sqlite3_tokenizer base;
|
||||
char delim[128]; /* flag ASCII delimiters */
|
||||
} simple_tokenizer;
|
||||
|
||||
typedef struct simple_tokenizer_cursor {
|
||||
sqlite3_tokenizer_cursor base;
|
||||
const char *pInput; /* input we are tokenizing */
|
||||
int nBytes; /* size of the input */
|
||||
int iOffset; /* current position in pInput */
|
||||
int iToken; /* index of next token to be returned */
|
||||
char *pToken; /* storage for current token */
|
||||
int nTokenAllocated; /* space allocated to zToken buffer */
|
||||
} simple_tokenizer_cursor;
|
||||
|
||||
|
||||
/* Forward declaration */
|
||||
static const sqlite3_tokenizer_module simpleTokenizerModule;
|
||||
|
||||
static int isDelim(simple_tokenizer *t, unsigned char c){
|
||||
return c<0x80 && t->delim[c];
|
||||
}
|
||||
|
||||
/*
|
||||
** Create a new tokenizer instance.
|
||||
*/
|
||||
static int simpleCreate(
|
||||
int argc, const char * const *argv,
|
||||
sqlite3_tokenizer **ppTokenizer
|
||||
){
|
||||
simple_tokenizer *t;
|
||||
|
||||
t = (simple_tokenizer *) calloc(sizeof(simple_tokenizer), 1);
|
||||
/* TODO(shess) Delimiters need to remain the same from run to run,
|
||||
** else we need to reindex. One solution would be a meta-table to
|
||||
** track such information in the database, then we'd only want this
|
||||
** information on the initial create.
|
||||
*/
|
||||
if( argc>1 ){
|
||||
int i, n = strlen(argv[1]);
|
||||
for(i=0; i<n; i++){
|
||||
unsigned char ch = argv[1][i];
|
||||
/* We explicitly don't support UTF-8 delimiters for now. */
|
||||
if( ch>=0x80 ){
|
||||
free(t);
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
t->delim[ch] = 1;
|
||||
}
|
||||
} else {
|
||||
/* Mark non-alphanumeric ASCII characters as delimiters */
|
||||
int i;
|
||||
for(i=1; i<0x80; i++){
|
||||
t->delim[i] = !isalnum(i);
|
||||
}
|
||||
}
|
||||
|
||||
*ppTokenizer = &t->base;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Destroy a tokenizer
|
||||
*/
|
||||
static int simpleDestroy(sqlite3_tokenizer *pTokenizer){
|
||||
free(pTokenizer);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Prepare to begin tokenizing a particular string. The input
|
||||
** string to be tokenized is pInput[0..nBytes-1]. A cursor
|
||||
** used to incrementally tokenize this string is returned in
|
||||
** *ppCursor.
|
||||
*/
|
||||
static int simpleOpen(
|
||||
sqlite3_tokenizer *pTokenizer, /* The tokenizer */
|
||||
const char *pInput, int nBytes, /* String to be tokenized */
|
||||
sqlite3_tokenizer_cursor **ppCursor /* OUT: Tokenization cursor */
|
||||
){
|
||||
simple_tokenizer_cursor *c;
|
||||
|
||||
c = (simple_tokenizer_cursor *) malloc(sizeof(simple_tokenizer_cursor));
|
||||
c->pInput = pInput;
|
||||
if( pInput==0 ){
|
||||
c->nBytes = 0;
|
||||
}else if( nBytes<0 ){
|
||||
c->nBytes = (int)strlen(pInput);
|
||||
}else{
|
||||
c->nBytes = nBytes;
|
||||
}
|
||||
c->iOffset = 0; /* start tokenizing at the beginning */
|
||||
c->iToken = 0;
|
||||
c->pToken = NULL; /* no space allocated, yet. */
|
||||
c->nTokenAllocated = 0;
|
||||
|
||||
*ppCursor = &c->base;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Close a tokenization cursor previously opened by a call to
|
||||
** simpleOpen() above.
|
||||
*/
|
||||
static int simpleClose(sqlite3_tokenizer_cursor *pCursor){
|
||||
simple_tokenizer_cursor *c = (simple_tokenizer_cursor *) pCursor;
|
||||
free(c->pToken);
|
||||
free(c);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Extract the next token from a tokenization cursor. The cursor must
|
||||
** have been opened by a prior call to simpleOpen().
|
||||
*/
|
||||
static int simpleNext(
|
||||
sqlite3_tokenizer_cursor *pCursor, /* Cursor returned by simpleOpen */
|
||||
const char **ppToken, /* OUT: *ppToken is the token text */
|
||||
int *pnBytes, /* OUT: Number of bytes in token */
|
||||
int *piStartOffset, /* OUT: Starting offset of token */
|
||||
int *piEndOffset, /* OUT: Ending offset of token */
|
||||
int *piPosition /* OUT: Position integer of token */
|
||||
){
|
||||
simple_tokenizer_cursor *c = (simple_tokenizer_cursor *) pCursor;
|
||||
simple_tokenizer *t = (simple_tokenizer *) pCursor->pTokenizer;
|
||||
unsigned char *p = (unsigned char *)c->pInput;
|
||||
|
||||
while( c->iOffset<c->nBytes ){
|
||||
int iStartOffset;
|
||||
|
||||
/* Scan past delimiter characters */
|
||||
while( c->iOffset<c->nBytes && isDelim(t, p[c->iOffset]) ){
|
||||
c->iOffset++;
|
||||
}
|
||||
|
||||
/* Count non-delimiter characters. */
|
||||
iStartOffset = c->iOffset;
|
||||
while( c->iOffset<c->nBytes && !isDelim(t, p[c->iOffset]) ){
|
||||
c->iOffset++;
|
||||
}
|
||||
|
||||
if( c->iOffset>iStartOffset ){
|
||||
int i, n = c->iOffset-iStartOffset;
|
||||
if( n>c->nTokenAllocated ){
|
||||
c->nTokenAllocated = n+20;
|
||||
c->pToken = realloc(c->pToken, c->nTokenAllocated);
|
||||
}
|
||||
for(i=0; i<n; i++){
|
||||
/* TODO(shess) This needs expansion to handle UTF-8
|
||||
** case-insensitivity.
|
||||
*/
|
||||
unsigned char ch = p[iStartOffset+i];
|
||||
c->pToken[i] = ch<0x80 ? tolower(ch) : ch;
|
||||
}
|
||||
*ppToken = c->pToken;
|
||||
*pnBytes = n;
|
||||
*piStartOffset = iStartOffset;
|
||||
*piEndOffset = c->iOffset;
|
||||
*piPosition = c->iToken++;
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
}
|
||||
return SQLITE_DONE;
|
||||
}
|
||||
|
||||
/*
|
||||
** The set of routines that implement the simple tokenizer
|
||||
*/
|
||||
static const sqlite3_tokenizer_module simpleTokenizerModule = {
|
||||
0,
|
||||
simpleCreate,
|
||||
simpleDestroy,
|
||||
simpleOpen,
|
||||
simpleClose,
|
||||
simpleNext,
|
||||
};
|
||||
|
||||
/*
|
||||
** Allocate a new simple tokenizer. Return a pointer to the new
|
||||
** tokenizer in *ppModule
|
||||
*/
|
||||
void sqlite3Fts2SimpleTokenizerModule(
|
||||
sqlite3_tokenizer_module const**ppModule
|
||||
){
|
||||
*ppModule = &simpleTokenizerModule;
|
||||
}
|
||||
|
||||
#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS2) */
|
||||
+79
-141
@@ -9,7 +9,7 @@
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** $Id: btree.c,v 1.328 2006/08/16 16:42:48 drh Exp $
|
||||
** $Id: btree.c,v 1.335 2007/02/10 19:22:36 drh Exp $
|
||||
**
|
||||
** This file implements a external (disk-based) database using BTrees.
|
||||
** For a detailed discussion of BTrees, refer to
|
||||
@@ -421,7 +421,8 @@ struct BtCursor {
|
||||
*/
|
||||
#if SQLITE_TEST
|
||||
# define TRACE(X) if( sqlite3_btree_trace )\
|
||||
{ sqlite3DebugPrintf X; fflush(stdout); }
|
||||
/* { sqlite3DebugPrintf X; fflush(stdout); } */ \
|
||||
{ printf X; fflush(stdout); }
|
||||
int sqlite3_btree_trace=0; /* True to enable tracing */
|
||||
#else
|
||||
# define TRACE(X)
|
||||
@@ -1039,91 +1040,6 @@ static int ptrmapPutOvfl(MemPage *pPage, int iCell){
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
** Do sanity checking on a page. Throw an exception if anything is
|
||||
** not right.
|
||||
**
|
||||
** This routine is used for internal error checking only. It is omitted
|
||||
** from most builds.
|
||||
*/
|
||||
#if defined(BTREE_DEBUG) && !defined(NDEBUG) && 0
|
||||
static void _pageIntegrity(MemPage *pPage){
|
||||
int usableSize;
|
||||
u8 *data;
|
||||
int i, j, idx, c, pc, hdr, nFree;
|
||||
int cellOffset;
|
||||
int nCell, cellLimit;
|
||||
u8 *used;
|
||||
|
||||
used = sqliteMallocRaw( pPage->pBt->pageSize );
|
||||
if( used==0 ) return;
|
||||
usableSize = pPage->pBt->usableSize;
|
||||
assert( pPage->aData==&((unsigned char*)pPage)[-pPage->pBt->pageSize] );
|
||||
hdr = pPage->hdrOffset;
|
||||
assert( hdr==(pPage->pgno==1 ? 100 : 0) );
|
||||
assert( pPage->pgno==sqlite3pager_pagenumber(pPage->aData) );
|
||||
c = pPage->aData[hdr];
|
||||
if( pPage->isInit ){
|
||||
assert( pPage->leaf == ((c & PTF_LEAF)!=0) );
|
||||
assert( pPage->zeroData == ((c & PTF_ZERODATA)!=0) );
|
||||
assert( pPage->leafData == ((c & PTF_LEAFDATA)!=0) );
|
||||
assert( pPage->intKey == ((c & (PTF_INTKEY|PTF_LEAFDATA))!=0) );
|
||||
assert( pPage->hasData ==
|
||||
!(pPage->zeroData || (!pPage->leaf && pPage->leafData)) );
|
||||
assert( pPage->cellOffset==pPage->hdrOffset+12-4*pPage->leaf );
|
||||
assert( pPage->nCell = get2byte(&pPage->aData[hdr+3]) );
|
||||
}
|
||||
data = pPage->aData;
|
||||
memset(used, 0, usableSize);
|
||||
for(i=0; i<hdr+10-pPage->leaf*4; i++) used[i] = 1;
|
||||
nFree = 0;
|
||||
pc = get2byte(&data[hdr+1]);
|
||||
while( pc ){
|
||||
int size;
|
||||
assert( pc>0 && pc<usableSize-4 );
|
||||
size = get2byte(&data[pc+2]);
|
||||
assert( pc+size<=usableSize );
|
||||
nFree += size;
|
||||
for(i=pc; i<pc+size; i++){
|
||||
assert( used[i]==0 );
|
||||
used[i] = 1;
|
||||
}
|
||||
pc = get2byte(&data[pc]);
|
||||
}
|
||||
idx = 0;
|
||||
nCell = get2byte(&data[hdr+3]);
|
||||
cellLimit = get2byte(&data[hdr+5]);
|
||||
assert( pPage->isInit==0
|
||||
|| pPage->nFree==nFree+data[hdr+7]+cellLimit-(cellOffset+2*nCell) );
|
||||
cellOffset = pPage->cellOffset;
|
||||
for(i=0; i<nCell; i++){
|
||||
int size;
|
||||
pc = get2byte(&data[cellOffset+2*i]);
|
||||
assert( pc>0 && pc<usableSize-4 );
|
||||
size = cellSize(pPage, &data[pc]);
|
||||
assert( pc+size<=usableSize );
|
||||
for(j=pc; j<pc+size; j++){
|
||||
assert( used[j]==0 );
|
||||
used[j] = 1;
|
||||
}
|
||||
}
|
||||
for(i=cellOffset+2*nCell; i<cellimit; i++){
|
||||
assert( used[i]==0 );
|
||||
used[i] = 1;
|
||||
}
|
||||
nFree = 0;
|
||||
for(i=0; i<usableSize; i++){
|
||||
assert( used[i]<=1 );
|
||||
if( used[i]==0 ) nFree++;
|
||||
}
|
||||
assert( nFree==data[hdr+7] );
|
||||
sqliteFree(used);
|
||||
}
|
||||
#define pageIntegrity(X) _pageIntegrity(X)
|
||||
#else
|
||||
# define pageIntegrity(X)
|
||||
#endif
|
||||
|
||||
/* A bunch of assert() statements to check the transaction state variables
|
||||
** of handle p (type Btree*) are internally consistent.
|
||||
*/
|
||||
@@ -1430,7 +1346,6 @@ static int initPage(
|
||||
}
|
||||
|
||||
pPage->isInit = 1;
|
||||
pageIntegrity(pPage);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
@@ -1461,7 +1376,6 @@ static void zeroPage(MemPage *pPage, int flags){
|
||||
pPage->idxShift = 0;
|
||||
pPage->nCell = 0;
|
||||
pPage->isInit = 1;
|
||||
pageIntegrity(pPage);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1636,8 +1550,13 @@ int sqlite3BtreeOpen(
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
rc = sqlite3pager_open(&pBt->pPager, zFilename, EXTRA_SIZE, flags);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3pager_read_fileheader(pBt->pPager,sizeof(zDbHeader),zDbHeader);
|
||||
}
|
||||
if( rc!=SQLITE_OK ){
|
||||
if( pBt->pPager ) sqlite3pager_close(pBt->pPager);
|
||||
if( pBt->pPager ){
|
||||
sqlite3pager_close(pBt->pPager);
|
||||
}
|
||||
sqliteFree(pBt);
|
||||
sqliteFree(p);
|
||||
*ppBtree = 0;
|
||||
@@ -1650,7 +1569,6 @@ int sqlite3BtreeOpen(
|
||||
pBt->pCursor = 0;
|
||||
pBt->pPage1 = 0;
|
||||
pBt->readOnly = sqlite3pager_isreadonly(pBt->pPager);
|
||||
sqlite3pager_read_fileheader(pBt->pPager, sizeof(zDbHeader), zDbHeader);
|
||||
pBt->pageSize = get2byte(&zDbHeader[16]);
|
||||
if( pBt->pageSize<512 || pBt->pageSize>SQLITE_MAX_PAGE_SIZE
|
||||
|| ((pBt->pageSize-1)&pBt->pageSize)!=0 ){
|
||||
@@ -2013,13 +1931,15 @@ static int lockBtreeWithRetry(Btree *pRef){
|
||||
*/
|
||||
static void unlockBtreeIfUnused(BtShared *pBt){
|
||||
if( pBt->inTransaction==TRANS_NONE && pBt->pCursor==0 && pBt->pPage1!=0 ){
|
||||
if( pBt->pPage1->aData==0 ){
|
||||
MemPage *pPage = pBt->pPage1;
|
||||
pPage->aData = &((u8*)pPage)[-pBt->pageSize];
|
||||
pPage->pBt = pBt;
|
||||
pPage->pgno = 1;
|
||||
if( sqlite3pager_refcount(pBt->pPager)>=1 ){
|
||||
if( pBt->pPage1->aData==0 ){
|
||||
MemPage *pPage = pBt->pPage1;
|
||||
pPage->aData = &((u8*)pPage)[-pBt->pageSize];
|
||||
pPage->pBt = pBt;
|
||||
pPage->pgno = 1;
|
||||
}
|
||||
releasePage(pBt->pPage1);
|
||||
}
|
||||
releasePage(pBt->pPage1);
|
||||
pBt->pPage1 = 0;
|
||||
pBt->inStmt = 0;
|
||||
}
|
||||
@@ -2971,7 +2891,6 @@ static int getPayload(
|
||||
assert( pCur->eState==CURSOR_VALID );
|
||||
pBt = pCur->pBtree->pBt;
|
||||
pPage = pCur->pPage;
|
||||
pageIntegrity(pPage);
|
||||
assert( pCur->idx>=0 && pCur->idx<pPage->nCell );
|
||||
getCellInfo(pCur);
|
||||
aPayload = pCur->info.pCell + pCur->info.nHeader;
|
||||
@@ -3109,7 +3028,6 @@ static const unsigned char *fetchPayload(
|
||||
assert( pCur!=0 && pCur->pPage!=0 );
|
||||
assert( pCur->eState==CURSOR_VALID );
|
||||
pPage = pCur->pPage;
|
||||
pageIntegrity(pPage);
|
||||
assert( pCur->idx>=0 && pCur->idx<pPage->nCell );
|
||||
getCellInfo(pCur);
|
||||
aPayload = pCur->info.pCell;
|
||||
@@ -3171,7 +3089,6 @@ static int moveToChild(BtCursor *pCur, u32 newPgno){
|
||||
assert( pCur->eState==CURSOR_VALID );
|
||||
rc = getAndInitPage(pBt, newPgno, &pNewPage, pCur->pPage);
|
||||
if( rc ) return rc;
|
||||
pageIntegrity(pNewPage);
|
||||
pNewPage->idxParent = pCur->idx;
|
||||
pOldPage = pCur->pPage;
|
||||
pOldPage->idxShift = 0;
|
||||
@@ -3219,10 +3136,8 @@ static void moveToParent(BtCursor *pCur){
|
||||
pPage = pCur->pPage;
|
||||
assert( pPage!=0 );
|
||||
assert( !isRootPage(pPage) );
|
||||
pageIntegrity(pPage);
|
||||
pParent = pPage->pParent;
|
||||
assert( pParent!=0 );
|
||||
pageIntegrity(pParent);
|
||||
idxParent = pPage->idxParent;
|
||||
sqlite3pager_ref(pParent->aData);
|
||||
releasePage(pPage);
|
||||
@@ -3252,7 +3167,6 @@ static int moveToRoot(BtCursor *pCur){
|
||||
return rc;
|
||||
}
|
||||
releasePage(pCur->pPage);
|
||||
pageIntegrity(pRoot);
|
||||
pCur->pPage = pRoot;
|
||||
}
|
||||
pCur->idx = 0;
|
||||
@@ -3396,7 +3310,7 @@ int sqlite3BtreeMoveto(BtCursor *pCur, const void *pKey, i64 nKey, int *pRes){
|
||||
assert( pCur->pPage->nCell==0 );
|
||||
return SQLITE_OK;
|
||||
}
|
||||
for(;;){
|
||||
for(;;){
|
||||
int lwr, upr;
|
||||
Pgno chldPg;
|
||||
MemPage *pPage = pCur->pPage;
|
||||
@@ -3406,7 +3320,6 @@ int sqlite3BtreeMoveto(BtCursor *pCur, const void *pKey, i64 nKey, int *pRes){
|
||||
if( !pPage->intKey && pKey==0 ){
|
||||
return SQLITE_CORRUPT_BKPT;
|
||||
}
|
||||
pageIntegrity(pPage);
|
||||
while( lwr<=upr ){
|
||||
void *pCellKey;
|
||||
i64 nCellKey;
|
||||
@@ -3659,14 +3572,14 @@ static int allocatePage(
|
||||
int rc;
|
||||
int n; /* Number of pages on the freelist */
|
||||
int k; /* Number of leaves on the trunk of the freelist */
|
||||
MemPage *pTrunk = 0;
|
||||
MemPage *pPrevTrunk = 0;
|
||||
|
||||
pPage1 = pBt->pPage1;
|
||||
n = get4byte(&pPage1->aData[36]);
|
||||
if( n>0 ){
|
||||
/* There are pages on the freelist. Reuse one of those pages. */
|
||||
MemPage *pTrunk = 0;
|
||||
Pgno iTrunk;
|
||||
MemPage *pPrevTrunk = 0;
|
||||
u8 searchList = 0; /* If the free-list must be searched for 'nearby' */
|
||||
|
||||
/* If the 'exact' parameter was true and a query of the pointer-map
|
||||
@@ -3707,16 +3620,8 @@ static int allocatePage(
|
||||
}
|
||||
rc = getPage(pBt, iTrunk, &pTrunk);
|
||||
if( rc ){
|
||||
releasePage(pPrevTrunk);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* TODO: This should move to after the loop? */
|
||||
rc = sqlite3pager_write(pTrunk->aData);
|
||||
if( rc ){
|
||||
releasePage(pTrunk);
|
||||
releasePage(pPrevTrunk);
|
||||
return rc;
|
||||
pTrunk = 0;
|
||||
goto end_allocate_page;
|
||||
}
|
||||
|
||||
k = get4byte(&pTrunk->aData[4]);
|
||||
@@ -3725,6 +3630,10 @@ static int allocatePage(
|
||||
** So extract the trunk page itself and use it as the newly
|
||||
** allocated page */
|
||||
assert( pPrevTrunk==0 );
|
||||
rc = sqlite3pager_write(pTrunk->aData);
|
||||
if( rc ){
|
||||
goto end_allocate_page;
|
||||
}
|
||||
*pPgno = iTrunk;
|
||||
memcpy(&pPage1->aData[32], &pTrunk->aData[0], 4);
|
||||
*ppPage = pTrunk;
|
||||
@@ -3732,7 +3641,8 @@ static int allocatePage(
|
||||
TRACE(("ALLOCATE: %d trunk - %d free pages left\n", *pPgno, n-1));
|
||||
}else if( k>pBt->usableSize/4 - 8 ){
|
||||
/* Value of k is out of range. Database corruption */
|
||||
return SQLITE_CORRUPT_BKPT;
|
||||
rc = SQLITE_CORRUPT_BKPT;
|
||||
goto end_allocate_page;
|
||||
#ifndef SQLITE_OMIT_AUTOVACUUM
|
||||
}else if( searchList && nearby==iTrunk ){
|
||||
/* The list is being searched and this trunk page is the page
|
||||
@@ -3741,6 +3651,10 @@ static int allocatePage(
|
||||
assert( *pPgno==iTrunk );
|
||||
*ppPage = pTrunk;
|
||||
searchList = 0;
|
||||
rc = sqlite3pager_write(pTrunk->aData);
|
||||
if( rc ){
|
||||
goto end_allocate_page;
|
||||
}
|
||||
if( k==0 ){
|
||||
if( !pPrevTrunk ){
|
||||
memcpy(&pPage1->aData[32], &pTrunk->aData[0], 4);
|
||||
@@ -3756,26 +3670,26 @@ static int allocatePage(
|
||||
Pgno iNewTrunk = get4byte(&pTrunk->aData[8]);
|
||||
rc = getPage(pBt, iNewTrunk, &pNewTrunk);
|
||||
if( rc!=SQLITE_OK ){
|
||||
releasePage(pTrunk);
|
||||
releasePage(pPrevTrunk);
|
||||
return rc;
|
||||
goto end_allocate_page;
|
||||
}
|
||||
rc = sqlite3pager_write(pNewTrunk->aData);
|
||||
if( rc!=SQLITE_OK ){
|
||||
releasePage(pNewTrunk);
|
||||
releasePage(pTrunk);
|
||||
releasePage(pPrevTrunk);
|
||||
return rc;
|
||||
goto end_allocate_page;
|
||||
}
|
||||
memcpy(&pNewTrunk->aData[0], &pTrunk->aData[0], 4);
|
||||
put4byte(&pNewTrunk->aData[4], k-1);
|
||||
memcpy(&pNewTrunk->aData[8], &pTrunk->aData[12], (k-1)*4);
|
||||
releasePage(pNewTrunk);
|
||||
if( !pPrevTrunk ){
|
||||
put4byte(&pPage1->aData[32], iNewTrunk);
|
||||
}else{
|
||||
rc = sqlite3pager_write(pPrevTrunk->aData);
|
||||
if( rc ){
|
||||
goto end_allocate_page;
|
||||
}
|
||||
put4byte(&pPrevTrunk->aData[0], iNewTrunk);
|
||||
}
|
||||
releasePage(pNewTrunk);
|
||||
}
|
||||
pTrunk = 0;
|
||||
TRACE(("ALLOCATE: %d trunk - %d free pages left\n", *pPgno, n-1));
|
||||
@@ -3785,6 +3699,10 @@ static int allocatePage(
|
||||
int closest;
|
||||
Pgno iPage;
|
||||
unsigned char *aData = pTrunk->aData;
|
||||
rc = sqlite3pager_write(aData);
|
||||
if( rc ){
|
||||
goto end_allocate_page;
|
||||
}
|
||||
if( nearby>0 ){
|
||||
int i, dist;
|
||||
closest = 0;
|
||||
@@ -3828,8 +3746,8 @@ static int allocatePage(
|
||||
}
|
||||
}
|
||||
releasePage(pPrevTrunk);
|
||||
pPrevTrunk = 0;
|
||||
}while( searchList );
|
||||
releasePage(pTrunk);
|
||||
}else{
|
||||
/* There are no pages on the freelist, so create a new page at the
|
||||
** end of the file */
|
||||
@@ -3858,6 +3776,10 @@ static int allocatePage(
|
||||
}
|
||||
|
||||
assert( *pPgno!=PENDING_BYTE_PAGE(pBt) );
|
||||
|
||||
end_allocate_page:
|
||||
releasePage(pTrunk);
|
||||
releasePage(pPrevTrunk);
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -4258,7 +4180,6 @@ static int insertCell(
|
||||
put2byte(&data[ins], idx);
|
||||
put2byte(&data[hdr+3], pPage->nCell);
|
||||
pPage->idxShift = 1;
|
||||
pageIntegrity(pPage);
|
||||
#ifndef SQLITE_OMIT_AUTOVACUUM
|
||||
if( pPage->pBt->autoVacuum ){
|
||||
/* The cell may contain a pointer to an overflow page. If so, write
|
||||
@@ -4998,8 +4919,6 @@ static int balance_nonroot(MemPage *pPage){
|
||||
** But the parent page will always be initialized.
|
||||
*/
|
||||
assert( pParent->isInit );
|
||||
/* assert( pPage->isInit ); // No! pPage might have been added to freelist */
|
||||
/* pageIntegrity(pPage); // No! pPage might have been added to freelist */
|
||||
rc = balance(pParent, 0);
|
||||
|
||||
/*
|
||||
@@ -5971,6 +5890,7 @@ int sqlite3BtreePageDump(Btree *p, int pgno, int recursive){
|
||||
** aResult[7] = Header size in bytes
|
||||
** aResult[8] = Local payload size
|
||||
** aResult[9] = Parent page number
|
||||
** aResult[10]= Page number of the first overflow page
|
||||
**
|
||||
** This routine is used for testing and debugging only.
|
||||
*/
|
||||
@@ -5984,14 +5904,12 @@ int sqlite3BtreeCursorInfo(BtCursor *pCur, int *aResult, int upCnt){
|
||||
return rc;
|
||||
}
|
||||
|
||||
pageIntegrity(pPage);
|
||||
assert( pPage->isInit );
|
||||
getTempCursor(pCur, &tmpCur);
|
||||
while( upCnt-- ){
|
||||
moveToParent(&tmpCur);
|
||||
}
|
||||
pPage = tmpCur.pPage;
|
||||
pageIntegrity(pPage);
|
||||
aResult[0] = sqlite3pager_pagenumber(pPage->aData);
|
||||
assert( aResult[0]==pPage->pgno );
|
||||
aResult[1] = tmpCur.idx;
|
||||
@@ -6021,6 +5939,11 @@ int sqlite3BtreeCursorInfo(BtCursor *pCur, int *aResult, int upCnt){
|
||||
}else{
|
||||
aResult[9] = pPage->pParent->pgno;
|
||||
}
|
||||
if( tmpCur.info.iOverflow ){
|
||||
aResult[10] = get4byte(&tmpCur.info.pCell[tmpCur.info.iOverflow]);
|
||||
}else{
|
||||
aResult[10] = 0;
|
||||
}
|
||||
releaseTempCursor(&tmpCur);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
@@ -6041,10 +5964,12 @@ Pager *sqlite3BtreePager(Btree *p){
|
||||
typedef struct IntegrityCk IntegrityCk;
|
||||
struct IntegrityCk {
|
||||
BtShared *pBt; /* The tree being checked out */
|
||||
Pager *pPager; /* The associated pager. Also accessible by pBt->pPager */
|
||||
int nPage; /* Number of pages in the database */
|
||||
int *anRef; /* Number of times each page is referenced */
|
||||
char *zErrMsg; /* An error message. NULL of no errors seen. */
|
||||
Pager *pPager; /* The associated pager. Also accessible by pBt->pPager */
|
||||
int nPage; /* Number of pages in the database */
|
||||
int *anRef; /* Number of times each page is referenced */
|
||||
int mxErr; /* Stop accumulating errors when this reaches zero */
|
||||
char *zErrMsg; /* An error message. NULL if no errors seen. */
|
||||
int nErr; /* Number of messages written to zErrMsg so far */
|
||||
};
|
||||
|
||||
#ifndef SQLITE_OMIT_INTEGRITY_CHECK
|
||||
@@ -6059,6 +5984,9 @@ static void checkAppendMsg(
|
||||
){
|
||||
va_list ap;
|
||||
char *zMsg2;
|
||||
if( !pCheck->mxErr ) return;
|
||||
pCheck->mxErr--;
|
||||
pCheck->nErr++;
|
||||
va_start(ap, zFormat);
|
||||
zMsg2 = sqlite3VMPrintf(zFormat, ap);
|
||||
va_end(ap);
|
||||
@@ -6142,7 +6070,7 @@ static void checkList(
|
||||
int i;
|
||||
int expected = N;
|
||||
int iFirst = iPage;
|
||||
while( N-- > 0 ){
|
||||
while( N-- > 0 && pCheck->mxErr ){
|
||||
unsigned char *pOvfl;
|
||||
if( iPage<1 ){
|
||||
checkAppendMsg(pCheck, zContext,
|
||||
@@ -6254,7 +6182,7 @@ static int checkTreePage(
|
||||
/* Check out all the cells.
|
||||
*/
|
||||
depth = 0;
|
||||
for(i=0; i<pPage->nCell; i++){
|
||||
for(i=0; i<pPage->nCell && pCheck->mxErr; i++){
|
||||
u8 *pCell;
|
||||
int sz;
|
||||
CellInfo info;
|
||||
@@ -6369,7 +6297,13 @@ static int checkTreePage(
|
||||
** and a pointer to that error message is returned. The calling function
|
||||
** is responsible for freeing the error message when it is done.
|
||||
*/
|
||||
char *sqlite3BtreeIntegrityCheck(Btree *p, int *aRoot, int nRoot){
|
||||
char *sqlite3BtreeIntegrityCheck(
|
||||
Btree *p, /* The btree to be checked */
|
||||
int *aRoot, /* An array of root pages numbers for individual trees */
|
||||
int nRoot, /* Number of entries in aRoot[] */
|
||||
int mxErr, /* Stop reporting errors after this many */
|
||||
int *pnErr /* Write number of errors seen to this variable */
|
||||
){
|
||||
int i;
|
||||
int nRef;
|
||||
IntegrityCk sCheck;
|
||||
@@ -6382,6 +6316,9 @@ char *sqlite3BtreeIntegrityCheck(Btree *p, int *aRoot, int nRoot){
|
||||
sCheck.pBt = pBt;
|
||||
sCheck.pPager = pBt->pPager;
|
||||
sCheck.nPage = sqlite3pager_pagecount(sCheck.pPager);
|
||||
sCheck.mxErr = mxErr;
|
||||
sCheck.nErr = 0;
|
||||
*pnErr = 0;
|
||||
if( sCheck.nPage==0 ){
|
||||
unlockBtreeIfUnused(pBt);
|
||||
return 0;
|
||||
@@ -6389,6 +6326,7 @@ char *sqlite3BtreeIntegrityCheck(Btree *p, int *aRoot, int nRoot){
|
||||
sCheck.anRef = sqliteMallocRaw( (sCheck.nPage+1)*sizeof(sCheck.anRef[0]) );
|
||||
if( !sCheck.anRef ){
|
||||
unlockBtreeIfUnused(pBt);
|
||||
*pnErr = 1;
|
||||
return sqlite3MPrintf("Unable to malloc %d bytes",
|
||||
(sCheck.nPage+1)*sizeof(sCheck.anRef[0]));
|
||||
}
|
||||
@@ -6406,7 +6344,7 @@ char *sqlite3BtreeIntegrityCheck(Btree *p, int *aRoot, int nRoot){
|
||||
|
||||
/* Check all the tables.
|
||||
*/
|
||||
for(i=0; i<nRoot; i++){
|
||||
for(i=0; i<nRoot && sCheck.mxErr; i++){
|
||||
if( aRoot[i]==0 ) continue;
|
||||
#ifndef SQLITE_OMIT_AUTOVACUUM
|
||||
if( pBt->autoVacuum && aRoot[i]>1 ){
|
||||
@@ -6418,7 +6356,7 @@ char *sqlite3BtreeIntegrityCheck(Btree *p, int *aRoot, int nRoot){
|
||||
|
||||
/* Make sure every page in the file is referenced
|
||||
*/
|
||||
for(i=1; i<=sCheck.nPage; i++){
|
||||
for(i=1; i<=sCheck.nPage && sCheck.mxErr; i++){
|
||||
#ifdef SQLITE_OMIT_AUTOVACUUM
|
||||
if( sCheck.anRef[i]==0 ){
|
||||
checkAppendMsg(&sCheck, 0, "Page %d is never used", i);
|
||||
@@ -6451,6 +6389,7 @@ char *sqlite3BtreeIntegrityCheck(Btree *p, int *aRoot, int nRoot){
|
||||
/* Clean up and report errors.
|
||||
*/
|
||||
sqliteFree(sCheck.anRef);
|
||||
*pnErr = sCheck.nErr;
|
||||
return sCheck.zErrMsg;
|
||||
}
|
||||
#endif /* SQLITE_OMIT_INTEGRITY_CHECK */
|
||||
@@ -6509,7 +6448,6 @@ int sqlite3BtreeCopyFile(Btree *pTo, Btree *pFrom){
|
||||
rc = sqlite3pager_get(pBtFrom->pPager, i, &pPage);
|
||||
if( rc ) break;
|
||||
rc = sqlite3pager_overwrite(pBtTo->pPager, i, pPage);
|
||||
if( rc ) break;
|
||||
sqlite3pager_unref(pPage);
|
||||
}
|
||||
for(i=nPage+1; rc==SQLITE_OK && i<=nToPage; i++){
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
** subsystem. See comments in the source code for a detailed description
|
||||
** of what each interface routine does.
|
||||
**
|
||||
** @(#) $Id: btree.h,v 1.71 2006/06/27 16:34:57 danielk1977 Exp $
|
||||
** @(#) $Id: btree.h,v 1.72 2007/01/27 02:24:55 drh Exp $
|
||||
*/
|
||||
#ifndef _BTREE_H_
|
||||
#define _BTREE_H_
|
||||
@@ -131,7 +131,7 @@ const void *sqlite3BtreeDataFetch(BtCursor*, int *pAmt);
|
||||
int sqlite3BtreeDataSize(BtCursor*, u32 *pSize);
|
||||
int sqlite3BtreeData(BtCursor*, u32 offset, u32 amt, void*);
|
||||
|
||||
char *sqlite3BtreeIntegrityCheck(Btree*, int *aRoot, int nRoot);
|
||||
char *sqlite3BtreeIntegrityCheck(Btree*, int *aRoot, int nRoot, int, int*);
|
||||
struct Pager *sqlite3BtreePager(Btree*);
|
||||
|
||||
|
||||
|
||||
+80
-11
@@ -22,7 +22,7 @@
|
||||
** COMMIT
|
||||
** ROLLBACK
|
||||
**
|
||||
** $Id: build.c,v 1.411 2006/09/11 23:45:49 drh Exp $
|
||||
** $Id: build.c,v 1.413 2007/02/02 12:44:37 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include <ctype.h>
|
||||
@@ -1222,6 +1222,10 @@ void sqlite3AddCollateType(Parse *pParse, const char *zType, int nType){
|
||||
** If no versions of the requested collations sequence are available, or
|
||||
** another error occurs, NULL is returned and an error message written into
|
||||
** pParse.
|
||||
**
|
||||
** This routine is a wrapper around sqlite3FindCollSeq(). This routine
|
||||
** invokes the collation factory if the named collation cannot be found
|
||||
** and generates an error message.
|
||||
*/
|
||||
CollSeq *sqlite3LocateCollSeq(Parse *pParse, const char *zName, int nName){
|
||||
sqlite3 *db = pParse->db;
|
||||
@@ -2457,7 +2461,7 @@ void sqlite3CreateIndex(
|
||||
const char *zColName = pListItem->zName;
|
||||
Column *pTabCol;
|
||||
int requestedSortOrder;
|
||||
char *zColl; /* Collation sequence */
|
||||
char *zColl; /* Collation sequence name */
|
||||
|
||||
for(j=0, pTabCol=pTab->aCol; j<pTab->nCol; j++, pTabCol++){
|
||||
if( sqlite3StrICmp(zColName, pTabCol->zName)==0 ) break;
|
||||
@@ -2467,6 +2471,12 @@ void sqlite3CreateIndex(
|
||||
pTab->zName, zColName);
|
||||
goto exit_create_index;
|
||||
}
|
||||
/* TODO: Add a test to make sure that the same column is not named
|
||||
** more than once within the same index. Only the first instance of
|
||||
** the column will ever be used by the optimizer. Note that using the
|
||||
** same column more than once cannot be an error because that would
|
||||
** break backwards compatibility - it needs to be a warning.
|
||||
*/
|
||||
pIndex->aiColumn[i] = j;
|
||||
if( pListItem->pExpr ){
|
||||
assert( pListItem->pExpr->pColl );
|
||||
@@ -2940,15 +2950,6 @@ void sqlite3SrcListAssignCursors(Parse *pParse, SrcList *pList){
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Add an alias to the last identifier on the given identifier list.
|
||||
*/
|
||||
void sqlite3SrcListAddAlias(SrcList *pList, Token *pToken){
|
||||
if( pList && pList->nSrc>0 ){
|
||||
pList->a[pList->nSrc-1].zAlias = sqlite3NameFromToken(pToken);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Delete an entire SrcList including all its substructure.
|
||||
*/
|
||||
@@ -2968,6 +2969,74 @@ void sqlite3SrcListDelete(SrcList *pList){
|
||||
sqliteFree(pList);
|
||||
}
|
||||
|
||||
/*
|
||||
** This routine is called by the parser to add a new term to the
|
||||
** end of a growing FROM clause. The "p" parameter is the part of
|
||||
** the FROM clause that has already been constructed. "p" is NULL
|
||||
** if this is the first term of the FROM clause. pTable and pDatabase
|
||||
** are the name of the table and database named in the FROM clause term.
|
||||
** pDatabase is NULL if the database name qualifier is missing - the
|
||||
** usual case. If the term has a alias, then pAlias points to the
|
||||
** alias token. If the term is a subquery, then pSubquery is the
|
||||
** SELECT statement that the subquery encodes. The pTable and
|
||||
** pDatabase parameters are NULL for subqueries. The pOn and pUsing
|
||||
** parameters are the content of the ON and USING clauses.
|
||||
**
|
||||
** Return a new SrcList which encodes is the FROM with the new
|
||||
** term added.
|
||||
*/
|
||||
SrcList *sqlite3SrcListAppendFromTerm(
|
||||
SrcList *p, /* The left part of the FROM clause already seen */
|
||||
Token *pTable, /* Name of the table to add to the FROM clause */
|
||||
Token *pDatabase, /* Name of the database containing pTable */
|
||||
Token *pAlias, /* The right-hand side of the AS subexpression */
|
||||
Select *pSubquery, /* A subquery used in place of a table name */
|
||||
Expr *pOn, /* The ON clause of a join */
|
||||
IdList *pUsing /* The USING clause of a join */
|
||||
){
|
||||
struct SrcList_item *pItem;
|
||||
p = sqlite3SrcListAppend(p, pTable, pDatabase);
|
||||
if( p==0 || p->nSrc==0 ){
|
||||
sqlite3ExprDelete(pOn);
|
||||
sqlite3IdListDelete(pUsing);
|
||||
sqlite3SelectDelete(pSubquery);
|
||||
return p;
|
||||
}
|
||||
pItem = &p->a[p->nSrc-1];
|
||||
if( pAlias && pAlias->n ){
|
||||
pItem->zAlias = sqlite3NameFromToken(pAlias);
|
||||
}
|
||||
pItem->pSelect = pSubquery;
|
||||
pItem->pOn = pOn;
|
||||
pItem->pUsing = pUsing;
|
||||
return p;
|
||||
}
|
||||
|
||||
/*
|
||||
** When building up a FROM clause in the parser, the join operator
|
||||
** is initially attached to the left operand. But the code generator
|
||||
** expects the join operator to be on the right operand. This routine
|
||||
** Shifts all join operators from left to right for an entire FROM
|
||||
** clause.
|
||||
**
|
||||
** Example: Suppose the join is like this:
|
||||
**
|
||||
** A natural cross join B
|
||||
**
|
||||
** The operator is "natural cross join". The A and B operands are stored
|
||||
** in p->a[0] and p->a[1], respectively. The parser initially stores the
|
||||
** operator with A. This routine shifts that operator over to B.
|
||||
*/
|
||||
void sqlite3SrcListShiftJoinType(SrcList *p){
|
||||
if( p && p->a ){
|
||||
int i;
|
||||
for(i=p->nSrc-1; i>0; i--){
|
||||
p->a[i].jointype = p->a[i-1].jointype;
|
||||
}
|
||||
p->a[0].jointype = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Begin a transaction
|
||||
*/
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
** This file contains functions used to access the internal hash tables
|
||||
** of user defined functions and collation sequences.
|
||||
**
|
||||
** $Id: callback.c,v 1.15 2006/05/24 12:43:27 drh Exp $
|
||||
** $Id: callback.c,v 1.16 2007/02/02 12:44:37 drh Exp $
|
||||
*/
|
||||
|
||||
#include "sqliteInt.h"
|
||||
@@ -195,6 +195,11 @@ static CollSeq *findCollSeqEntry(
|
||||
**
|
||||
** If the entry specified is not found and 'create' is true, then create a
|
||||
** new entry. Otherwise return NULL.
|
||||
**
|
||||
** A separate function sqlite3LocateCollSeq() is a wrapper around
|
||||
** this routine. sqlite3LocateCollSeq() invokes the collation factory
|
||||
** if necessary and generates an error message if the collating sequence
|
||||
** cannot be found.
|
||||
*/
|
||||
CollSeq *sqlite3FindCollSeq(
|
||||
sqlite3 *db,
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
** sqlite3RegisterDateTimeFunctions() found at the bottom of the file.
|
||||
** All other code has file scope.
|
||||
**
|
||||
** $Id: date.c,v 1.58 2006/09/25 18:05:04 drh Exp $
|
||||
** $Id: date.c,v 1.60 2007/01/08 16:19:07 drh Exp $
|
||||
**
|
||||
** NOTES:
|
||||
**
|
||||
@@ -840,7 +840,7 @@ static void strftimeFunc(
|
||||
y.M = 1;
|
||||
y.D = 1;
|
||||
computeJD(&y);
|
||||
nDay = x.rJD - y.rJD;
|
||||
nDay = x.rJD - y.rJD + 0.5;
|
||||
if( zFmt[i]=='W' ){
|
||||
int wd; /* 0=Monday, 1=Tuesday, ... 6=Sunday */
|
||||
wd = ((int)(x.rJD+0.5)) % 7;
|
||||
@@ -860,7 +860,7 @@ static void strftimeFunc(
|
||||
j += strlen(&z[j]);
|
||||
break;
|
||||
}
|
||||
case 'S': sprintf(&z[j],"%02d",(int)(x.s+0.5)); j+=2; break;
|
||||
case 'S': sprintf(&z[j],"%02d",(int)x.s); j+=2; break;
|
||||
case 'w': z[j++] = (((int)(x.rJD+1.5)) % 7) + '0'; break;
|
||||
case 'Y': sprintf(&z[j],"%04d",x.Y); j+=strlen(&z[j]); break;
|
||||
case '%': z[j++] = '%'; break;
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
** This file contains C code routines that are called by the parser
|
||||
** in order to generate code for DELETE FROM statements.
|
||||
**
|
||||
** $Id: delete.c,v 1.127 2006/06/19 03:05:10 danielk1977 Exp $
|
||||
** $Id: delete.c,v 1.128 2007/02/07 01:06:53 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
@@ -106,7 +106,8 @@ void sqlite3DeleteFrom(
|
||||
AuthContext sContext; /* Authorization context */
|
||||
int oldIdx = -1; /* Cursor for the OLD table of AFTER triggers */
|
||||
NameContext sNC; /* Name context to resolve expressions in */
|
||||
int iDb;
|
||||
int iDb; /* Database number */
|
||||
int memCnt = 0; /* Memory cell used for change counting */
|
||||
|
||||
#ifndef SQLITE_OMIT_TRIGGER
|
||||
int isView; /* True if attempting to delete from a view */
|
||||
@@ -204,7 +205,8 @@ void sqlite3DeleteFrom(
|
||||
** we are counting rows.
|
||||
*/
|
||||
if( db->flags & SQLITE_CountRows ){
|
||||
sqlite3VdbeAddOp(v, OP_Integer, 0, 0);
|
||||
memCnt = pParse->nMem++;
|
||||
sqlite3VdbeAddOp(v, OP_MemInt, 0, memCnt);
|
||||
}
|
||||
|
||||
/* Special case: A DELETE without a WHERE clause deletes everything.
|
||||
@@ -221,7 +223,7 @@ void sqlite3DeleteFrom(
|
||||
sqlite3OpenTable(pParse, iCur, iDb, pTab, OP_OpenRead);
|
||||
}
|
||||
sqlite3VdbeAddOp(v, OP_Rewind, iCur, sqlite3VdbeCurrentAddr(v)+2);
|
||||
addr2 = sqlite3VdbeAddOp(v, OP_AddImm, 1, 0);
|
||||
addr2 = sqlite3VdbeAddOp(v, OP_MemIncr, 1, memCnt);
|
||||
sqlite3VdbeAddOp(v, OP_Next, iCur, addr2);
|
||||
sqlite3VdbeResolveLabel(v, endOfLoop);
|
||||
sqlite3VdbeAddOp(v, OP_Close, iCur, 0);
|
||||
@@ -251,7 +253,7 @@ void sqlite3DeleteFrom(
|
||||
sqlite3VdbeAddOp(v, IsVirtual(pTab) ? OP_VRowid : OP_Rowid, iCur, 0);
|
||||
sqlite3VdbeAddOp(v, OP_FifoWrite, 0, 0);
|
||||
if( db->flags & SQLITE_CountRows ){
|
||||
sqlite3VdbeAddOp(v, OP_AddImm, 1, 0);
|
||||
sqlite3VdbeAddOp(v, OP_MemIncr, 1, memCnt);
|
||||
}
|
||||
|
||||
/* End the database scan loop.
|
||||
@@ -354,6 +356,7 @@ void sqlite3DeleteFrom(
|
||||
** invoke the callback function.
|
||||
*/
|
||||
if( db->flags & SQLITE_CountRows && pParse->nested==0 && !pParse->trigStack ){
|
||||
sqlite3VdbeAddOp(v, OP_MemLoad, memCnt, 0);
|
||||
sqlite3VdbeAddOp(v, OP_Callback, 1, 0);
|
||||
sqlite3VdbeSetNumCols(v, 1);
|
||||
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "rows deleted", P3_STATIC);
|
||||
|
||||
+70
-25
@@ -12,7 +12,7 @@
|
||||
** This file contains routines used for analyzing expressions and
|
||||
** for generating VDBE code that evaluates expressions in SQLite.
|
||||
**
|
||||
** $Id: expr.c,v 1.268 2006/08/24 15:18:25 drh Exp $
|
||||
** $Id: expr.c,v 1.275 2007/02/07 13:09:46 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include <ctype.h>
|
||||
@@ -49,6 +49,24 @@ char sqlite3ExprAffinity(Expr *pExpr){
|
||||
return pExpr->affinity;
|
||||
}
|
||||
|
||||
/*
|
||||
** Set the collating sequence for expression pExpr to be the collating
|
||||
** sequence named by pToken. Return a pointer to the revised expression.
|
||||
** The collating sequence is marked as "explicit" using the EP_ExpCollate
|
||||
** flag. An explicit collating sequence will override implicit
|
||||
** collating sequences.
|
||||
*/
|
||||
Expr *sqlite3ExprSetColl(Parse *pParse, Expr *pExpr, Token *pName){
|
||||
CollSeq *pColl;
|
||||
if( pExpr==0 ) return 0;
|
||||
pColl = sqlite3LocateCollSeq(pParse, (char*)pName->z, pName->n);
|
||||
if( pColl ){
|
||||
pExpr->pColl = pColl;
|
||||
pExpr->flags |= EP_ExpCollate;
|
||||
}
|
||||
return pExpr;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the default collation sequence for the expression pExpr. If
|
||||
** there is no default collation type, return 0.
|
||||
@@ -158,9 +176,20 @@ static int binaryCompareP1(Expr *pExpr1, Expr *pExpr2, int jumpIfNull){
|
||||
** type.
|
||||
*/
|
||||
static CollSeq* binaryCompareCollSeq(Parse *pParse, Expr *pLeft, Expr *pRight){
|
||||
CollSeq *pColl = sqlite3ExprCollSeq(pParse, pLeft);
|
||||
if( !pColl ){
|
||||
pColl = sqlite3ExprCollSeq(pParse, pRight);
|
||||
CollSeq *pColl;
|
||||
assert( pLeft );
|
||||
assert( pRight );
|
||||
if( pLeft->flags & EP_ExpCollate ){
|
||||
assert( pLeft->pColl );
|
||||
pColl = pLeft->pColl;
|
||||
}else if( pRight->flags & EP_ExpCollate ){
|
||||
assert( pRight->pColl );
|
||||
pColl = pRight->pColl;
|
||||
}else{
|
||||
pColl = sqlite3ExprCollSeq(pParse, pLeft);
|
||||
if( !pColl ){
|
||||
pColl = sqlite3ExprCollSeq(pParse, pRight);
|
||||
}
|
||||
}
|
||||
return pColl;
|
||||
}
|
||||
@@ -205,8 +234,18 @@ Expr *sqlite3Expr(int op, Expr *pLeft, Expr *pRight, const Token *pToken){
|
||||
if( pToken ){
|
||||
assert( pToken->dyn==0 );
|
||||
pNew->span = pNew->token = *pToken;
|
||||
}else if( pLeft && pRight ){
|
||||
sqlite3ExprSpan(pNew, &pLeft->span, &pRight->span);
|
||||
}else if( pLeft ){
|
||||
if( pRight ){
|
||||
sqlite3ExprSpan(pNew, &pLeft->span, &pRight->span);
|
||||
if( pRight->flags && EP_ExpCollate ){
|
||||
pNew->flags |= EP_ExpCollate;
|
||||
pNew->pColl = pRight->pColl;
|
||||
}
|
||||
}
|
||||
if( pLeft->flags && EP_ExpCollate ){
|
||||
pNew->flags |= EP_ExpCollate;
|
||||
pNew->pColl = pLeft->pColl;
|
||||
}
|
||||
}
|
||||
return pNew;
|
||||
}
|
||||
@@ -890,23 +929,26 @@ static int lookupName(
|
||||
/* Substitute the rowid (column -1) for the INTEGER PRIMARY KEY */
|
||||
pExpr->iColumn = j==pTab->iPKey ? -1 : j;
|
||||
pExpr->affinity = pTab->aCol[j].affinity;
|
||||
pExpr->pColl = sqlite3FindCollSeq(db, ENC(db), zColl,-1, 0);
|
||||
if( pItem->jointype & JT_NATURAL ){
|
||||
/* If this match occurred in the left table of a natural join,
|
||||
** then skip the right table to avoid a duplicate match */
|
||||
pItem++;
|
||||
i++;
|
||||
if( (pExpr->flags & EP_ExpCollate)==0 ){
|
||||
pExpr->pColl = sqlite3FindCollSeq(db, ENC(db), zColl,-1, 0);
|
||||
}
|
||||
if( (pUsing = pItem->pUsing)!=0 ){
|
||||
/* If this match occurs on a column that is in the USING clause
|
||||
** of a join, skip the search of the right table of the join
|
||||
** to avoid a duplicate match there. */
|
||||
int k;
|
||||
for(k=0; k<pUsing->nId; k++){
|
||||
if( sqlite3StrICmp(pUsing->a[k].zName, zCol)==0 ){
|
||||
pItem++;
|
||||
i++;
|
||||
break;
|
||||
if( i<pSrcList->nSrc-1 ){
|
||||
if( pItem[1].jointype & JT_NATURAL ){
|
||||
/* If this match occurred in the left table of a natural join,
|
||||
** then skip the right table to avoid a duplicate match */
|
||||
pItem++;
|
||||
i++;
|
||||
}else if( (pUsing = pItem[1].pUsing)!=0 ){
|
||||
/* If this match occurs on a column that is in the USING clause
|
||||
** of a join, skip the search of the right table of the join
|
||||
** to avoid a duplicate match there. */
|
||||
int k;
|
||||
for(k=0; k<pUsing->nId; k++){
|
||||
if( sqlite3StrICmp(pUsing->a[k].zName, zCol)==0 ){
|
||||
pItem++;
|
||||
i++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -945,7 +987,9 @@ static int lookupName(
|
||||
cnt++;
|
||||
pExpr->iColumn = iCol==pTab->iPKey ? -1 : iCol;
|
||||
pExpr->affinity = pTab->aCol[iCol].affinity;
|
||||
pExpr->pColl = sqlite3FindCollSeq(db, ENC(db), zColl,-1, 0);
|
||||
if( (pExpr->flags & EP_ExpCollate)==0 ){
|
||||
pExpr->pColl = sqlite3FindCollSeq(db, ENC(db), zColl,-1, 0);
|
||||
}
|
||||
pExpr->pTab = pTab;
|
||||
break;
|
||||
}
|
||||
@@ -1045,7 +1089,7 @@ static int lookupName(
|
||||
n = sizeof(Bitmask)*8-1;
|
||||
}
|
||||
assert( pMatch->iCursor==pExpr->iTable );
|
||||
pMatch->colUsed |= 1<<n;
|
||||
pMatch->colUsed |= ((Bitmask)1)<<n;
|
||||
}
|
||||
|
||||
lookupname_end:
|
||||
@@ -1180,7 +1224,7 @@ static int nameResolverStep(void *pArg, Expr *pExpr){
|
||||
}else{
|
||||
is_agg = pDef->xFunc==0;
|
||||
}
|
||||
#ifndef SQLITE_OMIT_AUTHORIZER
|
||||
#ifndef SQLITE_OMIT_AUTHORIZATION
|
||||
if( pDef ){
|
||||
auth = sqlite3AuthCheck(pParse, SQLITE_FUNCTION, 0, pDef->zName, 0);
|
||||
if( auth!=SQLITE_OK ){
|
||||
@@ -2207,6 +2251,7 @@ static int analyzeAggregate(void *pArg, Expr *pExpr){
|
||||
|
||||
|
||||
switch( pExpr->op ){
|
||||
case TK_AGG_COLUMN:
|
||||
case TK_COLUMN: {
|
||||
/* Check to see if the column is in one of the tables in the FROM
|
||||
** clause of the aggregate query */
|
||||
|
||||
+59
-6
@@ -16,7 +16,7 @@
|
||||
** sqliteRegisterBuildinFunctions() found at the bottom of the file.
|
||||
** All other code has file scope.
|
||||
**
|
||||
** $Id: func.c,v 1.134 2006/09/16 21:45:14 drh Exp $
|
||||
** $Id: func.c,v 1.136 2007/01/29 17:58:28 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include <ctype.h>
|
||||
@@ -272,6 +272,25 @@ static void randomFunc(
|
||||
sqlite3_result_int64(context, r);
|
||||
}
|
||||
|
||||
/*
|
||||
** Implementation of randomblob(N). Return a random blob
|
||||
** that is N bytes long.
|
||||
*/
|
||||
static void randomBlob(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
int n;
|
||||
unsigned char *p;
|
||||
assert( argc==1 );
|
||||
n = sqlite3_value_int(argv[0]);
|
||||
if( n<1 ) n = 1;
|
||||
p = sqlite3_malloc(n);
|
||||
sqlite3Randomness(n, p);
|
||||
sqlite3_result_blob(context, (char*)p, n, sqlite3_free);
|
||||
}
|
||||
|
||||
/*
|
||||
** Implementation of the last_insert_rowid() SQL function. The return
|
||||
** value is the same as the sqlite3_last_insert_rowid() API function.
|
||||
@@ -548,6 +567,12 @@ static void versionFunc(
|
||||
sqlite3_result_text(context, sqlite3_version, -1, SQLITE_STATIC);
|
||||
}
|
||||
|
||||
/* Array for converting from half-bytes (nybbles) into ASCII hex
|
||||
** digits. */
|
||||
static const char hexdigits[] = {
|
||||
'0', '1', '2', '3', '4', '5', '6', '7',
|
||||
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
|
||||
};
|
||||
|
||||
/*
|
||||
** EXPERIMENTAL - This is not an official function. The interface may
|
||||
@@ -573,10 +598,6 @@ static void quoteFunc(sqlite3_context *context, int argc, sqlite3_value **argv){
|
||||
break;
|
||||
}
|
||||
case SQLITE_BLOB: {
|
||||
static const char hexdigits[] = {
|
||||
'0', '1', '2', '3', '4', '5', '6', '7',
|
||||
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
|
||||
};
|
||||
char *zText = 0;
|
||||
int nBlob = sqlite3_value_bytes(argv[0]);
|
||||
char const *zBlob = sqlite3_value_blob(argv[0]);
|
||||
@@ -622,11 +643,41 @@ static void quoteFunc(sqlite3_context *context, int argc, sqlite3_value **argv){
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** The hex() function. Interpret the argument as a blob. Return
|
||||
** a hexadecimal rendering as text.
|
||||
*/
|
||||
static void hexFunc(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
int i, n;
|
||||
const unsigned char *pBlob;
|
||||
char *zHex, *z;
|
||||
assert( argc==1 );
|
||||
pBlob = sqlite3_value_blob(argv[0]);
|
||||
n = sqlite3_value_bytes(argv[0]);
|
||||
z = zHex = sqlite3_malloc(n*2 + 1);
|
||||
if( zHex==0 ) return;
|
||||
for(i=0; i<n; i++, pBlob++){
|
||||
unsigned char c = *pBlob;
|
||||
*(z++) = hexdigits[(c>>4)&0xf];
|
||||
*(z++) = hexdigits[c&0xf];
|
||||
}
|
||||
*z = 0;
|
||||
sqlite3_result_text(context, zHex, n*2, sqlite3_free);
|
||||
}
|
||||
|
||||
#ifdef SQLITE_SOUNDEX
|
||||
/*
|
||||
** Compute the soundex encoding of a word.
|
||||
*/
|
||||
static void soundexFunc(sqlite3_context *context, int argc, sqlite3_value **argv){
|
||||
static void soundexFunc(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
char zResult[8];
|
||||
const u8 *zIn;
|
||||
int i, j;
|
||||
@@ -1022,8 +1073,10 @@ void sqlite3RegisterBuiltinFunctions(sqlite3 *db){
|
||||
{ "coalesce", -1, 0, SQLITE_UTF8, 0, ifnullFunc },
|
||||
{ "coalesce", 0, 0, SQLITE_UTF8, 0, 0 },
|
||||
{ "coalesce", 1, 0, SQLITE_UTF8, 0, 0 },
|
||||
{ "hex", 1, 0, SQLITE_UTF8, 0, hexFunc },
|
||||
{ "ifnull", 2, 0, SQLITE_UTF8, 1, ifnullFunc },
|
||||
{ "random", -1, 0, SQLITE_UTF8, 0, randomFunc },
|
||||
{ "randomblob", 1, 0, SQLITE_UTF8, 0, randomBlob },
|
||||
{ "nullif", 2, 0, SQLITE_UTF8, 1, nullifFunc },
|
||||
{ "sqlite_version", 0, 0, SQLITE_UTF8, 0, versionFunc},
|
||||
{ "quote", 1, 0, SQLITE_UTF8, 0, quoteFunc },
|
||||
|
||||
+23
-40
@@ -75,6 +75,20 @@
|
||||
# define sqlite3_declare_vtab 0
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_OMIT_SHARED_CACHE
|
||||
# define sqlite3_enable_shared_cache 0
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_OMIT_TRACE
|
||||
# define sqlite3_profile 0
|
||||
# define sqlite3_trace 0
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_OMIT_GET_TABLE
|
||||
# define sqlite3_free_table 0
|
||||
# define sqlite3_get_table 0
|
||||
#endif
|
||||
|
||||
/*
|
||||
** The following structure contains pointers to all SQLite API routines.
|
||||
** A pointer to this structure is passed into extensions when they are
|
||||
@@ -154,7 +168,7 @@ const sqlite3_api_routines sqlite3_apis = {
|
||||
sqlite3_get_autocommit,
|
||||
sqlite3_get_auxdata,
|
||||
sqlite3_get_table,
|
||||
sqlite3_global_recover,
|
||||
0, /* Was sqlite3_global_recover(), but that function is deprecated */
|
||||
sqlite3_interrupt,
|
||||
sqlite3_last_insert_rowid,
|
||||
sqlite3_libversion,
|
||||
@@ -217,28 +231,6 @@ const sqlite3_api_routines sqlite3_apis = {
|
||||
sqlite3_overload_function,
|
||||
};
|
||||
|
||||
/*
|
||||
** The windows implementation of shared-library loaders
|
||||
*/
|
||||
#if defined(_WIN32) || defined(WIN32) || defined(__MINGW32__) || defined(__BORLANDC__)
|
||||
# include <windows.h>
|
||||
# define SQLITE_LIBRARY_TYPE HANDLE
|
||||
# define SQLITE_OPEN_LIBRARY(A) LoadLibrary(A)
|
||||
# define SQLITE_FIND_SYMBOL(A,B) GetProcAddress(A,B)
|
||||
# define SQLITE_CLOSE_LIBRARY(A) FreeLibrary(A)
|
||||
#endif /* windows */
|
||||
|
||||
/*
|
||||
** The unix implementation of shared-library loaders
|
||||
*/
|
||||
#if defined(HAVE_DLOPEN) && !defined(SQLITE_LIBRARY_TYPE)
|
||||
# include <dlfcn.h>
|
||||
# define SQLITE_LIBRARY_TYPE void*
|
||||
# define SQLITE_OPEN_LIBRARY(A) dlopen(A, RTLD_NOW | RTLD_GLOBAL)
|
||||
# define SQLITE_FIND_SYMBOL(A,B) dlsym(A,B)
|
||||
# define SQLITE_CLOSE_LIBRARY(A) dlclose(A)
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Attempt to load an SQLite extension library contained in the file
|
||||
** zFile. The entry point is zProc. zProc may be 0 in which case a
|
||||
@@ -257,11 +249,10 @@ int sqlite3_load_extension(
|
||||
const char *zProc, /* Entry point. Use "sqlite3_extension_init" if 0 */
|
||||
char **pzErrMsg /* Put error message here if not 0 */
|
||||
){
|
||||
#ifdef SQLITE_LIBRARY_TYPE
|
||||
SQLITE_LIBRARY_TYPE handle;
|
||||
void *handle;
|
||||
int (*xInit)(sqlite3*,char**,const sqlite3_api_routines*);
|
||||
char *zErrmsg = 0;
|
||||
SQLITE_LIBRARY_TYPE *aHandle;
|
||||
void **aHandle;
|
||||
|
||||
/* Ticket #1863. To avoid a creating security problems for older
|
||||
** applications that relink against newer versions of SQLite, the
|
||||
@@ -280,7 +271,7 @@ int sqlite3_load_extension(
|
||||
zProc = "sqlite3_extension_init";
|
||||
}
|
||||
|
||||
handle = SQLITE_OPEN_LIBRARY(zFile);
|
||||
handle = sqlite3OsDlopen(zFile);
|
||||
if( handle==0 ){
|
||||
if( pzErrMsg ){
|
||||
*pzErrMsg = sqlite3_mprintf("unable to open shared library [%s]", zFile);
|
||||
@@ -288,20 +279,20 @@ int sqlite3_load_extension(
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
xInit = (int(*)(sqlite3*,char**,const sqlite3_api_routines*))
|
||||
SQLITE_FIND_SYMBOL(handle, zProc);
|
||||
sqlite3OsDlsym(handle, zProc);
|
||||
if( xInit==0 ){
|
||||
if( pzErrMsg ){
|
||||
*pzErrMsg = sqlite3_mprintf("no entry point [%s] in shared library [%s]",
|
||||
zProc, zFile);
|
||||
}
|
||||
SQLITE_CLOSE_LIBRARY(handle);
|
||||
sqlite3OsDlclose(handle);
|
||||
return SQLITE_ERROR;
|
||||
}else if( xInit(db, &zErrmsg, &sqlite3_apis) ){
|
||||
if( pzErrMsg ){
|
||||
*pzErrMsg = sqlite3_mprintf("error during initialization: %s", zErrmsg);
|
||||
}
|
||||
sqlite3_free(zErrmsg);
|
||||
SQLITE_CLOSE_LIBRARY(handle);
|
||||
sqlite3OsDlclose(handle);
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
|
||||
@@ -317,14 +308,8 @@ int sqlite3_load_extension(
|
||||
sqliteFree(db->aExtension);
|
||||
db->aExtension = aHandle;
|
||||
|
||||
((SQLITE_LIBRARY_TYPE*)db->aExtension)[db->nExtension-1] = handle;
|
||||
db->aExtension[db->nExtension-1] = handle;
|
||||
return SQLITE_OK;
|
||||
#else
|
||||
if( pzErrMsg ){
|
||||
*pzErrMsg = sqlite3_mprintf("extension loading is disabled");
|
||||
}
|
||||
return SQLITE_ERROR;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -332,13 +317,11 @@ int sqlite3_load_extension(
|
||||
** to clean up loaded extensions
|
||||
*/
|
||||
void sqlite3CloseExtensions(sqlite3 *db){
|
||||
#ifdef SQLITE_LIBRARY_TYPE
|
||||
int i;
|
||||
for(i=0; i<db->nExtension; i++){
|
||||
SQLITE_CLOSE_LIBRARY(((SQLITE_LIBRARY_TYPE*)db->aExtension)[i]);
|
||||
sqlite3OsDlclose(db->aExtension[i]);
|
||||
}
|
||||
sqliteFree(db->aExtension);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
** other files are for internal use by SQLite and should not be
|
||||
** accessed by users of the library.
|
||||
**
|
||||
** $Id: main.c,v 1.358 2006/09/16 21:45:14 drh Exp $
|
||||
** $Id: main.c,v 1.360 2006/12/19 18:57:11 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include "os.h"
|
||||
@@ -942,7 +942,7 @@ static int openDatabase(
|
||||
/* Load automatic extensions - extensions that have been registered
|
||||
** using the sqlite3_automatic_extension() API.
|
||||
*/
|
||||
sqlite3AutoLoadExtensions(db);
|
||||
(void)sqlite3AutoLoadExtensions(db);
|
||||
|
||||
#ifdef SQLITE_ENABLE_FTS1
|
||||
{
|
||||
@@ -951,6 +951,13 @@ static int openDatabase(
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_ENABLE_FTS2
|
||||
{
|
||||
extern int sqlite3Fts2Init(sqlite3*);
|
||||
sqlite3Fts2Init(db);
|
||||
}
|
||||
#endif
|
||||
|
||||
opendb_out:
|
||||
if( SQLITE_NOMEM==(rc = sqlite3_errcode(db)) ){
|
||||
sqlite3_close(db);
|
||||
|
||||
+40
-3
@@ -81,9 +81,21 @@
|
||||
** prefix to reflect your program's name, so that if your program exits
|
||||
** prematurely, old temporary files can be easily identified. This can be done
|
||||
** using -DTEMP_FILE_PREFIX=myprefix_ on the compiler command line.
|
||||
**
|
||||
** 2006-10-31: The default prefix used to be "sqlite_". But then
|
||||
** Mcafee started using SQLite in their anti-virus product and it
|
||||
** started putting files with the "sqlite" name in the c:/temp folder.
|
||||
** This annoyed many windows users. Those users would then do a
|
||||
** Google search for "sqlite", find the telephone numbers of the
|
||||
** developers and call to wake them up at night and complain.
|
||||
** For this reason, the default name prefix is changed to be "sqlite"
|
||||
** spelled backwards. So the temp files are still identified, but
|
||||
** anybody smart enough to figure out the code is also likely smart
|
||||
** enough to know that calling the developer will not help get rid
|
||||
** of the file.
|
||||
*/
|
||||
#ifndef TEMP_FILE_PREFIX
|
||||
# define TEMP_FILE_PREFIX "sqlite_"
|
||||
# define TEMP_FILE_PREFIX "etilqs_"
|
||||
#endif
|
||||
|
||||
/*
|
||||
@@ -110,6 +122,9 @@
|
||||
#define sqlite3OsRealloc sqlite3GenericRealloc
|
||||
#define sqlite3OsFree sqlite3GenericFree
|
||||
#define sqlite3OsAllocationSize sqlite3GenericAllocationSize
|
||||
#define sqlite3OsDlopen sqlite3UnixDlopen
|
||||
#define sqlite3OsDlsym sqlite3UnixDlsym
|
||||
#define sqlite3OsDlclose sqlite3UnixDlclose
|
||||
#endif
|
||||
#if OS_WIN
|
||||
#define sqlite3OsOpenReadWrite sqlite3WinOpenReadWrite
|
||||
@@ -132,6 +147,9 @@
|
||||
#define sqlite3OsRealloc sqlite3GenericRealloc
|
||||
#define sqlite3OsFree sqlite3GenericFree
|
||||
#define sqlite3OsAllocationSize sqlite3GenericAllocationSize
|
||||
#define sqlite3OsDlopen sqlite3WinDlopen
|
||||
#define sqlite3OsDlsym sqlite3WinDlsym
|
||||
#define sqlite3OsDlclose sqlite3WinDlclose
|
||||
#endif
|
||||
#if OS_OS2
|
||||
#define sqlite3OsOpenReadWrite sqlite3Os2OpenReadWrite
|
||||
@@ -154,6 +172,9 @@
|
||||
#define sqlite3OsRealloc sqlite3GenericRealloc
|
||||
#define sqlite3OsFree sqlite3GenericFree
|
||||
#define sqlite3OsAllocationSize sqlite3GenericAllocationSize
|
||||
#define sqlite3OsDlopen sqlite3Os2Dlopen
|
||||
#define sqlite3OsDlsym sqlite3Os2Dlsym
|
||||
#define sqlite3OsDlclose sqlite3Os2Dlclose
|
||||
#endif
|
||||
|
||||
|
||||
@@ -337,6 +358,9 @@ void *sqlite3OsMalloc(int);
|
||||
void *sqlite3OsRealloc(void *, int);
|
||||
void sqlite3OsFree(void *);
|
||||
int sqlite3OsAllocationSize(void *);
|
||||
void *sqlite3OsDlopen(const char*);
|
||||
void *sqlite3OsDlsym(void*, const char*);
|
||||
int sqlite3OsDlclose(void*);
|
||||
|
||||
/*
|
||||
** If the SQLITE_ENABLE_REDEF_IO macro is defined, then the OS-layer
|
||||
@@ -381,16 +405,26 @@ struct sqlite3OsVtbl {
|
||||
void *(*xRealloc)(void *, int);
|
||||
void (*xFree)(void *);
|
||||
int (*xAllocationSize)(void *);
|
||||
|
||||
void *(*xDlopen)(const char*);
|
||||
void *(*xDlsym)(void*, const char*);
|
||||
int (*xDlclose)(void*);
|
||||
};
|
||||
|
||||
/* Macro used to comment out routines that do not exists when there is
|
||||
** no disk I/O
|
||||
** no disk I/O or extension loading
|
||||
*/
|
||||
#ifdef SQLITE_OMIT_DISKIO
|
||||
# define IF_DISKIO(X) 0
|
||||
#else
|
||||
# define IF_DISKIO(X) X
|
||||
#endif
|
||||
#ifdef SQLITE_OMIT_LOAD_EXTENSION
|
||||
# define IF_DLOPEN(X) 0
|
||||
#else
|
||||
# define IF_DLOPEN(X) X
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef _SQLITE_OS_C_
|
||||
/*
|
||||
@@ -416,7 +450,10 @@ struct sqlite3OsVtbl {
|
||||
sqlite3OsMalloc,
|
||||
sqlite3OsRealloc,
|
||||
sqlite3OsFree,
|
||||
sqlite3OsAllocationSize
|
||||
sqlite3OsAllocationSize,
|
||||
IF_DLOPEN( sqlite3OsDlopen ),
|
||||
IF_DLOPEN( sqlite3OsDlsym ),
|
||||
IF_DLOPEN( sqlite3OsDlclose ),
|
||||
};
|
||||
#else
|
||||
/*
|
||||
|
||||
@@ -12,6 +12,12 @@
|
||||
**
|
||||
** This file contains code that is specific to OS/2.
|
||||
*/
|
||||
|
||||
#if (__GNUC__ > 3 || __GNUC__ == 3 && __GNUC_MINOR__ >= 3) && defined(OS2_HIGH_MEMORY)
|
||||
/* os2safe.h has to be included before os2.h, needed for high mem */
|
||||
#include <os2safe.h>
|
||||
#endif
|
||||
|
||||
#include "sqliteInt.h"
|
||||
#include "os.h"
|
||||
|
||||
@@ -290,7 +296,14 @@ int os2Read( OsFile *id, void *pBuf, int amt ){
|
||||
SimulateIOError( return SQLITE_IOERR );
|
||||
TRACE3( "READ %d lock=%d\n", ((os2File*)id)->h, ((os2File*)id)->locktype );
|
||||
DosRead( ((os2File*)id)->h, pBuf, amt, &got );
|
||||
return (got == (ULONG)amt) ? SQLITE_OK : SQLITE_IOERR;
|
||||
if (got == (ULONG)amt)
|
||||
return SQLITE_OK;
|
||||
else if (got < 0)
|
||||
return SQLITE_IOERR_READ;
|
||||
else {
|
||||
memset(&((char*)pBuf)[got], 0, amt-got);
|
||||
return SQLITE_IOERR_SHORT_READ;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -768,6 +781,40 @@ int allocateOs2File( os2File *pInit, OsFile **pld ){
|
||||
** with other miscellanous aspects of the operating system interface
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef SQLITE_OMIT_LOAD_EXTENSION
|
||||
/*
|
||||
** Interfaces for opening a shared library, finding entry points
|
||||
** within the shared library, and closing the shared library.
|
||||
*/
|
||||
void *sqlite3Os2Dlopen(const char *zFilename){
|
||||
UCHAR loadErr[256];
|
||||
HMODULE hmod;
|
||||
APIRET rc;
|
||||
rc = DosLoadModule(loadErr, sizeof(loadErr), zFilename, &hmod);
|
||||
if (rc != NO_ERROR) return 0;
|
||||
return (void*)hmod;
|
||||
}
|
||||
void *sqlite3Os2Dlsym(void *pHandle, const char *zSymbol){
|
||||
PFN pfn;
|
||||
APIRET rc;
|
||||
rc = DosQueryProcAddr((HMODULE)pHandle, 0L, zSymbol, &pfn);
|
||||
if (rc != NO_ERROR) {
|
||||
/* if the symbol itself was not found, search again for the same
|
||||
* symbol with an extra underscore, that might be needed depending
|
||||
* on the calling convention */
|
||||
char _zSymbol[256] = "_";
|
||||
strncat(_zSymbol, zSymbol, 255);
|
||||
rc = DosQueryProcAddr((HMODULE)pHandle, 0L, _zSymbol, &pfn);
|
||||
}
|
||||
if (rc != NO_ERROR) return 0;
|
||||
return pfn;
|
||||
}
|
||||
int sqlite3Os2Dlclose(void *pHandle){
|
||||
return DosFreeModule((HMODULE)pHandle);
|
||||
}
|
||||
#endif /* SQLITE_OMIT_LOAD_EXTENSION */
|
||||
|
||||
|
||||
/*
|
||||
** Get information to seed the random number generator. The seed
|
||||
** is written into the buffer zBuf[256]. The calling function must
|
||||
|
||||
@@ -565,7 +565,7 @@ static sqlite3LockingStyle sqlite3TestLockingStyle(const char *filePath,
|
||||
lockInfo.l_whence = SEEK_SET;
|
||||
lockInfo.l_type = F_RDLCK;
|
||||
|
||||
if (fcntl(fd, F_GETLK, (int) &lockInfo) != -1) {
|
||||
if (fcntl(fd, F_GETLK, &lockInfo) != -1) {
|
||||
return posixLockingStyle;
|
||||
}
|
||||
|
||||
@@ -1000,10 +1000,14 @@ int sqlite3UnixIsDirWritable(char *zBuf){
|
||||
*/
|
||||
static int seekAndRead(unixFile *id, void *pBuf, int cnt){
|
||||
int got;
|
||||
i64 newOffset;
|
||||
#ifdef USE_PREAD
|
||||
got = pread(id->h, pBuf, cnt, id->offset);
|
||||
#else
|
||||
lseek(id->h, id->offset, SEEK_SET);
|
||||
newOffset = lseek(id->h, id->offset, SEEK_SET);
|
||||
if( newOffset!=id->offset ){
|
||||
return -1;
|
||||
}
|
||||
got = read(id->h, pBuf, cnt);
|
||||
#endif
|
||||
if( got>0 ){
|
||||
@@ -1026,12 +1030,13 @@ static int unixRead(OsFile *id, void *pBuf, int amt){
|
||||
TRACE5("READ %-3d %5d %7d %d\n", ((unixFile*)id)->h, got,
|
||||
last_page, TIMER_ELAPSED);
|
||||
SEEK(0);
|
||||
SimulateIOError( got=0 );
|
||||
SimulateIOError( got = -1 );
|
||||
if( got==amt ){
|
||||
return SQLITE_OK;
|
||||
}else if( got<0 ){
|
||||
return SQLITE_IOERR_READ;
|
||||
}else{
|
||||
memset(&((char*)pBuf)[got], 0, amt-got);
|
||||
return SQLITE_IOERR_SHORT_READ;
|
||||
}
|
||||
}
|
||||
@@ -1042,10 +1047,14 @@ static int unixRead(OsFile *id, void *pBuf, int amt){
|
||||
*/
|
||||
static int seekAndWrite(unixFile *id, const void *pBuf, int cnt){
|
||||
int got;
|
||||
i64 newOffset;
|
||||
#ifdef USE_PREAD
|
||||
got = pwrite(id->h, pBuf, cnt, id->offset);
|
||||
#else
|
||||
lseek(id->h, id->offset, SEEK_SET);
|
||||
newOffset = lseek(id->h, id->offset, SEEK_SET);
|
||||
if( newOffset!=id->offset ){
|
||||
return -1;
|
||||
}
|
||||
got = write(id->h, pBuf, cnt);
|
||||
#endif
|
||||
if( got>0 ){
|
||||
@@ -1159,13 +1168,26 @@ static int full_fsync(int fd, int fullSync, int dataOnly){
|
||||
#if HAVE_FULLFSYNC
|
||||
if( fullSync ){
|
||||
rc = fcntl(fd, F_FULLFSYNC, 0);
|
||||
}else
|
||||
#endif /* HAVE_FULLFSYNC */
|
||||
}else{
|
||||
rc = 1;
|
||||
}
|
||||
/* If the FULLFSYNC failed, fall back to attempting an fsync().
|
||||
* It shouldn't be possible for fullfsync to fail on the local
|
||||
* file system (on OSX), so failure indicates that FULLFSYNC
|
||||
* isn't supported for this file system. So, attempt an fsync
|
||||
* and (for now) ignore the overhead of a superfluous fcntl call.
|
||||
* It'd be better to detect fullfsync support once and avoid
|
||||
* the fcntl call every time sync is called.
|
||||
*/
|
||||
if( rc ) rc = fsync(fd);
|
||||
|
||||
#else
|
||||
if( dataOnly ){
|
||||
rc = fdatasync(fd);
|
||||
}else{
|
||||
rc = fsync(fd);
|
||||
}
|
||||
#endif /* HAVE_FULLFSYNC */
|
||||
#endif /* defined(SQLITE_NO_SYNC) */
|
||||
|
||||
return rc;
|
||||
@@ -2445,12 +2467,12 @@ static int allocateUnixFile(
|
||||
const char *zFilename, /* Name of the file being opened */
|
||||
int delFlag /* Delete-on-or-before-close flag */
|
||||
){
|
||||
sqlite3LockingStyle lockStyle;
|
||||
sqlite3LockingStyle lockingStyle;
|
||||
unixFile *pNew;
|
||||
unixFile f;
|
||||
int rc;
|
||||
|
||||
lockingStyle = sqlite3DetectLockingStyle(zFilename, f.h);
|
||||
lockingStyle = sqlite3DetectLockingStyle(zFilename, h);
|
||||
if ( lockingStyle == posixLockingStyle ) {
|
||||
sqlite3OsEnterMutex();
|
||||
rc = findLockInfo(h, &f.pLock, &f.pOpen);
|
||||
@@ -2485,7 +2507,7 @@ static int allocateUnixFile(
|
||||
return SQLITE_NOMEM;
|
||||
}else{
|
||||
*pNew = f;
|
||||
switch(lockStyle) {
|
||||
switch(lockingStyle) {
|
||||
case afpLockingStyle:
|
||||
/* afp locking uses the file path so it needs to be included in
|
||||
** the afpLockingContext */
|
||||
@@ -2581,6 +2603,23 @@ static int allocateUnixFile(
|
||||
****************************************************************************/
|
||||
|
||||
|
||||
#ifndef SQLITE_OMIT_LOAD_EXTENSION
|
||||
/*
|
||||
** Interfaces for opening a shared library, finding entry points
|
||||
** within the shared library, and closing the shared library.
|
||||
*/
|
||||
#include <dlfcn.h>
|
||||
void *sqlite3UnixDlopen(const char *zFilename){
|
||||
return dlopen(zFilename, RTLD_NOW | RTLD_GLOBAL);
|
||||
}
|
||||
void *sqlite3UnixDlsym(void *pHandle, const char *zSymbol){
|
||||
return dlsym(pHandle, zSymbol);
|
||||
}
|
||||
int sqlite3UnixDlclose(void *pHandle){
|
||||
return dlclose(pHandle);
|
||||
}
|
||||
#endif /* SQLITE_OMIT_LOAD_EXTENSION */
|
||||
|
||||
/*
|
||||
** Get information to seed the random number generator. The seed
|
||||
** is written into the buffer zBuf[256]. The calling function must
|
||||
|
||||
+258
-70
@@ -40,6 +40,7 @@
|
||||
*/
|
||||
#if defined(_WIN32_WCE)
|
||||
# define OS_WINCE 1
|
||||
# define AreFileApisANSI() 1
|
||||
#else
|
||||
# define OS_WINCE 0
|
||||
#endif
|
||||
@@ -124,16 +125,14 @@ int sqlite3_os_type = 0;
|
||||
#endif /* OS_WINCE */
|
||||
|
||||
/*
|
||||
** Convert a UTF-8 string to UTF-32. Space to hold the returned string
|
||||
** is obtained from sqliteMalloc.
|
||||
** Convert a UTF-8 string to microsoft unicode (UTF-16?).
|
||||
**
|
||||
** Space to hold the returned string is obtained from sqliteMalloc.
|
||||
*/
|
||||
static WCHAR *utf8ToUnicode(const char *zFilename){
|
||||
int nChar;
|
||||
WCHAR *zWideFilename;
|
||||
|
||||
if( !isNT() ){
|
||||
return 0;
|
||||
}
|
||||
nChar = MultiByteToWideChar(CP_UTF8, 0, zFilename, -1, NULL, 0);
|
||||
zWideFilename = sqliteMalloc( nChar*sizeof(zWideFilename[0]) );
|
||||
if( zWideFilename==0 ){
|
||||
@@ -148,7 +147,7 @@ static WCHAR *utf8ToUnicode(const char *zFilename){
|
||||
}
|
||||
|
||||
/*
|
||||
** Convert UTF-32 to UTF-8. Space to hold the returned string is
|
||||
** Convert microsoft unicode to UTF-8. Space to hold the returned string is
|
||||
** obtained from sqliteMalloc().
|
||||
*/
|
||||
static char *unicodeToUtf8(const WCHAR *zWideFilename){
|
||||
@@ -169,6 +168,91 @@ static char *unicodeToUtf8(const WCHAR *zWideFilename){
|
||||
return zFilename;
|
||||
}
|
||||
|
||||
/*
|
||||
** Convert an ansi string to microsoft unicode, based on the
|
||||
** current codepage settings for file apis.
|
||||
**
|
||||
** Space to hold the returned string is obtained
|
||||
** from sqliteMalloc.
|
||||
*/
|
||||
static WCHAR *mbcsToUnicode(const char *zFilename){
|
||||
int nByte;
|
||||
WCHAR *zMbcsFilename;
|
||||
int codepage = AreFileApisANSI() ? CP_ACP : CP_OEMCP;
|
||||
|
||||
nByte = MultiByteToWideChar(codepage, 0, zFilename, -1, NULL,0)*sizeof(WCHAR);
|
||||
zMbcsFilename = sqliteMalloc( nByte*sizeof(zMbcsFilename[0]) );
|
||||
if( zMbcsFilename==0 ){
|
||||
return 0;
|
||||
}
|
||||
nByte = MultiByteToWideChar(codepage, 0, zFilename, -1, zMbcsFilename, nByte);
|
||||
if( nByte==0 ){
|
||||
sqliteFree(zMbcsFilename);
|
||||
zMbcsFilename = 0;
|
||||
}
|
||||
return zMbcsFilename;
|
||||
}
|
||||
|
||||
/*
|
||||
** Convert microsoft unicode to multibyte character string, based on the
|
||||
** user's Ansi codepage.
|
||||
**
|
||||
** Space to hold the returned string is obtained from
|
||||
** sqliteMalloc().
|
||||
*/
|
||||
static char *unicodeToMbcs(const WCHAR *zWideFilename){
|
||||
int nByte;
|
||||
char *zFilename;
|
||||
int codepage = AreFileApisANSI() ? CP_ACP : CP_OEMCP;
|
||||
|
||||
nByte = WideCharToMultiByte(codepage, 0, zWideFilename, -1, 0, 0, 0, 0);
|
||||
zFilename = sqliteMalloc( nByte );
|
||||
if( zFilename==0 ){
|
||||
return 0;
|
||||
}
|
||||
nByte = WideCharToMultiByte(codepage, 0, zWideFilename, -1, zFilename, nByte,
|
||||
0, 0);
|
||||
if( nByte == 0 ){
|
||||
sqliteFree(zFilename);
|
||||
zFilename = 0;
|
||||
}
|
||||
return zFilename;
|
||||
}
|
||||
|
||||
/*
|
||||
** Convert multibyte character string to UTF-8. Space to hold the
|
||||
** returned string is obtained from sqliteMalloc().
|
||||
*/
|
||||
static char *mbcsToUtf8(const char *zFilename){
|
||||
char *zFilenameUtf8;
|
||||
WCHAR *zTmpWide;
|
||||
|
||||
zTmpWide = mbcsToUnicode(zFilename);
|
||||
if( zTmpWide==0 ){
|
||||
return 0;
|
||||
}
|
||||
zFilenameUtf8 = unicodeToUtf8(zTmpWide);
|
||||
sqliteFree(zTmpWide);
|
||||
return zFilenameUtf8;
|
||||
}
|
||||
|
||||
/*
|
||||
** Convert UTF-8 to multibyte character string. Space to hold the
|
||||
** returned string is obtained from sqliteMalloc().
|
||||
*/
|
||||
static char *utf8ToMbcs(const char *zFilename){
|
||||
char *zFilenameMbcs;
|
||||
WCHAR *zTmpWide;
|
||||
|
||||
zTmpWide = utf8ToUnicode(zFilename);
|
||||
if( zTmpWide==0 ){
|
||||
return 0;
|
||||
}
|
||||
zFilenameMbcs = unicodeToMbcs(zTmpWide);
|
||||
sqliteFree(zTmpWide);
|
||||
return zFilenameMbcs;
|
||||
}
|
||||
|
||||
#if OS_WINCE
|
||||
/*************************************************************************
|
||||
** This section contains code for WinCE only.
|
||||
@@ -475,6 +559,23 @@ static BOOL winceLockFileEx(
|
||||
*****************************************************************************/
|
||||
#endif /* OS_WINCE */
|
||||
|
||||
/*
|
||||
** Convert a UTF-8 filename into whatever form the underlying
|
||||
** operating system wants filenames in. Space to hold the result
|
||||
** is obtained from sqliteMalloc and must be freed by the calling
|
||||
** function.
|
||||
*/
|
||||
static void *convertUtf8Filename(const char *zFilename){
|
||||
void *zConverted = 0;
|
||||
if( isNT() ){
|
||||
zConverted = utf8ToUnicode(zFilename);
|
||||
}else{
|
||||
zConverted = utf8ToMbcs(zFilename);
|
||||
}
|
||||
/* caller will handle out of memory */
|
||||
return zConverted;
|
||||
}
|
||||
|
||||
/*
|
||||
** Delete the named file.
|
||||
**
|
||||
@@ -489,25 +590,28 @@ static BOOL winceLockFileEx(
|
||||
*/
|
||||
#define MX_DELETION_ATTEMPTS 3
|
||||
int sqlite3WinDelete(const char *zFilename){
|
||||
WCHAR *zWide = utf8ToUnicode(zFilename);
|
||||
int cnt = 0;
|
||||
int rc;
|
||||
if( zWide ){
|
||||
void *zConverted = convertUtf8Filename(zFilename);
|
||||
if( zConverted==0 ){
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
if( isNT() ){
|
||||
do{
|
||||
rc = DeleteFileW(zWide);
|
||||
}while( rc==0 && GetFileAttributesW(zWide)!=0xffffffff
|
||||
rc = DeleteFileW(zConverted);
|
||||
}while( rc==0 && GetFileAttributesW(zConverted)!=0xffffffff
|
||||
&& cnt++ < MX_DELETION_ATTEMPTS && (Sleep(100), 1) );
|
||||
sqliteFree(zWide);
|
||||
}else{
|
||||
#if OS_WINCE
|
||||
return SQLITE_NOMEM;
|
||||
#else
|
||||
do{
|
||||
rc = DeleteFileA(zFilename);
|
||||
}while( rc==0 && GetFileAttributesA(zFilename)!=0xffffffff
|
||||
rc = DeleteFileA(zConverted);
|
||||
}while( rc==0 && GetFileAttributesA(zConverted)!=0xffffffff
|
||||
&& cnt++ < MX_DELETION_ATTEMPTS && (Sleep(100), 1) );
|
||||
#endif
|
||||
}
|
||||
sqliteFree(zConverted);
|
||||
TRACE2("DELETE \"%s\"\n", zFilename);
|
||||
return rc!=0 ? SQLITE_OK : SQLITE_IOERR;
|
||||
}
|
||||
@@ -517,17 +621,20 @@ int sqlite3WinDelete(const char *zFilename){
|
||||
*/
|
||||
int sqlite3WinFileExists(const char *zFilename){
|
||||
int exists = 0;
|
||||
WCHAR *zWide = utf8ToUnicode(zFilename);
|
||||
if( zWide ){
|
||||
exists = GetFileAttributesW(zWide) != 0xffffffff;
|
||||
sqliteFree(zWide);
|
||||
void *zConverted = convertUtf8Filename(zFilename);
|
||||
if( zConverted==0 ){
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
if( isNT() ){
|
||||
exists = GetFileAttributesW((WCHAR*)zConverted) != 0xffffffff;
|
||||
}else{
|
||||
#if OS_WINCE
|
||||
return SQLITE_NOMEM;
|
||||
#else
|
||||
exists = GetFileAttributesA(zFilename) != 0xffffffff;
|
||||
exists = GetFileAttributesA((char*)zConverted) != 0xffffffff;
|
||||
#endif
|
||||
}
|
||||
sqliteFree(zConverted);
|
||||
return exists;
|
||||
}
|
||||
|
||||
@@ -554,10 +661,14 @@ int sqlite3WinOpenReadWrite(
|
||||
){
|
||||
winFile f;
|
||||
HANDLE h;
|
||||
WCHAR *zWide = utf8ToUnicode(zFilename);
|
||||
void *zConverted = convertUtf8Filename(zFilename);
|
||||
if( zConverted==0 ){
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
assert( *pId==0 );
|
||||
if( zWide ){
|
||||
h = CreateFileW(zWide,
|
||||
|
||||
if( isNT() ){
|
||||
h = CreateFileW((WCHAR*)zConverted,
|
||||
GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||
NULL,
|
||||
@@ -566,7 +677,7 @@ int sqlite3WinOpenReadWrite(
|
||||
NULL
|
||||
);
|
||||
if( h==INVALID_HANDLE_VALUE ){
|
||||
h = CreateFileW(zWide,
|
||||
h = CreateFileW((WCHAR*)zConverted,
|
||||
GENERIC_READ,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||
NULL,
|
||||
@@ -575,7 +686,7 @@ int sqlite3WinOpenReadWrite(
|
||||
NULL
|
||||
);
|
||||
if( h==INVALID_HANDLE_VALUE ){
|
||||
sqliteFree(zWide);
|
||||
sqliteFree(zConverted);
|
||||
return SQLITE_CANTOPEN;
|
||||
}
|
||||
*pReadonly = 1;
|
||||
@@ -585,16 +696,15 @@ int sqlite3WinOpenReadWrite(
|
||||
#if OS_WINCE
|
||||
if (!winceCreateLock(zFilename, &f)){
|
||||
CloseHandle(h);
|
||||
sqliteFree(zWide);
|
||||
sqliteFree(zConverted);
|
||||
return SQLITE_CANTOPEN;
|
||||
}
|
||||
#endif
|
||||
sqliteFree(zWide);
|
||||
}else{
|
||||
#if OS_WINCE
|
||||
return SQLITE_NOMEM;
|
||||
#else
|
||||
h = CreateFileA(zFilename,
|
||||
h = CreateFileA((char*)zConverted,
|
||||
GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||
NULL,
|
||||
@@ -603,7 +713,7 @@ int sqlite3WinOpenReadWrite(
|
||||
NULL
|
||||
);
|
||||
if( h==INVALID_HANDLE_VALUE ){
|
||||
h = CreateFileA(zFilename,
|
||||
h = CreateFileA((char*)zConverted,
|
||||
GENERIC_READ,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||
NULL,
|
||||
@@ -612,6 +722,7 @@ int sqlite3WinOpenReadWrite(
|
||||
NULL
|
||||
);
|
||||
if( h==INVALID_HANDLE_VALUE ){
|
||||
sqliteFree(zConverted);
|
||||
return SQLITE_CANTOPEN;
|
||||
}
|
||||
*pReadonly = 1;
|
||||
@@ -620,6 +731,9 @@ int sqlite3WinOpenReadWrite(
|
||||
}
|
||||
#endif /* OS_WINCE */
|
||||
}
|
||||
|
||||
sqliteFree(zConverted);
|
||||
|
||||
f.h = h;
|
||||
#if OS_WINCE
|
||||
f.zDeleteOnClose = 0;
|
||||
@@ -652,8 +766,11 @@ int sqlite3WinOpenReadWrite(
|
||||
int sqlite3WinOpenExclusive(const char *zFilename, OsFile **pId, int delFlag){
|
||||
winFile f;
|
||||
HANDLE h;
|
||||
int fileflags;
|
||||
WCHAR *zWide = utf8ToUnicode(zFilename);
|
||||
DWORD fileflags;
|
||||
void *zConverted = convertUtf8Filename(zFilename);
|
||||
if( zConverted==0 ){
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
assert( *pId == 0 );
|
||||
fileflags = FILE_FLAG_RANDOM_ACCESS;
|
||||
#if !OS_WINCE
|
||||
@@ -661,10 +778,10 @@ int sqlite3WinOpenExclusive(const char *zFilename, OsFile **pId, int delFlag){
|
||||
fileflags |= FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE;
|
||||
}
|
||||
#endif
|
||||
if( zWide ){
|
||||
if( isNT() ){
|
||||
int cnt = 0;
|
||||
do{
|
||||
h = CreateFileW(zWide,
|
||||
h = CreateFileW((WCHAR*)zConverted,
|
||||
GENERIC_READ | GENERIC_WRITE,
|
||||
0,
|
||||
NULL,
|
||||
@@ -673,14 +790,13 @@ int sqlite3WinOpenExclusive(const char *zFilename, OsFile **pId, int delFlag){
|
||||
NULL
|
||||
);
|
||||
}while( h==INVALID_HANDLE_VALUE && cnt++ < 2 && (Sleep(100), 1) );
|
||||
sqliteFree(zWide);
|
||||
}else{
|
||||
#if OS_WINCE
|
||||
return SQLITE_NOMEM;
|
||||
#else
|
||||
int cnt = 0;
|
||||
do{
|
||||
h = CreateFileA(zFilename,
|
||||
h = CreateFileA((char*)zConverted,
|
||||
GENERIC_READ | GENERIC_WRITE,
|
||||
0,
|
||||
NULL,
|
||||
@@ -691,14 +807,18 @@ int sqlite3WinOpenExclusive(const char *zFilename, OsFile **pId, int delFlag){
|
||||
}while( h==INVALID_HANDLE_VALUE && cnt++ < 2 && (Sleep(100), 1) );
|
||||
#endif /* OS_WINCE */
|
||||
}
|
||||
#if OS_WINCE
|
||||
if( delFlag && h!=INVALID_HANDLE_VALUE ){
|
||||
f.zDeleteOnClose = zConverted;
|
||||
zConverted = 0;
|
||||
}
|
||||
f.hMutex = NULL;
|
||||
#endif
|
||||
sqliteFree(zConverted);
|
||||
if( h==INVALID_HANDLE_VALUE ){
|
||||
return SQLITE_CANTOPEN;
|
||||
}
|
||||
f.h = h;
|
||||
#if OS_WINCE
|
||||
f.zDeleteOnClose = delFlag ? utf8ToUnicode(zFilename) : 0;
|
||||
f.hMutex = NULL;
|
||||
#endif
|
||||
TRACE3("OPEN EX %d \"%s\"\n", h, zFilename);
|
||||
return allocateWinFile(&f, pId);
|
||||
}
|
||||
@@ -713,10 +833,13 @@ int sqlite3WinOpenExclusive(const char *zFilename, OsFile **pId, int delFlag){
|
||||
int sqlite3WinOpenReadOnly(const char *zFilename, OsFile **pId){
|
||||
winFile f;
|
||||
HANDLE h;
|
||||
WCHAR *zWide = utf8ToUnicode(zFilename);
|
||||
void *zConverted = convertUtf8Filename(zFilename);
|
||||
if( zConverted==0 ){
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
assert( *pId==0 );
|
||||
if( zWide ){
|
||||
h = CreateFileW(zWide,
|
||||
if( isNT() ){
|
||||
h = CreateFileW((WCHAR*)zConverted,
|
||||
GENERIC_READ,
|
||||
0,
|
||||
NULL,
|
||||
@@ -724,12 +847,11 @@ int sqlite3WinOpenReadOnly(const char *zFilename, OsFile **pId){
|
||||
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
|
||||
NULL
|
||||
);
|
||||
sqliteFree(zWide);
|
||||
}else{
|
||||
#if OS_WINCE
|
||||
return SQLITE_NOMEM;
|
||||
#else
|
||||
h = CreateFileA(zFilename,
|
||||
h = CreateFileA((char*)zConverted,
|
||||
GENERIC_READ,
|
||||
0,
|
||||
NULL,
|
||||
@@ -739,6 +861,7 @@ int sqlite3WinOpenReadOnly(const char *zFilename, OsFile **pId){
|
||||
);
|
||||
#endif
|
||||
}
|
||||
sqliteFree(zConverted);
|
||||
if( h==INVALID_HANDLE_VALUE ){
|
||||
return SQLITE_CANTOPEN;
|
||||
}
|
||||
@@ -804,9 +927,21 @@ int sqlite3WinTempFileName(char *zBuf){
|
||||
strncpy(zTempPath, zMulti, SQLITE_TEMPNAME_SIZE-30);
|
||||
zTempPath[SQLITE_TEMPNAME_SIZE-30] = 0;
|
||||
sqliteFree(zMulti);
|
||||
}else{
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
}else{
|
||||
GetTempPathA(SQLITE_TEMPNAME_SIZE-30, zTempPath);
|
||||
char *zUtf8;
|
||||
char zMbcsPath[SQLITE_TEMPNAME_SIZE];
|
||||
GetTempPathA(SQLITE_TEMPNAME_SIZE-30, zMbcsPath);
|
||||
zUtf8 = mbcsToUtf8(zMbcsPath);
|
||||
if( zUtf8 ){
|
||||
strncpy(zTempPath, zUtf8, SQLITE_TEMPNAME_SIZE-30);
|
||||
zTempPath[SQLITE_TEMPNAME_SIZE-30] = 0;
|
||||
sqliteFree(zUtf8);
|
||||
}else{
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
}
|
||||
for(i=strlen(zTempPath); i>0 && zTempPath[i-1]=='\\'; i--){}
|
||||
zTempPath[i] = 0;
|
||||
@@ -866,15 +1001,16 @@ static int winClose(OsFile **pId){
|
||||
static int winRead(OsFile *id, void *pBuf, int amt){
|
||||
DWORD got;
|
||||
assert( id!=0 );
|
||||
SimulateIOError(return SQLITE_IOERR);
|
||||
SimulateIOError(return SQLITE_IOERR_READ);
|
||||
TRACE3("READ %d lock=%d\n", ((winFile*)id)->h, ((winFile*)id)->locktype);
|
||||
if( !ReadFile(((winFile*)id)->h, pBuf, amt, &got, 0) ){
|
||||
got = 0;
|
||||
return SQLITE_IOERR_READ;
|
||||
}
|
||||
if( got==(DWORD)amt ){
|
||||
return SQLITE_OK;
|
||||
}else{
|
||||
return SQLITE_IOERR;
|
||||
memset(&((char*)pBuf)[got], 0, amt-got);
|
||||
return SQLITE_IOERR_SHORT_READ;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -886,7 +1022,7 @@ static int winWrite(OsFile *id, const void *pBuf, int amt){
|
||||
int rc = 0;
|
||||
DWORD wrote;
|
||||
assert( id!=0 );
|
||||
SimulateIOError(return SQLITE_IOERR);
|
||||
SimulateIOError(return SQLITE_IOERR_READ);
|
||||
SimulateDiskfullError(return SQLITE_FULL);
|
||||
TRACE3("WRITE %d lock=%d\n", ((winFile*)id)->h, ((winFile*)id)->locktype);
|
||||
assert( amt>0 );
|
||||
@@ -946,7 +1082,7 @@ static int winSync(OsFile *id, int dataOnly){
|
||||
** than UNIX.
|
||||
*/
|
||||
int sqlite3WinSyncDirectory(const char *zDirname){
|
||||
SimulateIOError(return SQLITE_IOERR);
|
||||
SimulateIOError(return SQLITE_IOERR_READ);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
@@ -957,7 +1093,7 @@ static int winTruncate(OsFile *id, i64 nByte){
|
||||
LONG upperBits = nByte>>32;
|
||||
assert( id!=0 );
|
||||
TRACE3("TRUNCATE %d %lld\n", ((winFile*)id)->h, nByte);
|
||||
SimulateIOError(return SQLITE_IOERR);
|
||||
SimulateIOError(return SQLITE_IOERR_TRUNCATE);
|
||||
SetFilePointer(((winFile*)id)->h, nByte, &upperBits, FILE_BEGIN);
|
||||
SetEndOfFile(((winFile*)id)->h);
|
||||
return SQLITE_OK;
|
||||
@@ -969,7 +1105,7 @@ static int winTruncate(OsFile *id, i64 nByte){
|
||||
static int winFileSize(OsFile *id, i64 *pSize){
|
||||
DWORD upperBits, lowerBits;
|
||||
assert( id!=0 );
|
||||
SimulateIOError(return SQLITE_IOERR);
|
||||
SimulateIOError(return SQLITE_IOERR_FSTAT);
|
||||
lowerBits = GetFileSize(((winFile*)id)->h, &upperBits);
|
||||
*pSize = (((i64)upperBits)<<32) + lowerBits;
|
||||
return SQLITE_OK;
|
||||
@@ -1024,20 +1160,24 @@ static int unlockReadLock(winFile *pFile){
|
||||
*/
|
||||
int sqlite3WinIsDirWritable(char *zDirname){
|
||||
int fileAttr;
|
||||
WCHAR *zWide;
|
||||
void *zConverted;
|
||||
if( zDirname==0 ) return 0;
|
||||
if( !isNT() && strlen(zDirname)>MAX_PATH ) return 0;
|
||||
zWide = utf8ToUnicode(zDirname);
|
||||
if( zWide ){
|
||||
fileAttr = GetFileAttributesW(zWide);
|
||||
sqliteFree(zWide);
|
||||
|
||||
zConverted = convertUtf8Filename(zDirname);
|
||||
if( zConverted==0 ){
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
if( isNT() ){
|
||||
fileAttr = GetFileAttributesW((WCHAR*)zConverted);
|
||||
}else{
|
||||
#if OS_WINCE
|
||||
return 0;
|
||||
#else
|
||||
fileAttr = GetFileAttributesA(zDirname);
|
||||
fileAttr = GetFileAttributesA((char*)zConverted);
|
||||
#endif
|
||||
}
|
||||
sqliteFree(zConverted);
|
||||
if( fileAttr == 0xffffffff ) return 0;
|
||||
if( (fileAttr & FILE_ATTRIBUTE_DIRECTORY) != FILE_ATTRIBUTE_DIRECTORY ){
|
||||
return 0;
|
||||
@@ -1226,7 +1366,7 @@ static int winUnlock(OsFile *id, int locktype){
|
||||
if( locktype==SHARED_LOCK && !getReadLock(pFile) ){
|
||||
/* This should never happen. We should always be able to
|
||||
** reacquire the read lock */
|
||||
rc = SQLITE_IOERR;
|
||||
rc = SQLITE_IOERR_UNLOCK;
|
||||
}
|
||||
}
|
||||
if( type>=RESERVED_LOCK ){
|
||||
@@ -1260,24 +1400,33 @@ char *sqlite3WinFullPathname(const char *zRelative){
|
||||
/* WinCE has no concept of a relative pathname, or so I am told. */
|
||||
zFull = sqliteStrDup(zRelative);
|
||||
#else
|
||||
char *zNotUsed;
|
||||
WCHAR *zWide;
|
||||
int nByte;
|
||||
zWide = utf8ToUnicode(zRelative);
|
||||
if( zWide ){
|
||||
WCHAR *zTemp, *zNotUsedW;
|
||||
nByte = GetFullPathNameW(zWide, 0, 0, &zNotUsedW) + 1;
|
||||
void *zConverted;
|
||||
zConverted = convertUtf8Filename(zRelative);
|
||||
if( isNT() ){
|
||||
WCHAR *zTemp;
|
||||
nByte = GetFullPathNameW((WCHAR*)zConverted, 0, 0, 0) + 3;
|
||||
zTemp = sqliteMalloc( nByte*sizeof(zTemp[0]) );
|
||||
if( zTemp==0 ) return 0;
|
||||
GetFullPathNameW(zWide, nByte, zTemp, &zNotUsedW);
|
||||
sqliteFree(zWide);
|
||||
if( zTemp==0 ){
|
||||
sqliteFree(zConverted);
|
||||
return 0;
|
||||
}
|
||||
GetFullPathNameW((WCHAR*)zConverted, nByte, zTemp, 0);
|
||||
sqliteFree(zConverted);
|
||||
zFull = unicodeToUtf8(zTemp);
|
||||
sqliteFree(zTemp);
|
||||
}else{
|
||||
nByte = GetFullPathNameA(zRelative, 0, 0, &zNotUsed) + 1;
|
||||
zFull = sqliteMalloc( nByte*sizeof(zFull[0]) );
|
||||
if( zFull==0 ) return 0;
|
||||
GetFullPathNameA(zRelative, nByte, zFull, &zNotUsed);
|
||||
char *zTemp;
|
||||
nByte = GetFullPathNameA((char*)zConverted, 0, 0, 0) + 3;
|
||||
zTemp = sqliteMalloc( nByte*sizeof(zTemp[0]) );
|
||||
if( zTemp==0 ){
|
||||
sqliteFree(zConverted);
|
||||
return 0;
|
||||
}
|
||||
GetFullPathNameA((char*)zConverted, nByte, zTemp, 0);
|
||||
sqliteFree(zConverted);
|
||||
zFull = mbcsToUtf8(zTemp);
|
||||
sqliteFree(zTemp);
|
||||
}
|
||||
#endif
|
||||
return zFull;
|
||||
@@ -1359,6 +1508,45 @@ static int allocateWinFile(winFile *pInit, OsFile **pId){
|
||||
** with other miscellanous aspects of the operating system interface
|
||||
****************************************************************************/
|
||||
|
||||
#if !defined(SQLITE_OMIT_LOAD_EXTENSION)
|
||||
/*
|
||||
** Interfaces for opening a shared library, finding entry points
|
||||
** within the shared library, and closing the shared library.
|
||||
*/
|
||||
void *sqlite3WinDlopen(const char *zFilename){
|
||||
HANDLE h;
|
||||
void *zConverted = convertUtf8Filename(zFilename);
|
||||
if( zConverted==0 ){
|
||||
return 0;
|
||||
}
|
||||
if( isNT() ){
|
||||
h = LoadLibraryW((WCHAR*)zConverted);
|
||||
}else{
|
||||
#if OS_WINCE
|
||||
return 0;
|
||||
#else
|
||||
h = LoadLibraryA((char*)zConverted);
|
||||
#endif
|
||||
}
|
||||
sqliteFree(zConverted);
|
||||
return (void*)h;
|
||||
|
||||
}
|
||||
void *sqlite3WinDlsym(void *pHandle, const char *zSymbol){
|
||||
#if OS_WINCE
|
||||
/* The GetProcAddressA() routine is only available on wince. */
|
||||
return GetProcAddressA((HANDLE)pHandle, zSymbol);
|
||||
#else
|
||||
/* All other windows platforms expect GetProcAddress() to take
|
||||
** an Ansi string regardless of the _UNICODE setting */
|
||||
return GetProcAddress((HANDLE)pHandle, zSymbol);
|
||||
#endif
|
||||
}
|
||||
int sqlite3WinDlclose(void *pHandle){
|
||||
return FreeLibrary((HANDLE)pHandle);
|
||||
}
|
||||
#endif /* !SQLITE_OMIT_LOAD_EXTENSION */
|
||||
|
||||
/*
|
||||
** Get information to seed the random number generator. The seed
|
||||
** is written into the buffer zBuf[256]. The calling function must
|
||||
|
||||
+58
-74
@@ -18,7 +18,7 @@
|
||||
** file simultaneously, or one process from reading the database while
|
||||
** another is writing.
|
||||
**
|
||||
** @(#) $Id: pager.c,v 1.274 2006/10/03 19:05:19 drh Exp $
|
||||
** @(#) $Id: pager.c,v 1.282 2007/01/05 02:00:47 drh Exp $
|
||||
*/
|
||||
#ifndef SQLITE_OMIT_DISKIO
|
||||
#include "sqliteInt.h"
|
||||
@@ -31,6 +31,7 @@
|
||||
** Macros for troubleshooting. Normally turned off
|
||||
*/
|
||||
#if 0
|
||||
#define sqlite3DebugPrintf printf
|
||||
#define TRACE1(X) sqlite3DebugPrintf(X)
|
||||
#define TRACE2(X,Y) sqlite3DebugPrintf(X,Y)
|
||||
#define TRACE3(X,Y,Z) sqlite3DebugPrintf(X,Y,Z)
|
||||
@@ -350,7 +351,9 @@ static const unsigned char aJournalMagic[] = {
|
||||
/*
|
||||
** The default size of a disk sector
|
||||
*/
|
||||
#define PAGER_SECTOR_SIZE 512
|
||||
#ifndef PAGER_SECTOR_SIZE
|
||||
# define PAGER_SECTOR_SIZE 512
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Page number PAGER_MJ_PGNO is never used in an SQLite database (it is
|
||||
@@ -376,8 +379,8 @@ static const unsigned char aJournalMagic[] = {
|
||||
static int cnt = 0;
|
||||
if( !pager3_refinfo_enable ) return;
|
||||
sqlite3DebugPrintf(
|
||||
"REFCNT: %4d addr=%p nRef=%d\n",
|
||||
p->pgno, PGHDR_TO_DATA(p), p->nRef
|
||||
"REFCNT: %4d addr=%p nRef=%-3d total=%d\n",
|
||||
p->pgno, PGHDR_TO_DATA(p), p->nRef, p->pPager->nRef
|
||||
);
|
||||
cnt++; /* Something to set a breakpoint on */
|
||||
}
|
||||
@@ -847,6 +850,23 @@ static PgHdr *pager_lookup(Pager *pPager, Pgno pgno){
|
||||
return p;
|
||||
}
|
||||
|
||||
/*
|
||||
** Unlock the database file.
|
||||
**
|
||||
** Once all locks have been removed from the database file, other
|
||||
** processes or threads might change the file. So make sure all of
|
||||
** our internal cache is invalidated.
|
||||
*/
|
||||
static void pager_unlock(Pager *pPager){
|
||||
if( !MEMDB ){
|
||||
sqlite3OsUnlock(pPager->fd, NO_LOCK);
|
||||
pPager->dbSize = -1;
|
||||
}
|
||||
pPager->state = PAGER_UNLOCK;
|
||||
assert( pPager->pAll==0 );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Unlock the database and clear the in-memory cache. This routine
|
||||
** sets the state of the pager back to what it was when it was first
|
||||
@@ -871,11 +891,9 @@ static void pager_reset(Pager *pPager){
|
||||
if( pPager->state>=PAGER_RESERVED ){
|
||||
sqlite3pager_rollback(pPager);
|
||||
}
|
||||
sqlite3OsUnlock(pPager->fd, NO_LOCK);
|
||||
pPager->state = PAGER_UNLOCK;
|
||||
pPager->dbSize = -1;
|
||||
pager_unlock(pPager);
|
||||
pPager->nRef = 0;
|
||||
assert( pPager->journalOpen==0 );
|
||||
assert( pPager->errCode || (pPager->journalOpen==0 && pPager->stmtOpen==0) );
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -927,6 +945,7 @@ static int pager_unwritelock(Pager *pPager){
|
||||
pPager->setMaster = 0;
|
||||
pPager->needSync = 0;
|
||||
pPager->pFirstSynced = pPager->pFirst;
|
||||
pPager->dbSize = -1;
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -1421,6 +1440,7 @@ static int pager_stmt_playback(Pager *pPager){
|
||||
if( pPager->state>=PAGER_EXCLUSIVE ){
|
||||
rc = pager_truncate(pPager, pPager->stmtSize);
|
||||
}
|
||||
assert( pPager->state>=PAGER_SHARED );
|
||||
pPager->dbSize = pPager->stmtSize;
|
||||
|
||||
/* Figure out how many records are in the statement journal.
|
||||
@@ -1798,14 +1818,19 @@ void enable_simulated_io_errors(void){
|
||||
** response is to zero the memory at pDest and continue. A real IO error
|
||||
** will presumably recur and be picked up later (Todo: Think about this).
|
||||
*/
|
||||
void sqlite3pager_read_fileheader(Pager *pPager, int N, unsigned char *pDest){
|
||||
int sqlite3pager_read_fileheader(Pager *pPager, int N, unsigned char *pDest){
|
||||
int rc = SQLITE_OK;
|
||||
memset(pDest, 0, N);
|
||||
if( MEMDB==0 ){
|
||||
disable_simulated_io_errors();
|
||||
sqlite3OsSeek(pPager->fd, 0);
|
||||
sqlite3OsRead(pPager->fd, pDest, N);
|
||||
enable_simulated_io_errors();
|
||||
rc = sqlite3OsRead(pPager->fd, pDest, N);
|
||||
if( rc==SQLITE_IOERR_SHORT_READ ){
|
||||
rc = SQLITE_OK;
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1965,9 +1990,15 @@ static void memoryTruncate(Pager *pPager){
|
||||
*/
|
||||
static int pager_wait_on_lock(Pager *pPager, int locktype){
|
||||
int rc;
|
||||
|
||||
/* The OS lock values must be the same as the Pager lock values */
|
||||
assert( PAGER_SHARED==SHARED_LOCK );
|
||||
assert( PAGER_RESERVED==RESERVED_LOCK );
|
||||
assert( PAGER_EXCLUSIVE==EXCLUSIVE_LOCK );
|
||||
|
||||
/* If the file is currently unlocked then the size must be unknown */
|
||||
assert( pPager->state>=PAGER_SHARED || pPager->dbSize<0 || MEMDB );
|
||||
|
||||
if( pPager->state>=locktype ){
|
||||
rc = SQLITE_OK;
|
||||
}else{
|
||||
@@ -1986,6 +2017,7 @@ static int pager_wait_on_lock(Pager *pPager, int locktype){
|
||||
*/
|
||||
int sqlite3pager_truncate(Pager *pPager, Pgno nPage){
|
||||
int rc;
|
||||
assert( pPager->state>=PAGER_SHARED || MEMDB );
|
||||
sqlite3pager_pagecount(pPager);
|
||||
if( pPager->errCode ){
|
||||
rc = pPager->errCode;
|
||||
@@ -2032,7 +2064,6 @@ int sqlite3pager_truncate(Pager *pPager, Pgno nPage){
|
||||
** to the caller.
|
||||
*/
|
||||
int sqlite3pager_close(Pager *pPager){
|
||||
PgHdr *pPg, *pNext;
|
||||
#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
|
||||
/* A malloc() cannot fail in sqlite3ThreadData() as one or more calls to
|
||||
** malloc() must have already been made by this thread before it gets
|
||||
@@ -2044,46 +2075,10 @@ int sqlite3pager_close(Pager *pPager){
|
||||
assert( pTsd && pTsd->nAlloc );
|
||||
#endif
|
||||
|
||||
switch( pPager->state ){
|
||||
case PAGER_RESERVED:
|
||||
case PAGER_SYNCED:
|
||||
case PAGER_EXCLUSIVE: {
|
||||
/* We ignore any IO errors that occur during the rollback
|
||||
** operation. So disable IO error simulation so that testing
|
||||
** works more easily.
|
||||
*/
|
||||
disable_simulated_io_errors();
|
||||
sqlite3pager_rollback(pPager);
|
||||
enable_simulated_io_errors();
|
||||
if( !MEMDB ){
|
||||
sqlite3OsUnlock(pPager->fd, NO_LOCK);
|
||||
}
|
||||
assert( pPager->errCode || pPager->journalOpen==0 );
|
||||
break;
|
||||
}
|
||||
case PAGER_SHARED: {
|
||||
if( !MEMDB ){
|
||||
sqlite3OsUnlock(pPager->fd, NO_LOCK);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
/* Do nothing */
|
||||
break;
|
||||
}
|
||||
}
|
||||
for(pPg=pPager->pAll; pPg; pPg=pNext){
|
||||
#ifndef NDEBUG
|
||||
if( MEMDB ){
|
||||
PgHistory *pHist = PGHDR_TO_HIST(pPg, pPager);
|
||||
assert( !pPg->alwaysRollback );
|
||||
assert( !pHist->pOrig );
|
||||
assert( !pHist->pStmt );
|
||||
}
|
||||
#endif
|
||||
pNext = pPg->pNextAll;
|
||||
sqliteFree(pPg);
|
||||
}
|
||||
disable_simulated_io_errors();
|
||||
pPager->errCode = 0;
|
||||
pager_reset(pPager);
|
||||
enable_simulated_io_errors();
|
||||
TRACE2("CLOSE %d\n", PAGERID(pPager));
|
||||
assert( pPager->errCode || (pPager->journalOpen==0 && pPager->stmtOpen==0) );
|
||||
if( pPager->journalOpen ){
|
||||
@@ -2665,8 +2660,7 @@ int sqlite3pager_get(Pager *pPager, Pgno pgno, void **ppPage){
|
||||
*/
|
||||
rc = sqlite3OsLock(pPager->fd, EXCLUSIVE_LOCK);
|
||||
if( rc!=SQLITE_OK ){
|
||||
sqlite3OsUnlock(pPager->fd, NO_LOCK);
|
||||
pPager->state = PAGER_UNLOCK;
|
||||
pager_unlock(pPager);
|
||||
return pager_error(pPager, rc);
|
||||
}
|
||||
pPager->state = PAGER_EXCLUSIVE;
|
||||
@@ -2681,8 +2675,7 @@ int sqlite3pager_get(Pager *pPager, Pgno pgno, void **ppPage){
|
||||
*/
|
||||
rc = sqlite3OsOpenReadOnly(pPager->zJournal, &pPager->jfd);
|
||||
if( rc!=SQLITE_OK ){
|
||||
sqlite3OsUnlock(pPager->fd, NO_LOCK);
|
||||
pPager->state = PAGER_UNLOCK;
|
||||
pager_unlock(pPager);
|
||||
return SQLITE_BUSY;
|
||||
}
|
||||
pPager->journalOpen = 1;
|
||||
@@ -2789,19 +2782,10 @@ int sqlite3pager_get(Pager *pPager, Pgno pgno, void **ppPage){
|
||||
}
|
||||
TRACE3("FETCH %d page %d\n", PAGERID(pPager), pPg->pgno);
|
||||
CODEC1(pPager, PGHDR_TO_DATA(pPg), pPg->pgno, 3);
|
||||
if( rc!=SQLITE_OK ){
|
||||
i64 fileSize;
|
||||
int rc2 = sqlite3OsFileSize(pPager->fd, &fileSize);
|
||||
if( rc2!=SQLITE_OK || fileSize>=pgno*pPager->pageSize ){
|
||||
/* An IO error occured in one of the the sqlite3OsSeek() or
|
||||
** sqlite3OsRead() calls above. */
|
||||
pPg->pgno = 0;
|
||||
sqlite3pager_unref(PGHDR_TO_DATA(pPg));
|
||||
return rc;
|
||||
}else{
|
||||
clear_simulated_io_error();
|
||||
memset(PGHDR_TO_DATA(pPg), 0, pPager->pageSize);
|
||||
}
|
||||
if( rc!=SQLITE_OK && rc!=SQLITE_IOERR_SHORT_READ ){
|
||||
pPg->pgno = 0;
|
||||
sqlite3pager_unref(PGHDR_TO_DATA(pPg));
|
||||
return rc;
|
||||
}else{
|
||||
TEST_INCR(pPager->nRead);
|
||||
}
|
||||
@@ -2973,8 +2957,7 @@ failed_to_open_journal:
|
||||
*/
|
||||
sqlite3OsDelete(pPager->zJournal);
|
||||
}else{
|
||||
sqlite3OsUnlock(pPager->fd, NO_LOCK);
|
||||
pPager->state = PAGER_UNLOCK;
|
||||
pager_reset(pPager);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
@@ -3233,6 +3216,7 @@ int sqlite3pager_write(void *pData){
|
||||
|
||||
/* Update the database size and return.
|
||||
*/
|
||||
assert( pPager->state>=PAGER_SHARED );
|
||||
if( pPager->dbSize<(int)pPg->pgno ){
|
||||
pPager->dbSize = pPg->pgno;
|
||||
if( !MEMDB && pPager->dbSize==PENDING_BYTE/pPager->pageSize ){
|
||||
@@ -3308,6 +3292,7 @@ void sqlite3pager_dont_write(Pager *pPager, Pgno pgno){
|
||||
assert( pPg!=0 ); /* We never call _dont_write unless the page is in mem */
|
||||
pPg->alwaysRollback = 1;
|
||||
if( pPg->dirty && !pPager->stmtInUse ){
|
||||
assert( pPager->state>=PAGER_SHARED );
|
||||
if( pPager->dbSize==(int)pPg->pgno && pPager->origDbSize<pPager->dbSize ){
|
||||
/* If this pages is the last page in the file and the file has grown
|
||||
** during the current transaction, then do NOT mark the page as clean.
|
||||
@@ -3337,7 +3322,8 @@ void sqlite3pager_dont_rollback(void *pData){
|
||||
PgHdr *pPg = DATA_TO_PGHDR(pData);
|
||||
Pager *pPager = pPg->pPager;
|
||||
|
||||
if( pPager->state!=PAGER_EXCLUSIVE || pPager->journalOpen==0 ) return;
|
||||
assert( pPager->state>=PAGER_RESERVED );
|
||||
if( pPager->journalOpen==0 ) return;
|
||||
if( pPg->alwaysRollback || pPager->alwaysRollback || MEMDB ) return;
|
||||
if( !pPg->inJournal && (int)pPg->pgno <= pPager->origDbSize ){
|
||||
assert( pPager->aInJournal!=0 );
|
||||
@@ -3405,14 +3391,12 @@ int sqlite3pager_commit(Pager *pPager){
|
||||
** if there have been no changes to the database file. */
|
||||
assert( pPager->needSync==0 );
|
||||
rc = pager_unwritelock(pPager);
|
||||
pPager->dbSize = -1;
|
||||
return rc;
|
||||
}
|
||||
assert( pPager->journalOpen );
|
||||
rc = sqlite3pager_sync(pPager, 0, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = pager_unwritelock(pPager);
|
||||
pPager->dbSize = -1;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
@@ -3470,7 +3454,6 @@ int sqlite3pager_rollback(Pager *pPager){
|
||||
|
||||
if( !pPager->dirtyCache || !pPager->journalOpen ){
|
||||
rc = pager_unwritelock(pPager);
|
||||
pPager->dbSize = -1;
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -3546,6 +3529,7 @@ int sqlite3pager_stmt_begin(Pager *pPager){
|
||||
int rc;
|
||||
char zTemp[SQLITE_TEMPNAME_SIZE];
|
||||
assert( !pPager->stmtInUse );
|
||||
assert( pPager->state>=PAGER_SHARED );
|
||||
assert( pPager->dbSize>=0 );
|
||||
TRACE2("STMT-BEGIN %d\n", PAGERID(pPager));
|
||||
if( MEMDB ){
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
** subsystem. The page cache subsystem reads and writes a file a page
|
||||
** at a time and provides a journal for rollback.
|
||||
**
|
||||
** @(#) $Id: pager.h,v 1.51 2006/08/08 13:51:43 drh Exp $
|
||||
** @(#) $Id: pager.h,v 1.52 2006/11/06 21:20:26 drh Exp $
|
||||
*/
|
||||
|
||||
#ifndef _PAGER_H_
|
||||
@@ -75,7 +75,7 @@ void sqlite3pager_set_busyhandler(Pager*, BusyHandler *pBusyHandler);
|
||||
void sqlite3pager_set_destructor(Pager*, void(*)(void*,int));
|
||||
void sqlite3pager_set_reiniter(Pager*, void(*)(void*,int));
|
||||
int sqlite3pager_set_pagesize(Pager*, int);
|
||||
void sqlite3pager_read_fileheader(Pager*, int, unsigned char*);
|
||||
int sqlite3pager_read_fileheader(Pager*, int, unsigned char*);
|
||||
void sqlite3pager_set_cachesize(Pager*, int);
|
||||
int sqlite3pager_close(Pager *pPager);
|
||||
int sqlite3pager_get(Pager *pPager, Pgno pgno, void **ppPage);
|
||||
|
||||
+29
-38
@@ -14,7 +14,7 @@
|
||||
** the parser. Lemon will also generate a header file containing
|
||||
** numeric codes for all of the tokens.
|
||||
**
|
||||
** @(#) $Id: parse.y,v 1.210 2006/09/21 11:02:17 drh Exp $
|
||||
** @(#) $Id: parse.y,v 1.215 2007/02/02 12:44:37 drh Exp $
|
||||
*/
|
||||
|
||||
// All token codes are small integers with #defines that begin with "TK_"
|
||||
@@ -205,6 +205,7 @@ id(A) ::= ID(X). {A = X;}
|
||||
%left PLUS MINUS.
|
||||
%left STAR SLASH REM.
|
||||
%left CONCAT.
|
||||
%left COLLATE.
|
||||
%right UMINUS UPLUS BITNOT.
|
||||
|
||||
// And "ids" is an identifer-or-string.
|
||||
@@ -249,14 +250,14 @@ carglist ::= carglist carg.
|
||||
carglist ::= .
|
||||
carg ::= CONSTRAINT nm ccons.
|
||||
carg ::= ccons.
|
||||
carg ::= DEFAULT term(X). {sqlite3AddDefaultValue(pParse,X);}
|
||||
carg ::= DEFAULT LP expr(X) RP. {sqlite3AddDefaultValue(pParse,X);}
|
||||
carg ::= DEFAULT PLUS term(X). {sqlite3AddDefaultValue(pParse,X);}
|
||||
carg ::= DEFAULT MINUS term(X). {
|
||||
ccons ::= DEFAULT term(X). {sqlite3AddDefaultValue(pParse,X);}
|
||||
ccons ::= DEFAULT LP expr(X) RP. {sqlite3AddDefaultValue(pParse,X);}
|
||||
ccons ::= DEFAULT PLUS term(X). {sqlite3AddDefaultValue(pParse,X);}
|
||||
ccons ::= DEFAULT MINUS term(X). {
|
||||
Expr *p = sqlite3Expr(TK_UMINUS, X, 0, 0);
|
||||
sqlite3AddDefaultValue(pParse,p);
|
||||
}
|
||||
carg ::= DEFAULT id(X). {
|
||||
ccons ::= DEFAULT id(X). {
|
||||
Expr *p = sqlite3Expr(TK_STRING, 0, 0, &X);
|
||||
sqlite3AddDefaultValue(pParse,p);
|
||||
}
|
||||
@@ -444,7 +445,10 @@ as(X) ::= . {X.n = 0;}
|
||||
// A complete FROM clause.
|
||||
//
|
||||
from(A) ::= . {A = sqliteMalloc(sizeof(*A));}
|
||||
from(A) ::= FROM seltablist(X). {A = X;}
|
||||
from(A) ::= FROM seltablist(X). {
|
||||
A = X;
|
||||
sqlite3SrcListShiftJoinType(A);
|
||||
}
|
||||
|
||||
// "seltablist" is a "Select Table List" - the content of the FROM clause
|
||||
// in a SELECT statement. "stl_prefix" is a prefix of this list.
|
||||
@@ -455,31 +459,12 @@ stl_prefix(A) ::= seltablist(X) joinop(Y). {
|
||||
}
|
||||
stl_prefix(A) ::= . {A = 0;}
|
||||
seltablist(A) ::= stl_prefix(X) nm(Y) dbnm(D) as(Z) on_opt(N) using_opt(U). {
|
||||
A = sqlite3SrcListAppend(X,&Y,&D);
|
||||
if( Z.n ) sqlite3SrcListAddAlias(A,&Z);
|
||||
if( N ){
|
||||
if( A && A->nSrc>1 ){ A->a[A->nSrc-2].pOn = N; }
|
||||
else { sqlite3ExprDelete(N); }
|
||||
}
|
||||
if( U ){
|
||||
if( A && A->nSrc>1 ){ A->a[A->nSrc-2].pUsing = U; }
|
||||
else { sqlite3IdListDelete(U); }
|
||||
}
|
||||
A = sqlite3SrcListAppendFromTerm(X,&Y,&D,&Z,0,N,U);
|
||||
}
|
||||
%ifndef SQLITE_OMIT_SUBQUERY
|
||||
seltablist(A) ::= stl_prefix(X) LP seltablist_paren(S) RP
|
||||
as(Z) on_opt(N) using_opt(U). {
|
||||
A = sqlite3SrcListAppend(X,0,0);
|
||||
if( A && A->nSrc>0 ) A->a[A->nSrc-1].pSelect = S;
|
||||
if( Z.n ) sqlite3SrcListAddAlias(A,&Z);
|
||||
if( N ){
|
||||
if( A && A->nSrc>1 ){ A->a[A->nSrc-2].pOn = N; }
|
||||
else { sqlite3ExprDelete(N); }
|
||||
}
|
||||
if( U ){
|
||||
if( A && A->nSrc>1 ){ A->a[A->nSrc-2].pUsing = U; }
|
||||
else { sqlite3IdListDelete(U); }
|
||||
}
|
||||
A = sqlite3SrcListAppendFromTerm(X,0,0,&Z,S,N,U);
|
||||
}
|
||||
|
||||
// A seltablist_paren nonterminal represents anything in a FROM that
|
||||
@@ -490,6 +475,7 @@ seltablist(A) ::= stl_prefix(X) nm(Y) dbnm(D) as(Z) on_opt(N) using_opt(U). {
|
||||
%destructor seltablist_paren {sqlite3SelectDelete($$);}
|
||||
seltablist_paren(A) ::= select(S). {A = S;}
|
||||
seltablist_paren(A) ::= seltablist(F). {
|
||||
sqlite3SrcListShiftJoinType(F);
|
||||
A = sqlite3SelectNew(0,F,0,0,0,0,0,0,0);
|
||||
}
|
||||
%endif SQLITE_OMIT_SUBQUERY
|
||||
@@ -530,24 +516,21 @@ using_opt(U) ::= . {U = 0;}
|
||||
|
||||
orderby_opt(A) ::= . {A = 0;}
|
||||
orderby_opt(A) ::= ORDER BY sortlist(X). {A = X;}
|
||||
sortlist(A) ::= sortlist(X) COMMA sortitem(Y) collate(C) sortorder(Z). {
|
||||
A = sqlite3ExprListAppend(X,Y,C.n>0?&C:0);
|
||||
sortlist(A) ::= sortlist(X) COMMA sortitem(Y) sortorder(Z). {
|
||||
A = sqlite3ExprListAppend(X,Y,0);
|
||||
if( A ) A->a[A->nExpr-1].sortOrder = Z;
|
||||
}
|
||||
sortlist(A) ::= sortitem(Y) collate(C) sortorder(Z). {
|
||||
A = sqlite3ExprListAppend(0,Y,C.n>0?&C:0);
|
||||
sortlist(A) ::= sortitem(Y) sortorder(Z). {
|
||||
A = sqlite3ExprListAppend(0,Y,0);
|
||||
if( A && A->a ) A->a[0].sortOrder = Z;
|
||||
}
|
||||
sortitem(A) ::= expr(X). {A = X;}
|
||||
|
||||
%type sortorder {int}
|
||||
%type collate {Token}
|
||||
|
||||
sortorder(A) ::= ASC. {A = SQLITE_SO_ASC;}
|
||||
sortorder(A) ::= DESC. {A = SQLITE_SO_DESC;}
|
||||
sortorder(A) ::= . {A = SQLITE_SO_ASC;}
|
||||
collate(C) ::= . {C.z = 0; C.n = 0;}
|
||||
collate(C) ::= COLLATE id(X). {C = X;}
|
||||
|
||||
%type groupby_opt {ExprList*}
|
||||
%destructor groupby_opt {sqlite3ExprListDelete($$);}
|
||||
@@ -657,6 +640,9 @@ expr(A) ::= VARIABLE(X). {
|
||||
Expr *pExpr = A = sqlite3Expr(TK_VARIABLE, 0, 0, pToken);
|
||||
sqlite3ExprAssignVarNumber(pParse, pExpr);
|
||||
}
|
||||
expr(A) ::= expr(E) COLLATE id(C). {
|
||||
A = sqlite3ExprSetColl(pParse, E, &C);
|
||||
}
|
||||
%ifndef SQLITE_OMIT_CAST
|
||||
expr(A) ::= CAST(X) LP expr(E) AS typetoken(T) RP(Y). {
|
||||
A = sqlite3Expr(TK_CAST, E, 0, &T);
|
||||
@@ -892,6 +878,10 @@ idxlist(A) ::= idxitem(Y) collate(C) sortorder(Z). {
|
||||
}
|
||||
idxitem(A) ::= nm(X). {A = X;}
|
||||
|
||||
%type collate {Token}
|
||||
collate(C) ::= . {C.z = 0; C.n = 0;}
|
||||
collate(C) ::= COLLATE id(X). {C = X;}
|
||||
|
||||
|
||||
///////////////////////////// The DROP INDEX command /////////////////////////
|
||||
//
|
||||
@@ -907,14 +897,15 @@ cmd ::= VACUUM nm. {sqlite3Vacuum(pParse);}
|
||||
///////////////////////////// The PRAGMA command /////////////////////////////
|
||||
//
|
||||
%ifndef SQLITE_OMIT_PRAGMA
|
||||
cmd ::= PRAGMA nm(X) dbnm(Z) EQ nm(Y). {sqlite3Pragma(pParse,&X,&Z,&Y,0);}
|
||||
cmd ::= PRAGMA nm(X) dbnm(Z) EQ nmnum(Y). {sqlite3Pragma(pParse,&X,&Z,&Y,0);}
|
||||
cmd ::= PRAGMA nm(X) dbnm(Z) EQ ON(Y). {sqlite3Pragma(pParse,&X,&Z,&Y,0);}
|
||||
cmd ::= PRAGMA nm(X) dbnm(Z) EQ plus_num(Y). {sqlite3Pragma(pParse,&X,&Z,&Y,0);}
|
||||
cmd ::= PRAGMA nm(X) dbnm(Z) EQ minus_num(Y). {
|
||||
sqlite3Pragma(pParse,&X,&Z,&Y,1);
|
||||
}
|
||||
cmd ::= PRAGMA nm(X) dbnm(Z) LP nm(Y) RP. {sqlite3Pragma(pParse,&X,&Z,&Y,0);}
|
||||
cmd ::= PRAGMA nm(X) dbnm(Z) LP nmnum(Y) RP. {sqlite3Pragma(pParse,&X,&Z,&Y,0);}
|
||||
cmd ::= PRAGMA nm(X) dbnm(Z). {sqlite3Pragma(pParse,&X,&Z,0,0);}
|
||||
nmnum(A) ::= plus_num(X). {A = X;}
|
||||
nmnum(A) ::= nm(X). {A = X;}
|
||||
%endif SQLITE_OMIT_PRAGMA
|
||||
plus_num(A) ::= plus_opt number(X). {A = X;}
|
||||
minus_num(A) ::= MINUS number(X). {A = X;}
|
||||
|
||||
+35
-16
@@ -11,7 +11,7 @@
|
||||
*************************************************************************
|
||||
** This file contains code used to implement the PRAGMA command.
|
||||
**
|
||||
** $Id: pragma.c,v 1.124 2006/09/25 18:01:57 drh Exp $
|
||||
** $Id: pragma.c,v 1.127 2007/01/27 02:24:56 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include "os.h"
|
||||
@@ -483,14 +483,12 @@ void sqlite3Pragma(
|
||||
sqlite3ViewGetColumnNames(pParse, pTab);
|
||||
for(i=0, pCol=pTab->aCol; i<pTab->nCol; i++, pCol++){
|
||||
const Token *pDflt;
|
||||
static const Token noDflt = { (unsigned char*)"", 0, 0 };
|
||||
sqlite3VdbeAddOp(v, OP_Integer, i, 0);
|
||||
sqlite3VdbeOp3(v, OP_String8, 0, 0, pCol->zName, 0);
|
||||
sqlite3VdbeOp3(v, OP_String8, 0, 0,
|
||||
pCol->zType ? pCol->zType : "", 0);
|
||||
sqlite3VdbeAddOp(v, OP_Integer, pCol->notNull, 0);
|
||||
pDflt = pCol->pDflt ? &pCol->pDflt->span : &noDflt;
|
||||
if( pDflt->z ){
|
||||
if( pCol->pDflt && (pDflt = &pCol->pDflt->span)->z ){
|
||||
sqlite3VdbeOp3(v, OP_String8, 0, 0, (char*)pDflt->z, pDflt->n);
|
||||
}else{
|
||||
sqlite3VdbeAddOp(v, OP_Null, 0, 0);
|
||||
@@ -642,9 +640,13 @@ void sqlite3Pragma(
|
||||
}
|
||||
}else
|
||||
|
||||
#ifndef SQLITE_INTEGRITY_CHECK_ERROR_MAX
|
||||
# define SQLITE_INTEGRITY_CHECK_ERROR_MAX 100
|
||||
#endif
|
||||
|
||||
#ifndef SQLITE_OMIT_INTEGRITY_CHECK
|
||||
if( sqlite3StrICmp(zLeft, "integrity_check")==0 ){
|
||||
int i, j, addr;
|
||||
int i, j, addr, mxErr;
|
||||
|
||||
/* Code that appears at the end of the integrity check. If no error
|
||||
** messages have been generated, output OK. Otherwise output the
|
||||
@@ -662,7 +664,16 @@ void sqlite3Pragma(
|
||||
if( sqlite3ReadSchema(pParse) ) goto pragma_out;
|
||||
sqlite3VdbeSetNumCols(v, 1);
|
||||
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "integrity_check", P3_STATIC);
|
||||
sqlite3VdbeAddOp(v, OP_MemInt, 0, 0); /* Initialize error count to 0 */
|
||||
|
||||
/* Set the maximum error count */
|
||||
mxErr = SQLITE_INTEGRITY_CHECK_ERROR_MAX;
|
||||
if( zRight ){
|
||||
mxErr = atoi(zRight);
|
||||
if( mxErr<=0 ){
|
||||
mxErr = SQLITE_INTEGRITY_CHECK_ERROR_MAX;
|
||||
}
|
||||
}
|
||||
sqlite3VdbeAddOp(v, OP_MemInt, mxErr, 0);
|
||||
|
||||
/* Do an integrity check on each database file */
|
||||
for(i=0; i<db->nDb; i++){
|
||||
@@ -673,6 +684,9 @@ void sqlite3Pragma(
|
||||
if( OMIT_TEMPDB && i==1 ) continue;
|
||||
|
||||
sqlite3CodeVerifySchema(pParse, i);
|
||||
addr = sqlite3VdbeAddOp(v, OP_IfMemPos, 0, 0);
|
||||
sqlite3VdbeAddOp(v, OP_Halt, 0, 0);
|
||||
sqlite3VdbeJumpHere(v, addr);
|
||||
|
||||
/* Do an integrity check of the B-Tree
|
||||
*/
|
||||
@@ -687,28 +701,28 @@ void sqlite3Pragma(
|
||||
cnt++;
|
||||
}
|
||||
}
|
||||
assert( cnt>0 );
|
||||
sqlite3VdbeAddOp(v, OP_IntegrityCk, cnt, i);
|
||||
sqlite3VdbeAddOp(v, OP_Dup, 0, 1);
|
||||
addr = sqlite3VdbeOp3(v, OP_String8, 0, 0, "ok", P3_STATIC);
|
||||
sqlite3VdbeAddOp(v, OP_Eq, 0, addr+7);
|
||||
if( cnt==0 ) continue;
|
||||
sqlite3VdbeAddOp(v, OP_IntegrityCk, 0, i);
|
||||
addr = sqlite3VdbeAddOp(v, OP_IsNull, -1, 0);
|
||||
sqlite3VdbeOp3(v, OP_String8, 0, 0,
|
||||
sqlite3MPrintf("*** in database %s ***\n", db->aDb[i].zName),
|
||||
P3_DYNAMIC);
|
||||
sqlite3VdbeAddOp(v, OP_Pull, 1, 0);
|
||||
sqlite3VdbeAddOp(v, OP_Concat, 0, 1);
|
||||
sqlite3VdbeAddOp(v, OP_Concat, 0, 0);
|
||||
sqlite3VdbeAddOp(v, OP_Callback, 1, 0);
|
||||
sqlite3VdbeAddOp(v, OP_MemIncr, 1, 0);
|
||||
sqlite3VdbeJumpHere(v, addr);
|
||||
|
||||
/* Make sure all the indices are constructed correctly.
|
||||
*/
|
||||
sqlite3CodeVerifySchema(pParse, i);
|
||||
for(x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){
|
||||
Table *pTab = sqliteHashData(x);
|
||||
Index *pIdx;
|
||||
int loopTop;
|
||||
|
||||
if( pTab->pIndex==0 ) continue;
|
||||
addr = sqlite3VdbeAddOp(v, OP_IfMemPos, 0, 0);
|
||||
sqlite3VdbeAddOp(v, OP_Halt, 0, 0);
|
||||
sqlite3VdbeJumpHere(v, addr);
|
||||
sqlite3OpenTableAndIndices(pParse, pTab, 1, OP_OpenRead);
|
||||
sqlite3VdbeAddOp(v, OP_MemInt, 0, 1);
|
||||
loopTop = sqlite3VdbeAddOp(v, OP_Rewind, 1, 0);
|
||||
@@ -716,7 +730,7 @@ void sqlite3Pragma(
|
||||
for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){
|
||||
int jmp2;
|
||||
static const VdbeOpList idxErr[] = {
|
||||
{ OP_MemIncr, 1, 0, 0},
|
||||
{ OP_MemIncr, -1, 0, 0},
|
||||
{ OP_String8, 0, 0, "rowid "},
|
||||
{ OP_Rowid, 1, 0, 0},
|
||||
{ OP_String8, 0, 0, " missing from index "},
|
||||
@@ -741,13 +755,16 @@ void sqlite3Pragma(
|
||||
{ OP_MemLoad, 1, 0, 0},
|
||||
{ OP_MemLoad, 2, 0, 0},
|
||||
{ OP_Eq, 0, 0, 0}, /* 6 */
|
||||
{ OP_MemIncr, 1, 0, 0},
|
||||
{ OP_MemIncr, -1, 0, 0},
|
||||
{ OP_String8, 0, 0, "wrong # of entries in index "},
|
||||
{ OP_String8, 0, 0, 0}, /* 9 */
|
||||
{ OP_Concat, 0, 0, 0},
|
||||
{ OP_Callback, 1, 0, 0},
|
||||
};
|
||||
if( pIdx->tnum==0 ) continue;
|
||||
addr = sqlite3VdbeAddOp(v, OP_IfMemPos, 0, 0);
|
||||
sqlite3VdbeAddOp(v, OP_Halt, 0, 0);
|
||||
sqlite3VdbeJumpHere(v, addr);
|
||||
addr = sqlite3VdbeAddOpList(v, ArraySize(cntIdx), cntIdx);
|
||||
sqlite3VdbeChangeP1(v, addr+1, j+2);
|
||||
sqlite3VdbeChangeP2(v, addr+1, addr+4);
|
||||
@@ -759,6 +776,7 @@ void sqlite3Pragma(
|
||||
}
|
||||
}
|
||||
addr = sqlite3VdbeAddOpList(v, ArraySize(endCode), endCode);
|
||||
sqlite3VdbeChangeP1(v, addr+1, mxErr);
|
||||
sqlite3VdbeJumpHere(v, addr+2);
|
||||
}else
|
||||
#endif /* SQLITE_OMIT_INTEGRITY_CHECK */
|
||||
@@ -896,6 +914,7 @@ void sqlite3Pragma(
|
||||
sqlite3VdbeChangeP1(v, addr, iDb);
|
||||
sqlite3VdbeChangeP2(v, addr, iCookie);
|
||||
sqlite3VdbeSetNumCols(v, 1);
|
||||
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, zLeft, P3_TRANSIENT);
|
||||
}
|
||||
}
|
||||
#endif /* SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS */
|
||||
|
||||
+101
-7
@@ -13,7 +13,7 @@
|
||||
** interface, and routines that contribute to loading the database schema
|
||||
** from disk.
|
||||
**
|
||||
** $Id: prepare.c,v 1.40 2006/09/23 20:36:02 drh Exp $
|
||||
** $Id: prepare.c,v 1.43 2007/01/09 14:01:13 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include "os.h"
|
||||
@@ -445,12 +445,13 @@ int sqlite3SchemaToIndex(sqlite3 *db, Schema *pSchema){
|
||||
/*
|
||||
** Compile the UTF-8 encoded SQL statement zSql into a statement handle.
|
||||
*/
|
||||
int sqlite3_prepare(
|
||||
int sqlite3Prepare(
|
||||
sqlite3 *db, /* Database handle. */
|
||||
const char *zSql, /* UTF-8 encoded SQL statement. */
|
||||
int nBytes, /* Length of zSql in bytes. */
|
||||
int saveSqlFlag, /* True to copy SQL text into the sqlite3_stmt */
|
||||
sqlite3_stmt **ppStmt, /* OUT: A pointer to the prepared statement */
|
||||
const char** pzTail /* OUT: End of parsed string */
|
||||
const char **pzTail /* OUT: End of parsed string */
|
||||
){
|
||||
Parse sParse;
|
||||
char *zErrMsg = 0;
|
||||
@@ -503,7 +504,9 @@ int sqlite3_prepare(
|
||||
if( sqlite3MallocFailed() ){
|
||||
sParse.rc = SQLITE_NOMEM;
|
||||
}
|
||||
if( pzTail ) *pzTail = sParse.zTail;
|
||||
if( pzTail ){
|
||||
*pzTail = sParse.zTail;
|
||||
}
|
||||
rc = sParse.rc;
|
||||
|
||||
#ifndef SQLITE_OMIT_EXPLAIN
|
||||
@@ -521,13 +524,16 @@ int sqlite3_prepare(
|
||||
sqlite3VdbeSetColName(sParse.pVdbe, 3, COLNAME_NAME, "p2", P3_STATIC);
|
||||
sqlite3VdbeSetColName(sParse.pVdbe, 4, COLNAME_NAME, "p3", P3_STATIC);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if( sqlite3SafetyOff(db) ){
|
||||
rc = SQLITE_MISUSE;
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
if( saveSqlFlag ){
|
||||
sqlite3VdbeSetSql(sParse.pVdbe, zSql, sParse.zTail - zSql);
|
||||
}
|
||||
*ppStmt = (sqlite3_stmt*)sParse.pVdbe;
|
||||
}else if( sParse.pVdbe ){
|
||||
sqlite3_finalize((sqlite3_stmt*)sParse.pVdbe);
|
||||
@@ -546,14 +552,74 @@ int sqlite3_prepare(
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Rerun the compilation of a statement after a schema change.
|
||||
** Return true if the statement was recompiled successfully.
|
||||
** Return false if there is an error of some kind.
|
||||
*/
|
||||
int sqlite3Reprepare(Vdbe *p){
|
||||
int rc;
|
||||
Vdbe *pNew;
|
||||
const char *zSql;
|
||||
sqlite3 *db;
|
||||
|
||||
zSql = sqlite3VdbeGetSql(p);
|
||||
if( zSql==0 ){
|
||||
return 0;
|
||||
}
|
||||
db = sqlite3VdbeDb(p);
|
||||
rc = sqlite3Prepare(db, zSql, -1, 0, (sqlite3_stmt**)&pNew, 0);
|
||||
if( rc ){
|
||||
assert( pNew==0 );
|
||||
return 0;
|
||||
}else{
|
||||
assert( pNew!=0 );
|
||||
}
|
||||
sqlite3VdbeSwap(pNew, p);
|
||||
sqlite3_transfer_bindings((sqlite3_stmt*)pNew, (sqlite3_stmt*)p);
|
||||
sqlite3VdbeResetStepResult(pNew);
|
||||
sqlite3VdbeFinalize(pNew);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Two versions of the official API. Legacy and new use. In the legacy
|
||||
** version, the original SQL text is not saved in the prepared statement
|
||||
** and so if a schema change occurs, SQLITE_SCHEMA is returned by
|
||||
** sqlite3_step(). In the new version, the original SQL text is retained
|
||||
** and the statement is automatically recompiled if an schema change
|
||||
** occurs.
|
||||
*/
|
||||
int sqlite3_prepare(
|
||||
sqlite3 *db, /* Database handle. */
|
||||
const char *zSql, /* UTF-8 encoded SQL statement. */
|
||||
int nBytes, /* Length of zSql in bytes. */
|
||||
sqlite3_stmt **ppStmt, /* OUT: A pointer to the prepared statement */
|
||||
const char **pzTail /* OUT: End of parsed string */
|
||||
){
|
||||
return sqlite3Prepare(db,zSql,nBytes,0,ppStmt,pzTail);
|
||||
}
|
||||
int sqlite3_prepare_v2(
|
||||
sqlite3 *db, /* Database handle. */
|
||||
const char *zSql, /* UTF-8 encoded SQL statement. */
|
||||
int nBytes, /* Length of zSql in bytes. */
|
||||
sqlite3_stmt **ppStmt, /* OUT: A pointer to the prepared statement */
|
||||
const char **pzTail /* OUT: End of parsed string */
|
||||
){
|
||||
return sqlite3Prepare(db,zSql,nBytes,1,ppStmt,pzTail);
|
||||
}
|
||||
|
||||
|
||||
#ifndef SQLITE_OMIT_UTF16
|
||||
/*
|
||||
** Compile the UTF-16 encoded SQL statement zSql into a statement handle.
|
||||
*/
|
||||
int sqlite3_prepare16(
|
||||
static int sqlite3Prepare16(
|
||||
sqlite3 *db, /* Database handle. */
|
||||
const void *zSql, /* UTF-8 encoded SQL statement. */
|
||||
int nBytes, /* Length of zSql in bytes. */
|
||||
int saveSqlFlag, /* True to save SQL text into the sqlite3_stmt */
|
||||
sqlite3_stmt **ppStmt, /* OUT: A pointer to the prepared statement */
|
||||
const void **pzTail /* OUT: End of parsed string */
|
||||
){
|
||||
@@ -570,7 +636,7 @@ int sqlite3_prepare16(
|
||||
}
|
||||
zSql8 = sqlite3utf16to8(zSql, nBytes);
|
||||
if( zSql8 ){
|
||||
rc = sqlite3_prepare(db, zSql8, -1, ppStmt, &zTail8);
|
||||
rc = sqlite3Prepare(db, zSql8, -1, saveSqlFlag, ppStmt, &zTail8);
|
||||
}
|
||||
|
||||
if( zTail8 && pzTail ){
|
||||
@@ -585,4 +651,32 @@ int sqlite3_prepare16(
|
||||
sqliteFree(zSql8);
|
||||
return sqlite3ApiExit(db, rc);
|
||||
}
|
||||
|
||||
/*
|
||||
** Two versions of the official API. Legacy and new use. In the legacy
|
||||
** version, the original SQL text is not saved in the prepared statement
|
||||
** and so if a schema change occurs, SQLITE_SCHEMA is returned by
|
||||
** sqlite3_step(). In the new version, the original SQL text is retained
|
||||
** and the statement is automatically recompiled if an schema change
|
||||
** occurs.
|
||||
*/
|
||||
int sqlite3_prepare16(
|
||||
sqlite3 *db, /* Database handle. */
|
||||
const void *zSql, /* UTF-8 encoded SQL statement. */
|
||||
int nBytes, /* Length of zSql in bytes. */
|
||||
sqlite3_stmt **ppStmt, /* OUT: A pointer to the prepared statement */
|
||||
const void **pzTail /* OUT: End of parsed string */
|
||||
){
|
||||
return sqlite3Prepare16(db,zSql,nBytes,0,ppStmt,pzTail);
|
||||
}
|
||||
int sqlite3_prepare16_v2(
|
||||
sqlite3 *db, /* Database handle. */
|
||||
const void *zSql, /* UTF-8 encoded SQL statement. */
|
||||
int nBytes, /* Length of zSql in bytes. */
|
||||
sqlite3_stmt **ppStmt, /* OUT: A pointer to the prepared statement */
|
||||
const void **pzTail /* OUT: End of parsed string */
|
||||
){
|
||||
return sqlite3Prepare16(db,zSql,nBytes,1,ppStmt,pzTail);
|
||||
}
|
||||
|
||||
#endif /* SQLITE_OMIT_UTF16 */
|
||||
|
||||
@@ -857,7 +857,7 @@ void sqlite3DebugPrintf(const char *zFormat, ...){
|
||||
va_start(ap, zFormat);
|
||||
base_vprintf(0, 0, zBuf, sizeof(zBuf), zFormat, ap);
|
||||
va_end(ap);
|
||||
fprintf(stdout,"%d: %s", getpid(), zBuf);
|
||||
fprintf(stdout,"%s", zBuf);
|
||||
fflush(stdout);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
** Random numbers are used by some of the database backends in order
|
||||
** to generate random integer keys for tables or random filenames.
|
||||
**
|
||||
** $Id: random.c,v 1.15 2006/01/06 14:32:20 drh Exp $
|
||||
** $Id: random.c,v 1.16 2007/01/05 14:38:56 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include "os.h"
|
||||
@@ -37,7 +37,7 @@
|
||||
** (Later): Actually, OP_NewRowid does not depend on a good source of
|
||||
** randomness any more. But we will leave this code in all the same.
|
||||
*/
|
||||
static int randomByte(){
|
||||
static int randomByte(void){
|
||||
unsigned char t;
|
||||
|
||||
/* All threads share a single random number generator.
|
||||
|
||||
+131
-31
@@ -12,7 +12,7 @@
|
||||
** This file contains C code routines that are called by the parser
|
||||
** to handle SELECT statements in SQLite.
|
||||
**
|
||||
** $Id: select.c,v 1.321 2006/09/29 14:01:05 drh Exp $
|
||||
** $Id: select.c,v 1.326 2007/02/01 23:02:45 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
@@ -301,8 +301,8 @@ static int sqliteProcessJoin(Parse *pParse, Select *p){
|
||||
/* When the NATURAL keyword is present, add WHERE clause terms for
|
||||
** every column that the two tables have in common.
|
||||
*/
|
||||
if( pLeft->jointype & JT_NATURAL ){
|
||||
if( pLeft->pOn || pLeft->pUsing ){
|
||||
if( pRight->jointype & JT_NATURAL ){
|
||||
if( pRight->pOn || pRight->pUsing ){
|
||||
sqlite3ErrorMsg(pParse, "a NATURAL join may not have "
|
||||
"an ON or USING clause", 0);
|
||||
return 1;
|
||||
@@ -320,7 +320,7 @@ static int sqliteProcessJoin(Parse *pParse, Select *p){
|
||||
|
||||
/* Disallow both ON and USING clauses in the same join
|
||||
*/
|
||||
if( pLeft->pOn && pLeft->pUsing ){
|
||||
if( pRight->pOn && pRight->pUsing ){
|
||||
sqlite3ErrorMsg(pParse, "cannot have both ON and USING "
|
||||
"clauses in the same join");
|
||||
return 1;
|
||||
@@ -329,10 +329,10 @@ static int sqliteProcessJoin(Parse *pParse, Select *p){
|
||||
/* Add the ON clause to the end of the WHERE clause, connected by
|
||||
** an AND operator.
|
||||
*/
|
||||
if( pLeft->pOn ){
|
||||
setJoinExpr(pLeft->pOn, pRight->iCursor);
|
||||
p->pWhere = sqlite3ExprAnd(p->pWhere, pLeft->pOn);
|
||||
pLeft->pOn = 0;
|
||||
if( pRight->pOn ){
|
||||
setJoinExpr(pRight->pOn, pRight->iCursor);
|
||||
p->pWhere = sqlite3ExprAnd(p->pWhere, pRight->pOn);
|
||||
pRight->pOn = 0;
|
||||
}
|
||||
|
||||
/* Create extra terms on the WHERE clause for each column named
|
||||
@@ -342,8 +342,8 @@ static int sqliteProcessJoin(Parse *pParse, Select *p){
|
||||
** Report an error if any column mentioned in the USING clause is
|
||||
** not contained in both tables to be joined.
|
||||
*/
|
||||
if( pLeft->pUsing ){
|
||||
IdList *pList = pLeft->pUsing;
|
||||
if( pRight->pUsing ){
|
||||
IdList *pList = pRight->pUsing;
|
||||
for(j=0; j<pList->nId; j++){
|
||||
char *zName = pList->a[j].zName;
|
||||
if( columnIndex(pLeftTab, zName)<0 || columnIndex(pRightTab, zName)<0 ){
|
||||
@@ -1309,13 +1309,13 @@ static int prepSelectStmt(Parse *pParse, Select *p){
|
||||
|
||||
if( i>0 ){
|
||||
struct SrcList_item *pLeft = &pTabList->a[i-1];
|
||||
if( (pLeft->jointype & JT_NATURAL)!=0 &&
|
||||
if( (pLeft[1].jointype & JT_NATURAL)!=0 &&
|
||||
columnIndex(pLeft->pTab, zName)>=0 ){
|
||||
/* In a NATURAL join, omit the join columns from the
|
||||
** table on the right */
|
||||
continue;
|
||||
}
|
||||
if( sqlite3IdListIndex(pLeft->pUsing, zName)>=0 ){
|
||||
if( sqlite3IdListIndex(pLeft[1].pUsing, zName)>=0 ){
|
||||
/* In a join with a USING clause, omit columns in the
|
||||
** using clause from the table on the right. */
|
||||
continue;
|
||||
@@ -1936,6 +1936,7 @@ static int multiSelect(
|
||||
}
|
||||
sqlite3VdbeChangeP2(v, addr, nCol);
|
||||
sqlite3VdbeChangeP3(v, addr, (char*)pKeyInfo, P3_KEYINFO);
|
||||
pLoop->addrOpenEphm[i] = -1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1951,10 +1952,9 @@ static int multiSelect(
|
||||
apColl = pKeyInfo->aColl;
|
||||
for(i=0; i<nOrderByExpr; i++, pOTerm++, apColl++, pSortOrder++){
|
||||
Expr *pExpr = pOTerm->pExpr;
|
||||
char *zName = pOTerm->zName;
|
||||
assert( pExpr->op==TK_COLUMN && pExpr->iColumn<nCol );
|
||||
if( zName ){
|
||||
*apColl = sqlite3LocateCollSeq(pParse, zName, -1);
|
||||
if( (pExpr->flags & EP_ExpCollate) ){
|
||||
assert( pExpr->pColl!=0 );
|
||||
*apColl = pExpr->pColl;
|
||||
}else{
|
||||
*apColl = aCopy[pExpr->iColumn];
|
||||
}
|
||||
@@ -2175,7 +2175,7 @@ static int flattenSubquery(
|
||||
**
|
||||
** which is not at all the same thing.
|
||||
*/
|
||||
if( pSubSrc->nSrc>1 && iFrom>0 && (pSrc->a[iFrom-1].jointype & JT_OUTER)!=0 ){
|
||||
if( pSubSrc->nSrc>1 && (pSubitem->jointype & JT_OUTER)!=0 ){
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -2192,8 +2192,7 @@ static int flattenSubquery(
|
||||
** But the t2.x>0 test will always fail on a NULL row of t2, which
|
||||
** effectively converts the OUTER JOIN into an INNER JOIN.
|
||||
*/
|
||||
if( iFrom>0 && (pSrc->a[iFrom-1].jointype & JT_OUTER)!=0
|
||||
&& pSub->pWhere!=0 ){
|
||||
if( (pSubitem->jointype & JT_OUTER)!=0 && pSub->pWhere!=0 ){
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -2232,7 +2231,7 @@ static int flattenSubquery(
|
||||
pSrc->a[i+iFrom] = pSubSrc->a[i];
|
||||
memset(&pSubSrc->a[i], 0, sizeof(pSubSrc->a[i]));
|
||||
}
|
||||
pSrc->a[iFrom+nSubSrc-1].jointype = jointype;
|
||||
pSrc->a[iFrom].jointype = jointype;
|
||||
}
|
||||
|
||||
/* Now begin substituting subquery result set expressions for
|
||||
@@ -2478,8 +2477,14 @@ static int processOrderGroupBy(
|
||||
Expr *pE = pOrderBy->a[i].pExpr;
|
||||
if( sqlite3ExprIsInteger(pE, &iCol) ){
|
||||
if( iCol>0 && iCol<=pEList->nExpr ){
|
||||
CollSeq *pColl = pE->pColl;
|
||||
int flags = pE->flags & EP_ExpCollate;
|
||||
sqlite3ExprDelete(pE);
|
||||
pE = pOrderBy->a[i].pExpr = sqlite3ExprDup(pEList->a[iCol-1].pExpr);
|
||||
if( pColl && flags ){
|
||||
pE->pColl = pColl;
|
||||
pE->flags |= flags;
|
||||
}
|
||||
}else{
|
||||
sqlite3ErrorMsg(pParse,
|
||||
"%s BY column number %d out of range - should be "
|
||||
@@ -2605,7 +2610,14 @@ int sqlite3SelectResolve(
|
||||
}
|
||||
}
|
||||
|
||||
return SQLITE_OK;
|
||||
/* If this is one SELECT of a compound, be sure to resolve names
|
||||
** in the other SELECTs.
|
||||
*/
|
||||
if( p->pPrior ){
|
||||
return sqlite3SelectResolve(pParse, p->pPrior, pOuterNC);
|
||||
}else{
|
||||
return SQLITE_OK;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -2907,23 +2919,15 @@ int sqlite3Select(
|
||||
}
|
||||
#endif
|
||||
|
||||
/* If there is an ORDER BY clause, resolve any collation sequences
|
||||
** names that have been explicitly specified and create a sorting index.
|
||||
**
|
||||
** This sorting index might end up being unused if the data can be
|
||||
/* If there is an ORDER BY clause, then this sorting
|
||||
** index might end up being unused if the data can be
|
||||
** extracted in pre-sorted order. If that is the case, then the
|
||||
** OP_OpenEphemeral instruction will be changed to an OP_Noop once
|
||||
** we figure out that the sorting index is not needed. The addrSortIndex
|
||||
** variable is used to facilitate that change.
|
||||
*/
|
||||
if( pOrderBy ){
|
||||
struct ExprList_item *pTerm;
|
||||
KeyInfo *pKeyInfo;
|
||||
for(i=0, pTerm=pOrderBy->a; i<pOrderBy->nExpr; i++, pTerm++){
|
||||
if( pTerm->zName ){
|
||||
pTerm->pExpr->pColl = sqlite3LocateCollSeq(pParse, pTerm->zName, -1);
|
||||
}
|
||||
}
|
||||
if( pParse->nErr ){
|
||||
goto select_end;
|
||||
}
|
||||
@@ -3293,3 +3297,99 @@ select_end:
|
||||
sqliteFree(sAggInfo.aFunc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
#if defined(SQLITE_TEST) || defined(SQLITE_DEBUG)
|
||||
/*
|
||||
*******************************************************************************
|
||||
** The following code is used for testing and debugging only. The code
|
||||
** that follows does not appear in normal builds.
|
||||
**
|
||||
** These routines are used to print out the content of all or part of a
|
||||
** parse structures such as Select or Expr. Such printouts are useful
|
||||
** for helping to understand what is happening inside the code generator
|
||||
** during the execution of complex SELECT statements.
|
||||
**
|
||||
** These routine are not called anywhere from within the normal
|
||||
** code base. Then are intended to be called from within the debugger
|
||||
** or from temporary "printf" statements inserted for debugging.
|
||||
*/
|
||||
void sqlite3PrintExpr(Expr *p){
|
||||
if( p->token.z && p->token.n>0 ){
|
||||
sqlite3DebugPrintf("(%.*s", p->token.n, p->token.z);
|
||||
}else{
|
||||
sqlite3DebugPrintf("(%d", p->op);
|
||||
}
|
||||
if( p->pLeft ){
|
||||
sqlite3DebugPrintf(" ");
|
||||
sqlite3PrintExpr(p->pLeft);
|
||||
}
|
||||
if( p->pRight ){
|
||||
sqlite3DebugPrintf(" ");
|
||||
sqlite3PrintExpr(p->pRight);
|
||||
}
|
||||
sqlite3DebugPrintf(")");
|
||||
}
|
||||
void sqlite3PrintExprList(ExprList *pList){
|
||||
int i;
|
||||
for(i=0; i<pList->nExpr; i++){
|
||||
sqlite3PrintExpr(pList->a[i].pExpr);
|
||||
if( i<pList->nExpr-1 ){
|
||||
sqlite3DebugPrintf(", ");
|
||||
}
|
||||
}
|
||||
}
|
||||
void sqlite3PrintSelect(Select *p, int indent){
|
||||
sqlite3DebugPrintf("%*sSELECT(%p) ", indent, "", p);
|
||||
sqlite3PrintExprList(p->pEList);
|
||||
sqlite3DebugPrintf("\n");
|
||||
if( p->pSrc ){
|
||||
char *zPrefix;
|
||||
int i;
|
||||
zPrefix = "FROM";
|
||||
for(i=0; i<p->pSrc->nSrc; i++){
|
||||
struct SrcList_item *pItem = &p->pSrc->a[i];
|
||||
sqlite3DebugPrintf("%*s ", indent+6, zPrefix);
|
||||
zPrefix = "";
|
||||
if( pItem->pSelect ){
|
||||
sqlite3DebugPrintf("(\n");
|
||||
sqlite3PrintSelect(pItem->pSelect, indent+10);
|
||||
sqlite3DebugPrintf("%*s)", indent+8, "");
|
||||
}else if( pItem->zName ){
|
||||
sqlite3DebugPrintf("%s", pItem->zName);
|
||||
}
|
||||
if( pItem->pTab ){
|
||||
sqlite3DebugPrintf("(table: %s)", pItem->pTab->zName);
|
||||
}
|
||||
if( pItem->zAlias ){
|
||||
sqlite3DebugPrintf(" AS %s", pItem->zAlias);
|
||||
}
|
||||
if( i<p->pSrc->nSrc-1 ){
|
||||
sqlite3DebugPrintf(",");
|
||||
}
|
||||
sqlite3DebugPrintf("\n");
|
||||
}
|
||||
}
|
||||
if( p->pWhere ){
|
||||
sqlite3DebugPrintf("%*s WHERE ", indent, "");
|
||||
sqlite3PrintExpr(p->pWhere);
|
||||
sqlite3DebugPrintf("\n");
|
||||
}
|
||||
if( p->pGroupBy ){
|
||||
sqlite3DebugPrintf("%*s GROUP BY ", indent, "");
|
||||
sqlite3PrintExprList(p->pGroupBy);
|
||||
sqlite3DebugPrintf("\n");
|
||||
}
|
||||
if( p->pHaving ){
|
||||
sqlite3DebugPrintf("%*s HAVING ", indent, "");
|
||||
sqlite3PrintExpr(p->pHaving);
|
||||
sqlite3DebugPrintf("\n");
|
||||
}
|
||||
if( p->pOrderBy ){
|
||||
sqlite3DebugPrintf("%*s ORDER BY ", indent, "");
|
||||
sqlite3PrintExprList(p->pOrderBy);
|
||||
sqlite3DebugPrintf("\n");
|
||||
}
|
||||
}
|
||||
/* End of the structure debug printing code
|
||||
*****************************************************************************/
|
||||
#endif /* defined(SQLITE_TEST) || defined(SQLITE_DEBUG) */
|
||||
|
||||
+181
-84
@@ -12,7 +12,7 @@
|
||||
** This file contains code to implement the "sqlite" command line
|
||||
** utility for accessing SQLite databases.
|
||||
**
|
||||
** $Id: shell.c,v 1.150 2006/09/25 13:09:23 drh Exp $
|
||||
** $Id: shell.c,v 1.158 2007/01/08 14:31:36 drh Exp $
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@@ -60,6 +60,18 @@
|
||||
extern int isatty();
|
||||
#endif
|
||||
|
||||
/*
|
||||
** If the following flag is set, then command execution stops
|
||||
** at an error if we are not interactive.
|
||||
*/
|
||||
static int bail_on_error = 0;
|
||||
|
||||
/*
|
||||
** Threat stdin as an interactive input if the following variable
|
||||
** is true. Otherwise, assume stdin is connected to a file or pipe.
|
||||
*/
|
||||
static int stdin_is_interactive = 1;
|
||||
|
||||
/*
|
||||
** The following is the open SQLite database. We make a pointer
|
||||
** to this database a static variable so that it can be accessed
|
||||
@@ -184,10 +196,7 @@ static char *local_getline(char *zPrompt, FILE *in){
|
||||
}
|
||||
|
||||
/*
|
||||
** Retrieve a single line of input text. "isatty" is true if text
|
||||
** is coming from a terminal. In that case, we issue a prompt and
|
||||
** attempt to use "readline" for command-line editing. If "isatty"
|
||||
** is false, use "local_getline" instead of "readline" and issue no prompt.
|
||||
** Retrieve a single line of input text.
|
||||
**
|
||||
** zPrior is a string of prior text retrieved. If not the empty
|
||||
** string, then issue a continuation prompt.
|
||||
@@ -216,6 +225,7 @@ struct previous_mode_data {
|
||||
int showHeader;
|
||||
int colWidth[100];
|
||||
};
|
||||
|
||||
/*
|
||||
** An pointer to an instance of this structure is passed from
|
||||
** the main program to the callback. This is used to communicate
|
||||
@@ -227,6 +237,7 @@ struct callback_data {
|
||||
int cnt; /* Number of records displayed so far */
|
||||
FILE *out; /* Write results here */
|
||||
int mode; /* An output mode setting */
|
||||
int writableSchema; /* True if PRAGMA writable_schema=ON */
|
||||
int showHeader; /* True to show column names in List or Column mode */
|
||||
char *zDestTable; /* Name of destination table when MODE_Insert */
|
||||
char separator[20]; /* Separator character for MODE_List */
|
||||
@@ -350,6 +361,29 @@ static void output_html_string(FILE *out, const char *z){
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** If a field contains any character identified by a 1 in the following
|
||||
** array, then the string must be quoted for CSV.
|
||||
*/
|
||||
static const char needCsvQuote[] = {
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
};
|
||||
|
||||
/*
|
||||
** Output a single term of CSV. Actually, p->separator is used for
|
||||
** the separator, which may or may not be a comma. p->nullvalue is
|
||||
@@ -357,12 +391,27 @@ static void output_html_string(FILE *out, const char *z){
|
||||
** appear outside of quotes.
|
||||
*/
|
||||
static void output_csv(struct callback_data *p, const char *z, int bSep){
|
||||
FILE *out = p->out;
|
||||
if( z==0 ){
|
||||
fprintf(p->out,"%s",p->nullvalue);
|
||||
}else if( isNumber(z, 0) ){
|
||||
fprintf(p->out,"%s",z);
|
||||
fprintf(out,"%s",p->nullvalue);
|
||||
}else{
|
||||
output_c_string(p->out, z);
|
||||
int i;
|
||||
for(i=0; z[i]; i++){
|
||||
if( needCsvQuote[((unsigned char*)z)[i]] ){
|
||||
i = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( i==0 ){
|
||||
putc('"', out);
|
||||
for(i=0; z[i]; i++){
|
||||
if( z[i]=='"' ) putc('"', out);
|
||||
putc(z[i], out);
|
||||
}
|
||||
putc('"', out);
|
||||
}else{
|
||||
fprintf(out, "%s", z);
|
||||
}
|
||||
}
|
||||
if( bSep ){
|
||||
fprintf(p->out, p->separator);
|
||||
@@ -587,7 +636,7 @@ static void set_table_name(struct callback_data *p, const char *zName){
|
||||
** If the third argument, quote, is not '\0', then it is used as a
|
||||
** quote character for zAppend.
|
||||
*/
|
||||
static char * appendText(char *zIn, char const *zAppend, char quote){
|
||||
static char *appendText(char *zIn, char const *zAppend, char quote){
|
||||
int len;
|
||||
int i;
|
||||
int nAppend = strlen(zAppend);
|
||||
@@ -628,6 +677,9 @@ static char * appendText(char *zIn, char const *zAppend, char quote){
|
||||
/*
|
||||
** Execute a query statement that has a single result column. Print
|
||||
** that result column on a line by itself with a semicolon terminator.
|
||||
**
|
||||
** This is used, for example, to show the schema of the database by
|
||||
** querying the SQLITE_MASTER table.
|
||||
*/
|
||||
static int run_table_dump_query(FILE *out, sqlite3 *db, const char *zSelect){
|
||||
sqlite3_stmt *pSelect;
|
||||
@@ -669,6 +721,19 @@ static int dump_callback(void *pArg, int nArg, char **azArg, char **azCol){
|
||||
fprintf(p->out, "ANALYZE sqlite_master;\n");
|
||||
}else if( strncmp(zTable, "sqlite_", 7)==0 ){
|
||||
return 0;
|
||||
}else if( strncmp(zSql, "CREATE VIRTUAL TABLE", 20)==0 ){
|
||||
char *zIns;
|
||||
if( !p->writableSchema ){
|
||||
fprintf(p->out, "PRAGMA writable_schema=ON;\n");
|
||||
p->writableSchema = 1;
|
||||
}
|
||||
zIns = sqlite3_mprintf(
|
||||
"INSERT INTO sqlite_master(type,name,tbl_name,rootpage,sql)"
|
||||
"VALUES('table','%q','%q',0,'%q');",
|
||||
zTable, zTable, zSql);
|
||||
fprintf(p->out, "%s\n", zIns);
|
||||
sqlite3_free(zIns);
|
||||
return 0;
|
||||
}else{
|
||||
fprintf(p->out, "%s;\n", zSql);
|
||||
}
|
||||
@@ -702,7 +767,7 @@ static int dump_callback(void *pArg, int nArg, char **azArg, char **azCol){
|
||||
zSelect = appendText(zSelect, zText, '"');
|
||||
rc = sqlite3_step(pTableInfo);
|
||||
if( rc==SQLITE_ROW ){
|
||||
zSelect = appendText(zSelect, ") || ', ' || ", 0);
|
||||
zSelect = appendText(zSelect, ") || ',' || ", 0);
|
||||
}else{
|
||||
zSelect = appendText(zSelect, ") ", 0);
|
||||
}
|
||||
@@ -721,15 +786,14 @@ static int dump_callback(void *pArg, int nArg, char **azArg, char **azCol){
|
||||
rc = run_table_dump_query(p->out, p->db, zSelect);
|
||||
}
|
||||
if( zSelect ) free(zSelect);
|
||||
if( rc!=SQLITE_OK ){
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Run zQuery. Update dump_callback() as the callback routine.
|
||||
** Run zQuery. Use dump_callback() as the callback routine so that
|
||||
** the contents of the query are output as SQL statements.
|
||||
**
|
||||
** If we get a SQLITE_CORRUPT error, rerun the query after appending
|
||||
** "ORDER BY rowid DESC" to the end.
|
||||
*/
|
||||
@@ -757,6 +821,7 @@ static int run_schema_dump_query(
|
||||
** Text of a help message
|
||||
*/
|
||||
static char zHelp[] =
|
||||
".bail ON|OFF Stop after hitting an error. Default OFF\n"
|
||||
".databases List names and files of attached databases\n"
|
||||
".dump ?TABLE? ... Dump the database in an SQL text format\n"
|
||||
".echo ON|OFF Turn command echo on or off\n"
|
||||
@@ -793,7 +858,7 @@ static char zHelp[] =
|
||||
;
|
||||
|
||||
/* Forward reference */
|
||||
static void process_input(struct callback_data *p, FILE *in);
|
||||
static int process_input(struct callback_data *p, FILE *in);
|
||||
|
||||
/*
|
||||
** Make sure the database is open. If it is not, then open it. If
|
||||
@@ -853,11 +918,28 @@ static void resolve_backslashes(char *z){
|
||||
z[j] = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Interpret zArg as a boolean value. Return either 0 or 1.
|
||||
*/
|
||||
static int booleanValue(char *zArg){
|
||||
int val = atoi(zArg);
|
||||
int j;
|
||||
for(j=0; zArg[j]; j++){
|
||||
zArg[j] = tolower(zArg[j]);
|
||||
}
|
||||
if( strcmp(zArg,"on")==0 ){
|
||||
val = 1;
|
||||
}else if( strcmp(zArg,"yes")==0 ){
|
||||
val = 1;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
/*
|
||||
** If an input line begins with "." then invoke this routine to
|
||||
** process that line.
|
||||
**
|
||||
** Return 1 to exit and 0 to continue.
|
||||
** Return 1 on error, 2 to exit, and 0 otherwise.
|
||||
*/
|
||||
static int do_meta_command(char *zLine, struct callback_data *p){
|
||||
int i = 1;
|
||||
@@ -892,6 +974,10 @@ static int do_meta_command(char *zLine, struct callback_data *p){
|
||||
if( nArg==0 ) return rc;
|
||||
n = strlen(azArg[0]);
|
||||
c = azArg[0][0];
|
||||
if( c=='b' && n>1 && strncmp(azArg[0], "bail", n)==0 && nArg>1 ){
|
||||
bail_on_error = booleanValue(azArg[1]);
|
||||
}else
|
||||
|
||||
if( c=='d' && n>1 && strncmp(azArg[0], "databases", n)==0 ){
|
||||
struct callback_data data;
|
||||
char *zErrMsg = 0;
|
||||
@@ -914,19 +1000,15 @@ static int do_meta_command(char *zLine, struct callback_data *p){
|
||||
char *zErrMsg = 0;
|
||||
open_db(p);
|
||||
fprintf(p->out, "BEGIN TRANSACTION;\n");
|
||||
p->writableSchema = 0;
|
||||
if( nArg==1 ){
|
||||
run_schema_dump_query(p,
|
||||
"SELECT name, type, sql FROM sqlite_master "
|
||||
"WHERE sql NOT NULL AND type=='table' AND rootpage!=0", 0
|
||||
);
|
||||
run_schema_dump_query(p,
|
||||
"SELECT name, type, sql FROM sqlite_master "
|
||||
"WHERE sql NOT NULL AND "
|
||||
" AND type!='table' AND type!='meta'", 0
|
||||
"WHERE sql NOT NULL AND type=='table'", 0
|
||||
);
|
||||
run_table_dump_query(p->out, p->db,
|
||||
"SELECT sql FROM sqlite_master "
|
||||
"WHERE sql NOT NULL AND rootpage==0 AND type='table'"
|
||||
"WHERE sql NOT NULL AND type IN ('index','trigger','view')"
|
||||
);
|
||||
}else{
|
||||
int i;
|
||||
@@ -935,19 +1017,20 @@ static int do_meta_command(char *zLine, struct callback_data *p){
|
||||
run_schema_dump_query(p,
|
||||
"SELECT name, type, sql FROM sqlite_master "
|
||||
"WHERE tbl_name LIKE shellstatic() AND type=='table'"
|
||||
" AND rootpage!=0 AND sql NOT NULL", 0);
|
||||
run_schema_dump_query(p,
|
||||
"SELECT name, type, sql FROM sqlite_master "
|
||||
"WHERE tbl_name LIKE shellstatic() AND type!='table'"
|
||||
" AND type!='meta' AND sql NOT NULL", 0);
|
||||
" AND sql NOT NULL", 0);
|
||||
run_table_dump_query(p->out, p->db,
|
||||
"SELECT sql FROM sqlite_master "
|
||||
"WHERE sql NOT NULL AND rootpage==0 AND type='table'"
|
||||
"WHERE sql NOT NULL"
|
||||
" AND type IN ('index','trigger','view')"
|
||||
" AND tbl_name LIKE shellstatic()"
|
||||
);
|
||||
zShellStatic = 0;
|
||||
}
|
||||
}
|
||||
if( p->writableSchema ){
|
||||
fprintf(p->out, "PRAGMA writable_schema=OFF;\n");
|
||||
p->writableSchema = 0;
|
||||
}
|
||||
if( zErrMsg ){
|
||||
fprintf(stderr,"Error: %s\n", zErrMsg);
|
||||
sqlite3_free(zErrMsg);
|
||||
@@ -957,37 +1040,15 @@ static int do_meta_command(char *zLine, struct callback_data *p){
|
||||
}else
|
||||
|
||||
if( c=='e' && strncmp(azArg[0], "echo", n)==0 && nArg>1 ){
|
||||
int j;
|
||||
char *z = azArg[1];
|
||||
int val = atoi(azArg[1]);
|
||||
for(j=0; z[j]; j++){
|
||||
z[j] = tolower((unsigned char)z[j]);
|
||||
}
|
||||
if( strcmp(z,"on")==0 ){
|
||||
val = 1;
|
||||
}else if( strcmp(z,"yes")==0 ){
|
||||
val = 1;
|
||||
}
|
||||
p->echoOn = val;
|
||||
p->echoOn = booleanValue(azArg[1]);
|
||||
}else
|
||||
|
||||
if( c=='e' && strncmp(azArg[0], "exit", n)==0 ){
|
||||
rc = 1;
|
||||
rc = 2;
|
||||
}else
|
||||
|
||||
if( c=='e' && strncmp(azArg[0], "explain", n)==0 ){
|
||||
int j;
|
||||
static char zOne[] = "1";
|
||||
char *z = nArg>=2 ? azArg[1] : zOne;
|
||||
int val = atoi(z);
|
||||
for(j=0; z[j]; j++){
|
||||
z[j] = tolower((unsigned char)z[j]);
|
||||
}
|
||||
if( strcmp(z,"on")==0 ){
|
||||
val = 1;
|
||||
}else if( strcmp(z,"yes")==0 ){
|
||||
val = 1;
|
||||
}
|
||||
int val = nArg>=2 ? booleanValue(azArg[1]) : 1;
|
||||
if(val == 1) {
|
||||
if(!p->explainPrev.valid) {
|
||||
p->explainPrev.valid = 1;
|
||||
@@ -1018,21 +1079,9 @@ static int do_meta_command(char *zLine, struct callback_data *p){
|
||||
}
|
||||
}else
|
||||
|
||||
if( c=='h' && (strncmp(azArg[0], "header", n)==0
|
||||
||
|
||||
if( c=='h' && (strncmp(azArg[0], "header", n)==0 ||
|
||||
strncmp(azArg[0], "headers", n)==0 )&& nArg>1 ){
|
||||
int j;
|
||||
char *z = azArg[1];
|
||||
int val = atoi(azArg[1]);
|
||||
for(j=0; z[j]; j++){
|
||||
z[j] = tolower((unsigned char)z[j]);
|
||||
}
|
||||
if( strcmp(z,"on")==0 ){
|
||||
val = 1;
|
||||
}else if( strcmp(z,"yes")==0 ){
|
||||
val = 1;
|
||||
}
|
||||
p->showHeader = val;
|
||||
p->showHeader = booleanValue(azArg[1]);
|
||||
}else
|
||||
|
||||
if( c=='h' && strncmp(azArg[0], "help", n)==0 ){
|
||||
@@ -1069,6 +1118,7 @@ static int do_meta_command(char *zLine, struct callback_data *p){
|
||||
if( rc ){
|
||||
fprintf(stderr,"Error: %s\n", sqlite3_errmsg(db));
|
||||
nCol = 0;
|
||||
rc = 1;
|
||||
}else{
|
||||
nCol = sqlite3_column_count(pStmt);
|
||||
}
|
||||
@@ -1089,7 +1139,7 @@ static int do_meta_command(char *zLine, struct callback_data *p){
|
||||
if( rc ){
|
||||
fprintf(stderr, "Error: %s\n", sqlite3_errmsg(db));
|
||||
sqlite3_finalize(pStmt);
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
in = fopen(zFile, "rb");
|
||||
if( in==0 ){
|
||||
@@ -1135,6 +1185,7 @@ static int do_meta_command(char *zLine, struct callback_data *p){
|
||||
if( rc!=SQLITE_OK ){
|
||||
fprintf(stderr,"Error: %s\n", sqlite3_errmsg(db));
|
||||
zCommit = "ROLLBACK";
|
||||
rc = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1180,6 +1231,7 @@ static int do_meta_command(char *zLine, struct callback_data *p){
|
||||
if( rc!=SQLITE_OK ){
|
||||
fprintf(stderr, "%s\n", zErrMsg);
|
||||
sqlite3_free(zErrMsg);
|
||||
rc = 1;
|
||||
}
|
||||
}else
|
||||
#endif
|
||||
@@ -1214,7 +1266,7 @@ static int do_meta_command(char *zLine, struct callback_data *p){
|
||||
set_table_name(p, "table");
|
||||
}
|
||||
}else {
|
||||
fprintf(stderr,"mode should be on of: "
|
||||
fprintf(stderr,"mode should be one of: "
|
||||
"column csv html insert line list tabs tcl\n");
|
||||
}
|
||||
}else
|
||||
@@ -1251,7 +1303,7 @@ static int do_meta_command(char *zLine, struct callback_data *p){
|
||||
}else
|
||||
|
||||
if( c=='q' && strncmp(azArg[0], "quit", n)==0 ){
|
||||
rc = 1;
|
||||
rc = 2;
|
||||
}else
|
||||
|
||||
if( c=='r' && strncmp(azArg[0], "read", n)==0 && nArg==2 ){
|
||||
@@ -1403,6 +1455,8 @@ static int do_meta_command(char *zLine, struct callback_data *p){
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
}else{
|
||||
rc = 1;
|
||||
}
|
||||
sqlite3_free_table(azResult);
|
||||
}else
|
||||
@@ -1482,24 +1536,40 @@ static int _is_command_terminator(const char *zLine){
|
||||
** is coming from a file or device. A prompt is issued and history
|
||||
** is saved only if input is interactive. An interrupt signal will
|
||||
** cause this routine to exit immediately, unless input is interactive.
|
||||
**
|
||||
** Return the number of errors.
|
||||
*/
|
||||
static void process_input(struct callback_data *p, FILE *in){
|
||||
static int process_input(struct callback_data *p, FILE *in){
|
||||
char *zLine;
|
||||
char *zSql = 0;
|
||||
int nSql = 0;
|
||||
char *zErrMsg;
|
||||
int rc;
|
||||
while( fflush(p->out), (zLine = one_input_line(zSql, in))!=0 ){
|
||||
int errCnt = 0;
|
||||
int lineno = 0;
|
||||
int startline = 0;
|
||||
|
||||
while( errCnt==0 || !bail_on_error || (in==0 && stdin_is_interactive) ){
|
||||
fflush(p->out);
|
||||
zLine = one_input_line(zSql, in);
|
||||
if( zLine==0 ){
|
||||
break; /* We have reached EOF */
|
||||
}
|
||||
if( seenInterrupt ){
|
||||
if( in!=0 ) break;
|
||||
seenInterrupt = 0;
|
||||
}
|
||||
lineno++;
|
||||
if( p->echoOn ) printf("%s\n", zLine);
|
||||
if( (zSql==0 || zSql[0]==0) && _all_whitespace(zLine) ) continue;
|
||||
if( zLine && zLine[0]=='.' && nSql==0 ){
|
||||
int rc = do_meta_command(zLine, p);
|
||||
rc = do_meta_command(zLine, p);
|
||||
free(zLine);
|
||||
if( rc ) break;
|
||||
if( rc==2 ){
|
||||
break;
|
||||
}else if( rc ){
|
||||
errCnt++;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if( _is_command_terminator(zLine) ){
|
||||
@@ -1516,6 +1586,7 @@ static void process_input(struct callback_data *p, FILE *in){
|
||||
exit(1);
|
||||
}
|
||||
strcpy(zSql, zLine);
|
||||
startline = lineno;
|
||||
}
|
||||
}else{
|
||||
int len = strlen(zLine);
|
||||
@@ -1534,14 +1605,20 @@ static void process_input(struct callback_data *p, FILE *in){
|
||||
open_db(p);
|
||||
rc = sqlite3_exec(p->db, zSql, callback, p, &zErrMsg);
|
||||
if( rc || zErrMsg ){
|
||||
/* if( in!=0 && !p->echoOn ) printf("%s\n",zSql); */
|
||||
char zPrefix[100];
|
||||
if( in!=0 || !stdin_is_interactive ){
|
||||
sprintf(zPrefix, "SQL error near line %d:", startline);
|
||||
}else{
|
||||
sprintf(zPrefix, "SQL error:");
|
||||
}
|
||||
if( zErrMsg!=0 ){
|
||||
printf("SQL error: %s\n", zErrMsg);
|
||||
printf("%s %s\n", zPrefix, zErrMsg);
|
||||
sqlite3_free(zErrMsg);
|
||||
zErrMsg = 0;
|
||||
}else{
|
||||
printf("SQL error: %s\n", sqlite3_errmsg(p->db));
|
||||
printf("%s %s\n", zPrefix, sqlite3_errmsg(p->db));
|
||||
}
|
||||
errCnt++;
|
||||
}
|
||||
free(zSql);
|
||||
zSql = 0;
|
||||
@@ -1552,6 +1629,7 @@ static void process_input(struct callback_data *p, FILE *in){
|
||||
if( !_all_whitespace(zSql) ) printf("Incomplete SQL: %s\n", zSql);
|
||||
free(zSql);
|
||||
}
|
||||
return errCnt;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1642,7 +1720,7 @@ static void process_sqliterc(
|
||||
}
|
||||
in = fopen(sqliterc,"rb");
|
||||
if( in ){
|
||||
if( isatty(fileno(stdout)) ){
|
||||
if( stdin_is_interactive ){
|
||||
printf("Loading resources from %s\n",sqliterc);
|
||||
}
|
||||
process_input(p,in);
|
||||
@@ -1659,7 +1737,11 @@ static const char zOptions[] =
|
||||
" -init filename read/process named file\n"
|
||||
" -echo print commands before execution\n"
|
||||
" -[no]header turn headers on or off\n"
|
||||
" -bail stop after hitting an error\n"
|
||||
" -interactive force interactive I/O\n"
|
||||
" -batch force batch I/O\n"
|
||||
" -column set output mode to 'column'\n"
|
||||
" -csv set output mode to 'csv'\n"
|
||||
" -html set output mode to HTML\n"
|
||||
" -line set output mode to 'line'\n"
|
||||
" -list set output mode to 'list'\n"
|
||||
@@ -1698,6 +1780,7 @@ int main(int argc, char **argv){
|
||||
const char *zInitFile = 0;
|
||||
char *zFirstCmd = 0;
|
||||
int i;
|
||||
int rc = 0;
|
||||
|
||||
#ifdef __MACOS__
|
||||
argc = ccommand(&argv);
|
||||
@@ -1705,6 +1788,7 @@ int main(int argc, char **argv){
|
||||
|
||||
Argv0 = argv[0];
|
||||
main_init(&data);
|
||||
stdin_is_interactive = isatty(0);
|
||||
|
||||
/* Make sure we have a valid signal handler early, before anything
|
||||
** else is done.
|
||||
@@ -1718,7 +1802,10 @@ int main(int argc, char **argv){
|
||||
** and the first command to execute.
|
||||
*/
|
||||
for(i=1; i<argc-1; i++){
|
||||
char *z;
|
||||
if( argv[i][0]!='-' ) break;
|
||||
z = argv[i];
|
||||
if( z[0]=='-' && z[1]=='-' ) z++;
|
||||
if( strcmp(argv[i],"-separator")==0 || strcmp(argv[i],"-nullvalue")==0 ){
|
||||
i++;
|
||||
}else if( strcmp(argv[i],"-init")==0 ){
|
||||
@@ -1769,6 +1856,7 @@ int main(int argc, char **argv){
|
||||
*/
|
||||
for(i=1; i<argc && argv[i][0]=='-'; i++){
|
||||
char *z = argv[i];
|
||||
if( z[1]=='-' ){ z++; }
|
||||
if( strcmp(z,"-init")==0 ){
|
||||
i++;
|
||||
}else if( strcmp(z,"-html")==0 ){
|
||||
@@ -1779,6 +1867,9 @@ int main(int argc, char **argv){
|
||||
data.mode = MODE_Line;
|
||||
}else if( strcmp(z,"-column")==0 ){
|
||||
data.mode = MODE_Column;
|
||||
}else if( strcmp(z,"-csv")==0 ){
|
||||
data.mode = MODE_Csv;
|
||||
strcpy(data.separator,",");
|
||||
}else if( strcmp(z,"-separator")==0 ){
|
||||
i++;
|
||||
sprintf(data.separator,"%.*s",(int)sizeof(data.separator)-1,argv[i]);
|
||||
@@ -1791,9 +1882,15 @@ int main(int argc, char **argv){
|
||||
data.showHeader = 0;
|
||||
}else if( strcmp(z,"-echo")==0 ){
|
||||
data.echoOn = 1;
|
||||
}else if( strcmp(z,"-bail")==0 ){
|
||||
bail_on_error = 1;
|
||||
}else if( strcmp(z,"-version")==0 ){
|
||||
printf("%s\n", sqlite3_libversion());
|
||||
return 0;
|
||||
}else if( strcmp(z,"-interactive")==0 ){
|
||||
stdin_is_interactive = 1;
|
||||
}else if( strcmp(z,"-batch")==0 ){
|
||||
stdin_is_interactive = 0;
|
||||
}else if( strcmp(z,"-help")==0 || strcmp(z, "--help")==0 ){
|
||||
usage(1);
|
||||
}else{
|
||||
@@ -1821,7 +1918,7 @@ int main(int argc, char **argv){
|
||||
}else{
|
||||
/* Run commands received from standard input
|
||||
*/
|
||||
if( isatty(fileno(stdout)) && isatty(fileno(stdin)) ){
|
||||
if( stdin_is_interactive ){
|
||||
char *zHome;
|
||||
char *zHistory = 0;
|
||||
printf(
|
||||
@@ -1836,7 +1933,7 @@ int main(int argc, char **argv){
|
||||
#if defined(HAVE_READLINE) && HAVE_READLINE==1
|
||||
if( zHistory ) read_history(zHistory);
|
||||
#endif
|
||||
process_input(&data, 0);
|
||||
rc = process_input(&data, 0);
|
||||
if( zHistory ){
|
||||
stifle_history(100);
|
||||
write_history(zHistory);
|
||||
@@ -1844,7 +1941,7 @@ int main(int argc, char **argv){
|
||||
}
|
||||
free(zHome);
|
||||
}else{
|
||||
process_input(&data, stdin);
|
||||
rc = process_input(&data, stdin);
|
||||
}
|
||||
}
|
||||
set_table_name(&data, 0);
|
||||
@@ -1853,5 +1950,5 @@ int main(int argc, char **argv){
|
||||
fprintf(stderr,"error closing database: %s\n", sqlite3_errmsg(db));
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
** This header file defines the interface that the SQLite library
|
||||
** presents to client programs.
|
||||
**
|
||||
** @(#) $Id: sqlite.h.in,v 1.194 2006/09/16 21:45:14 drh Exp $
|
||||
** @(#) $Id: sqlite.h.in,v 1.198 2007/01/26 00:51:44 drh Exp $
|
||||
*/
|
||||
#ifndef _SQLITE3_H_
|
||||
#define _SQLITE3_H_
|
||||
@@ -125,7 +125,7 @@ typedef int (*sqlite3_callback)(void*,int,char**, char**);
|
||||
** value then the query is aborted, all subsequent SQL statements
|
||||
** are skipped and the sqlite3_exec() function returns the SQLITE_ABORT.
|
||||
**
|
||||
** The 4th parameter is an arbitrary pointer that is passed
|
||||
** The 1st parameter is an arbitrary pointer that is passed
|
||||
** to the callback function as its first parameter.
|
||||
**
|
||||
** The 2nd parameter to the callback function is the number of
|
||||
@@ -315,13 +315,30 @@ int sqlite3_complete16(const void *sql);
|
||||
** currently locked by another process or thread. If the busy callback
|
||||
** is NULL, then sqlite3_exec() returns SQLITE_BUSY immediately if
|
||||
** it finds a locked table. If the busy callback is not NULL, then
|
||||
** sqlite3_exec() invokes the callback with three arguments. The
|
||||
** second argument is the name of the locked table and the third
|
||||
** argument is the number of times the table has been busy. If the
|
||||
** sqlite3_exec() invokes the callback with two arguments. The
|
||||
** first argument to the handler is a copy of the void* pointer which
|
||||
** is the third argument to this routine. The second argument to
|
||||
** the handler is the number of times that the busy handler has
|
||||
** been invoked for this locking event. If the
|
||||
** busy callback returns 0, then sqlite3_exec() immediately returns
|
||||
** SQLITE_BUSY. If the callback returns non-zero, then sqlite3_exec()
|
||||
** tries to open the table again and the cycle repeats.
|
||||
**
|
||||
** The presence of a busy handler does not guarantee that
|
||||
** it will be invoked when there is lock contention.
|
||||
** If SQLite determines that invoking the busy handler could result in
|
||||
** a deadlock, it will return SQLITE_BUSY instead.
|
||||
** Consider a scenario where one process is holding a read lock that
|
||||
** it is trying to promote to a reserved lock and
|
||||
** a second process is holding a reserved lock that it is trying
|
||||
** to promote to an exclusive lock. The first process cannot proceed
|
||||
** because it is blocked by the second and the second process cannot
|
||||
** proceed because it is blocked by the first. If both processes
|
||||
** invoke the busy handlers, neither will make any progress. Therefore,
|
||||
** SQLite returns SQLITE_BUSY for the first process, hoping that this
|
||||
** will induce the first process to release its read lock and allow
|
||||
** the second process to proceed.
|
||||
**
|
||||
** The default busy callback is NULL.
|
||||
**
|
||||
** Sqlite is re-entrant, so the busy handler may start a new query.
|
||||
@@ -692,6 +709,31 @@ int sqlite3_prepare16(
|
||||
const void **pzTail /* OUT: Pointer to unused portion of zSql */
|
||||
);
|
||||
|
||||
/*
|
||||
** Newer versions of the prepare API work just like the legacy versions
|
||||
** but with one exception: The a copy of the SQL text is saved in the
|
||||
** sqlite3_stmt structure that is returned. If this copy exists, it
|
||||
** modifieds the behavior of sqlite3_step() slightly. First, sqlite3_step()
|
||||
** will no longer return an SQLITE_SCHEMA error but will instead automatically
|
||||
** rerun the compiler to rebuild the prepared statement. Secondly,
|
||||
** sqlite3_step() now turns a full result code - the result code that
|
||||
** use used to have to call sqlite3_reset() to get.
|
||||
*/
|
||||
int sqlite3_prepare_v2(
|
||||
sqlite3 *db, /* Database handle */
|
||||
const char *zSql, /* SQL statement, UTF-8 encoded */
|
||||
int nBytes, /* Length of zSql in bytes. */
|
||||
sqlite3_stmt **ppStmt, /* OUT: Statement handle */
|
||||
const char **pzTail /* OUT: Pointer to unused portion of zSql */
|
||||
);
|
||||
int sqlite3_prepare16_v2(
|
||||
sqlite3 *db, /* Database handle */
|
||||
const void *zSql, /* SQL statement, UTF-16 encoded */
|
||||
int nBytes, /* Length of zSql in bytes. */
|
||||
sqlite3_stmt **ppStmt, /* OUT: Statement handle */
|
||||
const void **pzTail /* OUT: Pointer to unused portion of zSql */
|
||||
);
|
||||
|
||||
/*
|
||||
** Pointers to the following two opaque structures are used to communicate
|
||||
** with the implementations of user-defined functions.
|
||||
@@ -1143,9 +1185,13 @@ void sqlite3_set_auxdata(sqlite3_context*, int, void*, void (*)(void*));
|
||||
** SQLITE_TRANSIENT value means that the content will likely change in
|
||||
** the near future and that SQLite should make its own private copy of
|
||||
** the content before returning.
|
||||
**
|
||||
** The typedef is necessary to work around problems in certain
|
||||
** C++ compilers. See ticket #2191.
|
||||
*/
|
||||
#define SQLITE_STATIC ((void(*)(void *))0)
|
||||
#define SQLITE_TRANSIENT ((void(*)(void *))-1)
|
||||
typedef void (*sqlite3_destructor_type)(void*);
|
||||
#define SQLITE_STATIC ((sqlite3_destructor_type)0)
|
||||
#define SQLITE_TRANSIENT ((sqlite3_destructor_type)-1)
|
||||
|
||||
/*
|
||||
** User-defined functions invoke the following routines in order to
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
** as extensions by SQLite should #include this file instead of
|
||||
** sqlite3.h.
|
||||
**
|
||||
** @(#) $Id: sqlite3ext.h,v 1.7 2006/09/22 23:38:21 shess Exp $
|
||||
** @(#) $Id: sqlite3ext.h,v 1.8 2007/01/09 14:37:18 drh Exp $
|
||||
*/
|
||||
#ifndef _SQLITE3EXT_H_
|
||||
#define _SQLITE3EXT_H_
|
||||
@@ -92,7 +92,7 @@ struct sqlite3_api_routines {
|
||||
void * (*get_auxdata)(sqlite3_context*,int);
|
||||
int (*get_table)(sqlite3*,const char*,char***,int*,int*,char**);
|
||||
int (*global_recover)(void);
|
||||
void (*interrupt)(sqlite3*);
|
||||
void (*interruptx)(sqlite3*);
|
||||
sqlite_int64 (*last_insert_rowid)(sqlite3*);
|
||||
const char * (*libversion)(void);
|
||||
int (*libversion_number)(void);
|
||||
@@ -222,7 +222,7 @@ struct sqlite3_api_routines {
|
||||
#define sqlite3_get_auxdata sqlite3_api->get_auxdata
|
||||
#define sqlite3_get_table sqlite3_api->get_table
|
||||
#define sqlite3_global_recover sqlite3_api->global_recover
|
||||
#define sqlite3_interrupt sqlite3_api->interrupt
|
||||
#define sqlite3_interrupt sqlite3_api->interruptx
|
||||
#define sqlite3_last_insert_rowid sqlite3_api->last_insert_rowid
|
||||
#define sqlite3_libversion sqlite3_api->libversion
|
||||
#define sqlite3_libversion_number sqlite3_api->libversion_number
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
*************************************************************************
|
||||
** Internal interface definitions for SQLite.
|
||||
**
|
||||
** @(#) $Id: sqliteInt.h,v 1.529 2006/09/23 20:36:02 drh Exp $
|
||||
** @(#) $Id: sqliteInt.h,v 1.536 2007/02/13 12:49:24 drh Exp $
|
||||
*/
|
||||
#ifndef _SQLITEINT_H_
|
||||
#define _SQLITEINT_H_
|
||||
@@ -463,7 +463,7 @@ struct sqlite3 {
|
||||
u8 busy; /* TRUE if currently initializing */
|
||||
} init;
|
||||
int nExtension; /* Number of loaded extensions */
|
||||
void *aExtension; /* Array of shared libraray handles */
|
||||
void **aExtension; /* Array of shared libraray handles */
|
||||
struct Vdbe *pVdbe; /* List of active virtual machines */
|
||||
int activeVdbeCnt; /* Number of vdbes currently executing */
|
||||
void (*xTrace)(void*,const char*); /* Trace function */
|
||||
@@ -1021,6 +1021,7 @@ struct Expr {
|
||||
#define EP_VarSelect 0x20 /* pSelect is correlated, not constant */
|
||||
#define EP_Dequoted 0x40 /* True if the string has been dequoted */
|
||||
#define EP_InfixFunc 0x80 /* True for an infix function: LIKE, GLOB, etc */
|
||||
#define EP_ExpCollate 0x100 /* Collating sequence specified explicitly */
|
||||
|
||||
/*
|
||||
** These macros can be used to test, set, or clear bits in the
|
||||
@@ -1078,8 +1079,12 @@ struct IdList {
|
||||
|
||||
/*
|
||||
** The bitmask datatype defined below is used for various optimizations.
|
||||
**
|
||||
** Changing this from a 64-bit to a 32-bit type limits the number of
|
||||
** tables in a join to 32 instead of 64. But it also reduces the size
|
||||
** of the library by 738 bytes on ix86.
|
||||
*/
|
||||
typedef unsigned int Bitmask;
|
||||
typedef u64 Bitmask;
|
||||
|
||||
/*
|
||||
** The following structure describes the FROM clause of a SELECT statement.
|
||||
@@ -1091,6 +1096,11 @@ typedef unsigned int Bitmask;
|
||||
** is modified by an INSERT, DELETE, or UPDATE statement. In standard SQL,
|
||||
** such a table must be a simple name: ID. But in SQLite, the table can
|
||||
** now be identified by a database name, a dot, then the table name: ID.ID.
|
||||
**
|
||||
** The jointype starts out showing the join type between the current table
|
||||
** and the next table on the list. The parser builds the list this way.
|
||||
** But sqlite3SrcListShiftJoinType() later shifts the jointypes so that each
|
||||
** jointype expresses the join between the table and the previous table.
|
||||
*/
|
||||
struct SrcList {
|
||||
i16 nSrc; /* Number of tables or subqueries in the FROM clause */
|
||||
@@ -1102,8 +1112,8 @@ struct SrcList {
|
||||
Table *pTab; /* An SQL table corresponding to zName */
|
||||
Select *pSelect; /* A SELECT statement used in place of a table name */
|
||||
u8 isPopulated; /* Temporary table associated with SELECT is populated */
|
||||
u8 jointype; /* Type of join between this table and the next */
|
||||
i16 iCursor; /* The VDBE cursor number used to access this table */
|
||||
u8 jointype; /* Type of join between this able and the previous */
|
||||
int iCursor; /* The VDBE cursor number used to access this table */
|
||||
Expr *pOn; /* The ON clause of a join */
|
||||
IdList *pUsing; /* The USING clause of a join */
|
||||
Bitmask colUsed; /* Bit N (1<<N) set if column N or pTab is used */
|
||||
@@ -1617,7 +1627,9 @@ int sqlite3ArrayAllocate(void**,int,int);
|
||||
IdList *sqlite3IdListAppend(IdList*, Token*);
|
||||
int sqlite3IdListIndex(IdList*,const char*);
|
||||
SrcList *sqlite3SrcListAppend(SrcList*, Token*, Token*);
|
||||
void sqlite3SrcListAddAlias(SrcList*, Token*);
|
||||
SrcList *sqlite3SrcListAppendFromTerm(SrcList*, Token*, Token*, Token*,
|
||||
Select*, Expr*, IdList*);
|
||||
void sqlite3SrcListShiftJoinType(SrcList*);
|
||||
void sqlite3SrcListAssignCursors(Parse*, SrcList*);
|
||||
void sqlite3IdListDelete(IdList*);
|
||||
void sqlite3SrcListDelete(SrcList*);
|
||||
@@ -1766,6 +1778,7 @@ int sqlite3ReadSchema(Parse *pParse);
|
||||
CollSeq *sqlite3FindCollSeq(sqlite3*,u8 enc, const char *,int,int);
|
||||
CollSeq *sqlite3LocateCollSeq(Parse *pParse, const char *zName, int nName);
|
||||
CollSeq *sqlite3ExprCollSeq(Parse *pParse, Expr *pExpr);
|
||||
Expr *sqlite3ExprSetColl(Parse *pParse, Expr *, Token *);
|
||||
int sqlite3CheckCollSeq(Parse *, CollSeq *);
|
||||
int sqlite3CheckIndexCollSeq(Parse *, Index *);
|
||||
int sqlite3CheckObjectName(Parse *, const char *);
|
||||
@@ -1876,6 +1889,7 @@ int sqlite3VtabCallDestroy(sqlite3*, int, const char *);
|
||||
int sqlite3VtabBegin(sqlite3 *, sqlite3_vtab *);
|
||||
FuncDef *sqlite3VtabOverloadFunction(FuncDef*, int nArg, Expr*);
|
||||
void sqlite3InvalidFunction(sqlite3_context*,int,sqlite3_value**);
|
||||
int sqlite3Reprepare(Vdbe*);
|
||||
|
||||
#ifdef SQLITE_SSE
|
||||
#include "sseInt.h"
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
*************************************************************************
|
||||
** A TCL Interface to SQLite
|
||||
**
|
||||
** $Id: tclsqlite.c,v 1.173 2006/09/02 14:17:00 drh Exp $
|
||||
** $Id: tclsqlite.c,v 1.176 2007/02/01 01:53:44 drh Exp $
|
||||
*/
|
||||
#ifndef NO_TCL /* Omit this whole file if TCL is unavailable */
|
||||
|
||||
@@ -1055,7 +1055,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
||||
return TCL_ERROR;
|
||||
}
|
||||
nByte = strlen(zSql);
|
||||
rc = sqlite3_prepare(pDb->db, zSql, 0, &pStmt, 0);
|
||||
rc = sqlite3_prepare(pDb->db, zSql, -1, &pStmt, 0);
|
||||
sqlite3_free(zSql);
|
||||
if( rc ){
|
||||
Tcl_AppendResult(interp, "Error: ", sqlite3_errmsg(pDb->db), 0);
|
||||
@@ -1081,7 +1081,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
||||
}
|
||||
zSql[j++] = ')';
|
||||
zSql[j] = 0;
|
||||
rc = sqlite3_prepare(pDb->db, zSql, 0, &pStmt, 0);
|
||||
rc = sqlite3_prepare(pDb->db, zSql, -1, &pStmt, 0);
|
||||
free(zSql);
|
||||
if( rc ){
|
||||
Tcl_AppendResult(interp, "Error: ", sqlite3_errmsg(pDb->db), 0);
|
||||
@@ -1173,6 +1173,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
||||
** default.
|
||||
*/
|
||||
case DB_ENABLE_LOAD_EXTENSION: {
|
||||
#ifndef SQLITE_OMIT_LOAD_EXTENSION
|
||||
int onoff;
|
||||
if( objc!=3 ){
|
||||
Tcl_WrongNumArgs(interp, 2, objv, "BOOLEAN");
|
||||
@@ -1183,6 +1184,11 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
||||
}
|
||||
sqlite3_enable_load_extension(pDb->db, onoff);
|
||||
break;
|
||||
#else
|
||||
Tcl_AppendResult(interp, "extension loading is turned off at compile-time",
|
||||
0);
|
||||
return TCL_ERROR;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -2055,7 +2061,7 @@ static int DbMain(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
||||
sqlite3_open(zFile, &p->db);
|
||||
Tcl_DStringFree(&translatedFilename);
|
||||
if( SQLITE_OK!=sqlite3_errcode(p->db) ){
|
||||
zErrMsg = strdup(sqlite3_errmsg(p->db));
|
||||
zErrMsg = sqlite3_mprintf("%s", sqlite3_errmsg(p->db));
|
||||
sqlite3_close(p->db);
|
||||
p->db = 0;
|
||||
}
|
||||
@@ -2065,7 +2071,7 @@ static int DbMain(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
||||
if( p->db==0 ){
|
||||
Tcl_SetResult(interp, zErrMsg, TCL_VOLATILE);
|
||||
Tcl_Free((char*)p);
|
||||
free(zErrMsg);
|
||||
sqlite3_free(zErrMsg);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
p->maxStmt = NUM_PREPARED_STMTS;
|
||||
|
||||
+268
-11
@@ -13,7 +13,7 @@
|
||||
** is not included in the SQLite library. It is used for automated
|
||||
** testing of the SQLite library.
|
||||
**
|
||||
** $Id: test1.c,v 1.222 2006/09/15 07:28:51 drh Exp $
|
||||
** $Id: test1.c,v 1.228 2007/02/05 14:21:48 danielk1977 Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include "tcl.h"
|
||||
@@ -63,6 +63,22 @@ static int get_sqlite_pointer(
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Decode a pointer to an sqlite3 object.
|
||||
*/
|
||||
static int getDbPointer(Tcl_Interp *interp, const char *zA, sqlite3 **ppDb){
|
||||
struct SqliteDb *p;
|
||||
Tcl_CmdInfo cmdInfo;
|
||||
if( Tcl_GetCommandInfo(interp, zA, &cmdInfo) ){
|
||||
p = (struct SqliteDb*)cmdInfo.objClientData;
|
||||
*ppDb = p->db;
|
||||
}else{
|
||||
*ppDb = (sqlite3*)sqlite3TextToPtr(zA);
|
||||
}
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
|
||||
const char *sqlite3TestErrorName(int rc){
|
||||
const char *zName = 0;
|
||||
switch( rc & 0xff ){
|
||||
@@ -121,14 +137,6 @@ int sqlite3TestErrCode(Tcl_Interp *interp, sqlite3 *db, int rc){
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Decode a pointer to an sqlite3 object.
|
||||
*/
|
||||
static int getDbPointer(Tcl_Interp *interp, const char *zA, sqlite3 **ppDb){
|
||||
*ppDb = (sqlite3*)sqlite3TextToPtr(zA);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Decode a pointer to an sqlite3_stmt object.
|
||||
*/
|
||||
@@ -227,6 +235,65 @@ static int test_exec_printf(
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: sqlite3_exec DB SQL
|
||||
**
|
||||
** Invoke the sqlite3_exec interface using the open database DB
|
||||
*/
|
||||
static int test_exec(
|
||||
void *NotUsed,
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int argc, /* Number of arguments */
|
||||
char **argv /* Text of each argument */
|
||||
){
|
||||
sqlite3 *db;
|
||||
Tcl_DString str;
|
||||
int rc;
|
||||
char *zErr = 0;
|
||||
char zBuf[30];
|
||||
if( argc!=3 ){
|
||||
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
|
||||
" DB SQL", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( getDbPointer(interp, argv[1], &db) ) return TCL_ERROR;
|
||||
Tcl_DStringInit(&str);
|
||||
rc = sqlite3_exec(db, argv[2], exec_printf_cb, &str, &zErr);
|
||||
sprintf(zBuf, "%d", rc);
|
||||
Tcl_AppendElement(interp, zBuf);
|
||||
Tcl_AppendElement(interp, rc==SQLITE_OK ? Tcl_DStringValue(&str) : zErr);
|
||||
Tcl_DStringFree(&str);
|
||||
if( zErr ) sqlite3_free(zErr);
|
||||
if( sqlite3TestErrCode(interp, db, rc) ) return TCL_ERROR;
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: sqlite3_exec_nr DB SQL
|
||||
**
|
||||
** Invoke the sqlite3_exec interface using the open database DB. Discard
|
||||
** all results
|
||||
*/
|
||||
static int test_exec_nr(
|
||||
void *NotUsed,
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int argc, /* Number of arguments */
|
||||
char **argv /* Text of each argument */
|
||||
){
|
||||
sqlite3 *db;
|
||||
int rc;
|
||||
char *zErr = 0;
|
||||
if( argc!=3 ){
|
||||
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
|
||||
" DB SQL", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( getDbPointer(interp, argv[1], &db) ) return TCL_ERROR;
|
||||
rc = sqlite3_exec(db, argv[2], 0, 0, &zErr);
|
||||
if( sqlite3TestErrCode(interp, db, rc) ) return TCL_ERROR;
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: sqlite3_mprintf_z_test SEPARATOR ARG0 ARG1 ...
|
||||
**
|
||||
@@ -543,6 +610,46 @@ static void sqlite3ExecFunc(
|
||||
sqliteFree(x.z);
|
||||
}
|
||||
|
||||
/*
|
||||
** Implementation of tkt2213func(), a scalar function that takes exactly
|
||||
** one argument. It has two interesting features:
|
||||
**
|
||||
** * It calls sqlite3_value_text() 3 times on the argument sqlite3_value*.
|
||||
** If the three pointers returned are not the same an SQL error is raised.
|
||||
**
|
||||
** * Otherwise it returns a copy of the text representation of it's
|
||||
** argument in such a way as the VDBE representation is a Mem* cell
|
||||
** with the MEM_Term flag clear.
|
||||
**
|
||||
** Ticket #2213 can therefore be tested by evaluating the following
|
||||
** SQL expression:
|
||||
**
|
||||
** tkt2213func(tkt2213func('a string'));
|
||||
*/
|
||||
static void tkt2213Function(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
int nText;
|
||||
unsigned char const *zText1;
|
||||
unsigned char const *zText2;
|
||||
unsigned char const *zText3;
|
||||
|
||||
nText = sqlite3_value_bytes(argv[0]);
|
||||
zText1 = sqlite3_value_text(argv[0]);
|
||||
zText2 = sqlite3_value_text(argv[0]);
|
||||
zText3 = sqlite3_value_text(argv[0]);
|
||||
|
||||
if( zText1!=zText2 || zText2!=zText3 ){
|
||||
sqlite3_result_error(context, "tkt2213 is not fixed", -1);
|
||||
}else{
|
||||
char *zCopy = (char *)sqlite3_malloc(nText);
|
||||
memcpy(zCopy, zText1, nText);
|
||||
sqlite3_result_text(context, zCopy, nText, sqlite3_free);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: sqlite_test_create_function DB
|
||||
**
|
||||
@@ -584,6 +691,10 @@ static int test_create_function(
|
||||
rc = sqlite3_create_function(db, "hex16", 1, SQLITE_ANY, 0,
|
||||
hex16Func, 0, 0);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_create_function(db, "tkt2213func", 1, SQLITE_ANY, 0,
|
||||
tkt2213Function, 0, 0);
|
||||
}
|
||||
|
||||
#ifndef SQLITE_OMIT_UTF16
|
||||
/* Use the sqlite3_create_function16() API here. Mainly for fun, but also
|
||||
@@ -693,6 +804,30 @@ static int test_create_aggregate(
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Usage: printf TEXT
|
||||
**
|
||||
** Send output to printf. Use this rather than puts to merge the output
|
||||
** in the correct sequence with debugging printfs inserted into C code.
|
||||
** Puts uses a separate buffer and debugging statements will be out of
|
||||
** sequence if it is used.
|
||||
*/
|
||||
static int test_printf(
|
||||
void *NotUsed,
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int argc, /* Number of arguments */
|
||||
char **argv /* Text of each argument */
|
||||
){
|
||||
if( argc!=2 ){
|
||||
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
|
||||
" TEXT\"", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
printf("%s\n", argv[1]);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** Usage: sqlite3_mprintf_int FORMAT INTEGER INTEGER INTEGER
|
||||
@@ -1348,7 +1483,7 @@ static int test_finalize(
|
||||
){
|
||||
sqlite3_stmt *pStmt;
|
||||
int rc;
|
||||
sqlite3 *db;
|
||||
sqlite3 *db = 0;
|
||||
|
||||
if( objc!=2 ){
|
||||
Tcl_AppendResult(interp, "wrong # args: should be \"",
|
||||
@@ -2489,7 +2624,60 @@ static int test_prepare(
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: sqlite3_prepare DB sql bytes tailvar
|
||||
** Usage: sqlite3_prepare_v2 DB sql bytes tailvar
|
||||
**
|
||||
** Compile up to <bytes> bytes of the supplied SQL string <sql> using
|
||||
** database handle <DB>. The parameter <tailval> is the name of a global
|
||||
** variable that is set to the unused portion of <sql> (if any). A
|
||||
** STMT handle is returned.
|
||||
*/
|
||||
static int test_prepare_v2(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
sqlite3 *db;
|
||||
const char *zSql;
|
||||
int bytes;
|
||||
const char *zTail = 0;
|
||||
sqlite3_stmt *pStmt = 0;
|
||||
char zBuf[50];
|
||||
int rc;
|
||||
|
||||
if( objc!=5 ){
|
||||
Tcl_AppendResult(interp, "wrong # args: should be \"",
|
||||
Tcl_GetString(objv[0]), " DB sql bytes tailvar", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
|
||||
zSql = Tcl_GetString(objv[2]);
|
||||
if( Tcl_GetIntFromObj(interp, objv[3], &bytes) ) return TCL_ERROR;
|
||||
|
||||
rc = sqlite3_prepare_v2(db, zSql, bytes, &pStmt, &zTail);
|
||||
if( sqlite3TestErrCode(interp, db, rc) ) return TCL_ERROR;
|
||||
if( zTail ){
|
||||
if( bytes>=0 ){
|
||||
bytes = bytes - (zTail-zSql);
|
||||
}
|
||||
Tcl_ObjSetVar2(interp, objv[4], 0, Tcl_NewStringObj(zTail, bytes), 0);
|
||||
}
|
||||
if( rc!=SQLITE_OK ){
|
||||
assert( pStmt==0 );
|
||||
sprintf(zBuf, "(%d) ", rc);
|
||||
Tcl_AppendResult(interp, zBuf, sqlite3_errmsg(db), 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
if( pStmt ){
|
||||
if( sqlite3TestMakePointerStr(interp, zBuf, pStmt) ) return TCL_ERROR;
|
||||
Tcl_AppendResult(interp, zBuf, 0);
|
||||
}
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: sqlite3_prepare16 DB sql bytes tailvar
|
||||
**
|
||||
** Compile up to <bytes> bytes of the supplied SQL string <sql> using
|
||||
** database handle <DB>. The parameter <tailval> is the name of a global
|
||||
@@ -2546,6 +2734,64 @@ static int test_prepare16(
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: sqlite3_prepare16_v2 DB sql bytes tailvar
|
||||
**
|
||||
** Compile up to <bytes> bytes of the supplied SQL string <sql> using
|
||||
** database handle <DB>. The parameter <tailval> is the name of a global
|
||||
** variable that is set to the unused portion of <sql> (if any). A
|
||||
** STMT handle is returned.
|
||||
*/
|
||||
static int test_prepare16_v2(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
#ifndef SQLITE_OMIT_UTF16
|
||||
sqlite3 *db;
|
||||
const void *zSql;
|
||||
const void *zTail = 0;
|
||||
Tcl_Obj *pTail = 0;
|
||||
sqlite3_stmt *pStmt = 0;
|
||||
char zBuf[50];
|
||||
int rc;
|
||||
int bytes; /* The integer specified as arg 3 */
|
||||
int objlen; /* The byte-array length of arg 2 */
|
||||
|
||||
if( objc!=5 ){
|
||||
Tcl_AppendResult(interp, "wrong # args: should be \"",
|
||||
Tcl_GetString(objv[0]), " DB sql bytes tailvar", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
|
||||
zSql = Tcl_GetByteArrayFromObj(objv[2], &objlen);
|
||||
if( Tcl_GetIntFromObj(interp, objv[3], &bytes) ) return TCL_ERROR;
|
||||
|
||||
rc = sqlite3_prepare16_v2(db, zSql, bytes, &pStmt, &zTail);
|
||||
if( sqlite3TestErrCode(interp, db, rc) ) return TCL_ERROR;
|
||||
if( rc ){
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
if( zTail ){
|
||||
objlen = objlen - ((u8 *)zTail-(u8 *)zSql);
|
||||
}else{
|
||||
objlen = 0;
|
||||
}
|
||||
pTail = Tcl_NewByteArrayObj((u8 *)zTail, objlen);
|
||||
Tcl_IncrRefCount(pTail);
|
||||
Tcl_ObjSetVar2(interp, objv[4], 0, pTail, 0);
|
||||
Tcl_DecrRefCount(pTail);
|
||||
|
||||
if( pStmt ){
|
||||
if( sqlite3TestMakePointerStr(interp, zBuf, pStmt) ) return TCL_ERROR;
|
||||
}
|
||||
Tcl_AppendResult(interp, zBuf, 0);
|
||||
#endif /* SQLITE_OMIT_UTF16 */
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: sqlite3_open filename ?options-list?
|
||||
*/
|
||||
@@ -3617,6 +3863,12 @@ static void set_options(Tcl_Interp *interp){
|
||||
Tcl_SetVar2(interp, "sqlite_options", "fts1", "0", TCL_GLOBAL_ONLY);
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_ENABLE_FTS2
|
||||
Tcl_SetVar2(interp, "sqlite_options", "fts2", "1", TCL_GLOBAL_ONLY);
|
||||
#else
|
||||
Tcl_SetVar2(interp, "sqlite_options", "fts2", "0", TCL_GLOBAL_ONLY);
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_OMIT_GLOBALRECOVER
|
||||
Tcl_SetVar2(interp, "sqlite_options", "globalrecover", "0", TCL_GLOBAL_ONLY);
|
||||
#else
|
||||
@@ -3828,6 +4080,8 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
|
||||
{ "sqlite3_mprintf_n_test", (Tcl_CmdProc*)test_mprintf_n },
|
||||
{ "sqlite3_last_insert_rowid", (Tcl_CmdProc*)test_last_rowid },
|
||||
{ "sqlite3_exec_printf", (Tcl_CmdProc*)test_exec_printf },
|
||||
{ "sqlite3_exec", (Tcl_CmdProc*)test_exec },
|
||||
{ "sqlite3_exec_nr", (Tcl_CmdProc*)test_exec_nr },
|
||||
{ "sqlite3_get_table_printf", (Tcl_CmdProc*)test_get_table_printf },
|
||||
{ "sqlite3_close", (Tcl_CmdProc*)sqlite_test_close },
|
||||
{ "sqlite3_create_function", (Tcl_CmdProc*)test_create_function },
|
||||
@@ -3849,6 +4103,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
|
||||
{ "sqlite3_get_autocommit", (Tcl_CmdProc*)get_autocommit },
|
||||
{ "sqlite3_stack_used", (Tcl_CmdProc*)test_stack_used },
|
||||
{ "sqlite3_busy_timeout", (Tcl_CmdProc*)test_busy_timeout },
|
||||
{ "printf", (Tcl_CmdProc*)test_printf },
|
||||
};
|
||||
static struct {
|
||||
char *zName;
|
||||
@@ -3877,6 +4132,8 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
|
||||
|
||||
{ "sqlite3_prepare", test_prepare ,0 },
|
||||
{ "sqlite3_prepare16", test_prepare16 ,0 },
|
||||
{ "sqlite3_prepare_v2", test_prepare_v2 ,0 },
|
||||
{ "sqlite3_prepare16_v2", test_prepare16_v2 ,0 },
|
||||
{ "sqlite3_finalize", test_finalize ,0 },
|
||||
{ "sqlite3_reset", test_reset ,0 },
|
||||
{ "sqlite3_expired", test_expired ,0 },
|
||||
|
||||
+79
-5
@@ -13,7 +13,7 @@
|
||||
** is not included in the SQLite library. It is used for automated
|
||||
** testing of the SQLite library.
|
||||
**
|
||||
** $Id: test3.c,v 1.67 2006/08/13 18:39:26 drh Exp $
|
||||
** $Id: test3.c,v 1.70 2007/02/10 19:22:36 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include "pager.h"
|
||||
@@ -567,6 +567,7 @@ static int btree_integrity_check(
|
||||
int nRoot;
|
||||
int *aRoot;
|
||||
int i;
|
||||
int nErr;
|
||||
char *zResult;
|
||||
|
||||
if( argc<3 ){
|
||||
@@ -576,16 +577,16 @@ static int btree_integrity_check(
|
||||
}
|
||||
pBt = sqlite3TextToPtr(argv[1]);
|
||||
nRoot = argc-2;
|
||||
aRoot = malloc( sizeof(int)*(argc-2) );
|
||||
aRoot = (int*)malloc( sizeof(int)*(argc-2) );
|
||||
for(i=0; i<argc-2; i++){
|
||||
if( Tcl_GetInt(interp, argv[i+2], &aRoot[i]) ) return TCL_ERROR;
|
||||
}
|
||||
#ifndef SQLITE_OMIT_INTEGRITY_CHECK
|
||||
zResult = sqlite3BtreeIntegrityCheck(pBt, aRoot, nRoot);
|
||||
zResult = sqlite3BtreeIntegrityCheck(pBt, aRoot, nRoot, 10000, &nErr);
|
||||
#else
|
||||
zResult = 0;
|
||||
#endif
|
||||
free(aRoot);
|
||||
free((void*)aRoot);
|
||||
if( zResult ){
|
||||
Tcl_AppendResult(interp, zResult, 0);
|
||||
sqliteFree(zResult);
|
||||
@@ -1051,6 +1052,7 @@ static int btree_data(
|
||||
rc = sqlite3BtreeData(pCur, 0, n, zBuf);
|
||||
if( rc ){
|
||||
Tcl_AppendResult(interp, errorName(rc), 0);
|
||||
free(zBuf);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
zBuf[n] = 0;
|
||||
@@ -1184,6 +1186,7 @@ static int btree_payload_size(
|
||||
** aResult[7] = Header size in bytes
|
||||
** aResult[8] = Local payload size
|
||||
** aResult[9] = Parent page number
|
||||
** aResult[10]= Page number of the first overflow page
|
||||
*/
|
||||
static int btree_cursor_info(
|
||||
void *NotUsed,
|
||||
@@ -1195,7 +1198,7 @@ static int btree_cursor_info(
|
||||
int rc;
|
||||
int i, j;
|
||||
int up;
|
||||
int aResult[10];
|
||||
int aResult[11];
|
||||
char zBuf[400];
|
||||
|
||||
if( argc!=2 && argc!=3 ){
|
||||
@@ -1223,6 +1226,76 @@ static int btree_cursor_info(
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Copied from btree.c:
|
||||
*/
|
||||
static u32 get4byte(unsigned char *p){
|
||||
return (p[0]<<24) | (p[1]<<16) | (p[2]<<8) | p[3];
|
||||
}
|
||||
|
||||
/*
|
||||
** btree_ovfl_info BTREE CURSOR
|
||||
**
|
||||
** Given a cursor, return the sequence of pages number that form the
|
||||
** overflow pages for the data of the entry that the cursor is point
|
||||
** to.
|
||||
*/
|
||||
static int btree_ovfl_info(
|
||||
void *NotUsed,
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int argc, /* Number of arguments */
|
||||
const char **argv /* Text of each argument */
|
||||
){
|
||||
Btree *pBt;
|
||||
BtCursor *pCur;
|
||||
Pager *pPager;
|
||||
int rc;
|
||||
int n;
|
||||
int dataSize;
|
||||
u32 pgno;
|
||||
void *pPage;
|
||||
int aResult[11];
|
||||
char zElem[100];
|
||||
Tcl_DString str;
|
||||
|
||||
if( argc!=3 ){
|
||||
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
|
||||
" BTREE CURSOR", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
pBt = sqlite3TextToPtr(argv[1]);
|
||||
pCur = sqlite3TextToPtr(argv[2]);
|
||||
if( (*(void**)pCur) != (void*)pBt ){
|
||||
Tcl_AppendResult(interp, "Cursor ", argv[2], " does not belong to btree ",
|
||||
argv[1], 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
pPager = sqlite3BtreePager(pBt);
|
||||
rc = sqlite3BtreeCursorInfo(pCur, aResult, 0);
|
||||
if( rc ){
|
||||
Tcl_AppendResult(interp, errorName(rc), 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
dataSize = sqlite3BtreeGetPageSize(pBt) - sqlite3BtreeGetReserve(pBt);
|
||||
Tcl_DStringInit(&str);
|
||||
n = aResult[6] - aResult[8];
|
||||
n = (n + dataSize - 1)/dataSize;
|
||||
pgno = (u32)aResult[10];
|
||||
while( pgno && n-- ){
|
||||
sprintf(zElem, "%d", pgno);
|
||||
Tcl_DStringAppendElement(&str, zElem);
|
||||
if( sqlite3pager_get(pPager, pgno, &pPage)!=SQLITE_OK ){
|
||||
Tcl_DStringFree(&str);
|
||||
Tcl_AppendResult(interp, "unable to get page ", zElem, 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
pgno = get4byte((unsigned char*)pPage);
|
||||
sqlite3pager_unref(pPage);
|
||||
}
|
||||
Tcl_DStringResult(interp, &str);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** The command is provided for the purpose of setting breakpoints.
|
||||
** in regression test scripts.
|
||||
@@ -1438,6 +1511,7 @@ int Sqlitetest3_Init(Tcl_Interp *interp){
|
||||
{ "btree_from_db", (Tcl_CmdProc*)btree_from_db },
|
||||
{ "btree_set_cache_size", (Tcl_CmdProc*)btree_set_cache_size },
|
||||
{ "btree_cursor_info", (Tcl_CmdProc*)btree_cursor_info },
|
||||
{ "btree_ovfl_info", (Tcl_CmdProc*)btree_ovfl_info },
|
||||
{ "btree_cursor_list", (Tcl_CmdProc*)btree_cursor_list },
|
||||
};
|
||||
int i;
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
** is not included in the SQLite library. It is used for automated
|
||||
** testing of the SQLite library.
|
||||
**
|
||||
** $Id: test8.c,v 1.43 2006/10/08 18:56:57 drh Exp $
|
||||
** $Id: test8.c,v 1.44 2007/01/03 23:37:29 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include "tcl.h"
|
||||
@@ -639,6 +639,7 @@ static int echoBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
|
||||
*/
|
||||
zQuery = sqlite3_mprintf("SELECT count(*) FROM %Q", pVtab->zTableName);
|
||||
rc = sqlite3_prepare(pVtab->db, zQuery, -1, &pStmt, 0);
|
||||
sqlite3_free(zQuery);
|
||||
if( rc!=SQLITE_OK ){
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -11,10 +11,10 @@
|
||||
*************************************************************************
|
||||
** Test extension for testing the sqlite3_auto_extension() function.
|
||||
**
|
||||
** $Id: test_autoext.c,v 1.1 2006/08/23 20:07:22 drh Exp $
|
||||
** $Id: test_autoext.c,v 1.2 2006/12/19 18:57:11 drh Exp $
|
||||
*/
|
||||
#ifndef SQLITE_OMIT_LOAD_EXTENSION
|
||||
#include "tcl.h"
|
||||
#ifndef SQLITE_OMIT_LOAD_EXTENSION
|
||||
#include "sqlite3ext.h"
|
||||
static SQLITE_EXTENSION_INIT1
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
** individual tokens and sends those tokens one-by-one over to the
|
||||
** parser for analysis.
|
||||
**
|
||||
** $Id: tokenize.c,v 1.124 2006/08/12 12:33:14 drh Exp $
|
||||
** $Id: tokenize.c,v 1.125 2007/01/26 19:31:01 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include "os.h"
|
||||
@@ -394,16 +394,16 @@ int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzErrMsg){
|
||||
int tokenType;
|
||||
int lastTokenParsed = -1;
|
||||
sqlite3 *db = pParse->db;
|
||||
extern void *sqlite3ParserAlloc(void*(*)(int));
|
||||
extern void *sqlite3ParserAlloc(void*(*)(size_t));
|
||||
extern void sqlite3ParserFree(void*, void(*)(void*));
|
||||
extern int sqlite3Parser(void*, int, Token, Parse*);
|
||||
extern void sqlite3Parser(void*, int, Token, Parse*);
|
||||
|
||||
if( db->activeVdbeCnt==0 ){
|
||||
db->u1.isInterrupted = 0;
|
||||
}
|
||||
pParse->rc = SQLITE_OK;
|
||||
i = 0;
|
||||
pEngine = sqlite3ParserAlloc((void*(*)(int))sqlite3MallocX);
|
||||
pEngine = sqlite3ParserAlloc((void*(*)(size_t))sqlite3MallocX);
|
||||
if( pEngine==0 ){
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
|
||||
@@ -668,12 +668,12 @@ static int codeTriggerProgram(
|
||||
pParse->trigStack->orconf = orconf;
|
||||
switch( pTriggerStep->op ){
|
||||
case TK_SELECT: {
|
||||
Select * ss = sqlite3SelectDup(pTriggerStep->pSelect);
|
||||
assert(ss);
|
||||
assert(ss->pSrc);
|
||||
sqlite3SelectResolve(pParse, ss, 0);
|
||||
sqlite3Select(pParse, ss, SRT_Discard, 0, 0, 0, 0, 0);
|
||||
sqlite3SelectDelete(ss);
|
||||
Select *ss = sqlite3SelectDup(pTriggerStep->pSelect);
|
||||
if( ss ){
|
||||
sqlite3SelectResolve(pParse, ss, 0);
|
||||
sqlite3Select(pParse, ss, SRT_Discard, 0, 0, 0, 0, 0);
|
||||
sqlite3SelectDelete(ss);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TK_UPDATE: {
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
** This file contains C code routines that are called by the parser
|
||||
** to handle UPDATE statements.
|
||||
**
|
||||
** $Id: update.c,v 1.133 2006/06/27 13:20:21 drh Exp $
|
||||
** $Id: update.c,v 1.134 2007/02/07 01:06:53 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
@@ -103,6 +103,7 @@ void sqlite3Update(
|
||||
AuthContext sContext; /* The authorization context */
|
||||
NameContext sNC; /* The name-context to resolve expressions in */
|
||||
int iDb; /* Database containing the table being updated */
|
||||
int memCnt = 0; /* Memory cell used for counting rows changed */
|
||||
|
||||
#ifndef SQLITE_OMIT_TRIGGER
|
||||
int isView; /* Trying to update a view */
|
||||
@@ -311,7 +312,8 @@ void sqlite3Update(
|
||||
/* Initialize the count of updated rows
|
||||
*/
|
||||
if( db->flags & SQLITE_CountRows && !pParse->trigStack ){
|
||||
sqlite3VdbeAddOp(v, OP_Integer, 0, 0);
|
||||
memCnt = pParse->nMem++;
|
||||
sqlite3VdbeAddOp(v, OP_MemInt, 0, memCnt);
|
||||
}
|
||||
|
||||
if( triggers_exist ){
|
||||
@@ -469,7 +471,7 @@ void sqlite3Update(
|
||||
/* Increment the row counter
|
||||
*/
|
||||
if( db->flags & SQLITE_CountRows && !pParse->trigStack){
|
||||
sqlite3VdbeAddOp(v, OP_AddImm, 1, 0);
|
||||
sqlite3VdbeAddOp(v, OP_MemIncr, 1, memCnt);
|
||||
}
|
||||
|
||||
/* If there are triggers, close all the cursors after each iteration
|
||||
@@ -514,6 +516,7 @@ void sqlite3Update(
|
||||
** invoke the callback function.
|
||||
*/
|
||||
if( db->flags & SQLITE_CountRows && !pParse->trigStack && pParse->nested==0 ){
|
||||
sqlite3VdbeAddOp(v, OP_MemLoad, memCnt, 0);
|
||||
sqlite3VdbeAddOp(v, OP_Callback, 1, 0);
|
||||
sqlite3VdbeSetNumCols(v, 1);
|
||||
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "rows updated", P3_STATIC);
|
||||
|
||||
+36
-15
@@ -12,7 +12,7 @@
|
||||
** This file contains routines used to translate between UTF-8,
|
||||
** UTF-16, UTF-16BE, and UTF-16LE.
|
||||
**
|
||||
** $Id: utf.c,v 1.42 2006/10/05 11:43:53 drh Exp $
|
||||
** $Id: utf.c,v 1.43 2006/10/19 01:58:44 drh Exp $
|
||||
**
|
||||
** Notes on UTF-8:
|
||||
**
|
||||
@@ -64,7 +64,7 @@
|
||||
|
||||
/*
|
||||
** This table maps from the first byte of a UTF-8 character to the number
|
||||
** of trailing bytes expected. A value '255' indicates that the table key
|
||||
** of trailing bytes expected. A value '4' indicates that the table key
|
||||
** is not a legal first byte for a UTF-8 character.
|
||||
*/
|
||||
static const u8 xtra_utf8_bytes[256] = {
|
||||
@@ -79,10 +79,10 @@ static const u8 xtra_utf8_bytes[256] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
|
||||
/* 10wwwwww */
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
||||
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
||||
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
||||
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
||||
|
||||
/* 110yyyyy */
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
@@ -92,7 +92,7 @@ static const u8 xtra_utf8_bytes[256] = {
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
|
||||
/* 11110yyy */
|
||||
3, 3, 3, 3, 3, 3, 3, 3, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4,
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -101,11 +101,24 @@ static const u8 xtra_utf8_bytes[256] = {
|
||||
** read by a naive implementation of a UTF-8 character reader. The code
|
||||
** in the READ_UTF8 macro explains things best.
|
||||
*/
|
||||
static const int xtra_utf8_bits[4] = {
|
||||
0,
|
||||
12416, /* (0xC0 << 6) + (0x80) */
|
||||
925824, /* (0xE0 << 12) + (0x80 << 6) + (0x80) */
|
||||
63447168 /* (0xF0 << 18) + (0x80 << 12) + (0x80 << 6) + 0x80 */
|
||||
static const int xtra_utf8_bits[] = {
|
||||
0,
|
||||
12416, /* (0xC0 << 6) + (0x80) */
|
||||
925824, /* (0xE0 << 12) + (0x80 << 6) + (0x80) */
|
||||
63447168 /* (0xF0 << 18) + (0x80 << 12) + (0x80 << 6) + 0x80 */
|
||||
};
|
||||
|
||||
/*
|
||||
** If a UTF-8 character contains N bytes extra bytes (N bytes follow
|
||||
** the initial byte so that the total character length is N+1) then
|
||||
** masking the character with utf8_mask[N] must produce a non-zero
|
||||
** result. Otherwise, we have an (illegal) overlong encoding.
|
||||
*/
|
||||
static const int utf_mask[] = {
|
||||
0x00000000,
|
||||
0xffffff80,
|
||||
0xfffff800,
|
||||
0xffff0000,
|
||||
};
|
||||
|
||||
#define READ_UTF8(zIn, c) { \
|
||||
@@ -113,11 +126,14 @@ static const int xtra_utf8_bits[4] = {
|
||||
c = *(zIn)++; \
|
||||
xtra = xtra_utf8_bytes[c]; \
|
||||
switch( xtra ){ \
|
||||
case 255: c = (int)0xFFFD; break; \
|
||||
case 4: c = (int)0xFFFD; break; \
|
||||
case 3: c = (c<<6) + *(zIn)++; \
|
||||
case 2: c = (c<<6) + *(zIn)++; \
|
||||
case 1: c = (c<<6) + *(zIn)++; \
|
||||
c -= xtra_utf8_bits[xtra]; \
|
||||
if( (utf_mask[xtra]&c)==0 \
|
||||
|| (c&0xFFFFF800)==0xD800 \
|
||||
|| (c&0xFFFFFFFE)==0xFFFE ){ c = 0xFFFD; } \
|
||||
} \
|
||||
}
|
||||
int sqlite3ReadUtf8(const unsigned char *z){
|
||||
@@ -181,6 +197,7 @@ int sqlite3ReadUtf8(const unsigned char *z){
|
||||
int c2 = (*zIn++); \
|
||||
c2 += ((*zIn++)<<8); \
|
||||
c = (c2&0x03FF) + ((c&0x003F)<<10) + (((c&0x03C0)+0x0040)<<10); \
|
||||
if( (c & 0xFFFF0000)==0 ) c = 0xFFFD; \
|
||||
} \
|
||||
}
|
||||
|
||||
@@ -191,6 +208,7 @@ int sqlite3ReadUtf8(const unsigned char *z){
|
||||
int c2 = ((*zIn++)<<8); \
|
||||
c2 += (*zIn++); \
|
||||
c = (c2&0x03FF) + ((c&0x003F)<<10) + (((c&0x03C0)+0x0040)<<10); \
|
||||
if( (c & 0xFFFF0000)==0 ) c = 0xFFFD; \
|
||||
} \
|
||||
}
|
||||
|
||||
@@ -556,7 +574,7 @@ void sqlite3utf16Substr(
|
||||
** characters in each encoding are inverses of each other.
|
||||
*/
|
||||
void sqlite3utfSelfTest(){
|
||||
unsigned int i;
|
||||
unsigned int i, t;
|
||||
unsigned char zBuf[20];
|
||||
unsigned char *z;
|
||||
int n;
|
||||
@@ -568,7 +586,10 @@ void sqlite3utfSelfTest(){
|
||||
n = z-zBuf;
|
||||
z = zBuf;
|
||||
READ_UTF8(z, c);
|
||||
assert( c==i );
|
||||
t = i;
|
||||
if( i>=0xD800 && i<=0xDFFF ) t = 0xFFFD;
|
||||
if( (i&0xFFFFFFFE)==0xFFFE ) t = 0xFFFD;
|
||||
assert( c==t );
|
||||
assert( (z-zBuf)==n );
|
||||
}
|
||||
for(i=0; i<0x00110000; i++){
|
||||
|
||||
+10
-59
@@ -14,27 +14,13 @@
|
||||
** Most of the code in this file may be omitted by defining the
|
||||
** SQLITE_OMIT_VACUUM macro.
|
||||
**
|
||||
** $Id: vacuum.c,v 1.63 2006/09/21 11:02:18 drh Exp $
|
||||
** $Id: vacuum.c,v 1.66 2007/01/03 23:37:29 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include "vdbeInt.h"
|
||||
#include "os.h"
|
||||
|
||||
#ifndef SQLITE_OMIT_VACUUM
|
||||
/*
|
||||
** Generate a random name of 20 character in length.
|
||||
*/
|
||||
static void randomName(unsigned char *zBuf){
|
||||
static const unsigned char zChars[] =
|
||||
"abcdefghijklmnopqrstuvwxyz"
|
||||
"0123456789";
|
||||
int i;
|
||||
sqlite3Randomness(20, zBuf);
|
||||
for(i=0; i<20; i++){
|
||||
zBuf[i] = zChars[ zBuf[i]%(sizeof(zChars)-1) ];
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Execute zSql on database db. Return an error code.
|
||||
*/
|
||||
@@ -92,59 +78,25 @@ void sqlite3Vacuum(Parse *pParse){
|
||||
*/
|
||||
int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){
|
||||
int rc = SQLITE_OK; /* Return code from service routines */
|
||||
const char *zFilename; /* full pathname of the database file */
|
||||
int nFilename; /* number of characters in zFilename[] */
|
||||
char *zTemp = 0; /* a temporary file in same directory as zFilename */
|
||||
Btree *pMain; /* The database being vacuumed */
|
||||
Btree *pTemp;
|
||||
char *zSql = 0;
|
||||
int saved_flags; /* Saved value of the db->flags */
|
||||
Db *pDb = 0; /* Database to detach at end of vacuum */
|
||||
Btree *pTemp; /* The temporary database we vacuum into */
|
||||
char *zSql = 0; /* SQL statements */
|
||||
int saved_flags; /* Saved value of the db->flags */
|
||||
Db *pDb = 0; /* Database to detach at end of vacuum */
|
||||
char zTemp[SQLITE_TEMPNAME_SIZE+20]; /* Name of the TEMP file */
|
||||
|
||||
/* Save the current value of the write-schema flag before setting it. */
|
||||
saved_flags = db->flags;
|
||||
db->flags |= SQLITE_WriteSchema | SQLITE_IgnoreChecks;
|
||||
|
||||
sqlite3OsTempFileName(zTemp);
|
||||
if( !db->autoCommit ){
|
||||
sqlite3SetString(pzErrMsg, "cannot VACUUM from within a transaction",
|
||||
(char*)0);
|
||||
rc = SQLITE_ERROR;
|
||||
goto end_of_vacuum;
|
||||
}
|
||||
|
||||
/* Get the full pathname of the database file and create a
|
||||
** temporary filename in the same directory as the original file.
|
||||
*/
|
||||
pMain = db->aDb[0].pBt;
|
||||
zFilename = sqlite3BtreeGetFilename(pMain);
|
||||
assert( zFilename );
|
||||
if( zFilename[0]=='\0' ){
|
||||
/* The in-memory database. Do nothing. Return directly to avoid causing
|
||||
** an error trying to DETACH the vacuum_db (which never got attached)
|
||||
** in the exit-handler.
|
||||
*/
|
||||
return SQLITE_OK;
|
||||
}
|
||||
nFilename = strlen(zFilename);
|
||||
zTemp = sqliteMalloc( nFilename+100 );
|
||||
if( zTemp==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
goto end_of_vacuum;
|
||||
}
|
||||
strcpy(zTemp, zFilename);
|
||||
|
||||
/* The randomName() procedure in the following loop uses an excellent
|
||||
** source of randomness to generate a name from a space of 1.3e+31
|
||||
** possibilities. So unless the directory already contains on the order
|
||||
** of 1.3e+31 files, the probability that the following loop will
|
||||
** run more than once or twice is vanishingly small. We are certain
|
||||
** enough that this loop will always terminate (and terminate quickly)
|
||||
** that we don't even bother to set a maximum loop count.
|
||||
*/
|
||||
do {
|
||||
zTemp[nFilename] = '-';
|
||||
randomName((unsigned char*)&zTemp[nFilename+1]);
|
||||
} while( sqlite3OsFileExists(zTemp) );
|
||||
|
||||
/* Attach the temporary database as 'vacuum_db'. The synchronous pragma
|
||||
** can be set to 'off' for this file, as it is not recovered if a crash
|
||||
@@ -307,10 +259,9 @@ end_of_vacuum:
|
||||
pDb->pSchema = 0;
|
||||
}
|
||||
|
||||
if( zTemp ){
|
||||
sqlite3OsDelete(zTemp);
|
||||
sqliteFree(zTemp);
|
||||
}
|
||||
sqlite3OsDelete(zTemp);
|
||||
strcat(zTemp, "-journal");
|
||||
sqlite3OsDelete(zTemp);
|
||||
sqliteFree( zSql );
|
||||
sqlite3ResetInternalSchema(db, 0);
|
||||
|
||||
|
||||
+74
-68
@@ -43,7 +43,7 @@
|
||||
** in this file for details. If in doubt, do not deviate from existing
|
||||
** commenting and indentation practices when changing or adding code.
|
||||
**
|
||||
** $Id: vdbe.c,v 1.577 2006/09/23 20:36:02 drh Exp $
|
||||
** $Id: vdbe.c,v 1.588 2007/01/27 13:37:22 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include "os.h"
|
||||
@@ -454,6 +454,21 @@ int sqlite3VdbeExec(
|
||||
p->resOnStack = 0;
|
||||
db->busyHandler.nBusy = 0;
|
||||
CHECK_FOR_INTERRUPT;
|
||||
#ifdef SQLITE_DEBUG
|
||||
if( (p->db->flags & SQLITE_VdbeListing)!=0
|
||||
|| sqlite3OsFileExists("vdbe_explain")
|
||||
){
|
||||
int i;
|
||||
printf("VDBE Program Listing:\n");
|
||||
sqlite3VdbePrintSql(p);
|
||||
for(i=0; i<p->nOp; i++){
|
||||
sqlite3VdbePrintOp(stdout, i, &p->aOp[i]);
|
||||
}
|
||||
}
|
||||
if( sqlite3OsFileExists("vdbe_trace") ){
|
||||
p->trace = stdout;
|
||||
}
|
||||
#endif
|
||||
for(pc=p->pc; rc==SQLITE_OK; pc++){
|
||||
assert( pc>=0 && pc<p->nOp );
|
||||
assert( pTos<=&p->aStack[pc] );
|
||||
@@ -1812,32 +1827,31 @@ case OP_IfNot: { /* no-push */
|
||||
|
||||
/* Opcode: IsNull P1 P2 *
|
||||
**
|
||||
** If any of the top abs(P1) values on the stack are NULL, then jump
|
||||
** to P2. Pop the stack P1 times if P1>0. If P1<0 leave the stack
|
||||
** unchanged.
|
||||
** Check the top of the stack and jump to P2 if the top of the stack
|
||||
** is NULL. If P1 is positive, then pop P1 elements from the stack
|
||||
** regardless of whether or not the jump is taken. If P1 is negative,
|
||||
** pop -P1 elements from the stack only if the jump is taken and leave
|
||||
** the stack unchanged if the jump is not taken.
|
||||
*/
|
||||
case OP_IsNull: { /* same as TK_ISNULL, no-push */
|
||||
int i, cnt;
|
||||
Mem *pTerm;
|
||||
cnt = pOp->p1;
|
||||
if( cnt<0 ) cnt = -cnt;
|
||||
pTerm = &pTos[1-cnt];
|
||||
assert( pTerm>=p->aStack );
|
||||
for(i=0; i<cnt; i++, pTerm++){
|
||||
if( pTerm->flags & MEM_Null ){
|
||||
pc = pOp->p2-1;
|
||||
break;
|
||||
if( pTos->flags & MEM_Null ){
|
||||
pc = pOp->p2-1;
|
||||
if( pOp->p1<0 ){
|
||||
popStack(&pTos, -pOp->p1);
|
||||
}
|
||||
}
|
||||
if( pOp->p1>0 ) popStack(&pTos, cnt);
|
||||
if( pOp->p1>0 ){
|
||||
popStack(&pTos, pOp->p1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/* Opcode: NotNull P1 P2 *
|
||||
**
|
||||
** Jump to P2 if the top P1 values on the stack are all not NULL. Pop the
|
||||
** stack if P1 times if P1 is greater than zero. If P1 is less than
|
||||
** zero then leave the stack unchanged.
|
||||
** Jump to P2 if the top abs(P1) values on the stack are all not NULL.
|
||||
** Regardless of whether or not the jump is taken, pop the stack
|
||||
** P1 times if P1 is greater than zero. But if P1 is negative,
|
||||
** leave the stack unchanged.
|
||||
*/
|
||||
case OP_NotNull: { /* same as TK_NOTNULL, no-push */
|
||||
int i, cnt;
|
||||
@@ -2010,7 +2024,9 @@ case OP_Column: {
|
||||
pC->aRow = 0;
|
||||
}
|
||||
}
|
||||
assert( zRec!=0 || avail>=payloadSize || avail>=9 );
|
||||
/* The following assert is true in all cases accept when
|
||||
** the database file has been corrupted externally.
|
||||
** assert( zRec!=0 || avail>=payloadSize || avail>=9 ); */
|
||||
szHdrSz = GetVarint((u8*)zData, offset);
|
||||
|
||||
/* The KeyFetch() or DataFetch() above are fast and will get the entire
|
||||
@@ -2501,6 +2517,8 @@ case OP_VerifyCookie: { /* no-push */
|
||||
}
|
||||
if( rc==SQLITE_OK && iMeta!=pOp->p2 ){
|
||||
sqlite3SetString(&p->zErrMsg, "database schema has changed", (char*)0);
|
||||
sqlite3ResetInternalSchema(db, pOp->p1);
|
||||
sqlite3ExpirePreparedStatements(db);
|
||||
rc = SQLITE_SCHEMA;
|
||||
}
|
||||
break;
|
||||
@@ -2907,7 +2925,7 @@ case OP_MoveGt: { /* no-push */
|
||||
**
|
||||
** The top of the stack holds a blob constructed by MakeRecord. P1 is
|
||||
** an index. If no entry exists in P1 that matches the blob then jump
|
||||
** to P1. If an entry does existing, fall through. The cursor is left
|
||||
** to P2. If an entry does existing, fall through. The cursor is left
|
||||
** pointing to the entry that matches. The blob is popped from the stack.
|
||||
**
|
||||
** The difference between this operation and Distinct is that
|
||||
@@ -3081,6 +3099,9 @@ case OP_NotExists: { /* no-push */
|
||||
pC->rowidIsValid = res==0;
|
||||
pC->nullRow = 0;
|
||||
pC->cacheStatus = CACHE_STALE;
|
||||
/* res might be uninitialized if rc!=SQLITE_OK. But if rc!=SQLITE_OK
|
||||
** processing is about to abort so we really do not care whether or not
|
||||
** the following jump is taken. */
|
||||
if( res!=0 ){
|
||||
pc = pOp->p2 - 1;
|
||||
pC->rowidIsValid = 0;
|
||||
@@ -3852,38 +3873,6 @@ case OP_IdxGE: { /* no-push */
|
||||
break;
|
||||
}
|
||||
|
||||
/* Opcode: IdxIsNull P1 P2 *
|
||||
**
|
||||
** The top of the stack contains an index entry such as might be generated
|
||||
** by the MakeIdxRec opcode. This routine looks at the first P1 fields of
|
||||
** that key. If any of the first P1 fields are NULL, then a jump is made
|
||||
** to address P2. Otherwise we fall straight through.
|
||||
**
|
||||
** The index entry is always popped from the stack.
|
||||
*/
|
||||
case OP_IdxIsNull: { /* no-push */
|
||||
int i = pOp->p1;
|
||||
int k, n;
|
||||
const char *z;
|
||||
u32 serial_type;
|
||||
|
||||
assert( pTos>=p->aStack );
|
||||
assert( pTos->flags & MEM_Blob );
|
||||
z = pTos->z;
|
||||
n = pTos->n;
|
||||
k = sqlite3GetVarint32((u8*)z, &serial_type);
|
||||
for(; k<n && i>0; i--){
|
||||
k += sqlite3GetVarint32((u8*)&z[k], &serial_type);
|
||||
if( serial_type==0 ){ /* Serial type 0 is a NULL */
|
||||
pc = pOp->p2-1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
Release(pTos);
|
||||
pTos--;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Opcode: Destroy P1 P2 *
|
||||
**
|
||||
** Delete an entire database table or index whose root page in the database
|
||||
@@ -3906,9 +3895,9 @@ case OP_IdxIsNull: { /* no-push */
|
||||
*/
|
||||
case OP_Destroy: {
|
||||
int iMoved;
|
||||
Vdbe *pVdbe;
|
||||
int iCnt;
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
Vdbe *pVdbe;
|
||||
iCnt = 0;
|
||||
for(pVdbe=db->pVdbe; pVdbe; pVdbe=pVdbe->pNext){
|
||||
if( pVdbe->magic==VDBE_MAGIC_RUN && pVdbe->inVtabMethod<2 && pVdbe->pc>=0 ){
|
||||
@@ -4032,10 +4021,14 @@ case OP_CreateTable: {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Opcode: ParseSchema P1 * P3
|
||||
/* Opcode: ParseSchema P1 P2 P3
|
||||
**
|
||||
** Read and parse all entries from the SQLITE_MASTER table of database P1
|
||||
** that match the WHERE clause P3.
|
||||
** that match the WHERE clause P3. P2 is the "force" flag. Always do
|
||||
** the parsing if P2 is true. If P2 is false, then this routine is a
|
||||
** no-op if the schema is not currently loaded. In other words, if P2
|
||||
** is false, the SQLITE_MASTER table is only parsed if the rest of the
|
||||
** schema is already loaded into the symbol table.
|
||||
**
|
||||
** This opcode invokes the parser to create a new virtual machine,
|
||||
** then runs the new virtual machine. It is thus a reentrant opcode.
|
||||
@@ -4047,7 +4040,9 @@ case OP_ParseSchema: { /* no-push */
|
||||
InitData initData;
|
||||
|
||||
assert( iDb>=0 && iDb<db->nDb );
|
||||
if( !DbHasProperty(db, iDb, DB_SchemaLoaded) ) break;
|
||||
if( !pOp->p2 && !DbHasProperty(db, iDb, DB_SchemaLoaded) ){
|
||||
break;
|
||||
}
|
||||
zMaster = SCHEMA_TABLE(iDb);
|
||||
initData.db = db;
|
||||
initData.iDb = pOp->p1;
|
||||
@@ -4125,11 +4120,16 @@ case OP_DropTrigger: { /* no-push */
|
||||
|
||||
|
||||
#ifndef SQLITE_OMIT_INTEGRITY_CHECK
|
||||
/* Opcode: IntegrityCk * P2 *
|
||||
/* Opcode: IntegrityCk P1 P2 *
|
||||
**
|
||||
** Do an analysis of the currently open database. Push onto the
|
||||
** stack the text of an error message describing any problems.
|
||||
** If there are no errors, push a "ok" onto the stack.
|
||||
** If no problems are found, push a NULL onto the stack.
|
||||
**
|
||||
** P1 is the address of a memory cell that contains the maximum
|
||||
** number of allowed errors. At most mem[P1] errors will be reported.
|
||||
** In other words, the analysis stops as soon as mem[P1] errors are
|
||||
** seen. Mem[P1] is updated with the number of errors remaining.
|
||||
**
|
||||
** The root page numbers of all tables in the database are integer
|
||||
** values on the stack. This opcode pulls as many integers as it
|
||||
@@ -4138,13 +4138,15 @@ case OP_DropTrigger: { /* no-push */
|
||||
** If P2 is not zero, the check is done on the auxiliary database
|
||||
** file, not the main database file.
|
||||
**
|
||||
** This opcode is used for testing purposes only.
|
||||
** This opcode is used to implement the integrity_check pragma.
|
||||
*/
|
||||
case OP_IntegrityCk: {
|
||||
int nRoot;
|
||||
int *aRoot;
|
||||
int j;
|
||||
int nErr;
|
||||
char *z;
|
||||
Mem *pnErr;
|
||||
|
||||
for(nRoot=0; &pTos[-nRoot]>=p->aStack; nRoot++){
|
||||
if( (pTos[-nRoot].flags & MEM_Int)==0 ) break;
|
||||
@@ -4152,6 +4154,10 @@ case OP_IntegrityCk: {
|
||||
assert( nRoot>0 );
|
||||
aRoot = sqliteMallocRaw( sizeof(int*)*(nRoot+1) );
|
||||
if( aRoot==0 ) goto no_mem;
|
||||
j = pOp->p1;
|
||||
assert( j>=0 && j<p->nMem );
|
||||
pnErr = &p->aMem[j];
|
||||
assert( (pnErr->flags & MEM_Int)!=0 );
|
||||
for(j=0; j<nRoot; j++){
|
||||
Mem *pMem = &pTos[-j];
|
||||
aRoot[j] = pMem->i;
|
||||
@@ -4159,12 +4165,12 @@ case OP_IntegrityCk: {
|
||||
aRoot[j] = 0;
|
||||
popStack(&pTos, nRoot);
|
||||
pTos++;
|
||||
z = sqlite3BtreeIntegrityCheck(db->aDb[pOp->p2].pBt, aRoot, nRoot);
|
||||
if( z==0 || z[0]==0 ){
|
||||
if( z ) sqliteFree(z);
|
||||
pTos->z = "ok";
|
||||
pTos->n = 2;
|
||||
pTos->flags = MEM_Str | MEM_Static | MEM_Term;
|
||||
z = sqlite3BtreeIntegrityCheck(db->aDb[pOp->p2].pBt, aRoot, nRoot,
|
||||
pnErr->i, &nErr);
|
||||
pnErr->i -= nErr;
|
||||
if( nErr==0 ){
|
||||
assert( z==0 );
|
||||
pTos->flags = MEM_Null;
|
||||
}else{
|
||||
pTos->z = z;
|
||||
pTos->n = strlen(z);
|
||||
@@ -4675,9 +4681,9 @@ case OP_VFilter: { /* no-push */
|
||||
assert( (pTos[0].flags&MEM_Int)!=0 && pTos[-1].flags==MEM_Int );
|
||||
nArg = pTos[-1].i;
|
||||
|
||||
/* Invoke the xFilter method if one is defined. */
|
||||
if( pModule->xFilter ){
|
||||
int res;
|
||||
/* Invoke the xFilter method */
|
||||
{
|
||||
int res = 0;
|
||||
int i;
|
||||
Mem **apArg = p->apArg;
|
||||
for(i = 0; i<nArg; i++){
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
** or VDBE. The VDBE implements an abstract machine that runs a
|
||||
** simple program to access and modify the underlying database.
|
||||
**
|
||||
** $Id: vdbe.h,v 1.105 2006/06/13 23:51:35 drh Exp $
|
||||
** $Id: vdbe.h,v 1.108 2007/01/09 14:01:14 drh Exp $
|
||||
*/
|
||||
#ifndef _SQLITE_VDBE_H_
|
||||
#define _SQLITE_VDBE_H_
|
||||
@@ -129,12 +129,16 @@ int sqlite3VdbeFinalize(Vdbe*);
|
||||
void sqlite3VdbeResolveLabel(Vdbe*, int);
|
||||
int sqlite3VdbeCurrentAddr(Vdbe*);
|
||||
void sqlite3VdbeTrace(Vdbe*,FILE*);
|
||||
void sqlite3VdbeResetStepResult(Vdbe*);
|
||||
int sqlite3VdbeReset(Vdbe*);
|
||||
int sqliteVdbeSetVariables(Vdbe*,int,const char**);
|
||||
void sqlite3VdbeSetNumCols(Vdbe*,int);
|
||||
int sqlite3VdbeSetColName(Vdbe*, int, int, const char *, int);
|
||||
void sqlite3VdbeCountChanges(Vdbe*);
|
||||
sqlite3 *sqlite3VdbeDb(Vdbe*);
|
||||
void sqlite3VdbeSetSql(Vdbe*, const char *z, int n);
|
||||
const char *sqlite3VdbeGetSql(Vdbe*);
|
||||
void sqlite3VdbeSwap(Vdbe*,Vdbe*);
|
||||
|
||||
#ifndef NDEBUG
|
||||
void sqlite3VdbeComment(Vdbe*, const char*, ...);
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
** 6000 lines long) it was split up into several smaller files and
|
||||
** this header information was factored out.
|
||||
*/
|
||||
#ifndef _VDBEINT_H_
|
||||
#define _VDBEINT_H_
|
||||
|
||||
/*
|
||||
** intToKey() and keyToInt() used to transform the rowid. But with
|
||||
@@ -328,6 +330,8 @@ struct Vdbe {
|
||||
u8 inVtabMethod; /* See comments above */
|
||||
int nChange; /* Number of db changes made since last reset */
|
||||
i64 startTime; /* Time when query started - used for profiling */
|
||||
int nSql; /* Number of bytes in zSql */
|
||||
char *zSql; /* Text of the SQL statement that generated this */
|
||||
#ifdef SQLITE_SSE
|
||||
int fetchId; /* Statement number used by sqlite3_fetch_statement */
|
||||
int lru; /* Counter used for LRU cache replacement */
|
||||
@@ -401,3 +405,5 @@ void sqlite3VdbeFifoInit(Fifo*);
|
||||
int sqlite3VdbeFifoPush(Fifo*, i64);
|
||||
int sqlite3VdbeFifoPop(Fifo*, i64*);
|
||||
void sqlite3VdbeFifoClear(Fifo*);
|
||||
|
||||
#endif /* !defined(_VDBEINT_H_) */
|
||||
|
||||
@@ -153,9 +153,13 @@ void sqlite3_result_value(sqlite3_context *pCtx, sqlite3_value *pValue){
|
||||
/*
|
||||
** Execute the statement pStmt, either until a row of data is ready, the
|
||||
** statement is completely executed or an error occurs.
|
||||
**
|
||||
** This routine implements the bulk of the logic behind the sqlite_step()
|
||||
** API. The only thing omitted is the automatic recompile if a
|
||||
** schema change has occurred. That detail is handled by the
|
||||
** outer sqlite3_step() wrapper procedure.
|
||||
*/
|
||||
int sqlite3_step(sqlite3_stmt *pStmt){
|
||||
Vdbe *p = (Vdbe*)pStmt;
|
||||
static int sqlite3Step(Vdbe *p){
|
||||
sqlite3 *db;
|
||||
int rc;
|
||||
|
||||
@@ -172,7 +176,8 @@ int sqlite3_step(sqlite3_stmt *pStmt){
|
||||
if( p->rc==SQLITE_OK ){
|
||||
p->rc = SQLITE_SCHEMA;
|
||||
}
|
||||
return SQLITE_ERROR;
|
||||
rc = SQLITE_ERROR;
|
||||
goto end_of_step;
|
||||
}
|
||||
db = p->db;
|
||||
if( sqlite3SafetyOn(db) ){
|
||||
@@ -254,9 +259,42 @@ int sqlite3_step(sqlite3_stmt *pStmt){
|
||||
|
||||
sqlite3Error(p->db, rc, 0);
|
||||
p->rc = sqlite3ApiExit(p->db, p->rc);
|
||||
end_of_step:
|
||||
assert( (rc&0xff)==rc );
|
||||
if( p->zSql && (rc&0xff)<SQLITE_ROW ){
|
||||
/* This behavior occurs if sqlite3_prepare_v2() was used to build
|
||||
** the prepared statement. Return error codes directly */
|
||||
return p->rc;
|
||||
}else{
|
||||
/* This is for legacy sqlite3_prepare() builds and when the code
|
||||
** is SQLITE_ROW or SQLITE_DONE */
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** This is the top-level implementation of sqlite3_step(). Call
|
||||
** sqlite3Step() to do most of the work. If a schema error occurs,
|
||||
** call sqlite3Reprepare() and try again.
|
||||
*/
|
||||
#ifdef SQLITE_OMIT_PARSER
|
||||
int sqlite3_step(sqlite3_stmt *pStmt){
|
||||
return sqlite3Step((Vdbe*)pStmt);
|
||||
}
|
||||
#else
|
||||
int sqlite3_step(sqlite3_stmt *pStmt){
|
||||
int cnt = 0;
|
||||
int rc;
|
||||
Vdbe *v = (Vdbe*)pStmt;
|
||||
while( (rc = sqlite3Step(v))==SQLITE_SCHEMA
|
||||
&& cnt++ < 5
|
||||
&& sqlite3Reprepare(v) ){
|
||||
sqlite3_reset(pStmt);
|
||||
v->expired = 0;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Extract the user data from a sqlite3_context structure and return a
|
||||
|
||||
+52
-19
@@ -48,6 +48,46 @@ Vdbe *sqlite3VdbeCreate(sqlite3 *db){
|
||||
return p;
|
||||
}
|
||||
|
||||
/*
|
||||
** Remember the SQL string for a prepared statement.
|
||||
*/
|
||||
void sqlite3VdbeSetSql(Vdbe *p, const char *z, int n){
|
||||
if( p==0 ) return;
|
||||
assert( p->zSql==0 );
|
||||
p->zSql = sqlite3StrNDup(z, n);
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the SQL associated with a prepared statement
|
||||
*/
|
||||
const char *sqlite3VdbeGetSql(Vdbe *p){
|
||||
return p->zSql;
|
||||
}
|
||||
|
||||
/*
|
||||
** Swap all content between two VDBE structures.
|
||||
*/
|
||||
void sqlite3VdbeSwap(Vdbe *pA, Vdbe *pB){
|
||||
Vdbe tmp, *pTmp;
|
||||
char *zTmp;
|
||||
int nTmp;
|
||||
tmp = *pA;
|
||||
*pA = *pB;
|
||||
*pB = tmp;
|
||||
pTmp = pA->pNext;
|
||||
pA->pNext = pB->pNext;
|
||||
pB->pNext = pTmp;
|
||||
pTmp = pA->pPrev;
|
||||
pA->pPrev = pB->pPrev;
|
||||
pB->pPrev = pTmp;
|
||||
zTmp = pA->zSql;
|
||||
pA->zSql = pB->zSql;
|
||||
pB->zSql = zTmp;
|
||||
nTmp = pA->nSql;
|
||||
pA->nSql = pB->nSql;
|
||||
pB->nSql = nTmp;
|
||||
}
|
||||
|
||||
/*
|
||||
** Turn tracing on or off
|
||||
*/
|
||||
@@ -812,21 +852,6 @@ void sqlite3VdbeMakeReady(
|
||||
p->aMem[n].flags = MEM_Null;
|
||||
}
|
||||
|
||||
#ifdef SQLITE_DEBUG
|
||||
if( (p->db->flags & SQLITE_VdbeListing)!=0
|
||||
|| sqlite3OsFileExists("vdbe_explain")
|
||||
){
|
||||
int i;
|
||||
printf("VDBE Program Listing:\n");
|
||||
sqlite3VdbePrintSql(p);
|
||||
for(i=0; i<p->nOp; i++){
|
||||
sqlite3VdbePrintOp(stdout, i, &p->aOp[i]);
|
||||
}
|
||||
}
|
||||
if( sqlite3OsFileExists("vdbe_trace") ){
|
||||
p->trace = stdout;
|
||||
}
|
||||
#endif
|
||||
p->pTos = &p->aStack[-1];
|
||||
p->pc = -1;
|
||||
p->rc = SQLITE_OK;
|
||||
@@ -1424,6 +1449,14 @@ int sqlite3VdbeHalt(Vdbe *p){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Each VDBE holds the result of the most recent sqlite3_step() call
|
||||
** in p->rc. This routine sets that result back to SQLITE_OK.
|
||||
*/
|
||||
void sqlite3VdbeResetStepResult(Vdbe *p){
|
||||
p->rc = SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Clean up a VDBE after execution but do not delete the VDBE just yet.
|
||||
** Write any error messages into *pzErrMsg. Return the result code.
|
||||
@@ -1574,6 +1607,7 @@ void sqlite3VdbeDelete(Vdbe *p){
|
||||
sqliteFree(p->aStack);
|
||||
releaseMemArray(p->aColName, p->nResColumn*COLNAME_N);
|
||||
sqliteFree(p->aColName);
|
||||
sqliteFree(p->zSql);
|
||||
p->magic = VDBE_MAGIC_DEAD;
|
||||
sqliteFree(p);
|
||||
}
|
||||
@@ -1892,14 +1926,13 @@ int sqlite3VdbeRecordCompare(
|
||||
idx2 += GetVarint( aKey2+idx2, serial_type2 );
|
||||
if( d2>=nKey2 && sqlite3VdbeSerialTypeLen(serial_type2)>0 ) break;
|
||||
|
||||
/* Assert that there is enough space left in each key for the blob of
|
||||
** data to go with the serial type just read. This assert may fail if
|
||||
** the file is corrupted. Then read the value from each key into mem1
|
||||
** and mem2 respectively.
|
||||
/* Extract the values to be compared.
|
||||
*/
|
||||
d1 += sqlite3VdbeSerialGet(&aKey1[d1], serial_type1, &mem1);
|
||||
d2 += sqlite3VdbeSerialGet(&aKey2[d2], serial_type2, &mem2);
|
||||
|
||||
/* Do the comparison
|
||||
*/
|
||||
rc = sqlite3MemCompare(&mem1, &mem2, i<nField ? pKeyInfo->aColl[i] : 0);
|
||||
if( mem1.flags & MEM_Dyn ) sqlite3VdbeMemRelease(&mem1);
|
||||
if( mem2.flags & MEM_Dyn ) sqlite3VdbeMemRelease(&mem2);
|
||||
|
||||
@@ -137,6 +137,7 @@ int sqlite3VdbeMemNulTerminate(Mem *pMem){
|
||||
}
|
||||
pMem->xDel = 0;
|
||||
pMem->z = z;
|
||||
pMem->flags |= MEM_Term;
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
*************************************************************************
|
||||
** This file contains code used to help implement virtual tables.
|
||||
**
|
||||
** $Id: vtab.c,v 1.37 2006/09/18 20:24:03 drh Exp $
|
||||
** $Id: vtab.c,v 1.39 2007/01/09 14:01:14 drh Exp $
|
||||
*/
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
#include "sqliteInt.h"
|
||||
@@ -230,7 +230,7 @@ void sqlite3VtabFinishParse(Parse *pParse, Token *pEnd){
|
||||
|
||||
sqlite3VdbeAddOp(v, OP_Expire, 0, 0);
|
||||
zWhere = sqlite3MPrintf("name='%q'", pTab->zName);
|
||||
sqlite3VdbeOp3(v, OP_ParseSchema, iDb, 0, zWhere, P3_DYNAMIC);
|
||||
sqlite3VdbeOp3(v, OP_ParseSchema, iDb, 1, zWhere, P3_DYNAMIC);
|
||||
sqlite3VdbeOp3(v, OP_VCreate, iDb, 0, pTab->zName, strlen(pTab->zName) + 1);
|
||||
}
|
||||
|
||||
@@ -340,7 +340,6 @@ static int vtabCallConstructor(
|
||||
*/
|
||||
int sqlite3VtabCallConnect(Parse *pParse, Table *pTab){
|
||||
Module *pMod;
|
||||
const char *zModule;
|
||||
int rc = SQLITE_OK;
|
||||
|
||||
if( !pTab || !pTab->isVirtual || pTab->pVtab ){
|
||||
@@ -348,7 +347,6 @@ int sqlite3VtabCallConnect(Parse *pParse, Table *pTab){
|
||||
}
|
||||
|
||||
pMod = pTab->pMod;
|
||||
zModule = pTab->azModuleArg[0];
|
||||
if( !pMod ){
|
||||
const char *zModule = pTab->azModuleArg[0];
|
||||
sqlite3ErrorMsg(pParse, "no such module: %s", zModule);
|
||||
|
||||
+183
-95
@@ -16,7 +16,7 @@
|
||||
** so is applicable. Because this module is responsible for selecting
|
||||
** indices, you might also think of this module as the "query optimizer".
|
||||
**
|
||||
** $Id: where.c,v 1.228 2006/06/27 13:20:22 drh Exp $
|
||||
** $Id: where.c,v 1.237 2007/02/06 13:26:33 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
@@ -43,6 +43,7 @@ int sqlite3_where_trace = 0;
|
||||
/* Forward reference
|
||||
*/
|
||||
typedef struct WhereClause WhereClause;
|
||||
typedef struct ExprMaskSet ExprMaskSet;
|
||||
|
||||
/*
|
||||
** The query generator uses an array of instances of this structure to
|
||||
@@ -106,6 +107,7 @@ struct WhereTerm {
|
||||
*/
|
||||
struct WhereClause {
|
||||
Parse *pParse; /* The parser context */
|
||||
ExprMaskSet *pMaskSet; /* Mapping of table indices to bitmasks */
|
||||
int nTerm; /* Number of terms */
|
||||
int nSlot; /* Number of entries in a[] */
|
||||
WhereTerm *a; /* Each a[] describes a term of the WHERE cluase */
|
||||
@@ -138,7 +140,6 @@ struct WhereClause {
|
||||
** numbers all get mapped into bit numbers that begin with 0 and contain
|
||||
** no gaps.
|
||||
*/
|
||||
typedef struct ExprMaskSet ExprMaskSet;
|
||||
struct ExprMaskSet {
|
||||
int n; /* Number of assigned cursor values */
|
||||
int ix[sizeof(Bitmask)*8]; /* Cursor assigned to each bit */
|
||||
@@ -157,28 +158,42 @@ struct ExprMaskSet {
|
||||
#define WO_GT (WO_EQ<<(TK_GT-TK_EQ))
|
||||
#define WO_GE (WO_EQ<<(TK_GE-TK_EQ))
|
||||
#define WO_MATCH 64
|
||||
#define WO_ISNULL 128
|
||||
|
||||
/*
|
||||
** Value for flags returned by bestIndex()
|
||||
** Value for flags returned by bestIndex().
|
||||
**
|
||||
** The least significant byte is reserved as a mask for WO_ values above.
|
||||
** The WhereLevel.flags field is usually set to WO_IN|WO_EQ|WO_ISNULL.
|
||||
** But if the table is the right table of a left join, WhereLevel.flags
|
||||
** is set to WO_IN|WO_EQ. The WhereLevel.flags field can then be used as
|
||||
** the "op" parameter to findTerm when we are resolving equality constraints.
|
||||
** ISNULL constraints will then not be used on the right table of a left
|
||||
** join. Tickets #2177 and #2189.
|
||||
*/
|
||||
#define WHERE_ROWID_EQ 0x0001 /* rowid=EXPR or rowid IN (...) */
|
||||
#define WHERE_ROWID_RANGE 0x0002 /* rowid<EXPR and/or rowid>EXPR */
|
||||
#define WHERE_COLUMN_EQ 0x0010 /* x=EXPR or x IN (...) */
|
||||
#define WHERE_COLUMN_RANGE 0x0020 /* x<EXPR and/or x>EXPR */
|
||||
#define WHERE_COLUMN_IN 0x0040 /* x IN (...) */
|
||||
#define WHERE_TOP_LIMIT 0x0100 /* x<EXPR or x<=EXPR constraint */
|
||||
#define WHERE_BTM_LIMIT 0x0200 /* x>EXPR or x>=EXPR constraint */
|
||||
#define WHERE_IDX_ONLY 0x0800 /* Use index only - omit table */
|
||||
#define WHERE_ORDERBY 0x1000 /* Output will appear in correct order */
|
||||
#define WHERE_REVERSE 0x2000 /* Scan in reverse order */
|
||||
#define WHERE_UNIQUE 0x4000 /* Selects no more than one row */
|
||||
#define WHERE_VIRTUALTABLE 0x8000 /* Use virtual-table processing */
|
||||
#define WHERE_ROWID_EQ 0x000100 /* rowid=EXPR or rowid IN (...) */
|
||||
#define WHERE_ROWID_RANGE 0x000200 /* rowid<EXPR and/or rowid>EXPR */
|
||||
#define WHERE_COLUMN_EQ 0x001000 /* x=EXPR or x IN (...) */
|
||||
#define WHERE_COLUMN_RANGE 0x002000 /* x<EXPR and/or x>EXPR */
|
||||
#define WHERE_COLUMN_IN 0x004000 /* x IN (...) */
|
||||
#define WHERE_TOP_LIMIT 0x010000 /* x<EXPR or x<=EXPR constraint */
|
||||
#define WHERE_BTM_LIMIT 0x020000 /* x>EXPR or x>=EXPR constraint */
|
||||
#define WHERE_IDX_ONLY 0x080000 /* Use index only - omit table */
|
||||
#define WHERE_ORDERBY 0x100000 /* Output will appear in correct order */
|
||||
#define WHERE_REVERSE 0x200000 /* Scan in reverse order */
|
||||
#define WHERE_UNIQUE 0x400000 /* Selects no more than one row */
|
||||
#define WHERE_VIRTUALTABLE 0x800000 /* Use virtual-table processing */
|
||||
|
||||
/*
|
||||
** Initialize a preallocated WhereClause structure.
|
||||
*/
|
||||
static void whereClauseInit(WhereClause *pWC, Parse *pParse){
|
||||
static void whereClauseInit(
|
||||
WhereClause *pWC, /* The WhereClause to be initialized */
|
||||
Parse *pParse, /* The parsing context */
|
||||
ExprMaskSet *pMaskSet /* Mapping from table indices to bitmasks */
|
||||
){
|
||||
pWC->pParse = pParse;
|
||||
pWC->pMaskSet = pMaskSet;
|
||||
pWC->nTerm = 0;
|
||||
pWC->nSlot = ARRAYSIZE(pWC->aStatic);
|
||||
pWC->a = pWC->aStatic;
|
||||
@@ -354,7 +369,7 @@ static int allowedOp(int op){
|
||||
assert( TK_LT>TK_EQ && TK_LT<TK_GE );
|
||||
assert( TK_LE>TK_EQ && TK_LE<TK_GE );
|
||||
assert( TK_GE==TK_EQ+4 );
|
||||
return op==TK_IN || (op>=TK_EQ && op<=TK_GE);
|
||||
return op==TK_IN || (op>=TK_EQ && op<=TK_GE) || op==TK_ISNULL;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -388,9 +403,12 @@ static int operatorMask(int op){
|
||||
assert( allowedOp(op) );
|
||||
if( op==TK_IN ){
|
||||
c = WO_IN;
|
||||
}else if( op==TK_ISNULL ){
|
||||
c = WO_ISNULL;
|
||||
}else{
|
||||
c = WO_EQ<<(op-TK_EQ);
|
||||
}
|
||||
assert( op!=TK_ISNULL || c==WO_ISNULL );
|
||||
assert( op!=TK_IN || c==WO_IN );
|
||||
assert( op!=TK_EQ || c==WO_EQ );
|
||||
assert( op!=TK_LT || c==WO_LT );
|
||||
@@ -422,7 +440,7 @@ static WhereTerm *findTerm(
|
||||
&& pTerm->leftColumn==iColumn
|
||||
&& (pTerm->eOperator & op)!=0
|
||||
){
|
||||
if( iCur>=0 && pIdx ){
|
||||
if( iCur>=0 && pIdx && pTerm->eOperator!=WO_ISNULL ){
|
||||
Expr *pX = pTerm->pExpr;
|
||||
CollSeq *pColl;
|
||||
char idxaff;
|
||||
@@ -451,7 +469,7 @@ static WhereTerm *findTerm(
|
||||
}
|
||||
|
||||
/* Forward reference */
|
||||
static void exprAnalyze(SrcList*, ExprMaskSet*, WhereClause*, int);
|
||||
static void exprAnalyze(SrcList*, WhereClause*, int);
|
||||
|
||||
/*
|
||||
** Call exprAnalyze on all terms in a WHERE clause.
|
||||
@@ -460,12 +478,11 @@ static void exprAnalyze(SrcList*, ExprMaskSet*, WhereClause*, int);
|
||||
*/
|
||||
static void exprAnalyzeAll(
|
||||
SrcList *pTabList, /* the FROM clause */
|
||||
ExprMaskSet *pMaskSet, /* table masks */
|
||||
WhereClause *pWC /* the WHERE clause to be analyzed */
|
||||
){
|
||||
int i;
|
||||
for(i=pWC->nTerm-1; i>=0; i--){
|
||||
exprAnalyze(pTabList, pMaskSet, pWC, i);
|
||||
exprAnalyze(pTabList, pWC, i);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -580,23 +597,27 @@ static void transferJoinMarkings(Expr *pDerived, Expr *pBase){
|
||||
*/
|
||||
static void exprAnalyze(
|
||||
SrcList *pSrc, /* the FROM clause */
|
||||
ExprMaskSet *pMaskSet, /* table masks */
|
||||
WhereClause *pWC, /* the WHERE clause */
|
||||
int idxTerm /* Index of the term to be analyzed */
|
||||
){
|
||||
WhereTerm *pTerm = &pWC->a[idxTerm];
|
||||
ExprMaskSet *pMaskSet = pWC->pMaskSet;
|
||||
Expr *pExpr = pTerm->pExpr;
|
||||
Bitmask prereqLeft;
|
||||
Bitmask prereqAll;
|
||||
int nPattern;
|
||||
int isComplete;
|
||||
int op;
|
||||
|
||||
if( sqlite3MallocFailed() ) return;
|
||||
prereqLeft = exprTableUsage(pMaskSet, pExpr->pLeft);
|
||||
if( pExpr->op==TK_IN ){
|
||||
op = pExpr->op;
|
||||
if( op==TK_IN ){
|
||||
assert( pExpr->pRight==0 );
|
||||
pTerm->prereqRight = exprListTableUsage(pMaskSet, pExpr->pList)
|
||||
| exprSelectTableUsage(pMaskSet, pExpr->pSelect);
|
||||
}else if( op==TK_ISNULL ){
|
||||
pTerm->prereqRight = 0;
|
||||
}else{
|
||||
pTerm->prereqRight = exprTableUsage(pMaskSet, pExpr->pRight);
|
||||
}
|
||||
@@ -608,13 +629,13 @@ static void exprAnalyze(
|
||||
pTerm->leftCursor = -1;
|
||||
pTerm->iParent = -1;
|
||||
pTerm->eOperator = 0;
|
||||
if( allowedOp(pExpr->op) && (pTerm->prereqRight & prereqLeft)==0 ){
|
||||
if( allowedOp(op) && (pTerm->prereqRight & prereqLeft)==0 ){
|
||||
Expr *pLeft = pExpr->pLeft;
|
||||
Expr *pRight = pExpr->pRight;
|
||||
if( pLeft->op==TK_COLUMN ){
|
||||
pTerm->leftCursor = pLeft->iTable;
|
||||
pTerm->leftColumn = pLeft->iColumn;
|
||||
pTerm->eOperator = operatorMask(pExpr->op);
|
||||
pTerm->eOperator = operatorMask(op);
|
||||
}
|
||||
if( pRight && pRight->op==TK_COLUMN ){
|
||||
WhereTerm *pNew;
|
||||
@@ -622,6 +643,10 @@ static void exprAnalyze(
|
||||
if( pTerm->leftCursor>=0 ){
|
||||
int idxNew;
|
||||
pDup = sqlite3ExprDup(pExpr);
|
||||
if( sqlite3MallocFailed() ){
|
||||
sqliteFree(pDup);
|
||||
return;
|
||||
}
|
||||
idxNew = whereClauseInsert(pWC, pDup, TERM_VIRTUAL|TERM_DYNAMIC);
|
||||
if( idxNew==0 ) return;
|
||||
pNew = &pWC->a[idxNew];
|
||||
@@ -659,7 +684,7 @@ static void exprAnalyze(
|
||||
pNewExpr = sqlite3Expr(ops[i], sqlite3ExprDup(pExpr->pLeft),
|
||||
sqlite3ExprDup(pList->a[i].pExpr), 0);
|
||||
idxNew = whereClauseInsert(pWC, pNewExpr, TERM_VIRTUAL|TERM_DYNAMIC);
|
||||
exprAnalyze(pSrc, pMaskSet, pWC, idxNew);
|
||||
exprAnalyze(pSrc, pWC, idxNew);
|
||||
pTerm = &pWC->a[idxTerm];
|
||||
pWC->a[idxNew].iParent = idxTerm;
|
||||
}
|
||||
@@ -688,9 +713,9 @@ static void exprAnalyze(
|
||||
WhereTerm *pOrTerm;
|
||||
|
||||
assert( (pTerm->flags & TERM_DYNAMIC)==0 );
|
||||
whereClauseInit(&sOr, pWC->pParse);
|
||||
whereClauseInit(&sOr, pWC->pParse, pMaskSet);
|
||||
whereSplit(&sOr, pExpr, TK_OR);
|
||||
exprAnalyzeAll(pSrc, pMaskSet, &sOr);
|
||||
exprAnalyzeAll(pSrc, &sOr);
|
||||
assert( sOr.nTerm>0 );
|
||||
j = 0;
|
||||
do{
|
||||
@@ -715,23 +740,22 @@ static void exprAnalyze(
|
||||
if( ok ){
|
||||
ExprList *pList = 0;
|
||||
Expr *pNew, *pDup;
|
||||
Expr *pLeft = 0;
|
||||
for(i=sOr.nTerm-1, pOrTerm=sOr.a; i>=0 && ok; i--, pOrTerm++){
|
||||
if( (pOrTerm->flags & TERM_OR_OK)==0 ) continue;
|
||||
pDup = sqlite3ExprDup(pOrTerm->pExpr->pRight);
|
||||
pList = sqlite3ExprListAppend(pList, pDup, 0);
|
||||
pLeft = pOrTerm->pExpr->pLeft;
|
||||
}
|
||||
pDup = sqlite3Expr(TK_COLUMN, 0, 0, 0);
|
||||
if( pDup ){
|
||||
pDup->iTable = iCursor;
|
||||
pDup->iColumn = iColumn;
|
||||
}
|
||||
assert( pLeft!=0 );
|
||||
pDup = sqlite3ExprDup(pLeft);
|
||||
pNew = sqlite3Expr(TK_IN, pDup, 0, 0);
|
||||
if( pNew ){
|
||||
int idxNew;
|
||||
transferJoinMarkings(pNew, pExpr);
|
||||
pNew->pList = pList;
|
||||
idxNew = whereClauseInsert(pWC, pNew, TERM_VIRTUAL|TERM_DYNAMIC);
|
||||
exprAnalyze(pSrc, pMaskSet, pWC, idxNew);
|
||||
exprAnalyze(pSrc, pWC, idxNew);
|
||||
pTerm = &pWC->a[idxTerm];
|
||||
pWC->a[idxNew].iParent = idxTerm;
|
||||
pTerm->nChild = 1;
|
||||
@@ -768,10 +792,10 @@ or_not_possible:
|
||||
}
|
||||
pNewExpr1 = sqlite3Expr(TK_GE, sqlite3ExprDup(pLeft), pStr1, 0);
|
||||
idxNew1 = whereClauseInsert(pWC, pNewExpr1, TERM_VIRTUAL|TERM_DYNAMIC);
|
||||
exprAnalyze(pSrc, pMaskSet, pWC, idxNew1);
|
||||
exprAnalyze(pSrc, pWC, idxNew1);
|
||||
pNewExpr2 = sqlite3Expr(TK_LT, sqlite3ExprDup(pLeft), pStr2, 0);
|
||||
idxNew2 = whereClauseInsert(pWC, pNewExpr2, TERM_VIRTUAL|TERM_DYNAMIC);
|
||||
exprAnalyze(pSrc, pMaskSet, pWC, idxNew2);
|
||||
exprAnalyze(pSrc, pWC, idxNew2);
|
||||
pTerm = &pWC->a[idxTerm];
|
||||
if( isComplete ){
|
||||
pWC->a[idxNew1].iParent = idxTerm;
|
||||
@@ -817,6 +841,25 @@ or_not_possible:
|
||||
#endif /* SQLITE_OMIT_VIRTUALTABLE */
|
||||
}
|
||||
|
||||
/*
|
||||
** Return TRUE if any of the expressions in pList->a[iFirst...] contain
|
||||
** a reference to any table other than the iBase table.
|
||||
*/
|
||||
static int referencesOtherTables(
|
||||
ExprList *pList, /* Search expressions in ths list */
|
||||
ExprMaskSet *pMaskSet, /* Mapping from tables to bitmaps */
|
||||
int iFirst, /* Be searching with the iFirst-th expression */
|
||||
int iBase /* Ignore references to this table */
|
||||
){
|
||||
Bitmask allowed = ~getMask(pMaskSet, iBase);
|
||||
while( iFirst<pList->nExpr ){
|
||||
if( (exprTableUsage(pMaskSet, pList->a[iFirst++].pExpr)&allowed)!=0 ){
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** This routine decides if pIdx can be used to satisfy the ORDER BY
|
||||
@@ -839,6 +882,7 @@ or_not_possible:
|
||||
*/
|
||||
static int isSortingIndex(
|
||||
Parse *pParse, /* Parsing context */
|
||||
ExprMaskSet *pMaskSet, /* Mapping from table indices to bitmaps */
|
||||
Index *pIdx, /* The index we are testing */
|
||||
int base, /* Cursor number for the table to be sorted */
|
||||
ExprList *pOrderBy, /* The ORDER BY clause */
|
||||
@@ -857,22 +901,43 @@ static int isSortingIndex(
|
||||
|
||||
/* Match terms of the ORDER BY clause against columns of
|
||||
** the index.
|
||||
**
|
||||
** Note that indices have pIdx->nColumn regular columns plus
|
||||
** one additional column containing the rowid. The rowid column
|
||||
** of the index is also allowed to match against the ORDER BY
|
||||
** clause.
|
||||
*/
|
||||
for(i=j=0, pTerm=pOrderBy->a; j<nTerm && i<pIdx->nColumn; i++){
|
||||
for(i=j=0, pTerm=pOrderBy->a; j<nTerm && i<=pIdx->nColumn; i++){
|
||||
Expr *pExpr; /* The expression of the ORDER BY pTerm */
|
||||
CollSeq *pColl; /* The collating sequence of pExpr */
|
||||
int termSortOrder; /* Sort order for this term */
|
||||
int iColumn; /* The i-th column of the index. -1 for rowid */
|
||||
int iSortOrder; /* 1 for DESC, 0 for ASC on the i-th index term */
|
||||
const char *zColl; /* Name of the collating sequence for i-th index term */
|
||||
|
||||
pExpr = pTerm->pExpr;
|
||||
if( pExpr->op!=TK_COLUMN || pExpr->iTable!=base ){
|
||||
/* Can not use an index sort on anything that is not a column in the
|
||||
** left-most table of the FROM clause */
|
||||
return 0;
|
||||
break;
|
||||
}
|
||||
pColl = sqlite3ExprCollSeq(pParse, pExpr);
|
||||
if( !pColl ) pColl = db->pDfltColl;
|
||||
if( pExpr->iColumn!=pIdx->aiColumn[i] ||
|
||||
sqlite3StrICmp(pColl->zName, pIdx->azColl[i]) ){
|
||||
if( !pColl ){
|
||||
pColl = db->pDfltColl;
|
||||
}
|
||||
if( i<pIdx->nColumn ){
|
||||
iColumn = pIdx->aiColumn[i];
|
||||
if( iColumn==pIdx->pTable->iPKey ){
|
||||
iColumn = -1;
|
||||
}
|
||||
iSortOrder = pIdx->aSortOrder[i];
|
||||
zColl = pIdx->azColl[i];
|
||||
}else{
|
||||
iColumn = -1;
|
||||
iSortOrder = 0;
|
||||
zColl = pColl->zName;
|
||||
}
|
||||
if( pExpr->iColumn!=iColumn || sqlite3StrICmp(pColl->zName, zColl) ){
|
||||
/* Term j of the ORDER BY clause does not match column i of the index */
|
||||
if( i<nEqCol ){
|
||||
/* If an index column that is constrained by == fails to match an
|
||||
@@ -888,8 +953,8 @@ static int isSortingIndex(
|
||||
}
|
||||
assert( pIdx->aSortOrder!=0 );
|
||||
assert( pTerm->sortOrder==0 || pTerm->sortOrder==1 );
|
||||
assert( pIdx->aSortOrder[i]==0 || pIdx->aSortOrder[i]==1 );
|
||||
termSortOrder = pIdx->aSortOrder[i] ^ pTerm->sortOrder;
|
||||
assert( iSortOrder==0 || iSortOrder==1 );
|
||||
termSortOrder = iSortOrder ^ pTerm->sortOrder;
|
||||
if( i>nEqCol ){
|
||||
if( termSortOrder!=sortOrder ){
|
||||
/* Indices can only be used if all ORDER BY terms past the
|
||||
@@ -901,13 +966,29 @@ static int isSortingIndex(
|
||||
}
|
||||
j++;
|
||||
pTerm++;
|
||||
if( iColumn<0 && !referencesOtherTables(pOrderBy, pMaskSet, j, base) ){
|
||||
/* If the indexed column is the primary key and everything matches
|
||||
** so far and none of the ORDER BY terms to the right reference other
|
||||
** tables in the join, then we are assured that the index can be used
|
||||
** to sort because the primary key is unique and so none of the other
|
||||
** columns will make any difference
|
||||
*/
|
||||
j = nTerm;
|
||||
}
|
||||
}
|
||||
|
||||
/* The index can be used for sorting if all terms of the ORDER BY clause
|
||||
** are covered.
|
||||
*/
|
||||
*pbRev = sortOrder!=0;
|
||||
if( j>=nTerm ){
|
||||
*pbRev = sortOrder!=0;
|
||||
/* All terms of the ORDER BY clause are covered by this index so
|
||||
** this index can be used for sorting. */
|
||||
return 1;
|
||||
}
|
||||
if( pIdx->onError!=OE_None && i==pIdx->nColumn
|
||||
&& !referencesOtherTables(pOrderBy, pMaskSet, j, base) ){
|
||||
/* All terms of this index match some prefix of the ORDER BY clause
|
||||
** and the index is UNIQUE and no terms on the tail of the ORDER BY
|
||||
** clause reference other tables in a join. If this is all true then
|
||||
** the order by clause is superfluous. */
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
@@ -921,6 +1002,7 @@ static int isSortingIndex(
|
||||
static int sortableByRowid(
|
||||
int base, /* Cursor number for table to be sorted */
|
||||
ExprList *pOrderBy, /* The ORDER BY clause */
|
||||
ExprMaskSet *pMaskSet, /* Mapping from tables to bitmaps */
|
||||
int *pbRev /* Set to 1 if ORDER BY is DESC */
|
||||
){
|
||||
Expr *p;
|
||||
@@ -928,8 +1010,8 @@ static int sortableByRowid(
|
||||
assert( pOrderBy!=0 );
|
||||
assert( pOrderBy->nExpr>0 );
|
||||
p = pOrderBy->a[0].pExpr;
|
||||
if( pOrderBy->nExpr==1 && p->op==TK_COLUMN && p->iTable==base
|
||||
&& p->iColumn==-1 ){
|
||||
if( p->op==TK_COLUMN && p->iTable==base && p->iColumn==-1
|
||||
&& !referencesOtherTables(pOrderBy, pMaskSet, 1, base) ){
|
||||
*pbRev = pOrderBy->a[0].sortOrder;
|
||||
return 1;
|
||||
}
|
||||
@@ -1232,6 +1314,7 @@ static double bestIndex(
|
||||
int rev; /* True to scan in reverse order */
|
||||
int flags; /* Flags associated with pProbe */
|
||||
int nEq; /* Number of == or IN constraints */
|
||||
int eqTermMask; /* Mask of valid equality operators */
|
||||
double cost; /* Cost of using pProbe */
|
||||
|
||||
TRACE(("bestIndex: tbl=%s notReady=%x\n", pSrc->pTab->zName, notReady));
|
||||
@@ -1246,7 +1329,7 @@ static double bestIndex(
|
||||
*/
|
||||
if( pProbe==0 &&
|
||||
findTerm(pWC, iCur, -1, 0, WO_EQ|WO_IN|WO_LT|WO_LE|WO_GT|WO_GE,0)==0 &&
|
||||
(pOrderBy==0 || !sortableByRowid(iCur, pOrderBy, &rev)) ){
|
||||
(pOrderBy==0 || !sortableByRowid(iCur, pOrderBy, pWC->pMaskSet, &rev)) ){
|
||||
*pFlags = 0;
|
||||
*ppIndex = 0;
|
||||
*pnEq = 0;
|
||||
@@ -1308,7 +1391,7 @@ static double bestIndex(
|
||||
/* If the table scan does not satisfy the ORDER BY clause, increase
|
||||
** the cost by NlogN to cover the expense of sorting. */
|
||||
if( pOrderBy ){
|
||||
if( sortableByRowid(iCur, pOrderBy, &rev) ){
|
||||
if( sortableByRowid(iCur, pOrderBy, pWC->pMaskSet, &rev) ){
|
||||
flags |= WHERE_ORDERBY|WHERE_ROWID_RANGE;
|
||||
if( rev ){
|
||||
flags |= WHERE_REVERSE;
|
||||
@@ -1323,6 +1406,17 @@ static double bestIndex(
|
||||
bestFlags = flags;
|
||||
}
|
||||
|
||||
/* If the pSrc table is the right table of a LEFT JOIN then we may not
|
||||
** use an index to satisfy IS NULL constraints on that table. This is
|
||||
** because columns might end up being NULL if the table does not match -
|
||||
** a circumstance which the index cannot help us discover. Ticket #2177.
|
||||
*/
|
||||
if( (pSrc->jointype & JT_LEFT)!=0 ){
|
||||
eqTermMask = WO_EQ|WO_IN;
|
||||
}else{
|
||||
eqTermMask = WO_EQ|WO_IN|WO_ISNULL;
|
||||
}
|
||||
|
||||
/* Look at each index.
|
||||
*/
|
||||
for(; pProbe; pProbe=pProbe->pNext){
|
||||
@@ -1337,7 +1431,7 @@ static double bestIndex(
|
||||
flags = 0;
|
||||
for(i=0; i<pProbe->nColumn; i++){
|
||||
int j = pProbe->aiColumn[i];
|
||||
pTerm = findTerm(pWC, iCur, j, notReady, WO_EQ|WO_IN, pProbe);
|
||||
pTerm = findTerm(pWC, iCur, j, notReady, eqTermMask, pProbe);
|
||||
if( pTerm==0 ) break;
|
||||
flags |= WHERE_COLUMN_EQ;
|
||||
if( pTerm->eOperator & WO_IN ){
|
||||
@@ -1381,7 +1475,7 @@ static double bestIndex(
|
||||
*/
|
||||
if( pOrderBy ){
|
||||
if( (flags & WHERE_COLUMN_IN)==0 &&
|
||||
isSortingIndex(pParse,pProbe,iCur,pOrderBy,nEq,&rev) ){
|
||||
isSortingIndex(pParse,pWC->pMaskSet,pProbe,iCur,pOrderBy,nEq,&rev) ){
|
||||
if( flags==0 ){
|
||||
flags = WHERE_COLUMN_RANGE;
|
||||
}
|
||||
@@ -1431,7 +1525,7 @@ static double bestIndex(
|
||||
*ppIndex = bestIdx;
|
||||
TRACE(("best index is %s, cost=%.9g, flags=%x, nEq=%d\n",
|
||||
bestIdx ? bestIdx->zName : "(none)", lowestCost, bestFlags, bestNEq));
|
||||
*pFlags = bestFlags;
|
||||
*pFlags = bestFlags | eqTermMask;
|
||||
*pnEq = bestNEq;
|
||||
return lowestCost;
|
||||
}
|
||||
@@ -1476,30 +1570,18 @@ static void disableTerm(WhereLevel *pLevel, WhereTerm *pTerm){
|
||||
}
|
||||
|
||||
/*
|
||||
** Generate code that builds a probe for an index. Details:
|
||||
**
|
||||
** * Check the top nColumn entries on the stack. If any
|
||||
** of those entries are NULL, jump immediately to brk,
|
||||
** which is the loop exit, since no index entry will match
|
||||
** if any part of the key is NULL. Pop (nColumn+nExtra)
|
||||
** elements from the stack.
|
||||
**
|
||||
** * Construct a probe entry from the top nColumn entries in
|
||||
** the stack with affinities appropriate for index pIdx.
|
||||
** Only nColumn elements are popped from the stack in this case
|
||||
** (by OP_MakeRecord).
|
||||
** Generate code that builds a probe for an index.
|
||||
**
|
||||
** There should be nColumn values on the stack. The index
|
||||
** to be probed is pIdx. Pop the values from the stack and
|
||||
** replace them all with a single record that is the index
|
||||
** problem.
|
||||
*/
|
||||
static void buildIndexProbe(
|
||||
Vdbe *v,
|
||||
int nColumn,
|
||||
int nExtra,
|
||||
int brk,
|
||||
Index *pIdx
|
||||
Vdbe *v, /* Generate code into this VM */
|
||||
int nColumn, /* The number of columns to check for NULL */
|
||||
Index *pIdx /* Index that we will be searching */
|
||||
){
|
||||
sqlite3VdbeAddOp(v, OP_NotNull, -nColumn, sqlite3VdbeCurrentAddr(v)+3);
|
||||
sqlite3VdbeAddOp(v, OP_Pop, nColumn+nExtra, 0);
|
||||
sqlite3VdbeAddOp(v, OP_Goto, 0, brk);
|
||||
sqlite3VdbeAddOp(v, OP_MakeRecord, nColumn, 0);
|
||||
sqlite3IndexAffinityStr(v, pIdx);
|
||||
}
|
||||
@@ -1523,15 +1605,17 @@ static void codeEqualityTerm(
|
||||
WhereLevel *pLevel /* When level of the FROM clause we are working on */
|
||||
){
|
||||
Expr *pX = pTerm->pExpr;
|
||||
if( pX->op!=TK_IN ){
|
||||
assert( pX->op==TK_EQ );
|
||||
Vdbe *v = pParse->pVdbe;
|
||||
if( pX->op==TK_EQ ){
|
||||
sqlite3ExprCode(pParse, pX->pRight);
|
||||
}else if( pX->op==TK_ISNULL ){
|
||||
sqlite3VdbeAddOp(v, OP_Null, 0, 0);
|
||||
#ifndef SQLITE_OMIT_SUBQUERY
|
||||
}else{
|
||||
int iTab;
|
||||
int *aIn;
|
||||
Vdbe *v = pParse->pVdbe;
|
||||
|
||||
assert( pX->op==TK_IN );
|
||||
sqlite3CodeSubselect(pParse, pX);
|
||||
iTab = pX->iTable;
|
||||
sqlite3VdbeAddOp(v, OP_Rewind, iTab, 0);
|
||||
@@ -1603,17 +1687,20 @@ static void codeAllEqualityTerms(
|
||||
|
||||
/* Evaluate the equality constraints
|
||||
*/
|
||||
for(j=0; j<pIdx->nColumn; j++){
|
||||
assert( pIdx->nColumn>=nEq );
|
||||
for(j=0; j<nEq; j++){
|
||||
int k = pIdx->aiColumn[j];
|
||||
pTerm = findTerm(pWC, iCur, k, notReady, WO_EQ|WO_IN, pIdx);
|
||||
pTerm = findTerm(pWC, iCur, k, notReady, pLevel->flags, pIdx);
|
||||
if( pTerm==0 ) break;
|
||||
assert( (pTerm->flags & TERM_CODED)==0 );
|
||||
codeEqualityTerm(pParse, pTerm, brk, pLevel);
|
||||
if( (pTerm->eOperator & WO_ISNULL)==0 ){
|
||||
sqlite3VdbeAddOp(v, OP_IsNull, termsInMem ? -1 : -(j+1), brk);
|
||||
}
|
||||
if( termsInMem ){
|
||||
sqlite3VdbeAddOp(v, OP_MemStore, pLevel->iMem+j+1, 1);
|
||||
}
|
||||
}
|
||||
assert( j==nEq );
|
||||
|
||||
/* Make sure all the constraint values are on the top of the stack
|
||||
*/
|
||||
@@ -1776,7 +1863,7 @@ WhereInfo *sqlite3WhereBegin(
|
||||
** subexpression is separated by an AND operator.
|
||||
*/
|
||||
initMaskSet(&maskSet);
|
||||
whereClauseInit(&wc, pParse);
|
||||
whereClauseInit(&wc, pParse, &maskSet);
|
||||
whereSplit(&wc, pWhere, TK_AND);
|
||||
|
||||
/* Allocate and initialize the WhereInfo structure that will become the
|
||||
@@ -1807,7 +1894,7 @@ WhereInfo *sqlite3WhereBegin(
|
||||
for(i=0; i<pTabList->nSrc; i++){
|
||||
createMask(&maskSet, pTabList->a[i].iCursor);
|
||||
}
|
||||
exprAnalyzeAll(pTabList, &maskSet, &wc);
|
||||
exprAnalyzeAll(pTabList, &wc);
|
||||
if( sqlite3MallocFailed() ){
|
||||
goto whereBeginNoMem;
|
||||
}
|
||||
@@ -1850,8 +1937,7 @@ WhereInfo *sqlite3WhereBegin(
|
||||
for(j=iFrom, pTabItem=&pTabList->a[j]; j<pTabList->nSrc; j++, pTabItem++){
|
||||
int doNotReorder; /* True if this table should not be reordered */
|
||||
|
||||
doNotReorder = (pTabItem->jointype & (JT_LEFT|JT_CROSS))!=0
|
||||
|| (j>0 && (pTabItem[-1].jointype & (JT_LEFT|JT_CROSS))!=0);
|
||||
doNotReorder = (pTabItem->jointype & (JT_LEFT|JT_CROSS))!=0;
|
||||
if( once && doNotReorder ) break;
|
||||
m = getMask(&maskSet, pTabItem->iCursor);
|
||||
if( (m & notReady)==0 ){
|
||||
@@ -1986,7 +2072,9 @@ WhereInfo *sqlite3WhereBegin(
|
||||
sqlite3VdbeOp3(v, OP_OpenRead, iIdxCur, pIx->tnum,
|
||||
(char*)pKey, P3_KEYINFO_HANDOFF);
|
||||
}
|
||||
if( (pLevel->flags & WHERE_IDX_ONLY)!=0 ){
|
||||
if( (pLevel->flags & (WHERE_IDX_ONLY|WHERE_COLUMN_RANGE))!=0 ){
|
||||
/* Only call OP_SetNumColumns on the index if we might later use
|
||||
** OP_Column on the index. */
|
||||
sqlite3VdbeAddOp(v, OP_SetNumColumns, iIdxCur, pIx->nColumn+1);
|
||||
}
|
||||
sqlite3CodeVerifySchema(pParse, iDb);
|
||||
@@ -2025,7 +2113,7 @@ WhereInfo *sqlite3WhereBegin(
|
||||
** initialize a memory cell that records if this table matches any
|
||||
** row of the left table of the join.
|
||||
*/
|
||||
if( pLevel->iFrom>0 && (pTabItem[-1].jointype & JT_LEFT)!=0 ){
|
||||
if( pLevel->iFrom>0 && (pTabItem[0].jointype & JT_LEFT)!=0 ){
|
||||
if( !pParse->nMem ) pParse->nMem++;
|
||||
pLevel->iLeftJoin = pParse->nMem++;
|
||||
sqlite3VdbeAddOp(v, OP_MemInt, 0, pLevel->iLeftJoin);
|
||||
@@ -2159,7 +2247,6 @@ WhereInfo *sqlite3WhereBegin(
|
||||
int btmEq=0; /* True if btm limit uses ==. False if strictly > */
|
||||
int topOp, btmOp; /* Operators for the top and bottom search bounds */
|
||||
int testOp;
|
||||
int nNotNull; /* Number of rows of index that must be non-NULL */
|
||||
int topLimit = (pLevel->flags & WHERE_TOP_LIMIT)!=0;
|
||||
int btmLimit = (pLevel->flags & WHERE_BTM_LIMIT)!=0;
|
||||
|
||||
@@ -2181,7 +2268,6 @@ WhereInfo *sqlite3WhereBegin(
|
||||
** operator and the top bound is a < or <= operator. For a descending
|
||||
** index the operators are reversed.
|
||||
*/
|
||||
nNotNull = nEq + topLimit;
|
||||
if( pIdx->aSortOrder[nEq]==SQLITE_SO_ASC ){
|
||||
topOp = WO_LT|WO_LE;
|
||||
btmOp = WO_GT|WO_GE;
|
||||
@@ -2206,6 +2292,7 @@ WhereInfo *sqlite3WhereBegin(
|
||||
pX = pTerm->pExpr;
|
||||
assert( (pTerm->flags & TERM_CODED)==0 );
|
||||
sqlite3ExprCode(pParse, pX->pRight);
|
||||
sqlite3VdbeAddOp(v, OP_IsNull, -(nEq+1), brk);
|
||||
topEq = pTerm->eOperator & (WO_LE|WO_GE);
|
||||
disableTerm(pLevel, pTerm);
|
||||
testOp = OP_IdxGE;
|
||||
@@ -2216,7 +2303,7 @@ WhereInfo *sqlite3WhereBegin(
|
||||
if( testOp!=OP_Noop ){
|
||||
int nCol = nEq + topLimit;
|
||||
pLevel->iMem = pParse->nMem++;
|
||||
buildIndexProbe(v, nCol, nEq, brk, pIdx);
|
||||
buildIndexProbe(v, nCol, pIdx);
|
||||
if( bRev ){
|
||||
int op = topEq ? OP_MoveLe : OP_MoveLt;
|
||||
sqlite3VdbeAddOp(v, op, iIdxCur, brk);
|
||||
@@ -2244,6 +2331,7 @@ WhereInfo *sqlite3WhereBegin(
|
||||
pX = pTerm->pExpr;
|
||||
assert( (pTerm->flags & TERM_CODED)==0 );
|
||||
sqlite3ExprCode(pParse, pX->pRight);
|
||||
sqlite3VdbeAddOp(v, OP_IsNull, -(nEq+1), brk);
|
||||
btmEq = pTerm->eOperator & (WO_LE|WO_GE);
|
||||
disableTerm(pLevel, pTerm);
|
||||
}else{
|
||||
@@ -2251,7 +2339,7 @@ WhereInfo *sqlite3WhereBegin(
|
||||
}
|
||||
if( nEq>0 || btmLimit ){
|
||||
int nCol = nEq + btmLimit;
|
||||
buildIndexProbe(v, nCol, 0, brk, pIdx);
|
||||
buildIndexProbe(v, nCol, pIdx);
|
||||
if( bRev ){
|
||||
pLevel->iMem = pParse->nMem++;
|
||||
sqlite3VdbeAddOp(v, OP_MemStore, pLevel->iMem, 1);
|
||||
@@ -2278,8 +2366,10 @@ WhereInfo *sqlite3WhereBegin(
|
||||
sqlite3VdbeChangeP3(v, -1, "+", P3_STATIC);
|
||||
}
|
||||
}
|
||||
sqlite3VdbeAddOp(v, OP_RowKey, iIdxCur, 0);
|
||||
sqlite3VdbeAddOp(v, OP_IdxIsNull, nNotNull, cont);
|
||||
if( topLimit | btmLimit ){
|
||||
sqlite3VdbeAddOp(v, OP_Column, iIdxCur, nEq);
|
||||
sqlite3VdbeAddOp(v, OP_IsNull, 1, cont);
|
||||
}
|
||||
if( !omitTable ){
|
||||
sqlite3VdbeAddOp(v, OP_IdxRowid, iIdxCur, 0);
|
||||
sqlite3VdbeAddOp(v, OP_MoveGe, iCur, 0);
|
||||
@@ -2305,7 +2395,7 @@ WhereInfo *sqlite3WhereBegin(
|
||||
/* Generate a single key that will be used to both start and terminate
|
||||
** the search
|
||||
*/
|
||||
buildIndexProbe(v, nEq, 0, brk, pIdx);
|
||||
buildIndexProbe(v, nEq, pIdx);
|
||||
sqlite3VdbeAddOp(v, OP_MemStore, pLevel->iMem, 0);
|
||||
|
||||
/* Generate code (1) to move to the first matching element of the table.
|
||||
@@ -2326,8 +2416,6 @@ WhereInfo *sqlite3WhereBegin(
|
||||
sqlite3VdbeOp3(v, OP_IdxGE, iIdxCur, brk, "+", P3_STATIC);
|
||||
pLevel->op = OP_Next;
|
||||
}
|
||||
sqlite3VdbeAddOp(v, OP_RowKey, iIdxCur, 0);
|
||||
sqlite3VdbeAddOp(v, OP_IdxIsNull, nEq, cont);
|
||||
if( !omitTable ){
|
||||
sqlite3VdbeAddOp(v, OP_IdxRowid, iIdxCur, 0);
|
||||
sqlite3VdbeAddOp(v, OP_MoveGe, iCur, 0);
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
#***********************************************************************
|
||||
# This file runs all tests.
|
||||
#
|
||||
# $Id: all.test,v 1.35 2006/01/17 15:36:33 danielk1977 Exp $
|
||||
# $Id: all.test,v 1.36 2006/11/23 21:09:11 drh Exp $
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
@@ -56,6 +56,7 @@ set EXCLUDE {
|
||||
malloc.test
|
||||
misuse.test
|
||||
memleak.test
|
||||
speed1.test
|
||||
}
|
||||
|
||||
# Files to include in the test. If this list is empty then everything
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
# file format change that may be used in the future to implement
|
||||
# "ALTER TABLE ... ADD COLUMN".
|
||||
#
|
||||
# $Id: alter2.test,v 1.5 2006/01/03 00:33:50 drh Exp $
|
||||
# $Id: alter2.test,v 1.6 2007/01/04 14:36:02 drh Exp $
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
@@ -25,7 +25,7 @@ ifcapable {!pragma} return
|
||||
# These tests do not work if there is a codec. The
|
||||
# btree_open command does not know how to handle codecs.
|
||||
#
|
||||
if {[catch {sqlite3 -has_codec} r] || $r} return
|
||||
#if {[catch {sqlite3 -has_codec} r] || $r} return
|
||||
|
||||
# The file format change affects the way row-records stored in tables (but
|
||||
# not indices) are interpreted. Before version 3.1.3, a row-record for a
|
||||
@@ -68,17 +68,13 @@ proc get_file_format {{fname test.db}} {
|
||||
#
|
||||
proc alter_table {tbl sql {file_format 2}} {
|
||||
sqlite3 dbat test.db
|
||||
puts one
|
||||
dbat eval {
|
||||
PRAGMA writable_schema = 1;
|
||||
UPDATE sqlite_master SET sql = $sql WHERE name = $tbl AND type = 'table';
|
||||
PRAGMA writable_schema = 0;
|
||||
}
|
||||
puts two
|
||||
dbat close
|
||||
puts three
|
||||
set_file_format 2
|
||||
puts four
|
||||
}
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
@@ -96,7 +92,6 @@ do_test alter2-1.2 {
|
||||
# ALTER TABLE abc ADD COLUMN c;
|
||||
alter_table abc {CREATE TABLE abc(a, b, c);}
|
||||
} {}
|
||||
exit
|
||||
do_test alter2-1.3 {
|
||||
execsql {
|
||||
SELECT * FROM abc;
|
||||
@@ -127,7 +122,7 @@ do_test alter2-1.8 {
|
||||
execsql {
|
||||
SELECT sum(a), c FROM abc GROUP BY c;
|
||||
}
|
||||
} {8.0 {} 1.0 10}
|
||||
} {8 {} 1 10}
|
||||
do_test alter2-1.9 {
|
||||
# ALTER TABLE abc ADD COLUMN d;
|
||||
alter_table abc {CREATE TABLE abc(a, b, c, d);}
|
||||
@@ -234,12 +229,12 @@ ifcapable trigger {
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
# Check that an error occurs if the database is upgraded to a file
|
||||
# format that SQLite does not support (in this case 4). Note: The
|
||||
# format that SQLite does not support (in this case 5). Note: The
|
||||
# file format is checked each time the schema is read, so changing the
|
||||
# file format requires incrementing the schema cookie.
|
||||
#
|
||||
do_test alter2-4.1 {
|
||||
set_file_format 4
|
||||
set_file_format 5
|
||||
} {}
|
||||
do_test alter2-4.2 {
|
||||
catchsql {
|
||||
@@ -341,7 +336,7 @@ do_test alter2-7.5 {
|
||||
execsql {
|
||||
SELECT a, typeof(a), b, typeof(b), c, typeof(c) FROM t1 LIMIT 1;
|
||||
}
|
||||
} {1 integer -123.0 real 5 text}
|
||||
} {1 integer -123 integer 5 text}
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
# Test that UPDATE trigger tables work with default values, and that when
|
||||
@@ -367,11 +362,11 @@ do_test alter2-8.2 {
|
||||
UPDATE t1 SET c = 10 WHERE a = 1;
|
||||
SELECT a, typeof(a), b, typeof(b), c, typeof(c) FROM t1 LIMIT 1;
|
||||
}
|
||||
} {1 integer -123.0 real 10 text}
|
||||
} {1 integer -123 integer 10 text}
|
||||
ifcapable trigger {
|
||||
do_test alter2-8.3 {
|
||||
set ::val
|
||||
} {-123 real 5 text -123 real 10 text}
|
||||
} {-123 integer 5 text -123 integer 10 text}
|
||||
}
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
@@ -395,7 +390,7 @@ ifcapable trigger {
|
||||
DELETE FROM t1 WHERE a = 2;
|
||||
}
|
||||
set ::val
|
||||
} {-123 real 5 text}
|
||||
} {-123 integer 5 text}
|
||||
}
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
# This file implements regression tests for SQLite library. The
|
||||
# focus of this script is btree database backend
|
||||
#
|
||||
# $Id: btree.test,v 1.37 2006/08/16 16:42:48 drh Exp $
|
||||
# $Id: btree.test,v 1.38 2007/01/03 23:37:29 drh Exp $
|
||||
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
@@ -548,7 +548,6 @@ do_test btree-8.1 {
|
||||
} {}
|
||||
btree_page_dump $::b1 1
|
||||
btree_page_dump $::b1 2
|
||||
btree_page_dump $::b1 3
|
||||
do_test btree-8.1.1 {
|
||||
lindex [btree_pager_stats $::b1] 1
|
||||
} {1}
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
# This file implements regression tests for SQLite library. The
|
||||
# focus of this script testing the callback-free C/C++ API.
|
||||
#
|
||||
# $Id: capi2.test,v 1.32 2006/08/16 16:42:48 drh Exp $
|
||||
# $Id: capi2.test,v 1.33 2007/01/03 23:37:29 drh Exp $
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
@@ -71,7 +71,7 @@ do_test capi2-1.6 {
|
||||
do_test capi2-1.7 {
|
||||
list [sqlite3_column_count $VM] [get_row_values $VM] [get_column_names $VM]
|
||||
} {2 {} {name rowid text INTEGER}}
|
||||
do_test capi2-1.8 {
|
||||
do_test capi2-1.8-misuse {
|
||||
sqlite3_step $VM
|
||||
} {SQLITE_MISUSE}
|
||||
|
||||
@@ -208,7 +208,7 @@ do_test capi2-3.11 {
|
||||
sqlite3_finalize $VM
|
||||
} {SQLITE_OK}
|
||||
do_test capi2-3.11b {db changes} {1}
|
||||
do_test capi2-3.12 {
|
||||
do_test capi2-3.12-misuse {
|
||||
sqlite3_finalize $VM
|
||||
} {SQLITE_MISUSE}
|
||||
do_test capi2-3.13 {
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
# This file implements regression tests for SQLite library. The
|
||||
# focus of this script testing the callback-free C/C++ API.
|
||||
#
|
||||
# $Id: capi3.test,v 1.46 2006/08/16 16:42:48 drh Exp $
|
||||
# $Id: capi3.test,v 1.47 2007/01/03 23:37:29 drh Exp $
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
@@ -152,14 +152,14 @@ do_test capi3-3.4 {
|
||||
do_test capi3-3.5 {
|
||||
sqlite3_close $db2
|
||||
} {SQLITE_OK}
|
||||
do_test capi3-3.6.1 {
|
||||
do_test capi3-3.6.1-misuse {
|
||||
sqlite3_close $db2
|
||||
} {SQLITE_MISUSE}
|
||||
do_test capi3-3.6.2 {
|
||||
do_test capi3-3.6.2-misuse {
|
||||
sqlite3_errmsg $db2
|
||||
} {library routine called out of sequence}
|
||||
ifcapable {utf16} {
|
||||
do_test capi3-3.6.3 {
|
||||
do_test capi3-3.6.3-misuse {
|
||||
utf8 [sqlite3_errmsg16 $db2]
|
||||
} {library routine called out of sequence}
|
||||
}
|
||||
@@ -612,7 +612,7 @@ check_data $STMT capi3-6.3 {INTEGER} {1} {1.0} {1}
|
||||
do_test capi3-6.3 {
|
||||
sqlite3_finalize $STMT
|
||||
} {SQLITE_OK}
|
||||
do_test capi3-6.4 {
|
||||
do_test capi3-6.4-misuse {
|
||||
db cache flush
|
||||
sqlite3_close $DB
|
||||
} {SQLITE_OK}
|
||||
@@ -991,7 +991,7 @@ if {[llength [info commands sqlite3_sleep]]>0} {
|
||||
|
||||
# Ticket #1219: Make sure binding APIs can handle a NULL pointer.
|
||||
#
|
||||
do_test capi3-14.1 {
|
||||
do_test capi3-14.1-misuse {
|
||||
set rc [catch {sqlite3_bind_text 0 1 hello 5} msg]
|
||||
lappend rc $msg
|
||||
} {1 SQLITE_MISUSE}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -12,7 +12,7 @@
|
||||
# This file implements regression tests for SQLite library. The
|
||||
# focus of this script is page cache subsystem.
|
||||
#
|
||||
# $Id: collate1.test,v 1.4 2005/11/01 15:48:25 drh Exp $
|
||||
# $Id: collate1.test,v 1.5 2007/02/01 23:02:46 drh Exp $
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
@@ -90,6 +90,21 @@ do_test collate1-1.4 {
|
||||
}
|
||||
} {{} 0x2D 0x119}
|
||||
do_test collate1-1.5 {
|
||||
execsql {
|
||||
SELECT c2 COLLATE hex FROM collate1t1 ORDER BY 1
|
||||
}
|
||||
} {{} 0x2D 0x119}
|
||||
do_test collate1-1.6 {
|
||||
execsql {
|
||||
SELECT c2 COLLATE hex FROM collate1t1 ORDER BY 1 ASC
|
||||
}
|
||||
} {{} 0x2D 0x119}
|
||||
do_test collate1-1.7 {
|
||||
execsql {
|
||||
SELECT c2 COLLATE hex FROM collate1t1 ORDER BY 1 DESC
|
||||
}
|
||||
} {0x119 0x2D {}}
|
||||
do_test collate1-1.99 {
|
||||
execsql {
|
||||
DROP TABLE collate1t1;
|
||||
}
|
||||
@@ -133,7 +148,59 @@ do_test collate1-2.6 {
|
||||
ORDER BY 1 COLLATE binary ASC, 2 COLLATE hex ASC;
|
||||
}
|
||||
} {{} {} 11 0x11 11 0x101 5 0xA 5 0x11 7 0xA}
|
||||
do_test collate1-2.7 {
|
||||
do_test collate1-2.12.1 {
|
||||
execsql {
|
||||
SELECT c1 COLLATE numeric, c2 FROM collate1t1
|
||||
ORDER BY 1, 2 COLLATE hex;
|
||||
}
|
||||
} {{} {} 5 0xA 5 0x11 7 0xA 11 0x11 11 0x101}
|
||||
do_test collate1-2.12.2 {
|
||||
execsql {
|
||||
SELECT c1 COLLATE hex, c2 FROM collate1t1
|
||||
ORDER BY 1 COLLATE numeric, 2 COLLATE hex;
|
||||
}
|
||||
} {{} {} 5 0xA 5 0x11 7 0xA 11 0x11 11 0x101}
|
||||
do_test collate1-2.12.3 {
|
||||
execsql {
|
||||
SELECT c1, c2 COLLATE hex FROM collate1t1
|
||||
ORDER BY 1 COLLATE numeric, 2;
|
||||
}
|
||||
} {{} {} 5 0xA 5 0x11 7 0xA 11 0x11 11 0x101}
|
||||
do_test collate1-2.12.4 {
|
||||
execsql {
|
||||
SELECT c1 COLLATE numeric, c2 COLLATE hex
|
||||
FROM collate1t1
|
||||
ORDER BY 1, 2;
|
||||
}
|
||||
} {{} {} 5 0xA 5 0x11 7 0xA 11 0x11 11 0x101}
|
||||
do_test collate1-2.13 {
|
||||
execsql {
|
||||
SELECT c1 COLLATE binary, c2 COLLATE hex
|
||||
FROM collate1t1
|
||||
ORDER BY 1, 2;
|
||||
}
|
||||
} {{} {} 11 0x11 11 0x101 5 0xA 5 0x11 7 0xA}
|
||||
do_test collate1-2.14 {
|
||||
execsql {
|
||||
SELECT c1, c2
|
||||
FROM collate1t1 ORDER BY 1 COLLATE binary DESC, 2 COLLATE hex;
|
||||
}
|
||||
} {7 0xA 5 0xA 5 0x11 11 0x11 11 0x101 {} {}}
|
||||
do_test collate1-2.15 {
|
||||
execsql {
|
||||
SELECT c1 COLLATE binary, c2 COLLATE hex
|
||||
FROM collate1t1
|
||||
ORDER BY 1 DESC, 2 DESC;
|
||||
}
|
||||
} {7 0xA 5 0x11 5 0xA 11 0x101 11 0x11 {} {}}
|
||||
do_test collate1-2.16 {
|
||||
execsql {
|
||||
SELECT c1 COLLATE hex, c2 COLLATE binary
|
||||
FROM collate1t1
|
||||
ORDER BY 1 COLLATE binary ASC, 2 COLLATE hex ASC;
|
||||
}
|
||||
} {{} {} 11 0x11 11 0x101 5 0xA 5 0x11 7 0xA}
|
||||
do_test collate1-2.99 {
|
||||
execsql {
|
||||
DROP TABLE collate1t1;
|
||||
}
|
||||
@@ -180,6 +247,12 @@ do_test collate1-3.5 {
|
||||
SELECT a as c1, b as c2 FROM collate1t1 ORDER BY c1 COLLATE binary;
|
||||
}
|
||||
} {{} {} 0x45 69 0x5 5 1 1}
|
||||
do_test collate1-3.5.1 {
|
||||
execsql {
|
||||
SELECT a COLLATE binary as c1, b as c2
|
||||
FROM collate1t1 ORDER BY c1;
|
||||
}
|
||||
} {{} {} 0x45 69 0x5 5 1 1}
|
||||
do_test collate1-3.6 {
|
||||
execsql {
|
||||
DROP TABLE collate1t1;
|
||||
@@ -220,6 +293,11 @@ do_test collate1-4.4 {
|
||||
SELECT c1||'' FROM collate1t1 ORDER BY 1;
|
||||
}
|
||||
} {{} 1 101 12}
|
||||
do_test collate1-4.4.1 {
|
||||
execsql {
|
||||
SELECT (c1||'') COLLATE numeric FROM collate1t1 ORDER BY 1;
|
||||
}
|
||||
} {{} 1 12 101}
|
||||
do_test collate1-4.5 {
|
||||
execsql {
|
||||
DROP TABLE collate1t1;
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
# This file implements regression tests for SQLite library. The
|
||||
# focus of this script is page cache subsystem.
|
||||
#
|
||||
# $Id: collate2.test,v 1.4 2005/01/21 03:12:16 danielk1977 Exp $
|
||||
# $Id: collate2.test,v 1.5 2007/02/01 23:02:46 drh Exp $
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
@@ -98,16 +98,67 @@ do_test collate2-1.1 {
|
||||
SELECT a FROM collate2t1 WHERE a > 'aa' ORDER BY 1;
|
||||
}
|
||||
} {ab bA bB ba bb}
|
||||
do_test collate2-1.1.1 {
|
||||
execsql {
|
||||
SELECT a FROM collate2t1 WHERE a COLLATE binary > 'aa' ORDER BY 1;
|
||||
}
|
||||
} {ab bA bB ba bb}
|
||||
do_test collate2-1.1.2 {
|
||||
execsql {
|
||||
SELECT a FROM collate2t1 WHERE b COLLATE binary > 'aa' ORDER BY 1;
|
||||
}
|
||||
} {ab bA bB ba bb}
|
||||
do_test collate2-1.1.3 {
|
||||
execsql {
|
||||
SELECT a FROM collate2t1 WHERE c COLLATE binary > 'aa' ORDER BY 1;
|
||||
}
|
||||
} {ab bA bB ba bb}
|
||||
do_test collate2-1.2 {
|
||||
execsql {
|
||||
SELECT b FROM collate2t1 WHERE b > 'aa' ORDER BY 1, oid;
|
||||
}
|
||||
} {ab aB Ab AB ba bA Ba BA bb bB Bb BB}
|
||||
do_test collate2-1.2.1 {
|
||||
execsql {
|
||||
SELECT b FROM collate2t1 WHERE a COLLATE nocase > 'aa'
|
||||
ORDER BY 1, oid;
|
||||
}
|
||||
} {ab aB Ab AB ba bA Ba BA bb bB Bb BB}
|
||||
do_test collate2-1.2.2 {
|
||||
execsql {
|
||||
SELECT b FROM collate2t1 WHERE b COLLATE nocase > 'aa'
|
||||
ORDER BY 1, oid;
|
||||
}
|
||||
} {ab aB Ab AB ba bA Ba BA bb bB Bb BB}
|
||||
do_test collate2-1.2.3 {
|
||||
execsql {
|
||||
SELECT b FROM collate2t1 WHERE c COLLATE nocase > 'aa'
|
||||
ORDER BY 1, oid;
|
||||
}
|
||||
} {ab aB Ab AB ba bA Ba BA bb bB Bb BB}
|
||||
do_test collate2-1.3 {
|
||||
execsql {
|
||||
SELECT c FROM collate2t1 WHERE c > 'aa' ORDER BY 1;
|
||||
}
|
||||
} {ba Ab Bb ab bb}
|
||||
do_test collate2-1.3.1 {
|
||||
execsql {
|
||||
SELECT c FROM collate2t1 WHERE a COLLATE backwards > 'aa'
|
||||
ORDER BY 1;
|
||||
}
|
||||
} {ba Ab Bb ab bb}
|
||||
do_test collate2-1.3.2 {
|
||||
execsql {
|
||||
SELECT c FROM collate2t1 WHERE b COLLATE backwards > 'aa'
|
||||
ORDER BY 1;
|
||||
}
|
||||
} {ba Ab Bb ab bb}
|
||||
do_test collate2-1.3.3 {
|
||||
execsql {
|
||||
SELECT c FROM collate2t1 WHERE c COLLATE backwards > 'aa'
|
||||
ORDER BY 1;
|
||||
}
|
||||
} {ba Ab Bb ab bb}
|
||||
do_test collate2-1.4 {
|
||||
execsql {
|
||||
SELECT a FROM collate2t1 WHERE a < 'aa' ORDER BY 1;
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
# This file implements tests for the conflict resolution extension
|
||||
# to SQLite.
|
||||
#
|
||||
# $Id: conflict.test,v 1.27 2006/01/17 09:35:02 danielk1977 Exp $
|
||||
# $Id: conflict.test,v 1.28 2007/01/03 23:37:29 drh Exp $
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
@@ -309,6 +309,7 @@ foreach {i conf1 cmd t0 t1 t2 t3} {
|
||||
if {$conf1!=""} {set conf1 "ON CONFLICT $conf1"}
|
||||
execsql {pragma temp_store=file}
|
||||
set ::sqlite_opentemp_count 0
|
||||
if {$i==2} btree_breakpoint
|
||||
set r0 [catch {execsql [subst {
|
||||
DROP TABLE t1;
|
||||
CREATE TABLE t1(a,b,c, UNIQUE(a) $conf1);
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
# This file implements regression tests for SQLite library. The
|
||||
# focus of this file is testing date and time functions.
|
||||
#
|
||||
# $Id: date.test,v 1.17 2006/09/25 18:03:29 drh Exp $
|
||||
# $Id: date.test,v 1.19 2007/01/08 16:19:07 drh Exp $
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
@@ -123,6 +123,17 @@ datetest 3.11.11 {strftime('%W','2004-07-18')} 28
|
||||
datetest 3.11.12 {strftime('%W','2004-12-31')} 52
|
||||
datetest 3.11.13 {strftime('%W','2007-12-31')} 53
|
||||
datetest 3.11.14 {strftime('%W','2007-01-01')} 01
|
||||
datetest 3.11.15 {strftime('%W %j',2454109.04140970)} {02 008}
|
||||
datetest 3.11.16 {strftime('%W %j',2454109.04140971)} {02 008}
|
||||
datetest 3.11.17 {strftime('%W %j',2454109.04140972)} {02 008}
|
||||
datetest 3.11.18 {strftime('%W %j',2454109.04140973)} {02 008}
|
||||
datetest 3.11.19 {strftime('%W %j',2454109.04140974)} {02 008}
|
||||
datetest 3.11.20 {strftime('%W %j',2454109.04140975)} {02 008}
|
||||
datetest 3.11.21 {strftime('%W %j',2454109.04140976)} {02 008}
|
||||
datetest 3.11.22 {strftime('%W %j',2454109.04140977)} {02 008}
|
||||
datetest 3.11.22 {strftime('%W %j',2454109.04140978)} {02 008}
|
||||
datetest 3.11.22 {strftime('%W %j',2454109.04140979)} {02 008}
|
||||
datetest 3.11.22 {strftime('%W %j',2454109.04140980)} {02 008}
|
||||
datetest 3.12 {strftime('%Y','2003-10-31 12:34:56.432')} 2003
|
||||
datetest 3.13 {strftime('%%','2003-10-31 12:34:56.432')} %
|
||||
datetest 3.14 {strftime('%_','2003-10-31 12:34:56.432')} NULL
|
||||
@@ -284,5 +295,19 @@ do_test date-13.1 {
|
||||
}
|
||||
} {{2006-09-24 10:50:26.047}}
|
||||
|
||||
# Ticket #2153
|
||||
datetest 13.2 {strftime('%Y-%m-%d %H:%M:%S', '2007-01-01 12:34:59.6')} \
|
||||
{2007-01-01 12:34:59}
|
||||
datetest 13.3 {strftime('%Y-%m-%d %H:%M:%f', '2007-01-01 12:34:59.6')} \
|
||||
{2007-01-01 12:34:59.600}
|
||||
datetest 13.4 {strftime('%Y-%m-%d %H:%M:%S', '2007-01-01 12:59:59.6')} \
|
||||
{2007-01-01 12:59:59}
|
||||
datetest 13.5 {strftime('%Y-%m-%d %H:%M:%f', '2007-01-01 12:59:59.6')} \
|
||||
{2007-01-01 12:59:59.600}
|
||||
datetest 13.6 {strftime('%Y-%m-%d %H:%M:%S', '2007-01-01 23:59:59.6')} \
|
||||
{2007-01-01 23:59:59}
|
||||
datetest 13.7 {strftime('%Y-%m-%d %H:%M:%f', '2007-01-01 23:59:59.6')} \
|
||||
{2007-01-01 23:59:59.600}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
# 2006 October 19
|
||||
#
|
||||
# The author disclaims copyright to this source code.
|
||||
#
|
||||
#*************************************************************************
|
||||
# This file implements regression tests for SQLite library. The
|
||||
# focus of this script is testing deletions in the FTS1 module.
|
||||
#
|
||||
# $Id: fts1e.test,v 1.1 2006/10/19 23:28:35 shess Exp $
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
# If SQLITE_ENABLE_FTS1 is defined, omit this file.
|
||||
ifcapable !fts1 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
# Construct a full-text search table containing keywords which are the
|
||||
# ordinal numbers of the bit positions set for a sequence of integers,
|
||||
# which are used for the rowid. There are a total of 30 INSERT and
|
||||
# DELETE statements, so that we'll test both the segmentMerge() merge
|
||||
# (over the first 16) and the termSelect() merge (over the level-1
|
||||
# segment and 14 level-0 segments).
|
||||
db eval {
|
||||
CREATE VIRTUAL TABLE t1 USING fts1(content);
|
||||
INSERT INTO t1 (rowid, content) VALUES(1, 'one');
|
||||
INSERT INTO t1 (rowid, content) VALUES(2, 'two');
|
||||
INSERT INTO t1 (rowid, content) VALUES(3, 'one two');
|
||||
INSERT INTO t1 (rowid, content) VALUES(4, 'three');
|
||||
DELETE FROM t1 WHERE rowid = 1;
|
||||
INSERT INTO t1 (rowid, content) VALUES(5, 'one three');
|
||||
INSERT INTO t1 (rowid, content) VALUES(6, 'two three');
|
||||
INSERT INTO t1 (rowid, content) VALUES(7, 'one two three');
|
||||
DELETE FROM t1 WHERE rowid = 4;
|
||||
INSERT INTO t1 (rowid, content) VALUES(8, 'four');
|
||||
INSERT INTO t1 (rowid, content) VALUES(9, 'one four');
|
||||
INSERT INTO t1 (rowid, content) VALUES(10, 'two four');
|
||||
DELETE FROM t1 WHERE rowid = 7;
|
||||
INSERT INTO t1 (rowid, content) VALUES(11, 'one two four');
|
||||
INSERT INTO t1 (rowid, content) VALUES(12, 'three four');
|
||||
INSERT INTO t1 (rowid, content) VALUES(13, 'one three four');
|
||||
DELETE FROM t1 WHERE rowid = 10;
|
||||
INSERT INTO t1 (rowid, content) VALUES(14, 'two three four');
|
||||
INSERT INTO t1 (rowid, content) VALUES(15, 'one two three four');
|
||||
INSERT INTO t1 (rowid, content) VALUES(16, 'five');
|
||||
DELETE FROM t1 WHERE rowid = 13;
|
||||
INSERT INTO t1 (rowid, content) VALUES(17, 'one five');
|
||||
INSERT INTO t1 (rowid, content) VALUES(18, 'two five');
|
||||
INSERT INTO t1 (rowid, content) VALUES(19, 'one two five');
|
||||
DELETE FROM t1 WHERE rowid = 16;
|
||||
INSERT INTO t1 (rowid, content) VALUES(20, 'three five');
|
||||
INSERT INTO t1 (rowid, content) VALUES(21, 'one three five');
|
||||
INSERT INTO t1 (rowid, content) VALUES(22, 'two three five');
|
||||
DELETE FROM t1 WHERE rowid = 19;
|
||||
DELETE FROM t1 WHERE rowid = 22;
|
||||
}
|
||||
|
||||
do_test fts1f-1.1 {
|
||||
execsql {SELECT COUNT(*) FROM t1}
|
||||
} {14}
|
||||
|
||||
do_test fts1e-2.1 {
|
||||
execsql {SELECT rowid FROM t1 WHERE content MATCH 'one'}
|
||||
} {3 5 9 11 15 17 21}
|
||||
|
||||
do_test fts1e-2.2 {
|
||||
execsql {SELECT rowid FROM t1 WHERE content MATCH 'two'}
|
||||
} {2 3 6 11 14 15 18}
|
||||
|
||||
do_test fts1e-2.3 {
|
||||
execsql {SELECT rowid FROM t1 WHERE content MATCH 'three'}
|
||||
} {5 6 12 14 15 20 21}
|
||||
|
||||
do_test fts1e-2.4 {
|
||||
execsql {SELECT rowid FROM t1 WHERE content MATCH 'four'}
|
||||
} {8 9 11 12 14 15}
|
||||
|
||||
do_test fts1e-2.5 {
|
||||
execsql {SELECT rowid FROM t1 WHERE content MATCH 'five'}
|
||||
} {17 18 20 21}
|
||||
|
||||
finish_test
|
||||
@@ -0,0 +1,90 @@
|
||||
# 2006 October 19
|
||||
#
|
||||
# The author disclaims copyright to this source code.
|
||||
#
|
||||
#*************************************************************************
|
||||
# This file implements regression tests for SQLite library. The
|
||||
# focus of this script is testing updates in the FTS1 module.
|
||||
#
|
||||
# $Id: fts1f.test,v 1.1 2006/10/19 23:28:35 shess Exp $
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
# If SQLITE_ENABLE_FTS1 is defined, omit this file.
|
||||
ifcapable !fts1 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
# Construct a full-text search table containing keywords which are the
|
||||
# ordinal numbers of the bit positions set for a sequence of integers,
|
||||
# which are used for the rowid. There are a total of 31 INSERT,
|
||||
# UPDATE, and DELETE statements, so that we'll test both the
|
||||
# segmentMerge() merge (over the first 16) and the termSelect() merge
|
||||
# (over the level-1 segment and 15 level-0 segments).
|
||||
db eval {
|
||||
CREATE VIRTUAL TABLE t1 USING fts1(content);
|
||||
INSERT INTO t1 (rowid, content) VALUES(1, 'one');
|
||||
INSERT INTO t1 (rowid, content) VALUES(2, 'two');
|
||||
INSERT INTO t1 (rowid, content) VALUES(3, 'one two');
|
||||
INSERT INTO t1 (rowid, content) VALUES(4, 'three');
|
||||
INSERT INTO t1 (rowid, content) VALUES(5, 'one three');
|
||||
INSERT INTO t1 (rowid, content) VALUES(6, 'two three');
|
||||
INSERT INTO t1 (rowid, content) VALUES(7, 'one two three');
|
||||
DELETE FROM t1 WHERE rowid = 4;
|
||||
INSERT INTO t1 (rowid, content) VALUES(8, 'four');
|
||||
UPDATE t1 SET content = 'update one three' WHERE rowid = 1;
|
||||
INSERT INTO t1 (rowid, content) VALUES(9, 'one four');
|
||||
INSERT INTO t1 (rowid, content) VALUES(10, 'two four');
|
||||
DELETE FROM t1 WHERE rowid = 7;
|
||||
INSERT INTO t1 (rowid, content) VALUES(11, 'one two four');
|
||||
INSERT INTO t1 (rowid, content) VALUES(12, 'three four');
|
||||
INSERT INTO t1 (rowid, content) VALUES(13, 'one three four');
|
||||
DELETE FROM t1 WHERE rowid = 10;
|
||||
INSERT INTO t1 (rowid, content) VALUES(14, 'two three four');
|
||||
INSERT INTO t1 (rowid, content) VALUES(15, 'one two three four');
|
||||
UPDATE t1 SET content = 'update two five' WHERE rowid = 8;
|
||||
INSERT INTO t1 (rowid, content) VALUES(16, 'five');
|
||||
DELETE FROM t1 WHERE rowid = 13;
|
||||
INSERT INTO t1 (rowid, content) VALUES(17, 'one five');
|
||||
INSERT INTO t1 (rowid, content) VALUES(18, 'two five');
|
||||
INSERT INTO t1 (rowid, content) VALUES(19, 'one two five');
|
||||
DELETE FROM t1 WHERE rowid = 16;
|
||||
INSERT INTO t1 (rowid, content) VALUES(20, 'three five');
|
||||
INSERT INTO t1 (rowid, content) VALUES(21, 'one three five');
|
||||
INSERT INTO t1 (rowid, content) VALUES(22, 'two three five');
|
||||
DELETE FROM t1 WHERE rowid = 19;
|
||||
UPDATE t1 SET content = 'update' WHERE rowid = 15;
|
||||
}
|
||||
|
||||
do_test fts1f-1.1 {
|
||||
execsql {SELECT COUNT(*) FROM t1}
|
||||
} {16}
|
||||
|
||||
do_test fts1e-2.0 {
|
||||
execsql {SELECT rowid FROM t1 WHERE content MATCH 'update'}
|
||||
} {1 8 15}
|
||||
|
||||
do_test fts1e-2.1 {
|
||||
execsql {SELECT rowid FROM t1 WHERE content MATCH 'one'}
|
||||
} {1 3 5 9 11 17 21}
|
||||
|
||||
do_test fts1e-2.2 {
|
||||
execsql {SELECT rowid FROM t1 WHERE content MATCH 'two'}
|
||||
} {2 3 6 8 11 14 18 22}
|
||||
|
||||
do_test fts1e-2.3 {
|
||||
execsql {SELECT rowid FROM t1 WHERE content MATCH 'three'}
|
||||
} {1 5 6 12 14 20 21 22}
|
||||
|
||||
do_test fts1e-2.4 {
|
||||
execsql {SELECT rowid FROM t1 WHERE content MATCH 'four'}
|
||||
} {9 11 12 14}
|
||||
|
||||
do_test fts1e-2.5 {
|
||||
execsql {SELECT rowid FROM t1 WHERE content MATCH 'five'}
|
||||
} {8 17 18 20 21 22}
|
||||
|
||||
finish_test
|
||||
@@ -0,0 +1,88 @@
|
||||
# 2007 January 17
|
||||
#
|
||||
# The author disclaims copyright to this source code.
|
||||
#
|
||||
#*************************************************************************
|
||||
# This file implements regression tests for SQLite fts1 library. The
|
||||
# focus here is testing handling of UPDATE when using UTF-16-encoded
|
||||
# databases.
|
||||
#
|
||||
# $Id: fts1i.test,v 1.2 2007/01/24 03:43:20 drh Exp $
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
# If SQLITE_ENABLE_FTS1 is defined, omit this file.
|
||||
ifcapable !fts1 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
# Return the UTF-16 representation of the supplied UTF-8 string $str.
|
||||
# If $nt is true, append two 0x00 bytes as a nul terminator.
|
||||
# NOTE(shess) Copied from capi3.test.
|
||||
proc utf16 {str {nt 1}} {
|
||||
set r [encoding convertto unicode $str]
|
||||
if {$nt} {
|
||||
append r "\x00\x00"
|
||||
}
|
||||
return $r
|
||||
}
|
||||
|
||||
db eval {
|
||||
PRAGMA encoding = "UTF-16le";
|
||||
CREATE VIRTUAL TABLE t1 USING fts1(content);
|
||||
}
|
||||
|
||||
do_test fts1i-1.0 {
|
||||
execsql {PRAGMA encoding}
|
||||
} {UTF-16le}
|
||||
|
||||
do_test fts1i-1.1 {
|
||||
execsql {INSERT INTO t1 (rowid, content) VALUES(1, 'one')}
|
||||
execsql {SELECT content FROM t1 WHERE rowid = 1}
|
||||
} {one}
|
||||
|
||||
do_test fts1i-1.2 {
|
||||
set sql "INSERT INTO t1 (rowid, content) VALUES(2, 'two')"
|
||||
set STMT [sqlite3_prepare $DB $sql -1 TAIL]
|
||||
sqlite3_step $STMT
|
||||
sqlite3_finalize $STMT
|
||||
execsql {SELECT content FROM t1 WHERE rowid = 2}
|
||||
} {two}
|
||||
|
||||
do_test fts1i-1.3 {
|
||||
set sql "INSERT INTO t1 (rowid, content) VALUES(3, 'three')"
|
||||
set STMT [sqlite3_prepare $DB $sql -1 TAIL]
|
||||
sqlite3_step $STMT
|
||||
sqlite3_finalize $STMT
|
||||
set sql "UPDATE t1 SET content = 'trois' WHERE rowid = 3"
|
||||
set STMT [sqlite3_prepare $DB $sql -1 TAIL]
|
||||
sqlite3_step $STMT
|
||||
sqlite3_finalize $STMT
|
||||
execsql {SELECT content FROM t1 WHERE rowid = 3}
|
||||
} {trois}
|
||||
|
||||
do_test fts1i-1.4 {
|
||||
set sql16 [utf16 {INSERT INTO t1 (rowid, content) VALUES(4, 'four')}]
|
||||
set STMT [sqlite3_prepare16 $DB $sql16 -1 TAIL]
|
||||
sqlite3_step $STMT
|
||||
sqlite3_finalize $STMT
|
||||
execsql {SELECT content FROM t1 WHERE rowid = 4}
|
||||
} {four}
|
||||
|
||||
do_test fts1i-1.5 {
|
||||
set sql16 [utf16 {INSERT INTO t1 (rowid, content) VALUES(5, 'five')}]
|
||||
set STMT [sqlite3_prepare16 $DB $sql16 -1 TAIL]
|
||||
sqlite3_step $STMT
|
||||
sqlite3_finalize $STMT
|
||||
set sql "UPDATE t1 SET content = 'cinq' WHERE rowid = 5"
|
||||
set STMT [sqlite3_prepare $DB $sql -1 TAIL]
|
||||
sqlite3_step $STMT
|
||||
sqlite3_finalize $STMT
|
||||
execsql {SELECT content FROM t1 WHERE rowid = 5}
|
||||
} {cinq}
|
||||
|
||||
finish_test
|
||||
@@ -0,0 +1,89 @@
|
||||
# 2007 February 6
|
||||
#
|
||||
# The author disclaims copyright to this source code.
|
||||
#
|
||||
#*************************************************************************
|
||||
# This file implements regression tests for SQLite library. This
|
||||
# tests creating fts1 tables in an attached database.
|
||||
#
|
||||
# $Id: fts1j.test,v 1.1 2007/02/07 01:01:18 shess Exp $
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
# If SQLITE_ENABLE_FTS1 is defined, omit this file.
|
||||
ifcapable !fts1 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
# Clean up anything left over from a previous pass.
|
||||
file delete -force test2.db
|
||||
file delete -force test2.db-journal
|
||||
sqlite3 db2 test2.db
|
||||
|
||||
db eval {
|
||||
CREATE VIRTUAL TABLE t3 USING fts1(content);
|
||||
INSERT INTO t3 (rowid, content) VALUES(1, "hello world");
|
||||
}
|
||||
|
||||
db2 eval {
|
||||
CREATE VIRTUAL TABLE t1 USING fts1(content);
|
||||
INSERT INTO t1 (rowid, content) VALUES(1, "hello world");
|
||||
INSERT INTO t1 (rowid, content) VALUES(2, "hello there");
|
||||
INSERT INTO t1 (rowid, content) VALUES(3, "cruel world");
|
||||
}
|
||||
|
||||
# This has always worked because the t1_* tables used by fts1 will be
|
||||
# the defaults.
|
||||
do_test fts1j-1.1 {
|
||||
execsql {
|
||||
ATTACH DATABASE 'test2.db' AS two;
|
||||
SELECT rowid FROM t1 WHERE t1 MATCH 'hello';
|
||||
DETACH DATABASE two;
|
||||
}
|
||||
} {1 2}
|
||||
# Make certain we're detached if there was an error.
|
||||
catch {db eval {DETACH DATABASE two}}
|
||||
|
||||
# In older code, this appears to work fine, but the t2_* tables used
|
||||
# by fts1 will be created in database 'main' instead of database
|
||||
# 'two'. It appears to work fine because the tables end up being the
|
||||
# defaults, but obviously is badly broken if you hope to use things
|
||||
# other than in the exact same ATTACH setup.
|
||||
do_test fts1j-1.2 {
|
||||
execsql {
|
||||
ATTACH DATABASE 'test2.db' AS two;
|
||||
CREATE VIRTUAL TABLE two.t2 USING fts1(content);
|
||||
INSERT INTO t2 (rowid, content) VALUES(1, "hello world");
|
||||
INSERT INTO t2 (rowid, content) VALUES(2, "hello there");
|
||||
INSERT INTO t2 (rowid, content) VALUES(3, "cruel world");
|
||||
SELECT rowid FROM t2 WHERE t2 MATCH 'hello';
|
||||
DETACH DATABASE two;
|
||||
}
|
||||
} {1 2}
|
||||
catch {db eval {DETACH DATABASE two}}
|
||||
|
||||
# In older code, this broke because the fts1 code attempted to create
|
||||
# t3_* tables in database 'main', but they already existed. Normally
|
||||
# this wouldn't happen without t3 itself existing, in which case the
|
||||
# fts1 code would never be called in the first place.
|
||||
do_test fts1j-1.3 {
|
||||
execsql {
|
||||
ATTACH DATABASE 'test2.db' AS two;
|
||||
|
||||
CREATE VIRTUAL TABLE two.t3 USING fts1(content);
|
||||
INSERT INTO two.t3 (rowid, content) VALUES(2, "hello there");
|
||||
INSERT INTO two.t3 (rowid, content) VALUES(3, "cruel world");
|
||||
SELECT rowid FROM two.t3 WHERE t3 MATCH 'hello';
|
||||
|
||||
DETACH DATABASE two;
|
||||
} db2
|
||||
} {2}
|
||||
catch {db eval {DETACH DATABASE two}}
|
||||
|
||||
catch {db2 close}
|
||||
file delete -force test2.db
|
||||
|
||||
finish_test
|
||||
@@ -0,0 +1,186 @@
|
||||
# 2006 September 9
|
||||
#
|
||||
# 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 implements regression tests for SQLite library. The
|
||||
# focus of this script is testing the FTS2 module.
|
||||
#
|
||||
# $Id: fts2a.test,v 1.1 2006/10/19 23:36:26 shess Exp $
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
# If SQLITE_ENABLE_FTS2 is defined, omit this file.
|
||||
ifcapable !fts2 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
# Construct a full-text search table containing five keywords:
|
||||
# one, two, three, four, and five, in various combinations. The
|
||||
# rowid for each will be a bitmask for the elements it contains.
|
||||
#
|
||||
db eval {
|
||||
CREATE VIRTUAL TABLE t1 USING fts2(content);
|
||||
INSERT INTO t1(content) VALUES('one');
|
||||
INSERT INTO t1(content) VALUES('two');
|
||||
INSERT INTO t1(content) VALUES('one two');
|
||||
INSERT INTO t1(content) VALUES('three');
|
||||
INSERT INTO t1(content) VALUES('one three');
|
||||
INSERT INTO t1(content) VALUES('two three');
|
||||
INSERT INTO t1(content) VALUES('one two three');
|
||||
INSERT INTO t1(content) VALUES('four');
|
||||
INSERT INTO t1(content) VALUES('one four');
|
||||
INSERT INTO t1(content) VALUES('two four');
|
||||
INSERT INTO t1(content) VALUES('one two four');
|
||||
INSERT INTO t1(content) VALUES('three four');
|
||||
INSERT INTO t1(content) VALUES('one three four');
|
||||
INSERT INTO t1(content) VALUES('two three four');
|
||||
INSERT INTO t1(content) VALUES('one two three four');
|
||||
INSERT INTO t1(content) VALUES('five');
|
||||
INSERT INTO t1(content) VALUES('one five');
|
||||
INSERT INTO t1(content) VALUES('two five');
|
||||
INSERT INTO t1(content) VALUES('one two five');
|
||||
INSERT INTO t1(content) VALUES('three five');
|
||||
INSERT INTO t1(content) VALUES('one three five');
|
||||
INSERT INTO t1(content) VALUES('two three five');
|
||||
INSERT INTO t1(content) VALUES('one two three five');
|
||||
INSERT INTO t1(content) VALUES('four five');
|
||||
INSERT INTO t1(content) VALUES('one four five');
|
||||
INSERT INTO t1(content) VALUES('two four five');
|
||||
INSERT INTO t1(content) VALUES('one two four five');
|
||||
INSERT INTO t1(content) VALUES('three four five');
|
||||
INSERT INTO t1(content) VALUES('one three four five');
|
||||
INSERT INTO t1(content) VALUES('two three four five');
|
||||
INSERT INTO t1(content) VALUES('one two three four five');
|
||||
}
|
||||
|
||||
do_test fts2a-1.1 {
|
||||
execsql {SELECT rowid FROM t1 WHERE content MATCH 'one'}
|
||||
} {1 3 5 7 9 11 13 15 17 19 21 23 25 27 29 31}
|
||||
do_test fts2a-1.2 {
|
||||
execsql {SELECT rowid FROM t1 WHERE content MATCH 'one two'}
|
||||
} {3 7 11 15 19 23 27 31}
|
||||
do_test fts2a-1.3 {
|
||||
execsql {SELECT rowid FROM t1 WHERE content MATCH 'two one'}
|
||||
} {3 7 11 15 19 23 27 31}
|
||||
do_test fts2a-1.4 {
|
||||
execsql {SELECT rowid FROM t1 WHERE content MATCH 'one two three'}
|
||||
} {7 15 23 31}
|
||||
do_test fts2a-1.5 {
|
||||
execsql {SELECT rowid FROM t1 WHERE content MATCH 'one three two'}
|
||||
} {7 15 23 31}
|
||||
do_test fts2a-1.6 {
|
||||
execsql {SELECT rowid FROM t1 WHERE content MATCH 'two three one'}
|
||||
} {7 15 23 31}
|
||||
do_test fts2a-1.7 {
|
||||
execsql {SELECT rowid FROM t1 WHERE content MATCH 'two one three'}
|
||||
} {7 15 23 31}
|
||||
do_test fts2a-1.8 {
|
||||
execsql {SELECT rowid FROM t1 WHERE content MATCH 'three one two'}
|
||||
} {7 15 23 31}
|
||||
do_test fts2a-1.9 {
|
||||
execsql {SELECT rowid FROM t1 WHERE content MATCH 'three two one'}
|
||||
} {7 15 23 31}
|
||||
do_test fts2a-1.10 {
|
||||
execsql {SELECT rowid FROM t1 WHERE content MATCH 'one two THREE'}
|
||||
} {7 15 23 31}
|
||||
do_test fts2a-1.11 {
|
||||
execsql {SELECT rowid FROM t1 WHERE content MATCH ' ONE Two three '}
|
||||
} {7 15 23 31}
|
||||
|
||||
do_test fts2a-2.1 {
|
||||
execsql {SELECT rowid FROM t1 WHERE content MATCH '"one"'}
|
||||
} {1 3 5 7 9 11 13 15 17 19 21 23 25 27 29 31}
|
||||
do_test fts2a-2.2 {
|
||||
execsql {SELECT rowid FROM t1 WHERE content MATCH '"one two"'}
|
||||
} {3 7 11 15 19 23 27 31}
|
||||
do_test fts2a-2.3 {
|
||||
execsql {SELECT rowid FROM t1 WHERE content MATCH '"two one"'}
|
||||
} {}
|
||||
do_test fts2a-2.4 {
|
||||
execsql {SELECT rowid FROM t1 WHERE content MATCH '"one two three"'}
|
||||
} {7 15 23 31}
|
||||
do_test fts2a-2.5 {
|
||||
execsql {SELECT rowid FROM t1 WHERE content MATCH '"one three two"'}
|
||||
} {}
|
||||
do_test fts2a-2.6 {
|
||||
execsql {SELECT rowid FROM t1 WHERE content MATCH '"one two three four"'}
|
||||
} {15 31}
|
||||
do_test fts2a-2.7 {
|
||||
execsql {SELECT rowid FROM t1 WHERE content MATCH '"one three two four"'}
|
||||
} {}
|
||||
do_test fts2a-2.8 {
|
||||
execsql {SELECT rowid FROM t1 WHERE content MATCH '"one three five"'}
|
||||
} {21}
|
||||
do_test fts2a-2.9 {
|
||||
execsql {SELECT rowid FROM t1 WHERE content MATCH '"one three" five'}
|
||||
} {21 29}
|
||||
do_test fts2a-2.10 {
|
||||
execsql {SELECT rowid FROM t1 WHERE content MATCH 'five "one three"'}
|
||||
} {21 29}
|
||||
do_test fts2a-2.11 {
|
||||
execsql {SELECT rowid FROM t1 WHERE content MATCH 'five "one three" four'}
|
||||
} {29}
|
||||
do_test fts2a-2.12 {
|
||||
execsql {SELECT rowid FROM t1 WHERE content MATCH 'five four "one three"'}
|
||||
} {29}
|
||||
do_test fts2a-2.13 {
|
||||
execsql {SELECT rowid FROM t1 WHERE content MATCH '"one three" four five'}
|
||||
} {29}
|
||||
|
||||
do_test fts2a-3.1 {
|
||||
execsql {SELECT rowid FROM t1 WHERE content MATCH 'one'}
|
||||
} {1 3 5 7 9 11 13 15 17 19 21 23 25 27 29 31}
|
||||
do_test fts2a-3.2 {
|
||||
execsql {SELECT rowid FROM t1 WHERE content MATCH 'one -two'}
|
||||
} {1 5 9 13 17 21 25 29}
|
||||
do_test fts2a-3.3 {
|
||||
execsql {SELECT rowid FROM t1 WHERE content MATCH '-two one'}
|
||||
} {1 5 9 13 17 21 25 29}
|
||||
|
||||
do_test fts2a-4.1 {
|
||||
execsql {SELECT rowid FROM t1 WHERE content MATCH 'one OR two'}
|
||||
} {1 2 3 5 6 7 9 10 11 13 14 15 17 18 19 21 22 23 25 26 27 29 30 31}
|
||||
do_test fts2a-4.2 {
|
||||
execsql {SELECT rowid FROM t1 WHERE content MATCH '"one two" OR three'}
|
||||
} {3 4 5 6 7 11 12 13 14 15 19 20 21 22 23 27 28 29 30 31}
|
||||
do_test fts2a-4.3 {
|
||||
execsql {SELECT rowid FROM t1 WHERE content MATCH 'three OR "one two"'}
|
||||
} {3 4 5 6 7 11 12 13 14 15 19 20 21 22 23 27 28 29 30 31}
|
||||
do_test fts2a-4.4 {
|
||||
execsql {SELECT rowid FROM t1 WHERE content MATCH 'one two OR three'}
|
||||
} {3 5 7 11 13 15 19 21 23 27 29 31}
|
||||
do_test fts2a-4.5 {
|
||||
execsql {SELECT rowid FROM t1 WHERE content MATCH 'three OR two one'}
|
||||
} {3 5 7 11 13 15 19 21 23 27 29 31}
|
||||
do_test fts2a-4.6 {
|
||||
execsql {SELECT rowid FROM t1 WHERE content MATCH 'one two OR three OR four'}
|
||||
} {3 5 7 9 11 13 15 19 21 23 25 27 29 31}
|
||||
do_test fts2a-4.7 {
|
||||
execsql {SELECT rowid FROM t1 WHERE content MATCH 'two OR three OR four one'}
|
||||
} {3 5 7 9 11 13 15 19 21 23 25 27 29 31}
|
||||
|
||||
# Test the ability to handle NULL content
|
||||
#
|
||||
do_test fts2a-5.1 {
|
||||
execsql {INSERT INTO t1(content) VALUES(NULL)}
|
||||
} {}
|
||||
do_test fts2a-5.2 {
|
||||
set rowid [db last_insert_rowid]
|
||||
execsql {SELECT content FROM t1 WHERE rowid=$rowid}
|
||||
} {{}}
|
||||
do_test fts2a-5.3 {
|
||||
execsql {SELECT rowid FROM t1 WHERE content MATCH NULL}
|
||||
} {}
|
||||
|
||||
|
||||
|
||||
finish_test
|
||||
@@ -0,0 +1,147 @@
|
||||
# 2006 September 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 implements regression tests for SQLite library. The
|
||||
# focus of this script is testing the FTS2 module.
|
||||
#
|
||||
# $Id: fts2b.test,v 1.1 2006/10/19 23:36:26 shess Exp $
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
# If SQLITE_ENABLE_FTS2 is defined, omit this file.
|
||||
ifcapable !fts2 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
# Fill the full-text index "t1" with phrases in english, spanish,
|
||||
# and german. For the i-th row, fill in the names for the bits
|
||||
# that are set in the value of i. The least significant bit is
|
||||
# 1. For example, the value 5 is 101 in binary which will be
|
||||
# converted to "one three" in english.
|
||||
#
|
||||
proc fill_multilanguage_fulltext_t1 {} {
|
||||
set english {one two three four five}
|
||||
set spanish {un dos tres cuatro cinco}
|
||||
set german {eine zwei drei vier funf}
|
||||
|
||||
for {set i 1} {$i<=31} {incr i} {
|
||||
set cmd "INSERT INTO t1 VALUES"
|
||||
set vset {}
|
||||
foreach lang {english spanish german} {
|
||||
set words {}
|
||||
for {set j 0; set k 1} {$j<5} {incr j; incr k $k} {
|
||||
if {$k&$i} {lappend words [lindex [set $lang] $j]}
|
||||
}
|
||||
lappend vset "'$words'"
|
||||
}
|
||||
set sql "INSERT INTO t1(english,spanish,german) VALUES([join $vset ,])"
|
||||
# puts $sql
|
||||
db eval $sql
|
||||
}
|
||||
}
|
||||
|
||||
# Construct a full-text search table containing five keywords:
|
||||
# one, two, three, four, and five, in various combinations. The
|
||||
# rowid for each will be a bitmask for the elements it contains.
|
||||
#
|
||||
db eval {
|
||||
CREATE VIRTUAL TABLE t1 USING fts2(english,spanish,german);
|
||||
}
|
||||
fill_multilanguage_fulltext_t1
|
||||
|
||||
do_test fts2b-1.1 {
|
||||
execsql {SELECT rowid FROM t1 WHERE english MATCH 'one'}
|
||||
} {1 3 5 7 9 11 13 15 17 19 21 23 25 27 29 31}
|
||||
do_test fts2b-1.2 {
|
||||
execsql {SELECT rowid FROM t1 WHERE spanish MATCH 'one'}
|
||||
} {}
|
||||
do_test fts2b-1.3 {
|
||||
execsql {SELECT rowid FROM t1 WHERE german MATCH 'one'}
|
||||
} {}
|
||||
do_test fts2b-1.4 {
|
||||
execsql {SELECT rowid FROM t1 WHERE t1 MATCH 'one'}
|
||||
} {1 3 5 7 9 11 13 15 17 19 21 23 25 27 29 31}
|
||||
do_test fts2b-1.5 {
|
||||
execsql {SELECT rowid FROM t1 WHERE t1 MATCH 'one dos drei'}
|
||||
} {7 15 23 31}
|
||||
do_test fts2b-1.6 {
|
||||
execsql {SELECT english, spanish, german FROM t1 WHERE rowid=1}
|
||||
} {one un eine}
|
||||
do_test fts2b-1.7 {
|
||||
execsql {SELECT rowid FROM t1 WHERE t1 MATCH '"one un"'}
|
||||
} {}
|
||||
|
||||
do_test fts2b-2.1 {
|
||||
execsql {
|
||||
CREATE VIRTUAL TABLE t2 USING fts2(from,to);
|
||||
INSERT INTO t2([from],[to]) VALUES ('one two three', 'four five six');
|
||||
SELECT [from], [to] FROM t2
|
||||
}
|
||||
} {{one two three} {four five six}}
|
||||
|
||||
|
||||
# Compute an SQL string that contains the words one, two, three,... to
|
||||
# describe bits set in the value $i. Only the lower 5 bits are examined.
|
||||
#
|
||||
proc wordset {i} {
|
||||
set x {}
|
||||
for {set j 0; set k 1} {$j<5} {incr j; incr k $k} {
|
||||
if {$k&$i} {lappend x [lindex {one two three four five} $j]}
|
||||
}
|
||||
return '$x'
|
||||
}
|
||||
|
||||
# Create a new FTS table with three columns:
|
||||
#
|
||||
# norm: words for the bits of rowid
|
||||
# plusone: words for the bits of rowid+1
|
||||
# invert: words for the bits of ~rowid
|
||||
#
|
||||
db eval {
|
||||
CREATE VIRTUAL TABLE t4 USING fts2([norm],'plusone',"invert");
|
||||
}
|
||||
for {set i 1} {$i<=15} {incr i} {
|
||||
set vset [list [wordset $i] [wordset [expr {$i+1}]] [wordset [expr {~$i}]]]
|
||||
db eval "INSERT INTO t4(norm,plusone,invert) VALUES([join $vset ,]);"
|
||||
}
|
||||
|
||||
do_test fts2b-4.1 {
|
||||
execsql {SELECT rowid FROM t4 WHERE t4 MATCH 'norm:one'}
|
||||
} {1 3 5 7 9 11 13 15}
|
||||
do_test fts2b-4.2 {
|
||||
execsql {SELECT rowid FROM t4 WHERE norm MATCH 'one'}
|
||||
} {1 3 5 7 9 11 13 15}
|
||||
do_test fts2b-4.3 {
|
||||
execsql {SELECT rowid FROM t4 WHERE t4 MATCH 'one'}
|
||||
} {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15}
|
||||
do_test fts2b-4.4 {
|
||||
execsql {SELECT rowid FROM t4 WHERE t4 MATCH 'plusone:one'}
|
||||
} {2 4 6 8 10 12 14}
|
||||
do_test fts2b-4.5 {
|
||||
execsql {SELECT rowid FROM t4 WHERE plusone MATCH 'one'}
|
||||
} {2 4 6 8 10 12 14}
|
||||
do_test fts2b-4.6 {
|
||||
execsql {SELECT rowid FROM t4 WHERE t4 MATCH 'norm:one plusone:two'}
|
||||
} {1 5 9 13}
|
||||
do_test fts2b-4.7 {
|
||||
execsql {SELECT rowid FROM t4 WHERE t4 MATCH 'norm:one two'}
|
||||
} {1 3 5 7 9 11 13 15}
|
||||
do_test fts2b-4.8 {
|
||||
execsql {SELECT rowid FROM t4 WHERE t4 MATCH 'plusone:two norm:one'}
|
||||
} {1 5 9 13}
|
||||
do_test fts2b-4.9 {
|
||||
execsql {SELECT rowid FROM t4 WHERE t4 MATCH 'two norm:one'}
|
||||
} {1 3 5 7 9 11 13 15}
|
||||
|
||||
|
||||
finish_test
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,65 @@
|
||||
# 2006 October 1
|
||||
#
|
||||
# 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 implements regression tests for SQLite library. The
|
||||
# focus of this script is testing the FTS2 module, and in particular
|
||||
# the Porter stemmer.
|
||||
#
|
||||
# $Id: fts2d.test,v 1.1 2006/10/19 23:36:26 shess Exp $
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
# If SQLITE_ENABLE_FTS2 is defined, omit this file.
|
||||
ifcapable !fts2 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
do_test fts2d-1.1 {
|
||||
execsql {
|
||||
CREATE VIRTUAL TABLE t1 USING fts2(content, tokenize porter);
|
||||
INSERT INTO t1(rowid, content) VALUES(1, 'running and jumping');
|
||||
SELECT rowid FROM t1 WHERE content MATCH 'run jump';
|
||||
}
|
||||
} {1}
|
||||
do_test fts2d-1.2 {
|
||||
execsql {
|
||||
SELECT snippet(t1) FROM t1 WHERE t1 MATCH 'run jump';
|
||||
}
|
||||
} {{<b>running</b> and <b>jumping</b>}}
|
||||
do_test fts2d-1.3 {
|
||||
execsql {
|
||||
INSERT INTO t1(rowid, content)
|
||||
VALUES(2, 'abcdefghijklmnopqrstuvwyxz');
|
||||
SELECT rowid, snippet(t1) FROM t1 WHERE t1 MATCH 'abcdefghijqrstuvwyxz'
|
||||
}
|
||||
} {2 <b>abcdefghijklmnopqrstuvwyxz</b>}
|
||||
do_test fts2d-1.4 {
|
||||
execsql {
|
||||
SELECT rowid, snippet(t1) FROM t1 WHERE t1 MATCH 'abcdefghijXXXXqrstuvwyxz'
|
||||
}
|
||||
} {2 <b>abcdefghijklmnopqrstuvwyxz</b>}
|
||||
do_test fts2d-1.5 {
|
||||
execsql {
|
||||
INSERT INTO t1(rowid, content)
|
||||
VALUES(3, 'The value is 123456789');
|
||||
SELECT rowid, snippet(t1) FROM t1 WHERE t1 MATCH '123789'
|
||||
}
|
||||
} {3 {The value is <b>123456789</b>}}
|
||||
do_test fts2d-1.6 {
|
||||
execsql {
|
||||
SELECT rowid, snippet(t1) FROM t1 WHERE t1 MATCH '123000000789'
|
||||
}
|
||||
} {3 {The value is <b>123456789</b>}}
|
||||
|
||||
|
||||
finish_test
|
||||
@@ -0,0 +1,85 @@
|
||||
# 2006 October 19
|
||||
#
|
||||
# The author disclaims copyright to this source code.
|
||||
#
|
||||
#*************************************************************************
|
||||
# This file implements regression tests for SQLite library. The
|
||||
# focus of this script is testing deletions in the FTS2 module.
|
||||
#
|
||||
# $Id: fts2e.test,v 1.1 2006/10/19 23:36:26 shess Exp $
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
# If SQLITE_ENABLE_FTS2 is defined, omit this file.
|
||||
ifcapable !fts2 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
# Construct a full-text search table containing keywords which are the
|
||||
# ordinal numbers of the bit positions set for a sequence of integers,
|
||||
# which are used for the rowid. There are a total of 30 INSERT and
|
||||
# DELETE statements, so that we'll test both the segmentMerge() merge
|
||||
# (over the first 16) and the termSelect() merge (over the level-1
|
||||
# segment and 14 level-0 segments).
|
||||
db eval {
|
||||
CREATE VIRTUAL TABLE t1 USING fts2(content);
|
||||
INSERT INTO t1 (rowid, content) VALUES(1, 'one');
|
||||
INSERT INTO t1 (rowid, content) VALUES(2, 'two');
|
||||
INSERT INTO t1 (rowid, content) VALUES(3, 'one two');
|
||||
INSERT INTO t1 (rowid, content) VALUES(4, 'three');
|
||||
DELETE FROM t1 WHERE rowid = 1;
|
||||
INSERT INTO t1 (rowid, content) VALUES(5, 'one three');
|
||||
INSERT INTO t1 (rowid, content) VALUES(6, 'two three');
|
||||
INSERT INTO t1 (rowid, content) VALUES(7, 'one two three');
|
||||
DELETE FROM t1 WHERE rowid = 4;
|
||||
INSERT INTO t1 (rowid, content) VALUES(8, 'four');
|
||||
INSERT INTO t1 (rowid, content) VALUES(9, 'one four');
|
||||
INSERT INTO t1 (rowid, content) VALUES(10, 'two four');
|
||||
DELETE FROM t1 WHERE rowid = 7;
|
||||
INSERT INTO t1 (rowid, content) VALUES(11, 'one two four');
|
||||
INSERT INTO t1 (rowid, content) VALUES(12, 'three four');
|
||||
INSERT INTO t1 (rowid, content) VALUES(13, 'one three four');
|
||||
DELETE FROM t1 WHERE rowid = 10;
|
||||
INSERT INTO t1 (rowid, content) VALUES(14, 'two three four');
|
||||
INSERT INTO t1 (rowid, content) VALUES(15, 'one two three four');
|
||||
INSERT INTO t1 (rowid, content) VALUES(16, 'five');
|
||||
DELETE FROM t1 WHERE rowid = 13;
|
||||
INSERT INTO t1 (rowid, content) VALUES(17, 'one five');
|
||||
INSERT INTO t1 (rowid, content) VALUES(18, 'two five');
|
||||
INSERT INTO t1 (rowid, content) VALUES(19, 'one two five');
|
||||
DELETE FROM t1 WHERE rowid = 16;
|
||||
INSERT INTO t1 (rowid, content) VALUES(20, 'three five');
|
||||
INSERT INTO t1 (rowid, content) VALUES(21, 'one three five');
|
||||
INSERT INTO t1 (rowid, content) VALUES(22, 'two three five');
|
||||
DELETE FROM t1 WHERE rowid = 19;
|
||||
DELETE FROM t1 WHERE rowid = 22;
|
||||
}
|
||||
|
||||
do_test fts2f-1.1 {
|
||||
execsql {SELECT COUNT(*) FROM t1}
|
||||
} {14}
|
||||
|
||||
do_test fts2e-2.1 {
|
||||
execsql {SELECT rowid FROM t1 WHERE content MATCH 'one'}
|
||||
} {3 5 9 11 15 17 21}
|
||||
|
||||
do_test fts2e-2.2 {
|
||||
execsql {SELECT rowid FROM t1 WHERE content MATCH 'two'}
|
||||
} {2 3 6 11 14 15 18}
|
||||
|
||||
do_test fts2e-2.3 {
|
||||
execsql {SELECT rowid FROM t1 WHERE content MATCH 'three'}
|
||||
} {5 6 12 14 15 20 21}
|
||||
|
||||
do_test fts2e-2.4 {
|
||||
execsql {SELECT rowid FROM t1 WHERE content MATCH 'four'}
|
||||
} {8 9 11 12 14 15}
|
||||
|
||||
do_test fts2e-2.5 {
|
||||
execsql {SELECT rowid FROM t1 WHERE content MATCH 'five'}
|
||||
} {17 18 20 21}
|
||||
|
||||
finish_test
|
||||
@@ -0,0 +1,90 @@
|
||||
# 2006 October 19
|
||||
#
|
||||
# The author disclaims copyright to this source code.
|
||||
#
|
||||
#*************************************************************************
|
||||
# This file implements regression tests for SQLite library. The
|
||||
# focus of this script is testing updates in the FTS2 module.
|
||||
#
|
||||
# $Id: fts2f.test,v 1.1 2006/10/19 23:36:26 shess Exp $
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
# If SQLITE_ENABLE_FTS2 is defined, omit this file.
|
||||
ifcapable !fts2 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
# Construct a full-text search table containing keywords which are the
|
||||
# ordinal numbers of the bit positions set for a sequence of integers,
|
||||
# which are used for the rowid. There are a total of 31 INSERT,
|
||||
# UPDATE, and DELETE statements, so that we'll test both the
|
||||
# segmentMerge() merge (over the first 16) and the termSelect() merge
|
||||
# (over the level-1 segment and 15 level-0 segments).
|
||||
db eval {
|
||||
CREATE VIRTUAL TABLE t1 USING fts2(content);
|
||||
INSERT INTO t1 (rowid, content) VALUES(1, 'one');
|
||||
INSERT INTO t1 (rowid, content) VALUES(2, 'two');
|
||||
INSERT INTO t1 (rowid, content) VALUES(3, 'one two');
|
||||
INSERT INTO t1 (rowid, content) VALUES(4, 'three');
|
||||
INSERT INTO t1 (rowid, content) VALUES(5, 'one three');
|
||||
INSERT INTO t1 (rowid, content) VALUES(6, 'two three');
|
||||
INSERT INTO t1 (rowid, content) VALUES(7, 'one two three');
|
||||
DELETE FROM t1 WHERE rowid = 4;
|
||||
INSERT INTO t1 (rowid, content) VALUES(8, 'four');
|
||||
UPDATE t1 SET content = 'update one three' WHERE rowid = 1;
|
||||
INSERT INTO t1 (rowid, content) VALUES(9, 'one four');
|
||||
INSERT INTO t1 (rowid, content) VALUES(10, 'two four');
|
||||
DELETE FROM t1 WHERE rowid = 7;
|
||||
INSERT INTO t1 (rowid, content) VALUES(11, 'one two four');
|
||||
INSERT INTO t1 (rowid, content) VALUES(12, 'three four');
|
||||
INSERT INTO t1 (rowid, content) VALUES(13, 'one three four');
|
||||
DELETE FROM t1 WHERE rowid = 10;
|
||||
INSERT INTO t1 (rowid, content) VALUES(14, 'two three four');
|
||||
INSERT INTO t1 (rowid, content) VALUES(15, 'one two three four');
|
||||
UPDATE t1 SET content = 'update two five' WHERE rowid = 8;
|
||||
INSERT INTO t1 (rowid, content) VALUES(16, 'five');
|
||||
DELETE FROM t1 WHERE rowid = 13;
|
||||
INSERT INTO t1 (rowid, content) VALUES(17, 'one five');
|
||||
INSERT INTO t1 (rowid, content) VALUES(18, 'two five');
|
||||
INSERT INTO t1 (rowid, content) VALUES(19, 'one two five');
|
||||
DELETE FROM t1 WHERE rowid = 16;
|
||||
INSERT INTO t1 (rowid, content) VALUES(20, 'three five');
|
||||
INSERT INTO t1 (rowid, content) VALUES(21, 'one three five');
|
||||
INSERT INTO t1 (rowid, content) VALUES(22, 'two three five');
|
||||
DELETE FROM t1 WHERE rowid = 19;
|
||||
UPDATE t1 SET content = 'update' WHERE rowid = 15;
|
||||
}
|
||||
|
||||
do_test fts2f-1.1 {
|
||||
execsql {SELECT COUNT(*) FROM t1}
|
||||
} {16}
|
||||
|
||||
do_test fts2e-2.0 {
|
||||
execsql {SELECT rowid FROM t1 WHERE content MATCH 'update'}
|
||||
} {1 8 15}
|
||||
|
||||
do_test fts2e-2.1 {
|
||||
execsql {SELECT rowid FROM t1 WHERE content MATCH 'one'}
|
||||
} {1 3 5 9 11 17 21}
|
||||
|
||||
do_test fts2e-2.2 {
|
||||
execsql {SELECT rowid FROM t1 WHERE content MATCH 'two'}
|
||||
} {2 3 6 8 11 14 18 22}
|
||||
|
||||
do_test fts2e-2.3 {
|
||||
execsql {SELECT rowid FROM t1 WHERE content MATCH 'three'}
|
||||
} {1 5 6 12 14 20 21 22}
|
||||
|
||||
do_test fts2e-2.4 {
|
||||
execsql {SELECT rowid FROM t1 WHERE content MATCH 'four'}
|
||||
} {9 11 12 14}
|
||||
|
||||
do_test fts2e-2.5 {
|
||||
execsql {SELECT rowid FROM t1 WHERE content MATCH 'five'}
|
||||
} {8 17 18 20 21 22}
|
||||
|
||||
finish_test
|
||||
@@ -0,0 +1,77 @@
|
||||
# 2006 October 19
|
||||
#
|
||||
# The author disclaims copyright to this source code.
|
||||
#
|
||||
#*************************************************************************
|
||||
# This file implements regression tests for SQLite library. The focus
|
||||
# of this script is testing handling of edge cases for various doclist
|
||||
# merging functions in the FTS2 module query logic.
|
||||
#
|
||||
# $Id: fts2g.test,v 1.1 2006/10/25 20:27:40 shess Exp $
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
# If SQLITE_ENABLE_FTS2 is defined, omit this file.
|
||||
ifcapable !fts2 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
db eval {
|
||||
CREATE VIRTUAL TABLE t1 USING fts2(content);
|
||||
INSERT INTO t1 (rowid, content) VALUES(1, 'this is a test');
|
||||
}
|
||||
|
||||
# No hits at all. Returns empty doclists from termSelect().
|
||||
do_test fts2g-1.1 {
|
||||
execsql {SELECT rowid FROM t1 WHERE t1 MATCH 'something'}
|
||||
} {}
|
||||
|
||||
# Empty left in docListExceptMerge().
|
||||
do_test fts2g-1.2 {
|
||||
execsql {SELECT rowid FROM t1 WHERE t1 MATCH '-this something'}
|
||||
} {}
|
||||
|
||||
# Empty right in docListExceptMerge().
|
||||
do_test fts2g-1.3 {
|
||||
execsql {SELECT rowid FROM t1 WHERE t1 MATCH 'this -something'}
|
||||
} {1}
|
||||
|
||||
# Empty left in docListPhraseMerge().
|
||||
do_test fts2g-1.4 {
|
||||
execsql {SELECT rowid FROM t1 WHERE t1 MATCH '"this something"'}
|
||||
} {}
|
||||
|
||||
# Empty right in docListPhraseMerge().
|
||||
do_test fts2g-1.5 {
|
||||
execsql {SELECT rowid FROM t1 WHERE t1 MATCH '"something is"'}
|
||||
} {}
|
||||
|
||||
# Empty left in docListOrMerge().
|
||||
do_test fts2g-1.6 {
|
||||
execsql {SELECT rowid FROM t1 WHERE t1 MATCH 'something OR this'}
|
||||
} {1}
|
||||
|
||||
# Empty right in docListOrMerge().
|
||||
do_test fts2g-1.7 {
|
||||
execsql {SELECT rowid FROM t1 WHERE t1 MATCH 'this OR something'}
|
||||
} {1}
|
||||
|
||||
# Empty left in docListAndMerge().
|
||||
do_test fts2g-1.8 {
|
||||
execsql {SELECT rowid FROM t1 WHERE t1 MATCH 'something this'}
|
||||
} {}
|
||||
|
||||
# Empty right in docListAndMerge().
|
||||
do_test fts2g-1.9 {
|
||||
execsql {SELECT rowid FROM t1 WHERE t1 MATCH 'this something'}
|
||||
} {}
|
||||
|
||||
# No support for all-except queries.
|
||||
do_test fts2g-1.10 {
|
||||
catchsql {SELECT rowid FROM t1 WHERE t1 MATCH '-this -something'}
|
||||
} {1 {SQL logic error or missing database}}
|
||||
|
||||
finish_test
|
||||
@@ -0,0 +1,76 @@
|
||||
# 2006 October 31 (scaaarey)
|
||||
#
|
||||
# The author disclaims copyright to this source code.
|
||||
#
|
||||
#*************************************************************************
|
||||
# This file implements regression tests for SQLite library. The focus
|
||||
# here is testing correct handling of excessively long terms.
|
||||
#
|
||||
# $Id: fts2h.test,v 1.1 2006/11/29 21:03:01 shess Exp $
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
# If SQLITE_ENABLE_FTS2 is defined, omit this file.
|
||||
ifcapable !fts2 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
# Generate a term of len copies of char.
|
||||
proc bigterm {char len} {
|
||||
for {set term ""} {$len>0} {incr len -1} {
|
||||
append term $char
|
||||
}
|
||||
return $term
|
||||
}
|
||||
|
||||
# Generate a document of bigterms based on characters from the list
|
||||
# chars.
|
||||
proc bigtermdoc {chars len} {
|
||||
set doc ""
|
||||
foreach char $chars {
|
||||
append doc " " [bigterm $char $len]
|
||||
}
|
||||
return $doc
|
||||
}
|
||||
|
||||
set len 5000
|
||||
set doc1 [bigtermdoc {a b c d} $len]
|
||||
set doc2 [bigtermdoc {b d e f} $len]
|
||||
set doc3 [bigtermdoc {a c e} $len]
|
||||
|
||||
set aterm [bigterm a $len]
|
||||
set bterm [bigterm b $len]
|
||||
set xterm [bigterm x $len]
|
||||
|
||||
db eval {
|
||||
CREATE VIRTUAL TABLE t1 USING fts2(content);
|
||||
INSERT INTO t1 (rowid, content) VALUES(1, $doc1);
|
||||
INSERT INTO t1 (rowid, content) VALUES(2, $doc2);
|
||||
INSERT INTO t1 (rowid, content) VALUES(3, $doc3);
|
||||
}
|
||||
|
||||
# No hits at all. Returns empty doclists from termSelect().
|
||||
do_test fts2h-1.1 {
|
||||
execsql {SELECT rowid FROM t1 WHERE t1 MATCH 'something'}
|
||||
} {}
|
||||
|
||||
do_test fts2h-1.2 {
|
||||
execsql {SELECT rowid FROM t1 WHERE t1 MATCH $aterm}
|
||||
} {1 3}
|
||||
|
||||
do_test fts2h-1.2 {
|
||||
execsql {SELECT rowid FROM t1 WHERE t1 MATCH $xterm}
|
||||
} {}
|
||||
|
||||
do_test fts2h-1.3 {
|
||||
execsql "SELECT rowid FROM t1 WHERE t1 MATCH '$aterm -$xterm'"
|
||||
} {1 3}
|
||||
|
||||
do_test fts2h-1.4 {
|
||||
execsql "SELECT rowid FROM t1 WHERE t1 MATCH '\"$aterm $bterm\"'"
|
||||
} {1}
|
||||
|
||||
finish_test
|
||||
@@ -0,0 +1,87 @@
|
||||
# 2007 January 17
|
||||
#
|
||||
# The author disclaims copyright to this source code.
|
||||
#
|
||||
#*************************************************************************
|
||||
# This file implements regression tests for SQLite fts2 library. The
|
||||
# focus here is testing handling of UPDATE when using UTF-16-encoded
|
||||
# databases.
|
||||
#
|
||||
# $Id: fts2i.test,v 1.2 2007/01/24 03:46:35 drh Exp $
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
# If SQLITE_ENABLE_FTS2 is defined, omit this file.
|
||||
ifcapable !fts2 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
# Return the UTF-16 representation of the supplied UTF-8 string $str.
|
||||
# If $nt is true, append two 0x00 bytes as a nul terminator.
|
||||
# NOTE(shess) Copied from capi3.test.
|
||||
proc utf16 {str {nt 1}} {
|
||||
set r [encoding convertto unicode $str]
|
||||
if {$nt} {
|
||||
append r "\x00\x00"
|
||||
}
|
||||
return $r
|
||||
}
|
||||
|
||||
db eval {
|
||||
PRAGMA encoding = "UTF-16le";
|
||||
CREATE VIRTUAL TABLE t1 USING fts2(content);
|
||||
}
|
||||
|
||||
do_test fts2i-1.0 {
|
||||
execsql {PRAGMA encoding}
|
||||
} {UTF-16le}
|
||||
|
||||
do_test fts2i-1.1 {
|
||||
execsql {INSERT INTO t1 (rowid, content) VALUES(1, 'one')}
|
||||
execsql {SELECT content FROM t1 WHERE rowid = 1}
|
||||
} {one}
|
||||
|
||||
do_test fts2i-1.2 {
|
||||
set sql "INSERT INTO t1 (rowid, content) VALUES(2, 'two')"
|
||||
set STMT [sqlite3_prepare $DB $sql -1 TAIL]
|
||||
sqlite3_step $STMT
|
||||
sqlite3_finalize $STMT
|
||||
execsql {SELECT content FROM t1 WHERE rowid = 2}
|
||||
} {two}
|
||||
|
||||
do_test fts2i-1.3 {
|
||||
set sql "INSERT INTO t1 (rowid, content) VALUES(3, 'three')"
|
||||
set STMT [sqlite3_prepare $DB $sql -1 TAIL]
|
||||
sqlite3_step $STMT
|
||||
sqlite3_finalize $STMT
|
||||
set sql "UPDATE t1 SET content = 'trois' WHERE rowid = 3"
|
||||
set STMT [sqlite3_prepare $DB $sql -1 TAIL]
|
||||
sqlite3_step $STMT
|
||||
sqlite3_finalize $STMT
|
||||
execsql {SELECT content FROM t1 WHERE rowid = 3}
|
||||
} {trois}
|
||||
|
||||
do_test fts2i-1.4 {
|
||||
set sql16 [utf16 {INSERT INTO t1 (rowid, content) VALUES(4, 'four')}]
|
||||
set STMT [sqlite3_prepare16 $DB $sql16 -1 TAIL]
|
||||
sqlite3_step $STMT
|
||||
sqlite3_finalize $STMT
|
||||
execsql {SELECT content FROM t1 WHERE rowid = 4}
|
||||
} {four}
|
||||
|
||||
do_test fts2i-1.5 {
|
||||
set sql16 [utf16 {INSERT INTO t1 (rowid, content) VALUES(5, 'five')}]
|
||||
set STMT [sqlite3_prepare16 $DB $sql16 -1 TAIL]
|
||||
sqlite3_step $STMT
|
||||
sqlite3_finalize $STMT
|
||||
set sql "UPDATE t1 SET content = 'cinq' WHERE rowid = 5"
|
||||
set STMT [sqlite3_prepare $DB $sql -1 TAIL]
|
||||
sqlite3_step $STMT
|
||||
sqlite3_finalize $STMT
|
||||
execsql {SELECT content FROM t1 WHERE rowid = 5}
|
||||
} {cinq}
|
||||
|
||||
finish_test
|
||||
@@ -0,0 +1,89 @@
|
||||
# 2007 February 6
|
||||
#
|
||||
# The author disclaims copyright to this source code.
|
||||
#
|
||||
#*************************************************************************
|
||||
# This file implements regression tests for SQLite library. This
|
||||
# tests creating fts2 tables in an attached database.
|
||||
#
|
||||
# $Id: fts2j.test,v 1.1 2007/02/07 01:01:18 shess Exp $
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
# If SQLITE_ENABLE_FTS2 is defined, omit this file.
|
||||
ifcapable !fts2 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
# Clean up anything left over from a previous pass.
|
||||
file delete -force test2.db
|
||||
file delete -force test2.db-journal
|
||||
sqlite3 db2 test2.db
|
||||
|
||||
db eval {
|
||||
CREATE VIRTUAL TABLE t3 USING fts2(content);
|
||||
INSERT INTO t3 (rowid, content) VALUES(1, "hello world");
|
||||
}
|
||||
|
||||
db2 eval {
|
||||
CREATE VIRTUAL TABLE t1 USING fts2(content);
|
||||
INSERT INTO t1 (rowid, content) VALUES(1, "hello world");
|
||||
INSERT INTO t1 (rowid, content) VALUES(2, "hello there");
|
||||
INSERT INTO t1 (rowid, content) VALUES(3, "cruel world");
|
||||
}
|
||||
|
||||
# This has always worked because the t1_* tables used by fts2 will be
|
||||
# the defaults.
|
||||
do_test fts2j-1.1 {
|
||||
execsql {
|
||||
ATTACH DATABASE 'test2.db' AS two;
|
||||
SELECT rowid FROM t1 WHERE t1 MATCH 'hello';
|
||||
DETACH DATABASE two;
|
||||
}
|
||||
} {1 2}
|
||||
# Make certain we're detached if there was an error.
|
||||
catch {db eval {DETACH DATABASE two}}
|
||||
|
||||
# In older code, this appears to work fine, but the t2_* tables used
|
||||
# by fts2 will be created in database 'main' instead of database
|
||||
# 'two'. It appears to work fine because the tables end up being the
|
||||
# defaults, but obviously is badly broken if you hope to use things
|
||||
# other than in the exact same ATTACH setup.
|
||||
do_test fts2j-1.2 {
|
||||
execsql {
|
||||
ATTACH DATABASE 'test2.db' AS two;
|
||||
CREATE VIRTUAL TABLE two.t2 USING fts2(content);
|
||||
INSERT INTO t2 (rowid, content) VALUES(1, "hello world");
|
||||
INSERT INTO t2 (rowid, content) VALUES(2, "hello there");
|
||||
INSERT INTO t2 (rowid, content) VALUES(3, "cruel world");
|
||||
SELECT rowid FROM t2 WHERE t2 MATCH 'hello';
|
||||
DETACH DATABASE two;
|
||||
}
|
||||
} {1 2}
|
||||
catch {db eval {DETACH DATABASE two}}
|
||||
|
||||
# In older code, this broke because the fts2 code attempted to create
|
||||
# t3_* tables in database 'main', but they already existed. Normally
|
||||
# this wouldn't happen without t3 itself existing, in which case the
|
||||
# fts2 code would never be called in the first place.
|
||||
do_test fts2j-1.3 {
|
||||
execsql {
|
||||
ATTACH DATABASE 'test2.db' AS two;
|
||||
|
||||
CREATE VIRTUAL TABLE two.t3 USING fts2(content);
|
||||
INSERT INTO two.t3 (rowid, content) VALUES(2, "hello there");
|
||||
INSERT INTO two.t3 (rowid, content) VALUES(3, "cruel world");
|
||||
SELECT rowid FROM two.t3 WHERE t3 MATCH 'hello';
|
||||
|
||||
DETACH DATABASE two;
|
||||
} db2
|
||||
} {2}
|
||||
catch {db eval {DETACH DATABASE two}}
|
||||
|
||||
catch {db2 close}
|
||||
file delete -force test2.db
|
||||
|
||||
finish_test
|
||||
@@ -11,7 +11,7 @@
|
||||
# This file implements regression tests for SQLite library. The
|
||||
# focus of this file is testing built-in functions.
|
||||
#
|
||||
# $Id: func.test,v 1.55 2006/09/16 21:45:14 drh Exp $
|
||||
# $Id: func.test,v 1.57 2007/01/29 17:58:28 drh Exp $
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
@@ -296,6 +296,35 @@ do_test func-9.1 {
|
||||
SELECT random() is not null;
|
||||
}
|
||||
} {1}
|
||||
do_test func-9.2 {
|
||||
execsql {
|
||||
SELECT typeof(random());
|
||||
}
|
||||
} {integer}
|
||||
do_test func-9.3 {
|
||||
execsql {
|
||||
SELECT randomblob(32) is not null;
|
||||
}
|
||||
} {1}
|
||||
do_test func-9.4 {
|
||||
execsql {
|
||||
SELECT typeof(randomblob(32));
|
||||
}
|
||||
} {blob}
|
||||
do_test func-9.5 {
|
||||
execsql {
|
||||
SELECT length(randomblob(32)), length(randomblob(-5)),
|
||||
length(randomblob(2000))
|
||||
}
|
||||
} {32 1 2000}
|
||||
|
||||
# The "hex()" function was added in order to be able to render blobs
|
||||
# generated by randomblob(). So this seems like a good place to test
|
||||
# hex().
|
||||
#
|
||||
do_test func-9.10 {
|
||||
execsql {SELECT hex(x'00112233445566778899aAbBcCdDeEfF')}
|
||||
} {00112233445566778899AABBCCDDEEFF}
|
||||
|
||||
# Use the "sqlite_register_test_function" TCL command which is part of
|
||||
# the text fixture in order to verify correct operation of some of
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
# The tests in this file use special facilities that are only
|
||||
# available in the SQLite test fixture.
|
||||
#
|
||||
# $Id: ioerr.test,v 1.27 2006/09/15 07:28:51 drh Exp $
|
||||
# $Id: ioerr.test,v 1.29 2007/01/04 14:58:14 drh Exp $
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
@@ -46,6 +46,9 @@ do_ioerr_test ioerr-1 -erc 1 -sqlprep {
|
||||
DELETE FROM t1 WHERE a<100;
|
||||
} -exclude [expr [string match [execsql {pragma auto_vacuum}] 1] ? 4 : 0]
|
||||
|
||||
finish_test
|
||||
return
|
||||
|
||||
# Test for IO errors during a VACUUM.
|
||||
#
|
||||
# The first IO call is excluded from the test. This call attempts to read
|
||||
@@ -165,6 +168,7 @@ ifcapable crashtest {
|
||||
# These tests can't be run on windows because the windows version of
|
||||
# SQLite holds a mandatory exclusive lock on journal files it has open.
|
||||
#
|
||||
btree_breakpoint
|
||||
if {$tcl_platform(platform)!="windows"} {
|
||||
do_ioerr_test ioerr-7 -tclprep {
|
||||
db close
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
# special feature is used to see what happens in the library if a malloc
|
||||
# were to really fail due to an out-of-memory situation.
|
||||
#
|
||||
# $Id: malloc.test,v 1.35 2006/10/04 11:55:50 drh Exp $
|
||||
# $Id: malloc.test,v 1.36 2006/10/18 23:26:39 drh Exp $
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
@@ -229,7 +229,10 @@ do_malloc_test 5 -sqlbody {
|
||||
CREATE TABLE t1(a,b);
|
||||
CREATE TABLE t2(x,y);
|
||||
CREATE TRIGGER r1 AFTER INSERT ON t1 BEGIN
|
||||
INSERT INTO t2(x,y) VALUES(new.rowid,1);
|
||||
INSERT INTO t2(x,y) VALUES(new.rowid,1);
|
||||
UPDATE t2 SET y=y+1 WHERE x=new.rowid;
|
||||
SELECT 123;
|
||||
DELETE FROM t2 WHERE x=new.rowid;
|
||||
END;
|
||||
INSERT INTO t1(a,b) VALUES(2,3);
|
||||
COMMIT;
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
# This file implements tests for miscellanous features that were
|
||||
# left out of other test files.
|
||||
#
|
||||
# $Id: misc5.test,v 1.15 2006/08/12 12:33:15 drh Exp $
|
||||
# $Id: misc5.test,v 1.16 2007/01/03 23:37:29 drh Exp $
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
@@ -573,7 +573,7 @@ do_test misc5-7.1 {
|
||||
|
||||
# Check the MISUSE return from sqlitee3_busy_timeout
|
||||
#
|
||||
do_test misc5-8.1 {
|
||||
do_test misc5-8.1-misuse {
|
||||
set DB [sqlite3_connection_pointer db]
|
||||
db close
|
||||
sqlite3_busy_timeout $DB 1000
|
||||
|
||||
+156
-16
@@ -12,7 +12,7 @@
|
||||
#
|
||||
# This file implements tests for the PRAGMA command.
|
||||
#
|
||||
# $Id: pragma.test,v 1.44 2006/08/14 14:23:43 drh Exp $
|
||||
# $Id: pragma.test,v 1.51 2007/01/27 14:26:07 drh Exp $
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
@@ -42,7 +42,8 @@ ifcapable !pragma {
|
||||
# that the "all.test" script does.
|
||||
#
|
||||
db close
|
||||
file delete test.db
|
||||
file delete test.db test.db-journal
|
||||
file delete test3.db test3.db-journal
|
||||
sqlite3 db test.db; set DB [sqlite3_connection_pointer db]
|
||||
|
||||
ifcapable pager_pragmas {
|
||||
@@ -258,12 +259,143 @@ if {![sqlite3 -has-codec] && $sqlite_options(integrityck)} {
|
||||
btree_close $db
|
||||
execsql {PRAGMA integrity_check}
|
||||
} {{rowid 1 missing from index i2} {wrong # of entries in index i2}}
|
||||
do_test pragma-3.3 {
|
||||
execsql {PRAGMA integrity_check=1}
|
||||
} {{rowid 1 missing from index i2}}
|
||||
do_test pragma-3.4 {
|
||||
execsql {
|
||||
ATTACH DATABASE 'test.db' AS t2;
|
||||
PRAGMA integrity_check
|
||||
}
|
||||
} {{rowid 1 missing from index i2} {wrong # of entries in index i2} {rowid 1 missing from index i2} {wrong # of entries in index i2}}
|
||||
do_test pragma-3.5 {
|
||||
execsql {
|
||||
PRAGMA integrity_check=3
|
||||
}
|
||||
} {{rowid 1 missing from index i2} {wrong # of entries in index i2} {rowid 1 missing from index i2}}
|
||||
do_test pragma-3.6 {
|
||||
execsql {
|
||||
PRAGMA integrity_check=xyz
|
||||
}
|
||||
} {{rowid 1 missing from index i2} {wrong # of entries in index i2} {rowid 1 missing from index i2} {wrong # of entries in index i2}}
|
||||
do_test pragma-3.7 {
|
||||
execsql {
|
||||
PRAGMA integrity_check=0
|
||||
}
|
||||
} {{rowid 1 missing from index i2} {wrong # of entries in index i2} {rowid 1 missing from index i2} {wrong # of entries in index i2}}
|
||||
|
||||
# Add additional corruption by appending unused pages to the end of
|
||||
# the database file testerr.db
|
||||
#
|
||||
do_test pragma-3.8 {
|
||||
execsql {DETACH t2}
|
||||
file delete -force testerr.db testerr.db-journal
|
||||
set out [open testerr.db w]
|
||||
fconfigure $out -translation binary
|
||||
set in [open test.db r]
|
||||
fconfigure $in -translation binary
|
||||
puts -nonewline $out [read $in]
|
||||
seek $in 0
|
||||
puts -nonewline $out [read $in]
|
||||
close $in
|
||||
close $out
|
||||
execsql {REINDEX t2}
|
||||
execsql {PRAGMA integrity_check}
|
||||
} {ok}
|
||||
do_test pragma-3.9 {
|
||||
execsql {
|
||||
ATTACH 'testerr.db' AS t2;
|
||||
PRAGMA integrity_check
|
||||
}
|
||||
} {{*** in database t2 ***
|
||||
Page 4 is never used
|
||||
Page 5 is never used
|
||||
Page 6 is never used} {rowid 1 missing from index i2} {wrong # of entries in index i2}}
|
||||
do_test pragma-3.10 {
|
||||
execsql {
|
||||
PRAGMA integrity_check=1
|
||||
}
|
||||
} {{*** in database t2 ***
|
||||
Page 4 is never used}}
|
||||
do_test pragma-3.11 {
|
||||
execsql {
|
||||
PRAGMA integrity_check=5
|
||||
}
|
||||
} {{*** in database t2 ***
|
||||
Page 4 is never used
|
||||
Page 5 is never used
|
||||
Page 6 is never used} {rowid 1 missing from index i2} {wrong # of entries in index i2}}
|
||||
do_test pragma-3.12 {
|
||||
execsql {
|
||||
PRAGMA integrity_check=4
|
||||
}
|
||||
} {{*** in database t2 ***
|
||||
Page 4 is never used
|
||||
Page 5 is never used
|
||||
Page 6 is never used} {rowid 1 missing from index i2}}
|
||||
do_test pragma-3.13 {
|
||||
execsql {
|
||||
PRAGMA integrity_check=3
|
||||
}
|
||||
} {{*** in database t2 ***
|
||||
Page 4 is never used
|
||||
Page 5 is never used
|
||||
Page 6 is never used}}
|
||||
do_test pragma-3.14 {
|
||||
execsql {
|
||||
PRAGMA integrity_check(2)
|
||||
}
|
||||
} {{*** in database t2 ***
|
||||
Page 4 is never used
|
||||
Page 5 is never used}}
|
||||
do_test pragma-3.15 {
|
||||
execsql {
|
||||
ATTACH 'testerr.db' AS t3;
|
||||
PRAGMA integrity_check
|
||||
}
|
||||
} {{*** in database t2 ***
|
||||
Page 4 is never used
|
||||
Page 5 is never used
|
||||
Page 6 is never used} {rowid 1 missing from index i2} {wrong # of entries in index i2} {*** in database t3 ***
|
||||
Page 4 is never used
|
||||
Page 5 is never used
|
||||
Page 6 is never used} {rowid 1 missing from index i2} {wrong # of entries in index i2}}
|
||||
do_test pragma-3.16 {
|
||||
execsql {
|
||||
PRAGMA integrity_check(9)
|
||||
}
|
||||
} {{*** in database t2 ***
|
||||
Page 4 is never used
|
||||
Page 5 is never used
|
||||
Page 6 is never used} {rowid 1 missing from index i2} {wrong # of entries in index i2} {*** in database t3 ***
|
||||
Page 4 is never used
|
||||
Page 5 is never used
|
||||
Page 6 is never used} {rowid 1 missing from index i2}}
|
||||
do_test pragma-3.17 {
|
||||
execsql {
|
||||
PRAGMA integrity_check=7
|
||||
}
|
||||
} {{*** in database t2 ***
|
||||
Page 4 is never used
|
||||
Page 5 is never used
|
||||
Page 6 is never used} {rowid 1 missing from index i2} {wrong # of entries in index i2} {*** in database t3 ***
|
||||
Page 4 is never used
|
||||
Page 5 is never used}}
|
||||
do_test pragma-3.18 {
|
||||
execsql {
|
||||
PRAGMA integrity_check=4
|
||||
}
|
||||
} {{*** in database t2 ***
|
||||
Page 4 is never used
|
||||
Page 5 is never used
|
||||
Page 6 is never used} {rowid 1 missing from index i2}}
|
||||
}
|
||||
do_test pragma-3.3 {
|
||||
execsql {
|
||||
DROP INDEX i2;
|
||||
}
|
||||
} {}
|
||||
do_test pragma-3.99 {
|
||||
catchsql {DETACH t3}
|
||||
catchsql {DETACH t2}
|
||||
file delete -force testerr.db testerr.db-journal
|
||||
catchsql {DROP INDEX i2}
|
||||
} {0 {}}
|
||||
|
||||
# Test modifying the cache_size of an attached database.
|
||||
ifcapable pager_pragmas {
|
||||
@@ -351,12 +483,20 @@ do_test pragma-6.2 {
|
||||
pragma table_info(t2)
|
||||
}
|
||||
} {0 a {} 0 {} 0 1 b {} 0 {} 0 2 c {} 0 {} 0}
|
||||
db nullvalue <<NULL>>
|
||||
do_test pragma-6.2.2 {
|
||||
execsql {
|
||||
CREATE TABLE t5(a TEXT DEFAULT CURRENT_TIMESTAMP, b DEFAULT (5+3));
|
||||
CREATE TABLE t5(
|
||||
a TEXT DEFAULT CURRENT_TIMESTAMP,
|
||||
b DEFAULT (5+3),
|
||||
c TEXT,
|
||||
d INTEGER DEFAULT NULL,
|
||||
e TEXT DEFAULT ''
|
||||
);
|
||||
PRAGMA table_info(t5);
|
||||
}
|
||||
} {0 a TEXT 0 CURRENT_TIMESTAMP 0 1 b {} 0 5+3 0}
|
||||
} {0 a TEXT 0 CURRENT_TIMESTAMP 0 1 b {} 0 5+3 0 2 c TEXT 0 <<NULL>> 0 3 d INTEGER 0 NULL 0 4 e TEXT 0 '' 0}
|
||||
db nullvalue {}
|
||||
ifcapable {foreignkey} {
|
||||
do_test pragma-6.3 {
|
||||
execsql {
|
||||
@@ -438,10 +578,10 @@ do_test pragma-8.1.1 {
|
||||
}
|
||||
} {}
|
||||
do_test pragma-8.1.2 {
|
||||
execsql {
|
||||
execsql2 {
|
||||
PRAGMA schema_version;
|
||||
}
|
||||
} 105
|
||||
} {schema_version 105}
|
||||
do_test pragma-8.1.3 {
|
||||
execsql {
|
||||
PRAGMA schema_version = 106;
|
||||
@@ -540,20 +680,20 @@ do_test pragma-8.1.18 {
|
||||
# Now test that the user-version can be read and written (and that we aren't
|
||||
# accidentally manipulating the schema-version instead).
|
||||
do_test pragma-8.2.1 {
|
||||
execsql {
|
||||
execsql2 {
|
||||
PRAGMA user_version;
|
||||
}
|
||||
} {0}
|
||||
} {user_version 0}
|
||||
do_test pragma-8.2.2 {
|
||||
execsql {
|
||||
PRAGMA user_version = 2;
|
||||
}
|
||||
} {}
|
||||
do_test pragma-8.2.3.1 {
|
||||
execsql {
|
||||
execsql2 {
|
||||
PRAGMA user_version;
|
||||
}
|
||||
} {2}
|
||||
} {user_version 2}
|
||||
do_test pragma-8.2.3.2 {
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
@@ -686,7 +826,7 @@ do_test pragma-9.6 {
|
||||
execsql {
|
||||
PRAGMA temp_store_directory;
|
||||
}
|
||||
} [pwd]
|
||||
} [list [pwd]]
|
||||
do_test pragma-9.7 {
|
||||
catchsql {
|
||||
PRAGMA temp_store_directory='/NON/EXISTENT/PATH/FOOBAR';
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#***********************************************************************
|
||||
# This file runs all tests.
|
||||
#
|
||||
# $Id: quick.test,v 1.45 2006/06/23 08:05:38 danielk1977 Exp $
|
||||
# $Id: quick.test,v 1.47 2006/11/23 21:09:11 drh Exp $
|
||||
|
||||
proc lshift {lvar} {
|
||||
upvar $lvar l
|
||||
@@ -50,6 +50,7 @@ set EXCLUDE {
|
||||
memleak.test
|
||||
misuse.test
|
||||
quick.test
|
||||
speed1.test
|
||||
|
||||
autovacuum_crash.test
|
||||
btree8.test
|
||||
@@ -63,9 +64,17 @@ if {[sqlite3 -has-codec]} {
|
||||
# conflict.test
|
||||
}
|
||||
|
||||
|
||||
# Files to include in the test. If this list is empty then everything
|
||||
# that is not in the EXCLUDE list is run.
|
||||
#
|
||||
set INCLUDE {
|
||||
}
|
||||
|
||||
foreach testfile [lsort -dictionary [glob $testdir/*.test]] {
|
||||
set tail [file tail $testfile]
|
||||
if {[lsearch -exact $EXCLUDE $tail]>=0} continue
|
||||
if {[llength $INCLUDE]>0 && [lsearch -exact $INCLUDE $tail]<0} continue
|
||||
source $testfile
|
||||
catch {db close}
|
||||
if {$sqlite_open_file_count>0} {
|
||||
|
||||
@@ -0,0 +1,334 @@
|
||||
# 2006 November 08
|
||||
#
|
||||
# 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 implements regression tests for SQLite library.
|
||||
#
|
||||
# This file tests the various conditions under which an SQLITE_SCHEMA
|
||||
# error should be returned. This is a copy of schema.test that
|
||||
# has been altered to use sqlite3_prepare_v2 instead of sqlite3_prepare
|
||||
#
|
||||
# $Id: schema2.test,v 1.1 2006/11/09 00:24:55 drh Exp $
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
# When any of the following types of SQL statements or actions are
|
||||
# executed, all pre-compiled statements are invalidated. An attempt
|
||||
# to execute an invalidated statement always returns SQLITE_SCHEMA.
|
||||
#
|
||||
# CREATE/DROP TABLE...................................schema2-1.*
|
||||
# CREATE/DROP VIEW....................................schema2-2.*
|
||||
# CREATE/DROP TRIGGER.................................schema2-3.*
|
||||
# CREATE/DROP INDEX...................................schema2-4.*
|
||||
# DETACH..............................................schema2-5.*
|
||||
# Deleting a user-function............................schema2-6.*
|
||||
# Deleting a collation sequence.......................schema2-7.*
|
||||
# Setting or changing the authorization function......schema2-8.*
|
||||
#
|
||||
# Test cases schema2-9.* and schema2-10.* test some specific bugs
|
||||
# that came up during development.
|
||||
#
|
||||
# Test cases schema2-11.* test that it is impossible to delete or
|
||||
# change a collation sequence or user-function while SQL statements
|
||||
# are executing. Adding new collations or functions is allowed.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
do_test schema2-1.1 {
|
||||
set ::STMT [sqlite3_prepare_v2 $::DB {SELECT * FROM sqlite_master} -1 TAIL]
|
||||
execsql {
|
||||
CREATE TABLE abc(a, b, c);
|
||||
}
|
||||
sqlite3_step $::STMT
|
||||
} {SQLITE_ROW}
|
||||
do_test schema2-1.2 {
|
||||
sqlite3_finalize $::STMT
|
||||
} {SQLITE_OK}
|
||||
do_test schema2-1.3 {
|
||||
set ::STMT [sqlite3_prepare_v2 $::DB {SELECT * FROM sqlite_master} -1 TAIL]
|
||||
execsql {
|
||||
DROP TABLE abc;
|
||||
}
|
||||
sqlite3_step $::STMT
|
||||
} {SQLITE_DONE}
|
||||
do_test schema2-1.4 {
|
||||
sqlite3_finalize $::STMT
|
||||
} {SQLITE_OK}
|
||||
|
||||
|
||||
ifcapable view {
|
||||
do_test schema2-2.1 {
|
||||
set ::STMT [sqlite3_prepare_v2 $::DB {SELECT * FROM sqlite_master} -1 TAIL]
|
||||
execsql {
|
||||
CREATE VIEW v1 AS SELECT * FROM sqlite_master;
|
||||
}
|
||||
sqlite3_step $::STMT
|
||||
} {SQLITE_ROW}
|
||||
do_test schema2-2.2 {
|
||||
sqlite3_finalize $::STMT
|
||||
} {SQLITE_OK}
|
||||
do_test schema2-2.3 {
|
||||
set ::STMT [sqlite3_prepare_v2 $::DB {SELECT * FROM sqlite_master} -1 TAIL]
|
||||
execsql {
|
||||
DROP VIEW v1;
|
||||
}
|
||||
sqlite3_step $::STMT
|
||||
} {SQLITE_DONE}
|
||||
do_test schema2-2.4 {
|
||||
sqlite3_finalize $::STMT
|
||||
} {SQLITE_OK}
|
||||
}
|
||||
|
||||
ifcapable trigger {
|
||||
do_test schema2-3.1 {
|
||||
execsql {
|
||||
CREATE TABLE abc(a, b, c);
|
||||
}
|
||||
set ::STMT [sqlite3_prepare_v2 $::DB {SELECT * FROM sqlite_master} -1 TAIL]
|
||||
execsql {
|
||||
CREATE TRIGGER abc_trig AFTER INSERT ON abc BEGIN
|
||||
SELECT 1, 2, 3;
|
||||
END;
|
||||
}
|
||||
sqlite3_step $::STMT
|
||||
} {SQLITE_ROW}
|
||||
do_test schema2-3.2 {
|
||||
sqlite3_finalize $::STMT
|
||||
} {SQLITE_OK}
|
||||
do_test schema2-3.3 {
|
||||
set ::STMT [sqlite3_prepare_v2 $::DB {SELECT * FROM sqlite_master} -1 TAIL]
|
||||
execsql {
|
||||
DROP TRIGGER abc_trig;
|
||||
}
|
||||
sqlite3_step $::STMT
|
||||
} {SQLITE_ROW}
|
||||
do_test schema2-3.4 {
|
||||
sqlite3_finalize $::STMT
|
||||
} {SQLITE_OK}
|
||||
}
|
||||
|
||||
do_test schema2-4.1 {
|
||||
catchsql {
|
||||
CREATE TABLE abc(a, b, c);
|
||||
}
|
||||
set ::STMT [sqlite3_prepare_v2 $::DB {SELECT * FROM sqlite_master} -1 TAIL]
|
||||
execsql {
|
||||
CREATE INDEX abc_index ON abc(a);
|
||||
}
|
||||
sqlite3_step $::STMT
|
||||
} {SQLITE_ROW}
|
||||
do_test schema2-4.2 {
|
||||
sqlite3_finalize $::STMT
|
||||
} {SQLITE_OK}
|
||||
do_test schema2-4.3 {
|
||||
set ::STMT [sqlite3_prepare_v2 $::DB {SELECT * FROM sqlite_master} -1 TAIL]
|
||||
execsql {
|
||||
DROP INDEX abc_index;
|
||||
}
|
||||
sqlite3_step $::STMT
|
||||
} {SQLITE_ROW}
|
||||
do_test schema2-4.4 {
|
||||
sqlite3_finalize $::STMT
|
||||
} {SQLITE_OK}
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
# Tests 5.1 to 5.4 check that prepared statements are invalidated when
|
||||
# a database is DETACHed (but not when one is ATTACHed).
|
||||
#
|
||||
do_test schema2-5.1 {
|
||||
set sql {SELECT * FROM abc;}
|
||||
set ::STMT [sqlite3_prepare_v2 $::DB $sql -1 TAIL]
|
||||
execsql {
|
||||
ATTACH 'test2.db' AS aux;
|
||||
}
|
||||
sqlite3_step $::STMT
|
||||
} {SQLITE_DONE}
|
||||
do_test schema2-5.2 {
|
||||
sqlite3_reset $::STMT
|
||||
} {SQLITE_OK}
|
||||
do_test schema2-5.3 {
|
||||
execsql {
|
||||
DETACH aux;
|
||||
}
|
||||
sqlite3_step $::STMT
|
||||
} {SQLITE_DONE}
|
||||
do_test schema2-5.4 {
|
||||
sqlite3_finalize $::STMT
|
||||
} {SQLITE_OK}
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
# Tests 6.* check that prepared statements are invalidated when
|
||||
# a user-function is deleted (but not when one is added).
|
||||
do_test schema2-6.1 {
|
||||
set sql {SELECT * FROM abc;}
|
||||
set ::STMT [sqlite3_prepare_v2 $::DB $sql -1 TAIL]
|
||||
db function hello_function {}
|
||||
sqlite3_step $::STMT
|
||||
} {SQLITE_DONE}
|
||||
do_test schema2-6.2 {
|
||||
sqlite3_reset $::STMT
|
||||
} {SQLITE_OK}
|
||||
do_test schema2-6.3 {
|
||||
sqlite_delete_function $::DB hello_function
|
||||
sqlite3_step $::STMT
|
||||
} {SQLITE_DONE}
|
||||
do_test schema2-6.4 {
|
||||
sqlite3_finalize $::STMT
|
||||
} {SQLITE_OK}
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
# Tests 7.* check that prepared statements are invalidated when
|
||||
# a collation sequence is deleted (but not when one is added).
|
||||
#
|
||||
ifcapable utf16 {
|
||||
do_test schema2-7.1 {
|
||||
set sql {SELECT * FROM abc;}
|
||||
set ::STMT [sqlite3_prepare_v2 $::DB $sql -1 TAIL]
|
||||
add_test_collate $::DB 1 1 1
|
||||
sqlite3_step $::STMT
|
||||
} {SQLITE_DONE}
|
||||
do_test schema2-7.2 {
|
||||
sqlite3_reset $::STMT
|
||||
} {SQLITE_OK}
|
||||
do_test schema2-7.3 {
|
||||
add_test_collate $::DB 0 0 0
|
||||
sqlite3_step $::STMT
|
||||
} {SQLITE_DONE}
|
||||
do_test schema2-7.4 {
|
||||
sqlite3_finalize $::STMT
|
||||
} {SQLITE_OK}
|
||||
}
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
# Tests 8.1 and 8.2 check that prepared statements are invalidated when
|
||||
# the authorization function is set.
|
||||
#
|
||||
ifcapable auth {
|
||||
do_test schema2-8.1 {
|
||||
set ::STMT [sqlite3_prepare_v2 $::DB {SELECT * FROM sqlite_master} -1 TAIL]
|
||||
db auth {}
|
||||
sqlite3_step $::STMT
|
||||
} {SQLITE_ROW}
|
||||
do_test schema2-8.3 {
|
||||
sqlite3_finalize $::STMT
|
||||
} {SQLITE_OK}
|
||||
}
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
# schema2-9.1: Test that if a table is dropped by one database connection,
|
||||
# other database connections are aware of the schema change.
|
||||
# schema2-9.2: Test that if a view is dropped by one database connection,
|
||||
# other database connections are aware of the schema change.
|
||||
#
|
||||
do_test schema2-9.1 {
|
||||
sqlite3 db2 test.db
|
||||
execsql {
|
||||
DROP TABLE abc;
|
||||
} db2
|
||||
db2 close
|
||||
catchsql {
|
||||
SELECT * FROM abc;
|
||||
}
|
||||
} {1 {no such table: abc}}
|
||||
execsql {
|
||||
CREATE TABLE abc(a, b, c);
|
||||
}
|
||||
ifcapable view {
|
||||
do_test schema2-9.2 {
|
||||
execsql {
|
||||
CREATE VIEW abcview AS SELECT * FROM abc;
|
||||
}
|
||||
sqlite3 db2 test.db
|
||||
execsql {
|
||||
DROP VIEW abcview;
|
||||
} db2
|
||||
db2 close
|
||||
catchsql {
|
||||
SELECT * FROM abcview;
|
||||
}
|
||||
} {1 {no such table: abcview}}
|
||||
}
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
# Test that if a CREATE TABLE statement fails because there are other
|
||||
# btree cursors open on the same database file it does not corrupt
|
||||
# the sqlite_master table.
|
||||
#
|
||||
do_test schema2-10.1 {
|
||||
execsql {
|
||||
INSERT INTO abc VALUES(1, 2, 3);
|
||||
}
|
||||
set sql {SELECT * FROM abc}
|
||||
set ::STMT [sqlite3_prepare_v2 $::DB $sql -1 TAIL]
|
||||
sqlite3_step $::STMT
|
||||
} {SQLITE_ROW}
|
||||
do_test schema2-10.2 {
|
||||
catchsql {
|
||||
CREATE TABLE t2(a, b, c);
|
||||
}
|
||||
} {1 {database table is locked}}
|
||||
do_test schema2-10.3 {
|
||||
sqlite3_finalize $::STMT
|
||||
} {SQLITE_OK}
|
||||
do_test schema2-10.4 {
|
||||
sqlite3 db2 test.db
|
||||
execsql {
|
||||
SELECT * FROM abc
|
||||
} db2
|
||||
} {1 2 3}
|
||||
do_test schema2-10.5 {
|
||||
db2 close
|
||||
} {}
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
# Attempting to delete or replace a user-function or collation sequence
|
||||
# while there are active statements returns an SQLITE_BUSY error.
|
||||
#
|
||||
# schema2-11.1 - 11.4: User function.
|
||||
# schema2-11.5 - 11.8: Collation sequence.
|
||||
#
|
||||
do_test schema2-11.1 {
|
||||
db function tstfunc {}
|
||||
set sql {SELECT * FROM abc}
|
||||
set ::STMT [sqlite3_prepare_v2 $::DB $sql -1 TAIL]
|
||||
sqlite3_step $::STMT
|
||||
} {SQLITE_ROW}
|
||||
do_test schema2-11.2 {
|
||||
sqlite_delete_function $::DB tstfunc
|
||||
} {SQLITE_BUSY}
|
||||
do_test schema2-11.3 {
|
||||
set rc [catch {
|
||||
db function tstfunc {}
|
||||
} msg]
|
||||
list $rc $msg
|
||||
} {1 {Unable to delete/modify user-function due to active statements}}
|
||||
do_test schema2-11.4 {
|
||||
sqlite3_finalize $::STMT
|
||||
} {SQLITE_OK}
|
||||
do_test schema2-11.5 {
|
||||
db collate tstcollate {}
|
||||
set sql {SELECT * FROM abc}
|
||||
set ::STMT [sqlite3_prepare_v2 $::DB $sql -1 TAIL]
|
||||
sqlite3_step $::STMT
|
||||
} {SQLITE_ROW}
|
||||
do_test schema2-11.6 {
|
||||
sqlite_delete_collation $::DB tstcollate
|
||||
} {SQLITE_BUSY}
|
||||
do_test schema2-11.7 {
|
||||
set rc [catch {
|
||||
db collate tstcollate {}
|
||||
} msg]
|
||||
list $rc $msg
|
||||
} {1 {Unable to delete/modify collation sequence due to active statements}}
|
||||
do_test schema2-11.8 {
|
||||
sqlite3_finalize $::STMT
|
||||
} {SQLITE_OK}
|
||||
|
||||
finish_test
|
||||
@@ -12,7 +12,7 @@
|
||||
# focus of this file is testing SELECT statements that contain
|
||||
# subqueries in their FROM clause.
|
||||
#
|
||||
# $Id: select6.test,v 1.24 2006/06/11 23:41:56 drh Exp $
|
||||
# $Id: select6.test,v 1.26 2006/11/30 13:06:00 drh Exp $
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
# focus of this file is testing compute SELECT statements and nested
|
||||
# views.
|
||||
#
|
||||
# $Id: select7.test,v 1.7 2005/03/29 03:11:00 danielk1977 Exp $
|
||||
# $Id: select7.test,v 1.8 2006/10/13 15:34:17 drh Exp $
|
||||
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
@@ -71,5 +71,39 @@ ifcapable subquery {
|
||||
}
|
||||
} [list 0 [execsql {SELECT * FROM sqlite_master ORDER BY name}]]
|
||||
}
|
||||
finish_test
|
||||
|
||||
# Ticket #2018 - Make sure names are resolved correctly on all
|
||||
# SELECT statements of a compound subquery.
|
||||
#
|
||||
ifcapable {subquery && compound} {
|
||||
do_test select7-4.1 {
|
||||
execsql {
|
||||
CREATE TABLE IF NOT EXISTS photo(pk integer primary key, x);
|
||||
CREATE TABLE IF NOT EXISTS tag(pk integer primary key, fk int, name);
|
||||
|
||||
SELECT P.pk from PHOTO P WHERE NOT EXISTS (
|
||||
SELECT T2.pk from TAG T2 WHERE T2.fk = P.pk
|
||||
EXCEPT
|
||||
SELECT T3.pk from TAG T3 WHERE T3.fk = P.pk AND T3.name LIKE '%foo%'
|
||||
);
|
||||
}
|
||||
} {}
|
||||
do_test select7-4.2 {
|
||||
execsql {
|
||||
INSERT INTO photo VALUES(1,1);
|
||||
INSERT INTO photo VALUES(2,2);
|
||||
INSERT INTO photo VALUES(3,3);
|
||||
INSERT INTO tag VALUES(11,1,'one');
|
||||
INSERT INTO tag VALUES(12,1,'two');
|
||||
INSERT INTO tag VALUES(21,1,'one-b');
|
||||
SELECT P.pk from PHOTO P WHERE NOT EXISTS (
|
||||
SELECT T2.pk from TAG T2 WHERE T2.fk = P.pk
|
||||
EXCEPT
|
||||
SELECT T3.pk from TAG T3 WHERE T3.fk = P.pk AND T3.name LIKE '%foo%'
|
||||
);
|
||||
}
|
||||
} {2 3}
|
||||
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -0,0 +1,280 @@
|
||||
# 2006 November 23
|
||||
#
|
||||
# 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 implements regression tests for SQLite library. The
|
||||
# focus of this script is measuring executing speed.
|
||||
#
|
||||
# $Id: speed1.test,v 1.2 2006/11/30 13:06:00 drh Exp $
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
set sqlout [open speed1.txt w]
|
||||
proc tracesql {sql} {
|
||||
puts $::sqlout $sql\;
|
||||
}
|
||||
db trace tracesql
|
||||
|
||||
# The number_name procedure below converts its argment (an integer)
|
||||
# into a string which is the English-language name for that number.
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# puts [number_name 123] -> "one hundred twenty three"
|
||||
#
|
||||
set ones {zero one two three four five six seven eight nine
|
||||
ten eleven twelve thirteen fourteen fifteen sixteen seventeen
|
||||
eighteen nineteen}
|
||||
set tens {{} ten twenty thirty forty fifty sixty seventy eighty ninety}
|
||||
proc number_name {n} {
|
||||
if {$n>=1000} {
|
||||
set txt "[number_name [expr {$n/1000}]] thousand"
|
||||
set n [expr {$n%1000}]
|
||||
} else {
|
||||
set txt {}
|
||||
}
|
||||
if {$n>=100} {
|
||||
append txt " [lindex $::ones [expr {$n/100}]] hundred"
|
||||
set n [expr {$n%100}]
|
||||
}
|
||||
if {$n>=20} {
|
||||
append txt " [lindex $::tens [expr {$n/10}]]"
|
||||
set n [expr {$n%10}]
|
||||
}
|
||||
if {$n>0} {
|
||||
append txt " [lindex $::ones $n]"
|
||||
}
|
||||
set txt [string trim $txt]
|
||||
if {$txt==""} {set txt zero}
|
||||
return $txt
|
||||
}
|
||||
|
||||
# Create a database schema.
|
||||
#
|
||||
do_test speed1-1.0 {
|
||||
execsql {
|
||||
pragma page_size=4096;
|
||||
CREATE TABLE t1(a INTEGER, b INTEGER, c TEXT);
|
||||
CREATE TABLE t2(a INTEGER, b INTEGER, c TEXT);
|
||||
CREATE INDEX i2a ON t2(a);
|
||||
CREATE INDEX i2b ON t2(b);
|
||||
SELECT name FROM sqlite_master ORDER BY 1;
|
||||
}
|
||||
} {i2a i2b t1 t2}
|
||||
|
||||
|
||||
# 50000 INSERTs on an unindexed table
|
||||
#
|
||||
set sql {}
|
||||
for {set i 1} {$i<=50000} {incr i} {
|
||||
set r [expr {int(rand()*500000)}]
|
||||
append sql "INSERT INTO t1 VALUES($i,$r,'[number_name $r]');\n"
|
||||
}
|
||||
db eval BEGIN
|
||||
speed_trial speed1-insert1 50000 row $sql
|
||||
db eval COMMIT
|
||||
|
||||
# 50000 INSERTs on an indexed table
|
||||
#
|
||||
set sql {}
|
||||
for {set i 1} {$i<=50000} {incr i} {
|
||||
set r [expr {int(rand()*500000)}]
|
||||
append sql "INSERT INTO t2 VALUES($i,$r,'[number_name $r]');\n"
|
||||
}
|
||||
db eval BEGIN
|
||||
speed_trial speed1-insert2 50000 row $sql
|
||||
db eval COMMIT
|
||||
|
||||
|
||||
|
||||
# 50 SELECTs on an integer comparison. There is no index so
|
||||
# a full table scan is required.
|
||||
#
|
||||
set sql {}
|
||||
for {set i 0} {$i<50} {incr i} {
|
||||
set lwr [expr {$i*100}]
|
||||
set upr [expr {($i+10)*100}]
|
||||
append sql "SELECT count(*), avg(b) FROM t1 WHERE b>=$lwr AND b<$upr;"
|
||||
}
|
||||
db eval BEGIN
|
||||
speed_trial speed1-select1 [expr {50*50000}] row $sql
|
||||
db eval COMMIT
|
||||
|
||||
# 50 SELECTs on an LIKE comparison. There is no index so a full
|
||||
# table scan is required.
|
||||
#
|
||||
set sql {}
|
||||
for {set i 0} {$i<50} {incr i} {
|
||||
append sql \
|
||||
"SELECT count(*), avg(b) FROM t1 WHERE c LIKE '%[number_name $i]%';"
|
||||
}
|
||||
db eval BEGIN
|
||||
speed_trial speed1-select2 [expr {50*50000}] row $sql
|
||||
db eval COMMIT
|
||||
|
||||
# Create indices
|
||||
#
|
||||
db eval BEGIN
|
||||
speed_trial speed1-createidx 150000 row {
|
||||
CREATE INDEX i1a ON t1(a);
|
||||
CREATE INDEX i1b ON t1(b);
|
||||
CREATE INDEX i1c ON t1(c);
|
||||
}
|
||||
db eval COMMIT
|
||||
|
||||
# 5000 SELECTs on an integer comparison where the integer is
|
||||
# indexed.
|
||||
#
|
||||
set sql {}
|
||||
for {set i 0} {$i<5000} {incr i} {
|
||||
set lwr [expr {$i*100}]
|
||||
set upr [expr {($i+10)*100}]
|
||||
append sql "SELECT count(*), avg(b) FROM t1 WHERE b>=$lwr AND b<$upr;"
|
||||
}
|
||||
db eval BEGIN
|
||||
speed_trial speed1-select3 5000 stmt $sql
|
||||
db eval COMMIT
|
||||
|
||||
# 100000 random SELECTs against rowid.
|
||||
#
|
||||
set sql {}
|
||||
for {set i 1} {$i<=100000} {incr i} {
|
||||
set id [expr {int(rand()*50000)+1}]
|
||||
append sql "SELECT c FROM t1 WHERE rowid=$id;"
|
||||
}
|
||||
db eval BEGIN
|
||||
speed_trial speed1-select4 100000 row $sql
|
||||
db eval COMMIT
|
||||
|
||||
# 100000 random SELECTs against a unique indexed column.
|
||||
#
|
||||
set sql {}
|
||||
for {set i 1} {$i<=100000} {incr i} {
|
||||
set id [expr {int(rand()*50000)+1}]
|
||||
append sql "SELECT c FROM t1 WHERE a=$id;"
|
||||
}
|
||||
db eval BEGIN
|
||||
speed_trial speed1-select5 100000 row $sql
|
||||
db eval COMMIT
|
||||
|
||||
# 50000 random SELECTs against an indexed column text column
|
||||
#
|
||||
set sql {}
|
||||
db eval {SELECT c FROM t1 ORDER BY random() LIMIT 50000} {
|
||||
append sql "SELECT c FROM t1 WHERE c='$c';"
|
||||
}
|
||||
db eval BEGIN
|
||||
speed_trial speed1-select6 50000 row $sql
|
||||
db eval COMMIT
|
||||
|
||||
|
||||
# Vacuum
|
||||
speed_trial speed1-vacuum 100000 row VACUUM
|
||||
|
||||
# 5000 updates of ranges where the field being compared is indexed.
|
||||
#
|
||||
set sql {}
|
||||
for {set i 0} {$i<5000} {incr i} {
|
||||
set lwr [expr {$i*2}]
|
||||
set upr [expr {($i+1)*2}]
|
||||
append sql "UPDATE t1 SET b=b*2 WHERE a>=$lwr AND a<$upr;"
|
||||
}
|
||||
db eval BEGIN
|
||||
speed_trial speed1-update1 5000 stmt $sql
|
||||
db eval COMMIT
|
||||
|
||||
# 50000 single-row updates. An index is used to find the row quickly.
|
||||
#
|
||||
set sql {}
|
||||
for {set i 0} {$i<50000} {incr i} {
|
||||
set r [expr {int(rand()*500000)}]
|
||||
append sql "UPDATE t1 SET b=$r WHERE a=$i;"
|
||||
}
|
||||
db eval BEGIN
|
||||
speed_trial speed1-update2 50000 row $sql
|
||||
db eval COMMIT
|
||||
|
||||
# 1 big text update that touches every row in the table.
|
||||
#
|
||||
speed_trial speed1-update3 50000 row {
|
||||
UPDATE t1 SET c=a;
|
||||
}
|
||||
|
||||
# Many individual text updates. Each row in the table is
|
||||
# touched through an index.
|
||||
#
|
||||
set sql {}
|
||||
for {set i 1} {$i<=50000} {incr i} {
|
||||
set r [expr {int(rand()*500000)}]
|
||||
append sql "UPDATE t1 SET c='[number_name $r]' WHERE a=$i;"
|
||||
}
|
||||
db eval BEGIN
|
||||
speed_trial speed1-update4 50000 row $sql
|
||||
db eval COMMIT
|
||||
|
||||
# Delete all content in a table.
|
||||
#
|
||||
speed_trial speed1-delete1 50000 row {DELETE FROM t1}
|
||||
|
||||
# Copy one table into another
|
||||
#
|
||||
speed_trial speed1-copy1 50000 row {INSERT INTO t1 SELECT * FROM t2}
|
||||
|
||||
# Delete all content in a table, one row at a time.
|
||||
#
|
||||
speed_trial speed1-delete2 50000 row {DELETE FROM t1 WHERE 1}
|
||||
|
||||
# Refill the table yet again
|
||||
#
|
||||
speed_trial speed1-copy2 50000 row {INSERT INTO t1 SELECT * FROM t2}
|
||||
|
||||
# Drop the table and recreate it without its indices.
|
||||
#
|
||||
db eval BEGIN
|
||||
speed_trial speed1-drop1 50000 row {
|
||||
DROP TABLE t1;
|
||||
CREATE TABLE t1(a INTEGER, b INTEGER, c TEXT);
|
||||
}
|
||||
db eval COMMIT
|
||||
|
||||
# Refill the table yet again. This copy should be faster because
|
||||
# there are no indices to deal with.
|
||||
#
|
||||
speed_trial speed1-copy3 50000 row {INSERT INTO t1 SELECT * FROM t2}
|
||||
|
||||
# Select 20000 rows from the table at random.
|
||||
#
|
||||
speed_trial speed1-random1 50000 row {
|
||||
SELECT rowid FROM t1 ORDER BY random() LIMIT 20000
|
||||
}
|
||||
|
||||
# Delete 20000 random rows from the table.
|
||||
#
|
||||
speed_trial speed1-random-del1 20000 row {
|
||||
DELETE FROM t1 WHERE rowid IN
|
||||
(SELECT rowid FROM t1 ORDER BY random() LIMIT 20000)
|
||||
}
|
||||
do_test speed1-1.1 {
|
||||
db one {SELECT count(*) FROM t1}
|
||||
} 30000
|
||||
|
||||
|
||||
# Delete 20000 more rows at random from the table.
|
||||
#
|
||||
speed_trial speed1-random-del2 20000 row {
|
||||
DELETE FROM t1 WHERE rowid IN
|
||||
(SELECT rowid FROM t1 ORDER BY random() LIMIT 20000)
|
||||
}
|
||||
do_test speed1-1.2 {
|
||||
db one {SELECT count(*) FROM t1}
|
||||
} 10000
|
||||
|
||||
finish_test
|
||||
@@ -12,7 +12,7 @@
|
||||
# focus of this file is testing the sqlite_exec_printf() and
|
||||
# sqlite_get_table_printf() APIs.
|
||||
#
|
||||
# $Id: tableapi.test,v 1.11 2006/06/27 20:39:05 drh Exp $
|
||||
# $Id: tableapi.test,v 1.12 2007/01/05 00:14:28 drh Exp $
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
@@ -208,7 +208,7 @@ do_test tableapi-5.2 {
|
||||
|
||||
do_test tableapi-6.1 {
|
||||
sqlite3_get_table_printf $::dbx {PRAGMA user_version} {}
|
||||
} {0 1 1 {} 0}
|
||||
} {0 1 1 user_version 0}
|
||||
|
||||
do_test tableapi-99.0 {
|
||||
sqlite3_close $::dbx
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
# This file implements some common TCL routines used for regression
|
||||
# testing the SQLite library
|
||||
#
|
||||
# $Id: tester.tcl,v 1.69 2006/10/04 11:55:50 drh Exp $
|
||||
# $Id: tester.tcl,v 1.72 2007/01/04 14:58:14 drh Exp $
|
||||
|
||||
# Make sure tclsqlite3 was compiled correctly. Abort now with an
|
||||
# error message if not.
|
||||
@@ -78,6 +78,9 @@ set nTest 0
|
||||
set skip_test 0
|
||||
set failList {}
|
||||
set maxErr 1000
|
||||
if {![info exists speedTest]} {
|
||||
set speedTest 0
|
||||
}
|
||||
|
||||
# Invoke the do_test procedure to run a single test
|
||||
#
|
||||
@@ -118,6 +121,21 @@ proc do_test {name cmd expected} {
|
||||
}
|
||||
}
|
||||
|
||||
# Run an SQL script.
|
||||
# Return the number of microseconds per statement.
|
||||
#
|
||||
proc speed_trial {name numstmt units sql} {
|
||||
puts -nonewline [format {%-20.20s } $name...]
|
||||
flush stdout
|
||||
set speed [time {sqlite3_exec_nr db $sql}]
|
||||
set tm [lindex $speed 0]
|
||||
set per [expr {$tm/(1.0*$numstmt)}]
|
||||
set rate [expr {1000000.0*$numstmt/$tm}]
|
||||
set u1 us/$units
|
||||
set u2 $units/s
|
||||
puts [format {%20.3f %-7s %20.5f %s} $per $u1 $rate $u2]
|
||||
}
|
||||
|
||||
# The procedure uses the special "sqlite_malloc_stat" command
|
||||
# (which is only available if SQLite is compiled with -DSQLITE_DEBUG=1)
|
||||
# to see how many malloc()s have not been free()ed. The number
|
||||
@@ -334,10 +352,13 @@ proc do_ioerr_test {testname args} {
|
||||
set ::ioerropts(-start) 1
|
||||
set ::ioerropts(-cksum) 0
|
||||
set ::ioerropts(-erc) 0
|
||||
set ::ioerropts(-count) 100000000
|
||||
array set ::ioerropts $args
|
||||
|
||||
set ::go 1
|
||||
for {set n $::ioerropts(-start)} {$::go} {incr n} {
|
||||
incr ::ioerropts(-count) -1
|
||||
if {$::ioerropts(-count)<0} break
|
||||
|
||||
# Skip this IO error if it was specified with the "-exclude" option.
|
||||
if {[info exists ::ioerropts(-exclude)]} {
|
||||
|
||||
@@ -39,12 +39,13 @@ volatile int all_stop = 0;
|
||||
** global variable to stop all other activity. Print the error message
|
||||
** or print OK if the string "ok" is seen.
|
||||
*/
|
||||
int check_callback(void *notUsed, int argc, char **argv, char **notUsed2){
|
||||
int check_callback(void *pid, int argc, char **argv, char **notUsed2){
|
||||
int id = (int)pid;
|
||||
if( strcmp(argv[0],"ok") ){
|
||||
all_stop = 1;
|
||||
fprintf(stderr,"pid=%d. %s\n", getpid(), argv[0]);
|
||||
fprintf(stderr,"id: %s\n", id, argv[0]);
|
||||
}else{
|
||||
/* fprintf(stderr,"pid=%d. OK\n", getpid()); */
|
||||
/* fprintf(stderr,"%d: OK\n", id); */
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -53,13 +54,13 @@ int check_callback(void *notUsed, int argc, char **argv, char **notUsed2){
|
||||
** Do an integrity check on the database. If the first integrity check
|
||||
** fails, try it a second time.
|
||||
*/
|
||||
int integrity_check(sqlite *db){
|
||||
int integrity_check(sqlite *db, int id){
|
||||
int rc;
|
||||
if( all_stop ) return 0;
|
||||
/* fprintf(stderr,"pid=%d: CHECK\n", getpid()); */
|
||||
/* fprintf(stderr,"%d: CHECK\n", id); */
|
||||
rc = sqlite3_exec(db, "pragma integrity_check", check_callback, 0, 0);
|
||||
if( rc!=SQLITE_OK && rc!=SQLITE_BUSY ){
|
||||
fprintf(stderr,"pid=%d, Integrity check returns %d\n", getpid(), rc);
|
||||
fprintf(stderr,"%d, Integrity check returns %d\n", id, rc);
|
||||
}
|
||||
if( all_stop ){
|
||||
sqlite3_exec(db, "pragma integrity_check", check_callback, 0, 0);
|
||||
@@ -70,21 +71,24 @@ int integrity_check(sqlite *db){
|
||||
/*
|
||||
** This is the worker thread
|
||||
*/
|
||||
void *worker(void *notUsed){
|
||||
void *worker(void *workerArg){
|
||||
sqlite *db;
|
||||
int id = (int)workerArg;
|
||||
int rc;
|
||||
int cnt = 0;
|
||||
fprintf(stderr, "Starting worker %d\n", id);
|
||||
while( !all_stop && cnt++<10000 ){
|
||||
if( cnt%1000==0 ) printf("pid=%d: %d\n", getpid(), cnt);
|
||||
if( cnt%100==0 ) printf("%d: %d\n", id, cnt);
|
||||
while( (sqlite3_open(DB_FILE, &db))!=SQLITE_OK ) sched_yield();
|
||||
sqlite3_exec(db, "PRAGMA synchronous=OFF", 0, 0, 0);
|
||||
integrity_check(db);
|
||||
/* integrity_check(db, id); */
|
||||
if( all_stop ){ sqlite3_close(db); break; }
|
||||
/* fprintf(stderr, "pid=%d: BEGIN\n", getpid()); */
|
||||
/* fprintf(stderr, "%d: BEGIN\n", id); */
|
||||
rc = sqlite3_exec(db, "INSERT INTO t1 VALUES('bogus data')", 0, 0, 0);
|
||||
/* fprintf(stderr, "pid=%d: END rc=%d\n", getpid(), rc); */
|
||||
/* fprintf(stderr, "%d: END rc=%d\n", id, rc); */
|
||||
sqlite3_close(db);
|
||||
}
|
||||
fprintf(stderr, "Worker %d finished\n", id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -100,7 +104,7 @@ int main(int argc, char **argv){
|
||||
char *zJournal = sqlite3_mprintf("%s-journal", DB_FILE);
|
||||
unlink(DB_FILE);
|
||||
unlink(zJournal);
|
||||
free(zJournal);
|
||||
sqlite3_free(zJournal);
|
||||
}
|
||||
sqlite3_open(DB_FILE, &db);
|
||||
if( db==0 ){
|
||||
@@ -114,7 +118,7 @@ int main(int argc, char **argv){
|
||||
}
|
||||
sqlite3_close(db);
|
||||
for(i=0; i<sizeof(aThread)/sizeof(aThread[0]); i++){
|
||||
pthread_create(&aThread[i], 0, worker, 0);
|
||||
pthread_create(&aThread[i], 0, worker, (void*)i);
|
||||
}
|
||||
for(i=0; i<sizeof(aThread)/sizeof(aThread[i]); i++){
|
||||
pthread_join(aThread[i], 0);
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
# 2007 January 03
|
||||
#
|
||||
# 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 implements regression tests for SQLite library.
|
||||
#
|
||||
# This file implements tests to verify that ticket #2141 has been
|
||||
# fixed.
|
||||
#
|
||||
#
|
||||
# $Id: tkt2141.test,v 1.1 2007/01/04 01:20:29 drh Exp $
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
|
||||
do_test tkt2141-1.1 {
|
||||
execsql {
|
||||
CREATE TABLE tab1 (t1_id integer PRIMARY KEY, t1_desc);
|
||||
INSERT INTO tab1 VALUES(1,'rec 1 tab 1');
|
||||
CREATE TABLE tab2 (t2_id integer PRIMARY KEY, t2_id_t1, t2_desc);
|
||||
INSERT INTO tab2 VALUES(1,1,'rec 1 tab 2');
|
||||
CREATE TABLE tab3 (t3_id integer PRIMARY KEY, t3_id_t2, t3_desc);
|
||||
INSERT INTO tab3 VALUES(1,1,'aa');
|
||||
SELECT *
|
||||
FROM tab1 t1 LEFT JOIN tab2 t2 ON t1.t1_id = t2.t2_id_t1
|
||||
WHERE t2.t2_id IN
|
||||
(SELECT t2_id FROM tab2, tab3 ON t2_id = t3_id_t2
|
||||
WHERE t3_id IN (1,2) GROUP BY t2_id);
|
||||
}
|
||||
} {1 {rec 1 tab 1} 1 1 {rec 1 tab 2}}
|
||||
do_test tkt2141-1.2 {
|
||||
execsql {
|
||||
SELECT *
|
||||
FROM tab1 t1 LEFT JOIN tab2 t2 ON t1.t1_id = t2.t2_id_t1
|
||||
WHERE t2.t2_id IN
|
||||
(SELECT t2_id FROM tab2, tab3 ON t2_id = t3_id_t2
|
||||
WHERE t3_id IN (1,2));
|
||||
}
|
||||
} {1 {rec 1 tab 1} 1 1 {rec 1 tab 2}}
|
||||
do_test tkt2141-1.3 {
|
||||
execsql {
|
||||
SELECT *
|
||||
FROM tab1 t1 LEFT JOIN tab2 t2
|
||||
WHERE t2.t2_id IN
|
||||
(SELECT t2_id FROM tab2, tab3 ON t2_id = t3_id_t2
|
||||
WHERE t3_id IN (1,2));
|
||||
}
|
||||
} {1 {rec 1 tab 1} 1 1 {rec 1 tab 2}}
|
||||
|
||||
finish_test
|
||||
@@ -0,0 +1,136 @@
|
||||
# 2007 January 26
|
||||
#
|
||||
# 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 implements regression tests for SQLite library.
|
||||
#
|
||||
# This file implements tests to verify that ticket #2192 has been
|
||||
# fixed.
|
||||
#
|
||||
#
|
||||
# $Id: tkt2192.test,v 1.1 2007/01/26 19:04:00 drh Exp $
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
|
||||
do_test tkt2191-1.1 {
|
||||
execsql {
|
||||
-- Raw data (RBS) --------
|
||||
|
||||
create table records (
|
||||
date real,
|
||||
type text,
|
||||
description text,
|
||||
value integer,
|
||||
acc_name text,
|
||||
acc_no text
|
||||
);
|
||||
|
||||
-- Direct Debits ----------------
|
||||
create view direct_debits as
|
||||
select * from records where type = 'D/D';
|
||||
|
||||
create view monthly_direct_debits as
|
||||
select strftime('%Y-%m', date) as date, (-1 * sum(value)) as value
|
||||
from direct_debits
|
||||
group by strftime('%Y-%m', date);
|
||||
|
||||
-- Expense Categories ---------------
|
||||
create view energy as
|
||||
select strftime('%Y-%m', date) as date, (-1 * sum(value)) as value
|
||||
from direct_debits
|
||||
where description like '%NPOWER%'
|
||||
group by strftime('%Y-%m', date);
|
||||
|
||||
create view phone_internet as
|
||||
select strftime('%Y-%m', date) as date, (-1 * sum(value)) as value
|
||||
from direct_debits
|
||||
where description like '%BT DIRECT%'
|
||||
or description like '%SUPANET%'
|
||||
or description like '%ORANGE%'
|
||||
group by strftime('%Y-%m', date);
|
||||
|
||||
create view credit_cards as
|
||||
select strftime('%Y-%m', date) as date, (-1 * sum(value)) as value
|
||||
from direct_debits where description like '%VISA%'
|
||||
group by strftime('%Y-%m', date);
|
||||
|
||||
-- Overview ---------------------
|
||||
|
||||
create view expense_overview as
|
||||
select 'Energy' as expense, date, value from energy
|
||||
union
|
||||
select 'Phone/Internet' as expense, date, value from phone_internet
|
||||
union
|
||||
select 'Credit Card' as expense, date, value from credit_cards;
|
||||
|
||||
create view jan as
|
||||
select 'jan', expense, value from expense_overview
|
||||
where date like '%-01';
|
||||
|
||||
create view nov as
|
||||
select 'nov', expense, value from expense_overview
|
||||
where date like '%-11';
|
||||
|
||||
create view summary as
|
||||
select * from jan join nov on (jan.expense = nov.expense);
|
||||
}
|
||||
} {}
|
||||
do_test tkt2192-1.2 {
|
||||
# set ::sqlite_addop_trace 1
|
||||
execsql {
|
||||
select * from summary;
|
||||
}
|
||||
} {}
|
||||
do_test tkt2192-2.1 {
|
||||
execsql {
|
||||
CREATE TABLE t1(a,b);
|
||||
CREATE VIEW v1 AS
|
||||
SELECT * FROM t1 WHERE b%7=0 UNION SELECT * FROM t1 WHERE b%5=0;
|
||||
INSERT INTO t1 VALUES(1,7);
|
||||
INSERT INTO t1 VALUES(2,10);
|
||||
INSERT INTO t1 VALUES(3,14);
|
||||
INSERT INTO t1 VALUES(4,15);
|
||||
INSERT INTO t1 VALUES(1,16);
|
||||
INSERT INTO t1 VALUES(2,17);
|
||||
INSERT INTO t1 VALUES(3,20);
|
||||
INSERT INTO t1 VALUES(4,21);
|
||||
INSERT INTO t1 VALUES(1,22);
|
||||
INSERT INTO t1 VALUES(2,24);
|
||||
INSERT INTO t1 VALUES(3,25);
|
||||
INSERT INTO t1 VALUES(4,26);
|
||||
INSERT INTO t1 VALUES(1,27);
|
||||
|
||||
SELECT b FROM v1 ORDER BY b;
|
||||
}
|
||||
} {7 10 14 15 20 21 25}
|
||||
do_test tkt2192-2.2 {
|
||||
execsql {
|
||||
SELECT * FROM v1 ORDER BY a, b;
|
||||
}
|
||||
} {1 7 2 10 3 14 3 20 3 25 4 15 4 21}
|
||||
do_test tkt2192-2.3 {
|
||||
execsql {
|
||||
SELECT x.a || '/' || x.b || '/' || y.b
|
||||
FROM v1 AS x JOIN v1 AS y ON x.a=y.a AND x.b<y.b
|
||||
ORDER BY x.a, x.b, y.b
|
||||
}
|
||||
} {3/14/20 3/14/25 3/20/25 4/15/21}
|
||||
do_test tkt2192-2.4 {
|
||||
execsql {
|
||||
CREATE VIEW v2 AS
|
||||
SELECT x.a || '/' || x.b || '/' || y.b AS z
|
||||
FROM v1 AS x JOIN v1 AS y ON x.a=y.a AND x.b<y.b
|
||||
ORDER BY x.a, x.b, y.b;
|
||||
SELECT * FROM v2;
|
||||
}
|
||||
} {3/14/20 3/14/25 3/20/25 4/15/21}
|
||||
|
||||
finish_test
|
||||
@@ -0,0 +1,30 @@
|
||||
# 2007 Febuary 05
|
||||
#
|
||||
# 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 implements regression tests for SQLite library.
|
||||
#
|
||||
# This file implements tests to verify that ticket #2213 has been
|
||||
# fixed.
|
||||
#
|
||||
#
|
||||
# $Id: tkt2213.test,v 1.1 2007/02/05 14:21:48 danielk1977 Exp $
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
do_test tkt2213-1 {
|
||||
sqlite3_create_function db
|
||||
catchsql {
|
||||
SELECT tkt2213func(tkt2213func('abcd'));
|
||||
}
|
||||
} {0 abcd}
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -194,7 +194,7 @@ do_test trigger4-7.2 {
|
||||
} {101 1001 102 2002 227 2127 228 2128}
|
||||
|
||||
integrity_check trigger4-99.9
|
||||
|
||||
db close
|
||||
file delete -force trigtest.db trigtest.db-journal
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
#***********************************************************************
|
||||
# This file runs all tests.
|
||||
#
|
||||
# $Id: utf16.test,v 1.5 2006/01/09 23:40:26 drh Exp $
|
||||
# $Id: utf16.test,v 1.6 2007/01/04 16:37:04 drh Exp $
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
@@ -23,7 +23,7 @@ if { [llength $argv]>0 } {
|
||||
set argv [list]
|
||||
} else {
|
||||
set F {
|
||||
alter.test alter2.test alter3.test
|
||||
alter.test alter3.test
|
||||
auth.test bind.test blob.test capi2.test capi3.test collate1.test
|
||||
collate2.test collate3.test collate4.test collate5.test collate6.test
|
||||
conflict.test date.test delete.test expr.test fkey1.test func.test
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
# This file implements regression tests for SQLite library. The
|
||||
# focus of this file is creating and dropping virtual tables.
|
||||
#
|
||||
# $Id: vtab1.test,v 1.38 2006/09/16 21:45:14 drh Exp $
|
||||
# $Id: vtab1.test,v 1.39 2007/01/09 14:01:14 drh Exp $
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
@@ -96,6 +96,29 @@ do_test vtab1-1.6 {
|
||||
}
|
||||
} {}
|
||||
|
||||
# Ticket #2156. Using the sqlite3_prepare_v2() API, make sure that
|
||||
# a CREATE VIRTUAL TABLE statement can be used multiple times.
|
||||
#
|
||||
do_test vtab1-1.2152.1 {
|
||||
set DB [sqlite3_connection_pointer db]
|
||||
set sql {CREATE VIRTUAL TABLE t2152a USING echo(t2152b)}
|
||||
set STMT [sqlite3_prepare_v2 $DB $sql -1 TAIL]
|
||||
sqlite3_step $STMT
|
||||
} SQLITE_ERROR
|
||||
do_test vtab-1.2152.2 {
|
||||
sqlite3_reset $STMT
|
||||
sqlite3_step $STMT
|
||||
} SQLITE_ERROR
|
||||
do_test vtab-1.2152.3 {
|
||||
sqlite3_reset $STMT
|
||||
db eval {CREATE TABLE t2152b(x,y)}
|
||||
sqlite3_step $STMT
|
||||
} SQLITE_DONE
|
||||
do_test vtab-1.2152.4 {
|
||||
sqlite3_finalize $STMT
|
||||
db eval {DROP TABLE t2152a; DROP TABLE t2152b}
|
||||
} {}
|
||||
|
||||
# Test to make sure nothing goes wrong and no memory is leaked if we
|
||||
# select an illegal table-name (i.e a reserved name or the name of a
|
||||
# table that already exists).
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user