mirror of
https://github.com/sqlite/sqlite.git
synced 2025-11-21 09:00:59 +03:00
Merge changes from the trunk into the threads branch.
FossilOrigin-Name: 416cb091267de91b9cadee9f7453b8627570b7d3
This commit is contained in:
@@ -1002,7 +1002,7 @@ static void analyzeOneTable(
|
||||
if( aGotoChng==0 ) continue;
|
||||
|
||||
/* Populate the register containing the index name. */
|
||||
if( pIdx->autoIndex==2 && !HasRowid(pTab) ){
|
||||
if( IsPrimaryKeyIndex(pIdx) && !HasRowid(pTab) ){
|
||||
zIdxName = pTab->zName;
|
||||
}else{
|
||||
zIdxName = pIdx->zName;
|
||||
|
||||
@@ -4218,12 +4218,6 @@ static const void *fetchPayload(
|
||||
assert( cursorHoldsMutex(pCur) );
|
||||
assert( pCur->aiIdx[pCur->iPage]<pCur->apPage[pCur->iPage]->nCell );
|
||||
assert( pCur->info.nSize>0 );
|
||||
#if 0
|
||||
if( pCur->info.nSize==0 ){
|
||||
btreeParseCell(pCur->apPage[pCur->iPage], pCur->aiIdx[pCur->iPage],
|
||||
&pCur->info);
|
||||
}
|
||||
#endif
|
||||
*pAmt = pCur->info.nLocal;
|
||||
return (void*)(pCur->info.pCell + pCur->info.nHeader);
|
||||
}
|
||||
|
||||
19
src/build.c
19
src/build.c
@@ -757,7 +757,7 @@ int sqlite3CheckObjectName(Parse *pParse, const char *zName){
|
||||
*/
|
||||
Index *sqlite3PrimaryKeyIndex(Table *pTab){
|
||||
Index *p;
|
||||
for(p=pTab->pIndex; p && p->autoIndex!=2; p=p->pNext){}
|
||||
for(p=pTab->pIndex; p && !IsPrimaryKeyIndex(p); p=p->pNext){}
|
||||
return p;
|
||||
}
|
||||
|
||||
@@ -1286,7 +1286,7 @@ void sqlite3AddPrimaryKey(
|
||||
p = sqlite3CreateIndex(pParse, 0, 0, 0, pList, onError, 0,
|
||||
0, sortOrder, 0);
|
||||
if( p ){
|
||||
p->autoIndex = 2;
|
||||
p->idxType = SQLITE_IDXTYPE_PRIMARYKEY;
|
||||
if( v ) sqlite3VdbeJumpHere(v, pParse->addrSkipPK);
|
||||
}
|
||||
pList = 0;
|
||||
@@ -1661,7 +1661,7 @@ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){
|
||||
assert( pParse->pNewTable==pTab );
|
||||
pPk = sqlite3CreateIndex(pParse, 0, 0, 0, pList, pTab->keyConf, 0, 0, 0, 0);
|
||||
if( pPk==0 ) return;
|
||||
pPk->autoIndex = 2;
|
||||
pPk->idxType = SQLITE_IDXTYPE_PRIMARYKEY;
|
||||
pTab->iPKey = -1;
|
||||
}else{
|
||||
pPk = sqlite3PrimaryKeyIndex(pTab);
|
||||
@@ -1684,7 +1684,7 @@ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){
|
||||
*/
|
||||
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
|
||||
int n;
|
||||
if( pIdx->autoIndex==2 ) continue;
|
||||
if( IsPrimaryKeyIndex(pIdx) ) continue;
|
||||
for(i=n=0; i<nPk; i++){
|
||||
if( !hasColumn(pIdx->aiColumn, pIdx->nKeyCol, pPk->aiColumn[i]) ) n++;
|
||||
}
|
||||
@@ -2764,7 +2764,7 @@ Index *sqlite3AllocateIndexObject(
|
||||
**
|
||||
** If the index is created successfully, return a pointer to the new Index
|
||||
** structure. This is used by sqlite3AddPrimaryKey() to mark the index
|
||||
** as the tables primary key (Index.autoIndex==2).
|
||||
** as the tables primary key (Index.idxType==SQLITE_IDXTYPE_PRIMARYKEY)
|
||||
*/
|
||||
Index *sqlite3CreateIndex(
|
||||
Parse *pParse, /* All information about this parse */
|
||||
@@ -2979,7 +2979,7 @@ Index *sqlite3CreateIndex(
|
||||
pIndex->pTable = pTab;
|
||||
pIndex->onError = (u8)onError;
|
||||
pIndex->uniqNotNull = onError!=OE_None;
|
||||
pIndex->autoIndex = (u8)(pName==0);
|
||||
pIndex->idxType = pName ? SQLITE_IDXTYPE_APPDEF : SQLITE_IDXTYPE_UNIQUE;
|
||||
pIndex->pSchema = db->aDb[iDb].pSchema;
|
||||
pIndex->nKeyCol = pList->nExpr;
|
||||
if( pPIWhere ){
|
||||
@@ -3091,7 +3091,7 @@ Index *sqlite3CreateIndex(
|
||||
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
|
||||
int k;
|
||||
assert( pIdx->onError!=OE_None );
|
||||
assert( pIdx->autoIndex );
|
||||
assert( pIdx->idxType!=SQLITE_IDXTYPE_APPDEF );
|
||||
assert( pIndex->onError!=OE_None );
|
||||
|
||||
if( pIdx->nKeyCol!=pIndex->nKeyCol ) continue;
|
||||
@@ -3314,7 +3314,7 @@ void sqlite3DropIndex(Parse *pParse, SrcList *pName, int ifExists){
|
||||
pParse->checkSchema = 1;
|
||||
goto exit_drop_index;
|
||||
}
|
||||
if( pIndex->autoIndex ){
|
||||
if( pIndex->idxType!=SQLITE_IDXTYPE_APPDEF ){
|
||||
sqlite3ErrorMsg(pParse, "index associated with UNIQUE "
|
||||
"or PRIMARY KEY constraint cannot be dropped", 0);
|
||||
goto exit_drop_index;
|
||||
@@ -3973,7 +3973,8 @@ void sqlite3UniqueConstraint(
|
||||
}
|
||||
zErr = sqlite3StrAccumFinish(&errMsg);
|
||||
sqlite3HaltConstraint(pParse,
|
||||
(pIdx->autoIndex==2)?SQLITE_CONSTRAINT_PRIMARYKEY:SQLITE_CONSTRAINT_UNIQUE,
|
||||
IsPrimaryKeyIndex(pIdx) ? SQLITE_CONSTRAINT_PRIMARYKEY
|
||||
: SQLITE_CONSTRAINT_UNIQUE,
|
||||
onError, zErr, P4_DYNAMIC, P5_ConstraintUnique);
|
||||
}
|
||||
|
||||
|
||||
@@ -233,8 +233,8 @@ int sqlite3FkLocateIndex(
|
||||
if( zKey==0 ){
|
||||
/* If zKey is NULL, then this foreign key is implicitly mapped to
|
||||
** the PRIMARY KEY of table pParent. The PRIMARY KEY index may be
|
||||
** identified by the test (Index.autoIndex==2). */
|
||||
if( pIdx->autoIndex==2 ){
|
||||
** identified by the test. */
|
||||
if( IsPrimaryKeyIndex(pIdx) ){
|
||||
if( aiCol ){
|
||||
int i;
|
||||
for(i=0; i<nCol; i++) aiCol[i] = pFKey->aCol[i].iFrom;
|
||||
|
||||
@@ -1463,7 +1463,7 @@ void sqlite3GenerateConstraintChecks(
|
||||
** KEY values of this row before the update. */
|
||||
int addrJump = sqlite3VdbeCurrentAddr(v)+pPk->nKeyCol;
|
||||
int op = OP_Ne;
|
||||
int regCmp = (pIdx->autoIndex==2 ? regIdx : regR);
|
||||
int regCmp = (IsPrimaryKeyIndex(pIdx) ? regIdx : regR);
|
||||
|
||||
for(i=0; i<pPk->nKeyCol; i++){
|
||||
char *p4 = (char*)sqlite3LocateCollSeq(pParse, pPk->azColl[i]);
|
||||
@@ -1564,7 +1564,7 @@ void sqlite3CompleteInsertion(
|
||||
sqlite3VdbeAddOp2(v, OP_IdxInsert, iIdxCur+i, aRegIdx[i]);
|
||||
pik_flags = 0;
|
||||
if( useSeekResult ) pik_flags = OPFLAG_USESEEKRESULT;
|
||||
if( pIdx->autoIndex==2 && !HasRowid(pTab) ){
|
||||
if( IsPrimaryKeyIndex(pIdx) && !HasRowid(pTab) ){
|
||||
assert( pParse->nested==0 );
|
||||
pik_flags |= OPFLAG_NCHANGE;
|
||||
}
|
||||
@@ -1650,7 +1650,7 @@ int sqlite3OpenTableAndIndices(
|
||||
for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){
|
||||
int iIdxCur = iBase++;
|
||||
assert( pIdx->pSchema==pTab->pSchema );
|
||||
if( pIdx->autoIndex==2 && !HasRowid(pTab) && piDataCur ){
|
||||
if( IsPrimaryKeyIndex(pIdx) && !HasRowid(pTab) && piDataCur ){
|
||||
*piDataCur = iIdxCur;
|
||||
}
|
||||
if( aToOpen==0 || aToOpen[i+1] ){
|
||||
|
||||
24
src/os_win.c
24
src/os_win.c
@@ -2828,7 +2828,7 @@ static int winGetReadLock(winFile *pFile){
|
||||
pFile->lastErrno = osGetLastError();
|
||||
/* No need to log a failure to lock */
|
||||
}
|
||||
OSTRACE(("READ-LOCK file=%p, rc=%s\n", pFile->h, sqlite3ErrName(res)));
|
||||
OSTRACE(("READ-LOCK file=%p, result=%d\n", pFile->h, res));
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -2852,7 +2852,7 @@ static int winUnlockReadLock(winFile *pFile){
|
||||
winLogError(SQLITE_IOERR_UNLOCK, pFile->lastErrno,
|
||||
"winUnlockReadLock", pFile->zPath);
|
||||
}
|
||||
OSTRACE(("READ-UNLOCK file=%p, rc=%s\n", pFile->h, sqlite3ErrName(res)));
|
||||
OSTRACE(("READ-UNLOCK file=%p, result=%d\n", pFile->h, res));
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -2927,8 +2927,8 @@ static int winLock(sqlite3_file *id, int locktype){
|
||||
** If you are using this code as a model for alternative VFSes, do not
|
||||
** copy this retry logic. It is a hack intended for Windows only.
|
||||
*/
|
||||
OSTRACE(("LOCK-PENDING-FAIL file=%p, count=%d, rc=%s\n",
|
||||
pFile->h, cnt, sqlite3ErrName(res)));
|
||||
OSTRACE(("LOCK-PENDING-FAIL file=%p, count=%d, result=%d\n",
|
||||
pFile->h, cnt, res));
|
||||
if( cnt ) sqlite3_win32_sleep(1);
|
||||
}
|
||||
gotPendingLock = res;
|
||||
@@ -3013,7 +3013,7 @@ static int winLock(sqlite3_file *id, int locktype){
|
||||
** non-zero, otherwise zero.
|
||||
*/
|
||||
static int winCheckReservedLock(sqlite3_file *id, int *pResOut){
|
||||
int rc;
|
||||
int res;
|
||||
winFile *pFile = (winFile*)id;
|
||||
|
||||
SimulateIOError( return SQLITE_IOERR_CHECKRESERVEDLOCK; );
|
||||
@@ -3021,17 +3021,17 @@ static int winCheckReservedLock(sqlite3_file *id, int *pResOut){
|
||||
|
||||
assert( id!=0 );
|
||||
if( pFile->locktype>=RESERVED_LOCK ){
|
||||
rc = 1;
|
||||
OSTRACE(("TEST-WR-LOCK file=%p, rc=%d (local)\n", pFile->h, rc));
|
||||
res = 1;
|
||||
OSTRACE(("TEST-WR-LOCK file=%p, result=%d (local)\n", pFile->h, res));
|
||||
}else{
|
||||
rc = winLockFile(&pFile->h, SQLITE_LOCKFILEEX_FLAGS,RESERVED_BYTE, 0, 1, 0);
|
||||
if( rc ){
|
||||
res = winLockFile(&pFile->h, SQLITE_LOCKFILEEX_FLAGS,RESERVED_BYTE, 0, 1, 0);
|
||||
if( res ){
|
||||
winUnlockFile(&pFile->h, RESERVED_BYTE, 0, 1, 0);
|
||||
}
|
||||
rc = !rc;
|
||||
OSTRACE(("TEST-WR-LOCK file=%p, rc=%d (remote)\n", pFile->h, rc));
|
||||
res = !res;
|
||||
OSTRACE(("TEST-WR-LOCK file=%p, result=%d (remote)\n", pFile->h, res));
|
||||
}
|
||||
*pResOut = rc;
|
||||
*pResOut = res;
|
||||
OSTRACE(("TEST-WR-LOCK file=%p, pResOut=%p, *pResOut=%d, rc=SQLITE_OK\n",
|
||||
pFile->h, pResOut, *pResOut));
|
||||
return SQLITE_OK;
|
||||
|
||||
@@ -4527,7 +4527,7 @@ static void explainSimpleCount(
|
||||
Index *pIdx /* Index used to optimize scan, or NULL */
|
||||
){
|
||||
if( pParse->explain==2 ){
|
||||
int bCover = (pIdx!=0 && (HasRowid(pTab) || pIdx->autoIndex!=2));
|
||||
int bCover = (pIdx!=0 && (HasRowid(pTab) || !IsPrimaryKeyIndex(pIdx)));
|
||||
char *zEqp = sqlite3MPrintf(pParse->db, "SCAN TABLE %s%s%s",
|
||||
pTab->zName,
|
||||
bCover ? " USING COVERING INDEX " : "",
|
||||
|
||||
355
src/shell.c
355
src/shell.c
@@ -446,8 +446,9 @@ struct previous_mode_data {
|
||||
struct callback_data {
|
||||
sqlite3 *db; /* The database */
|
||||
int echoOn; /* True to echo input commands */
|
||||
int autoEQP; /* Run EXPLAIN QUERY PLAN prior to seach SQL statement */
|
||||
int autoEQP; /* Run EXPLAIN QUERY PLAN prior to seach SQL stmt */
|
||||
int statsOn; /* True to display memory stats before each finalize */
|
||||
int outCount; /* Revert to stdout when reaching zero */
|
||||
int cnt; /* Number of records displayed so far */
|
||||
FILE *out; /* Write results here */
|
||||
FILE *traceOut; /* Output for sqlite3_trace() */
|
||||
@@ -878,7 +879,8 @@ static int shell_callback(void *pArg, int nArg, char **azArg, char **azCol, int
|
||||
}else if( aiType && aiType[i]==SQLITE_TEXT ){
|
||||
if( zSep[0] ) fprintf(p->out,"%s",zSep);
|
||||
output_quoted_string(p->out, azArg[i]);
|
||||
}else if( aiType && (aiType[i]==SQLITE_INTEGER || aiType[i]==SQLITE_FLOAT) ){
|
||||
}else if( aiType && (aiType[i]==SQLITE_INTEGER
|
||||
|| aiType[i]==SQLITE_FLOAT) ){
|
||||
fprintf(p->out,"%s%s",zSep, azArg[i]);
|
||||
}else if( aiType && aiType[i]==SQLITE_BLOB && p->pStmt ){
|
||||
const void *pBlob = sqlite3_column_blob(p->pStmt, i);
|
||||
@@ -1198,7 +1200,7 @@ static void explain_data_prepare(struct callback_data *p, sqlite3_stmt *pSql){
|
||||
|
||||
const char *azNext[] = { "Next", "Prev", "VPrev", "VNext", "SorterNext",
|
||||
"NextIfOpen", "PrevIfOpen", 0 };
|
||||
const char *azYield[] = { "Yield", "SeekLt", "SeekGt", "RowSetRead", "Rewind", 0 };
|
||||
const char *azYield[] = { "Yield", "SeekLT", "SeekGT", "RowSetRead", "Rewind", 0 };
|
||||
const char *azGoto[] = { "Goto", 0 };
|
||||
|
||||
/* Try to figure out if this is really an EXPLAIN statement. If this
|
||||
@@ -1570,17 +1572,17 @@ static int run_schema_dump_query(
|
||||
*/
|
||||
static char zHelp[] =
|
||||
".backup ?DB? FILE Backup DB (default \"main\") to FILE\n"
|
||||
".bail ON|OFF Stop after hitting an error. Default OFF\n"
|
||||
".bail on|off Stop after hitting an error. Default OFF\n"
|
||||
".clone NEWDB Clone data into NEWDB from the existing database\n"
|
||||
".databases List names and files of attached databases\n"
|
||||
".dump ?TABLE? ... Dump the database in an SQL text format\n"
|
||||
" If TABLE specified, only dump tables matching\n"
|
||||
" LIKE pattern TABLE.\n"
|
||||
".echo ON|OFF Turn command echo on or off\n"
|
||||
".echo on|off Turn command echo on or off\n"
|
||||
".exit Exit this program\n"
|
||||
".explain ?ON|OFF? Turn output mode suitable for EXPLAIN on or off.\n"
|
||||
".explain ?on|off? Turn output mode suitable for EXPLAIN on or off.\n"
|
||||
" With no args, it turns EXPLAIN on.\n"
|
||||
".header(s) ON|OFF Turn display of headers on or off\n"
|
||||
".headers on|off Turn display of headers on or off\n"
|
||||
".help Show this message\n"
|
||||
".import FILE TABLE Import data from FILE into TABLE\n"
|
||||
".indices ?TABLE? Show names of all indices\n"
|
||||
@@ -1603,9 +1605,9 @@ static char zHelp[] =
|
||||
" tabs Tab-separated values\n"
|
||||
" tcl TCL list elements\n"
|
||||
".nullvalue STRING Use STRING in place of NULL values\n"
|
||||
".once FILENAME Output for the next SQL command only to FILENAME\n"
|
||||
".open ?FILENAME? Close existing database and reopen FILENAME\n"
|
||||
".output FILENAME Send output to FILENAME\n"
|
||||
".output stdout Send output to the screen\n"
|
||||
".output ?FILENAME? Send output to FILENAME or stdout\n"
|
||||
".print STRING... Print literal STRING\n"
|
||||
".prompt MAIN CONTINUE Replace the standard prompts\n"
|
||||
".quit Exit this program\n"
|
||||
@@ -1616,19 +1618,19 @@ static char zHelp[] =
|
||||
" If TABLE specified, only show tables matching\n"
|
||||
" LIKE pattern TABLE.\n"
|
||||
".separator STRING Change separator used by output mode and .import\n"
|
||||
".shell CMD ARGS... Run CMD ARGS... in a system shell\n"
|
||||
".show Show the current values for various settings\n"
|
||||
".stats ON|OFF Turn stats on or off\n"
|
||||
".stats on|off Turn stats on or off\n"
|
||||
".system CMD ARGS... Run CMD ARGS... in a system shell\n"
|
||||
".tables ?TABLE? List names of tables\n"
|
||||
" If TABLE specified, only list tables matching\n"
|
||||
" LIKE pattern TABLE.\n"
|
||||
".timeout MS Try opening locked tables for MS milliseconds\n"
|
||||
".timer on|off Turn SQL timer on or off\n"
|
||||
".trace FILE|off Output each SQL statement as it is run\n"
|
||||
".vfsname ?AUX? Print the name of the VFS stack\n"
|
||||
".width NUM1 NUM2 ... Set column widths for \"column\" mode\n"
|
||||
;
|
||||
|
||||
static char zTimerHelp[] =
|
||||
".timer ON|OFF Turn the CPU timer measurement on or off\n"
|
||||
" Negative values right-justify\n"
|
||||
;
|
||||
|
||||
/* Forward reference */
|
||||
@@ -1672,6 +1674,7 @@ static void open_db(struct callback_data *p, int keepAlive){
|
||||
static void resolve_backslashes(char *z){
|
||||
int i, j;
|
||||
char c;
|
||||
while( *z && *z!='\\' ) z++;
|
||||
for(i=j=0; (c = z[i])!=0; i++, j++){
|
||||
if( c=='\\' ){
|
||||
c = z[++i];
|
||||
@@ -1697,7 +1700,7 @@ static void resolve_backslashes(char *z){
|
||||
}
|
||||
z[j] = c;
|
||||
}
|
||||
z[j] = 0;
|
||||
if( j<i ) z[j] = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -2141,6 +2144,19 @@ static void tryToClone(struct callback_data *p, const char *zNewDb){
|
||||
sqlite3_close(newDb);
|
||||
}
|
||||
|
||||
/*
|
||||
** Change the output file back to stdout
|
||||
*/
|
||||
static void output_reset(struct callback_data *p){
|
||||
if( p->outfile[0]=='|' ){
|
||||
pclose(p->out);
|
||||
}else{
|
||||
output_file_close(p->out);
|
||||
}
|
||||
p->outfile[0] = 0;
|
||||
p->out = stdout;
|
||||
}
|
||||
|
||||
/*
|
||||
** If an input line begins with "." then invoke this routine to
|
||||
** process that line.
|
||||
@@ -2239,8 +2255,13 @@ static int do_meta_command(char *zLine, struct callback_data *p){
|
||||
sqlite3_close(pDest);
|
||||
}else
|
||||
|
||||
if( c=='b' && n>=3 && strncmp(azArg[0], "bail", n)==0 && nArg>1 && nArg<3 ){
|
||||
bail_on_error = booleanValue(azArg[1]);
|
||||
if( c=='b' && n>=3 && strncmp(azArg[0], "bail", n)==0 ){
|
||||
if( nArg==2 ){
|
||||
bail_on_error = booleanValue(azArg[1]);
|
||||
}else{
|
||||
fprintf(stderr, "Usage: .bail on|off\n");
|
||||
rc = 1;
|
||||
}
|
||||
}else
|
||||
|
||||
/* The undocumented ".breakpoint" command causes a call to the no-op
|
||||
@@ -2250,11 +2271,16 @@ static int do_meta_command(char *zLine, struct callback_data *p){
|
||||
test_breakpoint();
|
||||
}else
|
||||
|
||||
if( c=='c' && strncmp(azArg[0], "clone", n)==0 && nArg>1 && nArg<3 ){
|
||||
tryToClone(p, azArg[1]);
|
||||
if( c=='c' && strncmp(azArg[0], "clone", n)==0 ){
|
||||
if( nArg==2 ){
|
||||
tryToClone(p, azArg[1]);
|
||||
}else{
|
||||
fprintf(stderr, "Usage: .clone FILENAME\n");
|
||||
rc = 1;
|
||||
}
|
||||
}else
|
||||
|
||||
if( c=='d' && n>1 && strncmp(azArg[0], "databases", n)==0 && nArg==1 ){
|
||||
if( c=='d' && n>1 && strncmp(azArg[0], "databases", n)==0 ){
|
||||
struct callback_data data;
|
||||
char *zErrMsg = 0;
|
||||
open_db(p, 0);
|
||||
@@ -2273,11 +2299,16 @@ static int do_meta_command(char *zLine, struct callback_data *p){
|
||||
}
|
||||
}else
|
||||
|
||||
if( c=='d' && strncmp(azArg[0], "dump", n)==0 && nArg<3 ){
|
||||
if( c=='d' && strncmp(azArg[0], "dump", n)==0 ){
|
||||
open_db(p, 0);
|
||||
/* When playing back a "dump", the content might appear in an order
|
||||
** which causes immediate foreign key constraints to be violated.
|
||||
** So disable foreign-key constraint enforcement to prevent problems. */
|
||||
if( nArg!=1 && nArg!=2 ){
|
||||
fprintf(stderr, "Usage: .dump ?LIKE-PATTERN?\n");
|
||||
rc = 1;
|
||||
goto meta_command_exit;
|
||||
}
|
||||
fprintf(p->out, "PRAGMA foreign_keys=OFF;\n");
|
||||
fprintf(p->out, "BEGIN TRANSACTION;\n");
|
||||
p->writableSchema = 0;
|
||||
@@ -2322,12 +2353,22 @@ static int do_meta_command(char *zLine, struct callback_data *p){
|
||||
fprintf(p->out, p->nErr ? "ROLLBACK; -- due to errors\n" : "COMMIT;\n");
|
||||
}else
|
||||
|
||||
if( c=='e' && strncmp(azArg[0], "echo", n)==0 && nArg>1 && nArg<3 ){
|
||||
p->echoOn = booleanValue(azArg[1]);
|
||||
if( c=='e' && strncmp(azArg[0], "echo", n)==0 ){
|
||||
if( nArg==2 ){
|
||||
p->echoOn = booleanValue(azArg[1]);
|
||||
}else{
|
||||
fprintf(stderr, "Usage: .echo on|off\n");
|
||||
rc = 1;
|
||||
}
|
||||
}else
|
||||
|
||||
if( c=='e' && strncmp(azArg[0], "eqp", n)==0 && nArg>1 && nArg<3 ){
|
||||
p->autoEQP = booleanValue(azArg[1]);
|
||||
if( c=='e' && strncmp(azArg[0], "eqp", n)==0 ){
|
||||
if( nArg==2 ){
|
||||
p->autoEQP = booleanValue(azArg[1]);
|
||||
}else{
|
||||
fprintf(stderr, "Usage: .eqp on|off\n");
|
||||
rc = 1;
|
||||
}
|
||||
}else
|
||||
|
||||
if( c=='e' && strncmp(azArg[0], "exit", n)==0 ){
|
||||
@@ -2335,7 +2376,7 @@ static int do_meta_command(char *zLine, struct callback_data *p){
|
||||
rc = 2;
|
||||
}else
|
||||
|
||||
if( c=='e' && strncmp(azArg[0], "explain", n)==0 && nArg<3 ){
|
||||
if( c=='e' && strncmp(azArg[0], "explain", n)==0 ){
|
||||
int val = nArg>=2 ? booleanValue(azArg[1]) : 1;
|
||||
if(val == 1) {
|
||||
if(!p->explainPrev.valid) {
|
||||
@@ -2370,19 +2411,20 @@ static int do_meta_command(char *zLine, struct callback_data *p){
|
||||
}
|
||||
}else
|
||||
|
||||
if( c=='h' && (strncmp(azArg[0], "header", n)==0 ||
|
||||
strncmp(azArg[0], "headers", n)==0) && nArg>1 && nArg<3 ){
|
||||
p->showHeader = booleanValue(azArg[1]);
|
||||
}else
|
||||
|
||||
if( c=='h' && strncmp(azArg[0], "help", n)==0 ){
|
||||
fprintf(stderr,"%s",zHelp);
|
||||
if( HAS_TIMER ){
|
||||
fprintf(stderr,"%s",zTimerHelp);
|
||||
if( c=='h' && strncmp(azArg[0], "headers", n)==0 ){
|
||||
if( nArg==2 ){
|
||||
p->showHeader = booleanValue(azArg[1]);
|
||||
}else{
|
||||
fprintf(stderr, "Usage: .headers on|off\n");
|
||||
rc = 1;
|
||||
}
|
||||
}else
|
||||
|
||||
if( c=='i' && strncmp(azArg[0], "import", n)==0 && nArg==3 ){
|
||||
if( c=='h' && strncmp(azArg[0], "help", n)==0 ){
|
||||
fprintf(p->out, "%s", zHelp);
|
||||
}else
|
||||
|
||||
if( c=='i' && strncmp(azArg[0], "import", n)==0 ){
|
||||
char *zTable = azArg[2]; /* Insert data into this table */
|
||||
char *zFile = azArg[1]; /* Name of file to extra content from */
|
||||
sqlite3_stmt *pStmt = NULL; /* A statement */
|
||||
@@ -2395,6 +2437,10 @@ static int do_meta_command(char *zLine, struct callback_data *p){
|
||||
CSVReader sCsv; /* Reader context */
|
||||
int (*xCloser)(FILE*); /* Procedure to close th3 connection */
|
||||
|
||||
if( nArg!=3 ){
|
||||
fprintf(stderr, "Usage: .import FILE TABLE\n");
|
||||
goto meta_command_exit;
|
||||
}
|
||||
seenInterrupt = 0;
|
||||
memset(&sCsv, 0, sizeof(sCsv));
|
||||
open_db(p, 0);
|
||||
@@ -2533,7 +2579,7 @@ static int do_meta_command(char *zLine, struct callback_data *p){
|
||||
if( needCommit ) sqlite3_exec(db, "COMMIT", 0, 0, 0);
|
||||
}else
|
||||
|
||||
if( c=='i' && strncmp(azArg[0], "indices", n)==0 && nArg<3 ){
|
||||
if( c=='i' && strncmp(azArg[0], "indices", n)==0 ){
|
||||
struct callback_data data;
|
||||
char *zErrMsg = 0;
|
||||
open_db(p, 0);
|
||||
@@ -2550,7 +2596,7 @@ static int do_meta_command(char *zLine, struct callback_data *p){
|
||||
"ORDER BY 1",
|
||||
callback, &data, &zErrMsg
|
||||
);
|
||||
}else{
|
||||
}else if( nArg==2 ){
|
||||
zShellStatic = azArg[1];
|
||||
rc = sqlite3_exec(p->db,
|
||||
"SELECT name FROM sqlite_master "
|
||||
@@ -2562,6 +2608,10 @@ static int do_meta_command(char *zLine, struct callback_data *p){
|
||||
callback, &data, &zErrMsg
|
||||
);
|
||||
zShellStatic = 0;
|
||||
}else{
|
||||
fprintf(stderr, "Usage: .indices ?LIKE-PATTERN?\n");
|
||||
rc = 1;
|
||||
goto meta_command_exit;
|
||||
}
|
||||
if( zErrMsg ){
|
||||
fprintf(stderr,"Error: %s\n", zErrMsg);
|
||||
@@ -2597,9 +2647,14 @@ static int do_meta_command(char *zLine, struct callback_data *p){
|
||||
#endif
|
||||
|
||||
#ifndef SQLITE_OMIT_LOAD_EXTENSION
|
||||
if( c=='l' && strncmp(azArg[0], "load", n)==0 && nArg>=2 ){
|
||||
if( c=='l' && strncmp(azArg[0], "load", n)==0 ){
|
||||
const char *zFile, *zProc;
|
||||
char *zErrMsg = 0;
|
||||
if( nArg<2 ){
|
||||
fprintf(stderr, "Usage: .load FILE ?ENTRYPOINT?\n");
|
||||
rc = 1;
|
||||
goto meta_command_exit;
|
||||
}
|
||||
zFile = azArg[1];
|
||||
zProc = nArg>=3 ? azArg[2] : 0;
|
||||
open_db(p, 0);
|
||||
@@ -2612,38 +2667,41 @@ static int do_meta_command(char *zLine, struct callback_data *p){
|
||||
}else
|
||||
#endif
|
||||
|
||||
if( c=='l' && strncmp(azArg[0], "log", n)==0 && nArg>=2 ){
|
||||
const char *zFile = azArg[1];
|
||||
output_file_close(p->pLog);
|
||||
p->pLog = output_file_open(zFile);
|
||||
if( c=='l' && strncmp(azArg[0], "log", n)==0 ){
|
||||
if( nArg!=2 ){
|
||||
fprintf(stderr, "Usage: .log FILENAME\n");
|
||||
rc = 1;
|
||||
}else{
|
||||
const char *zFile = azArg[1];
|
||||
output_file_close(p->pLog);
|
||||
p->pLog = output_file_open(zFile);
|
||||
}
|
||||
}else
|
||||
|
||||
if( c=='m' && strncmp(azArg[0], "mode", n)==0 && nArg==2 ){
|
||||
int n2 = strlen30(azArg[1]);
|
||||
if( (n2==4 && strncmp(azArg[1],"line",n2)==0)
|
||||
||
|
||||
(n2==5 && strncmp(azArg[1],"lines",n2)==0) ){
|
||||
if( c=='m' && strncmp(azArg[0], "mode", n)==0 ){
|
||||
const char *zMode = nArg>=2 ? azArg[1] : "";
|
||||
int n2 = (int)strlen(zMode);
|
||||
int c2 = zMode[0];
|
||||
if( c2=='l' && n2>2 && strncmp(azArg[1],"lines",n2)==0 ){
|
||||
p->mode = MODE_Line;
|
||||
}else if( (n2==6 && strncmp(azArg[1],"column",n2)==0)
|
||||
||
|
||||
(n2==7 && strncmp(azArg[1],"columns",n2)==0) ){
|
||||
}else if( c2=='c' && strncmp(azArg[1],"columns",n2)==0 ){
|
||||
p->mode = MODE_Column;
|
||||
}else if( n2==4 && strncmp(azArg[1],"list",n2)==0 ){
|
||||
}else if( c2=='l' && n2>2 && strncmp(azArg[1],"list",n2)==0 ){
|
||||
p->mode = MODE_List;
|
||||
}else if( n2==4 && strncmp(azArg[1],"html",n2)==0 ){
|
||||
}else if( c2=='h' && strncmp(azArg[1],"html",n2)==0 ){
|
||||
p->mode = MODE_Html;
|
||||
}else if( n2==3 && strncmp(azArg[1],"tcl",n2)==0 ){
|
||||
}else if( c2=='t' && strncmp(azArg[1],"tcl",n2)==0 ){
|
||||
p->mode = MODE_Tcl;
|
||||
sqlite3_snprintf(sizeof(p->separator), p->separator, " ");
|
||||
}else if( n2==3 && strncmp(azArg[1],"csv",n2)==0 ){
|
||||
}else if( c2=='c' && strncmp(azArg[1],"csv",n2)==0 ){
|
||||
p->mode = MODE_Csv;
|
||||
sqlite3_snprintf(sizeof(p->separator), p->separator, ",");
|
||||
}else if( n2==4 && strncmp(azArg[1],"tabs",n2)==0 ){
|
||||
}else if( c2=='t' && strncmp(azArg[1],"tabs",n2)==0 ){
|
||||
p->mode = MODE_List;
|
||||
sqlite3_snprintf(sizeof(p->separator), p->separator, "\t");
|
||||
}else if( n2==6 && strncmp(azArg[1],"insert",n2)==0 ){
|
||||
}else if( c2=='i' && strncmp(azArg[1],"insert",n2)==0 ){
|
||||
p->mode = MODE_Insert;
|
||||
set_table_name(p, "table");
|
||||
set_table_name(p, nArg>=3 ? azArg[2] : "table");
|
||||
}else {
|
||||
fprintf(stderr,"Error: mode should be one of: "
|
||||
"column csv html insert line list tabs tcl\n");
|
||||
@@ -2651,23 +2709,16 @@ static int do_meta_command(char *zLine, struct callback_data *p){
|
||||
}
|
||||
}else
|
||||
|
||||
if( c=='m' && strncmp(azArg[0], "mode", n)==0 && nArg==3 ){
|
||||
int n2 = strlen30(azArg[1]);
|
||||
if( n2==6 && strncmp(azArg[1],"insert",n2)==0 ){
|
||||
p->mode = MODE_Insert;
|
||||
set_table_name(p, azArg[2]);
|
||||
}else {
|
||||
fprintf(stderr, "Error: invalid arguments: "
|
||||
" \"%s\". Enter \".help\" for help\n", azArg[2]);
|
||||
if( c=='n' && strncmp(azArg[0], "nullvalue", n)==0 ){
|
||||
if( nArg==2 ){
|
||||
sqlite3_snprintf(sizeof(p->nullvalue), p->nullvalue,
|
||||
"%.*s", (int)ArraySize(p->nullvalue)-1, azArg[1]);
|
||||
}else{
|
||||
fprintf(stderr, "Usage: .nullvalue STRING\n");
|
||||
rc = 1;
|
||||
}
|
||||
}else
|
||||
|
||||
if( c=='n' && strncmp(azArg[0], "nullvalue", n)==0 && nArg==2 ) {
|
||||
sqlite3_snprintf(sizeof(p->nullvalue), p->nullvalue,
|
||||
"%.*s", (int)ArraySize(p->nullvalue)-1, azArg[1]);
|
||||
}else
|
||||
|
||||
if( c=='o' && strncmp(azArg[0], "open", n)==0 && n>=2 ){
|
||||
sqlite3 *savedDb = p->db;
|
||||
const char *zSavedFilename = p->zDbFilename;
|
||||
@@ -2688,32 +2739,45 @@ static int do_meta_command(char *zLine, struct callback_data *p){
|
||||
}
|
||||
}else
|
||||
|
||||
if( c=='o' && strncmp(azArg[0], "output", n)==0 && nArg==2 ){
|
||||
if( p->outfile[0]=='|' ){
|
||||
pclose(p->out);
|
||||
}else{
|
||||
output_file_close(p->out);
|
||||
if( c=='o'
|
||||
&& (strncmp(azArg[0], "output", n)==0 || strncmp(azArg[0], "once", n)==0)
|
||||
){
|
||||
const char *zFile = nArg>=2 ? azArg[1] : "stdout";
|
||||
if( nArg>2 ){
|
||||
fprintf(stderr, "Usage: .%s FILE\n", azArg[0]);
|
||||
rc = 1;
|
||||
goto meta_command_exit;
|
||||
}
|
||||
p->outfile[0] = 0;
|
||||
if( azArg[1][0]=='|' ){
|
||||
p->out = popen(&azArg[1][1], "w");
|
||||
if( n>1 && strncmp(azArg[0], "once", n)==0 ){
|
||||
if( nArg<2 ){
|
||||
fprintf(stderr, "Usage: .once FILE\n");
|
||||
rc = 1;
|
||||
goto meta_command_exit;
|
||||
}
|
||||
p->outCount = 2;
|
||||
}else{
|
||||
p->outCount = 0;
|
||||
}
|
||||
output_reset(p);
|
||||
if( zFile[0]=='|' ){
|
||||
p->out = popen(zFile + 1, "w");
|
||||
if( p->out==0 ){
|
||||
fprintf(stderr,"Error: cannot open pipe \"%s\"\n", &azArg[1][1]);
|
||||
fprintf(stderr,"Error: cannot open pipe \"%s\"\n", zFile + 1);
|
||||
p->out = stdout;
|
||||
rc = 1;
|
||||
}else{
|
||||
sqlite3_snprintf(sizeof(p->outfile), p->outfile, "%s", azArg[1]);
|
||||
sqlite3_snprintf(sizeof(p->outfile), p->outfile, "%s", zFile);
|
||||
}
|
||||
}else{
|
||||
p->out = output_file_open(azArg[1]);
|
||||
p->out = output_file_open(zFile);
|
||||
if( p->out==0 ){
|
||||
if( strcmp(azArg[1],"off")!=0 ){
|
||||
fprintf(stderr,"Error: cannot write to \"%s\"\n", azArg[1]);
|
||||
if( strcmp(zFile,"off")!=0 ){
|
||||
fprintf(stderr,"Error: cannot write to \"%s\"\n", zFile);
|
||||
}
|
||||
p->out = stdout;
|
||||
rc = 1;
|
||||
} else {
|
||||
sqlite3_snprintf(sizeof(p->outfile), p->outfile, "%s", azArg[1]);
|
||||
sqlite3_snprintf(sizeof(p->outfile), p->outfile, "%s", zFile);
|
||||
}
|
||||
}
|
||||
}else
|
||||
@@ -2727,7 +2791,7 @@ static int do_meta_command(char *zLine, struct callback_data *p){
|
||||
fprintf(p->out, "\n");
|
||||
}else
|
||||
|
||||
if( c=='p' && strncmp(azArg[0], "prompt", n)==0 && (nArg==2 || nArg==3)){
|
||||
if( c=='p' && strncmp(azArg[0], "prompt", n)==0 ){
|
||||
if( nArg >= 2) {
|
||||
strncpy(mainPrompt,azArg[1],(int)ArraySize(mainPrompt)-1);
|
||||
}
|
||||
@@ -2736,12 +2800,18 @@ static int do_meta_command(char *zLine, struct callback_data *p){
|
||||
}
|
||||
}else
|
||||
|
||||
if( c=='q' && strncmp(azArg[0], "quit", n)==0 && nArg==1 ){
|
||||
if( c=='q' && strncmp(azArg[0], "quit", n)==0 ){
|
||||
rc = 2;
|
||||
}else
|
||||
|
||||
if( c=='r' && n>=3 && strncmp(azArg[0], "read", n)==0 && nArg==2 ){
|
||||
FILE *alt = fopen(azArg[1], "rb");
|
||||
if( c=='r' && n>=3 && strncmp(azArg[0], "read", n)==0 ){
|
||||
FILE *alt;
|
||||
if( nArg!=2 ){
|
||||
fprintf(stderr, "Usage: .read FILE\n");
|
||||
rc = 1;
|
||||
goto meta_command_exit;
|
||||
}
|
||||
alt = fopen(azArg[1], "rb");
|
||||
if( alt==0 ){
|
||||
fprintf(stderr,"Error: cannot open \"%s\"\n", azArg[1]);
|
||||
rc = 1;
|
||||
@@ -2751,7 +2821,7 @@ static int do_meta_command(char *zLine, struct callback_data *p){
|
||||
}
|
||||
}else
|
||||
|
||||
if( c=='r' && n>=3 && strncmp(azArg[0], "restore", n)==0 && nArg>1 && nArg<4){
|
||||
if( c=='r' && n>=3 && strncmp(azArg[0], "restore", n)==0 ){
|
||||
const char *zSrcFile;
|
||||
const char *zDb;
|
||||
sqlite3 *pSrc;
|
||||
@@ -2761,9 +2831,13 @@ static int do_meta_command(char *zLine, struct callback_data *p){
|
||||
if( nArg==2 ){
|
||||
zSrcFile = azArg[1];
|
||||
zDb = "main";
|
||||
}else{
|
||||
}else if( nArg==3 ){
|
||||
zSrcFile = azArg[2];
|
||||
zDb = azArg[1];
|
||||
}else{
|
||||
fprintf(stderr, "Usage: .restore ?DB? FILE\n");
|
||||
rc = 1;
|
||||
goto meta_command_exit;
|
||||
}
|
||||
rc = sqlite3_open(zSrcFile, &pSrc);
|
||||
if( rc!=SQLITE_OK ){
|
||||
@@ -2798,14 +2872,14 @@ static int do_meta_command(char *zLine, struct callback_data *p){
|
||||
sqlite3_close(pSrc);
|
||||
}else
|
||||
|
||||
if( c=='s' && strncmp(azArg[0], "schema", n)==0 && nArg<3 ){
|
||||
if( c=='s' && strncmp(azArg[0], "schema", n)==0 ){
|
||||
struct callback_data data;
|
||||
char *zErrMsg = 0;
|
||||
open_db(p, 0);
|
||||
memcpy(&data, p, sizeof(data));
|
||||
data.showHeader = 0;
|
||||
data.mode = MODE_Semi;
|
||||
if( nArg>1 ){
|
||||
if( nArg==2 ){
|
||||
int i;
|
||||
for(i=0; azArg[1][i]; i++) azArg[1][i] = ToLower(azArg[1][i]);
|
||||
if( strcmp(azArg[1],"sqlite_master")==0 ){
|
||||
@@ -2849,7 +2923,7 @@ static int do_meta_command(char *zLine, struct callback_data *p){
|
||||
callback, &data, &zErrMsg);
|
||||
zShellStatic = 0;
|
||||
}
|
||||
}else{
|
||||
}else if( nArg==1 ){
|
||||
rc = sqlite3_exec(p->db,
|
||||
"SELECT sql FROM "
|
||||
" (SELECT sql sql, type type, tbl_name tbl_name, name name, rowid x"
|
||||
@@ -2859,6 +2933,10 @@ static int do_meta_command(char *zLine, struct callback_data *p){
|
||||
"ORDER BY rowid",
|
||||
callback, &data, &zErrMsg
|
||||
);
|
||||
}else{
|
||||
fprintf(stderr, "Usage: .schema ?LIKE-PATTERN?\n");
|
||||
rc = 1;
|
||||
goto meta_command_exit;
|
||||
}
|
||||
if( zErrMsg ){
|
||||
fprintf(stderr,"Error: %s\n", zErrMsg);
|
||||
@@ -2888,20 +2966,49 @@ static int do_meta_command(char *zLine, struct callback_data *p){
|
||||
for(i=1; i<nArg; i++){
|
||||
char zBuf[200];
|
||||
v = integerValue(azArg[i]);
|
||||
sqlite3_snprintf(sizeof(zBuf), zBuf, "%s: %lld 0x%llx\n", azArg[i], v, v);
|
||||
sqlite3_snprintf(sizeof(zBuf),zBuf,"%s: %lld 0x%llx\n", azArg[i],v,v);
|
||||
fprintf(p->out, "%s", zBuf);
|
||||
}
|
||||
}
|
||||
}else
|
||||
#endif
|
||||
|
||||
if( c=='s' && strncmp(azArg[0], "separator", n)==0 && nArg==2 ){
|
||||
sqlite3_snprintf(sizeof(p->separator), p->separator,
|
||||
"%.*s", (int)sizeof(p->separator)-1, azArg[1]);
|
||||
if( c=='s' && strncmp(azArg[0], "separator", n)==0 ){
|
||||
if( nArg==2 ){
|
||||
sqlite3_snprintf(sizeof(p->separator), p->separator,
|
||||
"%.*s", (int)sizeof(p->separator)-1, azArg[1]);
|
||||
}else{
|
||||
fprintf(stderr, "Usage: .separator STRING\n");
|
||||
rc = 1;
|
||||
}
|
||||
}else
|
||||
|
||||
if( c=='s' && strncmp(azArg[0], "show", n)==0 && nArg==1 ){
|
||||
if( c=='s'
|
||||
&& (strncmp(azArg[0], "shell", n)==0 || strncmp(azArg[0],"system",n)==0)
|
||||
){
|
||||
char *zCmd;
|
||||
int i;
|
||||
if( nArg<2 ){
|
||||
fprintf(stderr, "Usage: .system COMMAND\n");
|
||||
rc = 1;
|
||||
goto meta_command_exit;
|
||||
}
|
||||
zCmd = sqlite3_mprintf(strchr(azArg[1],' ')==0?"%s":"\"%s\"", azArg[1]);
|
||||
for(i=2; i<nArg; i++){
|
||||
zCmd = sqlite3_mprintf(strchr(azArg[i],' ')==0?"%z %s":"%z \"%s\"",
|
||||
zCmd, azArg[i]);
|
||||
}
|
||||
system(zCmd);
|
||||
sqlite3_free(zCmd);
|
||||
}else
|
||||
|
||||
if( c=='s' && strncmp(azArg[0], "show", n)==0 ){
|
||||
int i;
|
||||
if( nArg!=1 ){
|
||||
fprintf(stderr, "Usage: .show\n");
|
||||
rc = 1;
|
||||
goto meta_command_exit;
|
||||
}
|
||||
fprintf(p->out,"%9.9s: %s\n","echo", p->echoOn ? "on" : "off");
|
||||
fprintf(p->out,"%9.9s: %s\n","eqp", p->autoEQP ? "on" : "off");
|
||||
fprintf(p->out,"%9.9s: %s\n","explain", p->explainPrev.valid ? "on" :"off");
|
||||
@@ -2923,11 +3030,16 @@ static int do_meta_command(char *zLine, struct callback_data *p){
|
||||
fprintf(p->out,"\n");
|
||||
}else
|
||||
|
||||
if( c=='s' && strncmp(azArg[0], "stats", n)==0 && nArg>1 && nArg<3 ){
|
||||
p->statsOn = booleanValue(azArg[1]);
|
||||
if( c=='s' && strncmp(azArg[0], "stats", n)==0 ){
|
||||
if( nArg==2 ){
|
||||
p->statsOn = booleanValue(azArg[1]);
|
||||
}else{
|
||||
fprintf(stderr, "Usage: .stats on|off\n");
|
||||
rc = 1;
|
||||
}
|
||||
}else
|
||||
|
||||
if( c=='t' && n>1 && strncmp(azArg[0], "tables", n)==0 && nArg<3 ){
|
||||
if( c=='t' && n>1 && strncmp(azArg[0], "tables", n)==0 ){
|
||||
sqlite3_stmt *pStmt;
|
||||
char **azResult;
|
||||
int nRow, nAlloc;
|
||||
@@ -3133,20 +3245,32 @@ static int do_meta_command(char *zLine, struct callback_data *p){
|
||||
}
|
||||
}else
|
||||
|
||||
if( c=='t' && n>4 && strncmp(azArg[0], "timeout", n)==0 && nArg==2 ){
|
||||
if( c=='t' && n>4 && strncmp(azArg[0], "timeout", n)==0 ){
|
||||
open_db(p, 0);
|
||||
sqlite3_busy_timeout(p->db, (int)integerValue(azArg[1]));
|
||||
sqlite3_busy_timeout(p->db, nArg>=2 ? (int)integerValue(azArg[1]) : 0);
|
||||
}else
|
||||
|
||||
if( HAS_TIMER && c=='t' && n>=5 && strncmp(azArg[0], "timer", n)==0
|
||||
&& nArg==2
|
||||
){
|
||||
enableTimer = booleanValue(azArg[1]);
|
||||
if( c=='t' && n>=5 && strncmp(azArg[0], "timer", n)==0 ){
|
||||
if( nArg==2 ){
|
||||
enableTimer = booleanValue(azArg[1]);
|
||||
if( enableTimer && !HAS_TIMER ){
|
||||
fprintf(stderr, "Error: timer not available on this system.\n");
|
||||
enableTimer = 0;
|
||||
}
|
||||
}else{
|
||||
fprintf(stderr, "Usage: .timer on|off\n");
|
||||
rc = 1;
|
||||
}
|
||||
}else
|
||||
|
||||
if( c=='t' && strncmp(azArg[0], "trace", n)==0 && nArg>1 ){
|
||||
if( c=='t' && strncmp(azArg[0], "trace", n)==0 ){
|
||||
open_db(p, 0);
|
||||
output_file_close(p->traceOut);
|
||||
if( nArg!=2 ){
|
||||
fprintf(stderr, "Usage: .trace FILE|off\n");
|
||||
rc = 1;
|
||||
goto meta_command_exit;
|
||||
}
|
||||
p->traceOut = output_file_open(azArg[1]);
|
||||
#if !defined(SQLITE_OMIT_TRACE) && !defined(SQLITE_OMIT_FLOATING_POINT)
|
||||
if( p->traceOut==0 ){
|
||||
@@ -3177,11 +3301,11 @@ static int do_meta_command(char *zLine, struct callback_data *p){
|
||||
#if defined(SQLITE_DEBUG) && defined(SQLITE_ENABLE_WHERETRACE)
|
||||
if( c=='w' && strncmp(azArg[0], "wheretrace", n)==0 ){
|
||||
extern int sqlite3WhereTrace;
|
||||
sqlite3WhereTrace = booleanValue(azArg[1]);
|
||||
sqlite3WhereTrace = nArg>=2 ? booleanValue(azArg[1]) : 0xff;
|
||||
}else
|
||||
#endif
|
||||
|
||||
if( c=='w' && strncmp(azArg[0], "width", n)==0 && nArg>1 ){
|
||||
if( c=='w' && strncmp(azArg[0], "width", n)==0 ){
|
||||
int j;
|
||||
assert( nArg<=ArraySize(azArg) );
|
||||
for(j=1; j<nArg && j<ArraySize(p->colWidth); j++){
|
||||
@@ -3195,6 +3319,11 @@ static int do_meta_command(char *zLine, struct callback_data *p){
|
||||
rc = 1;
|
||||
}
|
||||
|
||||
meta_command_exit:
|
||||
if( p->outCount ){
|
||||
p->outCount--;
|
||||
if( p->outCount==0 ) output_reset(p);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -3362,6 +3491,10 @@ static int process_input(struct callback_data *p, FILE *in){
|
||||
errCnt++;
|
||||
}
|
||||
nSql = 0;
|
||||
if( p->outCount ){
|
||||
output_reset(p);
|
||||
p->outCount = 0;
|
||||
}
|
||||
}else if( nSql && _all_whitespace(zSql) ){
|
||||
if( p->echoOn ) printf("%s\n", zSql);
|
||||
nSql = 0;
|
||||
|
||||
@@ -1716,7 +1716,7 @@ struct Index {
|
||||
u16 nKeyCol; /* Number of columns forming the key */
|
||||
u16 nColumn; /* Number of columns stored in the index */
|
||||
u8 onError; /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */
|
||||
unsigned autoIndex:2; /* 1==UNIQUE, 2==PRIMARY KEY, 0==CREATE INDEX */
|
||||
unsigned idxType:2; /* 1==UNIQUE, 2==PRIMARY KEY, 0==CREATE INDEX */
|
||||
unsigned bUnordered:1; /* Use this index for == or IN queries only */
|
||||
unsigned uniqNotNull:1; /* True if UNIQUE and NOT NULL for all columns */
|
||||
unsigned isResized:1; /* True if resizeIndexObject() has been called */
|
||||
@@ -1729,6 +1729,16 @@ struct Index {
|
||||
#endif
|
||||
};
|
||||
|
||||
/*
|
||||
** Allowed values for Index.idxType
|
||||
*/
|
||||
#define SQLITE_IDXTYPE_APPDEF 0 /* Created using CREATE INDEX */
|
||||
#define SQLITE_IDXTYPE_UNIQUE 1 /* Implements a UNIQUE constraint */
|
||||
#define SQLITE_IDXTYPE_PRIMARYKEY 2 /* Is the PRIMARY KEY for the table */
|
||||
|
||||
/* Return true if index X is a PRIMARY KEY index */
|
||||
#define IsPrimaryKeyIndex(X) ((X)->idxType==SQLITE_IDXTYPE_PRIMARYKEY)
|
||||
|
||||
/*
|
||||
** Each sample stored in the sqlite_stat3 table is represented in memory
|
||||
** using a structure of this type. See documentation at the top of the
|
||||
|
||||
@@ -187,7 +187,7 @@ void sqlite3Update(
|
||||
iIdxCur = iDataCur+1;
|
||||
pPk = HasRowid(pTab) ? 0 : sqlite3PrimaryKeyIndex(pTab);
|
||||
for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, nIdx++){
|
||||
if( pIdx->autoIndex==2 && pPk!=0 ){
|
||||
if( IsPrimaryKeyIndex(pIdx) && pPk!=0 ){
|
||||
iDataCur = pParse->nTab;
|
||||
pTabList->a[0].iCursor = iDataCur;
|
||||
}
|
||||
|
||||
@@ -6304,7 +6304,7 @@ case OP_Init: { /* jump */
|
||||
if( zTrace ){
|
||||
int i;
|
||||
for(i=0; i<db->nDb; i++){
|
||||
if( MASKBIT(i) & p->btreeMask)==0 ) continue;
|
||||
if( (MASKBIT(i) & p->btreeMask)==0 ) continue;
|
||||
sqlite3_file_control(db, db->aDb[i].zName, SQLITE_FCNTL_TRACE, zTrace);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -637,7 +637,6 @@ static int vdbePmaReaderSeek(
|
||||
rc = sqlite3OsRead(
|
||||
pReadr->pFile, &pReadr->aBuffer[iBuf], nRead, pReadr->iReadOff
|
||||
);
|
||||
assert( rc!=SQLITE_IOERR_SHORT_READ );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
109
src/where.c
109
src/where.c
@@ -2722,7 +2722,7 @@ static void explainOneScan(
|
||||
Index *pIdx = pLoop->u.btree.pIndex;
|
||||
char *zWhere = explainIndexRange(db, pLoop, pItem->pTab);
|
||||
assert( !(flags&WHERE_AUTO_INDEX) || (flags&WHERE_IDX_ONLY) );
|
||||
if( !HasRowid(pItem->pTab) && pIdx->autoIndex==2 ){
|
||||
if( !HasRowid(pItem->pTab) && IsPrimaryKeyIndex(pIdx) ){
|
||||
zFmt = zWhere ? "%s USING PRIMARY KEY%.0s%s" : "%s%.0s%s";
|
||||
}else if( flags & WHERE_AUTO_INDEX ){
|
||||
zFmt = "%s USING AUTOMATIC COVERING INDEX%.0s%s";
|
||||
@@ -3223,7 +3223,7 @@ static Bitmask codeOneLoopStart(
|
||||
sqlite3VdbeAddOp2(v, OP_IdxRowid, iIdxCur, iRowidReg);
|
||||
sqlite3ExprCacheStore(pParse, iCur, -1, iRowidReg);
|
||||
sqlite3VdbeAddOp2(v, OP_Seek, iCur, iRowidReg); /* Deferred seek */
|
||||
}else{
|
||||
}else if( iCur!=iIdxCur ){
|
||||
Index *pPk = sqlite3PrimaryKeyIndex(pIdx->pTable);
|
||||
iRowidReg = sqlite3GetTempRange(pParse, pPk->nKeyCol);
|
||||
for(j=0; j<pPk->nKeyCol; j++){
|
||||
@@ -3293,6 +3293,10 @@ static Bitmask codeOneLoopStart(
|
||||
**
|
||||
** B: <after the loop>
|
||||
**
|
||||
** Added 2014-05-26: If the table is a WITHOUT ROWID table, then
|
||||
** use an ephermeral index instead of a RowSet to record the primary
|
||||
** keys of the rows we have already seen.
|
||||
**
|
||||
*/
|
||||
WhereClause *pOrWc; /* The OR-clause broken out into subterms */
|
||||
SrcList *pOrTab; /* Shortened table list or OR-clause generation */
|
||||
@@ -3307,6 +3311,7 @@ static Bitmask codeOneLoopStart(
|
||||
int untestedTerms = 0; /* Some terms not completely tested */
|
||||
int ii; /* Loop counter */
|
||||
Expr *pAndExpr = 0; /* An ".. AND (...)" expression */
|
||||
Table *pTab = pTabItem->pTab;
|
||||
|
||||
pTerm = pLoop->aLTerm[0];
|
||||
assert( pTerm!=0 );
|
||||
@@ -3339,7 +3344,8 @@ static Bitmask codeOneLoopStart(
|
||||
}
|
||||
|
||||
/* Initialize the rowset register to contain NULL. An SQL NULL is
|
||||
** equivalent to an empty rowset.
|
||||
** equivalent to an empty rowset. Or, create an ephermeral index
|
||||
** capable of holding primary keys in the case of a WITHOUT ROWID.
|
||||
**
|
||||
** Also initialize regReturn to contain the address of the instruction
|
||||
** immediately following the OP_Return at the bottom of the loop. This
|
||||
@@ -3350,9 +3356,16 @@ static Bitmask codeOneLoopStart(
|
||||
** called on an uninitialized cursor.
|
||||
*/
|
||||
if( (pWInfo->wctrlFlags & WHERE_DUPLICATES_OK)==0 ){
|
||||
regRowset = ++pParse->nMem;
|
||||
if( HasRowid(pTab) ){
|
||||
regRowset = ++pParse->nMem;
|
||||
sqlite3VdbeAddOp2(v, OP_Null, 0, regRowset);
|
||||
}else{
|
||||
Index *pPk = sqlite3PrimaryKeyIndex(pTab);
|
||||
regRowset = pParse->nTab++;
|
||||
sqlite3VdbeAddOp2(v, OP_OpenEphemeral, regRowset, pPk->nKeyCol);
|
||||
sqlite3VdbeSetP4KeyInfo(pParse, pPk);
|
||||
}
|
||||
regRowid = ++pParse->nMem;
|
||||
sqlite3VdbeAddOp2(v, OP_Null, 0, regRowset);
|
||||
}
|
||||
iRetInit = sqlite3VdbeAddOp2(v, OP_Integer, 0, regReturn);
|
||||
|
||||
@@ -3388,11 +3401,16 @@ static Bitmask codeOneLoopStart(
|
||||
}
|
||||
}
|
||||
|
||||
/* Run a separate WHERE clause for each term of the OR clause. After
|
||||
** eliminating duplicates from other WHERE clauses, the action for each
|
||||
** sub-WHERE clause is to to invoke the main loop body as a subroutine.
|
||||
*/
|
||||
for(ii=0; ii<pOrWc->nTerm; ii++){
|
||||
WhereTerm *pOrTerm = &pOrWc->a[ii];
|
||||
if( pOrTerm->leftCursor==iCur || (pOrTerm->eOperator & WO_AND)!=0 ){
|
||||
WhereInfo *pSubWInfo; /* Info for single OR-term scan */
|
||||
Expr *pOrExpr = pOrTerm->pExpr;
|
||||
WhereInfo *pSubWInfo; /* Info for single OR-term scan */
|
||||
Expr *pOrExpr = pOrTerm->pExpr; /* Current OR clause term */
|
||||
int j1 = 0; /* Address of jump operation */
|
||||
if( pAndExpr && !ExprHasProperty(pOrExpr, EP_FromJoin) ){
|
||||
pAndExpr->pLeft = pOrExpr;
|
||||
pOrExpr = pAndExpr;
|
||||
@@ -3407,17 +3425,63 @@ static Bitmask codeOneLoopStart(
|
||||
explainOneScan(
|
||||
pParse, pOrTab, &pSubWInfo->a[0], iLevel, pLevel->iFrom, 0
|
||||
);
|
||||
/* This is the sub-WHERE clause body. First skip over
|
||||
** duplicate rows from prior sub-WHERE clauses, and record the
|
||||
** rowid (or PRIMARY KEY) for the current row so that the same
|
||||
** row will be skipped in subsequent sub-WHERE clauses.
|
||||
*/
|
||||
if( (pWInfo->wctrlFlags & WHERE_DUPLICATES_OK)==0 ){
|
||||
int iSet = ((ii==pOrWc->nTerm-1)?-1:ii);
|
||||
int r;
|
||||
r = sqlite3ExprCodeGetColumn(pParse, pTabItem->pTab, -1, iCur,
|
||||
regRowid, 0);
|
||||
sqlite3VdbeAddOp4Int(v, OP_RowSetTest, regRowset,
|
||||
sqlite3VdbeCurrentAddr(v)+2, r, iSet);
|
||||
VdbeCoverage(v);
|
||||
int iSet = ((ii==pOrWc->nTerm-1)?-1:ii);
|
||||
if( HasRowid(pTab) ){
|
||||
r = sqlite3ExprCodeGetColumn(pParse, pTab, -1, iCur, regRowid, 0);
|
||||
j1 = sqlite3VdbeAddOp4Int(v, OP_RowSetTest, regRowset, 0, r,iSet);
|
||||
VdbeCoverage(v);
|
||||
}else{
|
||||
Index *pPk = sqlite3PrimaryKeyIndex(pTab);
|
||||
int nPk = pPk->nKeyCol;
|
||||
int iPk;
|
||||
|
||||
/* Read the PK into an array of temp registers. */
|
||||
r = sqlite3GetTempRange(pParse, nPk);
|
||||
for(iPk=0; iPk<nPk; iPk++){
|
||||
int iCol = pPk->aiColumn[iPk];
|
||||
sqlite3ExprCodeGetColumn(pParse, pTab, iCol, iCur, r+iPk, 0);
|
||||
}
|
||||
|
||||
/* Check if the temp table already contains this key. If so,
|
||||
** the row has already been included in the result set and
|
||||
** can be ignored (by jumping past the Gosub below). Otherwise,
|
||||
** insert the key into the temp table and proceed with processing
|
||||
** the row.
|
||||
**
|
||||
** Use some of the same optimizations as OP_RowSetTest: If iSet
|
||||
** is zero, assume that the key cannot already be present in
|
||||
** the temp table. And if iSet is -1, assume that there is no
|
||||
** need to insert the key into the temp table, as it will never
|
||||
** be tested for. */
|
||||
if( iSet ){
|
||||
j1 = sqlite3VdbeAddOp4Int(v, OP_Found, regRowset, 0, r, nPk);
|
||||
VdbeCoverage(v);
|
||||
}
|
||||
if( iSet>=0 ){
|
||||
sqlite3VdbeAddOp3(v, OP_MakeRecord, r, nPk, regRowid);
|
||||
sqlite3VdbeAddOp3(v, OP_IdxInsert, regRowset, regRowid, 0);
|
||||
if( iSet ) sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT);
|
||||
}
|
||||
|
||||
/* Release the array of temp registers */
|
||||
sqlite3ReleaseTempRange(pParse, r, nPk);
|
||||
}
|
||||
}
|
||||
|
||||
/* Invoke the main loop body as a subroutine */
|
||||
sqlite3VdbeAddOp2(v, OP_Gosub, regReturn, iLoopBody);
|
||||
|
||||
/* Jump here (skipping the main loop body subroutine) if the
|
||||
** current sub-WHERE row is a duplicate from prior sub-WHEREs. */
|
||||
if( j1 ) sqlite3VdbeJumpHere(v, j1);
|
||||
|
||||
/* The pSubWInfo->untestedTerms flag means that this OR term
|
||||
** contained one or more AND term from a notReady table. The
|
||||
** terms from the notReady table could not be tested and will
|
||||
@@ -3441,6 +3505,7 @@ static Bitmask codeOneLoopStart(
|
||||
assert( (pSubLoop->wsFlags & WHERE_AUTO_INDEX)==0 );
|
||||
if( (pSubLoop->wsFlags & WHERE_INDEXED)!=0
|
||||
&& (ii==0 || pSubLoop->u.btree.pIndex==pCov)
|
||||
&& (HasRowid(pTab) || !IsPrimaryKeyIndex(pSubLoop->u.btree.pIndex))
|
||||
){
|
||||
assert( pSubWInfo->a[0].iIdxCur==iCovCur );
|
||||
pCov = pSubLoop->u.btree.pIndex;
|
||||
@@ -4766,7 +4831,6 @@ static int whereLoopAddOr(WhereLoopBuilder *pBuilder, Bitmask mExtra){
|
||||
pNew = pBuilder->pNew;
|
||||
memset(&sSum, 0, sizeof(sSum));
|
||||
pItem = pWInfo->pTabList->a + pNew->iTab;
|
||||
if( !HasRowid(pItem->pTab) ) return SQLITE_OK;
|
||||
iCur = pItem->iCursor;
|
||||
|
||||
for(pTerm=pWC->a; pTerm<pWCEnd && rc==SQLITE_OK; pTerm++){
|
||||
@@ -6015,7 +6079,14 @@ WhereInfo *sqlite3WhereBegin(
|
||||
int op = OP_OpenRead;
|
||||
/* iIdxCur is always set if to a positive value if ONEPASS is possible */
|
||||
assert( iIdxCur!=0 || (pWInfo->wctrlFlags & WHERE_ONEPASS_DESIRED)==0 );
|
||||
if( pWInfo->okOnePass ){
|
||||
if( !HasRowid(pTab) && IsPrimaryKeyIndex(pIx)
|
||||
&& (wctrlFlags & WHERE_ONETABLE_ONLY)!=0
|
||||
){
|
||||
/* This is one term of an OR-optimization using the PRIMARY KEY of a
|
||||
** WITHOUT ROWID table. No need for a separate index */
|
||||
iIndexCur = pLevel->iTabCur;
|
||||
op = 0;
|
||||
}else if( pWInfo->okOnePass ){
|
||||
Index *pJ = pTabItem->pTab->pIndex;
|
||||
iIndexCur = iIdxCur;
|
||||
assert( wctrlFlags & WHERE_ONEPASS_DESIRED );
|
||||
@@ -6033,9 +6104,11 @@ WhereInfo *sqlite3WhereBegin(
|
||||
pLevel->iIdxCur = iIndexCur;
|
||||
assert( pIx->pSchema==pTab->pSchema );
|
||||
assert( iIndexCur>=0 );
|
||||
sqlite3VdbeAddOp3(v, op, iIndexCur, pIx->tnum, iDb);
|
||||
sqlite3VdbeSetP4KeyInfo(pParse, pIx);
|
||||
VdbeComment((v, "%s", pIx->zName));
|
||||
if( op ){
|
||||
sqlite3VdbeAddOp3(v, op, iIndexCur, pIx->tnum, iDb);
|
||||
sqlite3VdbeSetP4KeyInfo(pParse, pIx);
|
||||
VdbeComment((v, "%s", pIx->zName));
|
||||
}
|
||||
}
|
||||
if( iDb>=0 ) sqlite3CodeVerifySchema(pParse, iDb);
|
||||
notReady &= ~getMask(&pWInfo->sMaskSet, pTabItem->iCursor);
|
||||
|
||||
Reference in New Issue
Block a user