mirror of https://github.com/sqlite/sqlite
Extend RENAME TABLE to edit triggers and views. Still buggy.
FossilOrigin-Name: 01308bae3acf33f78b5bb90892085eab340df093aafc17e6ccf6a7d6cf324897
This commit is contained in:
parent
3a843f525c
commit
c9461ecc28
36
manifest
36
manifest
|
@ -1,5 +1,5 @@
|
|||
C Remove\san\sunnecessary\sIN_RENAME_COLUMN\smacro\sfrom\sresolve.c.
|
||||
D 2018-08-25T03:29:34.617
|
||||
C Extend\sRENAME\sTABLE\sto\sedit\striggers\sand\sviews.\sStill\sbuggy.
|
||||
D 2018-08-29T21:00:16.110
|
||||
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
|
||||
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
|
||||
F Makefile.in 2729786d5d188974913f07ea63cc84cd42cb9cac5f4aac823c40105e68e22f63
|
||||
|
@ -432,7 +432,7 @@ F spec.template 86a4a43b99ebb3e75e6b9a735d5fd293a24e90ca
|
|||
F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b
|
||||
F sqlite3.1 fc7ad8990fc8409983309bb80de8c811a7506786
|
||||
F sqlite3.pc.in 48fed132e7cb71ab676105d2a4dc77127d8c1f3a
|
||||
F src/alter.c 64dcb3cb53c93d49347ee2366f4583308be1e5022a83825c4554d7caeb9e5559
|
||||
F src/alter.c 78188ded77cf1d0a9abb273e7139e218ab24a4bcfdfa6ee07300430e539a12b6
|
||||
F src/analyze.c 3dc6b98cf007b005af89df165c966baaa48e8124f38c87b4d2b276fe7f0b9eb9
|
||||
F src/attach.c 4bd5b92633671d3e8ce431153ebb1893b50335818423b5373f3f27969f79769a
|
||||
F src/auth.c 32a5bbe3b755169ab6c66311c5225a3cd4f75a46c041f7fb117e0cbb68055114
|
||||
|
@ -442,7 +442,7 @@ F src/btmutex.c 8acc2f464ee76324bf13310df5692a262b801808984c1b79defb2503bbafadb6
|
|||
F src/btree.c 3f5e1a03db871e627bf5da21092bf7434ecfc5c5980bbd7d45eba13341340173
|
||||
F src/btree.h febb2e817be499570b7a2e32a9bbb4b607a9234f6b84bb9ae84916d4806e96f2
|
||||
F src/btreeInt.h 620ab4c7235f43572cf3ac2ac8723cbdf68073be4d29da24897c7b77dda5fd96
|
||||
F src/build.c b49367a942fb67b74b20927ef6a30a335d29a466c441a7f309fdf35cf0967ecc
|
||||
F src/build.c 79c5f75243665d9287e7a58b1a1de898d6f2baa094feeaa0741e40e174fea04d
|
||||
F src/callback.c 36caff1e7eb7deb58572d59c41cee8f064a11d00297616995c5050ea0cfc1288
|
||||
F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e
|
||||
F src/ctime.c b157b01081f92442f8b0218ddb93ddce8ebddad36dbddeecfdd771561dd4f387
|
||||
|
@ -450,7 +450,7 @@ F src/date.c ebe1dc7c8a347117bb02570f1a931c62dd78f4a2b1b516f4837d45b7d6426957
|
|||
F src/dbpage.c 4aa7f26198934dbd002e69418220eae3dbc71b010bbac32bd78faf86b52ce6c3
|
||||
F src/dbstat.c edabb82611143727511a45ca0859b8cd037851ebe756ae3db289859dd18b6f91
|
||||
F src/delete.c 107e28d3ef8bd72fd11953374ca9107cd74e8b09c3ded076a6048742d26ce7d2
|
||||
F src/expr.c 665eafd45010fe6aeb8952c07e44d913ac25aec72e9a0c8784c84aeb3cc14efe
|
||||
F src/expr.c e7d04a23bc4f42f0ad32b4d5c52a3fff13e113d68192c10d7c6c7ebe47a5f3f1
|
||||
F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007
|
||||
F src/fkey.c f59253c0be4b1e9dfcb073b6d6d6ab83090ae50c08b5c113b76013c4b157cd6a
|
||||
F src/func.c 7c288b4ce309b5a8b8473514b88e1f8e69a80134509a8c0db8e39c858e367e7f
|
||||
|
@ -487,23 +487,23 @@ F src/os_win.c 070cdbb400097c6cda54aa005356095afdc2f3ee691d17192c54724ef146a971
|
|||
F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a
|
||||
F src/pager.c 705de01dff9c3df9739c37a6d3b58cd2b1734fdabcef829b16cdc7721a9eeaa4
|
||||
F src/pager.h ecc554a55bc55d1c4ba5e17137b72e238e00bd81e72ff2662d8b9c8c10ae3963
|
||||
F src/parse.y adc9a1fb8dab56e53ad81876796cdbea221bb05919d1ed432a6538d5ef231caa
|
||||
F src/parse.y b3ca0ebaba6abe775e22c380fecfea17776ea150a6fb8e419a2a2274e478bc01
|
||||
F src/pcache.c 135ef0bc6fb2e3b7178d49ab5c9176254c8a691832c1bceb1156b2fbdd0869bd
|
||||
F src/pcache.h 072f94d29281cffd99e46c1539849f248c4b56ae7684c1f36626797fee375170
|
||||
F src/pcache1.c 716975564c15eb6679e97f734cec1bfd6c16ac3d4010f05f1f8e509fc7d19880
|
||||
F src/pragma.c 873b767f233932e97cbffd094aa61928be90aca03f946a94bb29ce5695e4885b
|
||||
F src/pragma.h bb83728944b42f6d409c77f5838a8edbdb0fe83046c5496ffc9602b40340a324
|
||||
F src/prepare.c 117e27c6826a83f461986c0cfbb09c31fe004922ce23a61bf78d82a46b0958d9
|
||||
F src/prepare.c f8e260d940a0e08494c0f30744521b2f832d7263eca9d02b050cea0ba144b097
|
||||
F src/printf.c 7f6f3cba8e0c49c19e30a1ff4e9aeda6e06814dcbad4b664a69e1b6cb6e7e365
|
||||
F src/random.c 80f5d666f23feb3e6665a6ce04c7197212a88384
|
||||
F src/resolve.c 11c177efc9c038629cb39b7a4802931d0befe69fc478f7ce72110b21cdd43a4d
|
||||
F src/resolve.c 352c6af1a99441206ff62a6f7429dbf537827f42c428639695220b9c8639e33b
|
||||
F src/rowset.c 7b7e7e479212e65b723bf40128c7b36dc5afdfac
|
||||
F src/select.c ae7396a314cc1bb1d767947cd57094e3a9ffcbb155ebc1b1c391e028c44a9a04
|
||||
F src/shell.c.in 6e0aad854be738a5d0368940459399be211e9ac43aebe92bb9ed46cfe38d0e1f
|
||||
F src/sqlite.h.in 82b5768e36ce796ecf93c73bd88bad99def831ce7d470138e213ac693bf4ceab
|
||||
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
|
||||
F src/sqlite3ext.h 9887b27e69c01e79c2cbe74ef73bf01af5b5703d6a7f0a4371e386d7249cb1c7
|
||||
F src/sqliteInt.h 679196cdd2f753ff21144198aeafa3e69d69acd3c226855584ef9b3551d85471
|
||||
F src/sqliteInt.h 83b8d196810c41eb0818793f07f9393b0a5b9173a16588ac9436245b279d3a11
|
||||
F src/sqliteLimit.h 1513bfb7b20378aa0041e7022d04acb73525de35b80b252f1b83fedb4de6a76b
|
||||
F src/status.c 46e7aec11f79dad50965a5ca5fa9de009f7d6bde08be2156f1538a0a296d4d0e
|
||||
F src/table.c b46ad567748f24a326d9de40e5b9659f96ffff34
|
||||
|
@ -561,9 +561,9 @@ F src/test_windirent.h 90dfbe95442c9762357fe128dc7ae3dc199d006de93eb33ba3972e0a9
|
|||
F src/test_window.c cdae419fdcea5bad6dcd9368c685abdad6deb59e9fc8b84b153de513d394ba3f
|
||||
F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
|
||||
F src/threads.c 4ae07fa022a3dc7c5beb373cf744a85d3c5c6c3c
|
||||
F src/tokenize.c 6449902efa1a880788ede649dfc65fca0d5685d39408a9a9b3a750a3e7a8b6c7
|
||||
F src/tokenize.c 9f55961518f77793edd56eee860ecf035d4370ebbb0726ad2f6cada6637fd16b
|
||||
F src/treeview.c e7a7f90552bb418533cdd0309b5eb71d4effa50165b880fc8c2001e613577e5f
|
||||
F src/trigger.c f6760d78be400164c95732b4aabccab27072ec66e9db2ee33baf70e65e467af0
|
||||
F src/trigger.c d3d78568f37fb2e6cdcc2d1e7b60156f15b0b600adec55b83c5d42f6cad250bd
|
||||
F src/update.c 345ce35eb1332eb4829857aa8b1f65a614b07dae91d0346c0dc2baacafbcc51b
|
||||
F src/upsert.c 47edd408cc73f8d3c00a140550d1ad180b407c146285947969dd09874802bf88
|
||||
F src/utf.c 810fbfebe12359f10bc2a011520a6e10879ab2a163bcb26c74768eab82ea62a5
|
||||
|
@ -595,13 +595,14 @@ F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
|
|||
F test/aggnested.test 18b00de006597e960a6b27ccec51474ac66cf1070a87c1933e5694dc02190ef1
|
||||
F test/alias.test 4529fbc152f190268a15f9384a5651bbbabc9d87
|
||||
F test/all.test ae17c4412639e16bd797c7617864a16c2badc0035c808ae8246f145e38f8e2f9
|
||||
F test/alter.test b820ab9dcf85f8e3a65bc8326accb2f0c7be64ef
|
||||
F test/alter.test 8388947daa6caee09f624242d5907dbb0c0da6704cb40c44a6a1c1b4119142ed
|
||||
F test/alter2.test 7ea05c7d92ac99349a802ef7ada17294dd647060
|
||||
F test/alter3.test 4d79934d812eaeacc6f22781a080f8cfe012fdc3
|
||||
F test/alter4.test b6d7b86860111864f6cddb54af313f5862dda23b
|
||||
F test/altercol.test 919898b3eef41d5d582af22d3c67c8b5a211ba30ac06821933421c24e3449e7d
|
||||
F test/altermalloc.test e81ac9657ed25c6c5bb09bebfa5a047cd8e4acfc
|
||||
F test/altermalloc2.test 0231398534c494401a70a1d06a63d7849cb5b317fcc14228cbdb53039eba7eae
|
||||
F test/altertab.test 6b2ebbe3a8fc3222ba773c7f884900367ef528cd90be361468fb15eb97dfcc51
|
||||
F test/amatch1.test b5ae7065f042b7f4c1c922933f4700add50cdb9f
|
||||
F test/analyze.test b3a9c67d00e1df7588a5b7be9a0292899f94fe8cac1f94a017277474ca2e59df
|
||||
F test/analyze3.test ff62d9029e6deb2c914490c6b00caf7fae47cc85cdc046e4a0d0a4d4b87c71d8
|
||||
|
@ -1758,7 +1759,10 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
|
|||
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
|
||||
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
|
||||
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
|
||||
P f0eed21d4e2e1f2c8e680a510a283ac21156c9766efec5b80a362a040424bce7
|
||||
R 765df4694289b6c53a17c97f9702ba6c
|
||||
U drh
|
||||
Z db8eccde43135de3a0e592fd50ea76f1
|
||||
P 5858c0bde7c210f07cfa72cee95325847b26733abab4f56301b57cd816931e97
|
||||
R 2ca3dc5affa16de92546f4b6ec0792d4
|
||||
T *branch * alter-table-rename-table
|
||||
T *sym-alter-table-rename-table *
|
||||
T -sym-alter-table-rename-column *
|
||||
U dan
|
||||
Z 93949a0c450c29bbb5ee4cb93008ceac
|
||||
|
|
|
@ -1 +1 @@
|
|||
5858c0bde7c210f07cfa72cee95325847b26733abab4f56301b57cd816931e97
|
||||
01308bae3acf33f78b5bb90892085eab340df093aafc17e6ccf6a7d6cf324897
|
862
src/alter.c
862
src/alter.c
|
@ -20,211 +20,6 @@
|
|||
*/
|
||||
#ifndef SQLITE_OMIT_ALTERTABLE
|
||||
|
||||
|
||||
/*
|
||||
** This function is used by SQL generated to implement the
|
||||
** ALTER TABLE command. The first argument is the text of a CREATE TABLE or
|
||||
** CREATE INDEX command. The second is a table name. The table name in
|
||||
** the CREATE TABLE or CREATE INDEX statement is replaced with the third
|
||||
** argument and the result returned. Examples:
|
||||
**
|
||||
** sqlite_rename_table('CREATE TABLE abc(a, b, c)', 'def')
|
||||
** -> 'CREATE TABLE def(a, b, c)'
|
||||
**
|
||||
** sqlite_rename_table('CREATE INDEX i ON abc(a)', 'def')
|
||||
** -> 'CREATE INDEX i ON def(a, b, c)'
|
||||
*/
|
||||
static void renameTableFunc(
|
||||
sqlite3_context *context,
|
||||
int NotUsed,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
unsigned char const *zSql = sqlite3_value_text(argv[0]);
|
||||
unsigned char const *zTableName = sqlite3_value_text(argv[1]);
|
||||
|
||||
int token;
|
||||
Token tname;
|
||||
unsigned char const *zCsr = zSql;
|
||||
int len = 0;
|
||||
char *zRet;
|
||||
|
||||
sqlite3 *db = sqlite3_context_db_handle(context);
|
||||
|
||||
UNUSED_PARAMETER(NotUsed);
|
||||
|
||||
/* The principle used to locate the table name in the CREATE TABLE
|
||||
** statement is that the table name is the first non-space token that
|
||||
** is immediately followed by a TK_LP or TK_USING token.
|
||||
*/
|
||||
if( zSql ){
|
||||
do {
|
||||
if( !*zCsr ){
|
||||
/* Ran out of input before finding an opening bracket. Return NULL. */
|
||||
return;
|
||||
}
|
||||
|
||||
/* Store the token that zCsr points to in tname. */
|
||||
tname.z = (char*)zCsr;
|
||||
tname.n = len;
|
||||
|
||||
/* Advance zCsr to the next token. Store that token type in 'token',
|
||||
** and its length in 'len' (to be used next iteration of this loop).
|
||||
*/
|
||||
do {
|
||||
zCsr += len;
|
||||
len = sqlite3GetToken(zCsr, &token);
|
||||
} while( token==TK_SPACE );
|
||||
assert( len>0 || !*zCsr );
|
||||
} while( token!=TK_LP && token!=TK_USING );
|
||||
|
||||
zRet = sqlite3MPrintf(db, "%.*s\"%w\"%s", (int)(((u8*)tname.z) - zSql),
|
||||
zSql, zTableName, tname.z+tname.n);
|
||||
sqlite3_result_text(context, zRet, -1, SQLITE_DYNAMIC);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** This C function implements an SQL user function that is used by SQL code
|
||||
** generated by the ALTER TABLE ... RENAME command to modify the definition
|
||||
** of any foreign key constraints that use the table being renamed as the
|
||||
** parent table. It is passed three arguments:
|
||||
**
|
||||
** 1) The complete text of the CREATE TABLE statement being modified,
|
||||
** 2) The old name of the table being renamed, and
|
||||
** 3) The new name of the table being renamed.
|
||||
**
|
||||
** It returns the new CREATE TABLE statement. For example:
|
||||
**
|
||||
** sqlite_rename_parent('CREATE TABLE t1(a REFERENCES t2)', 't2', 't3')
|
||||
** -> 'CREATE TABLE t1(a REFERENCES t3)'
|
||||
*/
|
||||
#ifndef SQLITE_OMIT_FOREIGN_KEY
|
||||
static void renameParentFunc(
|
||||
sqlite3_context *context,
|
||||
int NotUsed,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
sqlite3 *db = sqlite3_context_db_handle(context);
|
||||
char *zOutput = 0;
|
||||
char *zResult;
|
||||
unsigned char const *zInput = sqlite3_value_text(argv[0]);
|
||||
unsigned char const *zOld = sqlite3_value_text(argv[1]);
|
||||
unsigned char const *zNew = sqlite3_value_text(argv[2]);
|
||||
|
||||
unsigned const char *z; /* Pointer to token */
|
||||
int n; /* Length of token z */
|
||||
int token; /* Type of token */
|
||||
|
||||
UNUSED_PARAMETER(NotUsed);
|
||||
if( zInput==0 || zOld==0 ) return;
|
||||
for(z=zInput; *z; z=z+n){
|
||||
n = sqlite3GetToken(z, &token);
|
||||
if( token==TK_REFERENCES ){
|
||||
char *zParent;
|
||||
do {
|
||||
z += n;
|
||||
n = sqlite3GetToken(z, &token);
|
||||
}while( token==TK_SPACE );
|
||||
|
||||
if( token==TK_ILLEGAL ) break;
|
||||
zParent = sqlite3DbStrNDup(db, (const char *)z, n);
|
||||
if( zParent==0 ) break;
|
||||
sqlite3Dequote(zParent);
|
||||
if( 0==sqlite3StrICmp((const char *)zOld, zParent) ){
|
||||
char *zOut = sqlite3MPrintf(db, "%s%.*s\"%w\"",
|
||||
(zOutput?zOutput:""), (int)(z-zInput), zInput, (const char *)zNew
|
||||
);
|
||||
sqlite3DbFree(db, zOutput);
|
||||
zOutput = zOut;
|
||||
zInput = &z[n];
|
||||
}
|
||||
sqlite3DbFree(db, zParent);
|
||||
}
|
||||
}
|
||||
|
||||
zResult = sqlite3MPrintf(db, "%s%s", (zOutput?zOutput:""), zInput);
|
||||
sqlite3_result_text(context, zResult, -1, SQLITE_DYNAMIC);
|
||||
sqlite3DbFree(db, zOutput);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef SQLITE_OMIT_TRIGGER
|
||||
/* This function is used by SQL generated to implement the
|
||||
** ALTER TABLE command. The first argument is the text of a CREATE TRIGGER
|
||||
** statement. The second is a table name. The table name in the CREATE
|
||||
** TRIGGER statement is replaced with the third argument and the result
|
||||
** returned. This is analagous to renameTableFunc() above, except for CREATE
|
||||
** TRIGGER, not CREATE INDEX and CREATE TABLE.
|
||||
*/
|
||||
static void renameTriggerFunc(
|
||||
sqlite3_context *context,
|
||||
int NotUsed,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
unsigned char const *zSql = sqlite3_value_text(argv[0]);
|
||||
unsigned char const *zTableName = sqlite3_value_text(argv[1]);
|
||||
|
||||
int token;
|
||||
Token tname;
|
||||
int dist = 3;
|
||||
unsigned char const *zCsr = zSql;
|
||||
int len = 0;
|
||||
char *zRet;
|
||||
sqlite3 *db = sqlite3_context_db_handle(context);
|
||||
|
||||
UNUSED_PARAMETER(NotUsed);
|
||||
|
||||
/* The principle used to locate the table name in the CREATE TRIGGER
|
||||
** statement is that the table name is the first token that is immediately
|
||||
** preceded by either TK_ON or TK_DOT and immediately followed by one
|
||||
** of TK_WHEN, TK_BEGIN or TK_FOR.
|
||||
*/
|
||||
if( zSql ){
|
||||
do {
|
||||
|
||||
if( !*zCsr ){
|
||||
/* Ran out of input before finding the table name. Return NULL. */
|
||||
return;
|
||||
}
|
||||
|
||||
/* Store the token that zCsr points to in tname. */
|
||||
tname.z = (char*)zCsr;
|
||||
tname.n = len;
|
||||
|
||||
/* Advance zCsr to the next token. Store that token type in 'token',
|
||||
** and its length in 'len' (to be used next iteration of this loop).
|
||||
*/
|
||||
do {
|
||||
zCsr += len;
|
||||
len = sqlite3GetToken(zCsr, &token);
|
||||
}while( token==TK_SPACE );
|
||||
assert( len>0 || !*zCsr );
|
||||
|
||||
/* Variable 'dist' stores the number of tokens read since the most
|
||||
** recent TK_DOT or TK_ON. This means that when a WHEN, FOR or BEGIN
|
||||
** token is read and 'dist' equals 2, the condition stated above
|
||||
** to be met.
|
||||
**
|
||||
** Note that ON cannot be a database, table or column name, so
|
||||
** there is no need to worry about syntax like
|
||||
** "CREATE TRIGGER ... ON ON.ON BEGIN ..." etc.
|
||||
*/
|
||||
dist++;
|
||||
if( token==TK_DOT || token==TK_ON ){
|
||||
dist = 0;
|
||||
}
|
||||
} while( dist!=2 || (token!=TK_WHEN && token!=TK_FOR && token!=TK_BEGIN) );
|
||||
|
||||
/* Variable tname now contains the token that is the old table-name
|
||||
** in the CREATE TRIGGER statement.
|
||||
*/
|
||||
zRet = sqlite3MPrintf(db, "%.*s\"%w\"%s", (int)(((u8*)tname.z) - zSql),
|
||||
zSql, zTableName, tname.z+tname.n);
|
||||
sqlite3_result_text(context, zRet, -1, SQLITE_DYNAMIC);
|
||||
}
|
||||
}
|
||||
#endif /* !SQLITE_OMIT_TRIGGER */
|
||||
|
||||
/*
|
||||
** This function is used to create the text of expressions of the form:
|
||||
**
|
||||
|
@ -383,9 +178,6 @@ void sqlite3AlterRenameTable(
|
|||
int nTabName; /* Number of UTF-8 characters in zTabName */
|
||||
const char *zTabName; /* Original name of the table */
|
||||
Vdbe *v;
|
||||
#ifndef SQLITE_OMIT_TRIGGER
|
||||
char *zWhere = 0; /* Where clause to locate temp triggers */
|
||||
#endif
|
||||
VTable *pVTab = 0; /* Non-zero if this is a v-tab with an xRename() */
|
||||
u32 savedDbFlags; /* Saved value of db->mDbFlags */
|
||||
|
||||
|
@ -479,31 +271,20 @@ void sqlite3AlterRenameTable(
|
|||
zTabName = pTab->zName;
|
||||
nTabName = sqlite3Utf8CharLen(zTabName, -1);
|
||||
|
||||
#if !defined(SQLITE_OMIT_FOREIGN_KEY) && !defined(SQLITE_OMIT_TRIGGER)
|
||||
if( db->flags&SQLITE_ForeignKeys ){
|
||||
/* If foreign-key support is enabled, rewrite the CREATE TABLE
|
||||
** statements corresponding to all child tables of foreign key constraints
|
||||
** for which the renamed table is the parent table. */
|
||||
if( (zWhere=whereForeignKeys(pParse, pTab))!=0 ){
|
||||
/* Rewrite all CREATE TABLE, INDEX, TRIGGER or VIEW statements in
|
||||
** the schema to use the new table name. */
|
||||
sqlite3NestedParse(pParse,
|
||||
"UPDATE \"%w\".%s SET "
|
||||
"sql = sqlite_rename_parent(sql, %Q, %Q) "
|
||||
"WHERE %s;", zDb, MASTER_NAME, zTabName, zName, zWhere);
|
||||
sqlite3DbFree(db, zWhere);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
"sql = sqlite_rename_table(%Q, sql, %Q, %Q, 0) "
|
||||
"WHERE (type!='index' OR tbl_name=%Q COLLATE nocase)"
|
||||
"AND name NOT LIKE 'sqlite_%%'"
|
||||
, zDb, MASTER_NAME, zDb, zTabName, zName, zTabName
|
||||
);
|
||||
|
||||
/* Modify the sqlite_master table to use the new table name. */
|
||||
/* Update the tbl_name and name columns of the sqlite_master table
|
||||
** as required. */
|
||||
sqlite3NestedParse(pParse,
|
||||
"UPDATE %Q.%s SET "
|
||||
#ifdef SQLITE_OMIT_TRIGGER
|
||||
"sql = sqlite_rename_table(sql, %Q), "
|
||||
#else
|
||||
"sql = CASE "
|
||||
"WHEN type = 'trigger' THEN sqlite_rename_trigger(sql, %Q)"
|
||||
"ELSE sqlite_rename_table(sql, %Q) END, "
|
||||
#endif
|
||||
"tbl_name = %Q, "
|
||||
"name = CASE "
|
||||
"WHEN type='table' THEN %Q "
|
||||
|
@ -512,11 +293,9 @@ void sqlite3AlterRenameTable(
|
|||
"ELSE name END "
|
||||
"WHERE tbl_name=%Q COLLATE nocase AND "
|
||||
"(type='table' OR type='index' OR type='trigger');",
|
||||
zDb, MASTER_NAME, zName, zName, zName,
|
||||
#ifndef SQLITE_OMIT_TRIGGER
|
||||
zName,
|
||||
#endif
|
||||
zName, nTabName, zTabName
|
||||
zDb, MASTER_NAME,
|
||||
zName, zName, zName,
|
||||
nTabName, zTabName
|
||||
);
|
||||
|
||||
#ifndef SQLITE_OMIT_AUTOINCREMENT
|
||||
|
@ -530,35 +309,21 @@ void sqlite3AlterRenameTable(
|
|||
}
|
||||
#endif
|
||||
|
||||
#ifndef SQLITE_OMIT_TRIGGER
|
||||
/* If there are TEMP triggers on this table, modify the sqlite_temp_master
|
||||
** table. Don't do this if the table being ALTERed is itself located in
|
||||
** the temp database.
|
||||
*/
|
||||
if( (zWhere=whereTempTriggers(pParse, pTab))!=0 ){
|
||||
/* If the table being renamed is not itself part of the temp database,
|
||||
** edit view and trigger definitions within the temp database
|
||||
** as required. */
|
||||
if( iDb!=1 ){
|
||||
sqlite3NestedParse(pParse,
|
||||
"UPDATE sqlite_temp_master SET "
|
||||
"sql = sqlite_rename_trigger(sql, %Q), "
|
||||
"tbl_name = %Q "
|
||||
"WHERE %s;", zName, zName, zWhere);
|
||||
sqlite3DbFree(db, zWhere);
|
||||
"sql = sqlite_rename_table(%Q, sql, %Q, %Q, 1), "
|
||||
"tbl_name = "
|
||||
"CASE WHEN tbl_name=%Q COLLATE nocase THEN %Q ELSE tbl_name END "
|
||||
"WHERE type IN ('view', 'trigger')"
|
||||
, zDb, zTabName, zName, zTabName, zTabName, zName);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !defined(SQLITE_OMIT_FOREIGN_KEY) && !defined(SQLITE_OMIT_TRIGGER)
|
||||
if( db->flags&SQLITE_ForeignKeys ){
|
||||
FKey *p;
|
||||
for(p=sqlite3FkReferences(pTab); p; p=p->pNextTo){
|
||||
Table *pFrom = p->pFrom;
|
||||
if( pFrom!=pTab ){
|
||||
reloadTableSchema(pParse, p->pFrom, pFrom->zName);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Drop and reload the internal table schema. */
|
||||
reloadTableSchema(pParse, pTab, zName);
|
||||
sqlite3VdbeAddParseSchemaOp(pParse->pVdbe, iDb, 0);
|
||||
if( iDb!=1 ) sqlite3VdbeAddParseSchemaOp(pParse->pVdbe, 1, 0);
|
||||
|
||||
exit_rename_table:
|
||||
sqlite3SrcListDelete(db, pSrc);
|
||||
|
@ -946,6 +711,16 @@ struct RenameCtx {
|
|||
const char *zOld; /* Old column name */
|
||||
};
|
||||
|
||||
void renameTokenClear(Parse *pParse, void *pPtr){
|
||||
RenameToken *p;
|
||||
assert( pPtr );
|
||||
for(p=pParse->pRename; p; p=p->pNext){
|
||||
if( p->p==pPtr ){
|
||||
p->p = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Add a new RenameToken object mapping parse tree element pPtr into
|
||||
** token *pToken to the Parse object currently under construction.
|
||||
|
@ -954,6 +729,9 @@ struct RenameCtx {
|
|||
*/
|
||||
void *sqlite3RenameTokenMap(Parse *pParse, void *pPtr, Token *pToken){
|
||||
RenameToken *pNew;
|
||||
assert( pPtr || pParse->db->mallocFailed );
|
||||
|
||||
renameTokenClear(pParse, pPtr);
|
||||
pNew = sqlite3DbMallocZero(pParse->db, sizeof(RenameToken));
|
||||
if( pNew ){
|
||||
pNew->p = pPtr;
|
||||
|
@ -961,6 +739,7 @@ void *sqlite3RenameTokenMap(Parse *pParse, void *pPtr, Token *pToken){
|
|||
pNew->pNext = pParse->pRename;
|
||||
pParse->pRename = pNew;
|
||||
}
|
||||
|
||||
return pPtr;
|
||||
}
|
||||
|
||||
|
@ -971,13 +750,13 @@ void *sqlite3RenameTokenMap(Parse *pParse, void *pPtr, Token *pToken){
|
|||
*/
|
||||
void sqlite3RenameTokenRemap(Parse *pParse, void *pTo, void *pFrom){
|
||||
RenameToken *p;
|
||||
for(p=pParse->pRename; ALWAYS(p); p=p->pNext){
|
||||
if( pTo ) renameTokenClear(pParse, pTo);
|
||||
for(p=pParse->pRename; p; p=p->pNext){
|
||||
if( p->p==pFrom ){
|
||||
p->p = pTo;
|
||||
break;
|
||||
}
|
||||
}
|
||||
assert( pTo==0 || p );
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1143,6 +922,221 @@ static void renameColumnIdlistNames(
|
|||
}
|
||||
}
|
||||
|
||||
static int renameParseSql(
|
||||
Parse *p,
|
||||
const char *zDb,
|
||||
int bTable,
|
||||
sqlite3 *db,
|
||||
const char *zSql,
|
||||
int bTemp
|
||||
){
|
||||
int rc;
|
||||
char *zErr = 0;
|
||||
|
||||
db->init.iDb = bTemp ? 1 : sqlite3FindDbName(db, zDb);
|
||||
|
||||
/* Parse the SQL statement passed as the first argument. If no error
|
||||
** occurs and the parse does not result in a new table, index or
|
||||
** trigger object, the database must be corrupt. */
|
||||
memset(p, 0, sizeof(Parse));
|
||||
p->eParseMode = (bTable ? PARSE_MODE_RENAME_TABLE : PARSE_MODE_RENAME_COLUMN);
|
||||
p->db = db;
|
||||
p->nQueryLoop = 1;
|
||||
rc = sqlite3RunParser(p, zSql, &zErr);
|
||||
assert( p->zErrMsg==0 );
|
||||
assert( rc!=SQLITE_OK || zErr==0 );
|
||||
assert( (0!=p->pNewTable) + (0!=p->pNewIndex) + (0!=p->pNewTrigger)<2 );
|
||||
p->zErrMsg = zErr;
|
||||
if( db->mallocFailed ) rc = SQLITE_NOMEM;
|
||||
if( rc==SQLITE_OK
|
||||
&& p->pNewTable==0 && p->pNewIndex==0 && p->pNewTrigger==0
|
||||
){
|
||||
rc = SQLITE_CORRUPT_BKPT;
|
||||
}
|
||||
|
||||
#ifdef SQLITE_DEBUG
|
||||
/* Ensure that all mappings in the Parse.pRename list really do map to
|
||||
** a part of the input string. */
|
||||
if( rc==SQLITE_OK ){
|
||||
int nSql = sqlite3Strlen30(zSql);
|
||||
RenameToken *pToken;
|
||||
for(pToken=p->pRename; pToken; pToken=pToken->pNext){
|
||||
assert( pToken->t.z>=zSql && &pToken->t.z[pToken->t.n]<=&zSql[nSql] );
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
db->init.iDb = 0;
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int renameEditSql(
|
||||
sqlite3_context *pCtx, /* Return result here */
|
||||
RenameCtx *pRename, /* Rename context */
|
||||
const char *zSql, /* SQL statement to edit */
|
||||
const char *zNew, /* New token text */
|
||||
int bQuote /* True to always quote token */
|
||||
){
|
||||
int nNew = sqlite3Strlen30(zNew);
|
||||
int nSql = sqlite3Strlen30(zSql);
|
||||
sqlite3 *db = sqlite3_context_db_handle(pCtx);
|
||||
int rc = SQLITE_OK;
|
||||
char *zQuot;
|
||||
char *zOut;
|
||||
int nQuot;
|
||||
|
||||
/* Set zQuot to point to a buffer containing a quoted copy of the
|
||||
** identifier zNew. If the corresponding identifier in the original
|
||||
** ALTER TABLE statement was quoted (bQuote==1), then set zNew to
|
||||
** point to zQuot so that all substitutions are made using the
|
||||
** quoted version of the new column name. */
|
||||
zQuot = sqlite3_mprintf("\"%w\"", zNew);
|
||||
if( zQuot==0 ){
|
||||
return SQLITE_NOMEM;
|
||||
}else{
|
||||
nQuot = sqlite3Strlen30(zQuot);
|
||||
}
|
||||
if( bQuote ){
|
||||
zNew = zQuot;
|
||||
nNew = nQuot;
|
||||
}
|
||||
|
||||
/* At this point pRename->pList contains a list of RenameToken objects
|
||||
** corresponding to all tokens in the input SQL that must be replaced
|
||||
** with the new column name. All that remains is to construct and
|
||||
** return the edited SQL string. */
|
||||
assert( nQuot>=nNew );
|
||||
zOut = sqlite3DbMallocZero(db, nSql + pRename->nList*nQuot + 1);
|
||||
if( zOut ){
|
||||
int nOut = nSql;
|
||||
memcpy(zOut, zSql, nSql);
|
||||
while( pRename->pList ){
|
||||
int iOff; /* Offset of token to replace in zOut */
|
||||
RenameToken *pBest = renameColumnTokenNext(pRename);
|
||||
|
||||
u32 nReplace;
|
||||
const char *zReplace;
|
||||
if( sqlite3IsIdChar(*pBest->t.z) ){
|
||||
nReplace = nNew;
|
||||
zReplace = zNew;
|
||||
}else{
|
||||
nReplace = nQuot;
|
||||
zReplace = zQuot;
|
||||
}
|
||||
|
||||
iOff = pBest->t.z - zSql;
|
||||
if( pBest->t.n!=nReplace ){
|
||||
memmove(&zOut[iOff + nReplace], &zOut[iOff + pBest->t.n],
|
||||
nOut - (iOff + pBest->t.n)
|
||||
);
|
||||
nOut += nReplace - pBest->t.n;
|
||||
zOut[nOut] = '\0';
|
||||
}
|
||||
memcpy(&zOut[iOff], zReplace, nReplace);
|
||||
sqlite3DbFree(db, pBest);
|
||||
}
|
||||
|
||||
sqlite3_result_text(pCtx, zOut, -1, SQLITE_TRANSIENT);
|
||||
sqlite3DbFree(db, zOut);
|
||||
}else{
|
||||
rc = SQLITE_NOMEM;
|
||||
}
|
||||
|
||||
sqlite3_free(zQuot);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int renameResolveTrigger(
|
||||
Parse *pParse,
|
||||
const char *zDb
|
||||
){
|
||||
sqlite3 *db = pParse->db;
|
||||
TriggerStep *pStep;
|
||||
NameContext sNC;
|
||||
int rc = SQLITE_OK;
|
||||
|
||||
memset(&sNC, 0, sizeof(sNC));
|
||||
sNC.pParse = pParse;
|
||||
pParse->pTriggerTab = sqlite3FindTable(db, pParse->pNewTrigger->table, zDb);
|
||||
pParse->eTriggerOp = pParse->pNewTrigger->op;
|
||||
|
||||
/* Resolve symbols in WHEN clause */
|
||||
if( pParse->pNewTrigger->pWhen ){
|
||||
rc = sqlite3ResolveExprNames(&sNC, pParse->pNewTrigger->pWhen);
|
||||
}
|
||||
|
||||
for(pStep=pParse->pNewTrigger->step_list;
|
||||
rc==SQLITE_OK && pStep;
|
||||
pStep=pStep->pNext
|
||||
){
|
||||
if( pStep->pSelect ){
|
||||
sqlite3SelectPrep(pParse, pStep->pSelect, &sNC);
|
||||
if( pParse->nErr ) rc = pParse->rc;
|
||||
}
|
||||
if( rc==SQLITE_OK && pStep->zTarget ){
|
||||
Table *pTarget = sqlite3LocateTable(pParse, 0, pStep->zTarget, zDb);
|
||||
if( pTarget==0 ){
|
||||
rc = SQLITE_ERROR;
|
||||
}else{
|
||||
SrcList sSrc;
|
||||
memset(&sSrc, 0, sizeof(sSrc));
|
||||
sSrc.nSrc = 1;
|
||||
sSrc.a[0].zName = pStep->zTarget;
|
||||
sSrc.a[0].pTab = pTarget;
|
||||
sNC.pSrcList = &sSrc;
|
||||
if( pStep->pWhere ){
|
||||
rc = sqlite3ResolveExprNames(&sNC, pStep->pWhere);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3ResolveExprListNames(&sNC, pStep->pExprList);
|
||||
}
|
||||
assert( !pStep->pUpsert || (!pStep->pWhere && !pStep->pExprList) );
|
||||
if( pStep->pUpsert ){
|
||||
Upsert *pUpsert = pStep->pUpsert;
|
||||
assert( rc==SQLITE_OK );
|
||||
pUpsert->pUpsertSrc = &sSrc;
|
||||
sNC.uNC.pUpsert = pUpsert;
|
||||
sNC.ncFlags = NC_UUpsert;
|
||||
rc = sqlite3ResolveExprListNames(&sNC, pUpsert->pUpsertTarget);
|
||||
if( rc==SQLITE_OK ){
|
||||
ExprList *pUpsertSet = pUpsert->pUpsertSet;
|
||||
rc = sqlite3ResolveExprListNames(&sNC, pUpsertSet);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3ResolveExprNames(&sNC, pUpsert->pUpsertWhere);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3ResolveExprNames(&sNC, pUpsert->pUpsertTargetWhere);
|
||||
}
|
||||
sNC.ncFlags = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void renameWalkTrigger(Walker *pWalker, Trigger *pTrigger){
|
||||
TriggerStep *pStep;
|
||||
|
||||
/* Find tokens to edit in WHEN clause */
|
||||
sqlite3WalkExpr(pWalker, pTrigger->pWhen);
|
||||
|
||||
/* Find tokens to edit in trigger steps */
|
||||
for(pStep=pTrigger->step_list; pStep; pStep=pStep->pNext){
|
||||
sqlite3WalkSelect(pWalker, pStep->pSelect);
|
||||
sqlite3WalkExpr(pWalker, pStep->pWhere);
|
||||
sqlite3WalkExprList(pWalker, pStep->pExprList);
|
||||
if( pStep->pUpsert ){
|
||||
Upsert *pUpsert = pStep->pUpsert;
|
||||
sqlite3WalkExprList(pWalker, pUpsert->pUpsertTarget);
|
||||
sqlite3WalkExprList(pWalker, pUpsert->pUpsertSet);
|
||||
sqlite3WalkExpr(pWalker, pUpsert->pUpsertWhere);
|
||||
sqlite3WalkExpr(pWalker, pUpsert->pUpsertTargetWhere);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** SQL function:
|
||||
**
|
||||
|
@ -1191,6 +1185,7 @@ static void renameColumnFunc(
|
|||
int nNew = sqlite3_value_bytes(argv[6]);
|
||||
int bQuote = sqlite3_value_int(argv[7]);
|
||||
const char *zOld;
|
||||
int bTemp = 0;
|
||||
|
||||
int rc;
|
||||
char *zErr = 0;
|
||||
|
@ -1199,8 +1194,6 @@ static void renameColumnFunc(
|
|||
Index *pIdx;
|
||||
char *zOut = 0;
|
||||
|
||||
char *zQuot = 0; /* Quoted version of zNew */
|
||||
int nQuot = 0; /* Length of zQuot in bytes */
|
||||
int i;
|
||||
Table *pTab;
|
||||
|
||||
|
@ -1219,54 +1212,7 @@ static void renameColumnFunc(
|
|||
memset(&sCtx, 0, sizeof(sCtx));
|
||||
sCtx.iCol = ((iCol==pTab->iPKey) ? -1 : iCol);
|
||||
|
||||
/* Parse the SQL statement passed as the first argument. If no error
|
||||
** occurs and the parse does not result in a new table, index or
|
||||
** trigger object, the database must be corrupt. */
|
||||
memset(&sParse, 0, sizeof(sParse));
|
||||
sParse.eParseMode = PARSE_MODE_RENAME_COLUMN;
|
||||
sParse.db = db;
|
||||
sParse.nQueryLoop = 1;
|
||||
rc = sqlite3RunParser(&sParse, zSql, &zErr);
|
||||
assert( sParse.zErrMsg==0 );
|
||||
assert( rc!=SQLITE_OK || zErr==0 );
|
||||
assert( (!!sParse.pNewTable)+(!!sParse.pNewIndex)+(!!sParse.pNewTrigger)<2 );
|
||||
sParse.zErrMsg = zErr;
|
||||
if( db->mallocFailed ) rc = SQLITE_NOMEM;
|
||||
if( rc==SQLITE_OK
|
||||
&& sParse.pNewTable==0 && sParse.pNewIndex==0 && sParse.pNewTrigger==0
|
||||
){
|
||||
rc = SQLITE_CORRUPT_BKPT;
|
||||
}
|
||||
|
||||
#ifdef SQLITE_DEBUG
|
||||
/* Ensure that all mappings in the Parse.pRename list really do map to
|
||||
** a part of the input string. */
|
||||
assert( sqlite3Strlen30(zSql)==nSql );
|
||||
if( rc==SQLITE_OK ){
|
||||
RenameToken *pToken;
|
||||
for(pToken=sParse.pRename; pToken; pToken=pToken->pNext){
|
||||
assert( pToken->t.z>=zSql && &pToken->t.z[pToken->t.n]<=&zSql[nSql] );
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Set zQuot to point to a buffer containing a quoted copy of the
|
||||
** identifier zNew. If the corresponding identifier in the original
|
||||
** ALTER TABLE statement was quoted (bQuote==1), then set zNew to
|
||||
** point to zQuot so that all substitutions are made using the
|
||||
** quoted version of the new column name. */
|
||||
if( rc==SQLITE_OK ){
|
||||
zQuot = sqlite3_mprintf("\"%w\"", zNew);
|
||||
if( zQuot==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
nQuot = sqlite3Strlen30(zQuot);
|
||||
}
|
||||
}
|
||||
if( bQuote ){
|
||||
zNew = zQuot;
|
||||
nNew = nQuot;
|
||||
}
|
||||
rc = renameParseSql(&sParse, zDb, 0, db, zSql, bTemp);
|
||||
|
||||
/* Find tokens that need to be replaced. */
|
||||
memset(&sWalker, 0, sizeof(Walker));
|
||||
|
@ -1325,140 +1271,35 @@ static void renameColumnFunc(
|
|||
}else{
|
||||
/* A trigger */
|
||||
TriggerStep *pStep;
|
||||
NameContext sNC;
|
||||
memset(&sNC, 0, sizeof(sNC));
|
||||
sNC.pParse = &sParse;
|
||||
sParse.pTriggerTab = sqlite3FindTable(db, sParse.pNewTrigger->table, zDb);
|
||||
sParse.eTriggerOp = sParse.pNewTrigger->op;
|
||||
rc = renameResolveTrigger(&sParse, zDb);
|
||||
if( rc!=SQLITE_OK ) goto renameColumnFunc_done;
|
||||
|
||||
/* Resolve symbols in WHEN clause */
|
||||
if( sParse.pNewTrigger->pWhen ){
|
||||
rc = sqlite3ResolveExprNames(&sNC, sParse.pNewTrigger->pWhen);
|
||||
}
|
||||
|
||||
for(pStep=sParse.pNewTrigger->step_list;
|
||||
rc==SQLITE_OK && pStep;
|
||||
pStep=pStep->pNext
|
||||
){
|
||||
if( pStep->pSelect ){
|
||||
sqlite3SelectPrep(&sParse, pStep->pSelect, &sNC);
|
||||
if( sParse.nErr ) rc = sParse.rc;
|
||||
}
|
||||
if( rc==SQLITE_OK && pStep->zTarget ){
|
||||
for(pStep=sParse.pNewTrigger->step_list; pStep; pStep=pStep->pNext){
|
||||
if( pStep->zTarget ){
|
||||
Table *pTarget = sqlite3LocateTable(&sParse, 0, pStep->zTarget, zDb);
|
||||
if( pTarget==0 ){
|
||||
rc = SQLITE_ERROR;
|
||||
}else{
|
||||
SrcList sSrc;
|
||||
memset(&sSrc, 0, sizeof(sSrc));
|
||||
sSrc.nSrc = 1;
|
||||
sSrc.a[0].zName = pStep->zTarget;
|
||||
sSrc.a[0].pTab = pTarget;
|
||||
sNC.pSrcList = &sSrc;
|
||||
if( pStep->pWhere ){
|
||||
rc = sqlite3ResolveExprNames(&sNC, pStep->pWhere);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3ResolveExprListNames(&sNC, pStep->pExprList);
|
||||
}
|
||||
assert( !pStep->pUpsert || (!pStep->pWhere && !pStep->pExprList) );
|
||||
if( pStep->pUpsert ){
|
||||
Upsert *pUpsert = pStep->pUpsert;
|
||||
assert( rc==SQLITE_OK );
|
||||
pUpsert->pUpsertSrc = &sSrc;
|
||||
sNC.uNC.pUpsert = pUpsert;
|
||||
sNC.ncFlags = NC_UUpsert;
|
||||
rc = sqlite3ResolveExprListNames(&sNC, pUpsert->pUpsertTarget);
|
||||
if( rc==SQLITE_OK ){
|
||||
ExprList *pUpsertSet = pUpsert->pUpsertSet;
|
||||
if( pTarget==pTab ){
|
||||
if( pStep->pUpsert ){
|
||||
ExprList *pUpsertSet = pStep->pUpsert->pUpsertSet;
|
||||
renameColumnElistNames(&sParse, &sCtx, pUpsertSet, zOld);
|
||||
}
|
||||
rc = sqlite3ResolveExprListNames(&sNC, pUpsertSet);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3ResolveExprNames(&sNC, pUpsert->pUpsertWhere);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3ResolveExprNames(&sNC, pUpsert->pUpsertTargetWhere);
|
||||
}
|
||||
sNC.ncFlags = 0;
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK && pTarget==pTab ){
|
||||
renameColumnIdlistNames(&sParse, &sCtx, pStep->pIdList, zOld);
|
||||
renameColumnElistNames(&sParse, &sCtx, pStep->pExprList, zOld);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( rc!=SQLITE_OK ) goto renameColumnFunc_done;
|
||||
|
||||
/* Find tokens to edit in UPDATE OF clause */
|
||||
if( sParse.pTriggerTab==pTab ){
|
||||
renameColumnIdlistNames(&sParse, &sCtx,sParse.pNewTrigger->pColumns,zOld);
|
||||
}
|
||||
|
||||
/* Find tokens to edit in WHEN clause */
|
||||
sqlite3WalkExpr(&sWalker, sParse.pNewTrigger->pWhen);
|
||||
|
||||
/* Find tokens to edit in trigger steps */
|
||||
for(pStep=sParse.pNewTrigger->step_list; pStep; pStep=pStep->pNext){
|
||||
sqlite3WalkSelect(&sWalker, pStep->pSelect);
|
||||
sqlite3WalkExpr(&sWalker, pStep->pWhere);
|
||||
sqlite3WalkExprList(&sWalker, pStep->pExprList);
|
||||
if( pStep->pUpsert ){
|
||||
Upsert *pUpsert = pStep->pUpsert;
|
||||
sqlite3WalkExprList(&sWalker, pUpsert->pUpsertTarget);
|
||||
sqlite3WalkExprList(&sWalker, pUpsert->pUpsertSet);
|
||||
sqlite3WalkExpr(&sWalker, pUpsert->pUpsertWhere);
|
||||
sqlite3WalkExpr(&sWalker, pUpsert->pUpsertTargetWhere);
|
||||
}
|
||||
}
|
||||
/* Find tokens to edit in various expressions and selects */
|
||||
renameWalkTrigger(&sWalker, sParse.pNewTrigger);
|
||||
}
|
||||
|
||||
/* At this point sCtx.pList contains a list of RenameToken objects
|
||||
** corresponding to all tokens in the input SQL that must be replaced
|
||||
** with the new column name. All that remains is to construct and
|
||||
** return the edited SQL string. */
|
||||
assert( rc==SQLITE_OK );
|
||||
assert( nQuot>=nNew );
|
||||
zOut = sqlite3DbMallocZero(db, nSql + sCtx.nList*nQuot + 1);
|
||||
if( zOut ){
|
||||
int nOut = nSql;
|
||||
memcpy(zOut, zSql, nSql);
|
||||
while( sCtx.pList ){
|
||||
int iOff; /* Offset of token to replace in zOut */
|
||||
RenameToken *pBest = renameColumnTokenNext(&sCtx);
|
||||
|
||||
u32 nReplace;
|
||||
const char *zReplace;
|
||||
if( sqlite3IsIdChar(*pBest->t.z) ){
|
||||
nReplace = nNew;
|
||||
zReplace = zNew;
|
||||
}else{
|
||||
nReplace = nQuot;
|
||||
zReplace = zQuot;
|
||||
}
|
||||
|
||||
iOff = pBest->t.z - zSql;
|
||||
if( pBest->t.n!=nReplace ){
|
||||
memmove(&zOut[iOff + nReplace], &zOut[iOff + pBest->t.n],
|
||||
nOut - (iOff + pBest->t.n)
|
||||
);
|
||||
nOut += nReplace - pBest->t.n;
|
||||
zOut[nOut] = '\0';
|
||||
}
|
||||
memcpy(&zOut[iOff], zReplace, nReplace);
|
||||
sqlite3DbFree(db, pBest);
|
||||
}
|
||||
|
||||
sqlite3_result_text(context, zOut, -1, SQLITE_TRANSIENT);
|
||||
sqlite3DbFree(db, zOut);
|
||||
}else{
|
||||
rc = SQLITE_NOMEM;
|
||||
}
|
||||
rc = renameEditSql(context, &sCtx, zSql, zNew, bQuote);
|
||||
|
||||
renameColumnFunc_done:
|
||||
if( rc!=SQLITE_OK ){
|
||||
|
@ -1479,23 +1320,180 @@ renameColumnFunc_done:
|
|||
renameTokenFree(db, sCtx.pList);
|
||||
sqlite3DbFree(db, sParse.zErrMsg);
|
||||
sqlite3ParserReset(&sParse);
|
||||
sqlite3_free(zQuot);
|
||||
sqlite3BtreeLeaveAll(db);
|
||||
}
|
||||
|
||||
static int renameTableExprCb(Walker *pWalker, Expr *pExpr){
|
||||
RenameCtx *p = pWalker->u.pRename;
|
||||
if( pExpr->op==TK_COLUMN && p->pTab==pExpr->pTab ){
|
||||
renameTokenFind(pWalker->pParse, p, (void*)&pExpr->pTab);
|
||||
}
|
||||
return WRC_Continue;
|
||||
}
|
||||
|
||||
/*
|
||||
** This is a Walker select callback.
|
||||
*/
|
||||
static int renameTableSelectCb(Walker *pWalker, Select *pSelect){
|
||||
int i;
|
||||
RenameCtx *p = pWalker->u.pRename;
|
||||
SrcList *pSrc = pSelect->pSrc;
|
||||
for(i=0; i<pSrc->nSrc; i++){
|
||||
struct SrcList_item *pItem = &pSrc->a[i];
|
||||
if( pItem->pTab==p->pTab ){
|
||||
renameTokenFind(pWalker->pParse, p, pItem->zName);
|
||||
}
|
||||
}
|
||||
|
||||
return WRC_Continue;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** This C function implements an SQL user function that is used by SQL code
|
||||
** generated by the ALTER TABLE ... RENAME command to modify the definition
|
||||
** of any foreign key constraints that use the table being renamed as the
|
||||
** parent table. It is passed three arguments:
|
||||
**
|
||||
** 1) The complete text of the CREATE TABLE statement being modified,
|
||||
** 2) The old name of the table being renamed, and
|
||||
** 3) The new name of the table being renamed.
|
||||
**
|
||||
** It returns the new CREATE TABLE statement. For example:
|
||||
**
|
||||
** sqlite_rename_table('CREATE TABLE t1(a REFERENCES t2)', 't2', 't3')
|
||||
** -> 'CREATE TABLE t1(a REFERENCES t3)'
|
||||
*/
|
||||
static void renameTableFunc(
|
||||
sqlite3_context *context,
|
||||
int NotUsed,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
sqlite3 *db = sqlite3_context_db_handle(context);
|
||||
char *zOutput = 0;
|
||||
char *zResult;
|
||||
unsigned char const *zDb = sqlite3_value_text(argv[0]);
|
||||
unsigned char const *zInput = sqlite3_value_text(argv[1]);
|
||||
unsigned char const *zOld = sqlite3_value_text(argv[2]);
|
||||
unsigned char const *zNew = sqlite3_value_text(argv[3]);
|
||||
int bTemp = sqlite3_value_int(argv[4]);
|
||||
|
||||
unsigned const char *z; /* Pointer to token */
|
||||
int n; /* Length of token z */
|
||||
int token; /* Type of token */
|
||||
|
||||
Parse sParse;
|
||||
int rc;
|
||||
int bQuote = 1;
|
||||
RenameCtx sCtx;
|
||||
Walker sWalker;
|
||||
|
||||
if( zInput==0 || zOld==0 || zNew==0 ) return;
|
||||
|
||||
memset(&sCtx, 0, sizeof(RenameCtx));
|
||||
sCtx.pTab = sqlite3FindTable(db, zOld, zDb);
|
||||
memset(&sWalker, 0, sizeof(Walker));
|
||||
sWalker.pParse = &sParse;
|
||||
sWalker.xExprCallback = renameTableExprCb;
|
||||
sWalker.xSelectCallback = renameTableSelectCb;
|
||||
sWalker.u.pRename = &sCtx;
|
||||
|
||||
sqlite3BtreeEnterAll(db);
|
||||
|
||||
rc = renameParseSql(&sParse, zDb, 1, db, zInput, bTemp);
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
if( sParse.pNewTable ){
|
||||
Table *pTab = sParse.pNewTable;
|
||||
|
||||
if( pTab->pSelect ){
|
||||
NameContext sNC;
|
||||
memset(&sNC, 0, sizeof(sNC));
|
||||
sNC.pParse = &sParse;
|
||||
|
||||
sqlite3SelectPrep(&sParse, pTab->pSelect, &sNC);
|
||||
if( sParse.nErr ) rc = sParse.rc;
|
||||
sqlite3WalkSelect(&sWalker, pTab->pSelect);
|
||||
}else{
|
||||
/* Modify any FK definitions to point to the new table. */
|
||||
#ifndef SQLITE_OMIT_FOREIGN_KEY
|
||||
FKey *pFKey;
|
||||
for(pFKey=pTab->pFKey; pFKey; pFKey=pFKey->pNextFrom){
|
||||
if( sqlite3_stricmp(pFKey->zTo, zOld)==0 ){
|
||||
renameTokenFind(&sParse, &sCtx, (void*)pFKey->zTo);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* If this is the table being altered, fix any table refs in CHECK
|
||||
** expressions. Also update the name that appears right after the
|
||||
** "CREATE [VIRTUAL] TABLE" bit. */
|
||||
if( sqlite3_stricmp(zOld, pTab->zName)==0 ){
|
||||
sCtx.pTab = pTab;
|
||||
sqlite3WalkExprList(&sWalker, pTab->pCheck);
|
||||
renameTokenFind(&sParse, &sCtx, pTab->zName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else if( sParse.pNewIndex ){
|
||||
renameTokenFind(&sParse, &sCtx, sParse.pNewIndex->zName);
|
||||
sqlite3WalkExpr(&sWalker, sParse.pNewIndex->pPartIdxWhere);
|
||||
}
|
||||
|
||||
#ifndef SQLITE_OMIT_TRIGGER
|
||||
else if( sParse.pNewTrigger ){
|
||||
Trigger *pTrigger = sParse.pNewTrigger;
|
||||
TriggerStep *pStep;
|
||||
if( 0==sqlite3_stricmp(sParse.pNewTrigger->table, zOld)
|
||||
&& sCtx.pTab->pSchema==pTrigger->pTabSchema
|
||||
){
|
||||
renameTokenFind(&sParse, &sCtx, sParse.pNewTrigger->table);
|
||||
}
|
||||
|
||||
rc = renameResolveTrigger(&sParse, zDb);
|
||||
if( rc==SQLITE_OK ){
|
||||
renameWalkTrigger(&sWalker, pTrigger);
|
||||
}
|
||||
|
||||
for(pStep=pTrigger->step_list; pStep; pStep=pStep->pNext){
|
||||
if( pStep->zTarget && 0==sqlite3_stricmp(pStep->zTarget, zOld) ){
|
||||
renameTokenFind(&sParse, &sCtx, pStep->zTarget);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = renameEditSql(context, &sCtx, zInput, zNew, bQuote);
|
||||
}
|
||||
|
||||
if( rc!=SQLITE_OK ){
|
||||
sqlite3_result_error_code(context, rc);
|
||||
}
|
||||
if( sParse.pVdbe ){
|
||||
sqlite3VdbeFinalize(sParse.pVdbe);
|
||||
}
|
||||
sqlite3DeleteTable(db, sParse.pNewTable);
|
||||
if( sParse.pNewIndex ) sqlite3FreeIndex(db, sParse.pNewIndex);
|
||||
sqlite3DeleteTrigger(db, sParse.pNewTrigger);
|
||||
renameTokenFree(db, sParse.pRename);
|
||||
renameTokenFree(db, sCtx.pList);
|
||||
sqlite3DbFree(db, sParse.zErrMsg);
|
||||
sqlite3ParserReset(&sParse);
|
||||
sqlite3BtreeLeaveAll(db);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
** Register built-in functions used to help implement ALTER TABLE
|
||||
*/
|
||||
void sqlite3AlterFunctions(void){
|
||||
static FuncDef aAlterTableFuncs[] = {
|
||||
FUNCTION(sqlite_rename_table, 2, 0, 0, renameTableFunc),
|
||||
FUNCTION(sqlite_rename_column, 8, 0, 0, renameColumnFunc),
|
||||
#ifndef SQLITE_OMIT_TRIGGER
|
||||
FUNCTION(sqlite_rename_trigger, 2, 0, 0, renameTriggerFunc),
|
||||
#endif
|
||||
#ifndef SQLITE_OMIT_FOREIGN_KEY
|
||||
FUNCTION(sqlite_rename_parent, 3, 0, 0, renameParentFunc),
|
||||
#endif
|
||||
FUNCTION(sqlite_rename_table, 5, 0, 0, renameTableFunc),
|
||||
};
|
||||
sqlite3InsertBuiltinFuncs(aAlterTableFuncs, ArraySize(aAlterTableFuncs));
|
||||
}
|
||||
|
|
34
src/build.c
34
src/build.c
|
@ -783,7 +783,7 @@ int sqlite3TwoPartName(
|
|||
return -1;
|
||||
}
|
||||
}else{
|
||||
assert( db->init.iDb==0 || db->init.busy
|
||||
assert( db->init.iDb==0 || db->init.busy || IN_RENAME_OBJECT
|
||||
|| (db->mDbFlags & DBFLAG_Vacuum)!=0);
|
||||
iDb = db->init.iDb;
|
||||
*pUnqual = pName1;
|
||||
|
@ -878,6 +878,9 @@ void sqlite3StartTable(
|
|||
}
|
||||
if( !OMIT_TEMPDB && isTemp ) iDb = 1;
|
||||
zName = sqlite3NameFromToken(db, pName);
|
||||
if( IN_RENAME_OBJECT ){
|
||||
sqlite3RenameTokenMap(pParse, (void*)zName, pName);
|
||||
}
|
||||
}
|
||||
pParse->sNameToken = *pName;
|
||||
if( zName==0 ) return;
|
||||
|
@ -1072,7 +1075,7 @@ void sqlite3AddColumn(Parse *pParse, Token *pName, Token *pType){
|
|||
}
|
||||
z = sqlite3DbMallocRaw(db, pName->n + pType->n + 2);
|
||||
if( z==0 ) return;
|
||||
if( IN_RENAME_COLUMN ) sqlite3RenameTokenMap(pParse, (void*)z, pName);
|
||||
if( IN_RENAME_OBJECT ) sqlite3RenameTokenMap(pParse, (void*)z, pName);
|
||||
memcpy(z, pName->z, pName->n);
|
||||
z[pName->n] = 0;
|
||||
sqlite3Dequote(z);
|
||||
|
@ -1370,7 +1373,7 @@ void sqlite3AddPrimaryKey(
|
|||
&& sqlite3StrICmp(sqlite3ColumnType(pCol,""), "INTEGER")==0
|
||||
&& sortOrder!=SQLITE_SO_DESC
|
||||
){
|
||||
if( IN_RENAME_COLUMN && pList ){
|
||||
if( IN_RENAME_OBJECT && pList ){
|
||||
sqlite3RenameTokenRemap(pParse, &pTab->iPKey, pList->a[0].pExpr);
|
||||
}
|
||||
pTab->iPKey = iCol;
|
||||
|
@ -2175,7 +2178,7 @@ void sqlite3CreateView(
|
|||
** allocated rather than point to the input string - which means that
|
||||
** they will persist after the current sqlite3_exec() call returns.
|
||||
*/
|
||||
if( IN_RENAME_COLUMN ){
|
||||
if( IN_RENAME_OBJECT ){
|
||||
p->pSelect = pSelect;
|
||||
pSelect = 0;
|
||||
}else{
|
||||
|
@ -2748,6 +2751,9 @@ void sqlite3CreateForeignKey(
|
|||
pFKey->pNextFrom = p->pFKey;
|
||||
z = (char*)&pFKey->aCol[nCol];
|
||||
pFKey->zTo = z;
|
||||
if( IN_RENAME_OBJECT ){
|
||||
sqlite3RenameTokenMap(pParse, (void*)z, pTo);
|
||||
}
|
||||
memcpy(z, pTo->z, pTo->n);
|
||||
z[pTo->n] = 0;
|
||||
sqlite3Dequote(z);
|
||||
|
@ -2770,7 +2776,7 @@ void sqlite3CreateForeignKey(
|
|||
pFromCol->a[i].zName);
|
||||
goto fk_end;
|
||||
}
|
||||
if( IN_RENAME_COLUMN ){
|
||||
if( IN_RENAME_OBJECT ){
|
||||
sqlite3RenameTokenRemap(pParse, &pFKey->aCol[i], pFromCol->a[i].zName);
|
||||
}
|
||||
}
|
||||
|
@ -2779,7 +2785,7 @@ void sqlite3CreateForeignKey(
|
|||
for(i=0; i<nCol; i++){
|
||||
int n = sqlite3Strlen30(pToCol->a[i].zName);
|
||||
pFKey->aCol[i].zCol = z;
|
||||
if( IN_RENAME_COLUMN ){
|
||||
if( IN_RENAME_OBJECT ){
|
||||
sqlite3RenameTokenRemap(pParse, z, pToCol->a[i].zName);
|
||||
}
|
||||
memcpy(z, pToCol->a[i].zName, n);
|
||||
|
@ -3114,7 +3120,7 @@ void sqlite3CreateIndex(
|
|||
if( SQLITE_OK!=sqlite3CheckObjectName(pParse, zName) ){
|
||||
goto exit_create_index;
|
||||
}
|
||||
if( !IN_RENAME_COLUMN ){
|
||||
if( !IN_RENAME_OBJECT ){
|
||||
if( !db->init.busy ){
|
||||
if( sqlite3FindTable(db, zName, 0)!=0 ){
|
||||
sqlite3ErrorMsg(pParse, "there is already a table named %s", zName);
|
||||
|
@ -3151,7 +3157,7 @@ void sqlite3CreateIndex(
|
|||
/* Check for authorization to create an index.
|
||||
*/
|
||||
#ifndef SQLITE_OMIT_AUTHORIZATION
|
||||
if( !IN_RENAME_COLUMN ){
|
||||
if( !IN_RENAME_OBJECT ){
|
||||
const char *zDb = pDb->zDbSName;
|
||||
if( sqlite3AuthCheck(pParse, SQLITE_INSERT, SCHEMA_TABLE(iDb), 0, zDb) ){
|
||||
goto exit_create_index;
|
||||
|
@ -3239,7 +3245,7 @@ void sqlite3CreateIndex(
|
|||
** index key.
|
||||
*/
|
||||
pListItem = pList->a;
|
||||
if( IN_RENAME_COLUMN ){
|
||||
if( IN_RENAME_OBJECT ){
|
||||
pIndex->aColExpr = pList;
|
||||
pList = 0;
|
||||
}
|
||||
|
@ -3399,7 +3405,7 @@ void sqlite3CreateIndex(
|
|||
}
|
||||
}
|
||||
|
||||
if( !IN_RENAME_COLUMN ){
|
||||
if( !IN_RENAME_OBJECT ){
|
||||
|
||||
/* Link the new Index structure to its table and to the other
|
||||
** in-memory database structures.
|
||||
|
@ -3517,7 +3523,7 @@ void sqlite3CreateIndex(
|
|||
}
|
||||
pIndex = 0;
|
||||
}
|
||||
else if( IN_RENAME_COLUMN ){
|
||||
else if( IN_RENAME_OBJECT ){
|
||||
assert( pParse->pNewIndex==0 );
|
||||
pParse->pNewIndex = pIndex;
|
||||
pIndex = 0;
|
||||
|
@ -3713,7 +3719,7 @@ IdList *sqlite3IdListAppend(Parse *pParse, IdList *pList, Token *pToken){
|
|||
return 0;
|
||||
}
|
||||
pList->a[i].zName = sqlite3NameFromToken(db, pToken);
|
||||
if( IN_RENAME_COLUMN && pList->a[i].zName ){
|
||||
if( IN_RENAME_OBJECT && pList->a[i].zName ){
|
||||
sqlite3RenameTokenMap(pParse, (void*)pList->a[i].zName, pToken);
|
||||
}
|
||||
return pList;
|
||||
|
@ -3962,6 +3968,10 @@ SrcList *sqlite3SrcListAppendFromTerm(
|
|||
}
|
||||
assert( p->nSrc>0 );
|
||||
pItem = &p->a[p->nSrc-1];
|
||||
if( IN_RENAME_OBJECT && pItem->zName ){
|
||||
Token *pToken = (pDatabase && pDatabase->z) ? pDatabase : pTable;
|
||||
sqlite3RenameTokenMap(pParse, pItem->zName, pToken);
|
||||
}
|
||||
assert( pAlias!=0 );
|
||||
if( pAlias->n ){
|
||||
pItem->zAlias = sqlite3NameFromToken(db, pAlias);
|
||||
|
|
|
@ -1666,7 +1666,7 @@ void sqlite3ExprListSetName(
|
|||
assert( pItem->zName==0 );
|
||||
pItem->zName = sqlite3DbStrNDup(pParse->db, pName->z, pName->n);
|
||||
if( dequote ) sqlite3Dequote(pItem->zName);
|
||||
if( IN_RENAME_COLUMN ){
|
||||
if( IN_RENAME_OBJECT ){
|
||||
sqlite3RenameTokenMap(pParse, (void*)pItem->zName, pName);
|
||||
}
|
||||
}
|
||||
|
|
30
src/parse.y
30
src/parse.y
|
@ -683,10 +683,14 @@ dbnm(A) ::= DOT nm(X). {A = X;}
|
|||
|
||||
%type fullname {SrcList*}
|
||||
%destructor fullname {sqlite3SrcListDelete(pParse->db, $$);}
|
||||
fullname(A) ::= nm(X).
|
||||
{A = sqlite3SrcListAppend(pParse->db,0,&X,0); /*A-overwrites-X*/}
|
||||
fullname(A) ::= nm(X) DOT nm(Y).
|
||||
{A = sqlite3SrcListAppend(pParse->db,0,&X,&Y); /*A-overwrites-X*/}
|
||||
fullname(A) ::= nm(X). {
|
||||
A = sqlite3SrcListAppend(pParse->db,0,&X,0);
|
||||
if( IN_RENAME_OBJECT && A ) sqlite3RenameTokenMap(pParse, A->a[0].zName, &X);
|
||||
}
|
||||
fullname(A) ::= nm(X) DOT nm(Y). {
|
||||
A = sqlite3SrcListAppend(pParse->db,0,&X,&Y);
|
||||
if( IN_RENAME_OBJECT && A ) sqlite3RenameTokenMap(pParse, A->a[0].zName, &Y);
|
||||
}
|
||||
|
||||
%type xfullname {SrcList*}
|
||||
%destructor xfullname {sqlite3SrcListDelete(pParse->db, $$);}
|
||||
|
@ -954,7 +958,7 @@ idlist(A) ::= nm(Y).
|
|||
#if SQLITE_MAX_EXPR_DEPTH>0
|
||||
p->nHeight = 1;
|
||||
#endif
|
||||
if( IN_RENAME_COLUMN ){
|
||||
if( IN_RENAME_OBJECT ){
|
||||
return (Expr*)sqlite3RenameTokenMap(pParse, (void*)p, &t);
|
||||
}
|
||||
}
|
||||
|
@ -970,7 +974,10 @@ expr(A) ::= JOIN_KW(X). {A=tokenExpr(pParse,TK_ID,X); /*A-overwrites-X*/}
|
|||
expr(A) ::= nm(X) DOT nm(Y). {
|
||||
Expr *temp1 = sqlite3ExprAlloc(pParse->db, TK_ID, &X, 1);
|
||||
Expr *temp2 = sqlite3ExprAlloc(pParse->db, TK_ID, &Y, 1);
|
||||
if( IN_RENAME_COLUMN ) sqlite3RenameTokenMap(pParse, (void*)temp2, &Y);
|
||||
if( IN_RENAME_OBJECT ){
|
||||
sqlite3RenameTokenMap(pParse, (void*)temp2, &Y);
|
||||
sqlite3RenameTokenMap(pParse, (void*)temp1, &X);
|
||||
}
|
||||
A = sqlite3PExpr(pParse, TK_DOT, temp1, temp2);
|
||||
}
|
||||
expr(A) ::= nm(X) DOT nm(Y) DOT nm(Z). {
|
||||
|
@ -978,7 +985,10 @@ expr(A) ::= nm(X) DOT nm(Y) DOT nm(Z). {
|
|||
Expr *temp2 = sqlite3ExprAlloc(pParse->db, TK_ID, &Y, 1);
|
||||
Expr *temp3 = sqlite3ExprAlloc(pParse->db, TK_ID, &Z, 1);
|
||||
Expr *temp4 = sqlite3PExpr(pParse, TK_DOT, temp2, temp3);
|
||||
if( IN_RENAME_COLUMN ) sqlite3RenameTokenMap(pParse, (void*)temp3, &Z);
|
||||
if( IN_RENAME_OBJECT ){
|
||||
sqlite3RenameTokenMap(pParse, (void*)temp3, &Z);
|
||||
sqlite3RenameTokenMap(pParse, (void*)temp2, &Y);
|
||||
}
|
||||
A = sqlite3PExpr(pParse, TK_DOT, temp1, temp4);
|
||||
}
|
||||
term(A) ::= NULL|FLOAT|BLOB(X). {A=tokenExpr(pParse,@X,X); /*A-overwrites-X*/}
|
||||
|
@ -1277,6 +1287,9 @@ cmd ::= createkw(S) uniqueflag(U) INDEX ifnotexists(NE) nm(X) dbnm(D)
|
|||
sqlite3CreateIndex(pParse, &X, &D,
|
||||
sqlite3SrcListAppend(pParse->db,0,&Y,0), Z, U,
|
||||
&S, W, SQLITE_SO_ASC, NE, SQLITE_IDXTYPE_APPDEF);
|
||||
if( IN_RENAME_OBJECT && pParse->pNewIndex ){
|
||||
sqlite3RenameTokenMap(pParse, pParse->pNewIndex->zName, &Y);
|
||||
}
|
||||
}
|
||||
|
||||
%type uniqueflag {int}
|
||||
|
@ -1325,9 +1338,6 @@ uniqueflag(A) ::= . {A = OE_None;}
|
|||
pIdToken->n, pIdToken->z);
|
||||
}
|
||||
sqlite3ExprListSetName(pParse, p, pIdToken, 1);
|
||||
if( IN_RENAME_COLUMN && p ){
|
||||
sqlite3RenameTokenMap(pParse, (void*)(p->a[p->nExpr-1].zName), pIdToken);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
} // end %include
|
||||
|
|
|
@ -93,7 +93,7 @@ int sqlite3InitCallback(void *pInit, int argc, char **argv, char **NotUsed){
|
|||
rc = db->errCode;
|
||||
assert( (rc&0xFF)==(rcp&0xFF) );
|
||||
db->init.iDb = saved_iDb;
|
||||
assert( saved_iDb==0 || (db->mDbFlags & DBFLAG_Vacuum)!=0 );
|
||||
/* assert( saved_iDb==0 || (db->mDbFlags & DBFLAG_Vacuum)!=0 ); */
|
||||
if( SQLITE_OK!=rc ){
|
||||
if( db->init.orphanTrigger ){
|
||||
assert( iDb==1 );
|
||||
|
|
|
@ -264,6 +264,9 @@ static int lookupName(
|
|||
if( sqlite3StrICmp(zTabName, zTab)!=0 ){
|
||||
continue;
|
||||
}
|
||||
if( IN_RENAME_OBJECT && pItem->zAlias ){
|
||||
sqlite3RenameTokenRemap(pParse, 0, (void*)&pExpr->pTab);
|
||||
}
|
||||
}
|
||||
if( 0==(cntTab++) ){
|
||||
pMatch = pItem;
|
||||
|
@ -349,7 +352,7 @@ static int lookupName(
|
|||
#ifndef SQLITE_OMIT_UPSERT
|
||||
if( pExpr->iTable==2 ){
|
||||
testcase( iCol==(-1) );
|
||||
if( IN_RENAME_COLUMN ){
|
||||
if( IN_RENAME_OBJECT ){
|
||||
pExpr->iColumn = iCol;
|
||||
pExpr->pTab = pTab;
|
||||
eNewExprOp = TK_COLUMN;
|
||||
|
@ -442,7 +445,7 @@ static int lookupName(
|
|||
cnt = 1;
|
||||
pMatch = 0;
|
||||
assert( zTab==0 && zDb==0 );
|
||||
if( IN_RENAME_COLUMN ){
|
||||
if( IN_RENAME_OBJECT ){
|
||||
sqlite3RenameTokenRemap(pParse, 0, (void*)pExpr);
|
||||
}
|
||||
goto lookupname_end;
|
||||
|
@ -672,21 +675,25 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
|
|||
zTable = 0;
|
||||
zColumn = pExpr->u.zToken;
|
||||
}else{
|
||||
Expr *pLeft = pExpr->pLeft;
|
||||
notValid(pParse, pNC, "the \".\" operator", NC_IdxExpr);
|
||||
pRight = pExpr->pRight;
|
||||
if( pRight->op==TK_ID ){
|
||||
zDb = 0;
|
||||
zTable = pExpr->pLeft->u.zToken;
|
||||
}else{
|
||||
assert( pRight->op==TK_DOT );
|
||||
zDb = pExpr->pLeft->u.zToken;
|
||||
zTable = pRight->pLeft->u.zToken;
|
||||
zDb = pLeft->u.zToken;
|
||||
pLeft = pRight->pLeft;
|
||||
pRight = pRight->pRight;
|
||||
}
|
||||
zTable = pLeft->u.zToken;
|
||||
zColumn = pRight->u.zToken;
|
||||
if( IN_RENAME_COLUMN ){
|
||||
if( IN_RENAME_OBJECT ){
|
||||
sqlite3RenameTokenRemap(pParse, (void*)pExpr, (void*)pRight);
|
||||
}
|
||||
if( IN_RENAME_OBJECT ){
|
||||
sqlite3RenameTokenRemap(pParse, (void*)&pExpr->pTab, (void*)pLeft);
|
||||
}
|
||||
}
|
||||
return lookupName(pParse, zDb, zTable, zColumn, pNC, pExpr);
|
||||
}
|
||||
|
@ -769,7 +776,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
|
|||
}
|
||||
}
|
||||
|
||||
if( 0==IN_RENAME_COLUMN ){
|
||||
if( 0==IN_RENAME_OBJECT ){
|
||||
#ifndef SQLITE_OMIT_WINDOWFUNC
|
||||
assert( is_agg==0 || (pDef->funcFlags & SQLITE_FUNC_MINMAX)
|
||||
|| (pDef->xValue==0 && pDef->xInverse==0)
|
||||
|
|
|
@ -3123,6 +3123,7 @@ struct Parse {
|
|||
#define PARSE_MODE_NORMAL 0
|
||||
#define PARSE_MODE_DECLARE_VTAB 1
|
||||
#define PARSE_MODE_RENAME_COLUMN 2
|
||||
#define PARSE_MODE_RENAME_TABLE 3
|
||||
|
||||
/*
|
||||
** Sizes and pointers of various parts of the Parse object.
|
||||
|
@ -3142,9 +3143,9 @@ struct Parse {
|
|||
#endif
|
||||
|
||||
#if defined(SQLITE_OMIT_ALTERTABLE)
|
||||
#define IN_RENAME_COLUMN 0
|
||||
#define IN_RENAME_OBJECT 0
|
||||
#else
|
||||
#define IN_RENAME_COLUMN (pParse->eParseMode==PARSE_MODE_RENAME_COLUMN)
|
||||
#define IN_RENAME_OBJECT (pParse->eParseMode>=PARSE_MODE_RENAME_COLUMN)
|
||||
#endif
|
||||
|
||||
#if defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_OMIT_ALTERTABLE)
|
||||
|
|
|
@ -695,7 +695,7 @@ int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzErrMsg){
|
|||
*/
|
||||
sqlite3DeleteTable(db, pParse->pNewTable);
|
||||
}
|
||||
if( !IN_RENAME_COLUMN ){
|
||||
if( !IN_RENAME_OBJECT ){
|
||||
sqlite3DeleteTrigger(db, pParse->pNewTrigger);
|
||||
}
|
||||
|
||||
|
|
|
@ -181,7 +181,7 @@ void sqlite3BeginTrigger(
|
|||
goto trigger_cleanup;
|
||||
}
|
||||
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
|
||||
if( !IN_RENAME_COLUMN ){
|
||||
if( !IN_RENAME_OBJECT ){
|
||||
if( sqlite3HashFind(&(db->aDb[iDb].pSchema->trigHash),zName) ){
|
||||
if( !noErr ){
|
||||
sqlite3ErrorMsg(pParse, "trigger %T already exists", pName);
|
||||
|
@ -214,7 +214,7 @@ void sqlite3BeginTrigger(
|
|||
}
|
||||
|
||||
#ifndef SQLITE_OMIT_AUTHORIZATION
|
||||
if( !IN_RENAME_COLUMN ){
|
||||
if( !IN_RENAME_OBJECT ){
|
||||
int iTabDb = sqlite3SchemaToIndex(db, pTab->pSchema);
|
||||
int code = SQLITE_CREATE_TRIGGER;
|
||||
const char *zDb = db->aDb[iTabDb].zDbSName;
|
||||
|
@ -248,7 +248,8 @@ void sqlite3BeginTrigger(
|
|||
pTrigger->pTabSchema = pTab->pSchema;
|
||||
pTrigger->op = (u8)op;
|
||||
pTrigger->tr_tm = tr_tm==TK_BEFORE ? TRIGGER_BEFORE : TRIGGER_AFTER;
|
||||
if( IN_RENAME_COLUMN ){
|
||||
if( IN_RENAME_OBJECT ){
|
||||
sqlite3RenameTokenRemap(pParse, pTrigger->table, pTableName->a[0].zName);
|
||||
pTrigger->pWhen = pWhen;
|
||||
pWhen = 0;
|
||||
}else{
|
||||
|
@ -305,7 +306,7 @@ void sqlite3FinishTrigger(
|
|||
}
|
||||
|
||||
#ifndef SQLITE_OMIT_ALTERTABLE
|
||||
if( IN_RENAME_COLUMN ){
|
||||
if( IN_RENAME_OBJECT ){
|
||||
assert( !db->init.busy );
|
||||
pParse->pNewTrigger = pTrig;
|
||||
pTrig = 0;
|
||||
|
@ -353,7 +354,7 @@ void sqlite3FinishTrigger(
|
|||
|
||||
triggerfinish_cleanup:
|
||||
sqlite3DeleteTrigger(db, pTrig);
|
||||
assert( IN_RENAME_COLUMN || !pParse->pNewTrigger );
|
||||
assert( IN_RENAME_OBJECT || !pParse->pNewTrigger );
|
||||
sqlite3DeleteTriggerStep(db, pStepList);
|
||||
}
|
||||
|
||||
|
@ -400,12 +401,13 @@ TriggerStep *sqlite3TriggerSelectStep(
|
|||
** If an OOM error occurs, NULL is returned and db->mallocFailed is set.
|
||||
*/
|
||||
static TriggerStep *triggerStepAllocate(
|
||||
sqlite3 *db, /* Database connection */
|
||||
Parse *pParse, /* Parser context */
|
||||
u8 op, /* Trigger opcode */
|
||||
Token *pName, /* The target name */
|
||||
const char *zStart, /* Start of SQL text */
|
||||
const char *zEnd /* End of SQL text */
|
||||
){
|
||||
sqlite3 *db = pParse->db;
|
||||
TriggerStep *pTriggerStep;
|
||||
|
||||
pTriggerStep = sqlite3DbMallocZero(db, sizeof(TriggerStep) + pName->n + 1);
|
||||
|
@ -416,6 +418,9 @@ static TriggerStep *triggerStepAllocate(
|
|||
pTriggerStep->zTarget = z;
|
||||
pTriggerStep->op = op;
|
||||
pTriggerStep->zSpan = triggerSpanDup(db, zStart, zEnd);
|
||||
if( IN_RENAME_OBJECT ){
|
||||
sqlite3RenameTokenMap(pParse, pTriggerStep->zTarget, pName);
|
||||
}
|
||||
}
|
||||
return pTriggerStep;
|
||||
}
|
||||
|
@ -442,9 +447,9 @@ TriggerStep *sqlite3TriggerInsertStep(
|
|||
|
||||
assert(pSelect != 0 || db->mallocFailed);
|
||||
|
||||
pTriggerStep = triggerStepAllocate(db, TK_INSERT, pTableName, zStart, zEnd);
|
||||
pTriggerStep = triggerStepAllocate(pParse, TK_INSERT, pTableName,zStart,zEnd);
|
||||
if( pTriggerStep ){
|
||||
if( IN_RENAME_COLUMN ){
|
||||
if( IN_RENAME_OBJECT ){
|
||||
pTriggerStep->pSelect = pSelect;
|
||||
pSelect = 0;
|
||||
}else{
|
||||
|
@ -481,9 +486,9 @@ TriggerStep *sqlite3TriggerUpdateStep(
|
|||
sqlite3 *db = pParse->db;
|
||||
TriggerStep *pTriggerStep;
|
||||
|
||||
pTriggerStep = triggerStepAllocate(db, TK_UPDATE, pTableName, zStart, zEnd);
|
||||
pTriggerStep = triggerStepAllocate(pParse, TK_UPDATE, pTableName,zStart,zEnd);
|
||||
if( pTriggerStep ){
|
||||
if( IN_RENAME_COLUMN ){
|
||||
if( IN_RENAME_OBJECT ){
|
||||
pTriggerStep->pExprList = pEList;
|
||||
pTriggerStep->pWhere = pWhere;
|
||||
pEList = 0;
|
||||
|
@ -514,9 +519,9 @@ TriggerStep *sqlite3TriggerDeleteStep(
|
|||
sqlite3 *db = pParse->db;
|
||||
TriggerStep *pTriggerStep;
|
||||
|
||||
pTriggerStep = triggerStepAllocate(db, TK_DELETE, pTableName, zStart, zEnd);
|
||||
pTriggerStep = triggerStepAllocate(pParse, TK_DELETE, pTableName,zStart,zEnd);
|
||||
if( pTriggerStep ){
|
||||
if( IN_RENAME_COLUMN ){
|
||||
if( IN_RENAME_OBJECT ){
|
||||
pTriggerStep->pWhere = pWhere;
|
||||
pWhere = 0;
|
||||
}else{
|
||||
|
|
|
@ -898,28 +898,28 @@ do_execsql_test alter-17.6 {
|
|||
SELECT sqlite_rename_trigger(NULL,'abc');
|
||||
} {{}}
|
||||
do_execsql_test alter-17.7 {
|
||||
SELECT sqlite_rename_parent('CREATE TABLE t1(a REFERENCES "xyzzy")',
|
||||
SELECT sqlite_rename_parent('main', 'CREATE TABLE t1(a REFERENCES "xyzzy")',
|
||||
'xyzzy','lmnop');
|
||||
} {{CREATE TABLE t1(a REFERENCES "lmnop")}}
|
||||
do_execsql_test alter-17.8 {
|
||||
SELECT sqlite_rename_parent('CREATE TABLE t1(a REFERENCES "xyzzy")',
|
||||
SELECT sqlite_rename_parent('main', 'CREATE TABLE t1(a REFERENCES "xyzzy")',
|
||||
'xyzzy',NULL);
|
||||
} {{CREATE TABLE t1(a REFERENCES "(NULL)")}}
|
||||
} {{}}
|
||||
do_execsql_test alter-17.9 {
|
||||
SELECT sqlite_rename_parent('CREATE TABLE t1(a REFERENCES "xyzzy")',
|
||||
SELECT sqlite_rename_parent('main', 'CREATE TABLE t1(a REFERENCES "xyzzy")',
|
||||
NULL, 'lmnop');
|
||||
} {{}}
|
||||
do_execsql_test alter-17.10 {
|
||||
SELECT sqlite_rename_parent(NULL,'abc','xyz');
|
||||
SELECT sqlite_rename_parent('main', NULL,'abc','xyz');
|
||||
} {{}}
|
||||
do_execsql_test alter-17.11 {
|
||||
SELECT sqlite_rename_parent('create references ''','abc','xyz');
|
||||
} {{create references '}}
|
||||
do_execsql_test alter-17.12 {
|
||||
SELECT sqlite_rename_parent('create references "abc"123" ','abc','xyz');
|
||||
} {{create references "xyz"123" }}
|
||||
do_execsql_test alter-17.13 {
|
||||
SELECT sqlite_rename_parent("references '''",'abc','xyz');
|
||||
} {{references '''}}
|
||||
do_catchsql_test alter-17.11 {
|
||||
SELECT sqlite_rename_parent('main', 'create references ''','abc','xyz');
|
||||
} {1 {SQL logic error}}
|
||||
do_catchsql_test alter-17.12 {
|
||||
SELECT sqlite_rename_parent('main', 'create references "abc"123" ','abc','xyz');
|
||||
} {1 {SQL logic error}}
|
||||
do_catchsql_test alter-17.13 {
|
||||
SELECT sqlite_rename_parent('main', "references '''",'abc','xyz');
|
||||
} {1 {SQL logic error}}
|
||||
|
||||
finish_test
|
||||
|
|
|
@ -0,0 +1,165 @@
|
|||
# 2018 August 24
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
#*************************************************************************
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix altertab
|
||||
|
||||
# If SQLITE_OMIT_ALTERTABLE is defined, omit this file.
|
||||
ifcapable !altertable {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE TABLE t1(a, b, CHECK(t1.a != t1.b));
|
||||
|
||||
CREATE TABLE t2(a, b);
|
||||
CREATE INDEX t2expr ON t2(a) WHERE t2.b>0;
|
||||
}
|
||||
|
||||
do_execsql_test 1.1 {
|
||||
SELECT sql FROM sqlite_master
|
||||
} {
|
||||
{CREATE TABLE t1(a, b, CHECK(t1.a != t1.b))}
|
||||
{CREATE TABLE t2(a, b)}
|
||||
{CREATE INDEX t2expr ON t2(a) WHERE t2.b>0}
|
||||
}
|
||||
|
||||
do_execsql_test 1.2 {
|
||||
ALTER TABLE t1 RENAME TO t1new;
|
||||
}
|
||||
|
||||
do_execsql_test 1.3 {
|
||||
CREATE TABLE t3(c, d);
|
||||
ALTER TABLE t3 RENAME TO t3new;
|
||||
DROP TABLE t3new;
|
||||
}
|
||||
|
||||
do_execsql_test 1.4 {
|
||||
SELECT sql FROM sqlite_master
|
||||
} {
|
||||
{CREATE TABLE "t1new"(a, b, CHECK("t1new".a != "t1new".b))}
|
||||
{CREATE TABLE t2(a, b)}
|
||||
{CREATE INDEX t2expr ON t2(a) WHERE t2.b>0}
|
||||
}
|
||||
|
||||
|
||||
do_execsql_test 1.3 {
|
||||
ALTER TABLE t2 RENAME TO t2new;
|
||||
}
|
||||
do_execsql_test 1.4 {
|
||||
SELECT sql FROM sqlite_master
|
||||
} {
|
||||
{CREATE TABLE "t1new"(a, b, CHECK("t1new".a != "t1new".b))}
|
||||
{CREATE TABLE "t2new"(a, b)}
|
||||
{CREATE INDEX t2expr ON "t2new"(a) WHERE "t2new".b>0}
|
||||
}
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
register_echo_module db
|
||||
|
||||
do_execsql_test 2.0 {
|
||||
CREATE TABLE abc(a, b, c);
|
||||
INSERT INTO abc VALUES(1, 2, 3);
|
||||
CREATE VIRTUAL TABLE eee USING echo('abc');
|
||||
SELECT * FROM eee;
|
||||
} {1 2 3}
|
||||
|
||||
do_execsql_test 2.1 {
|
||||
ALTER TABLE eee RENAME TO fff;
|
||||
SELECT * FROM fff;
|
||||
} {1 2 3}
|
||||
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
|
||||
do_catchsql_test 2.2 {
|
||||
ALTER TABLE fff RENAME TO ggg;
|
||||
} {1 {no such module: echo}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
|
||||
do_execsql_test 3.0 {
|
||||
CREATE TABLE txx(a, b, c);
|
||||
INSERT INTO txx VALUES(1, 2, 3);
|
||||
CREATE VIEW vvv AS SELECT main.txx.a, txx.b, c FROM txx;
|
||||
CREATE VIEW uuu AS SELECT main.one.a, one.b, c FROM txx AS one;
|
||||
CREATE VIEW temp.ttt AS SELECT main.txx.a, txx.b, one.b, main.one.a FROM txx AS one, txx;
|
||||
}
|
||||
|
||||
do_execsql_test 3.1.1 {
|
||||
SELECT * FROM vvv;
|
||||
} {1 2 3}
|
||||
do_execsql_test 3.1.2 {
|
||||
ALTER TABLE txx RENAME TO "t xx";
|
||||
SELECT * FROM vvv;
|
||||
} {1 2 3}
|
||||
do_execsql_test 3.1.3 {
|
||||
SELECT sql FROM sqlite_master WHERE name='vvv';
|
||||
} {{CREATE VIEW vvv AS SELECT main."t xx".a, "t xx".b, c FROM "t xx"}}
|
||||
|
||||
|
||||
do_execsql_test 3.2.1 {
|
||||
SELECT * FROM uuu;
|
||||
} {1 2 3}
|
||||
do_execsql_test 3.2.2 {
|
||||
SELECT sql FROM sqlite_master WHERE name='uuu';;
|
||||
} {{CREATE VIEW uuu AS SELECT main.one.a, one.b, c FROM "t xx" AS one}}
|
||||
|
||||
do_execsql_test 3.3.1 {
|
||||
SELECT * FROM ttt;
|
||||
} {1 2 2 1}
|
||||
do_execsql_test 3.3.2 {
|
||||
SELECT sql FROM sqlite_temp_master WHERE name='ttt';
|
||||
} {{CREATE VIEW ttt AS SELECT main."t xx".a, "t xx".b, one.b, main.one.a FROM "t xx" AS one, "t xx"}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 4.0 {
|
||||
CREATE table t1(x, y);
|
||||
CREATE table t2(a, b);
|
||||
|
||||
CREATE TRIGGER tr1 AFTER INSERT ON t1 BEGIN
|
||||
SELECT t1.x, * FROM t1, t2;
|
||||
INSERT INTO t2 VALUES(new.x, new.y);
|
||||
END;
|
||||
}
|
||||
|
||||
do_execsql_test 4.1 {
|
||||
INSERT INTO t1 VALUES(1, 1);
|
||||
ALTER TABLE t1 RENAME TO t11;
|
||||
INSERT INTO t11 VALUES(2, 2);
|
||||
ALTER TABLE t2 RENAME TO t22;
|
||||
INSERT INTO t11 VALUES(3, 3);
|
||||
}
|
||||
|
||||
proc squish {a} {
|
||||
string trim [regsub -all {[[:space:]][[:space:]]*} $a { }]
|
||||
}
|
||||
db func squish squish
|
||||
do_test 4.2 {
|
||||
execsql { SELECT squish(sql) FROM sqlite_master WHERE name = 'tr1' }
|
||||
} [list [squish {
|
||||
CREATE TRIGGER tr1 AFTER INSERT ON "t11" BEGIN
|
||||
SELECT "t11".x, * FROM "t11", "t22";
|
||||
INSERT INTO "t22" VALUES(new.x, new.y);
|
||||
END
|
||||
}]]
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
Loading…
Reference in New Issue