Hi,
I need (and have attempted to add) limited support for foreign key
constraints mapping to virtual tables. It seems this is not possible
currently in sqlite, or at least, it gives me "foreign key mismatch" when I
try.
My use-case is this:
create virtual table vtbl using vtblXYZ;
create table x(i integer references vtbl(id));
insert into x(i) values(5);
where vtblXYZ is declared:
sqlite3_declare_vtab(db, "CREATE TABLE a(id INTEGER PRIMARY KEY,data TEXT
...
The patch below provides this support, but is limited to mapping to integer
primary keys on the virtual table, and it works as far as I know and am able
to test. My question is, is there a reason why virtual tables
can't/shouldn't be used in this way. Is my approach valid or is it likely
to back-fire!?
Many thanks for your input.
Andy
(This patch is based off version 3.7.10, sorry that its so old!)
Index: sqlite3.c
===================================================================
--- sqlite3.c (revision 9938)
+++ sqlite3.c (working copy)
@@ -83132,7 +83132,7 @@ SQLITE_PRIVATE void sqlite3AddPrimaryKey
Table *pTab = pParse->pNewTable;
char *zType = 0;
int iCol = -1, i;
- if( pTab==0 || IN_DECLARE_VTAB ) goto primary_key_exit;
+ if( pTab==0 ) goto primary_key_exit;
if( pTab->tabFlags & TF_HasPrimaryKey ){
sqlite3ErrorMsg(pParse,
"table \"%s\" has more than one primary key", pTab->zName);
@@ -83169,7 +83169,7 @@ SQLITE_PRIVATE void sqlite3AddPrimaryKey
sqlite3ErrorMsg(pParse, "AUTOINCREMENT is only allowed on an "
"INTEGER PRIMARY KEY");
#endif
- }else{
+ }else if( !IN_DECLARE_VTAB ){
Index *p;
p = sqlite3CreateIndex(pParse, 0, 0, 0, pList, onError, 0, 0,
sortOrder, 0);
if( p ){
@@ -89121,10 +89121,45 @@ static void fkLookupParent(
sqlite3VdbeAddOp3(v, OP_Eq, regData, iOk, regTemp);
}
- sqlite3OpenTable(pParse, iCur, iDb, pTab, OP_OpenRead);
- sqlite3VdbeAddOp3(v, OP_NotExists, iCur, 0, regTemp);
- sqlite3VdbeAddOp2(v, OP_Goto, 0, iOk);
- sqlite3VdbeJumpHere(v, sqlite3VdbeCurrentAddr(v)-2);
+ if (pTab->tabFlags & TF_Virtual) {
+ /* For a INTEGER PRIMARY KEY in a virtual table, we need to build
+ ** an explicit "EXISTS ( SELECT 1 FROM parent WHERE pkey=fkey )"
+ ** expression. */
+ sqlite3* db = pParse->db;
+ Expr* pPIK = sqlite3Expr(db, TK_ID, pFKey->aCol[0].zCol ?: "oid");
/* use ROWID if zCol null */
+ Expr* pFIK = sqlite3Expr(db, TK_REGISTER, 0);
+ if( pFIK ){
+ pFIK->iTable = regTemp;
+ }
+
+ Token tFrom;
+ tFrom.z = pTab->zName;
+ tFrom.n = sqlite3Strlen30(tFrom.z);
+
+ Select* pSelect = sqlite3SelectNew(pParse,
+ sqlite3ExprListAppend(pParse, 0, sqlite3PExpr(pParse,
TK_INTEGER, 0, 0, &sqlite3IntTokens[1])), /* SELECT 1 */
+ sqlite3SrcListAppend(db, 0, &tFrom, 0), /* FROM parent */
+ sqlite3PExpr(pParse, TK_EQ, pPIK, pFIK, 0), /* WHERE pkey=fkey
*/
+ 0, 0, 0, 0,
+ sqlite3PExpr(pParse, TK_INTEGER, 0, 0, &sqlite3IntTokens[1]),
/* LIMIT 1 */
+ 0
+ );
+
+ SelectDest dest;
+ int regTemp2 = sqlite3GetTempReg(pParse);
+ sqlite3SelectDestInit(&dest, SRT_Exists, regTemp2);
+ sqlite3VdbeAddOp2(v, OP_Integer, 0, regTemp2); /* initialise target
register */
+ sqlite3Select(pParse, pSelect, &dest);
+ sqlite3VdbeAddOp3(v, OP_If, regTemp2, iOkNoCur, 0); /* if found,
then ok */
+
+ sqlite3SelectDelete(db, pSelect);
+ sqlite3ReleaseTempReg(pParse, regTemp2);
+ }else{
+ sqlite3OpenTable(pParse, iCur, iDb, pTab, OP_OpenRead);
+ sqlite3VdbeAddOp3(v, OP_NotExists, iCur, 0, regTemp);
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, iOk);
+ sqlite3VdbeJumpHere(v, sqlite3VdbeCurrentAddr(v)-2);
+ }
sqlite3VdbeJumpHere(v, iMustBeInt);
sqlite3ReleaseTempReg(pParse, regTemp);
}else{
@@ -89192,6 +89227,7 @@ static void fkLookupParent(
sqlite3VdbeResolveLabel(v, iOk);
sqlite3VdbeAddOp1(v, OP_Close, iCur);
+ sqlite3VdbeResolveLabel(v, iOkNoCur);
}
/*
@@ -104518,6 +104553,13 @@ SQLITE_API int sqlite3_declare_vtab(sqli
&& (pParse->pNewTable->tabFlags & TF_Virtual)==0
){
if( !pTab->aCol ){
+ if( (pParse->pNewTable->tabFlags & TF_HasPrimaryKey) &&
+ pParse->pNewTable->iPKey >= 0 ){
+ assert( !(pTab->tabFlags & TF_HasPrimaryKey) );
+ pTab->tabFlags |= TF_HasPrimaryKey;
+ pTab->iPKey = pParse->pNewTable->iPKey;
+ pTab->keyConf = pParse->pNewTable->keyConf;
+ }
pTab->aCol = pParse->pNewTable->aCol;
pTab->nCol = pParse->pNewTable->nCol;
pParse->pNewTable->nCol = 0;
_______________________________________________
sqlite-users mailing list
sqlite-users@sqlite.org
http://sqlite.org:8080/cgi-bin/mailman/listinfo/sqlite-users