From: danielk1977 Date: Sat, 14 Mar 2009 08:37:23 +0000 (+0000) Subject: Fix for #3719. When synthesizing a CREATE TABLE statement as as result of a "CREATE... X-Git-Tag: version-3.6.15~405 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=1b870de6b060a82407b6d05933debedf67642aa4;p=thirdparty%2Fsqlite.git Fix for #3719. When synthesizing a CREATE TABLE statement as as result of a "CREATE TABLE AS", quote the column type names unless they are simple identifiers or simple identifiers followed by one or two dimensions (e.g. "VARCHAR(10)"). (CVS 6345) FossilOrigin-Name: 7c6437efe0a0e935cfa8041bd6b94070c8654fa4 --- diff --git a/manifest b/manifest index 1f51465ab9..569d7fa1d2 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Do\snot\soverrun\sa\sbuffer\sin\sthe\sgenfkey\scode\s(now\spart\sof\sshell.c).\sFix\sfor\s#3722.\s(CVS\s6344) -D 2009-03-13T15:32:53 +C Fix\sfor\s#3719.\sWhen\ssynthesizing\sa\sCREATE\sTABLE\sstatement\sas\sas\sresult\sof\sa\s"CREATE\sTABLE\sAS",\squote\sthe\scolumn\stype\snames\sunless\sthey\sare\ssimple\sidentifiers\sor\ssimple\sidentifiers\sfollowed\sby\sone\sor\stwo\sdimensions\s(e.g.\s"VARCHAR(10)").\s(CVS\s6345) +D 2009-03-14T08:37:24 F Makefile.arm-wince-mingw32ce-gcc fcd5e9cd67fe88836360bb4f9ef4cb7f8e2fb5a0 F Makefile.in d64baddbf55cdf33ff030e14da837324711a4ef7 F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654 @@ -106,7 +106,7 @@ F src/btmutex.c 341502bc496dc0840dcb00cde65680fb0e85c3ab F src/btree.c 6e7501d7a207dcc15b099e67231bc8cc86ef7fe9 F src/btree.h 96a019c9f28da38e79940512d7800e419cd8c702 F src/btreeInt.h 0a4884e6152d7cae9c741e91b830064c19fd2c05 -F src/build.c 741240c8d6a54201fa8757db1ee6efba71be59a2 +F src/build.c 7e5e93011bab2f142cb03faa72bb64ff30e321fc F src/callback.c 09c6fedc77a45db99ba25a75d61382830314b357 F src/complete.c cb14e06dbe79dee031031f0d9e686ff306afe07c F src/date.c 0d804df3bbda46329946a01ff5c75c3f4f135218 @@ -547,7 +547,7 @@ F test/subquery.test b524f57c9574b2c0347045b4510ef795d4686796 F test/subselect.test d24fd8757daf97dafd2e889c73ea4c4272dcf4e4 F test/substr.test 18f57c4ca8a598805c4d64e304c418734d843c1a F test/sync.test ded6b39d8d8ca3c0c5518516c6371b3316d3e3a3 -F test/table.test 0aac9468b69d2683e68ee2682cdae28d82a453ec +F test/table.test bf1bc3d9634342a3470bdf64b6190e7445b6b8a6 F test/tableapi.test 505031f15b18a750184d967d2c896cf88fcc969c F test/tclsqlite.test 413a8a887d89ea8fa7055e8d118ffb03b0a4c91a F test/tempdb.test b88ac8a19823cf771d742bf61eef93ef337c06b1 @@ -704,7 +704,7 @@ F tool/speedtest16.c c8a9c793df96db7e4933f0852abb7a03d48f2e81 F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224 F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e -P 03679857a320517a7b89e5214e948bce9af896a9 -R 6c05748751f4494b7f4484ee342f5594 +P 943b11fb188835f0c62b6064b084192b1bbe1c0c +R 80daaf5cc0fe3707a03f09e098f3c65b U danielk1977 -Z 81f620610e97249eb47012ea10670973 +Z e47d0a979c492c3c4fa5d4292c1f1fd6 diff --git a/manifest.uuid b/manifest.uuid index 6c600ae9ae..53c80d75dd 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -943b11fb188835f0c62b6064b084192b1bbe1c0c \ No newline at end of file +7c6437efe0a0e935cfa8041bd6b94070c8654fa4 \ No newline at end of file diff --git a/src/build.c b/src/build.c index 446a6903fd..4103e3cb25 100644 --- a/src/build.c +++ b/src/build.c @@ -22,7 +22,7 @@ ** COMMIT ** ROLLBACK ** -** $Id: build.c,v 1.521 2009/02/28 10:47:42 danielk1977 Exp $ +** $Id: build.c,v 1.522 2009/03/14 08:37:24 danielk1977 Exp $ */ #include "sqliteInt.h" @@ -1330,18 +1330,100 @@ static int identLength(const char *z){ } /* -** Write an identifier onto the end of the given string. Add -** quote characters as needed. +** This function is a wrapper around sqlite3GetToken() used by +** isValidDimension(). This function differs from sqlite3GetToken() in +** that: +** +** * Whitespace is ignored, and +** * The output variable *peToken is set to 0 if the end of the +** nul-terminated input string is reached. +*/ +static int getTokenNoSpace(unsigned char *z, int *peToken){ + int n = 0; + while( sqlite3Isspace(z[n]) ) n++; + if( !z[n] ){ + *peToken = 0; + return 0; + } + return n + sqlite3GetToken(&z[n], peToken); +} + +/* +** Parameter z points to a nul-terminated string. Return true if, when +** whitespace is ignored, the contents of this string matches one of +** the following patterns: +** +** "" +** "(number)" +** "(number,number)" */ -static void identPut(char *z, int *pIdx, char *zSignedIdent){ +static int isValidDimension(unsigned char *z){ + int eToken; + int n = 0; + n += getTokenNoSpace(&z[n], &eToken); + if( eToken ){ + if( eToken!=TK_LP ) return 0; + n += getTokenNoSpace(&z[n], &eToken); + if( eToken==TK_PLUS || eToken==TK_MINUS ){ + n += getTokenNoSpace(&z[n], &eToken); + } + if( eToken!=TK_INTEGER && eToken!=TK_FLOAT ) return 0; + n += getTokenNoSpace(&z[n], &eToken); + if( eToken==TK_COMMA ){ + n += getTokenNoSpace(&z[n], &eToken); + if( eToken==TK_PLUS || eToken==TK_MINUS ){ + n += getTokenNoSpace(&z[n], &eToken); + } + if( eToken!=TK_INTEGER && eToken!=TK_FLOAT ) return 0; + n += getTokenNoSpace(&z[n], &eToken); + } + if( eToken!=TK_RP ) return 0; + getTokenNoSpace(&z[n], &eToken); + } + if( eToken ) return 0; + return 1; +} + +/* +** The first parameter is a pointer to an output buffer. The second +** parameter is a pointer to an integer that contains the offset at +** which to write into the output buffer. This function copies the +** nul-terminated string pointed to by the third parameter, zSignedIdent, +** to the specified offset in the buffer and updates *pIdx to refer +** to the first byte after the last byte written before returning. +** +** If the string zSignedIdent consists entirely of alpha-numeric +** characters, does not begin with a digit and is not an SQL keyword, +** then it is copied to the output buffer exactly as it is. Otherwise, +** it is quoted using double-quotes. +*/ +static void identPut(char *z, int *pIdx, char *zSignedIdent, int isTypename){ unsigned char *zIdent = (unsigned char*)zSignedIdent; int i, j, needQuote; i = *pIdx; + for(j=0; zIdent[j]; j++){ if( !sqlite3Isalnum(zIdent[j]) && zIdent[j]!='_' ) break; } - needQuote = zIdent[j]!=0 || sqlite3Isdigit(zIdent[0]) - || sqlite3KeywordCode(zIdent, j)!=TK_ID; + needQuote = sqlite3Isdigit(zIdent[0]) || sqlite3KeywordCode(zIdent, j)!=TK_ID; + if( !needQuote ){ + if( isTypename ){ + /* If this is a type-name, allow a little more flexibility. In SQLite, + ** a type-name is specified as: + ** + ** ids [ids] [(number [, number])] + ** + ** where "ids" is either a quoted string or a simple identifier (in the + ** above notation, [] means optional). It is a bit tricky to check + ** for all cases, but it is good to avoid unnecessarily quoting common + ** typenames like VARCHAR(10). + */ + needQuote = !isValidDimension(&zIdent[j]); + }else{ + needQuote = zIdent[j]; + } + } + if( needQuote ) z[i++] = '"'; for(j=0; zIdent[j]; j++){ z[i++] = zIdent[j]; @@ -1367,7 +1449,7 @@ static char *createTableStmt(sqlite3 *db, Table *p){ n += identLength(pCol->zName); z = pCol->zType; if( z ){ - n += (sqlite3Strlen30(z) + 1); + n += identLength(z); } } n += identLength(p->zName); @@ -1388,18 +1470,17 @@ static char *createTableStmt(sqlite3 *db, Table *p){ } sqlite3_snprintf(n, zStmt, "CREATE TABLE "); k = sqlite3Strlen30(zStmt); - identPut(zStmt, &k, p->zName); + identPut(zStmt, &k, p->zName, 0); zStmt[k++] = '('; for(pCol=p->aCol, i=0; inCol; i++, pCol++){ sqlite3_snprintf(n-k, &zStmt[k], zSep); k += sqlite3Strlen30(&zStmt[k]); zSep = zSep2; - identPut(zStmt, &k, pCol->zName); + identPut(zStmt, &k, pCol->zName, 0); if( (z = pCol->zType)!=0 ){ zStmt[k++] = ' '; assert( (int)(sqlite3Strlen30(z)+k+1)<=n ); - sqlite3_snprintf(n-k, &zStmt[k], "%s", z); - k += sqlite3Strlen30(z); + identPut(zStmt, &k, z, 1); } } sqlite3_snprintf(n-k, &zStmt[k], "%s", zEnd); diff --git a/test/table.test b/test/table.test index 880a6cbaee..55508e92d9 100644 --- a/test/table.test +++ b/test/table.test @@ -11,7 +11,7 @@ # This file implements regression tests for SQLite library. The # focus of this file is testing the CREATE TABLE statement. # -# $Id: table.test,v 1.49 2009/01/16 11:04:58 danielk1977 Exp $ +# $Id: table.test,v 1.50 2009/03/14 08:37:24 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -399,6 +399,37 @@ do_test table-8.8 { } } {1 {no such table: no_such_table}} +do_test table-8.9 { + execsql { + CREATE TABLE t10("col.1" [char.3]); + CREATE TABLE t11 AS SELECT * FROM t10; + SELECT sql FROM sqlite_master WHERE name = 't11'; + } +} {{CREATE TABLE t11("col.1" "char.3")}} +do_test table-8.10 { + execsql { + CREATE TABLE t12( + a INTEGER, + b VARCHAR(10), + c VARCHAR(1,10), + d VARCHAR(+1,-10), + e VARCHAR (+1,-10), + f "VARCHAR (+1,-10, 5)", + g BIG INTEGER + ); + CREATE TABLE t13 AS SELECT * FROM t12; + SELECT sql FROM sqlite_master WHERE name = 't13'; + } +} {{CREATE TABLE t13( + a INTEGER, + b VARCHAR(10), + c VARCHAR(1,10), + d VARCHAR(+1,-10), + e VARCHAR (+1,-10), + f "VARCHAR (+1,-10, 5)", + g "BIG INTEGER" +)}} + # Make sure we cannot have duplicate column names within a table. # do_test table-9.1 {