From 1e24072d05a9d4e56a5d73b27a128560c4ada959 Mon Sep 17 00:00:00 2001 From: dan Date: Mon, 15 Mar 2021 20:22:34 +0000 Subject: [PATCH 1/3] Add internal SQL function sqlite_rename_quotefix(). For converting double-quoted strings to their single-quoted counterparts in DDL statements. FossilOrigin-Name: d874b300463ce0bbb53b7e2f88c6a12893e4fd751fcc7f810077ba108f4061ef --- manifest | 18 +++-- manifest.uuid | 2 +- src/alter.c | 191 ++++++++++++++++++++++++++++++++++++++++------ test/alterqf.test | 63 +++++++++++++++ 4 files changed, 241 insertions(+), 33 deletions(-) create mode 100644 test/alterqf.test diff --git a/manifest b/manifest index ea6ec02745..a8f815b6d6 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Update\sgenerated\sautoconf/Makefile.msc\sto\sreflect\sits\ssource. -D 2021-03-12T23:15:08.537 +C Add\sinternal\sSQL\sfunction\ssqlite_rename_quotefix().\sFor\sconverting\sdouble-quoted\sstrings\sto\stheir\ssingle-quoted\scounterparts\sin\sDDL\sstatements. +D 2021-03-15T20:22:34.220 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -476,7 +476,7 @@ F spec.template 86a4a43b99ebb3e75e6b9a735d5fd293a24e90ca F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b F sqlite3.1 fc7ad8990fc8409983309bb80de8c811a7506786 F sqlite3.pc.in 48fed132e7cb71ab676105d2a4dc77127d8c1f3a -F src/alter.c 1f9383846b52e655d40479a5a5659bf60300ed43f7467e6e85d66588ba48f51b +F src/alter.c 3be93b32c606533808b8ca785d6c4adb55fced922f61ed9e8ba61e9764df9ebf F src/analyze.c 01c6c6765cb4d40b473b71d85535093730770bb186f2f473abac25f07fcdee5c F src/attach.c 9cbe761e464025694df8e6f6ee4d9f41432c3a255ca9443ccbb4130eeb87cf72 F src/auth.c 08954fdc4cc2da5264ba5b75cfd90b67a6fc7d1710a02ccf917c38eadec77853 @@ -655,6 +655,7 @@ F test/alterlegacy.test f38c6d06cda39e1f7b955bbce57f2e3ef5b7cb566d3d1234502093e2 F test/altermalloc.test 167a47de41b5c638f5f5c6efb59784002b196fff70f98d9b4ed3cd74a3fb80c9 F test/altermalloc2.test fa7b1c1139ea39b8dec407cf1feb032ca8e0076bd429574969b619175ad0174b F test/altermalloc3.test 2c7bbd8cf3e9c4a91e28675bb62bcc2ef70f227967fa74349f03d9f4642f0615 +F test/alterqf.test 8bb0ab4fcf7ecbe44a6ba8d017605bfece8bc2f0316727e4df70848e6b689578 F test/altertab.test 7691872aadfe00a94b459af9086504bcf399dd936336e486da1b182930744b77 F test/altertab2.test b0d62f323ca5dab42b0bc028c52e310ebdd13e655e8fac070fe622bad7852c2b F test/altertab3.test 2b82fa2236a3a91553d53ae5555d8e723c7eec174c41f1fa62ff497355398479 @@ -1910,7 +1911,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 acd63062eb06748bfe9e4886639e4f2b54ea6a496a83f10716abbaba4115500b -R 17f134cb27374fc63f087829be8f2d98 -U larrybr -Z f38b52cd66603df216d7879edb7bd2e5 +P d9f8f488ff9d47fe7bb8838e683bae4fea038f7278ef885ecf292143a0dd88ed +R 648b014b70c7f6df9ebb9162a19c79e7 +T *branch * alter-quotefix +T *sym-alter-quotefix * +T -sym-trunk * +U dan +Z bed13b8bcfee4ae93f7eef5cee7d76af diff --git a/manifest.uuid b/manifest.uuid index 214626662f..855047c659 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -d9f8f488ff9d47fe7bb8838e683bae4fea038f7278ef885ecf292143a0dd88ed \ No newline at end of file +d874b300463ce0bbb53b7e2f88c6a12893e4fd751fcc7f810077ba108f4061ef \ No newline at end of file diff --git a/src/alter.c b/src/alter.c index 72697f860c..33940d0e2a 100644 --- a/src/alter.c +++ b/src/alter.c @@ -1109,47 +1109,75 @@ static int renameEditSql( int nSql = sqlite3Strlen30(zSql); sqlite3 *db = sqlite3_context_db_handle(pCtx); int rc = SQLITE_OK; - char *zQuot; + char *zQuot = 0; char *zOut; int nQuot; + char *zBuf1 = 0; + char *zBuf2 = 0; - /* 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 = sqlite3MPrintf(db, "\"%w\"", zNew); - if( zQuot==0 ){ - return SQLITE_NOMEM; + if( zNew ){ + /* 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 = sqlite3MPrintf(db, "\"%w\"", zNew); + if( zQuot==0 ){ + return SQLITE_NOMEM; + }else{ + nQuot = sqlite3Strlen30(zQuot); + } + if( bQuote ){ + zNew = zQuot; + nNew = nQuot; + } + + assert( nQuot>=nNew ); + zOut = sqlite3DbMallocZero(db, nSql + pRename->nList*nQuot + 1); }else{ - nQuot = sqlite3Strlen30(zQuot); - } - if( bQuote ){ - zNew = zQuot; - nNew = nQuot; + zOut = (char*)sqlite3DbMallocZero(db, (nSql*2+1) * 3); + if( zOut ){ + zBuf1 = &zOut[nSql*2+1]; + zBuf2 = &zOut[nSql*4+2]; + } } /* 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); + ** with the new column name, or with single-quoted versions of themselves. + ** All that remains is to construct and return the edited SQL string. */ 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; + RenameToken *pBest = renameColumnTokenNext(pRename); + + if( zNew ){ + if( sqlite3IsIdChar(*pBest->t.z) ){ + nReplace = nNew; + zReplace = zNew; + }else{ + nReplace = nQuot; + zReplace = zQuot; + } }else{ - nReplace = nQuot; - zReplace = zQuot; + /* Dequote the double-quoted token. Then requote it again, this time + ** using single quotes. If the character immediately following the + ** original token within the input SQL was a single quote ('), then + ** add another space after the new, single-quoted version of the + ** token. This is so that (SELECT "string"'alias') maps to + ** (SELECT 'string' 'alias'), and not (SELECT 'string''alias'). */ + memcpy(zBuf1, pBest->t.z, pBest->t.n); + zBuf1[pBest->t.n] = 0; + sqlite3Dequote(zBuf1); + sqlite3_snprintf(nSql*2, zBuf2, "%Q%s", zBuf1, + pBest->t.z[pBest->t.n]=='\'' ? " " : "" + ); + zReplace = zBuf2; + nReplace = sqlite3Strlen30(zReplace); } iOff = pBest->t.z - zSql; @@ -1694,6 +1722,118 @@ static void renameTableFunc( return; } +static int renameQuotefixExprCb(Walker *pWalker, Expr *pExpr){ + if( pExpr->op==TK_STRING && (pExpr->flags & EP_DblQuoted) ){ + renameTokenFind(pWalker->pParse, pWalker->u.pRename, (void*)pExpr); + } + return WRC_Continue; +} + +/* +** The implementation of an SQL scalar function that rewrites DDL statements +** so that any string literals that use double-quotes are modified so that +** they use single quotes. +** +** Two arguments must be passed: +** +** 0: Database name ("main", "temp" etc.). +** 1: SQL statement to edit. +** +** The returned value is the modified SQL statement. For example, given +** the database schema: +** +** CREATE TABLE t1(a, b, c); +** +** SELECT sqlite_rename_quotefix('main', +** 'CREATE VIEW v1 AS SELECT "a", "string" FROM t1' +** ); +** +** returns the string: +** +** CREATE VIEW v1 AS SELECT "a", 'string' FROM t1 +*/ +static void renameQuotefixFunc( + sqlite3_context *context, + int NotUsed, + sqlite3_value **argv +){ + sqlite3 *db = sqlite3_context_db_handle(context); + char const *zDb = (const char*)sqlite3_value_text(argv[0]); + char const *zInput = (const char*)sqlite3_value_text(argv[1]); + +#ifndef SQLITE_OMIT_AUTHORIZATION + sqlite3_xauth xAuth = db->xAuth; + db->xAuth = 0; +#endif + + sqlite3BtreeEnterAll(db); + + UNUSED_PARAMETER(NotUsed); + if( zDb && zInput ){ + int rc; + Parse sParse; + rc = renameParseSql(&sParse, zDb, db, zInput, 0); + + if( rc==SQLITE_OK ){ + RenameCtx sCtx; + Walker sWalker; + + /* Walker to find tokens that need to be replaced. */ + memset(&sCtx, 0, sizeof(RenameCtx)); + memset(&sWalker, 0, sizeof(Walker)); + sWalker.pParse = &sParse; + sWalker.xExprCallback = renameQuotefixExprCb; + sWalker.xSelectCallback = renameColumnSelectCb; + sWalker.u.pRename = &sCtx; + + if( sParse.pNewTable ){ + Select *pSelect = sParse.pNewTable->pSelect; + if( pSelect ){ + pSelect->selFlags &= ~SF_View; + sParse.rc = SQLITE_OK; + sqlite3SelectPrep(&sParse, pSelect, 0); + rc = (db->mallocFailed ? SQLITE_NOMEM : sParse.rc); + if( rc==SQLITE_OK ){ + sqlite3WalkSelect(&sWalker, pSelect); + } + }else{ + int i; + sqlite3WalkExprList(&sWalker, sParse.pNewTable->pCheck); +#ifndef SQLITE_OMIT_GENERATED_COLUMNS + for(i=0; inCol; i++){ + sqlite3WalkExpr(&sWalker, sParse.pNewTable->aCol[i].pDflt); + } +#endif /* SQLITE_OMIT_GENERATED_COLUMNS */ + } + }else if( sParse.pNewIndex ){ + sqlite3WalkExprList(&sWalker, sParse.pNewIndex->aColExpr); + sqlite3WalkExpr(&sWalker, sParse.pNewIndex->pPartIdxWhere); + }else{ +#ifndef SQLITE_OMIT_TRIGGER + rc = renameResolveTrigger(&sParse); + if( rc==SQLITE_OK ){ + renameWalkTrigger(&sWalker, sParse.pNewTrigger); + } +#endif /* SQLITE_OMIT_TRIGGER */ + } + + if( rc==SQLITE_OK ){ + rc = renameEditSql(context, &sCtx, zInput, 0, 0); + } + } + if( rc!=SQLITE_OK ){ + sqlite3_result_error_code(context, rc); + } + renameParseCleanup(&sParse); + } + +#ifndef SQLITE_OMIT_AUTHORIZATION + db->xAuth = xAuth; +#endif + + sqlite3BtreeLeaveAll(db); +} + /* ** An SQL user function that checks that there are no parse or symbol ** resolution problems in a CREATE TRIGGER|TABLE|VIEW|INDEX statement. @@ -1968,6 +2108,7 @@ void sqlite3AlterFunctions(void){ INTERNAL_FUNCTION(sqlite_rename_table, 7, renameTableFunc), INTERNAL_FUNCTION(sqlite_rename_test, 6, renameTableTest), INTERNAL_FUNCTION(sqlite_drop_column, 3, dropColumnFunc), + INTERNAL_FUNCTION(sqlite_rename_quotefix,2, renameQuotefixFunc), }; sqlite3InsertBuiltinFuncs(aAlterTableFuncs, ArraySize(aAlterTableFuncs)); } diff --git a/test/alterqf.test b/test/alterqf.test new file mode 100644 index 0000000000..d71ba3843d --- /dev/null +++ b/test/alterqf.test @@ -0,0 +1,63 @@ +# 2021 March 16 +# +# 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 +# script focuses on testing internal function sqlite_rename_quotefix() +# directly. +# + + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set testprefix alterqf + +sqlite3_test_control SQLITE_TESTCTRL_INTERNAL_FUNCTIONS db + +do_execsql_test 1.0 { + CREATE TABLE t1(a, b, c); + CREATE TABLE xyz(a CHECK (a!="str"), b); +} + +foreach {tn before after} { + 1 {CREATE VIEW v1 AS SELECT "a", "b", "notacolumn!", "c" FROM t1} + {CREATE VIEW v1 AS SELECT "a", "b", 'notacolumn!', "c" FROM t1} + + 2 {CREATE VIEW v1 AS SELECT "a", "b", "not'a'column!", "c" FROM t1} + {CREATE VIEW v1 AS SELECT "a", "b", 'not''a''column!', "c" FROM t1} + + 3 {CREATE VIEW v1 AS SELECT "a", "b", "not""a""column!", "c" FROM t1} + {CREATE VIEW v1 AS SELECT "a", "b", 'not"a"column!', "c" FROM t1} + + 4 {CREATE VIEW v1 AS SELECT "val", count("b") FROM t1 GROUP BY "abc"} + {CREATE VIEW v1 AS SELECT 'val', count("b") FROM t1 GROUP BY 'abc'} + + 5 {CREATE TABLE xyz(a CHECK (a!="str"), b AS (a||"str"))} + {CREATE TABLE xyz(a CHECK (a!='str'), b AS (a||'str'))} + + 6 {CREATE INDEX i1 ON t1(a || "str", "b", "val")} + {CREATE INDEX i1 ON t1(a || 'str', "b", 'val')} + + 7 {CREATE TRIGGER tr AFTER INSERT ON t1 BEGIN SELECT "abcd"; END} + {CREATE TRIGGER tr AFTER INSERT ON t1 BEGIN SELECT 'abcd'; END} + + 8 {CREATE VIEW v1 AS SELECT "string"'alias' FROM t1} + {CREATE VIEW v1 AS SELECT 'string' 'alias' FROM t1} + + 9 {CREATE INDEX i1 ON t1(a) WHERE "b"="bb"} + {CREATE INDEX i1 ON t1(a) WHERE "b"='bb'} + +} { + do_execsql_test 1.$tn { + SELECT sqlite_rename_quotefix('main', $before) + } [list $after] +} + + +finish_test From 2ad080aa8254d2558b9d3225160af86ded30c43d Mon Sep 17 00:00:00 2001 From: dan Date: Tue, 16 Mar 2021 16:14:48 +0000 Subject: [PATCH 2/3] Have ALTER TABLE RENAME COLUMN and DROP COLUMN use sqlite_rename_quotefix() to convert any double-quoted strings in the database schema to their single-quoted equivalents. FossilOrigin-Name: 6446c0961077396086251670102ea7bf17d54a6b0f0ca56c6af89028a1ff9039 --- manifest | 16 ++++++------ manifest.uuid | 2 +- src/alter.c | 58 +++++++++++++++++++++++++++++++++++--------- test/alterauth2.test | 3 +++ test/altercol.test | 15 +++++++++++- 5 files changed, 73 insertions(+), 21 deletions(-) diff --git a/manifest b/manifest index 1e4bac5ad7..2a42d587a1 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Merge\strunk\schanges\sinto\sthis\sbranch. -D 2021-03-16T11:21:36.460 +C Have\sALTER\sTABLE\sRENAME\sCOLUMN\sand\sDROP\sCOLUMN\suse\ssqlite_rename_quotefix()\sto\sconvert\sany\sdouble-quoted\sstrings\sin\sthe\sdatabase\sschema\sto\stheir\ssingle-quoted\sequivalents. +D 2021-03-16T16:14:48.466 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -476,7 +476,7 @@ F spec.template 86a4a43b99ebb3e75e6b9a735d5fd293a24e90ca F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b F sqlite3.1 fc7ad8990fc8409983309bb80de8c811a7506786 F sqlite3.pc.in 48fed132e7cb71ab676105d2a4dc77127d8c1f3a -F src/alter.c 36ff6fed67e383e65238e0a063795f0645a3eb3420cdacc3463d822874ecc06a +F src/alter.c 4f2c88554c65dea16cc5fb37ba464a4ec23147e51d87d22651f96a39fdbe2134 F src/analyze.c 01c6c6765cb4d40b473b71d85535093730770bb186f2f473abac25f07fcdee5c F src/attach.c 9cbe761e464025694df8e6f6ee4d9f41432c3a255ca9443ccbb4130eeb87cf72 F src/auth.c 08954fdc4cc2da5264ba5b75cfd90b67a6fc7d1710a02ccf917c38eadec77853 @@ -646,8 +646,8 @@ F test/alter2.test a966ccfcddf9ce0a4e0e6ff1aca9e6e7948e0e242cd7e43fc091948521807 F test/alter3.test e487958dec7932453e0b83baf21d6b1e71d5e7d9a55bc20eadfa62a51ddffc29 F test/alter4.test dfd6086faf461b27ca2d2999848dcd207edf23352fc1592d0005c0844f3f08cf F test/alterauth.test 63442ba61ceb0c1eeb63aac1f4f5cebfa509d352276059d27106ae256bafc959 -F test/alterauth2.test 794ac5cef251819fe364b4fe20f12f86e9c5d68070513c7fd26c17cb244c89af -F test/altercol.test 3456f7cc4196ef8f7d82d245d6e91940eb12bc95c36c91ac4b512f6b5c9a4fa9 +F test/alterauth2.test 381b1ab603c9ef96314a3158528ea17f7964449385a28eeaf8191120b2e24a8d +F test/altercol.test 91f4eb0023d90beee000c06d45f521e07a2444d013b83381c62e88d5f209560f F test/altercorrupt.test 584d707a80e106952d6382790c8919bcf9f0db678ed3a1c09fd98b7f9d1d3a10 F test/alterdropcol.test 596623cb8a72d9570bfb8417b0f302810efe007873796f03c17a9e9ff28dade1 F test/alterdropcol2.test 527fce683b200d620f560f666c44ae33e22728e990a10a48a543280dfd4b4d41 @@ -1911,7 +1911,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P d874b300463ce0bbb53b7e2f88c6a12893e4fd751fcc7f810077ba108f4061ef 0e255b26872b50581d470952dd98e21dd82d081885006f58d49daa4b4576b35d -R 5cdc3c3d5c2d0efb3f1add4791367d78 +P f15d51054afb1e3fec87938f2b04a5a0d0611b08248367850450de7c4166e3d1 +R ab802f6c03d96d9f26abdd92e7d8cf40 U dan -Z f7f517381588ea598ea089070b1ed19b +Z cd13fbb96f280d93ac5264d668cf2de0 diff --git a/manifest.uuid b/manifest.uuid index 68fe878edc..40604b2022 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -f15d51054afb1e3fec87938f2b04a5a0d0611b08248367850450de7c4166e3d1 \ No newline at end of file +6446c0961077396086251670102ea7bf17d54a6b0f0ca56c6af89028a1ff9039 \ No newline at end of file diff --git a/src/alter.c b/src/alter.c index c46228e2cd..016d019d3e 100644 --- a/src/alter.c +++ b/src/alter.c @@ -53,7 +53,8 @@ static void renameTestSchema( Parse *pParse, /* Parse context */ const char *zDb, /* Name of db to verify schema of */ int bTemp, /* True if this is the temp db */ - const char *zWhen /* "when" part of error message */ + const char *zWhen, /* "when" part of error message */ + int bNoDQS /* Do not allow DQS in the schema */ ){ pParse->colNamesSet = 1; sqlite3NestedParse(pParse, @@ -61,9 +62,9 @@ static void renameTestSchema( "FROM \"%w\"." DFLT_SCHEMA_TABLE " " "WHERE name NOT LIKE 'sqliteX_%%' ESCAPE 'X'" " AND sql NOT LIKE 'create virtual%%'" - " AND sqlite_rename_test(%Q, sql, type, name, %d, %Q)=NULL ", + " AND sqlite_rename_test(%Q, sql, type, name, %d, %Q, %d)=NULL ", zDb, - zDb, bTemp, zWhen + zDb, bTemp, zWhen, bNoDQS ); if( bTemp==0 ){ @@ -72,8 +73,32 @@ static void renameTestSchema( "FROM temp." DFLT_SCHEMA_TABLE " " "WHERE name NOT LIKE 'sqliteX_%%' ESCAPE 'X'" " AND sql NOT LIKE 'create virtual%%'" - " AND sqlite_rename_test(%Q, sql, type, name, 1, %Q)=NULL ", - zDb, zWhen + " AND sqlite_rename_test(%Q, sql, type, name, 1, %Q, %d)=NULL ", + zDb, zWhen, bNoDQS + ); + } +} + +/* +** Generate VM code to replace any double-quoted strings (but not double-quoted +** identifiers) within the "sql" column of the sqlite_schema table in +** database zDb with their single-quoted equivalents. If argument bTemp is +** not true, similarly update all SQL statements in the sqlite_schema table +** of the temp db. +*/ +static void renameFixQuotes(Parse *pParse, const char *zDb, int bTemp){ + sqlite3NestedParse(pParse, + "UPDATE \"%w\"." DFLT_SCHEMA_TABLE + " SET sql = sqlite_rename_quotefix(%Q, sql)" + "WHERE name NOT LIKE 'sqliteX_%%' ESCAPE 'X'" + " AND sql NOT LIKE 'create virtual%%'" , zDb, zDb + ); + if( bTemp==0 ){ + sqlite3NestedParse(pParse, + "UPDATE temp." DFLT_SCHEMA_TABLE + " SET sql = sqlite_rename_quotefix('temp', sql)" + "WHERE name NOT LIKE 'sqliteX_%%' ESCAPE 'X'" + " AND sql NOT LIKE 'create virtual%%'" ); } } @@ -236,7 +261,7 @@ void sqlite3AlterRenameTable( "sql = sqlite_rename_table(%Q, type, name, sql, %Q, %Q, 1), " "tbl_name = " "CASE WHEN tbl_name=%Q COLLATE nocase AND " - " sqlite_rename_test(%Q, sql, type, name, 1, 'after rename') " + " sqlite_rename_test(%Q, sql, type, name, 1, 'after rename', 0) " "THEN %Q ELSE tbl_name END " "WHERE type IN ('view', 'trigger')" , zDb, zTabName, zName, zTabName, zDb, zName); @@ -256,7 +281,7 @@ void sqlite3AlterRenameTable( #endif renameReloadSchema(pParse, iDb, INITFLAG_AlterRename); - renameTestSchema(pParse, zDb, iDb==1, "after rename"); + renameTestSchema(pParse, zDb, iDb==1, "after rename", 0); exit_rename_table: sqlite3SrcListDelete(db, pSrc); @@ -595,6 +620,10 @@ void sqlite3AlterRenameColumn( goto exit_rename_column; } + /* Ensure the schema contains no double-quoted strings */ + renameTestSchema(pParse, zDb, iSchema==1, "", 0); + renameFixQuotes(pParse, zDb, iSchema==1); + /* Do the rename operation using a recursive UPDATE statement that ** uses the sqlite_rename_column() SQL function to compute the new ** CREATE statement text for the sqlite_schema table. @@ -624,7 +653,7 @@ void sqlite3AlterRenameColumn( /* Drop and reload the database schema. */ renameReloadSchema(pParse, iSchema, INITFLAG_AlterRename); - renameTestSchema(pParse, zDb, iSchema==1, "after rename"); + renameTestSchema(pParse, zDb, iSchema==1, "after rename", 1); exit_rename_column: sqlite3SrcListDelete(db, pSrc); @@ -1847,6 +1876,7 @@ static void renameQuotefixFunc( ** 3: Object name. ** 4: True if object is from temp schema. ** 5: "when" part of error message. +** 6: True to disable the DQS quirk when parsing SQL. ** ** Unless it finds an error, this function normally returns NULL. However, it ** returns integer value 1 if: @@ -1865,6 +1895,7 @@ static void renameTableTest( int bTemp = sqlite3_value_int(argv[4]); int isLegacy = (db->flags & SQLITE_LegacyAlter); char const *zWhen = (const char*)sqlite3_value_text(argv[5]); + int bNoDQS = sqlite3_value_int(argv[6]); #ifndef SQLITE_OMIT_AUTHORIZATION sqlite3_xauth xAuth = db->xAuth; @@ -1872,10 +1903,14 @@ static void renameTableTest( #endif UNUSED_PARAMETER(NotUsed); + if( zDb && zInput ){ int rc; Parse sParse; + int flags = db->flags; + if( bNoDQS ) db->flags &= ~(SQLITE_DqsDML|SQLITE_DqsDDL); rc = renameParseSql(&sParse, zDb, db, zInput, bTemp); + db->flags |= (flags & (SQLITE_DqsDML|SQLITE_DqsDDL)); if( rc==SQLITE_OK ){ if( isLegacy==0 && sParse.pNewTable && sParse.pNewTable->pSelect ){ NameContext sNC; @@ -2036,7 +2071,8 @@ void sqlite3AlterDropColumn(Parse *pParse, SrcList *pSrc, Token *pName){ iDb = sqlite3SchemaToIndex(db, pTab->pSchema); assert( iDb>=0 ); zDb = db->aDb[iDb].zDbSName; - renameTestSchema(pParse, zDb, iDb==1, ""); + renameTestSchema(pParse, zDb, iDb==1, "", 0); + renameFixQuotes(pParse, zDb, iDb==1); sqlite3NestedParse(pParse, "UPDATE \"%w\"." DFLT_SCHEMA_TABLE " SET " "sql = sqlite_drop_column(%d, sql, %d) " @@ -2046,7 +2082,7 @@ void sqlite3AlterDropColumn(Parse *pParse, SrcList *pSrc, Token *pName){ /* Drop and reload the database schema. */ renameReloadSchema(pParse, iDb, INITFLAG_AlterDrop); - renameTestSchema(pParse, zDb, iDb==1, "after drop column"); + renameTestSchema(pParse, zDb, iDb==1, "after drop column", 1); /* Edit rows of table on disk */ if( pParse->nErr==0 && (pTab->aCol[iCol].colFlags & COLFLAG_VIRTUAL)==0 ){ @@ -2106,7 +2142,7 @@ void sqlite3AlterFunctions(void){ static FuncDef aAlterTableFuncs[] = { INTERNAL_FUNCTION(sqlite_rename_column, 9, renameColumnFunc), INTERNAL_FUNCTION(sqlite_rename_table, 7, renameTableFunc), - INTERNAL_FUNCTION(sqlite_rename_test, 6, renameTableTest), + INTERNAL_FUNCTION(sqlite_rename_test, 7, renameTableTest), INTERNAL_FUNCTION(sqlite_drop_column, 3, dropColumnFunc), INTERNAL_FUNCTION(sqlite_rename_quotefix,2, renameQuotefixFunc), }; diff --git a/test/alterauth2.test b/test/alterauth2.test index a411408b58..27ded1c3ea 100644 --- a/test/alterauth2.test +++ b/test/alterauth2.test @@ -82,6 +82,7 @@ do_auth_test 1.2 { {SQLITE_ALTER_TABLE main t2 {} {}} {SQLITE_FUNCTION {} like {} {}} {SQLITE_FUNCTION {} sqlite_rename_column {} {}} + {SQLITE_FUNCTION {} sqlite_rename_quotefix {} {}} {SQLITE_FUNCTION {} sqlite_rename_test {} {}} {SQLITE_READ sqlite_master name main {}} {SQLITE_READ sqlite_master sql main {}} @@ -100,6 +101,7 @@ do_auth_test 1.3 { } { {SQLITE_FUNCTION {} like {} {}} {SQLITE_FUNCTION {} sqlite_drop_column {} {}} + {SQLITE_FUNCTION {} sqlite_rename_quotefix {} {}} {SQLITE_FUNCTION {} sqlite_rename_test {} {}} {SQLITE_READ sqlite_master name main {}} {SQLITE_READ sqlite_master sql main {}} @@ -110,6 +112,7 @@ do_auth_test 1.3 { {SQLITE_READ sqlite_temp_master type temp {}} {SQLITE_SELECT {} {} {} {}} {SQLITE_UPDATE sqlite_master sql main {}} + {SQLITE_UPDATE sqlite_temp_master sql temp {}} } finish_test diff --git a/test/altercol.test b/test/altercol.test index 047917e5a1..f2063e45ca 100644 --- a/test/altercol.test +++ b/test/altercol.test @@ -567,7 +567,7 @@ do_execsql_test 13.1.6 { do_catchsql_test 13.1.7 { ALTER TABLE x1 RENAME COLUMN t TO ttt; -} {1 {database disk image is malformed}} +} {1 {error in index x1i: }} do_execsql_test 13.1.8 { DELETE FROM sqlite_master WHERE name = 'x1i'; @@ -835,5 +835,18 @@ do_execsql_test 22.0 { {CREATE TABLE t2(c, othername, extra AS (c + 1))} } +#------------------------------------------------------------------------- +# +reset_db +do_execsql_test 22.0 { + CREATE TABLE t1(a, b); + CREATE INDEX x1 on t1("c"=b); + INSERT INTO t1 VALUES('a', 'a'); + INSERT INTO t1 VALUES('b', 'b'); + INSERT INTO t1 VALUES('c', 'c'); + ALTER TABLE t1 RENAME COLUMN a TO "c"; + PRAGMA integrity_check; +} {ok} + finish_test From 1fffa73ea2f06bf4c748f9d7ee6dda40e2ac6e7f Mon Sep 17 00:00:00 2001 From: dan Date: Tue, 16 Mar 2021 18:24:49 +0000 Subject: [PATCH 3/3] Add tests for sqlite_rename_quotefix(). Fix a memory leak in the same. FossilOrigin-Name: 531550056c38589c99c9a97b6afdbf8f34ff8d2131d74e9d42af3506e8045064 --- manifest | 16 ++++++------- manifest.uuid | 2 +- src/alter.c | 1 + test/altermalloc3.test | 8 +++++++ test/alterqf.test | 54 +++++++++++++++++++++++++++++++++++++++--- 5 files changed, 69 insertions(+), 12 deletions(-) diff --git a/manifest b/manifest index 2a42d587a1..382335785b 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Have\sALTER\sTABLE\sRENAME\sCOLUMN\sand\sDROP\sCOLUMN\suse\ssqlite_rename_quotefix()\sto\sconvert\sany\sdouble-quoted\sstrings\sin\sthe\sdatabase\sschema\sto\stheir\ssingle-quoted\sequivalents. -D 2021-03-16T16:14:48.466 +C Add\stests\sfor\ssqlite_rename_quotefix().\sFix\sa\smemory\sleak\sin\sthe\ssame. +D 2021-03-16T18:24:49.563 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -476,7 +476,7 @@ F spec.template 86a4a43b99ebb3e75e6b9a735d5fd293a24e90ca F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b F sqlite3.1 fc7ad8990fc8409983309bb80de8c811a7506786 F sqlite3.pc.in 48fed132e7cb71ab676105d2a4dc77127d8c1f3a -F src/alter.c 4f2c88554c65dea16cc5fb37ba464a4ec23147e51d87d22651f96a39fdbe2134 +F src/alter.c 842bb68d8fe651a9c997a30ca212f892b9f293a1e6446d748aa2de6e78345f15 F src/analyze.c 01c6c6765cb4d40b473b71d85535093730770bb186f2f473abac25f07fcdee5c F src/attach.c 9cbe761e464025694df8e6f6ee4d9f41432c3a255ca9443ccbb4130eeb87cf72 F src/auth.c 08954fdc4cc2da5264ba5b75cfd90b67a6fc7d1710a02ccf917c38eadec77853 @@ -654,8 +654,8 @@ F test/alterdropcol2.test 527fce683b200d620f560f666c44ae33e22728e990a10a48a54328 F test/alterlegacy.test f38c6d06cda39e1f7b955bbce57f2e3ef5b7cb566d3d1234502093e228c15811 F test/altermalloc.test 167a47de41b5c638f5f5c6efb59784002b196fff70f98d9b4ed3cd74a3fb80c9 F test/altermalloc2.test fa7b1c1139ea39b8dec407cf1feb032ca8e0076bd429574969b619175ad0174b -F test/altermalloc3.test 2c7bbd8cf3e9c4a91e28675bb62bcc2ef70f227967fa74349f03d9f4642f0615 -F test/alterqf.test 8bb0ab4fcf7ecbe44a6ba8d017605bfece8bc2f0316727e4df70848e6b689578 +F test/altermalloc3.test 7a21b042413c3e9571af4f583e327a6961ec2b7a34eae57a530f89b236a46a72 +F test/alterqf.test 67568ad152db8c1187b15633b801242cf960f1beafc51261a3d1725d910baeb2 F test/altertab.test 7691872aadfe00a94b459af9086504bcf399dd936336e486da1b182930744b77 F test/altertab2.test b0d62f323ca5dab42b0bc028c52e310ebdd13e655e8fac070fe622bad7852c2b F test/altertab3.test 2b82fa2236a3a91553d53ae5555d8e723c7eec174c41f1fa62ff497355398479 @@ -1911,7 +1911,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P f15d51054afb1e3fec87938f2b04a5a0d0611b08248367850450de7c4166e3d1 -R ab802f6c03d96d9f26abdd92e7d8cf40 +P 6446c0961077396086251670102ea7bf17d54a6b0f0ca56c6af89028a1ff9039 +R b66159d3defa695ee162d96725abba18 U dan -Z cd13fbb96f280d93ac5264d668cf2de0 +Z c919991679b7a45f0f2c00ac17619d54 diff --git a/manifest.uuid b/manifest.uuid index 40604b2022..9d40d3ce27 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -6446c0961077396086251670102ea7bf17d54a6b0f0ca56c6af89028a1ff9039 \ No newline at end of file +531550056c38589c99c9a97b6afdbf8f34ff8d2131d74e9d42af3506e8045064 \ No newline at end of file diff --git a/src/alter.c b/src/alter.c index 016d019d3e..6a0818f651 100644 --- a/src/alter.c +++ b/src/alter.c @@ -1849,6 +1849,7 @@ static void renameQuotefixFunc( if( rc==SQLITE_OK ){ rc = renameEditSql(context, &sCtx, zInput, 0, 0); } + renameTokenFree(db, sCtx.pList); } if( rc!=SQLITE_OK ){ sqlite3_result_error_code(context, rc); diff --git a/test/altermalloc3.test b/test/altermalloc3.test index d84f6bbc94..c9011e802d 100644 --- a/test/altermalloc3.test +++ b/test/altermalloc3.test @@ -22,6 +22,14 @@ ifcapable !altertable { } do_execsql_test 1.0 { + CREATE TABLE x1( + one, two, three, PRIMARY KEY(one), + CHECK (three!="xyz"), CHECK (two!="one") + ) WITHOUT ROWID; + CREATE INDEX x1i ON x1(one+"two"+"four") WHERE "five"; + CREATE TEMP TRIGGER AFTER INSERT ON x1 BEGIN + UPDATE x1 SET two=new.three || "new" WHERE one=new.one||""; + END; CREATE TABLE t1(a, b, c, d, PRIMARY KEY(d, b)) WITHOUT ROWID; INSERT INTO t1 VALUES(1, 2, 3, 4); } diff --git a/test/alterqf.test b/test/alterqf.test index d71ba3843d..ce00f3b596 100644 --- a/test/alterqf.test +++ b/test/alterqf.test @@ -9,8 +9,7 @@ # #*********************************************************************** # This file implements regression tests for SQLite library. This -# script focuses on testing internal function sqlite_rename_quotefix() -# directly. +# script focuses on testing internal function sqlite_rename_quotefix(). # @@ -22,7 +21,6 @@ sqlite3_test_control SQLITE_TESTCTRL_INTERNAL_FUNCTIONS db do_execsql_test 1.0 { CREATE TABLE t1(a, b, c); - CREATE TABLE xyz(a CHECK (a!="str"), b); } foreach {tn before after} { @@ -53,11 +51,61 @@ foreach {tn before after} { 9 {CREATE INDEX i1 ON t1(a) WHERE "b"="bb"} {CREATE INDEX i1 ON t1(a) WHERE "b"='bb'} + 10 {CREATE TABLE t2(abc, xyz CHECK (xyz != "123"))} + {CREATE TABLE t2(abc, xyz CHECK (xyz != '123'))} + + 11 { + CREATE TRIGGER ott AFTER UPDATE ON t1 BEGIN + SELECT max("str", new."a") FROM t1 + WHERE group_concat("b", ",") OVER (ORDER BY c||"str"); + UPDATE t1 SET c= b + "str"; + DELETE FROM t1 WHERE EXISTS ( + SELECT 1 FROM t1 AS o WHERE o."a" = "o.a" AND t1.b IN("t1.b") + ); + END; + } { + CREATE TRIGGER ott AFTER UPDATE ON t1 BEGIN + SELECT max('str', new."a") FROM t1 + WHERE group_concat("b", ',') OVER (ORDER BY c||'str'); + UPDATE t1 SET c= b + 'str'; + DELETE FROM t1 WHERE EXISTS ( + SELECT 1 FROM t1 AS o WHERE o."a" = 'o.a' AND t1.b IN('t1.b') + ); + END; + } + } { do_execsql_test 1.$tn { SELECT sqlite_rename_quotefix('main', $before) } [list $after] } +#------------------------------------------------------------------------- +reset_db +do_execsql_test 2.0 { + CREATE TABLE x1( + one, two, three, PRIMARY KEY(one), + CHECK (three!="xyz"), CHECK (two!="one") + ) WITHOUT ROWID; + CREATE INDEX x1i ON x1(one+"two"+"four") WHERE "five"; + CREATE TEMP TRIGGER AFTER INSERT ON x1 BEGIN + UPDATE x1 SET two=new.three || "new" WHERE one=new.one||""; + END; +} + +do_execsql_test 2.1 { + ALTER TABLE x1 RENAME two TO 'four'; + SELECT sql FROM sqlite_schema; + SELECT sql FROM sqlite_temp_schema; +} {{CREATE TABLE x1( + one, "four", three, PRIMARY KEY(one), + CHECK (three!='xyz'), CHECK ("four"!="one") + ) WITHOUT ROWID} + {CREATE INDEX x1i ON x1(one+"four"+'four') WHERE 'five'} + {CREATE TRIGGER AFTER INSERT ON x1 BEGIN + UPDATE x1 SET "four"=new.three || 'new' WHERE one=new.one||''; + END} +} + finish_test