When preparing an UPDATE statement, avoid generating VDBE code for those foreign key related actions and constraint checks that may be seen to be unnecessary by considering the subset of table columns potentially modified by the UPDATE.
FossilOrigin-Name: e940b5de49baa1d6a4cf859fbbc0e0df86ac5dbf
This commit is contained in:
parent
74dade2155
commit
8ff2d9561a
25
manifest
25
manifest
@ -1,5 +1,5 @@
|
||||
C Rearrange\sthe\sorder\sof\sconditions\sin\san\s"if"\sstatement\sto\sfacilitate\stesting.
|
||||
D 2013-09-04T18:14:53.187
|
||||
C When\spreparing\san\sUPDATE\sstatement,\savoid\sgenerating\sVDBE\scode\sfor\sthose\sforeign\skey\srelated\sactions\sand\sconstraint\schecks\sthat\smay\sbe\sseen\sto\sbe\sunnecessary\sby\sconsidering\sthe\ssubset\sof\stable\scolumns\spotentially\smodified\sby\sthe\sUPDATE.
|
||||
D 2013-09-05T18:40:29.445
|
||||
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
|
||||
F Makefile.in 5e41da95d92656a5004b03d3576e8b226858a28e
|
||||
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
|
||||
@ -171,16 +171,16 @@ F src/callback.c d7e46f40c3cf53c43550b7da7a1d0479910b62cc
|
||||
F src/complete.c dc1d136c0feee03c2f7550bafc0d29075e36deac
|
||||
F src/ctime.c ea4b7f3623a0fcb1146e7f245d7410033e86859c
|
||||
F src/date.c 067a81c9942c497aafd2c260e13add8a7d0c7dd4
|
||||
F src/delete.c 2317c814866d9aa71fea16b3faf4fdd4d6a49b94
|
||||
F src/delete.c 2dc64ca360b7d7da481183ea920a813a0c203c97
|
||||
F src/expr.c 4d89bd03a04fcdb5ff71d86b4e0cc7d3230797b8
|
||||
F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb
|
||||
F src/fkey.c 914a6bbd987d857c41ac9d244efa6641f36faadb
|
||||
F src/fkey.c be866cd8c4fa6cae98ba33109578fd1a3311ee5b
|
||||
F src/func.c 5b064acd303b3e74f019ab551d423ff6cace4023
|
||||
F src/global.c 5caf4deab621abb45b4c607aad1bd21c20aac759
|
||||
F src/hash.c ac3470bbf1ca4ae4e306a8ecb0fdf1731810ffe4
|
||||
F src/hash.h 8890a25af81fb85a9ad7790d32eedab4b994da22
|
||||
F src/hwtime.h d32741c8f4df852c7d959236615444e2b1063b08
|
||||
F src/insert.c a66bcdc956145369c1a876709f47f69476973e15
|
||||
F src/insert.c a271771db927873c850da8928d6ee9fc058fb9fe
|
||||
F src/journal.c b4124532212b6952f42eb2c12fa3c25701d8ba8d
|
||||
F src/legacy.c 0df0b1550b9cc1f58229644735e317ac89131f12
|
||||
F src/lempar.c cdf0a000315332fc9b50b62f3b5e22e080a0952b
|
||||
@ -221,7 +221,7 @@ F src/shell.c d920a891ca09b8bd262cced7fb0ab9d723f7a747
|
||||
F src/sqlite.h.in ec40aa958a270416fb04b4f72210357bf163d2c5
|
||||
F src/sqlite3.rc fea433eb0a59f4c9393c8e6d76a6e2596b1fe0c0
|
||||
F src/sqlite3ext.h 886f5a34de171002ad46fae8c36a7d8051c190fc
|
||||
F src/sqliteInt.h 600086a5082e2291b0aeeefcfbb546f2bbda67b2
|
||||
F src/sqliteInt.h a083fc4effb15c15946a36919793c311bc247b57
|
||||
F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d
|
||||
F src/status.c 7ac05a5c7017d0b9f0b4bcd701228b784f987158
|
||||
F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e
|
||||
@ -273,7 +273,7 @@ F src/test_vfstrace.c 34b544e80ba7fb77be15395a609c669df2e660a2
|
||||
F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
|
||||
F src/tokenize.c 70061085a51f2f4fc15ece94f32c03bcb78e63b2
|
||||
F src/trigger.c 5c0ea9b8755e7c5e1a700f3e27ac4f8d92dd221e
|
||||
F src/update.c 7d9d38e4f341ada7d79035ea969cdefb8b9014d1
|
||||
F src/update.c f5182157f5d0d0a97bc5f5e3c9bdba0dfbe08f08
|
||||
F src/utf.c 6fc6c88d50448c469c5c196acf21617a24f90269
|
||||
F src/util.c f566b5138099a2df8533b190d0dcc74b7dfbe0c9
|
||||
F src/vacuum.c d9c5759f4c5a438bb43c2086f72c5d2edabc36c8
|
||||
@ -462,6 +462,7 @@ F test/fkey3.test 5ec899d12b13bcf1e9ef40eff7fb692fdb91392e
|
||||
F test/fkey4.test 86446017011273aad8f9a99c1a65019e7bd9ca9d
|
||||
F test/fkey5.test 0bf64f2d19ad80433ca0b24edbf604a18b353d5f
|
||||
F test/fkey6.test c555f7fc45d842cc84b0d3ff93951ce2b8c25fc8
|
||||
F test/fkey7.test e31d0e71a41c1d29349a16448d6c420e2c53a8fc
|
||||
F test/fkey_malloc.test bb74c9cb8f8fceed03b58f8a7ef2df98520bbd51
|
||||
F test/format4.test 1f0cac8ff3895e9359ed87e41aaabee982a812eb
|
||||
F test/fts-9fd058691.test 78b887e30ae6816df0e1fed6259de4b5a64ad33c
|
||||
@ -717,7 +718,7 @@ F test/pagesize.test 1dd51367e752e742f58e861e65ed7390603827a0
|
||||
F test/pcache.test b09104b03160aca0d968d99e8cd2c5b1921a993d
|
||||
F test/pcache2.test a83efe2dec0d392f814bfc998def1d1833942025
|
||||
F test/percentile.test b98fc868d71eb5619d42a1702e9ab91718cbed54
|
||||
F test/permutations.test 9cd3bba6f1e8658442d0dcfcef6c2e5efd131bdc
|
||||
F test/permutations.test 72f4f8881d388163ddbbeec0a6ed812e863ea2e6
|
||||
F test/pragma.test 5e7de6c32a5d764f09437d2025f07e4917b9e178
|
||||
F test/pragma2.test 224f0381f9411a78ae685cac24c13656a62021b7
|
||||
F test/printf.test ec9870c4dce8686a37818e0bf1aba6e6a1863552
|
||||
@ -1110,7 +1111,7 @@ F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
|
||||
F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381
|
||||
F tool/wherecosttest.c f407dc4c79786982a475261866a161cd007947ae
|
||||
F tool/win/sqlite.vsix 97894c2790eda7b5bce3cc79cb2a8ec2fde9b3ac
|
||||
P f929e9b41f2f69b086be3dd48fe557adbba7ae5c
|
||||
R cc5fb62c722092188f8662d393b9b48e
|
||||
U drh
|
||||
Z 3d3da6aaad5a89de084de232785827d1
|
||||
P 8462fb43c275a70db59c4339650225deeadeef00
|
||||
R d3b1cfd48ce8f1b63476cb1b87cb1fe9
|
||||
U dan
|
||||
Z ebec4296d6724df2f506ca7d4f2e487f
|
||||
|
@ -1 +1 @@
|
||||
8462fb43c275a70db59c4339650225deeadeef00
|
||||
e940b5de49baa1d6a4cf859fbbc0e0df86ac5dbf
|
@ -536,7 +536,7 @@ void sqlite3GenerateRowDelete(
|
||||
/* Do FK processing. This call checks that any FK constraints that
|
||||
** refer to this table (i.e. constraints attached to other tables)
|
||||
** are not violated by deleting this row. */
|
||||
sqlite3FkCheck(pParse, pTab, iOld, 0);
|
||||
sqlite3FkCheck(pParse, pTab, iOld, 0, 0, 0);
|
||||
}
|
||||
|
||||
/* Delete the index and table entries. Skip this step if pTab is really
|
||||
@ -553,7 +553,7 @@ void sqlite3GenerateRowDelete(
|
||||
/* Do any ON CASCADE, SET NULL or SET DEFAULT operations required to
|
||||
** handle rows (possibly in other tables) that refer via a foreign key
|
||||
** to the row just deleted. */
|
||||
sqlite3FkActions(pParse, pTab, 0, iOld);
|
||||
sqlite3FkActions(pParse, pTab, 0, iOld, 0, 0);
|
||||
|
||||
/* Invoke AFTER DELETE trigger programs. */
|
||||
sqlite3CodeRowTrigger(pParse, pTrigger,
|
||||
|
112
src/fkey.c
112
src/fkey.c
@ -682,6 +682,70 @@ void sqlite3FkDropTable(Parse *pParse, SrcList *pName, Table *pTab){
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** The second argument points to an FKey object representing a foreign key
|
||||
** for which pTab is the child table. An UPDATE statement against pTab
|
||||
** is currently being processed. For each column of the table that is
|
||||
** actually updated, the corresponding element in the aChange[] array
|
||||
** is zero or greater (if a column is unmodified the corresponding element
|
||||
** is set to -1). If the rowid column is modified by the UPDATE statement
|
||||
** the bChngRowid argument is non-zero.
|
||||
**
|
||||
** This function returns true if any of the columns that are part of the
|
||||
** child key for FK constraint *p are modified.
|
||||
*/
|
||||
static int fkChildIsModified(
|
||||
Table *pTab, /* Table being updated */
|
||||
FKey *p, /* Foreign key for which pTab is the child */
|
||||
int *aChange, /* Array indicating modified columns */
|
||||
int bChngRowid /* True if rowid is modified by this update */
|
||||
){
|
||||
int i;
|
||||
for(i=0; i<p->nCol; i++){
|
||||
int iChildKey = p->aCol[i].iFrom;
|
||||
if( aChange[iChildKey]>=0 ) return 1;
|
||||
if( iChildKey==pTab->iPKey && bChngRowid ) return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** The second argument points to an FKey object representing a foreign key
|
||||
** for which pTab is the parent table. An UPDATE statement against pTab
|
||||
** is currently being processed. For each column of the table that is
|
||||
** actually updated, the corresponding element in the aChange[] array
|
||||
** is zero or greater (if a column is unmodified the corresponding element
|
||||
** is set to -1). If the rowid column is modified by the UPDATE statement
|
||||
** the bChngRowid argument is non-zero.
|
||||
**
|
||||
** This function returns true if any of the columns that are part of the
|
||||
** parent key for FK constraint *p are modified.
|
||||
*/
|
||||
static int fkParentIsModified(
|
||||
Table *pTab,
|
||||
FKey *p,
|
||||
int *aChange,
|
||||
int bChngRowid
|
||||
){
|
||||
int i;
|
||||
for(i=0; i<p->nCol; i++){
|
||||
char *zKey = p->aCol[i].zCol;
|
||||
int iKey;
|
||||
for(iKey=0; iKey<pTab->nCol; iKey++){
|
||||
if( aChange[iKey]>=0 || (iKey==pTab->iPKey && bChngRowid) ){
|
||||
Column *pCol = &pTab->aCol[iKey];
|
||||
if( zKey ){
|
||||
if( 0==sqlite3StrICmp(pCol->zName, zKey) ) return 1;
|
||||
}else if( pCol->colFlags & COLFLAG_PRIMKEY ){
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is called when inserting, deleting or updating a row of
|
||||
** table pTab to generate VDBE code to perform foreign key constraint
|
||||
@ -706,7 +770,9 @@ void sqlite3FkCheck(
|
||||
Parse *pParse, /* Parse context */
|
||||
Table *pTab, /* Row is being deleted from this table */
|
||||
int regOld, /* Previous row data is stored here */
|
||||
int regNew /* New row data is stored here */
|
||||
int regNew, /* New row data is stored here */
|
||||
int *aChange, /* Array indicating UPDATEd columns (or 0) */
|
||||
int bChngRowid /* True if rowid is UPDATEd */
|
||||
){
|
||||
sqlite3 *db = pParse->db; /* Database handle */
|
||||
FKey *pFKey; /* Used to iterate through FKs */
|
||||
@ -734,6 +800,13 @@ void sqlite3FkCheck(
|
||||
int i;
|
||||
int isIgnore = 0;
|
||||
|
||||
if( aChange
|
||||
&& sqlite3_stricmp(pTab->zName, pFKey->zTo)!=0
|
||||
&& fkChildIsModified(pTab, pFKey, aChange, bChngRowid)==0
|
||||
){
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Find the parent table of this foreign key. Also find a unique index
|
||||
** on the parent key columns in the parent table. If either of these
|
||||
** schema items cannot be located, set an error in pParse and return
|
||||
@ -816,6 +889,10 @@ void sqlite3FkCheck(
|
||||
SrcList *pSrc;
|
||||
int *aiCol = 0;
|
||||
|
||||
if( aChange && fkParentIsModified(pTab, pFKey, aChange, bChngRowid)==0 ){
|
||||
continue;
|
||||
}
|
||||
|
||||
if( !pFKey->isDeferred && !(db->flags & SQLITE_DeferFKs)
|
||||
&& !pParse->pToplevel && !pParse->isMultiWrite
|
||||
){
|
||||
@ -889,6 +966,7 @@ u32 sqlite3FkOldmask(
|
||||
return mask;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** This function is called before generating code to update or delete a
|
||||
** row contained in table pTab. If the operation is a DELETE, then
|
||||
@ -918,32 +996,16 @@ int sqlite3FkRequired(
|
||||
}else{
|
||||
/* This is an UPDATE. Foreign key processing is only required if the
|
||||
** operation modifies one or more child or parent key columns. */
|
||||
int i;
|
||||
FKey *p;
|
||||
|
||||
/* Check if any child key columns are being modified. */
|
||||
for(p=pTab->pFKey; p; p=p->pNextFrom){
|
||||
for(i=0; i<p->nCol; i++){
|
||||
int iChildKey = p->aCol[i].iFrom;
|
||||
if( aChange[iChildKey]>=0 ) return 1;
|
||||
if( iChildKey==pTab->iPKey && chngRowid ) return 1;
|
||||
}
|
||||
if( fkChildIsModified(pTab, p, aChange, chngRowid) ) return 1;
|
||||
}
|
||||
|
||||
/* Check if any parent key columns are being modified. */
|
||||
for(p=sqlite3FkReferences(pTab); p; p=p->pNextTo){
|
||||
for(i=0; i<p->nCol; i++){
|
||||
char *zKey = p->aCol[i].zCol;
|
||||
int iKey;
|
||||
for(iKey=0; iKey<pTab->nCol; iKey++){
|
||||
Column *pCol = &pTab->aCol[iKey];
|
||||
if( (zKey ? !sqlite3StrICmp(pCol->zName, zKey)
|
||||
: (pCol->colFlags & COLFLAG_PRIMKEY)!=0) ){
|
||||
if( aChange[iKey]>=0 ) return 1;
|
||||
if( iKey==pTab->iPKey && chngRowid ) return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if( fkParentIsModified(pTab, p, aChange, chngRowid) ) return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1169,7 +1231,9 @@ void sqlite3FkActions(
|
||||
Parse *pParse, /* Parse context */
|
||||
Table *pTab, /* Table being updated or deleted from */
|
||||
ExprList *pChanges, /* Change-list for UPDATE, NULL for DELETE */
|
||||
int regOld /* Address of array containing old row */
|
||||
int regOld, /* Address of array containing old row */
|
||||
int *aChange, /* Array indicating UPDATEd columns (or 0) */
|
||||
int bChngRowid /* True if rowid is UPDATEd */
|
||||
){
|
||||
/* If foreign-key support is enabled, iterate through all FKs that
|
||||
** refer to table pTab. If there is an action associated with the FK
|
||||
@ -1178,9 +1242,11 @@ void sqlite3FkActions(
|
||||
if( pParse->db->flags&SQLITE_ForeignKeys ){
|
||||
FKey *pFKey; /* Iterator variable */
|
||||
for(pFKey = sqlite3FkReferences(pTab); pFKey; pFKey=pFKey->pNextTo){
|
||||
Trigger *pAction = fkActionTrigger(pParse, pTab, pFKey, pChanges);
|
||||
if( pAction ){
|
||||
sqlite3CodeRowTriggerDirect(pParse, pAction, pTab, regOld, OE_Abort, 0);
|
||||
if( aChange==0 || fkParentIsModified(pTab, pFKey, aChange, bChngRowid) ){
|
||||
Trigger *pAct = fkActionTrigger(pParse, pTab, pFKey, pChanges);
|
||||
if( pAct ){
|
||||
sqlite3CodeRowTriggerDirect(pParse, pAct, pTab, regOld, OE_Abort, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1031,7 +1031,7 @@ void sqlite3Insert(
|
||||
sqlite3GenerateConstraintChecks(pParse, pTab, baseCur, regIns, aRegIdx,
|
||||
keyColumn>=0, 0, onError, endOfLoop, &isReplace
|
||||
);
|
||||
sqlite3FkCheck(pParse, pTab, 0, regIns);
|
||||
sqlite3FkCheck(pParse, pTab, 0, regIns, 0, 0);
|
||||
sqlite3CompleteInsertion(
|
||||
pParse, pTab, baseCur, regIns, aRegIdx, 0, appendFlag, isReplace==0
|
||||
);
|
||||
|
@ -3209,18 +3209,18 @@ const char *sqlite3JournalModename(int);
|
||||
** provided (enforcement of FK constraints requires the triggers sub-system).
|
||||
*/
|
||||
#if !defined(SQLITE_OMIT_FOREIGN_KEY) && !defined(SQLITE_OMIT_TRIGGER)
|
||||
void sqlite3FkCheck(Parse*, Table*, int, int);
|
||||
void sqlite3FkCheck(Parse*, Table*, int, int, int*, int);
|
||||
void sqlite3FkDropTable(Parse*, SrcList *, Table*);
|
||||
void sqlite3FkActions(Parse*, Table*, ExprList*, int);
|
||||
void sqlite3FkActions(Parse*, Table*, ExprList*, int, int*, int);
|
||||
int sqlite3FkRequired(Parse*, Table*, int*, int);
|
||||
u32 sqlite3FkOldmask(Parse*, Table*);
|
||||
FKey *sqlite3FkReferences(Table *);
|
||||
#else
|
||||
#define sqlite3FkActions(a,b,c,d)
|
||||
#define sqlite3FkActions(a,b,c,d,e,f)
|
||||
#define sqlite3FkCheck(a,b,c,d)
|
||||
#define sqlite3FkDropTable(a,b,c)
|
||||
#define sqlite3FkOldmask(a,b) 0
|
||||
#define sqlite3FkRequired(a,b,c,d) 0
|
||||
#define sqlite3FkOldmask(a,b) 0
|
||||
#define sqlite3FkRequired(a,b,c,d,e,f) 0
|
||||
#endif
|
||||
#ifndef SQLITE_OMIT_FOREIGN_KEY
|
||||
void sqlite3FkDelete(sqlite3 *, Table*);
|
||||
|
@ -488,7 +488,7 @@ void sqlite3Update(
|
||||
|
||||
/* Do FK constraint checks. */
|
||||
if( hasFK ){
|
||||
sqlite3FkCheck(pParse, pTab, regOldRowid, 0);
|
||||
sqlite3FkCheck(pParse, pTab, regOldRowid, 0, aXRef, chngRowid);
|
||||
}
|
||||
|
||||
/* Delete the index entries associated with the current record. */
|
||||
@ -502,7 +502,7 @@ void sqlite3Update(
|
||||
sqlite3VdbeJumpHere(v, j1);
|
||||
|
||||
if( hasFK ){
|
||||
sqlite3FkCheck(pParse, pTab, 0, regNewRowid);
|
||||
sqlite3FkCheck(pParse, pTab, 0, regNewRowid, aXRef, chngRowid);
|
||||
}
|
||||
|
||||
/* Insert the new index entries and the new record. */
|
||||
@ -512,7 +512,7 @@ void sqlite3Update(
|
||||
** handle rows (possibly in other tables) that refer via a foreign key
|
||||
** to the row just updated. */
|
||||
if( hasFK ){
|
||||
sqlite3FkActions(pParse, pTab, pChanges, regOldRowid);
|
||||
sqlite3FkActions(pParse, pTab, pChanges, regOldRowid, aXRef, chngRowid);
|
||||
}
|
||||
}
|
||||
|
||||
|
54
test/fkey7.test
Normal file
54
test/fkey7.test
Normal file
@ -0,0 +1,54 @@
|
||||
# 2001 September 15
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
# This file implements tests for foreign keys.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix fkey7
|
||||
|
||||
ifcapable {!foreignkey} {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
do_execsql_test 1.1 {
|
||||
PRAGMA foreign_keys = 1;
|
||||
|
||||
CREATE TABLE s1(a PRIMARY KEY, b);
|
||||
CREATE TABLE par(a, b REFERENCES s1, c UNIQUE, PRIMARY KEY(a));
|
||||
|
||||
CREATE TABLE c1(a, b REFERENCES par);
|
||||
CREATE TABLE c2(a, b REFERENCES par);
|
||||
CREATE TABLE c3(a, b REFERENCES par(c));
|
||||
}
|
||||
|
||||
proc auth {op tbl args} {
|
||||
if {$op == "SQLITE_READ"} { set ::tbls($tbl) 1 }
|
||||
return "SQLITE_OK"
|
||||
}
|
||||
db auth auth
|
||||
db cache size 0
|
||||
proc do_tblsread_test {tn sql tbllist} {
|
||||
array unset ::tbls
|
||||
uplevel [list execsql $sql]
|
||||
uplevel [list do_test $tn {lsort [array names ::tbls]} $tbllist]
|
||||
}
|
||||
|
||||
do_tblsread_test 1.2 { UPDATE par SET b=? WHERE a=? } {par s1}
|
||||
do_tblsread_test 1.3 { UPDATE par SET a=? WHERE b=? } {c1 c2 par}
|
||||
do_tblsread_test 1.4 { UPDATE par SET c=? WHERE b=? } {c3 par}
|
||||
do_tblsread_test 1.5 { UPDATE par SET a=?,b=?,c=? WHERE b=? } {c1 c2 c3 par s1}
|
||||
|
||||
|
||||
finish_test
|
@ -509,7 +509,7 @@ test_suite "utf16" -description {
|
||||
} -files {
|
||||
alter.test alter3.test
|
||||
analyze.test analyze3.test analyze4.test analyze5.test analyze6.test
|
||||
analyze7.test analyze8.test analyze9.test analyzeA.test
|
||||
analyze7.test analyze8.test analyze9.test analyzeA.test analyzeB.test
|
||||
auth.test bind.test blob.test capi2.test capi3.test collate1.test
|
||||
collate2.test collate3.test collate4.test collate5.test collate6.test
|
||||
conflict.test date.test delete.test expr.test fkey1.test func.test
|
||||
|
Loading…
x
Reference in New Issue
Block a user