-C What\swould\sit\sbe\slike\sif\syou\scould\sadd\sthe\skeyword\s"STRICT"\safter\sa\sCREATE\nTABLE\sstatement\sto\scause\sthe\stable\sto\s(1)\sallow\sonly\sa\sfew\swell-defined\ndatatypes,\s(2)\srigidly\senforce\sthose\stypes,\s(3)\srequire\sNOT\sNULL\son\sPK\ncolumns,\s(4)\salways\senforce\sforeign\skey\sconstraint,\sand\sso\sforth?\s\sThis\nbranch\sseeks\sto\sexplore\sthat\squestion.
-D 2021-08-18T13:13:58.021
+C Trying\sto\sinsert\san\sincorrect\sdatatype\sinto\sa\sSTRICT\stable\sraises\san\nSQLITE_CONSTRAINT_DATATYPE\serror.\s\sSeems\sto\swork,\sthough\slots\smore\stesting\nis\sneeded.
+D 2021-08-18T19:22:27.620
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
F src/hash.h 9d56a9079d523b648774c1784b74b89bd93fac7b365210157482e4319a468f38
F src/hwtime.h cb1d7e3e1ed94b7aa6fde95ae2c2daccc3df826be26fc9ed7fd90d1750ae6144
F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71
-F src/insert.c 4ebff642574d3866316439b3dfce165f80e130e8969853c656d71b2afc5dd73c
+F src/insert.c dbf6ed6c070661691a50ec135bff47700a01c71ce9987819860c3a992dda8b95
F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa
F src/loadext.c 0aa9e7f08e168e3874cb54984408e3976dafdf5616d511952c425b5ac088ea3e
F src/main.c aab8cefb6bfbdbecc53fd19058fa053c0c5e591b2e5067d883ef999d019dcd29
F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92
F src/select.c 0577308f097363b6ebac223e210418810acf74e677f580597f7d0718476fe3ef
F src/shell.c.in f795a4ae3c35631f5edcfa754c7824ff1d8a75b23a07e22e664b50f82e826346
-F src/sqlite.h.in 43fcf0fe2af04081f420a906fc020bde1243851ba44b0aa567a27f94bf8c3145
+F src/sqlite.h.in 4e977a5e2ed1a9e8987ff65a2cab5f99a4298ebf040ea5ff636e1753339ff45a
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
F src/sqlite3ext.h e97f4e9b509408fea4c4e9bef5a41608dfac343b4d3c7a990dedde1e19af9510
-F src/sqliteInt.h 25581e591444d7be1eda83890a8828b28b8ff4c65ffd85a15acd4f0820260821
+F src/sqliteInt.h 732ef4ade4ea3e95ddc0a6be657c5f9eb1e4b8ede3105395fe0161c15fab9214
F src/sqliteLimit.h d7323ffea5208c6af2734574bae933ca8ed2ab728083caa117c9738581a31657
F src/status.c 4b8bc2a6905163a38b739854a35b826c737333fab5b1f8e03fa7eb9a4799c4c1
F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1
F src/utf.c ee39565f0843775cc2c81135751ddd93eceb91a673ea2c57f61c76f288b041a0
F src/util.c eafc8cfeb66fdbf8839922d13019b7882f242ac31b383e3451aab7744c54df3e
F src/vacuum.c 454973a59fb20bb982efc2df568a098616db6328a0491b6e84e2e07f7333db45
-F src/vdbe.c e5cdac52d7163c032ae3c54f1cff9391acd23ba79cea0d5a9524c00cc0a856e8
+F src/vdbe.c 0e70bef8e65fc217beb7909235502ce132d231fb656554205e0c13927b4140d1
F src/vdbe.h 25dabb25c7e157b84e59260cfb5b466c3ac103ede9f36f4db371332c47601abe
F src/vdbeInt.h 38206c8dd6b60ff03d9fd4f626b1b4fd0eef7cdc44f2fc2c1973b0f932a3f26b
F src/vdbeapi.c aa5aaf2c37676b83af5724c6cd8207a3064ed46a217fd180957f75ac84f7a2a5
F test/statfault.test f525a7bf633e50afd027700e9a486090684b1ac1
F test/stmt.test 54ed2cc0764bf3e48a058331813c3dbd19fc1d0827c3d8369914a5d8f564ec75
F test/stmtvtab1.test 6873dfb24f8e79cbb5b799b95c2e4349060eb7a3b811982749a84b359468e2d5
-F test/strict1.test d84d23c8b2e2742277d943db3a56d2cf0d886ad9bb4de46228aa6c7c676f6553
+F test/strict1.test 36a24e127e2cdc65ecf23d03c9a4fbe6d8f7cb80f4c4bf09eb220c1ce4c84daf
F test/subjournal.test 8d4e2572c0ee9a15549f0d8e40863161295107e52f07a3e8012a2e1fdd093c49
F test/subquery.test d7268d193dd33d5505df965399d3a594e76ae13f
F test/subquery2.test 90cf944b9de8204569cf656028391e4af1ccc8c0cc02d4ef38ee3be8de1ffb12
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P b9b0dcd5af072c22f2ce71cf9584b5b572fbcfbce6410a7d703b586adb8938ba
-R d1aed29af5d2a129b155039aa4e933b3
-T *branch * strict-tables
-T *sym-strict-tables *
-T -sym-trunk *
+P 78732b9f98936693ae29c85a692c35a84c7d065aec79903af34b08d18f10a5e6
+R 1fff96359c6ac1b1e05dcf91c71ba789
U drh
-Z d972776960407c2f55c8c38fcd2522b6
+Z 89d8d7739872c2c88b7cdfc8d1469967
-78732b9f98936693ae29c85a692c35a84c7d065aec79903af34b08d18f10a5e6
\ No newline at end of file
+a19305e5cfedf5c472200d6e05c1396443e348f052a40a0979f860f2ff06851d
\ No newline at end of file
*/
void sqlite3TableAffinity(Vdbe *v, Table *pTab, int iReg){
int i, j;
- char *zColAff = pTab->zColAff;
+ char *zColAff;
+ if( pTab->tabFlags & TF_Strict ){
+ if( iReg==0 ){
+ /* Move the previous opcode (which should be OP_MakeRecord) forward
+ ** by one slot and insert a new OP_TypeCheck where the current
+ ** OP_MakeRecord is found */
+ VdbeOp *pPrev;
+ sqlite3VdbeAppendP4(v, pTab, P4_TABLE);
+ pPrev = sqlite3VdbeGetOp(v, -1);
+ pPrev->opcode = OP_TypeCheck;
+ sqlite3VdbeAddOp3(v, OP_MakeRecord, pPrev->p1, pPrev->p2, pPrev->p3);
+ }else{
+ /* Insert an isolated OP_Typecheck */
+ sqlite3VdbeAddOp2(v, OP_TypeCheck, iReg, pTab->nNVCol);
+ sqlite3VdbeAppendP4(v, pTab, P4_TABLE);
+ }
+ return;
+ }
+ zColAff = pTab->zColAff;
if( zColAff==0 ){
sqlite3 *db = sqlite3VdbeDb(v);
zColAff = (char *)sqlite3DbMallocRaw(0, pTab->nCol+1);
#define SQLITE_CONSTRAINT_VTAB (SQLITE_CONSTRAINT | (9<<8))
#define SQLITE_CONSTRAINT_ROWID (SQLITE_CONSTRAINT |(10<<8))
#define SQLITE_CONSTRAINT_PINNED (SQLITE_CONSTRAINT |(11<<8))
+#define SQLITE_CONSTRAINT_DATATYPE (SQLITE_CONSTRAINT |(12<<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))
struct Column {
char *zCnName; /* Name of this column */
unsigned notNull :4; /* An OE_ code for handling a NOT NULL constraint */
- unsigned eCType :4; /* One of the standard types */
+ unsigned eCType :4; /* One of the standard types */
char affinity; /* One of the SQLITE_AFF_... values */
u8 szEst; /* Est size of value in this column. sizeof(INT)==1 */
u8 hName; /* Column name hash for faster lookup */
}
}
+/* Opcode: TypeCheck P1 P2 * P4 *
+** Synopsis: typecheck(r[P1@P2])
+**
+** Apply affinities to the range of P2 registers beginning with P1.
+** Take the affinities from the Table object in P4. If any value
+** cannot be coerced into the correct type, then raise an error.
+**
+** This opcode is similar to OP_Affinity except that this opcode
+** forces the register type to the Table column type. This is used
+** to implement "strict affinity".
+*/
+case OP_TypeCheck: {
+ Table *pTab;
+ Column *aCol;
+ int i;
+
+ assert( pOp->p4type==P4_TABLE );
+ pTab = pOp->p4.pTab;
+ assert( pTab->tabFlags & TF_Strict );
+ aCol = pTab->aCol;
+ pIn1 = &aMem[pOp->p1];
+ for(i=0; i<pTab->nCol; i++){
+ if( aCol[i].colFlags & COLFLAG_VIRTUAL ) continue;
+ assert( pIn1 < &aMem[pOp->p1+pOp->p2] );
+ applyAffinity(pIn1, aCol[i].affinity, encoding);
+ if( (pIn1->flags & MEM_Null)==0 ){
+ switch( aCol[i].eCType ){
+ case COLTYPE_BLOB: {
+ if( (pIn1->flags & MEM_Blob)==0 ) goto vdbe_type_error;
+ break;
+ }
+ case COLTYPE_INTEGER:
+ case COLTYPE_INT: {
+ if( (pIn1->flags & MEM_Int)==0 ) goto vdbe_type_error;
+ break;
+ }
+ case COLTYPE_TEXT: {
+ if( (pIn1->flags & MEM_Str)==0 ) goto vdbe_type_error;
+ break;
+ }
+ default: {
+ assert( aCol[i].eCType==COLTYPE_REAL );
+ if( pIn1->flags & MEM_Int ){
+ /* When applying REAL affinity, if the result is still an MEM_Int
+ ** that will fit in 6 bytes, then change the type to MEM_IntReal
+ ** so that we keep the high-resolution integer value but know that
+ ** the type really wants to be REAL. */
+ testcase( pIn1->u.i==140737488355328LL );
+ testcase( pIn1->u.i==140737488355327LL );
+ testcase( pIn1->u.i==-140737488355328LL );
+ testcase( pIn1->u.i==-140737488355329LL );
+ if( pIn1->u.i<=140737488355327LL && pIn1->u.i>=-140737488355328LL){
+ pIn1->flags |= MEM_IntReal;
+ pIn1->flags &= ~MEM_Int;
+ }else{
+ pIn1->u.r = (double)pIn1->u.i;
+ pIn1->flags |= MEM_Real;
+ pIn1->flags &= ~MEM_Int;
+ }
+ }else if( (pIn1->flags & MEM_Real)==0 ){
+ goto vdbe_type_error;
+ }
+ break;
+ }
+ }
+ }
+ REGISTER_TRACE((int)(pIn1-aMem), pIn1);
+ pIn1++;
+ }
+ assert( pIn1 == &aMem[pOp->p1+pOp->p2] );
+ break;
+
+vdbe_type_error:
+ sqlite3VdbeError(p, "%s.%s holds only %s values",
+ pTab->zName, aCol[i].zCnName, sqlite3StdType[aCol[i].eCType-1]);
+ rc = SQLITE_CONSTRAINT_DATATYPE;
+ goto abort_due_to_error;
+}
+
/* Opcode: Affinity P1 P2 * P4 *
** Synopsis: affinity(r[P1@P2])
**
do_catchsql_test strict1-1.6 {
CREATE TABLE t1(a TEXT PRIMARY KEY, b INT, c INTEGER, d REAL, e BLOB, f TEXT(50)) WITHOUT ROWID, STRICT;
} {1 {unknown datatype for t1.f: "TEXT(50)"}}
-
+
+do_execsql_test strict1-2.0 {
+ CREATE TABLE t1(
+ a INT,
+ b INTEGER,
+ c BLOB,
+ d TEXT,
+ e REAL
+ ) STRICT;
+} {}
+do_catchsql_test strict1-2.1 {
+ INSERT INTO t1(a) VALUES('xyz');
+} {1 {t1.a holds only INT values}}
+do_catchsql_test strict1-2.2 {
+ INSERT INTO t1(b) VALUES('xyz');
+} {1 {t1.b holds only INTEGER values}}
+do_catchsql_test strict1-2.3 {
+ INSERT INTO t1(c) VALUES('xyz');
+} {1 {t1.c holds only BLOB values}}
+do_catchsql_test strict1-2.4 {
+ INSERT INTO t1(d) VALUES(x'3142536475');
+} {1 {t1.d holds only TEXT values}}
+do_catchsql_test strict1-2.5 {
+ INSERT INTO t1(e) VALUES('xyz');
+} {1 {t1.e holds only REAL values}}
+
+do_execsql_test strict1-3.1 {
+ INSERT INTO t1(a, b) VALUES(1,2),('3','4'),(5.0, 6.0),(null,null);
+ SELECT a, b, '|' FROM t1;
+} {1 2 | 3 4 | 5 6 | {} {} |}
+do_catchsql_test strict1-3.2 {
+ INSERT INTO t1(a) VALUES(1.2);
+} {1 {t1.a holds only INT values}}
+do_catchsql_test strict1-3.3 {
+ INSERT INTO t1(a) VALUES(x'313233');
+} {1 {t1.a holds only INT values}}
+do_catchsql_test strict1-3.4 {
+ INSERT INTO t1(b) VALUES(1.2);
+} {1 {t1.b holds only INTEGER values}}
+do_catchsql_test strict1-3.5 {
+ INSERT INTO t1(b) VALUES(x'313233');
+} {1 {t1.b holds only INTEGER values}}
+
+do_execsql_test strict1-4.1 {
+ DELETE FROM t1;
+ INSERT INTO t1(c) VALUES(x'313233'), (NULL);
+ SELECT typeof(c), c FROM t1;
+} {blob 123 null {}}
+do_catchsql_test strict1-4.2 {
+ INSERT INTO t1(c) VALUES('456');
+} {1 {t1.c holds only BLOB values}}
+
finish_test