First cut at supporting CHECK constraints. Everything appears to work,
but much more testing is needed as well as documentation. (CVS 2754) FossilOrigin-Name: 2313d912baeca0fd516d524f16708953de483729
This commit is contained in:
+44
-1
@@ -22,7 +22,7 @@
|
||||
** COMMIT
|
||||
** ROLLBACK
|
||||
**
|
||||
** $Id: build.c,v 1.352 2005/11/01 15:48:24 drh Exp $
|
||||
** $Id: build.c,v 1.353 2005/11/03 00:41:17 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include <ctype.h>
|
||||
@@ -456,6 +456,9 @@ void sqlite3DeleteTable(sqlite3 *db, Table *pTable){
|
||||
sqliteFree(pTable->zName);
|
||||
sqliteFree(pTable->zColAff);
|
||||
sqlite3SelectDelete(pTable->pSelect);
|
||||
#ifndef SQLITE_OMIT_CHECK
|
||||
sqlite3ExprDelete(pTable->pCheck);
|
||||
#endif
|
||||
sqliteFree(pTable);
|
||||
}
|
||||
|
||||
@@ -1047,6 +1050,25 @@ primary_key_exit:
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
** Add a new CHECK constraint to the table currently under construction.
|
||||
*/
|
||||
void sqlite3AddCheckConstraint(
|
||||
Parse *pParse, /* Parsing context */
|
||||
Expr *pCheckExpr /* The check expression */
|
||||
){
|
||||
#ifndef SQLITE_OMIT_CHECK
|
||||
Table *pTab = pParse->pNewTable;
|
||||
if( pTab ){
|
||||
/* The CHECK expression must be duplicated so that tokens refer
|
||||
** to malloced space and not the (ephemeral) text of the CREATE TABLE
|
||||
** statement */
|
||||
pTab->pCheck = sqlite3ExprAnd(pTab->pCheck, sqlite3ExprDup(pCheckExpr));
|
||||
}
|
||||
#endif
|
||||
sqlite3ExprDelete(pCheckExpr);
|
||||
}
|
||||
|
||||
/*
|
||||
** Set the collation function of the most recently parsed table column
|
||||
** to the CollSeq given.
|
||||
@@ -1270,6 +1292,27 @@ void sqlite3EndTable(
|
||||
|
||||
assert( !db->init.busy || !pSelect );
|
||||
|
||||
#ifndef SQLITE_OMIT_CHECK
|
||||
/* Resolve names in all CHECK constraint expressions.
|
||||
*/
|
||||
if( p->pCheck ){
|
||||
SrcList sSrc; /* Fake SrcList for pParse->pNewTable */
|
||||
NameContext sNC; /* Name context for pParse->pNewTable */
|
||||
|
||||
memset(&sNC, 0, sizeof(sNC));
|
||||
memset(&sSrc, 0, sizeof(sSrc));
|
||||
sSrc.nSrc = 1;
|
||||
sSrc.a[0].zName = p->zName;
|
||||
sSrc.a[0].pTab = p;
|
||||
sSrc.a[0].iCursor = -1;
|
||||
sNC.pParse = pParse;
|
||||
sNC.pSrcList = &sSrc;
|
||||
if( sqlite3ExprResolveNames(&sNC, p->pCheck) ){
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif /* !defined(SQLITE_OMIT_CHECK) */
|
||||
|
||||
/* If the db->init.busy is 1 it means we are reading the SQL off the
|
||||
** "sqlite_master" or "sqlite_temp_master" table on the disk.
|
||||
** So do not write to the disk again. Extract the root page number
|
||||
|
||||
+30
-8
@@ -12,7 +12,7 @@
|
||||
** This file contains routines used for analyzing expressions and
|
||||
** for generating VDBE code that evaluates expressions in SQLite.
|
||||
**
|
||||
** $Id: expr.c,v 1.232 2005/11/01 15:48:24 drh Exp $
|
||||
** $Id: expr.c,v 1.233 2005/11/03 00:41:17 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include <ctype.h>
|
||||
@@ -802,7 +802,7 @@ int sqlite3IsRowid(const char *z){
|
||||
** in pParse and return non-zero. Return zero on success.
|
||||
*/
|
||||
static int lookupName(
|
||||
Parse *pParse, /* The parsing context */
|
||||
Parse *pParse, /* The parsing context */
|
||||
Token *pDbToken, /* Name of the database containing table, or NULL */
|
||||
Token *pTableToken, /* Name of table containing column, or NULL */
|
||||
Token *pColumnToken, /* Name of the column. */
|
||||
@@ -830,10 +830,9 @@ static int lookupName(
|
||||
|
||||
pExpr->iTable = -1;
|
||||
while( pNC && cnt==0 ){
|
||||
ExprList *pEList;
|
||||
SrcList *pSrcList = pNC->pSrcList;
|
||||
ExprList *pEList = pNC->pEList;
|
||||
|
||||
/* assert( zTab==0 || pEList==0 ); */
|
||||
if( pSrcList ){
|
||||
for(i=0, pItem=pSrcList->a; i<pSrcList->nSrc; i++, pItem++){
|
||||
Table *pTab = pItem->pTab;
|
||||
@@ -952,7 +951,7 @@ static int lookupName(
|
||||
** Note that the expression in the result set should have already been
|
||||
** resolved by the time the WHERE clause is resolved.
|
||||
*/
|
||||
if( cnt==0 && pEList!=0 && zTab==0 ){
|
||||
if( cnt==0 && (pEList = pNC->pEList)!=0 && zTab==0 ){
|
||||
for(j=0; j<pEList->nExpr; j++){
|
||||
char *zAs = pEList->a[j].zName;
|
||||
if( zAs!=0 && sqlite3StrICmp(zAs, zCol)==0 ){
|
||||
@@ -1081,7 +1080,7 @@ static int nameResolverStep(void *pArg, Expr *pExpr){
|
||||
if( ExprHasAnyProperty(pExpr, EP_Resolved) ) return 1;
|
||||
ExprSetProperty(pExpr, EP_Resolved);
|
||||
#ifndef NDEBUG
|
||||
if( pSrcList ){
|
||||
if( pSrcList && pSrcList->nAlloc>0 ){
|
||||
int i;
|
||||
for(i=0; i<pSrcList->nSrc; i++){
|
||||
assert( pSrcList->a[i].iCursor>=0 && pSrcList->a[i].iCursor<pParse->nTab);
|
||||
@@ -1441,6 +1440,8 @@ static void codeInteger(Vdbe *v, const char *z, int n){
|
||||
void sqlite3ExprCode(Parse *pParse, Expr *pExpr){
|
||||
Vdbe *v = pParse->pVdbe;
|
||||
int op;
|
||||
int stackChng = 1; /* Amount of change to stack depth */
|
||||
|
||||
if( v==0 ) return;
|
||||
if( pExpr==0 ){
|
||||
sqlite3VdbeAddOp(v, OP_Null, 0, 0);
|
||||
@@ -1462,7 +1463,11 @@ void sqlite3ExprCode(Parse *pParse, Expr *pExpr){
|
||||
/* Otherwise, fall thru into the TK_COLUMN case */
|
||||
}
|
||||
case TK_COLUMN: {
|
||||
if( pExpr->iColumn>=0 ){
|
||||
if( pExpr->iTable<0 ){
|
||||
/* This only happens when coding check constraints */
|
||||
assert( pParse->ckOffset>0 );
|
||||
sqlite3VdbeAddOp(v, OP_Dup, pParse->ckOffset-pExpr->iColumn-1, 1);
|
||||
}else if( pExpr->iColumn>=0 ){
|
||||
sqlite3VdbeAddOp(v, OP_Column, pExpr->iTable, pExpr->iColumn);
|
||||
sqlite3ColumnDefault(v, pExpr->pTab, pExpr->iColumn);
|
||||
}else{
|
||||
@@ -1525,6 +1530,7 @@ void sqlite3ExprCode(Parse *pParse, Expr *pExpr){
|
||||
case SQLITE_AFF_NONE: op = OP_ToBlob; break;
|
||||
}
|
||||
sqlite3VdbeAddOp(v, op, 0, 0);
|
||||
stackChng = 0;
|
||||
break;
|
||||
}
|
||||
#endif /* SQLITE_OMIT_CAST */
|
||||
@@ -1543,6 +1549,7 @@ void sqlite3ExprCode(Parse *pParse, Expr *pExpr){
|
||||
sqlite3ExprCode(pParse, pExpr->pLeft);
|
||||
sqlite3ExprCode(pParse, pExpr->pRight);
|
||||
codeCompare(pParse, pExpr->pLeft, pExpr->pRight, op, 0, 0);
|
||||
stackChng = -1;
|
||||
break;
|
||||
}
|
||||
case TK_AND:
|
||||
@@ -1571,6 +1578,7 @@ void sqlite3ExprCode(Parse *pParse, Expr *pExpr){
|
||||
sqlite3ExprCode(pParse, pExpr->pLeft);
|
||||
sqlite3ExprCode(pParse, pExpr->pRight);
|
||||
sqlite3VdbeAddOp(v, op, 0, 0);
|
||||
stackChng = -1;
|
||||
break;
|
||||
}
|
||||
case TK_UMINUS: {
|
||||
@@ -1596,6 +1604,7 @@ void sqlite3ExprCode(Parse *pParse, Expr *pExpr){
|
||||
assert( TK_NOT==OP_Not );
|
||||
sqlite3ExprCode(pParse, pExpr->pLeft);
|
||||
sqlite3VdbeAddOp(v, op, 0, 0);
|
||||
stackChng = 0;
|
||||
break;
|
||||
}
|
||||
case TK_ISNULL:
|
||||
@@ -1608,6 +1617,7 @@ void sqlite3ExprCode(Parse *pParse, Expr *pExpr){
|
||||
dest = sqlite3VdbeCurrentAddr(v) + 2;
|
||||
sqlite3VdbeAddOp(v, op, 1, dest);
|
||||
sqlite3VdbeAddOp(v, OP_AddImm, -1, 0);
|
||||
stackChng = 0;
|
||||
break;
|
||||
}
|
||||
case TK_AGG_FUNCTION: {
|
||||
@@ -1644,6 +1654,7 @@ void sqlite3ExprCode(Parse *pParse, Expr *pExpr){
|
||||
sqlite3VdbeOp3(v, OP_CollSeq, 0, 0, (char *)pColl, P3_COLLSEQ);
|
||||
}
|
||||
sqlite3VdbeOp3(v, OP_Function, constMask, nExpr, (char*)pDef, P3_FUNCDEF);
|
||||
stackChng = 1-nExpr;
|
||||
break;
|
||||
}
|
||||
#ifndef SQLITE_OMIT_SUBQUERY
|
||||
@@ -1702,6 +1713,7 @@ void sqlite3ExprCode(Parse *pParse, Expr *pExpr){
|
||||
case TK_UPLUS:
|
||||
case TK_AS: {
|
||||
sqlite3ExprCode(pParse, pExpr->pLeft);
|
||||
stackChng = 0;
|
||||
break;
|
||||
}
|
||||
case TK_CASE: {
|
||||
@@ -1767,9 +1779,15 @@ void sqlite3ExprCode(Parse *pParse, Expr *pExpr){
|
||||
sqlite3VdbeAddOp(v, OP_Goto, 0, pParse->trigStack->ignoreJump);
|
||||
VdbeComment((v, "# raise(IGNORE)"));
|
||||
}
|
||||
stackChng = 0;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
if( pParse->ckOffset ){
|
||||
pParse->ckOffset += stackChng;
|
||||
assert( pParse->ckOffset );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1837,6 +1855,7 @@ int sqlite3ExprCodeExprList(
|
||||
void sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){
|
||||
Vdbe *v = pParse->pVdbe;
|
||||
int op = 0;
|
||||
int ckOffset = pParse->ckOffset;
|
||||
if( v==0 || pExpr==0 ) return;
|
||||
op = pExpr->op;
|
||||
switch( op ){
|
||||
@@ -1911,6 +1930,7 @@ void sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){
|
||||
break;
|
||||
}
|
||||
}
|
||||
pParse->ckOffset = ckOffset;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1924,6 +1944,7 @@ void sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){
|
||||
void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){
|
||||
Vdbe *v = pParse->pVdbe;
|
||||
int op = 0;
|
||||
int ckOffset = pParse->ckOffset;
|
||||
if( v==0 || pExpr==0 ) return;
|
||||
|
||||
/* The value of pExpr->op and op are related as follows:
|
||||
@@ -2020,6 +2041,7 @@ void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){
|
||||
break;
|
||||
}
|
||||
}
|
||||
pParse->ckOffset = ckOffset;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
+13
-2
@@ -12,7 +12,7 @@
|
||||
** This file contains C code routines that are called by the parser
|
||||
** to handle INSERT statements in SQLite.
|
||||
**
|
||||
** $Id: insert.c,v 1.144 2005/11/01 15:48:24 drh Exp $
|
||||
** $Id: insert.c,v 1.145 2005/11/03 00:41:17 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
@@ -868,7 +868,18 @@ void sqlite3GenerateConstraintChecks(
|
||||
|
||||
/* Test all CHECK constraints
|
||||
*/
|
||||
/**** TBD ****/
|
||||
#ifndef SQLITE_OMIT_CHECK
|
||||
if( pTab->pCheck ){
|
||||
int allOk = sqlite3VdbeMakeLabel(v);
|
||||
assert( pParse->ckOffset==0 );
|
||||
pParse->ckOffset = nCol;
|
||||
sqlite3ExprIfTrue(pParse, pTab->pCheck, allOk, 0);
|
||||
assert( pParse->ckOffset==nCol );
|
||||
pParse->ckOffset = 0;
|
||||
sqlite3VdbeAddOp(v, OP_Halt, SQLITE_CONSTRAINT, OE_Abort);
|
||||
sqlite3VdbeResolveLabel(v, allOk);
|
||||
}
|
||||
#endif /* !defined(SQLITE_OMIT_CHECK) */
|
||||
|
||||
/* If we have an INTEGER PRIMARY KEY, make sure the primary key
|
||||
** of the new record does not previously exist. Except, if this
|
||||
|
||||
+2
-2
@@ -14,7 +14,7 @@
|
||||
** the parser. Lemon will also generate a header file containing
|
||||
** numeric codes for all of the tokens.
|
||||
**
|
||||
** @(#) $Id: parse.y,v 1.180 2005/09/16 02:38:10 drh Exp $
|
||||
** @(#) $Id: parse.y,v 1.181 2005/11/03 00:41:17 drh Exp $
|
||||
*/
|
||||
|
||||
// All token codes are small integers with #defines that begin with "TK_"
|
||||
@@ -268,7 +268,7 @@ ccons ::= NOT NULL onconf(R). {sqlite3AddNotNull(pParse, R);}
|
||||
ccons ::= PRIMARY KEY sortorder onconf(R) autoinc(I).
|
||||
{sqlite3AddPrimaryKey(pParse,0,R,I);}
|
||||
ccons ::= UNIQUE onconf(R). {sqlite3CreateIndex(pParse,0,0,0,0,R,0,0);}
|
||||
ccons ::= CHECK LP expr(X) RP onconf. {sqlite3ExprDelete(X);}
|
||||
ccons ::= CHECK LP expr(X) RP. {sqlite3AddCheckConstraint(pParse, X);}
|
||||
ccons ::= REFERENCES nm(T) idxlist_opt(TA) refargs(R).
|
||||
{sqlite3CreateForeignKey(pParse,0,&T,TA,R);}
|
||||
ccons ::= defer_subclause(D). {sqlite3DeferForeignKey(pParse,D);}
|
||||
|
||||
+2
-8
@@ -12,7 +12,7 @@
|
||||
** This file contains C code routines that are called by the parser
|
||||
** to handle SELECT statements in SQLite.
|
||||
**
|
||||
** $Id: select.c,v 1.277 2005/10/06 16:53:15 drh Exp $
|
||||
** $Id: select.c,v 1.278 2005/11/03 00:41:17 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
@@ -2384,14 +2384,8 @@ int sqlite3SelectResolve(
|
||||
/* Resolve the expressions in the LIMIT and OFFSET clauses. These
|
||||
** are not allowed to refer to any names, so pass an empty NameContext.
|
||||
*/
|
||||
memset(&sNC, 0, sizeof(sNC));
|
||||
sNC.pParse = pParse;
|
||||
sNC.hasAgg = 0;
|
||||
sNC.nErr = 0;
|
||||
sNC.nRef = 0;
|
||||
sNC.pEList = 0;
|
||||
sNC.allowAgg = 0;
|
||||
sNC.pSrcList = 0;
|
||||
sNC.pNext = 0;
|
||||
if( sqlite3ExprResolveNames(&sNC, p->pLimit) ||
|
||||
sqlite3ExprResolveNames(&sNC, p->pOffset) ){
|
||||
return SQLITE_ERROR;
|
||||
|
||||
+6
-1
@@ -11,7 +11,7 @@
|
||||
*************************************************************************
|
||||
** Internal interface definitions for SQLite.
|
||||
**
|
||||
** @(#) $Id: sqliteInt.h,v 1.424 2005/11/01 15:48:24 drh Exp $
|
||||
** @(#) $Id: sqliteInt.h,v 1.425 2005/11/03 00:41:17 drh Exp $
|
||||
*/
|
||||
#ifndef _SQLITEINT_H_
|
||||
#define _SQLITEINT_H_
|
||||
@@ -639,6 +639,9 @@ struct Table {
|
||||
Trigger *pTrigger; /* List of SQL triggers on this table */
|
||||
FKey *pFKey; /* Linked list of all foreign keys in this table */
|
||||
char *zColAff; /* String defining the affinity of each column */
|
||||
#ifndef SQLITE_OMIT_CHECK
|
||||
Expr *pCheck; /* The AND of all CHECK constraints */
|
||||
#endif
|
||||
#ifndef SQLITE_OMIT_ALTERTABLE
|
||||
int addColOffset; /* Offset in CREATE TABLE statement to add a new column */
|
||||
#endif
|
||||
@@ -1172,6 +1175,7 @@ struct Parse {
|
||||
int nTab; /* Number of previously allocated VDBE cursors */
|
||||
int nMem; /* Number of memory cells used so far */
|
||||
int nSet; /* Number of sets used so far */
|
||||
int ckOffset; /* Stack offset to data used by CHECK constraints */
|
||||
u32 writeMask; /* Start a write transaction on these databases */
|
||||
u32 cookieMask; /* Bitmask of schema verified databases */
|
||||
int cookieGoto; /* Address of OP_Goto to cookie verifier subroutine */
|
||||
@@ -1449,6 +1453,7 @@ void sqlite3StartTable(Parse*,Token*,Token*,Token*,int,int);
|
||||
void sqlite3AddColumn(Parse*,Token*);
|
||||
void sqlite3AddNotNull(Parse*, int);
|
||||
void sqlite3AddPrimaryKey(Parse*, ExprList*, int, int);
|
||||
void sqlite3AddCheckConstraint(Parse*, Expr*);
|
||||
void sqlite3AddColumnType(Parse*,Token*);
|
||||
void sqlite3AddDefaultValue(Parse*,Expr*);
|
||||
void sqlite3AddCollateType(Parse*, const char*, int);
|
||||
|
||||
+7
-1
@@ -13,7 +13,7 @@
|
||||
** is not included in the SQLite library. It is used for automated
|
||||
** testing of the SQLite library.
|
||||
**
|
||||
** $Id: test1.c,v 1.162 2005/09/19 13:15:23 drh Exp $
|
||||
** $Id: test1.c,v 1.163 2005/11/03 00:41:17 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include "tcl.h"
|
||||
@@ -2876,6 +2876,12 @@ static void set_options(Tcl_Interp *interp){
|
||||
Tcl_SetVar2(interp, "sqlite_options", "cast", "1", TCL_GLOBAL_ONLY);
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_OMIT_CHECK
|
||||
Tcl_SetVar2(interp, "sqlite_options", "check", "0", TCL_GLOBAL_ONLY);
|
||||
#else
|
||||
Tcl_SetVar2(interp, "sqlite_options", "check", "1", TCL_GLOBAL_ONLY);
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_OMIT_COMPLETE
|
||||
Tcl_SetVar2(interp, "sqlite_options", "complete", "0", TCL_GLOBAL_ONLY);
|
||||
#else
|
||||
|
||||
Reference in New Issue
Block a user