Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions src/insert.c
Original file line number Diff line number Diff line change
Expand Up @@ -2879,7 +2879,19 @@ void sqlite3CompleteInsertion(
}
#endif
}
#ifdef DOLTLITE_PROLLY
if( VisibleRowid(pTab) ){
pik_flags |= (update_flags & OPFLAG_ISUPDATE);
}
#endif
}
#ifdef DOLTLITE_PROLLY
if( (pik_flags & OPFLAG_NCHANGE)!=0 && VisibleRowid(pTab)
&& !pParse->nested ){
sqlite3VdbeAddOp3(v, OP_IdxInsert, iIdxCur+i, aRegIdx[i], aRegIdx[i]+1);
sqlite3VdbeAppendP4(v, pTab, P4_TABLE);
}else
#endif
sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iIdxCur+i, aRegIdx[i],
aRegIdx[i]+1,
pIdx->uniqNotNull ? pIdx->nKeyCol: pIdx->nColumn);
Expand Down Expand Up @@ -3518,6 +3530,12 @@ static int xferOptimization(
}
}
sqlite3VdbeAddOp2(v, OP_IdxInsert, iDest, regData);
#ifdef DOLTLITE_PROLLY
if( (idxInsFlags & OPFLAG_NCHANGE)!=0 && VisibleRowid(pDest)
&& !pParse->nested ){
sqlite3VdbeAppendP4(v, pDest, P4_TABLE);
}
#endif
sqlite3VdbeChangeP5(v, idxInsFlags|OPFLAG_APPEND);
sqlite3VdbeAddOp2(v, OP_Next, iSrc, addr1+1); VdbeCoverage(v);
sqlite3VdbeJumpHere(v, addr1);
Expand Down
62 changes: 56 additions & 6 deletions src/vdbe.c
Original file line number Diff line number Diff line change
Expand Up @@ -6327,6 +6327,9 @@ case OP_Delete: {
const char *zDb;
Table *pTab;
int opflags;
#ifdef DOLTLITE_PROLLY
i64 iHookRowid = 0;
#endif

opflags = pOp->p2;
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
Expand Down Expand Up @@ -6364,6 +6367,11 @@ case OP_Delete: {
if( (pOp->p5 & OPFLAG_SAVEPOSITION)!=0 && pC->isTable ){
pC->movetoTarget = sqlite3BtreeIntegerKey(pC->uc.pCursor);
}
#ifdef DOLTLITE_PROLLY
if( VisibleRowid(pTab) && !HasRowid(pTab) && db->xUpdateCallback ){
iHookRowid = sqlite3BtreeSqlRowid(pC->uc.pCursor);
}
#endif
}else{
zDb = 0;
pTab = 0;
Expand Down Expand Up @@ -6414,9 +6422,20 @@ case OP_Delete: {
/* Invoke the update-hook if required. */
if( opflags & OPFLAG_NCHANGE ){
p->nChange++;
if( db->xUpdateCallback && ALWAYS(pTab!=0) && HasRowid(pTab) ){
if( db->xUpdateCallback && ALWAYS(pTab!=0)
#ifdef DOLTLITE_PROLLY
&& VisibleRowid(pTab)
#else
&& HasRowid(pTab)
#endif
){
#ifdef DOLTLITE_PROLLY
db->xUpdateCallback(db->pUpdateArg, SQLITE_DELETE, zDb, pTab->zName,
HasRowid(pTab) ? pC->movetoTarget : iHookRowid);
#else
db->xUpdateCallback(db->pUpdateArg, SQLITE_DELETE, zDb, pTab->zName,
pC->movetoTarget);
#endif
assert( pC->iDb>=0 );
}
}
Expand Down Expand Up @@ -7036,8 +7055,21 @@ case OP_IdxInsert: { /* in2 */
if( rc ) goto abort_due_to_error;
x.nKey = pIn2->n;
x.pKey = pIn2->z;
x.aMem = aMem + pOp->p3;
x.nMem = (u16)pOp->p4.i;
#ifdef DOLTLITE_PROLLY
if( pOp->p4type==P4_TABLE ){
if( pOp->p3>0 && pC->pKeyInfo!=0 ){
x.aMem = aMem + pOp->p3;
x.nMem = pC->pKeyInfo->nKeyField;
}else{
x.aMem = 0;
x.nMem = 0;
}
}else
#endif
{
x.aMem = aMem + pOp->p3;
x.nMem = (u16)pOp->p4.i;
}
rc = sqlite3BtreeInsert(pC->uc.pCursor, &x,
(pOp->p5 & (OPFLAG_APPEND|OPFLAG_SAVEPOSITION|OPFLAG_PREFORMAT)),
((pOp->p5 & OPFLAG_USESEEKRESULT) ? pC->seekResult : 0)
Expand All @@ -7046,9 +7078,27 @@ case OP_IdxInsert: { /* in2 */
pC->cacheStatus = CACHE_STALE;
if( rc) goto abort_due_to_error;
#ifdef DOLTLITE_PROLLY
if( (pOp->p5 & OPFLAG_LASTROWID)!=0 && (pIn2->flags & MEM_Blob)!=0 ){
db->lastRowid = doltliteSyntheticRowidFromRecord(
(const u8*)pIn2->z, pIn2->n, pC->pKeyInfo);
if( (pIn2->flags & MEM_Blob)!=0 ){
i64 iRowid = 0;
int bRowid = 0;
if( (pOp->p5 & OPFLAG_LASTROWID)!=0 ){
iRowid = doltliteSyntheticRowidFromRecord(
(const u8*)pIn2->z, pIn2->n, pC->pKeyInfo);
db->lastRowid = iRowid;
bRowid = 1;
}
if( pOp->p4type==P4_TABLE && db->xUpdateCallback!=0 ){
Table *pTab = pOp->p4.pTab;
if( pTab && pTab->aCol && VisibleRowid(pTab) && pC->iDb>=0 ){
if( !bRowid ){
iRowid = doltliteSyntheticRowidFromRecord(
(const u8*)pIn2->z, pIn2->n, pC->pKeyInfo);
}
db->xUpdateCallback(db->pUpdateArg,
(pOp->p5 & OPFLAG_ISUPDATE) ? SQLITE_UPDATE : SQLITE_INSERT,
db->aDb[pC->iDb].zDbSName, pTab->zName, iRowid);
}
}
}
#endif
break;
Expand Down
122 changes: 121 additions & 1 deletion test/doltlite_regression_test_c.c
Original file line number Diff line number Diff line change
Expand Up @@ -13234,6 +13234,125 @@ static void run_reset_database_current_branch(void){
removeDbFiles(dbpath);
}

typedef struct UpdateHookLog UpdateHookLog;
struct UpdateHookLog {
char z[512];
};

static void clusteredUpdateHook(
void *p,
int op,
const char *zDb,
const char *zTbl,
sqlite3_int64 rowid
){
UpdateHookLog *pLog = (UpdateHookLog*)p;
char zOp;
char zOne[96];
(void)zDb;
if( op==SQLITE_INSERT ) zOp = 'I';
else if( op==SQLITE_UPDATE ) zOp = 'U';
else if( op==SQLITE_DELETE ) zOp = 'D';
else zOp = '?';
snprintf(zOne, sizeof(zOne), "%s%c:%s:%lld",
pLog->z[0] ? "," : "", zOp, zTbl ? zTbl : "", (long long)rowid);
if( strlen(pLog->z)+strlen(zOne)+1 < sizeof(pLog->z) ){
strcat(pLog->z, zOne);
}
}

static void run_clustered_pk_update_hook(void){
char dbpath[512];
sqlite3 *db = 0;
UpdateHookLog log;
sqlite3_int64 rA, rB;

printf("=== Clustered PK Update Hook Test ===\n\n");
make_dbpath(dbpath, sizeof(dbpath), "test_clustered_pk_update_hook");
removeDbFiles(dbpath);
check("cpk_hook_open", open_db(dbpath, &db)==SQLITE_OK);
if( !db ) return;

memset(&log, 0, sizeof(log));
sqlite3_update_hook(db, clusteredUpdateHook, &log);
check("cpk_hook_intpk_dml", execSql(db,
"CREATE TABLE ipk(a INTEGER PRIMARY KEY, v INT);"
"INSERT INTO ipk VALUES(1,1);"
"UPDATE ipk SET v=2 WHERE a=1;"
"DELETE FROM ipk WHERE a=1;")==SQLITE_OK);
check("cpk_hook_intpk_events", strcmp(log.z, "I:ipk:1,U:ipk:1,D:ipk:1")==0);

memset(&log, 0, sizeof(log));
check("cpk_hook_textpk_create", execSql(db,
"CREATE TABLE t(k TEXT PRIMARY KEY, v INT);")==SQLITE_OK);
check("cpk_hook_textpk_insert_a",
execSql(db, "INSERT INTO t VALUES('a',1);")==SQLITE_OK);
rA = queryInt64(db, "SELECT rowid FROM t WHERE k='a'");
check("cpk_hook_textpk_insert_b",
execSql(db, "INSERT INTO t VALUES('b',2);")==SQLITE_OK);
rB = queryInt64(db, "SELECT rowid FROM t WHERE k='b'");
check("cpk_hook_textpk_update",
execSql(db, "UPDATE t SET v=3 WHERE k='a';")==SQLITE_OK);
check("cpk_hook_textpk_update_unique",
queryInt64(db, "SELECT count(*) FROM t WHERE k='a'")==1);
check("cpk_hook_textpk_delete",
execSql(db, "DELETE FROM t WHERE k='b';")==SQLITE_OK);
{
char zWant[160];
snprintf(zWant, sizeof(zWant), "I:t:%lld,I:t:%lld,U:t:%lld,D:t:%lld",
(long long)rA, (long long)rB, (long long)rA, (long long)rB);
check("cpk_hook_textpk_rowids_nonzero", rA!=0 && rB!=0 && rA!=rB);
check("cpk_hook_textpk_events", strcmp(log.z, zWant)==0);
}

memset(&log, 0, sizeof(log));
check("cpk_hook_without_rowid_dml", execSql(db,
"CREATE TABLE w(k TEXT PRIMARY KEY, v INT) WITHOUT ROWID;"
"INSERT INTO w VALUES('a',1),('b',2);"
"UPDATE w SET v=3 WHERE k='a';"
"DELETE FROM w WHERE k='b';")==SQLITE_OK);
check("cpk_hook_without_rowid_silent", log.z[0]==0);

check("cpk_hook_cascade_schema", execSql(db,
"PRAGMA foreign_keys=ON;"
"CREATE TABLE p1(pid PRIMARY KEY);"
"CREATE TABLE c1(cid PRIMARY KEY,"
" pid REFERENCES p1(pid) ON UPDATE CASCADE);"
"INSERT INTO p1 VALUES(10),(20);"
"INSERT INTO c1 VALUES(11,10),(12,10),(21,20),(22,20);"
"UPDATE p1 SET pid = pid * 10;")==SQLITE_OK);
check("cpk_hook_cascade_child_count",
queryInt64(db, "SELECT count(*) FROM c1")==4);
check("cpk_hook_cascade_child_rows",
strcmp(queryScalarText(db,
"SELECT group_concat(cid||' '||pid, ' ') "
"FROM (SELECT cid, pid FROM c1 ORDER BY cid)"),
"11 100 12 100 21 200 22 200")==0);
check("cpk_hook_cascade_parent_rows",
strcmp(queryScalarText(db,
"SELECT group_concat(pid, ' ') FROM (SELECT pid FROM p1 ORDER BY pid)"),
"100 200")==0);

check("cpk_hook_cascade_attach", execSql(db,
"ATTACH ':memory:' AS aux;"
"CREATE TABLE aux.p1(pid PRIMARY KEY);"
"CREATE TABLE aux.c1(cid PRIMARY KEY,"
" pid REFERENCES p1(pid) ON UPDATE CASCADE);"
"INSERT INTO aux.p1 VALUES(10),(20);"
"INSERT INTO aux.c1 VALUES(11,10),(12,10),(21,20),(22,20);"
"UPDATE aux.p1 SET pid = pid * 10;")==SQLITE_OK);
check("cpk_hook_cascade_attach_count",
queryInt64(db, "SELECT count(*) FROM aux.c1")==4);
check("cpk_hook_cascade_attach_rows",
strcmp(queryScalarText(db,
"SELECT group_concat(cid||' '||pid, ' ') "
"FROM (SELECT cid, pid FROM aux.c1 ORDER BY cid)"),
"11 100 12 100 21 200 22 200")==0);

sqlite3_close(db);
removeDbFiles(dbpath);
}

static const RegressionCase aCases[] = {
{ "refs_vtab_snapshot_stability", "Refs Vtab Snapshot Stability Test", run_refs_vtab_snapshot_stability },
{ "storage_format_v12", "Storage Format Version 12 Test", run_storage_format_v12 },
Expand Down Expand Up @@ -13430,7 +13549,8 @@ static const RegressionCase aCases[] = {
{ "intpk_scan_delete_keeps_scan", "INT PK Scan Delete Keeps Scan Test", run_intpk_scan_delete_keeps_scan },
{ "count_flush_keeps_scan", "Count Flush Keeps Scan Test", run_count_flush_keeps_scan },
{ "negzero_sortkey_eq", "Negzero Sortkey Eq Test", run_negzero_sortkey_eq },
{ "reset_database_current_branch", "Reset Database Current Branch Test", run_reset_database_current_branch }
{ "reset_database_current_branch", "Reset Database Current Branch Test", run_reset_database_current_branch },
{ "clustered_pk_update_hook", "Clustered PK Update Hook Test", run_clustered_pk_update_hook }
};

static int run_case_by_name(const char *zName){
Expand Down
Loading