| | @@ -35,11 +35,11 @@ |
| 35 | 35 | ** ext/recover/sqlite3recover.h |
| 36 | 36 | ** src/shell.c.in |
| 37 | 37 | ** |
| 38 | 38 | ** To modify this program, get a copy of the canonical SQLite source tree, |
| 39 | 39 | ** edit the src/shell.c.in file and/or some of the other files that are |
| 40 | | -** listed above, then rerun the rerun "make shell.c". |
| 40 | +** listed above, then rerun the command "make shell.c". |
| 41 | 41 | */ |
| 42 | 42 | /************************* Begin src/shell.c.in ******************/ |
| 43 | 43 | /* |
| 44 | 44 | ** 2001 September 15 |
| 45 | 45 | ** |
| | @@ -49,11 +49,11 @@ |
| 49 | 49 | ** May you do good and not evil. |
| 50 | 50 | ** May you find forgiveness for yourself and forgive others. |
| 51 | 51 | ** May you share freely, never taking more than you give. |
| 52 | 52 | ** |
| 53 | 53 | ************************************************************************* |
| 54 | | -** This file contains code to implement the "sqlite" command line |
| 54 | +** This file contains code to implement the "sqlite3" command line |
| 55 | 55 | ** utility for accessing SQLite databases. |
| 56 | 56 | */ |
| 57 | 57 | #if (defined(_WIN32) || defined(WIN32)) && !defined(_CRT_SECURE_NO_WARNINGS) |
| 58 | 58 | /* This needs to come before any includes for MSVC compiler */ |
| 59 | 59 | #define _CRT_SECURE_NO_WARNINGS |
| | @@ -71,26 +71,22 @@ |
| 71 | 71 | # define SHELL_STRINGIFY(f) SHELL_STRINGIFY_(f) |
| 72 | 72 | #ifdef SQLITE_CUSTOM_INCLUDE |
| 73 | 73 | # include SHELL_STRINGIFY(SQLITE_CUSTOM_INCLUDE) |
| 74 | 74 | #endif |
| 75 | 75 | |
| 76 | | -/* |
| 77 | | -** Determine if we are dealing with WinRT, which provides only a subset of |
| 78 | | -** the full Win32 API. |
| 79 | | -*/ |
| 80 | | -#if !defined(SQLITE_OS_WINRT) |
| 81 | | -# define SQLITE_OS_WINRT 0 |
| 82 | | -#endif |
| 83 | | - |
| 84 | 76 | /* |
| 85 | 77 | ** If SQLITE_SHELL_FIDDLE is defined then the shell is modified |
| 86 | 78 | ** somewhat for use as a WASM module in a web browser. This flag |
| 87 | 79 | ** should only be used when building the "fiddle" web application, as |
| 88 | 80 | ** the browser-mode build has much different user input requirements |
| 89 | 81 | ** and this build mode rewires the user input subsystem to account for |
| 90 | 82 | ** that. |
| 91 | 83 | */ |
| 84 | +#if defined(SQLITE_SHELL_FIDDLE) |
| 85 | +# undef SQLITE_OMIT_LOAD_EXTENSION |
| 86 | +# define SQLITE_OMIT_LOAD_EXTENSION 1 |
| 87 | +#endif |
| 92 | 88 | |
| 93 | 89 | /* |
| 94 | 90 | ** Warning pragmas copied from msvc.h in the core. |
| 95 | 91 | */ |
| 96 | 92 | #if defined(_MSC_VER) |
| | @@ -218,13 +214,10 @@ |
| 218 | 214 | # define deliberate_fall_through |
| 219 | 215 | # endif |
| 220 | 216 | #endif |
| 221 | 217 | |
| 222 | 218 | #if defined(_WIN32) || defined(WIN32) |
| 223 | | -# if SQLITE_OS_WINRT |
| 224 | | -# define SQLITE_OMIT_POPEN 1 |
| 225 | | -# else |
| 226 | 219 | # include <io.h> |
| 227 | 220 | # include <fcntl.h> |
| 228 | 221 | # define isatty(h) _isatty(h) |
| 229 | 222 | # ifndef access |
| 230 | 223 | # define access(f,m) _access((f),(m)) |
| | @@ -235,11 +228,10 @@ |
| 235 | 228 | # ifndef strdup |
| 236 | 229 | # define strdup _strdup |
| 237 | 230 | # endif |
| 238 | 231 | # undef pclose |
| 239 | 232 | # define pclose _pclose |
| 240 | | -# endif |
| 241 | 233 | #else |
| 242 | 234 | /* Make sure isatty() has a prototype. */ |
| 243 | 235 | extern int isatty(int); |
| 244 | 236 | |
| 245 | 237 | # if !defined(__RTP__) && !defined(_WRS_KERNEL) && !defined(SQLITE_WASI) |
| | @@ -266,13 +258,10 @@ |
| 266 | 258 | #define ToLower(X) (char)tolower((unsigned char)X) |
| 267 | 259 | #define IsAlnum(X) isalnum((unsigned char)X) |
| 268 | 260 | #define IsAlpha(X) isalpha((unsigned char)X) |
| 269 | 261 | |
| 270 | 262 | #if defined(_WIN32) || defined(WIN32) |
| 271 | | -#if SQLITE_OS_WINRT |
| 272 | | -#include <intrin.h> |
| 273 | | -#endif |
| 274 | 263 | #undef WIN32_LEAN_AND_MEAN |
| 275 | 264 | #define WIN32_LEAN_AND_MEAN |
| 276 | 265 | #include <windows.h> |
| 277 | 266 | |
| 278 | 267 | /* string conversion routines only needed on Win32 */ |
| | @@ -704,13 +693,15 @@ |
| 704 | 693 | unsigned char bWordWrap; /* Try to wrap on word boundaries */ |
| 705 | 694 | unsigned char bTextJsonb; /* Render JSONB blobs as JSON text */ |
| 706 | 695 | unsigned char eDfltAlign; /* Default alignment, no covered by aAlignment */ |
| 707 | 696 | unsigned char eTitleAlign; /* Alignment for column headers */ |
| 708 | 697 | unsigned char bSplitColumn; /* Wrap single-column output into many columns */ |
| 698 | + unsigned char bBorder; /* Show outer border in Box and Table styles */ |
| 709 | 699 | short int nWrap; /* Wrap columns wider than this */ |
| 710 | 700 | short int nScreenWidth; /* Maximum overall table width */ |
| 711 | 701 | short int nLineLimit; /* Maximum number of lines for any row */ |
| 702 | + short int nTitleLimit; /* Maximum number of characters in a title */ |
| 712 | 703 | int nCharLimit; /* Maximum number of characters in a cell */ |
| 713 | 704 | int nWidth; /* Number of entries in aWidth[] */ |
| 714 | 705 | int nAlign; /* Number of entries in aAlignment[] */ |
| 715 | 706 | short int *aWidth; /* Column widths */ |
| 716 | 707 | unsigned char *aAlign; /* Column alignments */ |
| | @@ -775,10 +766,11 @@ |
| 775 | 766 | #define QRF_TEXT_Sql 2 /* Quote as an SQL literal */ |
| 776 | 767 | #define QRF_TEXT_Csv 3 /* CSV-style quoting */ |
| 777 | 768 | #define QRF_TEXT_Html 4 /* HTML-style quoting */ |
| 778 | 769 | #define QRF_TEXT_Tcl 5 /* C/Tcl quoting */ |
| 779 | 770 | #define QRF_TEXT_Json 6 /* JSON quoting */ |
| 771 | +#define QRF_TEXT_Relaxed 7 /* Relaxed SQL quoting */ |
| 780 | 772 | |
| 781 | 773 | /* |
| 782 | 774 | ** Quoting styles for BLOBs |
| 783 | 775 | ** Allowed values for sqlite3_qrf_spec.eBlob |
| 784 | 776 | */ |
| | @@ -842,17 +834,25 @@ |
| 842 | 834 | /* |
| 843 | 835 | ** Return an estimate of the width, in columns, for the single Unicode |
| 844 | 836 | ** character c. For normal characters, the answer is always 1. But the |
| 845 | 837 | ** estimate might be 0 or 2 for zero-width and double-width characters. |
| 846 | 838 | ** |
| 847 | | -** Different display devices display unicode using different widths. So |
| 839 | +** Different devices display unicode using different widths. So |
| 848 | 840 | ** it is impossible to know that true display width with 100% accuracy. |
| 849 | 841 | ** Inaccuracies in the width estimates might cause columns to be misaligned. |
| 850 | 842 | ** Unfortunately, there is nothing we can do about that. |
| 851 | 843 | */ |
| 852 | 844 | int sqlite3_qrf_wcwidth(int c); |
| 853 | 845 | |
| 846 | +/* |
| 847 | +** Return an estimate of the number of display columns used by the |
| 848 | +** string in the argument. The width of individual characters is |
| 849 | +** determined as for sqlite3_qrf_wcwidth(). VT100 escape code sequences |
| 850 | +** are assigned a width of zero. |
| 851 | +*/ |
| 852 | +size_t sqlite3_qrf_wcswidth(const char*); |
| 853 | + |
| 854 | 854 | |
| 855 | 855 | #ifdef __cplusplus |
| 856 | 856 | } |
| 857 | 857 | #endif |
| 858 | 858 | #endif /* !defined(SQLITE_QRF_H) */ |
| | @@ -915,11 +915,11 @@ |
| 915 | 915 | int mxWidth; /* Screen width */ |
| 916 | 916 | int mxHeight; /* nLineLimit */ |
| 917 | 917 | union { |
| 918 | 918 | struct { /* Content for QRF_STYLE_Line */ |
| 919 | 919 | int mxColWth; /* Maximum display width of any column */ |
| 920 | | - const char **azCol; /* Names of output columns (MODE_Line) */ |
| 920 | + char **azCol; /* Names of output columns (MODE_Line) */ |
| 921 | 921 | } sLine; |
| 922 | 922 | qrfEQPGraph *pGraph; /* EQP graph (Eqp, Stats, and StatsEst) */ |
| 923 | 923 | struct { /* Content for QRF_STYLE_Explain */ |
| 924 | 924 | int nIndent; /* Slots allocated for aiIndent */ |
| 925 | 925 | int iIndent; /* Current slot */ |
| | @@ -961,10 +961,19 @@ |
| 961 | 961 | #define qrfSpace(x) ((qrfCType[(unsigned char)x]&1)!=0) |
| 962 | 962 | #define qrfDigit(x) ((qrfCType[(unsigned char)x]&2)!=0) |
| 963 | 963 | #define qrfAlpha(x) ((qrfCType[(unsigned char)x]&4)!=0) |
| 964 | 964 | #define qrfAlnum(x) ((qrfCType[(unsigned char)x]&6)!=0) |
| 965 | 965 | |
| 966 | +#ifndef deliberate_fall_through |
| 967 | +/* Quiet some compilers about some of our intentional code. */ |
| 968 | +# if defined(GCC_VERSION) && GCC_VERSION>=7000000 |
| 969 | +# define deliberate_fall_through __attribute__((fallthrough)); |
| 970 | +# else |
| 971 | +# define deliberate_fall_through |
| 972 | +# endif |
| 973 | +#endif |
| 974 | + |
| 966 | 975 | /* |
| 967 | 976 | ** Set an error code and error message. |
| 968 | 977 | */ |
| 969 | 978 | static void qrfError( |
| 970 | 979 | Qrf *p, /* Query result state */ |
| | @@ -990,10 +999,19 @@ |
| 990 | 999 | */ |
| 991 | 1000 | static void qrfOom(Qrf *p){ |
| 992 | 1001 | qrfError(p, SQLITE_NOMEM, "out of memory"); |
| 993 | 1002 | } |
| 994 | 1003 | |
| 1004 | +/* |
| 1005 | +** Transfer any error in pStr over into p. |
| 1006 | +*/ |
| 1007 | +static void qrfStrErr(Qrf *p, sqlite3_str *pStr){ |
| 1008 | + int rc = pStr ? sqlite3_str_errcode(pStr) : 0; |
| 1009 | + if( rc ){ |
| 1010 | + qrfError(p, rc, sqlite3_errstr(rc)); |
| 1011 | + } |
| 1012 | +} |
| 995 | 1013 | |
| 996 | 1014 | |
| 997 | 1015 | /* |
| 998 | 1016 | ** Add a new entry to the EXPLAIN QUERY PLAN data |
| 999 | 1017 | */ |
| | @@ -1212,11 +1230,13 @@ |
| 1212 | 1230 | sqlite3_str_reset(pLine); |
| 1213 | 1231 | }else{ |
| 1214 | 1232 | qrfEqpAppend(p, iId, iPid, zo); |
| 1215 | 1233 | } |
| 1216 | 1234 | } |
| 1235 | + qrfStrErr(p, pLine); |
| 1217 | 1236 | sqlite3_free(sqlite3_str_finish(pLine)); |
| 1237 | + qrfStrErr(p, pStats); |
| 1218 | 1238 | sqlite3_free(sqlite3_str_finish(pStats)); |
| 1219 | 1239 | #endif |
| 1220 | 1240 | } |
| 1221 | 1241 | |
| 1222 | 1242 | |
| | @@ -1287,11 +1307,11 @@ |
| 1287 | 1307 | {1, 0x00ece}, {0, 0x00f18}, {1, 0x00f1a}, {0, 0x00f35}, {1, 0x00f36}, |
| 1288 | 1308 | {0, 0x00f37}, {1, 0x00f38}, {0, 0x00f39}, {1, 0x00f3a}, {0, 0x00f71}, |
| 1289 | 1309 | {1, 0x00f7f}, {0, 0x00f80}, {1, 0x00f85}, {0, 0x00f86}, {1, 0x00f88}, |
| 1290 | 1310 | {0, 0x00f90}, {1, 0x00f98}, {0, 0x00f99}, {1, 0x00fbd}, {0, 0x00fc6}, |
| 1291 | 1311 | {1, 0x00fc7}, {0, 0x0102d}, {1, 0x01031}, {0, 0x01032}, {1, 0x01033}, |
| 1292 | | - {0, 0x01036}, {1, 0x01038}, {0, 0x01039}, {1, 0x0103a}, {0, 0x01058}, |
| 1312 | + {0, 0x01036}, {1, 0x0103b}, {0, 0x01058}, |
| 1293 | 1313 | {1, 0x0105a}, {2, 0x01100}, {0, 0x01160}, {1, 0x01200}, {0, 0x0135f}, |
| 1294 | 1314 | {1, 0x01360}, {0, 0x01712}, {1, 0x01715}, {0, 0x01732}, {1, 0x01735}, |
| 1295 | 1315 | {0, 0x01752}, {1, 0x01754}, {0, 0x01772}, {1, 0x01774}, {0, 0x017b4}, |
| 1296 | 1316 | {1, 0x017b6}, {0, 0x017b7}, {1, 0x017be}, {0, 0x017c6}, {1, 0x017c7}, |
| 1297 | 1317 | {0, 0x017c9}, {1, 0x017d4}, {0, 0x017dd}, {1, 0x017de}, {0, 0x0180b}, |
| | @@ -1330,11 +1350,11 @@ |
| 1330 | 1350 | */ |
| 1331 | 1351 | int sqlite3_qrf_wcwidth(int c){ |
| 1332 | 1352 | int iFirst, iLast; |
| 1333 | 1353 | |
| 1334 | 1354 | /* Fast path for common characters */ |
| 1335 | | - if( c<=0x300 ) return 1; |
| 1355 | + if( c<0x300 ) return 1; |
| 1336 | 1356 | |
| 1337 | 1357 | /* The general case */ |
| 1338 | 1358 | iFirst = 0; |
| 1339 | 1359 | iLast = sizeof(aQrfUWidth)/sizeof(aQrfUWidth[0]) - 1; |
| 1340 | 1360 | while( iFirst<iLast-1 ){ |
| | @@ -1396,17 +1416,24 @@ |
| 1396 | 1416 | return i+1; |
| 1397 | 1417 | } |
| 1398 | 1418 | |
| 1399 | 1419 | /* |
| 1400 | 1420 | ** Return the length of a string in display characters. |
| 1401 | | -** Multibyte UTF8 characters count as a single character |
| 1402 | | -** for single-width characters, or as two characters for |
| 1403 | | -** double-width characters. |
| 1421 | +** |
| 1422 | +** Most characters of the input string count as 1, including |
| 1423 | +** multi-byte UTF8 characters. However, zero-width unicode |
| 1424 | +** characters and VT100 escape sequences count as zero, and |
| 1425 | +** double-width characters count as two. |
| 1426 | +** |
| 1427 | +** The definition of "zero-width" and "double-width" characters |
| 1428 | +** is not precise. It depends on the output device, to some extent, |
| 1429 | +** and it varies according to the Unicode version. This routine |
| 1430 | +** makes the best guess that it can. |
| 1404 | 1431 | */ |
| 1405 | | -static int qrfDisplayLength(const char *zIn){ |
| 1432 | +size_t sqlite3_qrf_wcswidth(const char *zIn){ |
| 1406 | 1433 | const unsigned char *z = (const unsigned char*)zIn; |
| 1407 | | - int n = 0; |
| 1434 | + size_t n = 0; |
| 1408 | 1435 | while( *z ){ |
| 1409 | 1436 | if( z[0]<' ' ){ |
| 1410 | 1437 | int k; |
| 1411 | 1438 | if( z[0]=='\033' && (k = qrfIsVt100(z))>0 ){ |
| 1412 | 1439 | z += k; |
| | @@ -1436,15 +1463,18 @@ |
| 1436 | 1463 | ** Write the number of newlines into *pnNL. So, *pnNL will |
| 1437 | 1464 | ** return 0 if everything fits on one line, or positive it |
| 1438 | 1465 | ** it will need to be split. |
| 1439 | 1466 | */ |
| 1440 | 1467 | static int qrfDisplayWidth(const char *zIn, sqlite3_int64 nByte, int *pnNL){ |
| 1441 | | - const unsigned char *z = (const unsigned char*)zIn; |
| 1442 | | - const unsigned char *zEnd = &z[nByte]; |
| 1468 | + const unsigned char *z; |
| 1469 | + const unsigned char *zEnd; |
| 1443 | 1470 | int mx = 0; |
| 1444 | 1471 | int n = 0; |
| 1445 | 1472 | int nNL = 0; |
| 1473 | + if( zIn==0 ) zIn = ""; |
| 1474 | + z = (const unsigned char*)zIn; |
| 1475 | + zEnd = &z[nByte]; |
| 1446 | 1476 | while( z<zEnd ){ |
| 1447 | 1477 | if( z[0]<' ' ){ |
| 1448 | 1478 | int k; |
| 1449 | 1479 | if( z[0]=='\033' && (k = qrfIsVt100(z))>0 ){ |
| 1450 | 1480 | z += k; |
| | @@ -1551,10 +1581,51 @@ |
| 1551 | 1581 | zOut[j++] = '^'; |
| 1552 | 1582 | zOut[j++] = 0x40+c; |
| 1553 | 1583 | } |
| 1554 | 1584 | } |
| 1555 | 1585 | } |
| 1586 | + |
| 1587 | +/* |
| 1588 | +** Determine if the string z[] can be shown as plain text. Return true |
| 1589 | +** if z[] is unambiguously text. Return false if z[] needs to be |
| 1590 | +** quoted. |
| 1591 | +** |
| 1592 | +** All of the following must be true in order for z[] to be relaxable: |
| 1593 | +** |
| 1594 | +** (1) z[] does not begin or end with ' or whitespace |
| 1595 | +** (2) z[] is not the same as the NULL rendering |
| 1596 | +** (3) z[] does not looks like a numeric literal |
| 1597 | +*/ |
| 1598 | +static int qrfRelaxable(Qrf *p, const char *z){ |
| 1599 | + size_t i, n; |
| 1600 | + if( z[0]=='\'' || qrfSpace(z[0]) ) return 0; |
| 1601 | + if( z[0]==0 ){ |
| 1602 | + return (p->spec.zNull!=0 && p->spec.zNull[0]!=0); |
| 1603 | + } |
| 1604 | + n = strlen(z); |
| 1605 | + if( n==0 || z[n-1]=='\'' || qrfSpace(z[n-1]) ) return 0; |
| 1606 | + if( p->spec.zNull && strcmp(p->spec.zNull,z)==0 ) return 0; |
| 1607 | + i = (z[0]=='-' || z[0]=='+'); |
| 1608 | + if( strcmp(z+i,"Inf")==0 ) return 0; |
| 1609 | + if( !qrfDigit(z[i]) ) return 1; |
| 1610 | + i++; |
| 1611 | + while( qrfDigit(z[i]) ){ i++; } |
| 1612 | + if( z[i]==0 ) return 0; |
| 1613 | + if( z[i]=='.' ){ |
| 1614 | + i++; |
| 1615 | + while( qrfDigit(z[i]) ){ i++; } |
| 1616 | + if( z[i]==0 ) return 0; |
| 1617 | + } |
| 1618 | + if( z[i]=='e' || z[i]=='E' ){ |
| 1619 | + i++; |
| 1620 | + if( z[i]=='+' || z[i]=='-' ){ i++; } |
| 1621 | + if( !qrfDigit(z[i]) ) return 1; |
| 1622 | + i++; |
| 1623 | + while( qrfDigit(z[i]) ){ i++; } |
| 1624 | + } |
| 1625 | + return z[i]!=0; |
| 1626 | +} |
| 1556 | 1627 | |
| 1557 | 1628 | /* |
| 1558 | 1629 | ** If a field contains any character identified by a 1 in the following |
| 1559 | 1630 | ** array, then the string must be quoted for CSV. |
| 1560 | 1631 | */ |
| | @@ -1581,10 +1652,16 @@ |
| 1581 | 1652 | ** Encode text appropriately and append it to pOut. |
| 1582 | 1653 | */ |
| 1583 | 1654 | static void qrfEncodeText(Qrf *p, sqlite3_str *pOut, const char *zTxt){ |
| 1584 | 1655 | int iStart = sqlite3_str_length(pOut); |
| 1585 | 1656 | switch( p->spec.eText ){ |
| 1657 | + case QRF_TEXT_Relaxed: |
| 1658 | + if( qrfRelaxable(p, zTxt) ){ |
| 1659 | + sqlite3_str_appendall(pOut, zTxt); |
| 1660 | + break; |
| 1661 | + } |
| 1662 | + deliberate_fall_through; /* FALLTHRU */ |
| 1586 | 1663 | case QRF_TEXT_Sql: { |
| 1587 | 1664 | if( p->spec.eEsc==QRF_ESC_Off ){ |
| 1588 | 1665 | sqlite3_str_appendf(pOut, "%Q", zTxt); |
| 1589 | 1666 | }else{ |
| 1590 | 1667 | sqlite3_str_appendf(pOut, "%#Q", zTxt); |
| | @@ -1739,10 +1816,57 @@ |
| 1739 | 1816 | return (const char*)sqlite3_column_text(p->pJTrans, 0); |
| 1740 | 1817 | }else{ |
| 1741 | 1818 | return 0; |
| 1742 | 1819 | } |
| 1743 | 1820 | } |
| 1821 | + |
| 1822 | +/* |
| 1823 | +** Adjust the input string zIn[] such that it is no more than N display |
| 1824 | +** characters wide. If it is wider than that, then truncate and add |
| 1825 | +** ellipsis. Or if zIn[] contains a \r or \n, truncate at that point, |
| 1826 | +** adding ellipsis. Embedded tabs in zIn[] are converted into ordinary |
| 1827 | +** spaces. |
| 1828 | +** |
| 1829 | +** Return this display width of the modified title string. |
| 1830 | +*/ |
| 1831 | +static int qrfTitleLimit(char *zIn, int N){ |
| 1832 | + unsigned char *z = (unsigned char*)zIn; |
| 1833 | + int n = 0; |
| 1834 | + unsigned char *zEllipsis = 0; |
| 1835 | + while( 1 /*exit-by-break*/ ){ |
| 1836 | + if( z[0]<' ' ){ |
| 1837 | + int k; |
| 1838 | + if( z[0]==0 ){ |
| 1839 | + zEllipsis = 0; |
| 1840 | + break; |
| 1841 | + }else if( z[0]=='\033' && (k = qrfIsVt100(z))>0 ){ |
| 1842 | + z += k; |
| 1843 | + }else if( z[0]=='\t' ){ |
| 1844 | + z[0] = ' '; |
| 1845 | + }else if( z[0]=='\n' || z[0]=='\r' ){ |
| 1846 | + z[0] = ' '; |
| 1847 | + }else{ |
| 1848 | + z++; |
| 1849 | + } |
| 1850 | + }else if( (0x80&z[0])==0 ){ |
| 1851 | + if( n>=(N-3) && zEllipsis==0 ) zEllipsis = z; |
| 1852 | + if( n==N ){ z[0] = 0; break; } |
| 1853 | + n++; |
| 1854 | + z++; |
| 1855 | + }else{ |
| 1856 | + int u = 0; |
| 1857 | + int len = sqlite3_qrf_decode_utf8(z, &u); |
| 1858 | + if( n+len>(N-3) && zEllipsis==0 ) zEllipsis = z; |
| 1859 | + if( n+len>N ){ z[0] = 0; break; } |
| 1860 | + z += len; |
| 1861 | + n += sqlite3_qrf_wcwidth(u); |
| 1862 | + } |
| 1863 | + } |
| 1864 | + if( zEllipsis && N>=3 ) memcpy(zEllipsis,"...",4); |
| 1865 | + return n; |
| 1866 | +} |
| 1867 | + |
| 1744 | 1868 | |
| 1745 | 1869 | /* |
| 1746 | 1870 | ** Render value pVal into pOut |
| 1747 | 1871 | */ |
| 1748 | 1872 | static void qrfRenderValue(Qrf *p, sqlite3_str *pOut, int iCol){ |
| | @@ -2124,40 +2248,10 @@ |
| 2124 | 2248 | } |
| 2125 | 2249 | } |
| 2126 | 2250 | sqlite3_str_append(pOut, (const char*)z, i); |
| 2127 | 2251 | } |
| 2128 | 2252 | |
| 2129 | | -/* |
| 2130 | | -** Output horizontally justified text into pOut. The text is the |
| 2131 | | -** first nVal bytes of zVal. Include nWS bytes of whitespace, either |
| 2132 | | -** split between both sides, or on the left, or on the right, depending |
| 2133 | | -** on eAlign. |
| 2134 | | -*/ |
| 2135 | | -static void qrfPrintAligned( |
| 2136 | | - sqlite3_str *pOut, /* Append text here */ |
| 2137 | | - const char *zVal, /* Text to append */ |
| 2138 | | - int nVal, /* Use only the first nVal bytes of zVal[] */ |
| 2139 | | - int nWS, /* Whitespace for horizonal alignment */ |
| 2140 | | - unsigned char eAlign /* Alignment type */ |
| 2141 | | -){ |
| 2142 | | - eAlign &= QRF_ALIGN_HMASK; |
| 2143 | | - if( eAlign==QRF_ALIGN_Center ){ |
| 2144 | | - /* Center the text */ |
| 2145 | | - sqlite3_str_appendchar(pOut, nWS/2, ' '); |
| 2146 | | - qrfAppendWithTabs(pOut, zVal, nVal); |
| 2147 | | - sqlite3_str_appendchar(pOut, nWS - nWS/2, ' '); |
| 2148 | | - }else if( eAlign==QRF_ALIGN_Right){ |
| 2149 | | - /* Right justify the text */ |
| 2150 | | - sqlite3_str_appendchar(pOut, nWS, ' '); |
| 2151 | | - qrfAppendWithTabs(pOut, zVal, nVal); |
| 2152 | | - }else{ |
| 2153 | | - /* Left justify the next */ |
| 2154 | | - qrfAppendWithTabs(pOut, zVal, nVal); |
| 2155 | | - sqlite3_str_appendchar(pOut, nWS, ' '); |
| 2156 | | - } |
| 2157 | | -} |
| 2158 | | - |
| 2159 | 2253 | /* |
| 2160 | 2254 | ** GCC does not define the offsetof() macro so we'll have to do it |
| 2161 | 2255 | ** ourselves. |
| 2162 | 2256 | */ |
| 2163 | 2257 | #ifndef offsetof |
| | @@ -2177,27 +2271,60 @@ |
| 2177 | 2271 | sqlite3_int64 nRow; /* Number of rows */ |
| 2178 | 2272 | sqlite3_int64 nAlloc; /* Number of cells allocated */ |
| 2179 | 2273 | sqlite3_int64 n; /* Number of cells. nCol*nRow */ |
| 2180 | 2274 | char **az; /* Content of all cells */ |
| 2181 | 2275 | int *aiWth; /* Width of each cell */ |
| 2276 | + unsigned char *abNum; /* True for each numeric cell */ |
| 2182 | 2277 | struct qrfPerCol { /* Per-column data */ |
| 2183 | 2278 | char *z; /* Cache of text for current row */ |
| 2184 | 2279 | int w; /* Computed width of this column */ |
| 2185 | 2280 | int mxW; /* Maximum natural (unwrapped) width */ |
| 2186 | 2281 | unsigned char e; /* Alignment */ |
| 2187 | 2282 | unsigned char fx; /* Width is fixed */ |
| 2283 | + unsigned char bNum; /* True if is numeric */ |
| 2188 | 2284 | } *a; /* One per column */ |
| 2189 | 2285 | }; |
| 2286 | + |
| 2287 | +/* |
| 2288 | +** Output horizontally justified text into pOut. The text is the |
| 2289 | +** first nVal bytes of zVal. Include nWS bytes of whitespace, either |
| 2290 | +** split between both sides, or on the left, or on the right, depending |
| 2291 | +** on eAlign. |
| 2292 | +*/ |
| 2293 | +static void qrfPrintAligned( |
| 2294 | + sqlite3_str *pOut, /* Append text here */ |
| 2295 | + struct qrfPerCol *pCol, /* Information about the text to print */ |
| 2296 | + int nVal, /* Use only the first nVal bytes of zVal[] */ |
| 2297 | + int nWS /* Whitespace for horizonal alignment */ |
| 2298 | +){ |
| 2299 | + unsigned char eAlign = pCol->e & QRF_ALIGN_HMASK; |
| 2300 | + if( eAlign==QRF_Auto && pCol->bNum ) eAlign = QRF_ALIGN_Right; |
| 2301 | + if( eAlign==QRF_ALIGN_Center ){ |
| 2302 | + /* Center the text */ |
| 2303 | + sqlite3_str_appendchar(pOut, nWS/2, ' '); |
| 2304 | + qrfAppendWithTabs(pOut, pCol->z, nVal); |
| 2305 | + sqlite3_str_appendchar(pOut, nWS - nWS/2, ' '); |
| 2306 | + }else if( eAlign==QRF_ALIGN_Right ){ |
| 2307 | + /* Right justify the text */ |
| 2308 | + sqlite3_str_appendchar(pOut, nWS, ' '); |
| 2309 | + qrfAppendWithTabs(pOut, pCol->z, nVal); |
| 2310 | + }else{ |
| 2311 | + /* Left justify the text */ |
| 2312 | + qrfAppendWithTabs(pOut, pCol->z, nVal); |
| 2313 | + sqlite3_str_appendchar(pOut, nWS, ' '); |
| 2314 | + } |
| 2315 | +} |
| 2190 | 2316 | |
| 2191 | 2317 | /* |
| 2192 | 2318 | ** Free all the memory allocates in the qrfColData object |
| 2193 | 2319 | */ |
| 2194 | 2320 | static void qrfColDataFree(qrfColData *p){ |
| 2195 | 2321 | sqlite3_int64 i; |
| 2196 | 2322 | for(i=0; i<p->n; i++) sqlite3_free(p->az[i]); |
| 2197 | 2323 | sqlite3_free(p->az); |
| 2198 | 2324 | sqlite3_free(p->aiWth); |
| 2325 | + sqlite3_free(p->abNum); |
| 2199 | 2326 | sqlite3_free(p->a); |
| 2200 | 2327 | memset(p, 0, sizeof(*p)); |
| 2201 | 2328 | } |
| 2202 | 2329 | |
| 2203 | 2330 | /* |
| | @@ -2205,10 +2332,11 @@ |
| 2205 | 2332 | ** Return non-zero if a memory allocation fails. |
| 2206 | 2333 | */ |
| 2207 | 2334 | static int qrfColDataEnlarge(qrfColData *p){ |
| 2208 | 2335 | char **azData; |
| 2209 | 2336 | int *aiWth; |
| 2337 | + unsigned char *abNum; |
| 2210 | 2338 | p->nAlloc = 2*p->nAlloc + 10*p->nCol; |
| 2211 | 2339 | azData = sqlite3_realloc64(p->az, p->nAlloc*sizeof(char*)); |
| 2212 | 2340 | if( azData==0 ){ |
| 2213 | 2341 | qrfOom(p->p); |
| 2214 | 2342 | qrfColDataFree(p); |
| | @@ -2220,26 +2348,38 @@ |
| 2220 | 2348 | qrfOom(p->p); |
| 2221 | 2349 | qrfColDataFree(p); |
| 2222 | 2350 | return 1; |
| 2223 | 2351 | } |
| 2224 | 2352 | p->aiWth = aiWth; |
| 2353 | + abNum = sqlite3_realloc64(p->abNum, p->nAlloc); |
| 2354 | + if( abNum==0 ){ |
| 2355 | + qrfOom(p->p); |
| 2356 | + qrfColDataFree(p); |
| 2357 | + return 1; |
| 2358 | + } |
| 2359 | + p->abNum = abNum; |
| 2225 | 2360 | return 0; |
| 2226 | 2361 | } |
| 2227 | 2362 | |
| 2228 | 2363 | /* |
| 2229 | 2364 | ** Print a markdown or table-style row separator using ascii-art |
| 2230 | 2365 | */ |
| 2231 | 2366 | static void qrfRowSeparator(sqlite3_str *pOut, qrfColData *p, char cSep){ |
| 2232 | 2367 | int i; |
| 2233 | 2368 | if( p->nCol>0 ){ |
| 2234 | | - sqlite3_str_append(pOut, &cSep, 1); |
| 2369 | + int useBorder = p->p->spec.bBorder!=QRF_No; |
| 2370 | + if( useBorder ){ |
| 2371 | + sqlite3_str_append(pOut, &cSep, 1); |
| 2372 | + } |
| 2235 | 2373 | sqlite3_str_appendchar(pOut, p->a[0].w+p->nMargin, '-'); |
| 2236 | 2374 | for(i=1; i<p->nCol; i++){ |
| 2237 | 2375 | sqlite3_str_append(pOut, &cSep, 1); |
| 2238 | 2376 | sqlite3_str_appendchar(pOut, p->a[i].w+p->nMargin, '-'); |
| 2239 | 2377 | } |
| 2240 | | - sqlite3_str_append(pOut, &cSep, 1); |
| 2378 | + if( useBorder ){ |
| 2379 | + sqlite3_str_append(pOut, &cSep, 1); |
| 2380 | + } |
| 2241 | 2381 | } |
| 2242 | 2382 | sqlite3_str_append(pOut, "\n", 1); |
| 2243 | 2383 | } |
| 2244 | 2384 | |
| 2245 | 2385 | /* |
| | @@ -2265,24 +2405,37 @@ |
| 2265 | 2405 | #define BOX_134 "\342\224\244" /* U+2524 -| */ |
| 2266 | 2406 | #define BOX_234 "\342\224\254" /* U+252c -,- */ |
| 2267 | 2407 | #define BOX_124 "\342\224\264" /* U+2534 -'- */ |
| 2268 | 2408 | #define BOX_1234 "\342\224\274" /* U+253c -|- */ |
| 2269 | 2409 | |
| 2410 | +/* Rounded corners: */ |
| 2411 | +#define BOX_R12 "\342\225\260" /* U+2570 '- */ |
| 2412 | +#define BOX_R23 "\342\225\255" /* U+256d ,- */ |
| 2413 | +#define BOX_R34 "\342\225\256" /* U+256e -, */ |
| 2414 | +#define BOX_R14 "\342\225\257" /* U+256f -' */ |
| 2415 | + |
| 2416 | +/* Doubled horizontal lines: */ |
| 2417 | +#define DBL_24 "\342\225\220" /* U+2550 === */ |
| 2418 | +#define DBL_123 "\342\225\236" /* U+255e |= */ |
| 2419 | +#define DBL_134 "\342\225\241" /* U+2561 =| */ |
| 2420 | +#define DBL_1234 "\342\225\252" /* U+256a =|= */ |
| 2421 | + |
| 2270 | 2422 | /* Draw horizontal line N characters long using unicode box |
| 2271 | 2423 | ** characters |
| 2272 | 2424 | */ |
| 2273 | | -static void qrfBoxLine(sqlite3_str *pOut, int N){ |
| 2274 | | - const char zDash[] = |
| 2275 | | - BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 |
| 2276 | | - BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24; |
| 2277 | | - const int nDash = sizeof(zDash) - 1; |
| 2425 | +static void qrfBoxLine(sqlite3_str *pOut, int N, int bDbl){ |
| 2426 | + const char *azDash[2] = { |
| 2427 | + BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24, |
| 2428 | + DBL_24 DBL_24 DBL_24 DBL_24 DBL_24 DBL_24 DBL_24 DBL_24 DBL_24 DBL_24 |
| 2429 | + };/* 0 1 2 3 4 5 6 7 8 9 */ |
| 2430 | + const int nDash = 30; |
| 2278 | 2431 | N *= 3; |
| 2279 | 2432 | while( N>nDash ){ |
| 2280 | | - sqlite3_str_append(pOut, zDash, nDash); |
| 2433 | + sqlite3_str_append(pOut, azDash[bDbl], nDash); |
| 2281 | 2434 | N -= nDash; |
| 2282 | 2435 | } |
| 2283 | | - sqlite3_str_append(pOut, zDash, N); |
| 2436 | + sqlite3_str_append(pOut, azDash[bDbl], N); |
| 2284 | 2437 | } |
| 2285 | 2438 | |
| 2286 | 2439 | /* |
| 2287 | 2440 | ** Draw a horizontal separator for a QRF_STYLE_Box table. |
| 2288 | 2441 | */ |
| | @@ -2289,21 +2442,27 @@ |
| 2289 | 2442 | static void qrfBoxSeparator( |
| 2290 | 2443 | sqlite3_str *pOut, |
| 2291 | 2444 | qrfColData *p, |
| 2292 | 2445 | const char *zSep1, |
| 2293 | 2446 | const char *zSep2, |
| 2294 | | - const char *zSep3 |
| 2447 | + const char *zSep3, |
| 2448 | + int bDbl |
| 2295 | 2449 | ){ |
| 2296 | 2450 | int i; |
| 2297 | 2451 | if( p->nCol>0 ){ |
| 2298 | | - sqlite3_str_appendall(pOut, zSep1); |
| 2299 | | - qrfBoxLine(pOut, p->a[0].w+p->nMargin); |
| 2452 | + int useBorder = p->p->spec.bBorder!=QRF_No; |
| 2453 | + if( useBorder ){ |
| 2454 | + sqlite3_str_appendall(pOut, zSep1); |
| 2455 | + } |
| 2456 | + qrfBoxLine(pOut, p->a[0].w+p->nMargin, bDbl); |
| 2300 | 2457 | for(i=1; i<p->nCol; i++){ |
| 2301 | 2458 | sqlite3_str_appendall(pOut, zSep2); |
| 2302 | | - qrfBoxLine(pOut, p->a[i].w+p->nMargin); |
| 2459 | + qrfBoxLine(pOut, p->a[i].w+p->nMargin, bDbl); |
| 2303 | 2460 | } |
| 2304 | | - sqlite3_str_appendall(pOut, zSep3); |
| 2461 | + if( useBorder ){ |
| 2462 | + sqlite3_str_appendall(pOut, zSep3); |
| 2463 | + } |
| 2305 | 2464 | } |
| 2306 | 2465 | sqlite3_str_append(pOut, "\n", 1); |
| 2307 | 2466 | } |
| 2308 | 2467 | |
| 2309 | 2468 | /* |
| | @@ -2382,10 +2541,11 @@ |
| 2382 | 2541 | static void qrfSplitColumn(qrfColData *pData, Qrf *p){ |
| 2383 | 2542 | int nCol = 1; |
| 2384 | 2543 | int *aw = 0; |
| 2385 | 2544 | char **az = 0; |
| 2386 | 2545 | int *aiWth = 0; |
| 2546 | + unsigned char *abNum = 0; |
| 2387 | 2547 | int nColNext = 2; |
| 2388 | 2548 | int w; |
| 2389 | 2549 | struct qrfPerCol *a = 0; |
| 2390 | 2550 | sqlite3_int64 nRow = 1; |
| 2391 | 2551 | sqlite3_int64 i; |
| | @@ -2419,35 +2579,47 @@ |
| 2419 | 2579 | if( a==0 ){ |
| 2420 | 2580 | sqlite3_free(az); |
| 2421 | 2581 | sqlite3_free(aiWth); |
| 2422 | 2582 | qrfOom(p); |
| 2423 | 2583 | return; |
| 2584 | + } |
| 2585 | + abNum = sqlite3_malloc64( nRow*nCol ); |
| 2586 | + if( abNum==0 ){ |
| 2587 | + sqlite3_free(az); |
| 2588 | + sqlite3_free(aiWth); |
| 2589 | + sqlite3_free(a); |
| 2590 | + qrfOom(p); |
| 2591 | + return; |
| 2424 | 2592 | } |
| 2425 | 2593 | for(i=0; i<pData->n; i++){ |
| 2426 | 2594 | sqlite3_int64 j = (i%nRow)*nCol + (i/nRow); |
| 2427 | 2595 | az[j] = pData->az[i]; |
| 2596 | + abNum[j]= pData->abNum[i]; |
| 2428 | 2597 | pData->az[i] = 0; |
| 2429 | 2598 | aiWth[j] = pData->aiWth[i]; |
| 2430 | 2599 | } |
| 2431 | 2600 | while( i<nRow*nCol ){ |
| 2432 | 2601 | sqlite3_int64 j = (i%nRow)*nCol + (i/nRow); |
| 2433 | 2602 | az[j] = sqlite3_mprintf(""); |
| 2434 | 2603 | if( az[j]==0 ) qrfOom(p); |
| 2435 | 2604 | aiWth[j] = 0; |
| 2605 | + abNum[j] = 0; |
| 2436 | 2606 | i++; |
| 2437 | 2607 | } |
| 2438 | 2608 | for(i=0; i<nCol; i++){ |
| 2439 | 2609 | a[i].fx = a[i].mxW = a[i].w = aw[i]; |
| 2440 | 2610 | a[i].e = pData->a[0].e; |
| 2441 | 2611 | } |
| 2442 | 2612 | sqlite3_free(pData->az); |
| 2443 | 2613 | sqlite3_free(pData->aiWth); |
| 2444 | 2614 | sqlite3_free(pData->a); |
| 2615 | + sqlite3_free(pData->abNum); |
| 2445 | 2616 | sqlite3_free(aw); |
| 2446 | 2617 | pData->az = az; |
| 2447 | 2618 | pData->aiWth = aiWth; |
| 2448 | 2619 | pData->a = a; |
| 2620 | + pData->abNum = abNum; |
| 2449 | 2621 | pData->nCol = nCol; |
| 2450 | 2622 | pData->n = pData->nAlloc = nRow*nCol; |
| 2451 | 2623 | for(i=w=0; i<nCol; i++) w += a[i].w; |
| 2452 | 2624 | pData->nMargin = (p->spec.nScreenWidth - w)/(nCol - 1); |
| 2453 | 2625 | if( pData->nMargin>5 ) pData->nMargin = 5; |
| | @@ -2467,10 +2639,11 @@ |
| 2467 | 2639 | if( p->spec.nScreenWidth==0 ) return; |
| 2468 | 2640 | if( p->spec.eStyle==QRF_STYLE_Column ){ |
| 2469 | 2641 | sepW = pData->nCol*2 - 2; |
| 2470 | 2642 | }else{ |
| 2471 | 2643 | sepW = pData->nCol*3 + 1; |
| 2644 | + if( p->spec.bBorder==QRF_No ) sepW -= 2; |
| 2472 | 2645 | } |
| 2473 | 2646 | nCol = pData->nCol; |
| 2474 | 2647 | for(i=sumW=0; i<nCol; i++) sumW += pData->a[i].w; |
| 2475 | 2648 | if( p->spec.nScreenWidth >= sumW+sepW ) return; |
| 2476 | 2649 | |
| | @@ -2478,10 +2651,11 @@ |
| 2478 | 2651 | pData->nMargin = 0; |
| 2479 | 2652 | if( p->spec.eStyle==QRF_STYLE_Column ){ |
| 2480 | 2653 | sepW = pData->nCol - 1; |
| 2481 | 2654 | }else{ |
| 2482 | 2655 | sepW = pData->nCol + 1; |
| 2656 | + if( p->spec.bBorder==QRF_No ) sepW -= 2; |
| 2483 | 2657 | } |
| 2484 | 2658 | targetW = p->spec.nScreenWidth - sepW; |
| 2485 | 2659 | |
| 2486 | 2660 | #define MIN_SQUOZE 8 |
| 2487 | 2661 | #define MIN_EX_SQUOZE 16 |
| | @@ -2550,10 +2724,11 @@ |
| 2550 | 2724 | } |
| 2551 | 2725 | |
| 2552 | 2726 | /* Initialize the data container */ |
| 2553 | 2727 | memset(&data, 0, sizeof(data)); |
| 2554 | 2728 | data.nCol = p->nCol; |
| 2729 | + data.p = p; |
| 2555 | 2730 | data.a = sqlite3_malloc64( nColumn*sizeof(struct qrfPerCol) ); |
| 2556 | 2731 | if( data.a==0 ){ |
| 2557 | 2732 | qrfOom(p); |
| 2558 | 2733 | return; |
| 2559 | 2734 | } |
| | @@ -2563,19 +2738,27 @@ |
| 2563 | 2738 | |
| 2564 | 2739 | /* Load the column header names and all cell content into data */ |
| 2565 | 2740 | if( p->spec.bTitles==QRF_Yes ){ |
| 2566 | 2741 | unsigned char saved_eText = p->spec.eText; |
| 2567 | 2742 | p->spec.eText = p->spec.eTitle; |
| 2743 | + memset(data.abNum, 0, nColumn); |
| 2568 | 2744 | for(i=0; i<nColumn; i++){ |
| 2569 | 2745 | const char *z = (const char*)sqlite3_column_name(p->pStmt,i); |
| 2570 | 2746 | int nNL = 0; |
| 2571 | 2747 | int n, w; |
| 2572 | 2748 | pStr = sqlite3_str_new(p->db); |
| 2573 | 2749 | qrfEncodeText(p, pStr, z ? z : ""); |
| 2574 | 2750 | n = sqlite3_str_length(pStr); |
| 2751 | + qrfStrErr(p, pStr); |
| 2575 | 2752 | z = data.az[data.n] = sqlite3_str_finish(pStr); |
| 2576 | | - data.aiWth[data.n] = w = qrfDisplayWidth(z, n, &nNL); |
| 2753 | + if( p->spec.nTitleLimit ){ |
| 2754 | + nNL = 0; |
| 2755 | + data.aiWth[data.n] = w = qrfTitleLimit(data.az[data.n], |
| 2756 | + p->spec.nTitleLimit ); |
| 2757 | + }else{ |
| 2758 | + data.aiWth[data.n] = w = qrfDisplayWidth(z, n, &nNL); |
| 2759 | + } |
| 2577 | 2760 | data.n++; |
| 2578 | 2761 | if( w>data.a[i].mxW ) data.a[i].mxW = w; |
| 2579 | 2762 | if( nNL ) data.bMultiRow = 1; |
| 2580 | 2763 | } |
| 2581 | 2764 | p->spec.eText = saved_eText; |
| | @@ -2587,14 +2770,17 @@ |
| 2587 | 2770 | } |
| 2588 | 2771 | for(i=0; i<nColumn; i++){ |
| 2589 | 2772 | char *z; |
| 2590 | 2773 | int nNL = 0; |
| 2591 | 2774 | int n, w; |
| 2775 | + int eType = sqlite3_column_type(p->pStmt,i); |
| 2592 | 2776 | pStr = sqlite3_str_new(p->db); |
| 2593 | 2777 | qrfRenderValue(p, pStr, i); |
| 2594 | 2778 | n = sqlite3_str_length(pStr); |
| 2779 | + qrfStrErr(p, pStr); |
| 2595 | 2780 | z = data.az[data.n] = sqlite3_str_finish(pStr); |
| 2781 | + data.abNum[data.n] = eType==SQLITE_INTEGER || eType==SQLITE_FLOAT; |
| 2596 | 2782 | data.aiWth[data.n] = w = qrfDisplayWidth(z, n, &nNL); |
| 2597 | 2783 | data.n++; |
| 2598 | 2784 | if( w>data.a[i].mxW ) data.a[i].mxW = w; |
| 2599 | 2785 | if( nNL ) data.bMultiRow = 1; |
| 2600 | 2786 | } |
| | @@ -2680,11 +2866,16 @@ |
| 2680 | 2866 | }else{ |
| 2681 | 2867 | rowStart = BOX_13; |
| 2682 | 2868 | colSep = BOX_13; |
| 2683 | 2869 | rowSep = BOX_13 "\n"; |
| 2684 | 2870 | } |
| 2685 | | - qrfBoxSeparator(p->pOut, &data, BOX_23, BOX_234, BOX_34); |
| 2871 | + if( p->spec.bBorder==QRF_No){ |
| 2872 | + rowStart += 3; |
| 2873 | + rowSep = "\n"; |
| 2874 | + }else{ |
| 2875 | + qrfBoxSeparator(p->pOut, &data, BOX_R23, BOX_234, BOX_R34, 0); |
| 2876 | + } |
| 2686 | 2877 | break; |
| 2687 | 2878 | case QRF_STYLE_Table: |
| 2688 | 2879 | if( data.nMargin ){ |
| 2689 | 2880 | rowStart = "| "; |
| 2690 | 2881 | colSep = " | "; |
| | @@ -2692,11 +2883,16 @@ |
| 2692 | 2883 | }else{ |
| 2693 | 2884 | rowStart = "|"; |
| 2694 | 2885 | colSep = "|"; |
| 2695 | 2886 | rowSep = "|\n"; |
| 2696 | 2887 | } |
| 2697 | | - qrfRowSeparator(p->pOut, &data, '+'); |
| 2888 | + if( p->spec.bBorder==QRF_No ){ |
| 2889 | + rowStart += 1; |
| 2890 | + rowSep = "\n"; |
| 2891 | + }else{ |
| 2892 | + qrfRowSeparator(p->pOut, &data, '+'); |
| 2893 | + } |
| 2698 | 2894 | break; |
| 2699 | 2895 | case QRF_STYLE_Column: { |
| 2700 | 2896 | static const char zSpace[] = " "; |
| 2701 | 2897 | rowStart = ""; |
| 2702 | 2898 | if( data.nMargin<2 ){ |
| | @@ -2724,20 +2920,32 @@ |
| 2724 | 2920 | szRowStart = (int)strlen(rowStart); |
| 2725 | 2921 | szRowSep = (int)strlen(rowSep); |
| 2726 | 2922 | szColSep = (int)strlen(colSep); |
| 2727 | 2923 | |
| 2728 | 2924 | bWW = (p->spec.bWordWrap==QRF_Yes && data.bMultiRow); |
| 2729 | | - bRTrim = (p->spec.eStyle==QRF_STYLE_Column); |
| 2730 | | - for(i=0; i<data.n; i+=nColumn){ |
| 2925 | + if( p->spec.eStyle==QRF_STYLE_Column |
| 2926 | + || (p->spec.bBorder==QRF_No |
| 2927 | + && (p->spec.eStyle==QRF_STYLE_Box || p->spec.eStyle==QRF_STYLE_Table) |
| 2928 | + ) |
| 2929 | + ){ |
| 2930 | + bRTrim = 1; |
| 2931 | + }else{ |
| 2932 | + bRTrim = 0; |
| 2933 | + } |
| 2934 | + for(i=0; i<data.n && sqlite3_str_errcode(p->pOut)==SQLITE_OK; i+=nColumn){ |
| 2731 | 2935 | int bMore; |
| 2732 | 2936 | int nRow = 0; |
| 2733 | 2937 | |
| 2734 | 2938 | /* Draw a single row of the table. This might be the title line |
| 2735 | 2939 | ** (if there is a title line) or a row in the body of the table. |
| 2736 | 2940 | ** The column number will be j. The row number is i/nColumn. |
| 2737 | 2941 | */ |
| 2738 | | - for(j=0; j<nColumn; j++){ data.a[j].z = data.az[i+j]; } |
| 2942 | + for(j=0; j<nColumn; j++){ |
| 2943 | + data.a[j].z = data.az[i+j]; |
| 2944 | + if( data.a[j].z==0 ) data.a[j].z = ""; |
| 2945 | + data.a[j].bNum = data.abNum[i+j]; |
| 2946 | + } |
| 2739 | 2947 | do{ |
| 2740 | 2948 | sqlite3_str_append(p->pOut, rowStart, szRowStart); |
| 2741 | 2949 | bMore = 0; |
| 2742 | 2950 | for(j=0; j<nColumn; j++){ |
| 2743 | 2951 | int nThis = 0; |
| | @@ -2744,11 +2952,11 @@ |
| 2744 | 2952 | int nWide = 0; |
| 2745 | 2953 | int iNext = 0; |
| 2746 | 2954 | int nWS; |
| 2747 | 2955 | qrfWrapLine(data.a[j].z, data.a[j].w, bWW, &nThis, &nWide, &iNext); |
| 2748 | 2956 | nWS = data.a[j].w - nWide; |
| 2749 | | - qrfPrintAligned(p->pOut, data.a[j].z, nThis, nWS, data.a[j].e); |
| 2957 | + qrfPrintAligned(p->pOut, &data.a[j], nThis, nWS); |
| 2750 | 2958 | data.a[j].z += iNext; |
| 2751 | 2959 | if( data.a[j].z[0]!=0 ){ |
| 2752 | 2960 | bMore = 1; |
| 2753 | 2961 | } |
| 2754 | 2962 | if( j<nColumn-1 ){ |
| | @@ -2766,11 +2974,12 @@ |
| 2766 | 2974 | if( data.a[j].z[0]==0 ){ |
| 2767 | 2975 | sqlite3_str_appendchar(p->pOut, data.a[j].w, ' '); |
| 2768 | 2976 | }else{ |
| 2769 | 2977 | int nE = 3; |
| 2770 | 2978 | if( nE>data.a[j].w ) nE = data.a[j].w; |
| 2771 | | - qrfPrintAligned(p->pOut, "...", nE, data.a[j].w-nE, data.a[j].e); |
| 2979 | + data.a[j].z = "..."; |
| 2980 | + qrfPrintAligned(p->pOut, &data.a[j], nE, data.a[j].w-nE); |
| 2772 | 2981 | } |
| 2773 | 2982 | if( j<nColumn-1 ){ |
| 2774 | 2983 | sqlite3_str_append(p->pOut, colSep, szColSep); |
| 2775 | 2984 | }else{ |
| 2776 | 2985 | if( bRTrim ) qrfRTrim(p->pOut); |
| | @@ -2794,12 +3003,14 @@ |
| 2794 | 3003 | qrfRowSeparator(p->pOut, &data, '+'); |
| 2795 | 3004 | } |
| 2796 | 3005 | break; |
| 2797 | 3006 | } |
| 2798 | 3007 | case QRF_STYLE_Box: { |
| 2799 | | - if( isTitleDataSeparator || data.bMultiRow ){ |
| 2800 | | - qrfBoxSeparator(p->pOut, &data, BOX_123, BOX_1234, BOX_134); |
| 3008 | + if( isTitleDataSeparator ){ |
| 3009 | + qrfBoxSeparator(p->pOut, &data, DBL_123, DBL_1234, DBL_134, 1); |
| 3010 | + }else if( data.bMultiRow ){ |
| 3011 | + qrfBoxSeparator(p->pOut, &data, BOX_123, BOX_1234, BOX_134, 0); |
| 2801 | 3012 | } |
| 2802 | 3013 | break; |
| 2803 | 3014 | } |
| 2804 | 3015 | case QRF_STYLE_Markdown: { |
| 2805 | 3016 | if( isTitleDataSeparator ){ |
| | @@ -2827,17 +3038,19 @@ |
| 2827 | 3038 | } |
| 2828 | 3039 | } |
| 2829 | 3040 | } |
| 2830 | 3041 | |
| 2831 | 3042 | /* Draw the line across the bottom of the table */ |
| 2832 | | - switch( p->spec.eStyle ){ |
| 2833 | | - case QRF_STYLE_Box: |
| 2834 | | - qrfBoxSeparator(p->pOut, &data, BOX_12, BOX_124, BOX_14); |
| 2835 | | - break; |
| 2836 | | - case QRF_STYLE_Table: |
| 2837 | | - qrfRowSeparator(p->pOut, &data, '+'); |
| 2838 | | - break; |
| 3043 | + if( p->spec.bBorder!=QRF_No ){ |
| 3044 | + switch( p->spec.eStyle ){ |
| 3045 | + case QRF_STYLE_Box: |
| 3046 | + qrfBoxSeparator(p->pOut, &data, BOX_R12, BOX_124, BOX_R14, 0); |
| 3047 | + break; |
| 3048 | + case QRF_STYLE_Table: |
| 3049 | + qrfRowSeparator(p->pOut, &data, '+'); |
| 3050 | + break; |
| 3051 | + } |
| 2839 | 3052 | } |
| 2840 | 3053 | qrfWrite(p); |
| 2841 | 3054 | |
| 2842 | 3055 | qrfColDataFree(&data); |
| 2843 | 3056 | return; |
| | @@ -2903,11 +3116,11 @@ |
| 2903 | 3116 | assert( 0==sqlite3_stricmp( sqlite3_column_name(p->pStmt, 0), "addr" ) ); |
| 2904 | 3117 | assert( 0==sqlite3_stricmp( sqlite3_column_name(p->pStmt, 1), "opcode" ) ); |
| 2905 | 3118 | assert( 0==sqlite3_stricmp( sqlite3_column_name(p->pStmt, 2), "p1" ) ); |
| 2906 | 3119 | assert( 0==sqlite3_stricmp( sqlite3_column_name(p->pStmt, 3), "p2" ) ); |
| 2907 | 3120 | |
| 2908 | | - for(iOp=0; SQLITE_ROW==sqlite3_step(p->pStmt); iOp++){ |
| 3121 | + for(iOp=0; SQLITE_ROW==sqlite3_step(p->pStmt) && !p->iErr; iOp++){ |
| 2909 | 3122 | int iAddr = sqlite3_column_int(p->pStmt, 0); |
| 2910 | 3123 | const char *zOp = (const char*)sqlite3_column_text(p->pStmt, 1); |
| 2911 | 3124 | int p1 = sqlite3_column_int(p->pStmt, 2); |
| 2912 | 3125 | int p2 = sqlite3_column_int(p->pStmt, 3); |
| 2913 | 3126 | |
| | @@ -2960,11 +3173,11 @@ |
| 2960 | 3173 | nWidth = sizeof(aScanExpWidth)/sizeof(int); |
| 2961 | 3174 | iIndent = 3; |
| 2962 | 3175 | } |
| 2963 | 3176 | if( nArg>nWidth ) nArg = nWidth; |
| 2964 | 3177 | |
| 2965 | | - for(iOp=0; sqlite3_step(p->pStmt)==SQLITE_ROW; iOp++){ |
| 3178 | + for(iOp=0; sqlite3_step(p->pStmt)==SQLITE_ROW && !p->iErr; iOp++){ |
| 2966 | 3179 | /* If this is the first row seen, print out the headers */ |
| 2967 | 3180 | if( iOp==0 ){ |
| 2968 | 3181 | for(i=0; i<nArg; i++){ |
| 2969 | 3182 | const char *zCol = sqlite3_column_name(p->pStmt, aMap[i]); |
| 2970 | 3183 | qrfWidthPrint(p,p->pOut, aWidth[i], zCol); |
| | @@ -2989,11 +3202,11 @@ |
| 2989 | 3202 | int w = aWidth[i]; |
| 2990 | 3203 | const char *zVal = (const char*)sqlite3_column_text(p->pStmt, aMap[i]); |
| 2991 | 3204 | int len; |
| 2992 | 3205 | if( i==nArg-1 ) w = 0; |
| 2993 | 3206 | if( zVal==0 ) zVal = ""; |
| 2994 | | - len = qrfDisplayLength(zVal); |
| 3207 | + len = (int)sqlite3_qrf_wcswidth(zVal); |
| 2995 | 3208 | if( len>w ){ |
| 2996 | 3209 | w = len; |
| 2997 | 3210 | zSep = " "; |
| 2998 | 3211 | } |
| 2999 | 3212 | if( i==iIndent && aiIndent && iOp<nIndent ){ |
| | @@ -3168,34 +3381,40 @@ |
| 3168 | 3381 | } |
| 3169 | 3382 | case QRF_STYLE_Line: { |
| 3170 | 3383 | sqlite3_str *pVal; |
| 3171 | 3384 | int mxW; |
| 3172 | 3385 | int bWW; |
| 3386 | + int nSep; |
| 3173 | 3387 | if( p->u.sLine.azCol==0 ){ |
| 3174 | 3388 | p->u.sLine.azCol = sqlite3_malloc64( p->nCol*sizeof(char*) ); |
| 3175 | 3389 | if( p->u.sLine.azCol==0 ){ |
| 3176 | 3390 | qrfOom(p); |
| 3177 | 3391 | break; |
| 3178 | 3392 | } |
| 3179 | 3393 | p->u.sLine.mxColWth = 0; |
| 3180 | 3394 | for(i=0; i<p->nCol; i++){ |
| 3181 | 3395 | int sz; |
| 3182 | | - p->u.sLine.azCol[i] = sqlite3_column_name(p->pStmt, i); |
| 3183 | | - if( p->u.sLine.azCol[i]==0 ) p->u.sLine.azCol[i] = "unknown"; |
| 3184 | | - sz = qrfDisplayLength(p->u.sLine.azCol[i]); |
| 3396 | + const char *zCName = sqlite3_column_name(p->pStmt, i); |
| 3397 | + if( zCName==0 ) zCName = "unknown"; |
| 3398 | + p->u.sLine.azCol[i] = sqlite3_mprintf("%s", zCName); |
| 3399 | + if( p->spec.nTitleLimit>0 ){ |
| 3400 | + (void)qrfTitleLimit(p->u.sLine.azCol[i], p->spec.nTitleLimit); |
| 3401 | + } |
| 3402 | + sz = (int)sqlite3_qrf_wcswidth(p->u.sLine.azCol[i]); |
| 3185 | 3403 | if( sz > p->u.sLine.mxColWth ) p->u.sLine.mxColWth = sz; |
| 3186 | 3404 | } |
| 3187 | 3405 | } |
| 3188 | 3406 | if( p->nRow ) sqlite3_str_append(p->pOut, "\n", 1); |
| 3189 | 3407 | pVal = sqlite3_str_new(p->db); |
| 3190 | | - mxW = p->mxWidth - (3 + p->u.sLine.mxColWth); |
| 3408 | + nSep = (int)strlen(p->spec.zColumnSep); |
| 3409 | + mxW = p->mxWidth - (nSep + p->u.sLine.mxColWth); |
| 3191 | 3410 | bWW = p->spec.bWordWrap==QRF_Yes; |
| 3192 | 3411 | for(i=0; i<p->nCol; i++){ |
| 3193 | 3412 | const char *zVal; |
| 3194 | 3413 | int cnt = 0; |
| 3195 | 3414 | qrfWidthPrint(p, p->pOut, -p->u.sLine.mxColWth, p->u.sLine.azCol[i]); |
| 3196 | | - sqlite3_str_append(p->pOut, " = ", 3); |
| 3415 | + sqlite3_str_append(p->pOut, p->spec.zColumnSep, nSep); |
| 3197 | 3416 | qrfRenderValue(p, pVal, i); |
| 3198 | 3417 | zVal = sqlite3_str_value(pVal); |
| 3199 | 3418 | if( zVal==0 ) zVal = ""; |
| 3200 | 3419 | do{ |
| 3201 | 3420 | int nThis, nWide, iNext; |
| | @@ -3210,10 +3429,11 @@ |
| 3210 | 3429 | sqlite3_str_append(p->pOut, "\n", 1); |
| 3211 | 3430 | zVal += iNext; |
| 3212 | 3431 | }while( zVal[0] ); |
| 3213 | 3432 | sqlite3_str_reset(pVal); |
| 3214 | 3433 | } |
| 3434 | + qrfStrErr(p, pVal); |
| 3215 | 3435 | sqlite3_free(sqlite3_str_finish(pVal)); |
| 3216 | 3436 | qrfWrite(p); |
| 3217 | 3437 | break; |
| 3218 | 3438 | } |
| 3219 | 3439 | case QRF_STYLE_Eqp: { |
| | @@ -3273,11 +3493,11 @@ |
| 3273 | 3493 | p->pOut = sqlite3_str_new(p->db); |
| 3274 | 3494 | if( p->pOut==0 ){ |
| 3275 | 3495 | qrfOom(p); |
| 3276 | 3496 | return; |
| 3277 | 3497 | } |
| 3278 | | - p->iErr = 0; |
| 3498 | + p->iErr = SQLITE_OK; |
| 3279 | 3499 | p->nCol = sqlite3_column_count(p->pStmt); |
| 3280 | 3500 | p->nRow = 0; |
| 3281 | 3501 | sz = sizeof(sqlite3_qrf_spec); |
| 3282 | 3502 | memcpy(&p->spec, pSpec, sz); |
| 3283 | 3503 | if( p->spec.zNull==0 ) p->spec.zNull = ""; |
| | @@ -3285,12 +3505,12 @@ |
| 3285 | 3505 | if( p->mxWidth<=0 ) p->mxWidth = QRF_MAX_WIDTH; |
| 3286 | 3506 | p->mxHeight = p->spec.nLineLimit; |
| 3287 | 3507 | if( p->mxHeight<=0 ) p->mxHeight = 2147483647; |
| 3288 | 3508 | if( p->spec.eStyle>QRF_STYLE_Table ) p->spec.eStyle = QRF_Auto; |
| 3289 | 3509 | if( p->spec.eEsc>QRF_ESC_Symbol ) p->spec.eEsc = QRF_Auto; |
| 3290 | | - if( p->spec.eText>QRF_TEXT_Json ) p->spec.eText = QRF_Auto; |
| 3291 | | - if( p->spec.eTitle>QRF_TEXT_Json ) p->spec.eTitle = QRF_Auto; |
| 3510 | + if( p->spec.eText>QRF_TEXT_Relaxed ) p->spec.eText = QRF_Auto; |
| 3511 | + if( p->spec.eTitle>QRF_TEXT_Relaxed ) p->spec.eTitle = QRF_Auto; |
| 3292 | 3512 | if( p->spec.eBlob>QRF_BLOB_Size ) p->spec.eBlob = QRF_Auto; |
| 3293 | 3513 | qrf_reinit: |
| 3294 | 3514 | switch( p->spec.eStyle ){ |
| 3295 | 3515 | case QRF_Auto: { |
| 3296 | 3516 | switch( sqlite3_stmt_isexplain(pStmt) ){ |
| | @@ -3321,10 +3541,16 @@ |
| 3321 | 3541 | p->spec.zNull = "NULL"; |
| 3322 | 3542 | if( p->spec.zTableName==0 || p->spec.zTableName[0]==0 ){ |
| 3323 | 3543 | p->spec.zTableName = "tab"; |
| 3324 | 3544 | } |
| 3325 | 3545 | break; |
| 3546 | + } |
| 3547 | + case QRF_STYLE_Line: { |
| 3548 | + if( p->spec.zColumnSep==0 ){ |
| 3549 | + p->spec.zColumnSep = ": "; |
| 3550 | + } |
| 3551 | + break; |
| 3326 | 3552 | } |
| 3327 | 3553 | case QRF_STYLE_Csv: { |
| 3328 | 3554 | p->spec.eStyle = QRF_STYLE_List; |
| 3329 | 3555 | p->spec.eText = QRF_TEXT_Csv; |
| 3330 | 3556 | p->spec.zColumnSep = ","; |
| | @@ -3430,11 +3656,15 @@ |
| 3430 | 3656 | qrfWrite(p); |
| 3431 | 3657 | } |
| 3432 | 3658 | break; |
| 3433 | 3659 | } |
| 3434 | 3660 | case QRF_STYLE_Line: { |
| 3435 | | - if( p->u.sLine.azCol ) sqlite3_free(p->u.sLine.azCol); |
| 3661 | + if( p->u.sLine.azCol ){ |
| 3662 | + int i; |
| 3663 | + for(i=0; i<p->nCol; i++) sqlite3_free(p->u.sLine.azCol[i]); |
| 3664 | + sqlite3_free(p->u.sLine.azCol); |
| 3665 | + } |
| 3436 | 3666 | break; |
| 3437 | 3667 | } |
| 3438 | 3668 | case QRF_STYLE_Stats: |
| 3439 | 3669 | case QRF_STYLE_StatsEst: |
| 3440 | 3670 | case QRF_STYLE_Eqp: { |
| | @@ -3441,10 +3671,11 @@ |
| 3441 | 3671 | qrfEqpRender(p, 0); |
| 3442 | 3672 | qrfWrite(p); |
| 3443 | 3673 | break; |
| 3444 | 3674 | } |
| 3445 | 3675 | } |
| 3676 | + qrfStrErr(p, p->pOut); |
| 3446 | 3677 | if( p->spec.pzOutput ){ |
| 3447 | 3678 | if( p->spec.pzOutput[0] ){ |
| 3448 | 3679 | sqlite3_int64 n, sz; |
| 3449 | 3680 | char *zCombined; |
| 3450 | 3681 | sz = strlen(p->spec.pzOutput[0]); |
| | @@ -3624,11 +3855,11 @@ |
| 3624 | 3855 | |
| 3625 | 3856 | /* Return the current wall-clock time in microseconds since the |
| 3626 | 3857 | ** Unix epoch (1970-01-01T00:00:00Z) |
| 3627 | 3858 | */ |
| 3628 | 3859 | static sqlite3_int64 timeOfDay(void){ |
| 3629 | | -#if defined(_WIN64) |
| 3860 | +#if defined(_WIN64) && _WIN32_WINNT >= _WIN32_WINNT_WIN8 |
| 3630 | 3861 | sqlite3_uint64 t; |
| 3631 | 3862 | FILETIME tm; |
| 3632 | 3863 | GetSystemTimePreciseAsFileTime(&tm); |
| 3633 | 3864 | t = ((u64)tm.dwHighDateTime<<32) | (u64)tm.dwLowDateTime; |
| 3634 | 3865 | t += 116444736000000000LL; |
| | @@ -3724,11 +3955,10 @@ |
| 3724 | 3955 | */ |
| 3725 | 3956 | static int hasTimer(void){ |
| 3726 | 3957 | if( getProcessTimesAddr ){ |
| 3727 | 3958 | return 1; |
| 3728 | 3959 | } else { |
| 3729 | | -#if !SQLITE_OS_WINRT |
| 3730 | 3960 | /* GetProcessTimes() isn't supported in WIN95 and some other Windows |
| 3731 | 3961 | ** versions. See if the version we are running on has it, and if it |
| 3732 | 3962 | ** does, save off a pointer to it and the current process handle. |
| 3733 | 3963 | */ |
| 3734 | 3964 | hProcess = GetCurrentProcess(); |
| | @@ -3741,11 +3971,10 @@ |
| 3741 | 3971 | return 1; |
| 3742 | 3972 | } |
| 3743 | 3973 | FreeLibrary(hinstLib); |
| 3744 | 3974 | } |
| 3745 | 3975 | } |
| 3746 | | -#endif |
| 3747 | 3976 | } |
| 3748 | 3977 | return 0; |
| 3749 | 3978 | } |
| 3750 | 3979 | |
| 3751 | 3980 | /* |
| | @@ -3860,10 +4089,18 @@ |
| 3860 | 4089 | #define PROMPT_LEN_MAX 128 |
| 3861 | 4090 | /* First line prompt. default: "sqlite> " */ |
| 3862 | 4091 | static char mainPrompt[PROMPT_LEN_MAX]; |
| 3863 | 4092 | /* Continuation prompt. default: " ...> " */ |
| 3864 | 4093 | static char continuePrompt[PROMPT_LEN_MAX]; |
| 4094 | + |
| 4095 | +/* |
| 4096 | +** Write I/O traces to the following stream. |
| 4097 | +*/ |
| 4098 | +#ifdef SQLITE_ENABLE_IOTRACE |
| 4099 | +static FILE *iotrace = 0; |
| 4100 | +#endif |
| 4101 | + |
| 3865 | 4102 | |
| 3866 | 4103 | /* This is variant of the standard-library strncpy() routine with the |
| 3867 | 4104 | ** one change that the destination string is always zero-terminated, even |
| 3868 | 4105 | ** if there is no zero-terminator in the first n-1 characters of the source |
| 3869 | 4106 | ** string. |
| | @@ -3980,17 +4217,10 @@ |
| 3980 | 4217 | */ |
| 3981 | 4218 | static void shell_check_oom(const void *p){ |
| 3982 | 4219 | if( p==0 ) shell_out_of_memory(); |
| 3983 | 4220 | } |
| 3984 | 4221 | |
| 3985 | | -/* |
| 3986 | | -** Write I/O traces to the following stream. |
| 3987 | | -*/ |
| 3988 | | -#ifdef SQLITE_ENABLE_IOTRACE |
| 3989 | | -static FILE *iotrace = 0; |
| 3990 | | -#endif |
| 3991 | | - |
| 3992 | 4222 | /* |
| 3993 | 4223 | ** This routine works like printf in that its first argument is a |
| 3994 | 4224 | ** format string and subsequent arguments are values to be substituted |
| 3995 | 4225 | ** in place of % fields. The result of formatting this string |
| 3996 | 4226 | ** is written to iotrace. |
| | @@ -6013,17 +6243,20 @@ |
| 6013 | 6243 | } |
| 6014 | 6244 | /* End of the hashing logic |
| 6015 | 6245 | *****************************************************************************/ |
| 6016 | 6246 | |
| 6017 | 6247 | /* |
| 6018 | | -** Implementation of the sha1(X) function. |
| 6248 | +** Two SQL functions: sha1(X) and sha1b(X). |
| 6019 | 6249 | ** |
| 6020 | | -** Return a lower-case hexadecimal rendering of the SHA1 hash of the |
| 6021 | | -** argument X. If X is a BLOB, it is hashed as is. For all other |
| 6250 | +** sha1(X) returns a lower-case hexadecimal rendering of the SHA1 hash |
| 6251 | +** of the argument X. If X is a BLOB, it is hashed as is. For all other |
| 6022 | 6252 | ** types of input, X is converted into a UTF-8 string and the string |
| 6023 | | -** is hash without the trailing 0x00 terminator. The hash of a NULL |
| 6253 | +** is hashed without the trailing 0x00 terminator. The hash of a NULL |
| 6024 | 6254 | ** value is NULL. |
| 6255 | +** |
| 6256 | +** sha1b(X) is the same except that it returns a 20-byte BLOB containing |
| 6257 | +** the binary hash instead of a hexadecimal string. |
| 6025 | 6258 | */ |
| 6026 | 6259 | static void sha1Func( |
| 6027 | 6260 | sqlite3_context *context, |
| 6028 | 6261 | int argc, |
| 6029 | 6262 | sqlite3_value **argv |
| | @@ -6040,15 +6273,17 @@ |
| 6040 | 6273 | hash_step(&cx, sqlite3_value_blob(argv[0]), nByte); |
| 6041 | 6274 | }else{ |
| 6042 | 6275 | hash_step(&cx, sqlite3_value_text(argv[0]), nByte); |
| 6043 | 6276 | } |
| 6044 | 6277 | if( sqlite3_user_data(context)!=0 ){ |
| 6278 | + /* sha1b() - binary result */ |
| 6045 | 6279 | hash_finish(&cx, zOut, 1); |
| 6046 | 6280 | sqlite3_result_blob(context, zOut, 20, SQLITE_TRANSIENT); |
| 6047 | 6281 | }else{ |
| 6282 | + /* sha1() - hexadecimal text result */ |
| 6048 | 6283 | hash_finish(&cx, zOut, 0); |
| 6049 | | - sqlite3_result_blob(context, zOut, 40, SQLITE_TRANSIENT); |
| 6284 | + sqlite3_result_text(context, zOut, 40, SQLITE_TRANSIENT); |
| 6050 | 6285 | } |
| 6051 | 6286 | } |
| 6052 | 6287 | |
| 6053 | 6288 | /* |
| 6054 | 6289 | ** Implementation of the sha1_query(SQL) function. |
| | @@ -8136,13 +8371,13 @@ |
| 8136 | 8371 | }else if( e<-10000 ){ |
| 8137 | 8372 | e = -10000; |
| 8138 | 8373 | } |
| 8139 | 8374 | |
| 8140 | 8375 | if( m<0 ){ |
| 8376 | + if( m<(-9223372036854775807LL) ) return; |
| 8141 | 8377 | isNeg = 1; |
| 8142 | 8378 | m = -m; |
| 8143 | | - if( m<0 ) return; |
| 8144 | 8379 | }else if( m==0 && e>-1000 && e<1000 ){ |
| 8145 | 8380 | sqlite3_result_double(context, 0.0); |
| 8146 | 8381 | return; |
| 8147 | 8382 | } |
| 8148 | 8383 | while( (m>>32)&0xffe00000 ){ |
| | @@ -9259,11 +9494,11 @@ |
| 9259 | 9494 | ** (X) match X |
| 9260 | 9495 | ** X|Y X or Y |
| 9261 | 9496 | ** ^X X occurring at the beginning of the string |
| 9262 | 9497 | ** X$ X occurring at the end of the string |
| 9263 | 9498 | ** . Match any single character |
| 9264 | | -** \c Character c where c is one of \{}()[]|*+?. |
| 9499 | +** \c Character c where c is one of \{}()[]|*+?-. |
| 9265 | 9500 | ** \c C-language escapes for c in afnrtv. ex: \t or \n |
| 9266 | 9501 | ** \uXXXX Where XXXX is exactly 4 hex digits, unicode value XXXX |
| 9267 | 9502 | ** \xXX Where XX is exactly 2 hex digits, unicode value XX |
| 9268 | 9503 | ** [abc] Any single character from the set abc |
| 9269 | 9504 | ** [^abc] Any single character not in the set abc |
| | @@ -9644,11 +9879,11 @@ |
| 9644 | 9879 | |
| 9645 | 9880 | /* A backslash character has been seen, read the next character and |
| 9646 | 9881 | ** return its interpretation. |
| 9647 | 9882 | */ |
| 9648 | 9883 | static unsigned re_esc_char(ReCompiled *p){ |
| 9649 | | - static const char zEsc[] = "afnrtv\\()*.+?[$^{|}]"; |
| 9884 | + static const char zEsc[] = "afnrtv\\()*.+?[$^{|}]-"; |
| 9650 | 9885 | static const char zTrans[] = "\a\f\n\r\t\v"; |
| 9651 | 9886 | int i, v = 0; |
| 9652 | 9887 | char c; |
| 9653 | 9888 | if( p->sIn.i>=p->sIn.mx ) return 0; |
| 9654 | 9889 | c = p->sIn.z[p->sIn.i]; |
| | @@ -9967,15 +10202,22 @@ |
| 9967 | 10202 | } |
| 9968 | 10203 | return pRe->zErr; |
| 9969 | 10204 | } |
| 9970 | 10205 | |
| 9971 | 10206 | /* |
| 9972 | | -** Compute a reasonable limit on the length of the REGEXP NFA. |
| 10207 | +** The value of LIMIT_MAX_PATTERN_LENGTH. |
| 9973 | 10208 | */ |
| 9974 | 10209 | static int re_maxlen(sqlite3_context *context){ |
| 9975 | 10210 | sqlite3 *db = sqlite3_context_db_handle(context); |
| 9976 | | - return 75 + sqlite3_limit(db, SQLITE_LIMIT_LIKE_PATTERN_LENGTH,-1)/2; |
| 10211 | + return sqlite3_limit(db, SQLITE_LIMIT_LIKE_PATTERN_LENGTH,-1); |
| 10212 | +} |
| 10213 | + |
| 10214 | +/* |
| 10215 | +** Maximum NFA size given a maximum pattern length. |
| 10216 | +*/ |
| 10217 | +static int re_maxnfa(int mxlen){ |
| 10218 | + return 75+mxlen/2; |
| 9977 | 10219 | } |
| 9978 | 10220 | |
| 9979 | 10221 | /* |
| 9980 | 10222 | ** Implementation of the regexp() SQL function. This function implements |
| 9981 | 10223 | ** the build-in REGEXP operator. The first argument to the function is the |
| | @@ -9997,14 +10239,21 @@ |
| 9997 | 10239 | int setAux = 0; /* True to invoke sqlite3_set_auxdata() */ |
| 9998 | 10240 | |
| 9999 | 10241 | (void)argc; /* Unused */ |
| 10000 | 10242 | pRe = sqlite3_get_auxdata(context, 0); |
| 10001 | 10243 | if( pRe==0 ){ |
| 10244 | + int mxLen = re_maxlen(context); |
| 10245 | + int nPattern; |
| 10002 | 10246 | zPattern = (const char*)sqlite3_value_text(argv[0]); |
| 10003 | 10247 | if( zPattern==0 ) return; |
| 10004 | | - zErr = re_compile(&pRe, zPattern, re_maxlen(context), |
| 10005 | | - sqlite3_user_data(context)!=0); |
| 10248 | + nPattern = sqlite3_value_bytes(argv[0]); |
| 10249 | + if( nPattern>mxLen ){ |
| 10250 | + zErr = "REGEXP pattern too big"; |
| 10251 | + }else{ |
| 10252 | + zErr = re_compile(&pRe, zPattern, re_maxnfa(mxLen), |
| 10253 | + sqlite3_user_data(context)!=0); |
| 10254 | + } |
| 10006 | 10255 | if( zErr ){ |
| 10007 | 10256 | re_free(pRe); |
| 10008 | 10257 | sqlite3_result_error(context, zErr, -1); |
| 10009 | 10258 | return; |
| 10010 | 10259 | } |
| | @@ -10066,11 +10315,11 @@ |
| 10066 | 10315 | "ATSTART", |
| 10067 | 10316 | }; |
| 10068 | 10317 | |
| 10069 | 10318 | zPattern = (const char*)sqlite3_value_text(argv[0]); |
| 10070 | 10319 | if( zPattern==0 ) return; |
| 10071 | | - zErr = re_compile(&pRe, zPattern, re_maxlen(context), |
| 10320 | + zErr = re_compile(&pRe, zPattern, re_maxnfa(re_maxlen(context)), |
| 10072 | 10321 | sqlite3_user_data(context)!=0); |
| 10073 | 10322 | if( zErr ){ |
| 10074 | 10323 | re_free(pRe); |
| 10075 | 10324 | sqlite3_result_error(context, zErr, -1); |
| 10076 | 10325 | return; |
| | @@ -10474,10 +10723,11 @@ |
| 10474 | 10723 | if( b1==0 ) return 1; |
| 10475 | 10724 | sz = MultiByteToWideChar(CP_UTF8, 0, zPath, sz, b1, sz); |
| 10476 | 10725 | b1[sz] = 0; |
| 10477 | 10726 | rc = _wstat(b1, pStatBuf); |
| 10478 | 10727 | if( rc==0 ) statTimesToUtc(zPath, pStatBuf); |
| 10728 | + sqlite3_free(b1); |
| 10479 | 10729 | return rc; |
| 10480 | 10730 | #else |
| 10481 | 10731 | return stat(zPath, pStatBuf); |
| 10482 | 10732 | #endif |
| 10483 | 10733 | } |
| | @@ -10604,11 +10854,10 @@ |
| 10604 | 10854 | } |
| 10605 | 10855 | } |
| 10606 | 10856 | |
| 10607 | 10857 | if( mtime>=0 ){ |
| 10608 | 10858 | #if defined(_WIN32) |
| 10609 | | -#if !SQLITE_OS_WINRT |
| 10610 | 10859 | /* Windows */ |
| 10611 | 10860 | FILETIME lastAccess; |
| 10612 | 10861 | FILETIME lastWrite; |
| 10613 | 10862 | SYSTEMTIME currentTime; |
| 10614 | 10863 | LONGLONG intervals; |
| | @@ -10635,11 +10884,10 @@ |
| 10635 | 10884 | CloseHandle(hFile); |
| 10636 | 10885 | return !bResult; |
| 10637 | 10886 | }else{ |
| 10638 | 10887 | return 1; |
| 10639 | 10888 | } |
| 10640 | | -#endif |
| 10641 | 10889 | #elif defined(AT_FDCWD) && 0 /* utimensat() is not universally available */ |
| 10642 | 10890 | /* Recent unix */ |
| 10643 | 10891 | struct timespec times[2]; |
| 10644 | 10892 | times[0].tv_nsec = times[1].tv_nsec = 0; |
| 10645 | 10893 | times[0].tv_sec = time(0); |
| | @@ -13337,11 +13585,11 @@ |
| 13337 | 13585 | rc = zipfileReadData( |
| 13338 | 13586 | pFile, aRead, nExtra+nFile, iOff+ZIPFILE_CDS_FIXED_SZ, pzErr |
| 13339 | 13587 | ); |
| 13340 | 13588 | }else{ |
| 13341 | 13589 | aRead = (u8*)&aBlob[iOff + ZIPFILE_CDS_FIXED_SZ]; |
| 13342 | | - if( (iOff + ZIPFILE_LFH_FIXED_SZ + nFile + nExtra)>nBlob ){ |
| 13590 | + if( (iOff + ZIPFILE_CDS_FIXED_SZ + nFile + nExtra)>nBlob ){ |
| 13343 | 13591 | rc = zipfileCorrupt(pzErr); |
| 13344 | 13592 | } |
| 13345 | 13593 | } |
| 13346 | 13594 | } |
| 13347 | 13595 | |
| | @@ -13362,15 +13610,16 @@ |
| 13362 | 13610 | ZipfileLFH lfh; |
| 13363 | 13611 | if( pFile ){ |
| 13364 | 13612 | rc = zipfileReadData(pFile, aRead, szFix, pNew->cds.iOffset, pzErr); |
| 13365 | 13613 | }else{ |
| 13366 | 13614 | aRead = (u8*)&aBlob[pNew->cds.iOffset]; |
| 13367 | | - if( (pNew->cds.iOffset + ZIPFILE_LFH_FIXED_SZ)>nBlob ){ |
| 13615 | + if( (pNew->cds.iOffset + ZIPFILE_LFH_FIXED_SZ)>(unsigned)nBlob ){ |
| 13368 | 13616 | rc = zipfileCorrupt(pzErr); |
| 13369 | 13617 | } |
| 13370 | 13618 | } |
| 13371 | 13619 | |
| 13620 | + memset(&lfh, 0, sizeof(lfh)); |
| 13372 | 13621 | if( rc==SQLITE_OK ) rc = zipfileReadLFH(aRead, &lfh); |
| 13373 | 13622 | if( rc==SQLITE_OK ){ |
| 13374 | 13623 | pNew->iDataOff = pNew->cds.iOffset + ZIPFILE_LFH_FIXED_SZ; |
| 13375 | 13624 | pNew->iDataOff += lfh.nFile + lfh.nExtra; |
| 13376 | 13625 | if( aBlob && pNew->cds.szCompressed ){ |
| | @@ -13470,11 +13719,11 @@ |
| 13470 | 13719 | }else{ |
| 13471 | 13720 | err = inflate(&str, Z_NO_FLUSH); |
| 13472 | 13721 | if( err!=Z_STREAM_END ){ |
| 13473 | 13722 | zipfileCtxErrorMsg(pCtx, "inflate() failed (%d)", err); |
| 13474 | 13723 | }else{ |
| 13475 | | - sqlite3_result_blob(pCtx, aRes, nOut, zipfileFree); |
| 13724 | + sqlite3_result_blob(pCtx, aRes, (int)str.total_out, zipfileFree); |
| 13476 | 13725 | aRes = 0; |
| 13477 | 13726 | } |
| 13478 | 13727 | } |
| 13479 | 13728 | sqlite3_free(aRes); |
| 13480 | 13729 | inflateEnd(&str); |
| | @@ -23948,28 +24197,28 @@ |
| 23948 | 24197 | u8 autoExplain; /* Automatically turn on .explain mode */ |
| 23949 | 24198 | u8 autoEQP; /* Run EXPLAIN QUERY PLAN prior to each SQL stmt */ |
| 23950 | 24199 | u8 autoEQPtrace; /* autoEQP is in trace mode */ |
| 23951 | 24200 | u8 scanstatsOn; /* True to display scan stats before each finalize */ |
| 23952 | 24201 | u8 bAutoScreenWidth; /* Using the TTY to determine screen width */ |
| 23953 | | - u8 mFlags; /* MFLG_ECHO and/or MFLG_CRLF */ |
| 24202 | + u8 mFlags; /* MFLG_ECHO, MFLG_CRLF, etc. */ |
| 23954 | 24203 | u8 eMode; /* One of the MODE_ values */ |
| 23955 | 24204 | sqlite3_qrf_spec spec; /* Spec to be passed into QRF */ |
| 23956 | 24205 | } Mode; |
| 23957 | 24206 | |
| 23958 | 24207 | /* Flags for Mode.mFlags */ |
| 23959 | 24208 | #define MFLG_ECHO 0x01 /* Echo inputs to output */ |
| 23960 | 24209 | #define MFLG_CRLF 0x02 /* Use CR/LF output line endings */ |
| 24210 | +#define MFLG_HDR 0x04 /* .header used to change headers on/off */ |
| 23961 | 24211 | |
| 23962 | 24212 | |
| 23963 | 24213 | /* |
| 23964 | 24214 | ** State information about the database connection is contained in an |
| 23965 | 24215 | ** instance of the following structure. |
| 23966 | 24216 | */ |
| 23967 | 24217 | typedef struct ShellState ShellState; |
| 23968 | 24218 | struct ShellState { |
| 23969 | 24219 | sqlite3 *db; /* The database */ |
| 23970 | | - int iCompat; /* Compatibility date YYYYMMDD */ |
| 23971 | 24220 | u8 openMode; /* SHELL_OPEN_NORMAL, _APPENDVFS, or _ZIPFILE */ |
| 23972 | 24221 | u8 doXdgOpen; /* Invoke start/open/xdg-open in output_reset() */ |
| 23973 | 24222 | u8 nEqpLevel; /* Depth of the EQP output graph */ |
| 23974 | 24223 | u8 eTraceType; /* SHELL_TRACE_* value for type of trace */ |
| 23975 | 24224 | u8 bSafeMode; /* True to prohibit unsafe operations */ |
| | @@ -24078,11 +24327,11 @@ |
| 24078 | 24327 | |
| 24079 | 24328 | /* Names of values for Mode.spec.eEsc and Mode.spec.eText |
| 24080 | 24329 | */ |
| 24081 | 24330 | static const char *qrfEscNames[] = { "auto", "off", "ascii", "symbol" }; |
| 24082 | 24331 | static const char *qrfQuoteNames[] = |
| 24083 | | - { "off","off","sql","hex","csv","tcl","json"}; |
| 24332 | + { "off","off","sql","hex","csv","tcl","json","relaxed"}; |
| 24084 | 24333 | |
| 24085 | 24334 | /* |
| 24086 | 24335 | ** These are the allowed shellFlgs values |
| 24087 | 24336 | */ |
| 24088 | 24337 | #define SHFLG_Pagecache 0x00000001 /* The --pagecache option is used */ |
| | @@ -24120,19 +24369,20 @@ |
| 24120 | 24369 | #define MODE_Json 10 /* Output JSON */ |
| 24121 | 24370 | #define MODE_Line 11 /* One column per line. Blank line between records */ |
| 24122 | 24371 | #define MODE_List 12 /* One record per line with a separator */ |
| 24123 | 24372 | #define MODE_Markdown 13 /* Markdown formatting */ |
| 24124 | 24373 | #define MODE_Off 14 /* No query output shown */ |
| 24125 | | -#define MODE_QBox 15 /* BOX with SQL-quoted content */ |
| 24126 | | -#define MODE_Quote 16 /* Quote values as for SQL */ |
| 24127 | | -#define MODE_Split 17 /* Split-column mode */ |
| 24128 | | -#define MODE_Table 18 /* MySQL-style table formatting */ |
| 24129 | | -#define MODE_Tabs 19 /* Tab-separated values */ |
| 24130 | | -#define MODE_Tcl 20 /* Space-separated list of TCL strings */ |
| 24131 | | -#define MODE_Www 21 /* Full web-page output */ |
| 24132 | | - |
| 24133 | | -#define MODE_BUILTIN 21 /* Maximum built-in mode */ |
| 24374 | +#define MODE_Psql 15 /* Similar to psql */ |
| 24375 | +#define MODE_QBox 16 /* BOX with SQL-quoted content */ |
| 24376 | +#define MODE_Quote 17 /* Quote values as for SQL */ |
| 24377 | +#define MODE_Split 18 /* Split-column mode */ |
| 24378 | +#define MODE_Table 19 /* MySQL-style table formatting */ |
| 24379 | +#define MODE_Tabs 20 /* Tab-separated values */ |
| 24380 | +#define MODE_Tcl 21 /* Space-separated list of TCL strings */ |
| 24381 | +#define MODE_Www 22 /* Full web-page output */ |
| 24382 | + |
| 24383 | +#define MODE_BUILTIN 22 /* Maximum built-in mode */ |
| 24134 | 24384 | #define MODE_BATCH 50 /* Default mode for batch processing */ |
| 24135 | 24385 | #define MODE_TTY 51 /* Default mode for interactive processing */ |
| 24136 | 24386 | #define MODE_USER 75 /* First user-defined mode */ |
| 24137 | 24387 | #define MODE_N_USER 25 /* Maximum number of user-defined modes */ |
| 24138 | 24388 | |
| | @@ -24149,43 +24399,45 @@ |
| 24149 | 24399 | unsigned char eHdr; /* Default header encoding. */ |
| 24150 | 24400 | unsigned char eBlob; /* Default blob encoding. */ |
| 24151 | 24401 | unsigned char bHdr; /* Show headers by default. 0: n/a, 1: no 2: yes */ |
| 24152 | 24402 | unsigned char eStyle; /* Underlying QRF style */ |
| 24153 | 24403 | unsigned char eCx; /* 0: other, 1: line, 2: columnar */ |
| 24404 | + unsigned char mFlg; /* Flags. 1=border-off 2=split-column */ |
| 24154 | 24405 | }; |
| 24155 | 24406 | |
| 24156 | 24407 | /* String constants used by built-in modes */ |
| 24157 | 24408 | static const char *aModeStr[] = |
| 24158 | 24409 | /* 0 1 2 3 4 5 6 7 8 */ |
| 24159 | 24410 | { 0, "\n", "|", " ", ",", "\r\n", "\036", "\037", "\t", |
| 24160 | | - "", "NULL", "null", "\"\"" }; |
| 24161 | | - /* 9 10 11 12 */ |
| 24411 | + "", "NULL", "null", "\"\"", ": ", }; |
| 24412 | + /* 9 10 11 12 13 */ |
| 24162 | 24413 | |
| 24163 | 24414 | static const ModeInfo aModeInfo[] = { |
| 24164 | | -/* zName eCSep eRSep eNull eText eHdr eBlob bHdr eStyle eCx */ |
| 24165 | | - { "ascii", 7, 6, 9, 1, 1, 0, 1, 12, 0 }, |
| 24166 | | - { "box", 0, 0, 9, 1, 1, 0, 2, 1, 2 }, |
| 24167 | | - { "c", 4, 1, 10, 5, 5, 4, 1, 12, 0 }, |
| 24168 | | - { "column", 0, 0, 9, 1, 1, 0, 2, 2, 2 }, |
| 24169 | | - { "count", 0, 0, 0, 0, 0, 0, 0, 3, 0 }, |
| 24170 | | - { "csv", 4, 5, 9, 3, 3, 0, 1, 12, 0 }, |
| 24171 | | - { "html", 0, 0, 9, 4, 4, 0, 2, 7, 0 }, |
| 24172 | | - { "insert", 0, 0, 10, 2, 2, 0, 1, 8, 0 }, |
| 24173 | | - { "jatom", 4, 1, 11, 6, 6, 0, 1, 12, 0 }, |
| 24174 | | - { "jobject", 0, 1, 11, 6, 6, 0, 0, 10, 0 }, |
| 24175 | | - { "json", 0, 0, 11, 6, 6, 0, 0, 9, 0 }, |
| 24176 | | - { "line", 0, 1, 9, 1, 1, 0, 0, 11, 1 }, |
| 24177 | | - { "list", 2, 1, 9, 1, 1, 0, 1, 12, 0 }, |
| 24178 | | - { "markdown", 0, 0, 9, 1, 1, 0, 2, 13, 2 }, |
| 24179 | | - { "off", 0, 0, 0, 0, 0, 0, 0, 14, 0 }, |
| 24180 | | - { "qbox", 0, 0, 10, 2, 1, 0, 2, 1, 2 }, |
| 24181 | | - { "quote", 4, 1, 10, 2, 2, 0, 1, 12, 0 }, |
| 24182 | | - { "split", 0, 0, 9, 1, 1, 0, 1, 2, 2 }, |
| 24183 | | - { "table", 0, 0, 9, 1, 1, 0, 2, 19, 2 }, |
| 24184 | | - { "tabs", 8, 1, 9, 3, 3, 0, 1, 12, 0 }, |
| 24185 | | - { "tcl", 3, 1, 12, 5, 5, 4, 1, 12, 0 }, |
| 24186 | | - { "www", 0, 0, 9, 4, 4, 0, 2, 7, 0 } |
| 24415 | +/* zName eCSep eRSep eNull eText eHdr eBlob bHdr eStyle eCx mFlg */ |
| 24416 | + { "ascii", 7, 6, 9, 1, 1, 0, 1, 12, 0, 0 }, |
| 24417 | + { "box", 0, 0, 9, 1, 1, 0, 2, 1, 2, 0 }, |
| 24418 | + { "c", 4, 1, 10, 5, 5, 4, 1, 12, 0, 0 }, |
| 24419 | + { "column", 0, 0, 9, 1, 1, 0, 2, 2, 2, 0 }, |
| 24420 | + { "count", 0, 0, 0, 0, 0, 0, 0, 3, 0, 0 }, |
| 24421 | + { "csv", 4, 5, 9, 3, 3, 0, 1, 12, 0, 0 }, |
| 24422 | + { "html", 0, 0, 9, 4, 4, 0, 2, 7, 0, 0 }, |
| 24423 | + { "insert", 0, 0, 10, 2, 2, 0, 1, 8, 0, 0 }, |
| 24424 | + { "jatom", 4, 1, 11, 6, 6, 0, 1, 12, 0, 0 }, |
| 24425 | + { "jobject", 0, 1, 11, 6, 6, 0, 0, 10, 0, 0 }, |
| 24426 | + { "json", 0, 0, 11, 6, 6, 0, 0, 9, 0, 0 }, |
| 24427 | + { "line", 13, 1, 9, 1, 1, 0, 0, 11, 1, 0 }, |
| 24428 | + { "list", 2, 1, 9, 1, 1, 0, 1, 12, 0, 0 }, |
| 24429 | + { "markdown", 0, 0, 9, 1, 1, 0, 2, 13, 2, 0 }, |
| 24430 | + { "off", 0, 0, 0, 0, 0, 0, 0, 14, 0, 0 }, |
| 24431 | + { "psql", 0, 0, 9, 1, 1, 0, 2, 19, 2, 1 }, |
| 24432 | + { "qbox", 0, 0, 10, 2, 1, 0, 2, 1, 2, 0 }, |
| 24433 | + { "quote", 4, 1, 10, 2, 2, 0, 1, 12, 0, 0 }, |
| 24434 | + { "split", 0, 0, 9, 1, 1, 0, 1, 2, 2, 2 }, |
| 24435 | + { "table", 0, 0, 9, 1, 1, 0, 2, 19, 2, 0 }, |
| 24436 | + { "tabs", 8, 1, 9, 3, 3, 0, 1, 12, 0, 0 }, |
| 24437 | + { "tcl", 3, 1, 12, 5, 5, 4, 1, 12, 0, 0 }, |
| 24438 | + { "www", 0, 0, 9, 4, 4, 0, 2, 7, 0, 0 } |
| 24187 | 24439 | }; /* | / / | / / | | \ |
| 24188 | 24440 | ** | / / | / / | | \_ 2: columnar |
| 24189 | 24441 | ** Index into aModeStr[] | / / | | 1: line |
| 24190 | 24442 | ** | / / | | 0: other |
| 24191 | 24443 | ** | / / | \ |
| | @@ -24212,10 +24464,23 @@ |
| 24212 | 24464 | #define SEP_Comma "," |
| 24213 | 24465 | #define SEP_CrLf "\r\n" |
| 24214 | 24466 | #define SEP_Unit "\x1F" |
| 24215 | 24467 | #define SEP_Record "\x1E" |
| 24216 | 24468 | |
| 24469 | +/* |
| 24470 | +** Default values for the various QRF limits |
| 24471 | +*/ |
| 24472 | +#ifndef DFLT_CHAR_LIMIT |
| 24473 | +# define DFLT_CHAR_LIMIT 300 |
| 24474 | +#endif |
| 24475 | +#ifndef DFLT_LINE_LIMIT |
| 24476 | +# define DFLT_LINE_LIMIT 5 |
| 24477 | +#endif |
| 24478 | +#ifndef DFLT_TITLE_LIMIT |
| 24479 | +# define DFLT_TITLE_LIMIT 20 |
| 24480 | +#endif |
| 24481 | + |
| 24217 | 24482 | /* |
| 24218 | 24483 | ** Limit input nesting via .read or any other input redirect. |
| 24219 | 24484 | ** It's not too expensive, so a generous allowance can be made. |
| 24220 | 24485 | */ |
| 24221 | 24486 | #define MAX_INPUT_NESTING 25 |
| | @@ -24305,13 +24570,20 @@ |
| 24305 | 24570 | if( pI->eCSep ) modeSetStr(&pM->spec.zColumnSep, aModeStr[pI->eCSep]); |
| 24306 | 24571 | if( pI->eRSep ) modeSetStr(&pM->spec.zRowSep, aModeStr[pI->eRSep]); |
| 24307 | 24572 | if( pI->eNull ) modeSetStr(&pM->spec.zNull, aModeStr[pI->eNull]); |
| 24308 | 24573 | pM->spec.eText = pI->eText; |
| 24309 | 24574 | pM->spec.eBlob = pI->eBlob; |
| 24310 | | - pM->spec.bTitles = pI->bHdr; |
| 24575 | + if( (pM->mFlags & MFLG_HDR)==0 ){ |
| 24576 | + pM->spec.bTitles = pI->bHdr; |
| 24577 | + } |
| 24311 | 24578 | pM->spec.eTitle = pI->eHdr; |
| 24312 | | - if( eMode==MODE_Split ){ |
| 24579 | + if( pI->mFlg & 0x01 ){ |
| 24580 | + pM->spec.bBorder = QRF_No; |
| 24581 | + }else{ |
| 24582 | + pM->spec.bBorder = QRF_Auto; |
| 24583 | + } |
| 24584 | + if( pI->mFlg & 0x02 ){ |
| 24313 | 24585 | pM->spec.bSplitColumn = QRF_Yes; |
| 24314 | 24586 | pM->bAutoScreenWidth = 1; |
| 24315 | 24587 | }else{ |
| 24316 | 24588 | pM->spec.bSplitColumn = QRF_No; |
| 24317 | 24589 | } |
| | @@ -24326,26 +24598,27 @@ |
| 24326 | 24598 | }else if( eMode==MODE_TTY ){ |
| 24327 | 24599 | u8 mFlags = p->mode.mFlags; |
| 24328 | 24600 | modeFree(&p->mode); |
| 24329 | 24601 | modeChange(p, MODE_QBox); |
| 24330 | 24602 | p->mode.bAutoScreenWidth = 1; |
| 24331 | | - p->mode.spec.nCharLimit = 300; |
| 24332 | | - p->mode.spec.nLineLimit = 5; |
| 24603 | + p->mode.spec.eText = QRF_TEXT_Relaxed; |
| 24604 | + p->mode.spec.nCharLimit = DFLT_CHAR_LIMIT; |
| 24605 | + p->mode.spec.nLineLimit = DFLT_LINE_LIMIT; |
| 24333 | 24606 | p->mode.spec.bTextJsonb = QRF_Yes; |
| 24607 | + p->mode.spec.nTitleLimit = DFLT_TITLE_LIMIT; |
| 24334 | 24608 | p->mode.mFlags = mFlags; |
| 24335 | 24609 | } |
| 24336 | 24610 | } |
| 24337 | 24611 | |
| 24338 | 24612 | /* |
| 24339 | | -** Set the mode to the default according to p->iCompat. It assumed |
| 24340 | | -** that the mode has already been freed and zeroed prior to calling |
| 24341 | | -** this routine. |
| 24613 | +** Set the mode to the default. It assumed that the mode has |
| 24614 | +** already been freed and zeroed prior to calling this routine. |
| 24342 | 24615 | */ |
| 24343 | 24616 | static void modeDefault(ShellState *p){ |
| 24344 | 24617 | p->mode.spec.iVersion = 1; |
| 24345 | 24618 | p->mode.autoExplain = 1; |
| 24346 | | - if( p->iCompat>=20251115 && (stdin_is_interactive || stdout_is_console) ){ |
| 24619 | + if( stdin_is_interactive || stdout_is_console ){ |
| 24347 | 24620 | modeChange(p, MODE_TTY); |
| 24348 | 24621 | }else{ |
| 24349 | 24622 | modeChange(p, MODE_BATCH); |
| 24350 | 24623 | } |
| 24351 | 24624 | } |
| | @@ -25075,10 +25348,12 @@ |
| 25075 | 25348 | z[j] = 0; |
| 25076 | 25349 | if( strlen30(z)>=79 ){ |
| 25077 | 25350 | for(i=j=0; (c = z[i])!=0; i++){ /* Copy from z[i] back to z[j] */ |
| 25078 | 25351 | if( c==cEnd ){ |
| 25079 | 25352 | cEnd = 0; |
| 25353 | + }else if( cEnd!=0){ |
| 25354 | + /* No-op */ |
| 25080 | 25355 | }else if( c=='"' || c=='\'' || c=='`' ){ |
| 25081 | 25356 | cEnd = c; |
| 25082 | 25357 | }else if( c=='[' ){ |
| 25083 | 25358 | cEnd = ']'; |
| 25084 | 25359 | }else if( c=='-' && z[i+1]=='-' ){ |
| | @@ -25889,10 +26164,11 @@ |
| 25889 | 26164 | if( pArg->mode.bAutoScreenWidth ){ |
| 25890 | 26165 | spec.nScreenWidth = shellScreenWidth(); |
| 25891 | 26166 | } |
| 25892 | 26167 | if( spec.eBlob==QRF_BLOB_Auto ){ |
| 25893 | 26168 | switch( spec.eText ){ |
| 26169 | + case QRF_TEXT_Relaxed: /* fall through */ |
| 25894 | 26170 | case QRF_TEXT_Sql: spec.eBlob = QRF_BLOB_Sql; break; |
| 25895 | 26171 | case QRF_TEXT_Json: spec.eBlob = QRF_BLOB_Json; break; |
| 25896 | 26172 | default: spec.eBlob = QRF_BLOB_Text; break; |
| 25897 | 26173 | } |
| 25898 | 26174 | } |
| | @@ -25902,11 +26178,11 @@ |
| 25902 | 26178 | rc = expertHandleSQL(pArg, zSql, pzErrMsg); |
| 25903 | 26179 | return expertFinish(pArg, (rc!=SQLITE_OK), pzErrMsg); |
| 25904 | 26180 | } |
| 25905 | 26181 | #endif |
| 25906 | 26182 | |
| 25907 | | - while( zSql[0] && (SQLITE_OK == rc) ){ |
| 26183 | + while( zSql && zSql[0] && (SQLITE_OK == rc) ){ |
| 25908 | 26184 | static const char *zStmtSql; |
| 25909 | 26185 | rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, &zLeftover); |
| 25910 | 26186 | if( SQLITE_OK != rc ){ |
| 25911 | 26187 | if( pzErrMsg ){ |
| 25912 | 26188 | *pzErrMsg = save_err_msg(db, "in prepare", rc, zSql); |
| | @@ -26365,12 +26641,12 @@ |
| 26365 | 26641 | ".bail on|off Stop after hitting an error. Default OFF", |
| 26366 | 26642 | #ifndef SQLITE_SHELL_FIDDLE |
| 26367 | 26643 | ".cd DIRECTORY Change the working directory to DIRECTORY", |
| 26368 | 26644 | #endif |
| 26369 | 26645 | ".changes on|off Show number of rows changed by SQL", |
| 26646 | + ".check OPTIONS ... Verify the results of a .testcase", |
| 26370 | 26647 | #ifndef SQLITE_SHELL_FIDDLE |
| 26371 | | - ".check GLOB Fail if output since .testcase does not match", |
| 26372 | 26648 | ".clone NEWDB Clone data into NEWDB from the existing database", |
| 26373 | 26649 | #endif |
| 26374 | 26650 | ".connection [close] [#] Open or close an auxiliary database connection", |
| 26375 | 26651 | ".crlf ?on|off? Whether or not to use \\r\\n line endings", |
| 26376 | 26652 | ".databases List names and files of attached databases", |
| | @@ -26546,13 +26822,11 @@ |
| 26546 | 26822 | " vmstep Show the virtual machine step count only", |
| 26547 | 26823 | #if !defined(SQLITE_NOHAVE_SYSTEM) && !defined(SQLITE_SHELL_FIDDLE) |
| 26548 | 26824 | ".system CMD ARGS... Run CMD ARGS... in a system shell", |
| 26549 | 26825 | #endif |
| 26550 | 26826 | ".tables ?TABLE? List names of tables matching LIKE pattern TABLE", |
| 26551 | | -#ifndef SQLITE_SHELL_FIDDLE |
| 26552 | | - ",testcase NAME Begin redirecting output to 'testcase-out.txt'", |
| 26553 | | -#endif |
| 26827 | + ".testcase NAME Begin a test case.", |
| 26554 | 26828 | ",testctrl CMD ... Run various sqlite3_test_control() operations", |
| 26555 | 26829 | " Run \".testctrl\" with no arguments for details", |
| 26556 | 26830 | ".timeout MS Try opening locked tables for MS milliseconds", |
| 26557 | 26831 | ".timer on|off Turn SQL timer on or off", |
| 26558 | 26832 | #ifndef SQLITE_OMIT_TRACE |
| | @@ -26597,19 +26871,31 @@ |
| 26597 | 26871 | "USAGE: .import [OPTIONS] FILE TABLE\n" |
| 26598 | 26872 | "\n" |
| 26599 | 26873 | "Import CSV or similar text from FILE into TABLE. If TABLE does\n" |
| 26600 | 26874 | "not exist, it is created using the first row of FILE as the column\n" |
| 26601 | 26875 | "names. If FILE begins with \"|\" then it is a command that is run\n" |
| 26602 | | -"and the output from the command is used as the input data.\n" |
| 26876 | +"and the output from the command is used as the input data. If\n" |
| 26877 | +"FILE begins with \"<<\" followed by a label, then content is read from\n" |
| 26878 | +"the script until the first line that matches the label.\n" |
| 26879 | +"\n" |
| 26880 | +"The content of FILE is interpreted using RFC-4180 (\"CSV\") quoting\n" |
| 26881 | +"rules unless the current mode is \"ascii\" or \"tabs\" or unless one\n" |
| 26882 | +"the --ascii option is used.\n" |
| 26603 | 26883 | "\n" |
| 26604 | | -"FILE is assumed to be in a CSV format, unless the current mode\n" |
| 26605 | | -"is \"ascii\" or \"tabs\" or unless one of the options below specify\n" |
| 26606 | | -"an alternative.\n" |
| 26884 | +"The column and row separators must be single ASCII characters. If\n" |
| 26885 | +"multiple characters or a Unicode character are specified for the\n" |
| 26886 | +"separators, then only the first byte of the separator is used. Except,\n" |
| 26887 | +"if the row separator is \\n and the mode is not --ascii, then \\r\\n is\n" |
| 26888 | +"understood as a row separator too.\n" |
| 26607 | 26889 | "\n" |
| 26608 | 26890 | "Options:\n" |
| 26609 | | -" --ascii Use \\037 and \\036 as column and row separators on input\n" |
| 26891 | +" --ascii Do not use RFC-4180 quoting. Use \\037 and \\036\n" |
| 26892 | +" as column and row separators on input, unless other\n" |
| 26893 | +" delimiters are specified using --colsep and/or --rowsep\n" |
| 26894 | +" --colsep CHAR Use CHAR as the column separator.\n" |
| 26610 | 26895 | " --csv Input is standard RFC-4180 CSV.\n" |
| 26896 | +" --rowsep CHAR Use CHAR as the row separator.\n" |
| 26611 | 26897 | " --schema S When creating TABLE, put it in schema S\n" |
| 26612 | 26898 | " --skip N Ignore the first N rows of input\n" |
| 26613 | 26899 | " -v Verbose mode\n" |
| 26614 | 26900 | }, |
| 26615 | 26901 | { ".mode", |
| | @@ -26625,10 +26911,11 @@ |
| 26625 | 26911 | " meaning \"left\", \"centered\", and \"right\", with\n" |
| 26626 | 26912 | " one letter per column starting from the left.\n" |
| 26627 | 26913 | " Unspecified alignment defaults to 'L'.\n" |
| 26628 | 26914 | " --blob-quote ARG ARG can be \"auto\", \"text\", \"sql\", \"hex\", \"tcl\",\n" |
| 26629 | 26915 | " \"json\", or \"size\". Default is \"auto\".\n" |
| 26916 | +" --border on|off Show outer border on \"box\" and \"table\" modes.\n" |
| 26630 | 26917 | " --charlimit N Set the maximum number of output characters to\n" |
| 26631 | 26918 | " show for any single SQL value to N. Longer values\n" |
| 26632 | 26919 | " truncated. Zero means \"no limit\".\n" |
| 26633 | 26920 | " --colsep STRING Use STRING as the column separator\n" |
| 26634 | 26921 | " --escape ESC Enable/disable escaping of control characters\n" |
| | @@ -26636,19 +26923,22 @@ |
| 26636 | 26923 | " or \"symbol\".\n" |
| 26637 | 26924 | " --linelimit N Set the maximum number of output lines to show for\n" |
| 26638 | 26925 | " any single SQL value to N. Longer values are\n" |
| 26639 | 26926 | " truncated. Zero means \"no limit\". Only works\n" |
| 26640 | 26927 | " in \"line\" mode and in columnar modes.\n" |
| 26641 | | -" --limits L,C Shorthand for \"--linelimit L --charlimit C\".\n" |
| 26642 | | -" Or \"off\" to mean \"0,0\". Or \"on\" for \"5,300\".\n" |
| 26928 | +" --limits L,C,T Shorthand for \"--linelimit L --charlimit C\n" |
| 26929 | +" --titlelimit T\". The \",T\" can be omitted in which\n" |
| 26930 | +" case the --titlelimit is unchanged. The argument\n" |
| 26931 | +" can also be \"off\" to mean \"0,0,0\" or \"on\" to\n" |
| 26932 | +" mean \"5,300,20\".\n" |
| 26643 | 26933 | " --list List available modes\n" |
| 26644 | 26934 | " --null STRING Render SQL NULL values as the given string\n" |
| 26645 | 26935 | " --once Setting changes to the right are reverted after\n" |
| 26646 | 26936 | " the next SQL command.\n" |
| 26647 | 26937 | " --quote ARG Enable/disable quoting of text. ARG can be\n" |
| 26648 | | -" \"off\", \"on\", \"sql\", \"csv\", \"html\", \"tcl\",\n" |
| 26649 | | -" or \"json\". \"off\" means show the text as-is.\n" |
| 26938 | +" \"off\", \"on\", \"sql\", \"relaxed\", \"csv\", \"html\",\n" |
| 26939 | +" \"tcl\", or \"json\". \"off\" means show the text as-is.\n" |
| 26650 | 26940 | " \"on\" is an alias for \"sql\".\n" |
| 26651 | 26941 | " --reset Changes all mode settings back to their default.\n" |
| 26652 | 26942 | " --rowsep STRING Use STRING as the row separator\n" |
| 26653 | 26943 | " --sw|--screenwidth N Declare the screen width of the output device\n" |
| 26654 | 26944 | " to be N characters. An attempt may be made to\n" |
| | @@ -26659,10 +26949,11 @@ |
| 26659 | 26949 | " --tag NAME Save mode to the left as NAME.\n" |
| 26660 | 26950 | " --textjsonb BOOLEAN If enabled, JSONB text is displayed as text JSON.\n" |
| 26661 | 26951 | " --title ARG Whether or not to show column headers, and if so\n" |
| 26662 | 26952 | " how to encode them. ARG can be \"off\", \"on\",\n" |
| 26663 | 26953 | " \"sql\", \"csv\", \"html\", \"tcl\", or \"json\".\n" |
| 26954 | +" --titlelimit N Limit the length of column titles to N characters.\n" |
| 26664 | 26955 | " -v|--verbose Verbose output\n" |
| 26665 | 26956 | " --widths LIST Set the columns widths for columnar modes. The\n" |
| 26666 | 26957 | " argument is a list of integers, one for each\n" |
| 26667 | 26958 | " column. A \"0\" width means use a dynamic width\n" |
| 26668 | 26959 | " based on the actual width of data. If there are\n" |
| | @@ -26686,23 +26977,17 @@ |
| 26686 | 26977 | " --bom Prepend a byte-order mark to the output\n" |
| 26687 | 26978 | " -e Accumulate output in a temporary text file then\n" |
| 26688 | 26979 | " launch a text editor when the redirection ends.\n" |
| 26689 | 26980 | " --error-prefix X Use X as the left-margin prefix for error messages.\n" |
| 26690 | 26981 | " Set to an empty string to restore the default.\n" |
| 26691 | | -" --glob GLOB Raise an error if the memory buffer does not match\n" |
| 26692 | | -" the GLOB pattern.\n" |
| 26693 | | -" --keep Continue using the same \"memory\" buffer. Do not\n" |
| 26694 | | -" reset it or delete it. Useful in combination with\n" |
| 26695 | | -" --glob, --not-glob, and/or --verify.\n" |
| 26696 | | -" ---notglob GLOB Raise an error if the memory buffer does not match\n" |
| 26697 | | -" the GLOB pattern.\n" |
| 26982 | +" --keep Keep redirecting output to its current destination.\n" |
| 26983 | +" Use this option in combination with --show or\n" |
| 26984 | +" with --error-prefix when you do not want to stop\n" |
| 26985 | +" a current redirection.\n" |
| 26698 | 26986 | " --plain Use plain text rather than HTML tables with -w\n" |
| 26699 | | -" --show Write the memory buffer to the screen, for debugging.\n" |
| 26700 | | -" --verify ENDMARK Read subsequent lines of text until the first line\n" |
| 26701 | | -" that matches ENDMARK. Discard the ENDMARK. Compare\n" |
| 26702 | | -" the text against the accumulated output in memory and\n" |
| 26703 | | -" raise an error if there are any differences.\n" |
| 26987 | +" --show Show output text captured by .testcase or by\n" |
| 26988 | +" redirecting to \"memory\".\n" |
| 26704 | 26989 | " -w Show the output in a web browser. Output is\n" |
| 26705 | 26990 | " written into a temporary HTML file until the\n" |
| 26706 | 26991 | " redirect ends, then the web browser is launched.\n" |
| 26707 | 26992 | " Query results are shown as HTML tables, unless\n" |
| 26708 | 26993 | " the --plain is used too.\n" |
| | @@ -26726,10 +27011,39 @@ |
| 26726 | 27011 | " file in a web browser\n" |
| 26727 | 27012 | " -x Show the output in a spreadsheet. Output is\n" |
| 26728 | 27013 | " written to a temp file as CSV then the spreadsheet\n" |
| 26729 | 27014 | " is launched when\n" |
| 26730 | 27015 | }, |
| 27016 | + { ".check", |
| 27017 | +"USAGE: .check [OPTIONS] PATTERN\n" |
| 27018 | +"\n" |
| 27019 | +"Verify results of commands since the most recent .testcase command.\n" |
| 27020 | +"Restore output to the console, unless --keep is used.\n" |
| 27021 | +"\n" |
| 27022 | +"If PATTERN starts with \"<<ENDMARK\" then the actual pattern is taken from\n" |
| 27023 | +"subsequent lines of text up to the first line that begins with ENDMARK.\n" |
| 27024 | +"All pattern lines and the ENDMARK are discarded.\n" |
| 27025 | +"\n" |
| 27026 | +"Options:\n" |
| 27027 | +" --exact Do an exact comparison including leading and\n" |
| 27028 | +" trailing whitespace.\n" |
| 27029 | +" --glob Treat PATTERN as a GLOB\n" |
| 27030 | +" --keep Do not reset the testcase. More .check commands\n" |
| 27031 | +" will follow.\n" |
| 27032 | +" --notglob Output should not match PATTERN\n" |
| 27033 | +" --show Write testcase output to the screen, for debugging.\n" |
| 27034 | + }, |
| 27035 | + { ".testcase", |
| 27036 | +"USAGE: .testcase [OPTIONS] NAME\n" |
| 27037 | +"\n" |
| 27038 | +"Start a new test case identified by NAME. All output\n" |
| 27039 | +"through the next \".check\" command is captured for comparison. See the\n" |
| 27040 | +"\".check\" commandn for additional informatioon.\n" |
| 27041 | +"\n" |
| 27042 | +"Options:\n" |
| 27043 | +" --error-prefix TEXT Change error message prefix text to TEXT\n" |
| 27044 | + }, |
| 26731 | 27045 | }; |
| 26732 | 27046 | |
| 26733 | 27047 | /* |
| 26734 | 27048 | ** Return a pointer to usage text for zCmd, or NULL if none exists. |
| 26735 | 27049 | */ |
| | @@ -26949,10 +27263,56 @@ |
| 26949 | 27263 | if( sqlite3_strglob(pSession->azFilter[i], zTab)==0 ) return 0; |
| 26950 | 27264 | } |
| 26951 | 27265 | return 1; |
| 26952 | 27266 | } |
| 26953 | 27267 | #endif |
| 27268 | + |
| 27269 | +/* |
| 27270 | +** Return the size of the named file in bytes. Or return a negative |
| 27271 | +** number if the file does not exist. |
| 27272 | +*/ |
| 27273 | +static sqlite3_int64 fileSize(const char *zFile){ |
| 27274 | +#if defined(_WIN32) || defined(WIN32) |
| 27275 | + struct _stat64 x; |
| 27276 | + if( _stat64(zFile, &x)!=0 ) return -1; |
| 27277 | + return (sqlite3_int64)x.st_size; |
| 27278 | +#else |
| 27279 | + struct stat x; |
| 27280 | + if( stat(zFile, &x)!=0 ) return -1; |
| 27281 | + return (sqlite3_int64)x.st_size; |
| 27282 | +#endif |
| 27283 | +} |
| 27284 | + |
| 27285 | +/* |
| 27286 | +** Return true if zFile is an SQLite database. |
| 27287 | +** |
| 27288 | +** Algorithm: |
| 27289 | +** * If the file does not exist -> return false |
| 27290 | +** * If the size of the file is not a multiple of 512 -> return false |
| 27291 | +** * If sqlite3_open() fails -> return false |
| 27292 | +** * if sqlite3_prepare() or sqlite3_step() fails -> return false |
| 27293 | +** * Otherwise -> return true |
| 27294 | +*/ |
| 27295 | +static int isDatabaseFile(const char *zFile, int openFlags){ |
| 27296 | + sqlite3 *db = 0; |
| 27297 | + sqlite3_stmt *pStmt = 0; |
| 27298 | + int rc; |
| 27299 | + sqlite3_int64 sz = fileSize(zFile); |
| 27300 | + if( sz<512 || (sz%512)!=0 ) return 0; |
| 27301 | + if( sqlite3_open_v2(zFile, &db, openFlags, 0)==SQLITE_OK |
| 27302 | + && sqlite3_prepare_v2(db,"SELECT count(*) FROM sqlite_schema",-1,&pStmt,0) |
| 27303 | + ==SQLITE_OK |
| 27304 | + && sqlite3_step(pStmt)==SQLITE_ROW |
| 27305 | + ){ |
| 27306 | + rc = 1; |
| 27307 | + }else{ |
| 27308 | + rc = 0; |
| 27309 | + } |
| 27310 | + sqlite3_finalize(pStmt); |
| 27311 | + sqlite3_close(db); |
| 27312 | + return rc; |
| 27313 | +} |
| 26954 | 27314 | |
| 26955 | 27315 | /* |
| 26956 | 27316 | ** Try to deduce the type of file for zName based on its content. Return |
| 26957 | 27317 | ** one of the SHELL_OPEN_* constants. |
| 26958 | 27318 | ** |
| | @@ -26962,24 +27322,16 @@ |
| 26962 | 27322 | ** the type cannot be determined from content. |
| 26963 | 27323 | */ |
| 26964 | 27324 | int deduceDatabaseType(const char *zName, int dfltZip, int openFlags){ |
| 26965 | 27325 | FILE *f; |
| 26966 | 27326 | size_t n; |
| 26967 | | - sqlite3 *db = 0; |
| 26968 | | - sqlite3_stmt *pStmt = 0; |
| 26969 | 27327 | int rc = SHELL_OPEN_UNSPEC; |
| 26970 | 27328 | char zBuf[100]; |
| 26971 | 27329 | if( access(zName,0)!=0 ) goto database_type_by_name; |
| 26972 | | - if( sqlite3_open_v2(zName, &db, openFlags, 0)==SQLITE_OK |
| 26973 | | - && sqlite3_prepare_v2(db,"SELECT count(*) FROM sqlite_schema",-1,&pStmt,0) |
| 26974 | | - ==SQLITE_OK |
| 26975 | | - && sqlite3_step(pStmt)==SQLITE_ROW |
| 26976 | | - ){ |
| 27330 | + if( isDatabaseFile(zName, openFlags) ){ |
| 26977 | 27331 | rc = SHELL_OPEN_NORMAL; |
| 26978 | 27332 | } |
| 26979 | | - sqlite3_finalize(pStmt); |
| 26980 | | - sqlite3_close(db); |
| 26981 | 27333 | if( rc==SHELL_OPEN_NORMAL ) return SHELL_OPEN_NORMAL; |
| 26982 | 27334 | f = sqlite3_fopen(zName, "rb"); |
| 26983 | 27335 | if( f==0 ) goto database_type_by_name; |
| 26984 | 27336 | n = fread(zBuf, 16, 1, f); |
| 26985 | 27337 | if( n==1 && memcmp(zBuf, "SQLite format 3", 16)==0 ){ |
| | @@ -27009,10 +27361,37 @@ |
| 27009 | 27361 | }else{ |
| 27010 | 27362 | rc = SHELL_OPEN_NORMAL; |
| 27011 | 27363 | } |
| 27012 | 27364 | return rc; |
| 27013 | 27365 | } |
| 27366 | + |
| 27367 | +/* |
| 27368 | +** If the text in z[] is the name of a readable file and that file appears |
| 27369 | +** to contain SQL text and/or dot-commands, then return true. If z[] is |
| 27370 | +** not a file, or if the file is unreadable, or if the file is a database |
| 27371 | +** or anything else that is not SQL text and dot-commands, then return false. |
| 27372 | +** |
| 27373 | +** If the bLeaveUninit flag is set, then be sure to leave SQLite in an |
| 27374 | +** uninitialized state. This means invoking sqlite3_shutdown() after any |
| 27375 | +** SQLite API is used. |
| 27376 | +** |
| 27377 | +** Some amount of guesswork is involved in this decision. |
| 27378 | +*/ |
| 27379 | +static int isScriptFile(const char *z, int bLeaveUninit){ |
| 27380 | + sqlite3_int64 sz = fileSize(z); |
| 27381 | + if( sz<=0 ) return 0; |
| 27382 | + if( (sz%512)==0 ){ |
| 27383 | + int rc = isDatabaseFile(z, SQLITE_OPEN_READONLY); |
| 27384 | + if( bLeaveUninit ){ |
| 27385 | + sqlite3_shutdown(); |
| 27386 | + } |
| 27387 | + if( rc ) return 0; /* Is a database */ |
| 27388 | + } |
| 27389 | + if( sqlite3_strlike("%.sql",z,0)==0 ) return 1; |
| 27390 | + if( sqlite3_strlike("%.txt",z,0)==0 ) return 1; |
| 27391 | + return 0; |
| 27392 | +} |
| 27014 | 27393 | |
| 27015 | 27394 | #ifndef SQLITE_OMIT_DESERIALIZE |
| 27016 | 27395 | /* |
| 27017 | 27396 | ** Reconstruct an in-memory database using the output from the "dbtotxt" |
| 27018 | 27397 | ** program. Read content from the file in p->aAuxDb[].zDbFilename. |
| | @@ -27655,11 +28034,13 @@ |
| 27655 | 28034 | typedef struct ImportCtx ImportCtx; |
| 27656 | 28035 | struct ImportCtx { |
| 27657 | 28036 | const char *zFile; /* Name of the input file */ |
| 27658 | 28037 | FILE *in; /* Read the CSV text from this input stream */ |
| 27659 | 28038 | int (SQLITE_CDECL *xCloser)(FILE*); /* Func to close in */ |
| 28039 | + char *zIn; /* Input text */ |
| 27660 | 28040 | char *z; /* Accumulated text for a field */ |
| 28041 | + i64 nUsed; /* Bytes of zIn[] used so far */ |
| 27661 | 28042 | i64 n; /* Number of bytes in z */ |
| 27662 | 28043 | i64 nAlloc; /* Space allocated for z[] */ |
| 27663 | 28044 | int nLine; /* Current line number */ |
| 27664 | 28045 | int nRow; /* Number of rows imported */ |
| 27665 | 28046 | int nErr; /* Number of errors encountered */ |
| | @@ -27675,13 +28056,32 @@ |
| 27675 | 28056 | p->xCloser(p->in); |
| 27676 | 28057 | p->in = 0; |
| 27677 | 28058 | } |
| 27678 | 28059 | sqlite3_free(p->z); |
| 27679 | 28060 | p->z = 0; |
| 28061 | + if( p->zIn ){ |
| 28062 | + sqlite3_free(p->zIn); |
| 28063 | + p->zIn = 0; |
| 28064 | + } |
| 27680 | 28065 | } |
| 27681 | 28066 | |
| 27682 | | -/* Append a single byte to z[] */ |
| 28067 | +/* Read a single character of the .import input text. Return EOF |
| 28068 | +** at end-of-file. |
| 28069 | +*/ |
| 28070 | +static int import_getc(ImportCtx *p){ |
| 28071 | + if( p->in ){ |
| 28072 | + return fgetc(p->in); |
| 28073 | + }else if( p->zIn && p->zIn[p->nUsed]!=0 ){ |
| 28074 | + return p->zIn[p->nUsed++]; |
| 28075 | + }else{ |
| 28076 | + return EOF; |
| 28077 | + } |
| 28078 | +} |
| 28079 | + |
| 28080 | +/* Append a single byte to the field value begin constructed |
| 28081 | +** in the p->z[] buffer |
| 28082 | +*/ |
| 27683 | 28083 | static void import_append_char(ImportCtx *p, int c){ |
| 27684 | 28084 | if( p->n+1>=p->nAlloc ){ |
| 27685 | 28085 | p->nAlloc += p->nAlloc + 100; |
| 27686 | 28086 | p->z = sqlite3_realloc64(p->z, p->nAlloc); |
| 27687 | 28087 | shell_check_oom(p->z); |
| | @@ -27693,12 +28093,12 @@ |
| 27693 | 28093 | ** with the option of having a separator other than ",". |
| 27694 | 28094 | ** |
| 27695 | 28095 | ** + Input comes from p->in. |
| 27696 | 28096 | ** + Store results in p->z of length p->n. Space to hold p->z comes |
| 27697 | 28097 | ** from sqlite3_malloc64(). |
| 27698 | | -** + Use p->cSep as the column separator. The default is ",". |
| 27699 | | -** + Use p->rSep as the row separator. The default is "\n". |
| 28098 | +** + Use p->cColSep as the column separator. The default is ",". |
| 28099 | +** + Use p->cRowSep as the row separator. The default is "\n". |
| 27700 | 28100 | ** + Keep track of the line number in p->nLine. |
| 27701 | 28101 | ** + Store the character that terminates the field in p->cTerm. Store |
| 27702 | 28102 | ** EOF on end-of-file. |
| 27703 | 28103 | ** + Report syntax errors on stderr |
| 27704 | 28104 | */ |
| | @@ -27705,11 +28105,11 @@ |
| 27705 | 28105 | static char *SQLITE_CDECL csv_read_one_field(ImportCtx *p){ |
| 27706 | 28106 | int c; |
| 27707 | 28107 | int cSep = (u8)p->cColSep; |
| 27708 | 28108 | int rSep = (u8)p->cRowSep; |
| 27709 | 28109 | p->n = 0; |
| 27710 | | - c = fgetc(p->in); |
| 28110 | + c = import_getc(p); |
| 27711 | 28111 | if( c==EOF || seenInterrupt ){ |
| 27712 | 28112 | p->cTerm = EOF; |
| 27713 | 28113 | return 0; |
| 27714 | 28114 | } |
| 27715 | 28115 | if( c=='"' ){ |
| | @@ -27716,11 +28116,11 @@ |
| 27716 | 28116 | int pc, ppc; |
| 27717 | 28117 | int startLine = p->nLine; |
| 27718 | 28118 | int cQuote = c; |
| 27719 | 28119 | pc = ppc = 0; |
| 27720 | 28120 | while( 1 ){ |
| 27721 | | - c = fgetc(p->in); |
| 28121 | + c = import_getc(p); |
| 27722 | 28122 | if( c==rSep ) p->nLine++; |
| 27723 | 28123 | if( c==cQuote ){ |
| 27724 | 28124 | if( pc==cQuote ){ |
| 27725 | 28125 | pc = 0; |
| 27726 | 28126 | continue; |
| | @@ -27752,24 +28152,24 @@ |
| 27752 | 28152 | }else{ |
| 27753 | 28153 | /* If this is the first field being parsed and it begins with the |
| 27754 | 28154 | ** UTF-8 BOM (0xEF BB BF) then skip the BOM */ |
| 27755 | 28155 | if( (c&0xff)==0xef && p->bNotFirst==0 ){ |
| 27756 | 28156 | import_append_char(p, c); |
| 27757 | | - c = fgetc(p->in); |
| 28157 | + c = import_getc(p); |
| 27758 | 28158 | if( (c&0xff)==0xbb ){ |
| 27759 | 28159 | import_append_char(p, c); |
| 27760 | | - c = fgetc(p->in); |
| 28160 | + c = import_getc(p); |
| 27761 | 28161 | if( (c&0xff)==0xbf ){ |
| 27762 | 28162 | p->bNotFirst = 1; |
| 27763 | 28163 | p->n = 0; |
| 27764 | 28164 | return csv_read_one_field(p); |
| 27765 | 28165 | } |
| 27766 | 28166 | } |
| 27767 | 28167 | } |
| 27768 | 28168 | while( c!=EOF && c!=cSep && c!=rSep ){ |
| 27769 | 28169 | import_append_char(p, c); |
| 27770 | | - c = fgetc(p->in); |
| 28170 | + c = import_getc(p); |
| 27771 | 28171 | } |
| 27772 | 28172 | if( c==rSep ){ |
| 27773 | 28173 | p->nLine++; |
| 27774 | 28174 | if( p->n>0 && p->z[p->n-1]=='\r' ) p->n--; |
| 27775 | 28175 | } |
| | @@ -27783,12 +28183,12 @@ |
| 27783 | 28183 | /* Read a single field of ASCII delimited text. |
| 27784 | 28184 | ** |
| 27785 | 28185 | ** + Input comes from p->in. |
| 27786 | 28186 | ** + Store results in p->z of length p->n. Space to hold p->z comes |
| 27787 | 28187 | ** from sqlite3_malloc64(). |
| 27788 | | -** + Use p->cSep as the column separator. The default is "\x1F". |
| 27789 | | -** + Use p->rSep as the row separator. The default is "\x1E". |
| 28188 | +** + Use p->cColSep as the column separator. The default is "\x1F". |
| 28189 | +** + Use p->cRowSep as the row separator. The default is "\x1E". |
| 27790 | 28190 | ** + Keep track of the row number in p->nLine. |
| 27791 | 28191 | ** + Store the character that terminates the field in p->cTerm. Store |
| 27792 | 28192 | ** EOF on end-of-file. |
| 27793 | 28193 | ** + Report syntax errors on stderr |
| 27794 | 28194 | */ |
| | @@ -27795,18 +28195,18 @@ |
| 27795 | 28195 | static char *SQLITE_CDECL ascii_read_one_field(ImportCtx *p){ |
| 27796 | 28196 | int c; |
| 27797 | 28197 | int cSep = (u8)p->cColSep; |
| 27798 | 28198 | int rSep = (u8)p->cRowSep; |
| 27799 | 28199 | p->n = 0; |
| 27800 | | - c = fgetc(p->in); |
| 28200 | + c = import_getc(p); |
| 27801 | 28201 | if( c==EOF || seenInterrupt ){ |
| 27802 | 28202 | p->cTerm = EOF; |
| 27803 | 28203 | return 0; |
| 27804 | 28204 | } |
| 27805 | 28205 | while( c!=EOF && c!=cSep && c!=rSep ){ |
| 27806 | 28206 | import_append_char(p, c); |
| 27807 | | - c = fgetc(p->in); |
| 28207 | + c = import_getc(p); |
| 27808 | 28208 | } |
| 27809 | 28209 | if( c==rSep ){ |
| 27810 | 28210 | p->nLine++; |
| 27811 | 28211 | } |
| 27812 | 28212 | p->cTerm = c; |
| | @@ -29721,11 +30121,13 @@ |
| 29721 | 30121 | |
| 29722 | 30122 | p = sqlite3_recover_init_sql( |
| 29723 | 30123 | pState->db, "main", recoverSqlCb, (void*)pState |
| 29724 | 30124 | ); |
| 29725 | 30125 | |
| 29726 | | - sqlite3_recover_config(p, 789, (void*)zRecoveryDb); /* Debug use only */ |
| 30126 | + if( !pState->bSafeMode ){ |
| 30127 | + sqlite3_recover_config(p, 789, (void*)zRecoveryDb); /* Debug use only */ |
| 30128 | + } |
| 29727 | 30129 | sqlite3_recover_config(p, SQLITE_RECOVER_LOST_AND_FOUND, (void*)zLAF); |
| 29728 | 30130 | sqlite3_recover_config(p, SQLITE_RECOVER_ROWIDS, (void*)&bRowids); |
| 29729 | 30131 | sqlite3_recover_config(p, SQLITE_RECOVER_FREELIST_CORRUPT,(void*)&bFreelist); |
| 29730 | 30132 | |
| 29731 | 30133 | cli_printf(pState->out, ".dbconfig defensive off\n"); |
| | @@ -30105,19 +30507,31 @@ |
| 30105 | 30507 | ** USAGE: .import [OPTIONS] FILE TABLE |
| 30106 | 30508 | ** |
| 30107 | 30509 | ** Import CSV or similar text from FILE into TABLE. If TABLE does |
| 30108 | 30510 | ** not exist, it is created using the first row of FILE as the column |
| 30109 | 30511 | ** names. If FILE begins with "|" then it is a command that is run |
| 30110 | | -** and the output from the command is used as the input data. |
| 30512 | +** and the output from the command is used as the input data. If |
| 30513 | +** FILE begins with "<<" followed by a label, then content is read from |
| 30514 | +** the script until the first line that matches the label. |
| 30515 | +** |
| 30516 | +** The content of FILE is interpreted using RFC-4180 ("CSV") quoting |
| 30517 | +** rules unless the current mode is "ascii" or "tabs" or unless one |
| 30518 | +** the --ascii option is used. |
| 30111 | 30519 | ** |
| 30112 | | -** FILE is assumed to be in a CSV format, unless the current mode |
| 30113 | | -** is "ascii" or "tabs" or unless one of the options below specify |
| 30114 | | -** an alternative. |
| 30520 | +** The column and row separators must be single ASCII characters. If |
| 30521 | +** multiple characters or a Unicode character are specified for the |
| 30522 | +** separators, then only the first byte of the separator is used. Except, |
| 30523 | +** if the row separator is \n and the mode is not --ascii, then \r\n is |
| 30524 | +** understood as a row separator too. |
| 30115 | 30525 | ** |
| 30116 | 30526 | ** Options: |
| 30117 | | -** --ascii Use \037 and \036 as column and row separators on input |
| 30527 | +** --ascii Do not use RFC-4180 quoting. Use \037 and \036 |
| 30528 | +** as column and row separators on input, unless other |
| 30529 | +** delimiters are specified using --colsep and/or --rowsep |
| 30530 | +** --colsep CHAR Use CHAR as the column separator. |
| 30118 | 30531 | ** --csv Input is standard RFC-4180 CSV. |
| 30532 | +** --rowsep CHAR Use CHAR as the row separator. |
| 30119 | 30533 | ** --schema S When creating TABLE, put it in schema S |
| 30120 | 30534 | ** --skip N Ignore the first N rows of input |
| 30121 | 30535 | ** -v Verbose mode |
| 30122 | 30536 | */ |
| 30123 | 30537 | static int dotCmdImport(ShellState *p){ |
| | @@ -30129,17 +30543,16 @@ |
| 30129 | 30543 | sqlite3_stmt *pStmt = NULL; /* A statement */ |
| 30130 | 30544 | int nCol; /* Number of columns in the table */ |
| 30131 | 30545 | i64 nByte; /* Number of bytes in an SQL string */ |
| 30132 | 30546 | int i, j; /* Loop counters */ |
| 30133 | 30547 | int needCommit; /* True to COMMIT or ROLLBACK at end */ |
| 30134 | | - int nSep; /* Number of bytes in spec.zColumnSep */ |
| 30135 | 30548 | char *zSql = 0; /* An SQL statement */ |
| 30136 | 30549 | ImportCtx sCtx; /* Reader context */ |
| 30137 | 30550 | char *(SQLITE_CDECL *xRead)(ImportCtx*); /* Func to read one value */ |
| 30138 | 30551 | int eVerbose = 0; /* Larger for more console output */ |
| 30139 | 30552 | i64 nSkip = 0; /* Initial lines to skip */ |
| 30140 | | - int useOutputMode = 1; /* Use output mode to determine separators */ |
| 30553 | + i64 iLineOffset = 0; /* Offset to the first line of input */ |
| 30141 | 30554 | char *zCreate = 0; /* CREATE TABLE statement text */ |
| 30142 | 30555 | int rc; /* Result code */ |
| 30143 | 30556 | |
| 30144 | 30557 | failIfSafeMode(p, "cannot run .import in safe mode"); |
| 30145 | 30558 | memset(&sCtx, 0, sizeof(sCtx)); |
| | @@ -30165,70 +30578,67 @@ |
| 30165 | 30578 | }else if( cli_strcmp(z,"-schema")==0 && i<nArg-1 ){ |
| 30166 | 30579 | zSchema = azArg[++i]; |
| 30167 | 30580 | }else if( cli_strcmp(z,"-skip")==0 && i<nArg-1 ){ |
| 30168 | 30581 | nSkip = integerValue(azArg[++i]); |
| 30169 | 30582 | }else if( cli_strcmp(z,"-ascii")==0 ){ |
| 30170 | | - sCtx.cColSep = SEP_Unit[0]; |
| 30171 | | - sCtx.cRowSep = SEP_Record[0]; |
| 30583 | + if( sCtx.cColSep==0 ) sCtx.cColSep = SEP_Unit[0]; |
| 30584 | + if( sCtx.cRowSep==0 ) sCtx.cRowSep = SEP_Record[0]; |
| 30172 | 30585 | xRead = ascii_read_one_field; |
| 30173 | | - useOutputMode = 0; |
| 30174 | 30586 | }else if( cli_strcmp(z,"-csv")==0 ){ |
| 30175 | | - sCtx.cColSep = ','; |
| 30176 | | - sCtx.cRowSep = '\n'; |
| 30587 | + if( sCtx.cColSep==0 ) sCtx.cColSep = ','; |
| 30588 | + if( sCtx.cRowSep==0 ) sCtx.cRowSep = '\n'; |
| 30177 | 30589 | xRead = csv_read_one_field; |
| 30178 | | - useOutputMode = 0; |
| 30590 | + }else if( cli_strcmp(z,"-colsep")==0 ){ |
| 30591 | + if( i==nArg-1 ){ |
| 30592 | + dotCmdError(p, i, "missing argument", 0); |
| 30593 | + return 1; |
| 30594 | + } |
| 30595 | + i++; |
| 30596 | + sCtx.cColSep = azArg[i][0]; |
| 30597 | + }else if( cli_strcmp(z,"-rowsep")==0 ){ |
| 30598 | + if( i==nArg-1 ){ |
| 30599 | + dotCmdError(p, i, "missing argument", 0); |
| 30600 | + return 1; |
| 30601 | + } |
| 30602 | + i++; |
| 30603 | + sCtx.cRowSep = azArg[i][0]; |
| 30179 | 30604 | }else{ |
| 30180 | 30605 | dotCmdError(p, i, "unknown option", 0); |
| 30181 | 30606 | return 1; |
| 30182 | 30607 | } |
| 30183 | 30608 | } |
| 30184 | 30609 | if( zTable==0 ){ |
| 30185 | | - cli_printf(p->out, "ERROR: missing %s argument\n", |
| 30610 | + dotCmdError(p, nArg, 0, "Missing %s argument\n", |
| 30186 | 30611 | zFile==0 ? "FILE" : "TABLE"); |
| 30187 | 30612 | return 1; |
| 30188 | 30613 | } |
| 30189 | 30614 | seenInterrupt = 0; |
| 30190 | 30615 | open_db(p, 0); |
| 30191 | | - if( useOutputMode ){ |
| 30192 | | - /* If neither the --csv or --ascii options are specified, then set |
| 30193 | | - ** the column and row separator characters from the output mode. */ |
| 30194 | | - if( p->mode.spec.zColumnSep==0 ){ |
| 30195 | | - modeSetStr(&p->mode.spec.zColumnSep, ","); |
| 30196 | | - nSep = 1; |
| 30197 | | - }else if( (nSep = strlen30(p->mode.spec.zColumnSep))==0 ){ |
| 30198 | | - eputz("Error: non-null column separator required for import\n"); |
| 30199 | | - return 1; |
| 30200 | | - } |
| 30201 | | - if( nSep>1 ){ |
| 30202 | | - eputz("Error: multi-character column separators not allowed" |
| 30203 | | - " for import\n"); |
| 30204 | | - return 1; |
| 30205 | | - } |
| 30206 | | - if( p->mode.spec.zRowSep==0 ){ |
| 30207 | | - modeSetStr(&p->mode.spec.zRowSep, "\n"); |
| 30208 | | - nSep = 1; |
| 30209 | | - }else if( (nSep = strlen30(p->mode.spec.zRowSep))==0 ){ |
| 30210 | | - eputz("Error: non-null row separator required for import\n"); |
| 30211 | | - return 1; |
| 30212 | | - } |
| 30213 | | - if( nSep==2 && p->mode.eMode==MODE_Csv |
| 30214 | | - && cli_strcmp(p->mode.spec.zRowSep,SEP_CrLf)==0 |
| 30215 | | - ){ |
| 30216 | | - /* When importing CSV (only), if the row separator is set to the |
| 30217 | | - ** default output row separator, change it to the default input |
| 30218 | | - ** row separator. This avoids having to maintain different input |
| 30219 | | - ** and output row separators. */ |
| 30220 | | - modeSetStr(&p->mode.spec.zRowSep, SEP_Row); |
| 30221 | | - nSep = strlen30(p->mode.spec.zRowSep); |
| 30222 | | - } |
| 30223 | | - if( nSep>1 ){ |
| 30224 | | - eputz("Error: multi-character row separators not allowed" |
| 30225 | | - " for import\n"); |
| 30226 | | - return 1; |
| 30227 | | - } |
| 30228 | | - sCtx.cColSep = (u8)p->mode.spec.zColumnSep[0]; |
| 30229 | | - sCtx.cRowSep = (u8)p->mode.spec.zRowSep[0]; |
| 30616 | + if( sCtx.cColSep==0 ){ |
| 30617 | + if( p->mode.spec.zColumnSep && p->mode.spec.zColumnSep[0]!=0 ){ |
| 30618 | + sCtx.cColSep = p->mode.spec.zColumnSep[0]; |
| 30619 | + }else{ |
| 30620 | + sCtx.cColSep = ','; |
| 30621 | + } |
| 30622 | + } |
| 30623 | + if( (sCtx.cColSep & 0x80)!=0 ){ |
| 30624 | + eputz("Error: .import column separator must be ASCII\n"); |
| 30625 | + return 1; |
| 30626 | + } |
| 30627 | + if( sCtx.cRowSep==0 ){ |
| 30628 | + if( p->mode.spec.zRowSep && p->mode.spec.zRowSep[0]!=0 ){ |
| 30629 | + sCtx.cRowSep = p->mode.spec.zRowSep[0]; |
| 30630 | + }else{ |
| 30631 | + sCtx.cRowSep = '\n'; |
| 30632 | + } |
| 30633 | + } |
| 30634 | + if( sCtx.cRowSep=='\r' && xRead!=ascii_read_one_field ){ |
| 30635 | + sCtx.cRowSep = '\n'; |
| 30636 | + } |
| 30637 | + if( (sCtx.cRowSep & 0x80)!=0 ){ |
| 30638 | + eputz("Error: .import row separator must be ASCII\n"); |
| 30639 | + return 1; |
| 30230 | 30640 | } |
| 30231 | 30641 | sCtx.zFile = zFile; |
| 30232 | 30642 | sCtx.nLine = 1; |
| 30233 | 30643 | if( sCtx.zFile[0]=='|' ){ |
| 30234 | 30644 | #ifdef SQLITE_OMIT_POPEN |
| | @@ -30237,19 +30647,58 @@ |
| 30237 | 30647 | #else |
| 30238 | 30648 | sCtx.in = sqlite3_popen(sCtx.zFile+1, "r"); |
| 30239 | 30649 | sCtx.zFile = "<pipe>"; |
| 30240 | 30650 | sCtx.xCloser = pclose; |
| 30241 | 30651 | #endif |
| 30652 | + }else if( sCtx.zFile[0]=='<' && sCtx.zFile[1]=='<' && sCtx.zFile[2]!=0 ){ |
| 30653 | + /* Input text comes from subsequent lines of script until the zFile |
| 30654 | + ** delimiter */ |
| 30655 | + int nEndMark = strlen30(zFile)-2; |
| 30656 | + char *zEndMark = &zFile[2]; |
| 30657 | + sqlite3_str *pContent = sqlite3_str_new(p->db); |
| 30658 | + int ckEnd = 1; |
| 30659 | + i64 iStart = p->lineno; |
| 30660 | + char zLine[2000]; |
| 30661 | + sCtx.zFile = p->zInFile; |
| 30662 | + sCtx.nLine = p->lineno+1; |
| 30663 | + iLineOffset = p->lineno; |
| 30664 | + while( sqlite3_fgets(zLine,sizeof(zLine),p->in) ){ |
| 30665 | + if( ckEnd && cli_strncmp(zLine,zEndMark,nEndMark)==0 ){ |
| 30666 | + ckEnd = 2; |
| 30667 | + if( strchr(zLine,'\n') ) p->lineno++; |
| 30668 | + break; |
| 30669 | + } |
| 30670 | + if( strchr(zLine,'\n') ){ |
| 30671 | + p->lineno++; |
| 30672 | + ckEnd = 1; |
| 30673 | + }else{ |
| 30674 | + ckEnd = 0; |
| 30675 | + } |
| 30676 | + sqlite3_str_appendall(pContent, zLine); |
| 30677 | + } |
| 30678 | + sCtx.zIn = sqlite3_str_finish(pContent); |
| 30679 | + if( sCtx.zIn==0 ){ |
| 30680 | + sCtx.zIn = sqlite3_mprintf(""); |
| 30681 | + } |
| 30682 | + if( ckEnd<2 ){ |
| 30683 | + i64 savedLn = p->lineno; |
| 30684 | + p->lineno = iStart; |
| 30685 | + dotCmdError(p, 0, 0,"Content terminator \"%s\" not found.",zEndMark); |
| 30686 | + p->lineno = savedLn; |
| 30687 | + import_cleanup(&sCtx); |
| 30688 | + return 1; |
| 30689 | + } |
| 30242 | 30690 | }else{ |
| 30243 | 30691 | sCtx.in = sqlite3_fopen(sCtx.zFile, "rb"); |
| 30244 | 30692 | sCtx.xCloser = fclose; |
| 30245 | 30693 | } |
| 30246 | | - if( sCtx.in==0 ){ |
| 30247 | | - cli_printf(stderr,"Error: cannot open \"%s\"\n", zFile); |
| 30694 | + if( sCtx.in==0 && sCtx.zIn==0 ){ |
| 30695 | + dotCmdError(p, 0, 0, "cannot open \"%s\"", zFile); |
| 30696 | + import_cleanup(&sCtx); |
| 30248 | 30697 | return 1; |
| 30249 | 30698 | } |
| 30250 | | - if( eVerbose>=2 || (eVerbose>=1 && useOutputMode) ){ |
| 30699 | + if( eVerbose>=1 ){ |
| 30251 | 30700 | char zSep[2]; |
| 30252 | 30701 | zSep[1] = 0; |
| 30253 | 30702 | zSep[0] = sCtx.cColSep; |
| 30254 | 30703 | cli_puts("Column separator ", p->out); |
| 30255 | 30704 | output_c_string(p->out, zSep); |
| | @@ -30402,10 +30851,14 @@ |
| 30402 | 30851 | if( z==0 && (xRead==csv_read_one_field) && i==nCol-1 && i>0 ){ |
| 30403 | 30852 | z = ""; |
| 30404 | 30853 | } |
| 30405 | 30854 | sqlite3_bind_text(pStmt, i+1, z, -1, SQLITE_TRANSIENT); |
| 30406 | 30855 | if( i<nCol-1 && sCtx.cTerm!=sCtx.cColSep ){ |
| 30856 | + if( i==0 && (strcmp(z,"\n")==0 || strcmp(z,"\r\n")==0) ){ |
| 30857 | + /* Ignore trailing \n or \r\n when some other row separator */ |
| 30858 | + break; |
| 30859 | + } |
| 30407 | 30860 | cli_printf(stderr,"%s:%d: expected %d columns but found %d" |
| 30408 | 30861 | " - filling the rest with NULL\n", |
| 30409 | 30862 | sCtx.zFile, startLine, nCol, i+1); |
| 30410 | 30863 | i += 2; |
| 30411 | 30864 | while( i<=nCol ){ sqlite3_bind_null(pStmt, i); i++; } |
| | @@ -30425,10 +30878,11 @@ |
| 30425 | 30878 | rc = sqlite3_reset(pStmt); |
| 30426 | 30879 | if( rc!=SQLITE_OK ){ |
| 30427 | 30880 | cli_printf(stderr,"%s:%d: INSERT failed: %s\n", |
| 30428 | 30881 | sCtx.zFile, startLine, sqlite3_errmsg(p->db)); |
| 30429 | 30882 | sCtx.nErr++; |
| 30883 | + if( bail_on_error ) break; |
| 30430 | 30884 | }else{ |
| 30431 | 30885 | sCtx.nRow++; |
| 30432 | 30886 | } |
| 30433 | 30887 | } |
| 30434 | 30888 | }while( sCtx.cTerm!=EOF ); |
| | @@ -30437,13 +30891,13 @@ |
| 30437 | 30891 | sqlite3_finalize(pStmt); |
| 30438 | 30892 | if( needCommit ) sqlite3_exec(p->db, "COMMIT", 0, 0, 0); |
| 30439 | 30893 | if( eVerbose>0 ){ |
| 30440 | 30894 | cli_printf(p->out, |
| 30441 | 30895 | "Added %d rows with %d errors using %d lines of input\n", |
| 30442 | | - sCtx.nRow, sCtx.nErr, sCtx.nLine-1); |
| 30896 | + sCtx.nRow, sCtx.nErr, sCtx.nLine-1-iLineOffset); |
| 30443 | 30897 | } |
| 30444 | | - return 0; |
| 30898 | + return sCtx.nErr ? 1 : 0; |
| 30445 | 30899 | } |
| 30446 | 30900 | |
| 30447 | 30901 | |
| 30448 | 30902 | /* |
| 30449 | 30903 | ** This function computes what to show the user about the configured |
| | @@ -30508,10 +30962,11 @@ |
| 30508 | 30962 | ** meaning "left", "centered", and "right", with |
| 30509 | 30963 | ** one letter per column starting from the left. |
| 30510 | 30964 | ** Unspecified alignment defaults to 'L'. |
| 30511 | 30965 | ** --blob-quote ARG ARG can be "auto", "text", "sql", "hex", "tcl", |
| 30512 | 30966 | ** "json", or "size". Default is "auto". |
| 30967 | +** --border on|off Show outer border on "box" and "table" modes. |
| 30513 | 30968 | ** --charlimit N Set the maximum number of output characters to |
| 30514 | 30969 | ** show for any single SQL value to N. Longer values |
| 30515 | 30970 | ** truncated. Zero means "no limit". |
| 30516 | 30971 | ** --colsep STRING Use STRING as the column separator |
| 30517 | 30972 | ** --escape ESC Enable/disable escaping of control characters |
| | @@ -30519,19 +30974,22 @@ |
| 30519 | 30974 | ** or "symbol". |
| 30520 | 30975 | ** --linelimit N Set the maximum number of output lines to show for |
| 30521 | 30976 | ** any single SQL value to N. Longer values are |
| 30522 | 30977 | ** truncated. Zero means "no limit". Only works |
| 30523 | 30978 | ** in "line" mode and in columnar modes. |
| 30524 | | -** --limits L,C Shorthand for "--linelimit L --charlimit C". |
| 30525 | | -** Or "off" to mean "0,0". Or "on" for "5,300". |
| 30979 | +** --limits L,C,T Shorthand for "--linelimit L --charlimit C |
| 30980 | +** --titlelimit T". The ",T" can be omitted in which |
| 30981 | +** case the --titlelimit is unchanged. The argument |
| 30982 | +** can also be "off" to mean "0,0,0" or "on" to |
| 30983 | +** mean "5,300,20". |
| 30526 | 30984 | ** --list List available modes |
| 30527 | 30985 | ** --null STRING Render SQL NULL values as the given string |
| 30528 | 30986 | ** --once Setting changes to the right are reverted after |
| 30529 | 30987 | ** the next SQL command. |
| 30530 | 30988 | ** --quote ARG Enable/disable quoting of text. ARG can be |
| 30531 | | -** "off", "on", "sql", "csv", "html", "tcl", |
| 30532 | | -** or "json". "off" means show the text as-is. |
| 30989 | +** "off", "on", "sql", "relaxed", "csv", "html", |
| 30990 | +** "tcl", or "json". "off" means show the text as-is. |
| 30533 | 30991 | ** "on" is an alias for "sql". |
| 30534 | 30992 | ** --reset Changes all mode settings back to their default. |
| 30535 | 30993 | ** --rowsep STRING Use STRING as the row separator |
| 30536 | 30994 | ** --sw|--screenwidth N Declare the screen width of the output device |
| 30537 | 30995 | ** to be N characters. An attempt may be made to |
| | @@ -30542,10 +31000,11 @@ |
| 30542 | 31000 | ** --tag NAME Save mode to the left as NAME. |
| 30543 | 31001 | ** --textjsonb BOOLEAN If enabled, JSONB text is displayed as text JSON. |
| 30544 | 31002 | ** --title ARG Whether or not to show column headers, and if so |
| 30545 | 31003 | ** how to encode them. ARG can be "off", "on", |
| 30546 | 31004 | ** "sql", "csv", "html", "tcl", or "json". |
| 31005 | +** --titlelimit N Limit the length of column titles to N characters. |
| 30547 | 31006 | ** -v|--verbose Verbose output |
| 30548 | 31007 | ** --widths LIST Set the columns widths for columnar modes. The |
| 30549 | 31008 | ** argument is a list of integers, one for each |
| 30550 | 31009 | ** column. A "0" width means use a dynamic width |
| 30551 | 31010 | ** based on the actual width of data. If there are |
| | @@ -30626,21 +31085,31 @@ |
| 30626 | 31085 | ** Must match QRF_BLOB_xxxx values. See also tag-20251124a */ |
| 30627 | 31086 | if( k>=0 ){ |
| 30628 | 31087 | p->mode.spec.eBlob = k & 0xff; |
| 30629 | 31088 | } |
| 30630 | 31089 | chng = 1; |
| 30631 | | - }else if( 0<=(k=pickStr(z,0,"-charlimit","-linelimit","")) ){ |
| 31090 | + }else if( optionMatch(z,"border") ){ |
| 31091 | + if( (++i)>=nArg ){ |
| 31092 | + dotCmdError(p, i-1, "missing argument", 0); |
| 31093 | + return 1; |
| 31094 | + } |
| 31095 | + k = pickStr(azArg[i], 0, "auto", "off", "on", ""); |
| 31096 | + if( k>=0 ){ |
| 31097 | + p->mode.spec.bBorder = k & 0x3; |
| 31098 | + } |
| 31099 | + chng = 1; |
| 31100 | + }else if( 0<=(k=pickStr(z,0,"-charlimit","-linelimit","-titlelimit","")) ){ |
| 30632 | 31101 | int w; /* 0 1 */ |
| 30633 | 31102 | if( i+1>=nArg ){ |
| 30634 | 31103 | dotCmdError(p, i, "missing argument", 0); |
| 30635 | 31104 | return 1; |
| 30636 | 31105 | } |
| 30637 | 31106 | w = integerValue(azArg[++i]); |
| 30638 | | - if( k==0 ){ |
| 30639 | | - p->mode.spec.nCharLimit = w; |
| 30640 | | - }else{ |
| 30641 | | - p->mode.spec.nLineLimit = w; |
| 31107 | + switch( k ){ |
| 31108 | + case 0: p->mode.spec.nCharLimit = w; break; |
| 31109 | + case 1: p->mode.spec.nLineLimit = w; break; |
| 31110 | + default: p->mode.spec.nTitleLimit = w; break; |
| 30642 | 31111 | } |
| 30643 | 31112 | chng = 1; |
| 30644 | 31113 | }else if( 0<=(k=pickStr(z,0,"-tablename","-rowsep","-colsep","-null","")) ){ |
| 30645 | 31114 | /* 0 1 2 3 */ |
| 30646 | 31115 | if( i+1>=nArg ){ |
| | @@ -30675,25 +31144,28 @@ |
| 30675 | 31144 | dotCmdError(p, i-1, "missing argument", 0); |
| 30676 | 31145 | return 1; |
| 30677 | 31146 | } |
| 30678 | 31147 | k = pickStr(azArg[i],0,"on","off",""); |
| 30679 | 31148 | if( k==0 ){ |
| 30680 | | - p->mode.spec.nLineLimit = 5; |
| 30681 | | - p->mode.spec.nCharLimit = 300; |
| 31149 | + p->mode.spec.nLineLimit = DFLT_LINE_LIMIT; |
| 31150 | + p->mode.spec.nCharLimit = DFLT_CHAR_LIMIT; |
| 31151 | + p->mode.spec.nTitleLimit = DFLT_TITLE_LIMIT; |
| 30682 | 31152 | }else if( k==1 ){ |
| 30683 | 31153 | p->mode.spec.nLineLimit = 0; |
| 30684 | 31154 | p->mode.spec.nCharLimit = 0; |
| 31155 | + p->mode.spec.nTitleLimit = 0; |
| 30685 | 31156 | }else{ |
| 30686 | | - int L, C; |
| 30687 | | - int nNum = sscanf(azArg[i], "%d,%d", &L, &C); |
| 30688 | | - if( nNum!=2 || L<0 || C<0 ){ |
| 30689 | | - dotCmdError(p, i, "bad argument", "Should be \"L,C\" where L and C" |
| 30690 | | - " are unsigned integers"); |
| 31157 | + int L, C, T = 0; |
| 31158 | + int nNum = sscanf(azArg[i], "%d,%d,%d", &L, &C, &T); |
| 31159 | + if( nNum<2 || L<0 || C<0 || T<0){ |
| 31160 | + dotCmdError(p, i, "bad argument", "Should be \"L,C,T\" where L, C" |
| 31161 | + " and T are unsigned integers"); |
| 30691 | 31162 | return 1; |
| 30692 | 31163 | } |
| 30693 | 31164 | p->mode.spec.nLineLimit = L; |
| 30694 | 31165 | p->mode.spec.nCharLimit = C; |
| 31166 | + if( nNum==3 ) p->mode.spec.nTitleLimit = T; |
| 30695 | 31167 | } |
| 30696 | 31168 | chng = 1; |
| 30697 | 31169 | }else if( optionMatch(z,"list") ){ |
| 30698 | 31170 | int ii; |
| 30699 | 31171 | cli_puts("available modes:", p->out); |
| | @@ -30726,13 +31198,14 @@ |
| 30726 | 31198 | ** how to set quoting. */ |
| 30727 | 31199 | i++; |
| 30728 | 31200 | if( (k = pickStr(azArg[i],0,"no","yes","0","1",""))>=0 ){ |
| 30729 | 31201 | k &= 1; /* 0 for "off". 1 for "on". */ |
| 30730 | 31202 | }else{ |
| 30731 | | - char *zErr = 0; /* 0 1 2 3 4 5 6 */ |
| 30732 | | - k = pickStr(azArg[i],&zErr,"off","on","sql","csv","html","tcl","json", |
| 30733 | | - ""); |
| 31203 | + char *zErr = 0; |
| 31204 | + k = pickStr(azArg[i],&zErr, |
| 31205 | + "off","on","sql","csv","html","tcl","json","relaxed",""); |
| 31206 | + /* 0 1 2 3 4 5 6 7 */ |
| 30734 | 31207 | if( k<0 ){ |
| 30735 | 31208 | dotCmdError(p, i, "unknown", "%z", zErr); |
| 30736 | 31209 | return 1; |
| 30737 | 31210 | } |
| 30738 | 31211 | } |
| | @@ -30757,10 +31230,13 @@ |
| 30757 | 31230 | p->mode.spec.eText = QRF_TEXT_Tcl; |
| 30758 | 31231 | break; |
| 30759 | 31232 | case 6: /* json */ |
| 30760 | 31233 | p->mode.spec.eText = QRF_TEXT_Json; |
| 30761 | 31234 | break; |
| 31235 | + case 7: /* relaxed */ |
| 31236 | + p->mode.spec.eText = QRF_TEXT_Relaxed; |
| 31237 | + break; |
| 30762 | 31238 | default: /* off */ |
| 30763 | 31239 | p->mode.spec.eText = QRF_TEXT_Plain; |
| 30764 | 31240 | break; |
| 30765 | 31241 | } |
| 30766 | 31242 | chng = 1; |
| | @@ -30832,10 +31308,11 @@ |
| 30832 | 31308 | if( k<0 ){ |
| 30833 | 31309 | dotCmdError(p, i, "bad --titles value","%z", zErr); |
| 30834 | 31310 | return 1; |
| 30835 | 31311 | } |
| 30836 | 31312 | p->mode.spec.bTitles = k>=1 ? QRF_Yes : QRF_No; |
| 31313 | + p->mode.mFlags &= ~MFLG_HDR; |
| 30837 | 31314 | p->mode.spec.eTitle = k>1 ? k-1 : aModeInfo[p->mode.eMode].eHdr; |
| 30838 | 31315 | chng = 1; |
| 30839 | 31316 | }else if( optionMatch(z,"widths") || optionMatch(z,"width") ){ |
| 30840 | 31317 | int nWidth = 0; |
| 30841 | 31318 | short int *aWidth; |
| | @@ -30847,16 +31324,16 @@ |
| 30847 | 31324 | zW = azArg[++i]; |
| 30848 | 31325 | /* Every width value takes at least 2 bytes in the input string to |
| 30849 | 31326 | ** specify, so strlen(zW) bytes should be plenty of space to hold the |
| 30850 | 31327 | ** result. */ |
| 30851 | 31328 | aWidth = malloc( strlen(zW) ); |
| 30852 | | - while( isspace(zW[0]) ) zW++; |
| 31329 | + while( IsSpace(zW[0]) ) zW++; |
| 30853 | 31330 | while( zW[0] ){ |
| 30854 | 31331 | int w = 0; |
| 30855 | 31332 | int nDigit = 0; |
| 30856 | | - k = zW[0]=='-' && isdigit(zW[1]); |
| 30857 | | - while( isdigit(zW[k]) ){ |
| 31333 | + k = zW[0]=='-' && IsDigit(zW[1]); |
| 31334 | + while( IsDigit(zW[k]) ){ |
| 30858 | 31335 | w = w*10 + zW[k] - '0'; |
| 30859 | 31336 | if( w>QRF_MAX_WIDTH ){ |
| 30860 | 31337 | dotCmdError(p,i+1,"width too big", |
| 30861 | 31338 | "Maximum column width is %d", QRF_MAX_WIDTH); |
| 30862 | 31339 | free(aWidth); |
| | @@ -30873,11 +31350,11 @@ |
| 30873 | 31350 | } |
| 30874 | 31351 | if( zW[0]=='-' ) w = -w; |
| 30875 | 31352 | aWidth[nWidth++] = w; |
| 30876 | 31353 | zW += k; |
| 30877 | 31354 | if( zW[0]==',' ) zW++; |
| 30878 | | - while( isspace(zW[0]) ) zW++; |
| 31355 | + while( IsSpace(zW[0]) ) zW++; |
| 30879 | 31356 | } |
| 30880 | 31357 | free(p->mode.spec.aWidth); |
| 30881 | 31358 | p->mode.spec.aWidth = aWidth; |
| 30882 | 31359 | p->mode.spec.nWidth = nWidth; |
| 30883 | 31360 | chng = 1; |
| | @@ -30928,39 +31405,52 @@ |
| 30928 | 31405 | unsigned char a = p->mode.spec.aAlign[ii]; |
| 30929 | 31406 | sqlite3_str_appendchar(pDesc, 1, "LLCR"[a&3]); |
| 30930 | 31407 | } |
| 30931 | 31408 | sqlite3_str_append(pDesc, "\"", 1); |
| 30932 | 31409 | } |
| 31410 | + if( bAll |
| 31411 | + || (p->mode.spec.bBorder==QRF_No) != ((pI->mFlg&1)!=0) |
| 31412 | + ){ |
| 31413 | + sqlite3_str_appendf(pDesc," --border %s", |
| 31414 | + p->mode.spec.bBorder==QRF_No ? "off" : "on"); |
| 31415 | + } |
| 30933 | 31416 | if( bAll || p->mode.spec.eBlob!=QRF_BLOB_Auto ){ |
| 30934 | 31417 | const char *azBQuote[] = |
| 30935 | 31418 | { "auto", "text", "sql", "hex", "tcl", "json", "size" }; |
| 30936 | 31419 | /* 0 1 2 3 4 5 6 |
| 30937 | 31420 | ** Must match QRF_BLOB_xxxx values. See all instances of tag-20251124a */ |
| 30938 | 31421 | u8 e = p->mode.spec.eBlob; |
| 30939 | 31422 | sqlite3_str_appendf(pDesc, " --blob-quote %s", azBQuote[e]); |
| 30940 | 31423 | } |
| 30941 | | - if( !bAll && p->mode.spec.nLineLimit==0 && p->mode.spec.nCharLimit>0 ){ |
| 30942 | | - sqlite3_str_appendf(pDesc, " --charlimit %d",p->mode.spec.nCharLimit); |
| 30943 | | - } |
| 30944 | 31424 | zSetting = aModeStr[pI->eCSep]; |
| 30945 | 31425 | if( bAll || (zSetting && cli_strcmp(zSetting,p->mode.spec.zColumnSep)!=0) ){ |
| 30946 | 31426 | sqlite3_str_appendf(pDesc, " --colsep "); |
| 30947 | 31427 | append_c_string(pDesc, p->mode.spec.zColumnSep); |
| 30948 | 31428 | } |
| 30949 | 31429 | if( bAll || p->mode.spec.eEsc!=QRF_Auto ){ |
| 30950 | 31430 | sqlite3_str_appendf(pDesc, " --escape %s",qrfEscNames[p->mode.spec.eEsc]); |
| 30951 | 31431 | } |
| 30952 | | - if( bAll || (p->mode.spec.nLineLimit>0 && p->mode.spec.nCharLimit>0) ){ |
| 30953 | | - if( p->mode.spec.nLineLimit==0 && p->mode.spec.nCharLimit==0 ){ |
| 31432 | + if( bAll |
| 31433 | + || (p->mode.spec.nLineLimit>0 && pI->eCx>0) |
| 31434 | + || p->mode.spec.nCharLimit>0 |
| 31435 | + || (p->mode.spec.nTitleLimit>0 && pI->eCx>0) |
| 31436 | + ){ |
| 31437 | + if( p->mode.spec.nLineLimit==0 |
| 31438 | + && p->mode.spec.nCharLimit==0 |
| 31439 | + && p->mode.spec.nTitleLimit==0 |
| 31440 | + ){ |
| 30954 | 31441 | sqlite3_str_appendf(pDesc, " --limits off"); |
| 31442 | + }else if( p->mode.spec.nLineLimit==DFLT_LINE_LIMIT |
| 31443 | + && p->mode.spec.nCharLimit==DFLT_CHAR_LIMIT |
| 31444 | + && p->mode.spec.nTitleLimit==DFLT_TITLE_LIMIT |
| 31445 | + ){ |
| 31446 | + sqlite3_str_appendf(pDesc, " --limits on"); |
| 30955 | 31447 | }else{ |
| 30956 | | - sqlite3_str_appendf(pDesc, " --limits %d,%d", |
| 30957 | | - p->mode.spec.nLineLimit, p->mode.spec.nCharLimit); |
| 31448 | + sqlite3_str_appendf(pDesc, " --limits %d,%d,%d", |
| 31449 | + p->mode.spec.nLineLimit, p->mode.spec.nCharLimit, |
| 31450 | + p->mode.spec.nTitleLimit); |
| 30958 | 31451 | } |
| 30959 | | - }else |
| 30960 | | - if( p->mode.spec.nCharLimit==0 && p->mode.spec.nLineLimit>0 && pI->eCx>0 ){ |
| 30961 | | - sqlite3_str_appendf(pDesc, " --linelimit %d",p->mode.spec.nLineLimit); |
| 30962 | 31452 | } |
| 30963 | 31453 | zSetting = aModeStr[pI->eNull]; |
| 30964 | 31454 | if( bAll || (zSetting && cli_strcmp(zSetting,p->mode.spec.zNull)!=0) ){ |
| 30965 | 31455 | sqlite3_str_appendf(pDesc, " --null "); |
| 30966 | 31456 | append_c_string(pDesc, p->mode.spec.zNull); |
| | @@ -30977,13 +31467,13 @@ |
| 30977 | 31467 | } |
| 30978 | 31468 | if( bAll |
| 30979 | 31469 | || (pI->eCx && (p->mode.spec.nScreenWidth>0 || p->mode.bAutoScreenWidth)) |
| 30980 | 31470 | ){ |
| 30981 | 31471 | if( p->mode.bAutoScreenWidth ){ |
| 30982 | | - sqlite3_str_appendall(pDesc, " --screenwidth auto"); |
| 31472 | + sqlite3_str_appendall(pDesc, " --sw auto"); |
| 30983 | 31473 | }else{ |
| 30984 | | - sqlite3_str_appendf(pDesc," --screenwidth %d", |
| 31474 | + sqlite3_str_appendf(pDesc," --sw %d", |
| 30985 | 31475 | p->mode.spec.nScreenWidth); |
| 30986 | 31476 | } |
| 30987 | 31477 | } |
| 30988 | 31478 | if( bAll || p->mode.eMode==MODE_Insert ){ |
| 30989 | 31479 | sqlite3_str_appendf(pDesc," --tablename "); |
| | @@ -31023,11 +31513,11 @@ |
| 31023 | 31513 | sqlite3_str_appendf(pDesc, " --wrap %d", p->mode.spec.nWrap); |
| 31024 | 31514 | } |
| 31025 | 31515 | if( !bAll ) sqlite3_str_append(pDesc, " --ww", 5); |
| 31026 | 31516 | } |
| 31027 | 31517 | zDesc = sqlite3_str_finish(pDesc); |
| 31028 | | - cli_printf(p->out, "current output mode: %s\n", zDesc); |
| 31518 | + cli_printf(p->out, ".mode %s\n", zDesc); |
| 31029 | 31519 | fflush(p->out); |
| 31030 | 31520 | sqlite3_free(zDesc); |
| 31031 | 31521 | } |
| 31032 | 31522 | return 0; |
| 31033 | 31523 | } |
| | @@ -31047,23 +31537,17 @@ |
| 31047 | 31537 | ** --bom Prepend a byte-order mark to the output |
| 31048 | 31538 | ** -e Accumulate output in a temporary text file then |
| 31049 | 31539 | ** launch a text editor when the redirection ends. |
| 31050 | 31540 | ** --error-prefix X Use X as the left-margin prefix for error messages. |
| 31051 | 31541 | ** Set to an empty string to restore the default. |
| 31052 | | -** --glob GLOB Raise an error if the memory buffer does not match |
| 31053 | | -** the GLOB pattern. |
| 31054 | | -** --keep Continue using the same "memory" buffer. Do not |
| 31055 | | -** reset it or delete it. Useful in combination with |
| 31056 | | -** --glob, --not-glob, and/or --verify. |
| 31057 | | -** ---notglob GLOB Raise an error if the memory buffer does not match |
| 31058 | | -** the GLOB pattern. |
| 31542 | +** --keep Keep redirecting output to its current destination. |
| 31543 | +** Use this option in combination with --show or |
| 31544 | +** with --error-prefix when you do not want to stop |
| 31545 | +** a current redirection. |
| 31059 | 31546 | ** --plain Use plain text rather than HTML tables with -w |
| 31060 | | -** --show Write the memory buffer to the screen, for debugging. |
| 31061 | | -** --verify ENDMARK Read subsequent lines of text until the first line |
| 31062 | | -** that matches ENDMARK. Discard the ENDMARK. Compare |
| 31063 | | -** the text against the accumulated output in memory and |
| 31064 | | -** raise an error if there are any differences. |
| 31547 | +** --show Show output text captured by .testcase or by |
| 31548 | +** redirecting to "memory". |
| 31065 | 31549 | ** -w Show the output in a web browser. Output is |
| 31066 | 31550 | ** written into a temporary HTML file until the |
| 31067 | 31551 | ** redirect ends, then the web browser is launched. |
| 31068 | 31552 | ** Query results are shown as HTML tables, unless |
| 31069 | 31553 | ** the --plain is used too. |
| | @@ -31101,13 +31585,11 @@ |
| 31101 | 31585 | char *zFile = 0; /* The FILE argument */ |
| 31102 | 31586 | int i; /* Loop counter */ |
| 31103 | 31587 | int eMode = 0; /* 0: .outout/.once, 'x'=.excel, 'w'=.www */ |
| 31104 | 31588 | int bOnce = 0; /* 0: .output, 1: .once, 2: .excel/.www */ |
| 31105 | 31589 | int bPlain = 0; /* --plain option */ |
| 31106 | | - int bKeep = 0; /* --keep option */ |
| 31107 | | - char *zCheck = 0; /* Argument to --glob, --notglob, --verify */ |
| 31108 | | - int eCheck = 0; /* 1: --glob, 2: --notglob, 3: --verify */ |
| 31590 | + int bKeep = 0; /* Keep redirecting */ |
| 31109 | 31591 | static const char *zBomUtf8 = "\357\273\277"; |
| 31110 | 31592 | const char *zBom = 0; |
| 31111 | 31593 | char c = azArg[0][0]; |
| 31112 | 31594 | int n = strlen30(azArg[0]); |
| 31113 | 31595 | |
| | @@ -31129,36 +31611,21 @@ |
| 31129 | 31611 | zBom = zBomUtf8; |
| 31130 | 31612 | }else if( cli_strcmp(z,"-plain")==0 ){ |
| 31131 | 31613 | bPlain = 1; |
| 31132 | 31614 | }else if( c=='o' && z[0]=='1' && z[1]!=0 && z[2]==0 |
| 31133 | 31615 | && (z[1]=='x' || z[1]=='e' || z[1]=='w') ){ |
| 31134 | | - if( bKeep || eMode || eCheck ){ |
| 31616 | + if( bKeep || eMode ){ |
| 31135 | 31617 | dotCmdError(p, i, "incompatible with prior options",0); |
| 31136 | 31618 | goto dotCmdOutput_error; |
| 31137 | 31619 | } |
| 31138 | 31620 | eMode = z[1]; |
| 31139 | | - }else if( cli_strcmp(z,"-keep")==0 ){ |
| 31140 | | - bKeep = 1; |
| 31141 | 31621 | }else if( cli_strcmp(z,"-show")==0 ){ |
| 31142 | 31622 | if( cli_output_capture ){ |
| 31143 | 31623 | sqlite3_fprintf(stdout, "%s", sqlite3_str_value(cli_output_capture)); |
| 31144 | 31624 | } |
| 31625 | + }else if( cli_strcmp(z,"-keep")==0 ){ |
| 31145 | 31626 | bKeep = 1; |
| 31146 | | - }else if( cli_strcmp(z,"-glob")==0 |
| 31147 | | - || cli_strcmp(z,"-notglob")==0 |
| 31148 | | - || cli_strcmp(z,"-verify")==0 |
| 31149 | | - ){ |
| 31150 | | - if( eCheck || eMode ){ |
| 31151 | | - dotCmdError(p, i, "incompatible with prior options",0); |
| 31152 | | - goto dotCmdOutput_error; |
| 31153 | | - } |
| 31154 | | - if( i+1>=nArg ){ |
| 31155 | | - dotCmdError(p, i, "missing argument", 0); |
| 31156 | | - goto dotCmdOutput_error; |
| 31157 | | - } |
| 31158 | | - zCheck = azArg[++i]; |
| 31159 | | - eCheck = z[1]=='g' ? 1 : z[1]=='n' ? 2 : 3; |
| 31160 | 31627 | }else if( optionMatch(z,"error-prefix") ){ |
| 31161 | 31628 | if( i+1>=nArg ){ |
| 31162 | 31629 | dotCmdError(p, i, "missing argument", 0); |
| 31163 | 31630 | return 1; |
| 31164 | 31631 | } |
| | @@ -31169,11 +31636,11 @@ |
| 31169 | 31636 | dotCmdError(p, i, "unknown option", 0); |
| 31170 | 31637 | sqlite3_free(zFile); |
| 31171 | 31638 | return 1; |
| 31172 | 31639 | } |
| 31173 | 31640 | }else if( zFile==0 && eMode==0 ){ |
| 31174 | | - if( bKeep || eCheck ){ |
| 31641 | + if( bKeep ){ |
| 31175 | 31642 | dotCmdError(p, i, "incompatible with prior options",0); |
| 31176 | 31643 | goto dotCmdOutput_error; |
| 31177 | 31644 | } |
| 31178 | 31645 | if( cli_strcmp(z, "memory")==0 && bOnce ){ |
| 31179 | 31646 | dotCmdError(p, 0, "cannot redirect to \"memory\"", 0); |
| | @@ -31205,53 +31672,10 @@ |
| 31205 | 31672 | if( bOnce ){ |
| 31206 | 31673 | p->nPopOutput = 2; |
| 31207 | 31674 | }else{ |
| 31208 | 31675 | p->nPopOutput = 0; |
| 31209 | 31676 | } |
| 31210 | | - if( eCheck ){ |
| 31211 | | - char *zTest; |
| 31212 | | - if( cli_output_capture ){ |
| 31213 | | - zTest = sqlite3_str_value(cli_output_capture); |
| 31214 | | - }else{ |
| 31215 | | - zTest = ""; |
| 31216 | | - } |
| 31217 | | - p->nTestRun++; |
| 31218 | | - if( eCheck==3 ){ |
| 31219 | | - int nCheck = strlen30(zCheck); |
| 31220 | | - sqlite3_str *pPattern = sqlite3_str_new(p->db); |
| 31221 | | - char *zPattern; |
| 31222 | | - sqlite3_int64 iStart = p->lineno; |
| 31223 | | - char zLine[2000]; |
| 31224 | | - while( sqlite3_fgets(zLine,sizeof(zLine),p->in) ){ |
| 31225 | | - if( strchr(zLine,'\n') ) p->lineno++; |
| 31226 | | - if( cli_strncmp(zCheck,zLine,nCheck)==0 ) break; |
| 31227 | | - sqlite3_str_appendall(pPattern, zLine); |
| 31228 | | - } |
| 31229 | | - zPattern = sqlite3_str_finish(pPattern); |
| 31230 | | - if( cli_strcmp(zPattern,zTest)!=0 ){ |
| 31231 | | - sqlite3_fprintf(stderr, |
| 31232 | | - "%s:%lld: --verify does matches prior output\n", |
| 31233 | | - p->zInFile, iStart); |
| 31234 | | - p->nTestErr++; |
| 31235 | | - } |
| 31236 | | - sqlite3_free(zPattern); |
| 31237 | | - }else{ |
| 31238 | | - char *zGlob = sqlite3_mprintf("*%s*", zCheck); |
| 31239 | | - if( eCheck==1 && sqlite3_strglob(zGlob, zTest)!=0 ){ |
| 31240 | | - sqlite3_fprintf(stderr, |
| 31241 | | - "%s:%lld: --glob \"%s\" does not match prior output\n", |
| 31242 | | - p->zInFile, p->lineno, zCheck); |
| 31243 | | - p->nTestErr++; |
| 31244 | | - }else if( eCheck==2 && sqlite3_strglob(zGlob, zTest)==0 ){ |
| 31245 | | - sqlite3_fprintf(stderr, |
| 31246 | | - "%s:%lld: --notglob \"%s\" matches prior output\n", |
| 31247 | | - p->zInFile, p->lineno, zCheck); |
| 31248 | | - p->nTestErr++; |
| 31249 | | - } |
| 31250 | | - sqlite3_free(zGlob); |
| 31251 | | - } |
| 31252 | | - } |
| 31253 | 31677 | if( !bKeep ) output_reset(p); |
| 31254 | 31678 | #ifndef SQLITE_NOHAVE_SYSTEM |
| 31255 | 31679 | if( eMode=='e' || eMode=='x' || eMode=='w' ){ |
| 31256 | 31680 | p->doXdgOpen = 1; |
| 31257 | 31681 | modePush(p); |
| | @@ -31328,10 +31752,199 @@ |
| 31328 | 31752 | |
| 31329 | 31753 | dotCmdOutput_error: |
| 31330 | 31754 | sqlite3_free(zFile); |
| 31331 | 31755 | return 1; |
| 31332 | 31756 | } |
| 31757 | + |
| 31758 | +/* |
| 31759 | +** DOT-COMMAND: .check |
| 31760 | +** USAGE: .check [OPTIONS] PATTERN |
| 31761 | +** |
| 31762 | +** Verify results of commands since the most recent .testcase command. |
| 31763 | +** Restore output to the console, unless --keep is used. |
| 31764 | +** |
| 31765 | +** If PATTERN starts with "<<ENDMARK" then the actual pattern is taken from |
| 31766 | +** subsequent lines of text up to the first line that begins with ENDMARK. |
| 31767 | +** All pattern lines and the ENDMARK are discarded. |
| 31768 | +** |
| 31769 | +** Options: |
| 31770 | +** --exact Do an exact comparison including leading and |
| 31771 | +** trailing whitespace. |
| 31772 | +** --glob Treat PATTERN as a GLOB |
| 31773 | +** --keep Do not reset the testcase. More .check commands |
| 31774 | +** will follow. |
| 31775 | +** --notglob Output should not match PATTERN |
| 31776 | +** --show Write testcase output to the screen, for debugging. |
| 31777 | +*/ |
| 31778 | +static int dotCmdCheck(ShellState *p){ |
| 31779 | + int nArg = p->dot.nArg; /* Number of arguments */ |
| 31780 | + char **azArg = p->dot.azArg; /* Text of the arguments */ |
| 31781 | + int i; /* Loop counter */ |
| 31782 | + int k; /* Result of pickStr() */ |
| 31783 | + char *zTest; /* Textcase result */ |
| 31784 | + int bKeep = 0; /* --keep option */ |
| 31785 | + char *zCheck = 0; /* PATTERN argument */ |
| 31786 | + char *zPattern = 0; /* Actual test pattern */ |
| 31787 | + int eCheck = 0; /* 1: --glob, 2: --notglob, 3: --exact */ |
| 31788 | + int isOk; /* True if results are OK */ |
| 31789 | + sqlite3_int64 iStart = p->lineno; /* Line number of .check statement */ |
| 31790 | + |
| 31791 | + if( p->zTestcase[0]==0 ){ |
| 31792 | + dotCmdError(p, 0, "no .testcase is active", 0); |
| 31793 | + return 1; |
| 31794 | + } |
| 31795 | + for(i=1; i<nArg; i++){ |
| 31796 | + char *z = azArg[i]; |
| 31797 | + if( z[0]=='-' && z[1]=='-' && z[2]!=0 ) z++; |
| 31798 | + if( cli_strcmp(z,"-keep")==0 ){ |
| 31799 | + bKeep = 1; |
| 31800 | + }else if( cli_strcmp(z,"-show")==0 ){ |
| 31801 | + if( cli_output_capture ){ |
| 31802 | + sqlite3_fprintf(stdout, "%s", sqlite3_str_value(cli_output_capture)); |
| 31803 | + } |
| 31804 | + bKeep = 1; |
| 31805 | + }else if( z[0]=='-' |
| 31806 | + && (k = pickStr(&z[1],0,"glob","notglob","exact",""))>=0 |
| 31807 | + ){ |
| 31808 | + if( eCheck && eCheck!=k+1 ){ |
| 31809 | + dotCmdError(p, i, "incompatible with prior options",0); |
| 31810 | + return 1; |
| 31811 | + } |
| 31812 | + eCheck = k+1; |
| 31813 | + }else if( zCheck ){ |
| 31814 | + dotCmdError(p, i, "unknown option", 0); |
| 31815 | + return 1; |
| 31816 | + }else{ |
| 31817 | + zCheck = azArg[i]; |
| 31818 | + } |
| 31819 | + } |
| 31820 | + if( zCheck==0 ){ |
| 31821 | + dotCmdError(p, 0, "no PATTERN specified", 0); |
| 31822 | + return 1; |
| 31823 | + } |
| 31824 | + if( cli_output_capture && sqlite3_str_length(cli_output_capture) ){ |
| 31825 | + zTest = sqlite3_str_value(cli_output_capture); |
| 31826 | + shell_check_oom(zTest); |
| 31827 | + }else{ |
| 31828 | + zTest = ""; |
| 31829 | + } |
| 31830 | + p->nTestRun++; |
| 31831 | + if( zCheck[0]=='<' && zCheck[1]=='<' && zCheck[2]!=0 ){ |
| 31832 | + int nCheck = strlen30(zCheck); |
| 31833 | + sqlite3_str *pPattern = sqlite3_str_new(p->db); |
| 31834 | + char zLine[2000]; |
| 31835 | + while( sqlite3_fgets(zLine,sizeof(zLine),p->in) ){ |
| 31836 | + if( strchr(zLine,'\n') ) p->lineno++; |
| 31837 | + if( cli_strncmp(&zCheck[2],zLine,nCheck-2)==0 ) break; |
| 31838 | + sqlite3_str_appendall(pPattern, zLine); |
| 31839 | + } |
| 31840 | + zPattern = sqlite3_str_finish(pPattern); |
| 31841 | + if( zPattern==0 ){ |
| 31842 | + zPattern = sqlite3_mprintf(""); |
| 31843 | + } |
| 31844 | + }else{ |
| 31845 | + zPattern = zCheck; |
| 31846 | + } |
| 31847 | + shell_check_oom(zPattern); |
| 31848 | + switch( eCheck ){ |
| 31849 | + case 1: { |
| 31850 | + char *zGlob = sqlite3_mprintf("*%s*", zPattern); |
| 31851 | + isOk = testcase_glob(zGlob, zTest)!=0; |
| 31852 | + sqlite3_free(zGlob); |
| 31853 | + break; |
| 31854 | + } |
| 31855 | + case 2: { |
| 31856 | + char *zGlob = sqlite3_mprintf("*%s*", zPattern); |
| 31857 | + isOk = testcase_glob(zGlob, zTest)==0; |
| 31858 | + sqlite3_free(zGlob); |
| 31859 | + break; |
| 31860 | + } |
| 31861 | + case 3: { |
| 31862 | + isOk = cli_strcmp(zTest,zPattern)==0; |
| 31863 | + break; |
| 31864 | + } |
| 31865 | + default: { |
| 31866 | + /* Skip leading and trailing \n and \r on both pattern and test output */ |
| 31867 | + const char *z1 = zPattern; |
| 31868 | + const char *z2 = zTest; |
| 31869 | + size_t n1, n2; |
| 31870 | + while( z1[0]=='\n' || z1[0]=='\r' ) z1++; |
| 31871 | + n1 = strlen(z1); |
| 31872 | + while( n1>0 && (z1[n1-1]=='\n' || z1[n1-1]=='\r') ) n1--; |
| 31873 | + while( z2[0]=='\n' || z2[0]=='\r' ) z2++; |
| 31874 | + n2 = strlen(z2); |
| 31875 | + while( n2>0 && (z2[n2-1]=='\n' || z2[n2-1]=='\r') ) n2--; |
| 31876 | + isOk = n1==n2 && memcmp(z1,z2,n1)==0; |
| 31877 | + break; |
| 31878 | + } |
| 31879 | + } |
| 31880 | + if( !isOk ){ |
| 31881 | + sqlite3_fprintf(stderr, |
| 31882 | + "%s:%lld: .check failed for testcase %s\n", |
| 31883 | + p->zInFile, iStart, p->zTestcase); |
| 31884 | + p->nTestErr++; |
| 31885 | + sqlite3_fprintf(stderr, "Expected: [%s]\n", zPattern); |
| 31886 | + sqlite3_fprintf(stderr, "Got: [%s]\n", zTest); |
| 31887 | + } |
| 31888 | + if( zPattern!=zCheck ){ |
| 31889 | + sqlite3_free(zPattern); |
| 31890 | + } |
| 31891 | + if( !bKeep ){ |
| 31892 | + output_reset(p); |
| 31893 | + p->zTestcase[0] = 0; |
| 31894 | + } |
| 31895 | + return 0; |
| 31896 | +} |
| 31897 | + |
| 31898 | +/* |
| 31899 | +** DOT-COMMAND: .testcase |
| 31900 | +** USAGE: .testcase [OPTIONS] NAME |
| 31901 | +** |
| 31902 | +** Start a new test case identified by NAME. All output |
| 31903 | +** through the next ".check" command is captured for comparison. See the |
| 31904 | +** ".check" commandn for additional informatioon. |
| 31905 | +** |
| 31906 | +** Options: |
| 31907 | +** --error-prefix TEXT Change error message prefix text to TEXT |
| 31908 | +*/ |
| 31909 | +static int dotCmdTestcase(ShellState *p){ |
| 31910 | + int nArg = p->dot.nArg; /* Number of arguments */ |
| 31911 | + char **azArg = p->dot.azArg; /* Text of the arguments */ |
| 31912 | + int i; /* Loop counter */ |
| 31913 | + const char *zName = 0; /* Testcase name */ |
| 31914 | + |
| 31915 | + for(i=1; i<nArg; i++){ |
| 31916 | + char *z = azArg[i]; |
| 31917 | + if( z[0]=='-' && z[1]=='-' && z[2]!=0 ) z++; |
| 31918 | + if( optionMatch(z,"error-prefix") ){ |
| 31919 | + if( i+1>=nArg ){ |
| 31920 | + dotCmdError(p, i, "missing argument", 0); |
| 31921 | + return 1; |
| 31922 | + } |
| 31923 | + free(p->zErrPrefix); |
| 31924 | + i++; |
| 31925 | + p->zErrPrefix = azArg[i][0]==0 ? 0 : strdup(azArg[i]); |
| 31926 | + }else if( zName ){ |
| 31927 | + dotCmdError(p, i, "unknown option", 0); |
| 31928 | + return 1; |
| 31929 | + }else{ |
| 31930 | + zName = azArg[i]; |
| 31931 | + } |
| 31932 | + } |
| 31933 | + output_reset(p); |
| 31934 | + if( cli_output_capture ){ |
| 31935 | + sqlite3_str_free(cli_output_capture); |
| 31936 | + } |
| 31937 | + cli_output_capture = sqlite3_str_new(0); |
| 31938 | + if( zName ){ |
| 31939 | + sqlite3_snprintf(sizeof(p->zTestcase), p->zTestcase, "%s", zName); |
| 31940 | + }else{ |
| 31941 | + sqlite3_snprintf(sizeof(p->zTestcase), p->zTestcase, "%s:%lld", |
| 31942 | + p->zInFile, p->lineno); |
| 31943 | + } |
| 31944 | + return 0; |
| 31945 | +} |
| 31333 | 31946 | |
| 31334 | 31947 | /* |
| 31335 | 31948 | ** Enlarge the space allocated in p->dot so that it can hold more |
| 31336 | 31949 | ** than nArg parsed command-line arguments. |
| 31337 | 31950 | */ |
| | @@ -31360,11 +31973,11 @@ |
| 31360 | 31973 | free(p->dot.zCopy); |
| 31361 | 31974 | z = p->dot.zCopy = strdup(zLine); |
| 31362 | 31975 | shell_check_oom(z); |
| 31363 | 31976 | szLine = strlen(z); |
| 31364 | 31977 | while( szLine>0 && IsSpace(z[szLine-1]) ) szLine--; |
| 31365 | | - if( szLine>0 && z[szLine-1]==';' && p->iCompat>=20251115 ){ |
| 31978 | + if( szLine>0 && z[szLine-1]==';' ){ |
| 31366 | 31979 | szLine--; |
| 31367 | 31980 | while( szLine>0 && IsSpace(z[szLine-1]) ) szLine--; |
| 31368 | 31981 | } |
| 31369 | 31982 | z[szLine] = 0; |
| 31370 | 31983 | parseDotRealloc(p, 2); |
| | @@ -31580,35 +32193,17 @@ |
| 31580 | 32193 | eputz("Usage: .changes on|off\n"); |
| 31581 | 32194 | rc = 1; |
| 31582 | 32195 | } |
| 31583 | 32196 | }else |
| 31584 | 32197 | |
| 31585 | | -#ifndef SQLITE_SHELL_FIDDLE |
| 31586 | 32198 | /* Cancel output redirection, if it is currently set (by .testcase) |
| 31587 | 32199 | ** Then read the content of the testcase-out.txt file and compare against |
| 31588 | 32200 | ** azArg[1]. If there are differences, report an error and exit. |
| 31589 | 32201 | */ |
| 31590 | 32202 | if( c=='c' && n>=3 && cli_strncmp(azArg[0], "check", n)==0 ){ |
| 31591 | | - char *zRes = 0; |
| 31592 | | - output_reset(p); |
| 31593 | | - if( nArg!=2 ){ |
| 31594 | | - eputz("Usage: .check GLOB-PATTERN\n"); |
| 31595 | | - rc = 2; |
| 31596 | | - }else if( (zRes = readFile("testcase-out.txt", 0))==0 ){ |
| 31597 | | - rc = 2; |
| 31598 | | - }else if( testcase_glob(azArg[1],zRes)==0 ){ |
| 31599 | | - cli_printf(stderr, |
| 31600 | | - "testcase-%s FAILED\n Expected: [%s]\n Got: [%s]\n", |
| 31601 | | - p->zTestcase, azArg[1], zRes); |
| 31602 | | - rc = 1; |
| 31603 | | - }else{ |
| 31604 | | - cli_printf(p->out, "testcase-%s ok\n", p->zTestcase); |
| 31605 | | - p->nCheck++; |
| 31606 | | - } |
| 31607 | | - sqlite3_free(zRes); |
| 32203 | + rc = dotCmdCheck(p); |
| 31608 | 32204 | }else |
| 31609 | | -#endif /* !defined(SQLITE_SHELL_FIDDLE) */ |
| 31610 | 32205 | |
| 31611 | 32206 | #ifndef SQLITE_SHELL_FIDDLE |
| 31612 | 32207 | if( c=='c' && cli_strncmp(azArg[0], "clone", n)==0 ){ |
| 31613 | 32208 | failIfSafeMode(p, "cannot run .clone in safe mode"); |
| 31614 | 32209 | if( nArg==2 ){ |
| | @@ -32187,10 +32782,11 @@ |
| 32187 | 32782 | }else |
| 32188 | 32783 | |
| 32189 | 32784 | if( c=='h' && cli_strncmp(azArg[0], "headers", n)==0 ){ |
| 32190 | 32785 | if( nArg==2 ){ |
| 32191 | 32786 | p->mode.spec.bTitles = booleanValue(azArg[1]) ? QRF_Yes : QRF_No; |
| 32787 | + p->mode.mFlags |= MFLG_HDR; |
| 32192 | 32788 | p->mode.spec.eTitle = aModeInfo[p->mode.eMode].eHdr; |
| 32193 | 32789 | }else{ |
| 32194 | 32790 | eputz("Usage: .headers on|off\n"); |
| 32195 | 32791 | rc = 1; |
| 32196 | 32792 | } |
| | @@ -32240,14 +32836,14 @@ |
| 32240 | 32836 | sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, p->db, "main", 0, 1); |
| 32241 | 32837 | goto meta_command_exit; |
| 32242 | 32838 | } |
| 32243 | 32839 | zSql = sqlite3_mprintf( |
| 32244 | 32840 | "SELECT rootpage, 0 FROM sqlite_schema" |
| 32245 | | - " WHERE name='%q' AND type='index'" |
| 32841 | + " WHERE type='index' AND lower(name)=lower('%q')" |
| 32246 | 32842 | "UNION ALL " |
| 32247 | 32843 | "SELECT rootpage, 1 FROM sqlite_schema" |
| 32248 | | - " WHERE name='%q' AND type='table'" |
| 32844 | + " WHERE type='table' AND lower(name)=lower('%q')" |
| 32249 | 32845 | " AND sql LIKE '%%without%%rowid%%'", |
| 32250 | 32846 | azArg[1], azArg[1] |
| 32251 | 32847 | ); |
| 32252 | 32848 | sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); |
| 32253 | 32849 | sqlite3_free(zSql); |
| | @@ -32401,13 +32997,14 @@ |
| 32401 | 32997 | goto meta_command_exit; |
| 32402 | 32998 | } |
| 32403 | 32999 | if( nArg==3 ){ |
| 32404 | 33000 | sqlite3_limit(p->db, aLimit[iLimit].limitCode, |
| 32405 | 33001 | (int)integerValue(azArg[2])); |
| 33002 | + }else{ |
| 33003 | + cli_printf(stdout, "%20s %d\n", aLimit[iLimit].zLimitName, |
| 33004 | + sqlite3_limit(p->db, aLimit[iLimit].limitCode, -1)); |
| 32406 | 33005 | } |
| 32407 | | - cli_printf(stdout, "%20s %d\n", aLimit[iLimit].zLimitName, |
| 32408 | | - sqlite3_limit(p->db, aLimit[iLimit].limitCode, -1)); |
| 32409 | 33006 | } |
| 32410 | 33007 | }else |
| 32411 | 33008 | |
| 32412 | 33009 | if( c=='l' && n>2 && cli_strncmp(azArg[0], "lint", n)==0 ){ |
| 32413 | 33010 | open_db(p, 0); |
| | @@ -32493,21 +33090,23 @@ |
| 32493 | 33090 | int iName = 1; /* Index in azArg[] of the filename */ |
| 32494 | 33091 | int newFlag = 0; /* True to delete file before opening */ |
| 32495 | 33092 | int openMode = SHELL_OPEN_UNSPEC; |
| 32496 | 33093 | int openFlags = SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE; |
| 32497 | 33094 | |
| 33095 | + if( p->bSafeMode ) openFlags = SQLITE_OPEN_READONLY; |
| 33096 | + |
| 32498 | 33097 | /* Check for command-line arguments */ |
| 32499 | 33098 | for(iName=1; iName<nArg; iName++){ |
| 32500 | 33099 | const char *z = azArg[iName]; |
| 32501 | 33100 | #ifndef SQLITE_SHELL_FIDDLE |
| 32502 | 33101 | if( optionMatch(z,"new") ){ |
| 32503 | 33102 | newFlag = 1; |
| 32504 | 33103 | #ifdef SQLITE_HAVE_ZLIB |
| 32505 | | - }else if( optionMatch(z, "zip") ){ |
| 33104 | + }else if( optionMatch(z, "zip") && !p->bSafeMode ){ |
| 32506 | 33105 | openMode = SHELL_OPEN_ZIPFILE; |
| 32507 | 33106 | #endif |
| 32508 | | - }else if( optionMatch(z, "append") ){ |
| 33107 | + }else if( optionMatch(z, "append") && !p->bSafeMode ){ |
| 32509 | 33108 | openMode = SHELL_OPEN_APPENDVFS; |
| 32510 | 33109 | }else if( optionMatch(z, "readonly") ){ |
| 32511 | 33110 | openFlags &= ~(SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE); |
| 32512 | 33111 | openFlags |= SQLITE_OPEN_READONLY; |
| 32513 | 33112 | }else if( optionMatch(z, "exclusive") ){ |
| | @@ -32950,21 +33549,22 @@ |
| 32950 | 33549 | || sqlite3_strlike(zName, "sqlite_schema", '\\')==0 |
| 32951 | 33550 | || sqlite3_strlike(zName,"sqlite_temp_master", '\\')==0 |
| 32952 | 33551 | || sqlite3_strlike(zName,"sqlite_temp_schema", '\\')==0; |
| 32953 | 33552 | if( isSchema ){ |
| 32954 | 33553 | cli_printf(p->out, |
| 32955 | | - "CREATE TABLE %s (\n" |
| 33554 | + "CREATE TABLE %ssqlite_schema (\n" |
| 32956 | 33555 | " type text,\n" |
| 32957 | 33556 | " name text,\n" |
| 32958 | 33557 | " tbl_name text,\n" |
| 32959 | 33558 | " rootpage integer,\n" |
| 32960 | 33559 | " sql text\n" |
| 32961 | | - ");\n", zName); |
| 33560 | + ");\n", |
| 33561 | + sqlite3_strlike("sqlite_t%",zName,0)==0 ? "temp." : "" |
| 33562 | + ); |
| 32962 | 33563 | } |
| 32963 | 33564 | } |
| 32964 | | - rc = sqlite3_prepare_v2(p->db, "SELECT name FROM pragma_database_list", |
| 32965 | | - -1, &pStmt, 0); |
| 33565 | + rc = sqlite3_prepare_v2(p->db, "PRAGMA database_list", -1, &pStmt, 0); |
| 32966 | 33566 | if( rc ){ |
| 32967 | 33567 | shellDatabaseError(p->db); |
| 32968 | 33568 | sqlite3_finalize(pStmt); |
| 32969 | 33569 | |
| 32970 | 33570 | rc = 1; |
| | @@ -32972,11 +33572,11 @@ |
| 32972 | 33572 | } |
| 32973 | 33573 | pSql = sqlite3_str_new(p->db); |
| 32974 | 33574 | sqlite3_str_appendf(pSql, "SELECT sql FROM", 0); |
| 32975 | 33575 | iSchema = 0; |
| 32976 | 33576 | while( sqlite3_step(pStmt)==SQLITE_ROW ){ |
| 32977 | | - const char *zDb = (const char*)sqlite3_column_text(pStmt, 0); |
| 33577 | + const char *zDb = (const char*)sqlite3_column_text(pStmt, 1); |
| 32978 | 33578 | char zScNum[30]; |
| 32979 | 33579 | sqlite3_snprintf(sizeof(zScNum), zScNum, "%d", ++iSchema); |
| 32980 | 33580 | sqlite3_str_appendall(pSql, zDiv); |
| 32981 | 33581 | zDiv = " UNION ALL "; |
| 32982 | 33582 | if( sqlite3_stricmp(zDb, "main")==0 ){ |
| | @@ -32992,11 +33592,12 @@ |
| 32992 | 33592 | " AS sql, type, tbl_name, name, rowid, %d AS snum, %Q as sname", |
| 32993 | 33593 | ++iSchema, zDb); |
| 32994 | 33594 | sqlite3_str_appendf(pSql," FROM \"%w\".sqlite_schema", zDb); |
| 32995 | 33595 | } |
| 32996 | 33596 | sqlite3_finalize(pStmt); |
| 32997 | | -#ifndef SQLITE_OMIT_INTROSPECTION_PRAGMAS |
| 33597 | +#if !defined(SQLITE_OMIT_INTROSPECTION_PRAGMAS) \ |
| 33598 | + && !defined(SQLITE_OMIT_VIRTUALTABLE) |
| 32998 | 33599 | if( zName ){ |
| 32999 | 33600 | sqlite3_str_appendall(pSql, |
| 33000 | 33601 | " UNION ALL SELECT shell_module_schema(name)," |
| 33001 | 33602 | " 'table', name, name, name, 9e+99, 'main' FROM pragma_module_list"); |
| 33002 | 33603 | } |
| | @@ -33016,11 +33617,11 @@ |
| 33016 | 33617 | }else{ |
| 33017 | 33618 | sqlite3_str_appendf(pSql, " LIKE %Q ESCAPE '\\' AND ", zName); |
| 33018 | 33619 | } |
| 33019 | 33620 | } |
| 33020 | 33621 | if( bNoSystemTabs ){ |
| 33021 | | - sqlite3_str_appendf(pSql, " name NOT LIKE 'sqlite__%%' ESCALE '_' AND "); |
| 33622 | + sqlite3_str_appendf(pSql, " name NOT LIKE 'sqlite__%%' ESCAPE '_' AND "); |
| 33022 | 33623 | } |
| 33023 | 33624 | sqlite3_str_appendf(pSql, "sql IS NOT NULL ORDER BY snum, rowid"); |
| 33024 | 33625 | if( bDebug ){ |
| 33025 | 33626 | cli_printf(p->out, "SQL: %s;\n", sqlite3_str_value(pSql)); |
| 33026 | 33627 | }else{ |
| | @@ -33734,25 +34335,15 @@ |
| 33734 | 34335 | sqlite3_str_free(pSql); |
| 33735 | 34336 | modePop(p); |
| 33736 | 34337 | if( rc ) return shellDatabaseError(p->db); |
| 33737 | 34338 | }else |
| 33738 | 34339 | |
| 33739 | | -#ifndef SQLITE_SHELL_FIDDLE |
| 33740 | | - /* Begin redirecting output to the file "testcase-out.txt" */ |
| 34340 | + /* Set the p->zTestcase name and begin redirecting output into |
| 34341 | + ** the cli_output_capture sqlite3_str */ |
| 33741 | 34342 | if( c=='t' && cli_strcmp(azArg[0],"testcase")==0 ){ |
| 33742 | | - output_reset(p); |
| 33743 | | - p->out = output_file_open(p, "testcase-out.txt"); |
| 33744 | | - if( p->out==0 ){ |
| 33745 | | - eputz("Error: cannot open 'testcase-out.txt'\n"); |
| 33746 | | - } |
| 33747 | | - if( nArg>=2 ){ |
| 33748 | | - sqlite3_snprintf(sizeof(p->zTestcase), p->zTestcase, "%s", azArg[1]); |
| 33749 | | - }else{ |
| 33750 | | - sqlite3_snprintf(sizeof(p->zTestcase), p->zTestcase, "?"); |
| 33751 | | - } |
| 34343 | + rc = dotCmdTestcase(p); |
| 33752 | 34344 | }else |
| 33753 | | -#endif /* !defined(SQLITE_SHELL_FIDDLE) */ |
| 33754 | 34345 | |
| 33755 | 34346 | #ifndef SQLITE_UNTESTABLE |
| 33756 | 34347 | if( c=='t' && n>=8 && cli_strncmp(azArg[0], "testctrl", n)==0 ){ |
| 33757 | 34348 | static const struct { |
| 33758 | 34349 | const char *zCtrlName; /* Name of a test-control option */ |
| | @@ -35068,11 +35659,10 @@ |
| 35068 | 35659 | " -bail stop after hitting an error\n" |
| 35069 | 35660 | " -batch force batch I/O\n" |
| 35070 | 35661 | " -box set output mode to 'box'\n" |
| 35071 | 35662 | " -cmd COMMAND run \"COMMAND\" before reading stdin\n" |
| 35072 | 35663 | " -column set output mode to 'column'\n" |
| 35073 | | - " -compat YYYYMMDD set default options for date YYYYMMDD\n" |
| 35074 | 35664 | " -csv set output mode to 'csv'\n" |
| 35075 | 35665 | #if !defined(SQLITE_OMIT_DESERIALIZE) |
| 35076 | 35666 | " -deserialize open the database using sqlite3_deserialize()\n" |
| 35077 | 35667 | #endif |
| 35078 | 35668 | " -echo print inputs before execution\n" |
| | @@ -35152,15 +35742,10 @@ |
| 35152 | 35742 | /* |
| 35153 | 35743 | ** Initialize the state information in data |
| 35154 | 35744 | */ |
| 35155 | 35745 | static void main_init(ShellState *p) { |
| 35156 | 35746 | memset(p, 0, sizeof(*p)); |
| 35157 | | -#if defined(COMPATIBILITY_DATE) |
| 35158 | | - p->iCompat = COMPATIBILITY_DATE; |
| 35159 | | -#else |
| 35160 | | - p->iCompat = 20251116; |
| 35161 | | -#endif |
| 35162 | 35747 | p->pAuxDb = &p->aAuxDb[0]; |
| 35163 | 35748 | p->shellFlgs = SHFLG_Lookaside; |
| 35164 | 35749 | sqlite3_config(SQLITE_CONFIG_LOG, shellLog, p); |
| 35165 | 35750 | #if !defined(SQLITE_SHELL_FIDDLE) |
| 35166 | 35751 | verify_uninitialized(); |
| | @@ -35174,22 +35759,18 @@ |
| 35174 | 35759 | /* |
| 35175 | 35760 | ** Output text to the console in a font that attracts extra attention. |
| 35176 | 35761 | */ |
| 35177 | 35762 | #if defined(_WIN32) || defined(WIN32) |
| 35178 | 35763 | static void printBold(const char *zText){ |
| 35179 | | -#if !SQLITE_OS_WINRT |
| 35180 | 35764 | HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE); |
| 35181 | 35765 | CONSOLE_SCREEN_BUFFER_INFO defaultScreenInfo; |
| 35182 | 35766 | GetConsoleScreenBufferInfo(out, &defaultScreenInfo); |
| 35183 | 35767 | SetConsoleTextAttribute(out, |
| 35184 | 35768 | FOREGROUND_RED|FOREGROUND_INTENSITY |
| 35185 | 35769 | ); |
| 35186 | | -#endif |
| 35187 | 35770 | sputz(stdout, zText); |
| 35188 | | -#if !SQLITE_OS_WINRT |
| 35189 | 35771 | SetConsoleTextAttribute(out, defaultScreenInfo.wAttributes); |
| 35190 | | -#endif |
| 35191 | 35772 | } |
| 35192 | 35773 | #else |
| 35193 | 35774 | static void printBold(const char *zText){ |
| 35194 | 35775 | cli_printf(stdout, "\033[1m%s\033[0m", zText); |
| 35195 | 35776 | } |
| | @@ -35219,29 +35800,65 @@ |
| 35219 | 35800 | cli_puts(z, p->out); |
| 35220 | 35801 | fflush(p->out); |
| 35221 | 35802 | return 1; |
| 35222 | 35803 | } |
| 35223 | 35804 | |
| 35224 | | -#ifndef SQLITE_SHELL_IS_UTF8 |
| 35225 | | -# if (defined(_WIN32) || defined(WIN32)) \ |
| 35226 | | - && (defined(_MSC_VER) || (defined(UNICODE) && defined(__GNUC__))) |
| 35227 | | -# define SQLITE_SHELL_IS_UTF8 (0) |
| 35228 | | -# else |
| 35229 | | -# define SQLITE_SHELL_IS_UTF8 (1) |
| 35230 | | -# endif |
| 35231 | | -#endif |
| 35232 | | - |
| 35805 | +/* Alternative name to the entry point for Fiddle */ |
| 35233 | 35806 | #ifdef SQLITE_SHELL_FIDDLE |
| 35234 | 35807 | # define main fiddle_main |
| 35235 | 35808 | #endif |
| 35236 | 35809 | |
| 35237 | | -#if SQLITE_SHELL_IS_UTF8 |
| 35238 | | -int SQLITE_CDECL main(int argc, char **argv){ |
| 35239 | | -#else |
| 35810 | +/* Use the wmain() entry point on Windows. Translate arguments to |
| 35811 | +** UTF8, then invoke the traditional main() entry point which is |
| 35812 | +** renamed using a #define to utf8_main() . |
| 35813 | +*/ |
| 35814 | +#if defined(_WIN32) && !defined(main) |
| 35815 | +# define main utf8_main /* Rename entry point to utf_main() */ |
| 35816 | +int SQLITE_CDECL utf8_main(int,char**); /* Forward declaration */ |
| 35240 | 35817 | int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ |
| 35241 | | - char **argv; |
| 35242 | | -#endif |
| 35818 | + int rc, i; |
| 35819 | + char **argv = malloc( sizeof(char*) * (argc+1) ); |
| 35820 | + char **orig = argv; |
| 35821 | + if( argv==0 ){ |
| 35822 | + fprintf(stderr, "malloc failed\n"); |
| 35823 | + exit(1); |
| 35824 | + } |
| 35825 | + for(i=0; i<argc; i++){ |
| 35826 | + int nByte = WideCharToMultiByte(CP_UTF8, 0, wargv[i], -1, 0, 0, 0, 0); |
| 35827 | + if( nByte==0 ){ |
| 35828 | + argv[i] = 0; |
| 35829 | + }else{ |
| 35830 | + argv[i] = malloc( nByte ); |
| 35831 | + if( argv[i]==0 ){ |
| 35832 | + fprintf(stderr, "malloc failed\n"); |
| 35833 | + exit(1); |
| 35834 | + } |
| 35835 | + nByte = WideCharToMultiByte(CP_UTF8, 0, wargv[i], -1, argv[i],nByte,0,0); |
| 35836 | + if( nByte==0 ){ |
| 35837 | + free(argv[i]); |
| 35838 | + argv[i] = 0; |
| 35839 | + } |
| 35840 | + } |
| 35841 | + } |
| 35842 | + argv[argc] = 0; |
| 35843 | + rc = utf8_main(argc, argv); |
| 35844 | + for(i=0; i<argc; i++) free(orig[i]); |
| 35845 | + free(argv); |
| 35846 | + return rc; |
| 35847 | +} |
| 35848 | +#endif /* WIN32 */ |
| 35849 | + |
| 35850 | +/* |
| 35851 | +** This is the main entry point for the process. Everything starts here. |
| 35852 | +** |
| 35853 | +** The "main" identifier may have been #defined to something else: |
| 35854 | +** |
| 35855 | +** utf8_main On Windows |
| 35856 | +** fiddle_main In Fiddle |
| 35857 | +** sqlite3_shell Other projects that use shell.c as a subroutine |
| 35858 | +*/ |
| 35859 | +int SQLITE_CDECL main(int argc, char **argv){ |
| 35243 | 35860 | #ifdef SQLITE_DEBUG |
| 35244 | 35861 | sqlite3_int64 mem_main_enter = 0; |
| 35245 | 35862 | #endif |
| 35246 | 35863 | char *zErrMsg = 0; |
| 35247 | 35864 | #ifdef SQLITE_SHELL_FIDDLE |
| | @@ -35259,14 +35876,10 @@ |
| 35259 | 35876 | int nOptsEnd = argc; |
| 35260 | 35877 | int bEnableVfstrace = 0; |
| 35261 | 35878 | char **azCmd = 0; |
| 35262 | 35879 | int *aiCmd = 0; |
| 35263 | 35880 | const char *zVfs = 0; /* Value of -vfs command-line option */ |
| 35264 | | -#if !SQLITE_SHELL_IS_UTF8 |
| 35265 | | - char **argvToFree = 0; |
| 35266 | | - int argcToFree = 0; |
| 35267 | | -#endif |
| 35268 | 35881 | setvbuf(stderr, 0, _IONBF, 0); /* Make sure stderr is unbuffered */ |
| 35269 | 35882 | |
| 35270 | 35883 | #ifdef SQLITE_SHELL_FIDDLE |
| 35271 | 35884 | stdin_is_interactive = 0; |
| 35272 | 35885 | stdout_is_console = 1; |
| | @@ -35291,15 +35904,11 @@ |
| 35291 | 35904 | ){ |
| 35292 | 35905 | exit(1); |
| 35293 | 35906 | } |
| 35294 | 35907 | }else{ |
| 35295 | 35908 | #if defined(_WIN32) || defined(WIN32) |
| 35296 | | -#if SQLITE_OS_WINRT |
| 35297 | | - __debugbreak(); |
| 35298 | | -#else |
| 35299 | 35909 | DebugBreak(); |
| 35300 | | -#endif |
| 35301 | 35910 | #elif defined(SIGTRAP) |
| 35302 | 35911 | raise(SIGTRAP); |
| 35303 | 35912 | #endif |
| 35304 | 35913 | } |
| 35305 | 35914 | } |
| | @@ -35321,36 +35930,10 @@ |
| 35321 | 35930 | exit(1); |
| 35322 | 35931 | } |
| 35323 | 35932 | #endif |
| 35324 | 35933 | main_init(&data); |
| 35325 | 35934 | |
| 35326 | | - /* On Windows, we must translate command-line arguments into UTF-8. |
| 35327 | | - ** The SQLite memory allocator subsystem has to be enabled in order to |
| 35328 | | - ** do this. But we want to run an sqlite3_shutdown() afterwards so that |
| 35329 | | - ** subsequent sqlite3_config() calls will work. So copy all results into |
| 35330 | | - ** memory that does not come from the SQLite memory allocator. |
| 35331 | | - */ |
| 35332 | | -#if !SQLITE_SHELL_IS_UTF8 |
| 35333 | | - sqlite3_initialize(); |
| 35334 | | - argvToFree = malloc(sizeof(argv[0])*argc*2); |
| 35335 | | - shell_check_oom(argvToFree); |
| 35336 | | - argcToFree = argc; |
| 35337 | | - argv = argvToFree + argc; |
| 35338 | | - for(i=0; i<argc; i++){ |
| 35339 | | - char *z = sqlite3_win32_unicode_to_utf8(wargv[i]); |
| 35340 | | - i64 n; |
| 35341 | | - shell_check_oom(z); |
| 35342 | | - n = strlen(z); |
| 35343 | | - argv[i] = malloc( n+1 ); |
| 35344 | | - shell_check_oom(argv[i]); |
| 35345 | | - memcpy(argv[i], z, n+1); |
| 35346 | | - argvToFree[i] = argv[i]; |
| 35347 | | - sqlite3_free(z); |
| 35348 | | - } |
| 35349 | | - sqlite3_shutdown(); |
| 35350 | | -#endif |
| 35351 | | - |
| 35352 | 35935 | assert( argc>=1 && argv && argv[0] ); |
| 35353 | 35936 | Argv0 = argv[0]; |
| 35354 | 35937 | |
| 35355 | 35938 | #ifdef SQLITE_SHELL_DBNAME_PROC |
| 35356 | 35939 | { |
| | @@ -35362,11 +35945,11 @@ |
| 35362 | 35945 | SQLITE_SHELL_DBNAME_PROC(&data.pAuxDb->zDbFilename); |
| 35363 | 35946 | warnInmemoryDb = 0; |
| 35364 | 35947 | } |
| 35365 | 35948 | #endif |
| 35366 | 35949 | |
| 35367 | | - /* Do an initial pass through the command-line argument to locate |
| 35950 | + /* Do an initial pass through the command-line arguments to locate |
| 35368 | 35951 | ** the name of the database file, the name of the initialization file, |
| 35369 | 35952 | ** the size of the alternative malloc heap, options affecting commands |
| 35370 | 35953 | ** or SQL run from the command line, and the first command to execute. |
| 35371 | 35954 | */ |
| 35372 | 35955 | #ifndef SQLITE_SHELL_FIDDLE |
| | @@ -35374,11 +35957,11 @@ |
| 35374 | 35957 | #endif |
| 35375 | 35958 | for(i=1; i<argc; i++){ |
| 35376 | 35959 | char *z; |
| 35377 | 35960 | z = argv[i]; |
| 35378 | 35961 | if( z[0]!='-' || i>nOptsEnd ){ |
| 35379 | | - if( data.aAuxDb->zDbFilename==0 ){ |
| 35962 | + if( data.aAuxDb->zDbFilename==0 && !isScriptFile(z,1) ){ |
| 35380 | 35963 | data.aAuxDb->zDbFilename = z; |
| 35381 | 35964 | }else{ |
| 35382 | 35965 | /* Excess arguments are interpreted as SQL (or dot-commands) and |
| 35383 | 35966 | ** mean that nothing is read from stdin */ |
| 35384 | 35967 | readStdin = 0; |
| | @@ -35419,15 +36002,10 @@ |
| 35419 | 36002 | if( n<2 ){ |
| 35420 | 36003 | sqlite3_fprintf(stderr,"minimum --screenwidth is 2\n"); |
| 35421 | 36004 | exit(1); |
| 35422 | 36005 | } |
| 35423 | 36006 | stdout_tty_width = n; |
| 35424 | | - }else if( cli_strcmp(z,"-compat")==0 ){ |
| 35425 | | - data.iCompat = atoi(cmdline_option_value(argc, argv, ++i)); |
| 35426 | | - modeFree(&data.mode); |
| 35427 | | - memset(&data.mode, 0, sizeof(data.mode)); |
| 35428 | | - modeDefault(&data); |
| 35429 | 36007 | }else if( cli_strcmp(z,"-utf8")==0 ){ |
| 35430 | 36008 | }else if( cli_strcmp(z,"-no-utf8")==0 ){ |
| 35431 | 36009 | }else if( cli_strcmp(z,"-no-rowid-in-view")==0 ){ |
| 35432 | 36010 | int val = 0; |
| 35433 | 36011 | sqlite3_config(SQLITE_CONFIG_ROWID_IN_VIEW, &val); |
| | @@ -35547,10 +36125,20 @@ |
| 35547 | 36125 | }else if( cli_strcmp(z,"-safe")==0 ){ |
| 35548 | 36126 | /* no-op - catch this on the second pass */ |
| 35549 | 36127 | }else if( cli_strcmp(z,"-escape")==0 && i+1<argc ){ |
| 35550 | 36128 | /* skip over the argument */ |
| 35551 | 36129 | i++; |
| 36130 | + }else if( cli_strcmp(z,"-test-argv")==0 ){ |
| 36131 | + /* Undocumented test option. Print the values in argv[] and exit. |
| 36132 | + ** Use this to verify that any translation of the argv[], for example |
| 36133 | + ** on Windows that receives wargv[] from the OS and must convert |
| 36134 | + ** to UTF8 prior to calling this routine. */ |
| 36135 | + int kk; |
| 36136 | + for(kk=0; kk<argc; kk++){ |
| 36137 | + sqlite3_fprintf(stdout,"argv[%d] = \"%s\"\n", kk, argv[kk]); |
| 36138 | + } |
| 36139 | + return 0; |
| 35552 | 36140 | } |
| 35553 | 36141 | } |
| 35554 | 36142 | #ifndef SQLITE_SHELL_FIDDLE |
| 35555 | 36143 | if( !bEnableVfstrace ) verify_uninitialized(); |
| 35556 | 36144 | #endif |
| | @@ -35573,11 +36161,30 @@ |
| 35573 | 36161 | |
| 35574 | 36162 | if( zVfs ){ |
| 35575 | 36163 | sqlite3_vfs *pVfs = sqlite3_vfs_find(zVfs); |
| 35576 | 36164 | if( pVfs ){ |
| 35577 | 36165 | sqlite3_vfs_register(pVfs, 1); |
| 35578 | | - }else{ |
| 36166 | + } |
| 36167 | +#if !defined(SQLITE_OMIT_LOAD_EXTENSION) |
| 36168 | + else if( access(zVfs,0)==0 ){ |
| 36169 | + /* If the VFS name is not the name of an existing VFS, but it is |
| 36170 | + ** the name of a file, then try to load that file as an extension. |
| 36171 | + ** Presumably the extension implements the desired VFS. */ |
| 36172 | + sqlite3 *db = 0; |
| 36173 | + char *zErr = 0; |
| 36174 | + sqlite3_open(":memory:", &db); |
| 36175 | + sqlite3_enable_load_extension(db, 1); |
| 36176 | + rc = sqlite3_load_extension(db, zVfs, 0, &zErr); |
| 36177 | + sqlite3_close(db); |
| 36178 | + if( (rc&0xff)!=SQLITE_OK ){ |
| 36179 | + cli_printf(stderr, "could not load extension VFS \"%s\": %s\n", |
| 36180 | + zVfs, zErr); |
| 36181 | + exit(1); |
| 36182 | + } |
| 36183 | + } |
| 36184 | +#endif |
| 36185 | + else{ |
| 35579 | 36186 | cli_printf(stderr,"no such VFS: \"%s\"\n", zVfs); |
| 35580 | 36187 | exit(1); |
| 35581 | 36188 | } |
| 35582 | 36189 | } |
| 35583 | 36190 | |
| | @@ -35586,11 +36193,12 @@ |
| 35586 | 36193 | data.pAuxDb->zDbFilename = ":memory:"; |
| 35587 | 36194 | warnInmemoryDb = argc==1; |
| 35588 | 36195 | #else |
| 35589 | 36196 | cli_printf(stderr, |
| 35590 | 36197 | "%s: Error: no database filename specified\n", Argv0); |
| 35591 | | - return 1; |
| 36198 | + rc = 1; |
| 36199 | + goto shell_main_exit; |
| 35592 | 36200 | #endif |
| 35593 | 36201 | } |
| 35594 | 36202 | data.out = stdout; |
| 35595 | 36203 | if( bEnableVfstrace ){ |
| 35596 | 36204 | vfstrace_register("trace",0,vfstraceOut, &data, 1); |
| | @@ -35613,11 +36221,11 @@ |
| 35613 | 36221 | ** is given on the command line, look for a file named ~/.sqliterc and |
| 35614 | 36222 | ** try to process it. |
| 35615 | 36223 | */ |
| 35616 | 36224 | if( !noInit ) process_sqliterc(&data,zInitFile); |
| 35617 | 36225 | |
| 35618 | | - /* Make a second pass through the command-line argument and set |
| 36226 | + /* Make a second pass through the command-line arguments and set |
| 35619 | 36227 | ** options. This second pass is delayed until after the initialization |
| 35620 | 36228 | ** file is processed so that the command-line arguments will override |
| 35621 | 36229 | ** settings in the initialization file. |
| 35622 | 36230 | */ |
| 35623 | 36231 | for(i=1; i<argc; i++){ |
| | @@ -35640,10 +36248,12 @@ |
| 35640 | 36248 | modeChange(&data, MODE_Json); |
| 35641 | 36249 | }else if( cli_strcmp(z,"-markdown")==0 ){ |
| 35642 | 36250 | modeChange(&data, MODE_Markdown); |
| 35643 | 36251 | }else if( cli_strcmp(z,"-table")==0 ){ |
| 35644 | 36252 | modeChange(&data, MODE_Table); |
| 36253 | + }else if( cli_strcmp(z,"-psql")==0 ){ |
| 36254 | + modeChange(&data, MODE_Psql); |
| 35645 | 36255 | }else if( cli_strcmp(z,"-box")==0 ){ |
| 35646 | 36256 | modeChange(&data, MODE_Box); |
| 35647 | 36257 | }else if( cli_strcmp(z,"-csv")==0 ){ |
| 35648 | 36258 | modeChange(&data, MODE_Csv); |
| 35649 | 36259 | }else if( cli_strcmp(z,"-escape")==0 && i+1<argc ){ |
| | @@ -35726,22 +36336,21 @@ |
| 35726 | 36336 | }else if( cli_strcmp(z,"-bail")==0 ){ |
| 35727 | 36337 | /* No-op. The bail_on_error flag should already be set. */ |
| 35728 | 36338 | }else if( cli_strcmp(z,"-version")==0 ){ |
| 35729 | 36339 | cli_printf(stdout, "%s %s (%d-bit)\n", |
| 35730 | 36340 | sqlite3_libversion(), sqlite3_sourceid(), 8*(int)sizeof(char*)); |
| 35731 | | - return 0; |
| 36341 | + rc = 0; |
| 36342 | + goto shell_main_exit; |
| 35732 | 36343 | }else if( cli_strcmp(z,"-interactive")==0 ){ |
| 35733 | 36344 | /* Need to check for interactive override here to so that it can |
| 35734 | 36345 | ** affect console setup (for Windows only) and testing thereof. |
| 35735 | 36346 | */ |
| 35736 | 36347 | stdin_is_interactive = 1; |
| 35737 | 36348 | }else if( cli_strcmp(z,"-batch")==0 ){ |
| 35738 | 36349 | /* already handled */ |
| 35739 | 36350 | }else if( cli_strcmp(z,"-screenwidth")==0 ){ |
| 35740 | 36351 | i++; |
| 35741 | | - }else if( cli_strcmp(z,"-compat")==0 ){ |
| 35742 | | - i++; |
| 35743 | 36352 | }else if( cli_strcmp(z,"-utf8")==0 ){ |
| 35744 | 36353 | /* already handled */ |
| 35745 | 36354 | }else if( cli_strcmp(z,"-no-utf8")==0 ){ |
| 35746 | 36355 | /* already handled */ |
| 35747 | 36356 | }else if( cli_strcmp(z,"-no-rowid-in-view")==0 ){ |
| | @@ -35783,29 +36392,33 @@ |
| 35783 | 36392 | ** we retain the goofy behavior for historical compatibility. */ |
| 35784 | 36393 | if( i==argc-1 ) break; |
| 35785 | 36394 | z = cmdline_option_value(argc,argv,++i); |
| 35786 | 36395 | if( z[0]=='.' ){ |
| 35787 | 36396 | rc = do_meta_command(z, &data); |
| 35788 | | - if( rc && bail_on_error ) return rc==2 ? 0 : rc; |
| 36397 | + if( rc && bail_on_error ){ |
| 36398 | + if( rc==2 ) rc = 0; |
| 36399 | + goto shell_main_exit; |
| 36400 | + } |
| 35789 | 36401 | }else{ |
| 35790 | 36402 | open_db(&data, 0); |
| 35791 | 36403 | rc = shell_exec(&data, z, &zErrMsg); |
| 35792 | 36404 | if( zErrMsg!=0 ){ |
| 35793 | 36405 | shellEmitError(zErrMsg); |
| 35794 | 36406 | sqlite3_free(zErrMsg); |
| 35795 | | - if( bail_on_error ) return rc!=0 ? rc : 1; |
| 36407 | + if( !rc ) rc = 1; |
| 35796 | 36408 | }else if( rc!=0 ){ |
| 35797 | 36409 | cli_printf(stderr,"Error: unable to process SQL \"%s\"\n", z); |
| 35798 | | - if( bail_on_error ) return rc; |
| 35799 | 36410 | } |
| 36411 | + if( bail_on_error ) goto shell_main_exit; |
| 35800 | 36412 | } |
| 35801 | 36413 | #if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_HAVE_ZLIB) |
| 35802 | 36414 | }else if( cli_strncmp(z, "-A", 2)==0 ){ |
| 35803 | 36415 | if( nCmd>0 ){ |
| 35804 | 36416 | cli_printf(stderr,"Error: cannot mix regular SQL or dot-commands" |
| 35805 | 36417 | " with \"%s\"\n", z); |
| 35806 | | - return 1; |
| 36418 | + rc = 1; |
| 36419 | + goto shell_main_exit; |
| 35807 | 36420 | } |
| 35808 | 36421 | open_db(&data, OPEN_DB_ZIPFILE); |
| 35809 | 36422 | if( z[2] ){ |
| 35810 | 36423 | argv[i] = &z[2]; |
| 35811 | 36424 | arDotCommand(&data, 1, argv+(i-1), argc-(i-1)); |
| | @@ -35821,11 +36434,12 @@ |
| 35821 | 36434 | }else if( cli_strcmp(z,"-unsafe-testing")==0 ){ |
| 35822 | 36435 | /* Acted upon in first pass. */ |
| 35823 | 36436 | }else{ |
| 35824 | 36437 | cli_printf(stderr,"%s: Error: unknown option: %s\n", Argv0, z); |
| 35825 | 36438 | eputz("Use -help for a list of options.\n"); |
| 35826 | | - return 1; |
| 36439 | + rc = 1; |
| 36440 | + goto shell_main_exit; |
| 35827 | 36441 | } |
| 35828 | 36442 | } |
| 35829 | 36443 | |
| 35830 | 36444 | if( !readStdin ){ |
| 35831 | 36445 | /* Run all arguments that do not begin with '-' as if they were separate |
| | @@ -35832,11 +36446,22 @@ |
| 35832 | 36446 | ** command-line inputs, except for the argToSkip argument which contains |
| 35833 | 36447 | ** the database filename. |
| 35834 | 36448 | */ |
| 35835 | 36449 | for(i=0; i<nCmd; i++){ |
| 35836 | 36450 | echo_group_input(&data, azCmd[i]); |
| 35837 | | - if( azCmd[i][0]=='.' ){ |
| 36451 | + if( isScriptFile(azCmd[i],0) ){ |
| 36452 | + FILE *inSaved = data.in; |
| 36453 | + i64 savedLineno = data.lineno; |
| 36454 | + int res = 1; |
| 36455 | + if( (data.in = openChrSource(azCmd[i]))!=0 ){ |
| 36456 | + res = process_input(&data, azCmd[i]); |
| 36457 | + fclose(data.in); |
| 36458 | + } |
| 36459 | + data.in = inSaved; |
| 36460 | + data.lineno = savedLineno; |
| 36461 | + if( res ) i = nCmd; |
| 36462 | + }else if( azCmd[i][0]=='.' ){ |
| 35838 | 36463 | char *zErrCtx = malloc( 64 ); |
| 35839 | 36464 | shell_check_oom(zErrCtx); |
| 35840 | 36465 | sqlite3_snprintf(64,zErrCtx,"argv[%i]:",aiCmd[i]); |
| 35841 | 36466 | data.zInFile = "<cmdline>"; |
| 35842 | 36467 | data.zErrPrefix = zErrCtx; |
| | @@ -35947,14 +36572,10 @@ |
| 35947 | 36572 | } |
| 35948 | 36573 | find_home_dir(1); |
| 35949 | 36574 | output_reset(&data); |
| 35950 | 36575 | data.doXdgOpen = 0; |
| 35951 | 36576 | clearTempFile(&data); |
| 35952 | | -#if !SQLITE_SHELL_IS_UTF8 |
| 35953 | | - for(i=0; i<argcToFree; i++) free(argvToFree[i]); |
| 35954 | | - free(argvToFree); |
| 35955 | | -#endif |
| 35956 | 36577 | modeFree(&data.mode); |
| 35957 | 36578 | if( data.nSavedModes ){ |
| 35958 | 36579 | int ii; |
| 35959 | 36580 | for(ii=0; ii<data.nSavedModes; ii++){ |
| 35960 | 36581 | modeFree(&data.aSavedModes[ii].mode); |
| 35961 | 36582 | |