| | @@ -39,14 +39,14 @@ |
| 39 | 39 | ** Optionally #include a user-defined header, whereby compilation options |
| 40 | 40 | ** may be set prior to where they take effect, but after platform setup. |
| 41 | 41 | ** If SQLITE_CUSTOM_INCLUDE=? is defined, its value names the #include |
| 42 | 42 | ** file. Note that this macro has a like effect on sqlite3.c compilation. |
| 43 | 43 | */ |
| 44 | +# define SHELL_STRINGIFY_(f) #f |
| 45 | +# define SHELL_STRINGIFY(f) SHELL_STRINGIFY_(f) |
| 44 | 46 | #ifdef SQLITE_CUSTOM_INCLUDE |
| 45 | | -# define INC_STRINGIFY_(f) #f |
| 46 | | -# define INC_STRINGIFY(f) INC_STRINGIFY_(f) |
| 47 | | -# include INC_STRINGIFY(SQLITE_CUSTOM_INCLUDE) |
| 47 | +# include SHELL_STRINGIFY(SQLITE_CUSTOM_INCLUDE) |
| 48 | 48 | #endif |
| 49 | 49 | |
| 50 | 50 | /* |
| 51 | 51 | ** Determine if we are dealing with WinRT, which provides only a subset of |
| 52 | 52 | ** the full Win32 API. |
| | @@ -13388,11 +13388,16 @@ |
| 13388 | 13388 | if( ur==0x7ff0000000000000LL ){ |
| 13389 | 13389 | raw_printf(p->out, "1e999"); |
| 13390 | 13390 | }else if( ur==0xfff0000000000000LL ){ |
| 13391 | 13391 | raw_printf(p->out, "-1e999"); |
| 13392 | 13392 | }else{ |
| 13393 | | - sqlite3_snprintf(50,z,"%!.20g", r); |
| 13393 | + sqlite3_int64 ir = (sqlite3_int64)r; |
| 13394 | + if( r==(double)ir ){ |
| 13395 | + sqlite3_snprintf(50,z,"%lld.0", ir); |
| 13396 | + }else{ |
| 13397 | + sqlite3_snprintf(50,z,"%!.20g", r); |
| 13398 | + } |
| 13394 | 13399 | raw_printf(p->out, "%s", z); |
| 13395 | 13400 | } |
| 13396 | 13401 | }else if( aiType && aiType[i]==SQLITE_BLOB && p->pStmt ){ |
| 13397 | 13402 | const void *pBlob = sqlite3_column_blob(p->pStmt, i); |
| 13398 | 13403 | int nBlob = sqlite3_column_bytes(p->pStmt, i); |
| | @@ -16828,14 +16833,14 @@ |
| 16828 | 16833 | } |
| 16829 | 16834 | |
| 16830 | 16835 | /* |
| 16831 | 16836 | ** Run an SQL command and return the single integer result. |
| 16832 | 16837 | */ |
| 16833 | | -static int db_int(ShellState *p, const char *zSql){ |
| 16838 | +static int db_int(sqlite3 *db, const char *zSql){ |
| 16834 | 16839 | sqlite3_stmt *pStmt; |
| 16835 | 16840 | int res = 0; |
| 16836 | | - sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); |
| 16841 | + sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0); |
| 16837 | 16842 | if( pStmt && sqlite3_step(pStmt)==SQLITE_ROW ){ |
| 16838 | 16843 | res = sqlite3_column_int(pStmt,0); |
| 16839 | 16844 | } |
| 16840 | 16845 | sqlite3_finalize(pStmt); |
| 16841 | 16846 | return res; |
| | @@ -16936,11 +16941,11 @@ |
| 16936 | 16941 | }else{ |
| 16937 | 16942 | zSchemaTab = sqlite3_mprintf("\"%w\".sqlite_schema", zDb); |
| 16938 | 16943 | } |
| 16939 | 16944 | for(i=0; i<ArraySize(aQuery); i++){ |
| 16940 | 16945 | char *zSql = sqlite3_mprintf(aQuery[i].zSql, zSchemaTab); |
| 16941 | | - int val = db_int(p, zSql); |
| 16946 | + int val = db_int(p->db, zSql); |
| 16942 | 16947 | sqlite3_free(zSql); |
| 16943 | 16948 | utf8_printf(p->out, "%-20s %d\n", aQuery[i].zName, val); |
| 16944 | 16949 | } |
| 16945 | 16950 | sqlite3_free(zSchemaTab); |
| 16946 | 16951 | sqlite3_file_control(p->db, zDb, SQLITE_FCNTL_DATA_VERSION, &iDataVersion); |
| | @@ -18299,10 +18304,11 @@ |
| 18299 | 18304 | *pRc = SQLITE_NOMEM; |
| 18300 | 18305 | } |
| 18301 | 18306 | } |
| 18302 | 18307 | return z; |
| 18303 | 18308 | } |
| 18309 | + |
| 18304 | 18310 | |
| 18305 | 18311 | /* |
| 18306 | 18312 | ** When running the ".recover" command, each output table, and the special |
| 18307 | 18313 | ** orphaned row table if it is required, is represented by an instance |
| 18308 | 18314 | ** of the following struct. |
| | @@ -18896,10 +18902,225 @@ |
| 18896 | 18902 | sqlite3_exec(pState->db, "DETACH recovery", 0, 0, 0); |
| 18897 | 18903 | return rc; |
| 18898 | 18904 | } |
| 18899 | 18905 | #endif /* !(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB) */ |
| 18900 | 18906 | |
| 18907 | + |
| 18908 | +/* |
| 18909 | + * zAutoColumn(zCol, &db, ?) => Maybe init db, add column zCol to it. |
| 18910 | + * zAutoColumn(0, &db, ?) => (db!=0) Form columns spec for CREATE TABLE, |
| 18911 | + * close db and set it to 0, and return the columns spec, to later |
| 18912 | + * be sqlite3_free()'ed by the caller. |
| 18913 | + * The return is 0 when either: |
| 18914 | + * (a) The db was not initialized and zCol==0 (There are no columns.) |
| 18915 | + * (b) zCol!=0 (Column was added, db initialized as needed.) |
| 18916 | + * The 3rd argument, pRenamed, references an out parameter. If the |
| 18917 | + * pointer is non-zero, its referent will be set to a summary of renames |
| 18918 | + * done if renaming was necessary, or set to 0 if none was done. The out |
| 18919 | + * string (if any) must be sqlite3_free()'ed by the caller. |
| 18920 | + */ |
| 18921 | +#ifdef SHELL_DEBUG |
| 18922 | +#define rc_err_oom_die(rc) \ |
| 18923 | + if( rc==SQLITE_NOMEM ) shell_check_oom(0); \ |
| 18924 | + else if(!(rc==SQLITE_OK||rc==SQLITE_DONE)) \ |
| 18925 | + fprintf(stderr,"E:%d\n",rc), assert(0) |
| 18926 | +#else |
| 18927 | +static void rc_err_oom_die(int rc){ |
| 18928 | + if( rc==SQLITE_NOMEM ) shell_check_oom(0); |
| 18929 | + assert(rc==SQLITE_OK||rc==SQLITE_DONE); |
| 18930 | +} |
| 18931 | +#endif |
| 18932 | + |
| 18933 | +#ifdef SHELL_COLFIX_DB /* If this is set, the DB can be in a file. */ |
| 18934 | +static char zCOL_DB[] = SHELL_STRINGIFY(SHELL_COLFIX_DB); |
| 18935 | +#else /* Otherwise, memory is faster/better for the transient DB. */ |
| 18936 | +static const char *zCOL_DB = ":memory:"; |
| 18937 | +#endif |
| 18938 | + |
| 18939 | +/* Define character (as C string) to separate generated column ordinal |
| 18940 | + * from protected part of incoming column names. This defaults to "_" |
| 18941 | + * so that incoming column identifiers that did not need not be quoted |
| 18942 | + * remain usable without being quoted. It must be one character. |
| 18943 | + */ |
| 18944 | +#ifndef SHELL_AUTOCOLUMN_SEP |
| 18945 | +# define AUTOCOLUMN_SEP "_" |
| 18946 | +#else |
| 18947 | +# define AUTOCOLUMN_SEP SHELL_STRINGIFY(SHELL_AUTOCOLUMN_SEP) |
| 18948 | +#endif |
| 18949 | + |
| 18950 | +static char *zAutoColumn(const char *zColNew, sqlite3 **pDb, char **pzRenamed){ |
| 18951 | + /* Queries and D{D,M}L used here */ |
| 18952 | + static const char * const zTabMake = "\ |
| 18953 | +CREATE TABLE ColNames(\ |
| 18954 | + cpos INTEGER PRIMARY KEY,\ |
| 18955 | + name TEXT, nlen INT, chop INT, reps INT, suff TEXT);\ |
| 18956 | +CREATE VIEW RepeatedNames AS \ |
| 18957 | +SELECT DISTINCT t.name FROM ColNames t \ |
| 18958 | +WHERE t.name COLLATE NOCASE IN (\ |
| 18959 | + SELECT o.name FROM ColNames o WHERE o.cpos<>t.cpos\ |
| 18960 | +);\ |
| 18961 | +"; |
| 18962 | + static const char * const zTabFill = "\ |
| 18963 | +INSERT INTO ColNames(name,nlen,chop,reps,suff)\ |
| 18964 | + VALUES(iif(length(?1)>0,?1,'?'),max(length(?1),1),0,0,'')\ |
| 18965 | +"; |
| 18966 | + static const char * const zHasDupes = "\ |
| 18967 | +SELECT count(DISTINCT (substring(name,1,nlen-chop)||suff) COLLATE NOCASE)\ |
| 18968 | + <count(name) FROM ColNames\ |
| 18969 | +"; |
| 18970 | +#ifdef SHELL_COLUMN_RENAME_CLEAN |
| 18971 | + static const char * const zDedoctor = "\ |
| 18972 | +UPDATE ColNames SET chop=iif(\ |
| 18973 | + (substring(name,nlen,1) BETWEEN '0' AND '9')\ |
| 18974 | + AND (rtrim(name,'0123456790') glob '*"AUTOCOLUMN_SEP"'),\ |
| 18975 | + nlen-length(rtrim(name, '"AUTOCOLUMN_SEP"0123456789')),\ |
| 18976 | + 0\ |
| 18977 | +)\ |
| 18978 | +"; |
| 18979 | +#endif |
| 18980 | + static const char * const zSetReps = "\ |
| 18981 | +UPDATE ColNames AS t SET reps=\ |
| 18982 | +(SELECT count(*) FROM ColNames d \ |
| 18983 | + WHERE substring(t.name,1,t.nlen-t.chop)=substring(d.name,1,d.nlen-d.chop)\ |
| 18984 | + COLLATE NOCASE\ |
| 18985 | +)\ |
| 18986 | +"; |
| 18987 | +#ifdef SQLITE_ENABLE_MATH_FUNCTIONS |
| 18988 | + static const char * const zColDigits = "\ |
| 18989 | +SELECT CAST(ceil(log(count(*)+0.5)) AS INT) FROM ColNames \ |
| 18990 | +"; |
| 18991 | +#endif |
| 18992 | + static const char * const zRenameRank = |
| 18993 | +#ifdef SHELL_COLUMN_RENAME_CLEAN |
| 18994 | + "UPDATE ColNames AS t SET suff=" |
| 18995 | + "iif(reps>1, printf('%c%0*d', '"AUTOCOLUMN_SEP"', $1, cpos), '')" |
| 18996 | +#else /* ...RENAME_MINIMAL_ONE_PASS */ |
| 18997 | +"WITH Lzn(nlz) AS (" /* Find minimum extraneous leading 0's for uniqueness */ |
| 18998 | +" SELECT 0 AS nlz" |
| 18999 | +" UNION" |
| 19000 | +" SELECT nlz+1 AS nlz FROM Lzn" |
| 19001 | +" WHERE EXISTS(" |
| 19002 | +" SELECT 1" |
| 19003 | +" FROM ColNames t, ColNames o" |
| 19004 | +" WHERE" |
| 19005 | +" iif(t.name IN (SELECT * FROM RepeatedNames)," |
| 19006 | +" printf('%s"AUTOCOLUMN_SEP"%s'," |
| 19007 | +" t.name, substring(printf('%.*c%0.*d',nlz+1,'0',$1,t.cpos),2))," |
| 19008 | +" t.name" |
| 19009 | +" )" |
| 19010 | +" =" |
| 19011 | +" iif(o.name IN (SELECT * FROM RepeatedNames)," |
| 19012 | +" printf('%s"AUTOCOLUMN_SEP"%s'," |
| 19013 | +" o.name, substring(printf('%.*c%0.*d',nlz+1,'0',$1,o.cpos),2))," |
| 19014 | +" o.name" |
| 19015 | +" )" |
| 19016 | +" COLLATE NOCASE" |
| 19017 | +" AND o.cpos<>t.cpos" |
| 19018 | +" GROUP BY t.cpos" |
| 19019 | +" )" |
| 19020 | +") UPDATE Colnames AS t SET" |
| 19021 | +" chop = 0," /* No chopping, never touch incoming names. */ |
| 19022 | +" suff = iif(name IN (SELECT * FROM RepeatedNames)," |
| 19023 | +" printf('"AUTOCOLUMN_SEP"%s', substring(" |
| 19024 | +" printf('%.*c%0.*d',(SELECT max(nlz) FROM Lzn)+1,'0',1,t.cpos),2))," |
| 19025 | +" ''" |
| 19026 | +" )" |
| 19027 | +#endif |
| 19028 | + ; |
| 19029 | + static const char * const zCollectVar = "\ |
| 19030 | +SELECT\ |
| 19031 | + '('||x'0a'\ |
| 19032 | + || group_concat(\ |
| 19033 | + cname||' TEXT',\ |
| 19034 | + ','||iif((cpos-1)%4>0, ' ', x'0a'||' '))\ |
| 19035 | + ||')' AS ColsSpec \ |
| 19036 | +FROM (\ |
| 19037 | + SELECT cpos, printf('\"%w\"',printf('%.*s%s', nlen-chop,name,suff)) AS cname \ |
| 19038 | + FROM ColNames ORDER BY cpos\ |
| 19039 | +)"; |
| 19040 | + static const char * const zRenamesDone = |
| 19041 | + "SELECT group_concat(" |
| 19042 | + " printf('\"%w\" to \"%w\"',name,printf('%.*s%s', nlen-chop, name, suff))," |
| 19043 | + " ','||x'0a')" |
| 19044 | + "FROM ColNames WHERE suff<>'' OR chop!=0" |
| 19045 | + ; |
| 19046 | + int rc; |
| 19047 | + sqlite3_stmt *pStmt = 0; |
| 19048 | + assert(pDb!=0); |
| 19049 | + if( zColNew ){ |
| 19050 | + /* Add initial or additional column. Init db if necessary. */ |
| 19051 | + if( *pDb==0 ){ |
| 19052 | + if( SQLITE_OK!=sqlite3_open(zCOL_DB, pDb) ) return 0; |
| 19053 | +#ifdef SHELL_COLFIX_DB |
| 19054 | + if(*zCOL_DB!=':') |
| 19055 | + sqlite3_exec(*pDb,"drop table if exists ColNames;" |
| 19056 | + "drop view if exists RepeatedNames;",0,0,0); |
| 19057 | +#endif |
| 19058 | + rc = sqlite3_exec(*pDb, zTabMake, 0, 0, 0); |
| 19059 | + rc_err_oom_die(rc); |
| 19060 | + } |
| 19061 | + assert(*pDb!=0); |
| 19062 | + rc = sqlite3_prepare_v2(*pDb, zTabFill, -1, &pStmt, 0); |
| 19063 | + rc_err_oom_die(rc); |
| 19064 | + rc = sqlite3_bind_text(pStmt, 1, zColNew, -1, 0); |
| 19065 | + rc_err_oom_die(rc); |
| 19066 | + rc = sqlite3_step(pStmt); |
| 19067 | + rc_err_oom_die(rc); |
| 19068 | + sqlite3_finalize(pStmt); |
| 19069 | + return 0; |
| 19070 | + }else if( *pDb==0 ){ |
| 19071 | + return 0; |
| 19072 | + }else{ |
| 19073 | + /* Formulate the columns spec, close the DB, zero *pDb. */ |
| 19074 | + char *zColsSpec = 0; |
| 19075 | + int hasDupes = db_int(*pDb, zHasDupes); |
| 19076 | +#ifdef SQLITE_ENABLE_MATH_FUNCTIONS |
| 19077 | + int nDigits = (hasDupes)? db_int(*pDb, zColDigits) : 0; |
| 19078 | +#else |
| 19079 | +# define nDigits 2 |
| 19080 | +#endif |
| 19081 | + if( hasDupes ){ |
| 19082 | +#ifdef SHELL_COLUMN_RENAME_CLEAN |
| 19083 | + rc = sqlite3_exec(*pDb, zDedoctor, 0, 0, 0); |
| 19084 | + rc_err_oom_die(rc); |
| 19085 | +#endif |
| 19086 | + rc = sqlite3_exec(*pDb, zSetReps, 0, 0, 0); |
| 19087 | + rc_err_oom_die(rc); |
| 19088 | + rc = sqlite3_prepare_v2(*pDb, zRenameRank, -1, &pStmt, 0); |
| 19089 | + rc_err_oom_die(rc); |
| 19090 | + sqlite3_bind_int(pStmt, 1, nDigits); |
| 19091 | + rc = sqlite3_step(pStmt); |
| 19092 | + sqlite3_finalize(pStmt); |
| 19093 | + assert(rc==SQLITE_DONE); |
| 19094 | + } |
| 19095 | + assert(db_int(*pDb, zHasDupes)==0); /* Consider: remove this */ |
| 19096 | + rc = sqlite3_prepare_v2(*pDb, zCollectVar, -1, &pStmt, 0); |
| 19097 | + rc_err_oom_die(rc); |
| 19098 | + rc = sqlite3_step(pStmt); |
| 19099 | + if( rc==SQLITE_ROW ){ |
| 19100 | + zColsSpec = sqlite3_mprintf("%s", sqlite3_column_text(pStmt, 0)); |
| 19101 | + }else{ |
| 19102 | + zColsSpec = 0; |
| 19103 | + } |
| 19104 | + if( pzRenamed!=0 ){ |
| 19105 | + if( !hasDupes ) *pzRenamed = 0; |
| 19106 | + else{ |
| 19107 | + sqlite3_finalize(pStmt); |
| 19108 | + if( SQLITE_OK==sqlite3_prepare_v2(*pDb, zRenamesDone, -1, &pStmt, 0) |
| 19109 | + && SQLITE_ROW==sqlite3_step(pStmt) ){ |
| 19110 | + *pzRenamed = sqlite3_mprintf("%s", sqlite3_column_text(pStmt, 0)); |
| 19111 | + }else |
| 19112 | + *pzRenamed = 0; |
| 19113 | + } |
| 19114 | + } |
| 19115 | + sqlite3_finalize(pStmt); |
| 19116 | + sqlite3_close(*pDb); |
| 19117 | + *pDb = 0; |
| 19118 | + return zColsSpec; |
| 19119 | + } |
| 19120 | +} |
| 19121 | + |
| 18901 | 19122 | /* |
| 18902 | 19123 | ** If an input line begins with "." then invoke this routine to |
| 18903 | 19124 | ** process that line. |
| 18904 | 19125 | ** |
| 18905 | 19126 | ** Return 1 on error, 2 to exit, and 0 otherwise. |
| | @@ -19844,24 +20065,33 @@ |
| 19844 | 20065 | rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); |
| 19845 | 20066 | import_append_char(&sCtx, 0); /* To ensure sCtx.z is allocated */ |
| 19846 | 20067 | if( rc && sqlite3_strglob("no such table: *", sqlite3_errmsg(p->db))==0 ){ |
| 19847 | 20068 | char *zCreate = sqlite3_mprintf("CREATE TABLE \"%w\".\"%w\"", |
| 19848 | 20069 | zSchema, zTable); |
| 19849 | | - char cSep = '('; |
| 20070 | + sqlite3 *dbCols = 0; |
| 20071 | + char *zRenames = 0; |
| 20072 | + char *zColDefs; |
| 19850 | 20073 | while( xRead(&sCtx) ){ |
| 19851 | | - zCreate = sqlite3_mprintf("%z%c\n \"%w\" TEXT", zCreate, cSep, sCtx.z); |
| 19852 | | - cSep = ','; |
| 20074 | + zAutoColumn(sCtx.z, &dbCols, 0); |
| 19853 | 20075 | if( sCtx.cTerm!=sCtx.cColSep ) break; |
| 19854 | 20076 | } |
| 19855 | | - if( cSep=='(' ){ |
| 20077 | + zColDefs = zAutoColumn(0, &dbCols, &zRenames); |
| 20078 | + if( zRenames!=0 ){ |
| 20079 | + utf8_printf((stdin_is_interactive && p->in==stdin)? p->out : stderr, |
| 20080 | + "Columns renamed during .import %s due to duplicates:\n" |
| 20081 | + "%s\n", sCtx.zFile, zRenames); |
| 20082 | + sqlite3_free(zRenames); |
| 20083 | + } |
| 20084 | + assert(dbCols==0); |
| 20085 | + if( zColDefs==0 ){ |
| 19856 | 20086 | sqlite3_free(zCreate); |
| 19857 | 20087 | import_cleanup(&sCtx); |
| 19858 | 20088 | utf8_printf(stderr,"%s: empty file\n", sCtx.zFile); |
| 19859 | 20089 | rc = 1; |
| 19860 | 20090 | goto meta_command_exit; |
| 19861 | 20091 | } |
| 19862 | | - zCreate = sqlite3_mprintf("%z\n)", zCreate); |
| 20092 | + zCreate = sqlite3_mprintf("%z%z\n", zCreate, zColDefs); |
| 19863 | 20093 | if( eVerbose>=1 ){ |
| 19864 | 20094 | utf8_printf(p->out, "%s\n", zCreate); |
| 19865 | 20095 | } |
| 19866 | 20096 | rc = sqlite3_exec(p->db, zCreate, 0, 0, 0); |
| 19867 | 20097 | if( rc ){ |
| 19868 | 20098 | |