Compare commits
53 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 470f17e8d9 | |||
| 338e311acb | |||
| 30fc7f8009 | |||
| c8c9cdd9dd | |||
| b9cd2c4536 | |||
| bbd574bc37 | |||
| cf8784cd8f | |||
| 7458a9f3f7 | |||
| 4c57e326ef | |||
| 186ebd41cf | |||
| e8cf689df3 | |||
| 16d261b3ef | |||
| 9e80403bfc | |||
| a02fd67b99 | |||
| 136c9903bf | |||
| 252f39619a | |||
| bc30e2023b | |||
| 8d1751b6fa | |||
| 6c319e1238 | |||
| be4ccb2838 | |||
| d0f9cdcc63 | |||
| 1f22f62582 | |||
| 26fb126622 | |||
| 7578456c25 | |||
| 1e76c22b4a | |||
| e297196524 | |||
| f80bba9d8d | |||
| dd7460f0fd | |||
| a44005af4f | |||
| 492ad131bd | |||
| a090ab90d6 | |||
| a3926f4bbf | |||
| fc97c1c8cb | |||
| 69ed38a822 | |||
| 1615c37e48 | |||
| fc29a86eee | |||
| 003edba0da | |||
| f2072d136d | |||
| cb38809159 | |||
| f0f9dbd384 | |||
| 446135d724 | |||
| 0cdbe1aee0 | |||
| 721e8539c3 | |||
| 4c54045372 | |||
| f2cf412a0a | |||
| 5ecf9039b0 | |||
| 89ee229810 | |||
| d720d394d0 | |||
| e3c05a5597 | |||
| e2188f0b0f | |||
| 60208c34bb | |||
| c6f36fa33a | |||
| 6fcf83a503 |
+2
-1
@@ -572,7 +572,8 @@ FUZZDATA = \
|
||||
$(TOP)/test/fuzzdata2.db \
|
||||
$(TOP)/test/fuzzdata3.db \
|
||||
$(TOP)/test/fuzzdata4.db \
|
||||
$(TOP)/test/fuzzdata5.db
|
||||
$(TOP)/test/fuzzdata5.db \
|
||||
$(TOP)/test/fuzzdata6.db
|
||||
|
||||
# Standard options to testfixture
|
||||
#
|
||||
|
||||
+2
-1
@@ -1599,7 +1599,8 @@ FUZZDATA = \
|
||||
$(TOP)\test\fuzzdata2.db \
|
||||
$(TOP)\test\fuzzdata3.db \
|
||||
$(TOP)\test\fuzzdata4.db \
|
||||
$(TOP)\test\fuzzdata5.db
|
||||
$(TOP)\test\fuzzdata5.db \
|
||||
$(TOP)\test\fuzzdata6.db
|
||||
# <</mark>>
|
||||
|
||||
# Additional compiler options for the shell. These are only effective
|
||||
|
||||
@@ -41,7 +41,6 @@ foreach {tn t} {1 ascii 2 unicode61} {
|
||||
#-------------------------------------------------------------------------
|
||||
# Check that "unicode61" really is the default tokenizer.
|
||||
#
|
||||
|
||||
do_execsql_test 2.0 "
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(x);
|
||||
CREATE VIRTUAL TABLE t2 USING fts5(x, tokenize = unicode61);
|
||||
@@ -56,5 +55,31 @@ do_execsql_test 2.1 "
|
||||
SELECT 't3' FROM t3 WHERE t3 MATCH '\xE0\xE8\xEC';
|
||||
" {t1 t2}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Check that codepoints that require 4 bytes to store in utf-8 (those that
|
||||
# require 17 or more bits to store).
|
||||
#
|
||||
|
||||
set A [db one {SELECT char(0x1F75E)}] ;# Type So
|
||||
set B [db one {SELECT char(0x1F5FD)}] ;# Type So
|
||||
set C [db one {SELECT char(0x2F802)}] ;# Type Lo
|
||||
set D [db one {SELECT char(0x2F808)}] ;# Type Lo
|
||||
|
||||
do_execsql_test 3.0 "
|
||||
CREATE VIRTUAL TABLE xyz USING fts5(x,
|
||||
tokenize = \"unicode61 separators '$C' tokenchars '$A'\"
|
||||
);
|
||||
CREATE VIRTUAL TABLE xyz_v USING fts5vocab(xyz, row);
|
||||
|
||||
INSERT INTO xyz VALUES('$A$B$C$D');
|
||||
"
|
||||
|
||||
do_execsql_test 3.1 {
|
||||
SELECT * FROM xyz_v;
|
||||
} [list $A 1 1 $D 1 1]
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
+1
-1
@@ -2118,7 +2118,7 @@ static int jsonEachColumn(
|
||||
}
|
||||
if( p->eType==JSON_ARRAY ){
|
||||
jsonPrintf(30, &x, "[%d]", p->iRowid);
|
||||
}else{
|
||||
}else if( p->eType==JSON_OBJECT ){
|
||||
jsonPrintf(pThis->n, &x, ".%.*s", pThis->n-2, pThis->u.zJContent+1);
|
||||
}
|
||||
}
|
||||
|
||||
+301
-102
@@ -24,14 +24,15 @@
|
||||
**
|
||||
** CREATE TABLE %_node(nodeno INTEGER PRIMARY KEY, data BLOB)
|
||||
** CREATE TABLE %_parent(nodeno INTEGER PRIMARY KEY, parentnode INTEGER)
|
||||
** CREATE TABLE %_rowid(rowid INTEGER PRIMARY KEY, nodeno INTEGER)
|
||||
** CREATE TABLE %_rowid(rowid INTEGER PRIMARY KEY, nodeno INTEGER, ...)
|
||||
**
|
||||
** The data for each node of the r-tree structure is stored in the %_node
|
||||
** table. For each node that is not the root node of the r-tree, there is
|
||||
** an entry in the %_parent table associating the node with its parent.
|
||||
** And for each row of data in the table, there is an entry in the %_rowid
|
||||
** table that maps from the entries rowid to the id of the node that it
|
||||
** is stored on.
|
||||
** is stored on. If the r-tree contains auxiliary columns, those are stored
|
||||
** on the end of the %_rowid table.
|
||||
**
|
||||
** The root node of an r-tree always exists, even if the r-tree table is
|
||||
** empty. The nodeno of the root node is always 1. All other nodes in the
|
||||
@@ -94,6 +95,9 @@ typedef struct RtreeSearchPoint RtreeSearchPoint;
|
||||
/* The rtree may have between 1 and RTREE_MAX_DIMENSIONS dimensions. */
|
||||
#define RTREE_MAX_DIMENSIONS 5
|
||||
|
||||
/* Maximum number of auxiliary columns */
|
||||
#define RTREE_MAX_AUX_COLUMN 100
|
||||
|
||||
/* Size of hash table Rtree.aHash. This hash table is not expected to
|
||||
** ever contain very many entries, so a fixed number of buckets is
|
||||
** used.
|
||||
@@ -122,12 +126,15 @@ struct Rtree {
|
||||
u8 eCoordType; /* RTREE_COORD_REAL32 or RTREE_COORD_INT32 */
|
||||
u8 nBytesPerCell; /* Bytes consumed per cell */
|
||||
u8 inWrTrans; /* True if inside write transaction */
|
||||
u8 nAux; /* # of auxiliary columns in %_rowid */
|
||||
int iDepth; /* Current depth of the r-tree structure */
|
||||
char *zDb; /* Name of database containing r-tree table */
|
||||
char *zName; /* Name of r-tree table */
|
||||
u32 nBusy; /* Current number of users of this structure */
|
||||
i64 nRowEst; /* Estimated number of rows in this table */
|
||||
u32 nCursor; /* Number of open cursors */
|
||||
u32 nNodeRef; /* Number RtreeNodes with positive nRef */
|
||||
char *zReadAuxSql; /* SQL for statement to read aux data */
|
||||
|
||||
/* List of nodes removed during a CondenseTree operation. List is
|
||||
** linked together via the pointer normally used for hash chains -
|
||||
@@ -154,6 +161,9 @@ struct Rtree {
|
||||
sqlite3_stmt *pWriteParent;
|
||||
sqlite3_stmt *pDeleteParent;
|
||||
|
||||
/* Statement for writing to the "aux:" fields, if there are any */
|
||||
sqlite3_stmt *pWriteAux;
|
||||
|
||||
RtreeNode *aHash[HASHSIZE]; /* Hash table of in-memory nodes. */
|
||||
};
|
||||
|
||||
@@ -230,6 +240,7 @@ struct RtreeCursor {
|
||||
sqlite3_vtab_cursor base; /* Base class. Must be first */
|
||||
u8 atEOF; /* True if at end of search */
|
||||
u8 bPoint; /* True if sPoint is valid */
|
||||
u8 bAuxValid; /* True if pReadAux is valid */
|
||||
int iStrategy; /* Copy of idxNum search parameter */
|
||||
int nConstraint; /* Number of entries in aConstraint */
|
||||
RtreeConstraint *aConstraint; /* Search constraints. */
|
||||
@@ -237,6 +248,7 @@ struct RtreeCursor {
|
||||
int nPoint; /* Number of slots used in aPoint[] */
|
||||
int mxLevel; /* iLevel value for root of the tree */
|
||||
RtreeSearchPoint *aPoint; /* Priority queue for search points */
|
||||
sqlite3_stmt *pReadAux; /* Statement to read aux-data */
|
||||
RtreeSearchPoint sPoint; /* Cached next search point */
|
||||
RtreeNode *aNode[RTREE_CACHE_SZ]; /* Rtree node cache */
|
||||
u32 anQueue[RTREE_MAX_DEPTH+1]; /* Number of queued entries by iLevel */
|
||||
@@ -523,6 +535,7 @@ static int writeInt64(u8 *p, i64 i){
|
||||
*/
|
||||
static void nodeReference(RtreeNode *p){
|
||||
if( p ){
|
||||
assert( p->nRef>0 );
|
||||
p->nRef++;
|
||||
}
|
||||
}
|
||||
@@ -590,6 +603,7 @@ static RtreeNode *nodeNew(Rtree *pRtree, RtreeNode *pParent){
|
||||
memset(pNode, 0, sizeof(RtreeNode) + pRtree->iNodeSize);
|
||||
pNode->zData = (u8 *)&pNode[1];
|
||||
pNode->nRef = 1;
|
||||
pRtree->nNodeRef++;
|
||||
pNode->pParent = pParent;
|
||||
pNode->isDirty = 1;
|
||||
nodeReference(pParent);
|
||||
@@ -623,10 +637,10 @@ static int nodeAcquire(
|
||||
/* Check if the requested node is already in the hash table. If so,
|
||||
** increase its reference count and return it.
|
||||
*/
|
||||
if( (pNode = nodeHashLookup(pRtree, iNode)) ){
|
||||
if( (pNode = nodeHashLookup(pRtree, iNode))!=0 ){
|
||||
assert( !pParent || !pNode->pParent || pNode->pParent==pParent );
|
||||
if( pParent && !pNode->pParent ){
|
||||
nodeReference(pParent);
|
||||
pParent->nRef++;
|
||||
pNode->pParent = pParent;
|
||||
}
|
||||
pNode->nRef++;
|
||||
@@ -665,6 +679,7 @@ static int nodeAcquire(
|
||||
pNode->pParent = pParent;
|
||||
pNode->zData = (u8 *)&pNode[1];
|
||||
pNode->nRef = 1;
|
||||
pRtree->nNodeRef++;
|
||||
pNode->iNode = iNode;
|
||||
pNode->isDirty = 0;
|
||||
pNode->pNext = 0;
|
||||
@@ -705,7 +720,10 @@ static int nodeAcquire(
|
||||
}
|
||||
*ppNode = pNode;
|
||||
}else{
|
||||
sqlite3_free(pNode);
|
||||
if( pNode ){
|
||||
pRtree->nNodeRef--;
|
||||
sqlite3_free(pNode);
|
||||
}
|
||||
*ppNode = 0;
|
||||
}
|
||||
|
||||
@@ -802,8 +820,10 @@ static int nodeRelease(Rtree *pRtree, RtreeNode *pNode){
|
||||
int rc = SQLITE_OK;
|
||||
if( pNode ){
|
||||
assert( pNode->nRef>0 );
|
||||
assert( pRtree->nNodeRef>0 );
|
||||
pNode->nRef--;
|
||||
if( pNode->nRef==0 ){
|
||||
pRtree->nNodeRef--;
|
||||
if( pNode->iNode==1 ){
|
||||
pRtree->iDepth = -1;
|
||||
}
|
||||
@@ -920,8 +940,9 @@ static void rtreeRelease(Rtree *pRtree){
|
||||
pRtree->nBusy--;
|
||||
if( pRtree->nBusy==0 ){
|
||||
pRtree->inWrTrans = 0;
|
||||
pRtree->nCursor = 0;
|
||||
assert( pRtree->nCursor==0 );
|
||||
nodeBlobReset(pRtree);
|
||||
assert( pRtree->nNodeRef==0 );
|
||||
sqlite3_finalize(pRtree->pWriteNode);
|
||||
sqlite3_finalize(pRtree->pDeleteNode);
|
||||
sqlite3_finalize(pRtree->pReadRowid);
|
||||
@@ -930,6 +951,8 @@ static void rtreeRelease(Rtree *pRtree){
|
||||
sqlite3_finalize(pRtree->pReadParent);
|
||||
sqlite3_finalize(pRtree->pWriteParent);
|
||||
sqlite3_finalize(pRtree->pDeleteParent);
|
||||
sqlite3_finalize(pRtree->pWriteAux);
|
||||
sqlite3_free(pRtree->zReadAuxSql);
|
||||
sqlite3_free(pRtree);
|
||||
}
|
||||
}
|
||||
@@ -1018,6 +1041,7 @@ static int rtreeClose(sqlite3_vtab_cursor *cur){
|
||||
RtreeCursor *pCsr = (RtreeCursor *)cur;
|
||||
assert( pRtree->nCursor>0 );
|
||||
freeCursorConstraints(pCsr);
|
||||
sqlite3_finalize(pCsr->pReadAux);
|
||||
sqlite3_free(pCsr->aPoint);
|
||||
for(ii=0; ii<RTREE_CACHE_SZ; ii++) nodeRelease(pRtree, pCsr->aNode[ii]);
|
||||
sqlite3_free(pCsr);
|
||||
@@ -1389,7 +1413,7 @@ static RtreeSearchPoint *rtreeSearchPointNew(
|
||||
if( ii<RTREE_CACHE_SZ ){
|
||||
assert( pCur->aNode[ii]==0 );
|
||||
pCur->aNode[ii] = pCur->aNode[0];
|
||||
}else{
|
||||
}else{
|
||||
nodeRelease(RTREE_OF_CURSOR(pCur), pCur->aNode[0]);
|
||||
}
|
||||
pCur->aNode[0] = 0;
|
||||
@@ -1560,6 +1584,10 @@ static int rtreeNext(sqlite3_vtab_cursor *pVtabCursor){
|
||||
|
||||
/* Move to the next entry that matches the configured constraints. */
|
||||
RTREE_QUEUE_TRACE(pCsr, "POP-Nx:");
|
||||
if( pCsr->bAuxValid ){
|
||||
pCsr->bAuxValid = 0;
|
||||
sqlite3_reset(pCsr->pReadAux);
|
||||
}
|
||||
rtreeSearchPointPop(pCsr);
|
||||
rc = rtreeStepToLeaf(pCsr);
|
||||
return rc;
|
||||
@@ -1594,7 +1622,7 @@ static int rtreeColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){
|
||||
if( p==0 ) return SQLITE_OK;
|
||||
if( i==0 ){
|
||||
sqlite3_result_int64(ctx, nodeGetRowid(pRtree, pNode, p->iCell));
|
||||
}else{
|
||||
}else if( i<=pRtree->nDim2 ){
|
||||
nodeGetCoord(pRtree, pNode, p->iCell, i-1, &c);
|
||||
#ifndef SQLITE_RTREE_INT_ONLY
|
||||
if( pRtree->eCoordType==RTREE_COORD_REAL32 ){
|
||||
@@ -1605,7 +1633,27 @@ static int rtreeColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){
|
||||
assert( pRtree->eCoordType==RTREE_COORD_INT32 );
|
||||
sqlite3_result_int(ctx, c.i);
|
||||
}
|
||||
}
|
||||
}else{
|
||||
if( !pCsr->bAuxValid ){
|
||||
if( pCsr->pReadAux==0 ){
|
||||
rc = sqlite3_prepare_v3(pRtree->db, pRtree->zReadAuxSql, -1, 0,
|
||||
&pCsr->pReadAux, 0);
|
||||
if( rc ) return rc;
|
||||
}
|
||||
sqlite3_bind_int64(pCsr->pReadAux, 1,
|
||||
nodeGetRowid(pRtree, pNode, p->iCell));
|
||||
rc = sqlite3_step(pCsr->pReadAux);
|
||||
if( rc==SQLITE_ROW ){
|
||||
pCsr->bAuxValid = 1;
|
||||
}else{
|
||||
sqlite3_reset(pCsr->pReadAux);
|
||||
if( rc==SQLITE_DONE ) rc = SQLITE_OK;
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
sqlite3_result_value(ctx,
|
||||
sqlite3_column_value(pCsr->pReadAux, i - pRtree->nDim2 + 1));
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
@@ -1683,14 +1731,17 @@ static int rtreeFilter(
|
||||
int ii;
|
||||
int rc = SQLITE_OK;
|
||||
int iCell = 0;
|
||||
sqlite3_stmt *pStmt;
|
||||
|
||||
rtreeReference(pRtree);
|
||||
|
||||
/* Reset the cursor to the same state as rtreeOpen() leaves it in. */
|
||||
freeCursorConstraints(pCsr);
|
||||
sqlite3_free(pCsr->aPoint);
|
||||
pStmt = pCsr->pReadAux;
|
||||
memset(pCsr, 0, sizeof(RtreeCursor));
|
||||
pCsr->base.pVtab = (sqlite3_vtab*)pRtree;
|
||||
pCsr->pReadAux = pStmt;
|
||||
|
||||
pCsr->iStrategy = idxNum;
|
||||
if( idxNum==1 ){
|
||||
@@ -1853,10 +1904,14 @@ static int rtreeBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
|
||||
*/
|
||||
pIdxInfo->estimatedCost = 30.0;
|
||||
pIdxInfo->estimatedRows = 1;
|
||||
pIdxInfo->idxFlags = SQLITE_INDEX_SCAN_UNIQUE;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
if( p->usable && (p->iColumn>0 || p->op==SQLITE_INDEX_CONSTRAINT_MATCH) ){
|
||||
if( p->usable
|
||||
&& ((p->iColumn>0 && p->iColumn<=pRtree->nDim2)
|
||||
|| p->op==SQLITE_INDEX_CONSTRAINT_MATCH)
|
||||
){
|
||||
u8 op;
|
||||
switch( p->op ){
|
||||
case SQLITE_INDEX_CONSTRAINT_EQ: op = RTREE_EQ; break;
|
||||
@@ -2429,7 +2484,7 @@ static int SplitNode(
|
||||
}else{
|
||||
pLeft = pNode;
|
||||
pRight = nodeNew(pRtree, pLeft->pParent);
|
||||
nodeReference(pLeft);
|
||||
pLeft->nRef++;
|
||||
}
|
||||
|
||||
if( !pLeft || !pRight ){
|
||||
@@ -2919,6 +2974,7 @@ static int rtreeDeleteRowid(Rtree *pRtree, sqlite3_int64 iDelete){
|
||||
rc = reinsertNodeContent(pRtree, pLeaf);
|
||||
}
|
||||
pRtree->pDeleted = pLeaf->pNext;
|
||||
pRtree->nNodeRef--;
|
||||
sqlite3_free(pLeaf);
|
||||
}
|
||||
|
||||
@@ -3015,19 +3071,57 @@ static int rtreeConstraintError(Rtree *pRtree, int iCol){
|
||||
static int rtreeUpdate(
|
||||
sqlite3_vtab *pVtab,
|
||||
int nData,
|
||||
sqlite3_value **azData,
|
||||
sqlite3_value **aData,
|
||||
sqlite_int64 *pRowid
|
||||
){
|
||||
Rtree *pRtree = (Rtree *)pVtab;
|
||||
int rc = SQLITE_OK;
|
||||
RtreeCell cell; /* New cell to insert if nData>1 */
|
||||
int bHaveRowid = 0; /* Set to 1 after new rowid is determined */
|
||||
RtreeCell cell; /* New cell to insert if nData>1 */
|
||||
int bHaveRowid = 0; /* Set to 1 after new rowid is determined */
|
||||
int bRtreeInsert = 0; /* True if rtree data is to be inserted */
|
||||
int nCoord; /* Number of coordinate columns */
|
||||
int ii; /* Loop counter */
|
||||
|
||||
if( pRtree->nNodeRef ){
|
||||
/* Unable to write to the btree while another cursor is reading from it,
|
||||
** since the write might do a rebalance which would disrupt the read
|
||||
** cursor. */
|
||||
return SQLITE_LOCKED_VTAB;
|
||||
}
|
||||
rtreeReference(pRtree);
|
||||
assert(nData>=1);
|
||||
|
||||
cell.iRowid = 0; /* Used only to suppress a compiler warning */
|
||||
|
||||
/* Set bRtreeInsert if this is an INSERT statement, or if it is an
|
||||
** UPDATE statement that changes the rowid or one of the coordinates.
|
||||
** Leave bRtreeInsert at zero if this is a DELETE or if this is an
|
||||
** UPDATE that only changes auxiliary columns.
|
||||
*/
|
||||
if( nData>1 ){
|
||||
nCoord = pRtree->nDim2;
|
||||
|
||||
/* NB: nData can only be less than nDim2+3 if the rtree is mis-declared
|
||||
** with "column" that are interpreted as table constraints.
|
||||
** Example: CREATE VIRTUAL TABLE bad USING rtree(x,y,CHECK(y>5));
|
||||
** This problem was discovered after years of use, so we silently ignore
|
||||
** these kinds of misdeclared tables to avoid breaking any legacy.
|
||||
*/
|
||||
if( nCoord > nData-3 ) nCoord = nData - 3;
|
||||
|
||||
if( sqlite3_value_type(aData[0])==SQLITE_NULL ){
|
||||
bRtreeInsert = 1; /* This is an INSERT statement */
|
||||
}else{
|
||||
/* This is an UPDATE statement. Check to see if the rowid (aData[2])
|
||||
** or any coordinate column (aData[3] through aData[nCoord+2])
|
||||
** has changed. */
|
||||
for(ii=nCoord+2; ii>=2; ii--){
|
||||
if( !sqlite3_value_nochange(aData[ii]) ) break;
|
||||
}
|
||||
bRtreeInsert = ii>=2;
|
||||
}
|
||||
}
|
||||
|
||||
/* Constraint handling. A write operation on an r-tree table may return
|
||||
** SQLITE_CONSTRAINT for two reasons:
|
||||
**
|
||||
@@ -3039,24 +3133,12 @@ static int rtreeUpdate(
|
||||
** case, SQLITE_CONSTRAINT must be returned regardless of the
|
||||
** conflict-handling mode specified by the user.
|
||||
*/
|
||||
if( nData>1 ){
|
||||
int ii;
|
||||
|
||||
/* Populate the cell.aCoord[] array. The first coordinate is azData[3].
|
||||
**
|
||||
** NB: nData can only be less than nDim*2+3 if the rtree is mis-declared
|
||||
** with "column" that are interpreted as table constraints.
|
||||
** Example: CREATE VIRTUAL TABLE bad USING rtree(x,y,CHECK(y>5));
|
||||
** This problem was discovered after years of use, so we silently ignore
|
||||
** these kinds of misdeclared tables to avoid breaking any legacy.
|
||||
*/
|
||||
assert( nData<=(pRtree->nDim2 + 3) );
|
||||
|
||||
if( bRtreeInsert ){
|
||||
#ifndef SQLITE_RTREE_INT_ONLY
|
||||
if( pRtree->eCoordType==RTREE_COORD_REAL32 ){
|
||||
for(ii=0; ii<nData-4; ii+=2){
|
||||
cell.aCoord[ii].f = rtreeValueDown(azData[ii+3]);
|
||||
cell.aCoord[ii+1].f = rtreeValueUp(azData[ii+4]);
|
||||
for(ii=0; ii<nCoord; ii+=2){
|
||||
cell.aCoord[ii].f = rtreeValueDown(aData[ii+3]);
|
||||
cell.aCoord[ii+1].f = rtreeValueUp(aData[ii+4]);
|
||||
if( cell.aCoord[ii].f>cell.aCoord[ii+1].f ){
|
||||
rc = rtreeConstraintError(pRtree, ii+1);
|
||||
goto constraint;
|
||||
@@ -3065,9 +3147,9 @@ static int rtreeUpdate(
|
||||
}else
|
||||
#endif
|
||||
{
|
||||
for(ii=0; ii<nData-4; ii+=2){
|
||||
cell.aCoord[ii].i = sqlite3_value_int(azData[ii+3]);
|
||||
cell.aCoord[ii+1].i = sqlite3_value_int(azData[ii+4]);
|
||||
for(ii=0; ii<nCoord; ii+=2){
|
||||
cell.aCoord[ii].i = sqlite3_value_int(aData[ii+3]);
|
||||
cell.aCoord[ii+1].i = sqlite3_value_int(aData[ii+4]);
|
||||
if( cell.aCoord[ii].i>cell.aCoord[ii+1].i ){
|
||||
rc = rtreeConstraintError(pRtree, ii+1);
|
||||
goto constraint;
|
||||
@@ -3077,10 +3159,10 @@ static int rtreeUpdate(
|
||||
|
||||
/* If a rowid value was supplied, check if it is already present in
|
||||
** the table. If so, the constraint has failed. */
|
||||
if( sqlite3_value_type(azData[2])!=SQLITE_NULL ){
|
||||
cell.iRowid = sqlite3_value_int64(azData[2]);
|
||||
if( sqlite3_value_type(azData[0])==SQLITE_NULL
|
||||
|| sqlite3_value_int64(azData[0])!=cell.iRowid
|
||||
if( sqlite3_value_type(aData[2])!=SQLITE_NULL ){
|
||||
cell.iRowid = sqlite3_value_int64(aData[2]);
|
||||
if( sqlite3_value_type(aData[0])==SQLITE_NULL
|
||||
|| sqlite3_value_int64(aData[0])!=cell.iRowid
|
||||
){
|
||||
int steprc;
|
||||
sqlite3_bind_int64(pRtree->pReadRowid, 1, cell.iRowid);
|
||||
@@ -3099,25 +3181,28 @@ static int rtreeUpdate(
|
||||
}
|
||||
}
|
||||
|
||||
/* If azData[0] is not an SQL NULL value, it is the rowid of a
|
||||
/* If aData[0] is not an SQL NULL value, it is the rowid of a
|
||||
** record to delete from the r-tree table. The following block does
|
||||
** just that.
|
||||
*/
|
||||
if( sqlite3_value_type(azData[0])!=SQLITE_NULL ){
|
||||
rc = rtreeDeleteRowid(pRtree, sqlite3_value_int64(azData[0]));
|
||||
if( sqlite3_value_type(aData[0])!=SQLITE_NULL
|
||||
&& (bRtreeInsert || nData==1)
|
||||
){
|
||||
rc = rtreeDeleteRowid(pRtree, sqlite3_value_int64(aData[0]));
|
||||
}
|
||||
|
||||
/* If the azData[] array contains more than one element, elements
|
||||
** (azData[2]..azData[argc-1]) contain a new record to insert into
|
||||
/* If the aData[] array contains more than one element, elements
|
||||
** (aData[2]..aData[argc-1]) contain a new record to insert into
|
||||
** the r-tree structure.
|
||||
*/
|
||||
if( rc==SQLITE_OK && nData>1 ){
|
||||
if( rc==SQLITE_OK && bRtreeInsert ){
|
||||
/* Insert the new record into the r-tree */
|
||||
RtreeNode *pLeaf = 0;
|
||||
|
||||
/* Figure out the rowid of the new row. */
|
||||
if( bHaveRowid==0 ){
|
||||
rc = newRowid(pRtree, &cell.iRowid);
|
||||
bHaveRowid = 1;
|
||||
}
|
||||
*pRowid = cell.iRowid;
|
||||
|
||||
@@ -3135,6 +3220,37 @@ static int rtreeUpdate(
|
||||
}
|
||||
}
|
||||
|
||||
/* Handle INSERT and UPDATE of auxiliary column data */
|
||||
if( rc==SQLITE_OK && nData>1 && pRtree->nAux ){
|
||||
if( sqlite3_value_type(aData[0])==SQLITE_NULL ){
|
||||
/* This is an INSERT statement. Check to see if any
|
||||
** auxiliary column is non-NULL and hence needs to be set */
|
||||
for(ii=pRtree->nAux+pRtree->nDim2+3; ii<nData; ii++){
|
||||
if( sqlite3_value_type(aData[ii])!=SQLITE_NULL ) break;
|
||||
}
|
||||
}else{
|
||||
/* This is an UPDATE statement. Check to see if any
|
||||
** auxiliary column value has changed. */
|
||||
for(ii=pRtree->nAux+pRtree->nDim2+3; ii<nData; ii++){
|
||||
if( sqlite3_value_nochange(aData[ii])==0 ) break;
|
||||
}
|
||||
}
|
||||
if( ii<nData ){
|
||||
sqlite3_stmt *pUp = pRtree->pWriteAux;
|
||||
int jj;
|
||||
if( bHaveRowid ){
|
||||
sqlite3_bind_int64(pUp, 1, cell.iRowid);
|
||||
}else{
|
||||
sqlite3_bind_int64(pUp, 1, sqlite3_value_int64(aData[1]));
|
||||
}
|
||||
for(jj=0; jj<pRtree->nAux; jj++){
|
||||
sqlite3_bind_value(pUp, jj+2, aData[pRtree->nDim2+3+jj]);
|
||||
}
|
||||
sqlite3_step(pUp);
|
||||
rc = sqlite3_reset(pUp);
|
||||
}
|
||||
}
|
||||
|
||||
constraint:
|
||||
rtreeRelease(pRtree);
|
||||
return rc;
|
||||
@@ -3289,18 +3405,18 @@ static int rtreeSqlInit(
|
||||
#define N_STATEMENT 8
|
||||
static const char *azSql[N_STATEMENT] = {
|
||||
/* Write the xxx_node table */
|
||||
"INSERT OR REPLACE INTO '%q'.'%q_node' VALUES(:1, :2)",
|
||||
"DELETE FROM '%q'.'%q_node' WHERE nodeno = :1",
|
||||
"INSERT OR REPLACE INTO '%q'.'%q_node' VALUES(?1, ?2)",
|
||||
"DELETE FROM '%q'.'%q_node' WHERE nodeno = ?1",
|
||||
|
||||
/* Read and write the xxx_rowid table */
|
||||
"SELECT nodeno FROM '%q'.'%q_rowid' WHERE rowid = :1",
|
||||
"INSERT OR REPLACE INTO '%q'.'%q_rowid' VALUES(:1, :2)",
|
||||
"DELETE FROM '%q'.'%q_rowid' WHERE rowid = :1",
|
||||
"SELECT nodeno FROM '%q'.'%q_rowid' WHERE rowid = ?1",
|
||||
"INSERT OR REPLACE INTO '%q'.'%q_rowid' VALUES(?1, ?2)",
|
||||
"DELETE FROM '%q'.'%q_rowid' WHERE rowid = ?1",
|
||||
|
||||
/* Read and write the xxx_parent table */
|
||||
"SELECT parentnode FROM '%q'.'%q_parent' WHERE nodeno = :1",
|
||||
"INSERT OR REPLACE INTO '%q'.'%q_parent' VALUES(:1, :2)",
|
||||
"DELETE FROM '%q'.'%q_parent' WHERE nodeno = :1"
|
||||
"SELECT parentnode FROM '%q'.'%q_parent' WHERE nodeno = ?1",
|
||||
"INSERT OR REPLACE INTO '%q'.'%q_parent' VALUES(?1, ?2)",
|
||||
"DELETE FROM '%q'.'%q_parent' WHERE nodeno = ?1"
|
||||
};
|
||||
sqlite3_stmt **appStmt[N_STATEMENT];
|
||||
int i;
|
||||
@@ -3308,14 +3424,25 @@ static int rtreeSqlInit(
|
||||
pRtree->db = db;
|
||||
|
||||
if( isCreate ){
|
||||
char *zCreate = sqlite3_mprintf(
|
||||
"CREATE TABLE \"%w\".\"%w_node\"(nodeno INTEGER PRIMARY KEY, data BLOB);"
|
||||
"CREATE TABLE \"%w\".\"%w_rowid\"(rowid INTEGER PRIMARY KEY, nodeno INTEGER);"
|
||||
"CREATE TABLE \"%w\".\"%w_parent\"(nodeno INTEGER PRIMARY KEY,"
|
||||
" parentnode INTEGER);"
|
||||
"INSERT INTO '%q'.'%q_node' VALUES(1, zeroblob(%d))",
|
||||
zDb, zPrefix, zDb, zPrefix, zDb, zPrefix, zDb, zPrefix, pRtree->iNodeSize
|
||||
);
|
||||
char *zCreate;
|
||||
sqlite3_str *p = sqlite3_str_new(db);
|
||||
int ii;
|
||||
sqlite3_str_appendf(p,
|
||||
"CREATE TABLE \"%w\".\"%w_rowid\"(rowid INTEGER PRIMARY KEY,nodeno",
|
||||
zDb, zPrefix);
|
||||
for(ii=0; ii<pRtree->nAux; ii++){
|
||||
sqlite3_str_appendf(p,",a%d",ii);
|
||||
}
|
||||
sqlite3_str_appendf(p,
|
||||
");CREATE TABLE \"%w\".\"%w_node\"(nodeno INTEGER PRIMARY KEY,data);",
|
||||
zDb, zPrefix);
|
||||
sqlite3_str_appendf(p,
|
||||
"CREATE TABLE \"%w\".\"%w_parent\"(nodeno INTEGER PRIMARY KEY,parentnode);",
|
||||
zDb, zPrefix);
|
||||
sqlite3_str_appendf(p,
|
||||
"INSERT INTO \"%w\".\"%w_node\"VALUES(1,zeroblob(%d))",
|
||||
zDb, zPrefix, pRtree->iNodeSize);
|
||||
zCreate = sqlite3_str_finish(p);
|
||||
if( !zCreate ){
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
@@ -3337,7 +3464,17 @@ static int rtreeSqlInit(
|
||||
|
||||
rc = rtreeQueryStat1(db, pRtree);
|
||||
for(i=0; i<N_STATEMENT && rc==SQLITE_OK; i++){
|
||||
char *zSql = sqlite3_mprintf(azSql[i], zDb, zPrefix);
|
||||
char *zSql;
|
||||
const char *zFormat;
|
||||
if( i!=3 || pRtree->nAux==0 ){
|
||||
zFormat = azSql[i];
|
||||
}else {
|
||||
/* An UPSERT is very slightly slower than REPLACE, but it is needed
|
||||
** if there are auxiliary columns */
|
||||
zFormat = "INSERT INTO\"%w\".\"%w_rowid\"(rowid,nodeno)VALUES(?1,?2)"
|
||||
"ON CONFLICT(rowid)DO UPDATE SET nodeno=excluded.nodeno";
|
||||
}
|
||||
zSql = sqlite3_mprintf(zFormat, zDb, zPrefix);
|
||||
if( zSql ){
|
||||
rc = sqlite3_prepare_v3(db, zSql, -1, SQLITE_PREPARE_PERSISTENT,
|
||||
appStmt[i], 0);
|
||||
@@ -3346,6 +3483,32 @@ static int rtreeSqlInit(
|
||||
}
|
||||
sqlite3_free(zSql);
|
||||
}
|
||||
if( pRtree->nAux ){
|
||||
pRtree->zReadAuxSql = sqlite3_mprintf(
|
||||
"SELECT * FROM \"%w\".\"%w_rowid\" WHERE rowid=?1",
|
||||
zDb, zPrefix);
|
||||
if( pRtree->zReadAuxSql==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
sqlite3_str *p = sqlite3_str_new(db);
|
||||
int ii;
|
||||
char *zSql;
|
||||
sqlite3_str_appendf(p, "UPDATE \"%w\".\"%w_rowid\"SET ", zDb, zPrefix);
|
||||
for(ii=0; ii<pRtree->nAux; ii++){
|
||||
if( ii ) sqlite3_str_append(p, ",", 1);
|
||||
sqlite3_str_appendf(p,"a%d=?%d",ii,ii+2);
|
||||
}
|
||||
sqlite3_str_appendf(p, " WHERE rowid=?1");
|
||||
zSql = sqlite3_str_finish(p);
|
||||
if( zSql==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
rc = sqlite3_prepare_v3(db, zSql, -1, SQLITE_PREPARE_PERSISTENT,
|
||||
&pRtree->pWriteAux, 0);
|
||||
sqlite3_free(zSql);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
@@ -3448,17 +3611,22 @@ static int rtreeInit(
|
||||
int nDb; /* Length of string argv[1] */
|
||||
int nName; /* Length of string argv[2] */
|
||||
int eCoordType = (pAux ? RTREE_COORD_INT32 : RTREE_COORD_REAL32);
|
||||
sqlite3_str *pSql;
|
||||
char *zSql;
|
||||
int ii = 4;
|
||||
int iErr;
|
||||
|
||||
const char *aErrMsg[] = {
|
||||
0, /* 0 */
|
||||
"Wrong number of columns for an rtree table", /* 1 */
|
||||
"Too few columns for an rtree table", /* 2 */
|
||||
"Too many columns for an rtree table" /* 3 */
|
||||
"Too many columns for an rtree table", /* 3 */
|
||||
"Auxiliary rtree columns must be last" /* 4 */
|
||||
};
|
||||
|
||||
int iErr = (argc<6) ? 2 : argc>(RTREE_MAX_DIMENSIONS*2+4) ? 3 : argc%2;
|
||||
if( aErrMsg[iErr] ){
|
||||
*pzErr = sqlite3_mprintf("%s", aErrMsg[iErr]);
|
||||
assert( RTREE_MAX_AUX_COLUMN<256 ); /* Aux columns counted by a u8 */
|
||||
if( argc>RTREE_MAX_AUX_COLUMN+3 ){
|
||||
*pzErr = sqlite3_mprintf("%s", aErrMsg[3]);
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
|
||||
@@ -3476,53 +3644,73 @@ static int rtreeInit(
|
||||
pRtree->base.pModule = &rtreeModule;
|
||||
pRtree->zDb = (char *)&pRtree[1];
|
||||
pRtree->zName = &pRtree->zDb[nDb+1];
|
||||
pRtree->nDim = (u8)((argc-4)/2);
|
||||
pRtree->nDim2 = pRtree->nDim*2;
|
||||
pRtree->nBytesPerCell = 8 + pRtree->nDim2*4;
|
||||
pRtree->eCoordType = (u8)eCoordType;
|
||||
memcpy(pRtree->zDb, argv[1], nDb);
|
||||
memcpy(pRtree->zName, argv[2], nName);
|
||||
|
||||
/* Figure out the node size to use. */
|
||||
rc = getNodeSize(db, pRtree, isCreate, pzErr);
|
||||
|
||||
/* Create/Connect to the underlying relational database schema. If
|
||||
** that is successful, call sqlite3_declare_vtab() to configure
|
||||
** the r-tree table schema.
|
||||
*/
|
||||
if( rc==SQLITE_OK ){
|
||||
if( (rc = rtreeSqlInit(pRtree, db, argv[1], argv[2], isCreate)) ){
|
||||
*pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(db));
|
||||
pSql = sqlite3_str_new(db);
|
||||
sqlite3_str_appendf(pSql, "CREATE TABLE x(%s", argv[3]);
|
||||
for(ii=4; ii<argc; ii++){
|
||||
if( argv[ii][0]=='+' ){
|
||||
pRtree->nAux++;
|
||||
sqlite3_str_appendf(pSql, ",%s", argv[ii]+1);
|
||||
}else if( pRtree->nAux>0 ){
|
||||
break;
|
||||
}else{
|
||||
char *zSql = sqlite3_mprintf("CREATE TABLE x(%s", argv[3]);
|
||||
char *zTmp;
|
||||
int ii;
|
||||
for(ii=4; zSql && ii<argc; ii++){
|
||||
zTmp = zSql;
|
||||
zSql = sqlite3_mprintf("%s, %s", zTmp, argv[ii]);
|
||||
sqlite3_free(zTmp);
|
||||
}
|
||||
if( zSql ){
|
||||
zTmp = zSql;
|
||||
zSql = sqlite3_mprintf("%s);", zTmp);
|
||||
sqlite3_free(zTmp);
|
||||
}
|
||||
if( !zSql ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else if( SQLITE_OK!=(rc = sqlite3_declare_vtab(db, zSql)) ){
|
||||
*pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(db));
|
||||
}
|
||||
sqlite3_free(zSql);
|
||||
pRtree->nDim2++;
|
||||
sqlite3_str_appendf(pSql, ",%s", argv[ii]);
|
||||
}
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
*ppVtab = (sqlite3_vtab *)pRtree;
|
||||
}else{
|
||||
assert( *ppVtab==0 );
|
||||
assert( pRtree->nBusy==1 );
|
||||
rtreeRelease(pRtree);
|
||||
sqlite3_str_appendf(pSql, ");");
|
||||
zSql = sqlite3_str_finish(pSql);
|
||||
if( !zSql ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else if( ii<argc ){
|
||||
*pzErr = sqlite3_mprintf("%s", aErrMsg[4]);
|
||||
rc = SQLITE_ERROR;
|
||||
}else if( SQLITE_OK!=(rc = sqlite3_declare_vtab(db, zSql)) ){
|
||||
*pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(db));
|
||||
}
|
||||
sqlite3_free(zSql);
|
||||
if( rc ) goto rtreeInit_fail;
|
||||
pRtree->nDim = pRtree->nDim2/2;
|
||||
if( pRtree->nDim<1 ){
|
||||
iErr = 2;
|
||||
}else if( pRtree->nDim2>RTREE_MAX_DIMENSIONS*2 ){
|
||||
iErr = 3;
|
||||
}else if( pRtree->nDim2 % 2 ){
|
||||
iErr = 1;
|
||||
}else{
|
||||
iErr = 0;
|
||||
}
|
||||
if( iErr ){
|
||||
*pzErr = sqlite3_mprintf("%s", aErrMsg[iErr]);
|
||||
goto rtreeInit_fail;
|
||||
}
|
||||
pRtree->nBytesPerCell = 8 + pRtree->nDim2*4;
|
||||
|
||||
/* Figure out the node size to use. */
|
||||
rc = getNodeSize(db, pRtree, isCreate, pzErr);
|
||||
if( rc ) goto rtreeInit_fail;
|
||||
rc = rtreeSqlInit(pRtree, db, argv[1], argv[2], isCreate);
|
||||
if( rc ){
|
||||
*pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(db));
|
||||
goto rtreeInit_fail;
|
||||
}
|
||||
|
||||
*ppVtab = (sqlite3_vtab *)pRtree;
|
||||
return SQLITE_OK;
|
||||
|
||||
rtreeInit_fail:
|
||||
if( rc==SQLITE_OK ) rc = SQLITE_ERROR;
|
||||
assert( *ppVtab==0 );
|
||||
assert( pRtree->nBusy==1 );
|
||||
rtreeRelease(pRtree);
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -3751,7 +3939,7 @@ static u8 *rtreeCheckGetNode(RtreeCheck *pCheck, i64 iNode, int *pnNode){
|
||||
** two tables are:
|
||||
**
|
||||
** CREATE TABLE %_parent(nodeno INTEGER PRIMARY KEY, parentnode INTEGER)
|
||||
** CREATE TABLE %_rowid(rowid INTEGER PRIMARY KEY, nodeno INTEGER)
|
||||
** CREATE TABLE %_rowid(rowid INTEGER PRIMARY KEY, nodeno INTEGER, ...)
|
||||
**
|
||||
** In both cases, this function checks that there exists an entry with
|
||||
** IPK value iKey and the second column set to iVal.
|
||||
@@ -3766,8 +3954,8 @@ static void rtreeCheckMapping(
|
||||
int rc;
|
||||
sqlite3_stmt *pStmt;
|
||||
const char *azSql[2] = {
|
||||
"SELECT parentnode FROM %Q.'%q_parent' WHERE nodeno=?",
|
||||
"SELECT nodeno FROM %Q.'%q_rowid' WHERE rowid=?"
|
||||
"SELECT parentnode FROM %Q.'%q_parent' WHERE nodeno=?1",
|
||||
"SELECT nodeno FROM %Q.'%q_rowid' WHERE rowid=?1"
|
||||
};
|
||||
|
||||
assert( bLeaf==0 || bLeaf==1 );
|
||||
@@ -3951,6 +4139,7 @@ static int rtreeCheckTable(
|
||||
RtreeCheck check; /* Common context for various routines */
|
||||
sqlite3_stmt *pStmt = 0; /* Used to find column count of rtree table */
|
||||
int bEnd = 0; /* True if transaction should be closed */
|
||||
int nAux = 0; /* Number of extra columns. */
|
||||
|
||||
/* Initialize the context object */
|
||||
memset(&check, 0, sizeof(check));
|
||||
@@ -3966,11 +4155,21 @@ static int rtreeCheckTable(
|
||||
bEnd = 1;
|
||||
}
|
||||
|
||||
/* Find the number of auxiliary columns */
|
||||
if( check.rc==SQLITE_OK ){
|
||||
pStmt = rtreeCheckPrepare(&check, "SELECT * FROM %Q.'%q_rowid'", zDb, zTab);
|
||||
if( pStmt ){
|
||||
nAux = sqlite3_column_count(pStmt) - 2;
|
||||
sqlite3_finalize(pStmt);
|
||||
}
|
||||
check.rc = SQLITE_OK;
|
||||
}
|
||||
|
||||
/* Find number of dimensions in the rtree table. */
|
||||
pStmt = rtreeCheckPrepare(&check, "SELECT * FROM %Q.%Q", zDb, zTab);
|
||||
if( pStmt ){
|
||||
int rc;
|
||||
check.nDim = (sqlite3_column_count(pStmt) - 1) / 2;
|
||||
check.nDim = (sqlite3_column_count(pStmt) - 1 - nAux) / 2;
|
||||
if( check.nDim<1 ){
|
||||
rtreeCheckAppendMsg(&check, "Schema corrupt or not an rtree");
|
||||
}else if( SQLITE_ROW==sqlite3_step(pStmt) ){
|
||||
|
||||
+43
-5
@@ -476,11 +476,11 @@ foreach {tn sql_template testdata} {
|
||||
}
|
||||
|
||||
3 "UPDATE %CONF% t1 SET idx = 2 WHERE idx = 4" {
|
||||
ROLLBACK 1 1 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6}
|
||||
ABORT 1 1 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7}
|
||||
IGNORE 1 0 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7}
|
||||
FAIL 1 1 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7}
|
||||
REPLACE 1 0 {1 1 2 3 4 2 4 5 6 7 3 3 4 5 6}
|
||||
ROLLBACK 0 1 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6}
|
||||
ABORT 0 1 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7}
|
||||
IGNORE 0 0 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7}
|
||||
FAIL 0 1 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7}
|
||||
REPLACE 0 0 {1 1 2 3 4 2 4 5 6 7 3 3 4 5 6}
|
||||
}
|
||||
|
||||
3 "UPDATE %CONF% t1 SET idx = ((idx+1)%5)+1 WHERE idx > 2" {
|
||||
@@ -609,5 +609,43 @@ do_execsql_test 15.2 {
|
||||
COMMIT;
|
||||
}
|
||||
|
||||
# Test cases for the new auxiliary columns feature
|
||||
#
|
||||
do_catchsql_test 16.100 {
|
||||
CREATE VIRTUAL TABLE t16 USING rtree(id,x0,x1,y0,+aux1,x1);
|
||||
} {1 {Auxiliary rtree columns must be last}}
|
||||
do_test 16.110 {
|
||||
set sql {
|
||||
CREATE VIRTUAL TABLE t16 USING rtree(
|
||||
id, x00, x01, x10, x11, x20, x21, x30, x31, x40, x41
|
||||
}
|
||||
for {set i 12} {$i<=100} {incr i} {
|
||||
append sql ", +a$i"
|
||||
}
|
||||
append sql ");"
|
||||
execsql $sql
|
||||
} {}
|
||||
do_test 16.120 {
|
||||
set sql {
|
||||
CREATE VIRTUAL TABLE t16b USING rtree(
|
||||
id, x00, x01, x10, x11, x20, x21, x30, x31, x40, x41
|
||||
}
|
||||
for {set i 12} {$i<=101} {incr i} {
|
||||
append sql ", +a$i"
|
||||
}
|
||||
append sql ");"
|
||||
catchsql $sql
|
||||
} {1 {Too many columns for an rtree table}}
|
||||
|
||||
do_execsql_test 16.130 {
|
||||
DROP TABLE IF EXISTS rt1;
|
||||
CREATE VIRTUAL TABLE rt1 USING rtree(id, x1, x2, +aux);
|
||||
INSERT INTO rt1 VALUES(1, 1, 2, 'aux1');
|
||||
INSERT INTO rt1 VALUES(2, 2, 3, 'aux2');
|
||||
INSERT INTO rt1 VALUES(3, 3, 4, 'aux3');
|
||||
INSERT INTO rt1 VALUES(4, 4, 5, 'aux4');
|
||||
SELECT * FROM rt1 WHERE id IN (1, 2, 3, 4);
|
||||
} {1 1.0 2.0 aux1 2 2.0 3.0 aux2 3 3.0 4.0 aux3 4 4.0 5.0 aux4}
|
||||
|
||||
expand_all_sql db
|
||||
finish_test
|
||||
|
||||
@@ -81,7 +81,7 @@ do_faultsim_test rtree3-2 -faults oom* -prep {
|
||||
do_malloc_test rtree3-3.prep {
|
||||
faultsim_delete_and_reopen
|
||||
execsql {
|
||||
CREATE VIRTUAL TABLE rt USING rtree(ii, x1, x2, y1, y2);
|
||||
CREATE VIRTUAL TABLE rt USING rtree(ii, x1, x2, y1, y2, +a1, +a2);
|
||||
INSERT INTO rt VALUES(NULL, 3, 5, 7, 9);
|
||||
}
|
||||
faultsim_save_and_close
|
||||
|
||||
+36
-3
@@ -38,11 +38,20 @@ do_test rtree8-1.1.1 {
|
||||
} {}
|
||||
do_test rtree8-1.1.2 {
|
||||
set res [list]
|
||||
db eval { SELECT * FROM t1 } {
|
||||
lappend res $x1 $x2
|
||||
set rc [catch {
|
||||
db eval { SELECT * FROM t1 } {
|
||||
lappend res $x1 $x2
|
||||
if {$id==3} { db eval { DELETE FROM t1 WHERE id>3 } }
|
||||
}
|
||||
} msg];
|
||||
lappend rc $msg
|
||||
set rc
|
||||
} {1 {database table is locked}}
|
||||
do_test rtree8-1.1.2b {
|
||||
db eval { SELECT * FROM t1 ORDER BY +id } {
|
||||
if {$id==3} { db eval { DELETE FROM t1 WHERE id>3 } }
|
||||
}
|
||||
set res
|
||||
db eval {SELECT x1, x2 FROM t1}
|
||||
} {1 3 2 4 3 5}
|
||||
do_test rtree8-1.1.3 {
|
||||
execsql { SELECT * FROM t1 }
|
||||
@@ -170,5 +179,29 @@ do_test rtree8-5.4 {
|
||||
} {}
|
||||
do_rtree_integrity_test rtree8-5.5 t2
|
||||
|
||||
# 2018-05-24
|
||||
# The following script caused an assertion fault and/or segfault
|
||||
# prior to the fix that prevents simultaneous reads and writes on
|
||||
# the same rtree virtual table.
|
||||
#
|
||||
do_test rtree8-6.1 {
|
||||
db close
|
||||
sqlite3 db :memory:
|
||||
db eval {
|
||||
PRAGMA page_size=512;
|
||||
CREATE VIRTUAL TABLE t1 USING rtree(id,x1,x2,y1,y2);
|
||||
WITH RECURSIVE c(x) AS (VALUES(0) UNION ALL SELECT x+1 FROM c WHERE x<49)
|
||||
INSERT INTO t1 SELECT x, x, x+1, x, x+1 FROM c;
|
||||
}
|
||||
set rc [catch {
|
||||
db eval {SELECT id FROM t1} x {
|
||||
db eval {DELETE FROM t1 WHERE id=$x(id)}
|
||||
}
|
||||
} msg]
|
||||
lappend rc $msg
|
||||
} {1 {database table is locked}}
|
||||
|
||||
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -178,7 +178,7 @@ do_execsql_test 4.3 {
|
||||
reset_db
|
||||
do_execsql_test 5.1 {
|
||||
CREATE TABLE t1(x PRIMARY KEY, y);
|
||||
CREATE VIRTUAL TABLE rt USING rtree(id, x1, x2);
|
||||
CREATE VIRTUAL TABLE rt USING rtree(id, x1, x2, +d1);
|
||||
|
||||
INSERT INTO t1(x) VALUES(1);
|
||||
INSERT INTO t1(x) SELECT x+1 FROM t1; -- 2
|
||||
@@ -192,7 +192,7 @@ do_execsql_test 5.1 {
|
||||
INSERT INTO t1(x) SELECT x+256 FROM t1; -- 512
|
||||
INSERT INTO t1(x) SELECT x+512 FROM t1; --1024
|
||||
|
||||
INSERT INTO rt SELECT x, x, x+1 FROM t1 WHERE x<=5;
|
||||
INSERT INTO rt SELECT x, x, x+1, printf('x%04xy',x) FROM t1 WHERE x<=5;
|
||||
}
|
||||
do_rtree_integrity_test 5.1.1 rt
|
||||
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
# 2018-05-16
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
# This file contains tests for the r-tree module, specifically the
|
||||
# auxiliary column mechanism.
|
||||
|
||||
if {![info exists testdir]} {
|
||||
set testdir [file join [file dirname [info script]] .. .. test]
|
||||
}
|
||||
source [file join [file dirname [info script]] rtree_util.tcl]
|
||||
source $testdir/tester.tcl
|
||||
ifcapable !rtree { finish_test ; return }
|
||||
|
||||
do_execsql_test rtreeH-100 {
|
||||
CREATE VIRTUAL TABLE t1 USING rtree(id,x0,x1,y0,y1,+label,+other);
|
||||
INSERT INTO t1(x0,x1,y0,y1,label) VALUES
|
||||
(0,10,0,10,'lower-left corner'),
|
||||
(0,10,90,100,'upper-left corner'),
|
||||
(90,100,0,10,'lower-right corner'),
|
||||
(90,100,90,100,'upper-right corner'),
|
||||
(40,60,40,60,'center'),
|
||||
(0,5,0,100,'left edge'),
|
||||
(95,100,0,100,'right edge'),
|
||||
(0,100,0,5,'bottom edge'),
|
||||
(0,100,95,100,'top edge'),
|
||||
(0,100,0,100,'the whole thing'),
|
||||
(0,50,0,100,'left half'),
|
||||
(51,100,0,100,'right half'),
|
||||
(0,100,0,50,'bottom half'),
|
||||
(0,100,51,100,'top half');
|
||||
} {}
|
||||
do_execsql_test rtreeH-101 {
|
||||
SELECT * FROM t1_rowid ORDER BY rowid
|
||||
} {1 1 {lower-left corner} {} 2 1 {upper-left corner} {} 3 1 {lower-right corner} {} 4 1 {upper-right corner} {} 5 1 center {} 6 1 {left edge} {} 7 1 {right edge} {} 8 1 {bottom edge} {} 9 1 {top edge} {} 10 1 {the whole thing} {} 11 1 {left half} {} 12 1 {right half} {} 13 1 {bottom half} {} 14 1 {top half} {}}
|
||||
|
||||
do_execsql_test rtreeH-102 {
|
||||
SELECT * FROM t1 WHERE rowid=5;
|
||||
} {5 40.0 60.0 40.0 60.0 center {}}
|
||||
do_execsql_test rtreeH-103 {
|
||||
SELECT * FROM t1 WHERE label='center';
|
||||
} {5 40.0 60.0 40.0 60.0 center {}}
|
||||
|
||||
do_rtree_integrity_test rtreeH-110 t1
|
||||
|
||||
do_execsql_test rtreeH-120 {
|
||||
SELECT label FROM t1 WHERE x1<=50 ORDER BY id
|
||||
} {{lower-left corner} {upper-left corner} {left edge} {left half}}
|
||||
do_execsql_test rtreeH-121 {
|
||||
SELECT label FROM t1 WHERE x1<=50 AND label NOT LIKE '%corner%' ORDER BY id
|
||||
} {{left edge} {left half}}
|
||||
|
||||
do_execsql_test rtreeH-200 {
|
||||
WITH RECURSIVE
|
||||
c1(x) AS (VALUES(0) UNION ALL SELECT x+1 FROM c1 WHERE x<99),
|
||||
c2(y) AS (VALUES(0) UNION ALL SELECT y+1 FROM c2 WHERE y<99)
|
||||
INSERT INTO t1(id, x0,x1,y0,y1,label)
|
||||
SELECT 1000+x+y*100, x, x+1, y, y+1, printf('box-%d,%d',x,y) FROM c1, c2;
|
||||
} {}
|
||||
|
||||
do_execsql_test rtreeH-210 {
|
||||
SELECT label FROM t1 WHERE x0>=48 AND x1<=50 AND y0>=48 AND y1<=50
|
||||
ORDER BY id;
|
||||
} {box-48,48 box-49,48 box-48,49 box-49,49}
|
||||
|
||||
do_execsql_test rtreeH-300 {
|
||||
UPDATE t1 SET label='x'||label
|
||||
WHERE x0>=49 AND x1<=50 AND y0>=49 AND y1<=50;
|
||||
SELECT label FROM t1 WHERE x0>=48 AND x1<=50 AND y0>=48 AND y1<=50
|
||||
ORDER BY id;
|
||||
} {box-48,48 box-49,48 box-48,49 xbox-49,49}
|
||||
|
||||
|
||||
finish_test
|
||||
@@ -501,7 +501,8 @@ FUZZDATA = \
|
||||
$(TOP)/test/fuzzdata2.db \
|
||||
$(TOP)/test/fuzzdata3.db \
|
||||
$(TOP)/test/fuzzdata4.db \
|
||||
$(TOP)/test/fuzzdata5.db
|
||||
$(TOP)/test/fuzzdata5.db \
|
||||
$(TOP)/test/fuzzdata6.db
|
||||
|
||||
# Standard options to testfixture
|
||||
#
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
C Fix\sharmless\scompiler\swarnings\sin\sthe\scell-overwrite\slogic.
|
||||
D 2018-05-07T11:29:59.385
|
||||
C This\sis\san\suntested\sproof-of-concept\sfor\senhancements\sto\sRTree\sthat\sattempt\nto\suse\ssqlite3_value_nochange()\sto\sreduce\sthe\samount\sof\swork\sassociated\swith\nUPDATE\soperations\sin\scases\swhere\seither\sthe\scoordinates\sor\sthe\sauxiliary\sdata\nis\sunchanged.
|
||||
D 2018-05-25T14:39:24.456
|
||||
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
|
||||
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
|
||||
F Makefile.in 5ce9343cba9c189046f1afe6d2bcc1f68079439febc05267b98aec6ecc752439
|
||||
F Makefile.in bfc40f350586923e0419d2ea4b559c37ec10ee4b6e210e08c14401f8e340f0da
|
||||
F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434
|
||||
F Makefile.msc 59179295f6a9b433e3f59a6dc2fcf6db6fcac35d92015294beb5d27f2924ebb9
|
||||
F Makefile.msc 681fb88cccf1fd58c0b9648f6a09b75332206ef72ca76012ad11699c320cec5f
|
||||
F README.md 7764d56778d567913ef11c82da9ab94aefa0826f7c243351e4e2d7adaef6f373
|
||||
F VERSION b7c9d1d11cb70ef8e90cfcf3c944aa58a9f801cc2ad487eebb0a110c16dfc2df
|
||||
F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50
|
||||
@@ -204,7 +204,7 @@ F ext/fts5/test/fts5synonym2.test b54cce5c34ec08ed616f646635538ae82e34a0e28f947e
|
||||
F ext/fts5/test/fts5tok1.test ce6551e41ff56f30b69963577324624733bed0d1753589f06120d664d9cd45c9
|
||||
F ext/fts5/test/fts5tok2.test dcacb32d4a2a3f0dd3215d4a3987f78ae4be21a2
|
||||
F ext/fts5/test/fts5tokenizer.test 6aeb5e8061ffc0ff9a5299f27beaee3b2b4b8b336d4f107262bca338bea8f8e9
|
||||
F ext/fts5/test/fts5unicode.test 1e5570df758f7e0b27ff0e087ee96f559d5cc9ade80afa9421537a8a20a7cfbf
|
||||
F ext/fts5/test/fts5unicode.test 17056f4efe6b0a5d4f41fdf7a7dc9af2873004562eaa899d40633b93dc95f5a9
|
||||
F ext/fts5/test/fts5unicode2.test 9b3df486de05fb4bde4aa7ee8de2e6dae1df6eb90e3f2e242c9383b95d314e3e
|
||||
F ext/fts5/test/fts5unicode3.test c3caecbe8264629ffe653b43ca5790b9793eba4422f92203e5247558e5a534e7
|
||||
F ext/fts5/test/fts5unindexed.test 9021af86a0fb9fc616f7a69a996db0116e7936d0db63892db6bafabbec21af4d
|
||||
@@ -280,7 +280,7 @@ F ext/misc/eval.c 6ea9b22a5fa0dd973b67ca4e53555be177bc0b7b263aadf1024429457c82c0
|
||||
F ext/misc/fileio.c 48c7751c78fc4cdd29d8c862fd2f3f98bbfefa2a3cf1ca1496df4bf02eb8cded
|
||||
F ext/misc/fuzzer.c 7c64b8197bb77b7d64eff7cac7848870235d4c25
|
||||
F ext/misc/ieee754.c f190d0cc5182529acb15babd177781be1ac1718c
|
||||
F ext/misc/json1.c dbe086615b9546c156bf32b9378fc09383b58bd17513b866cfd24c1e15281984
|
||||
F ext/misc/json1.c 02f5f0d9c131df79f4ee168d2b426f0f0d273b7771fc0bb5293c4e7692d9a2ee
|
||||
F ext/misc/memvfs.c ab36f49e02ebcdf85a1e08dc4d8599ea8f343e073ac9e0bca18a98b7e1ec9567
|
||||
F ext/misc/mmapwarm.c 70b618f2d0bde43fae288ad0b7498a629f2b6f61b50a27e06fae3cd23c83af29
|
||||
F ext/misc/nextchar.c 35c8b8baacb96d92abbb34a83a997b797075b342
|
||||
@@ -355,24 +355,25 @@ F ext/repair/test/checkfreelist01.test 3e8aa6aeb4007680c94a8d07b41c339aa635cc782
|
||||
F ext/repair/test/checkindex01.test 6945d0ffc0c1dc993b2ce88036b26e0f5d6fcc65da70fc9df27c2647bb358b0f
|
||||
F ext/repair/test/test.tcl 686d76d888dffd021f64260abf29a55c57b2cedfa7fc69150b42b1d6119aac3c
|
||||
F ext/rtree/README 6315c0d73ebf0ec40dedb5aa0e942bc8b54e3761
|
||||
F ext/rtree/rtree.c bc61010e978b5b8ae6dbb90274a2fbb5db5ff5e2880b5c6e8abd48eea77264db
|
||||
F ext/rtree/rtree.c 1a11f26cbcccc31391fe45e10804b07e64dabc4fb4803fb3d075bfe13360d8b8
|
||||
F ext/rtree/rtree.h 4a690463901cb5e6127cf05eb8e642f127012fd5003830dbc974eca5802d9412
|
||||
F ext/rtree/rtree1.test 47e2095bebea6813754fd7afa6a20e2b7b4ebcd5cb7dbcb6932b6c9f86bbf972
|
||||
F ext/rtree/rtree1.test 309afc04d4287542b2cd74f933296832cc681c7b014d9405cb329b62053a5349
|
||||
F ext/rtree/rtree2.test 5f25b01acd03470067a2d52783b2eb0a50bf836803d4342d20ca39e541220fe2
|
||||
F ext/rtree/rtree3.test 2cafe8265d1ff28f206fce88d114f208349df482
|
||||
F ext/rtree/rtree3.test 4ee5d7df86040efe3d8d84f141f2962a7745452200a7cba1db06f86d97050499
|
||||
F ext/rtree/rtree4.test 304de65d484540111b896827e4261815e5dca4ce28eeecd58be648cd73452c4b
|
||||
F ext/rtree/rtree5.test 49c9041d713d54560b315c2c7ef7207ee287eba1b20f8266968a06f2e55d3142
|
||||
F ext/rtree/rtree6.test 593e0d36510d5ac1d1fb39b018274ff17604fe8fdca8cf1f8e16559cea1477f4
|
||||
F ext/rtree/rtree7.test c8fb2e555b128dd0f0bdb520c61380014f497f8a23c40f2e820acc9f9e4fdce5
|
||||
F ext/rtree/rtree8.test 649f5a37ec656028a4a32674b9b1183104285a7625a09d2a8f52a1cef72c93f2
|
||||
F ext/rtree/rtree8.test 924926d7c64ac59fcca0809de472d9dd73c612f54daae1cf992bdd7dac90305b
|
||||
F ext/rtree/rtree9.test c646f12c8c1c68ef015c6c043d86a0c42488e2e68ed1bb1b0771a7ca246cbabf
|
||||
F ext/rtree/rtreeA.test 20623ca337ca3bd7e008cc9fb49e44dbe97f1a80b238e10a12bb4afcd0da3776
|
||||
F ext/rtree/rtreeB.test 4cec297f8e5c588654bbf3c6ed0903f10612be8a2878055dd25faf8c71758bc9
|
||||
F ext/rtree/rtreeC.test 55e40c4bd9735d9944280f0e664f39374e71bcd9cd3fe4e82786d20b48017fb5
|
||||
F ext/rtree/rtreeC.test 128928549d22b65c381ab1366760d08703cd75e34f6a7a506ece38f9330b7282
|
||||
F ext/rtree/rtreeD.test fe46aa7f012e137bd58294409b16c0d43976c3bb92c8f710481e577c4a1100dc
|
||||
F ext/rtree/rtreeE.test e65d3fc625da1800b412fc8785817327d43ccfec5f5973912d8c9e471928caa9
|
||||
F ext/rtree/rtreeF.test 81ffa7ef51c4e4618d497a57328c265bf576990c7070633b623b23cd450ed331
|
||||
F ext/rtree/rtreeG.test 1b9ca6e3effb48f4161edaa463ddeaa8fca4b2526d084f9cbf5dbe4e0184939c
|
||||
F ext/rtree/rtreeH.test aa08cc4fa8005b4c67446c7110205055b4d6da90e760e6f44b82dfa4cdf8d87a
|
||||
F ext/rtree/rtree_perf.tcl 6c18c1f23cd48e0f948930c98dfdd37dfccb5195
|
||||
F ext/rtree/rtree_util.tcl db734b4c5e75fed6acc56d9701f2235345acfdec750b5fc7b587936f5f6bceed
|
||||
F ext/rtree/rtreecheck.test 4d29103d1e16fcbf90135d1c637b833688492b063b2971dfb5dc6ba76555cfee
|
||||
@@ -415,7 +416,7 @@ F ext/userauth/userauth.c 3410be31283abba70255d71fd24734e017a4497f
|
||||
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
|
||||
F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
|
||||
F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60
|
||||
F main.mk 068618fe288bf9d93091b808be73e26a93fb2db8435328cc5760f9dd35ba168b
|
||||
F main.mk 3f50dfe5cb4257c1aca96e417636ed51bc2561e71d31a21e9ccdf66feb912f43
|
||||
F mkso.sh fd21c06b063bb16a5d25deea1752c2da6ac3ed83
|
||||
F mptest/config01.test 3c6adcbc50b991866855f1977ff172eb6d901271
|
||||
F mptest/config02.test 4415dfe36c48785f751e16e32c20b077c28ae504
|
||||
@@ -434,29 +435,29 @@ F src/auth.c 6277d63837357549fe14e723490d6dc1a38768d71c795c5eb5c0f8a99f918f73
|
||||
F src/backup.c faf17e60b43233c214aae6a8179d24503a61e83b
|
||||
F src/bitvec.c 17ea48eff8ba979f1f5b04cc484c7bb2be632f33
|
||||
F src/btmutex.c 8acc2f464ee76324bf13310df5692a262b801808984c1b79defb2503bbafadb6
|
||||
F src/btree.c b4ca692c0c557bba653412a0c3347f0e5c140e759f0d8349383b7de7281ad99e
|
||||
F src/btree.h 0866c0a08255142ea0e754aabd211c843cab32045c978a592a43152405ed0c84
|
||||
F src/btree.c 8270813c8f0ca91b2802e88ded3755d04ee962a923d431c13bcb6cf3e0c18f63
|
||||
F src/btree.h 448f15b98ea85dcf7e4eb76f731cadb89636c676ad25dfaac6de77cd66556598
|
||||
F src/btreeInt.h 620ab4c7235f43572cf3ac2ac8723cbdf68073be4d29da24897c7b77dda5fd96
|
||||
F src/build.c 0c2be5839f22aa2938f217c6c6c2120d9fc96872a546a37541a8271541cb355e
|
||||
F src/build.c 50ff3e0fa07646b4d797aae0f773efcdb7602f6a5e2f5da27856503f35200889
|
||||
F src/callback.c fe677cb5f5abb02f7a772a62a98c2f516426081df68856e8f2d5f950929b966a
|
||||
F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e
|
||||
F src/ctime.c 849d4cebe008cfc6e4799b034a172b4eaf8856b100739632a852732ba66eee48
|
||||
F src/date.c ebe1dc7c8a347117bb02570f1a931c62dd78f4a2b1b516f4837d45b7d6426957
|
||||
F src/dbpage.c 8db4c97f630e7d83f884ea75caf1ffd0988c160e9d530194d93721c80821e0f6
|
||||
F src/dbstat.c edabb82611143727511a45ca0859b8cd037851ebe756ae3db289859dd18b6f91
|
||||
F src/delete.c b0f90749e22d5e41a12dbf940f4811138cf97da54b46b737089b93eb64a2896f
|
||||
F src/delete.c 4c8c7604277a2041647f96b78f4b9a47858e9217e4fb333d35e7b5ab32c5b57f
|
||||
F src/expr.c af4a81a385277510bfc56df87c25d76fc365f98c33bc8797c4a8d84b88e31013
|
||||
F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007
|
||||
F src/fkey.c d617daf66b5515e2b42c1405b2b4984c30ca50fb705ab164271a9bf66c69e331
|
||||
F src/func.c 94f42cba2cc1c34aeaa441022ba0170ec3fec4bba54db4e0ded085c6dc0fdc51
|
||||
F src/func.c e2e3c02621a528a472933fd4733a5da635676f1461be73293f6e9f62f18d4eaa
|
||||
F src/global.c 9bf034fd560bdd514715170ed8460bb7f823cec113f0569ef3f18a20c7ccd128
|
||||
F src/hash.c a12580e143f10301ed5166ea4964ae2853d3905a511d4e0c44497245c7ce1f7a
|
||||
F src/hash.h ab34c5c54a9e9de2e790b24349ba5aab3dbb4fd4
|
||||
F src/hwtime.h 747c1bbe9df21a92e9c50f3bbec1de841dc5e5da
|
||||
F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71
|
||||
F src/insert.c 33a2c72b6182e8ddf697d604cc087c77ff5fc512a32b8b624641d41b390e249e
|
||||
F src/insert.c 25f2e3cb93821944dec28921c4cfb7729b3ac6e75d860fd7cd934265404a35b0
|
||||
F src/legacy.c 134ab3e3fae00a0f67a5187981d6935b24b337bcf0f4b3e5c9fa5763da95bf4e
|
||||
F src/loadext.c f6e4e416a736369f9e80eba609f0acda97148a8b0453784d670c78d3eed2f302
|
||||
F src/loadext.c 6aae5739198d96c51ae6eb97c4a5b1744c22ed7a5a565a5399a717780d48a36b
|
||||
F src/main.c b56b2d62d5d11e3f5100b25fca34c13c62a0fe73941f6873454a7fa8a454170d
|
||||
F src/malloc.c 07295435093ce354c6d9063ac05a2eeae28bd251d2e63c48b3d67c12c76f7e18
|
||||
F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645
|
||||
@@ -467,7 +468,7 @@ F src/mem5.c 9bf955937b07f8c32541c8a9991f33ce3173d944
|
||||
F src/memdb.c e94c478a757c4307fd170fe0a7650ef4cf722c59e5a95a8a7896ffedc1679139
|
||||
F src/memjournal.c 6f3d36a0a8f72f48f6c3c722f04301ac64f2515435fa42924293e46fc7994661
|
||||
F src/msvc.h 4942752b6a253116baaa8de75256c51a459a5e81
|
||||
F src/mutex.c b021263554c8a3995e9d53193b8194b96d1ed28e06c3b532dd7f7d29cf0c7d53
|
||||
F src/mutex.c bae36f8af32c22ad80bbf0ccebec63c252b6a2b86e4d3e42672ff287ebf4a604
|
||||
F src/mutex.h 779d588e3b7756ec3ecf7d78cde1d84aba414f85
|
||||
F src/mutex_noop.c 9d4309c075ba9cc7249e19412d3d62f7f94839c4
|
||||
F src/mutex_unix.c aaf9ebc3f89df28483c52208497a99a02cc3650011422fc9d4c57e4392f7fe58
|
||||
@@ -486,19 +487,19 @@ F src/parse.y 07784439d25f0bc64a656eece4caecc549b147d213f513cdbeb8430345ec2911
|
||||
F src/pcache.c 135ef0bc6fb2e3b7178d49ab5c9176254c8a691832c1bceb1156b2fbdd0869bd
|
||||
F src/pcache.h 072f94d29281cffd99e46c1539849f248c4b56ae7684c1f36626797fee375170
|
||||
F src/pcache1.c 716975564c15eb6679e97f734cec1bfd6c16ac3d4010f05f1f8e509fc7d19880
|
||||
F src/pragma.c bea56df3ae0637768c0da4fbbb8f2492f780980d95000034a105ff291bf7ca69
|
||||
F src/pragma.c c0d13c0e82a9197aef5533d63300c5b0c8a216ae1fd14ada64e1f12f398d7e82
|
||||
F src/pragma.h bb83728944b42f6d409c77f5838a8edbdb0fe83046c5496ffc9602b40340a324
|
||||
F src/prepare.c 95a9dba7a5d032039a77775188cb3b6fb17f2fa1a0b7cd915b30b4b823383ffa
|
||||
F src/printf.c d3b7844ddeb11fbbdd38dd84d09c9c1ac171d21fb038473c3aa97981201cc660
|
||||
F src/printf.c 1d1b4a568a58d0f32a5ff26c6b98db8a6e1883467f343a6406263cacd2e60c21
|
||||
F src/random.c 80f5d666f23feb3e6665a6ce04c7197212a88384
|
||||
F src/resolve.c 6415381a0e9d22c0e7cba33ca4a53f81474190862f5d4838190f5eb5b0b47bc9
|
||||
F src/rowset.c 7b7e7e479212e65b723bf40128c7b36dc5afdfac
|
||||
F src/select.c bf9185f40cbb98a1342c0149f66dff3fbadf262081305519927523b4ec7b876e
|
||||
F src/shell.c.in 29309f2ab656c8817fbc3b7910b9af8464557b91cba75277a03669399c8e2730
|
||||
F src/sqlite.h.in 469aed42e75193b7f5d88d812befa961c04746869475f96fcf434c9f67079f66
|
||||
F src/select.c a35d462ee7a3c0856ad7a9d9c8921fbf3d91d911a8f39ad9d61302eb43b24a71
|
||||
F src/shell.c.in 51c100206f4b7f86cd9affd80b764825e0edc36ca0190c442e4ca7994611bfe2
|
||||
F src/sqlite.h.in e379906b85cc3539c6a37ab972f2c28711b28854a457f54e137d20f75db5db7c
|
||||
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
|
||||
F src/sqlite3ext.h 83a3c4ce93d650bedfd1aa558cb85a516bd6d094445ee989740827d0d944368d
|
||||
F src/sqliteInt.h 4b98a37f27033ca887f2027f9a60636625fb2a4a9ef1b96c17f0db8fd951891f
|
||||
F src/sqlite3ext.h 9887b27e69c01e79c2cbe74ef73bf01af5b5703d6a7f0a4371e386d7249cb1c7
|
||||
F src/sqliteInt.h 5abdade4744cf3bd567afb65bb144bb3c61f6132f86813b995a5ca79c7b584e8
|
||||
F src/sqliteLimit.h 1513bfb7b20378aa0041e7022d04acb73525de35b80b252f1b83fedb4de6a76b
|
||||
F src/status.c 46e7aec11f79dad50965a5ca5fa9de009f7d6bde08be2156f1538a0a296d4d0e
|
||||
F src/table.c b46ad567748f24a326d9de40e5b9659f96ffff34
|
||||
@@ -530,7 +531,7 @@ F src/test_intarray.c 988fc61cb0ff539f4172c0d95f15287c92516f64
|
||||
F src/test_intarray.h f3b7672f5d1056eac563c0d6ea8480a660b1475c
|
||||
F src/test_journal.c 619f2aa10e0d7a5f87c0f06825bc61dfce1c6b9c7f3ad990fb13de6c3b8874a3
|
||||
F src/test_loadext.c 337056bae59f80b9eb00ba82088b39d0f4fe6dfd
|
||||
F src/test_malloc.c 5201422e2403e66a7a9c2b7d8df806acd8d2a0429822adb7e932f324e7b5b3c6
|
||||
F src/test_malloc.c dec0aa821b230773aeb3dd11d652c1193f7cedb18a20b25659bc672288115242
|
||||
F src/test_md5.c 7268e1e8c399d4a5e181b64ac20e1e6f3bc4dd9fc87abac02db145a3d951fa8c
|
||||
F src/test_multiplex.c e054459f7633f3ff8ce1245da724f9a8be189e4e
|
||||
F src/test_multiplex.h 5436d03f2d0501d04f3ed50a75819e190495b635
|
||||
@@ -556,30 +557,30 @@ F src/test_windirent.h 90dfbe95442c9762357fe128dc7ae3dc199d006de93eb33ba3972e0a9
|
||||
F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
|
||||
F src/threads.c 4ae07fa022a3dc7c5beb373cf744a85d3c5c6c3c
|
||||
F src/tokenize.c bbde32eac9eb1280f5292bcdfef66f5a57e43176cbf9347e0efab9f75e133f97
|
||||
F src/treeview.c 06dd506b1dcef01c5882a4189bc79afe2660dd992702f81cd7d003425a99cfbc
|
||||
F src/treeview.c 2c5c4bc0a443401db5fd621542150452ddf5055d38edd4eef868bc2b6bfb0260
|
||||
F src/trigger.c 4ace6d1d5ba9a89822deb287317f33c810440526eafe185c2d8a48c31df1e995
|
||||
F src/update.c 5be2f501ddc704fc04183bdb28b25eab930bb8553d973429a089ec94fa85cf2b
|
||||
F src/update.c 46dc24c6158446aaab45caee09b6d99327cb479268b83ffeb5b701823da3b67b
|
||||
F src/upsert.c ae4a4823b45c4daf87e8aea8c0f582a8844763271f5ed54ee5956c4c612734f4
|
||||
F src/utf.c 810fbfebe12359f10bc2a011520a6e10879ab2a163bcb26c74768eab82ea62a5
|
||||
F src/util.c d9eb0a6c4aae1b00a7369eadd7ca0bbe946cb4c953b6751aa20d357c2f482157
|
||||
F src/vacuum.c 37730af7540033135909ecaee3667dddec043293428d8718546d0d64ba4a5025
|
||||
F src/vdbe.c 066a4e1de2ed83e253adfd2e97a684cf562eaa41d31ee7f3d3e4c8aea4485a55
|
||||
F src/vdbe.c 565f7ccc4153627ef316bcfc2da86cb0766fac4b7dcd07c45a175c347d480f0a
|
||||
F src/vdbe.h d970d9738efdd09cb2df73e3a40856e7df13e88a3486789c49fcdd322c9eb8a2
|
||||
F src/vdbeInt.h 95f7adfdc5c8f1353321f55a6c5ec00a90877e3b85af5159e393afb41ff54110
|
||||
F src/vdbeapi.c 29d2baf9c1233131ec467d7bed1b7c8a03c27579048d768c4b04acf427838858
|
||||
F src/vdbeaux.c 58129ae46be079613df5c2c3714d80a78605415bfa10c9528634c7c2d2147727
|
||||
F src/vdbeaux.c 3b0650dbebebb196010d8af830551e61ea7dcc0e414a2b747f897305b0bd0b8f
|
||||
F src/vdbeblob.c f5c70f973ea3a9e915d1693278a5f890dc78594300cf4d54e64f2b0917c94191
|
||||
F src/vdbemem.c 0cbe9b9560e42b72983cf9e1bceba48f297e51142bfb6b57f3747cf60106b92d
|
||||
F src/vdbesort.c 731a09e5cb9e96b70c394c1b7cf3860fbe84acca7682e178615eb941a3a0ef2f
|
||||
F src/vdbetrace.c 48e11ebe040c6b41d146abed2602e3d00d621d7ebe4eb29b0a0f1617fd3c2f6c
|
||||
F src/vdbetrace.c 79d6dbbc479267b255a7de8080eee6e729928a0ef93ed9b0bfa5618875b48392
|
||||
F src/vtab.c 0e4885495172e1bdf54b12cce23b395ac74ef5729031f15e1bc1e3e6b360ed1a
|
||||
F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9
|
||||
F src/wal.c aa9cffc7a2bad6b826a86c8562dd4978398720ed41cb8ee7aa9d054eb8b456a0
|
||||
F src/wal.h 8de5d2d3de0956d6f6cb48c83a4012d5f227b8fe940f3a349a4b7e85ebcb492a
|
||||
F src/walker.c da987a20d40145c0a03c07d8fefcb2ed363becc7680d0500d9c79915591f5b1f
|
||||
F src/where.c aa94ef44ef36763817ca952e68c03db7eee5e2d4f1f0c3a98823a58c074157bd
|
||||
F src/whereInt.h 2610cb87dd95509995b63decc674c60f2757697a206cfe0c085ee53d9c43cfff
|
||||
F src/wherecode.c 300f945eb4552fb82ccf237b34dccb22f47522882faa9adcc04388e6e3f1d4b0
|
||||
F src/where.c 60ec752fcbe9f9e0271ac60548d159a540a1ee47a4f9fedc85e88a3d0e392dd1
|
||||
F src/whereInt.h cbae2bcd37cfebdb7812a8b188cdb19634ced2b9346470d1c270556b0c33ea53
|
||||
F src/wherecode.c 728c7f70731430ccdac807a79969873e1af6968bf1c4745dff3f9dd35f636cc8
|
||||
F src/whereexpr.c e90b2e76dcabc81edff56633bf281bc01d93b71e0c81482dc06925ce39f5844a
|
||||
F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
|
||||
F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd
|
||||
@@ -625,7 +626,7 @@ F test/auth.test 3d6cd8f3978ba55b1202574e6ecd79c6e00914ca44b9bfd6c1fe6fb873fcac8
|
||||
F test/auth2.test 9eb7fce9f34bf1f50d3f366fb3e606be5a2000a1
|
||||
F test/auth3.test db21405b95257c24d29273b6b31d0efc59e1d337e3d5804ba2d1fd4897b1ae49
|
||||
F test/autoanalyze1.test b9cc3f32a990fa56669b668d237c6d53e983554ae80c0604992e18869a0b2dec
|
||||
F test/autoinc.test 83aad64411583aac9ff0b629159ab4662029ab4e3f47090fce4efd132b304484
|
||||
F test/autoinc.test c6df81f1de9a5499f912c58e5389d0bcb8d42459df832b65aee24338ad422a42
|
||||
F test/autoindex1.test a09958fa756129af10b6582bcbf3cbdf11e305e027b393f393caef801159dee0
|
||||
F test/autoindex2.test 12ef578928102baaa0dc23ad397601a2f4ecb0df
|
||||
F test/autoindex3.test 2dd997d6590438b53e4f715f9278aa91c9299cf3f81246a0915269c35beb790e
|
||||
@@ -940,12 +941,13 @@ F test/fuzz2.test 76dc35b32b6d6f965259508508abce75a6c4d7e1
|
||||
F test/fuzz3.test 9c813e6613b837cb7a277b0383cd66bfa07042b4cf0317157c35852f30043c31
|
||||
F test/fuzz_common.tcl a87dfbb88c2a6b08a38e9a070dabd129e617b45b
|
||||
F test/fuzz_malloc.test 5b257a7652d8ee90b22e9cf80d9dbea31a4f3e6fed1d33de57b24b1bdb211d79
|
||||
F test/fuzzcheck.c 5eb86c6ac96833ee622f45bf47e8045999c1b4b10d05e4eb809894a4b39f2f84
|
||||
F test/fuzzcheck.c 3885207dc217c4dcdb2de4a3cb169a263afeef51ab9bd0ba8567289f0a19a470
|
||||
F test/fuzzdata1.db 7ee3227bad0e7ccdeb08a9e6822916777073c664
|
||||
F test/fuzzdata2.db f03a420d3b822cc82e4f894ca957618fbe9c4973
|
||||
F test/fuzzdata3.db c6586d3e3cef0fbc18108f9bb649aa77bfc38aba
|
||||
F test/fuzzdata4.db 1882f0055fb63214d8407ddc7aca9b0b1c59af21
|
||||
F test/fuzzdata5.db 9f0cdcc5c6e83b90cf9ae343bd07f684d2da2de7
|
||||
F test/fuzzdata5.db 117d821cde02e30a687f6361a34b98e6e0b05062df523cfee163c58564403b68
|
||||
F test/fuzzdata6.db 92a80e4afc172c24f662a10a612d188fb272de4a9bd19e017927c95f737de6d7
|
||||
F test/fuzzer1.test 3d4c4b7e547aba5e5511a2991e3e3d07166cfbb8
|
||||
F test/fuzzer2.test a85ef814ce071293bce1ad8dffa217cbbaad4c14
|
||||
F test/fuzzerfault.test 8792cd77fd5bce765b05d0c8e01b9edcf8af8536
|
||||
@@ -1020,7 +1022,7 @@ F test/journal3.test c9c29883f5bf535ae82ae21c472df6263806a22e467b6db7cd0d6d54530
|
||||
F test/jrnlmode.test a6693f2bed4541a21e703aaa37bb3e10de154130645952933b82b2dec0a8b539
|
||||
F test/jrnlmode2.test 8759a1d4657c064637f8b079592651530db738419e1d649c6df7048cd724363d
|
||||
F test/jrnlmode3.test 556b447a05be0e0963f4311e95ab1632b11c9eaa
|
||||
F test/json101.test 24e97954e3bd6404f3715888c7f8f835e36e19c7ae6513b5d9ab2d381498962d
|
||||
F test/json101.test b40a9f5395d8e669b0bc3eb550ad2ae9e5ada01fbce23c446c2a30a305a6d575
|
||||
F test/json102.test eeb54efa221e50b74a2d6fb9259963b48d7414dca3ce2fdfdeed45cb28487bc1
|
||||
F test/json103.test c5f6b85e69de05f6b3195f9f9d5ce9cd179099a0
|
||||
F test/json104.test 877d5845f6303899b7889ea5dd1bea99076e3100574d5c536082245c5805dcaa
|
||||
@@ -1118,7 +1120,7 @@ F test/openv2.test 0d3040974bf402e19b7df4b783e447289d7ab394
|
||||
F test/optfuzz-db01.c a0c256905c8ac79f9a5de2f374a3d9f757bef0dca2a238dc7c10cc8a38031834
|
||||
F test/optfuzz-db01.txt 21f6bdeadc701cf11528276e2a55c70bfcb846ba42df327f979bd9e7b6ce7041
|
||||
F test/optfuzz.c 50e330304eb1992e15ddd11f3daaad9bcc0d9aaad09cb2bcc77f9515df2e88b1
|
||||
F test/orderby1.test bb8535f52c42e91cfa110622ccff70597e531198bb4b033185ad84c11522ab1b
|
||||
F test/orderby1.test e4501f54721f804ca56922e253403ac6775f88e9f07569994ce99212b3ca5b10
|
||||
F test/orderby2.test bc11009f7cd99d96b1b11e57b199b00633eb5b04
|
||||
F test/orderby3.test 8619d06a3debdcd80a27c0fdea5c40b468854b99
|
||||
F test/orderby4.test 4d39bfbaaa3ae64d026ca2ff166353d2edca4ba4
|
||||
@@ -1238,7 +1240,7 @@ F test/sharedA.test 49d87ec54ab640fbbc3786ee3c01de94aaa482a3a9f834ad3fe92770eb69
|
||||
F test/sharedB.test 16cc7178e20965d75278f410943109b77b2e645e
|
||||
F test/shared_err.test 32634e404a3317eeb94abc7a099c556a346fdb8fb3858dbe222a4cbb8926a939
|
||||
F test/sharedlock.test 5ede3c37439067c43b0198f580fd374ebf15d304
|
||||
F test/shell1.test e2f7d375ae80eb16590af23d6c40cd0140b6d1f92642c1b71eb94630a144ee08
|
||||
F test/shell1.test 50154b0c4779df435b9e60ca60104b05f1cc217eab1aa383131359329e73d939
|
||||
F test/shell2.test e242a9912f44f4c23c3d1d802a83e934e84c853b
|
||||
F test/shell3.test ac8c2b744014c3e9a0e26bfd829ab65f00923dc1a91ffd044863e9423cc91494
|
||||
F test/shell4.test 89ad573879a745974ff2df20ff97c5d6ffffbd5d
|
||||
@@ -1276,7 +1278,7 @@ F test/speed3.test 694affeb9100526007436334cf7d08f3d74b85ef
|
||||
F test/speed4.test abc0ad3399dcf9703abed2fff8705e4f8e416715
|
||||
F test/speed4p.explain 6b5f104ebeb34a038b2f714150f51d01143e59aa
|
||||
F test/speed4p.test 377a0c48e5a92e0b11c1c5ebb1bc9d83a7312c922bc0cb05970ef5d6a96d1f0c
|
||||
F test/speedtest1.c 20cc4028b0e88392b5a635c2ea5d5e777d569bf7258aead37f8be7a886c38344
|
||||
F test/speedtest1.c cc7e6b4a7c9f3e3d1a497ae3f75236a832a2ce0f6a9b017f95d996c821605bfb
|
||||
F test/spellfix.test 951a6405d49d1a23d6b78027d3877b4a33eeb8221dcab5704b499755bb4f552e
|
||||
F test/spellfix2.test dfc8f519a3fc204cb2dfa8b4f29821ae90f6f8c3
|
||||
F test/spellfix3.test 0f9efaaa502a0e0a09848028518a6fb096c8ad33
|
||||
@@ -1604,7 +1606,7 @@ F test/whereJ.test 88287550f6ee604422403b053455b1ad894eeaa5c35d348532dfa1439286c
|
||||
F test/whereK.test f8e3cf26a8513ecc7f514f54df9f0572c046c42b
|
||||
F test/wherefault.test 1374c3aa198388925246475f84ad4cd5f9528864
|
||||
F test/wherelfault.test 9012e4ef5259058b771606616bd007af5d154e64cc25fa9fd4170f6411db44e3
|
||||
F test/wherelimit.test 1dee70c9cc147330156d75e23de88f771e624998b03ae316cb64e1d249f129d8
|
||||
F test/wherelimit.test 592081800806d297dd7449b1030c863d2883d6d42901837ccd2e5a9bd962edb0
|
||||
F test/wherelimit2.test be78ba3aa1831c6358fd7d5b9809bfd520f0c2a7d63a295e8f182e140ff137c3
|
||||
F test/wild001.test bca33f499866f04c24510d74baf1e578d4e44b1c
|
||||
F test/win32heap.test 10fd891266bd00af68671e702317726375e5407561d859be1aa04696f2aeee74
|
||||
@@ -1685,7 +1687,7 @@ F tool/showstat4.c 0682ebea7abf4d3657f53c4a243f2e7eab48eab344ed36a94bb75dcd19a5c
|
||||
F tool/showwal.c ad9d768f96ca6199ad3a8c9562d679680bd032dd01204ea3e5ea6fb931d81847
|
||||
F tool/soak1.tcl 8d407956e1a45b485a8e072470a3e629a27037fe
|
||||
F tool/spaceanal.tcl 4bfd19aad7eb3ce0372ef0255f58035e0bba4ff5e9acfd763a10c6fb365c8dec
|
||||
F tool/speed-check.sh 4ff9b095cf1a7643f0264e7fb7d23f0b12b7cce587a9de315877c378e90eeaf4
|
||||
F tool/speed-check.sh 27c7fe178d5b2f7c90a04a127907acda0bfe637fa85b13c43e03e5ed39b008b6
|
||||
F tool/speedtest.tcl 06c76698485ccf597b9e7dbb1ac70706eb873355
|
||||
F tool/speedtest16.c ecb6542862151c3e6509bbc00509b234562ae81e
|
||||
F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
|
||||
@@ -1727,7 +1729,10 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
|
||||
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
|
||||
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
|
||||
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
|
||||
P 9650f71b82ca1b3759e776bbd8a2e8ca61c51f80038e37c9c9a675a8c13b0b06
|
||||
R 0f12e4904e55641cfcfe9534f6ff08d6
|
||||
P b816023ce07d01024d5769e16db924374a49bf909edd12dc1344a0a1ef693db5
|
||||
R d13a5739b07f5c3bc2f350a7fd438bb4
|
||||
T *branch * rtree-update-optimization
|
||||
T *sym-rtree-update-optimization *
|
||||
T -sym-trunk *
|
||||
U drh
|
||||
Z eb245c54fb26ccb37327155d2c7d476e
|
||||
Z 339f042021a37adf237173fa2c6b25c7
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
3e11dc3183bc3e8ec49af244a8e8b3e07d12f7a2e59028b2bf64ce0ab589a91f
|
||||
6f7cfeff708521a3ac2f4fb64c37f3f9ae533c661f186ec1763e6bb88907cd4c
|
||||
+75
-30
@@ -6226,7 +6226,9 @@ static int clearCell(
|
||||
if( pInfo->nLocal==pInfo->nPayload ){
|
||||
return SQLITE_OK; /* No overflow pages. Return without doing anything */
|
||||
}
|
||||
if( pCell+pInfo->nSize-1 > pPage->aData+pPage->maskPage ){
|
||||
testcase( pCell + pInfo->nSize == pPage->aDataEnd );
|
||||
testcase( pCell + (pInfo->nSize-1) == pPage->aDataEnd );
|
||||
if( pCell + pInfo->nSize > pPage->aDataEnd ){
|
||||
/* Cell extends past end of page */
|
||||
return SQLITE_CORRUPT_PAGE(pPage);
|
||||
}
|
||||
@@ -8330,43 +8332,86 @@ int sqlite3BtreeInsert(
|
||||
invalidateIncrblobCursors(p, pCur->pgnoRoot, pX->nKey, 0);
|
||||
|
||||
/* If BTREE_SAVEPOSITION is set, the cursor must already be pointing
|
||||
** to a row with the same key as the new entry being inserted. */
|
||||
assert( (flags & BTREE_SAVEPOSITION)==0 ||
|
||||
((pCur->curFlags&BTCF_ValidNKey)!=0 && pX->nKey==pCur->info.nKey) );
|
||||
** to a row with the same key as the new entry being inserted.
|
||||
*/
|
||||
#ifdef SQLITE_DEBUG
|
||||
if( flags & BTREE_SAVEPOSITION ){
|
||||
assert( pCur->curFlags & BTCF_ValidNKey );
|
||||
assert( pX->nKey==pCur->info.nKey );
|
||||
assert( pCur->info.nSize!=0 );
|
||||
assert( loc==0 );
|
||||
}
|
||||
#endif
|
||||
|
||||
/* If the cursor is currently on the last row and we are appending a
|
||||
** new row onto the end, set the "loc" to avoid an unnecessary
|
||||
** btreeMoveto() call */
|
||||
/* On the other hand, BTREE_SAVEPOSITION==0 does not imply
|
||||
** that the cursor is not pointing to a row to be overwritten.
|
||||
** So do a complete check.
|
||||
*/
|
||||
if( (pCur->curFlags&BTCF_ValidNKey)!=0 && pX->nKey==pCur->info.nKey ){
|
||||
/* The current is currently pointing to the entry that is to be
|
||||
/* The cursor is pointing to the entry that is to be
|
||||
** overwritten */
|
||||
assert( pX->nData>=0 && pX->nZero>=0 );
|
||||
if( pCur->info.nSize!=0
|
||||
&& pCur->info.nPayload==(u32)pX->nData+pX->nZero
|
||||
){
|
||||
/* New entry is the same size as the old. Do an overwrite */
|
||||
return btreeOverwriteCell(pCur, pX);
|
||||
}
|
||||
loc = 0;
|
||||
assert( loc==0 );
|
||||
}else if( loc==0 ){
|
||||
/* The cursor is *not* pointing to the cell to be overwritten, nor
|
||||
** to an adjacent cell. Move the cursor so that it is pointing either
|
||||
** to the cell to be overwritten or an adjacent cell.
|
||||
*/
|
||||
rc = sqlite3BtreeMovetoUnpacked(pCur, 0, pX->nKey, flags!=0, &loc);
|
||||
if( rc ) return rc;
|
||||
}
|
||||
}else if( loc==0 && (flags & BTREE_SAVEPOSITION)==0 ){
|
||||
if( pX->nMem ){
|
||||
UnpackedRecord r;
|
||||
r.pKeyInfo = pCur->pKeyInfo;
|
||||
r.aMem = pX->aMem;
|
||||
r.nField = pX->nMem;
|
||||
r.default_rc = 0;
|
||||
r.errCode = 0;
|
||||
r.r1 = 0;
|
||||
r.r2 = 0;
|
||||
r.eqSeen = 0;
|
||||
rc = sqlite3BtreeMovetoUnpacked(pCur, &r, 0, flags!=0, &loc);
|
||||
}else{
|
||||
rc = btreeMoveto(pCur, pX->pKey, pX->nKey, flags!=0, &loc);
|
||||
}else{
|
||||
/* This is an index or a WITHOUT ROWID table */
|
||||
|
||||
/* If BTREE_SAVEPOSITION is set, the cursor must already be pointing
|
||||
** to a row with the same key as the new entry being inserted.
|
||||
*/
|
||||
assert( (flags & BTREE_SAVEPOSITION)==0 || loc==0 );
|
||||
|
||||
/* If the cursor is not already pointing either to the cell to be
|
||||
** overwritten, or if a new cell is being inserted, if the cursor is
|
||||
** not pointing to an immediately adjacent cell, then move the cursor
|
||||
** so that it does.
|
||||
*/
|
||||
if( loc==0 && (flags & BTREE_SAVEPOSITION)==0 ){
|
||||
if( pX->nMem ){
|
||||
UnpackedRecord r;
|
||||
r.pKeyInfo = pCur->pKeyInfo;
|
||||
r.aMem = pX->aMem;
|
||||
r.nField = pX->nMem;
|
||||
r.default_rc = 0;
|
||||
r.errCode = 0;
|
||||
r.r1 = 0;
|
||||
r.r2 = 0;
|
||||
r.eqSeen = 0;
|
||||
rc = sqlite3BtreeMovetoUnpacked(pCur, &r, 0, flags!=0, &loc);
|
||||
}else{
|
||||
rc = btreeMoveto(pCur, pX->pKey, pX->nKey, flags!=0, &loc);
|
||||
}
|
||||
if( rc ) return rc;
|
||||
}
|
||||
if( rc ) return rc;
|
||||
|
||||
/* If the cursor is currently pointing to an entry to be overwritten
|
||||
** and the new content is the same as as the old, then use the
|
||||
** overwrite optimization.
|
||||
*/
|
||||
if( loc==0 ){
|
||||
getCellInfo(pCur);
|
||||
if( pCur->info.nKey==pX->nKey ){
|
||||
BtreePayload x2;
|
||||
x2.pData = pX->pKey;
|
||||
x2.nData = pX->nKey;
|
||||
x2.nZero = 0;
|
||||
return btreeOverwriteCell(pCur, &x2);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
assert( pCur->eState==CURSOR_VALID || (pCur->eState==CURSOR_INVALID && loc) );
|
||||
|
||||
@@ -9205,14 +9250,14 @@ static void checkAppendMsg(
|
||||
pCheck->nErr++;
|
||||
va_start(ap, zFormat);
|
||||
if( pCheck->errMsg.nChar ){
|
||||
sqlite3StrAccumAppend(&pCheck->errMsg, "\n", 1);
|
||||
sqlite3_str_append(&pCheck->errMsg, "\n", 1);
|
||||
}
|
||||
if( pCheck->zPfx ){
|
||||
sqlite3XPrintf(&pCheck->errMsg, pCheck->zPfx, pCheck->v1, pCheck->v2);
|
||||
sqlite3_str_appendf(&pCheck->errMsg, pCheck->zPfx, pCheck->v1, pCheck->v2);
|
||||
}
|
||||
sqlite3VXPrintf(&pCheck->errMsg, zFormat, ap);
|
||||
sqlite3_str_vappendf(&pCheck->errMsg, zFormat, ap);
|
||||
va_end(ap);
|
||||
if( pCheck->errMsg.accError==STRACCUM_NOMEM ){
|
||||
if( pCheck->errMsg.accError==SQLITE_NOMEM ){
|
||||
pCheck->mallocFailed = 1;
|
||||
}
|
||||
}
|
||||
@@ -9796,11 +9841,11 @@ integrity_ck_cleanup:
|
||||
sqlite3PageFree(sCheck.heap);
|
||||
sqlite3_free(sCheck.aPgRef);
|
||||
if( sCheck.mallocFailed ){
|
||||
sqlite3StrAccumReset(&sCheck.errMsg);
|
||||
sqlite3_str_reset(&sCheck.errMsg);
|
||||
sCheck.nErr++;
|
||||
}
|
||||
*pnErr = sCheck.nErr;
|
||||
if( sCheck.nErr==0 ) sqlite3StrAccumReset(&sCheck.errMsg);
|
||||
if( sCheck.nErr==0 ) sqlite3_str_reset(&sCheck.errMsg);
|
||||
/* Make sure this analysis did not leave any unref() pages. */
|
||||
assert( nRef==sqlite3PagerRefcount(pBt->pPager) );
|
||||
sqlite3BtreeLeave(p);
|
||||
|
||||
+18
-3
@@ -259,13 +259,28 @@ int sqlite3BtreeDelete(BtCursor*, u8 flags);
|
||||
** entry in either an index or table btree.
|
||||
**
|
||||
** Index btrees (used for indexes and also WITHOUT ROWID tables) contain
|
||||
** an arbitrary key and no data. These btrees have pKey,nKey set to their
|
||||
** key and pData,nData,nZero set to zero.
|
||||
** an arbitrary key and no data. These btrees have pKey,nKey set to the
|
||||
** key and the pData,nData,nZero fields are uninitialized. The aMem,nMem
|
||||
** fields give an array of Mem objects that are a decomposition of the key.
|
||||
** The nMem field might be zero, indicating that no decomposition is available.
|
||||
**
|
||||
** Table btrees (used for rowid tables) contain an integer rowid used as
|
||||
** the key and passed in the nKey field. The pKey field is zero.
|
||||
** pData,nData hold the content of the new entry. nZero extra zero bytes
|
||||
** are appended to the end of the content when constructing the entry.
|
||||
** The aMem,nMem fields are uninitialized for table btrees.
|
||||
**
|
||||
** Field usage summary:
|
||||
**
|
||||
** Table BTrees Index Btrees
|
||||
**
|
||||
** pKey always NULL encoded key
|
||||
** nKey the ROWID length of pKey
|
||||
** pData data not used
|
||||
** aMem not used decomposed key value
|
||||
** nMem not used entries in aMem
|
||||
** nData length of pData not used
|
||||
** nZero extra zeros after pData not used
|
||||
**
|
||||
** This object is used to pass information into sqlite3BtreeInsert(). The
|
||||
** same information used to be passed as five separate parameters. But placing
|
||||
@@ -276,7 +291,7 @@ int sqlite3BtreeDelete(BtCursor*, u8 flags);
|
||||
struct BtreePayload {
|
||||
const void *pKey; /* Key content for indexes. NULL for tables */
|
||||
sqlite3_int64 nKey; /* Size of pKey for indexes. PRIMARY KEY for tabs */
|
||||
const void *pData; /* Data for tables. NULL for indexes */
|
||||
const void *pData; /* Data for tables. */
|
||||
sqlite3_value *aMem; /* First of nMem value in the unpacked pKey */
|
||||
u16 nMem; /* Number of aMem[] value. Might be zero */
|
||||
int nData; /* Size of pData. 0 if none. */
|
||||
|
||||
+5
-5
@@ -4207,16 +4207,16 @@ void sqlite3UniqueConstraint(
|
||||
|
||||
sqlite3StrAccumInit(&errMsg, pParse->db, 0, 0, 200);
|
||||
if( pIdx->aColExpr ){
|
||||
sqlite3XPrintf(&errMsg, "index '%q'", pIdx->zName);
|
||||
sqlite3_str_appendf(&errMsg, "index '%q'", pIdx->zName);
|
||||
}else{
|
||||
for(j=0; j<pIdx->nKeyCol; j++){
|
||||
char *zCol;
|
||||
assert( pIdx->aiColumn[j]>=0 );
|
||||
zCol = pTab->aCol[pIdx->aiColumn[j]].zName;
|
||||
if( j ) sqlite3StrAccumAppend(&errMsg, ", ", 2);
|
||||
sqlite3StrAccumAppendAll(&errMsg, pTab->zName);
|
||||
sqlite3StrAccumAppend(&errMsg, ".", 1);
|
||||
sqlite3StrAccumAppendAll(&errMsg, zCol);
|
||||
if( j ) sqlite3_str_append(&errMsg, ", ", 2);
|
||||
sqlite3_str_appendall(&errMsg, pTab->zName);
|
||||
sqlite3_str_append(&errMsg, ".", 1);
|
||||
sqlite3_str_appendall(&errMsg, zCol);
|
||||
}
|
||||
}
|
||||
zErr = sqlite3StrAccumFinish(&errMsg);
|
||||
|
||||
+7
-4
@@ -553,13 +553,16 @@ void sqlite3DeleteFrom(
|
||||
if( IsVirtual(pTab) ){
|
||||
const char *pVTab = (const char *)sqlite3GetVTable(db, pTab);
|
||||
sqlite3VtabMakeWritable(pParse, pTab);
|
||||
sqlite3VdbeAddOp4(v, OP_VUpdate, 0, 1, iKey, pVTab, P4_VTAB);
|
||||
sqlite3VdbeChangeP5(v, OE_Abort);
|
||||
assert( eOnePass==ONEPASS_OFF || eOnePass==ONEPASS_SINGLE );
|
||||
sqlite3MayAbort(pParse);
|
||||
if( eOnePass==ONEPASS_SINGLE && sqlite3IsToplevel(pParse) ){
|
||||
pParse->isMultiWrite = 0;
|
||||
if( eOnePass==ONEPASS_SINGLE ){
|
||||
sqlite3VdbeAddOp1(v, OP_Close, iTabCur);
|
||||
if( sqlite3IsToplevel(pParse) ){
|
||||
pParse->isMultiWrite = 0;
|
||||
}
|
||||
}
|
||||
sqlite3VdbeAddOp4(v, OP_VUpdate, 0, 1, iKey, pVTab, P4_VTAB);
|
||||
sqlite3VdbeChangeP5(v, OE_Abort);
|
||||
}else
|
||||
#endif
|
||||
{
|
||||
|
||||
+5
-5
@@ -251,7 +251,7 @@ static void printfFunc(
|
||||
x.apArg = argv+1;
|
||||
sqlite3StrAccumInit(&str, db, 0, 0, db->aLimit[SQLITE_LIMIT_LENGTH]);
|
||||
str.printfFlags = SQLITE_PRINTF_SQLFUNC;
|
||||
sqlite3XPrintf(&str, zFormat, &x);
|
||||
sqlite3_str_appendf(&str, zFormat, &x);
|
||||
n = str.nChar;
|
||||
sqlite3_result_text(context, sqlite3StrAccumFinish(&str), n,
|
||||
SQLITE_DYNAMIC);
|
||||
@@ -1654,20 +1654,20 @@ static void groupConcatStep(
|
||||
zSep = ",";
|
||||
nSep = 1;
|
||||
}
|
||||
if( zSep ) sqlite3StrAccumAppend(pAccum, zSep, nSep);
|
||||
if( zSep ) sqlite3_str_append(pAccum, zSep, nSep);
|
||||
}
|
||||
zVal = (char*)sqlite3_value_text(argv[0]);
|
||||
nVal = sqlite3_value_bytes(argv[0]);
|
||||
if( zVal ) sqlite3StrAccumAppend(pAccum, zVal, nVal);
|
||||
if( zVal ) sqlite3_str_append(pAccum, zVal, nVal);
|
||||
}
|
||||
}
|
||||
static void groupConcatFinalize(sqlite3_context *context){
|
||||
StrAccum *pAccum;
|
||||
pAccum = sqlite3_aggregate_context(context, 0);
|
||||
if( pAccum ){
|
||||
if( pAccum->accError==STRACCUM_TOOBIG ){
|
||||
if( pAccum->accError==SQLITE_TOOBIG ){
|
||||
sqlite3_result_error_toobig(context);
|
||||
}else if( pAccum->accError==STRACCUM_NOMEM ){
|
||||
}else if( pAccum->accError==SQLITE_NOMEM ){
|
||||
sqlite3_result_error_nomem(context);
|
||||
}else{
|
||||
sqlite3_result_text(context, sqlite3StrAccumFinish(pAccum), -1,
|
||||
|
||||
@@ -226,11 +226,26 @@ static int autoIncBegin(
|
||||
Table *pTab /* The table we are writing to */
|
||||
){
|
||||
int memId = 0; /* Register holding maximum rowid */
|
||||
assert( pParse->db->aDb[iDb].pSchema!=0 );
|
||||
if( (pTab->tabFlags & TF_Autoincrement)!=0
|
||||
&& (pParse->db->mDbFlags & DBFLAG_Vacuum)==0
|
||||
){
|
||||
Parse *pToplevel = sqlite3ParseToplevel(pParse);
|
||||
AutoincInfo *pInfo;
|
||||
Table *pSeqTab = pParse->db->aDb[iDb].pSchema->pSeqTab;
|
||||
|
||||
/* Verify that the sqlite_sequence table exists and is an ordinary
|
||||
** rowid table with exactly two columns.
|
||||
** Ticket d8dc2b3a58cd5dc2918a1d4acb 2018-05-23 */
|
||||
if( pSeqTab==0
|
||||
|| !HasRowid(pSeqTab)
|
||||
|| IsVirtual(pSeqTab)
|
||||
|| pSeqTab->nCol!=2
|
||||
){
|
||||
pParse->nErr++;
|
||||
pParse->rc = SQLITE_CORRUPT_SEQUENCE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
pInfo = pToplevel->pAinc;
|
||||
while( pInfo && pInfo->pTab!=pTab ){ pInfo = pInfo->pNext; }
|
||||
|
||||
+17
-4
@@ -434,7 +434,22 @@ static const sqlite3_api_routines sqlite3Apis = {
|
||||
/* Version 3.22.0 and later */
|
||||
sqlite3_vtab_nochange,
|
||||
sqlite3_value_nochange,
|
||||
sqlite3_vtab_collation
|
||||
sqlite3_vtab_collation,
|
||||
/* Version 3.24.0 and later */
|
||||
sqlite3_keyword_count,
|
||||
sqlite3_keyword_name,
|
||||
sqlite3_keyword_check,
|
||||
sqlite3_str_new,
|
||||
sqlite3_str_finish,
|
||||
sqlite3_str_appendf,
|
||||
sqlite3_str_vappendf,
|
||||
sqlite3_str_append,
|
||||
sqlite3_str_appendall,
|
||||
sqlite3_str_appendchar,
|
||||
sqlite3_str_reset,
|
||||
sqlite3_str_errcode,
|
||||
sqlite3_str_length,
|
||||
sqlite3_str_value
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -500,10 +515,8 @@ static int sqlite3LoadExtension(
|
||||
#if SQLITE_OS_UNIX || SQLITE_OS_WIN
|
||||
for(ii=0; ii<ArraySize(azEndings) && handle==0; ii++){
|
||||
char *zAltFile = sqlite3_mprintf("%s.%s", zFile, azEndings[ii]);
|
||||
int bExists = 0;
|
||||
if( zAltFile==0 ) return SQLITE_NOMEM_BKPT;
|
||||
sqlite3OsAccess(pVfs, zAltFile, SQLITE_ACCESS_EXISTS, &bExists);
|
||||
if( bExists ) handle = sqlite3OsDlOpen(pVfs, zAltFile);
|
||||
handle = sqlite3OsDlOpen(pVfs, zAltFile);
|
||||
sqlite3_free(zAltFile);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -358,4 +358,3 @@ int sqlite3_mutex_notheld(sqlite3_mutex *p){
|
||||
#endif
|
||||
|
||||
#endif /* !defined(SQLITE_MUTEX_OMIT) */
|
||||
|
||||
|
||||
+10
-10
@@ -2216,26 +2216,26 @@ static int pragmaVtabConnect(
|
||||
UNUSED_PARAMETER(argc);
|
||||
UNUSED_PARAMETER(argv);
|
||||
sqlite3StrAccumInit(&acc, 0, zBuf, sizeof(zBuf), 0);
|
||||
sqlite3StrAccumAppendAll(&acc, "CREATE TABLE x");
|
||||
sqlite3_str_appendall(&acc, "CREATE TABLE x");
|
||||
for(i=0, j=pPragma->iPragCName; i<pPragma->nPragCName; i++, j++){
|
||||
sqlite3XPrintf(&acc, "%c\"%s\"", cSep, pragCName[j]);
|
||||
sqlite3_str_appendf(&acc, "%c\"%s\"", cSep, pragCName[j]);
|
||||
cSep = ',';
|
||||
}
|
||||
if( i==0 ){
|
||||
sqlite3XPrintf(&acc, "(\"%s\"", pPragma->zName);
|
||||
sqlite3_str_appendf(&acc, "(\"%s\"", pPragma->zName);
|
||||
cSep = ',';
|
||||
i++;
|
||||
}
|
||||
j = 0;
|
||||
if( pPragma->mPragFlg & PragFlg_Result1 ){
|
||||
sqlite3StrAccumAppendAll(&acc, ",arg HIDDEN");
|
||||
sqlite3_str_appendall(&acc, ",arg HIDDEN");
|
||||
j++;
|
||||
}
|
||||
if( pPragma->mPragFlg & (PragFlg_SchemaOpt|PragFlg_SchemaReq) ){
|
||||
sqlite3StrAccumAppendAll(&acc, ",schema HIDDEN");
|
||||
sqlite3_str_appendall(&acc, ",schema HIDDEN");
|
||||
j++;
|
||||
}
|
||||
sqlite3StrAccumAppend(&acc, ")", 1);
|
||||
sqlite3_str_append(&acc, ")", 1);
|
||||
sqlite3StrAccumFinish(&acc);
|
||||
assert( strlen(zBuf) < sizeof(zBuf)-1 );
|
||||
rc = sqlite3_declare_vtab(db, zBuf);
|
||||
@@ -2387,13 +2387,13 @@ static int pragmaVtabFilter(
|
||||
}
|
||||
}
|
||||
sqlite3StrAccumInit(&acc, 0, 0, 0, pTab->db->aLimit[SQLITE_LIMIT_SQL_LENGTH]);
|
||||
sqlite3StrAccumAppendAll(&acc, "PRAGMA ");
|
||||
sqlite3_str_appendall(&acc, "PRAGMA ");
|
||||
if( pCsr->azArg[1] ){
|
||||
sqlite3XPrintf(&acc, "%Q.", pCsr->azArg[1]);
|
||||
sqlite3_str_appendf(&acc, "%Q.", pCsr->azArg[1]);
|
||||
}
|
||||
sqlite3StrAccumAppendAll(&acc, pTab->pName->zName);
|
||||
sqlite3_str_appendall(&acc, pTab->pName->zName);
|
||||
if( pCsr->azArg[0] ){
|
||||
sqlite3XPrintf(&acc, "=%Q", pCsr->azArg[0]);
|
||||
sqlite3_str_appendf(&acc, "=%Q", pCsr->azArg[0]);
|
||||
}
|
||||
zSql = sqlite3StrAccumFinish(&acc);
|
||||
if( zSql==0 ) return SQLITE_NOMEM;
|
||||
|
||||
+97
-43
@@ -134,7 +134,7 @@ static char et_getdigit(LONGDOUBLE_TYPE *val, int *cnt){
|
||||
** Set the StrAccum object to an error mode.
|
||||
*/
|
||||
static void setStrAccumError(StrAccum *p, u8 eError){
|
||||
assert( eError==STRACCUM_NOMEM || eError==STRACCUM_TOOBIG );
|
||||
assert( eError==SQLITE_NOMEM || eError==SQLITE_TOOBIG );
|
||||
p->accError = eError;
|
||||
p->nAlloc = 0;
|
||||
}
|
||||
@@ -168,8 +168,8 @@ static char *getTextArg(PrintfArguments *p){
|
||||
/*
|
||||
** Render a string given by "fmt" into the StrAccum object.
|
||||
*/
|
||||
void sqlite3VXPrintf(
|
||||
StrAccum *pAccum, /* Accumulate results here */
|
||||
void sqlite3_str_vappendf(
|
||||
sqlite3_str *pAccum, /* Accumulate results here */
|
||||
const char *fmt, /* Format string */
|
||||
va_list ap /* arguments */
|
||||
){
|
||||
@@ -226,11 +226,11 @@ void sqlite3VXPrintf(
|
||||
#else
|
||||
do{ fmt++; }while( *fmt && *fmt != '%' );
|
||||
#endif
|
||||
sqlite3StrAccumAppend(pAccum, bufpt, (int)(fmt - bufpt));
|
||||
sqlite3_str_append(pAccum, bufpt, (int)(fmt - bufpt));
|
||||
if( *fmt==0 ) break;
|
||||
}
|
||||
if( (c=(*++fmt))==0 ){
|
||||
sqlite3StrAccumAppend(pAccum, "%", 1);
|
||||
sqlite3_str_append(pAccum, "%", 1);
|
||||
break;
|
||||
}
|
||||
/* Find out what flags are present */
|
||||
@@ -408,7 +408,7 @@ void sqlite3VXPrintf(
|
||||
u64 n = (u64)precision + 10 + precision/3;
|
||||
zOut = zExtra = sqlite3Malloc( n );
|
||||
if( zOut==0 ){
|
||||
setStrAccumError(pAccum, STRACCUM_NOMEM);
|
||||
setStrAccumError(pAccum, SQLITE_NOMEM);
|
||||
return;
|
||||
}
|
||||
nOut = (int)n;
|
||||
@@ -533,7 +533,7 @@ void sqlite3VXPrintf(
|
||||
bufpt = zExtra
|
||||
= sqlite3Malloc( MAX(e2,0)+(i64)precision+(i64)width+15 );
|
||||
if( bufpt==0 ){
|
||||
setStrAccumError(pAccum, STRACCUM_NOMEM);
|
||||
setStrAccumError(pAccum, SQLITE_NOMEM);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -665,11 +665,11 @@ void sqlite3VXPrintf(
|
||||
if( precision>1 ){
|
||||
width -= precision-1;
|
||||
if( width>1 && !flag_leftjustify ){
|
||||
sqlite3AppendChar(pAccum, width-1, ' ');
|
||||
sqlite3_str_appendchar(pAccum, width-1, ' ');
|
||||
width = 0;
|
||||
}
|
||||
while( precision-- > 1 ){
|
||||
sqlite3StrAccumAppend(pAccum, buf, length);
|
||||
sqlite3_str_append(pAccum, buf, length);
|
||||
}
|
||||
}
|
||||
bufpt = buf;
|
||||
@@ -755,7 +755,7 @@ void sqlite3VXPrintf(
|
||||
if( n>etBUFSIZE ){
|
||||
bufpt = zExtra = sqlite3Malloc( n );
|
||||
if( bufpt==0 ){
|
||||
setStrAccumError(pAccum, STRACCUM_NOMEM);
|
||||
setStrAccumError(pAccum, SQLITE_NOMEM);
|
||||
return;
|
||||
}
|
||||
}else{
|
||||
@@ -779,7 +779,7 @@ void sqlite3VXPrintf(
|
||||
pToken = va_arg(ap, Token*);
|
||||
assert( bArgList==0 );
|
||||
if( pToken && pToken->n ){
|
||||
sqlite3StrAccumAppend(pAccum, (const char*)pToken->z, pToken->n);
|
||||
sqlite3_str_append(pAccum, (const char*)pToken->z, pToken->n);
|
||||
}
|
||||
length = width = 0;
|
||||
break;
|
||||
@@ -795,10 +795,10 @@ void sqlite3VXPrintf(
|
||||
assert( bArgList==0 );
|
||||
assert( k>=0 && k<pSrc->nSrc );
|
||||
if( pItem->zDatabase ){
|
||||
sqlite3StrAccumAppendAll(pAccum, pItem->zDatabase);
|
||||
sqlite3StrAccumAppend(pAccum, ".", 1);
|
||||
sqlite3_str_appendall(pAccum, pItem->zDatabase);
|
||||
sqlite3_str_append(pAccum, ".", 1);
|
||||
}
|
||||
sqlite3StrAccumAppendAll(pAccum, pItem->zName);
|
||||
sqlite3_str_appendall(pAccum, pItem->zName);
|
||||
length = width = 0;
|
||||
break;
|
||||
}
|
||||
@@ -817,11 +817,11 @@ void sqlite3VXPrintf(
|
||||
*/
|
||||
width -= length;
|
||||
if( width>0 ){
|
||||
if( !flag_leftjustify ) sqlite3AppendChar(pAccum, width, ' ');
|
||||
sqlite3StrAccumAppend(pAccum, bufpt, length);
|
||||
if( flag_leftjustify ) sqlite3AppendChar(pAccum, width, ' ');
|
||||
if( !flag_leftjustify ) sqlite3_str_appendchar(pAccum, width, ' ');
|
||||
sqlite3_str_append(pAccum, bufpt, length);
|
||||
if( flag_leftjustify ) sqlite3_str_appendchar(pAccum, width, ' ');
|
||||
}else{
|
||||
sqlite3StrAccumAppend(pAccum, bufpt, length);
|
||||
sqlite3_str_append(pAccum, bufpt, length);
|
||||
}
|
||||
|
||||
if( zExtra ){
|
||||
@@ -842,13 +842,13 @@ static int sqlite3StrAccumEnlarge(StrAccum *p, int N){
|
||||
char *zNew;
|
||||
assert( p->nChar+(i64)N >= p->nAlloc ); /* Only called if really needed */
|
||||
if( p->accError ){
|
||||
testcase(p->accError==STRACCUM_TOOBIG);
|
||||
testcase(p->accError==STRACCUM_NOMEM);
|
||||
testcase(p->accError==SQLITE_TOOBIG);
|
||||
testcase(p->accError==SQLITE_NOMEM);
|
||||
return 0;
|
||||
}
|
||||
if( p->mxAlloc==0 ){
|
||||
N = p->nAlloc - p->nChar - 1;
|
||||
setStrAccumError(p, STRACCUM_TOOBIG);
|
||||
setStrAccumError(p, SQLITE_TOOBIG);
|
||||
return N;
|
||||
}else{
|
||||
char *zOld = isMalloced(p) ? p->zText : 0;
|
||||
@@ -860,8 +860,8 @@ static int sqlite3StrAccumEnlarge(StrAccum *p, int N){
|
||||
szNew += p->nChar;
|
||||
}
|
||||
if( szNew > p->mxAlloc ){
|
||||
sqlite3StrAccumReset(p);
|
||||
setStrAccumError(p, STRACCUM_TOOBIG);
|
||||
sqlite3_str_reset(p);
|
||||
setStrAccumError(p, SQLITE_TOOBIG);
|
||||
return 0;
|
||||
}else{
|
||||
p->nAlloc = (int)szNew;
|
||||
@@ -878,8 +878,8 @@ static int sqlite3StrAccumEnlarge(StrAccum *p, int N){
|
||||
p->nAlloc = sqlite3DbMallocSize(p->db, zNew);
|
||||
p->printfFlags |= SQLITE_PRINTF_MALLOCED;
|
||||
}else{
|
||||
sqlite3StrAccumReset(p);
|
||||
setStrAccumError(p, STRACCUM_NOMEM);
|
||||
sqlite3_str_reset(p);
|
||||
setStrAccumError(p, SQLITE_NOMEM);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -889,7 +889,7 @@ static int sqlite3StrAccumEnlarge(StrAccum *p, int N){
|
||||
/*
|
||||
** Append N copies of character c to the given string buffer.
|
||||
*/
|
||||
void sqlite3AppendChar(StrAccum *p, int N, char c){
|
||||
void sqlite3_str_appendchar(sqlite3_str *p, int N, char c){
|
||||
testcase( p->nChar + (i64)N > 0x7fffffff );
|
||||
if( p->nChar+(i64)N >= p->nAlloc && (N = sqlite3StrAccumEnlarge(p, N))<=0 ){
|
||||
return;
|
||||
@@ -901,9 +901,9 @@ void sqlite3AppendChar(StrAccum *p, int N, char c){
|
||||
** The StrAccum "p" is not large enough to accept N new bytes of z[].
|
||||
** So enlarge if first, then do the append.
|
||||
**
|
||||
** This is a helper routine to sqlite3StrAccumAppend() that does special-case
|
||||
** This is a helper routine to sqlite3_str_append() that does special-case
|
||||
** work (enlarging the buffer) using tail recursion, so that the
|
||||
** sqlite3StrAccumAppend() routine can use fast calling semantics.
|
||||
** sqlite3_str_append() routine can use fast calling semantics.
|
||||
*/
|
||||
static void SQLITE_NOINLINE enlargeAndAppend(StrAccum *p, const char *z, int N){
|
||||
N = sqlite3StrAccumEnlarge(p, N);
|
||||
@@ -917,7 +917,7 @@ static void SQLITE_NOINLINE enlargeAndAppend(StrAccum *p, const char *z, int N){
|
||||
** Append N bytes of text from z to the StrAccum object. Increase the
|
||||
** size of the memory allocation for StrAccum if necessary.
|
||||
*/
|
||||
void sqlite3StrAccumAppend(StrAccum *p, const char *z, int N){
|
||||
void sqlite3_str_append(sqlite3_str *p, const char *z, int N){
|
||||
assert( z!=0 || N==0 );
|
||||
assert( p->zText!=0 || p->nChar==0 || p->accError );
|
||||
assert( N>=0 );
|
||||
@@ -934,8 +934,8 @@ void sqlite3StrAccumAppend(StrAccum *p, const char *z, int N){
|
||||
/*
|
||||
** Append the complete text of zero-terminated string z[] to the p string.
|
||||
*/
|
||||
void sqlite3StrAccumAppendAll(StrAccum *p, const char *z){
|
||||
sqlite3StrAccumAppend(p, z, sqlite3Strlen30(z));
|
||||
void sqlite3_str_appendall(sqlite3_str *p, const char *z){
|
||||
sqlite3_str_append(p, z, sqlite3Strlen30(z));
|
||||
}
|
||||
|
||||
|
||||
@@ -952,7 +952,7 @@ static SQLITE_NOINLINE char *strAccumFinishRealloc(StrAccum *p){
|
||||
memcpy(zText, p->zText, p->nChar+1);
|
||||
p->printfFlags |= SQLITE_PRINTF_MALLOCED;
|
||||
}else{
|
||||
setStrAccumError(p, STRACCUM_NOMEM);
|
||||
setStrAccumError(p, SQLITE_NOMEM);
|
||||
}
|
||||
p->zText = zText;
|
||||
return zText;
|
||||
@@ -967,14 +967,56 @@ char *sqlite3StrAccumFinish(StrAccum *p){
|
||||
return p->zText;
|
||||
}
|
||||
|
||||
/*
|
||||
** This singleton is an sqlite3_str object that is returned if
|
||||
** sqlite3_malloc() fails to provide space for a real one. This
|
||||
** sqlite3_str object accepts no new text and always returns
|
||||
** an SQLITE_NOMEM error.
|
||||
*/
|
||||
static sqlite3_str sqlite3OomStr = {
|
||||
0, 0, 0, 0, 0, SQLITE_NOMEM
|
||||
};
|
||||
|
||||
/* Finalize a string created using sqlite3_str_new().
|
||||
*/
|
||||
char *sqlite3_str_finish(sqlite3_str *p){
|
||||
char *z;
|
||||
if( p!=0 && p!=&sqlite3OomStr ){
|
||||
z = sqlite3StrAccumFinish(p);
|
||||
sqlite3_free(p);
|
||||
}else{
|
||||
z = 0;
|
||||
}
|
||||
return z;
|
||||
}
|
||||
|
||||
/* Return any error code associated with p */
|
||||
int sqlite3_str_errcode(sqlite3_str *p){
|
||||
return p ? p->accError : SQLITE_NOMEM;
|
||||
}
|
||||
|
||||
/* Return the current length of p in bytes */
|
||||
int sqlite3_str_length(sqlite3_str *p){
|
||||
return p ? p->nChar : 0;
|
||||
}
|
||||
|
||||
/* Return the current value for p */
|
||||
char *sqlite3_str_value(sqlite3_str *p){
|
||||
if( p==0 || p->nChar==0 ) return 0;
|
||||
p->zText[p->nChar] = 0;
|
||||
return p->zText;
|
||||
}
|
||||
|
||||
/*
|
||||
** Reset an StrAccum string. Reclaim all malloced memory.
|
||||
*/
|
||||
void sqlite3StrAccumReset(StrAccum *p){
|
||||
void sqlite3_str_reset(StrAccum *p){
|
||||
if( isMalloced(p) ){
|
||||
sqlite3DbFree(p->db, p->zText);
|
||||
p->printfFlags &= ~SQLITE_PRINTF_MALLOCED;
|
||||
}
|
||||
p->nAlloc = 0;
|
||||
p->nChar = 0;
|
||||
p->zText = 0;
|
||||
}
|
||||
|
||||
@@ -1002,6 +1044,18 @@ void sqlite3StrAccumInit(StrAccum *p, sqlite3 *db, char *zBase, int n, int mx){
|
||||
p->printfFlags = 0;
|
||||
}
|
||||
|
||||
/* Allocate and initialize a new dynamic string object */
|
||||
sqlite3_str *sqlite3_str_new(sqlite3 *db){
|
||||
sqlite3_str *p = sqlite3_malloc64(sizeof(*p));
|
||||
if( p ){
|
||||
sqlite3StrAccumInit(p, 0, 0, 0,
|
||||
db ? db->aLimit[SQLITE_LIMIT_LENGTH] : SQLITE_MAX_LENGTH);
|
||||
}else{
|
||||
p = &sqlite3OomStr;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
/*
|
||||
** Print into memory obtained from sqliteMalloc(). Use the internal
|
||||
** %-conversion extensions.
|
||||
@@ -1014,9 +1068,9 @@ char *sqlite3VMPrintf(sqlite3 *db, const char *zFormat, va_list ap){
|
||||
sqlite3StrAccumInit(&acc, db, zBase, sizeof(zBase),
|
||||
db->aLimit[SQLITE_LIMIT_LENGTH]);
|
||||
acc.printfFlags = SQLITE_PRINTF_INTERNAL;
|
||||
sqlite3VXPrintf(&acc, zFormat, ap);
|
||||
sqlite3_str_vappendf(&acc, zFormat, ap);
|
||||
z = sqlite3StrAccumFinish(&acc);
|
||||
if( acc.accError==STRACCUM_NOMEM ){
|
||||
if( acc.accError==SQLITE_NOMEM ){
|
||||
sqlite3OomFault(db);
|
||||
}
|
||||
return z;
|
||||
@@ -1054,7 +1108,7 @@ char *sqlite3_vmprintf(const char *zFormat, va_list ap){
|
||||
if( sqlite3_initialize() ) return 0;
|
||||
#endif
|
||||
sqlite3StrAccumInit(&acc, 0, zBase, sizeof(zBase), SQLITE_MAX_LENGTH);
|
||||
sqlite3VXPrintf(&acc, zFormat, ap);
|
||||
sqlite3_str_vappendf(&acc, zFormat, ap);
|
||||
z = sqlite3StrAccumFinish(&acc);
|
||||
return z;
|
||||
}
|
||||
@@ -1099,7 +1153,7 @@ char *sqlite3_vsnprintf(int n, char *zBuf, const char *zFormat, va_list ap){
|
||||
}
|
||||
#endif
|
||||
sqlite3StrAccumInit(&acc, 0, zBuf, n, 0);
|
||||
sqlite3VXPrintf(&acc, zFormat, ap);
|
||||
sqlite3_str_vappendf(&acc, zFormat, ap);
|
||||
zBuf[acc.nChar] = 0;
|
||||
return zBuf;
|
||||
}
|
||||
@@ -1121,7 +1175,7 @@ char *sqlite3_snprintf(int n, char *zBuf, const char *zFormat, ...){
|
||||
** allocate memory because it might be called while the memory allocator
|
||||
** mutex is held.
|
||||
**
|
||||
** sqlite3VXPrintf() might ask for *temporary* memory allocations for
|
||||
** sqlite3_str_vappendf() might ask for *temporary* memory allocations for
|
||||
** certain format characters (%q) or for very large precisions or widths.
|
||||
** Care must be taken that any sqlite3_log() calls that occur while the
|
||||
** memory mutex is held do not use these mechanisms.
|
||||
@@ -1131,7 +1185,7 @@ static void renderLogMsg(int iErrCode, const char *zFormat, va_list ap){
|
||||
char zMsg[SQLITE_PRINT_BUF_SIZE*3]; /* Complete log message */
|
||||
|
||||
sqlite3StrAccumInit(&acc, 0, zMsg, sizeof(zMsg), 0);
|
||||
sqlite3VXPrintf(&acc, zFormat, ap);
|
||||
sqlite3_str_vappendf(&acc, zFormat, ap);
|
||||
sqlite3GlobalConfig.xLog(sqlite3GlobalConfig.pLogArg, iErrCode,
|
||||
sqlite3StrAccumFinish(&acc));
|
||||
}
|
||||
@@ -1160,7 +1214,7 @@ void sqlite3DebugPrintf(const char *zFormat, ...){
|
||||
char zBuf[500];
|
||||
sqlite3StrAccumInit(&acc, 0, zBuf, sizeof(zBuf), 0);
|
||||
va_start(ap,zFormat);
|
||||
sqlite3VXPrintf(&acc, zFormat, ap);
|
||||
sqlite3_str_vappendf(&acc, zFormat, ap);
|
||||
va_end(ap);
|
||||
sqlite3StrAccumFinish(&acc);
|
||||
#ifdef SQLITE_OS_TRACE_PROC
|
||||
@@ -1177,12 +1231,12 @@ void sqlite3DebugPrintf(const char *zFormat, ...){
|
||||
|
||||
|
||||
/*
|
||||
** variable-argument wrapper around sqlite3VXPrintf(). The bFlags argument
|
||||
** variable-argument wrapper around sqlite3_str_vappendf(). The bFlags argument
|
||||
** can contain the bit SQLITE_PRINTF_INTERNAL enable internal formats.
|
||||
*/
|
||||
void sqlite3XPrintf(StrAccum *p, const char *zFormat, ...){
|
||||
void sqlite3_str_appendf(StrAccum *p, const char *zFormat, ...){
|
||||
va_list ap;
|
||||
va_start(ap,zFormat);
|
||||
sqlite3VXPrintf(p, zFormat, ap);
|
||||
sqlite3_str_vappendf(p, zFormat, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
+5
-3
@@ -627,9 +627,10 @@ static void pushOntoSorter(
|
||||
** case regData==regOrigData.
|
||||
** (3) Some output columns are omitted from the sort record due to
|
||||
** the SQLITE_ENABLE_SORTER_REFERENCE optimization, or due to the
|
||||
** SQLITE_ECEL_OMITREF optimization. In that case, regOrigData==0
|
||||
** to prevent this routine from trying to copy values that might
|
||||
** not exist.
|
||||
** SQLITE_ECEL_OMITREF optimization, or due to the
|
||||
** SortCtx.pDeferredRowLoad optimiation. In any of these cases
|
||||
** regOrigData is 0 to prevent this routine from trying to copy
|
||||
** values that might not yet exist.
|
||||
*/
|
||||
assert( nData==1 || regData==regOrigData || regOrigData==0 );
|
||||
|
||||
@@ -1010,6 +1011,7 @@ static void selectInnerLoop(
|
||||
assert( pSort!=0 );
|
||||
assert( hasDistinct==0 );
|
||||
pSort->pDeferredRowLoad = &sRowLoadInfo;
|
||||
regOrig = 0;
|
||||
}else{
|
||||
innerLoopLoadRow(pParse, p, &sRowLoadInfo);
|
||||
}
|
||||
|
||||
+118
-58
@@ -1722,7 +1722,7 @@ static void eqp_render_level(ShellState *p, int iEqpId){
|
||||
pNext = eqp_next_row(p, iEqpId, pRow);
|
||||
z = pRow->zText;
|
||||
utf8_printf(p->out, "%s%s%s\n", p->sGraph.zPrefix, pNext ? "|--" : "`--", z);
|
||||
if( n<sizeof(p->sGraph.zPrefix)-7 ){
|
||||
if( n<(int)sizeof(p->sGraph.zPrefix)-7 ){
|
||||
memcpy(&p->sGraph.zPrefix[n], pNext ? "| " : " ", 4);
|
||||
eqp_render_level(p, pRow->iEqpId);
|
||||
p->sGraph.zPrefix[n] = 0;
|
||||
@@ -3323,6 +3323,7 @@ static char zHelp[] =
|
||||
".auth ON|OFF Show authorizer callbacks\n"
|
||||
#endif
|
||||
".backup ?DB? FILE Backup DB (default \"main\") to FILE\n"
|
||||
" Add \"--append\" to open using appendvfs.\n"
|
||||
".bail on|off Stop after hitting an error. Default OFF\n"
|
||||
".binary on|off Turn binary output on or off. Default OFF\n"
|
||||
".cd DIRECTORY Change the working directory to DIRECTORY\n"
|
||||
@@ -3540,14 +3541,17 @@ static int session_filter(void *pCtx, const char *zTab){
|
||||
** Otherwise, assume an ordinary database regardless of the filename if
|
||||
** the type cannot be determined from content.
|
||||
*/
|
||||
static int deduceDatabaseType(const char *zName, int dfltZip){
|
||||
int deduceDatabaseType(const char *zName, int dfltZip){
|
||||
FILE *f = fopen(zName, "rb");
|
||||
size_t n;
|
||||
int rc = SHELL_OPEN_UNSPEC;
|
||||
char zBuf[100];
|
||||
if( f==0 ){
|
||||
if( dfltZip && sqlite3_strlike("%.zip",zName,0)==0 ) return SHELL_OPEN_ZIPFILE;
|
||||
return SHELL_OPEN_NORMAL;
|
||||
if( dfltZip && sqlite3_strlike("%.zip",zName,0)==0 ){
|
||||
return SHELL_OPEN_ZIPFILE;
|
||||
}else{
|
||||
return SHELL_OPEN_NORMAL;
|
||||
}
|
||||
}
|
||||
fseek(f, -25, SEEK_END);
|
||||
n = fread(zBuf, 25, 1, f);
|
||||
@@ -3560,21 +3564,39 @@ static int deduceDatabaseType(const char *zName, int dfltZip){
|
||||
&& zBuf[3]==0x06 ){
|
||||
rc = SHELL_OPEN_ZIPFILE;
|
||||
}else if( n==0 && dfltZip && sqlite3_strlike("%.zip",zName,0)==0 ){
|
||||
return SHELL_OPEN_ZIPFILE;
|
||||
rc = SHELL_OPEN_ZIPFILE;
|
||||
}
|
||||
}
|
||||
fclose(f);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Flags for open_db().
|
||||
**
|
||||
** The default behavior of open_db() is to exit(1) if the database fails to
|
||||
** open. The OPEN_DB_KEEPALIVE flag changes that so that it prints an error
|
||||
** but still returns without calling exit.
|
||||
**
|
||||
** The OPEN_DB_ZIPFILE flag causes open_db() to prefer to open files as a
|
||||
** ZIP archive if the file does not exist or is empty and its name matches
|
||||
** the *.zip pattern.
|
||||
*/
|
||||
#define OPEN_DB_KEEPALIVE 0x001 /* Return after error if true */
|
||||
#define OPEN_DB_ZIPFILE 0x002 /* Open as ZIP if name matches *.zip */
|
||||
|
||||
/*
|
||||
** Make sure the database is open. If it is not, then open it. If
|
||||
** the database fails to open, print an error message and exit.
|
||||
*/
|
||||
static void open_db(ShellState *p, int keepAlive){
|
||||
static void open_db(ShellState *p, int openFlags){
|
||||
if( p->db==0 ){
|
||||
if( p->openMode==SHELL_OPEN_UNSPEC && access(p->zDbFilename,0)==0 ){
|
||||
p->openMode = (u8)deduceDatabaseType(p->zDbFilename, 0);
|
||||
if( p->openMode==SHELL_OPEN_UNSPEC ){
|
||||
if( p->zDbFilename==0 || p->zDbFilename[0]==0 ){
|
||||
p->openMode = SHELL_OPEN_NORMAL;
|
||||
}else{
|
||||
p->openMode = (u8)deduceDatabaseType(p->zDbFilename,
|
||||
(openFlags & OPEN_DB_ZIPFILE)!=0);
|
||||
}
|
||||
}
|
||||
switch( p->openMode ){
|
||||
case SHELL_OPEN_APPENDVFS: {
|
||||
@@ -3600,7 +3622,7 @@ static void open_db(ShellState *p, int keepAlive){
|
||||
if( p->db==0 || SQLITE_OK!=sqlite3_errcode(p->db) ){
|
||||
utf8_printf(stderr,"Error: unable to open database \"%s\": %s\n",
|
||||
p->zDbFilename, sqlite3_errmsg(p->db));
|
||||
if( keepAlive ) return;
|
||||
if( openFlags & OPEN_DB_KEEPALIVE ) return;
|
||||
exit(1);
|
||||
}
|
||||
#ifndef SQLITE_OMIT_LOAD_EXTENSION
|
||||
@@ -3634,6 +3656,17 @@ static void open_db(ShellState *p, int keepAlive){
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Attempt to close the databaes connection. Report errors.
|
||||
*/
|
||||
void close_db(sqlite3 *db){
|
||||
int rc = sqlite3_close(db);
|
||||
if( rc ){
|
||||
utf8_printf(stderr, "Error: sqlite3_close() returns %d: %s\n",
|
||||
rc, sqlite3_errmsg(db));
|
||||
}
|
||||
}
|
||||
|
||||
#if HAVE_READLINE || HAVE_EDITLINE
|
||||
/*
|
||||
** Readline completion callbacks
|
||||
@@ -3675,7 +3708,7 @@ static void linenoise_completion(const char *zLine, linenoiseCompletions *lc){
|
||||
char zBuf[1000];
|
||||
|
||||
if( nLine>sizeof(zBuf)-30 ) return;
|
||||
if( zLine[0]=='.' ) return;
|
||||
if( zLine[0]=='.' || zLine[0]=='#') return;
|
||||
for(i=nLine-1; i>=0 && (isalnum(zLine[i]) || zLine[i]=='_'); i--){}
|
||||
if( i==nLine-1 ) return;
|
||||
iStart = i+1;
|
||||
@@ -4214,7 +4247,7 @@ static void tryToClone(ShellState *p, const char *zNewDb){
|
||||
sqlite3_exec(newDb, "COMMIT;", 0, 0, 0);
|
||||
sqlite3_exec(p->db, "PRAGMA writable_schema=OFF;", 0, 0, 0);
|
||||
}
|
||||
sqlite3_close(newDb);
|
||||
close_db(newDb);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -4869,6 +4902,7 @@ struct ArCommand {
|
||||
u8 bZip; /* True if the archive is a ZIP */
|
||||
u8 bDryRun; /* True if --dry-run */
|
||||
u8 bAppend; /* True if --append */
|
||||
u8 fromCmdLine; /* Run from -A instead of .archive */
|
||||
int nArg; /* Number of command arguments */
|
||||
char *zSrcTable; /* "sqlar", "zipfile($file)" or "zip" */
|
||||
const char *zFile; /* --file argument, or NULL */
|
||||
@@ -4915,13 +4949,18 @@ static int arUsage(FILE *f){
|
||||
** Print an error message for the .ar command to stderr and return
|
||||
** SQLITE_ERROR.
|
||||
*/
|
||||
static int arErrorMsg(const char *zFmt, ...){
|
||||
static int arErrorMsg(ArCommand *pAr, const char *zFmt, ...){
|
||||
va_list ap;
|
||||
char *z;
|
||||
va_start(ap, zFmt);
|
||||
z = sqlite3_vmprintf(zFmt, ap);
|
||||
va_end(ap);
|
||||
raw_printf(stderr, "Error: %s (try \".ar --help\")\n", z);
|
||||
utf8_printf(stderr, "Error: %s\n", z);
|
||||
if( pAr->fromCmdLine ){
|
||||
utf8_printf(stderr, "Use \"-A\" for more help\n");
|
||||
}else{
|
||||
utf8_printf(stderr, "Use \".archive --help\" for more help\n");
|
||||
}
|
||||
sqlite3_free(z);
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
@@ -4952,7 +4991,7 @@ static int arProcessSwitch(ArCommand *pAr, int eSwitch, const char *zArg){
|
||||
case AR_CMD_UPDATE:
|
||||
case AR_CMD_HELP:
|
||||
if( pAr->eCmd ){
|
||||
return arErrorMsg("multiple command options");
|
||||
return arErrorMsg(pAr, "multiple command options");
|
||||
}
|
||||
pAr->eCmd = eSwitch;
|
||||
break;
|
||||
@@ -5012,8 +5051,6 @@ static int arParseCommand(
|
||||
return arUsage(stderr);
|
||||
}else{
|
||||
char *z = azArg[1];
|
||||
memset(pAr, 0, sizeof(ArCommand));
|
||||
|
||||
if( z[0]!='-' ){
|
||||
/* Traditional style [tar] invocation */
|
||||
int i;
|
||||
@@ -5025,11 +5062,11 @@ static int arParseCommand(
|
||||
if( z[i]==pOpt->cShort ) break;
|
||||
}
|
||||
if( pOpt==pEnd ){
|
||||
return arErrorMsg("unrecognized option: %c", z[i]);
|
||||
return arErrorMsg(pAr, "unrecognized option: %c", z[i]);
|
||||
}
|
||||
if( pOpt->bArg ){
|
||||
if( iArg>=nArg ){
|
||||
return arErrorMsg("option requires an argument: %c",z[i]);
|
||||
return arErrorMsg(pAr, "option requires an argument: %c",z[i]);
|
||||
}
|
||||
zArg = azArg[iArg++];
|
||||
}
|
||||
@@ -5063,7 +5100,7 @@ static int arParseCommand(
|
||||
if( z[i]==pOpt->cShort ) break;
|
||||
}
|
||||
if( pOpt==pEnd ){
|
||||
return arErrorMsg("unrecognized option: %c\n", z[i]);
|
||||
return arErrorMsg(pAr, "unrecognized option: %c", z[i]);
|
||||
}
|
||||
if( pOpt->bArg ){
|
||||
if( i<(n-1) ){
|
||||
@@ -5071,7 +5108,7 @@ static int arParseCommand(
|
||||
i = n;
|
||||
}else{
|
||||
if( iArg>=(nArg-1) ){
|
||||
return arErrorMsg("option requires an argument: %c\n",z[i]);
|
||||
return arErrorMsg(pAr, "option requires an argument: %c",z[i]);
|
||||
}
|
||||
zArg = azArg[++iArg];
|
||||
}
|
||||
@@ -5093,7 +5130,7 @@ static int arParseCommand(
|
||||
const char *zLong = pOpt->zLong;
|
||||
if( (n-2)<=strlen30(zLong) && 0==memcmp(&z[2], zLong, n-2) ){
|
||||
if( pMatch ){
|
||||
return arErrorMsg("ambiguous option: %s",z);
|
||||
return arErrorMsg(pAr, "ambiguous option: %s",z);
|
||||
}else{
|
||||
pMatch = pOpt;
|
||||
}
|
||||
@@ -5101,11 +5138,11 @@ static int arParseCommand(
|
||||
}
|
||||
|
||||
if( pMatch==0 ){
|
||||
return arErrorMsg("unrecognized option: %s", z);
|
||||
return arErrorMsg(pAr, "unrecognized option: %s", z);
|
||||
}
|
||||
if( pMatch->bArg ){
|
||||
if( iArg>=(nArg-1) ){
|
||||
return arErrorMsg("option requires an argument: %s", z);
|
||||
return arErrorMsg(pAr, "option requires an argument: %s", z);
|
||||
}
|
||||
zArg = azArg[++iArg];
|
||||
}
|
||||
@@ -5234,6 +5271,7 @@ static int arListCommand(ArCommand *pAr){
|
||||
}
|
||||
}
|
||||
shellFinalize(&rc, pSql);
|
||||
sqlite3_free(zWhere);
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -5436,12 +5474,14 @@ end_ar_transaction:
|
||||
*/
|
||||
static int arDotCommand(
|
||||
ShellState *pState, /* Current shell tool state */
|
||||
int fromCmdLine, /* True if -A command-line option, not .ar cmd */
|
||||
char **azArg, /* Array of arguments passed to dot command */
|
||||
int nArg /* Number of entries in azArg[] */
|
||||
){
|
||||
ArCommand cmd;
|
||||
int rc;
|
||||
memset(&cmd, 0, sizeof(cmd));
|
||||
cmd.fromCmdLine = fromCmdLine;
|
||||
rc = arParseCommand(azArg, nArg, &cmd);
|
||||
if( rc==SQLITE_OK ){
|
||||
int eDbType = SHELL_OPEN_UNSPEC;
|
||||
@@ -5488,7 +5528,7 @@ static int arDotCommand(
|
||||
shellPutsFunc, 0, 0);
|
||||
|
||||
}
|
||||
if( cmd.zSrcTable==0 && cmd.bZip==0 ){
|
||||
if( cmd.zSrcTable==0 && cmd.bZip==0 && cmd.eCmd!=AR_CMD_HELP ){
|
||||
if( cmd.eCmd!=AR_CMD_CREATE
|
||||
&& sqlite3_table_column_metadata(cmd.db,0,"sqlar","name",0,0,0,0,0)
|
||||
){
|
||||
@@ -5524,7 +5564,7 @@ static int arDotCommand(
|
||||
}
|
||||
end_ar_command:
|
||||
if( cmd.db!=pState->db ){
|
||||
sqlite3_close(cmd.db);
|
||||
close_db(cmd.db);
|
||||
}
|
||||
sqlite3_free(cmd.zSrcTable);
|
||||
|
||||
@@ -5604,7 +5644,7 @@ static int do_meta_command(char *zLine, ShellState *p){
|
||||
#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_HAVE_ZLIB)
|
||||
if( c=='a' && strncmp(azArg[0], "archive", n)==0 ){
|
||||
open_db(p, 0);
|
||||
rc = arDotCommand(p, azArg, nArg);
|
||||
rc = arDotCommand(p, 0, azArg, nArg);
|
||||
}else
|
||||
#endif
|
||||
|
||||
@@ -5616,11 +5656,14 @@ static int do_meta_command(char *zLine, ShellState *p){
|
||||
sqlite3 *pDest;
|
||||
sqlite3_backup *pBackup;
|
||||
int j;
|
||||
const char *zVfs = 0;
|
||||
for(j=1; j<nArg; j++){
|
||||
const char *z = azArg[j];
|
||||
if( z[0]=='-' ){
|
||||
while( z[0]=='-' ) z++;
|
||||
/* No options to process at this time */
|
||||
if( z[1]=='-' ) z++;
|
||||
if( strcmp(z, "-append")==0 ){
|
||||
zVfs = "apndvfs";
|
||||
}else
|
||||
{
|
||||
utf8_printf(stderr, "unknown option: %s\n", azArg[j]);
|
||||
return 1;
|
||||
@@ -5631,7 +5674,7 @@ static int do_meta_command(char *zLine, ShellState *p){
|
||||
zDb = zDestFile;
|
||||
zDestFile = azArg[j];
|
||||
}else{
|
||||
raw_printf(stderr, "too many arguments to .backup\n");
|
||||
raw_printf(stderr, "Usage: .backup ?DB? ?--append? FILENAME\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@@ -5640,17 +5683,18 @@ static int do_meta_command(char *zLine, ShellState *p){
|
||||
return 1;
|
||||
}
|
||||
if( zDb==0 ) zDb = "main";
|
||||
rc = sqlite3_open(zDestFile, &pDest);
|
||||
rc = sqlite3_open_v2(zDestFile, &pDest,
|
||||
SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE, zVfs);
|
||||
if( rc!=SQLITE_OK ){
|
||||
utf8_printf(stderr, "Error: cannot open \"%s\"\n", zDestFile);
|
||||
sqlite3_close(pDest);
|
||||
close_db(pDest);
|
||||
return 1;
|
||||
}
|
||||
open_db(p, 0);
|
||||
pBackup = sqlite3_backup_init(pDest, "main", p->db, zDb);
|
||||
if( pBackup==0 ){
|
||||
utf8_printf(stderr, "Error: %s\n", sqlite3_errmsg(pDest));
|
||||
sqlite3_close(pDest);
|
||||
close_db(pDest);
|
||||
return 1;
|
||||
}
|
||||
while( (rc = sqlite3_backup_step(pBackup,100))==SQLITE_OK ){}
|
||||
@@ -5661,7 +5705,7 @@ static int do_meta_command(char *zLine, ShellState *p){
|
||||
utf8_printf(stderr, "Error: %s\n", sqlite3_errmsg(pDest));
|
||||
rc = 1;
|
||||
}
|
||||
sqlite3_close(pDest);
|
||||
close_db(pDest);
|
||||
}else
|
||||
|
||||
if( c=='b' && n>=3 && strncmp(azArg[0], "bail", n)==0 ){
|
||||
@@ -6000,11 +6044,11 @@ static int do_meta_command(char *zLine, ShellState *p){
|
||||
callback, &data, &zErrMsg);
|
||||
data.cMode = data.mode = MODE_Insert;
|
||||
data.zDestTable = "sqlite_stat1";
|
||||
shell_exec(p, "SELECT * FROM sqlite_stat1", &zErrMsg);
|
||||
shell_exec(&data, "SELECT * FROM sqlite_stat1", &zErrMsg);
|
||||
data.zDestTable = "sqlite_stat3";
|
||||
shell_exec(p, "SELECT * FROM sqlite_stat3", &zErrMsg);
|
||||
shell_exec(&data, "SELECT * FROM sqlite_stat3", &zErrMsg);
|
||||
data.zDestTable = "sqlite_stat4";
|
||||
shell_exec(p, "SELECT * FROM sqlite_stat4", &zErrMsg);
|
||||
shell_exec(&data, "SELECT * FROM sqlite_stat4", &zErrMsg);
|
||||
raw_printf(p->out, "ANALYZE sqlite_master;\n");
|
||||
}
|
||||
}else
|
||||
@@ -6477,7 +6521,7 @@ static int do_meta_command(char *zLine, ShellState *p){
|
||||
int newFlag = 0; /* True to delete file before opening */
|
||||
/* Close the existing database */
|
||||
session_close_all(p);
|
||||
sqlite3_close(p->db);
|
||||
close_db(p->db);
|
||||
p->db = 0;
|
||||
p->zDbFilename = 0;
|
||||
sqlite3_free(p->zFreeOnClose);
|
||||
@@ -6507,7 +6551,7 @@ static int do_meta_command(char *zLine, ShellState *p){
|
||||
if( zNewFilename ){
|
||||
if( newFlag ) shellDeleteFile(zNewFilename);
|
||||
p->zDbFilename = zNewFilename;
|
||||
open_db(p, 1);
|
||||
open_db(p, OPEN_DB_KEEPALIVE);
|
||||
if( p->db==0 ){
|
||||
utf8_printf(stderr, "Error: cannot open '%s'\n", zNewFilename);
|
||||
sqlite3_free(zNewFilename);
|
||||
@@ -6657,14 +6701,14 @@ static int do_meta_command(char *zLine, ShellState *p){
|
||||
rc = sqlite3_open(zSrcFile, &pSrc);
|
||||
if( rc!=SQLITE_OK ){
|
||||
utf8_printf(stderr, "Error: cannot open \"%s\"\n", zSrcFile);
|
||||
sqlite3_close(pSrc);
|
||||
close_db(pSrc);
|
||||
return 1;
|
||||
}
|
||||
open_db(p, 0);
|
||||
pBackup = sqlite3_backup_init(p->db, zDb, pSrc, "main");
|
||||
if( pBackup==0 ){
|
||||
utf8_printf(stderr, "Error: %s\n", sqlite3_errmsg(p->db));
|
||||
sqlite3_close(pSrc);
|
||||
close_db(pSrc);
|
||||
return 1;
|
||||
}
|
||||
while( (rc = sqlite3_backup_step(pBackup,100))==SQLITE_OK
|
||||
@@ -6684,7 +6728,7 @@ static int do_meta_command(char *zLine, ShellState *p){
|
||||
utf8_printf(stderr, "Error: %s\n", sqlite3_errmsg(p->db));
|
||||
rc = 1;
|
||||
}
|
||||
sqlite3_close(pSrc);
|
||||
close_db(pSrc);
|
||||
}else
|
||||
|
||||
if( c=='s' && strncmp(azArg[0], "scanstats", n)==0 ){
|
||||
@@ -7204,7 +7248,7 @@ static int do_meta_command(char *zLine, ShellState *p){
|
||||
utf8_printf(stderr, "Unknown option \"%s\" on \"%s\"\n",
|
||||
azArg[i], azArg[0]);
|
||||
raw_printf(stderr, "Should be one of: --schema"
|
||||
" --sha3-224 --sha3-255 --sha3-384 --sha3-512\n");
|
||||
" --sha3-224 --sha3-256 --sha3-384 --sha3-512\n");
|
||||
rc = 1;
|
||||
goto meta_command_exit;
|
||||
}
|
||||
@@ -7368,7 +7412,10 @@ static int do_meta_command(char *zLine, ShellState *p){
|
||||
initText(&s);
|
||||
open_db(p, 0);
|
||||
rc = sqlite3_prepare_v2(p->db, "PRAGMA database_list", -1, &pStmt, 0);
|
||||
if( rc ) return shellDatabaseError(p->db);
|
||||
if( rc ){
|
||||
sqlite3_finalize(pStmt);
|
||||
return shellDatabaseError(p->db);
|
||||
}
|
||||
|
||||
if( nArg>2 && c=='i' ){
|
||||
/* It is an historical accident that the .indexes command shows an error
|
||||
@@ -7376,6 +7423,7 @@ static int do_meta_command(char *zLine, ShellState *p){
|
||||
** command does not. */
|
||||
raw_printf(stderr, "Usage: .indexes ?LIKE-PATTERN?\n");
|
||||
rc = 1;
|
||||
sqlite3_finalize(pStmt);
|
||||
goto meta_command_exit;
|
||||
}
|
||||
for(ii=0; sqlite3_step(pStmt)==SQLITE_ROW; ii++){
|
||||
@@ -7892,7 +7940,7 @@ static int line_is_complete(char *zSql, int nSql){
|
||||
}
|
||||
|
||||
/*
|
||||
** Run a single line of SQL
|
||||
** Run a single line of SQL. Return the number of errors.
|
||||
*/
|
||||
static int runOneSqlLine(ShellState *p, char *zSql, FILE *in, int startline){
|
||||
int rc;
|
||||
@@ -7965,13 +8013,15 @@ static int process_input(ShellState *p, FILE *in){
|
||||
if( ShellHasFlag(p, SHFLG_Echo) ) printf("%s\n", zLine);
|
||||
continue;
|
||||
}
|
||||
if( zLine && zLine[0]=='.' && nSql==0 ){
|
||||
if( zLine && (zLine[0]=='.' || zLine[0]=='#') && nSql==0 ){
|
||||
if( ShellHasFlag(p, SHFLG_Echo) ) printf("%s\n", zLine);
|
||||
rc = do_meta_command(zLine, p);
|
||||
if( rc==2 ){ /* exit requested */
|
||||
break;
|
||||
}else if( rc ){
|
||||
errCnt++;
|
||||
if( zLine[0]=='.' ){
|
||||
rc = do_meta_command(zLine, p);
|
||||
if( rc==2 ){ /* exit requested */
|
||||
break;
|
||||
}else if( rc ){
|
||||
errCnt++;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
@@ -8013,7 +8063,7 @@ static int process_input(ShellState *p, FILE *in){
|
||||
}
|
||||
}
|
||||
if( nSql && !_all_whitespace(zSql) ){
|
||||
runOneSqlLine(p, zSql, in, startline);
|
||||
errCnt += runOneSqlLine(p, zSql, in, startline);
|
||||
}
|
||||
free(zSql);
|
||||
free(zLine);
|
||||
@@ -8274,6 +8324,10 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
|
||||
int nCmd = 0;
|
||||
char **azCmd = 0;
|
||||
const char *zVfs = 0; /* Value of -vfs command-line option */
|
||||
#if !SQLITE_SHELL_IS_UTF8
|
||||
char **argvToFree = 0;
|
||||
int argcToFree = 0;
|
||||
#endif
|
||||
|
||||
setBinaryMode(stdin, 0);
|
||||
setvbuf(stderr, 0, _IONBF, 0); /* Make sure stderr is unbuffered */
|
||||
@@ -8297,7 +8351,9 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
|
||||
*/
|
||||
#if !SQLITE_SHELL_IS_UTF8
|
||||
sqlite3_initialize();
|
||||
argv = malloc(sizeof(argv[0])*argc);
|
||||
argvToFree = malloc(sizeof(argv[0])*argc*2);
|
||||
argcToFree = argc;
|
||||
argv = argvToFree + argc;
|
||||
if( argv==0 ) shell_out_of_memory();
|
||||
for(i=0; i<argc; i++){
|
||||
char *z = sqlite3_win32_unicode_to_utf8(wargv[i]);
|
||||
@@ -8307,6 +8363,7 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
|
||||
argv[i] = malloc( n+1 );
|
||||
if( argv[i]==0 ) shell_out_of_memory();
|
||||
memcpy(argv[i], z, n+1);
|
||||
argvToFree[i] = argv[i];
|
||||
sqlite3_free(z);
|
||||
}
|
||||
sqlite3_shutdown();
|
||||
@@ -8628,12 +8685,12 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
|
||||
" with \"%s\"\n", z);
|
||||
return 1;
|
||||
}
|
||||
open_db(&data, 0);
|
||||
open_db(&data, OPEN_DB_ZIPFILE);
|
||||
if( z[2] ){
|
||||
argv[i] = &z[2];
|
||||
arDotCommand(&data, argv+(i-1), argc-(i-1));
|
||||
arDotCommand(&data, 1, argv+(i-1), argc-(i-1));
|
||||
}else{
|
||||
arDotCommand(&data, argv+i, argc-i);
|
||||
arDotCommand(&data, 1, argv+i, argc-i);
|
||||
}
|
||||
readStdin = 0;
|
||||
break;
|
||||
@@ -8712,7 +8769,7 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
|
||||
set_table_name(&data, 0);
|
||||
if( data.db ){
|
||||
session_close_all(&data);
|
||||
sqlite3_close(data.db);
|
||||
close_db(data.db);
|
||||
}
|
||||
sqlite3_free(data.zFreeOnClose);
|
||||
find_home_dir(1);
|
||||
@@ -8720,8 +8777,11 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
|
||||
data.doXdgOpen = 0;
|
||||
clearTempFile(&data);
|
||||
#if !SQLITE_SHELL_IS_UTF8
|
||||
for(i=0; i<argc; i++) free(argv[i]);
|
||||
free(argv);
|
||||
for(i=0; i<argcToFree; i++) free(argvToFree[i]);
|
||||
free(argvToFree);
|
||||
#endif
|
||||
/* Clear the global data structure so that valgrind will detect memory
|
||||
** leaks */
|
||||
memset(&data, 0, sizeof(data));
|
||||
return rc;
|
||||
}
|
||||
|
||||
+138
-4
@@ -504,6 +504,7 @@ int sqlite3_exec(
|
||||
#define SQLITE_IOERR_COMMIT_ATOMIC (SQLITE_IOERR | (30<<8))
|
||||
#define SQLITE_IOERR_ROLLBACK_ATOMIC (SQLITE_IOERR | (31<<8))
|
||||
#define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8))
|
||||
#define SQLITE_LOCKED_VTAB (SQLITE_LOCKED | (2<<8))
|
||||
#define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8))
|
||||
#define SQLITE_BUSY_SNAPSHOT (SQLITE_BUSY | (2<<8))
|
||||
#define SQLITE_CANTOPEN_NOTEMPDIR (SQLITE_CANTOPEN | (1<<8))
|
||||
@@ -511,6 +512,7 @@ int sqlite3_exec(
|
||||
#define SQLITE_CANTOPEN_FULLPATH (SQLITE_CANTOPEN | (3<<8))
|
||||
#define SQLITE_CANTOPEN_CONVPATH (SQLITE_CANTOPEN | (4<<8))
|
||||
#define SQLITE_CORRUPT_VTAB (SQLITE_CORRUPT | (1<<8))
|
||||
#define SQLITE_CORRUPT_SEQUENCE (SQLITE_CORRUPT | (2<<8))
|
||||
#define SQLITE_READONLY_RECOVERY (SQLITE_READONLY | (1<<8))
|
||||
#define SQLITE_READONLY_CANTLOCK (SQLITE_READONLY | (2<<8))
|
||||
#define SQLITE_READONLY_ROLLBACK (SQLITE_READONLY | (3<<8))
|
||||
@@ -7113,7 +7115,7 @@ int sqlite3_test_control(int op, ...);
|
||||
** using keywords as identifiers. Common techniques used to avoid keyword
|
||||
** name collisions include:
|
||||
** <ul>
|
||||
** <li> Put all indentifier names inside double-quotes. This is the official
|
||||
** <li> Put all identifier names inside double-quotes. This is the official
|
||||
** SQL way to escape identifier names.
|
||||
** <li> Put identifier names inside [...]. This is not standard SQL,
|
||||
** but it is what SQL Server does and so lots of programmers use this
|
||||
@@ -7132,6 +7134,138 @@ int sqlite3_keyword_count(void);
|
||||
int sqlite3_keyword_name(int,const char**,int*);
|
||||
int sqlite3_keyword_check(const char*,int);
|
||||
|
||||
/*
|
||||
** CAPI3REF: Dynamic String Object
|
||||
** KEYWORDS: {dynamic string}
|
||||
**
|
||||
** An instance of the sqlite3_str object contains a dynamically-sized
|
||||
** string under construction.
|
||||
**
|
||||
** The lifecycle of an sqlite3_str object is as follows:
|
||||
** <ol>
|
||||
** <li> ^The sqlite3_str object is created using [sqlite3_str_new()].
|
||||
** <li> ^Text is appended to the sqlite3_str object using various
|
||||
** methods, such as [sqlite3_str_appendf()].
|
||||
** <li> ^The sqlite3_str object is destroyed and the string it created
|
||||
** is returned using the [sqlite3_str_finish()] interface.
|
||||
** </ol>
|
||||
*/
|
||||
typedef struct sqlite3_str sqlite3_str;
|
||||
|
||||
/*
|
||||
** CAPI3REF: Create A New Dynamic String Object
|
||||
** CONSTRUCTOR: sqlite3_str
|
||||
**
|
||||
** ^The [sqlite3_str_new(D)] interface allocates and initializes
|
||||
** a new [sqlite3_str] object. To avoid memory leaks, the object returned by
|
||||
** [sqlite3_str_new()] must be freed by a subsequent call to
|
||||
** [sqlite3_str_finish(X)].
|
||||
**
|
||||
** ^The [sqlite3_str_new(D)] interface always returns a pointer to a
|
||||
** valid [sqlite3_str] object, though in the event of an out-of-memory
|
||||
** error the returned object might be a special singleton that will
|
||||
** silently reject new text, always return SQLITE_NOMEM from
|
||||
** [sqlite3_str_errcode()], always return 0 for
|
||||
** [sqlite3_str_length()], and always return NULL from
|
||||
** [sqlite3_str_finish(X)]. It is always safe to use the value
|
||||
** returned by [sqlite3_str_new(D)] as the sqlite3_str parameter
|
||||
** to any of the other [sqlite3_str] methods.
|
||||
**
|
||||
** The D parameter to [sqlite3_str_new(D)] may be NULL. If the
|
||||
** D parameter in [sqlite3_str_new(D)] is not NULL, then the maximum
|
||||
** length of the string contained in the [sqlite3_str] object will be
|
||||
** the value set for [sqlite3_limit](D,[SQLITE_LIMIT_LENGTH]) instead
|
||||
** of [SQLITE_MAX_LENGTH].
|
||||
*/
|
||||
sqlite3_str *sqlite3_str_new(sqlite3*);
|
||||
|
||||
/*
|
||||
** CAPI3REF: Finalize A Dynamic String
|
||||
** DESTRUCTOR: sqlite3_str
|
||||
**
|
||||
** ^The [sqlite3_str_finish(X)] interface destroys the sqlite3_str object X
|
||||
** and returns a pointer to a memory buffer obtained from [sqlite3_malloc64()]
|
||||
** that contains the constructed string. The calling application should
|
||||
** pass the returned value to [sqlite3_free()] to avoid a memory leak.
|
||||
** ^The [sqlite3_str_finish(X)] interface may return a NULL pointer if any
|
||||
** errors were encountered during construction of the string. ^The
|
||||
** [sqlite3_str_finish(X)] interface will also return a NULL pointer if the
|
||||
** string in [sqlite3_str] object X is zero bytes long.
|
||||
*/
|
||||
char *sqlite3_str_finish(sqlite3_str*);
|
||||
|
||||
/*
|
||||
** CAPI3REF: Add Content To A Dynamic String
|
||||
** METHOD: sqlite3_str
|
||||
**
|
||||
** These interfaces add content to an sqlite3_str object previously obtained
|
||||
** from [sqlite3_str_new()].
|
||||
**
|
||||
** ^The [sqlite3_str_appendf(X,F,...)] and
|
||||
** [sqlite3_str_vappendf(X,F,V)] interfaces uses the [built-in printf]
|
||||
** functionality of SQLite to append formatted text onto the end of
|
||||
** [sqlite3_str] object X.
|
||||
**
|
||||
** ^The [sqlite3_str_append(X,S,N)] method appends exactly N bytes from string S
|
||||
** onto the end of the [sqlite3_str] object X. N must be non-negative.
|
||||
** S must contain at least N non-zero bytes of content. To append a
|
||||
** zero-terminated string in its entirety, use the [sqlite3_str_appendall()]
|
||||
** method instead.
|
||||
**
|
||||
** ^The [sqlite3_str_appendall(X,S)] method appends the complete content of
|
||||
** zero-terminated string S onto the end of [sqlite3_str] object X.
|
||||
**
|
||||
** ^The [sqlite3_str_appendchar(X,N,C)] method appends N copies of the
|
||||
** single-byte character C onto the end of [sqlite3_str] object X.
|
||||
** ^This method can be used, for example, to add whitespace indentation.
|
||||
**
|
||||
** ^The [sqlite3_str_reset(X)] method resets the string under construction
|
||||
** inside [sqlite3_str] object X back to zero bytes in length.
|
||||
**
|
||||
** These methods do not return a result code. ^If an error occurs, that fact
|
||||
** is recorded in the [sqlite3_str] object and can be recovered by a
|
||||
** subsequent call to [sqlite3_str_errcode(X)].
|
||||
*/
|
||||
void sqlite3_str_appendf(sqlite3_str*, const char *zFormat, ...);
|
||||
void sqlite3_str_vappendf(sqlite3_str*, const char *zFormat, va_list);
|
||||
void sqlite3_str_append(sqlite3_str*, const char *zIn, int N);
|
||||
void sqlite3_str_appendall(sqlite3_str*, const char *zIn);
|
||||
void sqlite3_str_appendchar(sqlite3_str*, int N, char C);
|
||||
void sqlite3_str_reset(sqlite3_str*);
|
||||
|
||||
/*
|
||||
** CAPI3REF: Status Of A Dynamic String
|
||||
** METHOD: sqlite3_str
|
||||
**
|
||||
** These interfaces return the current status of an [sqlite3_str] object.
|
||||
**
|
||||
** ^If any prior errors have occurred while constructing the dynamic string
|
||||
** in sqlite3_str X, then the [sqlite3_str_errcode(X)] method will return
|
||||
** an appropriate error code. ^The [sqlite3_str_errcode(X)] method returns
|
||||
** [SQLITE_NOMEM] following any out-of-memory error, or
|
||||
** [SQLITE_TOOBIG] if the size of the dynamic string exceeds
|
||||
** [SQLITE_MAX_LENGTH], or [SQLITE_OK] if there have been no errors.
|
||||
**
|
||||
** ^The [sqlite3_str_length(X)] method returns the current length, in bytes,
|
||||
** of the dynamic string under construction in [sqlite3_str] object X.
|
||||
** ^The length returned by [sqlite3_str_length(X)] does not include the
|
||||
** zero-termination byte.
|
||||
**
|
||||
** ^The [sqlite3_str_value(X)] method returns a pointer to the current
|
||||
** content of the dynamic string under construction in X. The value
|
||||
** returned by [sqlite3_str_value(X)] is managed by the sqlite3_str object X
|
||||
** and might be freed or altered by any subsequent method on the same
|
||||
** [sqlite3_str] object. Applications must not used the pointer returned
|
||||
** [sqlite3_str_value(X)] after any subsequent method call on the same
|
||||
** object. ^Applications may change the content of the string returned
|
||||
** by [sqlite3_str_value(X)] as long as they do not write into any bytes
|
||||
** outside the range of 0 to [sqlite3_str_length(X)] and do not read or
|
||||
** write any byte after any subsequent sqlite3_str method call.
|
||||
*/
|
||||
int sqlite3_str_errcode(sqlite3_str*);
|
||||
int sqlite3_str_length(sqlite3_str*);
|
||||
char *sqlite3_str_value(sqlite3_str*);
|
||||
|
||||
/*
|
||||
** CAPI3REF: SQLite Runtime Status
|
||||
**
|
||||
@@ -8401,11 +8535,11 @@ int sqlite3_vtab_on_conflict(sqlite3 *);
|
||||
** method of a [virtual table], then it returns true if and only if the
|
||||
** column is being fetched as part of an UPDATE operation during which the
|
||||
** column value will not change. Applications might use this to substitute
|
||||
** a lighter-weight value to return that the corresponding [xUpdate] method
|
||||
** understands as a "no-change" value.
|
||||
** a return value that is less expensive to compute and that the corresponding
|
||||
** [xUpdate] method understands as a "no-change" value.
|
||||
**
|
||||
** If the [xColumn] method calls sqlite3_vtab_nochange() and finds that
|
||||
** the column is not changed by the UPDATE statement, they the xColumn
|
||||
** the column is not changed by the UPDATE statement, then the xColumn
|
||||
** method can optionally return without setting a result, without calling
|
||||
** any of the [sqlite3_result_int|sqlite3_result_xxxxx() interfaces].
|
||||
** In that case, [sqlite3_value_nochange(X)] will return true for the
|
||||
|
||||
@@ -295,6 +295,21 @@ struct sqlite3_api_routines {
|
||||
int (*vtab_nochange)(sqlite3_context*);
|
||||
int (*value_nochange)(sqlite3_value*);
|
||||
const char *(*vtab_collation)(sqlite3_index_info*,int);
|
||||
/* Version 3.24.0 and later */
|
||||
int (*keyword_count)(void);
|
||||
int (*keyword_name)(int,const char**,int*);
|
||||
int (*keyword_check)(const char*,int);
|
||||
sqlite3_str *(*str_new)(sqlite3*);
|
||||
char *(*str_finish)(sqlite3_str*);
|
||||
void (*str_appendf)(sqlite3_str*, const char *zFormat, ...);
|
||||
void (*str_vappendf)(sqlite3_str*, const char *zFormat, va_list);
|
||||
void (*str_append)(sqlite3_str*, const char *zIn, int N);
|
||||
void (*str_appendall)(sqlite3_str*, const char *zIn);
|
||||
void (*str_appendchar)(sqlite3_str*, int N, char C);
|
||||
void (*str_reset)(sqlite3_str*);
|
||||
int (*str_errcode)(sqlite3_str*);
|
||||
int (*str_length)(sqlite3_str*);
|
||||
char *(*str_value)(sqlite3_str*);
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -565,6 +580,21 @@ typedef int (*sqlite3_loadext_entry)(
|
||||
#define sqlite3_vtab_nochange sqlite3_api->vtab_nochange
|
||||
#define sqlite3_value_nochange sqlite3_api->value_nochange
|
||||
#define sqlite3_vtab_collation sqlite3_api->vtab_collation
|
||||
/* Version 3.24.0 and later */
|
||||
#define sqlite3_keyword_count sqlite3_api->keyword_count
|
||||
#define sqlite3_keyword_name sqlite3_api->keyword_name
|
||||
#define sqlite3_keyword_check sqlite3_api->keyword_check
|
||||
#define sqlite3_str_new sqlite3_api->str_new
|
||||
#define sqlite3_str_finish sqlite3_api->str_finish
|
||||
#define sqlite3_str_appendf sqlite3_api->str_appendf
|
||||
#define sqlite3_str_vappendf sqlite3_api->str_vappendf
|
||||
#define sqlite3_str_append sqlite3_api->str_append
|
||||
#define sqlite3_str_appendall sqlite3_api->str_appendall
|
||||
#define sqlite3_str_appendchar sqlite3_api->str_appendchar
|
||||
#define sqlite3_str_reset sqlite3_api->str_reset
|
||||
#define sqlite3_str_errcode sqlite3_api->str_errcode
|
||||
#define sqlite3_str_length sqlite3_api->str_length
|
||||
#define sqlite3_str_value sqlite3_api->str_value
|
||||
#endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */
|
||||
|
||||
#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
|
||||
|
||||
+3
-11
@@ -1093,7 +1093,7 @@ typedef struct Select Select;
|
||||
typedef struct SQLiteThread SQLiteThread;
|
||||
typedef struct SelectDest SelectDest;
|
||||
typedef struct SrcList SrcList;
|
||||
typedef struct StrAccum StrAccum;
|
||||
typedef struct sqlite3_str StrAccum; /* Internal alias for sqlite3_str */
|
||||
typedef struct Table Table;
|
||||
typedef struct TableLock TableLock;
|
||||
typedef struct Token Token;
|
||||
@@ -3273,17 +3273,15 @@ struct DbFixer {
|
||||
** An objected used to accumulate the text of a string where we
|
||||
** do not necessarily know how big the string will be in the end.
|
||||
*/
|
||||
struct StrAccum {
|
||||
struct sqlite3_str {
|
||||
sqlite3 *db; /* Optional database for lookaside. Can be NULL */
|
||||
char *zText; /* The string collected so far */
|
||||
u32 nAlloc; /* Amount of space allocated in zText */
|
||||
u32 mxAlloc; /* Maximum allowed allocation. 0 for no malloc usage */
|
||||
u32 nChar; /* Length of the string so far */
|
||||
u8 accError; /* STRACCUM_NOMEM or STRACCUM_TOOBIG */
|
||||
u8 accError; /* SQLITE_NOMEM or SQLITE_TOOBIG */
|
||||
u8 printfFlags; /* SQLITE_PRINTF flags below */
|
||||
};
|
||||
#define STRACCUM_NOMEM 1
|
||||
#define STRACCUM_TOOBIG 2
|
||||
#define SQLITE_PRINTF_INTERNAL 0x01 /* Internal-use-only converters allowed */
|
||||
#define SQLITE_PRINTF_SQLFUNC 0x02 /* SQL function arguments to VXPrintf */
|
||||
#define SQLITE_PRINTF_MALLOCED 0x04 /* True if xText is allocated space */
|
||||
@@ -3652,8 +3650,6 @@ struct PrintfArguments {
|
||||
sqlite3_value **apArg; /* The argument values */
|
||||
};
|
||||
|
||||
void sqlite3VXPrintf(StrAccum*, const char*, va_list);
|
||||
void sqlite3XPrintf(StrAccum*, const char*, ...);
|
||||
char *sqlite3MPrintf(sqlite3*,const char*, ...);
|
||||
char *sqlite3VMPrintf(sqlite3*,const char*, va_list);
|
||||
#if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE)
|
||||
@@ -4176,11 +4172,7 @@ int sqlite3ApiExit(sqlite3 *db, int);
|
||||
int sqlite3OpenTempDatabase(Parse *);
|
||||
|
||||
void sqlite3StrAccumInit(StrAccum*, sqlite3*, char*, int, int);
|
||||
void sqlite3StrAccumAppend(StrAccum*,const char*,int);
|
||||
void sqlite3StrAccumAppendAll(StrAccum*,const char*);
|
||||
void sqlite3AppendChar(StrAccum*,int,char);
|
||||
char *sqlite3StrAccumFinish(StrAccum*);
|
||||
void sqlite3StrAccumReset(StrAccum*);
|
||||
void sqlite3SelectDestInit(SelectDest*,int,int);
|
||||
Expr *sqlite3CreateColumnExpr(sqlite3 *, SrcList *, int, int);
|
||||
|
||||
|
||||
@@ -32,6 +32,8 @@ static struct MemFault {
|
||||
int nRepeat; /* Number of times to repeat the failure */
|
||||
int nBenign; /* Number of benign failures seen since last config */
|
||||
int nFail; /* Number of failures seen since last config */
|
||||
int nOkBefore; /* Successful allocations prior to the first fault */
|
||||
int nOkAfter; /* Successful allocations after a fault */
|
||||
u8 enable; /* True if enabled */
|
||||
int isInstalled; /* True if the fault simulation layer is installed */
|
||||
int isBenignMode; /* True if malloc failures are considered benign */
|
||||
@@ -47,18 +49,32 @@ static void sqlite3Fault(void){
|
||||
cnt++;
|
||||
}
|
||||
|
||||
/*
|
||||
** This routine exists as a place to set a breakpoint that will
|
||||
** fire the first time any malloc() fails on a single test case.
|
||||
** The sqlite3Fault() routine above runs on every malloc() failure.
|
||||
** This routine only runs on the first such failure.
|
||||
*/
|
||||
static void sqlite3FirstFault(void){
|
||||
static int cnt2 = 0;
|
||||
cnt2++;
|
||||
}
|
||||
|
||||
/*
|
||||
** Check to see if a fault should be simulated. Return true to simulate
|
||||
** the fault. Return false if the fault should not be simulated.
|
||||
*/
|
||||
static int faultsimStep(void){
|
||||
if( likely(!memfault.enable) ){
|
||||
memfault.nOkAfter++;
|
||||
return 0;
|
||||
}
|
||||
if( memfault.iCountdown>0 ){
|
||||
memfault.iCountdown--;
|
||||
memfault.nOkBefore++;
|
||||
return 0;
|
||||
}
|
||||
if( memfault.nFail==0 ) sqlite3FirstFault();
|
||||
sqlite3Fault();
|
||||
memfault.nFail++;
|
||||
if( memfault.isBenignMode>0 ){
|
||||
@@ -133,6 +149,8 @@ static void faultsimConfig(int nDelay, int nRepeat){
|
||||
memfault.nRepeat = nRepeat;
|
||||
memfault.nBenign = 0;
|
||||
memfault.nFail = 0;
|
||||
memfault.nOkBefore = 0;
|
||||
memfault.nOkAfter = 0;
|
||||
memfault.enable = nDelay>=0;
|
||||
|
||||
/* Sometimes, when running multi-threaded tests, the isBenignMode
|
||||
|
||||
+14
-14
@@ -58,16 +58,16 @@ static void sqlite3TreeViewLine(TreeView *p, const char *zFormat, ...){
|
||||
sqlite3StrAccumInit(&acc, 0, zBuf, sizeof(zBuf), 0);
|
||||
if( p ){
|
||||
for(i=0; i<p->iLevel && i<sizeof(p->bLine)-1; i++){
|
||||
sqlite3StrAccumAppend(&acc, p->bLine[i] ? "| " : " ", 4);
|
||||
sqlite3_str_append(&acc, p->bLine[i] ? "| " : " ", 4);
|
||||
}
|
||||
sqlite3StrAccumAppend(&acc, p->bLine[i] ? "|-- " : "'-- ", 4);
|
||||
sqlite3_str_append(&acc, p->bLine[i] ? "|-- " : "'-- ", 4);
|
||||
}
|
||||
if( zFormat!=0 ){
|
||||
va_start(ap, zFormat);
|
||||
sqlite3VXPrintf(&acc, zFormat, ap);
|
||||
sqlite3_str_vappendf(&acc, zFormat, ap);
|
||||
va_end(ap);
|
||||
assert( acc.nChar>0 );
|
||||
sqlite3StrAccumAppend(&acc, "\n", 1);
|
||||
sqlite3_str_append(&acc, "\n", 1);
|
||||
}
|
||||
sqlite3StrAccumFinish(&acc);
|
||||
fprintf(stdout,"%s", zBuf);
|
||||
@@ -101,17 +101,17 @@ void sqlite3TreeViewWith(TreeView *pView, const With *pWith, u8 moreToFollow){
|
||||
char zLine[1000];
|
||||
const struct Cte *pCte = &pWith->a[i];
|
||||
sqlite3StrAccumInit(&x, 0, zLine, sizeof(zLine), 0);
|
||||
sqlite3XPrintf(&x, "%s", pCte->zName);
|
||||
sqlite3_str_appendf(&x, "%s", pCte->zName);
|
||||
if( pCte->pCols && pCte->pCols->nExpr>0 ){
|
||||
char cSep = '(';
|
||||
int j;
|
||||
for(j=0; j<pCte->pCols->nExpr; j++){
|
||||
sqlite3XPrintf(&x, "%c%s", cSep, pCte->pCols->a[j].zName);
|
||||
sqlite3_str_appendf(&x, "%c%s", cSep, pCte->pCols->a[j].zName);
|
||||
cSep = ',';
|
||||
}
|
||||
sqlite3XPrintf(&x, ")");
|
||||
sqlite3_str_appendf(&x, ")");
|
||||
}
|
||||
sqlite3XPrintf(&x, " AS");
|
||||
sqlite3_str_appendf(&x, " AS");
|
||||
sqlite3StrAccumFinish(&x);
|
||||
sqlite3TreeViewItem(pView, zLine, i<pWith->nCte-1);
|
||||
sqlite3TreeViewSelect(pView, pCte->pSelect, 0);
|
||||
@@ -176,20 +176,20 @@ void sqlite3TreeViewSelect(TreeView *pView, const Select *p, u8 moreToFollow){
|
||||
StrAccum x;
|
||||
char zLine[100];
|
||||
sqlite3StrAccumInit(&x, 0, zLine, sizeof(zLine), 0);
|
||||
sqlite3XPrintf(&x, "{%d,*}", pItem->iCursor);
|
||||
sqlite3_str_appendf(&x, "{%d,*}", pItem->iCursor);
|
||||
if( pItem->zDatabase ){
|
||||
sqlite3XPrintf(&x, " %s.%s", pItem->zDatabase, pItem->zName);
|
||||
sqlite3_str_appendf(&x, " %s.%s", pItem->zDatabase, pItem->zName);
|
||||
}else if( pItem->zName ){
|
||||
sqlite3XPrintf(&x, " %s", pItem->zName);
|
||||
sqlite3_str_appendf(&x, " %s", pItem->zName);
|
||||
}
|
||||
if( pItem->pTab ){
|
||||
sqlite3XPrintf(&x, " tabname=%Q", pItem->pTab->zName);
|
||||
sqlite3_str_appendf(&x, " tabname=%Q", pItem->pTab->zName);
|
||||
}
|
||||
if( pItem->zAlias ){
|
||||
sqlite3XPrintf(&x, " (AS %s)", pItem->zAlias);
|
||||
sqlite3_str_appendf(&x, " (AS %s)", pItem->zAlias);
|
||||
}
|
||||
if( pItem->fg.jointype & JT_LEFT ){
|
||||
sqlite3XPrintf(&x, " LEFT-JOIN");
|
||||
sqlite3_str_appendf(&x, " LEFT-JOIN");
|
||||
}
|
||||
sqlite3StrAccumFinish(&x);
|
||||
sqlite3TreeViewItem(pView, zLine, i<p->pSrc->nSrc-1);
|
||||
|
||||
+9
-5
@@ -845,7 +845,7 @@ static void updateVirtualTable(
|
||||
int regRowid; /* Register for ephem table rowid */
|
||||
int iCsr = pSrc->a[0].iCursor; /* Cursor used for virtual table scan */
|
||||
int aDummy[2]; /* Unused arg for sqlite3WhereOkOnePass() */
|
||||
int bOnePass; /* True to use onepass strategy */
|
||||
int eOnePass; /* True to use onepass strategy */
|
||||
int addr; /* Address of OP_OpenEphemeral */
|
||||
|
||||
/* Allocate nArg registers in which to gather the arguments for VUpdate. Then
|
||||
@@ -890,12 +890,16 @@ static void updateVirtualTable(
|
||||
sqlite3VdbeAddOp2(v, OP_SCopy, regArg+2+iPk, regArg+1);
|
||||
}
|
||||
|
||||
bOnePass = sqlite3WhereOkOnePass(pWInfo, aDummy);
|
||||
eOnePass = sqlite3WhereOkOnePass(pWInfo, aDummy);
|
||||
|
||||
if( bOnePass ){
|
||||
/* There is no ONEPASS_MULTI on virtual tables */
|
||||
assert( eOnePass==ONEPASS_OFF || eOnePass==ONEPASS_SINGLE );
|
||||
|
||||
if( eOnePass ){
|
||||
/* If using the onepass strategy, no-op out the OP_OpenEphemeral coded
|
||||
** above. */
|
||||
sqlite3VdbeChangeToNoop(v, addr);
|
||||
sqlite3VdbeAddOp1(v, OP_Close, iCsr);
|
||||
}else{
|
||||
/* Create a record from the argument register contents and insert it into
|
||||
** the ephemeral table. */
|
||||
@@ -911,7 +915,7 @@ static void updateVirtualTable(
|
||||
}
|
||||
|
||||
|
||||
if( bOnePass==0 ){
|
||||
if( eOnePass==ONEPASS_OFF ){
|
||||
/* End the virtual table scan */
|
||||
sqlite3WhereEnd(pWInfo);
|
||||
|
||||
@@ -931,7 +935,7 @@ static void updateVirtualTable(
|
||||
|
||||
/* End of the ephemeral table scan. Or, if using the onepass strategy,
|
||||
** jump to here if the scan visited zero rows. */
|
||||
if( bOnePass==0 ){
|
||||
if( eOnePass==ONEPASS_OFF ){
|
||||
sqlite3VdbeAddOp2(v, OP_Next, ephemTab, addr+1); VdbeCoverage(v);
|
||||
sqlite3VdbeJumpHere(v, addr);
|
||||
sqlite3VdbeAddOp2(v, OP_Close, ephemTab, 0);
|
||||
|
||||
+2
-5
@@ -4287,11 +4287,8 @@ case OP_NewRowid: { /* out2 */
|
||||
pOut = out2Prerelease(p, pOp);
|
||||
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
|
||||
pC = p->apCsr[pOp->p1];
|
||||
if( !pC->isTable ){
|
||||
rc = SQLITE_CORRUPT_BKPT;
|
||||
goto abort_due_to_error;
|
||||
}
|
||||
assert( pC!=0 );
|
||||
assert( pC->isTable );
|
||||
assert( pC->eCurType==CURTYPE_BTREE );
|
||||
assert( pC->uc.pCursor!=0 );
|
||||
{
|
||||
@@ -6745,7 +6742,7 @@ case OP_VFilter: { /* jump */
|
||||
** If the VColumn opcode is being used to fetch the value of
|
||||
** an unchanging column during an UPDATE operation, then the P5
|
||||
** value is 1. Otherwise, P5 is 0. The P5 value is returned
|
||||
** by sqlite3_vtab_nochange() routine can can be used
|
||||
** by sqlite3_vtab_nochange() routine and can be used
|
||||
** by virtual table implementations to return special "no-change"
|
||||
** marks which can be more efficient, depending on the virtual table.
|
||||
*/
|
||||
|
||||
+29
-31
@@ -1306,23 +1306,23 @@ static void displayP4Expr(StrAccum *p, Expr *pExpr){
|
||||
const char *zOp = 0;
|
||||
switch( pExpr->op ){
|
||||
case TK_STRING:
|
||||
sqlite3XPrintf(p, "%Q", pExpr->u.zToken);
|
||||
sqlite3_str_appendf(p, "%Q", pExpr->u.zToken);
|
||||
break;
|
||||
case TK_INTEGER:
|
||||
sqlite3XPrintf(p, "%d", pExpr->u.iValue);
|
||||
sqlite3_str_appendf(p, "%d", pExpr->u.iValue);
|
||||
break;
|
||||
case TK_NULL:
|
||||
sqlite3XPrintf(p, "NULL");
|
||||
sqlite3_str_appendf(p, "NULL");
|
||||
break;
|
||||
case TK_REGISTER: {
|
||||
sqlite3XPrintf(p, "r[%d]", pExpr->iTable);
|
||||
sqlite3_str_appendf(p, "r[%d]", pExpr->iTable);
|
||||
break;
|
||||
}
|
||||
case TK_COLUMN: {
|
||||
if( pExpr->iColumn<0 ){
|
||||
sqlite3XPrintf(p, "rowid");
|
||||
sqlite3_str_appendf(p, "rowid");
|
||||
}else{
|
||||
sqlite3XPrintf(p, "c%d", (int)pExpr->iColumn);
|
||||
sqlite3_str_appendf(p, "c%d", (int)pExpr->iColumn);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -1354,18 +1354,18 @@ static void displayP4Expr(StrAccum *p, Expr *pExpr){
|
||||
case TK_NOTNULL: zOp = "NOTNULL"; break;
|
||||
|
||||
default:
|
||||
sqlite3XPrintf(p, "%s", "expr");
|
||||
sqlite3_str_appendf(p, "%s", "expr");
|
||||
break;
|
||||
}
|
||||
|
||||
if( zOp ){
|
||||
sqlite3XPrintf(p, "%s(", zOp);
|
||||
sqlite3_str_appendf(p, "%s(", zOp);
|
||||
displayP4Expr(p, pExpr->pLeft);
|
||||
if( pExpr->pRight ){
|
||||
sqlite3StrAccumAppend(p, ",", 1);
|
||||
sqlite3_str_append(p, ",", 1);
|
||||
displayP4Expr(p, pExpr->pRight);
|
||||
}
|
||||
sqlite3StrAccumAppend(p, ")", 1);
|
||||
sqlite3_str_append(p, ")", 1);
|
||||
}
|
||||
}
|
||||
#endif /* VDBE_DISPLAY_P4 && defined(SQLITE_ENABLE_CURSOR_HINTS) */
|
||||
@@ -1386,14 +1386,15 @@ static char *displayP4(Op *pOp, char *zTemp, int nTemp){
|
||||
int j;
|
||||
KeyInfo *pKeyInfo = pOp->p4.pKeyInfo;
|
||||
assert( pKeyInfo->aSortOrder!=0 );
|
||||
sqlite3XPrintf(&x, "k(%d", pKeyInfo->nKeyField);
|
||||
sqlite3_str_appendf(&x, "k(%d", pKeyInfo->nKeyField);
|
||||
for(j=0; j<pKeyInfo->nKeyField; j++){
|
||||
CollSeq *pColl = pKeyInfo->aColl[j];
|
||||
const char *zColl = pColl ? pColl->zName : "";
|
||||
if( strcmp(zColl, "BINARY")==0 ) zColl = "B";
|
||||
sqlite3XPrintf(&x, ",%s%s", pKeyInfo->aSortOrder[j] ? "-" : "", zColl);
|
||||
sqlite3_str_appendf(&x, ",%s%s",
|
||||
pKeyInfo->aSortOrder[j] ? "-" : "", zColl);
|
||||
}
|
||||
sqlite3StrAccumAppend(&x, ")", 1);
|
||||
sqlite3_str_append(&x, ")", 1);
|
||||
break;
|
||||
}
|
||||
#ifdef SQLITE_ENABLE_CURSOR_HINTS
|
||||
@@ -1404,31 +1405,31 @@ static char *displayP4(Op *pOp, char *zTemp, int nTemp){
|
||||
#endif
|
||||
case P4_COLLSEQ: {
|
||||
CollSeq *pColl = pOp->p4.pColl;
|
||||
sqlite3XPrintf(&x, "(%.20s)", pColl->zName);
|
||||
sqlite3_str_appendf(&x, "(%.20s)", pColl->zName);
|
||||
break;
|
||||
}
|
||||
case P4_FUNCDEF: {
|
||||
FuncDef *pDef = pOp->p4.pFunc;
|
||||
sqlite3XPrintf(&x, "%s(%d)", pDef->zName, pDef->nArg);
|
||||
sqlite3_str_appendf(&x, "%s(%d)", pDef->zName, pDef->nArg);
|
||||
break;
|
||||
}
|
||||
#if defined(SQLITE_DEBUG) || defined(VDBE_PROFILE)
|
||||
case P4_FUNCCTX: {
|
||||
FuncDef *pDef = pOp->p4.pCtx->pFunc;
|
||||
sqlite3XPrintf(&x, "%s(%d)", pDef->zName, pDef->nArg);
|
||||
sqlite3_str_appendf(&x, "%s(%d)", pDef->zName, pDef->nArg);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
case P4_INT64: {
|
||||
sqlite3XPrintf(&x, "%lld", *pOp->p4.pI64);
|
||||
sqlite3_str_appendf(&x, "%lld", *pOp->p4.pI64);
|
||||
break;
|
||||
}
|
||||
case P4_INT32: {
|
||||
sqlite3XPrintf(&x, "%d", pOp->p4.i);
|
||||
sqlite3_str_appendf(&x, "%d", pOp->p4.i);
|
||||
break;
|
||||
}
|
||||
case P4_REAL: {
|
||||
sqlite3XPrintf(&x, "%.16g", *pOp->p4.pReal);
|
||||
sqlite3_str_appendf(&x, "%.16g", *pOp->p4.pReal);
|
||||
break;
|
||||
}
|
||||
case P4_MEM: {
|
||||
@@ -1436,9 +1437,9 @@ static char *displayP4(Op *pOp, char *zTemp, int nTemp){
|
||||
if( pMem->flags & MEM_Str ){
|
||||
zP4 = pMem->z;
|
||||
}else if( pMem->flags & MEM_Int ){
|
||||
sqlite3XPrintf(&x, "%lld", pMem->u.i);
|
||||
sqlite3_str_appendf(&x, "%lld", pMem->u.i);
|
||||
}else if( pMem->flags & MEM_Real ){
|
||||
sqlite3XPrintf(&x, "%.16g", pMem->u.r);
|
||||
sqlite3_str_appendf(&x, "%.16g", pMem->u.r);
|
||||
}else if( pMem->flags & MEM_Null ){
|
||||
zP4 = "NULL";
|
||||
}else{
|
||||
@@ -1450,7 +1451,7 @@ static char *displayP4(Op *pOp, char *zTemp, int nTemp){
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
case P4_VTAB: {
|
||||
sqlite3_vtab *pVtab = pOp->p4.pVtab->pVtab;
|
||||
sqlite3XPrintf(&x, "vtab:%p", pVtab);
|
||||
sqlite3_str_appendf(&x, "vtab:%p", pVtab);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
@@ -1460,14 +1461,14 @@ static char *displayP4(Op *pOp, char *zTemp, int nTemp){
|
||||
int n = ai[0]; /* The first element of an INTARRAY is always the
|
||||
** count of the number of elements to follow */
|
||||
for(i=1; i<=n; i++){
|
||||
sqlite3XPrintf(&x, ",%d", ai[i]);
|
||||
sqlite3_str_appendf(&x, ",%d", ai[i]);
|
||||
}
|
||||
zTemp[0] = '[';
|
||||
sqlite3StrAccumAppend(&x, "]", 1);
|
||||
sqlite3_str_append(&x, "]", 1);
|
||||
break;
|
||||
}
|
||||
case P4_SUBPROGRAM: {
|
||||
sqlite3XPrintf(&x, "program");
|
||||
sqlite3_str_appendf(&x, "program");
|
||||
break;
|
||||
}
|
||||
case P4_DYNBLOB:
|
||||
@@ -1476,7 +1477,7 @@ static char *displayP4(Op *pOp, char *zTemp, int nTemp){
|
||||
break;
|
||||
}
|
||||
case P4_TABLE: {
|
||||
sqlite3XPrintf(&x, "%s", pOp->p4.pTab->zName);
|
||||
sqlite3_str_appendf(&x, "%s", pOp->p4.pTab->zName);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
@@ -3916,13 +3917,10 @@ static int sqlite3IntFloatCompare(i64 i, double r){
|
||||
i64 y;
|
||||
double s;
|
||||
if( r<-9223372036854775808.0 ) return +1;
|
||||
if( r>9223372036854775807.0 ) return -1;
|
||||
if( r>=9223372036854775808.0 ) return -1;
|
||||
y = (i64)r;
|
||||
if( i<y ) return -1;
|
||||
if( i>y ){
|
||||
if( y==SMALLEST_INT64 && r>0.0 ) return -1;
|
||||
return +1;
|
||||
}
|
||||
if( i>y ) return +1;
|
||||
s = (double)i;
|
||||
if( s<r ) return -1;
|
||||
if( s>r ) return +1;
|
||||
|
||||
+16
-16
@@ -93,17 +93,17 @@ char *sqlite3VdbeExpandSql(
|
||||
while( *zRawSql ){
|
||||
const char *zStart = zRawSql;
|
||||
while( *(zRawSql++)!='\n' && *zRawSql );
|
||||
sqlite3StrAccumAppend(&out, "-- ", 3);
|
||||
sqlite3_str_append(&out, "-- ", 3);
|
||||
assert( (zRawSql - zStart) > 0 );
|
||||
sqlite3StrAccumAppend(&out, zStart, (int)(zRawSql-zStart));
|
||||
sqlite3_str_append(&out, zStart, (int)(zRawSql-zStart));
|
||||
}
|
||||
}else if( p->nVar==0 ){
|
||||
sqlite3StrAccumAppend(&out, zRawSql, sqlite3Strlen30(zRawSql));
|
||||
sqlite3_str_append(&out, zRawSql, sqlite3Strlen30(zRawSql));
|
||||
}else{
|
||||
while( zRawSql[0] ){
|
||||
n = findNextHostParameter(zRawSql, &nToken);
|
||||
assert( n>0 );
|
||||
sqlite3StrAccumAppend(&out, zRawSql, n);
|
||||
sqlite3_str_append(&out, zRawSql, n);
|
||||
zRawSql += n;
|
||||
assert( zRawSql[0] || nToken==0 );
|
||||
if( nToken==0 ) break;
|
||||
@@ -129,11 +129,11 @@ char *sqlite3VdbeExpandSql(
|
||||
assert( idx>0 && idx<=p->nVar );
|
||||
pVar = &p->aVar[idx-1];
|
||||
if( pVar->flags & MEM_Null ){
|
||||
sqlite3StrAccumAppend(&out, "NULL", 4);
|
||||
sqlite3_str_append(&out, "NULL", 4);
|
||||
}else if( pVar->flags & MEM_Int ){
|
||||
sqlite3XPrintf(&out, "%lld", pVar->u.i);
|
||||
sqlite3_str_appendf(&out, "%lld", pVar->u.i);
|
||||
}else if( pVar->flags & MEM_Real ){
|
||||
sqlite3XPrintf(&out, "%!.15g", pVar->u.r);
|
||||
sqlite3_str_appendf(&out, "%!.15g", pVar->u.r);
|
||||
}else if( pVar->flags & MEM_Str ){
|
||||
int nOut; /* Number of bytes of the string text to include in output */
|
||||
#ifndef SQLITE_OMIT_UTF16
|
||||
@@ -143,7 +143,7 @@ char *sqlite3VdbeExpandSql(
|
||||
utf8.db = db;
|
||||
sqlite3VdbeMemSetStr(&utf8, pVar->z, pVar->n, enc, SQLITE_STATIC);
|
||||
if( SQLITE_NOMEM==sqlite3VdbeChangeEncoding(&utf8, SQLITE_UTF8) ){
|
||||
out.accError = STRACCUM_NOMEM;
|
||||
out.accError = SQLITE_NOMEM;
|
||||
out.nAlloc = 0;
|
||||
}
|
||||
pVar = &utf8;
|
||||
@@ -156,38 +156,38 @@ char *sqlite3VdbeExpandSql(
|
||||
while( nOut<pVar->n && (pVar->z[nOut]&0xc0)==0x80 ){ nOut++; }
|
||||
}
|
||||
#endif
|
||||
sqlite3XPrintf(&out, "'%.*q'", nOut, pVar->z);
|
||||
sqlite3_str_appendf(&out, "'%.*q'", nOut, pVar->z);
|
||||
#ifdef SQLITE_TRACE_SIZE_LIMIT
|
||||
if( nOut<pVar->n ){
|
||||
sqlite3XPrintf(&out, "/*+%d bytes*/", pVar->n-nOut);
|
||||
sqlite3_str_appendf(&out, "/*+%d bytes*/", pVar->n-nOut);
|
||||
}
|
||||
#endif
|
||||
#ifndef SQLITE_OMIT_UTF16
|
||||
if( enc!=SQLITE_UTF8 ) sqlite3VdbeMemRelease(&utf8);
|
||||
#endif
|
||||
}else if( pVar->flags & MEM_Zero ){
|
||||
sqlite3XPrintf(&out, "zeroblob(%d)", pVar->u.nZero);
|
||||
sqlite3_str_appendf(&out, "zeroblob(%d)", pVar->u.nZero);
|
||||
}else{
|
||||
int nOut; /* Number of bytes of the blob to include in output */
|
||||
assert( pVar->flags & MEM_Blob );
|
||||
sqlite3StrAccumAppend(&out, "x'", 2);
|
||||
sqlite3_str_append(&out, "x'", 2);
|
||||
nOut = pVar->n;
|
||||
#ifdef SQLITE_TRACE_SIZE_LIMIT
|
||||
if( nOut>SQLITE_TRACE_SIZE_LIMIT ) nOut = SQLITE_TRACE_SIZE_LIMIT;
|
||||
#endif
|
||||
for(i=0; i<nOut; i++){
|
||||
sqlite3XPrintf(&out, "%02x", pVar->z[i]&0xff);
|
||||
sqlite3_str_appendf(&out, "%02x", pVar->z[i]&0xff);
|
||||
}
|
||||
sqlite3StrAccumAppend(&out, "'", 1);
|
||||
sqlite3_str_append(&out, "'", 1);
|
||||
#ifdef SQLITE_TRACE_SIZE_LIMIT
|
||||
if( nOut<pVar->n ){
|
||||
sqlite3XPrintf(&out, "/*+%d bytes*/", pVar->n-nOut);
|
||||
sqlite3_str_appendf(&out, "/*+%d bytes*/", pVar->n-nOut);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
if( out.accError ) sqlite3StrAccumReset(&out);
|
||||
if( out.accError ) sqlite3_str_reset(&out);
|
||||
return sqlite3StrAccumFinish(&out);
|
||||
}
|
||||
|
||||
|
||||
+11
-6
@@ -2862,14 +2862,16 @@ static int whereLoopAddBtree(
|
||||
/* TUNING: One-time cost for computing the automatic index is
|
||||
** estimated to be X*N*log2(N) where N is the number of rows in
|
||||
** the table being indexed and where X is 7 (LogEst=28) for normal
|
||||
** tables or 1.375 (LogEst=4) for views and subqueries. The value
|
||||
** tables or 0.5 (LogEst=-10) for views and subqueries. The value
|
||||
** of X is smaller for views and subqueries so that the query planner
|
||||
** will be more aggressive about generating automatic indexes for
|
||||
** those objects, since there is no opportunity to add schema
|
||||
** indexes on subqueries and views. */
|
||||
pNew->rSetup = rLogSize + rSize + 4;
|
||||
pNew->rSetup = rLogSize + rSize;
|
||||
if( pTab->pSelect==0 && (pTab->tabFlags & TF_Ephemeral)==0 ){
|
||||
pNew->rSetup += 24;
|
||||
pNew->rSetup += 28;
|
||||
}else{
|
||||
pNew->rSetup -= 10;
|
||||
}
|
||||
ApplyCostMultiplier(pNew->rSetup, pTab->costMult);
|
||||
if( pNew->rSetup<0 ) pNew->rSetup = 0;
|
||||
@@ -4005,12 +4007,15 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){
|
||||
|
||||
if( (pWLoop->prereq & ~pFrom->maskLoop)!=0 ) continue;
|
||||
if( (pWLoop->maskSelf & pFrom->maskLoop)!=0 ) continue;
|
||||
if( (pWLoop->wsFlags & WHERE_AUTO_INDEX)!=0 && pFrom->nRow<10 ){
|
||||
if( (pWLoop->wsFlags & WHERE_AUTO_INDEX)!=0 && pFrom->nRow<3 ){
|
||||
/* Do not use an automatic index if the this loop is expected
|
||||
** to run less than 2 times. */
|
||||
** to run less than 1.25 times. It is tempting to also exclude
|
||||
** automatic index usage on an outer loop, but sometimes an automatic
|
||||
** index is useful in the outer loop of a correlated subquery. */
|
||||
assert( 10==sqlite3LogEst(2) );
|
||||
continue;
|
||||
}
|
||||
|
||||
/* At this point, pWLoop is a candidate to be the next loop.
|
||||
** Compute its cost */
|
||||
rUnsorted = sqlite3LogEstAdd(pWLoop->rSetup,pWLoop->rRun + pFrom->nRow);
|
||||
@@ -4988,7 +4993,7 @@ WhereInfo *sqlite3WhereBegin(
|
||||
}
|
||||
#endif
|
||||
addrExplain = sqlite3WhereExplainOneScan(
|
||||
pParse, pTabList, pLevel, ii, pLevel->iFrom, wctrlFlags
|
||||
pParse, pTabList, pLevel, wctrlFlags
|
||||
);
|
||||
pLevel->addrBody = sqlite3VdbeCurrentAddr(v);
|
||||
notReady = sqlite3WhereCodeOneLoopStart(pWInfo, ii, notReady);
|
||||
|
||||
+1
-3
@@ -467,12 +467,10 @@ int sqlite3WhereExplainOneScan(
|
||||
Parse *pParse, /* Parse context */
|
||||
SrcList *pTabList, /* Table list this loop refers to */
|
||||
WhereLevel *pLevel, /* Scan to write OP_Explain opcode for */
|
||||
int iLevel, /* Value for "level" column of output */
|
||||
int iFrom, /* Value for "from" column of output */
|
||||
u16 wctrlFlags /* Flags passed to sqlite3WhereBegin() */
|
||||
);
|
||||
#else
|
||||
# define sqlite3WhereExplainOneScan(u,v,w,x,y,z) 0
|
||||
# define sqlite3WhereExplainOneScan(u,v,w,x) 0
|
||||
#endif /* SQLITE_OMIT_EXPLAIN */
|
||||
#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
|
||||
void sqlite3WhereAddScanStatus(
|
||||
|
||||
+27
-27
@@ -51,23 +51,23 @@ static void explainAppendTerm(
|
||||
int i;
|
||||
|
||||
assert( nTerm>=1 );
|
||||
if( bAnd ) sqlite3StrAccumAppend(pStr, " AND ", 5);
|
||||
if( bAnd ) sqlite3_str_append(pStr, " AND ", 5);
|
||||
|
||||
if( nTerm>1 ) sqlite3StrAccumAppend(pStr, "(", 1);
|
||||
if( nTerm>1 ) sqlite3_str_append(pStr, "(", 1);
|
||||
for(i=0; i<nTerm; i++){
|
||||
if( i ) sqlite3StrAccumAppend(pStr, ",", 1);
|
||||
sqlite3StrAccumAppendAll(pStr, explainIndexColumnName(pIdx, iTerm+i));
|
||||
if( i ) sqlite3_str_append(pStr, ",", 1);
|
||||
sqlite3_str_appendall(pStr, explainIndexColumnName(pIdx, iTerm+i));
|
||||
}
|
||||
if( nTerm>1 ) sqlite3StrAccumAppend(pStr, ")", 1);
|
||||
if( nTerm>1 ) sqlite3_str_append(pStr, ")", 1);
|
||||
|
||||
sqlite3StrAccumAppend(pStr, zOp, 1);
|
||||
sqlite3_str_append(pStr, zOp, 1);
|
||||
|
||||
if( nTerm>1 ) sqlite3StrAccumAppend(pStr, "(", 1);
|
||||
if( nTerm>1 ) sqlite3_str_append(pStr, "(", 1);
|
||||
for(i=0; i<nTerm; i++){
|
||||
if( i ) sqlite3StrAccumAppend(pStr, ",", 1);
|
||||
sqlite3StrAccumAppend(pStr, "?", 1);
|
||||
if( i ) sqlite3_str_append(pStr, ",", 1);
|
||||
sqlite3_str_append(pStr, "?", 1);
|
||||
}
|
||||
if( nTerm>1 ) sqlite3StrAccumAppend(pStr, ")", 1);
|
||||
if( nTerm>1 ) sqlite3_str_append(pStr, ")", 1);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -91,11 +91,11 @@ static void explainIndexRange(StrAccum *pStr, WhereLoop *pLoop){
|
||||
int i, j;
|
||||
|
||||
if( nEq==0 && (pLoop->wsFlags&(WHERE_BTM_LIMIT|WHERE_TOP_LIMIT))==0 ) return;
|
||||
sqlite3StrAccumAppend(pStr, " (", 2);
|
||||
sqlite3_str_append(pStr, " (", 2);
|
||||
for(i=0; i<nEq; i++){
|
||||
const char *z = explainIndexColumnName(pIndex, i);
|
||||
if( i ) sqlite3StrAccumAppend(pStr, " AND ", 5);
|
||||
sqlite3XPrintf(pStr, i>=nSkip ? "%s=?" : "ANY(%s)", z);
|
||||
if( i ) sqlite3_str_append(pStr, " AND ", 5);
|
||||
sqlite3_str_appendf(pStr, i>=nSkip ? "%s=?" : "ANY(%s)", z);
|
||||
}
|
||||
|
||||
j = i;
|
||||
@@ -106,7 +106,7 @@ static void explainIndexRange(StrAccum *pStr, WhereLoop *pLoop){
|
||||
if( pLoop->wsFlags&WHERE_TOP_LIMIT ){
|
||||
explainAppendTerm(pStr, pIndex, pLoop->u.btree.nTop, j, i, "<");
|
||||
}
|
||||
sqlite3StrAccumAppend(pStr, ")", 1);
|
||||
sqlite3_str_append(pStr, ")", 1);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -122,8 +122,6 @@ int sqlite3WhereExplainOneScan(
|
||||
Parse *pParse, /* Parse context */
|
||||
SrcList *pTabList, /* Table list this loop refers to */
|
||||
WhereLevel *pLevel, /* Scan to write OP_Explain opcode for */
|
||||
int iLevel, /* Value for "level" column of output */
|
||||
int iFrom, /* Value for "from" column of output */
|
||||
u16 wctrlFlags /* Flags passed to sqlite3WhereBegin() */
|
||||
){
|
||||
int ret = 0;
|
||||
@@ -150,15 +148,15 @@ int sqlite3WhereExplainOneScan(
|
||||
|| (wctrlFlags&(WHERE_ORDERBY_MIN|WHERE_ORDERBY_MAX));
|
||||
|
||||
sqlite3StrAccumInit(&str, db, zBuf, sizeof(zBuf), SQLITE_MAX_LENGTH);
|
||||
sqlite3StrAccumAppendAll(&str, isSearch ? "SEARCH" : "SCAN");
|
||||
sqlite3_str_appendall(&str, isSearch ? "SEARCH" : "SCAN");
|
||||
if( pItem->pSelect ){
|
||||
sqlite3XPrintf(&str, " SUBQUERY 0x%p", pItem->pSelect);
|
||||
sqlite3_str_appendf(&str, " SUBQUERY 0x%p", pItem->pSelect);
|
||||
}else{
|
||||
sqlite3XPrintf(&str, " TABLE %s", pItem->zName);
|
||||
sqlite3_str_appendf(&str, " TABLE %s", pItem->zName);
|
||||
}
|
||||
|
||||
if( pItem->zAlias ){
|
||||
sqlite3XPrintf(&str, " AS %s", pItem->zAlias);
|
||||
sqlite3_str_appendf(&str, " AS %s", pItem->zAlias);
|
||||
}
|
||||
if( (flags & (WHERE_IPK|WHERE_VIRTUALTABLE))==0 ){
|
||||
const char *zFmt = 0;
|
||||
@@ -181,8 +179,8 @@ int sqlite3WhereExplainOneScan(
|
||||
zFmt = "INDEX %s";
|
||||
}
|
||||
if( zFmt ){
|
||||
sqlite3StrAccumAppend(&str, " USING ", 7);
|
||||
sqlite3XPrintf(&str, zFmt, pIdx->zName);
|
||||
sqlite3_str_append(&str, " USING ", 7);
|
||||
sqlite3_str_appendf(&str, zFmt, pIdx->zName);
|
||||
explainIndexRange(&str, pLoop);
|
||||
}
|
||||
}else if( (flags & WHERE_IPK)!=0 && (flags & WHERE_CONSTRAINT)!=0 ){
|
||||
@@ -197,19 +195,21 @@ int sqlite3WhereExplainOneScan(
|
||||
assert( flags&WHERE_TOP_LIMIT);
|
||||
zRangeOp = "<";
|
||||
}
|
||||
sqlite3XPrintf(&str, " USING INTEGER PRIMARY KEY (rowid%s?)",zRangeOp);
|
||||
sqlite3_str_appendf(&str,
|
||||
" USING INTEGER PRIMARY KEY (rowid%s?)",zRangeOp);
|
||||
}
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
else if( (flags & WHERE_VIRTUALTABLE)!=0 ){
|
||||
sqlite3XPrintf(&str, " VIRTUAL TABLE INDEX %d:%s",
|
||||
sqlite3_str_appendf(&str, " VIRTUAL TABLE INDEX %d:%s",
|
||||
pLoop->u.vtab.idxNum, pLoop->u.vtab.idxStr);
|
||||
}
|
||||
#endif
|
||||
#ifdef SQLITE_EXPLAIN_ESTIMATED_ROWS
|
||||
if( pLoop->nOut>=10 ){
|
||||
sqlite3XPrintf(&str, " (~%llu rows)", sqlite3LogEstToInt(pLoop->nOut));
|
||||
sqlite3_str_appendf(&str, " (~%llu rows)",
|
||||
sqlite3LogEstToInt(pLoop->nOut));
|
||||
}else{
|
||||
sqlite3StrAccumAppend(&str, " (~1 row)", 9);
|
||||
sqlite3_str_append(&str, " (~1 row)", 9);
|
||||
}
|
||||
#endif
|
||||
zMsg = sqlite3StrAccumFinish(&str);
|
||||
@@ -1956,7 +1956,7 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
||||
if( pSubWInfo ){
|
||||
WhereLoop *pSubLoop;
|
||||
int addrExplain = sqlite3WhereExplainOneScan(
|
||||
pParse, pOrTab, &pSubWInfo->a[0], iLevel, pLevel->iFrom, 0
|
||||
pParse, pOrTab, &pSubWInfo->a[0], 0
|
||||
);
|
||||
sqlite3WhereAddScanStatus(v, pOrTab, &pSubWInfo->a[0], addrExplain);
|
||||
|
||||
|
||||
@@ -684,4 +684,159 @@ do_execsql_test autoinc-11.1 {
|
||||
SELECT seq FROM sqlite_sequence WHERE name='t11';
|
||||
} {5}
|
||||
|
||||
# 2018-05-23 ticket d8dc2b3a58cd5dc2918a1d4acbba4676a23ada4c
|
||||
# Does not crash if the sqlite_sequence table schema is missing
|
||||
# or corrupt.
|
||||
#
|
||||
do_test autoinc-12.1 {
|
||||
db close
|
||||
forcedelete test.db
|
||||
sqlite3 db test.db
|
||||
db eval {
|
||||
CREATE TABLE fake_sequence(name TEXT PRIMARY KEY,seq) WITHOUT ROWID;
|
||||
PRAGMA writable_schema=on;
|
||||
UPDATE sqlite_master SET
|
||||
sql=replace(sql,'fake_','sqlite_'),
|
||||
name='sqlite_sequence',
|
||||
tbl_name='sqlite_sequence'
|
||||
WHERE name='fake_sequence';
|
||||
}
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
set res [catch {db eval {
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY AUTOINCREMENT, b TEXT);
|
||||
INSERT INTO t1(b) VALUES('one');
|
||||
}} msg]
|
||||
lappend res $msg
|
||||
} {1 {database disk image is malformed}}
|
||||
do_test autoinc-12.2 {
|
||||
db close
|
||||
forcedelete test.db
|
||||
sqlite3 db test.db
|
||||
db eval {
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY AUTOINCREMENT, b TEXT);
|
||||
INSERT INTO t1(b) VALUES('one');
|
||||
PRAGMA writable_schema=on;
|
||||
UPDATE sqlite_master SET
|
||||
sql=replace(sql,'sqlite_','x_'),
|
||||
name='x_sequence',
|
||||
tbl_name='x_sequence'
|
||||
WHERE name='sqlite_sequence';
|
||||
}
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
set res [catch {db eval {
|
||||
INSERT INTO t1(b) VALUES('two');
|
||||
}} msg]
|
||||
lappend res $msg
|
||||
} {1 {database disk image is malformed}}
|
||||
do_test autoinc-12.3 {
|
||||
db close
|
||||
forcedelete test.db
|
||||
sqlite3 db test.db
|
||||
db eval {
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY AUTOINCREMENT, b TEXT);
|
||||
INSERT INTO t1(b) VALUES('one');
|
||||
PRAGMA writable_schema=on;
|
||||
UPDATE sqlite_master SET
|
||||
sql='CREATE VIRTUAL TABLE sqlite_sequence USING sqlite_dbpage'
|
||||
WHERE name='sqlite_sequence';
|
||||
}
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
set res [catch {db eval {
|
||||
INSERT INTO t1(b) VALUES('two');
|
||||
}} msg]
|
||||
lappend res $msg
|
||||
} {1 {database disk image is malformed}}
|
||||
do_test autoinc-12.4 {
|
||||
db close
|
||||
forcedelete test.db
|
||||
sqlite3 db test.db
|
||||
db eval {
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY AUTOINCREMENT, b TEXT);
|
||||
INSERT INTO t1(b) VALUES('one');
|
||||
CREATE TABLE fake(name TEXT PRIMARY KEY,seq) WITHOUT ROWID;
|
||||
}
|
||||
set root1 [db one {SELECT rootpage FROM sqlite_master
|
||||
WHERE name='sqlite_sequence'}]
|
||||
set root2 [db one {SELECT rootpage FROM sqlite_master
|
||||
WHERE name='fake'}]
|
||||
db eval {
|
||||
PRAGMA writable_schema=on;
|
||||
UPDATE sqlite_master SET rootpage=$root2
|
||||
WHERE name='sqlite_sequence';
|
||||
UPDATE sqlite_master SET rootpage=$root1
|
||||
WHERE name='fake';
|
||||
}
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
set res [catch {db eval {
|
||||
INSERT INTO t1(b) VALUES('two');
|
||||
}} msg]
|
||||
lappend res $msg
|
||||
} {1 {database disk image is malformed}}
|
||||
breakpoint
|
||||
do_test autoinc-12.5 {
|
||||
db close
|
||||
forcedelete test.db
|
||||
sqlite3 db test.db
|
||||
db eval {
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY AUTOINCREMENT, b TEXT);
|
||||
INSERT INTO t1(b) VALUES('one');
|
||||
PRAGMA writable_schema=on;
|
||||
UPDATE sqlite_master SET
|
||||
sql='CREATE TABLE sqlite_sequence(x)'
|
||||
WHERE name='sqlite_sequence';
|
||||
}
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
set res [catch {db eval {
|
||||
INSERT INTO t1(b) VALUES('two');
|
||||
}} msg]
|
||||
lappend res $msg
|
||||
} {1 {database disk image is malformed}}
|
||||
do_test autoinc-12.6 {
|
||||
db close
|
||||
forcedelete test.db
|
||||
sqlite3 db test.db
|
||||
db eval {
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY AUTOINCREMENT, b TEXT);
|
||||
INSERT INTO t1(b) VALUES('one');
|
||||
PRAGMA writable_schema=on;
|
||||
UPDATE sqlite_master SET
|
||||
sql='CREATE TABLE sqlite_sequence(x,y INTEGER PRIMARY KEY)'
|
||||
WHERE name='sqlite_sequence';
|
||||
}
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
set res [catch {db eval {
|
||||
INSERT INTO t1(b) VALUES('two'),('three'),('four');
|
||||
INSERT INTO t1(b) VALUES('five');
|
||||
PRAGMA integrity_check;
|
||||
}} msg]
|
||||
lappend res $msg
|
||||
} {0 ok}
|
||||
do_test autoinc-12.7 {
|
||||
db close
|
||||
forcedelete test.db
|
||||
sqlite3 db test.db
|
||||
db eval {
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY AUTOINCREMENT, b TEXT);
|
||||
INSERT INTO t1(b) VALUES('one');
|
||||
PRAGMA writable_schema=on;
|
||||
UPDATE sqlite_master SET
|
||||
sql='CREATE TABLE sqlite_sequence(y INTEGER PRIMARY KEY,x)'
|
||||
WHERE name='sqlite_sequence';
|
||||
}
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
set res [catch {db eval {
|
||||
INSERT INTO t1(b) VALUES('two'),('three'),('four');
|
||||
INSERT INTO t1(b) VALUES('five');
|
||||
PRAGMA integrity_check;
|
||||
}} msg]
|
||||
lappend res $msg
|
||||
} {0 ok}
|
||||
|
||||
finish_test
|
||||
|
||||
+24
-12
@@ -720,11 +720,13 @@ static void rebuild_database(sqlite3 *db){
|
||||
"BEGIN;\n"
|
||||
"CREATE TEMP TABLE dbx AS SELECT DISTINCT dbcontent FROM db;\n"
|
||||
"DELETE FROM db;\n"
|
||||
"INSERT INTO db(dbid, dbcontent) SELECT NULL, dbcontent FROM dbx ORDER BY 2;\n"
|
||||
"INSERT INTO db(dbid, dbcontent) "
|
||||
" SELECT NULL, dbcontent FROM dbx ORDER BY 2;\n"
|
||||
"DROP TABLE dbx;\n"
|
||||
"CREATE TEMP TABLE sx AS SELECT DISTINCT sqltext FROM xsql;\n"
|
||||
"DELETE FROM xsql;\n"
|
||||
"INSERT INTO xsql(sqlid,sqltext) SELECT NULL, sqltext FROM sx ORDER BY 2;\n"
|
||||
"INSERT INTO xsql(sqlid,sqltext) "
|
||||
" SELECT NULL, sqltext FROM sx ORDER BY 2;\n"
|
||||
"DROP TABLE sx;\n"
|
||||
"COMMIT;\n"
|
||||
"PRAGMA page_size=1024;\n"
|
||||
@@ -807,7 +809,7 @@ static void showHelp(void){
|
||||
" -q|--quiet Reduced output\n"
|
||||
" --limit-mem N Limit memory used by test SQLite instance to N bytes\n"
|
||||
" --limit-vdbe Panic if any test runs for more than 100,000 cycles\n"
|
||||
" --load-sql ARGS... Load SQL scripts fro files into SOURCE-DB\n"
|
||||
" --load-sql ARGS... Load SQL scripts fron files into SOURCE-DB\n"
|
||||
" --load-db ARGS... Load template databases from files into SOURCE_DB\n"
|
||||
" -m TEXT Add a description to the database\n"
|
||||
" --native-vfs Use the native VFS for initially empty database files\n"
|
||||
@@ -827,7 +829,7 @@ int main(int argc, char **argv){
|
||||
int quietFlag = 0; /* True if --quiet or -q */
|
||||
int verboseFlag = 0; /* True if --verbose or -v */
|
||||
char *zInsSql = 0; /* SQL statement for --load-db or --load-sql */
|
||||
int iFirstInsArg = 0; /* First argv[] to use for --load-db or --load-sql */
|
||||
int iFirstInsArg = 0; /* First argv[] for --load-db or --load-sql */
|
||||
sqlite3 *db = 0; /* The open database connection */
|
||||
sqlite3_stmt *pStmt; /* A prepared statement */
|
||||
int rc; /* Result code from SQLite interface calls */
|
||||
@@ -847,9 +849,9 @@ int main(int argc, char **argv){
|
||||
int iSrcDb; /* Loop over all source databases */
|
||||
int nTest = 0; /* Total number of tests performed */
|
||||
char *zDbName = ""; /* Appreviated name of a source database */
|
||||
const char *zFailCode = 0; /* Value of the TEST_FAILURE environment variable */
|
||||
const char *zFailCode = 0; /* Value of the TEST_FAILURE env variable */
|
||||
int cellSzCkFlag = 0; /* --cell-size-check */
|
||||
int sqlFuzz = 0; /* True for SQL fuzz testing. False for DB fuzz */
|
||||
int sqlFuzz = 0; /* True for SQL fuzz. False for DB fuzz */
|
||||
int iTimeout = 120; /* Default 120-second timeout */
|
||||
int nMem = 0; /* Memory limit */
|
||||
int nMemThisDb = 0; /* Memory limit set by the CONFIG table */
|
||||
@@ -860,12 +862,14 @@ int main(int argc, char **argv){
|
||||
int ossFuzzThisDb = 0; /* ossFuzz value for this particular database */
|
||||
int nativeMalloc = 0; /* Turn off MEMSYS3/5 and lookaside if true */
|
||||
sqlite3_vfs *pDfltVfs; /* The default VFS */
|
||||
int openFlags4Data; /* Flags for sqlite3_open_v2() */
|
||||
|
||||
iBegin = timeOfDay();
|
||||
#ifdef __unix__
|
||||
signal(SIGALRM, timeoutHandler);
|
||||
#endif
|
||||
g.zArgv0 = argv[0];
|
||||
openFlags4Data = SQLITE_OPEN_READONLY;
|
||||
zFailCode = getenv("TEST_FAILURE");
|
||||
pDfltVfs = sqlite3_vfs_find(0);
|
||||
inmemVfsRegister(1);
|
||||
@@ -906,18 +910,21 @@ int main(int argc, char **argv){
|
||||
vdbeLimitFlag = 1;
|
||||
}else
|
||||
if( strcmp(z,"load-sql")==0 ){
|
||||
zInsSql = "INSERT INTO xsql(sqltext) VALUES(CAST(readfile(?1) AS text))";
|
||||
zInsSql = "INSERT INTO xsql(sqltext)VALUES(CAST(readfile(?1) AS text))";
|
||||
iFirstInsArg = i+1;
|
||||
openFlags4Data = SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE;
|
||||
break;
|
||||
}else
|
||||
if( strcmp(z,"load-db")==0 ){
|
||||
zInsSql = "INSERT INTO db(dbcontent) VALUES(readfile(?1))";
|
||||
iFirstInsArg = i+1;
|
||||
openFlags4Data = SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE;
|
||||
break;
|
||||
}else
|
||||
if( strcmp(z,"m")==0 ){
|
||||
if( i>=argc-1 ) fatalError("missing arguments on %s", argv[i]);
|
||||
zMsg = argv[++i];
|
||||
openFlags4Data = SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE;
|
||||
}else
|
||||
if( strcmp(z,"native-malloc")==0 ){
|
||||
nativeMalloc = 1;
|
||||
@@ -938,6 +945,7 @@ int main(int argc, char **argv){
|
||||
}else
|
||||
if( strcmp(z,"rebuild")==0 ){
|
||||
rebuildFlag = 1;
|
||||
openFlags4Data = SQLITE_OPEN_READWRITE;
|
||||
}else
|
||||
if( strcmp(z,"result-trace")==0 ){
|
||||
runFlags |= SQL_OUTPUT;
|
||||
@@ -983,7 +991,7 @@ int main(int argc, char **argv){
|
||||
/* Process each source database separately */
|
||||
for(iSrcDb=0; iSrcDb<nSrcDb; iSrcDb++){
|
||||
rc = sqlite3_open_v2(azSrcDb[iSrcDb], &db,
|
||||
SQLITE_OPEN_READWRITE, pDfltVfs->zName);
|
||||
openFlags4Data, pDfltVfs->zName);
|
||||
if( rc ){
|
||||
fatalError("cannot open source database %s - %s",
|
||||
azSrcDb[iSrcDb], sqlite3_errmsg(db));
|
||||
@@ -1014,7 +1022,8 @@ int main(int argc, char **argv){
|
||||
/* If the CONFIG(name,value) table exists, read db-specific settings
|
||||
** from that table */
|
||||
if( sqlite3_table_column_metadata(db,0,"config",0,0,0,0,0,0)==SQLITE_OK ){
|
||||
rc = sqlite3_prepare_v2(db, "SELECT name, value FROM config", -1, &pStmt, 0);
|
||||
rc = sqlite3_prepare_v2(db, "SELECT name, value FROM config",
|
||||
-1, &pStmt, 0);
|
||||
if( rc ) fatalError("cannot prepare query of CONFIG table: %s",
|
||||
sqlite3_errmsg(db));
|
||||
while( SQLITE_ROW==sqlite3_step(pStmt) ){
|
||||
@@ -1053,7 +1062,8 @@ int main(int argc, char **argv){
|
||||
}
|
||||
sqlite3_finalize(pStmt);
|
||||
rc = sqlite3_exec(db, "COMMIT", 0, 0, 0);
|
||||
if( rc ) fatalError("cannot commit the transaction: %s", sqlite3_errmsg(db));
|
||||
if( rc ) fatalError("cannot commit the transaction: %s",
|
||||
sqlite3_errmsg(db));
|
||||
rebuild_database(db);
|
||||
sqlite3_close(db);
|
||||
return 0;
|
||||
@@ -1197,7 +1207,8 @@ int main(int argc, char **argv){
|
||||
sqlite3_randomness(0,0);
|
||||
if( ossFuzzThisDb ){
|
||||
#ifndef SQLITE_OSS_FUZZ
|
||||
fatalError("--oss-fuzz not supported: recompile with -DSQLITE_OSS_FUZZ");
|
||||
fatalError("--oss-fuzz not supported: recompile"
|
||||
" with -DSQLITE_OSS_FUZZ");
|
||||
#else
|
||||
extern int LLVMFuzzerTestOneInput(const uint8_t*, size_t);
|
||||
LLVMFuzzerTestOneInput((const uint8_t*)pSql->a, (size_t)pSql->sz);
|
||||
@@ -1216,7 +1227,8 @@ int main(int argc, char **argv){
|
||||
setAlarm(iTimeout);
|
||||
#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
|
||||
if( sqlFuzz || vdbeLimitFlag ){
|
||||
sqlite3_progress_handler(db, 100000, progressHandler, &vdbeLimitFlag);
|
||||
sqlite3_progress_handler(db, 100000, progressHandler,
|
||||
&vdbeLimitFlag);
|
||||
}
|
||||
#endif
|
||||
do{
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -784,5 +784,35 @@ do_execsql_test json-13.110 {
|
||||
WHERE Z.value==t2.id);
|
||||
} {3 {{"value":3}} 1 {{"items":[3,5]}} 5 {{"value":5}} 1 {{"items":[3,5]}}}
|
||||
|
||||
# 2018-05-16
|
||||
# Incorrect fullkey output from json_each()
|
||||
# when the input JSON is not an array or object.
|
||||
#
|
||||
do_execsql_test json-14.100 {
|
||||
SELECT fullkey FROM json_each('123');
|
||||
} {$}
|
||||
do_execsql_test json-14.110 {
|
||||
SELECT fullkey FROM json_each('123.56');
|
||||
} {$}
|
||||
do_execsql_test json-14.120 {
|
||||
SELECT fullkey FROM json_each('"hello"');
|
||||
} {$}
|
||||
do_execsql_test json-14.130 {
|
||||
SELECT fullkey FROM json_each('null');
|
||||
} {$}
|
||||
do_execsql_test json-14.140 {
|
||||
SELECT fullkey FROM json_tree('123');
|
||||
} {$}
|
||||
do_execsql_test json-14.150 {
|
||||
SELECT fullkey FROM json_tree('123.56');
|
||||
} {$}
|
||||
do_execsql_test json-14.160 {
|
||||
SELECT fullkey FROM json_tree('"hello"');
|
||||
} {$}
|
||||
do_execsql_test json-14.170 {
|
||||
SELECT fullkey FROM json_tree('null');
|
||||
} {$}
|
||||
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -548,5 +548,15 @@ do_execsql_test 9.0 {
|
||||
SELECT (SELECT x||y FROM t2, t1 ORDER BY x, y);
|
||||
} {13}
|
||||
|
||||
# Problem found by OSSFuzz on 2018-05-05. This was caused by a new
|
||||
# optimization that had not been previously released.
|
||||
#
|
||||
do_execsql_test 10.0 {
|
||||
CREATE TABLE t10(a,b);
|
||||
INSERT INTO t10 VALUES(1,2),(8,9),(3,4),(5,4),(0,7);
|
||||
CREATE INDEX t10b ON t10(b);
|
||||
SELECT b, rowid, '^' FROM t10 ORDER BY b, a LIMIT 4;
|
||||
} {2 1 ^ 4 3 ^ 4 4 ^ 7 5 ^}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
+1
-1
@@ -256,7 +256,7 @@ do_test shell1-3.1.3 {
|
||||
do_test shell1-3.1.4 {
|
||||
# too many arguments
|
||||
catchcmd "test.db" ".backup FOO BAR BAD"
|
||||
} {1 {too many arguments to .backup}}
|
||||
} {1 {Usage: .backup ?DB? ?--append? FILENAME}}
|
||||
|
||||
# .bail ON|OFF Stop after hitting an error. Default OFF
|
||||
do_test shell1-3.2.1 {
|
||||
|
||||
+53
-6
@@ -1247,10 +1247,11 @@ void testset_rtree(int p1, int p2){
|
||||
unsigned mxCoord;
|
||||
unsigned x0, x1, y0, y1, z0, z1;
|
||||
unsigned iStep;
|
||||
unsigned mxRowid;
|
||||
int *aCheck = sqlite3_malloc( sizeof(int)*g.szTest*500 );
|
||||
|
||||
mxCoord = 15000;
|
||||
n = g.szTest*500;
|
||||
mxRowid = n = g.szTest*500;
|
||||
speedtest1_begin_test(100, "%d INSERTs into an r-tree", n);
|
||||
speedtest1_exec("BEGIN");
|
||||
speedtest1_exec("CREATE VIRTUAL TABLE rt1 USING rtree(id,x0,x1,y0,y1,z0,z1)");
|
||||
@@ -1277,7 +1278,7 @@ void testset_rtree(int p1, int p2){
|
||||
speedtest1_exec("INSERT INTO t1 SELECT * FROM rt1");
|
||||
speedtest1_end_test();
|
||||
|
||||
n = g.szTest*100;
|
||||
n = g.szTest*200;
|
||||
speedtest1_begin_test(110, "%d one-dimensional intersect slice queries", n);
|
||||
speedtest1_prepare("SELECT count(*) FROM rt1 WHERE x0>=?1 AND x1<=?2");
|
||||
iStep = mxCoord/n;
|
||||
@@ -1290,7 +1291,7 @@ void testset_rtree(int p1, int p2){
|
||||
speedtest1_end_test();
|
||||
|
||||
if( g.bVerify ){
|
||||
n = g.szTest*100;
|
||||
n = g.szTest*200;
|
||||
speedtest1_begin_test(111, "Verify result from 1-D intersect slice queries");
|
||||
speedtest1_prepare("SELECT count(*) FROM t1 WHERE x0>=?1 AND x1<=?2");
|
||||
iStep = mxCoord/n;
|
||||
@@ -1306,7 +1307,7 @@ void testset_rtree(int p1, int p2){
|
||||
speedtest1_end_test();
|
||||
}
|
||||
|
||||
n = g.szTest*100;
|
||||
n = g.szTest*200;
|
||||
speedtest1_begin_test(120, "%d one-dimensional overlap slice queries", n);
|
||||
speedtest1_prepare("SELECT count(*) FROM rt1 WHERE y1>=?1 AND y0<=?2");
|
||||
iStep = mxCoord/n;
|
||||
@@ -1319,7 +1320,7 @@ void testset_rtree(int p1, int p2){
|
||||
speedtest1_end_test();
|
||||
|
||||
if( g.bVerify ){
|
||||
n = g.szTest*100;
|
||||
n = g.szTest*200;
|
||||
speedtest1_begin_test(121, "Verify result from 1-D overlap slice queries");
|
||||
speedtest1_prepare("SELECT count(*) FROM t1 WHERE y1>=?1 AND y0<=?2");
|
||||
iStep = mxCoord/n;
|
||||
@@ -1336,7 +1337,7 @@ void testset_rtree(int p1, int p2){
|
||||
}
|
||||
|
||||
|
||||
n = g.szTest*100;
|
||||
n = g.szTest*200;
|
||||
speedtest1_begin_test(125, "%d custom geometry callback queries", n);
|
||||
sqlite3_rtree_geometry_callback(g.db, "xslice", xsliceGeometryCallback, 0);
|
||||
speedtest1_prepare("SELECT count(*) FROM rt1 WHERE id MATCH xslice(?1,?2)");
|
||||
@@ -1373,6 +1374,52 @@ void testset_rtree(int p1, int p2){
|
||||
speedtest1_run();
|
||||
}
|
||||
speedtest1_end_test();
|
||||
|
||||
n = g.szTest*50;
|
||||
speedtest1_begin_test(150, "%d UPDATEs using rowid", n);
|
||||
speedtest1_prepare("UPDATE rt1 SET x0=x0+100, x1=x1+100 WHERE id=?1");
|
||||
for(i=1; i<=n; i++){
|
||||
sqlite3_bind_int(g.pStmt, 1, (i*251)%mxRowid + 1);
|
||||
speedtest1_run();
|
||||
}
|
||||
speedtest1_end_test();
|
||||
|
||||
n = g.szTest*5;
|
||||
speedtest1_begin_test(155, "%d UPDATEs using one-dimensional overlap", n);
|
||||
speedtest1_prepare("UPDATE rt1 SET x0=x0-100, x1=x1-100"
|
||||
" WHERE y1>=?1 AND y0<=?1+5");
|
||||
iStep = mxCoord/n;
|
||||
for(i=0; i<n; i++){
|
||||
sqlite3_bind_int(g.pStmt, 1, i*iStep);
|
||||
speedtest1_run();
|
||||
aCheck[i] = atoi(g.zResult);
|
||||
}
|
||||
speedtest1_end_test();
|
||||
|
||||
n = g.szTest*50;
|
||||
speedtest1_begin_test(160, "%d DELETEs using rowid", n);
|
||||
speedtest1_prepare("DELETE FROM rt1 WHERE id=?1");
|
||||
for(i=1; i<=n; i++){
|
||||
sqlite3_bind_int(g.pStmt, 1, (i*257)%mxRowid + 1);
|
||||
speedtest1_run();
|
||||
}
|
||||
speedtest1_end_test();
|
||||
|
||||
|
||||
n = g.szTest*5;
|
||||
speedtest1_begin_test(165, "%d DELETEs using one-dimensional overlap", n);
|
||||
speedtest1_prepare("DELETE FROM rt1 WHERE y1>=?1 AND y0<=?1+5");
|
||||
iStep = mxCoord/n;
|
||||
for(i=0; i<n; i++){
|
||||
sqlite3_bind_int(g.pStmt, 1, i*iStep);
|
||||
speedtest1_run();
|
||||
aCheck[i] = atoi(g.zResult);
|
||||
}
|
||||
speedtest1_end_test();
|
||||
|
||||
speedtest1_begin_test(170, "Restore deleted entries using INSERT OR IGNORE");
|
||||
speedtest1_exec("INSERT OR IGNORE INTO rt1 SELECT * FROM t1");
|
||||
speedtest1_end_test();
|
||||
}
|
||||
#endif /* SQLITE_ENABLE_RTREE */
|
||||
|
||||
|
||||
+11
-6
@@ -51,15 +51,19 @@ ifcapable {update_delete_limit} {
|
||||
catchsql {UPDATE t1 SET y=1 WHERE x=1 ORDER BY x}
|
||||
} {1 {ORDER BY without LIMIT on UPDATE}}
|
||||
|
||||
execsql { DROP TABLE t1 }
|
||||
|
||||
# no AS on table sources
|
||||
#
|
||||
# UPDATE: As of version 3.24, AS clauses are allowed as part of
|
||||
# UPDATE or DELETE statements.
|
||||
do_test wherelimit-0.4 {
|
||||
catchsql {DELETE FROM t1 AS a WHERE x=1}
|
||||
} {1 {near "AS": syntax error}}
|
||||
do_test wherelimit-0.5 {
|
||||
catchsql {DELETE FROM t1 AS a WHERE a.x=1}
|
||||
} {0 {}}
|
||||
do_test wherelimit-0.5.1 {
|
||||
catchsql {UPDATE t1 AS a SET y=1 WHERE x=1}
|
||||
} {1 {near "AS": syntax error}}
|
||||
} {0 {}}
|
||||
do_test wherelimit-0.5.2 {
|
||||
catchsql {UPDATE t1 AS a SET y=1 WHERE t1.x=1}
|
||||
} {1 {no such column: t1.x}}
|
||||
|
||||
# OFFSET w/o LIMIT
|
||||
do_test wherelimit-0.6 {
|
||||
@@ -69,6 +73,7 @@ ifcapable {update_delete_limit} {
|
||||
catchsql {UPDATE t1 SET y=1 WHERE x=1 OFFSET 2}
|
||||
} {1 {near "OFFSET": syntax error}}
|
||||
|
||||
execsql { DROP TABLE t1 }
|
||||
|
||||
# check deletes w/o where clauses but with limit/offsets
|
||||
create_test_data 5
|
||||
|
||||
+5
-1
@@ -41,8 +41,12 @@ doExplain=0
|
||||
doCachegrind=1
|
||||
doVdbeProfile=0
|
||||
doWal=1
|
||||
doDiff=1
|
||||
while test "$1" != ""; do
|
||||
case $1 in
|
||||
--nodiff)
|
||||
doDiff=0
|
||||
;;
|
||||
--reprepare)
|
||||
SPEEDTEST_OPTS="$SPEEDTEST_OPTS $1"
|
||||
;;
|
||||
@@ -179,6 +183,6 @@ if test $doVdbeProfile -eq 1; then
|
||||
tclsh ../sqlite/tool/vdbe_profile.tcl >vdbeprofile-$NAME.txt
|
||||
open vdbeprofile-$NAME.txt
|
||||
fi
|
||||
if test "$NAME" != "$BASELINE" -a $doVdbeProfile -ne 1; then
|
||||
if test "$NAME" != "$BASELINE" -a $doVdbeProfile -ne 1 -a $doDiff -ne 0; then
|
||||
fossil test-diff --tk -c 20 cout-$BASELINE.txt cout-$NAME.txt
|
||||
fi
|
||||
|
||||
Reference in New Issue
Block a user