Enhance error detection and fix other issues in unionvtab code.
FossilOrigin-Name: 9c3f1b9a82e500e015deb0cc669fbb32e7f0cdc69f926ceff383ab946f8d8d18
This commit is contained in:
parent
d8ecefa5ab
commit
bcd303ac7d
@ -10,8 +10,42 @@
|
||||
**
|
||||
*************************************************************************
|
||||
**
|
||||
** This file contains the implementation of the "unionvtab" virtual
|
||||
** table. This module provides read-only access to multiple tables,
|
||||
** possibly in multiple database files, via a single database object.
|
||||
** The source tables must have the following characteristics:
|
||||
**
|
||||
** * They must all be rowid tables (not VIRTUAL or WITHOUT ROWID
|
||||
** tables or views).
|
||||
**
|
||||
** * Each table must have the same set of columns, declared in
|
||||
** the same order and with the same declared types.
|
||||
**
|
||||
** * The tables must not feature a user-defined column named "_rowid_".
|
||||
**
|
||||
** * Each table must contain a distinct range of rowid values.
|
||||
**
|
||||
** A "unionvtab" virtual table is created as follows:
|
||||
**
|
||||
** CREATE VIRTUAL TABLE <name> USING unionvtab(<sql statement>);
|
||||
**
|
||||
** The implementation evalutes <sql statement> whenever a unionvtab virtual
|
||||
** table is created or opened. It should return one row for each source
|
||||
** database table. The four columns required of each row are:
|
||||
**
|
||||
** 1. The name of the database containing the table ("main" or "temp" or
|
||||
** the name of an attached database). Or NULL to indicate that all
|
||||
** databases should be searched for the table in the usual fashion.
|
||||
**
|
||||
** 2. The name of the database table.
|
||||
**
|
||||
** 3. The smallest rowid in the range of rowids that may be stored in the
|
||||
** database table (an integer).
|
||||
**
|
||||
** 4. The largest rowid in the range of rowids that may be stored in the
|
||||
** database table (an integer).
|
||||
**
|
||||
*/
|
||||
#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_UNIONVTAB)
|
||||
|
||||
#include "sqlite3ext.h"
|
||||
SQLITE_EXTENSION_INIT1
|
||||
@ -20,6 +54,17 @@ SQLITE_EXTENSION_INIT1
|
||||
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
|
||||
/*
|
||||
** Largest and smallest possible 64-bit signed integers. These macros
|
||||
** copied from sqliteInt.h.
|
||||
*/
|
||||
#ifndef LARGEST_INT64
|
||||
# define LARGEST_INT64 (0xffffffff|(((sqlite3_int64)0x7fffffff)<<32))
|
||||
#endif
|
||||
#ifndef SMALLEST_INT64
|
||||
# define SMALLEST_INT64 (((sqlite3_int64)-1) - LARGEST_INT64)
|
||||
#endif
|
||||
|
||||
typedef struct UnionCsr UnionCsr;
|
||||
typedef struct UnionTab UnionTab;
|
||||
typedef struct UnionSrc UnionSrc;
|
||||
@ -36,14 +81,6 @@ struct UnionSrc {
|
||||
sqlite3_int64 iMax; /* Maximum rowid */
|
||||
};
|
||||
|
||||
/*
|
||||
** Virtual table cursor type for union vtab.
|
||||
*/
|
||||
struct UnionCsr {
|
||||
sqlite3_vtab_cursor base; /* Base class - must be first */
|
||||
sqlite3_stmt *pStmt; /* SQL statement to run */
|
||||
};
|
||||
|
||||
/*
|
||||
** Virtual table type for union vtab.
|
||||
*/
|
||||
@ -54,6 +91,14 @@ struct UnionTab {
|
||||
UnionSrc *aSrc; /* Array of source tables, sorted by rowid */
|
||||
};
|
||||
|
||||
/*
|
||||
** Virtual table cursor type for union vtab.
|
||||
*/
|
||||
struct UnionCsr {
|
||||
sqlite3_vtab_cursor base; /* Base class - must be first */
|
||||
sqlite3_stmt *pStmt; /* SQL statement to run */
|
||||
};
|
||||
|
||||
/*
|
||||
** If *pRc is other than SQLITE_OK when this function is called, it
|
||||
** always returns NULL. Otherwise, it attempts to allocate and return
|
||||
@ -134,11 +179,24 @@ static void unionDequote(char *z){
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is a no-op if *pRc is set to other than SQLITE_OK when it
|
||||
** is called. NULL is returned in this case.
|
||||
**
|
||||
** Otherwise, the SQL statement passed as the third argument is prepared
|
||||
** against the database handle passed as the second. If the statement is
|
||||
** successfully prepared, a pointer to the new statement handle is
|
||||
** returned. It is the responsibility of the caller to eventually free the
|
||||
** statement by calling sqlite3_finalize(). Alternatively, if statement
|
||||
** compilation fails, NULL is returned, *pRc is set to an SQLite error
|
||||
** code and *pzErr may be set to an error message buffer allocated by
|
||||
** sqlite3_malloc().
|
||||
*/
|
||||
static sqlite3_stmt *unionPrepare(
|
||||
int *pRc,
|
||||
sqlite3 *db,
|
||||
const char *zSql,
|
||||
char **pzErr
|
||||
int *pRc, /* IN/OUT: Error code */
|
||||
sqlite3 *db, /* Database handle */
|
||||
const char *zSql, /* SQL statement to prepare */
|
||||
char **pzErr /* OUT: Error message */
|
||||
){
|
||||
sqlite3_stmt *pRet = 0;
|
||||
if( *pRc==SQLITE_OK ){
|
||||
@ -151,6 +209,13 @@ static sqlite3_stmt *unionPrepare(
|
||||
return pRet;
|
||||
}
|
||||
|
||||
/*
|
||||
** Call sqlite3_reset() on SQL statement pStmt. If *pRc is set to
|
||||
** SQLITE_OK when this function is called, then it is set to the
|
||||
** value returned by sqlite3_reset() before this function exits.
|
||||
** In this case, *pzErr may be set to point to an error message
|
||||
** buffer allocated by sqlite3_malloc().
|
||||
*/
|
||||
static void unionReset(int *pRc, sqlite3_stmt *pStmt, char **pzErr){
|
||||
int rc = sqlite3_reset(pStmt);
|
||||
if( *pRc==SQLITE_OK ){
|
||||
@ -161,6 +226,11 @@ static void unionReset(int *pRc, sqlite3_stmt *pStmt, char **pzErr){
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Call sqlite3_finalize() on SQL statement pStmt. If *pRc is set to
|
||||
** SQLITE_OK when this function is called, then it is set to the
|
||||
** value returned by sqlite3_finalize() before this function exits.
|
||||
*/
|
||||
static void unionFinalize(int *pRc, sqlite3_stmt *pStmt){
|
||||
int rc = sqlite3_finalize(pStmt);
|
||||
if( *pRc==SQLITE_OK ) *pRc = rc;
|
||||
@ -183,32 +253,72 @@ static int unionDisconnect(sqlite3_vtab *pVtab){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is a no-op if *pRc is other than SQLITE_OK when it is
|
||||
** called. In this case it returns NULL.
|
||||
**
|
||||
** Otherwise, this function checks that the source table passed as the
|
||||
** second argument (a) exists, (b) is not a view and (c) has a column
|
||||
** named "_rowid_" of type "integer" that is the primary key.
|
||||
** If this is not the case, *pRc is set to SQLITE_ERROR and NULL is
|
||||
** returned.
|
||||
**
|
||||
** Finally, if the source table passes the checks above, a nul-terminated
|
||||
** string describing the column names and types belonging to the source
|
||||
** table is returned. Tables with the same set of column names and types
|
||||
** cause this function to return identical strings. Is is the responsibility
|
||||
** of the caller to free the returned string using sqlite3_free() when
|
||||
** it is no longer required.
|
||||
*/
|
||||
static char *unionSourceToStr(
|
||||
int *pRc,
|
||||
UnionSrc *pSrc,
|
||||
int *pRc, /* IN/OUT: Error code */
|
||||
sqlite3 *db, /* Database handle */
|
||||
UnionSrc *pSrc, /* Source table to test */
|
||||
sqlite3_stmt *pStmt,
|
||||
char **pzErr
|
||||
char **pzErr /* OUT: Error message */
|
||||
){
|
||||
char *zRet = 0;
|
||||
if( *pRc==SQLITE_OK ){
|
||||
sqlite3_bind_text(pStmt, 1, pSrc->zTab, -1, SQLITE_STATIC);
|
||||
sqlite3_bind_text(pStmt, 2, pSrc->zDb, -1, SQLITE_STATIC);
|
||||
if( SQLITE_ROW==sqlite3_step(pStmt) ){
|
||||
zRet = unionStrdup(pRc, (const char*)sqlite3_column_text(pStmt, 0));
|
||||
}
|
||||
unionReset(pRc, pStmt, pzErr);
|
||||
if( *pRc==SQLITE_OK && zRet==0 ){
|
||||
*pRc = SQLITE_ERROR;
|
||||
*pzErr = sqlite3_mprintf("no such table: %s%s%s",
|
||||
int bPk = 0;
|
||||
const char *zType = 0;
|
||||
|
||||
int rc = sqlite3_table_column_metadata(
|
||||
db, pSrc->zDb, pSrc->zTab, "_rowid_", &zType, 0, 0, &bPk, 0
|
||||
);
|
||||
if( rc==SQLITE_ERROR
|
||||
|| (rc==SQLITE_OK && (!bPk || sqlite3_stricmp("integer", zType)))
|
||||
){
|
||||
rc = SQLITE_ERROR;
|
||||
*pzErr = sqlite3_mprintf("no such rowid table: %s%s%s",
|
||||
(pSrc->zDb ? pSrc->zDb : ""),
|
||||
(pSrc->zDb ? "." : ""),
|
||||
pSrc->zTab
|
||||
);
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
sqlite3_bind_text(pStmt, 1, pSrc->zTab, -1, SQLITE_STATIC);
|
||||
sqlite3_bind_text(pStmt, 2, pSrc->zDb, -1, SQLITE_STATIC);
|
||||
if( SQLITE_ROW==sqlite3_step(pStmt) ){
|
||||
zRet = unionStrdup(&rc, (const char*)sqlite3_column_text(pStmt, 0));
|
||||
}
|
||||
unionReset(&rc, pStmt, pzErr);
|
||||
}
|
||||
|
||||
*pRc = rc;
|
||||
}
|
||||
|
||||
return zRet;
|
||||
}
|
||||
|
||||
/*
|
||||
** Check that all configured source tables exist and have the same column
|
||||
** names and datatypes. If this is not the case, or if some other error
|
||||
** occurs, return an SQLite error code. In this case *pzErr may be set
|
||||
** to point to an error message buffer allocated by sqlite3_mprintf().
|
||||
** Or, if no problems regarding the source tables are detected and no
|
||||
** other error occurs, SQLITE_OK is returned.
|
||||
*/
|
||||
static int unionSourceCheck(UnionTab *pTab, char **pzErr){
|
||||
const char *zSql =
|
||||
"SELECT group_concat(quote(name) || '.' || quote(type)) "
|
||||
@ -225,10 +335,10 @@ static int unionSourceCheck(UnionTab *pTab, char **pzErr){
|
||||
|
||||
pStmt = unionPrepare(&rc, pTab->db, zSql, pzErr);
|
||||
if( rc==SQLITE_OK ){
|
||||
z0 = unionSourceToStr(&rc, &pTab->aSrc[0], pStmt, pzErr);
|
||||
z0 = unionSourceToStr(&rc, pTab->db, &pTab->aSrc[0], pStmt, pzErr);
|
||||
}
|
||||
for(i=1; i<pTab->nSrc; i++){
|
||||
char *z = unionSourceToStr(&rc, &pTab->aSrc[i], pStmt, pzErr);
|
||||
char *z = unionSourceToStr(&rc, pTab->db, &pTab->aSrc[i], pStmt, pzErr);
|
||||
if( rc==SQLITE_OK && sqlite3_stricmp(z, z0) ){
|
||||
*pzErr = sqlite3_mprintf("source table schema mismatch");
|
||||
rc = SQLITE_ERROR;
|
||||
@ -272,21 +382,29 @@ static int unionConnect(
|
||||
}else{
|
||||
int nAlloc = 0; /* Allocated size of pTab->aSrc[] */
|
||||
sqlite3_stmt *pStmt = 0; /* Argument statement */
|
||||
char *zSql1 = unionStrdup(&rc, argv[3]);
|
||||
char *zSql2 = 0;
|
||||
char *zSql = 0; /* SQL statement */
|
||||
char *zArg = unionStrdup(&rc, argv[3]); /* Copy of argument to CVT */
|
||||
|
||||
if( zSql1 ){
|
||||
unionDequote(zSql1);
|
||||
zSql2 = sqlite3_mprintf("SELECT * FROM (%s) ORDER BY 3", zSql1);
|
||||
sqlite3_free(zSql1);
|
||||
zSql1 = 0;
|
||||
/* Prepare the SQL statement. Instead of executing it directly, sort
|
||||
** the results by the "minimum rowid" field. This makes it easier to
|
||||
** check that there are no rowid range overlaps between source tables
|
||||
** and that the UnionTab.aSrc[] array is always sorted by rowid. */
|
||||
if( zArg ){
|
||||
unionDequote(zArg);
|
||||
zSql = sqlite3_mprintf("SELECT * FROM (%s) ORDER BY 3", zArg);
|
||||
sqlite3_free(zArg);
|
||||
zArg = 0;
|
||||
}
|
||||
if( zSql2==0 ){
|
||||
if( zSql==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}
|
||||
pTab = unionMalloc(&rc, sizeof(UnionTab));
|
||||
pStmt = unionPrepare(&rc, db, zSql2, pzErr);
|
||||
pStmt = unionPrepare(&rc, db, zSql, pzErr);
|
||||
|
||||
/* Allocate the UnionTab structure */
|
||||
pTab = unionMalloc(&rc, sizeof(UnionTab));
|
||||
|
||||
/* Iterate through the rows returned by the SQL statement specified
|
||||
** as an argument to the CREATE VIRTUAL TABLE statement. */
|
||||
while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){
|
||||
const char *zDb = (const char*)sqlite3_column_text(pStmt, 0);
|
||||
const char *zTab = (const char*)sqlite3_column_text(pStmt, 1);
|
||||
@ -294,6 +412,7 @@ static int unionConnect(
|
||||
sqlite3_int64 iMax = sqlite3_column_int64(pStmt, 3);
|
||||
UnionSrc *pSrc;
|
||||
|
||||
/* Grow the pTab->aSrc[] array if required. */
|
||||
if( nAlloc<=pTab->nSrc ){
|
||||
int nNew = nAlloc ? nAlloc*2 : 8;
|
||||
UnionSrc *aNew = (UnionSrc*)sqlite3_realloc(
|
||||
@ -309,6 +428,7 @@ static int unionConnect(
|
||||
}
|
||||
}
|
||||
|
||||
/* Check for problems with the specified range of rowids */
|
||||
if( iMax<iMin || (pTab->nSrc>0 && iMin<=pTab->aSrc[pTab->nSrc-1].iMax) ){
|
||||
*pzErr = sqlite3_mprintf("rowid range mismatch error");
|
||||
rc = SQLITE_ERROR;
|
||||
@ -322,10 +442,10 @@ static int unionConnect(
|
||||
}
|
||||
unionFinalize(&rc, pStmt);
|
||||
pStmt = 0;
|
||||
sqlite3_free(zSql1);
|
||||
sqlite3_free(zSql2);
|
||||
zSql1 = 0;
|
||||
zSql2 = 0;
|
||||
sqlite3_free(zArg);
|
||||
sqlite3_free(zSql);
|
||||
zArg = 0;
|
||||
zSql = 0;
|
||||
|
||||
/* Verify that all source tables exist and have compatible schemas. */
|
||||
if( rc==SQLITE_OK ){
|
||||
@ -335,16 +455,16 @@ static int unionConnect(
|
||||
|
||||
/* Compose a CREATE TABLE statement and pass it to declare_vtab() */
|
||||
if( rc==SQLITE_OK ){
|
||||
zSql1 = sqlite3_mprintf("SELECT "
|
||||
zSql = sqlite3_mprintf("SELECT "
|
||||
"'CREATE TABLE xyz('"
|
||||
" || group_concat(quote(name) || ' ' || type, ', ')"
|
||||
" || ')'"
|
||||
"FROM pragma_table_info(%Q, ?)",
|
||||
pTab->aSrc[0].zTab
|
||||
);
|
||||
if( zSql1==0 ) rc = SQLITE_NOMEM;
|
||||
if( zSql==0 ) rc = SQLITE_NOMEM;
|
||||
}
|
||||
pStmt = unionPrepare(&rc, db, zSql1, pzErr);
|
||||
pStmt = unionPrepare(&rc, db, zSql, pzErr);
|
||||
if( rc==SQLITE_OK ){
|
||||
sqlite3_bind_text(pStmt, 1, pTab->aSrc[0].zDb, -1, SQLITE_STATIC);
|
||||
if( SQLITE_ROW==sqlite3_step(pStmt) ){
|
||||
@ -354,7 +474,7 @@ static int unionConnect(
|
||||
}
|
||||
|
||||
unionFinalize(&rc, pStmt);
|
||||
sqlite3_free(zSql1);
|
||||
sqlite3_free(zSql);
|
||||
}
|
||||
|
||||
if( rc!=SQLITE_OK ){
|
||||
@ -448,36 +568,59 @@ static int unionFilter(
|
||||
int rc = SQLITE_OK;
|
||||
int i;
|
||||
char *zSql = 0;
|
||||
int bZero = 0;
|
||||
|
||||
int bMinValid = 0;
|
||||
int bMaxValid = 0;
|
||||
sqlite3_int64 iMin;
|
||||
sqlite3_int64 iMax;
|
||||
sqlite3_int64 iMin = SMALLEST_INT64;
|
||||
sqlite3_int64 iMax = LARGEST_INT64;
|
||||
|
||||
assert( idxNum==0
|
||||
|| idxNum==SQLITE_INDEX_CONSTRAINT_EQ
|
||||
|| idxNum==SQLITE_INDEX_CONSTRAINT_LE
|
||||
|| idxNum==SQLITE_INDEX_CONSTRAINT_GE
|
||||
|| idxNum==SQLITE_INDEX_CONSTRAINT_LT
|
||||
|| idxNum==SQLITE_INDEX_CONSTRAINT_GT
|
||||
|| idxNum==(SQLITE_INDEX_CONSTRAINT_GE|SQLITE_INDEX_CONSTRAINT_LE)
|
||||
);
|
||||
|
||||
if( idxNum==SQLITE_INDEX_CONSTRAINT_EQ ){
|
||||
assert( argc==1 );
|
||||
iMin = iMax = sqlite3_value_int64(argv[0]);
|
||||
bMinValid = bMaxValid = 1;
|
||||
}else{
|
||||
if( idxNum & SQLITE_INDEX_CONSTRAINT_LE ){
|
||||
|
||||
if( idxNum & (SQLITE_INDEX_CONSTRAINT_LE|SQLITE_INDEX_CONSTRAINT_LT) ){
|
||||
assert( argc>=1 );
|
||||
iMax = sqlite3_value_int64(argv[0]);
|
||||
bMaxValid = 1;
|
||||
if( idxNum & SQLITE_INDEX_CONSTRAINT_LT ){
|
||||
if( iMax==SMALLEST_INT64 ){
|
||||
bZero = 1;
|
||||
}else{
|
||||
iMax--;
|
||||
}
|
||||
}
|
||||
}
|
||||
if( idxNum & SQLITE_INDEX_CONSTRAINT_GE ){
|
||||
|
||||
if( idxNum & (SQLITE_INDEX_CONSTRAINT_GE|SQLITE_INDEX_CONSTRAINT_GT) ){
|
||||
assert( argc>=1 );
|
||||
iMin = sqlite3_value_int64(argv[argc-1]);
|
||||
bMinValid = 1;
|
||||
if( idxNum & SQLITE_INDEX_CONSTRAINT_GT ){
|
||||
if( iMin==LARGEST_INT64 ){
|
||||
bZero = 1;
|
||||
}else{
|
||||
iMin++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
sqlite3_finalize(pCsr->pStmt);
|
||||
pCsr->pStmt = 0;
|
||||
if( bZero ){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
for(i=0; i<pTab->nSrc; i++){
|
||||
UnionSrc *pSrc = &pTab->aSrc[i];
|
||||
if( (bMinValid && iMin>pSrc->iMax) || (bMaxValid && iMax<pSrc->iMin) ){
|
||||
if( iMin>pSrc->iMax || iMax<pSrc->iMin ){
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -495,15 +638,15 @@ static int unionFilter(
|
||||
}
|
||||
|
||||
if( zSql ){
|
||||
if( bMinValid && bMaxValid && iMin==iMax ){
|
||||
if( iMin==iMax ){
|
||||
zSql = sqlite3_mprintf("%z WHERE rowid=%lld", zSql, iMin);
|
||||
}else{
|
||||
const char *zWhere = "WHERE";
|
||||
if( bMinValid && iMin>pSrc->iMin ){
|
||||
if( iMin!=SMALLEST_INT64 && iMin>pSrc->iMin ){
|
||||
zSql = sqlite3_mprintf("%z WHERE rowid>=%lld", zSql, iMin);
|
||||
zWhere = "AND";
|
||||
}
|
||||
if( bMaxValid && iMax<pSrc->iMax ){
|
||||
if( iMax!=LARGEST_INT64 && iMax<pSrc->iMax ){
|
||||
zSql = sqlite3_mprintf("%z %s rowid<=%lld", zSql, zWhere, iMax);
|
||||
}
|
||||
}
|
||||
@ -521,6 +664,21 @@ static int unionFilter(
|
||||
|
||||
/*
|
||||
** xBestIndex.
|
||||
**
|
||||
** This implementation searches for constraints on the rowid field. EQ,
|
||||
** LE, LT, GE and GT are handled.
|
||||
**
|
||||
** If there is an EQ comparison, then idxNum is set to INDEX_CONSTRAINT_EQ.
|
||||
** In this case the only argument passed to xFilter is the rhs of the ==
|
||||
** operator.
|
||||
**
|
||||
** Otherwise, if an LE or LT constraint is found, then the INDEX_CONSTRAINT_LE
|
||||
** or INDEX_CONSTRAINT_LT (but not both) bit is set in idxNum. The first
|
||||
** argument to xFilter is the rhs of the <= or < operator. Similarly, if
|
||||
** an GE or GT constraint is found, then the INDEX_CONSTRAINT_GE or
|
||||
** INDEX_CONSTRAINT_GT bit is set in idxNum. The rhs of the >= or > operator
|
||||
** is passed as either the first or second argument to xFilter, depending
|
||||
** on whether or not there is also a LT|LE constraint.
|
||||
*/
|
||||
static int unionBestIndex(
|
||||
sqlite3_vtab *tab,
|
||||
@ -556,6 +714,7 @@ static int unionBestIndex(
|
||||
pIdxInfo->estimatedCost = 3.0;
|
||||
pIdxInfo->idxNum = SQLITE_INDEX_CONSTRAINT_EQ;
|
||||
pIdxInfo->aConstraintUsage[iEq].argvIndex = 1;
|
||||
pIdxInfo->aConstraintUsage[iEq].omit = 1;
|
||||
}else{
|
||||
int iCons = 1;
|
||||
int idxNum = 0;
|
||||
@ -563,12 +722,14 @@ static int unionBestIndex(
|
||||
if( iLt>=0 ){
|
||||
nRow = nRow / 2;
|
||||
pIdxInfo->aConstraintUsage[iLt].argvIndex = iCons++;
|
||||
idxNum |= SQLITE_INDEX_CONSTRAINT_LE;
|
||||
pIdxInfo->aConstraintUsage[iLt].omit = 1;
|
||||
idxNum |= pIdxInfo->aConstraint[iLt].op;
|
||||
}
|
||||
if( iGt>=0 ){
|
||||
nRow = nRow / 2;
|
||||
pIdxInfo->aConstraintUsage[iGt].argvIndex = iCons++;
|
||||
idxNum |= SQLITE_INDEX_CONSTRAINT_GE;
|
||||
pIdxInfo->aConstraintUsage[iGt].omit = 1;
|
||||
idxNum |= pIdxInfo->aConstraint[iGt].op;
|
||||
}
|
||||
pIdxInfo->estimatedRows = nRow;
|
||||
pIdxInfo->estimatedCost = 3.0 * (double)nRow;
|
||||
@ -578,6 +739,9 @@ static int unionBestIndex(
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Register the unionvtab virtual table module with database handle db.
|
||||
*/
|
||||
static int createUnionVtab(sqlite3 *db){
|
||||
static sqlite3_module unionModule = {
|
||||
0, /* iVersion */
|
||||
@ -623,4 +787,3 @@ int sqlite3_unionvtab_init(
|
||||
return rc;
|
||||
}
|
||||
|
||||
#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_UNIONVTAB) */
|
||||
|
14
manifest
14
manifest
@ -1,5 +1,5 @@
|
||||
C Add\sthe\s"unionvtab"\svirtual\stable\sextension\sin\sext/misc/unionvtab.c.
|
||||
D 2017-07-15T20:48:30.280
|
||||
C Enhance\serror\sdetection\sand\sfix\sother\sissues\sin\sunionvtab\scode.
|
||||
D 2017-07-17T20:25:21.632
|
||||
F Makefile.in d9873c9925917cca9990ee24be17eb9613a668012c85a343aef7e5536ae266e8
|
||||
F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434
|
||||
F Makefile.msc 20850e3e8d4d4791e0531955852d768eb06f24138214870d543abb1a47346fba
|
||||
@ -281,7 +281,7 @@ F ext/misc/showauth.c 732578f0fe4ce42d577e1c86dc89dd14a006ab52
|
||||
F ext/misc/spellfix.c a4723b6aff748a417b5091b68a46443265c40f0d
|
||||
F ext/misc/stmt.c 6f16443abb3551e3f5813bb13ba19a30e7032830015b0f92fe0c0453045c0a11
|
||||
F ext/misc/totype.c 4a167594e791abeed95e0a8db028822b5e8fe512
|
||||
F ext/misc/unionvtab.c 594b8cebd11297c45d87fed5985548fa141d917f08e672aadea06c14f18bcf42
|
||||
F ext/misc/unionvtab.c e5b0f62563839da45952be404b80ba592a1183fae8c6976036177edc6c713ef7
|
||||
F ext/misc/vfslog.c fe40fab5c077a40477f7e5eba994309ecac6cc95
|
||||
F ext/misc/vfsstat.c bf10ef0bc51e1ad6756629e1edb142f7a8db1178
|
||||
F ext/misc/vtshim.c 1976e6dd68dd0d64508c91a6dfab8e75f8aaf6cd
|
||||
@ -1432,7 +1432,7 @@ F test/tt3_vacuum.c 1753f45917699c9c1f66b64c717a717c9379f776
|
||||
F test/types.test bf816ce73c7dfcfe26b700c19f97ef4050d194ff
|
||||
F test/types2.test 1aeb81976841a91eef292723649b5c4fe3bc3cac
|
||||
F test/types3.test 99e009491a54f4dc02c06bdbc0c5eea56ae3e25a
|
||||
F test/unionvtab.test c206279d350f473da61daeeaa58637d31a691ad7e7ba1d8826d5f5ea7545676d
|
||||
F test/unionvtab.test 41b5d8c843ed4968aa20ec5f25ab501256a81515952846ed6e1d48e4ece09f7e
|
||||
F test/unique.test 93f8b2ef5ea51b9495f8d6493429b1fd0f465264
|
||||
F test/unique2.test 3674e9f2a3f1fbbfd4772ac74b7a97090d0f77d2
|
||||
F test/unixexcl.test d936ba2b06794018e136418addd59a2354eeae97
|
||||
@ -1635,7 +1635,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
|
||||
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
|
||||
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
|
||||
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
|
||||
P 253945d480b052bfe311888022b5eb0be91c8c80cda05036e58207d57520262c
|
||||
R ee0a9dc5b1464f766485894fa2069cae
|
||||
P 62a86aa6c0519cf1fa232169122d3d6ae8d2f66b20530fb934a82a15712bd2f0
|
||||
R 84247a701b2728604bb146e760fbf4dd
|
||||
U dan
|
||||
Z d195e18cbc4e4f34f8cf4b9b0b0c8aaa
|
||||
Z fe159fd3b9fb252212619f29108c42d6
|
||||
|
@ -1 +1 @@
|
||||
62a86aa6c0519cf1fa232169122d3d6ae8d2f66b20530fb934a82a15712bd2f0
|
||||
9c3f1b9a82e500e015deb0cc669fbb32e7f0cdc69f926ceff383ab946f8d8d18
|
@ -98,13 +98,13 @@ do_catchsql_test 2.1.3 {
|
||||
|
||||
do_catchsql_test 2.2.1 {
|
||||
CREATE VIRTUAL TABLE temp.u1 USING unionvtab("VALUES(NULL, 't555', 1, 100)");
|
||||
} {1 {no such table: t555}}
|
||||
} {1 {no such rowid table: t555}}
|
||||
do_catchsql_test 2.2.2 {
|
||||
CREATE VIRTUAL TABLE temp.u1 USING unionvtab("VALUES('aux', 't555', 1, 100)");
|
||||
} {1 {no such table: aux.t555}}
|
||||
} {1 {no such rowid table: aux.t555}}
|
||||
do_catchsql_test 2.2.3 {
|
||||
CREATE VIRTUAL TABLE temp.u1 USING unionvtab("VALUES('xua', 't555', 1, 100)");
|
||||
} {1 {unknown database 'xua'}}
|
||||
} {1 {no such rowid table: xua.t555}}
|
||||
|
||||
do_execsql_test 2.4.0 {
|
||||
CREATE TABLE x1(a BLOB, b);
|
||||
|
Loading…
Reference in New Issue
Block a user