1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-10-27 08:52:26 +03:00

Add first cut of sqlite3_declare_vtab(). Not at all well tested yet. (CVS 3213)

FossilOrigin-Name: bbeb93b5bb26ba83ee7b7ae439ca5ceebebac9a0
This commit is contained in:
danielk1977
2006-06-12 11:24:37 +00:00
parent 78efaba10e
commit 7e6ebfb246
9 changed files with 225 additions and 45 deletions

View File

@@ -22,7 +22,7 @@
** COMMIT
** ROLLBACK
**
** $Id: build.c,v 1.396 2006/06/11 23:41:55 drh Exp $
** $Id: build.c,v 1.397 2006/06/12 11:24:37 danielk1977 Exp $
*/
#include "sqliteInt.h"
#include <ctype.h>
@@ -782,22 +782,28 @@ void sqlite3StartTable(
/* Make sure the new table name does not collide with an existing
** index or table name in the same database. Issue an error message if
** it does.
** it does. The exception is if the statement being parsed was passed
** to an sqlite3_declare_vtab() call. In that case only the column names
** and types will be used, so there is no need to test for namespace
** collisions.
*/
if( SQLITE_OK!=sqlite3ReadSchema(pParse) ){
goto begin_table_error;
}
pTable = sqlite3FindTable(db, zName, db->aDb[iDb].zName);
if( pTable ){
if( !noErr ){
sqlite3ErrorMsg(pParse, "table %T already exists", pName);
if( !IN_DECLARE_VTAB ){
if( SQLITE_OK!=sqlite3ReadSchema(pParse) ){
goto begin_table_error;
}
pTable = sqlite3FindTable(db, zName, db->aDb[iDb].zName);
if( pTable ){
if( !noErr ){
sqlite3ErrorMsg(pParse, "table %T already exists", pName);
}
goto begin_table_error;
}
if( sqlite3FindIndex(db, zName, 0)!=0 && (iDb==0 || !db->init.busy) ){
sqlite3ErrorMsg(pParse, "there is already an index named %s", zName);
goto begin_table_error;
}
goto begin_table_error;
}
if( sqlite3FindIndex(db, zName, 0)!=0 && (iDb==0 || !db->init.busy) ){
sqlite3ErrorMsg(pParse, "there is already an index named %s", zName);
goto begin_table_error;
}
pTable = sqliteMalloc( sizeof(Table) );
if( pTable==0 ){
pParse->rc = SQLITE_NOMEM;
@@ -1649,6 +1655,9 @@ int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){
** already known.
*/
if( pTable->nCol>0 ) return 0;
#ifndef SQLITE_OMIT_VIRTUALTABLE
if( pTable->isVirtual ) return 0;
#endif
/* A negative nCol is a special marker meaning that we are currently
** trying to compute the column names. If we enter this routine with

View File

@@ -12,7 +12,7 @@
** This header file defines the interface that the SQLite library
** presents to client programs.
**
** @(#) $Id: sqlite.h.in,v 1.168 2006/06/11 23:41:56 drh Exp $
** @(#) $Id: sqlite.h.in,v 1.169 2006/06/12 11:24:37 danielk1977 Exp $
*/
#ifndef _SQLITE3_H_
#define _SQLITE3_H_
@@ -1575,6 +1575,7 @@ int sqlite3_create_module(
const sqlite3_module *
);
int sqlite3_declare_vtab(sqlite3*, const char *zCreateTable);
/*
** Undo the hack that converts floating point types to integer for

View File

@@ -11,7 +11,7 @@
*************************************************************************
** Internal interface definitions for SQLite.
**
** @(#) $Id: sqliteInt.h,v 1.498 2006/06/12 06:09:18 danielk1977 Exp $
** @(#) $Id: sqliteInt.h,v 1.499 2006/06/12 11:24:37 danielk1977 Exp $
*/
#ifndef _SQLITEINT_H_
#define _SQLITEINT_H_
@@ -492,6 +492,7 @@ struct sqlite3 {
#endif
#ifndef SQLITE_OMIT_VIRTUALTABLE
Hash aModule; /* populated by sqlite3_create_module() */
Table *pVTab; /* vtab with active Connect/Create method */
#endif
Hash aFunc; /* All functions that can be in SQL exprs */
Hash aCollSeq; /* All collating sequences */
@@ -1283,9 +1284,16 @@ struct Parse {
int nArgAlloc; /* Number of bytes allocated for zArg[] */
int nArgUsed; /* Number of bytes of zArg[] used so far */
char *zArg; /* Complete text of a module argument */
u8 declareVtab; /* True if inside sqlite3_declare_vtab() */
#endif
};
#ifdef SQLITE_OMIT_VIRTUALTABLE
#define IN_DECLARE_VTAB 0
#else
#define IN_DECLARE_VTAB (pParse->declareVtab)
#endif
/*
** An instance of the following structure can be declared on a stack and used
** to save the Parse.zAuthContext value so that it can be restored later.
@@ -1791,6 +1799,7 @@ void sqlite3VtabFinishParse(Parse*, Token*);
void sqlite3VtabArgInit(Parse*);
void sqlite3VtabArgExtend(Parse*, Token*);
int sqlite3VtabCallCreate(sqlite3*, int, const char *, char **);
int sqlite3VtabCallConnect(Parse*, Table*);
#ifdef SQLITE_SSE
#include "sseInt.h"

View File

@@ -13,7 +13,7 @@
** is not included in the SQLite library. It is used for automated
** testing of the SQLite library.
**
** $Id: test8.c,v 1.2 2006/06/12 06:09:19 danielk1977 Exp $
** $Id: test8.c,v 1.3 2006/06/12 11:24:37 danielk1977 Exp $
*/
#include "sqliteInt.h"
#include "tcl.h"
@@ -30,6 +30,44 @@ static void appendToEchoModule(const sqlite3_module *pModule, const char *zArg){
Tcl_SetVar((Tcl_Interp *)(pModule->pAux), "echo_module", zArg, flags);
}
/*
** This function is called from within the echo-modules xCreate and
** xConnect methods. The argc and argv arguments are copies of those
** passed to the calling method. This function is responsible for
** calling sqlite3_declare_vtab() to declare the schema of the virtual
** table being created or connected.
**
** If the constructor was passed just one argument, i.e.:
**
** CREATE TABLE t1 AS echo(t2);
**
** Then t2 is assumed to be the name of a *real* database table. The
** schema of the virtual table is declared by passing a copy of the
** CREATE TABLE statement for the real table to sqlite3_declare_vtab().
** Hence, the virtual table should have exactly the same column names and
** types as the real table.
*/
static int echoDeclareVtab(sqlite3 *db, int argc, char **argv){
int rc = SQLITE_OK;
if( argc==2 ){
sqlite3_stmt *pStmt = 0;
sqlite3_prepare(db,
"SELECT sql FROM sqlite_master WHERE type = 'table' AND name = ?",
-1, &pStmt, 0);
sqlite3_bind_text(pStmt, 1, argv[1], -1, 0);
if( sqlite3_step(pStmt)==SQLITE_ROW ){
const char *zCreateTable = sqlite3_column_text(pStmt, 0);
sqlite3_declare_vtab(db, zCreateTable);
} else {
rc = SQLITE_ERROR;
}
sqlite3_finalize(pStmt);
}
return rc;
}
/* Methods for the echo module */
static int echoCreate(
sqlite3 *db,
@@ -38,7 +76,6 @@ static int echoCreate(
sqlite3_vtab **ppVtab
){
int i;
Tcl_Interp *interp = pModule->pAux;
*ppVtab = pModule->pAux;
appendToEchoModule(pModule, "xCreate");
@@ -46,6 +83,7 @@ static int echoCreate(
appendToEchoModule(pModule, argv[i]);
}
echoDeclareVtab(db, argc, argv);
return 0;
}
static int echoConnect(
@@ -63,6 +101,8 @@ static int echoConnect(
Tcl_SetVar(interp, "echo_module", argv[i],
TCL_APPEND_VALUE | TCL_LIST_ELEMENT | TCL_GLOBAL_ONLY);
}
echoDeclareVtab(db, argc, argv);
return 0;
}
static int echoDisconnect(sqlite3_vtab *pVtab){

View File

@@ -15,7 +15,7 @@
** individual tokens and sends those tokens one-by-one over to the
** parser for analysis.
**
** $Id: tokenize.c,v 1.119 2006/06/11 23:41:56 drh Exp $
** $Id: tokenize.c,v 1.120 2006/06/12 11:24:37 danielk1977 Exp $
*/
#include "sqliteInt.h"
#include "os.h"
@@ -483,10 +483,19 @@ abort_parse:
pParse->nTableLock = 0;
}
#endif
#ifndef SQLITE_OMIT_VIRTUALTABLE
sqliteFree(pParse->zArg);
#endif
sqlite3DeleteTable(pParse->db, pParse->pNewTable);
if( !IN_DECLARE_VTAB ){
/* If the pParse->declareVtab flag is set, do not delete any table
** structure built up in pParse->pNewTable. The calling code (see vtab.c)
** will take responsibility for freeing the Table structure.
*/
sqlite3DeleteTable(pParse->db, pParse->pNewTable);
}
sqlite3DeleteTrigger(pParse->pNewTrigger);
sqliteFree(pParse->apVarExpr);
if( nErr>0 && (pParse->rc==SQLITE_OK || pParse->rc==SQLITE_DONE) ){

View File

@@ -11,7 +11,7 @@
*************************************************************************
** This file contains code used to help implement virtual tables.
**
** $Id: vtab.c,v 1.2 2006/06/12 06:09:19 danielk1977 Exp $
** $Id: vtab.c,v 1.3 2006/06/12 11:24:37 danielk1977 Exp $
*/
#ifndef SQLITE_OMIT_VIRTUALTABLE
#include "sqliteInt.h"
@@ -128,7 +128,6 @@ void sqlite3VtabFinishParse(Parse *pParse, Token *pEnd){
if( pTab==0 ) return;
db = pParse->db;
if( pTab->nModuleArg<1 ) return;
pParse->pNewTable = 0;
zModule = pTab->azModuleArg[0];
pTab->pModule = (sqlite3_module*)sqlite3HashFind(&db->aModule,
zModule, strlen(zModule));
@@ -188,14 +187,10 @@ void sqlite3VtabFinishParse(Parse *pParse, Token *pEnd){
**
** TODO: If the module is already registered, should we call xConnect()
** here, or should it wait until the table is first referenced. Maybe
** it's better to be lazy here, in case xConnect() is expensive to call.
** it's better to be lazy here, in case xConnect() is expensive to call
** and the schema is reparsed a number of times.
*/
else {
#if 0
sqlite3_module *pMod = pTab->pModule;
assert( pMod->xConnect );
pMod->xConnect(db, pMod, pTab->nModuleArg, pTab->azModuleArg, &pTab->pVtab);
#endif
Table *pOld;
Schema *pSchema = pTab->pSchema;
const char *zName = pTab->zName;
@@ -205,6 +200,7 @@ void sqlite3VtabFinishParse(Parse *pParse, Token *pEnd){
assert( pTab==pOld ); /* Malloc must have failed inside HashInsert() */
return;
}
pParse->pNewTable = 0;
}
}
@@ -238,6 +234,84 @@ void sqlite3VtabArgExtend(Parse *pParse, Token *p){
pParse->zArg[pParse->nArgUsed] = 0;
}
/*
** This function is invoked by the parser to call the xConnect() method
** of table pTab. If an error occurs, an error code is returned and an error
** left in pParse.
*/
int sqlite3VtabCallConnect(Parse *pParse, Table *pTab){
sqlite3_module *pModule;
const char *zModule;
int rc = SQLITE_OK;
assert(pTab && pTab->isVirtual);
if( pTab->pVtab ){
return SQLITE_OK;
}
pModule = pTab->pModule;
zModule = pTab->azModuleArg[0];
if( !pModule || !pModule->xConnect ){
const char *zModule = pTab->azModuleArg[0];
sqlite3ErrorMsg(pParse, "unknown module: %s", zModule);
rc = SQLITE_ERROR;
} else {
char **azArg = pTab->azModuleArg;
int nArg = pTab->nModuleArg;
assert( !pParse->db->pVTab );
pParse->db->pVTab = pTab;
rc = pModule->xConnect(pParse->db, pModule, nArg, azArg, &pTab->pVtab);
pParse->db->pVTab = 0;
if( rc ){
sqlite3ErrorMsg(pParse, "module connect failed: %s", zModule);
}
}
return rc;
}
int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){
Parse sParse;
int rc = SQLITE_OK;
Table *pTab = db->pVTab;
char *zErr = 0;
if( !pTab ){
sqlite3Error(db, SQLITE_MISUSE, 0);
return SQLITE_MISUSE;
}
assert(pTab->isVirtual && pTab->nCol==0 && pTab->aCol==0);
memset(&sParse, 0, sizeof(Parse));
sParse.declareVtab = 1;
sParse.db = db;
if(
SQLITE_OK == sqlite3RunParser(&sParse, zCreateTable, &zErr) &&
sParse.pNewTable &&
!sParse.pNewTable->pSelect &&
!sParse.pNewTable->isVirtual
){
pTab->aCol = sParse.pNewTable->aCol;
pTab->nCol = sParse.pNewTable->nCol;
sParse.pNewTable->nCol = 0;
sParse.pNewTable->aCol = 0;
} else {
sqlite3Error(db, SQLITE_ERROR, zErr);
sqliteFree(zErr);
rc = SQLITE_ERROR;
}
sParse.declareVtab = 0;
sqlite3_finalize((sqlite3_stmt*)sParse.pVdbe);
sqlite3DeleteTable(0, sParse.pNewTable);
sParse.pNewTable = 0;
db->pVTab = 0;
return rc;
}
/*
** This function is invoked by the vdbe to call the xCreate method
** of the virtual table named zTab in database iDb.
@@ -250,11 +324,12 @@ int sqlite3VtabCallCreate(sqlite3 *db, int iDb, const char *zTab, char **pzErr){
int rc = SQLITE_OK;
Table *pTab;
sqlite3_module *pModule;
const char *zModule;
pTab = sqlite3FindTable(db, zTab, db->aDb[iDb].zName);
assert(pTab && pTab->isVirtual);
assert(pTab && pTab->isVirtual && !pTab->pVtab);
pModule = pTab->pModule;
const char *zModule = pTab->azModuleArg[0];
zModule = pTab->azModuleArg[0];
/* If the module has been registered and includes a Create method,
** invoke it now. If the module has not been registered, return an
@@ -264,10 +339,20 @@ int sqlite3VtabCallCreate(sqlite3 *db, int iDb, const char *zTab, char **pzErr){
*pzErr = sqlite3MPrintf("unknown module: %s", zModule);
rc = SQLITE_ERROR;
}else if( pModule->xCreate ){
/* TODO: Maybe the above condition should refer to pTable->needCreate. */
char **azArg = pTab->azModuleArg;
int nArg = pTab->nModuleArg;
assert( !db->pVTab );
db->pVTab = pTab;
rc = sqlite3SafetyOff(db);
assert( rc==SQLITE_OK );
rc = pModule->xCreate(db, pModule, nArg, azArg, &pTab->pVtab);
db->pVTab = 0;
if( rc ){
*pzErr = sqlite3MPrintf("module create failed: %s", zModule);
sqlite3SafetyOn(db);
} else {
rc = sqlite3SafetyOn(db);
}
}
if( SQLITE_OK==rc ){