]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Add support for on conflict clauses to fts3/fts4.
authordan <dan@noemail.net>
Mon, 25 Apr 2011 18:49:57 +0000 (18:49 +0000)
committerdan <dan@noemail.net>
Mon, 25 Apr 2011 18:49:57 +0000 (18:49 +0000)
FossilOrigin-Name: 6d2633a6d0a9bb88fb1a6adac0827dc51df2d4d2

13 files changed:
ext/fts3/fts3.c
ext/fts3/fts3_write.c
manifest
manifest.uuid
src/insert.c
src/sqlite.h.in
src/sqliteInt.h
src/update.c
src/vdbe.c
src/vtab.c
test/fts3atoken.test
test/fts3aux1.test
test/fts3conf.test [new file with mode: 0644]

index 20da05164d2a6cbffb0c2859e4859dadcc75606c..c7e06d4bb637461dbb7f9b03c7170f089ac9f58e 100644 (file)
@@ -524,6 +524,8 @@ static void fts3DeclareVtab(int *pRc, Fts3Table *p){
     char *zSql;                   /* SQL statement passed to declare_vtab() */
     char *zCols;                  /* List of user defined columns */
 
+    sqlite3_vtab_config(p->db, SQLITE_VTAB_CONSTRAINT_SUPPORT, 1);
+
     /* Create a list of user columns for the virtual table */
     zCols = sqlite3_mprintf("%Q, ", p->azColumn[0]);
     for(i=1; zCols && i<p->nColumn; i++){
index 1a00f8a961429c851ead06a5d1fc1574d6045f42..9d2bfaae8bc1de286e99e65331eae3bd403f2099 100644 (file)
@@ -747,14 +747,14 @@ static int fts3DeleteAll(Fts3Table *p){
 static void fts3DeleteTerms( 
   int *pRC,               /* Result code */
   Fts3Table *p,           /* The FTS table to delete from */
-  sqlite3_value **apVal,  /* apVal[] contains the docid to be deleted */
+  sqlite3_value *pRowid,  /* The docid to be deleted */
   u32 *aSz                /* Sizes of deleted document written here */
 ){
   int rc;
   sqlite3_stmt *pSelect;
 
   if( *pRC ) return;
-  rc = fts3SqlStmt(p, SQL_SELECT_CONTENT_BY_ROWID, &pSelect, apVal);
+  rc = fts3SqlStmt(p, SQL_SELECT_CONTENT_BY_ROWID, &pSelect, &pRowid);
   if( rc==SQLITE_OK ){
     if( SQLITE_ROW==sqlite3_step(pSelect) ){
       int i;
@@ -1894,16 +1894,16 @@ static void fts3SegWriterFree(SegmentWriter *pWriter){
 ** The first value in the apVal[] array is assumed to contain an integer.
 ** This function tests if there exist any documents with docid values that
 ** are different from that integer. i.e. if deleting the document with docid
-** apVal[0] would mean the FTS3 table were empty.
+** pRowid would mean the FTS3 table were empty.
 **
 ** If successful, *pisEmpty is set to true if the table is empty except for
-** document apVal[0], or false otherwise, and SQLITE_OK is returned. If an
+** document pRowid, or false otherwise, and SQLITE_OK is returned. If an
 ** error occurs, an SQLite error code is returned.
 */
-static int fts3IsEmpty(Fts3Table *p, sqlite3_value **apVal, int *pisEmpty){
+static int fts3IsEmpty(Fts3Table *p, sqlite3_value *pRowid, int *pisEmpty){
   sqlite3_stmt *pStmt;
   int rc;
-  rc = fts3SqlStmt(p, SQL_IS_EMPTY, &pStmt, apVal);
+  rc = fts3SqlStmt(p, SQL_IS_EMPTY, &pStmt, &pRowid);
   if( rc==SQLITE_OK ){
     if( SQLITE_ROW==sqlite3_step(pStmt) ){
       *pisEmpty = sqlite3_column_int(pStmt, 0);
@@ -2627,6 +2627,40 @@ int sqlite3Fts3DeferToken(
   return SQLITE_OK;
 }
 
+/*
+** SQLite value pRowid contains the rowid of a row that may or may not be
+** present in the FTS3 table. If it is, delete it and adjust the contents
+** of subsiduary data structures accordingly.
+*/
+static int fts3DeleteByRowid(
+  Fts3Table *p, 
+  sqlite3_value *pRowid, 
+  int *pnDoc,
+  int *aSzDel
+){
+  int isEmpty = 0;
+  int rc = fts3IsEmpty(p, pRowid, &isEmpty);
+  if( rc==SQLITE_OK ){
+    if( isEmpty ){
+      /* Deleting this row means the whole table is empty. In this case
+      ** delete the contents of all three tables and throw away any
+      ** data in the pendingTerms hash table.  */
+      rc = fts3DeleteAll(p);
+      *pnDoc--;
+    }else{
+      sqlite3_int64 iRemove = sqlite3_value_int64(pRowid);
+      rc = fts3PendingTermsDocid(p, iRemove);
+      fts3DeleteTerms(&rc, p, pRowid, aSzDel);
+      fts3SqlExec(&rc, p, SQL_DELETE_CONTENT, &pRowid);
+      if( sqlite3_changes(p->db) ) *pnDoc--;
+      if( p->bHasDocsize ){
+        fts3SqlExec(&rc, p, SQL_DELETE_DOCSIZE, &pRowid);
+      }
+    }
+  }
+
+  return rc;
+}
 
 /*
 ** This function does the work for the xUpdate method of FTS3 virtual
@@ -2645,46 +2679,74 @@ int sqlite3Fts3UpdateMethod(
   u32 *aSzIns;                    /* Sizes of inserted documents */
   u32 *aSzDel;                    /* Sizes of deleted documents */
   int nChng = 0;                  /* Net change in number of documents */
+  int bInsertDone = 0;
+  int bReplace = 0;               /* True if on conflict mode is REPLACE */
 
   assert( p->pSegments==0 );
 
+  /* Check for a "special" INSERT operation. One of the form:
+  **
+  **   INSERT INTO xyz(xyz) VALUES('command');
+  */
+  if( nArg>1 
+   && sqlite3_value_type(apVal[0])==SQLITE_NULL 
+   && sqlite3_value_type(apVal[p->nColumn+2])!=SQLITE_NULL 
+  ){
+    return fts3SpecialInsert(p, apVal[p->nColumn+2]);
+  }
+
   /* Allocate space to hold the change in document sizes */
   aSzIns = sqlite3_malloc( sizeof(aSzIns[0])*(p->nColumn+1)*2 );
   if( aSzIns==0 ) return SQLITE_NOMEM;
   aSzDel = &aSzIns[p->nColumn+1];
   memset(aSzIns, 0, sizeof(aSzIns[0])*(p->nColumn+1)*2);
 
-  /* If this is a DELETE or UPDATE operation, remove the old record. */
-  if( sqlite3_value_type(apVal[0])!=SQLITE_NULL ){
-    int isEmpty = 0;
-    rc = fts3IsEmpty(p, apVal, &isEmpty);
-    if( rc==SQLITE_OK ){
-      if( isEmpty ){
-        /* Deleting this row means the whole table is empty. In this case
-        ** delete the contents of all three tables and throw away any
-        ** data in the pendingTerms hash table.
-        */
-        rc = fts3DeleteAll(p);
+  /* If this is an INSERT operation, or an UPDATE that modifies the rowid
+  ** value, then this operation requires constraint handling.
+  **
+  ** If the on-conflict mode is REPLACE, this means that the existing row
+  ** should be deleted from the database before inserting the new row. Or,
+  ** if the on-conflict mode is other than REPLACE, then this method must
+  ** detect the conflict and return SQLITE_CONSTRAINT before beginning to
+  ** modify the database file.
+  */
+  if( nArg>1 ){
+    sqlite3_int64 iNewRowid;
+    sqlite3_value *pNewRowid = apVal[3+p->nColumn];
+    if( sqlite3_value_type(pNewRowid)==SQLITE_NULL ){
+      pNewRowid = apVal[1];
+    }
+    if( sqlite3_value_type(pNewRowid)!=SQLITE_NULL && ( 
+        sqlite3_value_type(apVal[0])==SQLITE_NULL
+     || sqlite3_value_int64(apVal[0])!=sqlite3_value_int64(pNewRowid)
+    )){
+      if( sqlite3_vtab_on_conflict(p->db)==SQLITE_REPLACE ){
+        rc = fts3DeleteByRowid(p, pNewRowid, &nChng, aSzDel);
       }else{
-        isRemove = 1;
-        iRemove = sqlite3_value_int64(apVal[0]);
-        rc = fts3PendingTermsDocid(p, iRemove);
-        fts3DeleteTerms(&rc, p, apVal, aSzDel);
-        fts3SqlExec(&rc, p, SQL_DELETE_CONTENT, apVal);
-        if( p->bHasDocsize ){
-          fts3SqlExec(&rc, p, SQL_DELETE_DOCSIZE, apVal);
-        }
-        nChng--;
+        rc = fts3InsertData(p, apVal, pRowid);
+        bInsertDone = 1;
       }
     }
-  }else if( sqlite3_value_type(apVal[p->nColumn+2])!=SQLITE_NULL ){
+  }
+  if( rc!=SQLITE_OK ){
     sqlite3_free(aSzIns);
-    return fts3SpecialInsert(p, apVal[p->nColumn+2]);
+    return rc;
+  }
+
+  /* If this is a DELETE or UPDATE operation, remove the old record. */
+  if( sqlite3_value_type(apVal[0])!=SQLITE_NULL ){
+    assert( sqlite3_value_type(apVal[0])==SQLITE_INTEGER );
+    rc = fts3DeleteByRowid(p, apVal[0], &nChng, aSzDel);
+    isRemove = 1;
+    iRemove = sqlite3_value_int64(apVal[0]);
   }
   
   /* If this is an INSERT or UPDATE operation, insert the new record. */
   if( nArg>1 && rc==SQLITE_OK ){
-    rc = fts3InsertData(p, apVal, pRowid);
+    if( bInsertDone==0 ){
+      rc = fts3InsertData(p, apVal, pRowid);
+      if( rc==SQLITE_CONSTRAINT ) rc = SQLITE_CORRUPT;
+    }
     if( rc==SQLITE_OK && (!isRemove || *pRowid!=iRemove) ){
       rc = fts3PendingTermsDocid(p, *pRowid);
     }
index 8eaf9155202b0d97b916e58d083ed81c1a68fe18..36dc26d0ca4069f4dfe5f8a32780cd3a6d447435 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Disable\sthe\stransfer\soptimization\sif\sthe\sdestination\stable\scontains\nany\sforeign\skey\sconstraint\sand\sforeign\skey\sconstraints\sare\senabled.\nTicket\s[6284df89debdf].
-D 2011-04-24T22:56:07.596
+C Add\ssupport\sfor\son\sconflict\sclauses\sto\sfts3/fts4.
+D 2011-04-25T18:49:57.773
 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
 F Makefile.in 7a4d9524721d40ef9ee26f93f9bd6a51dba106f2
 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@@ -61,7 +61,7 @@ F ext/fts2/mkfts2amal.tcl 974d5d438cb3f7c4a652639262f82418c1e4cff0
 F ext/fts3/README.syntax a19711dc5458c20734b8e485e75fb1981ec2427a
 F ext/fts3/README.tokenizers 998756696647400de63d5ba60e9655036cb966e9
 F ext/fts3/README.txt 8c18f41574404623b76917b9da66fcb0ab38328d
-F ext/fts3/fts3.c 5653c5654ac9b65bf3646af7e1d695c7e9b991a0
+F ext/fts3/fts3.c 94fa15fc9d6290e2ba042c24fc83e272c86a40c6
 F ext/fts3/fts3.h 3a10a0af180d502cecc50df77b1b22df142817fe
 F ext/fts3/fts3Int.h 945926ea4b6a686c3e9834640a252d9870b7191e
 F ext/fts3/fts3_aux.c 9e931f55eed8498dafe7bc1160f10cbb1a652fdf
@@ -74,7 +74,7 @@ F ext/fts3/fts3_snippet.c e857c6a89d81d3b89df59f3b44b35c68d8ed5c62
 F ext/fts3/fts3_tokenizer.c 055f3dc7369585350b28db1ee0f3b214dca6724d
 F ext/fts3/fts3_tokenizer.h 13ffd9fcb397fec32a05ef5cd9e0fa659bf3dbd3
 F ext/fts3/fts3_tokenizer1.c 6e5cbaa588924ac578263a598e4fb9f5c9bb179d
-F ext/fts3/fts3_write.c c0af09a04021926d7d84094fa950defc9213416d
+F ext/fts3/fts3_write.c 388a7c7119f322d8fd4a5c19c9bd5793da47ccce
 F ext/fts3/fts3speed.tcl b54caf6a18d38174f1a6e84219950d85e98bb1e9
 F ext/fts3/mkfts3amal.tcl 252ecb7fe6467854f2aa237bf2c390b74e71f100
 F ext/icu/README.txt bf8461d8cdc6b8f514c080e4e10dc3b2bbdfefa9
@@ -138,7 +138,7 @@ F src/global.c 02335177cf6946fe5525c6f0755cf181140debf3
 F src/hash.c 458488dcc159c301b8e7686280ab209f1fb915af
 F src/hash.h 2894c932d84d9f892d4b4023a75e501f83050970
 F src/hwtime.h d32741c8f4df852c7d959236615444e2b1063b08
-F src/insert.c cdee360e5cea59db6c4a980e4360499631222af6
+F src/insert.c 3eea5a53d2644116fb865afaa4699fabe62b441c
 F src/journal.c 552839e54d1bf76fb8f7abe51868b66acacf6a0e
 F src/legacy.c a199d7683d60cef73089e892409113e69c23a99f
 F src/lempar.c 7f026423f4d71d989e719a743f98a1cbd4e6d99e
@@ -178,9 +178,9 @@ F src/resolve.c 1c0f32b64f8e3f555fe1f732f9d6f501a7f05706
 F src/rowset.c 69afa95a97c524ba6faf3805e717b5b7ae85a697
 F src/select.c d9d440809025a58547e39f4f268c2a296bfb56ff
 F src/shell.c 72e7e176bf46d5c6518d15ac4ad6847c4bb5df79
-F src/sqlite.h.in fe9a777d43276b4778e92b16a8b89ea6c38bb32b
+F src/sqlite.h.in 92f2daa48c1926d79db79229fb583cdb22d2d4c5
 F src/sqlite3ext.h c90bd5507099f62043832d73f6425d8d5c5da754
-F src/sqliteInt.h ac8f3f5846275c634f6649969304a9e97f6f9854
+F src/sqliteInt.h 5facb244a286e5c9ecd2f59758019f24a9245c8e
 F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d
 F src/status.c 7ac64842c86cec2fc1a1d0e5c16d3beb8ad332bf
 F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e
@@ -231,11 +231,11 @@ F src/test_wholenumber.c 6129adfbe7c7444f2e60cc785927f3aa74e12290
 F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
 F src/tokenize.c 604607d6813e9551cf5189d899e0a25c12681080
 F src/trigger.c 144cc18bb701f3286484aae4292a9531f09278c8
-F src/update.c 81911be16ece3c3e7716aa18565b4814ec41f8b9
+F src/update.c 5bcb56e5c7380a2eecb0e71891dbd4ad7437748f
 F src/utf.c d83650c3ea08f7407bd9d0839d9885241c209c60
 F src/util.c 465fe10aabf0ca7d7826a156dab919b0b65c525a
 F src/vacuum.c 05513dca036a1e7848fe18d5ed1265ac0b32365e
-F src/vdbe.c 05deeec6659f2579674a5e6510b3ada2a442f8d5
+F src/vdbe.c ac7aab1148964422b0a91ae5d50d31724fbd82ec
 F src/vdbe.h 8a675fefdf7119441fe817c800a9a52440c2e797
 F src/vdbeInt.h fe8f58d305e629fff02f61f655aca1d299f1f6ae
 F src/vdbeapi.c e0e2672e0a96ae3f8575c8ecd02912a3e8a554a1
@@ -243,7 +243,7 @@ F src/vdbeaux.c 9ae5074b19bdff2d8806a278533956fb281510d5
 F src/vdbeblob.c c3ccb7c8732858c680f442932e66ad06bb036562
 F src/vdbemem.c 0498796b6ffbe45e32960d6a1f5adfb6e419883b
 F src/vdbetrace.c 5d0dc3d5fd54878cc8d6d28eb41deb8d5885b114
-F src/vtab.c b0abc931f95af94c9ffdf9f747eb191cda953123
+F src/vtab.c 0e89db3e7416ccdab5138883d69ed8006a7e992c
 F src/wal.c 7334009b396285b658a95a3b6bc6d2b016a1f794
 F src/wal.h 7a5fbb00114b7f2cd40c7e1003d4c41ce9d26840
 F src/walker.c 3112bb3afe1d85dc52317cb1d752055e9a781f8f
@@ -450,11 +450,12 @@ F test/fts3al.test 07d64326e79bbdbab20ee87fc3328fbf01641c9f
 F test/fts3am.test 218aa6ba0dfc50c7c16b2022aac5c6be593d08d8
 F test/fts3an.test a49ccadc07a2f7d646ec1b81bc09da2d85a85b18
 F test/fts3ao.test b83f99f70e9eec85f27d75801a974b3f820e01f9
-F test/fts3atoken.test bbb9e63a915f3df0e35d06e0add932b5bf2d54a9
-F test/fts3aux1.test 719c35cbbcc04dde8e5a54a6f69851a0af9ed1f2
+F test/fts3atoken.test 402ef2f7c2fb4b3d4fa0587df6441c1447e799b3
+F test/fts3aux1.test 0b02743955d56fc0d4d66236a26177bd1b726de0
 F test/fts3b.test e93bbb653e52afde110ad53bbd793f14fe7a8984
 F test/fts3c.test fc723a9cf10b397fdfc2b32e73c53c8b1ec02958
 F test/fts3comp1.test a0f5b16a2df44dd0b15751787130af2183167c0c
+F test/fts3conf.test 2dc3bce3fe20d1e9b0ecd27d4040d07a2b79d16b
 F test/fts3corrupt.test 7890cc202406858386ddf390a879dcf80bc10abf
 F test/fts3corrupt2.test 6d96efae2f8a6af3eeaf283aba437e6d0e5447ba
 F test/fts3cov.test e0fb00d8b715ddae4a94c305992dfc3ef70353d7
@@ -930,7 +931,10 @@ F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224
 F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e
 F tool/split-sqlite3c.tcl d9be87f1c340285a3e081eb19b4a247981ed290c
 F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f
-P 0ab24b133e332ad7f4517b8e113e9c241ee9af9f
-R 40e74cf200b39d155b455f390ce862eb
-U drh
-Z 87bc3e585b95e6d3397bca153c02ab82
+P ddeea5ab5f6c0c4a86cdfbbb9f24d9d54bf8d301
+R ca5f1cf4bf0f7df53478a4e115a67980
+T *branch * vtab-conflict
+T *sym-vtab-conflict *
+T -sym-trunk *
+U dan
+Z dd80c4f8ae24b83ff1c8f809d0d54000
index 6503aa6c356d33a0f90246271f45bcd64e892155..942ac844992e316de9b2e1cf8838c6bd84bfe23e 100644 (file)
@@ -1 +1 @@
-ddeea5ab5f6c0c4a86cdfbbb9f24d9d54bf8d301
\ No newline at end of file
+6d2633a6d0a9bb88fb1a6adac0827dc51df2d4d2
\ No newline at end of file
index 02456e776f2f64ec8c387199f4cf71284beab4db..452939db942f2b41006374df75c9edd51b3abe00 100644 (file)
@@ -969,6 +969,7 @@ void sqlite3Insert(
       const char *pVTab = (const char *)sqlite3GetVTable(db, pTab);
       sqlite3VtabMakeWritable(pParse, pTab);
       sqlite3VdbeAddOp4(v, OP_VUpdate, 1, pTab->nCol+2, regIns, pVTab, P4_VTAB);
+      sqlite3VdbeChangeP5(v, onError==OE_Default ? OE_Abort : onError);
       sqlite3MayAbort(pParse);
     }else
 #endif
index 421da8e6bd2a3d5696d15340a479a4e776f87c94..2c3b1a50312d04bbe976d630ba7a9d8dad723a36 100644 (file)
@@ -6389,6 +6389,26 @@ int sqlite3_wal_checkpoint_v2(
 #define SQLITE_CHECKPOINT_FULL    1
 #define SQLITE_CHECKPOINT_RESTART 2
 
+/*
+** CAPI3REF: Virtual Table Interface Configuration
+*/
+int sqlite3_vtab_config(sqlite3*, int op, ...);
+
+/*
+** CAPI3REF: Determine The Virtual Table Conflict Policy
+*/
+#define SQLITE_VTAB_CONSTRAINT_SUPPORT 1
+int sqlite3_vtab_on_conflict(sqlite3 *);
+
+/*
+** CAPI3REF: Virtual Table Conflict Policies
+*/
+#define SQLITE_ROLLBACK 1
+/* #define SQLITE_IGNORE 2 */
+#define SQLITE_FAIL     3
+/* #define SQLITE_ABORT 4 */
+#define SQLITE_REPLACE  5
+
 
 /*
 ** Undo the hack that converts floating point types to integer for
index ea0925e4175ebf9d818f43b3d47f69e27ee8eb63..f000a228ca7563c3fb8d02e9d1e4e1ec01a6a76f 100644 (file)
@@ -632,6 +632,7 @@ typedef struct TriggerPrg TriggerPrg;
 typedef struct TriggerStep TriggerStep;
 typedef struct UnpackedRecord UnpackedRecord;
 typedef struct VTable VTable;
+typedef struct VtabCtx VtabCtx;
 typedef struct Walker Walker;
 typedef struct WherePlan WherePlan;
 typedef struct WhereInfo WhereInfo;
@@ -811,6 +812,7 @@ struct sqlite3 {
   u8 dfltLockMode;              /* Default locking-mode for attached dbs */
   signed char nextAutovac;      /* Autovac setting after VACUUM if >=0 */
   u8 suppressErr;               /* Do not issue error messages if true */
+  u8 vtabOnConflict;            /* Value to return for s3_vtab_on_conflict() */
   int nextPagesize;             /* Pagesize after VACUUM if >0 */
   int nTable;                   /* Number of tables in the database */
   CollSeq *pDfltColl;           /* The default collating sequence (BINARY) */
@@ -869,7 +871,10 @@ struct sqlite3 {
 #endif
 #ifndef SQLITE_OMIT_VIRTUALTABLE
   Hash aModule;                 /* populated by sqlite3_create_module() */
+#if 0
   Table *pVTab;                 /* vtab with active Connect/Create method */
+#endif
+  VtabCtx *pVtabCtx;            /* Context for active vtab connect/create */
   VTable **aVTrans;             /* Virtual tables with open transactions */
   int nVTrans;                  /* Allocated size of aVTrans */
   VTable *pDisconnect;    /* Disconnect these in next sqlite3_prepare() */
@@ -1232,6 +1237,7 @@ struct VTable {
   Module *pMod;             /* Pointer to module implementation */
   sqlite3_vtab *pVtab;      /* Pointer to vtab instance */
   int nRef;                 /* Number of pointers to this structure */
+  u8 bConstraint;           /* True if constraints are supported */
   VTable *pNext;            /* Next in linked list (see above) */
 };
 
index 315034d86f5e5638c17a56dc707eb4cbb8add95f..aecf75cf35c0fe48bd0d434dc8fe627e4ae1522a 100644 (file)
@@ -23,7 +23,8 @@ static void updateVirtualTable(
   ExprList *pChanges,  /* The columns to change in the UPDATE statement */
   Expr *pRowidExpr,    /* Expression used to recompute the rowid */
   int *aXRef,          /* Mapping from columns of pTab to entries in pChanges */
-  Expr *pWhere         /* WHERE clause of the UPDATE statement */
+  Expr *pWhere,        /* WHERE clause of the UPDATE statement */
+  int onError          /* ON CONFLICT strategy */
 );
 #endif /* SQLITE_OMIT_VIRTUALTABLE */
 
@@ -267,7 +268,7 @@ void sqlite3Update(
   /* Virtual tables must be handled separately */
   if( IsVirtual(pTab) ){
     updateVirtualTable(pParse, pTabList, pTab, pChanges, pRowidExpr, aXRef,
-                       pWhere);
+                       pWhere, onError);
     pWhere = 0;
     pTabList = 0;
     goto update_cleanup;
@@ -597,7 +598,8 @@ static void updateVirtualTable(
   ExprList *pChanges,  /* The columns to change in the UPDATE statement */
   Expr *pRowid,        /* Expression used to recompute the rowid */
   int *aXRef,          /* Mapping from columns of pTab to entries in pChanges */
-  Expr *pWhere         /* WHERE clause of the UPDATE statement */
+  Expr *pWhere,        /* WHERE clause of the UPDATE statement */
+  int onError          /* ON CONFLICT strategy */
 ){
   Vdbe *v = pParse->pVdbe;  /* Virtual machine under construction */
   ExprList *pEList = 0;     /* The result set of the SELECT statement */
@@ -654,6 +656,7 @@ static void updateVirtualTable(
   }
   sqlite3VtabMakeWritable(pParse, pTab);
   sqlite3VdbeAddOp4(v, OP_VUpdate, 0, pTab->nCol+2, iReg, pVTab, P4_VTAB);
+  sqlite3VdbeChangeP5(v, onError==OE_Default ? OE_Abort : onError);
   sqlite3MayAbort(pParse);
   sqlite3VdbeAddOp2(v, OP_Next, ephemTab, addr+1);
   sqlite3VdbeJumpHere(v, addr);
index 5376b08a00ef9d9f2d27e0d54627fcbe64af20ff..71756f8fcac194ccbc2d5908d31faff1a9f3ae0c 100644 (file)
@@ -5773,11 +5773,15 @@ case OP_VUpdate: {
   Mem **apArg;
   Mem *pX;
 
+  assert( pOp->p2==1        || pOp->p5==OE_Fail   || pOp->p5==OE_Rollback 
+       || pOp->p5==OE_Abort || pOp->p5==OE_Ignore || pOp->p5==OE_Replace
+  );
   pVtab = pOp->p4.pVtab->pVtab;
   pModule = (sqlite3_module *)pVtab->pModule;
   nArg = pOp->p2;
   assert( pOp->p4type==P4_VTAB );
   if( ALWAYS(pModule->xUpdate) ){
+    u8 vtabOnConflict = db->vtabOnConflict;
     apArg = p->apArg;
     pX = &aMem[pOp->p3];
     for(i=0; i<nArg; i++){
@@ -5787,13 +5791,23 @@ case OP_VUpdate: {
       apArg[i] = pX;
       pX++;
     }
+    db->vtabOnConflict = pOp->p5;
     rc = pModule->xUpdate(pVtab, nArg, apArg, &rowid);
+    db->vtabOnConflict = vtabOnConflict;
     importVtabErrMsg(p, pVtab);
     if( rc==SQLITE_OK && pOp->p1 ){
       assert( nArg>1 && apArg[0] && (apArg[0]->flags&MEM_Null) );
       db->lastRowid = rowid;
     }
-    p->nChange++;
+    if( rc==SQLITE_CONSTRAINT && pOp->p4.pVtab->bConstraint ){
+      if( pOp->p5==OE_Ignore ){
+        rc = SQLITE_OK;
+      }else{
+        p->errorAction = ((pOp->p5==OE_Replace) ? OE_Abort : pOp->p5);
+      }
+    }else{
+      p->nChange++;
+    }
   }
   break;
 }
index b052de23a5ff5c7b32511ab5faed98dbb4efcc07..51826e91f984f8fee9fe36844d81d1b252eb1586 100644 (file)
 #ifndef SQLITE_OMIT_VIRTUALTABLE
 #include "sqliteInt.h"
 
+/*
+** Before a virtual table xCreate() or xConnect() method is invoked, the
+** sqlite3.pVtabCtx member variable is set to point to an instance of
+** this struct allocated on the stack. It is used by the implementation of 
+** the sqlite3_declare_vtab() and sqlite3_vtab_config() APIs, both of which
+** are invoked only from within xCreate and xConnect methods.
+*/
+struct VtabCtx {
+  Table *pTab;
+  VTable *pVTable;
+};
+
 /*
 ** The actual function that does the work of creating a new module.
 ** This function implements the sqlite3_create_module() and
@@ -434,6 +446,7 @@ static int vtabCallConstructor(
   int (*xConstruct)(sqlite3*,void*,int,const char*const*,sqlite3_vtab**,char**),
   char **pzErr
 ){
+  VtabCtx sCtx;
   VTable *pVTable;
   int rc;
   const char *const*azArg = (const char *const*)pTab->azModuleArg;
@@ -453,12 +466,14 @@ static int vtabCallConstructor(
   pVTable->db = db;
   pVTable->pMod = pMod;
 
-  assert( !db->pVTab );
-  assert( xConstruct );
-  db->pVTab = pTab;
-
   /* Invoke the virtual table constructor */
+  assert( &db->pVtabCtx );
+  assert( xConstruct );
+  sCtx.pTab = pTab;
+  sCtx.pVTable = pVTable;
+  db->pVtabCtx = &sCtx;
   rc = xConstruct(db, pMod->pAux, nArg, azArg, &pVTable->pVtab, &zErr);
+  db->pVtabCtx = 0;
   if( rc==SQLITE_NOMEM ) db->mallocFailed = 1;
 
   if( SQLITE_OK!=rc ){
@@ -474,7 +489,7 @@ static int vtabCallConstructor(
     ** the sqlite3_vtab object if successful.  */
     pVTable->pVtab->pModule = pMod->pModule;
     pVTable->nRef = 1;
-    if( db->pVTab ){
+    if( sCtx.pTab ){
       const char *zFormat = "vtable constructor did not declare schema: %s";
       *pzErr = sqlite3MPrintf(db, zFormat, pTab->zName);
       sqlite3VtabUnlock(pVTable);
@@ -522,7 +537,6 @@ static int vtabCallConstructor(
   }
 
   sqlite3DbFree(db, zModuleName);
-  db->pVTab = 0;
   return rc;
 }
 
@@ -642,7 +656,7 @@ int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){
   char *zErr = 0;
 
   sqlite3_mutex_enter(db->mutex);
-  pTab = db->pVTab;
+  pTab = db->pVtabCtx->pTab;
   if( !pTab ){
     sqlite3Error(db, SQLITE_MISUSE, 0);
     sqlite3_mutex_leave(db->mutex);
@@ -670,7 +684,7 @@ int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){
         pParse->pNewTable->nCol = 0;
         pParse->pNewTable->aCol = 0;
       }
-      db->pVTab = 0;
+      db->pVtabCtx->pTab = 0;
     }else{
       sqlite3Error(db, SQLITE_ERROR, (zErr ? "%s" : 0), zErr);
       sqlite3DbFree(db, zErr);
@@ -937,4 +951,44 @@ void sqlite3VtabMakeWritable(Parse *pParse, Table *pTab){
   }
 }
 
+int sqlite3_vtab_on_conflict(sqlite3 *db){
+  int aMap[] = { 
+    SQLITE_ROLLBACK, SQLITE_IGNORE, SQLITE_ABORT, SQLITE_FAIL, SQLITE_REPLACE 
+  };
+  assert( OE_Rollback==1 && OE_Abort==2 && OE_Fail==3 );
+  assert( OE_Ignore==4 && OE_Replace==5 );
+  assert( db->vtabOnConflict>=1 && db->vtabOnConflict<=5 );
+  return aMap[db->vtabOnConflict-1];
+}
+
+
+int sqlite3_vtab_config(sqlite3 *db, int op, ...){
+  va_list ap;
+  int rc = SQLITE_OK;
+
+  sqlite3_mutex_enter(db->mutex);
+
+  va_start(ap, op);
+  switch( op ){
+    case SQLITE_VTAB_CONSTRAINT_SUPPORT: {
+      VtabCtx *p = db->pVtabCtx;
+      if( !p ){
+        rc = SQLITE_MISUSE_BKPT;
+      }else{
+        assert( (p->pTab->tabFlags & TF_Virtual)!=0 );
+        p->pVTable->bConstraint = (u8)va_arg(ap, int);
+      }
+      break;
+    }
+    default:
+      rc = SQLITE_MISUSE_BKPT;
+      break;
+  }
+  va_end(ap);
+
+  if( rc!=SQLITE_OK ) sqlite3Error(db, rc, 0);
+  sqlite3_mutex_leave(db->mutex);
+  return rc;
+}
+
 #endif /* SQLITE_OMIT_VIRTUALTABLE */
index d4c4825004d100dcc6f9ca43e7ab9df07b5de8f9..554259d0a55f23f3013cb6833ec017a17e34f38d 100644 (file)
@@ -167,15 +167,16 @@ ifcapable icu {
   do_icu_test fts3token-4.6 MiddleOfTheOcean  $input $output
   do_icu_test fts3token-4.7 th_TH  $input $output
   do_icu_test fts3token-4.8 en_US  $input $output
-}
 
-do_execsql_test 5.1 {
-  CREATE VIRTUAL TABLE x1 USING fts3(name,TOKENIZE icu en_US);
-  insert into x1 (name) values (NULL);
-  insert into x1 (name) values (NULL);
-  delete from x1;
+  do_execsql_test 5.1 {
+    CREATE VIRTUAL TABLE x1 USING fts3(name,TOKENIZE icu en_US);
+    insert into x1 (name) values (NULL);
+    insert into x1 (name) values (NULL);
+    delete from x1;
+  }
 }
 
+
 do_test fts3token-internal {
   execsql { SELECT fts3_tokenizer_internal_test() }
 } {ok}
index 5359521ab7aa56d4e7fa63161c428401ebcea418..adda586353dc2e92373293b8d2be59d3cc77e550 100644 (file)
@@ -38,10 +38,10 @@ do_execsql_test 1.2 {
   six   1 1     three 4 6     two   1 1
 }
 
-do_execsql_test 1.3 {
-  DELETE FROM t1;
+do_execsql_test 1.3.1 { DELETE FROM t1; }
+do_execsql_test 1.3.2 {
   SELECT term, documents, occurrences FROM terms WHERE col = '*';
-} {}
+}
 
 do_execsql_test 1.4 {
   INSERT INTO t1 VALUES('a b a b a b a');
diff --git a/test/fts3conf.test b/test/fts3conf.test
new file mode 100644 (file)
index 0000000..c3f36d7
--- /dev/null
@@ -0,0 +1,63 @@
+# 2011 April 25
+#
+# The author disclaims copyright to this source code.  In place of
+# a legal notice, here is a blessing:
+#
+#    May you do good and not evil.
+#    May you find forgiveness for yourself and forgive others.
+#    May you share freely, never taking more than you give.
+#
+#*************************************************************************
+# This file implements regression tests for SQLite library.  The
+# focus of this script is testing the FTS3 module.
+
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set testprefix fts3conf
+
+# If SQLITE_ENABLE_FTS3 is defined, omit this file.
+ifcapable !fts3 {
+  finish_test
+  return
+}
+
+do_execsql_test 1.0 {
+  CREATE VIRTUAL TABLE t1 USING fts3(x);
+  INSERT INTO t1(rowid, x) VALUES(1, 'a b c d');
+  INSERT INTO t1(rowid, x) VALUES(2, 'e f g h');
+
+  CREATE TABLE source(a, b);
+  INSERT INTO source VALUES(4, 'z');
+  INSERT INTO source VALUES(2, 'y');
+}
+db_save_and_close
+
+set T1 "INTO t1(rowid, x) VALUES(1, 'x')"
+set T2 "INTO t1(rowid, x) SELECT * FROM source"
+
+foreach {tn sql constraint data} [subst {
+  1    "INSERT OR ROLLBACK $T1"   1 {{a b c d} {e f g h}}
+  2    "INSERT OR ABORT    $T1"   1 {{a b c d} {e f g h} {i j k l}}
+  3    "INSERT OR FAIL     $T1"   1 {{a b c d} {e f g h} {i j k l}}
+  4    "INSERT OR IGNORE   $T1"   0 {{a b c d} {e f g h} {i j k l}}
+  5    "INSERT OR REPLACE  $T1"   0 {x {e f g h} {i j k l}}
+
+  6    "INSERT OR ROLLBACK $T2"   1 {{a b c d} {e f g h}}
+  7    "INSERT OR ABORT    $T2"   1 {{a b c d} {e f g h} {i j k l}}
+  8    "INSERT OR FAIL     $T2"   1 {{a b c d} {e f g h} {i j k l} z}
+  9    "INSERT OR IGNORE   $T2"   0 {{a b c d} {e f g h} {i j k l} z}
+  10   "INSERT OR REPLACE  $T2"   0 {{a b c d} y {i j k l} z}
+}] {
+  db_restore_and_reopen
+  execsql { 
+    BEGIN;
+      INSERT INTO t1(rowid, x) VALUES(3, 'i j k l');
+  }
+  set R(0) {0 {}}
+  set R(1) {1 {constraint failed}}
+  do_catchsql_test 1.$tn.1 $sql $R($constraint)
+  do_catchsql_test 1.$tn.2 { SELECT * FROM t1 } [list 0 $data]
+}
+
+finish_test