我试图使用SQLite的新C接口preupdate hook:
https://www.sqlite.org/c3ref/preupdate_count.html
现在我的问题: pre_update API签名如下:
void *sqlite3_preupdate_hook(
sqlite3 *db,
void(*xPreUpdate)(
void *pCtx, /* Copy of third arg to preupdate_hook() */
sqlite3 *db, /* Database handle */
int op, /* SQLITE_UPDATE, DELETE or INSERT */
char const *zDb, /* Database name */
char const *zName, /* Table name */
sqlite3_int64 iKey1, /* Rowid of row about to be deleted/updated */
sqlite3_int64 iKey2 /* New rowid value (for a rowid UPDATE) */
),
void*
);
正如您所看到的,它向回调注入了一个指向注册钩子的数据库连接的指针。 根据我的经验和SQLite文档,我知道更新/提交/回滚挂钩不是可重入的,这意味着它们无法修改导致挂钩调用的连接。
我想使用这个pre_update回调来从数据库中读取和写入。
现在我有两个问题:
1)SQLite pre_update回调是否可重入并获得了从回调范围修改和读取数据库的支持?
2)如果确实如此,有人可以解释下列内容是如何实现的吗?
我创建了一个新数据库并使用SQLite shell运行以下内容:
sqlite> CREATE TABLE Parent(_index INTEGER PRIMARY KEY);
sqlite> INSERT INTO "Parent" VALUES(1);
sqlite> CREATE TABLE Child(_index INTEGER PRIMARY KEY, CONSTRAINT ch_fk FOREIGN KEY(_index) REFERENCES Parent(_index) DEFERRABLE INITIALLY DEFERRED);
sqlite> INSERT INTO "Child" VALUES(1);
现在我正在尝试创建一个自动操作,该操作应该在事务内部工作,并在每次父项更改其密钥时更新子项,因此它不会成为约束违规。
注意:我知道我可以使用triggers / SQLite外键UPDATE机制实现此行为,但我愿意测试此API的稳定性。
所以代码:
int main(int argc, char *argv[]){
int rc;
char *err_msg;
sqlite3 *sqlite_connection;
rc = sqlite3_open("__database__.db",&sqlite_connection);
rc += rc = sqlite3_exec(sqlite_connection, "PRAGMA foreign_keys=ON;", 0, 0, &err_msg);/* set foreign keys mechanism on */
if(rc != SQLITE_OK){
printf("Error initializing connection : %s\n",err_msg);
exit(rc);
}
sqlite3_preupdate_hook(sqlite_connection,pre_hook,NULL);
//Watch tables content before
rc = sqlite3_exec(sqlite_connection, "SELECT * FROM PARENT;", callback, (void*)NULL, &err_msg);
rc = sqlite3_exec(sqlite_connection, "SELECT * FROM CHILD;", callback, (void*)NULL, &err_msg);
//BEGIN TRANSACTION
rc = sqlite3_exec(sqlite_connection, "BEGIN", 0, 0, &err_msg);
/* Update table to invoke callback */
rc += sqlite3_exec(sqlite_connection, "UPDATE Parent SET _index = 2 WHERE _index = 1", 0, 0, &err_msg); /* Update table to invoke callback */
//Watch tables content after
rc = sqlite3_exec(sqlite_connection, "SELECT * FROM PARENT;", callback, (void*)NULL, &err_msg);
rc = sqlite3_exec(sqlite_connection, "SELECT * FROM CHILD;", callback, (void*)NULL, &err_msg);
rc += sqlite3_exec(sqlite_connection, "COMMIT;", 0, 0, &err_msg);
if(rc != SQLITE_OK){
printf("Error updating the database : %s\n",err_msg);
rc = sqlite3_exec(sqlite_connection, "ROLLBACK", 0, 0, &err_msg);
if(rc != SQLITE_OK){
printf("Error updating the database : %s\n",err_msg);
}
}
sqlite3_close(sqlite_connection);
return rc;
}
和pre_hook:
void pre_hook(
void *pCtx, /* Copy of third arg to preupdate_hook() */
sqlite3 *db, /* Database handle */
int op, /* SQLITE_UPDATE, DELETE or INSERT */
char const *zDb, /* Database name */
char const *zName, /* Table name */
sqlite3_int64 iKey1, /* Rowid of row about to be deleted/updated */
sqlite3_int64 iKey2){
char query_buffer[100];
char *err_msg;
if((strcmp("Parent",zName) == 0) && (SQLITE_UPDATE == op)){
sprintf(query_buffer,"UPDATE Child SET _index = %d WHERE _index = %d",(int)iKey2,(int)iKey1);
if(sqlite3_exec(db,query_buffer , 0, 0, &err_msg) != SQLITE_OK){
printf("Error executin trigger\n");
}
}
}
然后我得到输出:
//操作前: 家长: _indx:1
孩子: _indx:1
//操作后: 家长: _indx:2
孩子: _indx:2 更新数据库时出错:FOREIGN KEY约束违规
如你所见,根本没有违规行为!。
但是,当我按如下方式更改主要功能时:
1)取消签署pre_hook回调。
2)在回调之外操作子更新(在我们的情况下,在父更新之后)。
我突然得到相同的输出而没有错误。
我认为这意味着pre_update回调不是可重入的,但我寻求这个问题的专业答案。
答案 0 :(得分:0)
更新钩子实现不能做任何会修改的事情 调用更新挂钩的数据库连接。任何行动 修改数据库连接必须延迟到之后 完成触发更新挂钩的sqlite3_step()调用。 请注意,sqlite3_prepare_v2()和sqlite3_step()都会修改它们 数据库连接的含义"修改"在这一段中。