aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/build.c60
-rw-r--r--src/expr.c2
-rw-r--r--src/fkey.c12
-rw-r--r--src/insert.c54
-rw-r--r--src/main.c1
-rw-r--r--src/sqlite.h.in1
-rw-r--r--src/sqliteInt.h4
-rw-r--r--src/vdbe.c53
-rw-r--r--src/vdbe.h6
-rw-r--r--src/vdbeaux.c2
10 files changed, 128 insertions, 67 deletions
diff --git a/src/build.c b/src/build.c
index 56bc11c57..08438f2e6 100644
--- a/src/build.c
+++ b/src/build.c
@@ -2683,9 +2683,7 @@ static void sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){
addr2 = sqlite3VdbeCurrentAddr(v);
sqlite3VdbeAddOp4Int(v, OP_SorterCompare, iSorter, j2, regRecord,
pKey->nField - pIndex->nKeyCol);
- sqlite3HaltConstraint(pParse, SQLITE_CONSTRAINT_UNIQUE,
- OE_Abort, "indexed columns are not unique", P4_STATIC
- );
+ sqlite3UniqueConstraint(pParse, OE_Abort, pIndex);
}else{
addr2 = sqlite3VdbeCurrentAddr(v);
}
@@ -3949,7 +3947,8 @@ void sqlite3HaltConstraint(
int errCode, /* extended error code */
int onError, /* Constraint type */
char *p4, /* Error message */
- int p4type /* P4_STATIC or P4_TRANSIENT */
+ i8 p4type, /* P4_STATIC or P4_TRANSIENT */
+ u8 p5Errmsg /* P5_ErrMsg type */
){
Vdbe *v = sqlite3GetVdbe(pParse);
assert( (errCode&0xff)==SQLITE_CONSTRAINT );
@@ -3957,6 +3956,59 @@ void sqlite3HaltConstraint(
sqlite3MayAbort(pParse);
}
sqlite3VdbeAddOp4(v, OP_Halt, errCode, onError, 0, p4, p4type);
+ if( p5Errmsg ) sqlite3VdbeChangeP5(v, p5Errmsg);
+}
+
+/*
+** Code an OP_Halt due to UNIQUE or PRIMARY KEY constraint violation.
+*/
+void sqlite3UniqueConstraint(
+ Parse *pParse, /* Parsing context */
+ int onError, /* Constraint type */
+ Index *pIdx /* The index that triggers the constraint */
+){
+ char *zErr;
+ int j;
+ StrAccum errMsg;
+ Table *pTab = pIdx->pTable;
+
+ sqlite3StrAccumInit(&errMsg, 0, 0, 200);
+ errMsg.db = pParse->db;
+ for(j=0; j<pIdx->nKeyCol; j++){
+ char *zCol = pTab->aCol[pIdx->aiColumn[j]].zName;
+ if( j ) sqlite3StrAccumAppend(&errMsg, ", ", 2);
+ sqlite3StrAccumAppend(&errMsg, pTab->zName, -1);
+ sqlite3StrAccumAppend(&errMsg, ".", 1);
+ sqlite3StrAccumAppend(&errMsg, zCol, -1);
+ }
+ zErr = sqlite3StrAccumFinish(&errMsg);
+ sqlite3HaltConstraint(pParse,
+ (pIdx->autoIndex==2)?SQLITE_CONSTRAINT_PRIMARYKEY:SQLITE_CONSTRAINT_UNIQUE,
+ onError, zErr, 0, P5_ConstraintUnique);
+ sqlite3DbFree(errMsg.db, zErr);
+}
+
+
+/*
+** Code an OP_Halt due to non-unique rowid.
+*/
+void sqlite3RowidConstraint(
+ Parse *pParse, /* Parsing context */
+ int onError, /* Conflict resolution algorithm */
+ Table *pTab /* The table with the non-unique rowid */
+){
+ char *zMsg;
+ int rc;
+ if( pTab->iPKey>=0 ){
+ zMsg = sqlite3MPrintf(pParse->db, "%s.%s", pTab->zName,
+ pTab->aCol[pTab->iPKey].zName);
+ rc = SQLITE_CONSTRAINT_PRIMARYKEY;
+ }else{
+ zMsg = sqlite3MPrintf(pParse->db, "%s.rowid", pTab->zName);
+ rc = SQLITE_CONSTRAINT_ROWID;
+ }
+ sqlite3HaltConstraint(pParse, rc, onError, zMsg, P4_DYNAMIC,
+ P5_ConstraintUnique);
}
/*
diff --git a/src/expr.c b/src/expr.c
index 4a321dbc4..045ef15e8 100644
--- a/src/expr.c
+++ b/src/expr.c
@@ -2972,7 +2972,7 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
v, OP_Halt, SQLITE_OK, OE_Ignore, 0, pExpr->u.zToken,0);
}else{
sqlite3HaltConstraint(pParse, SQLITE_CONSTRAINT_TRIGGER,
- pExpr->affinity, pExpr->u.zToken, 0);
+ pExpr->affinity, pExpr->u.zToken, 0, 0);
}
break;
diff --git a/src/fkey.c b/src/fkey.c
index 0ef76800c..b0ff0f002 100644
--- a/src/fkey.c
+++ b/src/fkey.c
@@ -432,8 +432,7 @@ static void fkLookupParent(
** generated for will not open a statement transaction. */
assert( nIncr==1 );
sqlite3HaltConstraint(pParse, SQLITE_CONSTRAINT_FOREIGNKEY,
- OE_Abort, "foreign key constraint failed", P4_STATIC
- );
+ OE_Abort, 0, P4_STATIC, P5_ConstraintFK);
}else{
if( nIncr>0 && pFKey->isDeferred==0 ){
sqlite3ParseToplevel(pParse)->mayAbort = 1;
@@ -516,13 +515,13 @@ static Expr *exprTableColumn(
** --------------------------------------------------------------------------
** DELETE immediate Increment the "immediate constraint counter".
** Or, if the ON (UPDATE|DELETE) action is RESTRICT,
-** throw a "foreign key constraint failed" exception.
+** throw a "FOREIGN KEY constraint failed" exception.
**
** INSERT immediate Decrement the "immediate constraint counter".
**
** DELETE deferred Increment the "deferred constraint counter".
** Or, if the ON (UPDATE|DELETE) action is RESTRICT,
-** throw a "foreign key constraint failed" exception.
+** throw a "FOREIGN KEY constraint failed" exception.
**
** INSERT deferred Decrement the "deferred constraint counter".
**
@@ -734,8 +733,7 @@ void sqlite3FkDropTable(Parse *pParse, SrcList *pName, Table *pTab){
if( (db->flags & SQLITE_DeferFKs)==0 ){
sqlite3VdbeAddOp2(v, OP_FkIfZero, 0, sqlite3VdbeCurrentAddr(v)+2);
sqlite3HaltConstraint(pParse, SQLITE_CONSTRAINT_FOREIGNKEY,
- OE_Abort, "foreign key constraint failed", P4_STATIC
- );
+ OE_Abort, 0, P4_STATIC, P5_ConstraintFK);
}
if( iSkip ){
@@ -1213,7 +1211,7 @@ static Trigger *fkActionTrigger(
tFrom.z = zFrom;
tFrom.n = nFrom;
- pRaise = sqlite3Expr(db, TK_RAISE, "foreign key constraint failed");
+ pRaise = sqlite3Expr(db, TK_RAISE, "FOREIGN KEY constraint failed");
if( pRaise ){
pRaise->affinity = OE_Abort;
}
diff --git a/src/insert.c b/src/insert.c
index 18522a6ce..e9ccfe841 100644
--- a/src/insert.c
+++ b/src/insert.c
@@ -1278,12 +1278,11 @@ void sqlite3GenerateConstraintChecks(
/* Fall through */
case OE_Rollback:
case OE_Fail: {
- char *zMsg;
- sqlite3VdbeAddOp3(v, OP_HaltIfNull,
- SQLITE_CONSTRAINT_NOTNULL, onError, regNewData+1+i);
- zMsg = sqlite3MPrintf(db, "%s.%s may not be NULL",
- pTab->zName, pTab->aCol[i].zName);
- sqlite3VdbeChangeP4(v, -1, zMsg, P4_DYNAMIC);
+ char *zMsg = sqlite3MPrintf(db, "%s.%s", pTab->zName,
+ pTab->aCol[i].zName);
+ sqlite3VdbeAddOp4(v, OP_HaltIfNull, SQLITE_CONSTRAINT_NOTNULL, onError,
+ regNewData+1+i, zMsg, P4_DYNAMIC);
+ sqlite3VdbeChangeP5(v, P5_ConstraintNotNull);
break;
}
case OE_Ignore: {
@@ -1313,15 +1312,12 @@ void sqlite3GenerateConstraintChecks(
if( onError==OE_Ignore ){
sqlite3VdbeAddOp2(v, OP_Goto, 0, ignoreDest);
}else{
- char *zConsName = pCheck->a[i].zName;
+ char *zName = pCheck->a[i].zName;
+ if( zName==0 ) zName = pTab->zName;
if( onError==OE_Replace ) onError = OE_Abort; /* IMP: R-15569-63625 */
- if( zConsName ){
- zConsName = sqlite3MPrintf(db, "constraint %s failed", zConsName);
- }else{
- zConsName = 0;
- }
sqlite3HaltConstraint(pParse, SQLITE_CONSTRAINT_CHECK,
- onError, zConsName, P4_DYNAMIC);
+ onError, zName, P4_TRANSIENT,
+ P5_ConstraintCheck);
}
sqlite3VdbeResolveLabel(v, allOk);
}
@@ -1362,8 +1358,7 @@ void sqlite3GenerateConstraintChecks(
case OE_Rollback:
case OE_Abort:
case OE_Fail: {
- sqlite3HaltConstraint(pParse, SQLITE_CONSTRAINT_PRIMARYKEY,
- onError, "PRIMARY KEY must be unique", P4_STATIC);
+ sqlite3RowidConstraint(pParse, onError, pTab);
break;
}
case OE_Replace: {
@@ -1546,31 +1541,7 @@ void sqlite3GenerateConstraintChecks(
case OE_Rollback:
case OE_Abort:
case OE_Fail: {
- if( pIdx->autoIndex==2 ){
- sqlite3HaltConstraint(pParse, SQLITE_CONSTRAINT_PRIMARYKEY,
- onError, "PRIMARY KEY must be unique", 0);
- }else{
- int j;
- StrAccum errMsg;
- const char *zSep;
- char *zErr;
-
- sqlite3StrAccumInit(&errMsg, 0, 0, 200);
- errMsg.db = db;
- zSep = pIdx->nKeyCol>1 ? "columns " : "column ";
- for(j=0; j<pIdx->nKeyCol; j++){
- char *zCol = pTab->aCol[pIdx->aiColumn[j]].zName;
- sqlite3StrAccumAppend(&errMsg, zSep, -1);
- zSep = ", ";
- sqlite3StrAccumAppend(&errMsg, zCol, -1);
- }
- sqlite3StrAccumAppend(&errMsg,
- pIdx->nKeyCol>1 ? " are not unique" : " is not unique", -1);
- zErr = sqlite3StrAccumFinish(&errMsg);
- sqlite3HaltConstraint(pParse, SQLITE_CONSTRAINT_UNIQUE,
- onError, zErr, 0);
- sqlite3DbFree(errMsg.db, zErr);
- }
+ sqlite3UniqueConstraint(pParse, onError, pIdx);
break;
}
case OE_Ignore: {
@@ -2019,8 +1990,7 @@ static int xferOptimization(
if( pDest->iPKey>=0 ){
addr1 = sqlite3VdbeAddOp2(v, OP_Rowid, iSrc, regRowid);
addr2 = sqlite3VdbeAddOp3(v, OP_NotExists, iDest, 0, regRowid);
- sqlite3HaltConstraint(pParse, SQLITE_CONSTRAINT_PRIMARYKEY,
- onError, "PRIMARY KEY must be unique", P4_STATIC);
+ sqlite3RowidConstraint(pParse, onError, pDest);
sqlite3VdbeJumpHere(v, addr2);
autoIncStep(pParse, regAutoinc, regRowid);
}else if( pDest->pIndex==0 ){
diff --git a/src/main.c b/src/main.c
index 407de0e4d..a8b6c84ec 100644
--- a/src/main.c
+++ b/src/main.c
@@ -1135,6 +1135,7 @@ const char *sqlite3ErrName(int rc){
case SQLITE_CONSTRAINT_VTAB: zName = "SQLITE_CONSTRAINT_VTAB"; break;
case SQLITE_CONSTRAINT_FUNCTION:
zName = "SQLITE_CONSTRAINT_FUNCTION"; break;
+ case SQLITE_CONSTRAINT_ROWID: zName = "SQLITE_CONSTRAINT_ROWID"; break;
case SQLITE_MISMATCH: zName = "SQLITE_MISMATCH"; break;
case SQLITE_MISUSE: zName = "SQLITE_MISUSE"; break;
case SQLITE_NOLFS: zName = "SQLITE_NOLFS"; break;
diff --git a/src/sqlite.h.in b/src/sqlite.h.in
index d9f9feb31..403ee725b 100644
--- a/src/sqlite.h.in
+++ b/src/sqlite.h.in
@@ -496,6 +496,7 @@ int sqlite3_exec(
#define SQLITE_CONSTRAINT_TRIGGER (SQLITE_CONSTRAINT | (7<<8))
#define SQLITE_CONSTRAINT_UNIQUE (SQLITE_CONSTRAINT | (8<<8))
#define SQLITE_CONSTRAINT_VTAB (SQLITE_CONSTRAINT | (9<<8))
+#define SQLITE_CONSTRAINT_ROWID (SQLITE_CONSTRAINT |(10<<8))
#define SQLITE_NOTICE_RECOVER_WAL (SQLITE_NOTICE | (1<<8))
#define SQLITE_NOTICE_RECOVER_ROLLBACK (SQLITE_NOTICE | (2<<8))
#define SQLITE_WARNING_AUTOINDEX (SQLITE_WARNING | (1<<8))
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index 43a82b739..b0b976145 100644
--- a/src/sqliteInt.h
+++ b/src/sqliteInt.h
@@ -2938,7 +2938,9 @@ int sqlite3OpenTableAndIndices(Parse*, Table*, int, int, int*, int*);
void sqlite3BeginWriteOperation(Parse*, int, int);
void sqlite3MultiWrite(Parse*);
void sqlite3MayAbort(Parse*);
-void sqlite3HaltConstraint(Parse*, int, int, char*, int);
+void sqlite3HaltConstraint(Parse*, int, int, char*, i8, u8);
+void sqlite3UniqueConstraint(Parse*, int, Index*);
+void sqlite3RowidConstraint(Parse*, int, Table*);
Expr *sqlite3ExprDup(sqlite3*,Expr*,int);
ExprList *sqlite3ExprListDup(sqlite3*,ExprList*,int);
SrcList *sqlite3SrcListDup(sqlite3*,SrcList*,int);
diff --git a/src/vdbe.c b/src/vdbe.c
index 05b0b98e3..602bd1b51 100644
--- a/src/vdbe.c
+++ b/src/vdbe.c
@@ -804,12 +804,13 @@ case OP_Yield: { /* in1 */
break;
}
-/* Opcode: HaltIfNull P1 P2 P3 P4 *
+/* Opcode: HaltIfNull P1 P2 P3 P4 P5
** Synopsis: if r[P3] null then halt
**
** Check the value in register P3. If it is NULL then Halt using
** parameter P1, P2, and P4 as if this were a Halt instruction. If the
** value in register P3 is not NULL, then this routine is a no-op.
+** The P5 parameter should be 1.
*/
case OP_HaltIfNull: { /* in3 */
pIn3 = &aMem[pOp->p3];
@@ -817,7 +818,7 @@ case OP_HaltIfNull: { /* in3 */
/* Fall through into OP_Halt */
}
-/* Opcode: Halt P1 P2 * P4 *
+/* Opcode: Halt P1 P2 * P4 P5
**
** Exit immediately. All open cursors, etc are closed
** automatically.
@@ -832,11 +833,25 @@ case OP_HaltIfNull: { /* in3 */
**
** If P4 is not null then it is an error message string.
**
+** P5 is a value between 0 and 4, inclusive, that modifies the P4 string.
+**
+** 0: (no change)
+** 1: NOT NULL contraint failed: P4
+** 2: UNIQUE constraint failed: P4
+** 3: CHECK constraint failed: P4
+** 4: FOREIGN KEY constraint failed: P4
+**
+** If P5 is not zero and P4 is NULL, then everything after the ":" is
+** omitted.
+**
** There is an implied "Halt 0 0 0" instruction inserted at the very end of
** every program. So a jump past the last instruction of the program
** is the same as executing Halt.
*/
case OP_Halt: {
+ const char *zType;
+ const char *zLogFmt;
+
if( pOp->p1==SQLITE_OK && p->pFrame ){
/* Halt the sub-program. Return control to the parent frame. */
VdbeFrame *pFrame = p->pFrame;
@@ -857,18 +872,34 @@ case OP_Halt: {
aMem = p->aMem;
break;
}
-
+ if( pOp->p5 ){
+ static const char * const azType[] = { "NOT NULL", "UNIQUE", "CHECK",
+ "FOREIGN KEY" };
+ assert( pOp->p5>=1 && pOp->p5<=4 );
+ testcase( pOp->p5==1 );
+ testcase( pOp->p5==2 );
+ testcase( pOp->p5==3 );
+ testcase( pOp->p5==4 );
+ zType = azType[pOp->p5-1];
+ }else{
+ zType = 0;
+ }
p->rc = pOp->p1;
p->errorAction = (u8)pOp->p2;
p->pc = pc;
- if( pOp->p4.z ){
- assert( p->rc!=SQLITE_OK );
- sqlite3SetString(&p->zErrMsg, db, "%s", pOp->p4.z);
- testcase( sqlite3GlobalConfig.xLog!=0 );
- sqlite3_log(pOp->p1, "abort at %d in [%s]: %s", pc, p->zSql, pOp->p4.z);
- }else if( p->rc ){
- testcase( sqlite3GlobalConfig.xLog!=0 );
- sqlite3_log(pOp->p1, "constraint failed at %d in [%s]", pc, p->zSql);
+ if( p->rc ){
+ zLogFmt = "abort at %d in [%s]: %s";
+ if( zType && pOp->p4.z ){
+ sqlite3SetString(&p->zErrMsg, db, "%s constraint failed: %s",
+ zType, pOp->p4.z);
+ }else if( pOp->p4.z ){
+ sqlite3SetString(&p->zErrMsg, db, "%s", pOp->p4.z);
+ }else if( zType ){
+ sqlite3SetString(&p->zErrMsg, db, "%s constraint failed", zType);
+ }else{
+ zLogFmt = "abort at %d in [%s]";
+ }
+ sqlite3_log(pOp->p1, zLogFmt, pc, p->zSql, p->zErrMsg);
}
rc = sqlite3VdbeHalt(p);
assert( rc==SQLITE_BUSY || rc==SQLITE_OK || rc==SQLITE_ERROR );
diff --git a/src/vdbe.h b/src/vdbe.h
index 9a870ba4c..0f52ba6de 100644
--- a/src/vdbe.h
+++ b/src/vdbe.h
@@ -127,6 +127,12 @@ typedef struct VdbeOpList VdbeOpList;
#define P4_KEYINFO_HANDOFF (-16)
#define P4_KEYINFO_STATIC (-17)
+/* Error message codes for OP_Halt */
+#define P5_ConstraintNotNull 1
+#define P5_ConstraintUnique 2
+#define P5_ConstraintCheck 3
+#define P5_ConstraintFK 4
+
/*
** The Vdbe.aColName array contains 5n Mem structures, where n is the
** number of columns of data returned by the statement.
diff --git a/src/vdbeaux.c b/src/vdbeaux.c
index 8a1eea1b8..5a0e434cd 100644
--- a/src/vdbeaux.c
+++ b/src/vdbeaux.c
@@ -2198,7 +2198,7 @@ int sqlite3VdbeCheckFk(Vdbe *p, int deferred){
){
p->rc = SQLITE_CONSTRAINT_FOREIGNKEY;
p->errorAction = OE_Abort;
- sqlite3SetString(&p->zErrMsg, db, "foreign key constraint failed");
+ sqlite3SetString(&p->zErrMsg, db, "FOREIGN KEY constraint failed");
return SQLITE_ERROR;
}
return SQLITE_OK;