| | @@ -1,23 +1,47 @@ |
| 1 | | -/* DO NOT EDIT! |
| 2 | | -** This file is automatically generated by the script in the canonical |
| 3 | | -** SQLite source tree at tool/mkshellc.tcl. That script combines source |
| 4 | | -** code from various constituent source files of SQLite into this single |
| 5 | | -** "shell.c" file used to implement the SQLite command-line shell. |
| 6 | | -** |
| 7 | | -** Most of the code found below comes from the "src/shell.c.in" file in |
| 8 | | -** the canonical SQLite source tree. That main file contains "INCLUDE" |
| 9 | | -** lines that specify other files in the canonical source tree that are |
| 10 | | -** inserted to getnerate this complete program source file. |
| 11 | | -** |
| 12 | | -** The code from multiple files is combined into this single "shell.c" |
| 13 | | -** source file to help make the command-line program easier to compile. |
| 1 | +/* |
| 2 | +** This is the amalgamated source code to the "sqlite3" or "sqlite3.exe" |
| 3 | +** command-line shell (CLI) for SQLite. This file is automatically |
| 4 | +** generated by the tool/mkshellc.tcl script from the following sources: |
| 5 | +** |
| 6 | +** ext/expert/sqlite3expert.c |
| 7 | +** ext/expert/sqlite3expert.h |
| 8 | +** ext/intck/sqlite3intck.c |
| 9 | +** ext/intck/sqlite3intck.h |
| 10 | +** ext/misc/appendvfs.c |
| 11 | +** ext/misc/base64.c |
| 12 | +** ext/misc/base85.c |
| 13 | +** ext/misc/completion.c |
| 14 | +** ext/misc/decimal.c |
| 15 | +** ext/misc/fileio.c |
| 16 | +** ext/misc/ieee754.c |
| 17 | +** ext/misc/memtrace.c |
| 18 | +** ext/misc/pcachetrace.c |
| 19 | +** ext/misc/regexp.c |
| 20 | +** ext/misc/series.c |
| 21 | +** ext/misc/sha1.c |
| 22 | +** ext/misc/shathree.c |
| 23 | +** ext/misc/sqlar.c |
| 24 | +** ext/misc/sqlite3_stdio.c |
| 25 | +** ext/misc/sqlite3_stdio.h |
| 26 | +** ext/misc/stmtrand.c |
| 27 | +** ext/misc/uint.c |
| 28 | +** ext/misc/vfstrace.c |
| 29 | +** ext/misc/windirent.h |
| 30 | +** ext/misc/zipfile.c |
| 31 | +** ext/qrf/qrf.c |
| 32 | +** ext/qrf/qrf.h |
| 33 | +** ext/recover/dbdata.c |
| 34 | +** ext/recover/sqlite3recover.c |
| 35 | +** ext/recover/sqlite3recover.h |
| 36 | +** src/shell.c.in |
| 14 | 37 | ** |
| 15 | 38 | ** To modify this program, get a copy of the canonical SQLite source tree, |
| 16 | | -** edit the src/shell.c.in" and/or some of the other files that are included |
| 17 | | -** by "src/shell.c.in", then rerun the tool/mkshellc.tcl script. |
| 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". |
| 18 | 41 | */ |
| 42 | +/************************* Begin src/shell.c.in ******************/ |
| 19 | 43 | /* |
| 20 | 44 | ** 2001 September 15 |
| 21 | 45 | ** |
| 22 | 46 | ** The author disclaims copyright to this source code. In place of |
| 23 | 47 | ** a legal notice, here is a blessing: |
| | @@ -124,10 +148,11 @@ |
| 124 | 148 | typedef unsigned char u8; |
| 125 | 149 | #include <ctype.h> |
| 126 | 150 | #include <stdarg.h> |
| 127 | 151 | #ifndef _WIN32 |
| 128 | 152 | # include <sys/time.h> |
| 153 | +# include <sys/ioctl.h> |
| 129 | 154 | #endif |
| 130 | 155 | |
| 131 | 156 | #if !defined(_WIN32) && !defined(WIN32) |
| 132 | 157 | # include <signal.h> |
| 133 | 158 | # if !defined(__RTP__) && !defined(_WRS_KERNEL) && !defined(SQLITE_WASI) |
| | @@ -253,11 +278,11 @@ |
| 253 | 278 | /* string conversion routines only needed on Win32 */ |
| 254 | 279 | extern char *sqlite3_win32_unicode_to_utf8(LPCWSTR); |
| 255 | 280 | extern LPWSTR sqlite3_win32_utf8_to_unicode(const char *zText); |
| 256 | 281 | #endif |
| 257 | 282 | |
| 258 | | -/************************* Begin ../ext/misc/sqlite3_stdio.h ******************/ |
| 283 | +/************************* Begin ext/misc/sqlite3_stdio.h ******************/ |
| 259 | 284 | /* |
| 260 | 285 | ** 2024-09-24 |
| 261 | 286 | ** |
| 262 | 287 | ** The author disclaims copyright to this source code. In place of |
| 263 | 288 | ** a legal notice, here is a blessing: |
| | @@ -287,17 +312,19 @@ |
| 287 | 312 | #ifndef _SQLITE3_STDIO_H_ |
| 288 | 313 | #define _SQLITE3_STDIO_H_ 1 |
| 289 | 314 | #ifdef _WIN32 |
| 290 | 315 | /**** Definitions For Windows ****/ |
| 291 | 316 | #include <stdio.h> |
| 317 | +#include <stdarg.h> |
| 292 | 318 | #include <windows.h> |
| 293 | 319 | |
| 294 | 320 | FILE *sqlite3_fopen(const char *zFilename, const char *zMode); |
| 295 | 321 | FILE *sqlite3_popen(const char *zCommand, const char *type); |
| 296 | 322 | char *sqlite3_fgets(char *s, int size, FILE *stream); |
| 297 | 323 | int sqlite3_fputs(const char *s, FILE *stream); |
| 298 | 324 | int sqlite3_fprintf(FILE *stream, const char *format, ...); |
| 325 | +int sqlite3_vfprintf(FILE *stream, const char *format, va_list); |
| 299 | 326 | void sqlite3_fsetmode(FILE *stream, int mode); |
| 300 | 327 | |
| 301 | 328 | |
| 302 | 329 | #else |
| 303 | 330 | /**** Definitions For All Other Platforms ****/ |
| | @@ -305,17 +332,18 @@ |
| 305 | 332 | #define sqlite3_fopen fopen |
| 306 | 333 | #define sqlite3_popen popen |
| 307 | 334 | #define sqlite3_fgets fgets |
| 308 | 335 | #define sqlite3_fputs fputs |
| 309 | 336 | #define sqlite3_fprintf fprintf |
| 337 | +#define sqlite3_vfprintf vfprintf |
| 310 | 338 | #define sqlite3_fsetmode(F,X) /*no-op*/ |
| 311 | 339 | |
| 312 | 340 | #endif |
| 313 | 341 | #endif /* _SQLITE3_STDIO_H_ */ |
| 314 | 342 | |
| 315 | | -/************************* End ../ext/misc/sqlite3_stdio.h ********************/ |
| 316 | | -/************************* Begin ../ext/misc/sqlite3_stdio.c ******************/ |
| 343 | +/************************* End ext/misc/sqlite3_stdio.h ********************/ |
| 344 | +/************************* Begin ext/misc/sqlite3_stdio.c ******************/ |
| 317 | 345 | /* |
| 318 | 346 | ** 2024-09-24 |
| 319 | 347 | ** |
| 320 | 348 | ** The author disclaims copyright to this source code. In place of |
| 321 | 349 | ** a legal notice, here is a blessing: |
| | @@ -572,11 +600,11 @@ |
| 572 | 600 | } |
| 573 | 601 | } |
| 574 | 602 | |
| 575 | 603 | |
| 576 | 604 | /* |
| 577 | | -** Work-alike for fprintf() from the standard C library. |
| 605 | +** Work-alikes for fprintf() and vfprintf() from the standard C library. |
| 578 | 606 | */ |
| 579 | 607 | int sqlite3_fprintf(FILE *out, const char *zFormat, ...){ |
| 580 | 608 | int rc; |
| 581 | 609 | if( UseWtextForOutput(out) ){ |
| 582 | 610 | /* When writing to the command-prompt in Windows, it is necessary |
| | @@ -599,10 +627,28 @@ |
| 599 | 627 | rc = vfprintf(out, zFormat, ap); |
| 600 | 628 | va_end(ap); |
| 601 | 629 | } |
| 602 | 630 | return rc; |
| 603 | 631 | } |
| 632 | +int sqlite3_vfprintf(FILE *out, const char *zFormat, va_list ap){ |
| 633 | + int rc; |
| 634 | + if( UseWtextForOutput(out) ){ |
| 635 | + /* When writing to the command-prompt in Windows, it is necessary |
| 636 | + ** to use _O_WTEXT input mode and write UTF-16 characters. |
| 637 | + */ |
| 638 | + char *z; |
| 639 | + z = sqlite3_vmprintf(zFormat, ap); |
| 640 | + sqlite3_fputs(z, out); |
| 641 | + rc = (int)strlen(z); |
| 642 | + sqlite3_free(z); |
| 643 | + }else{ |
| 644 | + /* Writing to a file or other destination, just write bytes without |
| 645 | + ** any translation. */ |
| 646 | + rc = vfprintf(out, zFormat, ap); |
| 647 | + } |
| 648 | + return rc; |
| 649 | +} |
| 604 | 650 | |
| 605 | 651 | /* |
| 606 | 652 | ** Set the mode for an output stream. mode argument is typically _O_BINARY or |
| 607 | 653 | ** _O_TEXT. |
| 608 | 654 | */ |
| | @@ -617,11 +663,2697 @@ |
| 617 | 663 | } |
| 618 | 664 | } |
| 619 | 665 | |
| 620 | 666 | #endif /* defined(_WIN32) */ |
| 621 | 667 | |
| 622 | | -/************************* End ../ext/misc/sqlite3_stdio.c ********************/ |
| 668 | +/************************* End ext/misc/sqlite3_stdio.c ********************/ |
| 669 | +/************************* Begin ext/qrf/qrf.h ******************/ |
| 670 | +/* |
| 671 | +** 2025-10-20 |
| 672 | +** |
| 673 | +** The author disclaims copyright to this source code. In place of |
| 674 | +** a legal notice, here is a blessing: |
| 675 | +** |
| 676 | +** May you do good and not evil. |
| 677 | +** May you find forgiveness for yourself and forgive others. |
| 678 | +** May you share freely, never taking more than you give. |
| 679 | +** |
| 680 | +************************************************************************* |
| 681 | +** Header file for the Result-Format or "resfmt" utility library for SQLite. |
| 682 | +** See the resfmt.md documentation for additional information. |
| 683 | +*/ |
| 684 | +#ifndef SQLITE_QRF_H |
| 685 | +#define SQLITE_QRF_H |
| 686 | +#include <stdlib.h> |
| 687 | +/* #include "sqlite3.h" */ |
| 688 | + |
| 689 | +/* |
| 690 | +** Specification used by clients to define the output format they want |
| 691 | +*/ |
| 692 | +typedef struct sqlite3_qrf_spec sqlite3_qrf_spec; |
| 693 | +struct sqlite3_qrf_spec { |
| 694 | + unsigned char iVersion; /* Version number of this structure */ |
| 695 | + unsigned char eStyle; /* Formatting style. "box", "csv", etc... */ |
| 696 | + unsigned char eEsc; /* How to escape control characters in text */ |
| 697 | + unsigned char eText; /* Quoting style for text */ |
| 698 | + unsigned char eTitle; /* Quating style for the text of column names */ |
| 699 | + unsigned char eBlob; /* Quoting style for BLOBs */ |
| 700 | + unsigned char bTitles; /* True to show column names */ |
| 701 | + unsigned char bWordWrap; /* Try to wrap on word boundaries */ |
| 702 | + unsigned char bTextJsonb; /* Render JSONB blobs as JSON text */ |
| 703 | + unsigned char bTextNull; /* Apply eText encoding to zNull[] */ |
| 704 | + unsigned char eDfltAlign; /* Default alignment, no covered by aAlignment */ |
| 705 | + unsigned char eTitleAlign; /* Alignment for column headers */ |
| 706 | + short int nWrap; /* Wrap columns wider than this */ |
| 707 | + short int nScreenWidth; /* Maximum overall table width */ |
| 708 | + short int nLineLimit; /* Maximum number of lines for any row */ |
| 709 | + int nCharLimit; /* Maximum number of characters in a cell */ |
| 710 | + int nWidth; /* Number of entries in aWidth[] */ |
| 711 | + int nAlign; /* Number of entries in aAlignment[] */ |
| 712 | + short int *aWidth; /* Column widths */ |
| 713 | + unsigned char *aAlign; /* Column alignments */ |
| 714 | + char *zColumnSep; /* Alternative column separator */ |
| 715 | + char *zRowSep; /* Alternative row separator */ |
| 716 | + char *zTableName; /* Output table name */ |
| 717 | + char *zNull; /* Rendering of NULL */ |
| 718 | + char *(*xRender)(void*,sqlite3_value*); /* Render a value */ |
| 719 | + int (*xWrite)(void*,const char*,sqlite3_int64); /* Write output */ |
| 720 | + void *pRenderArg; /* First argument to the xRender callback */ |
| 721 | + void *pWriteArg; /* First argument to the xWrite callback */ |
| 722 | + char **pzOutput; /* Storage location for output string */ |
| 723 | + /* Additional fields may be added in the future */ |
| 724 | +}; |
| 725 | + |
| 726 | +/* |
| 727 | +** Interfaces |
| 728 | +*/ |
| 729 | +int sqlite3_format_query_result( |
| 730 | + sqlite3_stmt *pStmt, /* SQL statement to run */ |
| 731 | + const sqlite3_qrf_spec *pSpec, /* Result format specification */ |
| 732 | + char **pzErr /* OUT: Write error message here */ |
| 733 | +); |
| 734 | + |
| 735 | +/* |
| 736 | +** Range of values for sqlite3_qrf_spec.aWidth[] entries and for |
| 737 | +** sqlite3_qrf_spec.mxColWidth and .nScreenWidth |
| 738 | +*/ |
| 739 | +#define QRF_MAX_WIDTH 10000 |
| 740 | +#define QRF_MIN_WIDTH 0 |
| 741 | + |
| 742 | +/* |
| 743 | +** Output styles: |
| 744 | +*/ |
| 745 | +#define QRF_STYLE_Auto 0 /* Choose a style automatically */ |
| 746 | +#define QRF_STYLE_Box 1 /* Unicode box-drawing characters */ |
| 747 | +#define QRF_STYLE_Column 2 /* One record per line in neat columns */ |
| 748 | +#define QRF_STYLE_Count 3 /* Output only a count of the rows of output */ |
| 749 | +#define QRF_STYLE_Csv 4 /* Comma-separated-value */ |
| 750 | +#define QRF_STYLE_Eqp 5 /* Format EXPLAIN QUERY PLAN output */ |
| 751 | +#define QRF_STYLE_Explain 6 /* EXPLAIN output */ |
| 752 | +#define QRF_STYLE_Html 7 /* Generate an XHTML table */ |
| 753 | +#define QRF_STYLE_Insert 8 /* Generate SQL "insert" statements */ |
| 754 | +#define QRF_STYLE_Json 9 /* Output is a list of JSON objects */ |
| 755 | +#define QRF_STYLE_JObject 10 /* Independent JSON objects for each row */ |
| 756 | +#define QRF_STYLE_Line 11 /* One column per line. */ |
| 757 | +#define QRF_STYLE_List 12 /* One record per line with a separator */ |
| 758 | +#define QRF_STYLE_Markdown 13 /* Markdown formatting */ |
| 759 | +#define QRF_STYLE_Off 14 /* No query output shown */ |
| 760 | +#define QRF_STYLE_Quote 15 /* SQL-quoted, comma-separated */ |
| 761 | +#define QRF_STYLE_Stats 16 /* EQP-like output but with performance stats */ |
| 762 | +#define QRF_STYLE_StatsEst 17 /* EQP-like output with planner estimates */ |
| 763 | +#define QRF_STYLE_StatsVm 18 /* EXPLAIN-like output with performance stats */ |
| 764 | +#define QRF_STYLE_Table 19 /* MySQL-style table formatting */ |
| 765 | + |
| 766 | +/* |
| 767 | +** Quoting styles for text. |
| 768 | +** Allowed values for sqlite3_qrf_spec.eText |
| 769 | +*/ |
| 770 | +#define QRF_TEXT_Auto 0 /* Choose text encoding automatically */ |
| 771 | +#define QRF_TEXT_Plain 1 /* Literal text */ |
| 772 | +#define QRF_TEXT_Sql 2 /* Quote as an SQL literal */ |
| 773 | +#define QRF_TEXT_Csv 3 /* CSV-style quoting */ |
| 774 | +#define QRF_TEXT_Html 4 /* HTML-style quoting */ |
| 775 | +#define QRF_TEXT_Tcl 5 /* C/Tcl quoting */ |
| 776 | +#define QRF_TEXT_Json 6 /* JSON quoting */ |
| 777 | + |
| 778 | +/* |
| 779 | +** Quoting styles for BLOBs |
| 780 | +** Allowed values for sqlite3_qrf_spec.eBlob |
| 781 | +*/ |
| 782 | +#define QRF_BLOB_Auto 0 /* Determine BLOB quoting using eText */ |
| 783 | +#define QRF_BLOB_Text 1 /* Display content exactly as it is */ |
| 784 | +#define QRF_BLOB_Sql 2 /* Quote as an SQL literal */ |
| 785 | +#define QRF_BLOB_Hex 3 /* Hexadecimal representation */ |
| 786 | +#define QRF_BLOB_Tcl 4 /* "\000" notation */ |
| 787 | +#define QRF_BLOB_Json 5 /* A JSON string */ |
| 788 | + |
| 789 | +/* |
| 790 | +** Control-character escape modes. |
| 791 | +** Allowed values for sqlite3_qrf_spec.eEsc |
| 792 | +*/ |
| 793 | +#define QRF_ESC_Auto 0 /* Choose the ctrl-char escape automatically */ |
| 794 | +#define QRF_ESC_Off 1 /* Do not escape control characters */ |
| 795 | +#define QRF_ESC_Ascii 2 /* Unix-style escapes. Ex: U+0007 shows ^G */ |
| 796 | +#define QRF_ESC_Symbol 3 /* Unicode escapes. Ex: U+0007 shows U+2407 */ |
| 797 | + |
| 798 | +/* |
| 799 | +** Allowed values for "boolean" fields, such as "bColumnNames", "bWordWrap", |
| 800 | +** and "bTextJsonb". There is an extra "auto" variants so these are actually |
| 801 | +** tri-state settings, not booleans. |
| 802 | +*/ |
| 803 | +#define QRF_SW_Auto 0 /* Let QRF choose the best value */ |
| 804 | +#define QRF_SW_Off 1 /* This setting is forced off */ |
| 805 | +#define QRF_SW_On 2 /* This setting is forced on */ |
| 806 | +#define QRF_Auto 0 /* Alternate spelling for QRF_*_Auto */ |
| 807 | +#define QRF_No 1 /* Alternate spelling for QRF_SW_Off */ |
| 808 | +#define QRF_Yes 2 /* Alternate spelling for QRF_SW_On */ |
| 809 | + |
| 810 | +/* |
| 811 | +** Possible alignment values alignment settings |
| 812 | +** |
| 813 | +** Horizontal Vertial |
| 814 | +** ---------- -------- */ |
| 815 | +#define QRF_ALIGN_Auto 0 /* auto auto */ |
| 816 | +#define QRF_ALIGN_Left 1 /* left auto */ |
| 817 | +#define QRF_ALIGN_Center 2 /* center auto */ |
| 818 | +#define QRF_ALIGN_Right 3 /* right auto */ |
| 819 | +#define QRF_ALIGN_Top 4 /* auto top */ |
| 820 | +#define QRF_ALIGN_NW 5 /* left top */ |
| 821 | +#define QRF_ALIGN_N 6 /* center top */ |
| 822 | +#define QRF_ALIGN_NE 7 /* right top */ |
| 823 | +#define QRF_ALIGN_Middle 8 /* auto middle */ |
| 824 | +#define QRF_ALIGN_W 9 /* left middle */ |
| 825 | +#define QRF_ALIGN_C 10 /* center middle */ |
| 826 | +#define QRF_ALIGN_E 11 /* right middle */ |
| 827 | +#define QRF_ALIGN_Bottom 12 /* auto bottom */ |
| 828 | +#define QRF_ALIGN_SW 13 /* left bottom */ |
| 829 | +#define QRF_ALIGN_S 14 /* center bottom */ |
| 830 | +#define QRF_ALIGN_SE 15 /* right bottom */ |
| 831 | +#define QRF_ALIGN_HMASK 3 /* Horizontal alignment mask */ |
| 832 | +#define QRF_ALIGN_VMASK 12 /* Vertical alignment mask */ |
| 833 | + |
| 834 | +/* |
| 835 | +** Auxiliary routines contined within this module that might be useful |
| 836 | +** in other contexts, and which are therefore exported. |
| 837 | +*/ |
| 838 | +/* |
| 839 | +** Return an estimate of the width, in columns, for the single Unicode |
| 840 | +** character c. For normal characters, the answer is always 1. But the |
| 841 | +** estimate might be 0 or 2 for zero-width and double-width characters. |
| 842 | +** |
| 843 | +** Different display devices display unicode using different widths. So |
| 844 | +** it is impossible to know that true display width with 100% accuracy. |
| 845 | +** Inaccuracies in the width estimates might cause columns to be misaligned. |
| 846 | +** Unfortunately, there is nothing we can do about that. |
| 847 | +*/ |
| 848 | +int sqlite3_qrf_wcwidth(int c); |
| 849 | + |
| 850 | + |
| 851 | + |
| 852 | + |
| 853 | +#endif /* !defined(SQLITE_QRF_H) */ |
| 854 | + |
| 855 | +/************************* End ext/qrf/qrf.h ********************/ |
| 856 | +/************************* Begin ext/qrf/qrf.c ******************/ |
| 857 | +/* |
| 858 | +** 2025-10-20 |
| 859 | +** |
| 860 | +** The author disclaims copyright to this source code. In place of |
| 861 | +** a legal notice, here is a blessing: |
| 862 | +** |
| 863 | +** May you do good and not evil. |
| 864 | +** May you find forgiveness for yourself and forgive others. |
| 865 | +** May you share freely, never taking more than you give. |
| 866 | +** |
| 867 | +************************************************************************* |
| 868 | +** Implementation of the Result-Format or "qrf" utility library for SQLite. |
| 869 | +** See the qrf.md documentation for additional information. |
| 870 | +*/ |
| 871 | +#ifndef SQLITE_QRF_H |
| 872 | +#include "qrf.h" |
| 873 | +#endif |
| 874 | +#include <string.h> |
| 875 | +#include <assert.h> |
| 876 | + |
| 877 | +/* typedef sqlite3_int64 i64; */ |
| 878 | + |
| 879 | +/* A single line in the EQP output */ |
| 880 | +typedef struct qrfEQPGraphRow qrfEQPGraphRow; |
| 881 | +struct qrfEQPGraphRow { |
| 882 | + int iEqpId; /* ID for this row */ |
| 883 | + int iParentId; /* ID of the parent row */ |
| 884 | + qrfEQPGraphRow *pNext; /* Next row in sequence */ |
| 885 | + char zText[1]; /* Text to display for this row */ |
| 886 | +}; |
| 887 | + |
| 888 | +/* All EQP output is collected into an instance of the following */ |
| 889 | +typedef struct qrfEQPGraph qrfEQPGraph; |
| 890 | +struct qrfEQPGraph { |
| 891 | + qrfEQPGraphRow *pRow; /* Linked list of all rows of the EQP output */ |
| 892 | + qrfEQPGraphRow *pLast; /* Last element of the pRow list */ |
| 893 | + char zPrefix[100]; /* Graph prefix */ |
| 894 | +}; |
| 895 | + |
| 896 | +/* |
| 897 | +** Private state information. Subject to change from one release to the |
| 898 | +** next. |
| 899 | +*/ |
| 900 | +typedef struct Qrf Qrf; |
| 901 | +struct Qrf { |
| 902 | + sqlite3_stmt *pStmt; /* The statement whose output is to be rendered */ |
| 903 | + sqlite3 *db; /* The corresponding database connection */ |
| 904 | + sqlite3_stmt *pJTrans; /* JSONB to JSON translator statement */ |
| 905 | + char **pzErr; /* Write error message here, if not NULL */ |
| 906 | + sqlite3_str *pOut; /* Accumulated output */ |
| 907 | + int iErr; /* Error code */ |
| 908 | + int nCol; /* Number of output columns */ |
| 909 | + int expMode; /* Original sqlite3_stmt_isexplain() plus 1 */ |
| 910 | + int mxWidth; /* Screen width */ |
| 911 | + int mxHeight; /* nLineLimit */ |
| 912 | + union { |
| 913 | + struct { /* Content for QRF_STYLE_Line */ |
| 914 | + int mxColWth; /* Maximum display width of any column */ |
| 915 | + const char **azCol; /* Names of output columns (MODE_Line) */ |
| 916 | + } sLine; |
| 917 | + qrfEQPGraph *pGraph; /* EQP graph (Eqp, Stats, and StatsEst) */ |
| 918 | + struct { /* Content for QRF_STYLE_Explain */ |
| 919 | + int nIndent; /* Slots allocated for aiIndent */ |
| 920 | + int iIndent; /* Current slot */ |
| 921 | + int *aiIndent; /* Indentation for each opcode */ |
| 922 | + } sExpln; |
| 923 | + } u; |
| 924 | + sqlite3_int64 nRow; /* Number of rows handled so far */ |
| 925 | + int *actualWidth; /* Actual width of each column */ |
| 926 | + sqlite3_qrf_spec spec; /* Copy of the original spec */ |
| 927 | +}; |
| 928 | + |
| 929 | +/* |
| 930 | +** Data for substitute ctype.h functions. Used for x-platform |
| 931 | +** consistency and so that '_' is counted as an alphabetic |
| 932 | +** character. |
| 933 | +** |
| 934 | +** 0x01 - space |
| 935 | +** 0x02 - digit |
| 936 | +** 0x04 - alphabetic, including '_' |
| 937 | +*/ |
| 938 | +static const char qrfCType[] = { |
| 939 | + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, |
| 940 | + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 941 | + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 942 | + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, |
| 943 | + 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, |
| 944 | + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 4, |
| 945 | + 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, |
| 946 | + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, |
| 947 | + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 948 | + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 949 | + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 950 | + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 951 | + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 952 | + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 953 | + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 954 | + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 |
| 955 | +}; |
| 956 | +#define qrfSpace(x) ((qrfCType[(unsigned char)x]&1)!=0) |
| 957 | +#define qrfDigit(x) ((qrfCType[(unsigned char)x]&2)!=0) |
| 958 | +#define qrfAlpha(x) ((qrfCType[(unsigned char)x]&4)!=0) |
| 959 | +#define qrfAlnum(x) ((qrfCType[(unsigned char)x]&6)!=0) |
| 960 | + |
| 961 | +/* |
| 962 | +** Set an error code and error message. |
| 963 | +*/ |
| 964 | +static void qrfError( |
| 965 | + Qrf *p, /* Query result state */ |
| 966 | + int iCode, /* Error code */ |
| 967 | + const char *zFormat, /* Message format (or NULL) */ |
| 968 | + ... |
| 969 | +){ |
| 970 | + p->iErr = iCode; |
| 971 | + if( p->pzErr!=0 ){ |
| 972 | + sqlite3_free(*p->pzErr); |
| 973 | + *p->pzErr = 0; |
| 974 | + if( zFormat ){ |
| 975 | + va_list ap; |
| 976 | + va_start(ap, zFormat); |
| 977 | + *p->pzErr = sqlite3_vmprintf(zFormat, ap); |
| 978 | + va_end(ap); |
| 979 | + } |
| 980 | + } |
| 981 | +} |
| 982 | + |
| 983 | +/* |
| 984 | +** Out-of-memory error. |
| 985 | +*/ |
| 986 | +static void qrfOom(Qrf *p){ |
| 987 | + qrfError(p, SQLITE_NOMEM, "out of memory"); |
| 988 | +} |
| 989 | + |
| 990 | + |
| 991 | + |
| 992 | +/* |
| 993 | +** Add a new entry to the EXPLAIN QUERY PLAN data |
| 994 | +*/ |
| 995 | +static void qrfEqpAppend(Qrf *p, int iEqpId, int p2, const char *zText){ |
| 996 | + qrfEQPGraphRow *pNew; |
| 997 | + sqlite3_int64 nText; |
| 998 | + if( zText==0 ) return; |
| 999 | + if( p->u.pGraph==0 ){ |
| 1000 | + p->u.pGraph = sqlite3_malloc64( sizeof(qrfEQPGraph) ); |
| 1001 | + if( p->u.pGraph==0 ){ |
| 1002 | + qrfOom(p); |
| 1003 | + return; |
| 1004 | + } |
| 1005 | + memset(p->u.pGraph, 0, sizeof(qrfEQPGraph) ); |
| 1006 | + } |
| 1007 | + nText = strlen(zText); |
| 1008 | + pNew = sqlite3_malloc64( sizeof(*pNew) + nText ); |
| 1009 | + if( pNew==0 ){ |
| 1010 | + qrfOom(p); |
| 1011 | + return; |
| 1012 | + } |
| 1013 | + pNew->iEqpId = iEqpId; |
| 1014 | + pNew->iParentId = p2; |
| 1015 | + memcpy(pNew->zText, zText, nText+1); |
| 1016 | + pNew->pNext = 0; |
| 1017 | + if( p->u.pGraph->pLast ){ |
| 1018 | + p->u.pGraph->pLast->pNext = pNew; |
| 1019 | + }else{ |
| 1020 | + p->u.pGraph->pRow = pNew; |
| 1021 | + } |
| 1022 | + p->u.pGraph->pLast = pNew; |
| 1023 | +} |
| 1024 | + |
| 1025 | +/* |
| 1026 | +** Free and reset the EXPLAIN QUERY PLAN data that has been collected |
| 1027 | +** in p->u.pGraph. |
| 1028 | +*/ |
| 1029 | +static void qrfEqpReset(Qrf *p){ |
| 1030 | + qrfEQPGraphRow *pRow, *pNext; |
| 1031 | + if( p->u.pGraph ){ |
| 1032 | + for(pRow = p->u.pGraph->pRow; pRow; pRow = pNext){ |
| 1033 | + pNext = pRow->pNext; |
| 1034 | + sqlite3_free(pRow); |
| 1035 | + } |
| 1036 | + sqlite3_free(p->u.pGraph); |
| 1037 | + p->u.pGraph = 0; |
| 1038 | + } |
| 1039 | +} |
| 1040 | + |
| 1041 | +/* Return the next EXPLAIN QUERY PLAN line with iEqpId that occurs after |
| 1042 | +** pOld, or return the first such line if pOld is NULL |
| 1043 | +*/ |
| 1044 | +static qrfEQPGraphRow *qrfEqpNextRow(Qrf *p, int iEqpId, qrfEQPGraphRow *pOld){ |
| 1045 | + qrfEQPGraphRow *pRow = pOld ? pOld->pNext : p->u.pGraph->pRow; |
| 1046 | + while( pRow && pRow->iParentId!=iEqpId ) pRow = pRow->pNext; |
| 1047 | + return pRow; |
| 1048 | +} |
| 1049 | + |
| 1050 | +/* Render a single level of the graph that has iEqpId as its parent. Called |
| 1051 | +** recursively to render sublevels. |
| 1052 | +*/ |
| 1053 | +static void qrfEqpRenderLevel(Qrf *p, int iEqpId){ |
| 1054 | + qrfEQPGraphRow *pRow, *pNext; |
| 1055 | + i64 n = strlen(p->u.pGraph->zPrefix); |
| 1056 | + char *z; |
| 1057 | + for(pRow = qrfEqpNextRow(p, iEqpId, 0); pRow; pRow = pNext){ |
| 1058 | + pNext = qrfEqpNextRow(p, iEqpId, pRow); |
| 1059 | + z = pRow->zText; |
| 1060 | + sqlite3_str_appendf(p->pOut, "%s%s%s\n", p->u.pGraph->zPrefix, |
| 1061 | + pNext ? "|--" : "`--", z); |
| 1062 | + if( n<(i64)sizeof(p->u.pGraph->zPrefix)-7 ){ |
| 1063 | + memcpy(&p->u.pGraph->zPrefix[n], pNext ? "| " : " ", 4); |
| 1064 | + qrfEqpRenderLevel(p, pRow->iEqpId); |
| 1065 | + p->u.pGraph->zPrefix[n] = 0; |
| 1066 | + } |
| 1067 | + } |
| 1068 | +} |
| 1069 | + |
| 1070 | +/* |
| 1071 | +** Display and reset the EXPLAIN QUERY PLAN data |
| 1072 | +*/ |
| 1073 | +static void qrfEqpRender(Qrf *p, i64 nCycle){ |
| 1074 | + qrfEQPGraphRow *pRow; |
| 1075 | + if( p->u.pGraph!=0 && (pRow = p->u.pGraph->pRow)!=0 ){ |
| 1076 | + if( pRow->zText[0]=='-' ){ |
| 1077 | + if( pRow->pNext==0 ){ |
| 1078 | + qrfEqpReset(p); |
| 1079 | + return; |
| 1080 | + } |
| 1081 | + sqlite3_str_appendf(p->pOut, "%s\n", pRow->zText+3); |
| 1082 | + p->u.pGraph->pRow = pRow->pNext; |
| 1083 | + sqlite3_free(pRow); |
| 1084 | + }else if( nCycle>0 ){ |
| 1085 | + sqlite3_str_appendf(p->pOut, "QUERY PLAN (cycles=%lld [100%%])\n",nCycle); |
| 1086 | + }else{ |
| 1087 | + sqlite3_str_appendall(p->pOut, "QUERY PLAN\n"); |
| 1088 | + } |
| 1089 | + p->u.pGraph->zPrefix[0] = 0; |
| 1090 | + qrfEqpRenderLevel(p, 0); |
| 1091 | + qrfEqpReset(p); |
| 1092 | + } |
| 1093 | +} |
| 1094 | + |
| 1095 | +#ifdef SQLITE_ENABLE_STMT_SCANSTATUS |
| 1096 | +/* |
| 1097 | +** Helper function for qrfExpStats(). |
| 1098 | +** |
| 1099 | +*/ |
| 1100 | +static int qrfStatsHeight(sqlite3_stmt *p, int iEntry){ |
| 1101 | + int iPid = 0; |
| 1102 | + int ret = 1; |
| 1103 | + sqlite3_stmt_scanstatus_v2(p, iEntry, |
| 1104 | + SQLITE_SCANSTAT_SELECTID, SQLITE_SCANSTAT_COMPLEX, (void*)&iPid |
| 1105 | + ); |
| 1106 | + while( iPid!=0 ){ |
| 1107 | + int ii; |
| 1108 | + for(ii=0; 1; ii++){ |
| 1109 | + int iId; |
| 1110 | + int res; |
| 1111 | + res = sqlite3_stmt_scanstatus_v2(p, ii, |
| 1112 | + SQLITE_SCANSTAT_SELECTID, SQLITE_SCANSTAT_COMPLEX, (void*)&iId |
| 1113 | + ); |
| 1114 | + if( res ) break; |
| 1115 | + if( iId==iPid ){ |
| 1116 | + sqlite3_stmt_scanstatus_v2(p, ii, |
| 1117 | + SQLITE_SCANSTAT_PARENTID, SQLITE_SCANSTAT_COMPLEX, (void*)&iPid |
| 1118 | + ); |
| 1119 | + } |
| 1120 | + } |
| 1121 | + ret++; |
| 1122 | + } |
| 1123 | + return ret; |
| 1124 | +} |
| 1125 | +#endif /* SQLITE_ENABLE_STMT_SCANSTATUS */ |
| 1126 | + |
| 1127 | + |
| 1128 | +/* |
| 1129 | +** Generate ".scanstatus est" style of EQP output. |
| 1130 | +*/ |
| 1131 | +static void qrfEqpStats(Qrf *p){ |
| 1132 | +#ifndef SQLITE_ENABLE_STMT_SCANSTATUS |
| 1133 | + qrfError(p, SQLITE_ERROR, "not available in this build"); |
| 1134 | +#else |
| 1135 | + static const int f = SQLITE_SCANSTAT_COMPLEX; |
| 1136 | + sqlite3_stmt *pS = p->pStmt; |
| 1137 | + int i = 0; |
| 1138 | + i64 nTotal = 0; |
| 1139 | + int nWidth = 0; |
| 1140 | + sqlite3_str *pLine = sqlite3_str_new(p->db); |
| 1141 | + sqlite3_str *pStats = sqlite3_str_new(p->db); |
| 1142 | + qrfEqpReset(p); |
| 1143 | + |
| 1144 | + for(i=0; 1; i++){ |
| 1145 | + const char *z = 0; |
| 1146 | + int n = 0; |
| 1147 | + if( sqlite3_stmt_scanstatus_v2(pS,i,SQLITE_SCANSTAT_EXPLAIN,f,(void*)&z) ){ |
| 1148 | + break; |
| 1149 | + } |
| 1150 | + n = (int)strlen(z) + qrfStatsHeight(pS,i)*3; |
| 1151 | + if( n>nWidth ) nWidth = n; |
| 1152 | + } |
| 1153 | + nWidth += 4; |
| 1154 | + |
| 1155 | + sqlite3_stmt_scanstatus_v2(pS,-1, SQLITE_SCANSTAT_NCYCLE, f, (void*)&nTotal); |
| 1156 | + for(i=0; 1; i++){ |
| 1157 | + i64 nLoop = 0; |
| 1158 | + i64 nRow = 0; |
| 1159 | + i64 nCycle = 0; |
| 1160 | + int iId = 0; |
| 1161 | + int iPid = 0; |
| 1162 | + const char *zo = 0; |
| 1163 | + const char *zName = 0; |
| 1164 | + double rEst = 0.0; |
| 1165 | + |
| 1166 | + if( sqlite3_stmt_scanstatus_v2(pS,i,SQLITE_SCANSTAT_EXPLAIN,f,(void*)&zo) ){ |
| 1167 | + break; |
| 1168 | + } |
| 1169 | + sqlite3_stmt_scanstatus_v2(pS,i, SQLITE_SCANSTAT_EST,f,(void*)&rEst); |
| 1170 | + sqlite3_stmt_scanstatus_v2(pS,i, SQLITE_SCANSTAT_NLOOP,f,(void*)&nLoop); |
| 1171 | + sqlite3_stmt_scanstatus_v2(pS,i, SQLITE_SCANSTAT_NVISIT,f,(void*)&nRow); |
| 1172 | + sqlite3_stmt_scanstatus_v2(pS,i, SQLITE_SCANSTAT_NCYCLE,f,(void*)&nCycle); |
| 1173 | + sqlite3_stmt_scanstatus_v2(pS,i, SQLITE_SCANSTAT_SELECTID,f,(void*)&iId); |
| 1174 | + sqlite3_stmt_scanstatus_v2(pS,i, SQLITE_SCANSTAT_PARENTID,f,(void*)&iPid); |
| 1175 | + sqlite3_stmt_scanstatus_v2(pS,i, SQLITE_SCANSTAT_NAME,f,(void*)&zName); |
| 1176 | + |
| 1177 | + if( nCycle>=0 || nLoop>=0 || nRow>=0 ){ |
| 1178 | + const char *zSp = ""; |
| 1179 | + double rpl; |
| 1180 | + sqlite3_str_reset(pStats); |
| 1181 | + if( nCycle>=0 && nTotal>0 ){ |
| 1182 | + sqlite3_str_appendf(pStats, "cycles=%lld [%d%%]", |
| 1183 | + nCycle, ((nCycle*100)+nTotal/2) / nTotal |
| 1184 | + ); |
| 1185 | + zSp = " "; |
| 1186 | + } |
| 1187 | + if( nLoop>=0 ){ |
| 1188 | + sqlite3_str_appendf(pStats, "%sloops=%lld", zSp, nLoop); |
| 1189 | + zSp = " "; |
| 1190 | + } |
| 1191 | + if( nRow>=0 ){ |
| 1192 | + sqlite3_str_appendf(pStats, "%srows=%lld", zSp, nRow); |
| 1193 | + zSp = " "; |
| 1194 | + } |
| 1195 | + |
| 1196 | + if( p->spec.eStyle==QRF_STYLE_StatsEst ){ |
| 1197 | + rpl = (double)nRow / (double)nLoop; |
| 1198 | + sqlite3_str_appendf(pStats, "%srpl=%.1f est=%.1f", zSp, rpl, rEst); |
| 1199 | + } |
| 1200 | + |
| 1201 | + sqlite3_str_appendf(pLine, |
| 1202 | + "% *s (%s)", -1*(nWidth-qrfStatsHeight(pS,i)*3), zo, |
| 1203 | + sqlite3_str_value(pStats) |
| 1204 | + ); |
| 1205 | + sqlite3_str_reset(pStats); |
| 1206 | + qrfEqpAppend(p, iId, iPid, sqlite3_str_value(pLine)); |
| 1207 | + sqlite3_str_reset(pLine); |
| 1208 | + }else{ |
| 1209 | + qrfEqpAppend(p, iId, iPid, zo); |
| 1210 | + } |
| 1211 | + } |
| 1212 | + sqlite3_free(sqlite3_str_finish(pLine)); |
| 1213 | + sqlite3_free(sqlite3_str_finish(pStats)); |
| 1214 | +#endif |
| 1215 | +} |
| 1216 | + |
| 1217 | + |
| 1218 | +/* |
| 1219 | +** Reset the prepared statement. |
| 1220 | +*/ |
| 1221 | +static void qrfResetStmt(Qrf *p){ |
| 1222 | + int rc = sqlite3_reset(p->pStmt); |
| 1223 | + if( rc!=SQLITE_OK && p->iErr==SQLITE_OK ){ |
| 1224 | + qrfError(p, rc, "%s", sqlite3_errmsg(p->db)); |
| 1225 | + } |
| 1226 | +} |
| 1227 | + |
| 1228 | +/* |
| 1229 | +** If xWrite is defined, send all content of pOut to xWrite and |
| 1230 | +** reset pOut. |
| 1231 | +*/ |
| 1232 | +static void qrfWrite(Qrf *p){ |
| 1233 | + int n; |
| 1234 | + if( p->spec.xWrite && (n = sqlite3_str_length(p->pOut))>0 ){ |
| 1235 | + int rc = p->spec.xWrite(p->spec.pWriteArg, |
| 1236 | + sqlite3_str_value(p->pOut), |
| 1237 | + (sqlite3_int64)n); |
| 1238 | + sqlite3_str_reset(p->pOut); |
| 1239 | + if( rc ){ |
| 1240 | + qrfError(p, rc, "Failed to write %d bytes of output", n); |
| 1241 | + } |
| 1242 | + } |
| 1243 | +} |
| 1244 | + |
| 1245 | +/* Lookup table to estimate the number of columns consumed by a Unicode |
| 1246 | +** character. |
| 1247 | +*/ |
| 1248 | +static const struct { |
| 1249 | + unsigned char w; /* Width of the character in columns */ |
| 1250 | + int iFirst; /* First character in a span having this width */ |
| 1251 | +} aQrfUWidth[] = { |
| 1252 | + /* {1, 0x00000}, */ |
| 1253 | + {0, 0x00300}, {1, 0x00370}, {0, 0x00483}, {1, 0x00487}, {0, 0x00488}, |
| 1254 | + {1, 0x0048a}, {0, 0x00591}, {1, 0x005be}, {0, 0x005bf}, {1, 0x005c0}, |
| 1255 | + {0, 0x005c1}, {1, 0x005c3}, {0, 0x005c4}, {1, 0x005c6}, {0, 0x005c7}, |
| 1256 | + {1, 0x005c8}, {0, 0x00600}, {1, 0x00604}, {0, 0x00610}, {1, 0x00616}, |
| 1257 | + {0, 0x0064b}, {1, 0x0065f}, {0, 0x00670}, {1, 0x00671}, {0, 0x006d6}, |
| 1258 | + {1, 0x006e5}, {0, 0x006e7}, {1, 0x006e9}, {0, 0x006ea}, {1, 0x006ee}, |
| 1259 | + {0, 0x0070f}, {1, 0x00710}, {0, 0x00711}, {1, 0x00712}, {0, 0x00730}, |
| 1260 | + {1, 0x0074b}, {0, 0x007a6}, {1, 0x007b1}, {0, 0x007eb}, {1, 0x007f4}, |
| 1261 | + {0, 0x00901}, {1, 0x00903}, {0, 0x0093c}, {1, 0x0093d}, {0, 0x00941}, |
| 1262 | + {1, 0x00949}, {0, 0x0094d}, {1, 0x0094e}, {0, 0x00951}, {1, 0x00955}, |
| 1263 | + {0, 0x00962}, {1, 0x00964}, {0, 0x00981}, {1, 0x00982}, {0, 0x009bc}, |
| 1264 | + {1, 0x009bd}, {0, 0x009c1}, {1, 0x009c5}, {0, 0x009cd}, {1, 0x009ce}, |
| 1265 | + {0, 0x009e2}, {1, 0x009e4}, {0, 0x00a01}, {1, 0x00a03}, {0, 0x00a3c}, |
| 1266 | + {1, 0x00a3d}, {0, 0x00a41}, {1, 0x00a43}, {0, 0x00a47}, {1, 0x00a49}, |
| 1267 | + {0, 0x00a4b}, {1, 0x00a4e}, {0, 0x00a70}, {1, 0x00a72}, {0, 0x00a81}, |
| 1268 | + {1, 0x00a83}, {0, 0x00abc}, {1, 0x00abd}, {0, 0x00ac1}, {1, 0x00ac6}, |
| 1269 | + {0, 0x00ac7}, {1, 0x00ac9}, {0, 0x00acd}, {1, 0x00ace}, {0, 0x00ae2}, |
| 1270 | + {1, 0x00ae4}, {0, 0x00b01}, {1, 0x00b02}, {0, 0x00b3c}, {1, 0x00b3d}, |
| 1271 | + {0, 0x00b3f}, {1, 0x00b40}, {0, 0x00b41}, {1, 0x00b44}, {0, 0x00b4d}, |
| 1272 | + {1, 0x00b4e}, {0, 0x00b56}, {1, 0x00b57}, {0, 0x00b82}, {1, 0x00b83}, |
| 1273 | + {0, 0x00bc0}, {1, 0x00bc1}, {0, 0x00bcd}, {1, 0x00bce}, {0, 0x00c3e}, |
| 1274 | + {1, 0x00c41}, {0, 0x00c46}, {1, 0x00c49}, {0, 0x00c4a}, {1, 0x00c4e}, |
| 1275 | + {0, 0x00c55}, {1, 0x00c57}, {0, 0x00cbc}, {1, 0x00cbd}, {0, 0x00cbf}, |
| 1276 | + {1, 0x00cc0}, {0, 0x00cc6}, {1, 0x00cc7}, {0, 0x00ccc}, {1, 0x00cce}, |
| 1277 | + {0, 0x00ce2}, {1, 0x00ce4}, {0, 0x00d41}, {1, 0x00d44}, {0, 0x00d4d}, |
| 1278 | + {1, 0x00d4e}, {0, 0x00dca}, {1, 0x00dcb}, {0, 0x00dd2}, {1, 0x00dd5}, |
| 1279 | + {0, 0x00dd6}, {1, 0x00dd7}, {0, 0x00e31}, {1, 0x00e32}, {0, 0x00e34}, |
| 1280 | + {1, 0x00e3b}, {0, 0x00e47}, {1, 0x00e4f}, {0, 0x00eb1}, {1, 0x00eb2}, |
| 1281 | + {0, 0x00eb4}, {1, 0x00eba}, {0, 0x00ebb}, {1, 0x00ebd}, {0, 0x00ec8}, |
| 1282 | + {1, 0x00ece}, {0, 0x00f18}, {1, 0x00f1a}, {0, 0x00f35}, {1, 0x00f36}, |
| 1283 | + {0, 0x00f37}, {1, 0x00f38}, {0, 0x00f39}, {1, 0x00f3a}, {0, 0x00f71}, |
| 1284 | + {1, 0x00f7f}, {0, 0x00f80}, {1, 0x00f85}, {0, 0x00f86}, {1, 0x00f88}, |
| 1285 | + {0, 0x00f90}, {1, 0x00f98}, {0, 0x00f99}, {1, 0x00fbd}, {0, 0x00fc6}, |
| 1286 | + {1, 0x00fc7}, {0, 0x0102d}, {1, 0x01031}, {0, 0x01032}, {1, 0x01033}, |
| 1287 | + {0, 0x01036}, {1, 0x01038}, {0, 0x01039}, {1, 0x0103a}, {0, 0x01058}, |
| 1288 | + {1, 0x0105a}, {2, 0x01100}, {0, 0x01160}, {1, 0x01200}, {0, 0x0135f}, |
| 1289 | + {1, 0x01360}, {0, 0x01712}, {1, 0x01715}, {0, 0x01732}, {1, 0x01735}, |
| 1290 | + {0, 0x01752}, {1, 0x01754}, {0, 0x01772}, {1, 0x01774}, {0, 0x017b4}, |
| 1291 | + {1, 0x017b6}, {0, 0x017b7}, {1, 0x017be}, {0, 0x017c6}, {1, 0x017c7}, |
| 1292 | + {0, 0x017c9}, {1, 0x017d4}, {0, 0x017dd}, {1, 0x017de}, {0, 0x0180b}, |
| 1293 | + {1, 0x0180e}, {0, 0x018a9}, {1, 0x018aa}, {0, 0x01920}, {1, 0x01923}, |
| 1294 | + {0, 0x01927}, {1, 0x01929}, {0, 0x01932}, {1, 0x01933}, {0, 0x01939}, |
| 1295 | + {1, 0x0193c}, {0, 0x01a17}, {1, 0x01a19}, {0, 0x01b00}, {1, 0x01b04}, |
| 1296 | + {0, 0x01b34}, {1, 0x01b35}, {0, 0x01b36}, {1, 0x01b3b}, {0, 0x01b3c}, |
| 1297 | + {1, 0x01b3d}, {0, 0x01b42}, {1, 0x01b43}, {0, 0x01b6b}, {1, 0x01b74}, |
| 1298 | + {0, 0x01dc0}, {1, 0x01dcb}, {0, 0x01dfe}, {1, 0x01e00}, {0, 0x0200b}, |
| 1299 | + {1, 0x02010}, {0, 0x0202a}, {1, 0x0202f}, {0, 0x02060}, {1, 0x02064}, |
| 1300 | + {0, 0x0206a}, {1, 0x02070}, {0, 0x020d0}, {1, 0x020f0}, {2, 0x02329}, |
| 1301 | + {1, 0x0232b}, {2, 0x02e80}, {0, 0x0302a}, {2, 0x03030}, {1, 0x0303f}, |
| 1302 | + {2, 0x03040}, {0, 0x03099}, {2, 0x0309b}, {1, 0x0a4d0}, {0, 0x0a806}, |
| 1303 | + {1, 0x0a807}, {0, 0x0a80b}, {1, 0x0a80c}, {0, 0x0a825}, {1, 0x0a827}, |
| 1304 | + {2, 0x0ac00}, {1, 0x0d7a4}, {2, 0x0f900}, {1, 0x0fb00}, {0, 0x0fb1e}, |
| 1305 | + {1, 0x0fb1f}, {0, 0x0fe00}, {2, 0x0fe10}, {1, 0x0fe1a}, {0, 0x0fe20}, |
| 1306 | + {1, 0x0fe24}, {2, 0x0fe30}, {1, 0x0fe70}, {0, 0x0feff}, {2, 0x0ff00}, |
| 1307 | + {1, 0x0ff61}, {2, 0x0ffe0}, {1, 0x0ffe7}, {0, 0x0fff9}, {1, 0x0fffc}, |
| 1308 | + {0, 0x10a01}, {1, 0x10a04}, {0, 0x10a05}, {1, 0x10a07}, {0, 0x10a0c}, |
| 1309 | + {1, 0x10a10}, {0, 0x10a38}, {1, 0x10a3b}, {0, 0x10a3f}, {1, 0x10a40}, |
| 1310 | + {0, 0x1d167}, {1, 0x1d16a}, {0, 0x1d173}, {1, 0x1d183}, {0, 0x1d185}, |
| 1311 | + {1, 0x1d18c}, {0, 0x1d1aa}, {1, 0x1d1ae}, {0, 0x1d242}, {1, 0x1d245}, |
| 1312 | + {2, 0x20000}, {1, 0x2fffe}, {2, 0x30000}, {1, 0x3fffe}, {0, 0xe0001}, |
| 1313 | + {1, 0xe0002}, {0, 0xe0020}, {1, 0xe0080}, {0, 0xe0100}, {1, 0xe01f0} |
| 1314 | +}; |
| 1315 | + |
| 1316 | +/* |
| 1317 | +** Return an estimate of the width, in columns, for the single Unicode |
| 1318 | +** character c. For normal characters, the answer is always 1. But the |
| 1319 | +** estimate might be 0 or 2 for zero-width and double-width characters. |
| 1320 | +** |
| 1321 | +** Different display devices display unicode using different widths. So |
| 1322 | +** it is impossible to know that true display width with 100% accuracy. |
| 1323 | +** Inaccuracies in the width estimates might cause columns to be misaligned. |
| 1324 | +** Unfortunately, there is nothing we can do about that. |
| 1325 | +*/ |
| 1326 | +int sqlite3_qrf_wcwidth(int c){ |
| 1327 | + int iFirst, iLast; |
| 1328 | + |
| 1329 | + /* Fast path for common characters */ |
| 1330 | + if( c<=0x300 ) return 1; |
| 1331 | + |
| 1332 | + /* The general case */ |
| 1333 | + iFirst = 0; |
| 1334 | + iLast = sizeof(aQrfUWidth)/sizeof(aQrfUWidth[0]) - 1; |
| 1335 | + while( iFirst<iLast-1 ){ |
| 1336 | + int iMid = (iFirst+iLast)/2; |
| 1337 | + int cMid = aQrfUWidth[iMid].iFirst; |
| 1338 | + if( cMid < c ){ |
| 1339 | + iFirst = iMid; |
| 1340 | + }else if( cMid > c ){ |
| 1341 | + iLast = iMid - 1; |
| 1342 | + }else{ |
| 1343 | + return aQrfUWidth[iMid].w; |
| 1344 | + } |
| 1345 | + } |
| 1346 | + if( aQrfUWidth[iLast].iFirst > c ) return aQrfUWidth[iFirst].w; |
| 1347 | + return aQrfUWidth[iLast].w; |
| 1348 | +} |
| 1349 | + |
| 1350 | +/* |
| 1351 | +** Compute the value and length of a multi-byte UTF-8 character that |
| 1352 | +** begins at z[0]. Return the length. Write the Unicode value into *pU. |
| 1353 | +** |
| 1354 | +** This routine only works for *multi-byte* UTF-8 characters. It does |
| 1355 | +** not attempt to detect illegal characters. |
| 1356 | +*/ |
| 1357 | +int sqlite3_qrf_decode_utf8(const unsigned char *z, int *pU){ |
| 1358 | + if( (z[0] & 0xe0)==0xc0 && (z[1] & 0xc0)==0x80 ){ |
| 1359 | + *pU = ((z[0] & 0x1f)<<6) | (z[1] & 0x3f); |
| 1360 | + return 2; |
| 1361 | + } |
| 1362 | + if( (z[0] & 0xf0)==0xe0 && (z[1] & 0xc0)==0x80 && (z[2] & 0xc0)==0x80 ){ |
| 1363 | + *pU = ((z[0] & 0x0f)<<12) | ((z[1] & 0x3f)<<6) | (z[2] & 0x3f); |
| 1364 | + return 3; |
| 1365 | + } |
| 1366 | + if( (z[0] & 0xf8)==0xf0 && (z[1] & 0xc0)==0x80 && (z[2] & 0xc0)==0x80 |
| 1367 | + && (z[3] & 0xc0)==0x80 |
| 1368 | + ){ |
| 1369 | + *pU = ((z[0] & 0x0f)<<18) | ((z[1] & 0x3f)<<12) | ((z[2] & 0x3f))<<6 |
| 1370 | + | (z[3] & 0x3f); |
| 1371 | + return 4; |
| 1372 | + } |
| 1373 | + *pU = 0; |
| 1374 | + return 1; |
| 1375 | +} |
| 1376 | + |
| 1377 | +/* |
| 1378 | +** Check to see if z[] is a valid VT100 escape. If it is, then |
| 1379 | +** return the number of bytes in the escape sequence. Return 0 if |
| 1380 | +** z[] is not a VT100 escape. |
| 1381 | +** |
| 1382 | +** This routine assumes that z[0] is \033 (ESC). |
| 1383 | +*/ |
| 1384 | +static int qrfIsVt100(const unsigned char *z){ |
| 1385 | + int i; |
| 1386 | + if( z[1]!='[' ) return 0; |
| 1387 | + i = 2; |
| 1388 | + while( z[i]>=0x30 && z[i]<=0x3f ){ i++; } |
| 1389 | + while( z[i]>=0x20 && z[i]<=0x2f ){ i++; } |
| 1390 | + if( z[i]<0x40 || z[i]>0x7e ) return 0; |
| 1391 | + return i+1; |
| 1392 | +} |
| 1393 | + |
| 1394 | +/* |
| 1395 | +** Return the length of a string in display characters. |
| 1396 | +** Multibyte UTF8 characters count as a single character |
| 1397 | +** for single-width characters, or as two characters for |
| 1398 | +** double-width characters. |
| 1399 | +*/ |
| 1400 | +static int qrfDisplayLength(const char *zIn){ |
| 1401 | + const unsigned char *z = (const unsigned char*)zIn; |
| 1402 | + int n = 0; |
| 1403 | + while( *z ){ |
| 1404 | + if( z[0]<' ' ){ |
| 1405 | + int k; |
| 1406 | + if( z[0]=='\033' && (k = qrfIsVt100(z))>0 ){ |
| 1407 | + z += k; |
| 1408 | + }else{ |
| 1409 | + z++; |
| 1410 | + } |
| 1411 | + }else if( (0x80&z[0])==0 ){ |
| 1412 | + n++; |
| 1413 | + z++; |
| 1414 | + }else{ |
| 1415 | + int u = 0; |
| 1416 | + int len = sqlite3_qrf_decode_utf8(z, &u); |
| 1417 | + z += len; |
| 1418 | + n += sqlite3_qrf_wcwidth(u); |
| 1419 | + } |
| 1420 | + } |
| 1421 | + return n; |
| 1422 | +} |
| 1423 | + |
| 1424 | +/* |
| 1425 | +** Return the display width of the longest line of text |
| 1426 | +** in the (possibly) multi-line input string zIn[0..nByte]. |
| 1427 | +** zIn[] is not necessarily zero-terminated. Take |
| 1428 | +** into account tab characters, zero- and double-width |
| 1429 | +** characters, CR and NL, and VT100 escape codes. |
| 1430 | +** |
| 1431 | +** Write the number of newlines into *pnNL. So, *pnNL will |
| 1432 | +** return 0 if everything fits on one line, or positive it |
| 1433 | +** it will need to be split. |
| 1434 | +*/ |
| 1435 | +static int qrfDisplayWidth(const char *zIn, sqlite3_int64 nByte, int *pnNL){ |
| 1436 | + const unsigned char *z = (const unsigned char*)zIn; |
| 1437 | + const unsigned char *zEnd = &z[nByte]; |
| 1438 | + int mx = 0; |
| 1439 | + int n = 0; |
| 1440 | + int nNL = 0; |
| 1441 | + while( z<zEnd ){ |
| 1442 | + if( z[0]<' ' ){ |
| 1443 | + int k; |
| 1444 | + if( z[0]=='\033' && (k = qrfIsVt100(z))>0 ){ |
| 1445 | + z += k; |
| 1446 | + }else{ |
| 1447 | + if( z[0]=='\t' ){ |
| 1448 | + n = (n+8)&~7; |
| 1449 | + }else if( z[0]=='\n' || z[0]=='\r' ){ |
| 1450 | + nNL++; |
| 1451 | + if( n>mx ) mx = n; |
| 1452 | + n = 0; |
| 1453 | + } |
| 1454 | + z++; |
| 1455 | + } |
| 1456 | + }else if( (0x80&z[0])==0 ){ |
| 1457 | + n++; |
| 1458 | + z++; |
| 1459 | + }else{ |
| 1460 | + int u = 0; |
| 1461 | + int len = sqlite3_qrf_decode_utf8(z, &u); |
| 1462 | + z += len; |
| 1463 | + n += sqlite3_qrf_wcwidth(u); |
| 1464 | + } |
| 1465 | + } |
| 1466 | + if( mx>n ) n = mx; |
| 1467 | + if( pnNL ) *pnNL = nNL; |
| 1468 | + return n; |
| 1469 | +} |
| 1470 | + |
| 1471 | +/* |
| 1472 | +** Escape the input string if it is needed and in accordance with |
| 1473 | +** eEsc, which is either QRF_ESC_Ascii or QRF_ESC_Symbol. |
| 1474 | +** |
| 1475 | +** Escaping is needed if the string contains any control characters |
| 1476 | +** other than \t, \n, and \r\n |
| 1477 | +** |
| 1478 | +** If no escaping is needed (the common case) then set *ppOut to NULL |
| 1479 | +** and return 0. If escaping is needed, write the escaped string into |
| 1480 | +** memory obtained from sqlite3_malloc64() and make *ppOut point to that |
| 1481 | +** memory and return 0. If an error occurs, return non-zero. |
| 1482 | +** |
| 1483 | +** The caller is responsible for freeing *ppFree if it is non-NULL in order |
| 1484 | +** to reclaim memory. |
| 1485 | +*/ |
| 1486 | +static void qrfEscape( |
| 1487 | + int eEsc, /* QRF_ESC_Ascii or QRF_ESC_Symbol */ |
| 1488 | + sqlite3_str *pStr, /* String to be escaped */ |
| 1489 | + int iStart /* Begin escapding on this byte of pStr */ |
| 1490 | +){ |
| 1491 | + sqlite3_int64 i, j; /* Loop counters */ |
| 1492 | + sqlite3_int64 sz; /* Size of the string prior to escaping */ |
| 1493 | + sqlite3_int64 nCtrl = 0;/* Number of control characters to escape */ |
| 1494 | + unsigned char *zIn; /* Text to be escaped */ |
| 1495 | + unsigned char c; /* A single character of the text */ |
| 1496 | + unsigned char *zOut; /* Where to write the results */ |
| 1497 | + |
| 1498 | + /* Find the text to be escaped */ |
| 1499 | + zIn = (unsigned char*)sqlite3_str_value(pStr); |
| 1500 | + if( zIn==0 ) return; |
| 1501 | + zIn += iStart; |
| 1502 | + |
| 1503 | + /* Count the control characters */ |
| 1504 | + for(i=0; (c = zIn[i])!=0; i++){ |
| 1505 | + if( c<=0x1f |
| 1506 | + && c!='\t' |
| 1507 | + && c!='\n' |
| 1508 | + && (c!='\r' || zIn[i+1]!='\n') |
| 1509 | + ){ |
| 1510 | + nCtrl++; |
| 1511 | + } |
| 1512 | + } |
| 1513 | + if( nCtrl==0 ) return; /* Early out if no control characters */ |
| 1514 | + |
| 1515 | + /* Make space to hold the escapes. Copy the original text to the end |
| 1516 | + ** of the available space. */ |
| 1517 | + sz = sqlite3_str_length(pStr) - iStart; |
| 1518 | + if( eEsc==QRF_ESC_Symbol ) nCtrl *= 2; |
| 1519 | + sqlite3_str_appendchar(pStr, nCtrl, ' '); |
| 1520 | + zOut = (unsigned char*)sqlite3_str_value(pStr); |
| 1521 | + if( zOut==0 ) return; |
| 1522 | + zOut += iStart; |
| 1523 | + zIn = zOut + nCtrl; |
| 1524 | + memmove(zIn,zOut,sz); |
| 1525 | + |
| 1526 | + /* Convert the control characters */ |
| 1527 | + for(i=j=0; (c = zIn[i])!=0; i++){ |
| 1528 | + if( c>0x1f |
| 1529 | + || c=='\t' |
| 1530 | + || c=='\n' |
| 1531 | + || (c=='\r' && zIn[i+1]=='\n') |
| 1532 | + ){ |
| 1533 | + continue; |
| 1534 | + } |
| 1535 | + if( i>0 ){ |
| 1536 | + memmove(&zOut[j], zIn, i); |
| 1537 | + j += i; |
| 1538 | + } |
| 1539 | + zIn += i+1; |
| 1540 | + i = -1; |
| 1541 | + if( eEsc==QRF_ESC_Symbol ){ |
| 1542 | + zOut[j++] = 0xe2; |
| 1543 | + zOut[j++] = 0x90; |
| 1544 | + zOut[j++] = 0x80+c; |
| 1545 | + }else{ |
| 1546 | + zOut[j++] = '^'; |
| 1547 | + zOut[j++] = 0x40+c; |
| 1548 | + } |
| 1549 | + } |
| 1550 | +} |
| 1551 | + |
| 1552 | +/* |
| 1553 | +** If a field contains any character identified by a 1 in the following |
| 1554 | +** array, then the string must be quoted for CSV. |
| 1555 | +*/ |
| 1556 | +static const char qrfCsvQuote[] = { |
| 1557 | + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
| 1558 | + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
| 1559 | + 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, |
| 1560 | + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 1561 | + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 1562 | + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 1563 | + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 1564 | + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, |
| 1565 | + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
| 1566 | + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
| 1567 | + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
| 1568 | + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
| 1569 | + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
| 1570 | + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
| 1571 | + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
| 1572 | + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
| 1573 | +}; |
| 1574 | + |
| 1575 | +/* |
| 1576 | +** Encode text appropriately and append it to pOut. |
| 1577 | +*/ |
| 1578 | +static void qrfEncodeText(Qrf *p, sqlite3_str *pOut, const char *zTxt){ |
| 1579 | + int iStart = sqlite3_str_length(pOut); |
| 1580 | + switch( p->spec.eText ){ |
| 1581 | + case QRF_TEXT_Sql: { |
| 1582 | + if( p->spec.eEsc==QRF_ESC_Off ){ |
| 1583 | + sqlite3_str_appendf(pOut, "%Q", zTxt); |
| 1584 | + }else{ |
| 1585 | + sqlite3_str_appendf(pOut, "%#Q", zTxt); |
| 1586 | + } |
| 1587 | + break; |
| 1588 | + } |
| 1589 | + case QRF_TEXT_Csv: { |
| 1590 | + unsigned int i; |
| 1591 | + for(i=0; zTxt[i]; i++){ |
| 1592 | + if( qrfCsvQuote[((const unsigned char*)zTxt)[i]] ){ |
| 1593 | + i = 0; |
| 1594 | + break; |
| 1595 | + } |
| 1596 | + } |
| 1597 | + if( i==0 || strstr(zTxt, p->spec.zColumnSep)!=0 ){ |
| 1598 | + sqlite3_str_appendf(pOut, "\"%w\"", zTxt); |
| 1599 | + }else{ |
| 1600 | + sqlite3_str_appendall(pOut, zTxt); |
| 1601 | + } |
| 1602 | + break; |
| 1603 | + } |
| 1604 | + case QRF_TEXT_Html: { |
| 1605 | + const unsigned char *z = (const unsigned char*)zTxt; |
| 1606 | + while( *z ){ |
| 1607 | + unsigned int i = 0; |
| 1608 | + unsigned char c; |
| 1609 | + while( (c=z[i])>'>' |
| 1610 | + || (c && c!='<' && c!='>' && c!='&' && c!='\"' && c!='\'') |
| 1611 | + ){ |
| 1612 | + i++; |
| 1613 | + } |
| 1614 | + if( i>0 ){ |
| 1615 | + sqlite3_str_append(pOut, (const char*)z, i); |
| 1616 | + } |
| 1617 | + switch( z[i] ){ |
| 1618 | + case '>': sqlite3_str_append(pOut, "<", 4); break; |
| 1619 | + case '&': sqlite3_str_append(pOut, "&", 5); break; |
| 1620 | + case '<': sqlite3_str_append(pOut, "<", 4); break; |
| 1621 | + case '"': sqlite3_str_append(pOut, """, 6); break; |
| 1622 | + case '\'': sqlite3_str_append(pOut, "'", 5); break; |
| 1623 | + default: i--; |
| 1624 | + } |
| 1625 | + z += i + 1; |
| 1626 | + } |
| 1627 | + break; |
| 1628 | + } |
| 1629 | + case QRF_TEXT_Tcl: |
| 1630 | + case QRF_TEXT_Json: { |
| 1631 | + const unsigned char *z = (const unsigned char*)zTxt; |
| 1632 | + sqlite3_str_append(pOut, "\"", 1); |
| 1633 | + while( *z ){ |
| 1634 | + unsigned int i; |
| 1635 | + for(i=0; z[i]>=0x20 && z[i]!='\\' && z[i]!='"'; i++){} |
| 1636 | + if( i>0 ){ |
| 1637 | + sqlite3_str_append(pOut, (const char*)z, i); |
| 1638 | + } |
| 1639 | + if( z[i]==0 ) break; |
| 1640 | + switch( z[i] ){ |
| 1641 | + case '"': sqlite3_str_append(pOut, "\\\"", 2); break; |
| 1642 | + case '\\': sqlite3_str_append(pOut, "\\\\", 2); break; |
| 1643 | + case '\b': sqlite3_str_append(pOut, "\\b", 2); break; |
| 1644 | + case '\f': sqlite3_str_append(pOut, "\\f", 2); break; |
| 1645 | + case '\n': sqlite3_str_append(pOut, "\\n", 2); break; |
| 1646 | + case '\r': sqlite3_str_append(pOut, "\\r", 2); break; |
| 1647 | + case '\t': sqlite3_str_append(pOut, "\\t", 2); break; |
| 1648 | + default: { |
| 1649 | + if( p->spec.eText==QRF_TEXT_Json ){ |
| 1650 | + sqlite3_str_appendf(pOut, "\\u%04x", z[i]); |
| 1651 | + }else{ |
| 1652 | + sqlite3_str_appendf(pOut, "\\%03o", z[i]); |
| 1653 | + } |
| 1654 | + break; |
| 1655 | + } |
| 1656 | + } |
| 1657 | + z += i + 1; |
| 1658 | + } |
| 1659 | + sqlite3_str_append(pOut, "\"", 1); |
| 1660 | + break; |
| 1661 | + } |
| 1662 | + default: { |
| 1663 | + sqlite3_str_appendall(pOut, zTxt); |
| 1664 | + break; |
| 1665 | + } |
| 1666 | + } |
| 1667 | + if( p->spec.eEsc!=QRF_ESC_Off ){ |
| 1668 | + qrfEscape(p->spec.eEsc, pOut, iStart); |
| 1669 | + } |
| 1670 | +} |
| 1671 | + |
| 1672 | +/* |
| 1673 | +** Do a quick sanity check to see aBlob[0..nBlob-1] is valid JSONB |
| 1674 | +** return true if it is and false if it is not. |
| 1675 | +** |
| 1676 | +** False positives are possible, but not false negatives. |
| 1677 | +*/ |
| 1678 | +static int qrfJsonbQuickCheck(unsigned char *aBlob, int nBlob){ |
| 1679 | + unsigned char x; /* Payload size half-byte */ |
| 1680 | + int i; /* Loop counter */ |
| 1681 | + int n; /* Bytes in the payload size integer */ |
| 1682 | + sqlite3_uint64 sz; /* value of the payload size integer */ |
| 1683 | + |
| 1684 | + if( nBlob==0 ) return 0; |
| 1685 | + x = aBlob[0]>>4; |
| 1686 | + if( x<=11 ) return nBlob==(1+x); |
| 1687 | + n = x<14 ? x-11 : 4*(x-13); |
| 1688 | + if( nBlob<1+n ) return 0; |
| 1689 | + sz = aBlob[1]; |
| 1690 | + for(i=1; i<n; i++) sz = (sz<<8) + aBlob[i+1]; |
| 1691 | + return sz+n+1==(sqlite3_uint64)nBlob; |
| 1692 | +} |
| 1693 | + |
| 1694 | +/* |
| 1695 | +** The current iCol-th column of p->pStmt is known to be a BLOB. Check |
| 1696 | +** to see if that BLOB is really a JSONB blob. If it is, then translate |
| 1697 | +** it into a text JSON representation and return a pointer to that text JSON. |
| 1698 | +** If the BLOB is not JSONB, then return a NULL pointer. |
| 1699 | +** |
| 1700 | +** The memory used to hold the JSON text is managed internally by the |
| 1701 | +** "p" object and is overwritten and/or deallocated upon the next call |
| 1702 | +** to this routine (with the same p argument) or when the p object is |
| 1703 | +** finailized. |
| 1704 | +*/ |
| 1705 | +static const char *qrfJsonbToJson(Qrf *p, int iCol){ |
| 1706 | + int nByte; |
| 1707 | + const void *pBlob; |
| 1708 | + int rc; |
| 1709 | + nByte = sqlite3_column_bytes(p->pStmt, iCol); |
| 1710 | + pBlob = sqlite3_column_blob(p->pStmt, iCol); |
| 1711 | + if( qrfJsonbQuickCheck((unsigned char*)pBlob, nByte)==0 ){ |
| 1712 | + return 0; |
| 1713 | + } |
| 1714 | + if( p->pJTrans==0 ){ |
| 1715 | + sqlite3 *db; |
| 1716 | + rc = sqlite3_open(":memory:",&db); |
| 1717 | + if( rc ){ |
| 1718 | + sqlite3_close(db); |
| 1719 | + return 0; |
| 1720 | + } |
| 1721 | + rc = sqlite3_prepare_v2(db, "SELECT json(?1)", -1, &p->pJTrans, 0); |
| 1722 | + if( rc ){ |
| 1723 | + sqlite3_finalize(p->pJTrans); |
| 1724 | + p->pJTrans = 0; |
| 1725 | + sqlite3_close(db); |
| 1726 | + return 0; |
| 1727 | + } |
| 1728 | + }else{ |
| 1729 | + sqlite3_reset(p->pJTrans); |
| 1730 | + } |
| 1731 | + sqlite3_bind_blob(p->pJTrans, 1, (void*)pBlob, nByte, SQLITE_STATIC); |
| 1732 | + rc = sqlite3_step(p->pJTrans); |
| 1733 | + if( rc==SQLITE_ROW ){ |
| 1734 | + return (const char*)sqlite3_column_text(p->pJTrans, 0); |
| 1735 | + }else{ |
| 1736 | + return 0; |
| 1737 | + } |
| 1738 | +} |
| 1739 | + |
| 1740 | +/* |
| 1741 | +** Render value pVal into pOut |
| 1742 | +*/ |
| 1743 | +static void qrfRenderValue(Qrf *p, sqlite3_str *pOut, int iCol){ |
| 1744 | +#if SQLITE_VERSION_NUMBER>=3052000 |
| 1745 | + int iStartLen = sqlite3_str_length(pOut); |
| 1746 | +#endif |
| 1747 | + if( p->spec.xRender ){ |
| 1748 | + sqlite3_value *pVal; |
| 1749 | + char *z; |
| 1750 | + pVal = sqlite3_value_dup(sqlite3_column_value(p->pStmt,iCol)); |
| 1751 | + z = p->spec.xRender(p->spec.pRenderArg, pVal); |
| 1752 | + sqlite3_value_free(pVal); |
| 1753 | + if( z ){ |
| 1754 | + sqlite3_str_appendall(pOut, z); |
| 1755 | + sqlite3_free(z); |
| 1756 | + return; |
| 1757 | + } |
| 1758 | + } |
| 1759 | + switch( sqlite3_column_type(p->pStmt,iCol) ){ |
| 1760 | + case SQLITE_INTEGER: { |
| 1761 | + sqlite3_str_appendf(pOut, "%lld", sqlite3_column_int64(p->pStmt,iCol)); |
| 1762 | + break; |
| 1763 | + } |
| 1764 | + case SQLITE_FLOAT: { |
| 1765 | + const char *zTxt = (const char*)sqlite3_column_text(p->pStmt,iCol); |
| 1766 | + sqlite3_str_appendall(pOut, zTxt); |
| 1767 | + break; |
| 1768 | + } |
| 1769 | + case SQLITE_BLOB: { |
| 1770 | + if( p->spec.bTextJsonb==QRF_Yes ){ |
| 1771 | + const char *zJson = qrfJsonbToJson(p, iCol); |
| 1772 | + if( zJson ){ |
| 1773 | + if( p->spec.eText==QRF_TEXT_Sql ){ |
| 1774 | + sqlite3_str_append(pOut,"jsonb(",6); |
| 1775 | + qrfEncodeText(p, pOut, zJson); |
| 1776 | + sqlite3_str_append(pOut,")",1); |
| 1777 | + }else{ |
| 1778 | + qrfEncodeText(p, pOut, zJson); |
| 1779 | + } |
| 1780 | + break; |
| 1781 | + } |
| 1782 | + } |
| 1783 | + switch( p->spec.eBlob ){ |
| 1784 | + case QRF_BLOB_Hex: |
| 1785 | + case QRF_BLOB_Sql: { |
| 1786 | + int iStart; |
| 1787 | + int nBlob = sqlite3_column_bytes(p->pStmt,iCol); |
| 1788 | + int i, j; |
| 1789 | + char *zVal; |
| 1790 | + const unsigned char *a = sqlite3_column_blob(p->pStmt,iCol); |
| 1791 | + if( p->spec.eBlob==QRF_BLOB_Sql ){ |
| 1792 | + sqlite3_str_append(pOut, "x'", 2); |
| 1793 | + } |
| 1794 | + iStart = sqlite3_str_length(pOut); |
| 1795 | + sqlite3_str_appendchar(pOut, nBlob, ' '); |
| 1796 | + sqlite3_str_appendchar(pOut, nBlob, ' '); |
| 1797 | + if( p->spec.eBlob==QRF_BLOB_Sql ){ |
| 1798 | + sqlite3_str_appendchar(pOut, 1, '\''); |
| 1799 | + } |
| 1800 | + if( sqlite3_str_errcode(pOut) ) return; |
| 1801 | + zVal = sqlite3_str_value(pOut); |
| 1802 | + for(i=0, j=iStart; i<nBlob; i++, j+=2){ |
| 1803 | + unsigned char c = a[i]; |
| 1804 | + zVal[j] = "0123456789abcdef"[(c>>4)&0xf]; |
| 1805 | + zVal[j+1] = "0123456789abcdef"[(c)&0xf]; |
| 1806 | + } |
| 1807 | + break; |
| 1808 | + } |
| 1809 | + case QRF_BLOB_Tcl: |
| 1810 | + case QRF_BLOB_Json: { |
| 1811 | + int iStart; |
| 1812 | + int nBlob = sqlite3_column_bytes(p->pStmt,iCol); |
| 1813 | + int i, j; |
| 1814 | + char *zVal; |
| 1815 | + const unsigned char *a = sqlite3_column_blob(p->pStmt,iCol); |
| 1816 | + int szC = p->spec.eBlob==QRF_BLOB_Json ? 6 : 4; |
| 1817 | + sqlite3_str_append(pOut, "\"", 1); |
| 1818 | + iStart = sqlite3_str_length(pOut); |
| 1819 | + for(i=szC; i>0; i--){ |
| 1820 | + sqlite3_str_appendchar(pOut, nBlob, ' '); |
| 1821 | + } |
| 1822 | + sqlite3_str_appendchar(pOut, 1, '"'); |
| 1823 | + if( sqlite3_str_errcode(pOut) ) return; |
| 1824 | + zVal = sqlite3_str_value(pOut); |
| 1825 | + for(i=0, j=iStart; i<nBlob; i++, j+=szC){ |
| 1826 | + unsigned char c = a[i]; |
| 1827 | + zVal[j] = '\\'; |
| 1828 | + if( szC==4 ){ |
| 1829 | + zVal[j+1] = '0' + ((c>>6)&3); |
| 1830 | + zVal[j+2] = '0' + ((c>>3)&7); |
| 1831 | + zVal[j+3] = '0' + (c&7); |
| 1832 | + }else{ |
| 1833 | + zVal[j+1] = 'u'; |
| 1834 | + zVal[j+2] = '0'; |
| 1835 | + zVal[j+3] = '0'; |
| 1836 | + zVal[j+4] = "0123456789abcdef"[(c>>4)&0xf]; |
| 1837 | + zVal[j+5] = "0123456789abcdef"[(c)&0xf]; |
| 1838 | + } |
| 1839 | + } |
| 1840 | + break; |
| 1841 | + } |
| 1842 | + default: { |
| 1843 | + const char *zTxt = (const char*)sqlite3_column_text(p->pStmt,iCol); |
| 1844 | + qrfEncodeText(p, pOut, zTxt); |
| 1845 | + } |
| 1846 | + } |
| 1847 | + break; |
| 1848 | + } |
| 1849 | + case SQLITE_NULL: { |
| 1850 | + if( p->spec.bTextNull==QRF_Yes ){ |
| 1851 | + qrfEncodeText(p, pOut, p->spec.zNull); |
| 1852 | + }else{ |
| 1853 | + sqlite3_str_appendall(pOut, p->spec.zNull); |
| 1854 | + } |
| 1855 | + break; |
| 1856 | + } |
| 1857 | + case SQLITE_TEXT: { |
| 1858 | + const char *zTxt = (const char*)sqlite3_column_text(p->pStmt,iCol); |
| 1859 | + qrfEncodeText(p, pOut, zTxt); |
| 1860 | + break; |
| 1861 | + } |
| 1862 | + } |
| 1863 | +#if SQLITE_VERSION_NUMBER>=3052000 |
| 1864 | + if( p->spec.nCharLimit>0 |
| 1865 | + && (sqlite3_str_length(pOut) - iStartLen) > p->spec.nCharLimit |
| 1866 | + ){ |
| 1867 | + const unsigned char *z; |
| 1868 | + int ii = 0, w = 0, limit = p->spec.nCharLimit; |
| 1869 | + z = (const unsigned char*)sqlite3_str_value(pOut) + iStartLen; |
| 1870 | + if( limit<4 ) limit = 4; |
| 1871 | + while( 1 ){ |
| 1872 | + if( z[ii]<' ' ){ |
| 1873 | + int k; |
| 1874 | + if( z[ii]=='\033' && (k = qrfIsVt100(z+ii))>0 ){ |
| 1875 | + ii += k; |
| 1876 | + }else if( z[ii]==0 ){ |
| 1877 | + break; |
| 1878 | + }else{ |
| 1879 | + ii++; |
| 1880 | + } |
| 1881 | + }else if( (0x80&z[ii])==0 ){ |
| 1882 | + w++; |
| 1883 | + if( w>limit ) break; |
| 1884 | + ii++; |
| 1885 | + }else{ |
| 1886 | + int u = 0; |
| 1887 | + int len = sqlite3_qrf_decode_utf8(&z[ii], &u); |
| 1888 | + w += sqlite3_qrf_wcwidth(u); |
| 1889 | + if( w>limit ) break; |
| 1890 | + ii += len; |
| 1891 | + } |
| 1892 | + } |
| 1893 | + if( w>limit ){ |
| 1894 | + sqlite3_str_truncate(pOut, iStartLen+ii); |
| 1895 | + sqlite3_str_append(pOut, "...", 3); |
| 1896 | + } |
| 1897 | + } |
| 1898 | +#endif |
| 1899 | +} |
| 1900 | + |
| 1901 | +/* |
| 1902 | +** Store string zUtf to pOut as w characters. If w is negative, |
| 1903 | +** then right-justify the text. W is the width in display characters, not |
| 1904 | +** in bytes. Double-width unicode characters count as two characters. |
| 1905 | +** VT100 escape sequences count as zero. And so forth. |
| 1906 | +*/ |
| 1907 | +static void qrfWidthPrint(Qrf *p, sqlite3_str *pOut, int w, const char *zUtf){ |
| 1908 | + const unsigned char *a = (const unsigned char*)zUtf; |
| 1909 | + static const int mxW = 10000000; |
| 1910 | + unsigned char c; |
| 1911 | + int i = 0; |
| 1912 | + int n = 0; |
| 1913 | + int k; |
| 1914 | + int aw; |
| 1915 | + (void)p; |
| 1916 | + if( w<-mxW ){ |
| 1917 | + w = -mxW; |
| 1918 | + }else if( w>mxW ){ |
| 1919 | + w= mxW; |
| 1920 | + } |
| 1921 | + aw = w<0 ? -w : w; |
| 1922 | + if( a==0 ) a = (const unsigned char*)""; |
| 1923 | + while( (c = a[i])!=0 ){ |
| 1924 | + if( (c&0xc0)==0xc0 ){ |
| 1925 | + int u; |
| 1926 | + int len = sqlite3_qrf_decode_utf8(a+i, &u); |
| 1927 | + int x = sqlite3_qrf_wcwidth(u); |
| 1928 | + if( x+n>aw ){ |
| 1929 | + break; |
| 1930 | + } |
| 1931 | + i += len; |
| 1932 | + n += x; |
| 1933 | + }else if( c==0x1b && (k = qrfIsVt100(&a[i]))>0 ){ |
| 1934 | + i += k; |
| 1935 | + }else if( n>=aw ){ |
| 1936 | + break; |
| 1937 | + }else{ |
| 1938 | + n++; |
| 1939 | + i++; |
| 1940 | + } |
| 1941 | + } |
| 1942 | + if( n>=aw ){ |
| 1943 | + sqlite3_str_append(pOut, zUtf, i); |
| 1944 | + }else if( w<0 ){ |
| 1945 | + if( aw>n ) sqlite3_str_appendchar(pOut, aw-n, ' '); |
| 1946 | + sqlite3_str_append(pOut, zUtf, i); |
| 1947 | + }else{ |
| 1948 | + sqlite3_str_append(pOut, zUtf, i); |
| 1949 | + if( aw>n ) sqlite3_str_appendchar(pOut, aw-n, ' '); |
| 1950 | + } |
| 1951 | +} |
| 1952 | + |
| 1953 | +/* |
| 1954 | +** (*pz)[] is a line of text that is to be displayed the box or table or |
| 1955 | +** similar tabular formats. z[] contain newlines or might be too wide |
| 1956 | +** to fit in the columns so will need to be split into multiple line. |
| 1957 | +** |
| 1958 | +** This routine determines: |
| 1959 | +** |
| 1960 | +** * How many bytes of z[] should be shown on the current line. |
| 1961 | +** * How many character positions those bytes will cover. |
| 1962 | +** * The byte offset to the start of the next line. |
| 1963 | +*/ |
| 1964 | +static void qrfWrapLine( |
| 1965 | + const char *zIn, /* Input text to be displayed */ |
| 1966 | + int w, /* Column width in characters (not bytes) */ |
| 1967 | + int bWrap, /* True if we should do word-wrapping */ |
| 1968 | + int *pnThis, /* OUT: How many bytes of z[] for the current line */ |
| 1969 | + int *pnWide, /* OUT: How wide is the text of this line */ |
| 1970 | + int *piNext /* OUT: Offset into z[] to start of the next line */ |
| 1971 | +){ |
| 1972 | + int i; /* Input bytes consumed */ |
| 1973 | + int k; /* Bytes in a VT100 code */ |
| 1974 | + int n; /* Output column number */ |
| 1975 | + const unsigned char *z = (const unsigned char*)zIn; |
| 1976 | + unsigned char c = 0; |
| 1977 | + |
| 1978 | + if( zIn[0]==0 ){ |
| 1979 | + *pnThis = 0; |
| 1980 | + *pnWide = 0; |
| 1981 | + *piNext = 0; |
| 1982 | + return; |
| 1983 | + } |
| 1984 | + n = 0; |
| 1985 | + for(i=0; n<w; i++){ |
| 1986 | + c = zIn[i]; |
| 1987 | + if( c>=0xc0 ){ |
| 1988 | + int u; |
| 1989 | + int len = sqlite3_qrf_decode_utf8(&z[i], &u); |
| 1990 | + int wcw = sqlite3_qrf_wcwidth(u); |
| 1991 | + if( wcw+n>w ) break; |
| 1992 | + i += len-1; |
| 1993 | + n += wcw; |
| 1994 | + continue; |
| 1995 | + } |
| 1996 | + if( c>=' ' ){ |
| 1997 | + n++; |
| 1998 | + continue; |
| 1999 | + } |
| 2000 | + if( c==0 || c=='\n' ) break; |
| 2001 | + if( c=='\r' && zIn[i+1]=='\n' ){ c = zIn[++i]; break; } |
| 2002 | + if( c=='\t' ){ |
| 2003 | + int wcw = 8 - (n&7); |
| 2004 | + if( n+wcw>w ) break; |
| 2005 | + n += wcw; |
| 2006 | + continue; |
| 2007 | + } |
| 2008 | + if( c==0x1b && (k = qrfIsVt100(&z[i]))>0 ){ |
| 2009 | + i += k-1; |
| 2010 | + }else{ |
| 2011 | + n++; |
| 2012 | + } |
| 2013 | + } |
| 2014 | + if( c==0 ){ |
| 2015 | + *pnThis = i; |
| 2016 | + *pnWide = n; |
| 2017 | + *piNext = i; |
| 2018 | + return; |
| 2019 | + } |
| 2020 | + if( c=='\n' ){ |
| 2021 | + *pnThis = i; |
| 2022 | + *pnWide = n; |
| 2023 | + *piNext = i+1; |
| 2024 | + return; |
| 2025 | + } |
| 2026 | + |
| 2027 | + /* If we get this far, that means the current line will end at some |
| 2028 | + ** point that is neither a "\n" or a 0x00. Figure out where that |
| 2029 | + ** split should occur |
| 2030 | + */ |
| 2031 | + if( bWrap && z[i]!=0 && !qrfSpace(z[i]) && qrfAlnum(c)==qrfAlnum(z[i]) ){ |
| 2032 | + /* Perhaps try to back up to a better place to break the line */ |
| 2033 | + for(k=i-1; k>=i/2; k--){ |
| 2034 | + if( qrfSpace(z[k]) ) break; |
| 2035 | + } |
| 2036 | + if( k<i/2 ){ |
| 2037 | + for(k=i; k>=i/2; k--){ |
| 2038 | + if( qrfAlnum(z[k-1])!=qrfAlnum(z[k]) && (z[k]&0xc0)!=0x80 ) break; |
| 2039 | + } |
| 2040 | + } |
| 2041 | + if( k>=i/2 ){ |
| 2042 | + i = k; |
| 2043 | + n = qrfDisplayWidth((const char*)z, k, 0); |
| 2044 | + } |
| 2045 | + } |
| 2046 | + *pnThis = i; |
| 2047 | + *pnWide = n; |
| 2048 | + while( zIn[i]==' ' || zIn[i]=='\t' || zIn[i]=='\r' ){ i++; } |
| 2049 | + *piNext = i; |
| 2050 | +} |
| 2051 | + |
| 2052 | +/* |
| 2053 | +** Append nVal bytes of text from zVal onto the end of pOut. |
| 2054 | +** Convert tab characters in zVal to the appropriate number of |
| 2055 | +** spaces. |
| 2056 | +*/ |
| 2057 | +static void qrfAppendWithTabs( |
| 2058 | + sqlite3_str *pOut, /* Append text here */ |
| 2059 | + const char *zVal, /* Text to append */ |
| 2060 | + int nVal /* Use only the first nVal bytes of zVal[] */ |
| 2061 | +){ |
| 2062 | + int i = 0; |
| 2063 | + unsigned int col = 0; |
| 2064 | + unsigned char *z = (unsigned char *)zVal; |
| 2065 | + while( i<nVal ){ |
| 2066 | + unsigned char c = z[i]; |
| 2067 | + if( c<' ' ){ |
| 2068 | + int k; |
| 2069 | + sqlite3_str_append(pOut, (const char*)z, i); |
| 2070 | + nVal -= i; |
| 2071 | + z += i; |
| 2072 | + i = 0; |
| 2073 | + if( c=='\033' && (k = qrfIsVt100(z))>0 ){ |
| 2074 | + sqlite3_str_append(pOut, (const char*)z, k); |
| 2075 | + z += k; |
| 2076 | + nVal -= k; |
| 2077 | + }else if( c=='\t' ){ |
| 2078 | + k = 8 - (col&7); |
| 2079 | + sqlite3_str_appendchar(pOut, k, ' '); |
| 2080 | + col += k; |
| 2081 | + z++; |
| 2082 | + nVal--; |
| 2083 | + }else if( c=='\r' && nVal==1 ){ |
| 2084 | + z++; |
| 2085 | + nVal--; |
| 2086 | + }else{ |
| 2087 | + char zCtrlPik[4]; |
| 2088 | + col++; |
| 2089 | + zCtrlPik[0] = 0xe2; |
| 2090 | + zCtrlPik[1] = 0x90; |
| 2091 | + zCtrlPik[2] = 0x80+c; |
| 2092 | + sqlite3_str_append(pOut, zCtrlPik, 3); |
| 2093 | + z++; |
| 2094 | + nVal--; |
| 2095 | + } |
| 2096 | + }else if( (0x80&c)==0 ){ |
| 2097 | + i++; |
| 2098 | + col++; |
| 2099 | + }else{ |
| 2100 | + int u = 0; |
| 2101 | + int len = sqlite3_qrf_decode_utf8(&z[i], &u); |
| 2102 | + i += len; |
| 2103 | + col += sqlite3_qrf_wcwidth(u); |
| 2104 | + } |
| 2105 | + } |
| 2106 | + sqlite3_str_append(pOut, (const char*)z, i); |
| 2107 | +} |
| 2108 | + |
| 2109 | +/* |
| 2110 | +** Output horizontally justified text into pOut. The text is the |
| 2111 | +** first nVal bytes of zVal. Include nWS bytes of whitespace, either |
| 2112 | +** split between both sides, or on the left, or on the right, depending |
| 2113 | +** on eAlign. |
| 2114 | +*/ |
| 2115 | +static void qrfPrintAligned( |
| 2116 | + sqlite3_str *pOut, /* Append text here */ |
| 2117 | + const char *zVal, /* Text to append */ |
| 2118 | + int nVal, /* Use only the first nVal bytes of zVal[] */ |
| 2119 | + int nWS, /* Whitespace for horizonal alignment */ |
| 2120 | + unsigned char eAlign /* Alignment type */ |
| 2121 | +){ |
| 2122 | + eAlign &= QRF_ALIGN_HMASK; |
| 2123 | + if( eAlign==QRF_ALIGN_Center ){ |
| 2124 | + /* Center the text */ |
| 2125 | + sqlite3_str_appendchar(pOut, nWS/2, ' '); |
| 2126 | + qrfAppendWithTabs(pOut, zVal, nVal); |
| 2127 | + sqlite3_str_appendchar(pOut, nWS - nWS/2, ' '); |
| 2128 | + }else if( eAlign==QRF_ALIGN_Right){ |
| 2129 | + /* Right justify the text */ |
| 2130 | + sqlite3_str_appendchar(pOut, nWS, ' '); |
| 2131 | + qrfAppendWithTabs(pOut, zVal, nVal); |
| 2132 | + }else{ |
| 2133 | + /* Left justify the next */ |
| 2134 | + qrfAppendWithTabs(pOut, zVal, nVal); |
| 2135 | + sqlite3_str_appendchar(pOut, nWS, ' '); |
| 2136 | + } |
| 2137 | +} |
| 2138 | + |
| 2139 | +/* |
| 2140 | +** GCC does not define the offsetof() macro so we'll have to do it |
| 2141 | +** ourselves. |
| 2142 | +*/ |
| 2143 | +#ifndef offsetof |
| 2144 | +# define offsetof(ST,M) ((size_t)((char*)&((ST*)0)->M - (char*)0)) |
| 2145 | +#endif |
| 2146 | + |
| 2147 | +/* |
| 2148 | +** Data for columnar layout, collected into a single object so |
| 2149 | +** that it can be more easily passed into subroutines. |
| 2150 | +*/ |
| 2151 | +typedef struct qrfColData qrfColData; |
| 2152 | +struct qrfColData { |
| 2153 | + Qrf *p; /* The QRF instance */ |
| 2154 | + int nCol; /* Number of columns in the table */ |
| 2155 | + unsigned char bMultiRow; /* One or more cells will span multiple lines */ |
| 2156 | + unsigned char nMargin; /* Width of column margins */ |
| 2157 | + sqlite3_int64 nRow; /* Number of rows */ |
| 2158 | + sqlite3_int64 nAlloc; /* Number of cells allocated */ |
| 2159 | + sqlite3_int64 n; /* Number of cells. nCol*nRow */ |
| 2160 | + char **az; /* Content of all cells */ |
| 2161 | + int *aiWth; /* Width of each cell */ |
| 2162 | + struct qrfPerCol { /* Per-column data */ |
| 2163 | + char *z; /* Cache of text for current row */ |
| 2164 | + int w; /* Computed width of this column */ |
| 2165 | + int mxW; /* Maximum natural (unwrapped) width */ |
| 2166 | + unsigned char e; /* Alignment */ |
| 2167 | + unsigned char fx; /* Width is fixed */ |
| 2168 | + } *a; /* One per column */ |
| 2169 | +}; |
| 2170 | + |
| 2171 | +/* |
| 2172 | +** Free all the memory allocates in the qrfColData object |
| 2173 | +*/ |
| 2174 | +static void qrfColDataFree(qrfColData *p){ |
| 2175 | + sqlite3_int64 i; |
| 2176 | + for(i=0; i<p->n; i++) sqlite3_free(p->az[i]); |
| 2177 | + sqlite3_free(p->az); |
| 2178 | + sqlite3_free(p->aiWth); |
| 2179 | + sqlite3_free(p->a); |
| 2180 | + memset(p, 0, sizeof(*p)); |
| 2181 | +} |
| 2182 | + |
| 2183 | +/* |
| 2184 | +** Allocate space for more cells in the qrfColData object. |
| 2185 | +** Return non-zero if a memory allocation fails. |
| 2186 | +*/ |
| 2187 | +static int qrfColDataEnlarge(qrfColData *p){ |
| 2188 | + char **azData; |
| 2189 | + int *aiWth; |
| 2190 | + p->nAlloc = 2*p->nAlloc + 10*p->nCol; |
| 2191 | + azData = sqlite3_realloc64(p->az, p->nAlloc*sizeof(char*)); |
| 2192 | + if( azData==0 ){ |
| 2193 | + qrfOom(p->p); |
| 2194 | + qrfColDataFree(p); |
| 2195 | + return 1; |
| 2196 | + } |
| 2197 | + p->az = azData; |
| 2198 | + aiWth = sqlite3_realloc64(p->aiWth, p->nAlloc*sizeof(int)); |
| 2199 | + if( aiWth==0 ){ |
| 2200 | + qrfOom(p->p); |
| 2201 | + qrfColDataFree(p); |
| 2202 | + return 1; |
| 2203 | + } |
| 2204 | + p->aiWth = aiWth; |
| 2205 | + return 0; |
| 2206 | +} |
| 2207 | + |
| 2208 | +/* |
| 2209 | +** Print a markdown or table-style row separator using ascii-art |
| 2210 | +*/ |
| 2211 | +static void qrfRowSeparator(sqlite3_str *pOut, qrfColData *p, char cSep){ |
| 2212 | + int i; |
| 2213 | + if( p->nCol>0 ){ |
| 2214 | + sqlite3_str_append(pOut, &cSep, 1); |
| 2215 | + sqlite3_str_appendchar(pOut, p->a[0].w+p->nMargin, '-'); |
| 2216 | + for(i=1; i<p->nCol; i++){ |
| 2217 | + sqlite3_str_append(pOut, &cSep, 1); |
| 2218 | + sqlite3_str_appendchar(pOut, p->a[i].w+p->nMargin, '-'); |
| 2219 | + } |
| 2220 | + sqlite3_str_append(pOut, &cSep, 1); |
| 2221 | + } |
| 2222 | + sqlite3_str_append(pOut, "\n", 1); |
| 2223 | +} |
| 2224 | + |
| 2225 | +/* |
| 2226 | +** UTF8 box-drawing characters. Imagine box lines like this: |
| 2227 | +** |
| 2228 | +** 1 |
| 2229 | +** | |
| 2230 | +** 4 --+-- 2 |
| 2231 | +** | |
| 2232 | +** 3 |
| 2233 | +** |
| 2234 | +** Each box characters has between 2 and 4 of the lines leading from |
| 2235 | +** the center. The characters are here identified by the numbers of |
| 2236 | +** their corresponding lines. |
| 2237 | +*/ |
| 2238 | +#define BOX_24 "\342\224\200" /* U+2500 --- */ |
| 2239 | +#define BOX_13 "\342\224\202" /* U+2502 | */ |
| 2240 | +#define BOX_23 "\342\224\214" /* U+250c ,- */ |
| 2241 | +#define BOX_34 "\342\224\220" /* U+2510 -, */ |
| 2242 | +#define BOX_12 "\342\224\224" /* U+2514 '- */ |
| 2243 | +#define BOX_14 "\342\224\230" /* U+2518 -' */ |
| 2244 | +#define BOX_123 "\342\224\234" /* U+251c |- */ |
| 2245 | +#define BOX_134 "\342\224\244" /* U+2524 -| */ |
| 2246 | +#define BOX_234 "\342\224\254" /* U+252c -,- */ |
| 2247 | +#define BOX_124 "\342\224\264" /* U+2534 -'- */ |
| 2248 | +#define BOX_1234 "\342\224\274" /* U+253c -|- */ |
| 2249 | + |
| 2250 | +/* Draw horizontal line N characters long using unicode box |
| 2251 | +** characters |
| 2252 | +*/ |
| 2253 | +static void qrfBoxLine(sqlite3_str *pOut, int N){ |
| 2254 | + const char zDash[] = |
| 2255 | + BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 |
| 2256 | + BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24; |
| 2257 | + const int nDash = sizeof(zDash) - 1; |
| 2258 | + N *= 3; |
| 2259 | + while( N>nDash ){ |
| 2260 | + sqlite3_str_append(pOut, zDash, nDash); |
| 2261 | + N -= nDash; |
| 2262 | + } |
| 2263 | + sqlite3_str_append(pOut, zDash, N); |
| 2264 | +} |
| 2265 | + |
| 2266 | +/* |
| 2267 | +** Draw a horizontal separator for a QRF_STYLE_Box table. |
| 2268 | +*/ |
| 2269 | +static void qrfBoxSeparator( |
| 2270 | + sqlite3_str *pOut, |
| 2271 | + qrfColData *p, |
| 2272 | + const char *zSep1, |
| 2273 | + const char *zSep2, |
| 2274 | + const char *zSep3 |
| 2275 | +){ |
| 2276 | + int i; |
| 2277 | + if( p->nCol>0 ){ |
| 2278 | + sqlite3_str_appendall(pOut, zSep1); |
| 2279 | + qrfBoxLine(pOut, p->a[0].w+p->nMargin); |
| 2280 | + for(i=1; i<p->nCol; i++){ |
| 2281 | + sqlite3_str_appendall(pOut, zSep2); |
| 2282 | + qrfBoxLine(pOut, p->a[i].w+p->nMargin); |
| 2283 | + } |
| 2284 | + sqlite3_str_appendall(pOut, zSep3); |
| 2285 | + } |
| 2286 | + sqlite3_str_append(pOut, "\n", 1); |
| 2287 | +} |
| 2288 | + |
| 2289 | +/* |
| 2290 | +** Load into pData the default alignment for the body of a table. |
| 2291 | +*/ |
| 2292 | +static void qrfLoadAlignment(qrfColData *pData, Qrf *p){ |
| 2293 | + sqlite3_int64 i; |
| 2294 | + for(i=0; i<pData->nCol; i++){ |
| 2295 | + pData->a[i].e = p->spec.eDfltAlign; |
| 2296 | + if( i<p->spec.nAlign ){ |
| 2297 | + unsigned char ax = p->spec.aAlign[i]; |
| 2298 | + if( (ax & QRF_ALIGN_HMASK)!=0 ){ |
| 2299 | + pData->a[i].e = (ax & QRF_ALIGN_HMASK) | |
| 2300 | + (pData->a[i].e & QRF_ALIGN_VMASK); |
| 2301 | + } |
| 2302 | + }else if( i<p->spec.nWidth ){ |
| 2303 | + if( p->spec.aWidth[i]<0 ){ |
| 2304 | + pData->a[i].e = QRF_ALIGN_Right | |
| 2305 | + (pData->a[i].e & QRF_ALIGN_VMASK); |
| 2306 | + } |
| 2307 | + } |
| 2308 | + } |
| 2309 | +} |
| 2310 | + |
| 2311 | +/* |
| 2312 | +** Adjust the layout for the screen width restriction |
| 2313 | +*/ |
| 2314 | +static void qrfRestrictScreenWidth(qrfColData *pData, Qrf *p){ |
| 2315 | + int sepW; /* Width of all box separators and margins */ |
| 2316 | + int sumW; /* Total width of data area over all columns */ |
| 2317 | + int targetW; /* Desired total data area */ |
| 2318 | + int i; /* Loop counters */ |
| 2319 | + int nCol; /* Number of columns */ |
| 2320 | + |
| 2321 | + pData->nMargin = 2; /* Default to normal margins */ |
| 2322 | + if( p->spec.nScreenWidth==0 ) return; |
| 2323 | + if( p->spec.eStyle==QRF_STYLE_Column ){ |
| 2324 | + sepW = pData->nCol*2 - 2; |
| 2325 | + }else{ |
| 2326 | + sepW = pData->nCol*3 + 1; |
| 2327 | + } |
| 2328 | + nCol = pData->nCol; |
| 2329 | + for(i=sumW=0; i<nCol; i++) sumW += pData->a[i].w; |
| 2330 | + if( p->spec.nScreenWidth >= sumW+sepW ) return; |
| 2331 | + |
| 2332 | + /* First thing to do is reduce the separation between columns */ |
| 2333 | + pData->nMargin = 0; |
| 2334 | + if( p->spec.eStyle==QRF_STYLE_Column ){ |
| 2335 | + sepW = pData->nCol - 1; |
| 2336 | + }else{ |
| 2337 | + sepW = pData->nCol + 1; |
| 2338 | + } |
| 2339 | + targetW = p->spec.nScreenWidth - sepW; |
| 2340 | + |
| 2341 | +#define MIN_SQUOZE 8 |
| 2342 | +#define MIN_EX_SQUOZE 16 |
| 2343 | + /* Reduce the width of the widest eligible column. A column is |
| 2344 | + ** eligible for narrowing if: |
| 2345 | + ** |
| 2346 | + ** * It is not a fixed-width column (a[0].fx is false) |
| 2347 | + ** * The current width is more than MIN_SQUOZE |
| 2348 | + ** * Either: |
| 2349 | + ** + The current width is more then MIN_EX_SQUOZE, or |
| 2350 | + ** + The current width is more than half the max width (a[].mxW) |
| 2351 | + ** |
| 2352 | + ** Keep making reductions until either no more reductions are |
| 2353 | + ** possible or until the size target is reached. |
| 2354 | + */ |
| 2355 | + while( sumW > targetW ){ |
| 2356 | + int gain, w; |
| 2357 | + int ix = -1; |
| 2358 | + int mx = 0; |
| 2359 | + for(i=0; i<nCol; i++){ |
| 2360 | + if( pData->a[i].fx==0 |
| 2361 | + && (w = pData->a[i].w)>mx |
| 2362 | + && w>MIN_SQUOZE |
| 2363 | + && (w>MIN_EX_SQUOZE || w*2>pData->a[i].mxW) |
| 2364 | + ){ |
| 2365 | + ix = i; |
| 2366 | + mx = w; |
| 2367 | + } |
| 2368 | + } |
| 2369 | + if( ix<0 ) break; |
| 2370 | + if( mx>=MIN_SQUOZE*2 ){ |
| 2371 | + gain = mx/2; |
| 2372 | + }else{ |
| 2373 | + gain = mx - MIN_SQUOZE; |
| 2374 | + } |
| 2375 | + if( sumW - gain < targetW ){ |
| 2376 | + gain = sumW - targetW; |
| 2377 | + } |
| 2378 | + sumW -= gain; |
| 2379 | + pData->a[ix].w -= gain; |
| 2380 | + pData->bMultiRow = 1; |
| 2381 | + } |
| 2382 | +} |
| 2383 | + |
| 2384 | +/* |
| 2385 | +** Columnar modes require that the entire query be evaluated first, with |
| 2386 | +** results written into memory, so that we can compute appropriate column |
| 2387 | +** widths. |
| 2388 | +*/ |
| 2389 | +static void qrfColumnar(Qrf *p){ |
| 2390 | + sqlite3_int64 i, j; /* Loop counters */ |
| 2391 | + const char *colSep = 0; /* Column separator text */ |
| 2392 | + const char *rowSep = 0; /* Row terminator text */ |
| 2393 | + const char *rowStart = 0; /* Row start text */ |
| 2394 | + int szColSep, szRowSep, szRowStart; /* Size in bytes of previous 3 */ |
| 2395 | + int rc; /* Result code */ |
| 2396 | + int nColumn = p->nCol; /* Number of columns */ |
| 2397 | + int bWW; /* True to do word-wrap */ |
| 2398 | + sqlite3_str *pStr; /* Temporary rendering */ |
| 2399 | + qrfColData data; /* Columnar layout data */ |
| 2400 | + |
| 2401 | + rc = sqlite3_step(p->pStmt); |
| 2402 | + if( rc!=SQLITE_ROW || nColumn==0 ){ |
| 2403 | + return; /* No output */ |
| 2404 | + } |
| 2405 | + |
| 2406 | + /* Initialize the data container */ |
| 2407 | + memset(&data, 0, sizeof(data)); |
| 2408 | + data.nCol = p->nCol; |
| 2409 | + data.a = sqlite3_malloc64( nColumn*sizeof(struct qrfPerCol) ); |
| 2410 | + if( data.a==0 ){ |
| 2411 | + qrfOom(p); |
| 2412 | + return; |
| 2413 | + } |
| 2414 | + memset(data.a, 0, nColumn*sizeof(struct qrfPerCol) ); |
| 2415 | + if( qrfColDataEnlarge(&data) ) return; |
| 2416 | + assert( data.az!=0 ); |
| 2417 | + |
| 2418 | + /* Load the column header names and all cell content into data */ |
| 2419 | + if( p->spec.bTitles==QRF_Yes ){ |
| 2420 | + unsigned char saved_eText = p->spec.eText; |
| 2421 | + p->spec.eText = p->spec.eTitle; |
| 2422 | + for(i=0; i<nColumn; i++){ |
| 2423 | + const char *z = (const char*)sqlite3_column_name(p->pStmt,i); |
| 2424 | + int nNL = 0; |
| 2425 | + int n, w; |
| 2426 | + pStr = sqlite3_str_new(p->db); |
| 2427 | + qrfEncodeText(p, pStr, z ? z : ""); |
| 2428 | + n = sqlite3_str_length(pStr); |
| 2429 | + z = data.az[data.n] = sqlite3_str_finish(pStr); |
| 2430 | + data.aiWth[data.n] = w = qrfDisplayWidth(z, n, &nNL); |
| 2431 | + data.n++; |
| 2432 | + if( w>data.a[i].mxW ) data.a[i].mxW = w; |
| 2433 | + if( nNL ) data.bMultiRow = 1; |
| 2434 | + } |
| 2435 | + p->spec.eText = saved_eText; |
| 2436 | + p->nRow++; |
| 2437 | + } |
| 2438 | + do{ |
| 2439 | + if( data.n+nColumn > data.nAlloc ){ |
| 2440 | + if( qrfColDataEnlarge(&data) ) return; |
| 2441 | + } |
| 2442 | + for(i=0; i<nColumn; i++){ |
| 2443 | + char *z; |
| 2444 | + int nNL = 0; |
| 2445 | + int n, w; |
| 2446 | + pStr = sqlite3_str_new(p->db); |
| 2447 | + qrfRenderValue(p, pStr, i); |
| 2448 | + n = sqlite3_str_length(pStr); |
| 2449 | + z = data.az[data.n] = sqlite3_str_finish(pStr); |
| 2450 | + data.aiWth[data.n] = w = qrfDisplayWidth(z, n, &nNL); |
| 2451 | + data.n++; |
| 2452 | + if( w>data.a[i].mxW ) data.a[i].mxW = w; |
| 2453 | + if( nNL ) data.bMultiRow = 1; |
| 2454 | + } |
| 2455 | + p->nRow++; |
| 2456 | + }while( sqlite3_step(p->pStmt)==SQLITE_ROW && p->iErr==SQLITE_OK ); |
| 2457 | + if( p->iErr ){ |
| 2458 | + qrfColDataFree(&data); |
| 2459 | + return; |
| 2460 | + } |
| 2461 | + |
| 2462 | + /* Compute the width and alignment of every column */ |
| 2463 | + if( p->spec.bTitles==QRF_No ){ |
| 2464 | + qrfLoadAlignment(&data, p); |
| 2465 | + }else{ |
| 2466 | + unsigned char e; |
| 2467 | + if( p->spec.eTitleAlign==QRF_Auto ){ |
| 2468 | + e = QRF_ALIGN_Center; |
| 2469 | + }else{ |
| 2470 | + e = p->spec.eTitleAlign; |
| 2471 | + } |
| 2472 | + for(i=0; i<nColumn; i++) data.a[i].e = e; |
| 2473 | + } |
| 2474 | + |
| 2475 | + for(i=0; i<nColumn; i++){ |
| 2476 | + int w = 0; |
| 2477 | + if( i<p->spec.nWidth ){ |
| 2478 | + w = p->spec.aWidth[i]; |
| 2479 | + if( w==(-32768) ){ |
| 2480 | + w = 0; |
| 2481 | + if( p->spec.nAlign>i && (p->spec.aAlign[i] & QRF_ALIGN_HMASK)==0 ){ |
| 2482 | + data.a[i].e |= QRF_ALIGN_Right; |
| 2483 | + } |
| 2484 | + }else if( w<0 ){ |
| 2485 | + w = -w; |
| 2486 | + if( p->spec.nAlign>i && (p->spec.aAlign[i] & QRF_ALIGN_HMASK)==0 ){ |
| 2487 | + data.a[i].e |= QRF_ALIGN_Right; |
| 2488 | + } |
| 2489 | + } |
| 2490 | + if( w ) data.a[i].fx = 1; |
| 2491 | + } |
| 2492 | + if( w==0 ){ |
| 2493 | + w = data.a[i].mxW; |
| 2494 | + if( p->spec.nWrap>0 && w>p->spec.nWrap ){ |
| 2495 | + w = p->spec.nWrap; |
| 2496 | + data.bMultiRow = 1; |
| 2497 | + } |
| 2498 | + }else if( (data.bMultiRow==0 || w==1) && data.a[i].mxW>w ){ |
| 2499 | + data.bMultiRow = 1; |
| 2500 | + if( w==1 ){ |
| 2501 | + /* If aiWth[j] is 2 or more, then there might be a double-wide |
| 2502 | + ** character somewhere. So make the column width at least 2. */ |
| 2503 | + w = 2; |
| 2504 | + } |
| 2505 | + } |
| 2506 | + data.a[i].w = w; |
| 2507 | + } |
| 2508 | + |
| 2509 | + /* Adjust the column widths due to screen width restrictions */ |
| 2510 | + qrfRestrictScreenWidth(&data, p); |
| 2511 | + |
| 2512 | + /* Draw the line across the top of the table. Also initialize |
| 2513 | + ** the row boundary and column separator texts. */ |
| 2514 | + switch( p->spec.eStyle ){ |
| 2515 | + case QRF_STYLE_Box: |
| 2516 | + if( data.nMargin ){ |
| 2517 | + rowStart = BOX_13 " "; |
| 2518 | + colSep = " " BOX_13 " "; |
| 2519 | + rowSep = " " BOX_13 "\n"; |
| 2520 | + }else{ |
| 2521 | + rowStart = BOX_13; |
| 2522 | + colSep = BOX_13; |
| 2523 | + rowSep = BOX_13 "\n"; |
| 2524 | + } |
| 2525 | + qrfBoxSeparator(p->pOut, &data, BOX_23, BOX_234, BOX_34); |
| 2526 | + break; |
| 2527 | + case QRF_STYLE_Table: |
| 2528 | + if( data.nMargin ){ |
| 2529 | + rowStart = "| "; |
| 2530 | + colSep = " | "; |
| 2531 | + rowSep = " |\n"; |
| 2532 | + }else{ |
| 2533 | + rowStart = "|"; |
| 2534 | + colSep = "|"; |
| 2535 | + rowSep = "|\n"; |
| 2536 | + } |
| 2537 | + qrfRowSeparator(p->pOut, &data, '+'); |
| 2538 | + break; |
| 2539 | + case QRF_STYLE_Column: |
| 2540 | + rowStart = ""; |
| 2541 | + colSep = data.nMargin ? " " : " "; |
| 2542 | + rowSep = "\n"; |
| 2543 | + break; |
| 2544 | + default: /*case QRF_STYLE_Markdown:*/ |
| 2545 | + if( data.nMargin ){ |
| 2546 | + rowStart = "| "; |
| 2547 | + colSep = " | "; |
| 2548 | + rowSep = " |\n"; |
| 2549 | + }else{ |
| 2550 | + rowStart = "|"; |
| 2551 | + colSep = "|"; |
| 2552 | + rowSep = "|\n"; |
| 2553 | + } |
| 2554 | + break; |
| 2555 | + } |
| 2556 | + szRowStart = (int)strlen(rowStart); |
| 2557 | + szRowSep = (int)strlen(rowSep); |
| 2558 | + szColSep = (int)strlen(colSep); |
| 2559 | + |
| 2560 | + bWW = (p->spec.bWordWrap==QRF_Yes && data.bMultiRow); |
| 2561 | + for(i=0; i<data.n; i+=nColumn){ |
| 2562 | + int bMore; |
| 2563 | + int nRow = 0; |
| 2564 | + |
| 2565 | + /* Draw a single row of the table. This might be the title line |
| 2566 | + ** (if there is a title line) or a row in the body of the table. |
| 2567 | + ** The column number will be j. The row number is i/nColumn. |
| 2568 | + */ |
| 2569 | + for(j=0; j<nColumn; j++){ data.a[j].z = data.az[i+j]; } |
| 2570 | + do{ |
| 2571 | + sqlite3_str_append(p->pOut, rowStart, szRowStart); |
| 2572 | + bMore = 0; |
| 2573 | + for(j=0; j<nColumn; j++){ |
| 2574 | + int nThis = 0; |
| 2575 | + int nWide = 0; |
| 2576 | + int iNext = 0; |
| 2577 | + int nWS; |
| 2578 | + qrfWrapLine(data.a[j].z, data.a[j].w, bWW, &nThis, &nWide, &iNext); |
| 2579 | + nWS = data.a[j].w - nWide; |
| 2580 | + qrfPrintAligned(p->pOut, data.a[j].z, nThis, nWS, data.a[j].e); |
| 2581 | + data.a[j].z += iNext; |
| 2582 | + if( data.a[j].z[0]!=0 ) bMore = 1; |
| 2583 | + if( j<nColumn-1 ){ |
| 2584 | + sqlite3_str_append(p->pOut, colSep, szColSep); |
| 2585 | + }else{ |
| 2586 | + sqlite3_str_append(p->pOut, rowSep, szRowSep); |
| 2587 | + } |
| 2588 | + } |
| 2589 | + }while( bMore && ++nRow < p->mxHeight ); |
| 2590 | + if( bMore ){ |
| 2591 | + /* This row was terminated by nLineLimit. Show ellipsis. */ |
| 2592 | + sqlite3_str_append(p->pOut, rowStart, szRowStart); |
| 2593 | + for(j=0; j<nColumn; j++){ |
| 2594 | + if( data.a[j].z[0]==0 ){ |
| 2595 | + sqlite3_str_appendchar(p->pOut, data.a[j].w, ' '); |
| 2596 | + }else{ |
| 2597 | + int nE = 3; |
| 2598 | + if( nE>data.a[j].w ) nE = data.a[j].w; |
| 2599 | + qrfPrintAligned(p->pOut, "...", nE, data.a[j].w-nE, data.a[j].e); |
| 2600 | + } |
| 2601 | + if( j<nColumn-1 ){ |
| 2602 | + sqlite3_str_append(p->pOut, colSep, szColSep); |
| 2603 | + }else{ |
| 2604 | + sqlite3_str_append(p->pOut, rowSep, szRowSep); |
| 2605 | + } |
| 2606 | + } |
| 2607 | + } |
| 2608 | + |
| 2609 | + /* Draw either (1) the separator between the title line and the body |
| 2610 | + ** of the table, or (2) separators between individual rows of the table |
| 2611 | + ** body. isTitleDataSeparator will be true if we are doing (1). |
| 2612 | + */ |
| 2613 | + if( (i==0 || data.bMultiRow) && i+nColumn<data.n ){ |
| 2614 | + int isTitleDataSeparator = (i==0 && p->spec.bTitles==QRF_Yes); |
| 2615 | + if( isTitleDataSeparator ){ |
| 2616 | + qrfLoadAlignment(&data, p); |
| 2617 | + } |
| 2618 | + switch( p->spec.eStyle ){ |
| 2619 | + case QRF_STYLE_Table: { |
| 2620 | + if( isTitleDataSeparator || data.bMultiRow ){ |
| 2621 | + qrfRowSeparator(p->pOut, &data, '+'); |
| 2622 | + } |
| 2623 | + break; |
| 2624 | + } |
| 2625 | + case QRF_STYLE_Box: { |
| 2626 | + if( isTitleDataSeparator || data.bMultiRow ){ |
| 2627 | + qrfBoxSeparator(p->pOut, &data, BOX_123, BOX_1234, BOX_134); |
| 2628 | + } |
| 2629 | + break; |
| 2630 | + } |
| 2631 | + case QRF_STYLE_Markdown: { |
| 2632 | + if( isTitleDataSeparator ){ |
| 2633 | + qrfRowSeparator(p->pOut, &data, '|'); |
| 2634 | + } |
| 2635 | + break; |
| 2636 | + } |
| 2637 | + case QRF_STYLE_Column: { |
| 2638 | + if( isTitleDataSeparator ){ |
| 2639 | + for(j=0; j<nColumn; j++){ |
| 2640 | + sqlite3_str_appendchar(p->pOut, data.a[j].w, '-'); |
| 2641 | + if( j<nColumn-1 ){ |
| 2642 | + sqlite3_str_append(p->pOut, colSep, szColSep); |
| 2643 | + }else{ |
| 2644 | + sqlite3_str_append(p->pOut, rowSep, szRowSep); |
| 2645 | + } |
| 2646 | + } |
| 2647 | + }else if( data.bMultiRow ){ |
| 2648 | + sqlite3_str_append(p->pOut, "\n", 1); |
| 2649 | + } |
| 2650 | + break; |
| 2651 | + } |
| 2652 | + } |
| 2653 | + } |
| 2654 | + } |
| 2655 | + |
| 2656 | + /* Draw the line across the bottom of the table */ |
| 2657 | + switch( p->spec.eStyle ){ |
| 2658 | + case QRF_STYLE_Box: |
| 2659 | + qrfBoxSeparator(p->pOut, &data, BOX_12, BOX_124, BOX_14); |
| 2660 | + break; |
| 2661 | + case QRF_STYLE_Table: |
| 2662 | + qrfRowSeparator(p->pOut, &data, '+'); |
| 2663 | + break; |
| 2664 | + } |
| 2665 | + qrfWrite(p); |
| 2666 | + |
| 2667 | + qrfColDataFree(&data); |
| 2668 | + return; |
| 2669 | +} |
| 2670 | + |
| 2671 | +/* |
| 2672 | +** Parameter azArray points to a zero-terminated array of strings. zStr |
| 2673 | +** points to a single nul-terminated string. Return non-zero if zStr |
| 2674 | +** is equal, according to strcmp(), to any of the strings in the array. |
| 2675 | +** Otherwise, return zero. |
| 2676 | +*/ |
| 2677 | +static int qrfStringInArray(const char *zStr, const char **azArray){ |
| 2678 | + int i; |
| 2679 | + if( zStr==0 ) return 0; |
| 2680 | + for(i=0; azArray[i]; i++){ |
| 2681 | + if( 0==strcmp(zStr, azArray[i]) ) return 1; |
| 2682 | + } |
| 2683 | + return 0; |
| 2684 | +} |
| 2685 | + |
| 2686 | +/* |
| 2687 | +** Print out an EXPLAIN with indentation. This is a two-pass algorithm. |
| 2688 | +** |
| 2689 | +** On the first pass, we compute aiIndent[iOp] which is the amount of |
| 2690 | +** indentation to apply to the iOp-th opcode. The output actually occurs |
| 2691 | +** on the second pass. |
| 2692 | +** |
| 2693 | +** The indenting rules are: |
| 2694 | +** |
| 2695 | +** * For each "Next", "Prev", "VNext" or "VPrev" instruction, indent |
| 2696 | +** all opcodes that occur between the p2 jump destination and the opcode |
| 2697 | +** itself by 2 spaces. |
| 2698 | +** |
| 2699 | +** * Do the previous for "Return" instructions for when P2 is positive. |
| 2700 | +** See tag-20220407a in wherecode.c and vdbe.c. |
| 2701 | +** |
| 2702 | +** * For each "Goto", if the jump destination is earlier in the program |
| 2703 | +** and ends on one of: |
| 2704 | +** Yield SeekGt SeekLt RowSetRead Rewind |
| 2705 | +** or if the P1 parameter is one instead of zero, |
| 2706 | +** then indent all opcodes between the earlier instruction |
| 2707 | +** and "Goto" by 2 spaces. |
| 2708 | +*/ |
| 2709 | +static void qrfExplain(Qrf *p){ |
| 2710 | + int *abYield = 0; /* abYield[iOp] is rue if opcode iOp is an OP_Yield */ |
| 2711 | + int *aiIndent = 0; /* Indent the iOp-th opcode by aiIndent[iOp] */ |
| 2712 | + i64 nAlloc = 0; /* Allocated size of aiIndent[], abYield */ |
| 2713 | + int nIndent = 0; /* Number of entries in aiIndent[] */ |
| 2714 | + int iOp; /* Opcode number */ |
| 2715 | + int i; /* Column loop counter */ |
| 2716 | + |
| 2717 | + const char *azNext[] = { "Next", "Prev", "VPrev", "VNext", "SorterNext", |
| 2718 | + "Return", 0 }; |
| 2719 | + const char *azYield[] = { "Yield", "SeekLT", "SeekGT", "RowSetRead", |
| 2720 | + "Rewind", 0 }; |
| 2721 | + const char *azGoto[] = { "Goto", 0 }; |
| 2722 | + |
| 2723 | + /* The caller guarantees that the leftmost 4 columns of the statement |
| 2724 | + ** passed to this function are equivalent to the leftmost 4 columns |
| 2725 | + ** of EXPLAIN statement output. In practice the statement may be |
| 2726 | + ** an EXPLAIN, or it may be a query on the bytecode() virtual table. */ |
| 2727 | + assert( sqlite3_column_count(p->pStmt)>=4 ); |
| 2728 | + assert( 0==sqlite3_stricmp( sqlite3_column_name(p->pStmt, 0), "addr" ) ); |
| 2729 | + assert( 0==sqlite3_stricmp( sqlite3_column_name(p->pStmt, 1), "opcode" ) ); |
| 2730 | + assert( 0==sqlite3_stricmp( sqlite3_column_name(p->pStmt, 2), "p1" ) ); |
| 2731 | + assert( 0==sqlite3_stricmp( sqlite3_column_name(p->pStmt, 3), "p2" ) ); |
| 2732 | + |
| 2733 | + for(iOp=0; SQLITE_ROW==sqlite3_step(p->pStmt); iOp++){ |
| 2734 | + int iAddr = sqlite3_column_int(p->pStmt, 0); |
| 2735 | + const char *zOp = (const char*)sqlite3_column_text(p->pStmt, 1); |
| 2736 | + int p1 = sqlite3_column_int(p->pStmt, 2); |
| 2737 | + int p2 = sqlite3_column_int(p->pStmt, 3); |
| 2738 | + |
| 2739 | + /* Assuming that p2 is an instruction address, set variable p2op to the |
| 2740 | + ** index of that instruction in the aiIndent[] array. p2 and p2op may be |
| 2741 | + ** different if the current instruction is part of a sub-program generated |
| 2742 | + ** by an SQL trigger or foreign key. */ |
| 2743 | + int p2op = (p2 + (iOp-iAddr)); |
| 2744 | + |
| 2745 | + /* Grow the aiIndent array as required */ |
| 2746 | + if( iOp>=nAlloc ){ |
| 2747 | + nAlloc += 100; |
| 2748 | + aiIndent = (int*)sqlite3_realloc64(aiIndent, nAlloc*sizeof(int)); |
| 2749 | + abYield = (int*)sqlite3_realloc64(abYield, nAlloc*sizeof(int)); |
| 2750 | + if( aiIndent==0 || abYield==0 ){ |
| 2751 | + qrfOom(p); |
| 2752 | + sqlite3_free(aiIndent); |
| 2753 | + sqlite3_free(abYield); |
| 2754 | + return; |
| 2755 | + } |
| 2756 | + } |
| 2757 | + |
| 2758 | + abYield[iOp] = qrfStringInArray(zOp, azYield); |
| 2759 | + aiIndent[iOp] = 0; |
| 2760 | + nIndent = iOp+1; |
| 2761 | + if( qrfStringInArray(zOp, azNext) && p2op>0 ){ |
| 2762 | + for(i=p2op; i<iOp; i++) aiIndent[i] += 2; |
| 2763 | + } |
| 2764 | + if( qrfStringInArray(zOp, azGoto) && p2op<iOp && (abYield[p2op] || p1) ){ |
| 2765 | + for(i=p2op; i<iOp; i++) aiIndent[i] += 2; |
| 2766 | + } |
| 2767 | + } |
| 2768 | + sqlite3_free(abYield); |
| 2769 | + |
| 2770 | + /* Second pass. Actually generate output */ |
| 2771 | + sqlite3_reset(p->pStmt); |
| 2772 | + if( p->iErr==SQLITE_OK ){ |
| 2773 | + static const int aExplainWidth[] = {4, 13, 4, 4, 4, 13, 2, 13}; |
| 2774 | + static const int aExplainMap[] = {0, 1, 2, 3, 4, 5, 6, 7 }; |
| 2775 | + static const int aScanExpWidth[] = {4,15, 6, 13, 4, 4, 4, 13, 2, 13}; |
| 2776 | + static const int aScanExpMap[] = {0, 9, 8, 1, 2, 3, 4, 5, 6, 7 }; |
| 2777 | + const int *aWidth = aExplainWidth; |
| 2778 | + const int *aMap = aExplainMap; |
| 2779 | + int nWidth = sizeof(aExplainWidth)/sizeof(int); |
| 2780 | + int iIndent = 1; |
| 2781 | + int nArg = p->nCol; |
| 2782 | + if( p->spec.eStyle==QRF_STYLE_StatsVm ){ |
| 2783 | + aWidth = aScanExpWidth; |
| 2784 | + aMap = aScanExpMap; |
| 2785 | + nWidth = sizeof(aScanExpWidth)/sizeof(int); |
| 2786 | + iIndent = 3; |
| 2787 | + } |
| 2788 | + if( nArg>nWidth ) nArg = nWidth; |
| 2789 | + |
| 2790 | + for(iOp=0; sqlite3_step(p->pStmt)==SQLITE_ROW; iOp++){ |
| 2791 | + /* If this is the first row seen, print out the headers */ |
| 2792 | + if( iOp==0 ){ |
| 2793 | + for(i=0; i<nArg; i++){ |
| 2794 | + const char *zCol = sqlite3_column_name(p->pStmt, aMap[i]); |
| 2795 | + qrfWidthPrint(p,p->pOut, aWidth[i], zCol); |
| 2796 | + if( i==nArg-1 ){ |
| 2797 | + sqlite3_str_append(p->pOut, "\n", 1); |
| 2798 | + }else{ |
| 2799 | + sqlite3_str_append(p->pOut, " ", 2); |
| 2800 | + } |
| 2801 | + } |
| 2802 | + for(i=0; i<nArg; i++){ |
| 2803 | + sqlite3_str_appendf(p->pOut, "%.*c", aWidth[i], '-'); |
| 2804 | + if( i==nArg-1 ){ |
| 2805 | + sqlite3_str_append(p->pOut, "\n", 1); |
| 2806 | + }else{ |
| 2807 | + sqlite3_str_append(p->pOut, " ", 2); |
| 2808 | + } |
| 2809 | + } |
| 2810 | + } |
| 2811 | + |
| 2812 | + for(i=0; i<nArg; i++){ |
| 2813 | + const char *zSep = " "; |
| 2814 | + int w = aWidth[i]; |
| 2815 | + const char *zVal = (const char*)sqlite3_column_text(p->pStmt, aMap[i]); |
| 2816 | + int len; |
| 2817 | + if( i==nArg-1 ) w = 0; |
| 2818 | + if( zVal==0 ) zVal = ""; |
| 2819 | + len = qrfDisplayLength(zVal); |
| 2820 | + if( len>w ){ |
| 2821 | + w = len; |
| 2822 | + zSep = " "; |
| 2823 | + } |
| 2824 | + if( i==iIndent && aiIndent && iOp<nIndent ){ |
| 2825 | + sqlite3_str_appendchar(p->pOut, aiIndent[iOp], ' '); |
| 2826 | + } |
| 2827 | + qrfWidthPrint(p, p->pOut, w, zVal); |
| 2828 | + if( i==nArg-1 ){ |
| 2829 | + sqlite3_str_append(p->pOut, "\n", 1); |
| 2830 | + }else{ |
| 2831 | + sqlite3_str_appendall(p->pOut, zSep); |
| 2832 | + } |
| 2833 | + } |
| 2834 | + p->nRow++; |
| 2835 | + } |
| 2836 | + qrfWrite(p); |
| 2837 | + } |
| 2838 | + sqlite3_free(aiIndent); |
| 2839 | +} |
| 2840 | + |
| 2841 | +/* |
| 2842 | +** Do a "scanstatus vm" style EXPLAIN listing on p->pStmt. |
| 2843 | +** |
| 2844 | +** p->pStmt is probably not an EXPLAIN query. Instead, construct a |
| 2845 | +** new query that is a bytecode() rendering of p->pStmt with extra |
| 2846 | +** columns for the "scanstatus vm" outputs, and run the results of |
| 2847 | +** that new query through the normal EXPLAIN formatting. |
| 2848 | +*/ |
| 2849 | +static void qrfScanStatusVm(Qrf *p){ |
| 2850 | + sqlite3_stmt *pOrigStmt = p->pStmt; |
| 2851 | + sqlite3_stmt *pExplain; |
| 2852 | + int rc; |
| 2853 | + static const char *zSql = |
| 2854 | + " SELECT addr, opcode, p1, p2, p3, p4, p5, comment, nexec," |
| 2855 | + " format('% 6s (%.2f%%)'," |
| 2856 | + " CASE WHEN ncycle<100_000 THEN ncycle || ' '" |
| 2857 | + " WHEN ncycle<100_000_000 THEN (ncycle/1_000) || 'K'" |
| 2858 | + " WHEN ncycle<100_000_000_000 THEN (ncycle/1_000_000) || 'M'" |
| 2859 | + " ELSE (ncycle/1000_000_000) || 'G' END," |
| 2860 | + " ncycle*100.0/(sum(ncycle) OVER ())" |
| 2861 | + " ) AS cycles" |
| 2862 | + " FROM bytecode(?1)"; |
| 2863 | + rc = sqlite3_prepare_v2(p->db, zSql, -1, &pExplain, 0); |
| 2864 | + if( rc ){ |
| 2865 | + qrfError(p, rc, "%s", sqlite3_errmsg(p->db)); |
| 2866 | + sqlite3_finalize(pExplain); |
| 2867 | + return; |
| 2868 | + } |
| 2869 | + sqlite3_bind_pointer(pExplain, 1, pOrigStmt, "stmt-pointer", 0); |
| 2870 | + p->pStmt = pExplain; |
| 2871 | + p->nCol = 10; |
| 2872 | + qrfExplain(p); |
| 2873 | + sqlite3_finalize(pExplain); |
| 2874 | + p->pStmt = pOrigStmt; |
| 2875 | +} |
| 2876 | + |
| 2877 | +/* |
| 2878 | +** Attempt to determine if identifier zName needs to be quoted, either |
| 2879 | +** because it contains non-alphanumeric characters, or because it is an |
| 2880 | +** SQLite keyword. Be conservative in this estimate: When in doubt assume |
| 2881 | +** that quoting is required. |
| 2882 | +** |
| 2883 | +** Return 1 if quoting is required. Return 0 if no quoting is required. |
| 2884 | +*/ |
| 2885 | + |
| 2886 | +static int qrf_need_quote(const char *zName){ |
| 2887 | + int i; |
| 2888 | + const unsigned char *z = (const unsigned char*)zName; |
| 2889 | + if( z==0 ) return 1; |
| 2890 | + if( !qrfAlpha(z[0]) ) return 1; |
| 2891 | + for(i=0; z[i]; i++){ |
| 2892 | + if( !qrfAlnum(z[i]) ) return 1; |
| 2893 | + } |
| 2894 | + return sqlite3_keyword_check(zName, i)!=0; |
| 2895 | +} |
| 2896 | + |
| 2897 | +/* |
| 2898 | +** Helper function for QRF_STYLE_Json and QRF_STYLE_JObject. |
| 2899 | +** The initial "{" for a JSON object that will contain row content |
| 2900 | +** has been output. Now output all the content. |
| 2901 | +*/ |
| 2902 | +static void qrfOneJsonRow(Qrf *p){ |
| 2903 | + int i, nItem; |
| 2904 | + for(nItem=i=0; i<p->nCol; i++){ |
| 2905 | + const char *zCName; |
| 2906 | + zCName = sqlite3_column_name(p->pStmt, i); |
| 2907 | + if( nItem>0 ) sqlite3_str_append(p->pOut, ",", 1); |
| 2908 | + nItem++; |
| 2909 | + qrfEncodeText(p, p->pOut, zCName); |
| 2910 | + sqlite3_str_append(p->pOut, ":", 1); |
| 2911 | + qrfRenderValue(p, p->pOut, i); |
| 2912 | + } |
| 2913 | + qrfWrite(p); |
| 2914 | +} |
| 2915 | + |
| 2916 | +/* |
| 2917 | +** Render a single row of output for non-columnar styles - any |
| 2918 | +** style that lets us render row by row as the content is received |
| 2919 | +** from the query. |
| 2920 | +*/ |
| 2921 | +static void qrfOneSimpleRow(Qrf *p){ |
| 2922 | + int i; |
| 2923 | + switch( p->spec.eStyle ){ |
| 2924 | + case QRF_STYLE_Off: |
| 2925 | + case QRF_STYLE_Count: { |
| 2926 | + /* No-op */ |
| 2927 | + break; |
| 2928 | + } |
| 2929 | + case QRF_STYLE_Json: { |
| 2930 | + if( p->nRow==0 ){ |
| 2931 | + sqlite3_str_append(p->pOut, "[{", 2); |
| 2932 | + }else{ |
| 2933 | + sqlite3_str_append(p->pOut, "},\n{", 4); |
| 2934 | + } |
| 2935 | + qrfOneJsonRow(p); |
| 2936 | + break; |
| 2937 | + } |
| 2938 | + case QRF_STYLE_JObject: { |
| 2939 | + if( p->nRow==0 ){ |
| 2940 | + sqlite3_str_append(p->pOut, "{", 1); |
| 2941 | + }else{ |
| 2942 | + sqlite3_str_append(p->pOut, "}\n{", 3); |
| 2943 | + } |
| 2944 | + qrfOneJsonRow(p); |
| 2945 | + break; |
| 2946 | + } |
| 2947 | + case QRF_STYLE_Html: { |
| 2948 | + if( p->nRow==0 && p->spec.bTitles==QRF_Yes ){ |
| 2949 | + sqlite3_str_append(p->pOut, "<TR>", 4); |
| 2950 | + for(i=0; i<p->nCol; i++){ |
| 2951 | + const char *zCName = sqlite3_column_name(p->pStmt, i); |
| 2952 | + sqlite3_str_append(p->pOut, "\n<TH>", 5); |
| 2953 | + qrfEncodeText(p, p->pOut, zCName); |
| 2954 | + } |
| 2955 | + sqlite3_str_append(p->pOut, "\n</TR>\n", 7); |
| 2956 | + } |
| 2957 | + sqlite3_str_append(p->pOut, "<TR>", 4); |
| 2958 | + for(i=0; i<p->nCol; i++){ |
| 2959 | + sqlite3_str_append(p->pOut, "\n<TD>", 5); |
| 2960 | + qrfRenderValue(p, p->pOut, i); |
| 2961 | + } |
| 2962 | + sqlite3_str_append(p->pOut, "\n</TR>\n", 7); |
| 2963 | + qrfWrite(p); |
| 2964 | + break; |
| 2965 | + } |
| 2966 | + case QRF_STYLE_Insert: { |
| 2967 | + if( qrf_need_quote(p->spec.zTableName) ){ |
| 2968 | + sqlite3_str_appendf(p->pOut,"INSERT INTO \"%w\"",p->spec.zTableName); |
| 2969 | + }else{ |
| 2970 | + sqlite3_str_appendf(p->pOut,"INSERT INTO %s",p->spec.zTableName); |
| 2971 | + } |
| 2972 | + if( p->spec.bTitles==QRF_Yes ){ |
| 2973 | + for(i=0; i<p->nCol; i++){ |
| 2974 | + const char *zCName = sqlite3_column_name(p->pStmt, i); |
| 2975 | + if( qrf_need_quote(zCName) ){ |
| 2976 | + sqlite3_str_appendf(p->pOut, "%c\"%w\"", |
| 2977 | + i==0 ? '(' : ',', zCName); |
| 2978 | + }else{ |
| 2979 | + sqlite3_str_appendf(p->pOut, "%c%s", |
| 2980 | + i==0 ? '(' : ',', zCName); |
| 2981 | + } |
| 2982 | + } |
| 2983 | + sqlite3_str_append(p->pOut, ")", 1); |
| 2984 | + } |
| 2985 | + sqlite3_str_append(p->pOut," VALUES(", 8); |
| 2986 | + for(i=0; i<p->nCol; i++){ |
| 2987 | + if( i>0 ) sqlite3_str_append(p->pOut, ",", 1); |
| 2988 | + qrfRenderValue(p, p->pOut, i); |
| 2989 | + } |
| 2990 | + sqlite3_str_append(p->pOut, ");\n", 3); |
| 2991 | + qrfWrite(p); |
| 2992 | + break; |
| 2993 | + } |
| 2994 | + case QRF_STYLE_Line: { |
| 2995 | + sqlite3_str *pVal; |
| 2996 | + int mxW; |
| 2997 | + int bWW; |
| 2998 | + if( p->u.sLine.azCol==0 ){ |
| 2999 | + p->u.sLine.azCol = sqlite3_malloc64( p->nCol*sizeof(char*) ); |
| 3000 | + if( p->u.sLine.azCol==0 ){ |
| 3001 | + qrfOom(p); |
| 3002 | + break; |
| 3003 | + } |
| 3004 | + p->u.sLine.mxColWth = 0; |
| 3005 | + for(i=0; i<p->nCol; i++){ |
| 3006 | + int sz; |
| 3007 | + p->u.sLine.azCol[i] = sqlite3_column_name(p->pStmt, i); |
| 3008 | + if( p->u.sLine.azCol[i]==0 ) p->u.sLine.azCol[i] = "unknown"; |
| 3009 | + sz = qrfDisplayLength(p->u.sLine.azCol[i]); |
| 3010 | + if( sz > p->u.sLine.mxColWth ) p->u.sLine.mxColWth = sz; |
| 3011 | + } |
| 3012 | + } |
| 3013 | + if( p->nRow ) sqlite3_str_append(p->pOut, "\n", 1); |
| 3014 | + pVal = sqlite3_str_new(p->db); |
| 3015 | + mxW = p->mxWidth - (3 + p->u.sLine.mxColWth); |
| 3016 | + bWW = p->spec.bWordWrap==QRF_Yes; |
| 3017 | + for(i=0; i<p->nCol; i++){ |
| 3018 | + const char *zVal; |
| 3019 | + int cnt = 0; |
| 3020 | + qrfWidthPrint(p, p->pOut, -p->u.sLine.mxColWth, p->u.sLine.azCol[i]); |
| 3021 | + sqlite3_str_append(p->pOut, " = ", 3); |
| 3022 | + qrfRenderValue(p, pVal, i); |
| 3023 | + zVal = sqlite3_str_value(pVal); |
| 3024 | + if( zVal==0 ) zVal = ""; |
| 3025 | + do{ |
| 3026 | + int nThis, nWide, iNext; |
| 3027 | + qrfWrapLine(zVal, mxW, bWW, &nThis, &nWide, &iNext); |
| 3028 | + if( cnt ) sqlite3_str_appendchar(p->pOut,p->u.sLine.mxColWth+3,' '); |
| 3029 | + cnt++; |
| 3030 | + if( cnt>p->mxHeight ){ |
| 3031 | + zVal = "..."; |
| 3032 | + nThis = iNext = 3; |
| 3033 | + } |
| 3034 | + sqlite3_str_append(p->pOut, zVal, nThis); |
| 3035 | + sqlite3_str_append(p->pOut, "\n", 1); |
| 3036 | + zVal += iNext; |
| 3037 | + }while( zVal[0] ); |
| 3038 | + sqlite3_str_reset(pVal); |
| 3039 | + } |
| 3040 | + sqlite3_free(sqlite3_str_finish(pVal)); |
| 3041 | + qrfWrite(p); |
| 3042 | + break; |
| 3043 | + } |
| 3044 | + case QRF_STYLE_Eqp: { |
| 3045 | + const char *zEqpLine = (const char*)sqlite3_column_text(p->pStmt,3); |
| 3046 | + int iEqpId = sqlite3_column_int(p->pStmt, 0); |
| 3047 | + int iParentId = sqlite3_column_int(p->pStmt, 1); |
| 3048 | + if( zEqpLine==0 ) zEqpLine = ""; |
| 3049 | + if( zEqpLine[0]=='-' ) qrfEqpRender(p, 0); |
| 3050 | + qrfEqpAppend(p, iEqpId, iParentId, zEqpLine); |
| 3051 | + break; |
| 3052 | + } |
| 3053 | + default: { /* QRF_STYLE_List */ |
| 3054 | + if( p->nRow==0 && p->spec.bTitles==QRF_Yes ){ |
| 3055 | + int saved_eText = p->spec.eText; |
| 3056 | + p->spec.eText = p->spec.eTitle; |
| 3057 | + for(i=0; i<p->nCol; i++){ |
| 3058 | + const char *zCName = sqlite3_column_name(p->pStmt, i); |
| 3059 | + if( i>0 ) sqlite3_str_appendall(p->pOut, p->spec.zColumnSep); |
| 3060 | + qrfEncodeText(p, p->pOut, zCName); |
| 3061 | + } |
| 3062 | + sqlite3_str_appendall(p->pOut, p->spec.zRowSep); |
| 3063 | + qrfWrite(p); |
| 3064 | + p->spec.eText = saved_eText; |
| 3065 | + } |
| 3066 | + for(i=0; i<p->nCol; i++){ |
| 3067 | + if( i>0 ) sqlite3_str_appendall(p->pOut, p->spec.zColumnSep); |
| 3068 | + qrfRenderValue(p, p->pOut, i); |
| 3069 | + } |
| 3070 | + sqlite3_str_appendall(p->pOut, p->spec.zRowSep); |
| 3071 | + qrfWrite(p); |
| 3072 | + break; |
| 3073 | + } |
| 3074 | + } |
| 3075 | + p->nRow++; |
| 3076 | +} |
| 3077 | + |
| 3078 | +/* |
| 3079 | +** Initialize the internal Qrf object. |
| 3080 | +*/ |
| 3081 | +static void qrfInitialize( |
| 3082 | + Qrf *p, /* State object to be initialized */ |
| 3083 | + sqlite3_stmt *pStmt, /* Query whose output to be formatted */ |
| 3084 | + const sqlite3_qrf_spec *pSpec, /* Format specification */ |
| 3085 | + char **pzErr /* Write errors here */ |
| 3086 | +){ |
| 3087 | + size_t sz; /* Size of pSpec[], based on pSpec->iVersion */ |
| 3088 | + memset(p, 0, sizeof(*p)); |
| 3089 | + p->pzErr = pzErr; |
| 3090 | + if( pSpec->iVersion!=1 ){ |
| 3091 | + qrfError(p, SQLITE_ERROR, |
| 3092 | + "unusable sqlite3_qrf_spec.iVersion (%d)", |
| 3093 | + pSpec->iVersion); |
| 3094 | + return; |
| 3095 | + } |
| 3096 | + p->pStmt = pStmt; |
| 3097 | + p->db = sqlite3_db_handle(pStmt); |
| 3098 | + p->pOut = sqlite3_str_new(p->db); |
| 3099 | + if( p->pOut==0 ){ |
| 3100 | + qrfOom(p); |
| 3101 | + return; |
| 3102 | + } |
| 3103 | + p->iErr = 0; |
| 3104 | + p->nCol = sqlite3_column_count(p->pStmt); |
| 3105 | + p->nRow = 0; |
| 3106 | + sz = sizeof(sqlite3_qrf_spec); |
| 3107 | + memcpy(&p->spec, pSpec, sz); |
| 3108 | + if( p->spec.zNull==0 ) p->spec.zNull = ""; |
| 3109 | + p->mxWidth = p->spec.nScreenWidth; |
| 3110 | + if( p->mxWidth<=0 ) p->mxWidth = QRF_MAX_WIDTH; |
| 3111 | + p->mxHeight = p->spec.nLineLimit; |
| 3112 | + if( p->mxHeight<=0 ) p->mxHeight = 2147483647; |
| 3113 | +qrf_reinit: |
| 3114 | + switch( p->spec.eStyle ){ |
| 3115 | + case QRF_Auto: { |
| 3116 | + switch( sqlite3_stmt_isexplain(pStmt) ){ |
| 3117 | + case 0: p->spec.eStyle = QRF_STYLE_Box; break; |
| 3118 | + case 1: p->spec.eStyle = QRF_STYLE_Explain; break; |
| 3119 | + default: p->spec.eStyle = QRF_STYLE_Eqp; break; |
| 3120 | + } |
| 3121 | + goto qrf_reinit; |
| 3122 | + } |
| 3123 | + case QRF_STYLE_List: { |
| 3124 | + if( p->spec.zColumnSep==0 ) p->spec.zColumnSep = "|"; |
| 3125 | + if( p->spec.zRowSep==0 ) p->spec.zRowSep = "\n"; |
| 3126 | + break; |
| 3127 | + } |
| 3128 | + case QRF_STYLE_JObject: |
| 3129 | + case QRF_STYLE_Json: { |
| 3130 | + p->spec.eText = QRF_TEXT_Json; |
| 3131 | + p->spec.eBlob = QRF_BLOB_Json; |
| 3132 | + p->spec.zNull = "null"; |
| 3133 | + break; |
| 3134 | + } |
| 3135 | + case QRF_STYLE_Html: { |
| 3136 | + p->spec.eText = QRF_TEXT_Html; |
| 3137 | + p->spec.zNull = "null"; |
| 3138 | + break; |
| 3139 | + } |
| 3140 | + case QRF_STYLE_Insert: { |
| 3141 | + p->spec.eText = QRF_TEXT_Sql; |
| 3142 | + p->spec.eBlob = QRF_BLOB_Sql; |
| 3143 | + p->spec.zNull = "NULL"; |
| 3144 | + if( p->spec.zTableName==0 || p->spec.zTableName[0]==0 ){ |
| 3145 | + p->spec.zTableName = "tab"; |
| 3146 | + } |
| 3147 | + break; |
| 3148 | + } |
| 3149 | + case QRF_STYLE_Csv: { |
| 3150 | + p->spec.eStyle = QRF_STYLE_List; |
| 3151 | + p->spec.eText = QRF_TEXT_Csv; |
| 3152 | + p->spec.eBlob = QRF_BLOB_Text; |
| 3153 | + p->spec.zColumnSep = ","; |
| 3154 | + p->spec.zRowSep = "\r\n"; |
| 3155 | + p->spec.zNull = ""; |
| 3156 | + break; |
| 3157 | + } |
| 3158 | + case QRF_STYLE_Quote: { |
| 3159 | + p->spec.eText = QRF_TEXT_Sql; |
| 3160 | + p->spec.eBlob = QRF_BLOB_Sql; |
| 3161 | + p->spec.zNull = "NULL"; |
| 3162 | + p->spec.zColumnSep = ","; |
| 3163 | + p->spec.zRowSep = "\n"; |
| 3164 | + break; |
| 3165 | + } |
| 3166 | + case QRF_STYLE_Eqp: { |
| 3167 | + int expMode = sqlite3_stmt_isexplain(p->pStmt); |
| 3168 | + if( expMode!=2 ){ |
| 3169 | + sqlite3_stmt_explain(p->pStmt, 2); |
| 3170 | + p->expMode = expMode+1; |
| 3171 | + } |
| 3172 | + break; |
| 3173 | + } |
| 3174 | + case QRF_STYLE_Explain: { |
| 3175 | + int expMode = sqlite3_stmt_isexplain(p->pStmt); |
| 3176 | + if( expMode!=1 ){ |
| 3177 | + sqlite3_stmt_explain(p->pStmt, 1); |
| 3178 | + p->expMode = expMode+1; |
| 3179 | + } |
| 3180 | + break; |
| 3181 | + } |
| 3182 | + } |
| 3183 | + if( p->spec.eEsc==QRF_Auto ){ |
| 3184 | + p->spec.eEsc = QRF_ESC_Ascii; |
| 3185 | + } |
| 3186 | + if( p->spec.eText==QRF_Auto ){ |
| 3187 | + p->spec.eText = QRF_TEXT_Plain; |
| 3188 | + } |
| 3189 | + if( p->spec.eTitle==QRF_Auto ){ |
| 3190 | + switch( p->spec.eStyle ){ |
| 3191 | + case QRF_STYLE_Box: |
| 3192 | + case QRF_STYLE_Column: |
| 3193 | + case QRF_STYLE_Table: |
| 3194 | + p->spec.eTitle = QRF_TEXT_Plain; |
| 3195 | + break; |
| 3196 | + default: |
| 3197 | + p->spec.eTitle = p->spec.eText; |
| 3198 | + break; |
| 3199 | + } |
| 3200 | + } |
| 3201 | + if( p->spec.eBlob==QRF_Auto ){ |
| 3202 | + switch( p->spec.eText ){ |
| 3203 | + case QRF_TEXT_Sql: p->spec.eBlob = QRF_BLOB_Sql; break; |
| 3204 | + case QRF_TEXT_Csv: p->spec.eBlob = QRF_BLOB_Tcl; break; |
| 3205 | + case QRF_TEXT_Tcl: p->spec.eBlob = QRF_BLOB_Tcl; break; |
| 3206 | + case QRF_TEXT_Json: p->spec.eBlob = QRF_BLOB_Json; break; |
| 3207 | + default: p->spec.eBlob = QRF_BLOB_Text; break; |
| 3208 | + } |
| 3209 | + } |
| 3210 | + if( p->spec.bTitles==QRF_Auto ){ |
| 3211 | + switch( p->spec.eStyle ){ |
| 3212 | + case QRF_STYLE_Box: |
| 3213 | + case QRF_STYLE_Csv: |
| 3214 | + case QRF_STYLE_Column: |
| 3215 | + case QRF_STYLE_Table: |
| 3216 | + case QRF_STYLE_Markdown: |
| 3217 | + p->spec.bTitles = QRF_Yes; |
| 3218 | + break; |
| 3219 | + default: |
| 3220 | + p->spec.bTitles = QRF_No; |
| 3221 | + break; |
| 3222 | + } |
| 3223 | + } |
| 3224 | + if( p->spec.bWordWrap==QRF_Auto ){ |
| 3225 | + p->spec.bWordWrap = QRF_Yes; |
| 3226 | + } |
| 3227 | + if( p->spec.bTextJsonb==QRF_Auto ){ |
| 3228 | + p->spec.bTextJsonb = QRF_No; |
| 3229 | + } |
| 3230 | + if( p->spec.zColumnSep==0 ) p->spec.zColumnSep = ","; |
| 3231 | + if( p->spec.zRowSep==0 ) p->spec.zRowSep = "\n"; |
| 3232 | +} |
| 3233 | + |
| 3234 | +/* |
| 3235 | +** Finish rendering the results |
| 3236 | +*/ |
| 3237 | +static void qrfFinalize(Qrf *p){ |
| 3238 | + switch( p->spec.eStyle ){ |
| 3239 | + case QRF_STYLE_Count: { |
| 3240 | + sqlite3_str_appendf(p->pOut, "%lld\n", p->nRow); |
| 3241 | + qrfWrite(p); |
| 3242 | + break; |
| 3243 | + } |
| 3244 | + case QRF_STYLE_Json: { |
| 3245 | + sqlite3_str_append(p->pOut, "}]\n", 3); |
| 3246 | + qrfWrite(p); |
| 3247 | + break; |
| 3248 | + } |
| 3249 | + case QRF_STYLE_JObject: { |
| 3250 | + sqlite3_str_append(p->pOut, "}\n", 2); |
| 3251 | + qrfWrite(p); |
| 3252 | + break; |
| 3253 | + } |
| 3254 | + case QRF_STYLE_Line: { |
| 3255 | + if( p->u.sLine.azCol ) sqlite3_free(p->u.sLine.azCol); |
| 3256 | + break; |
| 3257 | + } |
| 3258 | + case QRF_STYLE_Stats: |
| 3259 | + case QRF_STYLE_StatsEst: |
| 3260 | + case QRF_STYLE_Eqp: { |
| 3261 | + qrfEqpRender(p, 0); |
| 3262 | + qrfWrite(p); |
| 3263 | + break; |
| 3264 | + } |
| 3265 | + } |
| 3266 | + if( p->spec.pzOutput ){ |
| 3267 | + if( p->spec.pzOutput[0] ){ |
| 3268 | + sqlite3_int64 n, sz; |
| 3269 | + char *zCombined; |
| 3270 | + sz = strlen(p->spec.pzOutput[0]); |
| 3271 | + n = sqlite3_str_length(p->pOut); |
| 3272 | + zCombined = sqlite3_realloc(p->spec.pzOutput[0], sz+n+1); |
| 3273 | + if( zCombined==0 ){ |
| 3274 | + sqlite3_free(p->spec.pzOutput[0]); |
| 3275 | + p->spec.pzOutput[0] = 0; |
| 3276 | + qrfOom(p); |
| 3277 | + }else{ |
| 3278 | + p->spec.pzOutput[0] = zCombined; |
| 3279 | + memcpy(zCombined+sz, sqlite3_str_value(p->pOut), n+1); |
| 3280 | + } |
| 3281 | + sqlite3_free(sqlite3_str_finish(p->pOut)); |
| 3282 | + }else{ |
| 3283 | + p->spec.pzOutput[0] = sqlite3_str_finish(p->pOut); |
| 3284 | + } |
| 3285 | + }else if( p->pOut ){ |
| 3286 | + sqlite3_free(sqlite3_str_finish(p->pOut)); |
| 3287 | + } |
| 3288 | + if( p->expMode>0 ){ |
| 3289 | + sqlite3_stmt_explain(p->pStmt, p->expMode-1); |
| 3290 | + } |
| 3291 | + if( p->actualWidth ){ |
| 3292 | + sqlite3_free(p->actualWidth); |
| 3293 | + } |
| 3294 | + if( p->pJTrans ){ |
| 3295 | + sqlite3 *db = sqlite3_db_handle(p->pJTrans); |
| 3296 | + sqlite3_finalize(p->pJTrans); |
| 3297 | + sqlite3_close(db); |
| 3298 | + } |
| 3299 | +} |
| 3300 | + |
| 3301 | +/* |
| 3302 | +** Run the prepared statement pStmt and format the results according |
| 3303 | +** to the specification provided in pSpec. Return an error code. |
| 3304 | +** If pzErr is not NULL and if an error occurs, write an error message |
| 3305 | +** into *pzErr. |
| 3306 | +*/ |
| 3307 | +int sqlite3_format_query_result( |
| 3308 | + sqlite3_stmt *pStmt, /* Statement to evaluate */ |
| 3309 | + const sqlite3_qrf_spec *pSpec, /* Format specification */ |
| 3310 | + char **pzErr /* Write error message here */ |
| 3311 | +){ |
| 3312 | + Qrf qrf; /* The new Qrf being created */ |
| 3313 | + |
| 3314 | + if( pStmt==0 ) return SQLITE_OK; /* No-op */ |
| 3315 | + if( pSpec==0 ) return SQLITE_MISUSE; |
| 3316 | + qrfInitialize(&qrf, pStmt, pSpec, pzErr); |
| 3317 | + switch( qrf.spec.eStyle ){ |
| 3318 | + case QRF_STYLE_Box: |
| 3319 | + case QRF_STYLE_Column: |
| 3320 | + case QRF_STYLE_Markdown: |
| 3321 | + case QRF_STYLE_Table: { |
| 3322 | + /* Columnar modes require that the entire query be evaluated and the |
| 3323 | + ** results stored in memory, so that we can compute column widths */ |
| 3324 | + qrfColumnar(&qrf); |
| 3325 | + break; |
| 3326 | + } |
| 3327 | + case QRF_STYLE_Explain: { |
| 3328 | + qrfExplain(&qrf); |
| 3329 | + break; |
| 3330 | + } |
| 3331 | + case QRF_STYLE_StatsVm: { |
| 3332 | + qrfScanStatusVm(&qrf); |
| 3333 | + break; |
| 3334 | + } |
| 3335 | + case QRF_STYLE_Stats: |
| 3336 | + case QRF_STYLE_StatsEst: { |
| 3337 | + qrfEqpStats(&qrf); |
| 3338 | + break; |
| 3339 | + } |
| 3340 | + default: { |
| 3341 | + /* Non-columnar modes where the output can occur after each row |
| 3342 | + ** of result is received */ |
| 3343 | + while( qrf.iErr==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){ |
| 3344 | + qrfOneSimpleRow(&qrf); |
| 3345 | + } |
| 3346 | + break; |
| 3347 | + } |
| 3348 | + } |
| 3349 | + qrfResetStmt(&qrf); |
| 3350 | + qrfFinalize(&qrf); |
| 3351 | + return qrf.iErr; |
| 3352 | +} |
| 3353 | + |
| 3354 | +/************************* End ext/qrf/qrf.c ********************/ |
| 623 | 3355 | |
| 624 | 3356 | /* Use console I/O package as a direct INCLUDE. */ |
| 625 | 3357 | #define SQLITE_INTERNAL_LINKAGE static |
| 626 | 3358 | |
| 627 | 3359 | #ifdef SQLITE_SHELL_FIDDLE |
| | @@ -631,12 +3363,71 @@ |
| 631 | 3363 | # define SQLITE_CIO_NO_TRANSLATE |
| 632 | 3364 | # define SQLITE_CIO_NO_SETMODE |
| 633 | 3365 | # define SQLITE_CIO_NO_FLUSH |
| 634 | 3366 | #endif |
| 635 | 3367 | |
| 636 | | -#define eputz(z) sqlite3_fputs(z,stderr) |
| 637 | | -#define sputz(fp,z) sqlite3_fputs(z,fp) |
| 3368 | +/* |
| 3369 | +** Output routines that are able to redirect to memory rather than |
| 3370 | +** doing actually I/O. |
| 3371 | +** Works like. |
| 3372 | +** -------------- |
| 3373 | +** cli_printf(FILE*, const char*, ...); fprintf() |
| 3374 | +** cli_puts(const char*, FILE*); fputs() |
| 3375 | +** cli_vprintf(FILE*, const char*, va_list); vfprintf() |
| 3376 | +** |
| 3377 | +** These are just thin wrappers with the following added semantics: |
| 3378 | +** If the file-scope variable cli_output_capture is not NULL, and |
| 3379 | +** if the FILE* argument is stdout or stderr, then rather than |
| 3380 | +** writing to stdout/stdout, append the text to the cli_output_capture |
| 3381 | +** variable. |
| 3382 | +** |
| 3383 | +** The cli_exit(int) routine works like exit() except that it |
| 3384 | +** first dumps any capture output to stdout. |
| 3385 | +*/ |
| 3386 | +static sqlite3_str *cli_output_capture = 0; |
| 3387 | +static int cli_printf(FILE *out, const char *zFormat, ...){ |
| 3388 | + va_list ap; |
| 3389 | + int rc; |
| 3390 | + va_start(ap,zFormat); |
| 3391 | + if( cli_output_capture && (out==stdout || out==stderr) ){ |
| 3392 | + sqlite3_str_vappendf(cli_output_capture, zFormat, ap); |
| 3393 | + rc = 1; |
| 3394 | + }else{ |
| 3395 | + rc = sqlite3_vfprintf(out, zFormat, ap); |
| 3396 | + } |
| 3397 | + va_end(ap); |
| 3398 | + return rc; |
| 3399 | +} |
| 3400 | +static int cli_puts(const char *zText, FILE *out){ |
| 3401 | + if( cli_output_capture && (out==stdout || out==stderr) ){ |
| 3402 | + sqlite3_str_appendall(cli_output_capture, zText); |
| 3403 | + return 1; |
| 3404 | + } |
| 3405 | + return sqlite3_fputs(zText, out); |
| 3406 | +} |
| 3407 | +#if 0 /* Not currently used - available if we need it later */ |
| 3408 | +static int cli_vprintf(FILE *out, const char *zFormat, va_list ap){ |
| 3409 | + if( cli_output_capture && (out==stdout || out==stderr) ){ |
| 3410 | + sqlite3_str_vappendf(cli_output_capture, zFormat, ap); |
| 3411 | + return 1; |
| 3412 | + }else{ |
| 3413 | + return sqlite3_vfprintf(out, zFormat, ap); |
| 3414 | + } |
| 3415 | +} |
| 3416 | +#endif |
| 3417 | +static void cli_exit(int rc){ |
| 3418 | + if( cli_output_capture ){ |
| 3419 | + char *z = sqlite3_str_finish(cli_output_capture); |
| 3420 | + sqlite3_fputs(z, stdout); |
| 3421 | + fflush(stdout); |
| 3422 | + } |
| 3423 | + exit(rc); |
| 3424 | +} |
| 3425 | + |
| 3426 | + |
| 3427 | +#define eputz(z) cli_puts(z,stderr) |
| 3428 | +#define sputz(fp,z) cli_puts(z,fp) |
| 638 | 3429 | |
| 639 | 3430 | /* True if the timer is enabled */ |
| 640 | 3431 | static int enableTimer = 0; |
| 641 | 3432 | |
| 642 | 3433 | /* A version of strcmp() that works with NULL values */ |
| | @@ -723,11 +3514,11 @@ |
| 723 | 3514 | static void endTimer(FILE *out){ |
| 724 | 3515 | if( enableTimer ){ |
| 725 | 3516 | sqlite3_int64 iEnd = timeOfDay(); |
| 726 | 3517 | struct rusage sEnd; |
| 727 | 3518 | getrusage(RUSAGE_SELF, &sEnd); |
| 728 | | - sqlite3_fprintf(out, "Run Time: real %.6f user %.6f sys %.6f\n", |
| 3519 | + cli_printf(out, "Run Time: real %.6f user %.6f sys %.6f\n", |
| 729 | 3520 | (iEnd - iBegin)*0.000001, |
| 730 | 3521 | timeDiff(&sBegin.ru_utime, &sEnd.ru_utime), |
| 731 | 3522 | timeDiff(&sBegin.ru_stime, &sEnd.ru_stime)); |
| 732 | 3523 | } |
| 733 | 3524 | } |
| | @@ -804,17 +3595,17 @@ |
| 804 | 3595 | FILETIME ftCreation, ftExit, ftKernelEnd, ftUserEnd; |
| 805 | 3596 | sqlite3_int64 ftWallEnd = timeOfDay(); |
| 806 | 3597 | getProcessTimesAddr(hProcess,&ftCreation,&ftExit,&ftKernelEnd,&ftUserEnd); |
| 807 | 3598 | #ifdef _WIN64 |
| 808 | 3599 | /* microsecond precision on 64-bit windows */ |
| 809 | | - sqlite3_fprintf(out, "Run Time: real %.6f user %f sys %f\n", |
| 3600 | + cli_printf(out, "Run Time: real %.6f user %f sys %f\n", |
| 810 | 3601 | (ftWallEnd - ftWallBegin)*0.000001, |
| 811 | 3602 | timeDiff(&ftUserBegin, &ftUserEnd), |
| 812 | 3603 | timeDiff(&ftKernelBegin, &ftKernelEnd)); |
| 813 | 3604 | #else |
| 814 | 3605 | /* millisecond precisino on 32-bit windows */ |
| 815 | | - sqlite3_fprintf(out, "Run Time: real %.3f user %.3f sys %.3f\n", |
| 3606 | + cli_printf(out, "Run Time: real %.3f user %.3f sys %.3f\n", |
| 816 | 3607 | (ftWallEnd - ftWallBegin)*0.000001, |
| 817 | 3608 | timeDiff(&ftUserBegin, &ftUserEnd), |
| 818 | 3609 | timeDiff(&ftKernelBegin, &ftKernelEnd)); |
| 819 | 3610 | #endif |
| 820 | 3611 | } |
| | @@ -851,16 +3642,21 @@ |
| 851 | 3642 | ** is true. Otherwise, assume stdin is connected to a file or pipe. |
| 852 | 3643 | */ |
| 853 | 3644 | static int stdin_is_interactive = 1; |
| 854 | 3645 | |
| 855 | 3646 | /* |
| 856 | | -** On Windows systems we need to know if standard output is a console |
| 857 | | -** in order to show that UTF-16 translation is done in the sign-on |
| 858 | | -** banner. The following variable is true if it is the console. |
| 3647 | +** Treat stdout like a TTY if true. |
| 859 | 3648 | */ |
| 860 | 3649 | static int stdout_is_console = 1; |
| 861 | 3650 | |
| 3651 | +/* |
| 3652 | +** Use this value as the width of the output device. Or, figure it |
| 3653 | +** out at runtime if the value is negative. Or use a default width |
| 3654 | +** if this value is zero. |
| 3655 | +*/ |
| 3656 | +static int stdout_tty_width = -1; |
| 3657 | + |
| 862 | 3658 | /* |
| 863 | 3659 | ** The following is the open SQLite database. We make a pointer |
| 864 | 3660 | ** to this database a static variable so that it can be accessed |
| 865 | 3661 | ** by the SIGINT handler to interrupt database processing. |
| 866 | 3662 | */ |
| | @@ -994,11 +3790,11 @@ |
| 994 | 3790 | #endif /* !defined(SQLITE_OMIT_DYNAPROMPT) */ |
| 995 | 3791 | |
| 996 | 3792 | /* Indicate out-of-memory and exit. */ |
| 997 | 3793 | static void shell_out_of_memory(void){ |
| 998 | 3794 | eputz("Error: out of memory\n"); |
| 999 | | - exit(1); |
| 3795 | + cli_exit(1); |
| 1000 | 3796 | } |
| 1001 | 3797 | |
| 1002 | 3798 | /* Check a pointer to see if it is NULL. If it is NULL, exit with an |
| 1003 | 3799 | ** out-of-memory error. |
| 1004 | 3800 | */ |
| | @@ -1025,306 +3821,24 @@ |
| 1025 | 3821 | char *z; |
| 1026 | 3822 | if( iotrace==0 ) return; |
| 1027 | 3823 | va_start(ap, zFormat); |
| 1028 | 3824 | z = sqlite3_vmprintf(zFormat, ap); |
| 1029 | 3825 | va_end(ap); |
| 1030 | | - sqlite3_fprintf(iotrace, "%s", z); |
| 3826 | + cli_printf(iotrace, "%s", z); |
| 1031 | 3827 | sqlite3_free(z); |
| 1032 | 3828 | } |
| 1033 | 3829 | #endif |
| 1034 | 3830 | |
| 1035 | | -/* Lookup table to estimate the number of columns consumed by a Unicode |
| 1036 | | -** character. |
| 1037 | | -*/ |
| 1038 | | -static const struct { |
| 1039 | | - unsigned char w; /* Width of the character in columns */ |
| 1040 | | - int iFirst; /* First character in a span having this width */ |
| 1041 | | -} aUWidth[] = { |
| 1042 | | - /* {1, 0x00000}, */ |
| 1043 | | - {0, 0x00300}, {1, 0x00370}, {0, 0x00483}, {1, 0x00487}, {0, 0x00488}, |
| 1044 | | - {1, 0x0048a}, {0, 0x00591}, {1, 0x005be}, {0, 0x005bf}, {1, 0x005c0}, |
| 1045 | | - {0, 0x005c1}, {1, 0x005c3}, {0, 0x005c4}, {1, 0x005c6}, {0, 0x005c7}, |
| 1046 | | - {1, 0x005c8}, {0, 0x00600}, {1, 0x00604}, {0, 0x00610}, {1, 0x00616}, |
| 1047 | | - {0, 0x0064b}, {1, 0x0065f}, {0, 0x00670}, {1, 0x00671}, {0, 0x006d6}, |
| 1048 | | - {1, 0x006e5}, {0, 0x006e7}, {1, 0x006e9}, {0, 0x006ea}, {1, 0x006ee}, |
| 1049 | | - {0, 0x0070f}, {1, 0x00710}, {0, 0x00711}, {1, 0x00712}, {0, 0x00730}, |
| 1050 | | - {1, 0x0074b}, {0, 0x007a6}, {1, 0x007b1}, {0, 0x007eb}, {1, 0x007f4}, |
| 1051 | | - {0, 0x00901}, {1, 0x00903}, {0, 0x0093c}, {1, 0x0093d}, {0, 0x00941}, |
| 1052 | | - {1, 0x00949}, {0, 0x0094d}, {1, 0x0094e}, {0, 0x00951}, {1, 0x00955}, |
| 1053 | | - {0, 0x00962}, {1, 0x00964}, {0, 0x00981}, {1, 0x00982}, {0, 0x009bc}, |
| 1054 | | - {1, 0x009bd}, {0, 0x009c1}, {1, 0x009c5}, {0, 0x009cd}, {1, 0x009ce}, |
| 1055 | | - {0, 0x009e2}, {1, 0x009e4}, {0, 0x00a01}, {1, 0x00a03}, {0, 0x00a3c}, |
| 1056 | | - {1, 0x00a3d}, {0, 0x00a41}, {1, 0x00a43}, {0, 0x00a47}, {1, 0x00a49}, |
| 1057 | | - {0, 0x00a4b}, {1, 0x00a4e}, {0, 0x00a70}, {1, 0x00a72}, {0, 0x00a81}, |
| 1058 | | - {1, 0x00a83}, {0, 0x00abc}, {1, 0x00abd}, {0, 0x00ac1}, {1, 0x00ac6}, |
| 1059 | | - {0, 0x00ac7}, {1, 0x00ac9}, {0, 0x00acd}, {1, 0x00ace}, {0, 0x00ae2}, |
| 1060 | | - {1, 0x00ae4}, {0, 0x00b01}, {1, 0x00b02}, {0, 0x00b3c}, {1, 0x00b3d}, |
| 1061 | | - {0, 0x00b3f}, {1, 0x00b40}, {0, 0x00b41}, {1, 0x00b44}, {0, 0x00b4d}, |
| 1062 | | - {1, 0x00b4e}, {0, 0x00b56}, {1, 0x00b57}, {0, 0x00b82}, {1, 0x00b83}, |
| 1063 | | - {0, 0x00bc0}, {1, 0x00bc1}, {0, 0x00bcd}, {1, 0x00bce}, {0, 0x00c3e}, |
| 1064 | | - {1, 0x00c41}, {0, 0x00c46}, {1, 0x00c49}, {0, 0x00c4a}, {1, 0x00c4e}, |
| 1065 | | - {0, 0x00c55}, {1, 0x00c57}, {0, 0x00cbc}, {1, 0x00cbd}, {0, 0x00cbf}, |
| 1066 | | - {1, 0x00cc0}, {0, 0x00cc6}, {1, 0x00cc7}, {0, 0x00ccc}, {1, 0x00cce}, |
| 1067 | | - {0, 0x00ce2}, {1, 0x00ce4}, {0, 0x00d41}, {1, 0x00d44}, {0, 0x00d4d}, |
| 1068 | | - {1, 0x00d4e}, {0, 0x00dca}, {1, 0x00dcb}, {0, 0x00dd2}, {1, 0x00dd5}, |
| 1069 | | - {0, 0x00dd6}, {1, 0x00dd7}, {0, 0x00e31}, {1, 0x00e32}, {0, 0x00e34}, |
| 1070 | | - {1, 0x00e3b}, {0, 0x00e47}, {1, 0x00e4f}, {0, 0x00eb1}, {1, 0x00eb2}, |
| 1071 | | - {0, 0x00eb4}, {1, 0x00eba}, {0, 0x00ebb}, {1, 0x00ebd}, {0, 0x00ec8}, |
| 1072 | | - {1, 0x00ece}, {0, 0x00f18}, {1, 0x00f1a}, {0, 0x00f35}, {1, 0x00f36}, |
| 1073 | | - {0, 0x00f37}, {1, 0x00f38}, {0, 0x00f39}, {1, 0x00f3a}, {0, 0x00f71}, |
| 1074 | | - {1, 0x00f7f}, {0, 0x00f80}, {1, 0x00f85}, {0, 0x00f86}, {1, 0x00f88}, |
| 1075 | | - {0, 0x00f90}, {1, 0x00f98}, {0, 0x00f99}, {1, 0x00fbd}, {0, 0x00fc6}, |
| 1076 | | - {1, 0x00fc7}, {0, 0x0102d}, {1, 0x01031}, {0, 0x01032}, {1, 0x01033}, |
| 1077 | | - {0, 0x01036}, {1, 0x01038}, {0, 0x01039}, {1, 0x0103a}, {0, 0x01058}, |
| 1078 | | - {1, 0x0105a}, {2, 0x01100}, {0, 0x01160}, {1, 0x01200}, {0, 0x0135f}, |
| 1079 | | - {1, 0x01360}, {0, 0x01712}, {1, 0x01715}, {0, 0x01732}, {1, 0x01735}, |
| 1080 | | - {0, 0x01752}, {1, 0x01754}, {0, 0x01772}, {1, 0x01774}, {0, 0x017b4}, |
| 1081 | | - {1, 0x017b6}, {0, 0x017b7}, {1, 0x017be}, {0, 0x017c6}, {1, 0x017c7}, |
| 1082 | | - {0, 0x017c9}, {1, 0x017d4}, {0, 0x017dd}, {1, 0x017de}, {0, 0x0180b}, |
| 1083 | | - {1, 0x0180e}, {0, 0x018a9}, {1, 0x018aa}, {0, 0x01920}, {1, 0x01923}, |
| 1084 | | - {0, 0x01927}, {1, 0x01929}, {0, 0x01932}, {1, 0x01933}, {0, 0x01939}, |
| 1085 | | - {1, 0x0193c}, {0, 0x01a17}, {1, 0x01a19}, {0, 0x01b00}, {1, 0x01b04}, |
| 1086 | | - {0, 0x01b34}, {1, 0x01b35}, {0, 0x01b36}, {1, 0x01b3b}, {0, 0x01b3c}, |
| 1087 | | - {1, 0x01b3d}, {0, 0x01b42}, {1, 0x01b43}, {0, 0x01b6b}, {1, 0x01b74}, |
| 1088 | | - {0, 0x01dc0}, {1, 0x01dcb}, {0, 0x01dfe}, {1, 0x01e00}, {0, 0x0200b}, |
| 1089 | | - {1, 0x02010}, {0, 0x0202a}, {1, 0x0202f}, {0, 0x02060}, {1, 0x02064}, |
| 1090 | | - {0, 0x0206a}, {1, 0x02070}, {0, 0x020d0}, {1, 0x020f0}, {2, 0x02329}, |
| 1091 | | - {1, 0x0232b}, {2, 0x02e80}, {0, 0x0302a}, {2, 0x03030}, {1, 0x0303f}, |
| 1092 | | - {2, 0x03040}, {0, 0x03099}, {2, 0x0309b}, {1, 0x0a4d0}, {0, 0x0a806}, |
| 1093 | | - {1, 0x0a807}, {0, 0x0a80b}, {1, 0x0a80c}, {0, 0x0a825}, {1, 0x0a827}, |
| 1094 | | - {2, 0x0ac00}, {1, 0x0d7a4}, {2, 0x0f900}, {1, 0x0fb00}, {0, 0x0fb1e}, |
| 1095 | | - {1, 0x0fb1f}, {0, 0x0fe00}, {2, 0x0fe10}, {1, 0x0fe1a}, {0, 0x0fe20}, |
| 1096 | | - {1, 0x0fe24}, {2, 0x0fe30}, {1, 0x0fe70}, {0, 0x0feff}, {2, 0x0ff00}, |
| 1097 | | - {1, 0x0ff61}, {2, 0x0ffe0}, {1, 0x0ffe7}, {0, 0x0fff9}, {1, 0x0fffc}, |
| 1098 | | - {0, 0x10a01}, {1, 0x10a04}, {0, 0x10a05}, {1, 0x10a07}, {0, 0x10a0c}, |
| 1099 | | - {1, 0x10a10}, {0, 0x10a38}, {1, 0x10a3b}, {0, 0x10a3f}, {1, 0x10a40}, |
| 1100 | | - {0, 0x1d167}, {1, 0x1d16a}, {0, 0x1d173}, {1, 0x1d183}, {0, 0x1d185}, |
| 1101 | | - {1, 0x1d18c}, {0, 0x1d1aa}, {1, 0x1d1ae}, {0, 0x1d242}, {1, 0x1d245}, |
| 1102 | | - {2, 0x20000}, {1, 0x2fffe}, {2, 0x30000}, {1, 0x3fffe}, {0, 0xe0001}, |
| 1103 | | - {1, 0xe0002}, {0, 0xe0020}, {1, 0xe0080}, {0, 0xe0100}, {1, 0xe01f0} |
| 1104 | | -}; |
| 1105 | | - |
| 1106 | | -/* |
| 1107 | | -** Return an estimate of the width, in columns, for the single Unicode |
| 1108 | | -** character c. For normal characters, the answer is always 1. But the |
| 1109 | | -** estimate might be 0 or 2 for zero-width and double-width characters. |
| 1110 | | -** |
| 1111 | | -** Different display devices display unicode using different widths. So |
| 1112 | | -** it is impossible to know that true display width with 100% accuracy. |
| 1113 | | -** Inaccuracies in the width estimates might cause columns to be misaligned. |
| 1114 | | -** Unfortunately, there is nothing we can do about that. |
| 1115 | | -*/ |
| 1116 | | -int cli_wcwidth(int c){ |
| 1117 | | - int iFirst, iLast; |
| 1118 | | - |
| 1119 | | - /* Fast path for common characters */ |
| 1120 | | - if( c<=0x300 ) return 1; |
| 1121 | | - |
| 1122 | | - /* The general case */ |
| 1123 | | - iFirst = 0; |
| 1124 | | - iLast = sizeof(aUWidth)/sizeof(aUWidth[0]) - 1; |
| 1125 | | - while( iFirst<iLast-1 ){ |
| 1126 | | - int iMid = (iFirst+iLast)/2; |
| 1127 | | - int cMid = aUWidth[iMid].iFirst; |
| 1128 | | - if( cMid < c ){ |
| 1129 | | - iFirst = iMid; |
| 1130 | | - }else if( cMid > c ){ |
| 1131 | | - iLast = iMid - 1; |
| 1132 | | - }else{ |
| 1133 | | - return aUWidth[iMid].w; |
| 1134 | | - } |
| 1135 | | - } |
| 1136 | | - if( aUWidth[iLast].iFirst > c ) return aUWidth[iFirst].w; |
| 1137 | | - return aUWidth[iLast].w; |
| 1138 | | -} |
| 1139 | | - |
| 1140 | | -/* |
| 1141 | | -** Compute the value and length of a multi-byte UTF-8 character that |
| 1142 | | -** begins at z[0]. Return the length. Write the Unicode value into *pU. |
| 1143 | | -** |
| 1144 | | -** This routine only works for *multi-byte* UTF-8 characters. |
| 1145 | | -*/ |
| 1146 | | -static int decodeUtf8(const unsigned char *z, int *pU){ |
| 1147 | | - if( (z[0] & 0xe0)==0xc0 && (z[1] & 0xc0)==0x80 ){ |
| 1148 | | - *pU = ((z[0] & 0x1f)<<6) | (z[1] & 0x3f); |
| 1149 | | - return 2; |
| 1150 | | - } |
| 1151 | | - if( (z[0] & 0xf0)==0xe0 && (z[1] & 0xc0)==0x80 && (z[2] & 0xc0)==0x80 ){ |
| 1152 | | - *pU = ((z[0] & 0x0f)<<12) | ((z[1] & 0x3f)<<6) | (z[2] & 0x3f); |
| 1153 | | - return 3; |
| 1154 | | - } |
| 1155 | | - if( (z[0] & 0xf8)==0xf0 && (z[1] & 0xc0)==0x80 && (z[2] & 0xc0)==0x80 |
| 1156 | | - && (z[3] & 0xc0)==0x80 |
| 1157 | | - ){ |
| 1158 | | - *pU = ((z[0] & 0x0f)<<18) | ((z[1] & 0x3f)<<12) | ((z[2] & 0x3f))<<6 |
| 1159 | | - | (z[3] & 0x3f); |
| 1160 | | - return 4; |
| 1161 | | - } |
| 1162 | | - *pU = 0; |
| 1163 | | - return 1; |
| 1164 | | -} |
| 1165 | | - |
| 1166 | | - |
| 1167 | | -#if 0 /* NOT USED */ |
| 1168 | | -/* |
| 1169 | | -** Return the width, in display columns, of a UTF-8 string. |
| 1170 | | -** |
| 1171 | | -** Each normal character counts as 1. Zero-width characters count |
| 1172 | | -** as zero, and double-width characters count as 2. |
| 1173 | | -*/ |
| 1174 | | -int cli_wcswidth(const char *z){ |
| 1175 | | - const unsigned char *a = (const unsigned char*)z; |
| 1176 | | - int n = 0; |
| 1177 | | - int i = 0; |
| 1178 | | - unsigned char c; |
| 1179 | | - while( (c = a[i])!=0 ){ |
| 1180 | | - if( c>=0xc0 ){ |
| 1181 | | - int u; |
| 1182 | | - int len = decodeUtf8(&a[i], &u); |
| 1183 | | - i += len; |
| 1184 | | - n += cli_wcwidth(u); |
| 1185 | | - }else if( c>=' ' ){ |
| 1186 | | - n++; |
| 1187 | | - i++; |
| 1188 | | - }else{ |
| 1189 | | - i++; |
| 1190 | | - } |
| 1191 | | - } |
| 1192 | | - return n; |
| 1193 | | -} |
| 1194 | | -#endif |
| 1195 | | - |
| 1196 | | -/* |
| 1197 | | -** Check to see if z[] is a valid VT100 escape. If it is, then |
| 1198 | | -** return the number of bytes in the escape sequence. Return 0 if |
| 1199 | | -** z[] is not a VT100 escape. |
| 1200 | | -** |
| 1201 | | -** This routine assumes that z[0] is \033 (ESC). |
| 1202 | | -*/ |
| 1203 | | -static int isVt100(const unsigned char *z){ |
| 1204 | | - int i; |
| 1205 | | - if( z[1]!='[' ) return 0; |
| 1206 | | - i = 2; |
| 1207 | | - while( z[i]>=0x30 && z[i]<=0x3f ){ i++; } |
| 1208 | | - while( z[i]>=0x20 && z[i]<=0x2f ){ i++; } |
| 1209 | | - if( z[i]<0x40 || z[i]>0x7e ) return 0; |
| 1210 | | - return i+1; |
| 1211 | | -} |
| 1212 | | - |
| 1213 | | -/* |
| 1214 | | -** Output string zUtf to stdout as w characters. If w is negative, |
| 1215 | | -** then right-justify the text. W is the width in UTF-8 characters, not |
| 1216 | | -** in bytes. This is different from the %*.*s specification in printf |
| 1217 | | -** since with %*.*s the width is measured in bytes, not characters. |
| 1218 | | -** |
| 1219 | | -** Take into account zero-width and double-width Unicode characters. |
| 1220 | | -** In other words, a zero-width character does not count toward the |
| 1221 | | -** the w limit. A double-width character counts as two. |
| 1222 | | -** |
| 1223 | | -** w should normally be a small number. A couple hundred at most. This |
| 1224 | | -** routine caps w at 100 million to avoid integer overflow issues. |
| 1225 | | -*/ |
| 1226 | | -static void utf8_width_print(FILE *out, int w, const char *zUtf){ |
| 1227 | | - const unsigned char *a = (const unsigned char*)zUtf; |
| 1228 | | - static const int mxW = 10000000; |
| 1229 | | - unsigned char c; |
| 1230 | | - int i = 0; |
| 1231 | | - int n = 0; |
| 1232 | | - int k; |
| 1233 | | - int aw; |
| 1234 | | - if( w<-mxW ){ |
| 1235 | | - w = -mxW; |
| 1236 | | - }else if( w>mxW ){ |
| 1237 | | - w= mxW; |
| 1238 | | - } |
| 1239 | | - aw = w<0 ? -w : w; |
| 1240 | | - if( zUtf==0 ) zUtf = ""; |
| 1241 | | - while( (c = a[i])!=0 ){ |
| 1242 | | - if( (c&0xc0)==0xc0 ){ |
| 1243 | | - int u; |
| 1244 | | - int len = decodeUtf8(a+i, &u); |
| 1245 | | - int x = cli_wcwidth(u); |
| 1246 | | - if( x+n>aw ){ |
| 1247 | | - break; |
| 1248 | | - } |
| 1249 | | - i += len; |
| 1250 | | - n += x; |
| 1251 | | - }else if( c==0x1b && (k = isVt100(&a[i]))>0 ){ |
| 1252 | | - i += k; |
| 1253 | | - }else if( n>=aw ){ |
| 1254 | | - break; |
| 1255 | | - }else{ |
| 1256 | | - n++; |
| 1257 | | - i++; |
| 1258 | | - } |
| 1259 | | - } |
| 1260 | | - if( n>=aw ){ |
| 1261 | | - sqlite3_fprintf(out, "%.*s", i, zUtf); |
| 1262 | | - }else if( w<0 ){ |
| 1263 | | - sqlite3_fprintf(out, "%*s%s", aw-n, "", zUtf); |
| 1264 | | - }else{ |
| 1265 | | - sqlite3_fprintf(out, "%s%*s", zUtf, aw-n, ""); |
| 1266 | | - } |
| 1267 | | -} |
| 1268 | | - |
| 1269 | | - |
| 1270 | | -/* |
| 1271 | | -** Determines if a string is a number of not. |
| 1272 | | -*/ |
| 1273 | | -static int isNumber(const char *z, int *realnum){ |
| 1274 | | - if( *z=='-' || *z=='+' ) z++; |
| 1275 | | - if( !IsDigit(*z) ){ |
| 1276 | | - return 0; |
| 1277 | | - } |
| 1278 | | - z++; |
| 1279 | | - if( realnum ) *realnum = 0; |
| 1280 | | - while( IsDigit(*z) ){ z++; } |
| 1281 | | - if( *z=='.' ){ |
| 1282 | | - z++; |
| 1283 | | - if( !IsDigit(*z) ) return 0; |
| 1284 | | - while( IsDigit(*z) ){ z++; } |
| 1285 | | - if( realnum ) *realnum = 1; |
| 1286 | | - } |
| 1287 | | - if( *z=='e' || *z=='E' ){ |
| 1288 | | - z++; |
| 1289 | | - if( *z=='+' || *z=='-' ) z++; |
| 1290 | | - if( !IsDigit(*z) ) return 0; |
| 1291 | | - while( IsDigit(*z) ){ z++; } |
| 1292 | | - if( realnum ) *realnum = 1; |
| 1293 | | - } |
| 1294 | | - return *z==0; |
| 1295 | | -} |
| 1296 | | - |
| 1297 | 3831 | /* |
| 1298 | 3832 | ** Compute a string length that is limited to what can be stored in |
| 1299 | 3833 | ** lower 30 bits of a 32-bit signed integer. |
| 1300 | 3834 | */ |
| 1301 | 3835 | static int strlen30(const char *z){ |
| 1302 | | - const char *z2 = z; |
| 1303 | | - while( *z2 ){ z2++; } |
| 1304 | | - return 0x3fffffff & (int)(z2 - z); |
| 1305 | | -} |
| 1306 | | - |
| 1307 | | -/* |
| 1308 | | -** Return the length of a string in characters. Multibyte UTF8 characters |
| 1309 | | -** count as a single character for single-width characters, or as two |
| 1310 | | -** characters for double-width characters. |
| 1311 | | -*/ |
| 1312 | | -static int strlenChar(const char *z){ |
| 1313 | | - int n = 0; |
| 1314 | | - while( *z ){ |
| 1315 | | - if( (0x80&z[0])==0 ){ |
| 1316 | | - n++; |
| 1317 | | - z++; |
| 1318 | | - }else{ |
| 1319 | | - int u = 0; |
| 1320 | | - int len = decodeUtf8((const u8*)z, &u); |
| 1321 | | - z += len; |
| 1322 | | - n += cli_wcwidth(u); |
| 1323 | | - } |
| 1324 | | - } |
| 1325 | | - return n; |
| 3836 | + size_t n; |
| 3837 | + if( z==0 ) return 0; |
| 3838 | + n = strlen(z); |
| 3839 | + return n>0x3fffffff ? 0x3fffffff : (int)n; |
| 1326 | 3840 | } |
| 1327 | 3841 | |
| 1328 | 3842 | /* |
| 1329 | 3843 | ** Return open FILE * if zFile exists, can be opened for read |
| 1330 | 3844 | ** and is an ordinary file or a character stream source. |
| | @@ -1770,11 +4284,11 @@ |
| 1770 | 4284 | ** work here in the middle of this regular program. |
| 1771 | 4285 | */ |
| 1772 | 4286 | #define SQLITE_EXTENSION_INIT1 |
| 1773 | 4287 | #define SQLITE_EXTENSION_INIT2(X) (void)(X) |
| 1774 | 4288 | |
| 1775 | | -/************************* Begin ../ext/misc/windirent.h ******************/ |
| 4289 | +/************************* Begin ext/misc/windirent.h ******************/ |
| 1776 | 4290 | /* |
| 1777 | 4291 | ** 2025-06-05 |
| 1778 | 4292 | ** |
| 1779 | 4293 | ** The author disclaims copyright to this source code. In place of |
| 1780 | 4294 | ** a legal notice, here is a blessing: |
| | @@ -1935,12 +4449,12 @@ |
| 1935 | 4449 | return &pDir->cur; |
| 1936 | 4450 | } |
| 1937 | 4451 | |
| 1938 | 4452 | #endif /* defined(_WIN32) && defined(_MSC_VER) */ |
| 1939 | 4453 | |
| 1940 | | -/************************* End ../ext/misc/windirent.h ********************/ |
| 1941 | | -/************************* Begin ../ext/misc/memtrace.c ******************/ |
| 4454 | +/************************* End ext/misc/windirent.h ********************/ |
| 4455 | +/************************* Begin ext/misc/memtrace.c ******************/ |
| 1942 | 4456 | /* |
| 1943 | 4457 | ** 2019-01-21 |
| 1944 | 4458 | ** |
| 1945 | 4459 | ** The author disclaims copyright to this source code. In place of |
| 1946 | 4460 | ** a legal notice, here is a blessing: |
| | @@ -2046,12 +4560,12 @@ |
| 2046 | 4560 | } |
| 2047 | 4561 | memtraceOut = 0; |
| 2048 | 4562 | return rc; |
| 2049 | 4563 | } |
| 2050 | 4564 | |
| 2051 | | -/************************* End ../ext/misc/memtrace.c ********************/ |
| 2052 | | -/************************* Begin ../ext/misc/pcachetrace.c ******************/ |
| 4565 | +/************************* End ext/misc/memtrace.c ********************/ |
| 4566 | +/************************* Begin ext/misc/pcachetrace.c ******************/ |
| 2053 | 4567 | /* |
| 2054 | 4568 | ** 2023-06-21 |
| 2055 | 4569 | ** |
| 2056 | 4570 | ** The author disclaims copyright to this source code. In place of |
| 2057 | 4571 | ** a legal notice, here is a blessing: |
| | @@ -2228,12 +4742,12 @@ |
| 2228 | 4742 | } |
| 2229 | 4743 | pcachetraceOut = 0; |
| 2230 | 4744 | return rc; |
| 2231 | 4745 | } |
| 2232 | 4746 | |
| 2233 | | -/************************* End ../ext/misc/pcachetrace.c ********************/ |
| 2234 | | -/************************* Begin ../ext/misc/shathree.c ******************/ |
| 4747 | +/************************* End ext/misc/pcachetrace.c ********************/ |
| 4748 | +/************************* Begin ext/misc/shathree.c ******************/ |
| 2235 | 4749 | /* |
| 2236 | 4750 | ** 2017-03-08 |
| 2237 | 4751 | ** |
| 2238 | 4752 | ** The author disclaims copyright to this source code. In place of |
| 2239 | 4753 | ** a legal notice, here is a blessing: |
| | @@ -3085,12 +5599,12 @@ |
| 3085 | 5599 | 0, sha3QueryFunc, 0, 0); |
| 3086 | 5600 | } |
| 3087 | 5601 | return rc; |
| 3088 | 5602 | } |
| 3089 | 5603 | |
| 3090 | | -/************************* End ../ext/misc/shathree.c ********************/ |
| 3091 | | -/************************* Begin ../ext/misc/sha1.c ******************/ |
| 5604 | +/************************* End ext/misc/shathree.c ********************/ |
| 5605 | +/************************* Begin ext/misc/sha1.c ******************/ |
| 3092 | 5606 | /* |
| 3093 | 5607 | ** 2017-01-27 |
| 3094 | 5608 | ** |
| 3095 | 5609 | ** The author disclaims copyright to this source code. In place of |
| 3096 | 5610 | ** a legal notice, here is a blessing: |
| | @@ -3497,12 +6011,12 @@ |
| 3497 | 6011 | sha1QueryFunc, 0, 0); |
| 3498 | 6012 | } |
| 3499 | 6013 | return rc; |
| 3500 | 6014 | } |
| 3501 | 6015 | |
| 3502 | | -/************************* End ../ext/misc/sha1.c ********************/ |
| 3503 | | -/************************* Begin ../ext/misc/uint.c ******************/ |
| 6016 | +/************************* End ext/misc/sha1.c ********************/ |
| 6017 | +/************************* Begin ext/misc/uint.c ******************/ |
| 3504 | 6018 | /* |
| 3505 | 6019 | ** 2020-04-14 |
| 3506 | 6020 | ** |
| 3507 | 6021 | ** The author disclaims copyright to this source code. In place of |
| 3508 | 6022 | ** a legal notice, here is a blessing: |
| | @@ -3592,12 +6106,12 @@ |
| 3592 | 6106 | SQLITE_EXTENSION_INIT2(pApi); |
| 3593 | 6107 | (void)pzErrMsg; /* Unused parameter */ |
| 3594 | 6108 | return sqlite3_create_collation(db, "uint", SQLITE_UTF8, 0, uintCollFunc); |
| 3595 | 6109 | } |
| 3596 | 6110 | |
| 3597 | | -/************************* End ../ext/misc/uint.c ********************/ |
| 3598 | | -/************************* Begin ../ext/misc/decimal.c ******************/ |
| 6111 | +/************************* End ext/misc/uint.c ********************/ |
| 6112 | +/************************* Begin ext/misc/decimal.c ******************/ |
| 3599 | 6113 | /* |
| 3600 | 6114 | ** 2020-06-22 |
| 3601 | 6115 | ** |
| 3602 | 6116 | ** The author disclaims copyright to this source code. In place of |
| 3603 | 6117 | ** a legal notice, here is a blessing: |
| | @@ -4497,14 +7011,14 @@ |
| 4497 | 7011 | 0, decimalCollFunc); |
| 4498 | 7012 | } |
| 4499 | 7013 | return rc; |
| 4500 | 7014 | } |
| 4501 | 7015 | |
| 4502 | | -/************************* End ../ext/misc/decimal.c ********************/ |
| 7016 | +/************************* End ext/misc/decimal.c ********************/ |
| 4503 | 7017 | #undef sqlite3_base_init |
| 4504 | 7018 | #define sqlite3_base_init sqlite3_base64_init |
| 4505 | | -/************************* Begin ../ext/misc/base64.c ******************/ |
| 7019 | +/************************* Begin ext/misc/base64.c ******************/ |
| 4506 | 7020 | /* |
| 4507 | 7021 | ** 2022-11-18 |
| 4508 | 7022 | ** |
| 4509 | 7023 | ** The author disclaims copyright to this source code. In place of |
| 4510 | 7024 | ** a legal notice, here is a blessing: |
| | @@ -4799,15 +7313,15 @@ |
| 4799 | 7313 | ** allows shell.c, as distributed, to have this extension built in. |
| 4800 | 7314 | */ |
| 4801 | 7315 | #define BASE64_INIT(db) sqlite3_base64_init(db, 0, 0) |
| 4802 | 7316 | #define BASE64_EXPOSE(db, pzErr) /* Not needed, ..._init() does this. */ |
| 4803 | 7317 | |
| 4804 | | -/************************* End ../ext/misc/base64.c ********************/ |
| 7318 | +/************************* End ext/misc/base64.c ********************/ |
| 4805 | 7319 | #undef sqlite3_base_init |
| 4806 | 7320 | #define sqlite3_base_init sqlite3_base85_init |
| 4807 | 7321 | #define OMIT_BASE85_CHECKER |
| 4808 | | -/************************* Begin ../ext/misc/base85.c ******************/ |
| 7322 | +/************************* Begin ext/misc/base85.c ******************/ |
| 4809 | 7323 | /* |
| 4810 | 7324 | ** 2022-11-16 |
| 4811 | 7325 | ** |
| 4812 | 7326 | ** The author disclaims copyright to this source code. In place of |
| 4813 | 7327 | ** a legal notice, here is a blessing: |
| | @@ -5259,12 +7773,12 @@ |
| 5259 | 7773 | return rc; |
| 5260 | 7774 | } |
| 5261 | 7775 | |
| 5262 | 7776 | #endif |
| 5263 | 7777 | |
| 5264 | | -/************************* End ../ext/misc/base85.c ********************/ |
| 5265 | | -/************************* Begin ../ext/misc/ieee754.c ******************/ |
| 7778 | +/************************* End ext/misc/base85.c ********************/ |
| 7779 | +/************************* Begin ext/misc/ieee754.c ******************/ |
| 5266 | 7780 | /* |
| 5267 | 7781 | ** 2013-04-17 |
| 5268 | 7782 | ** |
| 5269 | 7783 | ** The author disclaims copyright to this source code. In place of |
| 5270 | 7784 | ** a legal notice, here is a blessing: |
| | @@ -5589,12 +8103,12 @@ |
| 5589 | 8103 | aFunc[i].xFunc, 0, 0); |
| 5590 | 8104 | } |
| 5591 | 8105 | return rc; |
| 5592 | 8106 | } |
| 5593 | 8107 | |
| 5594 | | -/************************* End ../ext/misc/ieee754.c ********************/ |
| 5595 | | -/************************* Begin ../ext/misc/series.c ******************/ |
| 8108 | +/************************* End ext/misc/ieee754.c ********************/ |
| 8109 | +/************************* Begin ext/misc/series.c ******************/ |
| 5596 | 8110 | /* |
| 5597 | 8111 | ** 2015-08-18, 2023-04-28 |
| 5598 | 8112 | ** |
| 5599 | 8113 | ** The author disclaims copyright to this source code. In place of |
| 5600 | 8114 | ** a legal notice, here is a blessing: |
| | @@ -6529,12 +9043,12 @@ |
| 6529 | 9043 | rc = sqlite3_create_module(db, "generate_series", &seriesModule, 0); |
| 6530 | 9044 | #endif |
| 6531 | 9045 | return rc; |
| 6532 | 9046 | } |
| 6533 | 9047 | |
| 6534 | | -/************************* End ../ext/misc/series.c ********************/ |
| 6535 | | -/************************* Begin ../ext/misc/regexp.c ******************/ |
| 9048 | +/************************* End ext/misc/series.c ********************/ |
| 9049 | +/************************* Begin ext/misc/regexp.c ******************/ |
| 6536 | 9050 | /* |
| 6537 | 9051 | ** 2012-11-13 |
| 6538 | 9052 | ** |
| 6539 | 9053 | ** The author disclaims copyright to this source code. In place of |
| 6540 | 9054 | ** a legal notice, here is a blessing: |
| | @@ -7446,13 +9960,13 @@ |
| 7446 | 9960 | #endif /* SQLITE_DEBUG */ |
| 7447 | 9961 | } |
| 7448 | 9962 | return rc; |
| 7449 | 9963 | } |
| 7450 | 9964 | |
| 7451 | | -/************************* End ../ext/misc/regexp.c ********************/ |
| 9965 | +/************************* End ext/misc/regexp.c ********************/ |
| 7452 | 9966 | #ifndef SQLITE_SHELL_FIDDLE |
| 7453 | | -/************************* Begin ../ext/misc/fileio.c ******************/ |
| 9967 | +/************************* Begin ext/misc/fileio.c ******************/ |
| 7454 | 9968 | /* |
| 7455 | 9969 | ** 2014-06-13 |
| 7456 | 9970 | ** |
| 7457 | 9971 | ** The author disclaims copyright to this source code. In place of |
| 7458 | 9972 | ** a legal notice, here is a blessing: |
| | @@ -8574,20 +11088,12 @@ |
| 8574 | 11088 | rc = fsdirRegister(db); |
| 8575 | 11089 | } |
| 8576 | 11090 | return rc; |
| 8577 | 11091 | } |
| 8578 | 11092 | |
| 8579 | | -#if defined(FILEIO_WIN32_DLL) && (defined(_WIN32) || defined(WIN32)) |
| 8580 | | -/* To allow a standalone DLL, make test_windirent.c use the same |
| 8581 | | - * redefined SQLite API calls as the above extension code does. |
| 8582 | | - * Just pull in this .c to accomplish this. As a beneficial side |
| 8583 | | - * effect, this extension becomes a single translation unit. */ |
| 8584 | | -# include "test_windirent.c" |
| 8585 | | -#endif |
| 8586 | | - |
| 8587 | | -/************************* End ../ext/misc/fileio.c ********************/ |
| 8588 | | -/************************* Begin ../ext/misc/completion.c ******************/ |
| 11093 | +/************************* End ext/misc/fileio.c ********************/ |
| 11094 | +/************************* Begin ext/misc/completion.c ******************/ |
| 8589 | 11095 | /* |
| 8590 | 11096 | ** 2017-07-10 |
| 8591 | 11097 | ** |
| 8592 | 11098 | ** The author disclaims copyright to this source code. In place of |
| 8593 | 11099 | ** a legal notice, here is a blessing: |
| | @@ -9095,12 +11601,12 @@ |
| 9095 | 11601 | rc = sqlite3CompletionVtabInit(db); |
| 9096 | 11602 | #endif |
| 9097 | 11603 | return rc; |
| 9098 | 11604 | } |
| 9099 | 11605 | |
| 9100 | | -/************************* End ../ext/misc/completion.c ********************/ |
| 9101 | | -/************************* Begin ../ext/misc/appendvfs.c ******************/ |
| 11606 | +/************************* End ext/misc/completion.c ********************/ |
| 11607 | +/************************* Begin ext/misc/appendvfs.c ******************/ |
| 9102 | 11608 | /* |
| 9103 | 11609 | ** 2017-10-20 |
| 9104 | 11610 | ** |
| 9105 | 11611 | ** The author disclaims copyright to this source code. In place of |
| 9106 | 11612 | ** a legal notice, here is a blessing: |
| | @@ -9770,14 +12276,14 @@ |
| 9770 | 12276 | #endif |
| 9771 | 12277 | if( rc==SQLITE_OK ) rc = SQLITE_OK_LOAD_PERMANENTLY; |
| 9772 | 12278 | return rc; |
| 9773 | 12279 | } |
| 9774 | 12280 | |
| 9775 | | -/************************* End ../ext/misc/appendvfs.c ********************/ |
| 12281 | +/************************* End ext/misc/appendvfs.c ********************/ |
| 9776 | 12282 | #endif |
| 9777 | 12283 | #ifdef SQLITE_HAVE_ZLIB |
| 9778 | | -/************************* Begin ../ext/misc/zipfile.c ******************/ |
| 12284 | +/************************* Begin ext/misc/zipfile.c ******************/ |
| 9779 | 12285 | /* |
| 9780 | 12286 | ** 2017-12-26 |
| 9781 | 12287 | ** |
| 9782 | 12288 | ** The author disclaims copyright to this source code. In place of |
| 9783 | 12289 | ** a legal notice, here is a blessing: |
| | @@ -12062,12 +14568,12 @@ |
| 12062 | 14568 | SQLITE_EXTENSION_INIT2(pApi); |
| 12063 | 14569 | (void)pzErrMsg; /* Unused parameter */ |
| 12064 | 14570 | return zipfileRegister(db); |
| 12065 | 14571 | } |
| 12066 | 14572 | |
| 12067 | | -/************************* End ../ext/misc/zipfile.c ********************/ |
| 12068 | | -/************************* Begin ../ext/misc/sqlar.c ******************/ |
| 14573 | +/************************* End ext/misc/zipfile.c ********************/ |
| 14574 | +/************************* Begin ext/misc/sqlar.c ******************/ |
| 12069 | 14575 | /* |
| 12070 | 14576 | ** 2017-12-17 |
| 12071 | 14577 | ** |
| 12072 | 14578 | ** The author disclaims copyright to this source code. In place of |
| 12073 | 14579 | ** a legal notice, here is a blessing: |
| | @@ -12191,14 +14697,14 @@ |
| 12191 | 14697 | sqlarUncompressFunc, 0, 0); |
| 12192 | 14698 | } |
| 12193 | 14699 | return rc; |
| 12194 | 14700 | } |
| 12195 | 14701 | |
| 12196 | | -/************************* End ../ext/misc/sqlar.c ********************/ |
| 14702 | +/************************* End ext/misc/sqlar.c ********************/ |
| 12197 | 14703 | #endif |
| 12198 | 14704 | #if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_AUTHORIZATION) |
| 12199 | | -/************************* Begin ../ext/expert/sqlite3expert.h ******************/ |
| 14705 | +/************************* Begin ext/expert/sqlite3expert.h ******************/ |
| 12200 | 14706 | /* |
| 12201 | 14707 | ** 2017 April 07 |
| 12202 | 14708 | ** |
| 12203 | 14709 | ** The author disclaims copyright to this source code. In place of |
| 12204 | 14710 | ** a legal notice, here is a blessing: |
| | @@ -12364,12 +14870,12 @@ |
| 12364 | 14870 | */ |
| 12365 | 14871 | void sqlite3_expert_destroy(sqlite3expert*); |
| 12366 | 14872 | |
| 12367 | 14873 | #endif /* !defined(SQLITEEXPERT_H) */ |
| 12368 | 14874 | |
| 12369 | | -/************************* End ../ext/expert/sqlite3expert.h ********************/ |
| 12370 | | -/************************* Begin ../ext/expert/sqlite3expert.c ******************/ |
| 14875 | +/************************* End ext/expert/sqlite3expert.h ********************/ |
| 14876 | +/************************* Begin ext/expert/sqlite3expert.c ******************/ |
| 12371 | 14877 | /* |
| 12372 | 14878 | ** 2017 April 09 |
| 12373 | 14879 | ** |
| 12374 | 14880 | ** The author disclaims copyright to this source code. In place of |
| 12375 | 14881 | ** a legal notice, here is a blessing: |
| | @@ -14603,13 +17109,13 @@ |
| 14603 | 17109 | } |
| 14604 | 17110 | } |
| 14605 | 17111 | |
| 14606 | 17112 | #endif /* ifndef SQLITE_OMIT_VIRTUALTABLE */ |
| 14607 | 17113 | |
| 14608 | | -/************************* End ../ext/expert/sqlite3expert.c ********************/ |
| 17114 | +/************************* End ext/expert/sqlite3expert.c ********************/ |
| 14609 | 17115 | #endif |
| 14610 | | -/************************* Begin ../ext/intck/sqlite3intck.h ******************/ |
| 17116 | +/************************* Begin ext/intck/sqlite3intck.h ******************/ |
| 14611 | 17117 | /* |
| 14612 | 17118 | ** 2024-02-08 |
| 14613 | 17119 | ** |
| 14614 | 17120 | ** The author disclaims copyright to this source code. In place of |
| 14615 | 17121 | ** a legal notice, here is a blessing: |
| | @@ -14778,12 +17284,12 @@ |
| 14778 | 17284 | } /* end of the 'extern "C"' block */ |
| 14779 | 17285 | #endif |
| 14780 | 17286 | |
| 14781 | 17287 | #endif /* ifndef _SQLITE_INTCK_H */ |
| 14782 | 17288 | |
| 14783 | | -/************************* End ../ext/intck/sqlite3intck.h ********************/ |
| 14784 | | -/************************* Begin ../ext/intck/sqlite3intck.c ******************/ |
| 17289 | +/************************* End ext/intck/sqlite3intck.h ********************/ |
| 17290 | +/************************* Begin ext/intck/sqlite3intck.c ******************/ |
| 14785 | 17291 | /* |
| 14786 | 17292 | ** 2024-02-08 |
| 14787 | 17293 | ** |
| 14788 | 17294 | ** The author disclaims copyright to this source code. In place of |
| 14789 | 17295 | ** a legal notice, here is a blessing: |
| | @@ -14942,10 +17448,11 @@ |
| 14942 | 17448 | } |
| 14943 | 17449 | }else{ |
| 14944 | 17450 | sqlite3_free(zRet); |
| 14945 | 17451 | zRet = 0; |
| 14946 | 17452 | } |
| 17453 | + va_end(ap); |
| 14947 | 17454 | return zRet; |
| 14948 | 17455 | } |
| 14949 | 17456 | |
| 14950 | 17457 | /* |
| 14951 | 17458 | ** This is used by sqlite3_intck_unlock() to save the vector key value |
| | @@ -15721,12 +18228,12 @@ |
| 15721 | 18228 | } |
| 15722 | 18229 | } |
| 15723 | 18230 | return p->zTestSql; |
| 15724 | 18231 | } |
| 15725 | 18232 | |
| 15726 | | -/************************* End ../ext/intck/sqlite3intck.c ********************/ |
| 15727 | | -/************************* Begin ../ext/misc/stmtrand.c ******************/ |
| 18233 | +/************************* End ext/intck/sqlite3intck.c ********************/ |
| 18234 | +/************************* Begin ext/misc/stmtrand.c ******************/ |
| 15728 | 18235 | /* |
| 15729 | 18236 | ** 2024-05-24 |
| 15730 | 18237 | ** |
| 15731 | 18238 | ** The author disclaims copyright to this source code. In place of |
| 15732 | 18239 | ** a legal notice, here is a blessing: |
| | @@ -15821,12 +18328,12 @@ |
| 15821 | 18328 | stmtrandFunc, 0, 0); |
| 15822 | 18329 | } |
| 15823 | 18330 | return rc; |
| 15824 | 18331 | } |
| 15825 | 18332 | |
| 15826 | | -/************************* End ../ext/misc/stmtrand.c ********************/ |
| 15827 | | -/************************* Begin ../ext/misc/vfstrace.c ******************/ |
| 18333 | +/************************* End ext/misc/stmtrand.c ********************/ |
| 18334 | +/************************* Begin ext/misc/vfstrace.c ******************/ |
| 15828 | 18335 | /* |
| 15829 | 18336 | ** 2011 March 16 |
| 15830 | 18337 | ** |
| 15831 | 18338 | ** The author disclaims copyright to this source code. In place of |
| 15832 | 18339 | ** a legal notice, here is a blessing: |
| | @@ -17035,19 +19542,19 @@ |
| 17035 | 19542 | if( pVfs->xOpen!=vfstraceOpen ) return; |
| 17036 | 19543 | sqlite3_vfs_unregister(pVfs); |
| 17037 | 19544 | sqlite3_free(pVfs); |
| 17038 | 19545 | } |
| 17039 | 19546 | |
| 17040 | | -/************************* End ../ext/misc/vfstrace.c ********************/ |
| 19547 | +/************************* End ext/misc/vfstrace.c ********************/ |
| 17041 | 19548 | |
| 17042 | 19549 | #if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB) |
| 17043 | 19550 | #define SQLITE_SHELL_HAVE_RECOVER 1 |
| 17044 | 19551 | #else |
| 17045 | 19552 | #define SQLITE_SHELL_HAVE_RECOVER 0 |
| 17046 | 19553 | #endif |
| 17047 | 19554 | #if SQLITE_SHELL_HAVE_RECOVER |
| 17048 | | -/************************* Begin ../ext/recover/sqlite3recover.h ******************/ |
| 19555 | +/************************* Begin ext/recover/sqlite3recover.h ******************/ |
| 17049 | 19556 | /* |
| 17050 | 19557 | ** 2022-08-27 |
| 17051 | 19558 | ** |
| 17052 | 19559 | ** The author disclaims copyright to this source code. In place of |
| 17053 | 19560 | ** a legal notice, here is a blessing: |
| | @@ -17294,13 +19801,13 @@ |
| 17294 | 19801 | } /* end of the 'extern "C"' block */ |
| 17295 | 19802 | #endif |
| 17296 | 19803 | |
| 17297 | 19804 | #endif /* ifndef _SQLITE_RECOVER_H */ |
| 17298 | 19805 | |
| 17299 | | -/************************* End ../ext/recover/sqlite3recover.h ********************/ |
| 19806 | +/************************* End ext/recover/sqlite3recover.h ********************/ |
| 17300 | 19807 | # ifndef SQLITE_HAVE_SQLITE3R |
| 17301 | | -/************************* Begin ../ext/recover/dbdata.c ******************/ |
| 19808 | +/************************* Begin ext/recover/dbdata.c ******************/ |
| 17302 | 19809 | /* |
| 17303 | 19810 | ** 2019-04-17 |
| 17304 | 19811 | ** |
| 17305 | 19812 | ** The author disclaims copyright to this source code. In place of |
| 17306 | 19813 | ** a legal notice, here is a blessing: |
| | @@ -18321,12 +20828,12 @@ |
| 18321 | 20828 | return sqlite3DbdataRegister(db); |
| 18322 | 20829 | } |
| 18323 | 20830 | |
| 18324 | 20831 | #endif /* ifndef SQLITE_OMIT_VIRTUALTABLE */ |
| 18325 | 20832 | |
| 18326 | | -/************************* End ../ext/recover/dbdata.c ********************/ |
| 18327 | | -/************************* Begin ../ext/recover/sqlite3recover.c ******************/ |
| 20833 | +/************************* End ext/recover/dbdata.c ********************/ |
| 20834 | +/************************* Begin ext/recover/sqlite3recover.c ******************/ |
| 18328 | 20835 | /* |
| 18329 | 20836 | ** 2022-08-27 |
| 18330 | 20837 | ** |
| 18331 | 20838 | ** The author disclaims copyright to this source code. In place of |
| 18332 | 20839 | ** a legal notice, here is a blessing: |
| | @@ -21225,11 +23732,11 @@ |
| 21225 | 23732 | return rc; |
| 21226 | 23733 | } |
| 21227 | 23734 | |
| 21228 | 23735 | #endif /* ifndef SQLITE_OMIT_VIRTUALTABLE */ |
| 21229 | 23736 | |
| 21230 | | -/************************* End ../ext/recover/sqlite3recover.c ********************/ |
| 23737 | +/************************* End ext/recover/sqlite3recover.c ********************/ |
| 21231 | 23738 | # endif /* SQLITE_HAVE_SQLITE3R */ |
| 21232 | 23739 | #endif |
| 21233 | 23740 | #ifdef SQLITE_SHELL_EXTSRC |
| 21234 | 23741 | # include SHELL_STRINGIFY(SQLITE_SHELL_EXTSRC) |
| 21235 | 23742 | #endif |
| | @@ -21253,97 +23760,78 @@ |
| 21253 | 23760 | sqlite3expert *pExpert; |
| 21254 | 23761 | int bVerbose; |
| 21255 | 23762 | }; |
| 21256 | 23763 | #endif |
| 21257 | 23764 | |
| 21258 | | -/* A single line in the EQP output */ |
| 21259 | | -typedef struct EQPGraphRow EQPGraphRow; |
| 21260 | | -struct EQPGraphRow { |
| 21261 | | - int iEqpId; /* ID for this row */ |
| 21262 | | - int iParentId; /* ID of the parent row */ |
| 21263 | | - EQPGraphRow *pNext; /* Next row in sequence */ |
| 21264 | | - char zText[1]; /* Text to display for this row */ |
| 21265 | | -}; |
| 21266 | | - |
| 21267 | | -/* All EQP output is collected into an instance of the following */ |
| 21268 | | -typedef struct EQPGraph EQPGraph; |
| 21269 | | -struct EQPGraph { |
| 21270 | | - EQPGraphRow *pRow; /* Linked list of all rows of the EQP output */ |
| 21271 | | - EQPGraphRow *pLast; /* Last element of the pRow list */ |
| 21272 | | - char zPrefix[100]; /* Graph prefix */ |
| 21273 | | -}; |
| 21274 | | - |
| 21275 | | -/* Parameters affecting columnar mode result display (defaulting together) */ |
| 21276 | | -typedef struct ColModeOpts { |
| 21277 | | - int iWrap; /* In columnar modes, wrap lines reaching this limit */ |
| 21278 | | - u8 bQuote; /* Quote results for .mode box and table */ |
| 21279 | | - u8 bWordWrap; /* In columnar modes, wrap at word boundaries */ |
| 21280 | | -} ColModeOpts; |
| 21281 | | -#define ColModeOpts_default { 60, 0, 0 } |
| 21282 | | -#define ColModeOpts_default_qbox { 60, 1, 0 } |
| 23765 | +/* All the parameters that determine how to render query results. |
| 23766 | +*/ |
| 23767 | +typedef struct Mode { |
| 23768 | + u8 autoExplain; /* Automatically turn on .explain mode */ |
| 23769 | + u8 autoEQP; /* Run EXPLAIN QUERY PLAN prior to each SQL stmt */ |
| 23770 | + u8 autoEQPtrace; /* autoEQP is in trace mode */ |
| 23771 | + u8 scanstatsOn; /* True to display scan stats before each finalize */ |
| 23772 | + u8 bAutoScreenWidth; /* Using the TTY to determine screen width */ |
| 23773 | + u8 mFlags; /* MFLG_ECHO and/or MFLG_CRLF */ |
| 23774 | + u8 eMode; /* One of the MODE_ values */ |
| 23775 | + sqlite3_qrf_spec spec; /* Spec to be passed into QRF */ |
| 23776 | +} Mode; |
| 23777 | + |
| 23778 | +/* Flags for Mode.mFlags */ |
| 23779 | +#define MFLG_ECHO 0x01 /* Echo inputs to output */ |
| 23780 | +#define MFLG_CRLF 0x02 /* Use CR/LF output line endings */ |
| 23781 | + |
| 21283 | 23782 | |
| 21284 | 23783 | /* |
| 21285 | 23784 | ** State information about the database connection is contained in an |
| 21286 | 23785 | ** instance of the following structure. |
| 21287 | 23786 | */ |
| 21288 | 23787 | typedef struct ShellState ShellState; |
| 21289 | 23788 | struct ShellState { |
| 21290 | 23789 | sqlite3 *db; /* The database */ |
| 21291 | | - u8 autoExplain; /* Automatically turn on .explain mode */ |
| 21292 | | - u8 autoEQP; /* Run EXPLAIN QUERY PLAN prior to each SQL stmt */ |
| 21293 | | - u8 autoEQPtest; /* autoEQP is in test mode */ |
| 21294 | | - u8 autoEQPtrace; /* autoEQP is in trace mode */ |
| 21295 | | - u8 scanstatsOn; /* True to display scan stats before each finalize */ |
| 23790 | + int iCompat; /* Compatibility date YYYYMMDD */ |
| 21296 | 23791 | u8 openMode; /* SHELL_OPEN_NORMAL, _APPENDVFS, or _ZIPFILE */ |
| 21297 | 23792 | u8 doXdgOpen; /* Invoke start/open/xdg-open in output_reset() */ |
| 21298 | 23793 | u8 nEqpLevel; /* Depth of the EQP output graph */ |
| 21299 | 23794 | u8 eTraceType; /* SHELL_TRACE_* value for type of trace */ |
| 21300 | 23795 | u8 bSafeMode; /* True to prohibit unsafe operations */ |
| 21301 | 23796 | u8 bSafeModePersist; /* The long-term value of bSafeMode */ |
| 21302 | 23797 | u8 eRestoreState; /* See comments above doAutoDetectRestore() */ |
| 21303 | | - u8 crlfMode; /* Do NL-to-CRLF translations when enabled (maybe) */ |
| 21304 | | - u8 eEscMode; /* Escape mode for text output */ |
| 21305 | | - ColModeOpts cmOpts; /* Option values affecting columnar mode output */ |
| 21306 | 23798 | unsigned statsOn; /* True to display memory stats before each finalize */ |
| 21307 | 23799 | unsigned mEqpLines; /* Mask of vertical lines in the EQP output graph */ |
| 23800 | + u8 nPopOutput; /* Revert .output settings when reaching zero */ |
| 23801 | + u8 nPopMode; /* Revert .mode settings when reaching zero */ |
| 21308 | 23802 | int inputNesting; /* Track nesting level of .read and other redirects */ |
| 21309 | | - int outCount; /* Revert to stdout when reaching zero */ |
| 21310 | | - int cnt; /* Number of records displayed so far */ |
| 21311 | 23803 | i64 lineno; /* Line number of last line read from in */ |
| 23804 | + const char *zInFile; /* Name of the input file */ |
| 21312 | 23805 | int openFlags; /* Additional flags to open. (SQLITE_OPEN_NOFOLLOW) */ |
| 21313 | 23806 | FILE *in; /* Read commands from this stream */ |
| 21314 | 23807 | FILE *out; /* Write results here */ |
| 21315 | 23808 | FILE *traceOut; /* Output for sqlite3_trace() */ |
| 21316 | 23809 | int nErr; /* Number of errors seen */ |
| 21317 | | - int mode; /* An output mode setting */ |
| 21318 | | - int modePrior; /* Saved mode */ |
| 21319 | | - int cMode; /* temporary output mode for the current query */ |
| 21320 | | - int normalMode; /* Output mode before ".explain on" */ |
| 21321 | 23810 | int writableSchema; /* True if PRAGMA writable_schema=ON */ |
| 21322 | | - int showHeader; /* True to show column names in List or Column mode */ |
| 21323 | 23811 | int nCheck; /* Number of ".check" commands run */ |
| 21324 | 23812 | unsigned nProgress; /* Number of progress callbacks encountered */ |
| 21325 | 23813 | unsigned mxProgress; /* Maximum progress callbacks before failing */ |
| 21326 | 23814 | unsigned flgProgress; /* Flags for the progress callback */ |
| 21327 | 23815 | unsigned shellFlgs; /* Various flags */ |
| 21328 | | - unsigned priorShFlgs; /* Saved copy of flags */ |
| 23816 | + unsigned nTestRun; /* Number of test cases run */ |
| 23817 | + unsigned nTestErr; /* Number of test cases that failed */ |
| 21329 | 23818 | sqlite3_int64 szMax; /* --maxsize argument to .open */ |
| 21330 | 23819 | char *zDestTable; /* Name of destination table when MODE_Insert */ |
| 21331 | 23820 | char *zTempFile; /* Temporary file that might need deleting */ |
| 23821 | + char *zErrPrefix; /* Alternative error message prefix */ |
| 21332 | 23822 | char zTestcase[30]; /* Name of current test case */ |
| 21333 | | - char colSeparator[20]; /* Column separator character for several modes */ |
| 21334 | | - char rowSeparator[20]; /* Row separator character for MODE_Ascii */ |
| 21335 | | - char colSepPrior[20]; /* Saved column separator */ |
| 21336 | | - char rowSepPrior[20]; /* Saved row separator */ |
| 21337 | | - int *colWidth; /* Requested width of each column in columnar modes */ |
| 21338 | | - int *actualWidth; /* Actual width of each column */ |
| 21339 | | - int nWidth; /* Number of slots in colWidth[] and actualWidth[] */ |
| 21340 | | - char nullValue[20]; /* The text to print when a NULL comes back from |
| 21341 | | - ** the database */ |
| 21342 | 23823 | char outfile[FILENAME_MAX]; /* Filename for *out */ |
| 21343 | 23824 | sqlite3_stmt *pStmt; /* Current statement if any. */ |
| 21344 | 23825 | FILE *pLog; /* Write log output here */ |
| 23826 | + Mode mode; /* Current display mode */ |
| 23827 | + Mode modePrior; /* Backup */ |
| 23828 | + struct SavedMode { /* Ability to define custom mode configurations */ |
| 23829 | + char *zTag; /* Name of this saved mode */ |
| 23830 | + Mode mode; /* The saved mode */ |
| 23831 | + } *aSavedModes; /* Array of saved .mode settings. system malloc() */ |
| 23832 | + int nSavedModes; /* Number of saved .mode settings */ |
| 21345 | 23833 | struct AuxDb { /* Storage space for auxiliary database connections */ |
| 21346 | 23834 | sqlite3 *db; /* Connection pointer */ |
| 21347 | 23835 | const char *zDbFilename; /* Filename used to open the connection */ |
| 21348 | 23836 | char *zFreeOnClose; /* Free this memory allocation on close */ |
| 21349 | 23837 | #if defined(SQLITE_ENABLE_SESSION) |
| | @@ -21350,18 +23838,23 @@ |
| 21350 | 23838 | int nSession; /* Number of active sessions */ |
| 21351 | 23839 | OpenSession aSession[4]; /* Array of sessions. [0] is in focus. */ |
| 21352 | 23840 | #endif |
| 21353 | 23841 | } aAuxDb[5], /* Array of all database connections */ |
| 21354 | 23842 | *pAuxDb; /* Currently active database connection */ |
| 21355 | | - int *aiIndent; /* Array of indents used in MODE_Explain */ |
| 21356 | | - int nIndent; /* Size of array aiIndent[] */ |
| 21357 | | - int iIndent; /* Index of current op in aiIndent[] */ |
| 21358 | 23843 | char *zNonce; /* Nonce for temporary safe-mode escapes */ |
| 21359 | | - EQPGraph sGraph; /* Information for the graphical EXPLAIN QUERY PLAN */ |
| 21360 | 23844 | #if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_AUTHORIZATION) |
| 21361 | 23845 | ExpertInfo expert; /* Valid if previous command was ".expert OPT..." */ |
| 21362 | 23846 | #endif |
| 23847 | + struct DotCmdLine { /* Info about arguments to a dot-command */ |
| 23848 | + const char *zOrig; /* Original text of the dot-command */ |
| 23849 | + char *zCopy; /* Copy of zOrig, from malloc() */ |
| 23850 | + int nAlloc; /* Size of allocates for arrays below */ |
| 23851 | + int nArg; /* Number of argument slots actually used */ |
| 23852 | + char **azArg; /* Pointer to each argument, dequoted */ |
| 23853 | + int *aiOfst; /* Offset into zOrig[] for start of each arg */ |
| 23854 | + char *abQuot; /* True if the argment was originally quoted */ |
| 23855 | + } dot; |
| 21363 | 23856 | #ifdef SQLITE_SHELL_FIDDLE |
| 21364 | 23857 | struct { |
| 21365 | 23858 | const char * zInput; /* Input string from wasm/JS proxy */ |
| 21366 | 23859 | const char * zPos; /* Cursor pos into zInput */ |
| 21367 | 23860 | const char * zDefaultDbName; /* Default name for db file */ |
| | @@ -21372,11 +23865,11 @@ |
| 21372 | 23865 | #ifdef SQLITE_SHELL_FIDDLE |
| 21373 | 23866 | static ShellState shellState; |
| 21374 | 23867 | #endif |
| 21375 | 23868 | |
| 21376 | 23869 | |
| 21377 | | -/* Allowed values for ShellState.autoEQP |
| 23870 | +/* Allowed values for ShellState.mode.autoEQP |
| 21378 | 23871 | */ |
| 21379 | 23872 | #define AUTOEQP_off 0 /* Automatic EXPLAIN QUERY PLAN is off */ |
| 21380 | 23873 | #define AUTOEQP_on 1 /* Automatic EQP is on */ |
| 21381 | 23874 | #define AUTOEQP_trigger 2 /* On and also show plans for triggers */ |
| 21382 | 23875 | #define AUTOEQP_full 3 /* Show full EXPLAIN */ |
| | @@ -21401,30 +23894,25 @@ |
| 21401 | 23894 | #define SHELL_PROGRESS_RESET 0x02 /* Reset the count when the progress |
| 21402 | 23895 | ** callback limit is reached, and for each |
| 21403 | 23896 | ** top-level SQL statement */ |
| 21404 | 23897 | #define SHELL_PROGRESS_ONCE 0x04 /* Cancel the --limit after firing once */ |
| 21405 | 23898 | |
| 21406 | | -/* Allowed values for ShellState.eEscMode. The default value should |
| 21407 | | -** be 0, so to change the default, reorder the names. |
| 23899 | +/* Names of values for Mode.spec.eEsc and Mode.spec.eText |
| 21408 | 23900 | */ |
| 21409 | | -#define SHELL_ESC_ASCII 0 /* Substitute ^Y for X where Y=X+0x40 */ |
| 21410 | | -#define SHELL_ESC_SYMBOL 1 /* Substitute U+2400 graphics */ |
| 21411 | | -#define SHELL_ESC_OFF 2 /* Send characters verbatim */ |
| 21412 | | - |
| 21413 | | -static const char *shell_EscModeNames[] = { "ascii", "symbol", "off" }; |
| 23901 | +static const char *qrfEscNames[] = { "auto", "off", "ascii", "symbol" }; |
| 23902 | +static const char *qrfQuoteNames[] = |
| 23903 | + { "off","off","sql","hex","csv","tcl","json"}; |
| 21414 | 23904 | |
| 21415 | 23905 | /* |
| 21416 | 23906 | ** These are the allowed shellFlgs values |
| 21417 | 23907 | */ |
| 21418 | 23908 | #define SHFLG_Pagecache 0x00000001 /* The --pagecache option is used */ |
| 21419 | 23909 | #define SHFLG_Lookaside 0x00000002 /* Lookaside memory is used */ |
| 21420 | 23910 | #define SHFLG_Backslash 0x00000004 /* The --backslash option is used */ |
| 21421 | 23911 | #define SHFLG_PreserveRowid 0x00000008 /* .dump preserves rowid values */ |
| 21422 | | -#define SHFLG_Newlines 0x00000010 /* .dump --newline flag */ |
| 23912 | +#define SHFLG_NoErrLineno 0x00000010 /* Omit line numbers from error msgs */ |
| 21423 | 23913 | #define SHFLG_CountChanges 0x00000020 /* .changes setting */ |
| 21424 | | -#define SHFLG_Echo 0x00000040 /* .echo on/off, or --echo setting */ |
| 21425 | | -#define SHFLG_HeaderSet 0x00000080 /* showHeader has been specified */ |
| 21426 | 23914 | #define SHFLG_DumpDataOnly 0x00000100 /* .dump show data only */ |
| 21427 | 23915 | #define SHFLG_DumpNoSys 0x00000200 /* .dump omits system tables */ |
| 21428 | 23916 | #define SHFLG_TestingMode 0x00000400 /* allow unsafe testing features */ |
| 21429 | 23917 | |
| 21430 | 23918 | /* |
| | @@ -21433,58 +23921,105 @@ |
| 21433 | 23921 | #define ShellHasFlag(P,X) (((P)->shellFlgs & (X))!=0) |
| 21434 | 23922 | #define ShellSetFlag(P,X) ((P)->shellFlgs|=(X)) |
| 21435 | 23923 | #define ShellClearFlag(P,X) ((P)->shellFlgs&=(~(X))) |
| 21436 | 23924 | |
| 21437 | 23925 | /* |
| 21438 | | -** These are the allowed modes. |
| 23926 | +** These are the allowed values for Mode.eMode. There is a lot of overlap |
| 23927 | +** between these values and the Mode.spec.eStyle values, but they are not |
| 23928 | +** one-to-one, and thus need to be tracked separately. |
| 21439 | 23929 | */ |
| 21440 | | -#define MODE_Line 0 /* One column per line. Blank line between records */ |
| 21441 | | -#define MODE_Column 1 /* One record per line in neat columns */ |
| 21442 | | -#define MODE_List 2 /* One record per line with a separator */ |
| 21443 | | -#define MODE_Semi 3 /* Same as MODE_List but append ";" to each line */ |
| 21444 | | -#define MODE_Html 4 /* Generate an XHTML table */ |
| 21445 | | -#define MODE_Insert 5 /* Generate SQL "insert" statements */ |
| 21446 | | -#define MODE_Quote 6 /* Quote values as for SQL */ |
| 21447 | | -#define MODE_Tcl 7 /* Generate ANSI-C or TCL quoted elements */ |
| 21448 | | -#define MODE_Csv 8 /* Quote strings, numbers are plain */ |
| 21449 | | -#define MODE_Explain 9 /* Like MODE_Column, but do not truncate data */ |
| 21450 | | -#define MODE_Ascii 10 /* Use ASCII unit and record separators (0x1F/0x1E) */ |
| 21451 | | -#define MODE_Pretty 11 /* Pretty-print schemas */ |
| 21452 | | -#define MODE_EQP 12 /* Converts EXPLAIN QUERY PLAN output into a graph */ |
| 21453 | | -#define MODE_Json 13 /* Output JSON */ |
| 21454 | | -#define MODE_Markdown 14 /* Markdown formatting */ |
| 21455 | | -#define MODE_Table 15 /* MySQL-style table formatting */ |
| 21456 | | -#define MODE_Box 16 /* Unicode box-drawing characters */ |
| 21457 | | -#define MODE_Count 17 /* Output only a count of the rows of output */ |
| 21458 | | -#define MODE_Off 18 /* No query output shown */ |
| 21459 | | -#define MODE_ScanExp 19 /* Like MODE_Explain, but for ".scanstats vm" */ |
| 21460 | | -#define MODE_Www 20 /* Full web-page output */ |
| 21461 | | - |
| 21462 | | -static const char *modeDescr[] = { |
| 21463 | | - "line", |
| 21464 | | - "column", |
| 21465 | | - "list", |
| 21466 | | - "semi", |
| 21467 | | - "html", |
| 21468 | | - "insert", |
| 21469 | | - "quote", |
| 21470 | | - "tcl", |
| 21471 | | - "csv", |
| 21472 | | - "explain", |
| 21473 | | - "ascii", |
| 21474 | | - "prettyprint", |
| 21475 | | - "eqp", |
| 21476 | | - "json", |
| 21477 | | - "markdown", |
| 21478 | | - "table", |
| 21479 | | - "box", |
| 21480 | | - "count", |
| 21481 | | - "off", |
| 21482 | | - "scanexp", |
| 21483 | | - "www", |
| 23930 | +#define MODE_Ascii 0 /* Use ASCII unit and record separators (0x1F/0x1E) */ |
| 23931 | +#define MODE_Box 1 /* Unicode box-drawing characters */ |
| 23932 | +#define MODE_C 2 /* Comma-separated list of C-strings */ |
| 23933 | +#define MODE_Column 3 /* One record per line in neat columns */ |
| 23934 | +#define MODE_Count 4 /* Output only a count of the rows of output */ |
| 23935 | +#define MODE_Csv 5 /* Quote strings, numbers are plain */ |
| 23936 | +#define MODE_Html 6 /* Generate an XHTML table */ |
| 23937 | +#define MODE_Insert 7 /* Generate SQL "insert" statements */ |
| 23938 | +#define MODE_JAtom 8 /* Comma-separated list of JSON atoms */ |
| 23939 | +#define MODE_JObject 9 /* One JSON object per row */ |
| 23940 | +#define MODE_Json 10 /* Output JSON */ |
| 23941 | +#define MODE_Line 11 /* One column per line. Blank line between records */ |
| 23942 | +#define MODE_List 12 /* One record per line with a separator */ |
| 23943 | +#define MODE_Markdown 13 /* Markdown formatting */ |
| 23944 | +#define MODE_Off 14 /* No query output shown */ |
| 23945 | +#define MODE_QBox 15 /* BOX with SQL-quoted content */ |
| 23946 | +#define MODE_Quote 16 /* Quote values as for SQL */ |
| 23947 | +#define MODE_Table 17 /* MySQL-style table formatting */ |
| 23948 | +#define MODE_Tabs 18 /* Tab-separated values */ |
| 23949 | +#define MODE_Tcl 19 /* Space-separated list of TCL strings */ |
| 23950 | +#define MODE_Www 20 /* Full web-page output */ |
| 23951 | + |
| 23952 | +#define MODE_BUILTIN 20 /* Maximum built-in mode */ |
| 23953 | +#define MODE_BATCH 50 /* Default mode for batch processing */ |
| 23954 | +#define MODE_TTY 51 /* Default mode for interactive processing */ |
| 23955 | +#define MODE_USER 75 /* First user-defined mode */ |
| 23956 | +#define MODE_N_USER 25 /* Maximum number of user-defined modes */ |
| 23957 | + |
| 23958 | +/* |
| 23959 | +** Information about built-in display modes |
| 23960 | +*/ |
| 23961 | +typedef struct ModeInfo ModeInfo; |
| 23962 | +struct ModeInfo { |
| 23963 | + char zName[9]; /* Symbolic name of the mode */ |
| 23964 | + unsigned char eCSep; /* Column separator */ |
| 23965 | + unsigned char eRSep; /* Row separator */ |
| 23966 | + unsigned char eNull; /* Null representation */ |
| 23967 | + unsigned char eText; /* Default text encoding */ |
| 23968 | + unsigned char eHdr; /* Default header encoding. */ |
| 23969 | + unsigned char eBlob; /* Default blob encoding. */ |
| 23970 | + unsigned char bHdr; /* Show headers by default. 0: n/a, 1: no 2: yes */ |
| 23971 | + unsigned char eStyle; /* Underlying QRF style */ |
| 23972 | + unsigned char eCx; /* 0: other, 1: line, 2: columnar */ |
| 21484 | 23973 | }; |
| 21485 | 23974 | |
| 23975 | +/* String constants used by built-in modes */ |
| 23976 | +static const char *aModeStr[] = |
| 23977 | + /* 0 1 2 3 4 5 6 7 8 */ |
| 23978 | + { 0, "\n", "|", " ", ",", "\r\n", "\036", "\037", "\t", |
| 23979 | + "", "NULL", "null", "\"\"" }; |
| 23980 | + /* 9 10 11 12 */ |
| 23981 | + |
| 23982 | +static const ModeInfo aModeInfo[] = { |
| 23983 | +/* zName eCSep eRSep eNull eText eHdr eBlob bHdr eStyle eCx */ |
| 23984 | + { "ascii", 7, 6, 9, 1, 1, 1, 1, 12, 0 }, |
| 23985 | + { "box", 0, 0, 9, 1, 1, 1, 2, 1, 2 }, |
| 23986 | + { "c", 4, 1, 10, 5, 5, 4, 1, 12, 0 }, |
| 23987 | + { "column", 0, 0, 9, 1, 1, 1, 2, 2, 2 }, |
| 23988 | + { "count", 0, 0, 0, 0, 0, 0, 0, 3, 0 }, |
| 23989 | + { "csv", 4, 5, 9, 3, 3, 3, 1, 12, 0 }, |
| 23990 | + { "html", 0, 0, 9, 4, 4, 1, 2, 7, 0 }, |
| 23991 | + { "insert", 0, 0, 10, 2, 2, 2, 1, 8, 0 }, |
| 23992 | + { "jatom", 4, 1, 11, 6, 6, 5, 1, 12, 0 }, |
| 23993 | + { "jobject", 0, 1, 11, 6, 6, 5, 0, 10, 0 }, |
| 23994 | + { "json", 0, 0, 11, 6, 6, 0, 0, 9, 0 }, |
| 23995 | + { "line", 0, 1, 9, 1, 1, 0, 0, 11, 1 }, |
| 23996 | + { "list", 2, 1, 9, 1, 1, 1, 1, 12, 0 }, |
| 23997 | + { "markdown", 0, 0, 9, 1, 1, 1, 2, 13, 2 }, |
| 23998 | + { "off", 0, 0, 0, 0, 0, 0, 0, 14, 0 }, |
| 23999 | + { "qbox", 0, 0, 9, 2, 1, 2, 2, 1, 2 }, |
| 24000 | + { "quote", 4, 1, 10, 2, 2, 2, 1, 12, 0 }, |
| 24001 | + { "table", 0, 0, 9, 1, 1, 1, 2, 19, 2 }, |
| 24002 | + { "tabs", 8, 1, 9, 3, 3, 1, 1, 12, 0 }, |
| 24003 | + { "tcl", 3, 1, 12, 5, 5, 4, 1, 12, 0 }, |
| 24004 | + { "www", 0, 0, 9, 4, 4, 1, 2, 7, 0 } |
| 24005 | +}; /* | / / | / / | | \ |
| 24006 | + ** | / / | / / | | \_ 2: columnar |
| 24007 | + ** Index into aModeStr[] | / / | | 1: line |
| 24008 | + ** | / / | | 0: other |
| 24009 | + ** | / / | \ |
| 24010 | + ** text encoding |/ | show | \ |
| 24011 | + ** v-------------------' | hdrs? | The QRF style |
| 24012 | + ** 0: n/a blob | v-----' |
| 24013 | + ** 1: plain v_---------' 0: n/a |
| 24014 | + ** 2: sql 0: n/a 1: no |
| 24015 | + ** 3: csv 1: as-text 2: yes |
| 24016 | + ** 4: html 2: sql |
| 24017 | + ** 5: c 3: hex |
| 24018 | + ** 6: json 4: c |
| 24019 | + ** 5: json |
| 24020 | + ******************************************************************/ |
| 21486 | 24021 | /* |
| 21487 | 24022 | ** These are the column/row/line separators used by the various |
| 21488 | 24023 | ** import/export modes. |
| 21489 | 24024 | */ |
| 21490 | 24025 | #define SEP_Column "|" |
| | @@ -21499,18 +24034,183 @@ |
| 21499 | 24034 | /* |
| 21500 | 24035 | ** Limit input nesting via .read or any other input redirect. |
| 21501 | 24036 | ** It's not too expensive, so a generous allowance can be made. |
| 21502 | 24037 | */ |
| 21503 | 24038 | #define MAX_INPUT_NESTING 25 |
| 24039 | + |
| 24040 | + |
| 24041 | +/* |
| 24042 | +** Clear a display mode, freeing any allocated memory that it |
| 24043 | +** contains. |
| 24044 | +*/ |
| 24045 | +static void modeFree(Mode *p){ |
| 24046 | + free(p->spec.aWidth); |
| 24047 | + free(p->spec.aAlign); |
| 24048 | + free(p->spec.zColumnSep); |
| 24049 | + free(p->spec.zRowSep); |
| 24050 | + free(p->spec.zTableName); |
| 24051 | + free(p->spec.zNull); |
| 24052 | + memset(p, 0, sizeof(*p)); |
| 24053 | + p->spec.iVersion = 1; |
| 24054 | +} |
| 24055 | + |
| 24056 | +/* |
| 24057 | +** Duplicate Mode pSrc into pDest. pDest is assumed to be |
| 24058 | +** uninitialized prior to invoking this routine. |
| 24059 | +*/ |
| 24060 | +static void modeDup(Mode *pDest, Mode *pSrc){ |
| 24061 | + memcpy(pDest, pSrc, sizeof(*pDest)); |
| 24062 | + if( pDest->spec.aWidth ){ |
| 24063 | + size_t sz = sizeof(pSrc->spec.aWidth[0]) * pSrc->spec.nWidth; |
| 24064 | + pDest->spec.aWidth = malloc( sz ); |
| 24065 | + if( pDest->spec.aWidth ){ |
| 24066 | + memcpy(pDest->spec.aWidth, pSrc->spec.aWidth, sz); |
| 24067 | + }else{ |
| 24068 | + pDest->spec.nWidth = 0; |
| 24069 | + } |
| 24070 | + } |
| 24071 | + if( pDest->spec.aAlign ){ |
| 24072 | + size_t sz = sizeof(pSrc->spec.aAlign[0]) * pSrc->spec.nAlign; |
| 24073 | + pDest->spec.aAlign = malloc( sz ); |
| 24074 | + if( pDest->spec.aAlign ){ |
| 24075 | + memcpy(pDest->spec.aAlign, pSrc->spec.aAlign, sz); |
| 24076 | + }else{ |
| 24077 | + pDest->spec.nAlign = 0; |
| 24078 | + } |
| 24079 | + } |
| 24080 | + if( pDest->spec.zColumnSep ){ |
| 24081 | + pDest->spec.zColumnSep = strdup(pSrc->spec.zColumnSep); |
| 24082 | + } |
| 24083 | + if( pDest->spec.zRowSep ){ |
| 24084 | + pDest->spec.zRowSep = strdup(pSrc->spec.zRowSep); |
| 24085 | + } |
| 24086 | + if( pDest->spec.zTableName ){ |
| 24087 | + pDest->spec.zTableName = strdup(pSrc->spec.zTableName); |
| 24088 | + } |
| 24089 | + if( pDest->spec.zNull ){ |
| 24090 | + pDest->spec.zNull = strdup(pSrc->spec.zNull); |
| 24091 | + } |
| 24092 | +} |
| 24093 | + |
| 24094 | +/* |
| 24095 | +** Set a string value to a copy of the zNew string in memory |
| 24096 | +** obtained from system malloc(). |
| 24097 | +*/ |
| 24098 | +static void modeSetStr(char **az, const char *zNew){ |
| 24099 | + free(*az); |
| 24100 | + if( zNew==0 ){ |
| 24101 | + *az = 0; |
| 24102 | + }else{ |
| 24103 | + size_t n = strlen(zNew); |
| 24104 | + *az = malloc( n+1 ); |
| 24105 | + if( *az ){ |
| 24106 | + memcpy(*az, zNew, n+1 ); |
| 24107 | + } |
| 24108 | + } |
| 24109 | +} |
| 24110 | + |
| 24111 | +/* |
| 24112 | +** Change the mode to eMode |
| 24113 | +*/ |
| 24114 | +static void modeChange(ShellState *p, unsigned char eMode){ |
| 24115 | + const ModeInfo *pI; |
| 24116 | + if( eMode<ArraySize(aModeInfo) ){ |
| 24117 | + pI = &aModeInfo[eMode]; |
| 24118 | + Mode *pM = &p->mode; |
| 24119 | + pM->eMode = eMode; |
| 24120 | + if( pI->eCSep ) modeSetStr(&pM->spec.zColumnSep, aModeStr[pI->eCSep]); |
| 24121 | + if( pI->eRSep ) modeSetStr(&pM->spec.zRowSep, aModeStr[pI->eRSep]); |
| 24122 | + if( pI->eNull ) modeSetStr(&pM->spec.zNull, aModeStr[pI->eNull]); |
| 24123 | + pM->spec.eText = pI->eText; |
| 24124 | + pM->spec.eBlob = pI->eBlob; |
| 24125 | + pM->spec.bTitles = pI->bHdr; |
| 24126 | + pM->spec.eTitle = pI->eHdr; |
| 24127 | + }else if( eMode>=MODE_USER && eMode-MODE_USER<p->nSavedModes ){ |
| 24128 | + modeFree(&p->mode); |
| 24129 | + modeDup(&p->mode, &p->aSavedModes[eMode-MODE_USER].mode); |
| 24130 | + }else if( eMode==MODE_BATCH ){ |
| 24131 | + u8 mFlags = p->mode.mFlags; |
| 24132 | + modeFree(&p->mode); |
| 24133 | + modeChange(p, MODE_List); |
| 24134 | + p->mode.mFlags = mFlags; |
| 24135 | + }else if( eMode==MODE_TTY ){ |
| 24136 | + u8 mFlags = p->mode.mFlags; |
| 24137 | + modeFree(&p->mode); |
| 24138 | + modeChange(p, MODE_QBox); |
| 24139 | + p->mode.bAutoScreenWidth = 1; |
| 24140 | + p->mode.spec.nCharLimit = 300; |
| 24141 | + p->mode.spec.nLineLimit = 5; |
| 24142 | + p->mode.spec.bTextJsonb = QRF_Yes; |
| 24143 | + p->mode.mFlags = mFlags; |
| 24144 | + } |
| 24145 | +} |
| 24146 | + |
| 24147 | +/* |
| 24148 | +** Set the mode to the default according to p->iCompat. It assumed |
| 24149 | +** that the mode has already been freed and zeroed prior to calling |
| 24150 | +** this routine. |
| 24151 | +*/ |
| 24152 | +static void modeDefault(ShellState *p){ |
| 24153 | + p->mode.spec.iVersion = 1; |
| 24154 | + p->mode.autoExplain = 1; |
| 24155 | + if( p->iCompat>=20251115 && (stdin_is_interactive || stdout_is_console) ){ |
| 24156 | + modeChange(p, MODE_TTY); |
| 24157 | + }else{ |
| 24158 | + modeChange(p, MODE_BATCH); |
| 24159 | + } |
| 24160 | +} |
| 24161 | + |
| 24162 | +/* |
| 24163 | +** Find the number of a display mode given its name. Return -1 if |
| 24164 | +** the name does not match any mode. |
| 24165 | +** |
| 24166 | +** Saved modes are also searched if p!=NULL. The number returned |
| 24167 | +** for a saved mode is the index into the p->aSavedModes[] array |
| 24168 | +** plus MODE_USER. |
| 24169 | +** |
| 24170 | +** Two special mode names are also available: "batch" and "tty". |
| 24171 | +** evaluate to the default mode for batch operation and interactive |
| 24172 | +** operation on a TTY, respectively. |
| 24173 | +*/ |
| 24174 | +static int modeFind(ShellState *p, const char *zName){ |
| 24175 | + int i; |
| 24176 | + for(i=0; i<ArraySize(aModeInfo); i++){ |
| 24177 | + if( cli_strcmp(aModeInfo[i].zName,zName)==0 ) return i; |
| 24178 | + } |
| 24179 | + for(i=0; i<p->nSavedModes; i++){ |
| 24180 | + if( cli_strcmp(p->aSavedModes[i].zTag,zName)==0 ) return i+MODE_USER; |
| 24181 | + } |
| 24182 | + if( strcmp(zName,"batch")==0 ) return MODE_BATCH; |
| 24183 | + if( strcmp(zName,"tty")==0 ) return MODE_TTY; |
| 24184 | + return -1; |
| 24185 | +} |
| 24186 | + |
| 24187 | +/* |
| 24188 | +** Save or restore the current output mode |
| 24189 | +*/ |
| 24190 | +static void modePush(ShellState *p){ |
| 24191 | + if( p->nPopMode==0 ){ |
| 24192 | + modeFree(&p->modePrior); |
| 24193 | + modeDup(&p->modePrior,&p->mode); |
| 24194 | + } |
| 24195 | +} |
| 24196 | +static void modePop(ShellState *p){ |
| 24197 | + if( p->modePrior.spec.iVersion>0 ){ |
| 24198 | + modeFree(&p->mode); |
| 24199 | + p->mode = p->modePrior; |
| 24200 | + memset(&p->modePrior, 0, sizeof(p->modePrior)); |
| 24201 | + } |
| 24202 | +} |
| 24203 | + |
| 21504 | 24204 | |
| 21505 | 24205 | /* |
| 21506 | 24206 | ** A callback for the sqlite3_log() interface. |
| 21507 | 24207 | */ |
| 21508 | 24208 | static void shellLog(void *pArg, int iErrCode, const char *zMsg){ |
| 21509 | 24209 | ShellState *p = (ShellState*)pArg; |
| 21510 | 24210 | if( p->pLog==0 ) return; |
| 21511 | | - sqlite3_fprintf(p->pLog, "(%d) %s\n", iErrCode, zMsg); |
| 24211 | + cli_printf(p->pLog, "(%d) %s\n", iErrCode, zMsg); |
| 21512 | 24212 | fflush(p->pLog); |
| 21513 | 24213 | } |
| 21514 | 24214 | |
| 21515 | 24215 | /* |
| 21516 | 24216 | ** SQL function: shell_putsnl(X) |
| | @@ -21523,13 +24223,29 @@ |
| 21523 | 24223 | int nVal, |
| 21524 | 24224 | sqlite3_value **apVal |
| 21525 | 24225 | ){ |
| 21526 | 24226 | ShellState *p = (ShellState*)sqlite3_user_data(pCtx); |
| 21527 | 24227 | (void)nVal; |
| 21528 | | - sqlite3_fprintf(p->out, "%s\n", sqlite3_value_text(apVal[0])); |
| 24228 | + cli_printf(p->out, "%s\n", sqlite3_value_text(apVal[0])); |
| 21529 | 24229 | sqlite3_result_value(pCtx, apVal[0]); |
| 21530 | 24230 | } |
| 24231 | + |
| 24232 | +/* |
| 24233 | +** Compute the name of the location of an input error in memory |
| 24234 | +** obtained from sqlite3_malloc(). |
| 24235 | +*/ |
| 24236 | +static char *shellErrorLocation(ShellState *p){ |
| 24237 | + char *zLoc; |
| 24238 | + if( p->zErrPrefix ){ |
| 24239 | + zLoc = sqlite3_mprintf("%s", p->zErrPrefix); |
| 24240 | + }else if( p->zInFile==0 || strcmp(p->zInFile,"<stdin>")==0){ |
| 24241 | + zLoc = sqlite3_mprintf("line %lld:", p->lineno); |
| 24242 | + }else{ |
| 24243 | + zLoc = sqlite3_mprintf("%s:%lld:", p->zInFile, p->lineno); |
| 24244 | + } |
| 24245 | + return zLoc; |
| 24246 | +} |
| 21531 | 24247 | |
| 21532 | 24248 | /* |
| 21533 | 24249 | ** If in safe mode, print an error message described by the arguments |
| 21534 | 24250 | ** and exit immediately. |
| 21535 | 24251 | */ |
| | @@ -21539,17 +24255,53 @@ |
| 21539 | 24255 | ... |
| 21540 | 24256 | ){ |
| 21541 | 24257 | if( p->bSafeMode ){ |
| 21542 | 24258 | va_list ap; |
| 21543 | 24259 | char *zMsg; |
| 24260 | + char *zLoc = shellErrorLocation(p); |
| 21544 | 24261 | va_start(ap, zErrMsg); |
| 21545 | 24262 | zMsg = sqlite3_vmprintf(zErrMsg, ap); |
| 21546 | 24263 | va_end(ap); |
| 21547 | | - sqlite3_fprintf(stderr, "line %lld: %s\n", p->lineno, zMsg); |
| 21548 | | - exit(1); |
| 24264 | + cli_printf(stderr, "%s %s\n", zLoc, zMsg); |
| 24265 | + cli_exit(1); |
| 21549 | 24266 | } |
| 21550 | 24267 | } |
| 24268 | + |
| 24269 | +/* |
| 24270 | +** Issue an error message from a dot-command. |
| 24271 | +*/ |
| 24272 | +static void dotCmdError( |
| 24273 | + ShellState *p, /* Shell state */ |
| 24274 | + int iArg, /* Index of argument on which error occurred */ |
| 24275 | + const char *zBrief, /* Brief (<20 character) error description */ |
| 24276 | + const char *zDetail, /* Error details */ |
| 24277 | + ... |
| 24278 | +){ |
| 24279 | + FILE *out = stderr; |
| 24280 | + char *zLoc = shellErrorLocation(p); |
| 24281 | + if( zBrief!=0 && iArg>=0 && iArg<p->dot.nArg ){ |
| 24282 | + int i = p->dot.aiOfst[iArg]; |
| 24283 | + int nPrompt = strlen30(zBrief) + 5; |
| 24284 | + cli_printf(out, "%s %s\n", zLoc, p->dot.zOrig); |
| 24285 | + if( i > nPrompt ){ |
| 24286 | + cli_printf(out, "%s %*s%s ---^\n", zLoc, 1+i-nPrompt, "", zBrief); |
| 24287 | + }else{ |
| 24288 | + cli_printf(out, "%s %*s^--- %s\n", zLoc, i, "", zBrief); |
| 24289 | + } |
| 24290 | + } |
| 24291 | + if( zDetail ){ |
| 24292 | + char *zMsg; |
| 24293 | + va_list ap; |
| 24294 | + va_start(ap, zDetail); |
| 24295 | + zMsg = sqlite3_vmprintf(zDetail,ap); |
| 24296 | + va_end(ap); |
| 24297 | + cli_printf(out,"%s %s\n", zLoc, zMsg); |
| 24298 | + sqlite3_free(zMsg); |
| 24299 | + } |
| 24300 | + sqlite3_free(zLoc); |
| 24301 | +} |
| 24302 | + |
| 21551 | 24303 | |
| 21552 | 24304 | /* |
| 21553 | 24305 | ** SQL function: edit(VALUE) |
| 21554 | 24306 | ** edit(VALUE,EDITOR) |
| 21555 | 24307 | ** |
| | @@ -21691,161 +24443,25 @@ |
| 21691 | 24443 | sqlite3_free(zTempFile); |
| 21692 | 24444 | sqlite3_free(p); |
| 21693 | 24445 | } |
| 21694 | 24446 | #endif /* SQLITE_NOHAVE_SYSTEM */ |
| 21695 | 24447 | |
| 21696 | | -/* |
| 21697 | | -** Save or restore the current output mode |
| 21698 | | -*/ |
| 21699 | | -static void outputModePush(ShellState *p){ |
| 21700 | | - p->modePrior = p->mode; |
| 21701 | | - p->priorShFlgs = p->shellFlgs; |
| 21702 | | - memcpy(p->colSepPrior, p->colSeparator, sizeof(p->colSeparator)); |
| 21703 | | - memcpy(p->rowSepPrior, p->rowSeparator, sizeof(p->rowSeparator)); |
| 21704 | | -} |
| 21705 | | -static void outputModePop(ShellState *p){ |
| 21706 | | - p->mode = p->modePrior; |
| 21707 | | - p->shellFlgs = p->priorShFlgs; |
| 21708 | | - memcpy(p->colSeparator, p->colSepPrior, sizeof(p->colSeparator)); |
| 21709 | | - memcpy(p->rowSeparator, p->rowSepPrior, sizeof(p->rowSeparator)); |
| 21710 | | -} |
| 21711 | | - |
| 21712 | 24448 | /* |
| 21713 | 24449 | ** Set output mode to text or binary for Windows. |
| 21714 | 24450 | */ |
| 21715 | 24451 | static void setCrlfMode(ShellState *p){ |
| 21716 | 24452 | #ifdef _WIN32 |
| 21717 | | - if( p->crlfMode ){ |
| 24453 | + if( p->mode.mFlags & MFLG_CRLF ){ |
| 21718 | 24454 | sqlite3_fsetmode(p->out, _O_TEXT); |
| 21719 | 24455 | }else{ |
| 21720 | 24456 | sqlite3_fsetmode(p->out, _O_BINARY); |
| 21721 | 24457 | } |
| 21722 | 24458 | #else |
| 21723 | 24459 | UNUSED_PARAMETER(p); |
| 21724 | 24460 | #endif |
| 21725 | 24461 | } |
| 21726 | 24462 | |
| 21727 | | -/* |
| 21728 | | -** Output the given string as a hex-encoded blob (eg. X'1234' ) |
| 21729 | | -*/ |
| 21730 | | -static void output_hex_blob(FILE *out, const void *pBlob, int nBlob){ |
| 21731 | | - int i; |
| 21732 | | - unsigned char *aBlob = (unsigned char*)pBlob; |
| 21733 | | - |
| 21734 | | - char *zStr = sqlite3_malloc64((i64)nBlob*2 + 1); |
| 21735 | | - shell_check_oom(zStr); |
| 21736 | | - |
| 21737 | | - for(i=0; i<nBlob; i++){ |
| 21738 | | - static const char aHex[] = { |
| 21739 | | - '0', '1', '2', '3', '4', '5', '6', '7', |
| 21740 | | - '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' |
| 21741 | | - }; |
| 21742 | | - zStr[i*2] = aHex[ (aBlob[i] >> 4) ]; |
| 21743 | | - zStr[i*2+1] = aHex[ (aBlob[i] & 0x0F) ]; |
| 21744 | | - } |
| 21745 | | - zStr[i*2] = '\0'; |
| 21746 | | - |
| 21747 | | - sqlite3_fprintf(out, "X'%s'", zStr); |
| 21748 | | - sqlite3_free(zStr); |
| 21749 | | -} |
| 21750 | | - |
| 21751 | | -/* |
| 21752 | | -** Output the given string as a quoted string using SQL quoting conventions: |
| 21753 | | -** |
| 21754 | | -** (1) Single quotes (') within the string are doubled |
| 21755 | | -** (2) The while string is enclosed in '...' |
| 21756 | | -** (3) Control characters other than \n, \t, and \r\n are escaped |
| 21757 | | -** using \u00XX notation and if such substitutions occur, |
| 21758 | | -** the whole string is enclosed in unistr('...') instead of '...'. |
| 21759 | | -** |
| 21760 | | -** Step (3) is omitted if the control-character escape mode is OFF. |
| 21761 | | -** |
| 21762 | | -** See also: output_quoted_escaped_string() which does the same except |
| 21763 | | -** that it does not make exceptions for \n, \t, and \r\n in step (3). |
| 21764 | | -*/ |
| 21765 | | -static void output_quoted_string(ShellState *p, const char *zInX){ |
| 21766 | | - int i; |
| 21767 | | - int needUnistr = 0; |
| 21768 | | - int needDblQuote = 0; |
| 21769 | | - const unsigned char *z = (const unsigned char*)zInX; |
| 21770 | | - unsigned char c; |
| 21771 | | - FILE *out = p->out; |
| 21772 | | - sqlite3_fsetmode(out, _O_BINARY); |
| 21773 | | - if( z==0 ) return; |
| 21774 | | - for(i=0; (c = z[i])!=0; i++){ |
| 21775 | | - if( c=='\'' ){ needDblQuote = 1; } |
| 21776 | | - if( c>0x1f ) continue; |
| 21777 | | - if( c=='\t' || c=='\n' ) continue; |
| 21778 | | - if( c=='\r' && z[i+1]=='\n' ) continue; |
| 21779 | | - needUnistr = 1; |
| 21780 | | - break; |
| 21781 | | - } |
| 21782 | | - if( (needDblQuote==0 && needUnistr==0) |
| 21783 | | - || (needDblQuote==0 && p->eEscMode==SHELL_ESC_OFF) |
| 21784 | | - ){ |
| 21785 | | - sqlite3_fprintf(out, "'%s'",z); |
| 21786 | | - }else if( p->eEscMode==SHELL_ESC_OFF ){ |
| 21787 | | - char *zEncoded = sqlite3_mprintf("%Q", z); |
| 21788 | | - sqlite3_fputs(zEncoded, out); |
| 21789 | | - sqlite3_free(zEncoded); |
| 21790 | | - }else{ |
| 21791 | | - if( needUnistr ){ |
| 21792 | | - sqlite3_fputs("unistr('", out); |
| 21793 | | - }else{ |
| 21794 | | - sqlite3_fputs("'", out); |
| 21795 | | - } |
| 21796 | | - while( *z ){ |
| 21797 | | - for(i=0; (c = z[i])!=0; i++){ |
| 21798 | | - if( c=='\'' ) break; |
| 21799 | | - if( c>0x1f ) continue; |
| 21800 | | - if( c=='\t' || c=='\n' ) continue; |
| 21801 | | - if( c=='\r' && z[i+1]=='\n' ) continue; |
| 21802 | | - break; |
| 21803 | | - } |
| 21804 | | - if( i ){ |
| 21805 | | - sqlite3_fprintf(out, "%.*s", i, z); |
| 21806 | | - z += i; |
| 21807 | | - } |
| 21808 | | - if( c==0 ) break; |
| 21809 | | - if( c=='\'' ){ |
| 21810 | | - sqlite3_fputs("''", out); |
| 21811 | | - }else{ |
| 21812 | | - sqlite3_fprintf(out, "\\u%04x", c); |
| 21813 | | - } |
| 21814 | | - z++; |
| 21815 | | - } |
| 21816 | | - if( needUnistr ){ |
| 21817 | | - sqlite3_fputs("')", out); |
| 21818 | | - }else{ |
| 21819 | | - sqlite3_fputs("'", out); |
| 21820 | | - } |
| 21821 | | - } |
| 21822 | | - setCrlfMode(p); |
| 21823 | | -} |
| 21824 | | - |
| 21825 | | -/* |
| 21826 | | -** Output the given string as a quoted string using SQL quoting conventions. |
| 21827 | | -** Additionallly , escape the "\n" and "\r" characters so that they do not |
| 21828 | | -** get corrupted by end-of-line translation facilities in some operating |
| 21829 | | -** systems. |
| 21830 | | -** |
| 21831 | | -** This is like output_quoted_string() but with the addition of the \r\n |
| 21832 | | -** escape mechanism. |
| 21833 | | -*/ |
| 21834 | | -static void output_quoted_escaped_string(ShellState *p, const char *z){ |
| 21835 | | - char *zEscaped; |
| 21836 | | - sqlite3_fsetmode(p->out, _O_BINARY); |
| 21837 | | - if( p->eEscMode==SHELL_ESC_OFF ){ |
| 21838 | | - zEscaped = sqlite3_mprintf("%Q", z); |
| 21839 | | - }else{ |
| 21840 | | - zEscaped = sqlite3_mprintf("%#Q", z); |
| 21841 | | - } |
| 21842 | | - sqlite3_fputs(zEscaped, p->out); |
| 21843 | | - sqlite3_free(zEscaped); |
| 21844 | | - setCrlfMode(p); |
| 21845 | | -} |
| 21846 | | - |
| 21847 | 24463 | /* |
| 21848 | 24464 | ** Find earliest of chars within s specified in zAny. |
| 21849 | 24465 | ** With ns == ~0, is like strpbrk(s,zAny) and s must be 0-terminated. |
| 21850 | 24466 | */ |
| 21851 | 24467 | static const char *anyOfInStr(const char *s, const char *zAny, size_t ns){ |
| | @@ -21905,17 +24521,63 @@ |
| 21905 | 24521 | static const char *zq = "\""; |
| 21906 | 24522 | static long ctrlMask = ~0L; |
| 21907 | 24523 | static const char *zDQBSRO = "\"\\\x7f"; /* double-quote, backslash, rubout */ |
| 21908 | 24524 | char ace[3] = "\\?"; |
| 21909 | 24525 | char cbsSay; |
| 21910 | | - sqlite3_fputs(zq, out); |
| 24526 | + cli_puts(zq, out); |
| 24527 | + if( z==0 ) z = ""; |
| 24528 | + while( *z!=0 ){ |
| 24529 | + const char *pcDQBSRO = anyOfInStr(z, zDQBSRO, ~(size_t)0); |
| 24530 | + const char *pcPast = zSkipValidUtf8(z, INT_MAX, ctrlMask); |
| 24531 | + const char *pcEnd = (pcDQBSRO && pcDQBSRO < pcPast)? pcDQBSRO : pcPast; |
| 24532 | + if( pcEnd > z ){ |
| 24533 | + cli_printf(out, "%.*s", (int)(pcEnd-z), z); |
| 24534 | + } |
| 24535 | + if( (c = *pcEnd)==0 ) break; |
| 24536 | + ++pcEnd; |
| 24537 | + switch( c ){ |
| 24538 | + case '\\': case '"': |
| 24539 | + cbsSay = (char)c; |
| 24540 | + break; |
| 24541 | + case '\t': cbsSay = 't'; break; |
| 24542 | + case '\n': cbsSay = 'n'; break; |
| 24543 | + case '\r': cbsSay = 'r'; break; |
| 24544 | + case '\f': cbsSay = 'f'; break; |
| 24545 | + default: cbsSay = 0; break; |
| 24546 | + } |
| 24547 | + if( cbsSay ){ |
| 24548 | + ace[1] = cbsSay; |
| 24549 | + cli_puts(ace, out); |
| 24550 | + }else if( !isprint(c&0xff) ){ |
| 24551 | + cli_printf(out, "\\%03o", c&0xff); |
| 24552 | + }else{ |
| 24553 | + ace[1] = (char)c; |
| 24554 | + cli_puts(ace+1, out); |
| 24555 | + } |
| 24556 | + z = pcEnd; |
| 24557 | + } |
| 24558 | + cli_puts(zq, out); |
| 24559 | +} |
| 24560 | + |
| 24561 | +/* Encode input string z[] as a C-language string literal and |
| 24562 | +** append it to the sqlite3_str. If z is NULL render and empty string. |
| 24563 | +*/ |
| 24564 | +static void append_c_string(sqlite3_str *out, const char *z){ |
| 24565 | + char c; |
| 24566 | + static const char *zq = "\""; |
| 24567 | + static long ctrlMask = ~0L; |
| 24568 | + static const char *zDQBSRO = "\"\\\x7f"; /* double-quote, backslash, rubout */ |
| 24569 | + char ace[3] = "\\?"; |
| 24570 | + char cbsSay; |
| 24571 | + if( z==0 ) z = ""; |
| 24572 | + sqlite3_str_appendall(out,zq); |
| 21911 | 24573 | while( *z!=0 ){ |
| 21912 | 24574 | const char *pcDQBSRO = anyOfInStr(z, zDQBSRO, ~(size_t)0); |
| 21913 | 24575 | const char *pcPast = zSkipValidUtf8(z, INT_MAX, ctrlMask); |
| 21914 | 24576 | const char *pcEnd = (pcDQBSRO && pcDQBSRO < pcPast)? pcDQBSRO : pcPast; |
| 21915 | 24577 | if( pcEnd > z ){ |
| 21916 | | - sqlite3_fprintf(out, "%.*s", (int)(pcEnd-z), z); |
| 24578 | + sqlite3_str_appendf(out, "%.*s", (int)(pcEnd-z), z); |
| 21917 | 24579 | } |
| 21918 | 24580 | if( (c = *pcEnd)==0 ) break; |
| 21919 | 24581 | ++pcEnd; |
| 21920 | 24582 | switch( c ){ |
| 21921 | 24583 | case '\\': case '"': |
| | @@ -21927,255 +24589,63 @@ |
| 21927 | 24589 | case '\f': cbsSay = 'f'; break; |
| 21928 | 24590 | default: cbsSay = 0; break; |
| 21929 | 24591 | } |
| 21930 | 24592 | if( cbsSay ){ |
| 21931 | 24593 | ace[1] = cbsSay; |
| 21932 | | - sqlite3_fputs(ace, out); |
| 21933 | | - }else if( !isprint(c&0xff) ){ |
| 21934 | | - sqlite3_fprintf(out, "\\%03o", c&0xff); |
| 21935 | | - }else{ |
| 21936 | | - ace[1] = (char)c; |
| 21937 | | - sqlite3_fputs(ace+1, out); |
| 21938 | | - } |
| 21939 | | - z = pcEnd; |
| 21940 | | - } |
| 21941 | | - sqlite3_fputs(zq, out); |
| 21942 | | -} |
| 21943 | | - |
| 21944 | | -/* |
| 21945 | | -** Output the given string as quoted according to JSON quoting rules. |
| 21946 | | -*/ |
| 21947 | | -static void output_json_string(FILE *out, const char *z, i64 n){ |
| 21948 | | - unsigned char c; |
| 21949 | | - static const char *zq = "\""; |
| 21950 | | - static long ctrlMask = ~0L; |
| 21951 | | - static const char *zDQBS = "\"\\"; |
| 21952 | | - const char *pcLimit; |
| 21953 | | - char ace[3] = "\\?"; |
| 21954 | | - char cbsSay; |
| 21955 | | - |
| 21956 | | - if( z==0 ) z = ""; |
| 21957 | | - pcLimit = z + ((n<0)? strlen(z) : (size_t)n); |
| 21958 | | - sqlite3_fputs(zq, out); |
| 21959 | | - while( z < pcLimit ){ |
| 21960 | | - const char *pcDQBS = anyOfInStr(z, zDQBS, pcLimit-z); |
| 21961 | | - const char *pcPast = zSkipValidUtf8(z, (int)(pcLimit-z), ctrlMask); |
| 21962 | | - const char *pcEnd = (pcDQBS && pcDQBS < pcPast)? pcDQBS : pcPast; |
| 21963 | | - if( pcEnd > z ){ |
| 21964 | | - sqlite3_fprintf(out, "%.*s", (int)(pcEnd-z), z); |
| 21965 | | - z = pcEnd; |
| 21966 | | - } |
| 21967 | | - if( z >= pcLimit ) break; |
| 21968 | | - c = (unsigned char)*(z++); |
| 21969 | | - switch( c ){ |
| 21970 | | - case '"': case '\\': |
| 21971 | | - cbsSay = (char)c; |
| 21972 | | - break; |
| 21973 | | - case '\b': cbsSay = 'b'; break; |
| 21974 | | - case '\f': cbsSay = 'f'; break; |
| 21975 | | - case '\n': cbsSay = 'n'; break; |
| 21976 | | - case '\r': cbsSay = 'r'; break; |
| 21977 | | - case '\t': cbsSay = 't'; break; |
| 21978 | | - default: cbsSay = 0; break; |
| 21979 | | - } |
| 21980 | | - if( cbsSay ){ |
| 21981 | | - ace[1] = cbsSay; |
| 21982 | | - sqlite3_fputs(ace, out); |
| 21983 | | - }else if( c<=0x1f || c>=0x7f ){ |
| 21984 | | - sqlite3_fprintf(out, "\\u%04x", c); |
| 21985 | | - }else{ |
| 21986 | | - ace[1] = (char)c; |
| 21987 | | - sqlite3_fputs(ace+1, out); |
| 21988 | | - } |
| 21989 | | - } |
| 21990 | | - sqlite3_fputs(zq, out); |
| 21991 | | -} |
| 21992 | | - |
| 21993 | | -/* |
| 21994 | | -** Escape the input string if it is needed and in accordance with |
| 21995 | | -** eEscMode. |
| 21996 | | -** |
| 21997 | | -** Escaping is needed if the string contains any control characters |
| 21998 | | -** other than \t, \n, and \r\n |
| 21999 | | -** |
| 22000 | | -** If no escaping is needed (the common case) then set *ppFree to NULL |
| 22001 | | -** and return the original string. If escaping is needed, write the |
| 22002 | | -** escaped string into memory obtained from sqlite3_malloc64() or the |
| 22003 | | -** equivalent, and return the new string and set *ppFree to the new string |
| 22004 | | -** as well. |
| 22005 | | -** |
| 22006 | | -** The caller is responsible for freeing *ppFree if it is non-NULL in order |
| 22007 | | -** to reclaim memory. |
| 22008 | | -*/ |
| 22009 | | -static const char *escapeOutput( |
| 22010 | | - ShellState *p, |
| 22011 | | - const char *zInX, |
| 22012 | | - char **ppFree |
| 22013 | | -){ |
| 22014 | | - i64 i, j; |
| 22015 | | - i64 nCtrl = 0; |
| 22016 | | - unsigned char *zIn; |
| 22017 | | - unsigned char c; |
| 22018 | | - unsigned char *zOut; |
| 22019 | | - |
| 22020 | | - |
| 22021 | | - /* No escaping if disabled */ |
| 22022 | | - if( p->eEscMode==SHELL_ESC_OFF ){ |
| 22023 | | - *ppFree = 0; |
| 22024 | | - return zInX; |
| 22025 | | - } |
| 22026 | | - |
| 22027 | | - /* Count the number of control characters in the string. */ |
| 22028 | | - zIn = (unsigned char*)zInX; |
| 22029 | | - for(i=0; (c = zIn[i])!=0; i++){ |
| 22030 | | - if( c<=0x1f |
| 22031 | | - && c!='\t' |
| 22032 | | - && c!='\n' |
| 22033 | | - && (c!='\r' || zIn[i+1]!='\n') |
| 22034 | | - ){ |
| 22035 | | - nCtrl++; |
| 22036 | | - } |
| 22037 | | - } |
| 22038 | | - if( nCtrl==0 ){ |
| 22039 | | - *ppFree = 0; |
| 22040 | | - return zInX; |
| 22041 | | - } |
| 22042 | | - if( p->eEscMode==SHELL_ESC_SYMBOL ) nCtrl *= 2; |
| 22043 | | - zOut = sqlite3_malloc64( i + nCtrl + 1 ); |
| 22044 | | - shell_check_oom(zOut); |
| 22045 | | - for(i=j=0; (c = zIn[i])!=0; i++){ |
| 22046 | | - if( c>0x1f |
| 22047 | | - || c=='\t' |
| 22048 | | - || c=='\n' |
| 22049 | | - || (c=='\r' && zIn[i+1]=='\n') |
| 22050 | | - ){ |
| 22051 | | - continue; |
| 22052 | | - } |
| 22053 | | - if( i>0 ){ |
| 22054 | | - memcpy(&zOut[j], zIn, i); |
| 22055 | | - j += i; |
| 22056 | | - } |
| 22057 | | - zIn += i+1; |
| 22058 | | - i = -1; |
| 22059 | | - switch( p->eEscMode ){ |
| 22060 | | - case SHELL_ESC_SYMBOL: |
| 22061 | | - zOut[j++] = 0xe2; |
| 22062 | | - zOut[j++] = 0x90; |
| 22063 | | - zOut[j++] = 0x80+c; |
| 22064 | | - break; |
| 22065 | | - case SHELL_ESC_ASCII: |
| 22066 | | - zOut[j++] = '^'; |
| 22067 | | - zOut[j++] = 0x40+c; |
| 22068 | | - break; |
| 22069 | | - } |
| 22070 | | - } |
| 22071 | | - if( i>0 ){ |
| 22072 | | - memcpy(&zOut[j], zIn, i); |
| 22073 | | - j += i; |
| 22074 | | - } |
| 22075 | | - zOut[j] = 0; |
| 22076 | | - *ppFree = (char*)zOut; |
| 22077 | | - return (char*)zOut; |
| 22078 | | -} |
| 22079 | | - |
| 22080 | | -/* |
| 22081 | | -** Output the given string with characters that are special to |
| 22082 | | -** HTML escaped. |
| 22083 | | -*/ |
| 22084 | | -static void output_html_string(FILE *out, const char *z){ |
| 22085 | | - int i; |
| 22086 | | - if( z==0 ) z = ""; |
| 22087 | | - while( *z ){ |
| 22088 | | - for(i=0; z[i] |
| 22089 | | - && z[i]!='<' |
| 22090 | | - && z[i]!='&' |
| 22091 | | - && z[i]!='>' |
| 22092 | | - && z[i]!='\"' |
| 22093 | | - && z[i]!='\''; |
| 22094 | | - i++){} |
| 22095 | | - if( i>0 ){ |
| 22096 | | - sqlite3_fprintf(out, "%.*s",i,z); |
| 22097 | | - } |
| 22098 | | - if( z[i]=='<' ){ |
| 22099 | | - sqlite3_fputs("<", out); |
| 22100 | | - }else if( z[i]=='&' ){ |
| 22101 | | - sqlite3_fputs("&", out); |
| 22102 | | - }else if( z[i]=='>' ){ |
| 22103 | | - sqlite3_fputs(">", out); |
| 22104 | | - }else if( z[i]=='\"' ){ |
| 22105 | | - sqlite3_fputs(""", out); |
| 22106 | | - }else if( z[i]=='\'' ){ |
| 22107 | | - sqlite3_fputs("'", out); |
| 22108 | | - }else{ |
| 22109 | | - break; |
| 22110 | | - } |
| 22111 | | - z += i + 1; |
| 22112 | | - } |
| 22113 | | -} |
| 22114 | | - |
| 22115 | | -/* |
| 22116 | | -** If a field contains any character identified by a 1 in the following |
| 22117 | | -** array, then the string must be quoted for CSV. |
| 22118 | | -*/ |
| 22119 | | -static const char needCsvQuote[] = { |
| 22120 | | - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
| 22121 | | - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
| 22122 | | - 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, |
| 22123 | | - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 22124 | | - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 22125 | | - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 22126 | | - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 22127 | | - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, |
| 22128 | | - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
| 22129 | | - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
| 22130 | | - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
| 22131 | | - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
| 22132 | | - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
| 22133 | | - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
| 22134 | | - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
| 22135 | | - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
| 22136 | | -}; |
| 22137 | | - |
| 22138 | | -/* |
| 22139 | | -** Output a single term of CSV. Actually, p->colSeparator is used for |
| 22140 | | -** the separator, which may or may not be a comma. p->nullValue is |
| 22141 | | -** the null value. Strings are quoted if necessary. The separator |
| 22142 | | -** is only issued if bSep is true. |
| 22143 | | -*/ |
| 22144 | | -static void output_csv(ShellState *p, const char *z, int bSep){ |
| 22145 | | - if( z==0 ){ |
| 22146 | | - sqlite3_fprintf(p->out, "%s",p->nullValue); |
| 22147 | | - }else{ |
| 22148 | | - unsigned i; |
| 22149 | | - for(i=0; z[i]; i++){ |
| 22150 | | - if( needCsvQuote[((unsigned char*)z)[i]] ){ |
| 22151 | | - i = 0; |
| 22152 | | - break; |
| 22153 | | - } |
| 22154 | | - } |
| 22155 | | - if( i==0 || strstr(z, p->colSeparator)!=0 ){ |
| 22156 | | - char *zQuoted = sqlite3_mprintf("\"%w\"", z); |
| 22157 | | - shell_check_oom(zQuoted); |
| 22158 | | - sqlite3_fputs(zQuoted, p->out); |
| 22159 | | - sqlite3_free(zQuoted); |
| 22160 | | - }else{ |
| 22161 | | - sqlite3_fputs(z, p->out); |
| 22162 | | - } |
| 22163 | | - } |
| 22164 | | - if( bSep ){ |
| 22165 | | - sqlite3_fputs(p->colSeparator, p->out); |
| 22166 | | - } |
| 24594 | + sqlite3_str_appendall(out,ace); |
| 24595 | + }else if( !isprint(c&0xff) ){ |
| 24596 | + sqlite3_str_appendf(out, "\\%03o", c&0xff); |
| 24597 | + }else{ |
| 24598 | + ace[1] = (char)c; |
| 24599 | + sqlite3_str_appendall(out, ace+1); |
| 24600 | + } |
| 24601 | + z = pcEnd; |
| 24602 | + } |
| 24603 | + sqlite3_str_appendall(out, zq); |
| 22167 | 24604 | } |
| 22168 | 24605 | |
| 22169 | 24606 | /* |
| 22170 | 24607 | ** This routine runs when the user presses Ctrl-C |
| 22171 | 24608 | */ |
| 22172 | 24609 | static void interrupt_handler(int NotUsed){ |
| 22173 | 24610 | UNUSED_PARAMETER(NotUsed); |
| 22174 | | - if( ++seenInterrupt>1 ) exit(1); |
| 24611 | + if( ++seenInterrupt>1 ) cli_exit(1); |
| 22175 | 24612 | if( globalDb ) sqlite3_interrupt(globalDb); |
| 22176 | 24613 | } |
| 24614 | + |
| 24615 | +/* Try to determine the screen width. Use the default if unable. |
| 24616 | +*/ |
| 24617 | +int shellScreenWidth(void){ |
| 24618 | + if( stdout_tty_width>0 ) return stdout_tty_width; |
| 24619 | +#if defined(TIOCGSIZE) |
| 24620 | + struct ttysize ts; |
| 24621 | + if( ioctl(STDIN_FILENO, TIOCGSIZE, &ts)>=0 |
| 24622 | + || ioctl(STDOUT_FILENO, TIOCGSIZE, &ts)>=0 |
| 24623 | + || ioctl(STDERR_FILENO, TIOCGSIZE, &ts)>=0 |
| 24624 | + ){ |
| 24625 | + return ts.ts_cols; |
| 24626 | + } |
| 24627 | +#elif defined(TIOCGWINSZ) |
| 24628 | + struct winsize ws; |
| 24629 | + if( ioctl(STDIN_FILENO, TIOCGWINSZ, &ws)>=0 |
| 24630 | + || ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws)>=0 |
| 24631 | + || ioctl(STDERR_FILENO, TIOCGWINSZ, &ws)>=0 |
| 24632 | + ){ |
| 24633 | + return ws.ws_col; |
| 24634 | + } |
| 24635 | +#elif defined(_WIN32) |
| 24636 | + CONSOLE_SCREEN_BUFFER_INFO csbi; |
| 24637 | + if( GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi) |
| 24638 | + || GetConsoleScreenBufferInfo(GetStdHandle(STD_ERROR_HANDLE), &csbi) |
| 24639 | + || GetConsoleScreenBufferInfo(GetStdHandle(STD_INPUT_HANDLE), &csbi) |
| 24640 | + ){ |
| 24641 | + return csbi.srWindow.Right - csbi.srWindow.Left + 1; |
| 24642 | + } |
| 24643 | +#endif |
| 24644 | +#define DEFAULT_SCREEN_WIDTH 80 |
| 24645 | + return DEFAULT_SCREEN_WIDTH; |
| 24646 | +} |
| 22177 | 24647 | |
| 22178 | 24648 | #if (defined(_WIN32) || defined(WIN32)) && !defined(_WIN32_WCE) |
| 22179 | 24649 | /* |
| 22180 | 24650 | ** This routine runs for console events (e.g. Ctrl-C) on Win32 |
| 22181 | 24651 | */ |
| | @@ -22268,27 +24738,27 @@ |
| 22268 | 24738 | const char *az[4]; |
| 22269 | 24739 | az[0] = zA1; |
| 22270 | 24740 | az[1] = zA2; |
| 22271 | 24741 | az[2] = zA3; |
| 22272 | 24742 | az[3] = zA4; |
| 22273 | | - sqlite3_fprintf(p->out, "authorizer: %s", azAction[op]); |
| 24743 | + cli_printf(p->out, "authorizer: %s", azAction[op]); |
| 22274 | 24744 | for(i=0; i<4; i++){ |
| 22275 | | - sqlite3_fputs(" ", p->out); |
| 24745 | + cli_puts(" ", p->out); |
| 22276 | 24746 | if( az[i] ){ |
| 22277 | 24747 | output_c_string(p->out, az[i]); |
| 22278 | 24748 | }else{ |
| 22279 | | - sqlite3_fputs("NULL", p->out); |
| 24749 | + cli_puts("NULL", p->out); |
| 22280 | 24750 | } |
| 22281 | 24751 | } |
| 22282 | | - sqlite3_fputs("\n", p->out); |
| 24752 | + cli_puts("\n", p->out); |
| 22283 | 24753 | if( p->bSafeMode ) (void)safeModeAuth(pClientData, op, zA1, zA2, zA3, zA4); |
| 22284 | 24754 | return SQLITE_OK; |
| 22285 | 24755 | } |
| 22286 | 24756 | #endif |
| 22287 | 24757 | |
| 22288 | 24758 | /* |
| 22289 | | -** Print a schema statement. Part of MODE_Semi and MODE_Pretty output. |
| 24759 | +** Print a schema statement. This is helper routine to dump_callbac(). |
| 22290 | 24760 | ** |
| 22291 | 24761 | ** This routine converts some CREATE TABLE statements for shadow tables |
| 22292 | 24762 | ** in FTS3/4/5 into CREATE TABLE IF NOT EXISTS statements. |
| 22293 | 24763 | ** |
| 22294 | 24764 | ** If the schema statement in z[] contains a start-of-comment and if |
| | @@ -22315,22 +24785,16 @@ |
| 22315 | 24785 | } |
| 22316 | 24786 | sqlite3_free(zNew); |
| 22317 | 24787 | } |
| 22318 | 24788 | } |
| 22319 | 24789 | if( sqlite3_strglob("CREATE TABLE ['\"]*", z)==0 ){ |
| 22320 | | - sqlite3_fprintf(out, "CREATE TABLE IF NOT EXISTS %s%s", z+13, zTail); |
| 24790 | + cli_printf(out, "CREATE TABLE IF NOT EXISTS %s%s", z+13, zTail); |
| 22321 | 24791 | }else{ |
| 22322 | | - sqlite3_fprintf(out, "%s%s", z, zTail); |
| 24792 | + cli_printf(out, "%s%s", z, zTail); |
| 22323 | 24793 | } |
| 22324 | 24794 | sqlite3_free(zToFree); |
| 22325 | 24795 | } |
| 22326 | | -static void printSchemaLineN(FILE *out, char *z, int n, const char *zTail){ |
| 22327 | | - char c = z[n]; |
| 22328 | | - z[n] = 0; |
| 22329 | | - printSchemaLine(out, z, zTail); |
| 22330 | | - z[n] = c; |
| 22331 | | -} |
| 22332 | 24796 | |
| 22333 | 24797 | /* |
| 22334 | 24798 | ** Return true if string z[] has nothing but whitespace and comments to the |
| 22335 | 24799 | ** end of the first line. |
| 22336 | 24800 | */ |
| | @@ -22344,99 +24808,132 @@ |
| 22344 | 24808 | } |
| 22345 | 24809 | return 1; |
| 22346 | 24810 | } |
| 22347 | 24811 | |
| 22348 | 24812 | /* |
| 22349 | | -** Add a new entry to the EXPLAIN QUERY PLAN data |
| 22350 | | -*/ |
| 22351 | | -static void eqp_append(ShellState *p, int iEqpId, int p2, const char *zText){ |
| 22352 | | - EQPGraphRow *pNew; |
| 22353 | | - i64 nText; |
| 22354 | | - if( zText==0 ) return; |
| 22355 | | - nText = strlen(zText); |
| 22356 | | - if( p->autoEQPtest ){ |
| 22357 | | - sqlite3_fprintf(p->out, "%d,%d,%s\n", iEqpId, p2, zText); |
| 22358 | | - } |
| 22359 | | - pNew = sqlite3_malloc64( sizeof(*pNew) + nText ); |
| 22360 | | - shell_check_oom(pNew); |
| 22361 | | - pNew->iEqpId = iEqpId; |
| 22362 | | - pNew->iParentId = p2; |
| 22363 | | - memcpy(pNew->zText, zText, nText+1); |
| 22364 | | - pNew->pNext = 0; |
| 22365 | | - if( p->sGraph.pLast ){ |
| 22366 | | - p->sGraph.pLast->pNext = pNew; |
| 22367 | | - }else{ |
| 22368 | | - p->sGraph.pRow = pNew; |
| 22369 | | - } |
| 22370 | | - p->sGraph.pLast = pNew; |
| 22371 | | -} |
| 22372 | | - |
| 22373 | | -/* |
| 22374 | | -** Free and reset the EXPLAIN QUERY PLAN data that has been collected |
| 22375 | | -** in p->sGraph. |
| 22376 | | -*/ |
| 22377 | | -static void eqp_reset(ShellState *p){ |
| 22378 | | - EQPGraphRow *pRow, *pNext; |
| 22379 | | - for(pRow = p->sGraph.pRow; pRow; pRow = pNext){ |
| 22380 | | - pNext = pRow->pNext; |
| 22381 | | - sqlite3_free(pRow); |
| 22382 | | - } |
| 22383 | | - memset(&p->sGraph, 0, sizeof(p->sGraph)); |
| 22384 | | -} |
| 22385 | | - |
| 22386 | | -/* Return the next EXPLAIN QUERY PLAN line with iEqpId that occurs after |
| 22387 | | -** pOld, or return the first such line if pOld is NULL |
| 22388 | | -*/ |
| 22389 | | -static EQPGraphRow *eqp_next_row(ShellState *p, int iEqpId, EQPGraphRow *pOld){ |
| 22390 | | - EQPGraphRow *pRow = pOld ? pOld->pNext : p->sGraph.pRow; |
| 22391 | | - while( pRow && pRow->iParentId!=iEqpId ) pRow = pRow->pNext; |
| 22392 | | - return pRow; |
| 22393 | | -} |
| 22394 | | - |
| 22395 | | -/* Render a single level of the graph that has iEqpId as its parent. Called |
| 22396 | | -** recursively to render sublevels. |
| 22397 | | -*/ |
| 22398 | | -static void eqp_render_level(ShellState *p, int iEqpId){ |
| 22399 | | - EQPGraphRow *pRow, *pNext; |
| 22400 | | - i64 n = strlen(p->sGraph.zPrefix); |
| 22401 | | - char *z; |
| 22402 | | - for(pRow = eqp_next_row(p, iEqpId, 0); pRow; pRow = pNext){ |
| 22403 | | - pNext = eqp_next_row(p, iEqpId, pRow); |
| 22404 | | - z = pRow->zText; |
| 22405 | | - sqlite3_fprintf(p->out, "%s%s%s\n", p->sGraph.zPrefix, |
| 22406 | | - pNext ? "|--" : "`--", z); |
| 22407 | | - if( n<(i64)sizeof(p->sGraph.zPrefix)-7 ){ |
| 22408 | | - memcpy(&p->sGraph.zPrefix[n], pNext ? "| " : " ", 4); |
| 22409 | | - eqp_render_level(p, pRow->iEqpId); |
| 22410 | | - p->sGraph.zPrefix[n] = 0; |
| 22411 | | - } |
| 22412 | | - } |
| 22413 | | -} |
| 22414 | | - |
| 22415 | | -/* |
| 22416 | | -** Display and reset the EXPLAIN QUERY PLAN data |
| 22417 | | -*/ |
| 22418 | | -static void eqp_render(ShellState *p, i64 nCycle){ |
| 22419 | | - EQPGraphRow *pRow = p->sGraph.pRow; |
| 22420 | | - if( pRow ){ |
| 22421 | | - if( pRow->zText[0]=='-' ){ |
| 22422 | | - if( pRow->pNext==0 ){ |
| 22423 | | - eqp_reset(p); |
| 22424 | | - return; |
| 22425 | | - } |
| 22426 | | - sqlite3_fprintf(p->out, "%s\n", pRow->zText+3); |
| 22427 | | - p->sGraph.pRow = pRow->pNext; |
| 22428 | | - sqlite3_free(pRow); |
| 22429 | | - }else if( nCycle>0 ){ |
| 22430 | | - sqlite3_fprintf(p->out, "QUERY PLAN (cycles=%lld [100%%])\n", nCycle); |
| 22431 | | - }else{ |
| 22432 | | - sqlite3_fputs("QUERY PLAN\n", p->out); |
| 22433 | | - } |
| 22434 | | - p->sGraph.zPrefix[0] = 0; |
| 22435 | | - eqp_render_level(p, 0); |
| 22436 | | - eqp_reset(p); |
| 22437 | | - } |
| 24813 | +** SQL Function: shell_format_schema(SQL,FLAGS) |
| 24814 | +** |
| 24815 | +** This function is internally by the CLI to assist with the |
| 24816 | +** ".schema", ".fullschema", and ".dump" commands. The first |
| 24817 | +** argument is the value from sqlite_schema.sql. The value returned |
| 24818 | +** is a modification of the input that can actually be run as SQL |
| 24819 | +** to recreate the schema object. |
| 24820 | +** |
| 24821 | +** When FLAGS is zero, the only changes is to append ";". If the |
| 24822 | +** 0x01 bit of FLAGS is set, then transformations are made to implement |
| 24823 | +** ".schema --indent". |
| 24824 | +*/ |
| 24825 | +static void shellFormatSchema( |
| 24826 | + sqlite3_context *pCtx, |
| 24827 | + int nVal, |
| 24828 | + sqlite3_value **apVal |
| 24829 | +){ |
| 24830 | + int flags; /* Value of 2nd parameter */ |
| 24831 | + const char *zSql; /* Value of 1st parameter */ |
| 24832 | + int nSql; /* Bytes of text in zSql[] */ |
| 24833 | + sqlite3_str *pOut; /* Output buffer */ |
| 24834 | + char *z; /* Writable copy of zSql */ |
| 24835 | + int i, j; /* Loop counters */ |
| 24836 | + int nParen = 0; |
| 24837 | + char cEnd = 0; |
| 24838 | + char c; |
| 24839 | + int nLine = 0; |
| 24840 | + int isIndex; |
| 24841 | + int isWhere = 0; |
| 24842 | + |
| 24843 | + assert( nVal==2 ); |
| 24844 | + pOut = sqlite3_str_new(sqlite3_context_db_handle(pCtx)); |
| 24845 | + nSql = sqlite3_value_bytes(apVal[0]); |
| 24846 | + zSql = (const char*)sqlite3_value_text(apVal[0]); |
| 24847 | + if( zSql==0 || zSql[0]==0 ) goto shellFormatSchema_finish; |
| 24848 | + flags = sqlite3_value_int(apVal[1]); |
| 24849 | + if( (flags & 0x01)==0 ){ |
| 24850 | + sqlite3_str_append(pOut, zSql, nSql); |
| 24851 | + sqlite3_str_append(pOut, ";", 1); |
| 24852 | + goto shellFormatSchema_finish; |
| 24853 | + } |
| 24854 | + if( sqlite3_strlike("CREATE VIEW%", zSql, 0)==0 |
| 24855 | + || sqlite3_strlike("CREATE TRIG%", zSql, 0)==0 |
| 24856 | + ){ |
| 24857 | + sqlite3_str_append(pOut, zSql, nSql); |
| 24858 | + sqlite3_str_append(pOut, ";", 1); |
| 24859 | + goto shellFormatSchema_finish; |
| 24860 | + } |
| 24861 | + isIndex = sqlite3_strlike("CREATE INDEX%", zSql, 0)==0 |
| 24862 | + || sqlite3_strlike("CREATE UNIQUE INDEX%", zSql, 0)==0; |
| 24863 | + z = sqlite3_mprintf("%s", zSql); |
| 24864 | + if( z==0 ){ |
| 24865 | + sqlite3_str_free(pOut); |
| 24866 | + sqlite3_result_error_nomem(pCtx); |
| 24867 | + return; |
| 24868 | + } |
| 24869 | + j = 0; |
| 24870 | + for(i=0; IsSpace(z[i]); i++){} |
| 24871 | + for(; (c = z[i])!=0; i++){ |
| 24872 | + if( IsSpace(c) ){ |
| 24873 | + if( z[j-1]=='\r' ) z[j-1] = '\n'; |
| 24874 | + if( IsSpace(z[j-1]) || z[j-1]=='(' ) continue; |
| 24875 | + }else if( (c=='(' || c==')') && j>0 && IsSpace(z[j-1]) ){ |
| 24876 | + j--; |
| 24877 | + } |
| 24878 | + z[j++] = c; |
| 24879 | + } |
| 24880 | + while( j>0 && IsSpace(z[j-1]) ){ j--; } |
| 24881 | + z[j] = 0; |
| 24882 | + if( strlen30(z)>=79 ){ |
| 24883 | + for(i=j=0; (c = z[i])!=0; i++){ /* Copy from z[i] back to z[j] */ |
| 24884 | + if( c==cEnd ){ |
| 24885 | + cEnd = 0; |
| 24886 | + }else if( c=='"' || c=='\'' || c=='`' ){ |
| 24887 | + cEnd = c; |
| 24888 | + }else if( c=='[' ){ |
| 24889 | + cEnd = ']'; |
| 24890 | + }else if( c=='-' && z[i+1]=='-' ){ |
| 24891 | + cEnd = '\n'; |
| 24892 | + }else if( c=='(' ){ |
| 24893 | + nParen++; |
| 24894 | + }else if( c==')' ){ |
| 24895 | + nParen--; |
| 24896 | + if( nLine>0 && nParen==0 && j>0 && !isWhere ){ |
| 24897 | + sqlite3_str_append(pOut, z, j); |
| 24898 | + sqlite3_str_append(pOut, "\n", 1); |
| 24899 | + j = 0; |
| 24900 | + } |
| 24901 | + }else if( (c=='w' || c=='W') |
| 24902 | + && nParen==0 && isIndex |
| 24903 | + && sqlite3_strnicmp("WHERE",&z[i],5)==0 |
| 24904 | + && !IsAlnum(z[i+5]) && z[i+5]!='_' ){ |
| 24905 | + isWhere = 1; |
| 24906 | + }else if( isWhere && (c=='A' || c=='a') |
| 24907 | + && nParen==0 |
| 24908 | + && sqlite3_strnicmp("AND",&z[i],3)==0 |
| 24909 | + && !IsAlnum(z[i+3]) && z[i+3]!='_' ){ |
| 24910 | + sqlite3_str_append(pOut, z, j); |
| 24911 | + sqlite3_str_append(pOut, "\n ", 5); |
| 24912 | + j = 0; |
| 24913 | + } |
| 24914 | + z[j++] = c; |
| 24915 | + if( nParen==1 && cEnd==0 |
| 24916 | + && (c=='(' || c=='\n' || (c==',' && !wsToEol(z+i+1))) |
| 24917 | + && !isWhere |
| 24918 | + ){ |
| 24919 | + if( c=='\n' ) j--; |
| 24920 | + sqlite3_str_append(pOut, z, j); |
| 24921 | + sqlite3_str_append(pOut, "\n ", 3); |
| 24922 | + j = 0; |
| 24923 | + nLine++; |
| 24924 | + while( IsSpace(z[i+1]) ){ i++; } |
| 24925 | + } |
| 24926 | + } |
| 24927 | + z[j] = 0; |
| 24928 | + } |
| 24929 | + sqlite3_str_appendall(pOut, z); |
| 24930 | + sqlite3_str_append(pOut, ";", 1); |
| 24931 | + sqlite3_free(z); |
| 24932 | + |
| 24933 | +shellFormatSchema_finish: |
| 24934 | + sqlite3_result_text(pCtx, sqlite3_str_finish(pOut), -1, sqlite3_free); |
| 22438 | 24935 | } |
| 22439 | 24936 | |
| 22440 | 24937 | #ifndef SQLITE_OMIT_PROGRESS_CALLBACK |
| 22441 | 24938 | /* |
| 22442 | 24939 | ** Progress handler callback. |
| | @@ -22443,496 +24940,22 @@ |
| 22443 | 24940 | */ |
| 22444 | 24941 | static int progress_handler(void *pClientData) { |
| 22445 | 24942 | ShellState *p = (ShellState*)pClientData; |
| 22446 | 24943 | p->nProgress++; |
| 22447 | 24944 | if( p->nProgress>=p->mxProgress && p->mxProgress>0 ){ |
| 22448 | | - sqlite3_fprintf(p->out, "Progress limit reached (%u)\n", p->nProgress); |
| 24945 | + cli_printf(p->out, "Progress limit reached (%u)\n", p->nProgress); |
| 22449 | 24946 | if( p->flgProgress & SHELL_PROGRESS_RESET ) p->nProgress = 0; |
| 22450 | 24947 | if( p->flgProgress & SHELL_PROGRESS_ONCE ) p->mxProgress = 0; |
| 22451 | 24948 | return 1; |
| 22452 | 24949 | } |
| 22453 | 24950 | if( (p->flgProgress & SHELL_PROGRESS_QUIET)==0 ){ |
| 22454 | | - sqlite3_fprintf(p->out, "Progress %u\n", p->nProgress); |
| 24951 | + cli_printf(p->out, "Progress %u\n", p->nProgress); |
| 22455 | 24952 | } |
| 22456 | 24953 | return 0; |
| 22457 | 24954 | } |
| 22458 | 24955 | #endif /* SQLITE_OMIT_PROGRESS_CALLBACK */ |
| 22459 | 24956 | |
| 22460 | | -/* |
| 22461 | | -** Print N dashes |
| 22462 | | -*/ |
| 22463 | | -static void print_dashes(FILE *out, int N){ |
| 22464 | | - const char zDash[] = "--------------------------------------------------"; |
| 22465 | | - const int nDash = sizeof(zDash) - 1; |
| 22466 | | - while( N>nDash ){ |
| 22467 | | - sqlite3_fputs(zDash, out); |
| 22468 | | - N -= nDash; |
| 22469 | | - } |
| 22470 | | - sqlite3_fprintf(out, "%.*s", N, zDash); |
| 22471 | | -} |
| 22472 | | - |
| 22473 | | -/* |
| 22474 | | -** Print a markdown or table-style row separator using ascii-art |
| 22475 | | -*/ |
| 22476 | | -static void print_row_separator( |
| 22477 | | - ShellState *p, |
| 22478 | | - int nArg, |
| 22479 | | - const char *zSep |
| 22480 | | -){ |
| 22481 | | - int i; |
| 22482 | | - if( nArg>0 ){ |
| 22483 | | - sqlite3_fputs(zSep, p->out); |
| 22484 | | - print_dashes(p->out, p->actualWidth[0]+2); |
| 22485 | | - for(i=1; i<nArg; i++){ |
| 22486 | | - sqlite3_fputs(zSep, p->out); |
| 22487 | | - print_dashes(p->out, p->actualWidth[i]+2); |
| 22488 | | - } |
| 22489 | | - sqlite3_fputs(zSep, p->out); |
| 22490 | | - } |
| 22491 | | - sqlite3_fputs("\n", p->out); |
| 22492 | | -} |
| 22493 | | - |
| 22494 | | -/* |
| 22495 | | -** This is the callback routine that the shell |
| 22496 | | -** invokes for each row of a query result. |
| 22497 | | -*/ |
| 22498 | | -static int shell_callback( |
| 22499 | | - void *pArg, |
| 22500 | | - int nArg, /* Number of result columns */ |
| 22501 | | - char **azArg, /* Text of each result column */ |
| 22502 | | - char **azCol, /* Column names */ |
| 22503 | | - int *aiType /* Column types. Might be NULL */ |
| 22504 | | -){ |
| 22505 | | - int i; |
| 22506 | | - ShellState *p = (ShellState*)pArg; |
| 22507 | | - |
| 22508 | | - if( azArg==0 ) return 0; |
| 22509 | | - switch( p->cMode ){ |
| 22510 | | - case MODE_Count: |
| 22511 | | - case MODE_Off: { |
| 22512 | | - break; |
| 22513 | | - } |
| 22514 | | - case MODE_Line: { |
| 22515 | | - int w = 5; |
| 22516 | | - if( azArg==0 ) break; |
| 22517 | | - for(i=0; i<nArg; i++){ |
| 22518 | | - int len = strlen30(azCol[i] ? azCol[i] : ""); |
| 22519 | | - if( len>w ) w = len; |
| 22520 | | - } |
| 22521 | | - if( p->cnt++>0 ) sqlite3_fputs(p->rowSeparator, p->out); |
| 22522 | | - for(i=0; i<nArg; i++){ |
| 22523 | | - char *pFree = 0; |
| 22524 | | - const char *pDisplay; |
| 22525 | | - pDisplay = escapeOutput(p, azArg[i] ? azArg[i] : p->nullValue, &pFree); |
| 22526 | | - sqlite3_fprintf(p->out, "%*s = %s%s", w, azCol[i], |
| 22527 | | - pDisplay, p->rowSeparator); |
| 22528 | | - if( pFree ) sqlite3_free(pFree); |
| 22529 | | - } |
| 22530 | | - break; |
| 22531 | | - } |
| 22532 | | - case MODE_ScanExp: |
| 22533 | | - case MODE_Explain: { |
| 22534 | | - static const int aExplainWidth[] = {4, 13, 4, 4, 4, 13, 2, 13}; |
| 22535 | | - static const int aExplainMap[] = {0, 1, 2, 3, 4, 5, 6, 7 }; |
| 22536 | | - static const int aScanExpWidth[] = {4, 15, 6, 13, 4, 4, 4, 13, 2, 13}; |
| 22537 | | - static const int aScanExpMap[] = {0, 9, 8, 1, 2, 3, 4, 5, 6, 7 }; |
| 22538 | | - |
| 22539 | | - const int *aWidth = aExplainWidth; |
| 22540 | | - const int *aMap = aExplainMap; |
| 22541 | | - int nWidth = ArraySize(aExplainWidth); |
| 22542 | | - int iIndent = 1; |
| 22543 | | - |
| 22544 | | - if( p->cMode==MODE_ScanExp ){ |
| 22545 | | - aWidth = aScanExpWidth; |
| 22546 | | - aMap = aScanExpMap; |
| 22547 | | - nWidth = ArraySize(aScanExpWidth); |
| 22548 | | - iIndent = 3; |
| 22549 | | - } |
| 22550 | | - if( nArg>nWidth ) nArg = nWidth; |
| 22551 | | - |
| 22552 | | - /* If this is the first row seen, print out the headers */ |
| 22553 | | - if( p->cnt++==0 ){ |
| 22554 | | - for(i=0; i<nArg; i++){ |
| 22555 | | - utf8_width_print(p->out, aWidth[i], azCol[ aMap[i] ]); |
| 22556 | | - sqlite3_fputs(i==nArg-1 ? "\n" : " ", p->out); |
| 22557 | | - } |
| 22558 | | - for(i=0; i<nArg; i++){ |
| 22559 | | - print_dashes(p->out, aWidth[i]); |
| 22560 | | - sqlite3_fputs(i==nArg-1 ? "\n" : " ", p->out); |
| 22561 | | - } |
| 22562 | | - } |
| 22563 | | - |
| 22564 | | - /* If there is no data, exit early. */ |
| 22565 | | - if( azArg==0 ) break; |
| 22566 | | - |
| 22567 | | - for(i=0; i<nArg; i++){ |
| 22568 | | - const char *zSep = " "; |
| 22569 | | - int w = aWidth[i]; |
| 22570 | | - const char *zVal = azArg[ aMap[i] ]; |
| 22571 | | - if( i==nArg-1 ) w = 0; |
| 22572 | | - if( zVal && strlenChar(zVal)>w ){ |
| 22573 | | - w = strlenChar(zVal); |
| 22574 | | - zSep = " "; |
| 22575 | | - } |
| 22576 | | - if( i==iIndent && p->aiIndent && p->pStmt ){ |
| 22577 | | - if( p->iIndent<p->nIndent ){ |
| 22578 | | - sqlite3_fprintf(p->out, "%*.s", p->aiIndent[p->iIndent], ""); |
| 22579 | | - } |
| 22580 | | - p->iIndent++; |
| 22581 | | - } |
| 22582 | | - utf8_width_print(p->out, w, zVal ? zVal : p->nullValue); |
| 22583 | | - sqlite3_fputs(i==nArg-1 ? "\n" : zSep, p->out); |
| 22584 | | - } |
| 22585 | | - break; |
| 22586 | | - } |
| 22587 | | - case MODE_Semi: { /* .schema and .fullschema output */ |
| 22588 | | - printSchemaLine(p->out, azArg[0], ";\n"); |
| 22589 | | - break; |
| 22590 | | - } |
| 22591 | | - case MODE_Pretty: { /* .schema and .fullschema with --indent */ |
| 22592 | | - char *z; |
| 22593 | | - int j; |
| 22594 | | - int nParen = 0; |
| 22595 | | - char cEnd = 0; |
| 22596 | | - char c; |
| 22597 | | - int nLine = 0; |
| 22598 | | - int isIndex; |
| 22599 | | - int isWhere = 0; |
| 22600 | | - assert( nArg==1 ); |
| 22601 | | - if( azArg[0]==0 ) break; |
| 22602 | | - if( sqlite3_strlike("CREATE VIEW%", azArg[0], 0)==0 |
| 22603 | | - || sqlite3_strlike("CREATE TRIG%", azArg[0], 0)==0 |
| 22604 | | - ){ |
| 22605 | | - sqlite3_fprintf(p->out, "%s;\n", azArg[0]); |
| 22606 | | - break; |
| 22607 | | - } |
| 22608 | | - isIndex = sqlite3_strlike("CREATE INDEX%", azArg[0], 0)==0 |
| 22609 | | - || sqlite3_strlike("CREATE UNIQUE INDEX%", azArg[0], 0)==0; |
| 22610 | | - z = sqlite3_mprintf("%s", azArg[0]); |
| 22611 | | - shell_check_oom(z); |
| 22612 | | - j = 0; |
| 22613 | | - for(i=0; IsSpace(z[i]); i++){} |
| 22614 | | - for(; (c = z[i])!=0; i++){ |
| 22615 | | - if( IsSpace(c) ){ |
| 22616 | | - if( z[j-1]=='\r' ) z[j-1] = '\n'; |
| 22617 | | - if( IsSpace(z[j-1]) || z[j-1]=='(' ) continue; |
| 22618 | | - }else if( (c=='(' || c==')') && j>0 && IsSpace(z[j-1]) ){ |
| 22619 | | - j--; |
| 22620 | | - } |
| 22621 | | - z[j++] = c; |
| 22622 | | - } |
| 22623 | | - while( j>0 && IsSpace(z[j-1]) ){ j--; } |
| 22624 | | - z[j] = 0; |
| 22625 | | - if( strlen30(z)>=79 ){ |
| 22626 | | - for(i=j=0; (c = z[i])!=0; i++){ /* Copy from z[i] back to z[j] */ |
| 22627 | | - if( c==cEnd ){ |
| 22628 | | - cEnd = 0; |
| 22629 | | - }else if( c=='"' || c=='\'' || c=='`' ){ |
| 22630 | | - cEnd = c; |
| 22631 | | - }else if( c=='[' ){ |
| 22632 | | - cEnd = ']'; |
| 22633 | | - }else if( c=='-' && z[i+1]=='-' ){ |
| 22634 | | - cEnd = '\n'; |
| 22635 | | - }else if( c=='(' ){ |
| 22636 | | - nParen++; |
| 22637 | | - }else if( c==')' ){ |
| 22638 | | - nParen--; |
| 22639 | | - if( nLine>0 && nParen==0 && j>0 && !isWhere ){ |
| 22640 | | - printSchemaLineN(p->out, z, j, "\n"); |
| 22641 | | - j = 0; |
| 22642 | | - } |
| 22643 | | - }else if( (c=='w' || c=='W') |
| 22644 | | - && nParen==0 && isIndex |
| 22645 | | - && sqlite3_strnicmp("WHERE",&z[i],5)==0 |
| 22646 | | - && !IsAlnum(z[i+5]) && z[i+5]!='_' ){ |
| 22647 | | - isWhere = 1; |
| 22648 | | - }else if( isWhere && (c=='A' || c=='a') |
| 22649 | | - && nParen==0 |
| 22650 | | - && sqlite3_strnicmp("AND",&z[i],3)==0 |
| 22651 | | - && !IsAlnum(z[i+3]) && z[i+3]!='_' ){ |
| 22652 | | - printSchemaLineN(p->out, z, j, "\n "); |
| 22653 | | - j = 0; |
| 22654 | | - } |
| 22655 | | - z[j++] = c; |
| 22656 | | - if( nParen==1 && cEnd==0 |
| 22657 | | - && (c=='(' || c=='\n' || (c==',' && !wsToEol(z+i+1))) |
| 22658 | | - && !isWhere |
| 22659 | | - ){ |
| 22660 | | - if( c=='\n' ) j--; |
| 22661 | | - printSchemaLineN(p->out, z, j, "\n "); |
| 22662 | | - j = 0; |
| 22663 | | - nLine++; |
| 22664 | | - while( IsSpace(z[i+1]) ){ i++; } |
| 22665 | | - } |
| 22666 | | - } |
| 22667 | | - z[j] = 0; |
| 22668 | | - } |
| 22669 | | - printSchemaLine(p->out, z, ";\n"); |
| 22670 | | - sqlite3_free(z); |
| 22671 | | - break; |
| 22672 | | - } |
| 22673 | | - case MODE_List: { |
| 22674 | | - if( p->cnt++==0 && p->showHeader ){ |
| 22675 | | - for(i=0; i<nArg; i++){ |
| 22676 | | - char *z = azCol[i]; |
| 22677 | | - char *pFree; |
| 22678 | | - const char *zOut = escapeOutput(p, z, &pFree); |
| 22679 | | - sqlite3_fprintf(p->out, "%s%s", zOut, |
| 22680 | | - i==nArg-1 ? p->rowSeparator : p->colSeparator); |
| 22681 | | - if( pFree ) sqlite3_free(pFree); |
| 22682 | | - } |
| 22683 | | - } |
| 22684 | | - if( azArg==0 ) break; |
| 22685 | | - for(i=0; i<nArg; i++){ |
| 22686 | | - char *z = azArg[i]; |
| 22687 | | - char *pFree; |
| 22688 | | - const char *zOut; |
| 22689 | | - if( z==0 ) z = p->nullValue; |
| 22690 | | - zOut = escapeOutput(p, z, &pFree); |
| 22691 | | - sqlite3_fputs(zOut, p->out); |
| 22692 | | - if( pFree ) sqlite3_free(pFree); |
| 22693 | | - sqlite3_fputs((i<nArg-1)? p->colSeparator : p->rowSeparator, p->out); |
| 22694 | | - } |
| 22695 | | - break; |
| 22696 | | - } |
| 22697 | | - case MODE_Www: |
| 22698 | | - case MODE_Html: { |
| 22699 | | - if( p->cnt==0 && p->cMode==MODE_Www ){ |
| 22700 | | - sqlite3_fputs( |
| 22701 | | - "</PRE>\n" |
| 22702 | | - "<TABLE border='1' cellspacing='0' cellpadding='2'>\n" |
| 22703 | | - ,p->out |
| 22704 | | - ); |
| 22705 | | - } |
| 22706 | | - if( p->cnt==0 && (p->showHeader || p->cMode==MODE_Www) ){ |
| 22707 | | - sqlite3_fputs("<TR>", p->out); |
| 22708 | | - for(i=0; i<nArg; i++){ |
| 22709 | | - sqlite3_fputs("<TH>", p->out); |
| 22710 | | - output_html_string(p->out, azCol[i]); |
| 22711 | | - sqlite3_fputs("</TH>\n", p->out); |
| 22712 | | - } |
| 22713 | | - sqlite3_fputs("</TR>\n", p->out); |
| 22714 | | - } |
| 22715 | | - p->cnt++; |
| 22716 | | - if( azArg==0 ) break; |
| 22717 | | - sqlite3_fputs("<TR>", p->out); |
| 22718 | | - for(i=0; i<nArg; i++){ |
| 22719 | | - sqlite3_fputs("<TD>", p->out); |
| 22720 | | - output_html_string(p->out, azArg[i] ? azArg[i] : p->nullValue); |
| 22721 | | - sqlite3_fputs("</TD>\n", p->out); |
| 22722 | | - } |
| 22723 | | - sqlite3_fputs("</TR>\n", p->out); |
| 22724 | | - break; |
| 22725 | | - } |
| 22726 | | - case MODE_Tcl: { |
| 22727 | | - if( p->cnt++==0 && p->showHeader ){ |
| 22728 | | - for(i=0; i<nArg; i++){ |
| 22729 | | - output_c_string(p->out, azCol[i] ? azCol[i] : ""); |
| 22730 | | - if(i<nArg-1) sqlite3_fputs(p->colSeparator, p->out); |
| 22731 | | - } |
| 22732 | | - sqlite3_fputs(p->rowSeparator, p->out); |
| 22733 | | - } |
| 22734 | | - if( azArg==0 ) break; |
| 22735 | | - for(i=0; i<nArg; i++){ |
| 22736 | | - output_c_string(p->out, azArg[i] ? azArg[i] : p->nullValue); |
| 22737 | | - if(i<nArg-1) sqlite3_fputs(p->colSeparator, p->out); |
| 22738 | | - } |
| 22739 | | - sqlite3_fputs(p->rowSeparator, p->out); |
| 22740 | | - break; |
| 22741 | | - } |
| 22742 | | - case MODE_Csv: { |
| 22743 | | - sqlite3_fsetmode(p->out, _O_BINARY); |
| 22744 | | - if( p->cnt++==0 && p->showHeader ){ |
| 22745 | | - for(i=0; i<nArg; i++){ |
| 22746 | | - output_csv(p, azCol[i] ? azCol[i] : "", i<nArg-1); |
| 22747 | | - } |
| 22748 | | - sqlite3_fputs(p->rowSeparator, p->out); |
| 22749 | | - } |
| 22750 | | - if( nArg>0 ){ |
| 22751 | | - for(i=0; i<nArg; i++){ |
| 22752 | | - output_csv(p, azArg[i], i<nArg-1); |
| 22753 | | - } |
| 22754 | | - sqlite3_fputs(p->rowSeparator, p->out); |
| 22755 | | - } |
| 22756 | | - setCrlfMode(p); |
| 22757 | | - break; |
| 22758 | | - } |
| 22759 | | - case MODE_Insert: { |
| 22760 | | - if( azArg==0 ) break; |
| 22761 | | - sqlite3_fprintf(p->out, "INSERT INTO %s",p->zDestTable); |
| 22762 | | - if( p->showHeader ){ |
| 22763 | | - sqlite3_fputs("(", p->out); |
| 22764 | | - for(i=0; i<nArg; i++){ |
| 22765 | | - if( i>0 ) sqlite3_fputs(",", p->out); |
| 22766 | | - if( quoteChar(azCol[i]) ){ |
| 22767 | | - char *z = sqlite3_mprintf("\"%w\"", azCol[i]); |
| 22768 | | - shell_check_oom(z); |
| 22769 | | - sqlite3_fputs(z, p->out); |
| 22770 | | - sqlite3_free(z); |
| 22771 | | - }else{ |
| 22772 | | - sqlite3_fprintf(p->out, "%s", azCol[i]); |
| 22773 | | - } |
| 22774 | | - } |
| 22775 | | - sqlite3_fputs(")", p->out); |
| 22776 | | - } |
| 22777 | | - p->cnt++; |
| 22778 | | - for(i=0; i<nArg; i++){ |
| 22779 | | - sqlite3_fputs(i>0 ? "," : " VALUES(", p->out); |
| 22780 | | - if( (azArg[i]==0) || (aiType && aiType[i]==SQLITE_NULL) ){ |
| 22781 | | - sqlite3_fputs("NULL", p->out); |
| 22782 | | - }else if( aiType && aiType[i]==SQLITE_TEXT ){ |
| 22783 | | - if( ShellHasFlag(p, SHFLG_Newlines) ){ |
| 22784 | | - output_quoted_string(p, azArg[i]); |
| 22785 | | - }else{ |
| 22786 | | - output_quoted_escaped_string(p, azArg[i]); |
| 22787 | | - } |
| 22788 | | - }else if( aiType && aiType[i]==SQLITE_INTEGER ){ |
| 22789 | | - sqlite3_fputs(azArg[i], p->out); |
| 22790 | | - }else if( aiType && aiType[i]==SQLITE_FLOAT ){ |
| 22791 | | - char z[50]; |
| 22792 | | - double r = sqlite3_column_double(p->pStmt, i); |
| 22793 | | - sqlite3_uint64 ur; |
| 22794 | | - memcpy(&ur,&r,sizeof(r)); |
| 22795 | | - if( ur==0x7ff0000000000000LL ){ |
| 22796 | | - sqlite3_fputs("9.0e+999", p->out); |
| 22797 | | - }else if( ur==0xfff0000000000000LL ){ |
| 22798 | | - sqlite3_fputs("-9.0e+999", p->out); |
| 22799 | | - }else{ |
| 22800 | | - sqlite3_int64 ir = (sqlite3_int64)r; |
| 22801 | | - if( r==(double)ir ){ |
| 22802 | | - sqlite3_snprintf(50,z,"%lld.0", ir); |
| 22803 | | - }else{ |
| 22804 | | - sqlite3_snprintf(50,z,"%!.20g", r); |
| 22805 | | - } |
| 22806 | | - sqlite3_fputs(z, p->out); |
| 22807 | | - } |
| 22808 | | - }else if( aiType && aiType[i]==SQLITE_BLOB && p->pStmt ){ |
| 22809 | | - const void *pBlob = sqlite3_column_blob(p->pStmt, i); |
| 22810 | | - int nBlob = sqlite3_column_bytes(p->pStmt, i); |
| 22811 | | - output_hex_blob(p->out, pBlob, nBlob); |
| 22812 | | - }else if( isNumber(azArg[i], 0) ){ |
| 22813 | | - sqlite3_fputs(azArg[i], p->out); |
| 22814 | | - }else if( ShellHasFlag(p, SHFLG_Newlines) ){ |
| 22815 | | - output_quoted_string(p, azArg[i]); |
| 22816 | | - }else{ |
| 22817 | | - output_quoted_escaped_string(p, azArg[i]); |
| 22818 | | - } |
| 22819 | | - } |
| 22820 | | - sqlite3_fputs(");\n", p->out); |
| 22821 | | - break; |
| 22822 | | - } |
| 22823 | | - case MODE_Json: { |
| 22824 | | - if( azArg==0 ) break; |
| 22825 | | - if( p->cnt==0 ){ |
| 22826 | | - sqlite3_fputs("[{", p->out); |
| 22827 | | - }else{ |
| 22828 | | - sqlite3_fputs(",\n{", p->out); |
| 22829 | | - } |
| 22830 | | - p->cnt++; |
| 22831 | | - for(i=0; i<nArg; i++){ |
| 22832 | | - output_json_string(p->out, azCol[i], -1); |
| 22833 | | - sqlite3_fputs(":", p->out); |
| 22834 | | - if( (azArg[i]==0) || (aiType && aiType[i]==SQLITE_NULL) ){ |
| 22835 | | - sqlite3_fputs("null", p->out); |
| 22836 | | - }else if( aiType && aiType[i]==SQLITE_FLOAT ){ |
| 22837 | | - char z[50]; |
| 22838 | | - double r = sqlite3_column_double(p->pStmt, i); |
| 22839 | | - sqlite3_uint64 ur; |
| 22840 | | - memcpy(&ur,&r,sizeof(r)); |
| 22841 | | - if( ur==0x7ff0000000000000LL ){ |
| 22842 | | - sqlite3_fputs("9.0e+999", p->out); |
| 22843 | | - }else if( ur==0xfff0000000000000LL ){ |
| 22844 | | - sqlite3_fputs("-9.0e+999", p->out); |
| 22845 | | - }else{ |
| 22846 | | - sqlite3_snprintf(50,z,"%!.20g", r); |
| 22847 | | - sqlite3_fputs(z, p->out); |
| 22848 | | - } |
| 22849 | | - }else if( aiType && aiType[i]==SQLITE_BLOB && p->pStmt ){ |
| 22850 | | - const void *pBlob = sqlite3_column_blob(p->pStmt, i); |
| 22851 | | - int nBlob = sqlite3_column_bytes(p->pStmt, i); |
| 22852 | | - output_json_string(p->out, pBlob, nBlob); |
| 22853 | | - }else if( aiType && aiType[i]==SQLITE_TEXT ){ |
| 22854 | | - output_json_string(p->out, azArg[i], -1); |
| 22855 | | - }else{ |
| 22856 | | - sqlite3_fputs(azArg[i], p->out); |
| 22857 | | - } |
| 22858 | | - if( i<nArg-1 ){ |
| 22859 | | - sqlite3_fputs(",", p->out); |
| 22860 | | - } |
| 22861 | | - } |
| 22862 | | - sqlite3_fputs("}", p->out); |
| 22863 | | - break; |
| 22864 | | - } |
| 22865 | | - case MODE_Quote: { |
| 22866 | | - if( azArg==0 ) break; |
| 22867 | | - if( p->cnt==0 && p->showHeader ){ |
| 22868 | | - for(i=0; i<nArg; i++){ |
| 22869 | | - if( i>0 ) sqlite3_fputs(p->colSeparator, p->out); |
| 22870 | | - output_quoted_string(p, azCol[i]); |
| 22871 | | - } |
| 22872 | | - sqlite3_fputs(p->rowSeparator, p->out); |
| 22873 | | - } |
| 22874 | | - p->cnt++; |
| 22875 | | - for(i=0; i<nArg; i++){ |
| 22876 | | - if( i>0 ) sqlite3_fputs(p->colSeparator, p->out); |
| 22877 | | - if( (azArg[i]==0) || (aiType && aiType[i]==SQLITE_NULL) ){ |
| 22878 | | - sqlite3_fputs("NULL", p->out); |
| 22879 | | - }else if( aiType && aiType[i]==SQLITE_TEXT ){ |
| 22880 | | - output_quoted_string(p, azArg[i]); |
| 22881 | | - }else if( aiType && aiType[i]==SQLITE_INTEGER ){ |
| 22882 | | - sqlite3_fputs(azArg[i], p->out); |
| 22883 | | - }else if( aiType && aiType[i]==SQLITE_FLOAT ){ |
| 22884 | | - char z[50]; |
| 22885 | | - double r = sqlite3_column_double(p->pStmt, i); |
| 22886 | | - sqlite3_snprintf(50,z,"%!.20g", r); |
| 22887 | | - sqlite3_fputs(z, p->out); |
| 22888 | | - }else if( aiType && aiType[i]==SQLITE_BLOB && p->pStmt ){ |
| 22889 | | - const void *pBlob = sqlite3_column_blob(p->pStmt, i); |
| 22890 | | - int nBlob = sqlite3_column_bytes(p->pStmt, i); |
| 22891 | | - output_hex_blob(p->out, pBlob, nBlob); |
| 22892 | | - }else if( isNumber(azArg[i], 0) ){ |
| 22893 | | - sqlite3_fputs(azArg[i], p->out); |
| 22894 | | - }else{ |
| 22895 | | - output_quoted_string(p, azArg[i]); |
| 22896 | | - } |
| 22897 | | - } |
| 22898 | | - sqlite3_fputs(p->rowSeparator, p->out); |
| 22899 | | - break; |
| 22900 | | - } |
| 22901 | | - case MODE_Ascii: { |
| 22902 | | - if( p->cnt++==0 && p->showHeader ){ |
| 22903 | | - for(i=0; i<nArg; i++){ |
| 22904 | | - if( i>0 ) sqlite3_fputs(p->colSeparator, p->out); |
| 22905 | | - sqlite3_fputs(azCol[i] ? azCol[i] : "", p->out); |
| 22906 | | - } |
| 22907 | | - sqlite3_fputs(p->rowSeparator, p->out); |
| 22908 | | - } |
| 22909 | | - if( azArg==0 ) break; |
| 22910 | | - for(i=0; i<nArg; i++){ |
| 22911 | | - if( i>0 ) sqlite3_fputs(p->colSeparator, p->out); |
| 22912 | | - sqlite3_fputs(azArg[i] ? azArg[i] : p->nullValue, p->out); |
| 22913 | | - } |
| 22914 | | - sqlite3_fputs(p->rowSeparator, p->out); |
| 22915 | | - break; |
| 22916 | | - } |
| 22917 | | - case MODE_EQP: { |
| 22918 | | - eqp_append(p, atoi(azArg[0]), atoi(azArg[1]), azArg[3]); |
| 22919 | | - break; |
| 22920 | | - } |
| 22921 | | - } |
| 22922 | | - return 0; |
| 22923 | | -} |
| 22924 | | - |
| 22925 | | -/* |
| 22926 | | -** This is the callback routine that the SQLite library |
| 22927 | | -** invokes for each row of a query result. |
| 22928 | | -*/ |
| 22929 | | -static int callback(void *pArg, int nArg, char **azArg, char **azCol){ |
| 22930 | | - /* since we don't have type info, call the shell_callback with a NULL value */ |
| 22931 | | - return shell_callback(pArg, nArg, azArg, azCol, NULL); |
| 22932 | | -} |
| 22933 | | - |
| 22934 | 24957 | /* |
| 22935 | 24958 | ** This is the callback routine from sqlite3_exec() that appends all |
| 22936 | 24959 | ** output onto the end of a ShellText object. |
| 22937 | 24960 | */ |
| 22938 | 24961 | static int captureOutputCallback(void *pArg, int nArg, char **azArg, char **az){ |
| | @@ -22988,11 +25011,11 @@ |
| 22988 | 25011 | "INSERT INTO selftest(tno,op,cmd,ans)" |
| 22989 | 25012 | " SELECT rowid*10,op,cmd,ans FROM [_shell$self];\n" |
| 22990 | 25013 | "DROP TABLE [_shell$self];" |
| 22991 | 25014 | ,0,0,&zErrMsg); |
| 22992 | 25015 | if( zErrMsg ){ |
| 22993 | | - sqlite3_fprintf(stderr, "SELFTEST initialization failure: %s\n", zErrMsg); |
| 25016 | + cli_printf(stderr, "SELFTEST initialization failure: %s\n", zErrMsg); |
| 22994 | 25017 | sqlite3_free(zErrMsg); |
| 22995 | 25018 | } |
| 22996 | 25019 | sqlite3_exec(p->db, "RELEASE selftest_init",0,0,0); |
| 22997 | 25020 | } |
| 22998 | 25021 | |
| | @@ -23006,15 +25029,11 @@ |
| 23006 | 25029 | if( p->zDestTable ){ |
| 23007 | 25030 | sqlite3_free(p->zDestTable); |
| 23008 | 25031 | p->zDestTable = 0; |
| 23009 | 25032 | } |
| 23010 | 25033 | if( zName==0 ) return; |
| 23011 | | - if( quoteChar(zName) ){ |
| 23012 | | - p->zDestTable = sqlite3_mprintf("\"%w\"", zName); |
| 23013 | | - }else{ |
| 23014 | | - p->zDestTable = sqlite3_mprintf("%s", zName); |
| 23015 | | - } |
| 25034 | + p->zDestTable = sqlite3_mprintf("%s", zName); |
| 23016 | 25035 | shell_check_oom(p->zDestTable); |
| 23017 | 25036 | } |
| 23018 | 25037 | |
| 23019 | 25038 | /* |
| 23020 | 25039 | ** Maybe construct two lines of text that point out the position of a |
| | @@ -23080,36 +25099,36 @@ |
| 23080 | 25099 | int i; |
| 23081 | 25100 | const char *z; |
| 23082 | 25101 | rc = sqlite3_prepare_v2(p->db, zSelect, -1, &pSelect, 0); |
| 23083 | 25102 | if( rc!=SQLITE_OK || !pSelect ){ |
| 23084 | 25103 | char *zContext = shell_error_context(zSelect, p->db); |
| 23085 | | - sqlite3_fprintf(p->out, "/**** ERROR: (%d) %s *****/\n%s", |
| 25104 | + cli_printf(p->out, "/**** ERROR: (%d) %s *****/\n%s", |
| 23086 | 25105 | rc, sqlite3_errmsg(p->db), zContext); |
| 23087 | 25106 | sqlite3_free(zContext); |
| 23088 | 25107 | if( (rc&0xff)!=SQLITE_CORRUPT ) p->nErr++; |
| 23089 | 25108 | return rc; |
| 23090 | 25109 | } |
| 23091 | 25110 | rc = sqlite3_step(pSelect); |
| 23092 | 25111 | nResult = sqlite3_column_count(pSelect); |
| 23093 | 25112 | while( rc==SQLITE_ROW ){ |
| 23094 | 25113 | z = (const char*)sqlite3_column_text(pSelect, 0); |
| 23095 | | - sqlite3_fprintf(p->out, "%s", z); |
| 25114 | + cli_printf(p->out, "%s", z); |
| 23096 | 25115 | for(i=1; i<nResult; i++){ |
| 23097 | | - sqlite3_fprintf(p->out, ",%s", sqlite3_column_text(pSelect, i)); |
| 25116 | + cli_printf(p->out, ",%s", sqlite3_column_text(pSelect, i)); |
| 23098 | 25117 | } |
| 23099 | 25118 | if( z==0 ) z = ""; |
| 23100 | 25119 | while( z[0] && (z[0]!='-' || z[1]!='-') ) z++; |
| 23101 | 25120 | if( z[0] ){ |
| 23102 | | - sqlite3_fputs("\n;\n", p->out); |
| 25121 | + cli_puts("\n;\n", p->out); |
| 23103 | 25122 | }else{ |
| 23104 | | - sqlite3_fputs(";\n", p->out); |
| 25123 | + cli_puts(";\n", p->out); |
| 23105 | 25124 | } |
| 23106 | 25125 | rc = sqlite3_step(pSelect); |
| 23107 | 25126 | } |
| 23108 | 25127 | rc = sqlite3_finalize(pSelect); |
| 23109 | 25128 | if( rc!=SQLITE_OK ){ |
| 23110 | | - sqlite3_fprintf(p->out, "/**** ERROR: (%d) %s *****/\n", |
| 25129 | + cli_printf(p->out, "/**** ERROR: (%d) %s *****/\n", |
| 23111 | 25130 | rc, sqlite3_errmsg(p->db)); |
| 23112 | 25131 | if( (rc&0xff)!=SQLITE_CORRUPT ) p->nErr++; |
| 23113 | 25132 | } |
| 23114 | 25133 | return rc; |
| 23115 | 25134 | } |
| | @@ -23165,11 +25184,11 @@ |
| 23165 | 25184 | }; |
| 23166 | 25185 | int i; |
| 23167 | 25186 | for(i=0; i<ArraySize(aTrans); i++){ |
| 23168 | 25187 | int n = strlen30(aTrans[i].zPattern); |
| 23169 | 25188 | if( cli_strncmp(aTrans[i].zPattern, z, n)==0 ){ |
| 23170 | | - sqlite3_fprintf(out, "%-36s %s", aTrans[i].zDesc, &z[n]); |
| 25189 | + cli_printf(out, "%-36s %s", aTrans[i].zDesc, &z[n]); |
| 23171 | 25190 | break; |
| 23172 | 25191 | } |
| 23173 | 25192 | } |
| 23174 | 25193 | } |
| 23175 | 25194 | fclose(in); |
| | @@ -23197,11 +25216,11 @@ |
| 23197 | 25216 | if( nPercent>1 ){ |
| 23198 | 25217 | sqlite3_snprintf(sizeof(zLine), zLine, zFormat, iCur, iHiwtr); |
| 23199 | 25218 | }else{ |
| 23200 | 25219 | sqlite3_snprintf(sizeof(zLine), zLine, zFormat, iHiwtr); |
| 23201 | 25220 | } |
| 23202 | | - sqlite3_fprintf(out, "%-36s %s\n", zLabel, zLine); |
| 25221 | + cli_printf(out, "%-36s %s\n", zLabel, zLine); |
| 23203 | 25222 | } |
| 23204 | 25223 | |
| 23205 | 25224 | /* |
| 23206 | 25225 | ** Display memory stats. |
| 23207 | 25226 | */ |
| | @@ -23219,34 +25238,34 @@ |
| 23219 | 25238 | if( pArg->pStmt && pArg->statsOn==2 ){ |
| 23220 | 25239 | int nCol, i, x; |
| 23221 | 25240 | sqlite3_stmt *pStmt = pArg->pStmt; |
| 23222 | 25241 | char z[100]; |
| 23223 | 25242 | nCol = sqlite3_column_count(pStmt); |
| 23224 | | - sqlite3_fprintf(out, "%-36s %d\n", "Number of output columns:", nCol); |
| 25243 | + cli_printf(out, "%-36s %d\n", "Number of output columns:", nCol); |
| 23225 | 25244 | for(i=0; i<nCol; i++){ |
| 23226 | 25245 | sqlite3_snprintf(sizeof(z),z,"Column %d %nname:", i, &x); |
| 23227 | | - sqlite3_fprintf(out, "%-36s %s\n", z, sqlite3_column_name(pStmt,i)); |
| 25246 | + cli_printf(out, "%-36s %s\n", z, sqlite3_column_name(pStmt,i)); |
| 23228 | 25247 | #ifndef SQLITE_OMIT_DECLTYPE |
| 23229 | 25248 | sqlite3_snprintf(30, z+x, "declared type:"); |
| 23230 | | - sqlite3_fprintf(out, "%-36s %s\n", z, sqlite3_column_decltype(pStmt, i)); |
| 25249 | + cli_printf(out, "%-36s %s\n", z, sqlite3_column_decltype(pStmt, i)); |
| 23231 | 25250 | #endif |
| 23232 | 25251 | #ifdef SQLITE_ENABLE_COLUMN_METADATA |
| 23233 | 25252 | sqlite3_snprintf(30, z+x, "database name:"); |
| 23234 | | - sqlite3_fprintf(out, "%-36s %s\n", z, |
| 25253 | + cli_printf(out, "%-36s %s\n", z, |
| 23235 | 25254 | sqlite3_column_database_name(pStmt,i)); |
| 23236 | 25255 | sqlite3_snprintf(30, z+x, "table name:"); |
| 23237 | | - sqlite3_fprintf(out, "%-36s %s\n", z, sqlite3_column_table_name(pStmt,i)); |
| 25256 | + cli_printf(out, "%-36s %s\n", z, sqlite3_column_table_name(pStmt,i)); |
| 23238 | 25257 | sqlite3_snprintf(30, z+x, "origin name:"); |
| 23239 | | - sqlite3_fprintf(out, "%-36s %s\n", z,sqlite3_column_origin_name(pStmt,i)); |
| 25258 | + cli_printf(out, "%-36s %s\n", z,sqlite3_column_origin_name(pStmt,i)); |
| 23240 | 25259 | #endif |
| 23241 | 25260 | } |
| 23242 | 25261 | } |
| 23243 | 25262 | |
| 23244 | 25263 | if( pArg->statsOn==3 ){ |
| 23245 | 25264 | if( pArg->pStmt ){ |
| 23246 | 25265 | iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_VM_STEP,bReset); |
| 23247 | | - sqlite3_fprintf(out, "VM-steps: %d\n", iCur); |
| 25266 | + cli_printf(out, "VM-steps: %d\n", iCur); |
| 23248 | 25267 | } |
| 23249 | 25268 | return 0; |
| 23250 | 25269 | } |
| 23251 | 25270 | |
| 23252 | 25271 | displayStatLine(out, "Memory Used:", |
| | @@ -23271,93 +25290,93 @@ |
| 23271 | 25290 | if( db ){ |
| 23272 | 25291 | if( pArg->shellFlgs & SHFLG_Lookaside ){ |
| 23273 | 25292 | iHiwtr = iCur = -1; |
| 23274 | 25293 | sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_USED, |
| 23275 | 25294 | &iCur, &iHiwtr, bReset); |
| 23276 | | - sqlite3_fprintf(out, |
| 25295 | + cli_printf(out, |
| 23277 | 25296 | "Lookaside Slots Used: %d (max %d)\n", iCur, iHiwtr); |
| 23278 | 25297 | sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_HIT, |
| 23279 | 25298 | &iCur, &iHiwtr, bReset); |
| 23280 | | - sqlite3_fprintf(out, |
| 25299 | + cli_printf(out, |
| 23281 | 25300 | "Successful lookaside attempts: %d\n", iHiwtr); |
| 23282 | 25301 | sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE, |
| 23283 | 25302 | &iCur, &iHiwtr, bReset); |
| 23284 | | - sqlite3_fprintf(out, |
| 25303 | + cli_printf(out, |
| 23285 | 25304 | "Lookaside failures due to size: %d\n", iHiwtr); |
| 23286 | 25305 | sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL, |
| 23287 | 25306 | &iCur, &iHiwtr, bReset); |
| 23288 | | - sqlite3_fprintf(out, |
| 25307 | + cli_printf(out, |
| 23289 | 25308 | "Lookaside failures due to OOM: %d\n", iHiwtr); |
| 23290 | 25309 | } |
| 23291 | 25310 | iHiwtr = iCur = -1; |
| 23292 | 25311 | sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_USED, &iCur, &iHiwtr, bReset); |
| 23293 | | - sqlite3_fprintf(out, |
| 25312 | + cli_printf(out, |
| 23294 | 25313 | "Pager Heap Usage: %d bytes\n", iCur); |
| 23295 | 25314 | iHiwtr = iCur = -1; |
| 23296 | 25315 | sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_HIT, &iCur, &iHiwtr, 1); |
| 23297 | | - sqlite3_fprintf(out, |
| 25316 | + cli_printf(out, |
| 23298 | 25317 | "Page cache hits: %d\n", iCur); |
| 23299 | 25318 | iHiwtr = iCur = -1; |
| 23300 | 25319 | sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_MISS, &iCur, &iHiwtr, 1); |
| 23301 | | - sqlite3_fprintf(out, |
| 25320 | + cli_printf(out, |
| 23302 | 25321 | "Page cache misses: %d\n", iCur); |
| 23303 | 25322 | iHiwtr64 = iCur64 = -1; |
| 23304 | 25323 | sqlite3_db_status64(db, SQLITE_DBSTATUS_TEMPBUF_SPILL, &iCur64, &iHiwtr64, |
| 23305 | 25324 | 0); |
| 23306 | 25325 | iHiwtr = iCur = -1; |
| 23307 | 25326 | sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_WRITE, &iCur, &iHiwtr, 1); |
| 23308 | | - sqlite3_fprintf(out, |
| 25327 | + cli_printf(out, |
| 23309 | 25328 | "Page cache writes: %d\n", iCur); |
| 23310 | 25329 | iHiwtr = iCur = -1; |
| 23311 | 25330 | sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_SPILL, &iCur, &iHiwtr, 1); |
| 23312 | | - sqlite3_fprintf(out, |
| 25331 | + cli_printf(out, |
| 23313 | 25332 | "Page cache spills: %d\n", iCur); |
| 23314 | | - sqlite3_fprintf(out, |
| 25333 | + cli_printf(out, |
| 23315 | 25334 | "Temporary data spilled to disk: %lld\n", iCur64); |
| 23316 | 25335 | sqlite3_db_status64(db, SQLITE_DBSTATUS_TEMPBUF_SPILL, &iCur64, &iHiwtr64, |
| 23317 | 25336 | 1); |
| 23318 | 25337 | iHiwtr = iCur = -1; |
| 23319 | 25338 | sqlite3_db_status(db, SQLITE_DBSTATUS_SCHEMA_USED, &iCur, &iHiwtr, bReset); |
| 23320 | | - sqlite3_fprintf(out, |
| 25339 | + cli_printf(out, |
| 23321 | 25340 | "Schema Heap Usage: %d bytes\n", iCur); |
| 23322 | 25341 | iHiwtr = iCur = -1; |
| 23323 | 25342 | sqlite3_db_status(db, SQLITE_DBSTATUS_STMT_USED, &iCur, &iHiwtr, bReset); |
| 23324 | | - sqlite3_fprintf(out, |
| 25343 | + cli_printf(out, |
| 23325 | 25344 | "Statement Heap/Lookaside Usage: %d bytes\n", iCur); |
| 23326 | 25345 | } |
| 23327 | 25346 | |
| 23328 | 25347 | if( pArg->pStmt ){ |
| 23329 | 25348 | int iHit, iMiss; |
| 23330 | 25349 | iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_FULLSCAN_STEP, |
| 23331 | 25350 | bReset); |
| 23332 | | - sqlite3_fprintf(out, |
| 25351 | + cli_printf(out, |
| 23333 | 25352 | "Fullscan Steps: %d\n", iCur); |
| 23334 | 25353 | iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_SORT, bReset); |
| 23335 | | - sqlite3_fprintf(out, |
| 25354 | + cli_printf(out, |
| 23336 | 25355 | "Sort Operations: %d\n", iCur); |
| 23337 | 25356 | iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_AUTOINDEX,bReset); |
| 23338 | | - sqlite3_fprintf(out, |
| 25357 | + cli_printf(out, |
| 23339 | 25358 | "Autoindex Inserts: %d\n", iCur); |
| 23340 | 25359 | iHit = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_FILTER_HIT, |
| 23341 | 25360 | bReset); |
| 23342 | 25361 | iMiss = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_FILTER_MISS, |
| 23343 | 25362 | bReset); |
| 23344 | 25363 | if( iHit || iMiss ){ |
| 23345 | | - sqlite3_fprintf(out, |
| 25364 | + cli_printf(out, |
| 23346 | 25365 | "Bloom filter bypass taken: %d/%d\n", iHit, iHit+iMiss); |
| 23347 | 25366 | } |
| 23348 | 25367 | iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_VM_STEP, bReset); |
| 23349 | | - sqlite3_fprintf(out, |
| 25368 | + cli_printf(out, |
| 23350 | 25369 | "Virtual Machine Steps: %d\n", iCur); |
| 23351 | 25370 | iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_REPREPARE,bReset); |
| 23352 | | - sqlite3_fprintf(out, |
| 25371 | + cli_printf(out, |
| 23353 | 25372 | "Reprepare operations: %d\n", iCur); |
| 23354 | 25373 | iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_RUN, bReset); |
| 23355 | | - sqlite3_fprintf(out, |
| 25374 | + cli_printf(out, |
| 23356 | 25375 | "Number of times run: %d\n", iCur); |
| 23357 | 25376 | iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_MEMUSED, bReset); |
| 23358 | | - sqlite3_fprintf(out, |
| 25377 | + cli_printf(out, |
| 23359 | 25378 | "Memory used by prepared stmt: %d\n", iCur); |
| 23360 | 25379 | } |
| 23361 | 25380 | |
| 23362 | 25381 | #ifdef __linux__ |
| 23363 | 25382 | displayLinuxIoStats(pArg->out); |
| | @@ -23366,271 +25385,10 @@ |
| 23366 | 25385 | /* Do not remove this machine readable comment: extra-stats-output-here */ |
| 23367 | 25386 | |
| 23368 | 25387 | return 0; |
| 23369 | 25388 | } |
| 23370 | 25389 | |
| 23371 | | - |
| 23372 | | -#ifdef SQLITE_ENABLE_STMT_SCANSTATUS |
| 23373 | | -static int scanStatsHeight(sqlite3_stmt *p, int iEntry){ |
| 23374 | | - int iPid = 0; |
| 23375 | | - int ret = 1; |
| 23376 | | - sqlite3_stmt_scanstatus_v2(p, iEntry, |
| 23377 | | - SQLITE_SCANSTAT_SELECTID, SQLITE_SCANSTAT_COMPLEX, (void*)&iPid |
| 23378 | | - ); |
| 23379 | | - while( iPid!=0 ){ |
| 23380 | | - int ii; |
| 23381 | | - for(ii=0; 1; ii++){ |
| 23382 | | - int iId; |
| 23383 | | - int res; |
| 23384 | | - res = sqlite3_stmt_scanstatus_v2(p, ii, |
| 23385 | | - SQLITE_SCANSTAT_SELECTID, SQLITE_SCANSTAT_COMPLEX, (void*)&iId |
| 23386 | | - ); |
| 23387 | | - if( res ) break; |
| 23388 | | - if( iId==iPid ){ |
| 23389 | | - sqlite3_stmt_scanstatus_v2(p, ii, |
| 23390 | | - SQLITE_SCANSTAT_PARENTID, SQLITE_SCANSTAT_COMPLEX, (void*)&iPid |
| 23391 | | - ); |
| 23392 | | - } |
| 23393 | | - } |
| 23394 | | - ret++; |
| 23395 | | - } |
| 23396 | | - return ret; |
| 23397 | | -} |
| 23398 | | -#endif |
| 23399 | | - |
| 23400 | | -#ifdef SQLITE_ENABLE_STMT_SCANSTATUS |
| 23401 | | -static void display_explain_scanstats( |
| 23402 | | - sqlite3 *db, /* Database to query */ |
| 23403 | | - ShellState *pArg /* Pointer to ShellState */ |
| 23404 | | -){ |
| 23405 | | - static const int f = SQLITE_SCANSTAT_COMPLEX; |
| 23406 | | - sqlite3_stmt *p = pArg->pStmt; |
| 23407 | | - int ii = 0; |
| 23408 | | - i64 nTotal = 0; |
| 23409 | | - int nWidth = 0; |
| 23410 | | - eqp_reset(pArg); |
| 23411 | | - |
| 23412 | | - for(ii=0; 1; ii++){ |
| 23413 | | - const char *z = 0; |
| 23414 | | - int n = 0; |
| 23415 | | - if( sqlite3_stmt_scanstatus_v2(p,ii,SQLITE_SCANSTAT_EXPLAIN,f,(void*)&z) ){ |
| 23416 | | - break; |
| 23417 | | - } |
| 23418 | | - n = (int)strlen(z) + scanStatsHeight(p, ii)*3; |
| 23419 | | - if( n>nWidth ) nWidth = n; |
| 23420 | | - } |
| 23421 | | - nWidth += 4; |
| 23422 | | - |
| 23423 | | - sqlite3_stmt_scanstatus_v2(p, -1, SQLITE_SCANSTAT_NCYCLE, f, (void*)&nTotal); |
| 23424 | | - for(ii=0; 1; ii++){ |
| 23425 | | - i64 nLoop = 0; |
| 23426 | | - i64 nRow = 0; |
| 23427 | | - i64 nCycle = 0; |
| 23428 | | - int iId = 0; |
| 23429 | | - int iPid = 0; |
| 23430 | | - const char *zo = 0; |
| 23431 | | - const char *zName = 0; |
| 23432 | | - char *zText = 0; |
| 23433 | | - double rEst = 0.0; |
| 23434 | | - |
| 23435 | | - if( sqlite3_stmt_scanstatus_v2(p,ii,SQLITE_SCANSTAT_EXPLAIN,f,(void*)&zo) ){ |
| 23436 | | - break; |
| 23437 | | - } |
| 23438 | | - sqlite3_stmt_scanstatus_v2(p, ii, SQLITE_SCANSTAT_EST,f,(void*)&rEst); |
| 23439 | | - sqlite3_stmt_scanstatus_v2(p, ii, SQLITE_SCANSTAT_NLOOP,f,(void*)&nLoop); |
| 23440 | | - sqlite3_stmt_scanstatus_v2(p, ii, SQLITE_SCANSTAT_NVISIT,f,(void*)&nRow); |
| 23441 | | - sqlite3_stmt_scanstatus_v2(p, ii, SQLITE_SCANSTAT_NCYCLE,f,(void*)&nCycle); |
| 23442 | | - sqlite3_stmt_scanstatus_v2(p, ii, SQLITE_SCANSTAT_SELECTID,f,(void*)&iId); |
| 23443 | | - sqlite3_stmt_scanstatus_v2(p, ii, SQLITE_SCANSTAT_PARENTID,f,(void*)&iPid); |
| 23444 | | - sqlite3_stmt_scanstatus_v2(p, ii, SQLITE_SCANSTAT_NAME,f,(void*)&zName); |
| 23445 | | - |
| 23446 | | - zText = sqlite3_mprintf("%s", zo); |
| 23447 | | - if( nCycle>=0 || nLoop>=0 || nRow>=0 ){ |
| 23448 | | - char *z = 0; |
| 23449 | | - if( nCycle>=0 && nTotal>0 ){ |
| 23450 | | - z = sqlite3_mprintf("%zcycles=%lld [%d%%]", z, |
| 23451 | | - nCycle, ((nCycle*100)+nTotal/2) / nTotal |
| 23452 | | - ); |
| 23453 | | - } |
| 23454 | | - if( nLoop>=0 ){ |
| 23455 | | - z = sqlite3_mprintf("%z%sloops=%lld", z, z ? " " : "", nLoop); |
| 23456 | | - } |
| 23457 | | - if( nRow>=0 ){ |
| 23458 | | - z = sqlite3_mprintf("%z%srows=%lld", z, z ? " " : "", nRow); |
| 23459 | | - } |
| 23460 | | - |
| 23461 | | - if( zName && pArg->scanstatsOn>1 ){ |
| 23462 | | - double rpl = (double)nRow / (double)nLoop; |
| 23463 | | - z = sqlite3_mprintf("%z rpl=%.1f est=%.1f", z, rpl, rEst); |
| 23464 | | - } |
| 23465 | | - |
| 23466 | | - zText = sqlite3_mprintf( |
| 23467 | | - "% *z (%z)", -1*(nWidth-scanStatsHeight(p, ii)*3), zText, z |
| 23468 | | - ); |
| 23469 | | - } |
| 23470 | | - |
| 23471 | | - eqp_append(pArg, iId, iPid, zText); |
| 23472 | | - sqlite3_free(zText); |
| 23473 | | - } |
| 23474 | | - |
| 23475 | | - eqp_render(pArg, nTotal); |
| 23476 | | -} |
| 23477 | | -#endif |
| 23478 | | - |
| 23479 | | - |
| 23480 | | -/* |
| 23481 | | -** Parameter azArray points to a zero-terminated array of strings. zStr |
| 23482 | | -** points to a single nul-terminated string. Return non-zero if zStr |
| 23483 | | -** is equal, according to strcmp(), to any of the strings in the array. |
| 23484 | | -** Otherwise, return zero. |
| 23485 | | -*/ |
| 23486 | | -static int str_in_array(const char *zStr, const char **azArray){ |
| 23487 | | - int i; |
| 23488 | | - for(i=0; azArray[i]; i++){ |
| 23489 | | - if( 0==cli_strcmp(zStr, azArray[i]) ) return 1; |
| 23490 | | - } |
| 23491 | | - return 0; |
| 23492 | | -} |
| 23493 | | - |
| 23494 | | -/* |
| 23495 | | -** If compiled statement pSql appears to be an EXPLAIN statement, allocate |
| 23496 | | -** and populate the ShellState.aiIndent[] array with the number of |
| 23497 | | -** spaces each opcode should be indented before it is output. |
| 23498 | | -** |
| 23499 | | -** The indenting rules are: |
| 23500 | | -** |
| 23501 | | -** * For each "Next", "Prev", "VNext" or "VPrev" instruction, indent |
| 23502 | | -** all opcodes that occur between the p2 jump destination and the opcode |
| 23503 | | -** itself by 2 spaces. |
| 23504 | | -** |
| 23505 | | -** * Do the previous for "Return" instructions for when P2 is positive. |
| 23506 | | -** See tag-20220407a in wherecode.c and vdbe.c. |
| 23507 | | -** |
| 23508 | | -** * For each "Goto", if the jump destination is earlier in the program |
| 23509 | | -** and ends on one of: |
| 23510 | | -** Yield SeekGt SeekLt RowSetRead Rewind |
| 23511 | | -** or if the P1 parameter is one instead of zero, |
| 23512 | | -** then indent all opcodes between the earlier instruction |
| 23513 | | -** and "Goto" by 2 spaces. |
| 23514 | | -*/ |
| 23515 | | -static void explain_data_prepare(ShellState *p, sqlite3_stmt *pSql){ |
| 23516 | | - int *abYield = 0; /* True if op is an OP_Yield */ |
| 23517 | | - int nAlloc = 0; /* Allocated size of p->aiIndent[], abYield */ |
| 23518 | | - int iOp; /* Index of operation in p->aiIndent[] */ |
| 23519 | | - |
| 23520 | | - const char *azNext[] = { "Next", "Prev", "VPrev", "VNext", "SorterNext", |
| 23521 | | - "Return", 0 }; |
| 23522 | | - const char *azYield[] = { "Yield", "SeekLT", "SeekGT", "RowSetRead", |
| 23523 | | - "Rewind", 0 }; |
| 23524 | | - const char *azGoto[] = { "Goto", 0 }; |
| 23525 | | - |
| 23526 | | - /* The caller guarantees that the leftmost 4 columns of the statement |
| 23527 | | - ** passed to this function are equivalent to the leftmost 4 columns |
| 23528 | | - ** of EXPLAIN statement output. In practice the statement may be |
| 23529 | | - ** an EXPLAIN, or it may be a query on the bytecode() virtual table. */ |
| 23530 | | - assert( sqlite3_column_count(pSql)>=4 ); |
| 23531 | | - assert( 0==sqlite3_stricmp( sqlite3_column_name(pSql, 0), "addr" ) ); |
| 23532 | | - assert( 0==sqlite3_stricmp( sqlite3_column_name(pSql, 1), "opcode" ) ); |
| 23533 | | - assert( 0==sqlite3_stricmp( sqlite3_column_name(pSql, 2), "p1" ) ); |
| 23534 | | - assert( 0==sqlite3_stricmp( sqlite3_column_name(pSql, 3), "p2" ) ); |
| 23535 | | - |
| 23536 | | - for(iOp=0; SQLITE_ROW==sqlite3_step(pSql); iOp++){ |
| 23537 | | - int i; |
| 23538 | | - int iAddr = sqlite3_column_int(pSql, 0); |
| 23539 | | - const char *zOp = (const char*)sqlite3_column_text(pSql, 1); |
| 23540 | | - int p1 = sqlite3_column_int(pSql, 2); |
| 23541 | | - int p2 = sqlite3_column_int(pSql, 3); |
| 23542 | | - |
| 23543 | | - /* Assuming that p2 is an instruction address, set variable p2op to the |
| 23544 | | - ** index of that instruction in the aiIndent[] array. p2 and p2op may be |
| 23545 | | - ** different if the current instruction is part of a sub-program generated |
| 23546 | | - ** by an SQL trigger or foreign key. */ |
| 23547 | | - int p2op = (p2 + (iOp-iAddr)); |
| 23548 | | - |
| 23549 | | - /* Grow the p->aiIndent array as required */ |
| 23550 | | - if( iOp>=nAlloc ){ |
| 23551 | | - nAlloc += 100; |
| 23552 | | - p->aiIndent = (int*)sqlite3_realloc64(p->aiIndent, nAlloc*sizeof(int)); |
| 23553 | | - shell_check_oom(p->aiIndent); |
| 23554 | | - abYield = (int*)sqlite3_realloc64(abYield, nAlloc*sizeof(int)); |
| 23555 | | - shell_check_oom(abYield); |
| 23556 | | - } |
| 23557 | | - |
| 23558 | | - abYield[iOp] = str_in_array(zOp, azYield); |
| 23559 | | - p->aiIndent[iOp] = 0; |
| 23560 | | - p->nIndent = iOp+1; |
| 23561 | | - if( str_in_array(zOp, azNext) && p2op>0 ){ |
| 23562 | | - for(i=p2op; i<iOp; i++) p->aiIndent[i] += 2; |
| 23563 | | - } |
| 23564 | | - if( str_in_array(zOp, azGoto) && p2op<iOp && (abYield[p2op] || p1) ){ |
| 23565 | | - for(i=p2op; i<iOp; i++) p->aiIndent[i] += 2; |
| 23566 | | - } |
| 23567 | | - } |
| 23568 | | - |
| 23569 | | - p->iIndent = 0; |
| 23570 | | - sqlite3_free(abYield); |
| 23571 | | - sqlite3_reset(pSql); |
| 23572 | | -} |
| 23573 | | - |
| 23574 | | -/* |
| 23575 | | -** Free the array allocated by explain_data_prepare(). |
| 23576 | | -*/ |
| 23577 | | -static void explain_data_delete(ShellState *p){ |
| 23578 | | - sqlite3_free(p->aiIndent); |
| 23579 | | - p->aiIndent = 0; |
| 23580 | | - p->nIndent = 0; |
| 23581 | | - p->iIndent = 0; |
| 23582 | | -} |
| 23583 | | - |
| 23584 | | -static void exec_prepared_stmt(ShellState*, sqlite3_stmt*); |
| 23585 | | - |
| 23586 | | -/* |
| 23587 | | -** Display scan stats. |
| 23588 | | -*/ |
| 23589 | | -static void display_scanstats( |
| 23590 | | - sqlite3 *db, /* Database to query */ |
| 23591 | | - ShellState *pArg /* Pointer to ShellState */ |
| 23592 | | -){ |
| 23593 | | -#ifndef SQLITE_ENABLE_STMT_SCANSTATUS |
| 23594 | | - UNUSED_PARAMETER(db); |
| 23595 | | - UNUSED_PARAMETER(pArg); |
| 23596 | | -#else |
| 23597 | | - if( pArg->scanstatsOn==3 ){ |
| 23598 | | - const char *zSql = |
| 23599 | | - " SELECT addr, opcode, p1, p2, p3, p4, p5, comment, nexec," |
| 23600 | | - " format('% 6s (%.2f%%)'," |
| 23601 | | - " CASE WHEN ncycle<100_000 THEN ncycle || ' '" |
| 23602 | | - " WHEN ncycle<100_000_000 THEN (ncycle/1_000) || 'K'" |
| 23603 | | - " WHEN ncycle<100_000_000_000 THEN (ncycle/1_000_000) || 'M'" |
| 23604 | | - " ELSE (ncycle/1000_000_000) || 'G' END," |
| 23605 | | - " ncycle*100.0/(sum(ncycle) OVER ())" |
| 23606 | | - " ) AS cycles" |
| 23607 | | - " FROM bytecode(?)"; |
| 23608 | | - |
| 23609 | | - int rc = SQLITE_OK; |
| 23610 | | - sqlite3_stmt *pStmt = 0; |
| 23611 | | - rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0); |
| 23612 | | - if( rc==SQLITE_OK ){ |
| 23613 | | - sqlite3_stmt *pSave = pArg->pStmt; |
| 23614 | | - pArg->pStmt = pStmt; |
| 23615 | | - sqlite3_bind_pointer(pStmt, 1, pSave, "stmt-pointer", 0); |
| 23616 | | - |
| 23617 | | - pArg->cnt = 0; |
| 23618 | | - pArg->cMode = MODE_ScanExp; |
| 23619 | | - explain_data_prepare(pArg, pStmt); |
| 23620 | | - exec_prepared_stmt(pArg, pStmt); |
| 23621 | | - explain_data_delete(pArg); |
| 23622 | | - |
| 23623 | | - sqlite3_finalize(pStmt); |
| 23624 | | - pArg->pStmt = pSave; |
| 23625 | | - } |
| 23626 | | - }else{ |
| 23627 | | - display_explain_scanstats(db, pArg); |
| 23628 | | - } |
| 23629 | | -#endif |
| 23630 | | -} |
| 23631 | | - |
| 23632 | 25390 | /* |
| 23633 | 25391 | ** Disable and restore .wheretrace and .treetrace/.selecttrace settings. |
| 23634 | 25392 | */ |
| 23635 | 25393 | static unsigned int savedSelectTrace; |
| 23636 | 25394 | static unsigned int savedWhereTrace; |
| | @@ -23756,584 +25514,10 @@ |
| 23756 | 25514 | sqlite3_reset(pQ); |
| 23757 | 25515 | } |
| 23758 | 25516 | sqlite3_finalize(pQ); |
| 23759 | 25517 | } |
| 23760 | 25518 | |
| 23761 | | -/* |
| 23762 | | -** UTF8 box-drawing characters. Imagine box lines like this: |
| 23763 | | -** |
| 23764 | | -** 1 |
| 23765 | | -** | |
| 23766 | | -** 4 --+-- 2 |
| 23767 | | -** | |
| 23768 | | -** 3 |
| 23769 | | -** |
| 23770 | | -** Each box characters has between 2 and 4 of the lines leading from |
| 23771 | | -** the center. The characters are here identified by the numbers of |
| 23772 | | -** their corresponding lines. |
| 23773 | | -*/ |
| 23774 | | -#define BOX_24 "\342\224\200" /* U+2500 --- */ |
| 23775 | | -#define BOX_13 "\342\224\202" /* U+2502 | */ |
| 23776 | | -#define BOX_23 "\342\224\214" /* U+250c ,- */ |
| 23777 | | -#define BOX_34 "\342\224\220" /* U+2510 -, */ |
| 23778 | | -#define BOX_12 "\342\224\224" /* U+2514 '- */ |
| 23779 | | -#define BOX_14 "\342\224\230" /* U+2518 -' */ |
| 23780 | | -#define BOX_123 "\342\224\234" /* U+251c |- */ |
| 23781 | | -#define BOX_134 "\342\224\244" /* U+2524 -| */ |
| 23782 | | -#define BOX_234 "\342\224\254" /* U+252c -,- */ |
| 23783 | | -#define BOX_124 "\342\224\264" /* U+2534 -'- */ |
| 23784 | | -#define BOX_1234 "\342\224\274" /* U+253c -|- */ |
| 23785 | | - |
| 23786 | | -/* Draw horizontal line N characters long using unicode box |
| 23787 | | -** characters |
| 23788 | | -*/ |
| 23789 | | -static void print_box_line(FILE *out, int N){ |
| 23790 | | - const char zDash[] = |
| 23791 | | - BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 |
| 23792 | | - BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24; |
| 23793 | | - const int nDash = sizeof(zDash) - 1; |
| 23794 | | - N *= 3; |
| 23795 | | - while( N>nDash ){ |
| 23796 | | - sqlite3_fputs(zDash, out); |
| 23797 | | - N -= nDash; |
| 23798 | | - } |
| 23799 | | - sqlite3_fprintf(out, "%.*s", N, zDash); |
| 23800 | | -} |
| 23801 | | - |
| 23802 | | -/* |
| 23803 | | -** Draw a horizontal separator for a MODE_Box table. |
| 23804 | | -*/ |
| 23805 | | -static void print_box_row_separator( |
| 23806 | | - ShellState *p, |
| 23807 | | - int nArg, |
| 23808 | | - const char *zSep1, |
| 23809 | | - const char *zSep2, |
| 23810 | | - const char *zSep3 |
| 23811 | | -){ |
| 23812 | | - int i; |
| 23813 | | - if( nArg>0 ){ |
| 23814 | | - sqlite3_fputs(zSep1, p->out); |
| 23815 | | - print_box_line(p->out, p->actualWidth[0]+2); |
| 23816 | | - for(i=1; i<nArg; i++){ |
| 23817 | | - sqlite3_fputs(zSep2, p->out); |
| 23818 | | - print_box_line(p->out, p->actualWidth[i]+2); |
| 23819 | | - } |
| 23820 | | - sqlite3_fputs(zSep3, p->out); |
| 23821 | | - } |
| 23822 | | - sqlite3_fputs("\n", p->out); |
| 23823 | | -} |
| 23824 | | - |
| 23825 | | -/* |
| 23826 | | -** z[] is a line of text that is to be displayed the .mode box or table or |
| 23827 | | -** similar tabular formats. z[] might contain control characters such |
| 23828 | | -** as \n, \t, \f, or \r. |
| 23829 | | -** |
| 23830 | | -** Compute characters to display on the first line of z[]. Stop at the |
| 23831 | | -** first \r, \n, or \f. Expand \t into spaces. Return a copy (obtained |
| 23832 | | -** from malloc()) of that first line, which caller should free sometime. |
| 23833 | | -** Write anything to display on the next line into *pzTail. If this is |
| 23834 | | -** the last line, write a NULL into *pzTail. (*pzTail is not allocated.) |
| 23835 | | -*/ |
| 23836 | | -static char *translateForDisplayAndDup( |
| 23837 | | - ShellState *p, /* To access current settings */ |
| 23838 | | - const unsigned char *z, /* Input text to be transformed */ |
| 23839 | | - const unsigned char **pzTail, /* OUT: Tail of the input for next line */ |
| 23840 | | - int mxWidth, /* Max width. 0 means no limit */ |
| 23841 | | - u8 bWordWrap /* If true, avoid breaking mid-word */ |
| 23842 | | -){ |
| 23843 | | - int i; /* Input bytes consumed */ |
| 23844 | | - int j; /* Output bytes generated */ |
| 23845 | | - int k; /* Input bytes to be displayed */ |
| 23846 | | - int n; /* Output column number */ |
| 23847 | | - unsigned char *zOut; /* Output text */ |
| 23848 | | - |
| 23849 | | - if( z==0 ){ |
| 23850 | | - *pzTail = 0; |
| 23851 | | - return 0; |
| 23852 | | - } |
| 23853 | | - if( mxWidth<0 ) mxWidth = -mxWidth; |
| 23854 | | - if( mxWidth==0 ) mxWidth = 1000000; |
| 23855 | | - i = j = n = 0; |
| 23856 | | - while( n<mxWidth ){ |
| 23857 | | - unsigned char c = z[i]; |
| 23858 | | - if( c>=0xc0 ){ |
| 23859 | | - int u; |
| 23860 | | - int len = decodeUtf8(&z[i], &u); |
| 23861 | | - i += len; |
| 23862 | | - j += len; |
| 23863 | | - n += cli_wcwidth(u); |
| 23864 | | - continue; |
| 23865 | | - } |
| 23866 | | - if( c>=' ' ){ |
| 23867 | | - n++; |
| 23868 | | - i++; |
| 23869 | | - j++; |
| 23870 | | - continue; |
| 23871 | | - } |
| 23872 | | - if( c==0 || c=='\n' || (c=='\r' && z[i+1]=='\n') ) break; |
| 23873 | | - if( c=='\t' ){ |
| 23874 | | - do{ |
| 23875 | | - n++; |
| 23876 | | - j++; |
| 23877 | | - }while( (n&7)!=0 && n<mxWidth ); |
| 23878 | | - i++; |
| 23879 | | - continue; |
| 23880 | | - } |
| 23881 | | - if( c==0x1b && p->eEscMode==SHELL_ESC_OFF && (k = isVt100(&z[i]))>0 ){ |
| 23882 | | - i += k; |
| 23883 | | - j += k; |
| 23884 | | - }else{ |
| 23885 | | - n++; |
| 23886 | | - j += 3; |
| 23887 | | - i++; |
| 23888 | | - } |
| 23889 | | - } |
| 23890 | | - if( n>=mxWidth && bWordWrap ){ |
| 23891 | | - /* Perhaps try to back up to a better place to break the line */ |
| 23892 | | - for(k=i; k>i/2; k--){ |
| 23893 | | - if( IsSpace(z[k-1]) ) break; |
| 23894 | | - } |
| 23895 | | - if( k<=i/2 ){ |
| 23896 | | - for(k=i; k>i/2; k--){ |
| 23897 | | - if( IsAlnum(z[k-1])!=IsAlnum(z[k]) && (z[k]&0xc0)!=0x80 ) break; |
| 23898 | | - } |
| 23899 | | - } |
| 23900 | | - if( k<=i/2 ){ |
| 23901 | | - k = i; |
| 23902 | | - }else{ |
| 23903 | | - i = k; |
| 23904 | | - while( z[i]==' ' ) i++; |
| 23905 | | - } |
| 23906 | | - }else{ |
| 23907 | | - k = i; |
| 23908 | | - } |
| 23909 | | - if( n>=mxWidth && z[i]>=' ' ){ |
| 23910 | | - *pzTail = &z[i]; |
| 23911 | | - }else if( z[i]=='\r' && z[i+1]=='\n' ){ |
| 23912 | | - *pzTail = z[i+2] ? &z[i+2] : 0; |
| 23913 | | - }else if( z[i]==0 || z[i+1]==0 ){ |
| 23914 | | - *pzTail = 0; |
| 23915 | | - }else{ |
| 23916 | | - *pzTail = &z[i+1]; |
| 23917 | | - } |
| 23918 | | - zOut = malloc( j+1 ); |
| 23919 | | - shell_check_oom(zOut); |
| 23920 | | - i = j = n = 0; |
| 23921 | | - while( i<k ){ |
| 23922 | | - unsigned char c = z[i]; |
| 23923 | | - if( c>=0xc0 ){ |
| 23924 | | - int u; |
| 23925 | | - int len = decodeUtf8(&z[i], &u); |
| 23926 | | - do{ zOut[j++] = z[i++]; }while( (--len)>0 ); |
| 23927 | | - n += cli_wcwidth(u); |
| 23928 | | - continue; |
| 23929 | | - } |
| 23930 | | - if( c>=' ' ){ |
| 23931 | | - n++; |
| 23932 | | - zOut[j++] = z[i++]; |
| 23933 | | - continue; |
| 23934 | | - } |
| 23935 | | - if( c==0 ) break; |
| 23936 | | - if( z[i]=='\t' ){ |
| 23937 | | - do{ |
| 23938 | | - n++; |
| 23939 | | - zOut[j++] = ' '; |
| 23940 | | - }while( (n&7)!=0 && n<mxWidth ); |
| 23941 | | - i++; |
| 23942 | | - continue; |
| 23943 | | - } |
| 23944 | | - switch( p->eEscMode ){ |
| 23945 | | - case SHELL_ESC_SYMBOL: |
| 23946 | | - zOut[j++] = 0xe2; |
| 23947 | | - zOut[j++] = 0x90; |
| 23948 | | - zOut[j++] = 0x80 + c; |
| 23949 | | - break; |
| 23950 | | - case SHELL_ESC_ASCII: |
| 23951 | | - zOut[j++] = '^'; |
| 23952 | | - zOut[j++] = 0x40 + c; |
| 23953 | | - break; |
| 23954 | | - case SHELL_ESC_OFF: { |
| 23955 | | - int nn; |
| 23956 | | - if( c==0x1b && (nn = isVt100(&z[i]))>0 ){ |
| 23957 | | - memcpy(&zOut[j], &z[i], nn); |
| 23958 | | - j += nn; |
| 23959 | | - i += nn - 1; |
| 23960 | | - }else{ |
| 23961 | | - zOut[j++] = c; |
| 23962 | | - } |
| 23963 | | - break; |
| 23964 | | - } |
| 23965 | | - } |
| 23966 | | - i++; |
| 23967 | | - } |
| 23968 | | - zOut[j] = 0; |
| 23969 | | - return (char*)zOut; |
| 23970 | | -} |
| 23971 | | - |
| 23972 | | -/* Return true if the text string z[] contains characters that need |
| 23973 | | -** unistr() escaping. |
| 23974 | | -*/ |
| 23975 | | -static int needUnistr(const unsigned char *z){ |
| 23976 | | - unsigned char c; |
| 23977 | | - if( z==0 ) return 0; |
| 23978 | | - while( (c = *z)>0x1f || c=='\t' || c=='\n' || (c=='\r' && z[1]=='\n') ){ z++; } |
| 23979 | | - return c!=0; |
| 23980 | | -} |
| 23981 | | - |
| 23982 | | -/* Extract the value of the i-th current column for pStmt as an SQL literal |
| 23983 | | -** value. Memory is obtained from sqlite3_malloc64() and must be freed by |
| 23984 | | -** the caller. |
| 23985 | | -*/ |
| 23986 | | -static char *quoted_column(sqlite3_stmt *pStmt, int i){ |
| 23987 | | - switch( sqlite3_column_type(pStmt, i) ){ |
| 23988 | | - case SQLITE_NULL: { |
| 23989 | | - return sqlite3_mprintf("NULL"); |
| 23990 | | - } |
| 23991 | | - case SQLITE_INTEGER: |
| 23992 | | - case SQLITE_FLOAT: { |
| 23993 | | - return sqlite3_mprintf("%s",sqlite3_column_text(pStmt,i)); |
| 23994 | | - } |
| 23995 | | - case SQLITE_TEXT: { |
| 23996 | | - const unsigned char *zText = sqlite3_column_text(pStmt,i); |
| 23997 | | - return sqlite3_mprintf(needUnistr(zText)?"%#Q":"%Q",zText); |
| 23998 | | - } |
| 23999 | | - case SQLITE_BLOB: { |
| 24000 | | - int j; |
| 24001 | | - sqlite3_str *pStr = sqlite3_str_new(0); |
| 24002 | | - const unsigned char *a = sqlite3_column_blob(pStmt,i); |
| 24003 | | - int n = sqlite3_column_bytes(pStmt,i); |
| 24004 | | - sqlite3_str_append(pStr, "x'", 2); |
| 24005 | | - for(j=0; j<n; j++){ |
| 24006 | | - sqlite3_str_appendf(pStr, "%02x", a[j]); |
| 24007 | | - } |
| 24008 | | - sqlite3_str_append(pStr, "'", 1); |
| 24009 | | - return sqlite3_str_finish(pStr); |
| 24010 | | - } |
| 24011 | | - } |
| 24012 | | - return 0; /* Not reached */ |
| 24013 | | -} |
| 24014 | | - |
| 24015 | | -/* |
| 24016 | | -** Run a prepared statement and output the result in one of the |
| 24017 | | -** table-oriented formats: MODE_Column, MODE_Markdown, MODE_Table, |
| 24018 | | -** or MODE_Box. |
| 24019 | | -** |
| 24020 | | -** This is different from ordinary exec_prepared_stmt() in that |
| 24021 | | -** it has to run the entire query and gather the results into memory |
| 24022 | | -** first, in order to determine column widths, before providing |
| 24023 | | -** any output. |
| 24024 | | -*/ |
| 24025 | | -static void exec_prepared_stmt_columnar( |
| 24026 | | - ShellState *p, /* Pointer to ShellState */ |
| 24027 | | - sqlite3_stmt *pStmt /* Statement to run */ |
| 24028 | | -){ |
| 24029 | | - sqlite3_int64 nRow = 0; |
| 24030 | | - int nColumn = 0; |
| 24031 | | - char **azData = 0; |
| 24032 | | - sqlite3_int64 nAlloc = 0; |
| 24033 | | - char *abRowDiv = 0; |
| 24034 | | - const unsigned char *uz; |
| 24035 | | - const char *z; |
| 24036 | | - char **azQuoted = 0; |
| 24037 | | - int rc; |
| 24038 | | - sqlite3_int64 i, nData; |
| 24039 | | - int j, nTotal, w, n; |
| 24040 | | - const char *colSep = 0; |
| 24041 | | - const char *rowSep = 0; |
| 24042 | | - const unsigned char **azNextLine = 0; |
| 24043 | | - int bNextLine = 0; |
| 24044 | | - int bMultiLineRowExists = 0; |
| 24045 | | - int bw = p->cmOpts.bWordWrap; |
| 24046 | | - const char *zEmpty = ""; |
| 24047 | | - const char *zShowNull = p->nullValue; |
| 24048 | | - |
| 24049 | | - rc = sqlite3_step(pStmt); |
| 24050 | | - if( rc!=SQLITE_ROW ) return; |
| 24051 | | - nColumn = sqlite3_column_count(pStmt); |
| 24052 | | - if( nColumn==0 ) goto columnar_end; |
| 24053 | | - nAlloc = nColumn*4; |
| 24054 | | - if( nAlloc<=0 ) nAlloc = 1; |
| 24055 | | - azData = sqlite3_malloc64( nAlloc*sizeof(char*) ); |
| 24056 | | - shell_check_oom(azData); |
| 24057 | | - azNextLine = sqlite3_malloc64( nColumn*sizeof(char*) ); |
| 24058 | | - shell_check_oom(azNextLine); |
| 24059 | | - memset((void*)azNextLine, 0, nColumn*sizeof(char*) ); |
| 24060 | | - if( p->cmOpts.bQuote ){ |
| 24061 | | - azQuoted = sqlite3_malloc64( nColumn*sizeof(char*) ); |
| 24062 | | - shell_check_oom(azQuoted); |
| 24063 | | - memset(azQuoted, 0, nColumn*sizeof(char*) ); |
| 24064 | | - } |
| 24065 | | - abRowDiv = sqlite3_malloc64( nAlloc/nColumn ); |
| 24066 | | - shell_check_oom(abRowDiv); |
| 24067 | | - if( nColumn>p->nWidth ){ |
| 24068 | | - p->colWidth = realloc(p->colWidth, (nColumn+1)*2*sizeof(int)); |
| 24069 | | - shell_check_oom(p->colWidth); |
| 24070 | | - for(i=p->nWidth; i<nColumn; i++) p->colWidth[i] = 0; |
| 24071 | | - p->nWidth = nColumn; |
| 24072 | | - p->actualWidth = &p->colWidth[nColumn]; |
| 24073 | | - } |
| 24074 | | - memset(p->actualWidth, 0, nColumn*sizeof(int)); |
| 24075 | | - for(i=0; i<nColumn; i++){ |
| 24076 | | - w = p->colWidth[i]; |
| 24077 | | - if( w<0 ) w = -w; |
| 24078 | | - p->actualWidth[i] = w; |
| 24079 | | - } |
| 24080 | | - for(i=0; i<nColumn; i++){ |
| 24081 | | - const unsigned char *zNotUsed; |
| 24082 | | - int wx = p->colWidth[i]; |
| 24083 | | - if( wx==0 ){ |
| 24084 | | - wx = p->cmOpts.iWrap; |
| 24085 | | - } |
| 24086 | | - if( wx<0 ) wx = -wx; |
| 24087 | | - uz = (const unsigned char*)sqlite3_column_name(pStmt,i); |
| 24088 | | - if( uz==0 ) uz = (u8*)""; |
| 24089 | | - azData[i] = translateForDisplayAndDup(p, uz, &zNotUsed, wx, bw); |
| 24090 | | - } |
| 24091 | | - do{ |
| 24092 | | - int useNextLine = bNextLine; |
| 24093 | | - bNextLine = 0; |
| 24094 | | - if( (nRow+2)*nColumn >= nAlloc ){ |
| 24095 | | - nAlloc *= 2; |
| 24096 | | - azData = sqlite3_realloc64(azData, nAlloc*sizeof(char*)); |
| 24097 | | - shell_check_oom(azData); |
| 24098 | | - abRowDiv = sqlite3_realloc64(abRowDiv, nAlloc/nColumn); |
| 24099 | | - shell_check_oom(abRowDiv); |
| 24100 | | - } |
| 24101 | | - abRowDiv[nRow] = 1; |
| 24102 | | - nRow++; |
| 24103 | | - for(i=0; i<nColumn; i++){ |
| 24104 | | - int wx = p->colWidth[i]; |
| 24105 | | - if( wx==0 ){ |
| 24106 | | - wx = p->cmOpts.iWrap; |
| 24107 | | - } |
| 24108 | | - if( wx<0 ) wx = -wx; |
| 24109 | | - if( useNextLine ){ |
| 24110 | | - uz = azNextLine[i]; |
| 24111 | | - if( uz==0 ) uz = (u8*)zEmpty; |
| 24112 | | - }else if( p->cmOpts.bQuote ){ |
| 24113 | | - assert( azQuoted!=0 ); |
| 24114 | | - sqlite3_free(azQuoted[i]); |
| 24115 | | - azQuoted[i] = quoted_column(pStmt,i); |
| 24116 | | - uz = (const unsigned char*)azQuoted[i]; |
| 24117 | | - }else{ |
| 24118 | | - uz = (const unsigned char*)sqlite3_column_text(pStmt,i); |
| 24119 | | - if( uz==0 ) uz = (u8*)zShowNull; |
| 24120 | | - } |
| 24121 | | - azData[nRow*nColumn + i] |
| 24122 | | - = translateForDisplayAndDup(p, uz, &azNextLine[i], wx, bw); |
| 24123 | | - if( azNextLine[i] ){ |
| 24124 | | - bNextLine = 1; |
| 24125 | | - abRowDiv[nRow-1] = 0; |
| 24126 | | - bMultiLineRowExists = 1; |
| 24127 | | - } |
| 24128 | | - } |
| 24129 | | - }while( bNextLine || sqlite3_step(pStmt)==SQLITE_ROW ); |
| 24130 | | - nTotal = nColumn*(nRow+1); |
| 24131 | | - for(i=0; i<nTotal; i++){ |
| 24132 | | - z = azData[i]; |
| 24133 | | - if( z==0 ) z = (char*)zEmpty; |
| 24134 | | - n = strlenChar(z); |
| 24135 | | - j = i%nColumn; |
| 24136 | | - if( n>p->actualWidth[j] ) p->actualWidth[j] = n; |
| 24137 | | - } |
| 24138 | | - if( seenInterrupt ) goto columnar_end; |
| 24139 | | - switch( p->cMode ){ |
| 24140 | | - case MODE_Column: { |
| 24141 | | - colSep = " "; |
| 24142 | | - rowSep = "\n"; |
| 24143 | | - if( p->showHeader ){ |
| 24144 | | - for(i=0; i<nColumn; i++){ |
| 24145 | | - w = p->actualWidth[i]; |
| 24146 | | - if( p->colWidth[i]<0 ) w = -w; |
| 24147 | | - utf8_width_print(p->out, w, azData[i]); |
| 24148 | | - sqlite3_fputs(i==nColumn-1?"\n":" ", p->out); |
| 24149 | | - } |
| 24150 | | - for(i=0; i<nColumn; i++){ |
| 24151 | | - print_dashes(p->out, p->actualWidth[i]); |
| 24152 | | - sqlite3_fputs(i==nColumn-1?"\n":" ", p->out); |
| 24153 | | - } |
| 24154 | | - } |
| 24155 | | - break; |
| 24156 | | - } |
| 24157 | | - case MODE_Table: { |
| 24158 | | - colSep = " | "; |
| 24159 | | - rowSep = " |\n"; |
| 24160 | | - print_row_separator(p, nColumn, "+"); |
| 24161 | | - sqlite3_fputs("| ", p->out); |
| 24162 | | - for(i=0; i<nColumn; i++){ |
| 24163 | | - w = p->actualWidth[i]; |
| 24164 | | - n = strlenChar(azData[i]); |
| 24165 | | - sqlite3_fprintf(p->out, "%*s%s%*s", (w-n)/2, "", |
| 24166 | | - azData[i], (w-n+1)/2, ""); |
| 24167 | | - sqlite3_fputs(i==nColumn-1?" |\n":" | ", p->out); |
| 24168 | | - } |
| 24169 | | - print_row_separator(p, nColumn, "+"); |
| 24170 | | - break; |
| 24171 | | - } |
| 24172 | | - case MODE_Markdown: { |
| 24173 | | - colSep = " | "; |
| 24174 | | - rowSep = " |\n"; |
| 24175 | | - sqlite3_fputs("| ", p->out); |
| 24176 | | - for(i=0; i<nColumn; i++){ |
| 24177 | | - w = p->actualWidth[i]; |
| 24178 | | - n = strlenChar(azData[i]); |
| 24179 | | - sqlite3_fprintf(p->out, "%*s%s%*s", (w-n)/2, "", |
| 24180 | | - azData[i], (w-n+1)/2, ""); |
| 24181 | | - sqlite3_fputs(i==nColumn-1?" |\n":" | ", p->out); |
| 24182 | | - } |
| 24183 | | - print_row_separator(p, nColumn, "|"); |
| 24184 | | - break; |
| 24185 | | - } |
| 24186 | | - case MODE_Box: { |
| 24187 | | - colSep = " " BOX_13 " "; |
| 24188 | | - rowSep = " " BOX_13 "\n"; |
| 24189 | | - print_box_row_separator(p, nColumn, BOX_23, BOX_234, BOX_34); |
| 24190 | | - sqlite3_fputs(BOX_13 " ", p->out); |
| 24191 | | - for(i=0; i<nColumn; i++){ |
| 24192 | | - w = p->actualWidth[i]; |
| 24193 | | - n = strlenChar(azData[i]); |
| 24194 | | - sqlite3_fprintf(p->out, "%*s%s%*s%s", |
| 24195 | | - (w-n)/2, "", azData[i], (w-n+1)/2, "", |
| 24196 | | - i==nColumn-1?" "BOX_13"\n":" "BOX_13" "); |
| 24197 | | - } |
| 24198 | | - print_box_row_separator(p, nColumn, BOX_123, BOX_1234, BOX_134); |
| 24199 | | - break; |
| 24200 | | - } |
| 24201 | | - } |
| 24202 | | - for(i=nColumn, j=0; i<nTotal; i++, j++){ |
| 24203 | | - if( j==0 && p->cMode!=MODE_Column ){ |
| 24204 | | - sqlite3_fputs(p->cMode==MODE_Box?BOX_13" ":"| ", p->out); |
| 24205 | | - } |
| 24206 | | - z = azData[i]; |
| 24207 | | - if( z==0 ) z = p->nullValue; |
| 24208 | | - w = p->actualWidth[j]; |
| 24209 | | - if( p->colWidth[j]<0 ) w = -w; |
| 24210 | | - utf8_width_print(p->out, w, z); |
| 24211 | | - if( j==nColumn-1 ){ |
| 24212 | | - sqlite3_fputs(rowSep, p->out); |
| 24213 | | - if( bMultiLineRowExists && abRowDiv[i/nColumn-1] && i+1<nTotal ){ |
| 24214 | | - if( p->cMode==MODE_Table ){ |
| 24215 | | - print_row_separator(p, nColumn, "+"); |
| 24216 | | - }else if( p->cMode==MODE_Box ){ |
| 24217 | | - print_box_row_separator(p, nColumn, BOX_123, BOX_1234, BOX_134); |
| 24218 | | - }else if( p->cMode==MODE_Column ){ |
| 24219 | | - sqlite3_fputs("\n", p->out); |
| 24220 | | - } |
| 24221 | | - } |
| 24222 | | - j = -1; |
| 24223 | | - if( seenInterrupt ) goto columnar_end; |
| 24224 | | - }else{ |
| 24225 | | - sqlite3_fputs(colSep, p->out); |
| 24226 | | - } |
| 24227 | | - } |
| 24228 | | - if( p->cMode==MODE_Table ){ |
| 24229 | | - print_row_separator(p, nColumn, "+"); |
| 24230 | | - }else if( p->cMode==MODE_Box ){ |
| 24231 | | - print_box_row_separator(p, nColumn, BOX_12, BOX_124, BOX_14); |
| 24232 | | - } |
| 24233 | | -columnar_end: |
| 24234 | | - if( seenInterrupt ){ |
| 24235 | | - sqlite3_fputs("Interrupt\n", p->out); |
| 24236 | | - } |
| 24237 | | - nData = (nRow+1)*nColumn; |
| 24238 | | - for(i=0; i<nData; i++){ |
| 24239 | | - z = azData[i]; |
| 24240 | | - if( z!=zEmpty && z!=zShowNull ) free(azData[i]); |
| 24241 | | - } |
| 24242 | | - sqlite3_free(azData); |
| 24243 | | - sqlite3_free((void*)azNextLine); |
| 24244 | | - sqlite3_free(abRowDiv); |
| 24245 | | - if( azQuoted ){ |
| 24246 | | - for(i=0; i<nColumn; i++) sqlite3_free(azQuoted[i]); |
| 24247 | | - sqlite3_free(azQuoted); |
| 24248 | | - } |
| 24249 | | -} |
| 24250 | | - |
| 24251 | | -/* |
| 24252 | | -** Run a prepared statement |
| 24253 | | -*/ |
| 24254 | | -static void exec_prepared_stmt( |
| 24255 | | - ShellState *pArg, /* Pointer to ShellState */ |
| 24256 | | - sqlite3_stmt *pStmt /* Statement to run */ |
| 24257 | | -){ |
| 24258 | | - int rc; |
| 24259 | | - sqlite3_uint64 nRow = 0; |
| 24260 | | - |
| 24261 | | - if( pArg->cMode==MODE_Column |
| 24262 | | - || pArg->cMode==MODE_Table |
| 24263 | | - || pArg->cMode==MODE_Box |
| 24264 | | - || pArg->cMode==MODE_Markdown |
| 24265 | | - ){ |
| 24266 | | - exec_prepared_stmt_columnar(pArg, pStmt); |
| 24267 | | - return; |
| 24268 | | - } |
| 24269 | | - |
| 24270 | | - /* perform the first step. this will tell us if we |
| 24271 | | - ** have a result set or not and how wide it is. |
| 24272 | | - */ |
| 24273 | | - rc = sqlite3_step(pStmt); |
| 24274 | | - /* if we have a result set... */ |
| 24275 | | - if( SQLITE_ROW == rc ){ |
| 24276 | | - /* allocate space for col name ptr, value ptr, and type */ |
| 24277 | | - int nCol = sqlite3_column_count(pStmt); |
| 24278 | | - void *pData = sqlite3_malloc64(3*nCol*sizeof(const char*) + 1); |
| 24279 | | - if( !pData ){ |
| 24280 | | - shell_out_of_memory(); |
| 24281 | | - }else{ |
| 24282 | | - char **azCols = (char **)pData; /* Names of result columns */ |
| 24283 | | - char **azVals = &azCols[nCol]; /* Results */ |
| 24284 | | - int *aiTypes = (int *)&azVals[nCol]; /* Result types */ |
| 24285 | | - int i, x; |
| 24286 | | - assert(sizeof(int) <= sizeof(char *)); |
| 24287 | | - /* save off ptrs to column names */ |
| 24288 | | - for(i=0; i<nCol; i++){ |
| 24289 | | - azCols[i] = (char *)sqlite3_column_name(pStmt, i); |
| 24290 | | - } |
| 24291 | | - do{ |
| 24292 | | - nRow++; |
| 24293 | | - /* extract the data and data types */ |
| 24294 | | - for(i=0; i<nCol; i++){ |
| 24295 | | - aiTypes[i] = x = sqlite3_column_type(pStmt, i); |
| 24296 | | - if( x==SQLITE_BLOB |
| 24297 | | - && pArg |
| 24298 | | - && (pArg->cMode==MODE_Insert || pArg->cMode==MODE_Quote) |
| 24299 | | - ){ |
| 24300 | | - azVals[i] = ""; |
| 24301 | | - }else{ |
| 24302 | | - azVals[i] = (char*)sqlite3_column_text(pStmt, i); |
| 24303 | | - } |
| 24304 | | - if( !azVals[i] && (aiTypes[i]!=SQLITE_NULL) ){ |
| 24305 | | - rc = SQLITE_NOMEM; |
| 24306 | | - break; /* from for */ |
| 24307 | | - } |
| 24308 | | - } /* end for */ |
| 24309 | | - |
| 24310 | | - /* if data and types extracted successfully... */ |
| 24311 | | - if( SQLITE_ROW == rc ){ |
| 24312 | | - /* call the supplied callback with the result row data */ |
| 24313 | | - if( shell_callback(pArg, nCol, azVals, azCols, aiTypes) ){ |
| 24314 | | - rc = SQLITE_ABORT; |
| 24315 | | - }else{ |
| 24316 | | - rc = sqlite3_step(pStmt); |
| 24317 | | - } |
| 24318 | | - } |
| 24319 | | - } while( SQLITE_ROW == rc ); |
| 24320 | | - sqlite3_free(pData); |
| 24321 | | - if( pArg->cMode==MODE_Json ){ |
| 24322 | | - sqlite3_fputs("]\n", pArg->out); |
| 24323 | | - }else if( pArg->cMode==MODE_Www ){ |
| 24324 | | - sqlite3_fputs("</TABLE>\n<PRE>\n", pArg->out); |
| 24325 | | - }else if( pArg->cMode==MODE_Count ){ |
| 24326 | | - char zBuf[200]; |
| 24327 | | - sqlite3_snprintf(sizeof(zBuf), zBuf, "%llu row%s\n", |
| 24328 | | - nRow, nRow!=1 ? "s" : ""); |
| 24329 | | - printf("%s", zBuf); |
| 24330 | | - } |
| 24331 | | - } |
| 24332 | | - } |
| 24333 | | -} |
| 24334 | | - |
| 24335 | 25519 | #if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_AUTHORIZATION) |
| 24336 | 25520 | /* |
| 24337 | 25521 | ** This function is called to process SQL if the previous shell command |
| 24338 | 25522 | ** was ".expert". It passes the SQL in the second argument directly to |
| 24339 | 25523 | ** the sqlite3expert object. |
| | @@ -24381,25 +25565,25 @@ |
| 24381 | 25565 | int nQuery = sqlite3_expert_count(p); |
| 24382 | 25566 | int i; |
| 24383 | 25567 | |
| 24384 | 25568 | if( bVerbose ){ |
| 24385 | 25569 | const char *zCand = sqlite3_expert_report(p,0,EXPERT_REPORT_CANDIDATES); |
| 24386 | | - sqlite3_fputs("-- Candidates -----------------------------\n", out); |
| 24387 | | - sqlite3_fprintf(out, "%s\n", zCand); |
| 25570 | + cli_puts("-- Candidates -----------------------------\n", out); |
| 25571 | + cli_printf(out, "%s\n", zCand); |
| 24388 | 25572 | } |
| 24389 | 25573 | for(i=0; i<nQuery; i++){ |
| 24390 | 25574 | const char *zSql = sqlite3_expert_report(p, i, EXPERT_REPORT_SQL); |
| 24391 | 25575 | const char *zIdx = sqlite3_expert_report(p, i, EXPERT_REPORT_INDEXES); |
| 24392 | 25576 | const char *zEQP = sqlite3_expert_report(p, i, EXPERT_REPORT_PLAN); |
| 24393 | 25577 | if( zIdx==0 ) zIdx = "(no new indexes)\n"; |
| 24394 | 25578 | if( bVerbose ){ |
| 24395 | | - sqlite3_fprintf(out, |
| 25579 | + cli_printf(out, |
| 24396 | 25580 | "-- Query %d --------------------------------\n" |
| 24397 | 25581 | "%s\n\n" |
| 24398 | 25582 | ,i+1, zSql); |
| 24399 | 25583 | } |
| 24400 | | - sqlite3_fprintf(out, "%s\n%s\n", zIdx, zEQP); |
| 25584 | + cli_printf(out, "%s\n%s\n", zIdx, zEQP); |
| 24401 | 25585 | } |
| 24402 | 25586 | } |
| 24403 | 25587 | } |
| 24404 | 25588 | sqlite3_expert_destroy(p); |
| 24405 | 25589 | pState->expert.pExpert = 0; |
| | @@ -24430,30 +25614,30 @@ |
| 24430 | 25614 | if( n>=2 && 0==cli_strncmp(z, "-verbose", n) ){ |
| 24431 | 25615 | pState->expert.bVerbose = 1; |
| 24432 | 25616 | } |
| 24433 | 25617 | else if( n>=2 && 0==cli_strncmp(z, "-sample", n) ){ |
| 24434 | 25618 | if( i==(nArg-1) ){ |
| 24435 | | - sqlite3_fprintf(stderr, "option requires an argument: %s\n", z); |
| 25619 | + cli_printf(stderr, "option requires an argument: %s\n", z); |
| 24436 | 25620 | rc = SQLITE_ERROR; |
| 24437 | 25621 | }else{ |
| 24438 | 25622 | iSample = (int)integerValue(azArg[++i]); |
| 24439 | 25623 | if( iSample<0 || iSample>100 ){ |
| 24440 | | - sqlite3_fprintf(stderr,"value out of range: %s\n", azArg[i]); |
| 25624 | + cli_printf(stderr,"value out of range: %s\n", azArg[i]); |
| 24441 | 25625 | rc = SQLITE_ERROR; |
| 24442 | 25626 | } |
| 24443 | 25627 | } |
| 24444 | 25628 | } |
| 24445 | 25629 | else{ |
| 24446 | | - sqlite3_fprintf(stderr,"unknown option: %s\n", z); |
| 25630 | + cli_printf(stderr,"unknown option: %s\n", z); |
| 24447 | 25631 | rc = SQLITE_ERROR; |
| 24448 | 25632 | } |
| 24449 | 25633 | } |
| 24450 | 25634 | |
| 24451 | 25635 | if( rc==SQLITE_OK ){ |
| 24452 | 25636 | pState->expert.pExpert = sqlite3_expert_new(pState->db, &zErr); |
| 24453 | 25637 | if( pState->expert.pExpert==0 ){ |
| 24454 | | - sqlite3_fprintf(stderr, |
| 25638 | + cli_printf(stderr, |
| 24455 | 25639 | "sqlite3_expert_new: %s\n", zErr ? zErr : "out of memory"); |
| 24456 | 25640 | rc = SQLITE_ERROR; |
| 24457 | 25641 | }else{ |
| 24458 | 25642 | sqlite3_expert_config( |
| 24459 | 25643 | pState->expert.pExpert, EXPERT_CONFIG_SAMPLE, iSample |
| | @@ -24463,10 +25647,19 @@ |
| 24463 | 25647 | sqlite3_free(zErr); |
| 24464 | 25648 | |
| 24465 | 25649 | return rc; |
| 24466 | 25650 | } |
| 24467 | 25651 | #endif /* !SQLITE_OMIT_VIRTUALTABLE && !SQLITE_OMIT_AUTHORIZATION */ |
| 25652 | + |
| 25653 | +/* |
| 25654 | +** QRF write callback |
| 25655 | +*/ |
| 25656 | +static int shellWriteQR(void *pX, const char *z, sqlite3_int64 n){ |
| 25657 | + ShellState *pArg = (ShellState*)pX; |
| 25658 | + cli_printf(pArg->out, "%.*s", (int)n, z); |
| 25659 | + return SQLITE_OK; |
| 25660 | +} |
| 24468 | 25661 | |
| 24469 | 25662 | /* |
| 24470 | 25663 | ** Execute a statement or set of statements. Print |
| 24471 | 25664 | ** any result rows/columns depending on the current mode |
| 24472 | 25665 | ** set via the supplied callback. |
| | @@ -24483,13 +25676,26 @@ |
| 24483 | 25676 | sqlite3_stmt *pStmt = NULL; /* Statement to execute. */ |
| 24484 | 25677 | int rc = SQLITE_OK; /* Return Code */ |
| 24485 | 25678 | int rc2; |
| 24486 | 25679 | const char *zLeftover; /* Tail of unprocessed SQL */ |
| 24487 | 25680 | sqlite3 *db = pArg->db; |
| 25681 | + unsigned char eStyle; |
| 25682 | + sqlite3_qrf_spec spec; |
| 24488 | 25683 | |
| 24489 | 25684 | if( pzErrMsg ){ |
| 24490 | 25685 | *pzErrMsg = NULL; |
| 25686 | + } |
| 25687 | + memcpy(&spec, &pArg->mode.spec, sizeof(spec)); |
| 25688 | + spec.xWrite = shellWriteQR; |
| 25689 | + spec.pWriteArg = (void*)pArg; |
| 25690 | + if( pArg->mode.eMode==MODE_Insert && ShellHasFlag(pArg, SHFLG_PreserveRowid) ){ |
| 25691 | + spec.bTitles = QRF_SW_On; |
| 25692 | + } |
| 25693 | + assert( pArg->mode.eMode>=0 && pArg->mode.eMode<ArraySize(aModeInfo) ); |
| 25694 | + eStyle = aModeInfo[pArg->mode.eMode].eStyle; |
| 25695 | + if( pArg->mode.bAutoScreenWidth ){ |
| 25696 | + spec.nScreenWidth = shellScreenWidth(); |
| 24491 | 25697 | } |
| 24492 | 25698 | |
| 24493 | 25699 | #if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_AUTHORIZATION) |
| 24494 | 25700 | if( pArg->expert.pExpert ){ |
| 24495 | 25701 | rc = expertHandleSQL(pArg, zSql, pzErrMsg); |
| | @@ -24503,10 +25709,11 @@ |
| 24503 | 25709 | if( SQLITE_OK != rc ){ |
| 24504 | 25710 | if( pzErrMsg ){ |
| 24505 | 25711 | *pzErrMsg = save_err_msg(db, "in prepare", rc, zSql); |
| 24506 | 25712 | } |
| 24507 | 25713 | }else{ |
| 25714 | + int isExplain; |
| 24508 | 25715 | if( !pStmt ){ |
| 24509 | 25716 | /* this happens for a comment or white-space */ |
| 24510 | 25717 | zSql = zLeftover; |
| 24511 | 25718 | while( IsSpace(zSql[0]) ) zSql++; |
| 24512 | 25719 | continue; |
| | @@ -24513,93 +25720,73 @@ |
| 24513 | 25720 | } |
| 24514 | 25721 | zStmtSql = sqlite3_sql(pStmt); |
| 24515 | 25722 | if( zStmtSql==0 ) zStmtSql = ""; |
| 24516 | 25723 | while( IsSpace(zStmtSql[0]) ) zStmtSql++; |
| 24517 | 25724 | |
| 24518 | | - /* save off the prepared statement handle and reset row count */ |
| 25725 | + /* save off the prepared statement handle */ |
| 24519 | 25726 | if( pArg ){ |
| 24520 | 25727 | pArg->pStmt = pStmt; |
| 24521 | | - pArg->cnt = 0; |
| 24522 | 25728 | } |
| 24523 | | - |
| 25729 | + |
| 24524 | 25730 | /* Show the EXPLAIN QUERY PLAN if .eqp is on */ |
| 24525 | | - if( pArg && pArg->autoEQP && sqlite3_stmt_isexplain(pStmt)==0 ){ |
| 24526 | | - sqlite3_stmt *pExplain; |
| 25731 | + isExplain = sqlite3_stmt_isexplain(pStmt); |
| 25732 | + if( pArg && pArg->mode.autoEQP && isExplain==0 ){ |
| 24527 | 25733 | int triggerEQP = 0; |
| 24528 | 25734 | disable_debug_trace_modes(); |
| 24529 | 25735 | sqlite3_db_config(db, SQLITE_DBCONFIG_TRIGGER_EQP, -1, &triggerEQP); |
| 24530 | | - if( pArg->autoEQP>=AUTOEQP_trigger ){ |
| 25736 | + if( pArg->mode.autoEQP>=AUTOEQP_trigger ){ |
| 24531 | 25737 | sqlite3_db_config(db, SQLITE_DBCONFIG_TRIGGER_EQP, 1, 0); |
| 24532 | 25738 | } |
| 24533 | | - pExplain = pStmt; |
| 24534 | | - sqlite3_reset(pExplain); |
| 24535 | | - rc = sqlite3_stmt_explain(pExplain, 2); |
| 24536 | | - if( rc==SQLITE_OK ){ |
| 24537 | | - bind_prepared_stmt(pArg, pExplain); |
| 24538 | | - while( sqlite3_step(pExplain)==SQLITE_ROW ){ |
| 24539 | | - const char *zEQPLine = (const char*)sqlite3_column_text(pExplain,3); |
| 24540 | | - int iEqpId = sqlite3_column_int(pExplain, 0); |
| 24541 | | - int iParentId = sqlite3_column_int(pExplain, 1); |
| 24542 | | - if( zEQPLine==0 ) zEQPLine = ""; |
| 24543 | | - if( zEQPLine[0]=='-' ) eqp_render(pArg, 0); |
| 24544 | | - eqp_append(pArg, iEqpId, iParentId, zEQPLine); |
| 24545 | | - } |
| 24546 | | - eqp_render(pArg, 0); |
| 24547 | | - } |
| 24548 | | - if( pArg->autoEQP>=AUTOEQP_full ){ |
| 24549 | | - /* Also do an EXPLAIN for ".eqp full" mode */ |
| 24550 | | - sqlite3_reset(pExplain); |
| 24551 | | - rc = sqlite3_stmt_explain(pExplain, 1); |
| 24552 | | - if( rc==SQLITE_OK ){ |
| 24553 | | - pArg->cMode = MODE_Explain; |
| 24554 | | - assert( sqlite3_stmt_isexplain(pExplain)==1 ); |
| 24555 | | - bind_prepared_stmt(pArg, pExplain); |
| 24556 | | - explain_data_prepare(pArg, pExplain); |
| 24557 | | - exec_prepared_stmt(pArg, pExplain); |
| 24558 | | - explain_data_delete(pArg); |
| 24559 | | - } |
| 24560 | | - } |
| 24561 | | - if( pArg->autoEQP>=AUTOEQP_trigger && triggerEQP==0 ){ |
| 25739 | + sqlite3_reset(pStmt); |
| 25740 | + spec.eStyle = QRF_STYLE_Auto; |
| 25741 | + sqlite3_stmt_explain(pStmt, 2-(pArg->mode.autoEQP>=AUTOEQP_full)); |
| 25742 | + sqlite3_format_query_result(pStmt, &spec, 0); |
| 25743 | + if( pArg->mode.autoEQP>=AUTOEQP_trigger && triggerEQP==0 ){ |
| 24562 | 25744 | sqlite3_db_config(db, SQLITE_DBCONFIG_TRIGGER_EQP, 0, 0); |
| 24563 | 25745 | } |
| 24564 | 25746 | sqlite3_reset(pStmt); |
| 24565 | 25747 | sqlite3_stmt_explain(pStmt, 0); |
| 24566 | 25748 | restore_debug_trace_modes(); |
| 24567 | 25749 | } |
| 24568 | 25750 | |
| 24569 | | - if( pArg ){ |
| 24570 | | - int bIsExplain = (sqlite3_stmt_isexplain(pStmt)==1); |
| 24571 | | - pArg->cMode = pArg->mode; |
| 24572 | | - if( pArg->autoExplain ){ |
| 24573 | | - if( bIsExplain ){ |
| 24574 | | - pArg->cMode = MODE_Explain; |
| 24575 | | - } |
| 24576 | | - if( sqlite3_stmt_isexplain(pStmt)==2 ){ |
| 24577 | | - pArg->cMode = MODE_EQP; |
| 24578 | | - } |
| 24579 | | - } |
| 24580 | | - |
| 24581 | | - /* If the shell is currently in ".explain" mode, gather the extra |
| 24582 | | - ** data required to add indents to the output.*/ |
| 24583 | | - if( pArg->cMode==MODE_Explain && bIsExplain ){ |
| 24584 | | - explain_data_prepare(pArg, pStmt); |
| 24585 | | - } |
| 24586 | | - } |
| 24587 | | - |
| 24588 | 25751 | bind_prepared_stmt(pArg, pStmt); |
| 24589 | | - exec_prepared_stmt(pArg, pStmt); |
| 24590 | | - explain_data_delete(pArg); |
| 24591 | | - eqp_render(pArg, 0); |
| 25752 | + if( isExplain && pArg->mode.autoExplain ){ |
| 25753 | + spec.eStyle = isExplain==1 ? QRF_STYLE_Explain : QRF_STYLE_Eqp; |
| 25754 | + sqlite3_format_query_result(pStmt, &spec, pzErrMsg); |
| 25755 | + }else if( pArg->mode.eMode==MODE_Www ){ |
| 25756 | + cli_printf(pArg->out, |
| 25757 | + "</PRE>\n" |
| 25758 | + "<TABLE border='1' cellspacing='0' cellpadding='2'>\n"); |
| 25759 | + spec.eStyle = QRF_STYLE_Html; |
| 25760 | + sqlite3_format_query_result(pStmt, &spec, pzErrMsg); |
| 25761 | + cli_printf(pArg->out, |
| 25762 | + "</TABLE>\n" |
| 25763 | + "<PRE>"); |
| 25764 | + }else{ |
| 25765 | + spec.eStyle = eStyle; |
| 25766 | + sqlite3_format_query_result(pStmt, &spec, pzErrMsg); |
| 25767 | + } |
| 24592 | 25768 | |
| 24593 | 25769 | /* print usage stats if stats on */ |
| 24594 | 25770 | if( pArg && pArg->statsOn ){ |
| 24595 | 25771 | display_stats(db, pArg, 0); |
| 24596 | 25772 | } |
| 24597 | 25773 | |
| 24598 | 25774 | /* print loop-counters if required */ |
| 24599 | | - if( pArg && pArg->scanstatsOn ){ |
| 24600 | | - display_scanstats(db, pArg); |
| 25775 | + if( pArg && pArg->mode.scanstatsOn ){ |
| 25776 | + char *zErr = 0; |
| 25777 | + switch( pArg->mode.scanstatsOn ){ |
| 25778 | + case 1: spec.eStyle = QRF_STYLE_Stats; break; |
| 25779 | + case 2: spec.eStyle = QRF_STYLE_StatsEst; break; |
| 25780 | + default: spec.eStyle = QRF_STYLE_StatsVm; break; |
| 25781 | + } |
| 25782 | + sqlite3_reset(pStmt); |
| 25783 | + rc = sqlite3_format_query_result(pStmt, &spec, &zErr); |
| 25784 | + if( rc ){ |
| 25785 | + cli_printf(stderr, "Stats query failed: %s\n", zErr); |
| 25786 | + sqlite3_free(zErr); |
| 25787 | + } |
| 24601 | 25788 | } |
| 24602 | 25789 | |
| 24603 | 25790 | /* Finalize the statement just executed. If this fails, save a |
| 24604 | 25791 | ** copy of the error message. Otherwise, set zSql to point to the |
| 24605 | 25792 | ** next statement to execute. */ |
| | @@ -24791,34 +25978,34 @@ |
| 24791 | 25978 | ** have been recreated by prior repopulations. See forum posts: |
| 24792 | 25979 | ** 2024-10-13T17:10:01z and 2025-10-29T19:38:43z |
| 24793 | 25980 | */ |
| 24794 | 25981 | if( db_int(p->db, "SELECT count(*) FROM sqlite_sequence")>0 ){ |
| 24795 | 25982 | if( !p->writableSchema ){ |
| 24796 | | - sqlite3_fputs("PRAGMA writable_schema=ON;\n", p->out); |
| 25983 | + cli_puts("PRAGMA writable_schema=ON;\n", p->out); |
| 24797 | 25984 | p->writableSchema = 1; |
| 24798 | 25985 | } |
| 24799 | | - sqlite3_fputs("CREATE TABLE IF NOT EXISTS sqlite_sequence(name,seq);\n" |
| 25986 | + cli_puts("CREATE TABLE IF NOT EXISTS sqlite_sequence(name,seq);\n" |
| 24800 | 25987 | "DELETE FROM sqlite_sequence;\n", p->out); |
| 24801 | 25988 | } |
| 24802 | 25989 | }else if( sqlite3_strglob("sqlite_stat?", zTable)==0 && !noSys ){ |
| 24803 | | - if( !dataOnly ) sqlite3_fputs("ANALYZE sqlite_schema;\n", p->out); |
| 25990 | + if( !dataOnly ) cli_puts("ANALYZE sqlite_schema;\n", p->out); |
| 24804 | 25991 | }else if( cli_strncmp(zTable, "sqlite_", 7)==0 ){ |
| 24805 | 25992 | return 0; |
| 24806 | 25993 | }else if( dataOnly ){ |
| 24807 | 25994 | /* no-op */ |
| 24808 | 25995 | }else if( cli_strncmp(zSql, "CREATE VIRTUAL TABLE", 20)==0 ){ |
| 24809 | 25996 | char *zIns; |
| 24810 | 25997 | if( !p->writableSchema ){ |
| 24811 | | - sqlite3_fputs("PRAGMA writable_schema=ON;\n", p->out); |
| 25998 | + cli_puts("PRAGMA writable_schema=ON;\n", p->out); |
| 24812 | 25999 | p->writableSchema = 1; |
| 24813 | 26000 | } |
| 24814 | 26001 | zIns = sqlite3_mprintf( |
| 24815 | 26002 | "INSERT INTO sqlite_schema(type,name,tbl_name,rootpage,sql)" |
| 24816 | 26003 | "VALUES('table','%q','%q',0,'%q');", |
| 24817 | 26004 | zTable, zTable, zSql); |
| 24818 | 26005 | shell_check_oom(zIns); |
| 24819 | | - sqlite3_fprintf(p->out, "%s\n", zIns); |
| 26006 | + cli_printf(p->out, "%s\n", zIns); |
| 24820 | 26007 | sqlite3_free(zIns); |
| 24821 | 26008 | return 0; |
| 24822 | 26009 | }else{ |
| 24823 | 26010 | printSchemaLine(p->out, zSql, ";\n"); |
| 24824 | 26011 | } |
| | @@ -24826,12 +26013,11 @@ |
| 24826 | 26013 | if( cli_strcmp(zType, "table")==0 ){ |
| 24827 | 26014 | ShellText sSelect; |
| 24828 | 26015 | ShellText sTable; |
| 24829 | 26016 | char **azCol; |
| 24830 | 26017 | int i; |
| 24831 | | - char *savedDestTable; |
| 24832 | | - int savedMode; |
| 26018 | + Mode savedMode; |
| 24833 | 26019 | |
| 24834 | 26020 | azCol = tableColumnList(p, zTable); |
| 24835 | 26021 | if( azCol==0 ){ |
| 24836 | 26022 | p->nErr++; |
| 24837 | 26023 | return 0; |
| | @@ -24870,22 +26056,22 @@ |
| 24870 | 26056 | } |
| 24871 | 26057 | freeColumnList(azCol); |
| 24872 | 26058 | appendText(&sSelect, " FROM ", 0); |
| 24873 | 26059 | appendText(&sSelect, zTable, quoteChar(zTable)); |
| 24874 | 26060 | |
| 24875 | | - savedDestTable = p->zDestTable; |
| 26061 | + |
| 24876 | 26062 | savedMode = p->mode; |
| 24877 | | - p->zDestTable = sTable.zTxt; |
| 24878 | | - p->mode = p->cMode = MODE_Insert; |
| 26063 | + p->mode.spec.zTableName = (char*)zTable; |
| 26064 | + p->mode.eMode = MODE_Insert; |
| 26065 | + p->mode.spec.bTitles = QRF_No; |
| 24879 | 26066 | rc = shell_exec(p, sSelect.zTxt, 0); |
| 24880 | 26067 | if( (rc&0xff)==SQLITE_CORRUPT ){ |
| 24881 | | - sqlite3_fputs("/****** CORRUPTION ERROR *******/\n", p->out); |
| 26068 | + cli_puts("/****** CORRUPTION ERROR *******/\n", p->out); |
| 24882 | 26069 | toggleSelectOrder(p->db); |
| 24883 | 26070 | shell_exec(p, sSelect.zTxt, 0); |
| 24884 | 26071 | toggleSelectOrder(p->db); |
| 24885 | 26072 | } |
| 24886 | | - p->zDestTable = savedDestTable; |
| 24887 | 26073 | p->mode = savedMode; |
| 24888 | 26074 | freeText(&sTable); |
| 24889 | 26075 | freeText(&sSelect); |
| 24890 | 26076 | if( rc ) p->nErr++; |
| 24891 | 26077 | } |
| | @@ -24907,22 +26093,22 @@ |
| 24907 | 26093 | char *zErr = 0; |
| 24908 | 26094 | rc = sqlite3_exec(p->db, zQuery, dump_callback, p, &zErr); |
| 24909 | 26095 | if( rc==SQLITE_CORRUPT ){ |
| 24910 | 26096 | char *zQ2; |
| 24911 | 26097 | int len = strlen30(zQuery); |
| 24912 | | - sqlite3_fputs("/****** CORRUPTION ERROR *******/\n", p->out); |
| 26098 | + cli_puts("/****** CORRUPTION ERROR *******/\n", p->out); |
| 24913 | 26099 | if( zErr ){ |
| 24914 | | - sqlite3_fprintf(p->out, "/****** %s ******/\n", zErr); |
| 26100 | + cli_printf(p->out, "/****** %s ******/\n", zErr); |
| 24915 | 26101 | sqlite3_free(zErr); |
| 24916 | 26102 | zErr = 0; |
| 24917 | 26103 | } |
| 24918 | 26104 | zQ2 = malloc( len+100 ); |
| 24919 | 26105 | if( zQ2==0 ) return rc; |
| 24920 | 26106 | sqlite3_snprintf(len+100, zQ2, "%s ORDER BY rowid DESC", zQuery); |
| 24921 | 26107 | rc = sqlite3_exec(p->db, zQ2, dump_callback, p, &zErr); |
| 24922 | 26108 | if( rc ){ |
| 24923 | | - sqlite3_fprintf(p->out, "/****** ERROR: %s ******/\n", zErr); |
| 26109 | + cli_printf(p->out, "/****** ERROR: %s ******/\n", zErr); |
| 24924 | 26110 | }else{ |
| 24925 | 26111 | rc = SQLITE_CORRUPT; |
| 24926 | 26112 | } |
| 24927 | 26113 | free(zQ2); |
| 24928 | 26114 | } |
| | @@ -25019,27 +26205,14 @@ |
| 25019 | 26205 | ".explain ?on|off|auto? Change the EXPLAIN formatting mode. Default: auto", |
| 25020 | 26206 | ".filectrl CMD ... Run various sqlite3_file_control() operations", |
| 25021 | 26207 | " --schema SCHEMA Use SCHEMA instead of \"main\"", |
| 25022 | 26208 | " --help Show CMD details", |
| 25023 | 26209 | ".fullschema ?--indent? Show schema and the content of sqlite_stat tables", |
| 25024 | | - ".headers on|off Turn display of headers on or off", |
| 26210 | + ",headers on|off Turn display of headers on or off", |
| 25025 | 26211 | ".help ?-all? ?PATTERN? Show help text for PATTERN", |
| 25026 | 26212 | #ifndef SQLITE_SHELL_FIDDLE |
| 25027 | 26213 | ".import FILE TABLE Import data from FILE into TABLE", |
| 25028 | | - " Options:", |
| 25029 | | - " --ascii Use \\037 and \\036 as column and row separators", |
| 25030 | | - " --csv Use , and \\n as column and row separators", |
| 25031 | | - " --skip N Skip the first N rows of input", |
| 25032 | | - " --schema S Target table to be S.TABLE", |
| 25033 | | - " -v \"Verbose\" - increase auxiliary output", |
| 25034 | | - " Notes:", |
| 25035 | | - " * If TABLE does not exist, it is created. The first row of input", |
| 25036 | | - " determines the column names.", |
| 25037 | | - " * If neither --csv or --ascii are used, the input mode is derived", |
| 25038 | | - " from the \".mode\" output mode", |
| 25039 | | - " * If FILE begins with \"|\" then it is a command that generates the", |
| 25040 | | - " input text.", |
| 25041 | 26214 | #endif |
| 25042 | 26215 | #ifndef SQLITE_OMIT_TEST_CONTROL |
| 25043 | 26216 | ".imposter INDEX TABLE Create imposter table TABLE on index INDEX", |
| 25044 | 26217 | #endif |
| 25045 | 26218 | ".indexes ?TABLE? Show names of indexes", |
| | @@ -25060,46 +26233,16 @@ |
| 25060 | 26233 | ".log FILE|on|off Turn logging on or off. FILE can be stderr/stdout", |
| 25061 | 26234 | #else |
| 25062 | 26235 | ".log on|off Turn logging on or off.", |
| 25063 | 26236 | #endif |
| 25064 | 26237 | ".mode ?MODE? ?OPTIONS? Set output mode", |
| 25065 | | - " MODE is one of:", |
| 25066 | | - " ascii Columns/rows delimited by 0x1F and 0x1E", |
| 25067 | | - " box Tables using unicode box-drawing characters", |
| 25068 | | - " csv Comma-separated values", |
| 25069 | | - " column Output in columns. (See .width)", |
| 25070 | | - " html HTML <table> code", |
| 25071 | | - " insert SQL insert statements for TABLE", |
| 25072 | | - " json Results in a JSON array", |
| 25073 | | - " line One value per line", |
| 25074 | | - " list Values delimited by \"|\"", |
| 25075 | | - " markdown Markdown table format", |
| 25076 | | - " qbox Shorthand for \"box --wrap 60 --quote\"", |
| 25077 | | - " quote Escape answers as for SQL", |
| 25078 | | - " table ASCII-art table", |
| 25079 | | - " tabs Tab-separated values", |
| 25080 | | - " tcl TCL list elements", |
| 25081 | | - " OPTIONS: (for columnar modes or insert mode):", |
| 25082 | | - " --escape T ctrl-char escape; T is one of: symbol, ascii, off", |
| 25083 | | - " --wrap N Wrap output lines to no longer than N characters", |
| 25084 | | - " --wordwrap B Wrap or not at word boundaries per B (on/off)", |
| 25085 | | - " --ww Shorthand for \"--wordwrap 1\"", |
| 25086 | | - " --quote Quote output text as SQL literals", |
| 25087 | | - " --noquote Do not quote output text", |
| 25088 | | - " TABLE The name of SQL table used for \"insert\" mode", |
| 25089 | 26238 | #ifndef SQLITE_SHELL_FIDDLE |
| 25090 | 26239 | ".nonce STRING Suspend safe mode for one command if nonce matches", |
| 25091 | 26240 | #endif |
| 25092 | 26241 | ".nullvalue STRING Use STRING in place of NULL values", |
| 25093 | 26242 | #ifndef SQLITE_SHELL_FIDDLE |
| 25094 | 26243 | ".once ?OPTIONS? ?FILE? Output for the next SQL command only to FILE", |
| 25095 | | - " If FILE begins with '|' then open as a pipe", |
| 25096 | | - " --bom Put a UTF8 byte-order mark at the beginning", |
| 25097 | | - " -e Send output to the system text editor", |
| 25098 | | - " --plain Use text/plain output instead of HTML for -w option", |
| 25099 | | - " -w Send output as HTML to a web browser (same as \".www\")", |
| 25100 | | - " -x Send output as CSV to a spreadsheet (same as \".excel\")", |
| 25101 | 26244 | /* Note that .open is (partially) available in WASM builds but is |
| 25102 | 26245 | ** currently only intended to be used by the fiddle tool, not |
| 25103 | 26246 | ** end users, so is "undocumented." */ |
| 25104 | 26247 | ".open ?OPTIONS? ?FILE? Close existing database and reopen FILE", |
| 25105 | 26248 | " Options:", |
| | @@ -25121,18 +26264,10 @@ |
| 25121 | 26264 | " --nofollow Do not follow symbolic links", |
| 25122 | 26265 | " --readonly Open FILE readonly", |
| 25123 | 26266 | " --zip FILE is a ZIP archive", |
| 25124 | 26267 | #ifndef SQLITE_SHELL_FIDDLE |
| 25125 | 26268 | ".output ?FILE? Send output to FILE or stdout if FILE is omitted", |
| 25126 | | - " If FILE begins with '|' then open it as a pipe.", |
| 25127 | | - " If FILE is 'off' then output is disabled.", |
| 25128 | | - " Options:", |
| 25129 | | - " --bom Prefix output with a UTF8 byte-order mark", |
| 25130 | | - " -e Send output to the system text editor", |
| 25131 | | - " --plain Use text/plain for -w option", |
| 25132 | | - " -w Send output to a web browser", |
| 25133 | | - " -x Send output as CSV to a spreadsheet", |
| 25134 | 26269 | #endif |
| 25135 | 26270 | ".parameter CMD ... Manage SQL parameter bindings", |
| 25136 | 26271 | " clear Erase all bindings", |
| 25137 | 26272 | " init Initialize the TEMP table that holds bindings", |
| 25138 | 26273 | " list List the current parameter bindings", |
| | @@ -25171,11 +26306,11 @@ |
| 25171 | 26306 | " --nosys Omit objects whose names start with \"sqlite_\"", |
| 25172 | 26307 | ",selftest ?OPTIONS? Run tests defined in the SELFTEST table", |
| 25173 | 26308 | " Options:", |
| 25174 | 26309 | " --init Create a new SELFTEST table", |
| 25175 | 26310 | " -v Verbose output", |
| 25176 | | - ".separator COL ?ROW? Change the column and row separators", |
| 26311 | + ",separator COL ?ROW? Change the column and row separators", |
| 25177 | 26312 | #if defined(SQLITE_ENABLE_SESSION) |
| 25178 | 26313 | ".session ?NAME? CMD ... Create or control sessions", |
| 25179 | 26314 | " Subcommands:", |
| 25180 | 26315 | " attach TABLE Attach TABLE", |
| 25181 | 26316 | " changeset FILE Write a changeset into FILE", |
| | @@ -25198,11 +26333,11 @@ |
| 25198 | 26333 | " --sha3-512 Use the sha3-512 algorithm", |
| 25199 | 26334 | " Any other argument is a LIKE pattern for tables to hash", |
| 25200 | 26335 | #if !defined(SQLITE_NOHAVE_SYSTEM) && !defined(SQLITE_SHELL_FIDDLE) |
| 25201 | 26336 | ".shell CMD ARGS... Run CMD ARGS... in a system shell", |
| 25202 | 26337 | #endif |
| 25203 | | - ".show Show the current values for various settings", |
| 26338 | + ",show Show the current values for various settings", |
| 25204 | 26339 | ".stats ?ARG? Show stats or turn stats on or off", |
| 25205 | 26340 | " off Turn off automatic stat display", |
| 25206 | 26341 | " on Turn on automatic stat display", |
| 25207 | 26342 | " stmt Show statement stats", |
| 25208 | 26343 | " vmstep Show the virtual machine step count only", |
| | @@ -25239,17 +26374,167 @@ |
| 25239 | 26374 | #endif |
| 25240 | 26375 | ".version Show source, library and compiler versions", |
| 25241 | 26376 | ".vfsinfo ?AUX? Information about the top-level VFS", |
| 25242 | 26377 | ".vfslist List all available VFSes", |
| 25243 | 26378 | ".vfsname ?AUX? Print the name of the VFS stack", |
| 25244 | | - ".width NUM1 NUM2 ... Set minimum column widths for columnar output", |
| 26379 | + ",width NUM1 NUM2 ... Set minimum column widths for columnar output", |
| 25245 | 26380 | " Negative values right-justify", |
| 25246 | 26381 | #ifndef SQLITE_SHELL_FIDDLE |
| 25247 | 26382 | ".www Display output of the next command in web browser", |
| 25248 | 26383 | " --plain Show results as text/plain, not as HTML", |
| 25249 | 26384 | #endif |
| 25250 | 26385 | }; |
| 26386 | + |
| 26387 | +/************************************************************** |
| 26388 | +** "Usage" help text automatically generated from comments */ |
| 26389 | +static const struct { |
| 26390 | + const char *zCmd; /* Name of the dot-command */ |
| 26391 | + const char *zUsage; /* Documentation */ |
| 26392 | +} aUsage[] = { |
| 26393 | + { ".import", |
| 26394 | +"USAGE: .import [OPTIONS] FILE TABLE\n" |
| 26395 | +"\n" |
| 26396 | +"Import CSV or similar text from FILE into TABLE. If TABLE does\n" |
| 26397 | +"not exist, it is created using the first row of FILE as the column\n" |
| 26398 | +"names. If FILE begins with \"|\" then it is a command that is run\n" |
| 26399 | +"and the output from the command is used as the input data.\n" |
| 26400 | +"\n" |
| 26401 | +"FILE is assumed to be in a CSV format, unless the current mode\n" |
| 26402 | +"is \"ascii\" or \"tabs\" or unless one of the options below specify\n" |
| 26403 | +"an alternative.\n" |
| 26404 | +"\n" |
| 26405 | +"Options:\n" |
| 26406 | +" --ascii Use \\037 and \\036 as column and row separators on input\n" |
| 26407 | +" --csv Input is standard RFC-4180 CSV.\n" |
| 26408 | +" --schema S When creating TABLE, put it in schema S\n" |
| 26409 | +" --skip N Ignore the first N rows of input\n" |
| 26410 | +" -v Verbose mode\n" |
| 26411 | + }, |
| 26412 | + { ".mode", |
| 26413 | +"USAGE: .mode [MODE] [OPTIONS]\n" |
| 26414 | +"\n" |
| 26415 | +"Change the output mode to MODE and/or apply OPTIONS to the\n" |
| 26416 | +"output mode. If no arguments, show the current output mode\n" |
| 26417 | +"and relevant options.\n" |
| 26418 | +"\n" |
| 26419 | +"Options:\n" |
| 26420 | +" --align STRING Set the alignment of text in columnar modes\n" |
| 26421 | +" String consists of characters 'L', 'C', 'R'\n" |
| 26422 | +" meaning \"left\", \"centered\", and \"right\", with\n" |
| 26423 | +" one letter per column starting from the left.\n" |
| 26424 | +" Unspecified alignment defaults to 'L'.\n" |
| 26425 | +" --charlimit N Set the maximum number of output characters to\n" |
| 26426 | +" show for any single SQL value to N. Longer values\n" |
| 26427 | +" truncated. Zero means \"no limit\".\n" |
| 26428 | +" --colsep STRING Use STRING as the column separator\n" |
| 26429 | +" --escape ESC Enable/disable escaping of control characters\n" |
| 26430 | +" in output. ESC can be \"off\", \"ascii\", or\n" |
| 26431 | +" \"symbol\".\n" |
| 26432 | +" --linelimit N Set the maximum number of output lines to show for\n" |
| 26433 | +" any single SQL value to N. Longer values are\n" |
| 26434 | +" truncated. Zero means \"no limit\". Only works\n" |
| 26435 | +" in \"line\" mode and in columnar modes.\n" |
| 26436 | +" --list List available modes\n" |
| 26437 | +" --null STRING Render SQL NULL values as the given string\n" |
| 26438 | +" --once Setting changes to the right are reverted after\n" |
| 26439 | +" the next SQL command.\n" |
| 26440 | +" --quote ARG Enable/disable quoting of text. ARG can be\n" |
| 26441 | +" \"off\", \"on\", \"sql\", \"csv\", \"html\", \"tcl\",\n" |
| 26442 | +" or \"json\". \"off\" means show the text as-is.\n" |
| 26443 | +" \"on and \"sql\" are synonyms.\n" |
| 26444 | +" --reset Changes all mode settings back to their default.\n" |
| 26445 | +" --rowsep STRING Use STRING as the row separator\n" |
| 26446 | +" --screenwidth N Declare the screen width of the output device\n" |
| 26447 | +" to be N characters. An attempt may be made to\n" |
| 26448 | +" wrap output text to fit within this limit. Zero\n" |
| 26449 | +" means \"no limit\". Or N can be \"auto\" to set the\n" |
| 26450 | +" width automatically.\n" |
| 26451 | +" --tablename NAME Set the name of the table for \"insert\" mode.\n" |
| 26452 | +" --tag NAME Save mode to the left as NAME.\n" |
| 26453 | +" --textjsonb BOOLEAN If enabled, JSONB text is displayed as text JSON.\n" |
| 26454 | +" --title ARG Whether or not to show column headers, and if so\n" |
| 26455 | +" how to encode them. ARG can be \"off\", \"on\",\n" |
| 26456 | +" \"sql\", \"csv\", \"html\", \"tcl\", or \"json\".\n" |
| 26457 | +" -v|--verbose Verbose output\n" |
| 26458 | +" --widths LIST Set the columns widths for columnar modes. The\n" |
| 26459 | +" argument is a list of integers, one for each\n" |
| 26460 | +" column. A \"0\" width means use a dynamic width\n" |
| 26461 | +" based on the actual width of data. If there are\n" |
| 26462 | +" fewer entries in LIST than columns, \"0\" is used\n" |
| 26463 | +" for the unspecified widths.\n" |
| 26464 | +" --wordwrap BOOLEAN Enable/disable word wrapping\n" |
| 26465 | +" --wrap N Wrap columns wider than N characters\n" |
| 26466 | +" --ww Shorthand for \"--wordwrap on\"\n" |
| 26467 | + }, |
| 26468 | + { ".output", |
| 26469 | +"USAGE: .output [OPTIONS] [FILE]\n" |
| 26470 | +"\n" |
| 26471 | +"Begin redirecting output to FILE. Or if FILE is omitted, revert\n" |
| 26472 | +"to sending output to the console. If FILE begins with \"|\" then\n" |
| 26473 | +"the remainder of file is taken as a pipe and output is directed\n" |
| 26474 | +"into that pipe. If FILE is \"memory\" then output is captured in an\n" |
| 26475 | +"internal memory buffer. If FILE is \"off\" then output is redirected\n" |
| 26476 | +"into /dev/null or the equivalent.\n" |
| 26477 | +"\n" |
| 26478 | +"Options:\n" |
| 26479 | +" --bom Prepend a byte-order mark to the output\n" |
| 26480 | +" -e Accumulate output in a temporary text file then\n" |
| 26481 | +" launch a text editor when the redirection ends.\n" |
| 26482 | +" --error-prefix X Use X as the left-margin prefix for error messages.\n" |
| 26483 | +" Set to an empty string to restore the default.\n" |
| 26484 | +" --glob GLOB Raise an error if the memory buffer does not match\n" |
| 26485 | +" the GLOB pattern.\n" |
| 26486 | +" --keep Continue using the same \"memory\" buffer. Do not\n" |
| 26487 | +" reset it or delete it. Useful in combination with\n" |
| 26488 | +" --glob, --not-glob, and/or --verify.\n" |
| 26489 | +" ---notglob GLOB Raise an error if the memory buffer does not match\n" |
| 26490 | +" the GLOB pattern.\n" |
| 26491 | +" --plain Use plain text rather than HTML tables with -w\n" |
| 26492 | +" --show Write the memory buffer to the screen, for debugging.\n" |
| 26493 | +" --verify ENDMARK Read subsequent lines of text until the first line\n" |
| 26494 | +" that matches ENDMARK. Discard the ENDMARK. Compare\n" |
| 26495 | +" the text against the accumulated output in memory and\n" |
| 26496 | +" raise an error if there are any differences.\n" |
| 26497 | +" -w Show the output in a web browser. Output is\n" |
| 26498 | +" written into a temporary HTML file until the\n" |
| 26499 | +" redirect ends, then the web browser is launched.\n" |
| 26500 | +" Query results are shown as HTML tables, unless\n" |
| 26501 | +" the --plain is used too.\n" |
| 26502 | +" -x Show the output in a spreadsheet. Output is\n" |
| 26503 | +" written to a temp file as CSV then the spreadsheet\n" |
| 26504 | +" is launched when\n" |
| 26505 | + }, |
| 26506 | + { ".once", |
| 26507 | +"USAGE: .once [OPTIONS] FILE ...\n" |
| 26508 | +"\n" |
| 26509 | +"Write the output for the next line of SQL or the next dot-command into\n" |
| 26510 | +"FILE. If FILE begins with \"|\" then it is a program into which output\n" |
| 26511 | +"is written. The FILE argument should be omitted if one of the -e, -w,\n" |
| 26512 | +"or -x options is used.\n" |
| 26513 | +"\n" |
| 26514 | +"Options:\n" |
| 26515 | +" -e Capture output into a temporary file then bring up\n" |
| 26516 | +" a text editor on that temporary file.\n" |
| 26517 | +" --plain Use plain text rather than HTML tables with -w\n" |
| 26518 | +" -w Capture output into an HTML file then bring up that\n" |
| 26519 | +" file in a web browser\n" |
| 26520 | +" -x Show the output in a spreadsheet. Output is\n" |
| 26521 | +" written to a temp file as CSV then the spreadsheet\n" |
| 26522 | +" is launched when\n" |
| 26523 | + }, |
| 26524 | +}; |
| 26525 | + |
| 26526 | +/* |
| 26527 | +** Return a pointer to usage text for zCmd, or NULL if none exists. |
| 26528 | +*/ |
| 26529 | +static const char *findUsage(const char *zCmd){ |
| 26530 | + int i; |
| 26531 | + for(i=0; i<ArraySize(aUsage); i++){ |
| 26532 | + if( sqlite3_strglob(zCmd, aUsage[i].zCmd)==0 ) return aUsage[i].zUsage; |
| 26533 | + } |
| 26534 | + return 0; |
| 26535 | +} |
| 25251 | 26536 | |
| 25252 | 26537 | /* |
| 25253 | 26538 | ** Output help text for commands that match zPattern. |
| 25254 | 26539 | ** |
| 25255 | 26540 | ** * If zPattern is NULL, then show all documented commands, but |
| | @@ -25276,10 +26561,11 @@ |
| 25276 | 26561 | static int showHelp(FILE *out, const char *zPattern){ |
| 25277 | 26562 | int i = 0; |
| 25278 | 26563 | int j = 0; |
| 25279 | 26564 | int n = 0; |
| 25280 | 26565 | char *zPat; |
| 26566 | + const char *zHit = 0; |
| 25281 | 26567 | if( zPattern==0 ){ |
| 25282 | 26568 | /* Show just the first line for all help topics */ |
| 25283 | 26569 | zPattern = "[a-z]"; |
| 25284 | 26570 | }else if( cli_strcmp(zPattern,"-a")==0 |
| 25285 | 26571 | || cli_strcmp(zPattern,"-all")==0 |
| | @@ -25293,41 +26579,50 @@ |
| 25293 | 26579 | for(i=0; i<ArraySize(azHelp); i++){ |
| 25294 | 26580 | if( azHelp[i][0]=='.' ){ |
| 25295 | 26581 | show = 0; |
| 25296 | 26582 | }else if( azHelp[i][0]==',' ){ |
| 25297 | 26583 | show = 1; |
| 25298 | | - sqlite3_fprintf(out, ".%s\n", &azHelp[i][1]); |
| 26584 | + cli_printf(out, ".%s\n", &azHelp[i][1]); |
| 25299 | 26585 | n++; |
| 25300 | 26586 | }else if( show ){ |
| 25301 | | - sqlite3_fprintf(out, "%s\n", azHelp[i]); |
| 26587 | + cli_printf(out, "%s\n", azHelp[i]); |
| 25302 | 26588 | } |
| 25303 | 26589 | } |
| 25304 | 26590 | return n; |
| 25305 | 26591 | } |
| 25306 | 26592 | |
| 25307 | 26593 | /* Seek documented commands for which zPattern is an exact prefix */ |
| 25308 | | - zPat = sqlite3_mprintf(".%s*", zPattern); |
| 26594 | + zPat = sqlite3_mprintf(".%s*", zPattern[0]=='.' ? &zPattern[1] : zPattern); |
| 25309 | 26595 | shell_check_oom(zPat); |
| 25310 | 26596 | for(i=0; i<ArraySize(azHelp); i++){ |
| 25311 | 26597 | if( sqlite3_strglob(zPat, azHelp[i])==0 ){ |
| 25312 | | - sqlite3_fprintf(out, "%s\n", azHelp[i]); |
| 26598 | + if( zHit ) cli_printf(out, "%s\n", zHit); |
| 26599 | + zHit = azHelp[i]; |
| 25313 | 26600 | j = i+1; |
| 25314 | 26601 | n++; |
| 25315 | 26602 | } |
| 25316 | 26603 | } |
| 25317 | | - sqlite3_free(zPat); |
| 25318 | 26604 | if( n ){ |
| 25319 | 26605 | if( n==1 ){ |
| 25320 | | - /* when zPattern is a prefix of exactly one command, then include |
| 25321 | | - ** the details of that command, which should begin at offset j */ |
| 25322 | | - while( j<ArraySize(azHelp)-1 && azHelp[j][0]==' ' ){ |
| 25323 | | - sqlite3_fprintf(out, "%s\n", azHelp[j]); |
| 25324 | | - j++; |
| 25325 | | - } |
| 25326 | | - } |
| 25327 | | - return n; |
| 25328 | | - } |
| 26606 | + const char *zUsage = findUsage(zPat); |
| 26607 | + if( zUsage ){ |
| 26608 | + cli_puts(zUsage, out); |
| 26609 | + }else{ |
| 26610 | + /* when zPattern is a prefix of exactly one command, then include |
| 26611 | + ** the details of that command, which should begin at offset j */ |
| 26612 | + cli_printf(out, "%s\n", zHit); |
| 26613 | + while( j<ArraySize(azHelp)-1 && azHelp[j][0]==' ' ){ |
| 26614 | + cli_printf(out, "%s\n", azHelp[j]); |
| 26615 | + j++; |
| 26616 | + } |
| 26617 | + } |
| 26618 | + }else{ |
| 26619 | + cli_printf(out, "%s\n", zHit); |
| 26620 | + } |
| 26621 | + } |
| 26622 | + sqlite3_free(zPat); |
| 26623 | + if( n ) return n; |
| 25329 | 26624 | |
| 25330 | 26625 | /* Look for documented commands that contain zPattern anywhere. |
| 25331 | 26626 | ** Show complete text of all documented commands that match. */ |
| 25332 | 26627 | zPat = sqlite3_mprintf("%%%s%%", zPattern); |
| 25333 | 26628 | shell_check_oom(zPat); |
| | @@ -25336,14 +26631,14 @@ |
| 25336 | 26631 | while( i<ArraySize(azHelp)-1 && azHelp[i+1][0]==' ' ) ++i; |
| 25337 | 26632 | continue; |
| 25338 | 26633 | } |
| 25339 | 26634 | if( azHelp[i][0]=='.' ) j = i; |
| 25340 | 26635 | if( sqlite3_strlike(zPat, azHelp[i], 0)==0 ){ |
| 25341 | | - sqlite3_fprintf(out, "%s\n", azHelp[j]); |
| 26636 | + cli_printf(out, "%s\n", azHelp[j]); |
| 25342 | 26637 | while( j<ArraySize(azHelp)-1 && azHelp[j+1][0]==' ' ){ |
| 25343 | 26638 | j++; |
| 25344 | | - sqlite3_fprintf(out, "%s\n", azHelp[j]); |
| 26639 | + cli_printf(out, "%s\n", azHelp[j]); |
| 25345 | 26640 | } |
| 25346 | 26641 | i = j; |
| 25347 | 26642 | n++; |
| 25348 | 26643 | } |
| 25349 | 26644 | } |
| | @@ -25376,27 +26671,27 @@ |
| 25376 | 26671 | char *pBuf; |
| 25377 | 26672 | int rc; |
| 25378 | 26673 | if( in==0 ) return 0; |
| 25379 | 26674 | rc = fseek(in, 0, SEEK_END); |
| 25380 | 26675 | if( rc!=0 ){ |
| 25381 | | - sqlite3_fprintf(stderr,"Error: '%s' not seekable\n", zName); |
| 26676 | + cli_printf(stderr,"Error: '%s' not seekable\n", zName); |
| 25382 | 26677 | fclose(in); |
| 25383 | 26678 | return 0; |
| 25384 | 26679 | } |
| 25385 | 26680 | nIn = ftell(in); |
| 25386 | 26681 | rewind(in); |
| 25387 | 26682 | pBuf = sqlite3_malloc64( nIn+1 ); |
| 25388 | 26683 | if( pBuf==0 ){ |
| 25389 | | - sqlite3_fputs("Error: out of memory\n", stderr); |
| 26684 | + cli_puts("Error: out of memory\n", stderr); |
| 25390 | 26685 | fclose(in); |
| 25391 | 26686 | return 0; |
| 25392 | 26687 | } |
| 25393 | 26688 | nRead = fread(pBuf, nIn, 1, in); |
| 25394 | 26689 | fclose(in); |
| 25395 | 26690 | if( nRead!=1 ){ |
| 25396 | 26691 | sqlite3_free(pBuf); |
| 25397 | | - sqlite3_fprintf(stderr,"Error: cannot read '%s'\n", zName); |
| 26692 | + cli_printf(stderr,"Error: cannot read '%s'\n", zName); |
| 25398 | 26693 | return 0; |
| 25399 | 26694 | } |
| 25400 | 26695 | pBuf[nIn] = 0; |
| 25401 | 26696 | if( pnByte ) *pnByte = nIn; |
| 25402 | 26697 | return pBuf; |
| | @@ -25529,11 +26824,11 @@ |
| 25529 | 26824 | unsigned int x[16]; |
| 25530 | 26825 | char zLine[1000]; |
| 25531 | 26826 | if( zDbFilename ){ |
| 25532 | 26827 | in = sqlite3_fopen(zDbFilename, "r"); |
| 25533 | 26828 | if( in==0 ){ |
| 25534 | | - sqlite3_fprintf(stderr,"cannot open \"%s\" for reading\n", zDbFilename); |
| 26829 | + cli_printf(stderr,"cannot open \"%s\" for reading\n", zDbFilename); |
| 25535 | 26830 | return 0; |
| 25536 | 26831 | } |
| 25537 | 26832 | nLine = 0; |
| 25538 | 26833 | }else{ |
| 25539 | 26834 | in = p->in; |
| | @@ -25545,11 +26840,11 @@ |
| 25545 | 26840 | if( sqlite3_fgets(zLine, sizeof(zLine), in)==0 ) goto readHexDb_error; |
| 25546 | 26841 | rc = sscanf(zLine, "| size %d pagesize %d", &n, &pgsz); |
| 25547 | 26842 | if( rc!=2 ) goto readHexDb_error; |
| 25548 | 26843 | if( n<0 ) goto readHexDb_error; |
| 25549 | 26844 | if( pgsz<512 || pgsz>65536 || (pgsz & (pgsz-1))!=0 ){ |
| 25550 | | - sqlite3_fputs("invalid pagesize\n", stderr); |
| 26845 | + cli_puts("invalid pagesize\n", stderr); |
| 25551 | 26846 | goto readHexDb_error; |
| 25552 | 26847 | } |
| 25553 | 26848 | sz = ((i64)n+pgsz-1)&~(pgsz-1); /* Round up to nearest multiple of pgsz */ |
| 25554 | 26849 | a = sqlite3_malloc64( sz ? sz : 1 ); |
| 25555 | 26850 | shell_check_oom(a); |
| | @@ -25556,11 +26851,11 @@ |
| 25556 | 26851 | memset(a, 0, sz); |
| 25557 | 26852 | for(nLine++; sqlite3_fgets(zLine, sizeof(zLine), in)!=0; nLine++){ |
| 25558 | 26853 | int j = 0; /* Page number from "| page" line */ |
| 25559 | 26854 | int k = 0; /* Offset from "| page" line */ |
| 25560 | 26855 | if( nLine>=2000000000 ){ |
| 25561 | | - sqlite3_fprintf(stderr, "input too big\n"); |
| 26856 | + cli_printf(stderr, "input too big\n"); |
| 25562 | 26857 | goto readHexDb_error; |
| 25563 | 26858 | } |
| 25564 | 26859 | rc = sscanf(zLine, "| page %d offset %d", &j, &k); |
| 25565 | 26860 | if( rc==2 ){ |
| 25566 | 26861 | iOffset = k; |
| | @@ -25597,11 +26892,11 @@ |
| 25597 | 26892 | if(cli_strncmp(zLine, "| end ", 6)==0 ) break; |
| 25598 | 26893 | } |
| 25599 | 26894 | p->lineno = nLine; |
| 25600 | 26895 | } |
| 25601 | 26896 | sqlite3_free(a); |
| 25602 | | - sqlite3_fprintf(stderr,"Error on line %lld of --hexdb input\n", nLine); |
| 26897 | + cli_printf(stderr,"Error on line %lld of --hexdb input\n", nLine); |
| 25603 | 26898 | return 0; |
| 25604 | 26899 | } |
| 25605 | 26900 | #endif /* SQLITE_OMIT_DESERIALIZE */ |
| 25606 | 26901 | |
| 25607 | 26902 | /* |
| | @@ -25702,23 +26997,23 @@ |
| 25702 | 26997 | sqlite3_open_v2(zDbFilename, &p->db, p->openFlags, 0); |
| 25703 | 26998 | break; |
| 25704 | 26999 | } |
| 25705 | 27000 | } |
| 25706 | 27001 | if( p->db==0 || SQLITE_OK!=sqlite3_errcode(p->db) ){ |
| 25707 | | - sqlite3_fprintf(stderr,"Error: unable to open database \"%s\": %s\n", |
| 27002 | + cli_printf(stderr,"Error: unable to open database \"%s\": %s\n", |
| 25708 | 27003 | zDbFilename, sqlite3_errmsg(p->db)); |
| 25709 | 27004 | if( (openFlags & OPEN_DB_KEEPALIVE)==0 ){ |
| 25710 | | - exit(1); |
| 27005 | + cli_exit(1); |
| 25711 | 27006 | } |
| 25712 | 27007 | sqlite3_close(p->db); |
| 25713 | 27008 | sqlite3_open(":memory:", &p->db); |
| 25714 | 27009 | if( p->db==0 || SQLITE_OK!=sqlite3_errcode(p->db) ){ |
| 25715 | | - sqlite3_fputs("Also: unable to open substitute in-memory database.\n", |
| 27010 | + cli_puts("Also: unable to open substitute in-memory database.\n", |
| 25716 | 27011 | stderr); |
| 25717 | | - exit(1); |
| 27012 | + cli_exit(1); |
| 25718 | 27013 | }else{ |
| 25719 | | - sqlite3_fprintf(stderr, |
| 27014 | + cli_printf(stderr, |
| 25720 | 27015 | "Notice: using substitute in-memory database instead of \"%s\"\n", |
| 25721 | 27016 | zDbFilename); |
| 25722 | 27017 | } |
| 25723 | 27018 | } |
| 25724 | 27019 | globalDb = p->db; |
| | @@ -25792,10 +27087,12 @@ |
| 25792 | 27087 | shellAddSchemaName, 0, 0); |
| 25793 | 27088 | sqlite3_create_function(p->db, "shell_module_schema", 1, SQLITE_UTF8, p, |
| 25794 | 27089 | shellModuleSchema, 0, 0); |
| 25795 | 27090 | sqlite3_create_function(p->db, "shell_putsnl", 1, SQLITE_UTF8, p, |
| 25796 | 27091 | shellPutsFunc, 0, 0); |
| 27092 | + sqlite3_create_function(p->db, "shell_format_schema", 2, SQLITE_UTF8, p, |
| 27093 | + shellFormatSchema, 0, 0); |
| 25797 | 27094 | sqlite3_create_function(p->db, "usleep",1,SQLITE_UTF8,0, |
| 25798 | 27095 | shellUSleepFunc, 0, 0); |
| 25799 | 27096 | #ifndef SQLITE_NOHAVE_SYSTEM |
| 25800 | 27097 | sqlite3_create_function(p->db, "edit", 1, SQLITE_UTF8, 0, |
| 25801 | 27098 | editFunc, 0, 0); |
| | @@ -25826,11 +27123,11 @@ |
| 25826 | 27123 | } |
| 25827 | 27124 | rc = sqlite3_deserialize(p->db, "main", aData, nData, nData, |
| 25828 | 27125 | SQLITE_DESERIALIZE_RESIZEABLE | |
| 25829 | 27126 | SQLITE_DESERIALIZE_FREEONCLOSE); |
| 25830 | 27127 | if( rc ){ |
| 25831 | | - sqlite3_fprintf(stderr,"Error: sqlite3_deserialize() returns %d\n", rc); |
| 27128 | + cli_printf(stderr,"Error: sqlite3_deserialize() returns %d\n", rc); |
| 25832 | 27129 | } |
| 25833 | 27130 | if( p->szMax>0 ){ |
| 25834 | 27131 | sqlite3_file_control(p->db, "main", SQLITE_FCNTL_SIZE_LIMIT, &p->szMax); |
| 25835 | 27132 | } |
| 25836 | 27133 | } |
| | @@ -25841,11 +27138,11 @@ |
| 25841 | 27138 | if( p->bSafeModePersist ){ |
| 25842 | 27139 | sqlite3_set_authorizer(p->db, safeModeAuth, p); |
| 25843 | 27140 | } |
| 25844 | 27141 | #endif |
| 25845 | 27142 | sqlite3_db_config( |
| 25846 | | - p->db, SQLITE_DBCONFIG_STMT_SCANSTATUS, p->scanstatsOn, (int*)0 |
| 27143 | + p->db, SQLITE_DBCONFIG_STMT_SCANSTATUS, p->mode.scanstatsOn, (int*)0 |
| 25847 | 27144 | ); |
| 25848 | 27145 | } |
| 25849 | 27146 | } |
| 25850 | 27147 | |
| 25851 | 27148 | /* |
| | @@ -25852,11 +27149,11 @@ |
| 25852 | 27149 | ** Attempt to close the database connection. Report errors. |
| 25853 | 27150 | */ |
| 25854 | 27151 | void close_db(sqlite3 *db){ |
| 25855 | 27152 | int rc = sqlite3_close(db); |
| 25856 | 27153 | if( rc ){ |
| 25857 | | - sqlite3_fprintf(stderr, |
| 27154 | + cli_printf(stderr, |
| 25858 | 27155 | "Error: sqlite3_close() returns %d: %s\n", rc, sqlite3_errmsg(db)); |
| 25859 | 27156 | } |
| 25860 | 27157 | } |
| 25861 | 27158 | |
| 25862 | 27159 | #if (HAVE_READLINE || HAVE_EDITLINE) \ |
| | @@ -26025,11 +27322,11 @@ |
| 26025 | 27322 | return 1; |
| 26026 | 27323 | } |
| 26027 | 27324 | if( sqlite3_stricmp(zArg, "off")==0 || sqlite3_stricmp(zArg,"no")==0 ){ |
| 26028 | 27325 | return 0; |
| 26029 | 27326 | } |
| 26030 | | - sqlite3_fprintf(stderr, |
| 27327 | + cli_printf(stderr, |
| 26031 | 27328 | "ERROR: Not a boolean value: \"%s\". Assuming \"no\".\n", zArg); |
| 26032 | 27329 | return 0; |
| 26033 | 27330 | } |
| 26034 | 27331 | |
| 26035 | 27332 | /* |
| | @@ -26053,22 +27350,22 @@ |
| 26053 | 27350 | /* |
| 26054 | 27351 | ** Try to open an output file. The names "stdout" and "stderr" are |
| 26055 | 27352 | ** recognized and do the right thing. NULL is returned if the output |
| 26056 | 27353 | ** filename is "off". |
| 26057 | 27354 | */ |
| 26058 | | -static FILE *output_file_open(const char *zFile){ |
| 27355 | +static FILE *output_file_open(ShellState *p, const char *zFile){ |
| 26059 | 27356 | FILE *f; |
| 26060 | 27357 | if( cli_strcmp(zFile,"stdout")==0 ){ |
| 26061 | 27358 | f = stdout; |
| 26062 | 27359 | }else if( cli_strcmp(zFile, "stderr")==0 ){ |
| 26063 | 27360 | f = stderr; |
| 26064 | | - }else if( cli_strcmp(zFile, "off")==0 ){ |
| 27361 | + }else if( cli_strcmp(zFile, "off")==0 || p->bSafeMode ){ |
| 26065 | 27362 | f = 0; |
| 26066 | 27363 | }else{ |
| 26067 | 27364 | f = sqlite3_fopen(zFile, "w"); |
| 26068 | 27365 | if( f==0 ){ |
| 26069 | | - sqlite3_fprintf(stderr,"Error: cannot open \"%s\"\n", zFile); |
| 27366 | + cli_printf(stderr,"Error: cannot open \"%s\"\n", zFile); |
| 26070 | 27367 | } |
| 26071 | 27368 | } |
| 26072 | 27369 | return f; |
| 26073 | 27370 | } |
| 26074 | 27371 | |
| | @@ -26117,16 +27414,16 @@ |
| 26117 | 27414 | if( nSql>1000000000 ) nSql = 1000000000; |
| 26118 | 27415 | while( nSql>0 && zSql[nSql-1]==';' ){ nSql--; } |
| 26119 | 27416 | switch( mType ){ |
| 26120 | 27417 | case SQLITE_TRACE_ROW: |
| 26121 | 27418 | case SQLITE_TRACE_STMT: { |
| 26122 | | - sqlite3_fprintf(p->traceOut, "%.*s;\n", (int)nSql, zSql); |
| 27419 | + cli_printf(p->traceOut, "%.*s;\n", (int)nSql, zSql); |
| 26123 | 27420 | break; |
| 26124 | 27421 | } |
| 26125 | 27422 | case SQLITE_TRACE_PROFILE: { |
| 26126 | 27423 | sqlite3_int64 nNanosec = pX ? *(sqlite3_int64*)pX : 0; |
| 26127 | | - sqlite3_fprintf(p->traceOut, |
| 27424 | + cli_printf(p->traceOut, |
| 26128 | 27425 | "%.*s; -- %lld ns\n", (int)nSql, zSql, nNanosec); |
| 26129 | 27426 | break; |
| 26130 | 27427 | } |
| 26131 | 27428 | } |
| 26132 | 27429 | return 0; |
| | @@ -26230,15 +27527,15 @@ |
| 26230 | 27527 | do{ p->n--; }while( p->z[p->n]!=cQuote ); |
| 26231 | 27528 | p->cTerm = c; |
| 26232 | 27529 | break; |
| 26233 | 27530 | } |
| 26234 | 27531 | if( pc==cQuote && c!='\r' ){ |
| 26235 | | - sqlite3_fprintf(stderr,"%s:%d: unescaped %c character\n", |
| 27532 | + cli_printf(stderr,"%s:%d: unescaped %c character\n", |
| 26236 | 27533 | p->zFile, p->nLine, cQuote); |
| 26237 | 27534 | } |
| 26238 | 27535 | if( c==EOF ){ |
| 26239 | | - sqlite3_fprintf(stderr,"%s:%d: unterminated %c-quoted field\n", |
| 27536 | + cli_printf(stderr,"%s:%d: unterminated %c-quoted field\n", |
| 26240 | 27537 | p->zFile, startLine, cQuote); |
| 26241 | 27538 | p->cTerm = c; |
| 26242 | 27539 | break; |
| 26243 | 27540 | } |
| 26244 | 27541 | import_append_char(p, c); |
| | @@ -26333,11 +27630,11 @@ |
| 26333 | 27630 | |
| 26334 | 27631 | zQuery = sqlite3_mprintf("SELECT * FROM \"%w\"", zTable); |
| 26335 | 27632 | shell_check_oom(zQuery); |
| 26336 | 27633 | rc = sqlite3_prepare_v2(p->db, zQuery, -1, &pQuery, 0); |
| 26337 | 27634 | if( rc ){ |
| 26338 | | - sqlite3_fprintf(stderr,"Error %d: %s on [%s]\n", |
| 27635 | + cli_printf(stderr,"Error %d: %s on [%s]\n", |
| 26339 | 27636 | sqlite3_extended_errcode(p->db), sqlite3_errmsg(p->db), zQuery); |
| 26340 | 27637 | goto end_data_xfer; |
| 26341 | 27638 | } |
| 26342 | 27639 | n = sqlite3_column_count(pQuery); |
| 26343 | 27640 | zInsert = sqlite3_malloc64(200 + nTable + n*3); |
| | @@ -26350,11 +27647,11 @@ |
| 26350 | 27647 | i += 2; |
| 26351 | 27648 | } |
| 26352 | 27649 | memcpy(zInsert+i, ");", 3); |
| 26353 | 27650 | rc = sqlite3_prepare_v2(newDb, zInsert, -1, &pInsert, 0); |
| 26354 | 27651 | if( rc ){ |
| 26355 | | - sqlite3_fprintf(stderr,"Error %d: %s on [%s]\n", |
| 27652 | + cli_printf(stderr,"Error %d: %s on [%s]\n", |
| 26356 | 27653 | sqlite3_extended_errcode(newDb), sqlite3_errmsg(newDb), zInsert); |
| 26357 | 27654 | goto end_data_xfer; |
| 26358 | 27655 | } |
| 26359 | 27656 | for(k=0; k<2; k++){ |
| 26360 | 27657 | while( (rc = sqlite3_step(pQuery))==SQLITE_ROW ){ |
| | @@ -26386,11 +27683,11 @@ |
| 26386 | 27683 | } |
| 26387 | 27684 | } |
| 26388 | 27685 | } /* End for */ |
| 26389 | 27686 | rc = sqlite3_step(pInsert); |
| 26390 | 27687 | if( rc!=SQLITE_OK && rc!=SQLITE_ROW && rc!=SQLITE_DONE ){ |
| 26391 | | - sqlite3_fprintf(stderr,"Error %d: %s\n", |
| 27688 | + cli_printf(stderr,"Error %d: %s\n", |
| 26392 | 27689 | sqlite3_extended_errcode(newDb), sqlite3_errmsg(newDb)); |
| 26393 | 27690 | } |
| 26394 | 27691 | sqlite3_reset(pInsert); |
| 26395 | 27692 | cnt++; |
| 26396 | 27693 | if( (cnt%spinRate)==0 ){ |
| | @@ -26404,11 +27701,11 @@ |
| 26404 | 27701 | zQuery = sqlite3_mprintf("SELECT * FROM \"%w\" ORDER BY rowid DESC;", |
| 26405 | 27702 | zTable); |
| 26406 | 27703 | shell_check_oom(zQuery); |
| 26407 | 27704 | rc = sqlite3_prepare_v2(p->db, zQuery, -1, &pQuery, 0); |
| 26408 | 27705 | if( rc ){ |
| 26409 | | - sqlite3_fprintf(stderr,"Warning: cannot step \"%s\" backwards", zTable); |
| 27706 | + cli_printf(stderr,"Warning: cannot step \"%s\" backwards", zTable); |
| 26410 | 27707 | break; |
| 26411 | 27708 | } |
| 26412 | 27709 | } /* End for(k=0...) */ |
| 26413 | 27710 | |
| 26414 | 27711 | end_data_xfer: |
| | @@ -26441,24 +27738,24 @@ |
| 26441 | 27738 | zQuery = sqlite3_mprintf("SELECT name, sql FROM sqlite_schema" |
| 26442 | 27739 | " WHERE %s ORDER BY rowid ASC", zWhere); |
| 26443 | 27740 | shell_check_oom(zQuery); |
| 26444 | 27741 | rc = sqlite3_prepare_v2(p->db, zQuery, -1, &pQuery, 0); |
| 26445 | 27742 | if( rc ){ |
| 26446 | | - sqlite3_fprintf(stderr, |
| 27743 | + cli_printf(stderr, |
| 26447 | 27744 | "Error: (%d) %s on [%s]\n", sqlite3_extended_errcode(p->db), |
| 26448 | 27745 | sqlite3_errmsg(p->db), zQuery); |
| 26449 | 27746 | goto end_schema_xfer; |
| 26450 | 27747 | } |
| 26451 | 27748 | while( (rc = sqlite3_step(pQuery))==SQLITE_ROW ){ |
| 26452 | 27749 | zName = sqlite3_column_text(pQuery, 0); |
| 26453 | 27750 | zSql = sqlite3_column_text(pQuery, 1); |
| 26454 | 27751 | if( zName==0 || zSql==0 ) continue; |
| 26455 | 27752 | if( sqlite3_stricmp((char*)zName, "sqlite_sequence")!=0 ){ |
| 26456 | | - sqlite3_fprintf(stdout, "%s... ", zName); fflush(stdout); |
| 27753 | + cli_printf(stdout, "%s... ", zName); fflush(stdout); |
| 26457 | 27754 | sqlite3_exec(newDb, (const char*)zSql, 0, 0, &zErrMsg); |
| 26458 | 27755 | if( zErrMsg ){ |
| 26459 | | - sqlite3_fprintf(stderr,"Error: %s\nSQL: [%s]\n", zErrMsg, zSql); |
| 27756 | + cli_printf(stderr,"Error: %s\nSQL: [%s]\n", zErrMsg, zSql); |
| 26460 | 27757 | sqlite3_free(zErrMsg); |
| 26461 | 27758 | zErrMsg = 0; |
| 26462 | 27759 | } |
| 26463 | 27760 | } |
| 26464 | 27761 | if( xForEach ){ |
| | @@ -26472,23 +27769,23 @@ |
| 26472 | 27769 | zQuery = sqlite3_mprintf("SELECT name, sql FROM sqlite_schema" |
| 26473 | 27770 | " WHERE %s ORDER BY rowid DESC", zWhere); |
| 26474 | 27771 | shell_check_oom(zQuery); |
| 26475 | 27772 | rc = sqlite3_prepare_v2(p->db, zQuery, -1, &pQuery, 0); |
| 26476 | 27773 | if( rc ){ |
| 26477 | | - sqlite3_fprintf(stderr,"Error: (%d) %s on [%s]\n", |
| 27774 | + cli_printf(stderr,"Error: (%d) %s on [%s]\n", |
| 26478 | 27775 | sqlite3_extended_errcode(p->db), sqlite3_errmsg(p->db), zQuery); |
| 26479 | 27776 | goto end_schema_xfer; |
| 26480 | 27777 | } |
| 26481 | 27778 | while( sqlite3_step(pQuery)==SQLITE_ROW ){ |
| 26482 | 27779 | zName = sqlite3_column_text(pQuery, 0); |
| 26483 | 27780 | zSql = sqlite3_column_text(pQuery, 1); |
| 26484 | 27781 | if( zName==0 || zSql==0 ) continue; |
| 26485 | 27782 | if( sqlite3_stricmp((char*)zName, "sqlite_sequence")==0 ) continue; |
| 26486 | | - sqlite3_fprintf(stdout, "%s... ", zName); fflush(stdout); |
| 27783 | + cli_printf(stdout, "%s... ", zName); fflush(stdout); |
| 26487 | 27784 | sqlite3_exec(newDb, (const char*)zSql, 0, 0, &zErrMsg); |
| 26488 | 27785 | if( zErrMsg ){ |
| 26489 | | - sqlite3_fprintf(stderr,"Error: %s\nSQL: [%s]\n", zErrMsg, zSql); |
| 27786 | + cli_printf(stderr,"Error: %s\nSQL: [%s]\n", zErrMsg, zSql); |
| 26490 | 27787 | sqlite3_free(zErrMsg); |
| 26491 | 27788 | zErrMsg = 0; |
| 26492 | 27789 | } |
| 26493 | 27790 | if( xForEach ){ |
| 26494 | 27791 | xForEach(p, newDb, (const char*)zName); |
| | @@ -26508,16 +27805,16 @@ |
| 26508 | 27805 | */ |
| 26509 | 27806 | static void tryToClone(ShellState *p, const char *zNewDb){ |
| 26510 | 27807 | int rc; |
| 26511 | 27808 | sqlite3 *newDb = 0; |
| 26512 | 27809 | if( access(zNewDb,0)==0 ){ |
| 26513 | | - sqlite3_fprintf(stderr,"File \"%s\" already exists.\n", zNewDb); |
| 27810 | + cli_printf(stderr,"File \"%s\" already exists.\n", zNewDb); |
| 26514 | 27811 | return; |
| 26515 | 27812 | } |
| 26516 | 27813 | rc = sqlite3_open(zNewDb, &newDb); |
| 26517 | 27814 | if( rc ){ |
| 26518 | | - sqlite3_fprintf(stderr, |
| 27815 | + cli_printf(stderr, |
| 26519 | 27816 | "Cannot create output database: %s\n", sqlite3_errmsg(newDb)); |
| 26520 | 27817 | }else{ |
| 26521 | 27818 | sqlite3_exec(p->db, "PRAGMA writable_schema=ON;", 0, 0, 0); |
| 26522 | 27819 | sqlite3_exec(newDb, "BEGIN EXCLUSIVE;", 0, 0, 0); |
| 26523 | 27820 | tryToCloneSchema(p, newDb, "type='table'", tryToCloneData); |
| | @@ -26532,16 +27829,16 @@ |
| 26532 | 27829 | /* |
| 26533 | 27830 | ** Change the output stream (file or pipe or console) to something else. |
| 26534 | 27831 | */ |
| 26535 | 27832 | static void output_redir(ShellState *p, FILE *pfNew){ |
| 26536 | 27833 | if( p->out != stdout ){ |
| 26537 | | - sqlite3_fputs("Output already redirected.\n", stderr); |
| 27834 | + cli_puts("Output already redirected.\n", stderr); |
| 26538 | 27835 | }else{ |
| 26539 | 27836 | p->out = pfNew; |
| 26540 | 27837 | setCrlfMode(p); |
| 26541 | | - if( p->mode==MODE_Www ){ |
| 26542 | | - sqlite3_fputs( |
| 27838 | + if( p->mode.eMode==MODE_Www ){ |
| 27839 | + cli_puts( |
| 26543 | 27840 | "<!DOCTYPE html>\n" |
| 26544 | 27841 | "<HTML><BODY><PRE>\n", |
| 26545 | 27842 | p->out |
| 26546 | 27843 | ); |
| 26547 | 27844 | } |
| | @@ -26559,12 +27856,12 @@ |
| 26559 | 27856 | if( p->outfile[0]=='|' ){ |
| 26560 | 27857 | #ifndef SQLITE_OMIT_POPEN |
| 26561 | 27858 | pclose(p->out); |
| 26562 | 27859 | #endif |
| 26563 | 27860 | }else{ |
| 26564 | | - if( p->mode==MODE_Www ){ |
| 26565 | | - sqlite3_fputs("</PRE></BODY></HTML>\n", p->out); |
| 27861 | + if( p->mode.eMode==MODE_Www ){ |
| 27862 | + cli_puts("</PRE></BODY></HTML>\n", p->out); |
| 26566 | 27863 | } |
| 26567 | 27864 | output_file_close(p->out); |
| 26568 | 27865 | #ifndef SQLITE_NOHAVE_SYSTEM |
| 26569 | 27866 | if( p->doXdgOpen ){ |
| 26570 | 27867 | const char *zXdgOpenCmd = |
| | @@ -26576,26 +27873,30 @@ |
| 26576 | 27873 | "xdg-open"; |
| 26577 | 27874 | #endif |
| 26578 | 27875 | char *zCmd; |
| 26579 | 27876 | zCmd = sqlite3_mprintf("%s %s", zXdgOpenCmd, p->zTempFile); |
| 26580 | 27877 | if( system(zCmd) ){ |
| 26581 | | - sqlite3_fprintf(stderr,"Failed: [%s]\n", zCmd); |
| 27878 | + cli_printf(stderr,"Failed: [%s]\n", zCmd); |
| 26582 | 27879 | }else{ |
| 26583 | 27880 | /* Give the start/open/xdg-open command some time to get |
| 26584 | 27881 | ** going before we continue, and potential delete the |
| 26585 | 27882 | ** p->zTempFile data file out from under it */ |
| 26586 | 27883 | sqlite3_sleep(2000); |
| 26587 | 27884 | } |
| 26588 | 27885 | sqlite3_free(zCmd); |
| 26589 | | - outputModePop(p); |
| 27886 | + modePop(p); |
| 26590 | 27887 | p->doXdgOpen = 0; |
| 26591 | 27888 | } |
| 26592 | 27889 | #endif /* !defined(SQLITE_NOHAVE_SYSTEM) */ |
| 26593 | 27890 | } |
| 26594 | 27891 | p->outfile[0] = 0; |
| 26595 | 27892 | p->out = stdout; |
| 26596 | 27893 | setCrlfMode(p); |
| 27894 | + if( cli_output_capture ){ |
| 27895 | + sqlite3_str_free(cli_output_capture); |
| 27896 | + cli_output_capture = 0; |
| 27897 | + } |
| 26597 | 27898 | } |
| 26598 | 27899 | #else |
| 26599 | 27900 | # define output_redir(SS,pfO) |
| 26600 | 27901 | # define output_reset(SS) |
| 26601 | 27902 | #endif |
| | @@ -26676,11 +27977,11 @@ |
| 26676 | 27977 | if( p->db==0 ) return 1; |
| 26677 | 27978 | rc = sqlite3_prepare_v2(p->db, |
| 26678 | 27979 | "SELECT data FROM sqlite_dbpage(?1) WHERE pgno=1", |
| 26679 | 27980 | -1, &pStmt, 0); |
| 26680 | 27981 | if( rc ){ |
| 26681 | | - sqlite3_fprintf(stderr,"error: %s\n", sqlite3_errmsg(p->db)); |
| 27982 | + cli_printf(stderr,"error: %s\n", sqlite3_errmsg(p->db)); |
| 26682 | 27983 | sqlite3_finalize(pStmt); |
| 26683 | 27984 | return 1; |
| 26684 | 27985 | } |
| 26685 | 27986 | sqlite3_bind_text(pStmt, 1, zDb, -1, SQLITE_STATIC); |
| 26686 | 27987 | if( sqlite3_step(pStmt)==SQLITE_ROW |
| | @@ -26689,32 +27990,32 @@ |
| 26689 | 27990 | const u8 *pb = sqlite3_column_blob(pStmt,0); |
| 26690 | 27991 | shell_check_oom(pb); |
| 26691 | 27992 | memcpy(aHdr, pb, 100); |
| 26692 | 27993 | sqlite3_finalize(pStmt); |
| 26693 | 27994 | }else{ |
| 26694 | | - sqlite3_fputs("unable to read database header\n", stderr); |
| 27995 | + cli_puts("unable to read database header\n", stderr); |
| 26695 | 27996 | sqlite3_finalize(pStmt); |
| 26696 | 27997 | return 1; |
| 26697 | 27998 | } |
| 26698 | 27999 | i = get2byteInt(aHdr+16); |
| 26699 | 28000 | if( i==1 ) i = 65536; |
| 26700 | | - sqlite3_fprintf(p->out, "%-20s %d\n", "database page size:", i); |
| 26701 | | - sqlite3_fprintf(p->out, "%-20s %d\n", "write format:", aHdr[18]); |
| 26702 | | - sqlite3_fprintf(p->out, "%-20s %d\n", "read format:", aHdr[19]); |
| 26703 | | - sqlite3_fprintf(p->out, "%-20s %d\n", "reserved bytes:", aHdr[20]); |
| 28001 | + cli_printf(p->out, "%-20s %d\n", "database page size:", i); |
| 28002 | + cli_printf(p->out, "%-20s %d\n", "write format:", aHdr[18]); |
| 28003 | + cli_printf(p->out, "%-20s %d\n", "read format:", aHdr[19]); |
| 28004 | + cli_printf(p->out, "%-20s %d\n", "reserved bytes:", aHdr[20]); |
| 26704 | 28005 | for(i=0; i<ArraySize(aField); i++){ |
| 26705 | 28006 | int ofst = aField[i].ofst; |
| 26706 | 28007 | unsigned int val = get4byteInt(aHdr + ofst); |
| 26707 | | - sqlite3_fprintf(p->out, "%-20s %u", aField[i].zName, val); |
| 28008 | + cli_printf(p->out, "%-20s %u", aField[i].zName, val); |
| 26708 | 28009 | switch( ofst ){ |
| 26709 | 28010 | case 56: { |
| 26710 | | - if( val==1 ) sqlite3_fputs(" (utf8)", p->out); |
| 26711 | | - if( val==2 ) sqlite3_fputs(" (utf16le)", p->out); |
| 26712 | | - if( val==3 ) sqlite3_fputs(" (utf16be)", p->out); |
| 28011 | + if( val==1 ) cli_puts(" (utf8)", p->out); |
| 28012 | + if( val==2 ) cli_puts(" (utf16le)", p->out); |
| 28013 | + if( val==3 ) cli_puts(" (utf16be)", p->out); |
| 26713 | 28014 | } |
| 26714 | 28015 | } |
| 26715 | | - sqlite3_fputs("\n", p->out); |
| 28016 | + cli_puts("\n", p->out); |
| 26716 | 28017 | } |
| 26717 | 28018 | if( zDb==0 ){ |
| 26718 | 28019 | zSchemaTab = sqlite3_mprintf("main.sqlite_schema"); |
| 26719 | 28020 | }else if( cli_strcmp(zDb,"temp")==0 ){ |
| 26720 | 28021 | zSchemaTab = sqlite3_mprintf("%s", "sqlite_temp_schema"); |
| | @@ -26721,15 +28022,15 @@ |
| 26721 | 28022 | }else{ |
| 26722 | 28023 | zSchemaTab = sqlite3_mprintf("\"%w\".sqlite_schema", zDb); |
| 26723 | 28024 | } |
| 26724 | 28025 | for(i=0; i<ArraySize(aQuery); i++){ |
| 26725 | 28026 | int val = db_int(p->db, aQuery[i].zSql, zSchemaTab); |
| 26726 | | - sqlite3_fprintf(p->out, "%-20s %d\n", aQuery[i].zName, val); |
| 28027 | + cli_printf(p->out, "%-20s %d\n", aQuery[i].zName, val); |
| 26727 | 28028 | } |
| 26728 | 28029 | sqlite3_free(zSchemaTab); |
| 26729 | 28030 | sqlite3_file_control(p->db, zDb, SQLITE_FCNTL_DATA_VERSION, &iDataVersion); |
| 26730 | | - sqlite3_fprintf(p->out, "%-20s %u\n", "data version", iDataVersion); |
| 28031 | + cli_printf(p->out, "%-20s %u\n", "data version", iDataVersion); |
| 26731 | 28032 | return 0; |
| 26732 | 28033 | } |
| 26733 | 28034 | #endif /* SQLITE_SHELL_HAVE_RECOVER */ |
| 26734 | 28035 | |
| 26735 | 28036 | /* |
| | @@ -26785,11 +28086,11 @@ |
| 26785 | 28086 | zTail++; |
| 26786 | 28087 | } |
| 26787 | 28088 | } |
| 26788 | 28089 | zName = strdup(zTail); |
| 26789 | 28090 | shell_check_oom(zName); |
| 26790 | | - sqlite3_fprintf(p->out, "| size %lld pagesize %d filename %s\n", |
| 28091 | + cli_printf(p->out, "| size %lld pagesize %d filename %s\n", |
| 26791 | 28092 | nPage*pgSz, pgSz, zName); |
| 26792 | 28093 | sqlite3_finalize(pStmt); |
| 26793 | 28094 | pStmt = 0; |
| 26794 | 28095 | rc = sqlite3_prepare_v2(p->db, |
| 26795 | 28096 | "SELECT pgno, data FROM sqlite_dbpage ORDER BY pgno", -1, &pStmt, 0); |
| | @@ -26801,31 +28102,31 @@ |
| 26801 | 28102 | for(i=0; i<pgSz; i+=16){ |
| 26802 | 28103 | const u8 *aLine = aData+i; |
| 26803 | 28104 | for(j=0; j<16 && aLine[j]==0; j++){} |
| 26804 | 28105 | if( j==16 ) continue; |
| 26805 | 28106 | if( !seenPageLabel ){ |
| 26806 | | - sqlite3_fprintf(p->out, "| page %lld offset %lld\n",pgno,(pgno-1)*pgSz); |
| 28107 | + cli_printf(p->out, "| page %lld offset %lld\n",pgno,(pgno-1)*pgSz); |
| 26807 | 28108 | seenPageLabel = 1; |
| 26808 | 28109 | } |
| 26809 | | - sqlite3_fprintf(p->out, "| %5d:", i); |
| 26810 | | - for(j=0; j<16; j++) sqlite3_fprintf(p->out, " %02x", aLine[j]); |
| 26811 | | - sqlite3_fprintf(p->out, " "); |
| 28110 | + cli_printf(p->out, "| %5d:", i); |
| 28111 | + for(j=0; j<16; j++) cli_printf(p->out, " %02x", aLine[j]); |
| 28112 | + cli_printf(p->out, " "); |
| 26812 | 28113 | for(j=0; j<16; j++){ |
| 26813 | 28114 | unsigned char c = (unsigned char)aLine[j]; |
| 26814 | | - sqlite3_fprintf(p->out, "%c", bShow[c]); |
| 28115 | + cli_printf(p->out, "%c", bShow[c]); |
| 26815 | 28116 | } |
| 26816 | | - sqlite3_fprintf(p->out, "\n"); |
| 28117 | + cli_printf(p->out, "\n"); |
| 26817 | 28118 | } |
| 26818 | 28119 | } |
| 26819 | 28120 | sqlite3_finalize(pStmt); |
| 26820 | | - sqlite3_fprintf(p->out, "| end %s\n", zName); |
| 28121 | + cli_printf(p->out, "| end %s\n", zName); |
| 26821 | 28122 | free(zName); |
| 26822 | 28123 | return 0; |
| 26823 | 28124 | |
| 26824 | 28125 | dbtotxt_error: |
| 26825 | 28126 | if( rc ){ |
| 26826 | | - sqlite3_fprintf(stderr, "ERROR: %s\n", sqlite3_errmsg(p->db)); |
| 28127 | + cli_printf(stderr, "ERROR: %s\n", sqlite3_errmsg(p->db)); |
| 26827 | 28128 | } |
| 26828 | 28129 | sqlite3_finalize(pStmt); |
| 26829 | 28130 | free(zName); |
| 26830 | 28131 | return 1; |
| 26831 | 28132 | } |
| | @@ -26832,11 +28133,11 @@ |
| 26832 | 28133 | |
| 26833 | 28134 | /* |
| 26834 | 28135 | ** Print the given string as an error message. |
| 26835 | 28136 | */ |
| 26836 | 28137 | static void shellEmitError(const char *zErr){ |
| 26837 | | - sqlite3_fprintf(stderr,"Error: %s\n", zErr); |
| 28138 | + cli_printf(stderr,"Error: %s\n", zErr); |
| 26838 | 28139 | } |
| 26839 | 28140 | /* |
| 26840 | 28141 | ** Print the current sqlite3_errmsg() value to stderr and return 1. |
| 26841 | 28142 | */ |
| 26842 | 28143 | static int shellDatabaseError(sqlite3 *db){ |
| | @@ -27015,42 +28316,45 @@ |
| 27015 | 28316 | if( shellDeleteFile(p->zTempFile) ) return; |
| 27016 | 28317 | sqlite3_free(p->zTempFile); |
| 27017 | 28318 | p->zTempFile = 0; |
| 27018 | 28319 | } |
| 27019 | 28320 | |
| 28321 | +/* Forward reference */ |
| 28322 | +static char *find_home_dir(int clearFlag); |
| 28323 | + |
| 27020 | 28324 | /* |
| 27021 | 28325 | ** Create a new temp file name with the given suffix. |
| 28326 | +** |
| 28327 | +** Because the classic temp folders like /tmp are no longer |
| 28328 | +** accessible to web browsers, for security reasons, create the |
| 28329 | +** temp file in the user's home directory. |
| 27022 | 28330 | */ |
| 27023 | 28331 | static void newTempFile(ShellState *p, const char *zSuffix){ |
| 28332 | + char *zHome; /* Home directory */ |
| 28333 | + int i; /* Loop counter */ |
| 28334 | + sqlite3_uint64 r = 0; /* Integer with 64 bits of randomness */ |
| 28335 | + char zRand[32]; /* Text string with 160 bits of randomness */ |
| 28336 | +#ifdef _WIN32 |
| 28337 | + const char cDirSep = '\\'; |
| 28338 | +#else |
| 28339 | + const char cDirSep = '/'; |
| 28340 | +#endif |
| 28341 | + |
| 28342 | + for(i=0; i<31; i++){ |
| 28343 | + if( (i%12)==0 ) sqlite3_randomness(sizeof(r),&r); |
| 28344 | + zRand[i] = "0123456789abcdefghijklmnopqrstuvwxyz"[r%36]; |
| 28345 | + r /= 36; |
| 28346 | + } |
| 28347 | + zRand[i] = 0; |
| 27024 | 28348 | clearTempFile(p); |
| 27025 | 28349 | sqlite3_free(p->zTempFile); |
| 27026 | 28350 | p->zTempFile = 0; |
| 27027 | | - if( p->db ){ |
| 27028 | | - sqlite3_file_control(p->db, 0, SQLITE_FCNTL_TEMPFILENAME, &p->zTempFile); |
| 27029 | | - } |
| 27030 | | - if( p->zTempFile==0 ){ |
| 27031 | | - /* If p->db is an in-memory database then the TEMPFILENAME file-control |
| 27032 | | - ** will not work and we will need to fallback to guessing */ |
| 27033 | | - char *zTemp; |
| 27034 | | - sqlite3_uint64 r; |
| 27035 | | - sqlite3_randomness(sizeof(r), &r); |
| 27036 | | - zTemp = getenv("TEMP"); |
| 27037 | | - if( zTemp==0 ) zTemp = getenv("TMP"); |
| 27038 | | - if( zTemp==0 ){ |
| 27039 | | -#ifdef _WIN32 |
| 27040 | | - zTemp = "\\tmp"; |
| 27041 | | -#else |
| 27042 | | - zTemp = "/tmp"; |
| 27043 | | -#endif |
| 27044 | | - } |
| 27045 | | - p->zTempFile = sqlite3_mprintf("%s/temp%llx.%s", zTemp, r, zSuffix); |
| 27046 | | - }else{ |
| 27047 | | - p->zTempFile = sqlite3_mprintf("%z.%s", p->zTempFile, zSuffix); |
| 27048 | | - } |
| 28351 | + zHome = find_home_dir(0); |
| 28352 | + p->zTempFile = sqlite3_mprintf("%s%ctemp-%s.%s", |
| 28353 | + zHome,cDirSep,zRand,zSuffix); |
| 27049 | 28354 | shell_check_oom(p->zTempFile); |
| 27050 | 28355 | } |
| 27051 | | - |
| 27052 | 28356 | |
| 27053 | 28357 | /* |
| 27054 | 28358 | ** The implementation of SQL scalar function fkey_collate_clause(), used |
| 27055 | 28359 | ** by the ".lint fkey-indexes" command. This scalar function is always |
| 27056 | 28360 | ** called with four arguments - the parent table name, the parent column name, |
| | @@ -27192,11 +28496,11 @@ |
| 27192 | 28496 | else if( n>1 && sqlite3_strnicmp("-groupbyparent", azArg[i], n)==0 ){ |
| 27193 | 28497 | bGroupByParent = 1; |
| 27194 | 28498 | zIndent = " "; |
| 27195 | 28499 | } |
| 27196 | 28500 | else{ |
| 27197 | | - sqlite3_fprintf(stderr, |
| 28501 | + cli_printf(stderr, |
| 27198 | 28502 | "Usage: %s %s ?-verbose? ?-groupbyparent?\n", azArg[0], azArg[1]); |
| 27199 | 28503 | return SQLITE_ERROR; |
| 27200 | 28504 | } |
| 27201 | 28505 | } |
| 27202 | 28506 | |
| | @@ -27237,45 +28541,45 @@ |
| 27237 | 28541 | } |
| 27238 | 28542 | rc = sqlite3_finalize(pExplain); |
| 27239 | 28543 | if( rc!=SQLITE_OK ) break; |
| 27240 | 28544 | |
| 27241 | 28545 | if( res<0 ){ |
| 27242 | | - sqlite3_fputs("Error: internal error", stderr); |
| 28546 | + cli_puts("Error: internal error", stderr); |
| 27243 | 28547 | break; |
| 27244 | 28548 | }else{ |
| 27245 | 28549 | if( bGroupByParent |
| 27246 | 28550 | && (bVerbose || res==0) |
| 27247 | 28551 | && (zPrev==0 || sqlite3_stricmp(zParent, zPrev)) |
| 27248 | 28552 | ){ |
| 27249 | | - sqlite3_fprintf(out, "-- Parent table %s\n", zParent); |
| 28553 | + cli_printf(out, "-- Parent table %s\n", zParent); |
| 27250 | 28554 | sqlite3_free(zPrev); |
| 27251 | 28555 | zPrev = sqlite3_mprintf("%s", zParent); |
| 27252 | 28556 | } |
| 27253 | 28557 | |
| 27254 | 28558 | if( res==0 ){ |
| 27255 | | - sqlite3_fprintf(out, "%s%s --> %s\n", zIndent, zCI, zTarget); |
| 28559 | + cli_printf(out, "%s%s --> %s\n", zIndent, zCI, zTarget); |
| 27256 | 28560 | }else if( bVerbose ){ |
| 27257 | | - sqlite3_fprintf(out, |
| 28561 | + cli_printf(out, |
| 27258 | 28562 | "%s/* no extra indexes required for %s -> %s */\n", |
| 27259 | 28563 | zIndent, zFrom, zTarget |
| 27260 | 28564 | ); |
| 27261 | 28565 | } |
| 27262 | 28566 | } |
| 27263 | 28567 | } |
| 27264 | 28568 | sqlite3_free(zPrev); |
| 27265 | 28569 | |
| 27266 | 28570 | if( rc!=SQLITE_OK ){ |
| 27267 | | - sqlite3_fprintf(stderr,"%s\n", sqlite3_errmsg(db)); |
| 28571 | + cli_printf(stderr,"%s\n", sqlite3_errmsg(db)); |
| 27268 | 28572 | } |
| 27269 | 28573 | |
| 27270 | 28574 | rc2 = sqlite3_finalize(pSql); |
| 27271 | 28575 | if( rc==SQLITE_OK && rc2!=SQLITE_OK ){ |
| 27272 | 28576 | rc = rc2; |
| 27273 | | - sqlite3_fprintf(stderr,"%s\n", sqlite3_errmsg(db)); |
| 28577 | + cli_printf(stderr,"%s\n", sqlite3_errmsg(db)); |
| 27274 | 28578 | } |
| 27275 | 28579 | }else{ |
| 27276 | | - sqlite3_fprintf(stderr,"%s\n", sqlite3_errmsg(db)); |
| 28580 | + cli_printf(stderr,"%s\n", sqlite3_errmsg(db)); |
| 27277 | 28581 | } |
| 27278 | 28582 | |
| 27279 | 28583 | return rc; |
| 27280 | 28584 | } |
| 27281 | 28585 | |
| | @@ -27291,13 +28595,13 @@ |
| 27291 | 28595 | n = (nArg>=2 ? strlen30(azArg[1]) : 0); |
| 27292 | 28596 | if( n<1 || sqlite3_strnicmp(azArg[1], "fkey-indexes", n) ) goto usage; |
| 27293 | 28597 | return lintFkeyIndexes(pState, azArg, nArg); |
| 27294 | 28598 | |
| 27295 | 28599 | usage: |
| 27296 | | - sqlite3_fprintf(stderr,"Usage %s sub-command ?switches...?\n", azArg[0]); |
| 27297 | | - sqlite3_fprintf(stderr, "Where sub-commands are:\n"); |
| 27298 | | - sqlite3_fprintf(stderr, " fkey-indexes\n"); |
| 28600 | + cli_printf(stderr,"Usage %s sub-command ?switches...?\n", azArg[0]); |
| 28601 | + cli_printf(stderr, "Where sub-commands are:\n"); |
| 28602 | + cli_printf(stderr, " fkey-indexes\n"); |
| 27299 | 28603 | return SQLITE_ERROR; |
| 27300 | 28604 | } |
| 27301 | 28605 | |
| 27302 | 28606 | static void shellPrepare( |
| 27303 | 28607 | sqlite3 *db, |
| | @@ -27307,11 +28611,11 @@ |
| 27307 | 28611 | ){ |
| 27308 | 28612 | *ppStmt = 0; |
| 27309 | 28613 | if( *pRc==SQLITE_OK ){ |
| 27310 | 28614 | int rc = sqlite3_prepare_v2(db, zSql, -1, ppStmt, 0); |
| 27311 | 28615 | if( rc!=SQLITE_OK ){ |
| 27312 | | - sqlite3_fprintf(stderr, |
| 28616 | + cli_printf(stderr, |
| 27313 | 28617 | "sql error: %s (%d)\n", sqlite3_errmsg(db), sqlite3_errcode(db)); |
| 27314 | 28618 | *pRc = rc; |
| 27315 | 28619 | } |
| 27316 | 28620 | } |
| 27317 | 28621 | } |
| | @@ -27352,11 +28656,11 @@ |
| 27352 | 28656 | if( pStmt ){ |
| 27353 | 28657 | sqlite3 *db = sqlite3_db_handle(pStmt); |
| 27354 | 28658 | int rc = sqlite3_finalize(pStmt); |
| 27355 | 28659 | if( *pRc==SQLITE_OK ){ |
| 27356 | 28660 | if( rc!=SQLITE_OK ){ |
| 27357 | | - sqlite3_fprintf(stderr,"SQL error: %s\n", sqlite3_errmsg(db)); |
| 28661 | + cli_printf(stderr,"SQL error: %s\n", sqlite3_errmsg(db)); |
| 27358 | 28662 | } |
| 27359 | 28663 | *pRc = rc; |
| 27360 | 28664 | } |
| 27361 | 28665 | } |
| 27362 | 28666 | } |
| | @@ -27374,11 +28678,11 @@ |
| 27374 | 28678 | ){ |
| 27375 | 28679 | int rc = sqlite3_reset(pStmt); |
| 27376 | 28680 | if( *pRc==SQLITE_OK ){ |
| 27377 | 28681 | if( rc!=SQLITE_OK ){ |
| 27378 | 28682 | sqlite3 *db = sqlite3_db_handle(pStmt); |
| 27379 | | - sqlite3_fprintf(stderr,"SQL error: %s\n", sqlite3_errmsg(db)); |
| 28683 | + cli_printf(stderr,"SQL error: %s\n", sqlite3_errmsg(db)); |
| 27380 | 28684 | } |
| 27381 | 28685 | *pRc = rc; |
| 27382 | 28686 | } |
| 27383 | 28687 | } |
| 27384 | 28688 | #endif /* !defined SQLITE_OMIT_VIRTUALTABLE */ |
| | @@ -27427,13 +28731,13 @@ |
| 27427 | 28731 | va_start(ap, zFmt); |
| 27428 | 28732 | z = sqlite3_vmprintf(zFmt, ap); |
| 27429 | 28733 | va_end(ap); |
| 27430 | 28734 | shellEmitError(z); |
| 27431 | 28735 | if( pAr->fromCmdLine ){ |
| 27432 | | - sqlite3_fputs("Use \"-A\" for more help\n", stderr); |
| 28736 | + cli_puts("Use \"-A\" for more help\n", stderr); |
| 27433 | 28737 | }else{ |
| 27434 | | - sqlite3_fputs("Use \".archive --help\" for more help\n", stderr); |
| 28738 | + cli_puts("Use \".archive --help\" for more help\n", stderr); |
| 27435 | 28739 | } |
| 27436 | 28740 | sqlite3_free(z); |
| 27437 | 28741 | return SQLITE_ERROR; |
| 27438 | 28742 | } |
| 27439 | 28743 | |
| | @@ -27529,11 +28833,11 @@ |
| 27529 | 28833 | }; |
| 27530 | 28834 | int nSwitch = sizeof(aSwitch) / sizeof(struct ArSwitch); |
| 27531 | 28835 | struct ArSwitch *pEnd = &aSwitch[nSwitch]; |
| 27532 | 28836 | |
| 27533 | 28837 | if( nArg<=1 ){ |
| 27534 | | - sqlite3_fprintf(stderr, "Wrong number of arguments. Usage:\n"); |
| 28838 | + cli_printf(stderr, "Wrong number of arguments. Usage:\n"); |
| 27535 | 28839 | return arUsage(stderr); |
| 27536 | 28840 | }else{ |
| 27537 | 28841 | char *z = azArg[1]; |
| 27538 | 28842 | if( z[0]!='-' ){ |
| 27539 | 28843 | /* Traditional style [tar] invocation */ |
| | @@ -27635,11 +28939,11 @@ |
| 27635 | 28939 | } |
| 27636 | 28940 | } |
| 27637 | 28941 | } |
| 27638 | 28942 | } |
| 27639 | 28943 | if( pAr->eCmd==0 ){ |
| 27640 | | - sqlite3_fprintf(stderr, "Required argument missing. Usage:\n"); |
| 28944 | + cli_printf(stderr, "Required argument missing. Usage:\n"); |
| 27641 | 28945 | return arUsage(stderr); |
| 27642 | 28946 | } |
| 27643 | 28947 | return SQLITE_OK; |
| 27644 | 28948 | } |
| 27645 | 28949 | |
| | @@ -27678,11 +28982,11 @@ |
| 27678 | 28982 | if( SQLITE_ROW==sqlite3_step(pTest) ){ |
| 27679 | 28983 | bOk = 1; |
| 27680 | 28984 | } |
| 27681 | 28985 | shellReset(&rc, pTest); |
| 27682 | 28986 | if( rc==SQLITE_OK && bOk==0 ){ |
| 27683 | | - sqlite3_fprintf(stderr,"not found in archive: %s\n", z); |
| 28987 | + cli_printf(stderr,"not found in archive: %s\n", z); |
| 27684 | 28988 | rc = SQLITE_ERROR; |
| 27685 | 28989 | } |
| 27686 | 28990 | } |
| 27687 | 28991 | shellFinalize(&rc, pTest); |
| 27688 | 28992 | } |
| | @@ -27761,19 +29065,19 @@ |
| 27761 | 29065 | arWhereClause(&rc, pAr, &zWhere); |
| 27762 | 29066 | |
| 27763 | 29067 | shellPreparePrintf(pAr->db, &rc, &pSql, zSql, azCols[pAr->bVerbose], |
| 27764 | 29068 | pAr->zSrcTable, zWhere); |
| 27765 | 29069 | if( pAr->bDryRun ){ |
| 27766 | | - sqlite3_fprintf(pAr->out, "%s\n", sqlite3_sql(pSql)); |
| 29070 | + cli_printf(pAr->out, "%s\n", sqlite3_sql(pSql)); |
| 27767 | 29071 | }else{ |
| 27768 | 29072 | while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSql) ){ |
| 27769 | 29073 | if( pAr->bVerbose ){ |
| 27770 | | - sqlite3_fprintf(pAr->out, "%s % 10d %s %s\n", |
| 29074 | + cli_printf(pAr->out, "%s % 10d %s %s\n", |
| 27771 | 29075 | sqlite3_column_text(pSql, 0), sqlite3_column_int(pSql, 1), |
| 27772 | 29076 | sqlite3_column_text(pSql, 2),sqlite3_column_text(pSql, 3)); |
| 27773 | 29077 | }else{ |
| 27774 | | - sqlite3_fprintf(pAr->out, "%s\n", sqlite3_column_text(pSql, 0)); |
| 29078 | + cli_printf(pAr->out, "%s\n", sqlite3_column_text(pSql, 0)); |
| 27775 | 29079 | } |
| 27776 | 29080 | } |
| 27777 | 29081 | } |
| 27778 | 29082 | shellFinalize(&rc, pSql); |
| 27779 | 29083 | sqlite3_free(zWhere); |
| | @@ -27796,11 +29100,11 @@ |
| 27796 | 29100 | } |
| 27797 | 29101 | if( rc==SQLITE_OK ){ |
| 27798 | 29102 | zSql = sqlite3_mprintf("DELETE FROM %s WHERE %s;", |
| 27799 | 29103 | pAr->zSrcTable, zWhere); |
| 27800 | 29104 | if( pAr->bDryRun ){ |
| 27801 | | - sqlite3_fprintf(pAr->out, "%s\n", zSql); |
| 29105 | + cli_printf(pAr->out, "%s\n", zSql); |
| 27802 | 29106 | }else{ |
| 27803 | 29107 | char *zErr = 0; |
| 27804 | 29108 | rc = sqlite3_exec(pAr->db, "SAVEPOINT ar;", 0, 0, 0); |
| 27805 | 29109 | if( rc==SQLITE_OK ){ |
| 27806 | 29110 | rc = sqlite3_exec(pAr->db, zSql, 0, 0, &zErr); |
| | @@ -27809,11 +29113,11 @@ |
| 27809 | 29113 | }else{ |
| 27810 | 29114 | rc = sqlite3_exec(pAr->db, "RELEASE ar;", 0, 0, 0); |
| 27811 | 29115 | } |
| 27812 | 29116 | } |
| 27813 | 29117 | if( zErr ){ |
| 27814 | | - sqlite3_fprintf(stdout, "ERROR: %s\n", zErr); /* stdout? */ |
| 29118 | + cli_printf(stdout, "ERROR: %s\n", zErr); /* stdout? */ |
| 27815 | 29119 | sqlite3_free(zErr); |
| 27816 | 29120 | } |
| 27817 | 29121 | } |
| 27818 | 29122 | } |
| 27819 | 29123 | sqlite3_free(zWhere); |
| | @@ -27873,15 +29177,15 @@ |
| 27873 | 29177 | ** populating them changes the timestamp). */ |
| 27874 | 29178 | for(i=0; i<2; i++){ |
| 27875 | 29179 | j = sqlite3_bind_parameter_index(pSql, "$dirOnly"); |
| 27876 | 29180 | sqlite3_bind_int(pSql, j, i); |
| 27877 | 29181 | if( pAr->bDryRun ){ |
| 27878 | | - sqlite3_fprintf(pAr->out, "%s\n", sqlite3_sql(pSql)); |
| 29182 | + cli_printf(pAr->out, "%s\n", sqlite3_sql(pSql)); |
| 27879 | 29183 | }else{ |
| 27880 | 29184 | while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSql) ){ |
| 27881 | 29185 | if( i==0 && pAr->bVerbose ){ |
| 27882 | | - sqlite3_fprintf(pAr->out, "%s\n", sqlite3_column_text(pSql, 0)); |
| 29186 | + cli_printf(pAr->out, "%s\n", sqlite3_column_text(pSql, 0)); |
| 27883 | 29187 | } |
| 27884 | 29188 | } |
| 27885 | 29189 | } |
| 27886 | 29190 | shellReset(&rc, pSql); |
| 27887 | 29191 | } |
| | @@ -27897,17 +29201,17 @@ |
| 27897 | 29201 | ** Run the SQL statement in zSql. Or if doing a --dryrun, merely print it out. |
| 27898 | 29202 | */ |
| 27899 | 29203 | static int arExecSql(ArCommand *pAr, const char *zSql){ |
| 27900 | 29204 | int rc; |
| 27901 | 29205 | if( pAr->bDryRun ){ |
| 27902 | | - sqlite3_fprintf(pAr->out, "%s\n", zSql); |
| 29206 | + cli_printf(pAr->out, "%s\n", zSql); |
| 27903 | 29207 | rc = SQLITE_OK; |
| 27904 | 29208 | }else{ |
| 27905 | 29209 | char *zErr = 0; |
| 27906 | 29210 | rc = sqlite3_exec(pAr->db, zSql, 0, 0, &zErr); |
| 27907 | 29211 | if( zErr ){ |
| 27908 | | - sqlite3_fprintf(stdout, "ERROR: %s\n", zErr); |
| 29212 | + cli_printf(stdout, "ERROR: %s\n", zErr); |
| 27909 | 29213 | sqlite3_free(zErr); |
| 27910 | 29214 | } |
| 27911 | 29215 | } |
| 27912 | 29216 | return rc; |
| 27913 | 29217 | } |
| | @@ -28079,17 +29383,17 @@ |
| 28079 | 29383 | }else{ |
| 28080 | 29384 | flags = SQLITE_OPEN_READONLY; |
| 28081 | 29385 | } |
| 28082 | 29386 | cmd.db = 0; |
| 28083 | 29387 | if( cmd.bDryRun ){ |
| 28084 | | - sqlite3_fprintf(cmd.out, "-- open database '%s'%s\n", cmd.zFile, |
| 29388 | + cli_printf(cmd.out, "-- open database '%s'%s\n", cmd.zFile, |
| 28085 | 29389 | eDbType==SHELL_OPEN_APPENDVFS ? " using 'apndvfs'" : ""); |
| 28086 | 29390 | } |
| 28087 | 29391 | rc = sqlite3_open_v2(cmd.zFile, &cmd.db, flags, |
| 28088 | 29392 | eDbType==SHELL_OPEN_APPENDVFS ? "apndvfs" : 0); |
| 28089 | 29393 | if( rc!=SQLITE_OK ){ |
| 28090 | | - sqlite3_fprintf(stderr, "cannot open file: %s (%s)\n", |
| 29394 | + cli_printf(stderr, "cannot open file: %s (%s)\n", |
| 28091 | 29395 | cmd.zFile, sqlite3_errmsg(cmd.db)); |
| 28092 | 29396 | goto end_ar_command; |
| 28093 | 29397 | } |
| 28094 | 29398 | sqlite3_fileio_init(cmd.db, 0, 0); |
| 28095 | 29399 | sqlite3_sqlar_init(cmd.db, 0, 0); |
| | @@ -28099,11 +29403,11 @@ |
| 28099 | 29403 | } |
| 28100 | 29404 | if( cmd.zSrcTable==0 && cmd.bZip==0 && cmd.eCmd!=AR_CMD_HELP ){ |
| 28101 | 29405 | if( cmd.eCmd!=AR_CMD_CREATE |
| 28102 | 29406 | && sqlite3_table_column_metadata(cmd.db,0,"sqlar","name",0,0,0,0,0) |
| 28103 | 29407 | ){ |
| 28104 | | - sqlite3_fprintf(stderr, "database does not contain an 'sqlar' table\n"); |
| 29408 | + cli_printf(stderr, "database does not contain an 'sqlar' table\n"); |
| 28105 | 29409 | rc = SQLITE_ERROR; |
| 28106 | 29410 | goto end_ar_command; |
| 28107 | 29411 | } |
| 28108 | 29412 | cmd.zSrcTable = sqlite3_mprintf("sqlar"); |
| 28109 | 29413 | } |
| | @@ -28157,11 +29461,11 @@ |
| 28157 | 29461 | ** This function is used as a callback by the recover extension. Simply |
| 28158 | 29462 | ** print the supplied SQL statement to stdout. |
| 28159 | 29463 | */ |
| 28160 | 29464 | static int recoverSqlCb(void *pCtx, const char *zSql){ |
| 28161 | 29465 | ShellState *pState = (ShellState*)pCtx; |
| 28162 | | - sqlite3_fprintf(pState->out, "%s;\n", zSql); |
| 29466 | + cli_printf(pState->out, "%s;\n", zSql); |
| 28163 | 29467 | return SQLITE_OK; |
| 28164 | 29468 | } |
| 28165 | 29469 | |
| 28166 | 29470 | /* |
| 28167 | 29471 | ** This function is called to recover data from the database. A script |
| | @@ -28200,11 +29504,11 @@ |
| 28200 | 29504 | }else |
| 28201 | 29505 | if( n<=10 && memcmp("-no-rowids", z, n)==0 ){ |
| 28202 | 29506 | bRowids = 0; |
| 28203 | 29507 | } |
| 28204 | 29508 | else{ |
| 28205 | | - sqlite3_fprintf(stderr,"unexpected option: %s\n", azArg[i]); |
| 29509 | + cli_printf(stderr,"unexpected option: %s\n", azArg[i]); |
| 28206 | 29510 | showHelp(pState->out, azArg[0]); |
| 28207 | 29511 | return 1; |
| 28208 | 29512 | } |
| 28209 | 29513 | } |
| 28210 | 29514 | |
| | @@ -28215,16 +29519,16 @@ |
| 28215 | 29519 | sqlite3_recover_config(p, 789, (void*)zRecoveryDb); /* Debug use only */ |
| 28216 | 29520 | sqlite3_recover_config(p, SQLITE_RECOVER_LOST_AND_FOUND, (void*)zLAF); |
| 28217 | 29521 | sqlite3_recover_config(p, SQLITE_RECOVER_ROWIDS, (void*)&bRowids); |
| 28218 | 29522 | sqlite3_recover_config(p, SQLITE_RECOVER_FREELIST_CORRUPT,(void*)&bFreelist); |
| 28219 | 29523 | |
| 28220 | | - sqlite3_fprintf(pState->out, ".dbconfig defensive off\n"); |
| 29524 | + cli_printf(pState->out, ".dbconfig defensive off\n"); |
| 28221 | 29525 | sqlite3_recover_run(p); |
| 28222 | 29526 | if( sqlite3_recover_errcode(p)!=SQLITE_OK ){ |
| 28223 | 29527 | const char *zErr = sqlite3_recover_errmsg(p); |
| 28224 | 29528 | int errCode = sqlite3_recover_errcode(p); |
| 28225 | | - sqlite3_fprintf(stderr,"sql error: %s (%d)\n", zErr, errCode); |
| 29529 | + cli_printf(stderr,"sql error: %s (%d)\n", zErr, errCode); |
| 28226 | 29530 | } |
| 28227 | 29531 | rc = sqlite3_recover_finish(p); |
| 28228 | 29532 | return rc; |
| 28229 | 29533 | } |
| 28230 | 29534 | #endif /* SQLITE_SHELL_HAVE_RECOVER */ |
| | @@ -28242,25 +29546,25 @@ |
| 28242 | 29546 | i64 nError = 0; |
| 28243 | 29547 | const char *zErr = 0; |
| 28244 | 29548 | while( SQLITE_OK==sqlite3_intck_step(p) ){ |
| 28245 | 29549 | const char *zMsg = sqlite3_intck_message(p); |
| 28246 | 29550 | if( zMsg ){ |
| 28247 | | - sqlite3_fprintf(pState->out, "%s\n", zMsg); |
| 29551 | + cli_printf(pState->out, "%s\n", zMsg); |
| 28248 | 29552 | nError++; |
| 28249 | 29553 | } |
| 28250 | 29554 | nStep++; |
| 28251 | 29555 | if( nStepPerUnlock && (nStep % nStepPerUnlock)==0 ){ |
| 28252 | 29556 | sqlite3_intck_unlock(p); |
| 28253 | 29557 | } |
| 28254 | 29558 | } |
| 28255 | 29559 | rc = sqlite3_intck_error(p, &zErr); |
| 28256 | 29560 | if( zErr ){ |
| 28257 | | - sqlite3_fprintf(stderr,"%s\n", zErr); |
| 29561 | + cli_printf(stderr,"%s\n", zErr); |
| 28258 | 29562 | } |
| 28259 | 29563 | sqlite3_intck_close(p); |
| 28260 | 29564 | |
| 28261 | | - sqlite3_fprintf(pState->out, "%lld steps, %lld errors\n", nStep, nError); |
| 29565 | + cli_printf(pState->out, "%lld steps, %lld errors\n", nStep, nError); |
| 28262 | 29566 | } |
| 28263 | 29567 | |
| 28264 | 29568 | return rc; |
| 28265 | 29569 | } |
| 28266 | 29570 | |
| | @@ -28279,11 +29583,11 @@ |
| 28279 | 29583 | */ |
| 28280 | 29584 | #ifdef SHELL_DEBUG |
| 28281 | 29585 | #define rc_err_oom_die(rc) \ |
| 28282 | 29586 | if( rc==SQLITE_NOMEM ) shell_check_oom(0); \ |
| 28283 | 29587 | else if(!(rc==SQLITE_OK||rc==SQLITE_DONE)) \ |
| 28284 | | - sqlite3_fprintf(stderr,"E:%d\n",rc), assert(0) |
| 29588 | + cli_printf(stderr,"E:%d\n",rc), assert(0) |
| 28285 | 29589 | #else |
| 28286 | 29590 | static void rc_err_oom_die(int rc){ |
| 28287 | 29591 | if( rc==SQLITE_NOMEM ) shell_check_oom(0); |
| 28288 | 29592 | assert(rc==SQLITE_OK||rc==SQLITE_DONE); |
| 28289 | 29593 | } |
| | @@ -28496,11 +29800,11 @@ |
| 28496 | 29800 | shellPreparePrintf(p->db, &rc, &pStmt, |
| 28497 | 29801 | "SELECT 1 FROM sqlite_schema o WHERE " |
| 28498 | 29802 | "sql LIKE 'CREATE VIRTUAL TABLE%%' AND %s", zLike ? zLike : "true" |
| 28499 | 29803 | ); |
| 28500 | 29804 | if( rc==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){ |
| 28501 | | - sqlite3_fputs("/* WARNING: " |
| 29805 | + cli_puts("/* WARNING: " |
| 28502 | 29806 | "Script requires that SQLITE_DBCONFIG_DEFENSIVE be disabled */\n", |
| 28503 | 29807 | p->out |
| 28504 | 29808 | ); |
| 28505 | 29809 | } |
| 28506 | 29810 | shellFinalize(&rc, pStmt); |
| | @@ -28529,69 +29833,1323 @@ |
| 28529 | 29833 | return SQLITE_OK; |
| 28530 | 29834 | } |
| 28531 | 29835 | if( faultsim_state.iCnt ){ |
| 28532 | 29836 | if( faultsim_state.iCnt>0 ) faultsim_state.iCnt--; |
| 28533 | 29837 | if( faultsim_state.eVerbose>=2 ){ |
| 28534 | | - sqlite3_fprintf(stdout, |
| 29838 | + cli_printf(stdout, |
| 28535 | 29839 | "FAULT-SIM id=%d no-fault (cnt=%d)\n", iArg, faultsim_state.iCnt); |
| 28536 | 29840 | } |
| 28537 | 29841 | return SQLITE_OK; |
| 28538 | 29842 | } |
| 28539 | 29843 | if( faultsim_state.eVerbose>=1 ){ |
| 28540 | | - sqlite3_fprintf(stdout, |
| 29844 | + cli_printf(stdout, |
| 28541 | 29845 | "FAULT-SIM id=%d returns %d\n", iArg, faultsim_state.iErr); |
| 28542 | 29846 | } |
| 28543 | 29847 | faultsim_state.iCnt = faultsim_state.iInterval; |
| 28544 | 29848 | faultsim_state.nHit++; |
| 28545 | 29849 | if( faultsim_state.nRepeat>0 && faultsim_state.nRepeat<=faultsim_state.nHit ){ |
| 28546 | 29850 | faultsim_state.iCnt = -1; |
| 28547 | 29851 | } |
| 28548 | 29852 | return faultsim_state.iErr; |
| 28549 | 29853 | } |
| 29854 | + |
| 29855 | +/* |
| 29856 | +** pickStr(zArg, &zErr, zS1, zS2, ..., ""); |
| 29857 | +** |
| 29858 | +** Try to match zArg against zS1, zS2, and so forth until the first |
| 29859 | +** emptry string. Return the index of the match or -1 if none is found. |
| 29860 | +** If no match is found, and &zErr is not NULL, then write into |
| 29861 | +** zErr a message describing the valid choices. |
| 29862 | +*/ |
| 29863 | +static int pickStr(const char *zArg, char **pzErr, ...){ |
| 29864 | + int i, n; |
| 29865 | + const char *z; |
| 29866 | + sqlite3_str *pMsg; |
| 29867 | + va_list ap; |
| 29868 | + va_start(ap, pzErr); |
| 29869 | + i = 0; |
| 29870 | + while( (z = va_arg(ap,const char*))!=0 && z[0]!=0 ){ |
| 29871 | + if( cli_strcmp(zArg, z)==0 ) return i; |
| 29872 | + i++; |
| 29873 | + } |
| 29874 | + va_end(ap); |
| 29875 | + if( pzErr==0 ) return -1; |
| 29876 | + n = i; |
| 29877 | + pMsg = sqlite3_str_new(0); |
| 29878 | + va_start(ap, pzErr); |
| 29879 | + sqlite3_str_appendall(pMsg, "should be"); |
| 29880 | + i = 0; |
| 29881 | + while( (z = va_arg(ap, const char*))!=0 && z[0]!=0 ){ |
| 29882 | + if( i==n-1 ){ |
| 29883 | + sqlite3_str_append(pMsg,", or",4); |
| 29884 | + }else if( i>0 ){ |
| 29885 | + sqlite3_str_append(pMsg, ",", 1); |
| 29886 | + } |
| 29887 | + sqlite3_str_appendf(pMsg, " %s", z); |
| 29888 | + i++; |
| 29889 | + } |
| 29890 | + va_end(ap); |
| 29891 | + *pzErr = sqlite3_str_finish(pMsg); |
| 29892 | + return -1; |
| 29893 | +} |
| 29894 | + |
| 29895 | +/* |
| 29896 | +** DOT-COMMAND: .import |
| 29897 | +** |
| 29898 | +** USAGE: .import [OPTIONS] FILE TABLE |
| 29899 | +** |
| 29900 | +** Import CSV or similar text from FILE into TABLE. If TABLE does |
| 29901 | +** not exist, it is created using the first row of FILE as the column |
| 29902 | +** names. If FILE begins with "|" then it is a command that is run |
| 29903 | +** and the output from the command is used as the input data. |
| 29904 | +** |
| 29905 | +** FILE is assumed to be in a CSV format, unless the current mode |
| 29906 | +** is "ascii" or "tabs" or unless one of the options below specify |
| 29907 | +** an alternative. |
| 29908 | +** |
| 29909 | +** Options: |
| 29910 | +** --ascii Use \037 and \036 as column and row separators on input |
| 29911 | +** --csv Input is standard RFC-4180 CSV. |
| 29912 | +** --schema S When creating TABLE, put it in schema S |
| 29913 | +** --skip N Ignore the first N rows of input |
| 29914 | +** -v Verbose mode |
| 29915 | +*/ |
| 29916 | +static int dotCmdImport(ShellState *p){ |
| 29917 | + int nArg = p->dot.nArg; /* Number of arguments */ |
| 29918 | + char **azArg = p->dot.azArg;/* Argument list */ |
| 29919 | + char *zTable = 0; /* Insert data into this table */ |
| 29920 | + char *zSchema = 0; /* Schema of zTable */ |
| 29921 | + char *zFile = 0; /* Name of file to extra content from */ |
| 29922 | + sqlite3_stmt *pStmt = NULL; /* A statement */ |
| 29923 | + int nCol; /* Number of columns in the table */ |
| 29924 | + i64 nByte; /* Number of bytes in an SQL string */ |
| 29925 | + int i, j; /* Loop counters */ |
| 29926 | + int needCommit; /* True to COMMIT or ROLLBACK at end */ |
| 29927 | + int nSep; /* Number of bytes in spec.zColumnSep */ |
| 29928 | + char *zSql = 0; /* An SQL statement */ |
| 29929 | + ImportCtx sCtx; /* Reader context */ |
| 29930 | + char *(SQLITE_CDECL *xRead)(ImportCtx*); /* Func to read one value */ |
| 29931 | + int eVerbose = 0; /* Larger for more console output */ |
| 29932 | + i64 nSkip = 0; /* Initial lines to skip */ |
| 29933 | + int useOutputMode = 1; /* Use output mode to determine separators */ |
| 29934 | + char *zCreate = 0; /* CREATE TABLE statement text */ |
| 29935 | + int rc; /* Result code */ |
| 29936 | + |
| 29937 | + failIfSafeMode(p, "cannot run .import in safe mode"); |
| 29938 | + memset(&sCtx, 0, sizeof(sCtx)); |
| 29939 | + if( p->mode.eMode==MODE_Ascii ){ |
| 29940 | + xRead = ascii_read_one_field; |
| 29941 | + }else{ |
| 29942 | + xRead = csv_read_one_field; |
| 29943 | + } |
| 29944 | + for(i=1; i<nArg; i++){ |
| 29945 | + char *z = azArg[i]; |
| 29946 | + if( z[0]=='-' && z[1]=='-' ) z++; |
| 29947 | + if( z[0]!='-' ){ |
| 29948 | + if( zFile==0 ){ |
| 29949 | + zFile = z; |
| 29950 | + }else if( zTable==0 ){ |
| 29951 | + zTable = z; |
| 29952 | + }else{ |
| 29953 | + dotCmdError(p, i, "unknown argument", 0); |
| 29954 | + return 1; |
| 29955 | + } |
| 29956 | + }else if( cli_strcmp(z,"-v")==0 ){ |
| 29957 | + eVerbose++; |
| 29958 | + }else if( cli_strcmp(z,"-schema")==0 && i<nArg-1 ){ |
| 29959 | + zSchema = azArg[++i]; |
| 29960 | + }else if( cli_strcmp(z,"-skip")==0 && i<nArg-1 ){ |
| 29961 | + nSkip = integerValue(azArg[++i]); |
| 29962 | + }else if( cli_strcmp(z,"-ascii")==0 ){ |
| 29963 | + sCtx.cColSep = SEP_Unit[0]; |
| 29964 | + sCtx.cRowSep = SEP_Record[0]; |
| 29965 | + xRead = ascii_read_one_field; |
| 29966 | + useOutputMode = 0; |
| 29967 | + }else if( cli_strcmp(z,"-csv")==0 ){ |
| 29968 | + sCtx.cColSep = ','; |
| 29969 | + sCtx.cRowSep = '\n'; |
| 29970 | + xRead = csv_read_one_field; |
| 29971 | + useOutputMode = 0; |
| 29972 | + }else{ |
| 29973 | + dotCmdError(p, i, "unknown option", 0); |
| 29974 | + return 1; |
| 29975 | + } |
| 29976 | + } |
| 29977 | + if( zTable==0 ){ |
| 29978 | + cli_printf(p->out, "ERROR: missing %s argument\n", |
| 29979 | + zFile==0 ? "FILE" : "TABLE"); |
| 29980 | + return 1; |
| 29981 | + } |
| 29982 | + seenInterrupt = 0; |
| 29983 | + open_db(p, 0); |
| 29984 | + if( useOutputMode ){ |
| 29985 | + /* If neither the --csv or --ascii options are specified, then set |
| 29986 | + ** the column and row separator characters from the output mode. */ |
| 29987 | + if( p->mode.spec.zColumnSep==0 ){ |
| 29988 | + modeSetStr(&p->mode.spec.zColumnSep, ","); |
| 29989 | + nSep = 1; |
| 29990 | + }else if( (nSep = strlen30(p->mode.spec.zColumnSep))==0 ){ |
| 29991 | + eputz("Error: non-null column separator required for import\n"); |
| 29992 | + return 1; |
| 29993 | + } |
| 29994 | + if( nSep>1 ){ |
| 29995 | + eputz("Error: multi-character column separators not allowed" |
| 29996 | + " for import\n"); |
| 29997 | + return 1; |
| 29998 | + } |
| 29999 | + if( p->mode.spec.zRowSep==0 ){ |
| 30000 | + modeSetStr(&p->mode.spec.zRowSep, "\n"); |
| 30001 | + nSep = 1; |
| 30002 | + }else if( (nSep = strlen30(p->mode.spec.zRowSep))==0 ){ |
| 30003 | + eputz("Error: non-null row separator required for import\n"); |
| 30004 | + return 1; |
| 30005 | + } |
| 30006 | + if( nSep==2 && p->mode.eMode==MODE_Csv |
| 30007 | + && cli_strcmp(p->mode.spec.zRowSep,SEP_CrLf)==0 |
| 30008 | + ){ |
| 30009 | + /* When importing CSV (only), if the row separator is set to the |
| 30010 | + ** default output row separator, change it to the default input |
| 30011 | + ** row separator. This avoids having to maintain different input |
| 30012 | + ** and output row separators. */ |
| 30013 | + modeSetStr(&p->mode.spec.zRowSep, SEP_Row); |
| 30014 | + nSep = strlen30(p->mode.spec.zRowSep); |
| 30015 | + } |
| 30016 | + if( nSep>1 ){ |
| 30017 | + eputz("Error: multi-character row separators not allowed" |
| 30018 | + " for import\n"); |
| 30019 | + return 1; |
| 30020 | + } |
| 30021 | + sCtx.cColSep = (u8)p->mode.spec.zColumnSep[0]; |
| 30022 | + sCtx.cRowSep = (u8)p->mode.spec.zRowSep[0]; |
| 30023 | + } |
| 30024 | + sCtx.zFile = zFile; |
| 30025 | + sCtx.nLine = 1; |
| 30026 | + if( sCtx.zFile[0]=='|' ){ |
| 30027 | +#ifdef SQLITE_OMIT_POPEN |
| 30028 | + eputz("Error: pipes are not supported in this OS\n"); |
| 30029 | + return 1; |
| 30030 | +#else |
| 30031 | + sCtx.in = sqlite3_popen(sCtx.zFile+1, "r"); |
| 30032 | + sCtx.zFile = "<pipe>"; |
| 30033 | + sCtx.xCloser = pclose; |
| 30034 | +#endif |
| 30035 | + }else{ |
| 30036 | + sCtx.in = sqlite3_fopen(sCtx.zFile, "rb"); |
| 30037 | + sCtx.xCloser = fclose; |
| 30038 | + } |
| 30039 | + if( sCtx.in==0 ){ |
| 30040 | + cli_printf(stderr,"Error: cannot open \"%s\"\n", zFile); |
| 30041 | + return 1; |
| 30042 | + } |
| 30043 | + if( eVerbose>=2 || (eVerbose>=1 && useOutputMode) ){ |
| 30044 | + char zSep[2]; |
| 30045 | + zSep[1] = 0; |
| 30046 | + zSep[0] = sCtx.cColSep; |
| 30047 | + cli_puts("Column separator ", p->out); |
| 30048 | + output_c_string(p->out, zSep); |
| 30049 | + cli_puts(", row separator ", p->out); |
| 30050 | + zSep[0] = sCtx.cRowSep; |
| 30051 | + output_c_string(p->out, zSep); |
| 30052 | + cli_puts("\n", p->out); |
| 30053 | + } |
| 30054 | + sCtx.z = sqlite3_malloc64(120); |
| 30055 | + if( sCtx.z==0 ){ |
| 30056 | + import_cleanup(&sCtx); |
| 30057 | + shell_out_of_memory(); |
| 30058 | + } |
| 30059 | + /* Below, resources must be freed before exit. */ |
| 30060 | + while( nSkip>0 ){ |
| 30061 | + nSkip--; |
| 30062 | + while( xRead(&sCtx) && sCtx.cTerm==sCtx.cColSep ){} |
| 30063 | + } |
| 30064 | + import_append_char(&sCtx, 0); /* To ensure sCtx.z is allocated */ |
| 30065 | + if( sqlite3_table_column_metadata(p->db, zSchema, zTable,0,0,0,0,0,0) |
| 30066 | + && 0==db_int(p->db, "SELECT count(*) FROM \"%w\".sqlite_schema" |
| 30067 | + " WHERE name=%Q AND type='view'", |
| 30068 | + zSchema ? zSchema : "main", zTable) |
| 30069 | + ){ |
| 30070 | + /* Table does not exist. Create it. */ |
| 30071 | + sqlite3 *dbCols = 0; |
| 30072 | + char *zRenames = 0; |
| 30073 | + char *zColDefs; |
| 30074 | + zCreate = sqlite3_mprintf("CREATE TABLE \"%w\".\"%w\"", |
| 30075 | + zSchema ? zSchema : "main", zTable); |
| 30076 | + while( xRead(&sCtx) ){ |
| 30077 | + zAutoColumn(sCtx.z, &dbCols, 0); |
| 30078 | + if( sCtx.cTerm!=sCtx.cColSep ) break; |
| 30079 | + } |
| 30080 | + zColDefs = zAutoColumn(0, &dbCols, &zRenames); |
| 30081 | + if( zRenames!=0 ){ |
| 30082 | + cli_printf((stdin_is_interactive && p->in==stdin)? p->out : stderr, |
| 30083 | + "Columns renamed during .import %s due to duplicates:\n" |
| 30084 | + "%s\n", sCtx.zFile, zRenames); |
| 30085 | + sqlite3_free(zRenames); |
| 30086 | + } |
| 30087 | + assert(dbCols==0); |
| 30088 | + if( zColDefs==0 ){ |
| 30089 | + cli_printf(stderr,"%s: empty file\n", sCtx.zFile); |
| 30090 | + import_cleanup(&sCtx); |
| 30091 | + sqlite3_free(zCreate); |
| 30092 | + return 1; |
| 30093 | + } |
| 30094 | + zCreate = sqlite3_mprintf("%z%z\n", zCreate, zColDefs); |
| 30095 | + if( zCreate==0 ){ |
| 30096 | + import_cleanup(&sCtx); |
| 30097 | + shell_out_of_memory(); |
| 30098 | + } |
| 30099 | + if( eVerbose>=1 ){ |
| 30100 | + cli_printf(p->out, "%s\n", zCreate); |
| 30101 | + } |
| 30102 | + rc = sqlite3_exec(p->db, zCreate, 0, 0, 0); |
| 30103 | + if( rc ){ |
| 30104 | + cli_printf(stderr, |
| 30105 | + "%s failed:\n%s\n", zCreate, sqlite3_errmsg(p->db)); |
| 30106 | + } |
| 30107 | + sqlite3_free(zCreate); |
| 30108 | + zCreate = 0; |
| 30109 | + if( rc ){ |
| 30110 | + import_cleanup(&sCtx); |
| 30111 | + return 1; |
| 30112 | + } |
| 30113 | + } |
| 30114 | + zSql = sqlite3_mprintf("SELECT count(*) FROM pragma_table_info(%Q,%Q);", |
| 30115 | + zTable, zSchema); |
| 30116 | + if( zSql==0 ){ |
| 30117 | + import_cleanup(&sCtx); |
| 30118 | + shell_out_of_memory(); |
| 30119 | + } |
| 30120 | + rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); |
| 30121 | + sqlite3_free(zSql); |
| 30122 | + zSql = 0; |
| 30123 | + if( rc ){ |
| 30124 | + if (pStmt) sqlite3_finalize(pStmt); |
| 30125 | + shellDatabaseError(p->db); |
| 30126 | + import_cleanup(&sCtx); |
| 30127 | + return 1; |
| 30128 | + } |
| 30129 | + if( sqlite3_step(pStmt)==SQLITE_ROW ){ |
| 30130 | + nCol = sqlite3_column_int(pStmt, 0); |
| 30131 | + }else{ |
| 30132 | + nCol = 0; |
| 30133 | + } |
| 30134 | + sqlite3_finalize(pStmt); |
| 30135 | + pStmt = 0; |
| 30136 | + if( nCol==0 ) return 0; /* no columns, no error */ |
| 30137 | + |
| 30138 | + nByte = 64 /* space for "INSERT INTO", "VALUES(", ")\0" */ |
| 30139 | + + (zSchema ? strlen(zSchema)*2 + 2: 0) /* Quoted schema name */ |
| 30140 | + + strlen(zTable)*2 + 2 /* Quoted table name */ |
| 30141 | + + nCol*2; /* Space for ",?" for each column */ |
| 30142 | + zSql = sqlite3_malloc64( nByte ); |
| 30143 | + if( zSql==0 ){ |
| 30144 | + import_cleanup(&sCtx); |
| 30145 | + shell_out_of_memory(); |
| 30146 | + } |
| 30147 | + if( zSchema ){ |
| 30148 | + sqlite3_snprintf(nByte, zSql, "INSERT INTO \"%w\".\"%w\" VALUES(?", |
| 30149 | + zSchema, zTable); |
| 30150 | + }else{ |
| 30151 | + sqlite3_snprintf(nByte, zSql, "INSERT INTO \"%w\" VALUES(?", zTable); |
| 30152 | + } |
| 30153 | + j = strlen30(zSql); |
| 30154 | + for(i=1; i<nCol; i++){ |
| 30155 | + zSql[j++] = ','; |
| 30156 | + zSql[j++] = '?'; |
| 30157 | + } |
| 30158 | + zSql[j++] = ')'; |
| 30159 | + zSql[j] = 0; |
| 30160 | + assert( j<nByte ); |
| 30161 | + if( eVerbose>=2 ){ |
| 30162 | + cli_printf(p->out, "Insert using: %s\n", zSql); |
| 30163 | + } |
| 30164 | + rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); |
| 30165 | + sqlite3_free(zSql); |
| 30166 | + zSql = 0; |
| 30167 | + if( rc ){ |
| 30168 | + shellDatabaseError(p->db); |
| 30169 | + if (pStmt) sqlite3_finalize(pStmt); |
| 30170 | + import_cleanup(&sCtx); |
| 30171 | + return 1; |
| 30172 | + } |
| 30173 | + needCommit = sqlite3_get_autocommit(p->db); |
| 30174 | + if( needCommit ) sqlite3_exec(p->db, "BEGIN", 0, 0, 0); |
| 30175 | + do{ |
| 30176 | + int startLine = sCtx.nLine; |
| 30177 | + for(i=0; i<nCol; i++){ |
| 30178 | + char *z = xRead(&sCtx); |
| 30179 | + /* |
| 30180 | + ** Did we reach end-of-file before finding any columns? |
| 30181 | + ** If so, stop instead of NULL filling the remaining columns. |
| 30182 | + */ |
| 30183 | + if( z==0 && i==0 ) break; |
| 30184 | + /* |
| 30185 | + ** Did we reach end-of-file OR end-of-line before finding any |
| 30186 | + ** columns in ASCII mode? If so, stop instead of NULL filling |
| 30187 | + ** the remaining columns. |
| 30188 | + */ |
| 30189 | + if( p->mode.eMode==MODE_Ascii && (z==0 || z[0]==0) && i==0 ) break; |
| 30190 | + /* |
| 30191 | + ** For CSV mode, per RFC 4180, accept EOF in lieu of final |
| 30192 | + ** record terminator but only for last field of multi-field row. |
| 30193 | + ** (If there are too few fields, it's not valid CSV anyway.) |
| 30194 | + */ |
| 30195 | + if( z==0 && (xRead==csv_read_one_field) && i==nCol-1 && i>0 ){ |
| 30196 | + z = ""; |
| 30197 | + } |
| 30198 | + sqlite3_bind_text(pStmt, i+1, z, -1, SQLITE_TRANSIENT); |
| 30199 | + if( i<nCol-1 && sCtx.cTerm!=sCtx.cColSep ){ |
| 30200 | + cli_printf(stderr,"%s:%d: expected %d columns but found %d" |
| 30201 | + " - filling the rest with NULL\n", |
| 30202 | + sCtx.zFile, startLine, nCol, i+1); |
| 30203 | + i += 2; |
| 30204 | + while( i<=nCol ){ sqlite3_bind_null(pStmt, i); i++; } |
| 30205 | + } |
| 30206 | + } |
| 30207 | + if( sCtx.cTerm==sCtx.cColSep ){ |
| 30208 | + do{ |
| 30209 | + xRead(&sCtx); |
| 30210 | + i++; |
| 30211 | + }while( sCtx.cTerm==sCtx.cColSep ); |
| 30212 | + cli_printf(stderr, |
| 30213 | + "%s:%d: expected %d columns but found %d - extras ignored\n", |
| 30214 | + sCtx.zFile, startLine, nCol, i); |
| 30215 | + } |
| 30216 | + if( i>=nCol ){ |
| 30217 | + sqlite3_step(pStmt); |
| 30218 | + rc = sqlite3_reset(pStmt); |
| 30219 | + if( rc!=SQLITE_OK ){ |
| 30220 | + cli_printf(stderr,"%s:%d: INSERT failed: %s\n", |
| 30221 | + sCtx.zFile, startLine, sqlite3_errmsg(p->db)); |
| 30222 | + sCtx.nErr++; |
| 30223 | + }else{ |
| 30224 | + sCtx.nRow++; |
| 30225 | + } |
| 30226 | + } |
| 30227 | + }while( sCtx.cTerm!=EOF ); |
| 30228 | + |
| 30229 | + import_cleanup(&sCtx); |
| 30230 | + sqlite3_finalize(pStmt); |
| 30231 | + if( needCommit ) sqlite3_exec(p->db, "COMMIT", 0, 0, 0); |
| 30232 | + if( eVerbose>0 ){ |
| 30233 | + cli_printf(p->out, |
| 30234 | + "Added %d rows with %d errors using %d lines of input\n", |
| 30235 | + sCtx.nRow, sCtx.nErr, sCtx.nLine-1); |
| 30236 | + } |
| 30237 | + return 0; |
| 30238 | +} |
| 30239 | + |
| 30240 | + |
| 30241 | +/* |
| 30242 | +** This function computes what to show the user about the configured |
| 30243 | +** titles (or column-names). Output is an integer between 0 and 3: |
| 30244 | +** |
| 30245 | +** 0: The titles do not matter. Never show anything. |
| 30246 | +** 1: Show "--titles off" |
| 30247 | +** 2: Show "--titles on" |
| 30248 | +** 3: Show "--title VALUE" where VALUE is an encoding method |
| 30249 | +** to use, one of: plain sql csv html tcl json |
| 30250 | +** |
| 30251 | +** Inputs are: |
| 30252 | +** |
| 30253 | +** spec.bTitles (bT) Whether or not to show the titles |
| 30254 | +** spec.eTitle (eT) The actual encoding to be used for titles |
| 30255 | +** ModeInfo.bHdr (bH) Default value for spec.bTitles |
| 30256 | +** ModeInfo.eHdr (eH) Default value for spec.eTitle |
| 30257 | +** bAll Whether the -v option is used |
| 30258 | +*/ |
| 30259 | +static int modeTitleDsply(ShellState *p, int bAll){ |
| 30260 | + int eMode = p->mode.eMode; |
| 30261 | + const ModeInfo *pI = &aModeInfo[eMode]; |
| 30262 | + int bT = p->mode.spec.bTitles; |
| 30263 | + int eT = p->mode.spec.eTitle; |
| 30264 | + int bH = pI->bHdr; |
| 30265 | + int eH = pI->eHdr; |
| 30266 | + |
| 30267 | + /* Variable "v" is the truth table that will determine the answer |
| 30268 | + ** |
| 30269 | + ** Actual encoding is different from default |
| 30270 | + ** vvvvvvvv */ |
| 30271 | + sqlite3_uint64 v = 0x0133013311220102; |
| 30272 | + /* ^^^^ ^^^^ |
| 30273 | + ** Upper 2-byte groups for when ON/OFF disagrees with |
| 30274 | + ** the default. */ |
| 30275 | + |
| 30276 | + if( bH==0 ) return 0; /* Header not appliable. Ex: off, count */ |
| 30277 | + |
| 30278 | + if( eT==0 ) eT = eH; /* Fill in missing spec.eTitle */ |
| 30279 | + if( bT==0 ) bT = bH; /* Fill in missing spec.bTitles */ |
| 30280 | + |
| 30281 | + if( eT!=eH ) v >>= 32; /* Encoding disagree in upper 4-bytes */ |
| 30282 | + if( bT!=bH ) v >>= 16; /* ON/OFF disagree in upper 2-byte pairs */ |
| 30283 | + if( bT<2 ) v >>= 8; /* ON in even bytes, OFF in odd bytes (1st byte 0) */ |
| 30284 | + if( !bAll ) v >>= 4; /* bAll values are in the lower half-byte */ |
| 30285 | + |
| 30286 | + return v & 3; /* Return the selected truth-table entry */ |
| 30287 | +} |
| 30288 | + |
| 30289 | +/* |
| 30290 | +** DOT-COMMAND: .mode |
| 30291 | +** |
| 30292 | +** USAGE: .mode [MODE] [OPTIONS] |
| 30293 | +** |
| 30294 | +** Change the output mode to MODE and/or apply OPTIONS to the |
| 30295 | +** output mode. If no arguments, show the current output mode |
| 30296 | +** and relevant options. |
| 30297 | +** |
| 30298 | +** Options: |
| 30299 | +** --align STRING Set the alignment of text in columnar modes |
| 30300 | +** String consists of characters 'L', 'C', 'R' |
| 30301 | +** meaning "left", "centered", and "right", with |
| 30302 | +** one letter per column starting from the left. |
| 30303 | +** Unspecified alignment defaults to 'L'. |
| 30304 | +** --charlimit N Set the maximum number of output characters to |
| 30305 | +** show for any single SQL value to N. Longer values |
| 30306 | +** truncated. Zero means "no limit". |
| 30307 | +** --colsep STRING Use STRING as the column separator |
| 30308 | +** --escape ESC Enable/disable escaping of control characters |
| 30309 | +** in output. ESC can be "off", "ascii", or |
| 30310 | +** "symbol". |
| 30311 | +** --linelimit N Set the maximum number of output lines to show for |
| 30312 | +** any single SQL value to N. Longer values are |
| 30313 | +** truncated. Zero means "no limit". Only works |
| 30314 | +** in "line" mode and in columnar modes. |
| 30315 | +** --list List available modes |
| 30316 | +** --null STRING Render SQL NULL values as the given string |
| 30317 | +** --once Setting changes to the right are reverted after |
| 30318 | +** the next SQL command. |
| 30319 | +** --quote ARG Enable/disable quoting of text. ARG can be |
| 30320 | +** "off", "on", "sql", "csv", "html", "tcl", |
| 30321 | +** or "json". "off" means show the text as-is. |
| 30322 | +** "on and "sql" are synonyms. |
| 30323 | +** --reset Changes all mode settings back to their default. |
| 30324 | +** --rowsep STRING Use STRING as the row separator |
| 30325 | +** --screenwidth N Declare the screen width of the output device |
| 30326 | +** to be N characters. An attempt may be made to |
| 30327 | +** wrap output text to fit within this limit. Zero |
| 30328 | +** means "no limit". Or N can be "auto" to set the |
| 30329 | +** width automatically. |
| 30330 | +** --tablename NAME Set the name of the table for "insert" mode. |
| 30331 | +** --tag NAME Save mode to the left as NAME. |
| 30332 | +** --textjsonb BOOLEAN If enabled, JSONB text is displayed as text JSON. |
| 30333 | +** --title ARG Whether or not to show column headers, and if so |
| 30334 | +** how to encode them. ARG can be "off", "on", |
| 30335 | +** "sql", "csv", "html", "tcl", or "json". |
| 30336 | +** -v|--verbose Verbose output |
| 30337 | +** --widths LIST Set the columns widths for columnar modes. The |
| 30338 | +** argument is a list of integers, one for each |
| 30339 | +** column. A "0" width means use a dynamic width |
| 30340 | +** based on the actual width of data. If there are |
| 30341 | +** fewer entries in LIST than columns, "0" is used |
| 30342 | +** for the unspecified widths. |
| 30343 | +** --wordwrap BOOLEAN Enable/disable word wrapping |
| 30344 | +** --wrap N Wrap columns wider than N characters |
| 30345 | +** --ww Shorthand for "--wordwrap on" |
| 30346 | +*/ |
| 30347 | +static int dotCmdMode(ShellState *p){ |
| 30348 | + int nArg = p->dot.nArg; /* Number of arguments */ |
| 30349 | + char **azArg = p->dot.azArg;/* Argument list */ |
| 30350 | + int eMode = -1; /* New mode value, or -1 for none */ |
| 30351 | + int iMode = -1; /* Index of the argument that is the mode name */ |
| 30352 | + int i; /* Loop counter */ |
| 30353 | + int k; /* Misc index variable */ |
| 30354 | + int chng = 0; /* True if anything has changed */ |
| 30355 | + int bAll = 0; /* Show all details of the mode */ |
| 30356 | + |
| 30357 | + for(i=1; i<nArg; i++){ |
| 30358 | + const char *z = azArg[i]; |
| 30359 | + if( z[0]=='-' && z[1]=='-' ) z++; |
| 30360 | + if( z[0]!='-' |
| 30361 | + && iMode<0 |
| 30362 | + && (eMode = modeFind(p, azArg[i]))>=0 |
| 30363 | + && eMode!=MODE_Www |
| 30364 | + ){ |
| 30365 | + iMode = i; |
| 30366 | + modeChange(p, eMode); |
| 30367 | + /* (Legacy) If the mode is MODE_Insert and the next argument |
| 30368 | + ** is not an option, then the next argument must be the table |
| 30369 | + ** name. |
| 30370 | + */ |
| 30371 | + if( i+1<nArg && azArg[i+1][0]!='-' ){ |
| 30372 | + i++; |
| 30373 | + modeSetStr(&p->mode.spec.zTableName, azArg[i]); |
| 30374 | + } |
| 30375 | + chng = 1; |
| 30376 | + }else if( optionMatch(z,"align") ){ |
| 30377 | + char *zAlign; |
| 30378 | + int nAlign; |
| 30379 | + int nErr = 0; |
| 30380 | + if( i+1>=nArg ){ |
| 30381 | + dotCmdError(p, i, "missing argument", 0); |
| 30382 | + return 1; |
| 30383 | + } |
| 30384 | + i++; |
| 30385 | + zAlign = azArg[i]; |
| 30386 | + nAlign = 0x3fff & strlen(zAlign); |
| 30387 | + free(p->mode.spec.aAlign); |
| 30388 | + p->mode.spec.aAlign = malloc(nAlign); |
| 30389 | + shell_check_oom(p->mode.spec.aAlign); |
| 30390 | + for(k=0; k<nAlign; k++){ |
| 30391 | + unsigned char c = 0; |
| 30392 | + switch( zAlign[k] ){ |
| 30393 | + case 'l': case 'L': c = QRF_ALIGN_Left; break; |
| 30394 | + case 'c': case 'C': c = QRF_ALIGN_Center; break; |
| 30395 | + case 'r': case 'R': c = QRF_ALIGN_Right; break; |
| 30396 | + default: nErr++; break; |
| 30397 | + } |
| 30398 | + p->mode.spec.aAlign[k] = c; |
| 30399 | + } |
| 30400 | + p->mode.spec.nAlign = nAlign; |
| 30401 | + chng = 1; |
| 30402 | + if( nErr ){ |
| 30403 | + dotCmdError(p, i, "bad alignment string", |
| 30404 | + "Should contain only characters L, C, and R."); |
| 30405 | + return 1; |
| 30406 | + } |
| 30407 | + }else if( 0<=(k=pickStr(z,0,"-charlimit","-linelimit","")) ){ |
| 30408 | + int w; /* 0 1 */ |
| 30409 | + if( i+1>=nArg ){ |
| 30410 | + dotCmdError(p, i, "missing argument", 0); |
| 30411 | + return 1; |
| 30412 | + } |
| 30413 | + w = integerValue(azArg[++i]); |
| 30414 | + if( k==0 ){ |
| 30415 | + p->mode.spec.nCharLimit = w; |
| 30416 | + }else{ |
| 30417 | + p->mode.spec.nLineLimit = w; |
| 30418 | + } |
| 30419 | + chng = 1; |
| 30420 | + }else if( 0<=(k=pickStr(z,0,"-tablename","-rowsep","-colsep","-null","")) ){ |
| 30421 | + /* 0 1 2 3 */ |
| 30422 | + if( i+1>=nArg ){ |
| 30423 | + dotCmdError(p, i, "missing argument", 0); |
| 30424 | + return 1; |
| 30425 | + } |
| 30426 | + i++; |
| 30427 | + switch( k ){ |
| 30428 | + case 0: modeSetStr(&p->mode.spec.zTableName, azArg[i]); break; |
| 30429 | + case 1: modeSetStr(&p->mode.spec.zRowSep, azArg[i]); break; |
| 30430 | + case 2: modeSetStr(&p->mode.spec.zColumnSep, azArg[i]); break; |
| 30431 | + case 3: modeSetStr(&p->mode.spec.zNull, azArg[i]); break; |
| 30432 | + } |
| 30433 | + chng = 1; |
| 30434 | + }else if( optionMatch(z,"escape") ){ |
| 30435 | + /* See similar code at tag-20250224-1 */ |
| 30436 | + char *zErr = 0; |
| 30437 | + if( i+1>=nArg ){ |
| 30438 | + dotCmdError(p, i, "missing argument", 0); |
| 30439 | + return 1; |
| 30440 | + } |
| 30441 | + i++; /* 0 1 2 <-- One less than QRF_ESC_ */ |
| 30442 | + k = pickStr(azArg[i],&zErr,"off","ascii","symbol",""); |
| 30443 | + if( k<0 ){ |
| 30444 | + dotCmdError(p, i, "unknown escape type", "%s", zErr); |
| 30445 | + sqlite3_free(zErr); |
| 30446 | + return 1; |
| 30447 | + } |
| 30448 | + p->mode.spec.eEsc = k+1; |
| 30449 | + chng = 1; |
| 30450 | + }else if( optionMatch(z,"list") ){ |
| 30451 | + int ii; |
| 30452 | + cli_puts("available modes:", p->out); |
| 30453 | + for(ii=0; ii<ArraySize(aModeInfo); ii++){ |
| 30454 | + if( ii==MODE_Www ) continue; |
| 30455 | + cli_printf(p->out, " %s", aModeInfo[ii].zName); |
| 30456 | + } |
| 30457 | + for(ii=0; ii<p->nSavedModes; ii++){ |
| 30458 | + cli_printf(p->out, " %s", p->aSavedModes[ii].zTag); |
| 30459 | + } |
| 30460 | + cli_puts(" batch tty\n", p->out); |
| 30461 | + }else if( optionMatch(z,"quote") ){ |
| 30462 | + if( i+1<nArg |
| 30463 | + && azArg[i+1][0]!='-' |
| 30464 | + && (iMode>0 || strcmp(azArg[i+1],"off")==0 || modeFind(p, azArg[i+1])<0) |
| 30465 | + ){ |
| 30466 | + /* --quote is followed by an argument other that is not an option |
| 30467 | + ** or a mode name. See it must be a boolean or a keyword to describe |
| 30468 | + ** how to set quoting. */ |
| 30469 | + i++; |
| 30470 | + if( (k = pickStr(azArg[i],0,"no","yes","0","1",""))>=0 ){ |
| 30471 | + k &= 1; /* 0 for "off". 1 for "on". */ |
| 30472 | + }else{ |
| 30473 | + char *zErr = 0; /* 0 1 2 3 4 5 6 */ |
| 30474 | + k = pickStr(azArg[i],&zErr,"off","on","sql","csv","html","tcl","json", |
| 30475 | + ""); |
| 30476 | + if( k<0 ){ |
| 30477 | + dotCmdError(p, i, "unknown", "%z", zErr); |
| 30478 | + return 1; |
| 30479 | + } |
| 30480 | + } |
| 30481 | + }else{ |
| 30482 | + /* (Legacy) no following boolean argument. Turn quoting on */ |
| 30483 | + k = 1; |
| 30484 | + } |
| 30485 | + switch( k ){ |
| 30486 | + case 1: /* on */ |
| 30487 | + case 2: /* sql */ |
| 30488 | + p->mode.spec.eText = QRF_TEXT_Sql; |
| 30489 | + p->mode.spec.eBlob = QRF_BLOB_Sql; |
| 30490 | + break; |
| 30491 | + case 3: /* csv */ |
| 30492 | + p->mode.spec.eText = QRF_TEXT_Csv; |
| 30493 | + p->mode.spec.eBlob = QRF_BLOB_Text; |
| 30494 | + break; |
| 30495 | + case 4: /* html */ |
| 30496 | + p->mode.spec.eText = QRF_TEXT_Html; |
| 30497 | + p->mode.spec.eBlob = QRF_BLOB_Text; |
| 30498 | + break; |
| 30499 | + case 5: /* tcl */ |
| 30500 | + p->mode.spec.eText = QRF_TEXT_Tcl; |
| 30501 | + p->mode.spec.eBlob = QRF_BLOB_Text; |
| 30502 | + break; |
| 30503 | + case 6: /* json */ |
| 30504 | + p->mode.spec.eText = QRF_TEXT_Json; |
| 30505 | + p->mode.spec.eBlob = QRF_BLOB_Json; |
| 30506 | + break; |
| 30507 | + default: /* off */ |
| 30508 | + p->mode.spec.eText = QRF_TEXT_Plain; |
| 30509 | + p->mode.spec.eBlob = QRF_BLOB_Text; |
| 30510 | + break; |
| 30511 | + } |
| 30512 | + chng = 1; |
| 30513 | + }else if( optionMatch(z,"once") ){ |
| 30514 | + p->nPopMode = 0; |
| 30515 | + modePush(p); |
| 30516 | + p->nPopMode = 1; |
| 30517 | + }else if( optionMatch(z,"noquote") ){ |
| 30518 | + /* (undocumented legacy) --noquote always turns quoting off */ |
| 30519 | + p->mode.spec.eText = QRF_TEXT_Plain; |
| 30520 | + p->mode.spec.eBlob = QRF_BLOB_Text; |
| 30521 | + chng = 1; |
| 30522 | + }else if( optionMatch(z,"reset") ){ |
| 30523 | + int saved_eMode = p->mode.eMode; |
| 30524 | + modeFree(&p->mode); |
| 30525 | + modeChange(p, saved_eMode); |
| 30526 | + }else if( optionMatch(z,"screenwidth") ){ |
| 30527 | + if( i+1>=nArg ){ |
| 30528 | + dotCmdError(p, i, "missing argument", 0); |
| 30529 | + return 1; |
| 30530 | + } |
| 30531 | + k = pickStr(azArg[i+1],0,"off","auto",""); |
| 30532 | + if( k==0 ){ |
| 30533 | + p->mode.bAutoScreenWidth = 0; |
| 30534 | + p->mode.spec.nScreenWidth = 0; |
| 30535 | + }else if( k==1 ){ |
| 30536 | + p->mode.bAutoScreenWidth = 1; |
| 30537 | + }else{ |
| 30538 | + i64 w = integerValue(azArg[i+1]); |
| 30539 | + p->mode.bAutoScreenWidth = 0; |
| 30540 | + if( w<0 ) w = 0; |
| 30541 | + if( w>QRF_MAX_WIDTH ) w = QRF_MAX_WIDTH; |
| 30542 | + p->mode.spec.nScreenWidth = w; |
| 30543 | + } |
| 30544 | + i++; |
| 30545 | + chng = 1; |
| 30546 | + }else if( optionMatch(z,"tag") ){ |
| 30547 | + size_t nByte; |
| 30548 | + int n; |
| 30549 | + const char *zTag; |
| 30550 | + if( i+1>=nArg ){ |
| 30551 | + dotCmdError(p, i, "missing argument", 0); |
| 30552 | + return 1; |
| 30553 | + } |
| 30554 | + zTag = azArg[++i]; |
| 30555 | + if( modeFind(p, zTag)>=0 ){ |
| 30556 | + dotCmdError(p, i, "mode already exists", 0); |
| 30557 | + return 1; |
| 30558 | + } |
| 30559 | + if( p->nSavedModes > MODE_N_USER ){ |
| 30560 | + dotCmdError(p, i-1, "cannot add more modes", 0); |
| 30561 | + return 1; |
| 30562 | + } |
| 30563 | + n = p->nSavedModes++; |
| 30564 | + nByte = sizeof(p->aSavedModes[0]); |
| 30565 | + nByte *= n+1; |
| 30566 | + p->aSavedModes = realloc( p->aSavedModes, nByte ); |
| 30567 | + shell_check_oom(p->aSavedModes); |
| 30568 | + p->aSavedModes[n].zTag = strdup(zTag); |
| 30569 | + shell_check_oom(p->aSavedModes[n].zTag); |
| 30570 | + modeDup(&p->aSavedModes[n].mode, &p->mode); |
| 30571 | + chng = 1; |
| 30572 | + }else if( optionMatch(z,"textjsonb") ){ |
| 30573 | + if( i+1>=nArg ){ |
| 30574 | + dotCmdError(p, i, "missing argument", 0); |
| 30575 | + return 1; |
| 30576 | + } |
| 30577 | + p->mode.spec.bTextJsonb = booleanValue(azArg[++i]) ? QRF_Yes : QRF_No; |
| 30578 | + chng = 1; |
| 30579 | + }else if( optionMatch(z,"titles") || optionMatch(z,"title") ){ |
| 30580 | + char *zErr = 0; |
| 30581 | + if( i+1>=nArg ){ |
| 30582 | + dotCmdError(p, i, "missing argument", 0); |
| 30583 | + return 1; |
| 30584 | + } |
| 30585 | + k = pickStr(azArg[++i],&zErr, |
| 30586 | + "off","on","plain","sql","csv","html","tcl","json",""); |
| 30587 | + /* 0 1 2 3 4 5 6 7 */ |
| 30588 | + if( k<0 ){ |
| 30589 | + dotCmdError(p, i, "bad --titles value","%z", zErr); |
| 30590 | + return 1; |
| 30591 | + } |
| 30592 | + p->mode.spec.bTitles = k>=1 ? QRF_Yes : QRF_No; |
| 30593 | + p->mode.spec.eTitle = k>1 ? k-1 : aModeInfo[p->mode.eMode].eHdr; |
| 30594 | + chng = 1; |
| 30595 | + }else if( optionMatch(z,"widths") || optionMatch(z,"width") ){ |
| 30596 | + int nWidth = 0; |
| 30597 | + short int *aWidth; |
| 30598 | + const char *zW; |
| 30599 | + if( i+1>=nArg ){ |
| 30600 | + dotCmdError(p, i, "missing argument", 0); |
| 30601 | + return 1; |
| 30602 | + } |
| 30603 | + zW = azArg[++i]; |
| 30604 | + /* Every width value takes at least 2 bytes in the input string to |
| 30605 | + ** specify, so strlen(zW) bytes should be plenty of space to hold the |
| 30606 | + ** result. */ |
| 30607 | + aWidth = malloc( strlen(zW) ); |
| 30608 | + while( isspace(zW[0]) ) zW++; |
| 30609 | + while( zW[0] ){ |
| 30610 | + int w = 0; |
| 30611 | + int nDigit = 0; |
| 30612 | + k = zW[0]=='-' && isdigit(zW[1]); |
| 30613 | + while( isdigit(zW[k]) ){ |
| 30614 | + w = w*10 + zW[k] - '0'; |
| 30615 | + if( w>QRF_MAX_WIDTH ){ |
| 30616 | + dotCmdError(p,i+1,"width too big", |
| 30617 | + "Maximum column width is %d", QRF_MAX_WIDTH); |
| 30618 | + free(aWidth); |
| 30619 | + return 1; |
| 30620 | + } |
| 30621 | + nDigit++; |
| 30622 | + k++; |
| 30623 | + } |
| 30624 | + if( nDigit==0 ){ |
| 30625 | + dotCmdError(p,i+1,"syntax error", |
| 30626 | + "should be a comma-separated list if integers"); |
| 30627 | + free(aWidth); |
| 30628 | + return 1; |
| 30629 | + } |
| 30630 | + if( zW[0]=='-' ) w = -w; |
| 30631 | + aWidth[nWidth++] = w; |
| 30632 | + zW += k; |
| 30633 | + if( zW[0]==',' ) zW++; |
| 30634 | + while( isspace(zW[0]) ) zW++; |
| 30635 | + } |
| 30636 | + free(p->mode.spec.aWidth); |
| 30637 | + p->mode.spec.aWidth = aWidth; |
| 30638 | + p->mode.spec.nWidth = nWidth; |
| 30639 | + chng = 1; |
| 30640 | + }else if( optionMatch(z,"wrap") ){ |
| 30641 | + int w; |
| 30642 | + if( i+1>=nArg ){ |
| 30643 | + dotCmdError(p, i, "missing argument", 0); |
| 30644 | + return 1; |
| 30645 | + } |
| 30646 | + w = integerValue(azArg[++i]); |
| 30647 | + if( w<(-QRF_MAX_WIDTH) ) w = -QRF_MAX_WIDTH; |
| 30648 | + if( w>QRF_MAX_WIDTH ) w = QRF_MAX_WIDTH; |
| 30649 | + p->mode.spec.nWrap = w; |
| 30650 | + chng = 1; |
| 30651 | + }else if( optionMatch(z,"ww") ){ |
| 30652 | + p->mode.spec.bWordWrap = QRF_Yes; |
| 30653 | + chng = 1; |
| 30654 | + }else if( optionMatch(z,"wordwrap") ){ |
| 30655 | + if( i+1>=nArg ){ |
| 30656 | + dotCmdError(p, i, "missing argument", 0); |
| 30657 | + return 1; |
| 30658 | + } |
| 30659 | + p->mode.spec.bWordWrap = (u8)booleanValue(azArg[++i]) ? QRF_Yes : QRF_No; |
| 30660 | + chng = 1; |
| 30661 | + }else if( optionMatch(z,"v") || optionMatch(z,"verbose") ){ |
| 30662 | + bAll = 1; |
| 30663 | + }else if( z[0]=='-' ){ |
| 30664 | + dotCmdError(p, i, "bad option", "Use \".help .mode\" for more info"); |
| 30665 | + return 1; |
| 30666 | + }else{ |
| 30667 | + dotCmdError(p, i, iMode>0?"bad argument":"unknown mode", |
| 30668 | + "Use \".help .mode\" for more info"); |
| 30669 | + return 1; |
| 30670 | + } |
| 30671 | + } |
| 30672 | + if( !chng || bAll ){ |
| 30673 | + const ModeInfo *pI = aModeInfo + p->mode.eMode; |
| 30674 | + sqlite3_str *pDesc = sqlite3_str_new(p->db); |
| 30675 | + char *zDesc; |
| 30676 | + const char *zSetting; |
| 30677 | + |
| 30678 | + if( p->nPopMode ) sqlite3_str_appendall(pDesc, "--once "); |
| 30679 | + sqlite3_str_appendall(pDesc,pI->zName); |
| 30680 | + if( bAll || (p->mode.spec.nAlign && pI->eCx==2) ){ |
| 30681 | + int ii; |
| 30682 | + sqlite3_str_appendall(pDesc, " --align \""); |
| 30683 | + for(ii=0; ii<p->mode.spec.nAlign; ii++){ |
| 30684 | + unsigned char a = p->mode.spec.aAlign[ii]; |
| 30685 | + sqlite3_str_appendchar(pDesc, 1, "LLCR"[a&3]); |
| 30686 | + } |
| 30687 | + sqlite3_str_append(pDesc, "\"", 1); |
| 30688 | + } |
| 30689 | + if( bAll || p->mode.spec.nCharLimit>0 ){ |
| 30690 | + sqlite3_str_appendf(pDesc, " --charlimit %d",p->mode.spec.nCharLimit); |
| 30691 | + } |
| 30692 | + zSetting = aModeStr[pI->eCSep]; |
| 30693 | + if( bAll || (zSetting && cli_strcmp(zSetting,p->mode.spec.zColumnSep)!=0) ){ |
| 30694 | + sqlite3_str_appendf(pDesc, " --colsep "); |
| 30695 | + append_c_string(pDesc, p->mode.spec.zColumnSep); |
| 30696 | + } |
| 30697 | + if( bAll || p->mode.spec.eEsc!=QRF_Auto ){ |
| 30698 | + sqlite3_str_appendf(pDesc, " --escape %s",qrfEscNames[p->mode.spec.eEsc]); |
| 30699 | + } |
| 30700 | + if( bAll || (p->mode.spec.nLineLimit>0 && pI->eCx>0) ){ |
| 30701 | + sqlite3_str_appendf(pDesc, " --linelimit %d",p->mode.spec.nLineLimit); |
| 30702 | + } |
| 30703 | + zSetting = aModeStr[pI->eNull]; |
| 30704 | + if( bAll || (zSetting && cli_strcmp(zSetting,p->mode.spec.zNull)!=0) ){ |
| 30705 | + sqlite3_str_appendf(pDesc, " --null "); |
| 30706 | + append_c_string(pDesc, p->mode.spec.zNull); |
| 30707 | + } |
| 30708 | + if( bAll |
| 30709 | + || (pI->eText!=p->mode.spec.eText && (pI->eText>1 || p->mode.spec.eText>1)) |
| 30710 | + ){ |
| 30711 | + sqlite3_str_appendf(pDesc," --quote %s",qrfQuoteNames[p->mode.spec.eText]); |
| 30712 | + } |
| 30713 | + zSetting = aModeStr[pI->eRSep]; |
| 30714 | + if( bAll || (zSetting && cli_strcmp(zSetting,p->mode.spec.zRowSep)!=0) ){ |
| 30715 | + sqlite3_str_appendf(pDesc, " --rowsep "); |
| 30716 | + append_c_string(pDesc, p->mode.spec.zRowSep); |
| 30717 | + } |
| 30718 | + if( bAll |
| 30719 | + || (pI->eCx && (p->mode.spec.nScreenWidth>0 || p->mode.bAutoScreenWidth)) |
| 30720 | + ){ |
| 30721 | + if( p->mode.bAutoScreenWidth ){ |
| 30722 | + sqlite3_str_appendall(pDesc, " --screenwidth auto"); |
| 30723 | + }else{ |
| 30724 | + sqlite3_str_appendf(pDesc," --screenwidth %d", |
| 30725 | + p->mode.spec.nScreenWidth); |
| 30726 | + } |
| 30727 | + } |
| 30728 | + if( bAll || p->mode.eMode==MODE_Insert ){ |
| 30729 | + sqlite3_str_appendf(pDesc," --tablename "); |
| 30730 | + append_c_string(pDesc, p->mode.spec.zTableName); |
| 30731 | + } |
| 30732 | + if( bAll || p->mode.spec.bTextJsonb ){ |
| 30733 | + sqlite3_str_appendf(pDesc," --textjsonb %s", |
| 30734 | + p->mode.spec.bTextJsonb==QRF_Yes ? "on" : "off"); |
| 30735 | + } |
| 30736 | + k = modeTitleDsply(p, bAll); |
| 30737 | + if( k==1 ){ |
| 30738 | + sqlite3_str_appendall(pDesc, " --titles off"); |
| 30739 | + }else if( k==2 ){ |
| 30740 | + sqlite3_str_appendall(pDesc, " --titles on"); |
| 30741 | + }else if( k==3 ){ |
| 30742 | + static const char *azTitle[] = |
| 30743 | + { "plain", "sql", "csv", "html", "tcl", "json"}; |
| 30744 | + sqlite3_str_appendf(pDesc, " --titles %s", |
| 30745 | + azTitle[p->mode.spec.eTitle-1]); |
| 30746 | + } |
| 30747 | + if( p->mode.spec.nWidth>0 && (bAll || pI->eCx==2) ){ |
| 30748 | + int ii; |
| 30749 | + const char *zSep = " --widths "; |
| 30750 | + for(ii=0; ii<p->mode.spec.nWidth; ii++){ |
| 30751 | + sqlite3_str_appendf(pDesc, "%s%d", zSep, (int)p->mode.spec.aWidth[ii]); |
| 30752 | + zSep = ","; |
| 30753 | + } |
| 30754 | + }else if( bAll ){ |
| 30755 | + sqlite3_str_appendall(pDesc, " --widths \"\""); |
| 30756 | + } |
| 30757 | + if( bAll || (pI->eCx>0 && p->mode.spec.bWordWrap) ){ |
| 30758 | + if( bAll ){ |
| 30759 | + sqlite3_str_appendf(pDesc, " --wordwrap %s", |
| 30760 | + p->mode.spec.bWordWrap==QRF_Yes ? "on" : "off"); |
| 30761 | + } |
| 30762 | + if( p->mode.spec.nWrap ){ |
| 30763 | + sqlite3_str_appendf(pDesc, " --wrap %d", p->mode.spec.nWrap); |
| 30764 | + } |
| 30765 | + if( !bAll ) sqlite3_str_append(pDesc, " --ww", 5); |
| 30766 | + } |
| 30767 | + zDesc = sqlite3_str_finish(pDesc); |
| 30768 | + cli_printf(p->out, "current output mode: %s\n", zDesc); |
| 30769 | + fflush(p->out); |
| 30770 | + sqlite3_free(zDesc); |
| 30771 | + } |
| 30772 | + return 0; |
| 30773 | +} |
| 30774 | + |
| 30775 | +/* |
| 30776 | +** DOT-COMMAND: .output |
| 30777 | +** USAGE: .output [OPTIONS] [FILE] |
| 30778 | +** |
| 30779 | +** Begin redirecting output to FILE. Or if FILE is omitted, revert |
| 30780 | +** to sending output to the console. If FILE begins with "|" then |
| 30781 | +** the remainder of file is taken as a pipe and output is directed |
| 30782 | +** into that pipe. If FILE is "memory" then output is captured in an |
| 30783 | +** internal memory buffer. If FILE is "off" then output is redirected |
| 30784 | +** into /dev/null or the equivalent. |
| 30785 | +** |
| 30786 | +** Options: |
| 30787 | +** --bom Prepend a byte-order mark to the output |
| 30788 | +** -e Accumulate output in a temporary text file then |
| 30789 | +** launch a text editor when the redirection ends. |
| 30790 | +** --error-prefix X Use X as the left-margin prefix for error messages. |
| 30791 | +** Set to an empty string to restore the default. |
| 30792 | +** --glob GLOB Raise an error if the memory buffer does not match |
| 30793 | +** the GLOB pattern. |
| 30794 | +** --keep Continue using the same "memory" buffer. Do not |
| 30795 | +** reset it or delete it. Useful in combination with |
| 30796 | +** --glob, --not-glob, and/or --verify. |
| 30797 | +** ---notglob GLOB Raise an error if the memory buffer does not match |
| 30798 | +** the GLOB pattern. |
| 30799 | +** --plain Use plain text rather than HTML tables with -w |
| 30800 | +** --show Write the memory buffer to the screen, for debugging. |
| 30801 | +** --verify ENDMARK Read subsequent lines of text until the first line |
| 30802 | +** that matches ENDMARK. Discard the ENDMARK. Compare |
| 30803 | +** the text against the accumulated output in memory and |
| 30804 | +** raise an error if there are any differences. |
| 30805 | +** -w Show the output in a web browser. Output is |
| 30806 | +** written into a temporary HTML file until the |
| 30807 | +** redirect ends, then the web browser is launched. |
| 30808 | +** Query results are shown as HTML tables, unless |
| 30809 | +** the --plain is used too. |
| 30810 | +** -x Show the output in a spreadsheet. Output is |
| 30811 | +** written to a temp file as CSV then the spreadsheet |
| 30812 | +** is launched when |
| 30813 | +** |
| 30814 | +** DOT-COMMAND: .once |
| 30815 | +** USAGE: .once [OPTIONS] FILE ... |
| 30816 | +** |
| 30817 | +** Write the output for the next line of SQL or the next dot-command into |
| 30818 | +** FILE. If FILE begins with "|" then it is a program into which output |
| 30819 | +** is written. The FILE argument should be omitted if one of the -e, -w, |
| 30820 | +** or -x options is used. |
| 30821 | +** |
| 30822 | +** Options: |
| 30823 | +** -e Capture output into a temporary file then bring up |
| 30824 | +** a text editor on that temporary file. |
| 30825 | +** --plain Use plain text rather than HTML tables with -w |
| 30826 | +** -w Capture output into an HTML file then bring up that |
| 30827 | +** file in a web browser |
| 30828 | +** -x Show the output in a spreadsheet. Output is |
| 30829 | +** written to a temp file as CSV then the spreadsheet |
| 30830 | +** is launched when |
| 30831 | +** |
| 30832 | +** DOT-COMMAND: .excel |
| 30833 | +** Shorthand for ".once -x" |
| 30834 | +** |
| 30835 | +** DOT-COMMAND: .www [--plain] |
| 30836 | +** Shorthand for ".once -w" or ".once --plain -w" |
| 30837 | +*/ |
| 30838 | +static int dotCmdOutput(ShellState *p){ |
| 30839 | + int nArg = p->dot.nArg; /* Number of arguments */ |
| 30840 | + char **azArg = p->dot.azArg; /* Text of the arguments */ |
| 30841 | + char *zFile = 0; /* The FILE argument */ |
| 30842 | + int i; /* Loop counter */ |
| 30843 | + int eMode = 0; /* 0: .outout/.once, 'x'=.excel, 'w'=.www */ |
| 30844 | + int bOnce = 0; /* 0: .output, 1: .once, 2: .excel/.www */ |
| 30845 | + int bPlain = 0; /* --plain option */ |
| 30846 | + int bKeep = 0; /* --keep option */ |
| 30847 | + char *zCheck = 0; /* Argument to --glob, --notglob, --verify */ |
| 30848 | + int eCheck = 0; /* 1: --glob, 2: --notglob, 3: --verify */ |
| 30849 | + static const char *zBomUtf8 = "\357\273\277"; |
| 30850 | + const char *zBom = 0; |
| 30851 | + char c = azArg[0][0]; |
| 30852 | + int n = strlen30(azArg[0]); |
| 30853 | + |
| 30854 | + failIfSafeMode(p, "cannot run .%s in safe mode", azArg[0]); |
| 30855 | + if( c=='e' ){ |
| 30856 | + eMode = 'x'; |
| 30857 | + bOnce = 2; |
| 30858 | + }else if( c=='w' ){ |
| 30859 | + eMode = 'w'; |
| 30860 | + bOnce = 2; |
| 30861 | + }else if( n>=2 && cli_strncmp(azArg[0],"once",n)==0 ){ |
| 30862 | + bOnce = 1; |
| 30863 | + } |
| 30864 | + for(i=1; i<nArg; i++){ |
| 30865 | + char *z = azArg[i]; |
| 30866 | + if( z[0]=='-' ){ |
| 30867 | + if( z[1]=='-' ) z++; |
| 30868 | + if( cli_strcmp(z,"-bom")==0 ){ |
| 30869 | + zBom = zBomUtf8; |
| 30870 | + }else if( cli_strcmp(z,"-plain")==0 ){ |
| 30871 | + bPlain = 1; |
| 30872 | + }else if( c=='o' && z[0]=='1' && z[1]!=0 && z[2]==0 |
| 30873 | + && (z[1]=='x' || z[1]=='e' || z[1]=='w') ){ |
| 30874 | + if( bKeep || eMode || eCheck ){ |
| 30875 | + dotCmdError(p, i, "incompatible with prior options",0); |
| 30876 | + goto dotCmdOutput_error; |
| 30877 | + } |
| 30878 | + eMode = z[1]; |
| 30879 | + }else if( cli_strcmp(z,"-keep")==0 ){ |
| 30880 | + bKeep = 1; |
| 30881 | + }else if( cli_strcmp(z,"-show")==0 ){ |
| 30882 | + if( cli_output_capture ){ |
| 30883 | + sqlite3_fprintf(stdout, "%s", sqlite3_str_value(cli_output_capture)); |
| 30884 | + } |
| 30885 | + bKeep = 1; |
| 30886 | + }else if( cli_strcmp(z,"-glob")==0 |
| 30887 | + || cli_strcmp(z,"-notglob")==0 |
| 30888 | + || cli_strcmp(z,"-verify")==0 |
| 30889 | + ){ |
| 30890 | + if( eCheck || eMode ){ |
| 30891 | + dotCmdError(p, i, "incompatible with prior options",0); |
| 30892 | + goto dotCmdOutput_error; |
| 30893 | + } |
| 30894 | + if( i+1>=nArg ){ |
| 30895 | + dotCmdError(p, i, "missing argument", 0); |
| 30896 | + goto dotCmdOutput_error; |
| 30897 | + } |
| 30898 | + zCheck = azArg[++i]; |
| 30899 | + eCheck = z[1]=='g' ? 1 : z[1]=='n' ? 2 : 3; |
| 30900 | + }else if( optionMatch(z,"error-prefix") ){ |
| 30901 | + if( i+1>=nArg ){ |
| 30902 | + dotCmdError(p, i, "missing argument", 0); |
| 30903 | + return 1; |
| 30904 | + } |
| 30905 | + free(p->zErrPrefix); |
| 30906 | + i++; |
| 30907 | + p->zErrPrefix = azArg[i][0]==0 ? 0 : strdup(azArg[i]); |
| 30908 | + }else{ |
| 30909 | + dotCmdError(p, i, "unknown option", 0); |
| 30910 | + sqlite3_free(zFile); |
| 30911 | + return 1; |
| 30912 | + } |
| 30913 | + }else if( zFile==0 && eMode==0 ){ |
| 30914 | + if( bKeep || eCheck ){ |
| 30915 | + dotCmdError(p, i, "incompatible with prior options",0); |
| 30916 | + goto dotCmdOutput_error; |
| 30917 | + } |
| 30918 | + if( cli_strcmp(z, "memory")==0 && bOnce ){ |
| 30919 | + dotCmdError(p, 0, "cannot redirect to \"memory\"", 0); |
| 30920 | + goto dotCmdOutput_error; |
| 30921 | + } |
| 30922 | + if( cli_strcmp(z, "off")==0 ){ |
| 30923 | +#ifdef _WIN32 |
| 30924 | + zFile = sqlite3_mprintf("nul"); |
| 30925 | +#else |
| 30926 | + zFile = sqlite3_mprintf("/dev/null"); |
| 30927 | +#endif |
| 30928 | + }else{ |
| 30929 | + zFile = sqlite3_mprintf("%s", z); |
| 30930 | + } |
| 30931 | + if( zFile && zFile[0]=='|' ){ |
| 30932 | + while( i+1<nArg ) zFile = sqlite3_mprintf("%z %s", zFile, azArg[++i]); |
| 30933 | + break; |
| 30934 | + } |
| 30935 | + }else{ |
| 30936 | + dotCmdError(p, i, "surplus argument", 0); |
| 30937 | + sqlite3_free(zFile); |
| 30938 | + return 1; |
| 30939 | + } |
| 30940 | + } |
| 30941 | + if( zFile==0 && !bKeep ){ |
| 30942 | + zFile = sqlite3_mprintf("stdout"); |
| 30943 | + shell_check_oom(zFile); |
| 30944 | + } |
| 30945 | + if( bOnce ){ |
| 30946 | + p->nPopOutput = 2; |
| 30947 | + }else{ |
| 30948 | + p->nPopOutput = 0; |
| 30949 | + } |
| 30950 | + if( eCheck ){ |
| 30951 | + char *zTest; |
| 30952 | + if( cli_output_capture ){ |
| 30953 | + zTest = sqlite3_str_value(cli_output_capture); |
| 30954 | + }else{ |
| 30955 | + zTest = ""; |
| 30956 | + } |
| 30957 | + p->nTestRun++; |
| 30958 | + if( eCheck==3 ){ |
| 30959 | + int nCheck = strlen30(zCheck); |
| 30960 | + sqlite3_str *pPattern = sqlite3_str_new(p->db); |
| 30961 | + char *zPattern; |
| 30962 | + sqlite3_int64 iStart = p->lineno; |
| 30963 | + char zLine[2000]; |
| 30964 | + while( sqlite3_fgets(zLine,sizeof(zLine),p->in) ){ |
| 30965 | + if( strchr(zLine,'\n') ) p->lineno++; |
| 30966 | + if( cli_strncmp(zCheck,zLine,nCheck)==0 ) break; |
| 30967 | + sqlite3_str_appendall(pPattern, zLine); |
| 30968 | + } |
| 30969 | + zPattern = sqlite3_str_finish(pPattern); |
| 30970 | + if( cli_strcmp(zPattern,zTest)!=0 ){ |
| 30971 | + sqlite3_fprintf(stderr, |
| 30972 | + "%s:%lld: --verify does matches prior output\n", |
| 30973 | + p->zInFile, iStart); |
| 30974 | + p->nTestErr++; |
| 30975 | + } |
| 30976 | + sqlite3_free(zPattern); |
| 30977 | + }else{ |
| 30978 | + char *zGlob = sqlite3_mprintf("*%s*", zCheck); |
| 30979 | + if( eCheck==1 && sqlite3_strglob(zGlob, zTest)!=0 ){ |
| 30980 | + sqlite3_fprintf(stderr, |
| 30981 | + "%s:%lld: --glob \"%s\" does not match prior output\n", |
| 30982 | + p->zInFile, p->lineno, zCheck); |
| 30983 | + p->nTestErr++; |
| 30984 | + }else if( eCheck==2 && sqlite3_strglob(zGlob, zTest)==0 ){ |
| 30985 | + sqlite3_fprintf(stderr, |
| 30986 | + "%s:%lld: --notglob \"%s\" matches prior output\n", |
| 30987 | + p->zInFile, p->lineno, zCheck); |
| 30988 | + p->nTestErr++; |
| 30989 | + } |
| 30990 | + sqlite3_free(zGlob); |
| 30991 | + } |
| 30992 | + } |
| 30993 | + if( !bKeep ) output_reset(p); |
| 30994 | +#ifndef SQLITE_NOHAVE_SYSTEM |
| 30995 | + if( eMode=='e' || eMode=='x' || eMode=='w' ){ |
| 30996 | + p->doXdgOpen = 1; |
| 30997 | + modePush(p); |
| 30998 | + if( eMode=='x' ){ |
| 30999 | + /* spreadsheet mode. Output as CSV. */ |
| 31000 | + newTempFile(p, "csv"); |
| 31001 | + p->mode.mFlags &= ~MFLG_ECHO; |
| 31002 | + p->mode.eMode = MODE_Csv; |
| 31003 | + modeSetStr(&p->mode.spec.zColumnSep, SEP_Comma); |
| 31004 | + modeSetStr(&p->mode.spec.zRowSep, SEP_CrLf); |
| 31005 | +#ifdef _WIN32 |
| 31006 | + zBom = zBomUtf8; /* Always include the BOM on Windows, as Excel does |
| 31007 | + ** not work without it. */ |
| 31008 | +#endif |
| 31009 | + }else if( eMode=='w' ){ |
| 31010 | + /* web-browser mode. */ |
| 31011 | + newTempFile(p, "html"); |
| 31012 | + if( !bPlain ) p->mode.eMode = MODE_Www; |
| 31013 | + }else{ |
| 31014 | + /* text editor mode */ |
| 31015 | + newTempFile(p, "txt"); |
| 31016 | + } |
| 31017 | + sqlite3_free(zFile); |
| 31018 | + zFile = sqlite3_mprintf("%s", p->zTempFile); |
| 31019 | + } |
| 31020 | +#endif /* SQLITE_NOHAVE_SYSTEM */ |
| 31021 | + if( !bKeep ) shell_check_oom(zFile); |
| 31022 | + if( bKeep ){ |
| 31023 | + /* no-op */ |
| 31024 | + }else if( cli_strcmp(zFile,"memory")==0 ){ |
| 31025 | + if( cli_output_capture ){ |
| 31026 | + sqlite3_str_free(cli_output_capture); |
| 31027 | + } |
| 31028 | + cli_output_capture = sqlite3_str_new(0); |
| 31029 | + }else if( zFile[0]=='|' ){ |
| 31030 | +#ifdef SQLITE_OMIT_POPEN |
| 31031 | + eputz("Error: pipes are not supported in this OS\n"); |
| 31032 | + output_redir(p, stdout); |
| 31033 | + goto dotCmdOutput_error; |
| 31034 | +#else |
| 31035 | + FILE *pfPipe = sqlite3_popen(zFile + 1, "w"); |
| 31036 | + if( pfPipe==0 ){ |
| 31037 | + assert( stderr!=NULL ); |
| 31038 | + cli_printf(stderr,"Error: cannot open pipe \"%s\"\n", zFile + 1); |
| 31039 | + goto dotCmdOutput_error; |
| 31040 | + }else{ |
| 31041 | + output_redir(p, pfPipe); |
| 31042 | + if( zBom ) cli_puts(zBom, pfPipe); |
| 31043 | + sqlite3_snprintf(sizeof(p->outfile), p->outfile, "%s", zFile); |
| 31044 | + } |
| 31045 | +#endif |
| 31046 | + }else{ |
| 31047 | + FILE *pfFile = output_file_open(p, zFile); |
| 31048 | + if( pfFile==0 ){ |
| 31049 | + if( cli_strcmp(zFile,"off")!=0 ){ |
| 31050 | + assert( stderr!=NULL ); |
| 31051 | + cli_printf(stderr,"Error: cannot write to \"%s\"\n", zFile); |
| 31052 | + } |
| 31053 | + goto dotCmdOutput_error; |
| 31054 | + } else { |
| 31055 | + output_redir(p, pfFile); |
| 31056 | + if( zBom ) cli_puts(zBom, pfFile); |
| 31057 | + if( bPlain && eMode=='w' ){ |
| 31058 | + cli_puts( |
| 31059 | + "<!DOCTYPE html>\n<BODY>\n<PLAINTEXT>\n", |
| 31060 | + pfFile |
| 31061 | + ); |
| 31062 | + } |
| 31063 | + sqlite3_snprintf(sizeof(p->outfile), p->outfile, "%s", zFile); |
| 31064 | + } |
| 31065 | + } |
| 31066 | + sqlite3_free(zFile); |
| 31067 | + return 0; |
| 31068 | + |
| 31069 | +dotCmdOutput_error: |
| 31070 | + sqlite3_free(zFile); |
| 31071 | + return 1; |
| 31072 | +} |
| 31073 | +/* |
| 31074 | +** Parse input line zLine up into individual arguments. Retain the |
| 31075 | +** parse in the p->dot substructure. |
| 31076 | +*/ |
| 31077 | +static void parseDotRealloc(ShellState *p, int nArg){ |
| 31078 | + p->dot.nAlloc = nArg+22; |
| 31079 | + p->dot.azArg = realloc(p->dot.azArg,p->dot.nAlloc*sizeof(char*)); |
| 31080 | + shell_check_oom(p->dot.azArg); |
| 31081 | + p->dot.aiOfst = realloc(p->dot.aiOfst,p->dot.nAlloc*sizeof(int)); |
| 31082 | + shell_check_oom(p->dot.aiOfst); |
| 31083 | + p->dot.abQuot = realloc(p->dot.abQuot,p->dot.nAlloc); |
| 31084 | + shell_check_oom(p->dot.abQuot); |
| 31085 | +} |
| 31086 | +static void parseDotCmdArgs(const char *zLine, ShellState *p){ |
| 31087 | + char *z; |
| 31088 | + int h = 1; |
| 31089 | + int nArg = 0; |
| 31090 | + |
| 31091 | + p->dot.zOrig = zLine; |
| 31092 | + free(p->dot.zCopy); |
| 31093 | + z = p->dot.zCopy = strdup(zLine); |
| 31094 | + shell_check_oom(z); |
| 31095 | + parseDotRealloc(p, 2); |
| 31096 | + while( z[h] ){ |
| 31097 | + while( IsSpace(z[h]) ){ h++; } |
| 31098 | + if( z[h]==0 ) break; |
| 31099 | + if( nArg+2>p->dot.nAlloc ){ |
| 31100 | + parseDotRealloc(p, nArg); |
| 31101 | + } |
| 31102 | + if( z[h]=='\'' || z[h]=='"' ){ |
| 31103 | + int delim = z[h++]; |
| 31104 | + p->dot.abQuot[nArg] = 1; |
| 31105 | + p->dot.azArg[nArg] = &z[h]; |
| 31106 | + p->dot.aiOfst[nArg] = h; |
| 31107 | + while( z[h] && z[h]!=delim ){ |
| 31108 | + if( z[h]=='\\' && delim=='"' && z[h+1]!=0 ) h++; |
| 31109 | + h++; |
| 31110 | + } |
| 31111 | + if( z[h]==delim ){ |
| 31112 | + z[h++] = 0; |
| 31113 | + } |
| 31114 | + if( delim=='"' ) resolve_backslashes(p->dot.azArg[nArg]); |
| 31115 | + }else{ |
| 31116 | + p->dot.abQuot[nArg] = 0; |
| 31117 | + p->dot.azArg[nArg] = &z[h]; |
| 31118 | + p->dot.aiOfst[nArg] = h; |
| 31119 | + while( z[h] && !IsSpace(z[h]) ){ h++; } |
| 31120 | + if( z[h] ) z[h++] = 0; |
| 31121 | + } |
| 31122 | + nArg++; |
| 31123 | + } |
| 31124 | + p->dot.nArg = nArg; |
| 31125 | + p->dot.azArg[nArg] = 0; |
| 31126 | +} |
| 28550 | 31127 | |
| 28551 | 31128 | /* |
| 28552 | 31129 | ** If an input line begins with "." then invoke this routine to |
| 28553 | 31130 | ** process that line. |
| 28554 | 31131 | ** |
| 28555 | 31132 | ** Return 1 on error, 2 to exit, and 0 otherwise. |
| 28556 | 31133 | */ |
| 28557 | | -static int do_meta_command(char *zLine, ShellState *p){ |
| 28558 | | - int h = 1; |
| 28559 | | - int nArg = 0; |
| 31134 | +static int do_meta_command(const char *zLine, ShellState *p){ |
| 31135 | + int nArg; |
| 28560 | 31136 | int n, c; |
| 28561 | 31137 | int rc = 0; |
| 28562 | | - char *azArg[52]; |
| 31138 | + char **azArg; |
| 28563 | 31139 | |
| 28564 | 31140 | #if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_AUTHORIZATION) |
| 28565 | 31141 | if( p->expert.pExpert ){ |
| 28566 | 31142 | expertFinish(p, 1, 0); |
| 28567 | 31143 | } |
| 28568 | 31144 | #endif |
| 28569 | 31145 | |
| 28570 | | - /* Parse the input line into tokens. |
| 31146 | + /* Parse the input line into tokens stored in p->dot. |
| 28571 | 31147 | */ |
| 28572 | | - while( zLine[h] && nArg<ArraySize(azArg)-1 ){ |
| 28573 | | - while( IsSpace(zLine[h]) ){ h++; } |
| 28574 | | - if( zLine[h]==0 ) break; |
| 28575 | | - if( zLine[h]=='\'' || zLine[h]=='"' ){ |
| 28576 | | - int delim = zLine[h++]; |
| 28577 | | - azArg[nArg++] = &zLine[h]; |
| 28578 | | - while( zLine[h] && zLine[h]!=delim ){ |
| 28579 | | - if( zLine[h]=='\\' && delim=='"' && zLine[h+1]!=0 ) h++; |
| 28580 | | - h++; |
| 28581 | | - } |
| 28582 | | - if( zLine[h]==delim ){ |
| 28583 | | - zLine[h++] = 0; |
| 28584 | | - } |
| 28585 | | - if( delim=='"' ) resolve_backslashes(azArg[nArg-1]); |
| 28586 | | - }else{ |
| 28587 | | - azArg[nArg++] = &zLine[h]; |
| 28588 | | - while( zLine[h] && !IsSpace(zLine[h]) ){ h++; } |
| 28589 | | - if( zLine[h] ) zLine[h++] = 0; |
| 28590 | | - } |
| 28591 | | - } |
| 28592 | | - azArg[nArg] = 0; |
| 31148 | + parseDotCmdArgs(zLine, p); |
| 31149 | + nArg = p->dot.nArg; |
| 31150 | + azArg = p->dot.azArg; |
| 28593 | 31151 | |
| 28594 | 31152 | /* Process the input line. |
| 28595 | 31153 | */ |
| 28596 | 31154 | if( nArg==0 ) return 0; /* no tokens, no error */ |
| 28597 | 31155 | n = strlen30(azArg[0]); |
| | @@ -28599,11 +31157,11 @@ |
| 28599 | 31157 | clearTempFile(p); |
| 28600 | 31158 | |
| 28601 | 31159 | #ifndef SQLITE_OMIT_AUTHORIZATION |
| 28602 | 31160 | if( c=='a' && cli_strncmp(azArg[0], "auth", n)==0 ){ |
| 28603 | 31161 | if( nArg!=2 ){ |
| 28604 | | - sqlite3_fprintf(stderr, "Usage: .auth ON|OFF\n"); |
| 31162 | + cli_printf(stderr, "Usage: .auth ON|OFF\n"); |
| 28605 | 31163 | rc = 1; |
| 28606 | 31164 | goto meta_command_exit; |
| 28607 | 31165 | } |
| 28608 | 31166 | open_db(p, 0); |
| 28609 | 31167 | if( booleanValue(azArg[1]) ){ |
| | @@ -28646,32 +31204,32 @@ |
| 28646 | 31204 | }else |
| 28647 | 31205 | if( cli_strcmp(z, "-async")==0 ){ |
| 28648 | 31206 | bAsync = 1; |
| 28649 | 31207 | }else |
| 28650 | 31208 | { |
| 28651 | | - sqlite3_fprintf(stderr,"unknown option: %s\n", azArg[j]); |
| 31209 | + dotCmdError(p, j, "unknown option", "should be -append or -async"); |
| 28652 | 31210 | return 1; |
| 28653 | 31211 | } |
| 28654 | 31212 | }else if( zDestFile==0 ){ |
| 28655 | 31213 | zDestFile = azArg[j]; |
| 28656 | 31214 | }else if( zDb==0 ){ |
| 28657 | 31215 | zDb = zDestFile; |
| 28658 | 31216 | zDestFile = azArg[j]; |
| 28659 | 31217 | }else{ |
| 28660 | | - sqlite3_fprintf(stderr, "Usage: .backup ?DB? ?OPTIONS? FILENAME\n"); |
| 31218 | + cli_printf(stderr, "Usage: .backup ?DB? ?OPTIONS? FILENAME\n"); |
| 28661 | 31219 | return 1; |
| 28662 | 31220 | } |
| 28663 | 31221 | } |
| 28664 | 31222 | if( zDestFile==0 ){ |
| 28665 | | - sqlite3_fprintf(stderr, "missing FILENAME argument on .backup\n"); |
| 31223 | + cli_printf(stderr, "missing FILENAME argument on .backup\n"); |
| 28666 | 31224 | return 1; |
| 28667 | 31225 | } |
| 28668 | 31226 | if( zDb==0 ) zDb = "main"; |
| 28669 | 31227 | rc = sqlite3_open_v2(zDestFile, &pDest, |
| 28670 | 31228 | SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE, zVfs); |
| 28671 | 31229 | if( rc!=SQLITE_OK ){ |
| 28672 | | - sqlite3_fprintf(stderr,"Error: cannot open \"%s\"\n", zDestFile); |
| 31230 | + cli_printf(stderr,"Error: cannot open \"%s\"\n", zDestFile); |
| 28673 | 31231 | close_db(pDest); |
| 28674 | 31232 | return 1; |
| 28675 | 31233 | } |
| 28676 | 31234 | if( bAsync ){ |
| 28677 | 31235 | sqlite3_exec(pDest, "PRAGMA synchronous=OFF; PRAGMA journal_mode=OFF;", |
| | @@ -28728,11 +31286,11 @@ |
| 28728 | 31286 | sqlite3_free(z); |
| 28729 | 31287 | #else |
| 28730 | 31288 | rc = chdir(azArg[1]); |
| 28731 | 31289 | #endif |
| 28732 | 31290 | if( rc ){ |
| 28733 | | - sqlite3_fprintf(stderr,"Cannot change to directory \"%s\"\n", azArg[1]); |
| 31291 | + cli_printf(stderr,"Cannot change to directory \"%s\"\n", azArg[1]); |
| 28734 | 31292 | rc = 1; |
| 28735 | 31293 | } |
| 28736 | 31294 | }else{ |
| 28737 | 31295 | eputz("Usage: .cd DIRECTORY\n"); |
| 28738 | 31296 | rc = 1; |
| | @@ -28761,16 +31319,16 @@ |
| 28761 | 31319 | eputz("Usage: .check GLOB-PATTERN\n"); |
| 28762 | 31320 | rc = 2; |
| 28763 | 31321 | }else if( (zRes = readFile("testcase-out.txt", 0))==0 ){ |
| 28764 | 31322 | rc = 2; |
| 28765 | 31323 | }else if( testcase_glob(azArg[1],zRes)==0 ){ |
| 28766 | | - sqlite3_fprintf(stderr, |
| 31324 | + cli_printf(stderr, |
| 28767 | 31325 | "testcase-%s FAILED\n Expected: [%s]\n Got: [%s]\n", |
| 28768 | 31326 | p->zTestcase, azArg[1], zRes); |
| 28769 | 31327 | rc = 1; |
| 28770 | 31328 | }else{ |
| 28771 | | - sqlite3_fprintf(p->out, "testcase-%s ok\n", p->zTestcase); |
| 31329 | + cli_printf(p->out, "testcase-%s ok\n", p->zTestcase); |
| 28772 | 31330 | p->nCheck++; |
| 28773 | 31331 | } |
| 28774 | 31332 | sqlite3_free(zRes); |
| 28775 | 31333 | }else |
| 28776 | 31334 | #endif /* !defined(SQLITE_SHELL_FIDDLE) */ |
| | @@ -28799,13 +31357,13 @@ |
| 28799 | 31357 | zFile = "(memory)"; |
| 28800 | 31358 | }else if( zFile[0]==0 ){ |
| 28801 | 31359 | zFile = "(temporary-file)"; |
| 28802 | 31360 | } |
| 28803 | 31361 | if( p->pAuxDb == &p->aAuxDb[i] ){ |
| 28804 | | - sqlite3_fprintf(stdout, "ACTIVE %d: %s\n", i, zFile); |
| 31362 | + cli_printf(stdout, "ACTIVE %d: %s\n", i, zFile); |
| 28805 | 31363 | }else if( p->aAuxDb[i].db!=0 ){ |
| 28806 | | - sqlite3_fprintf(stdout, " %d: %s\n", i, zFile); |
| 31364 | + cli_printf(stdout, " %d: %s\n", i, zFile); |
| 28807 | 31365 | } |
| 28808 | 31366 | } |
| 28809 | 31367 | }else if( nArg==2 && IsDigit(azArg[1][0]) && azArg[1][1]==0 ){ |
| 28810 | 31368 | int i = azArg[1][0] - '0'; |
| 28811 | 31369 | if( p->pAuxDb != &p->aAuxDb[i] && i>=0 && i<ArraySize(p->aAuxDb) ){ |
| | @@ -28837,16 +31395,21 @@ |
| 28837 | 31395 | && (cli_strncmp(azArg[0], "crlf", n)==0 |
| 28838 | 31396 | || cli_strncmp(azArg[0], "crnl",n)==0) |
| 28839 | 31397 | ){ |
| 28840 | 31398 | if( nArg==2 ){ |
| 28841 | 31399 | #ifdef _WIN32 |
| 28842 | | - p->crlfMode = booleanValue(azArg[1]); |
| 31400 | + if( booleanValue(azArg[1]) ){ |
| 31401 | + p->mode.mFlags |= MFLG_CRLF; |
| 31402 | + }else{ |
| 31403 | + p->mode.mFlags &= ~MFLG_CRLF; |
| 31404 | + } |
| 28843 | 31405 | #else |
| 28844 | | - p->crlfMode = 0; |
| 31406 | + p->mode.mFlags &= ~MFLG_CRLF; |
| 28845 | 31407 | #endif |
| 28846 | 31408 | } |
| 28847 | | - sqlite3_fprintf(stderr, "crlf is %s\n", p->crlfMode ? "ON" : "OFF"); |
| 31409 | + cli_printf(stderr, "crlf is %s\n", |
| 31410 | + (p->mode.mFlags & MFLG_CRLF)!=0 ? "ON" : "OFF"); |
| 28848 | 31411 | }else |
| 28849 | 31412 | |
| 28850 | 31413 | if( c=='d' && n>1 && cli_strncmp(azArg[0], "databases", n)==0 ){ |
| 28851 | 31414 | char **azName = 0; |
| 28852 | 31415 | int nName = 0; |
| | @@ -28872,11 +31435,11 @@ |
| 28872 | 31435 | sqlite3_finalize(pStmt); |
| 28873 | 31436 | for(i=0; i<nName; i++){ |
| 28874 | 31437 | int eTxn = sqlite3_txn_state(p->db, azName[i*2]); |
| 28875 | 31438 | int bRdonly = sqlite3_db_readonly(p->db, azName[i*2]); |
| 28876 | 31439 | const char *z = azName[i*2+1]; |
| 28877 | | - sqlite3_fprintf(p->out, "%s: %s %s%s\n", |
| 31440 | + cli_printf(p->out, "%s: %s %s%s\n", |
| 28878 | 31441 | azName[i*2], z && z[0] ? z : "\"\"", bRdonly ? "r/o" : "r/w", |
| 28879 | 31442 | eTxn==SQLITE_TXN_NONE ? "" : |
| 28880 | 31443 | eTxn==SQLITE_TXN_READ ? " read-txn" : " write-txn"); |
| 28881 | 31444 | free(azName[i*2]); |
| 28882 | 31445 | free(azName[i*2+1]); |
| | @@ -28917,17 +31480,17 @@ |
| 28917 | 31480 | if( nArg>1 && cli_strcmp(azArg[1], aDbConfig[ii].zName)!=0 ) continue; |
| 28918 | 31481 | if( nArg>=3 ){ |
| 28919 | 31482 | sqlite3_db_config(p->db, aDbConfig[ii].op, booleanValue(azArg[2]), 0); |
| 28920 | 31483 | } |
| 28921 | 31484 | sqlite3_db_config(p->db, aDbConfig[ii].op, -1, &v); |
| 28922 | | - sqlite3_fprintf(p->out, "%19s %s\n", |
| 31485 | + cli_printf(p->out, "%19s %s\n", |
| 28923 | 31486 | aDbConfig[ii].zName, v ? "on" : "off"); |
| 28924 | 31487 | if( nArg>1 ) break; |
| 28925 | 31488 | } |
| 28926 | 31489 | if( nArg>1 && ii==ArraySize(aDbConfig) ){ |
| 28927 | | - sqlite3_fprintf(stderr,"Error: unknown dbconfig \"%s\"\n", azArg[1]); |
| 28928 | | - eputz("Enter \".dbconfig\" with no arguments for a list\n"); |
| 31490 | + dotCmdError(p, 1, "unknown dbconfig", |
| 31491 | + "Enter \".dbconfig\" with no arguments for a list"); |
| 28929 | 31492 | } |
| 28930 | 31493 | }else |
| 28931 | 31494 | |
| 28932 | 31495 | #if SQLITE_SHELL_HAVE_RECOVER |
| 28933 | 31496 | if( c=='d' && n>=3 && cli_strncmp(azArg[0], "dbinfo", n)==0 ){ |
| | @@ -28942,42 +31505,41 @@ |
| 28942 | 31505 | |
| 28943 | 31506 | if( c=='d' && cli_strncmp(azArg[0], "dump", n)==0 ){ |
| 28944 | 31507 | char *zLike = 0; |
| 28945 | 31508 | char *zSql; |
| 28946 | 31509 | int i; |
| 28947 | | - int savedShowHeader = p->showHeader; |
| 28948 | 31510 | int savedShellFlags = p->shellFlgs; |
| 31511 | + Mode saved_mode; |
| 28949 | 31512 | ShellClearFlag(p, |
| 28950 | | - SHFLG_PreserveRowid|SHFLG_Newlines|SHFLG_Echo |
| 28951 | | - |SHFLG_DumpDataOnly|SHFLG_DumpNoSys); |
| 31513 | + SHFLG_PreserveRowid|SHFLG_DumpDataOnly|SHFLG_DumpNoSys); |
| 28952 | 31514 | for(i=1; i<nArg; i++){ |
| 28953 | 31515 | if( azArg[i][0]=='-' ){ |
| 28954 | 31516 | const char *z = azArg[i]+1; |
| 28955 | 31517 | if( z[0]=='-' ) z++; |
| 28956 | 31518 | if( cli_strcmp(z,"preserve-rowids")==0 ){ |
| 28957 | 31519 | #ifdef SQLITE_OMIT_VIRTUALTABLE |
| 28958 | | - eputz("The --preserve-rowids option is not compatible" |
| 28959 | | - " with SQLITE_OMIT_VIRTUALTABLE\n"); |
| 31520 | + dotCmdError(p, i, "unable", |
| 31521 | + "The --preserve-rowids option is not compatible" |
| 31522 | + " with SQLITE_OMIT_VIRTUALTABLE"); |
| 28960 | 31523 | rc = 1; |
| 28961 | 31524 | sqlite3_free(zLike); |
| 28962 | 31525 | goto meta_command_exit; |
| 28963 | 31526 | #else |
| 28964 | 31527 | ShellSetFlag(p, SHFLG_PreserveRowid); |
| 28965 | 31528 | #endif |
| 28966 | 31529 | }else |
| 28967 | 31530 | if( cli_strcmp(z,"newlines")==0 ){ |
| 28968 | | - ShellSetFlag(p, SHFLG_Newlines); |
| 31531 | + /*ShellSetFlag(p, SHFLG_Newlines);*/ |
| 28969 | 31532 | }else |
| 28970 | 31533 | if( cli_strcmp(z,"data-only")==0 ){ |
| 28971 | 31534 | ShellSetFlag(p, SHFLG_DumpDataOnly); |
| 28972 | 31535 | }else |
| 28973 | 31536 | if( cli_strcmp(z,"nosys")==0 ){ |
| 28974 | 31537 | ShellSetFlag(p, SHFLG_DumpNoSys); |
| 28975 | 31538 | }else |
| 28976 | 31539 | { |
| 28977 | | - sqlite3_fprintf(stderr, |
| 28978 | | - "Unknown option \"%s\" on \".dump\"\n", azArg[i]); |
| 31540 | + dotCmdError(p, i, "unknown option", 0); |
| 28979 | 31541 | rc = 1; |
| 28980 | 31542 | sqlite3_free(zLike); |
| 28981 | 31543 | goto meta_command_exit; |
| 28982 | 31544 | } |
| 28983 | 31545 | }else{ |
| | @@ -29003,20 +31565,21 @@ |
| 29003 | 31565 | } |
| 29004 | 31566 | } |
| 29005 | 31567 | |
| 29006 | 31568 | open_db(p, 0); |
| 29007 | 31569 | |
| 31570 | + modeDup(&saved_mode, &p->mode); |
| 29008 | 31571 | outputDumpWarning(p, zLike); |
| 29009 | 31572 | if( (p->shellFlgs & SHFLG_DumpDataOnly)==0 ){ |
| 29010 | 31573 | /* When playing back a "dump", the content might appear in an order |
| 29011 | 31574 | ** which causes immediate foreign key constraints to be violated. |
| 29012 | 31575 | ** So disable foreign-key constraint enforcement to prevent problems. */ |
| 29013 | | - sqlite3_fputs("PRAGMA foreign_keys=OFF;\n", p->out); |
| 29014 | | - sqlite3_fputs("BEGIN TRANSACTION;\n", p->out); |
| 31576 | + cli_puts("PRAGMA foreign_keys=OFF;\n", p->out); |
| 31577 | + cli_puts("BEGIN TRANSACTION;\n", p->out); |
| 29015 | 31578 | } |
| 29016 | 31579 | p->writableSchema = 0; |
| 29017 | | - p->showHeader = 0; |
| 31580 | + p->mode.spec.bTitles = QRF_No; |
| 29018 | 31581 | /* Set writable_schema=ON since doing so forces SQLite to initialize |
| 29019 | 31582 | ** as much of the schema as it can even if the sqlite_schema table is |
| 29020 | 31583 | ** corrupt. */ |
| 29021 | 31584 | sqlite3_exec(p->db, "SAVEPOINT dump; PRAGMA writable_schema=ON", 0, 0, 0); |
| 29022 | 31585 | p->nErr = 0; |
| | @@ -29041,25 +31604,31 @@ |
| 29041 | 31604 | run_table_dump_query(p, zSql); |
| 29042 | 31605 | sqlite3_free(zSql); |
| 29043 | 31606 | } |
| 29044 | 31607 | sqlite3_free(zLike); |
| 29045 | 31608 | if( p->writableSchema ){ |
| 29046 | | - sqlite3_fputs("PRAGMA writable_schema=OFF;\n", p->out); |
| 31609 | + cli_puts("PRAGMA writable_schema=OFF;\n", p->out); |
| 29047 | 31610 | p->writableSchema = 0; |
| 29048 | 31611 | } |
| 29049 | 31612 | sqlite3_exec(p->db, "PRAGMA writable_schema=OFF;", 0, 0, 0); |
| 29050 | 31613 | sqlite3_exec(p->db, "RELEASE dump;", 0, 0, 0); |
| 29051 | 31614 | if( (p->shellFlgs & SHFLG_DumpDataOnly)==0 ){ |
| 29052 | | - sqlite3_fputs(p->nErr?"ROLLBACK; -- due to errors\n":"COMMIT;\n", p->out); |
| 31615 | + cli_puts(p->nErr?"ROLLBACK; -- due to errors\n":"COMMIT;\n", p->out); |
| 29053 | 31616 | } |
| 29054 | | - p->showHeader = savedShowHeader; |
| 29055 | 31617 | p->shellFlgs = savedShellFlags; |
| 31618 | + modeFree(&p->mode); |
| 31619 | + p->mode = saved_mode; |
| 31620 | + rc = p->nErr>0; |
| 29056 | 31621 | }else |
| 29057 | 31622 | |
| 29058 | 31623 | if( c=='e' && cli_strncmp(azArg[0], "echo", n)==0 ){ |
| 29059 | 31624 | if( nArg==2 ){ |
| 29060 | | - setOrClearFlag(p, SHFLG_Echo, azArg[1]); |
| 31625 | + if( booleanValue(azArg[1]) ){ |
| 31626 | + p->mode.mFlags |= MFLG_ECHO; |
| 31627 | + }else{ |
| 31628 | + p->mode.mFlags &= ~MFLG_ECHO; |
| 31629 | + } |
| 29061 | 31630 | }else{ |
| 29062 | 31631 | eputz("Usage: .echo on|off\n"); |
| 29063 | 31632 | rc = 1; |
| 29064 | 31633 | } |
| 29065 | 31634 | }else |
| | @@ -29069,74 +31638,58 @@ |
| 29069 | 31638 | rc = shell_dbtotxt_command(p, nArg, azArg); |
| 29070 | 31639 | }else |
| 29071 | 31640 | |
| 29072 | 31641 | if( c=='e' && cli_strncmp(azArg[0], "eqp", n)==0 ){ |
| 29073 | 31642 | if( nArg==2 ){ |
| 29074 | | - p->autoEQPtest = 0; |
| 29075 | | - if( p->autoEQPtrace ){ |
| 31643 | + if( p->mode.autoEQPtrace ){ |
| 29076 | 31644 | if( p->db ) sqlite3_exec(p->db, "PRAGMA vdbe_trace=OFF;", 0, 0, 0); |
| 29077 | | - p->autoEQPtrace = 0; |
| 31645 | + p->mode.autoEQPtrace = 0; |
| 29078 | 31646 | } |
| 29079 | 31647 | if( cli_strcmp(azArg[1],"full")==0 ){ |
| 29080 | | - p->autoEQP = AUTOEQP_full; |
| 31648 | + p->mode.autoEQP = AUTOEQP_full; |
| 29081 | 31649 | }else if( cli_strcmp(azArg[1],"trigger")==0 ){ |
| 29082 | | - p->autoEQP = AUTOEQP_trigger; |
| 31650 | + p->mode.autoEQP = AUTOEQP_trigger; |
| 29083 | 31651 | #ifdef SQLITE_DEBUG |
| 29084 | | - }else if( cli_strcmp(azArg[1],"test")==0 ){ |
| 29085 | | - p->autoEQP = AUTOEQP_on; |
| 29086 | | - p->autoEQPtest = 1; |
| 29087 | 31652 | }else if( cli_strcmp(azArg[1],"trace")==0 ){ |
| 29088 | | - p->autoEQP = AUTOEQP_full; |
| 29089 | | - p->autoEQPtrace = 1; |
| 31653 | + p->mode.autoEQP = AUTOEQP_full; |
| 31654 | + p->mode.autoEQPtrace = 1; |
| 29090 | 31655 | open_db(p, 0); |
| 29091 | 31656 | sqlite3_exec(p->db, "SELECT name FROM sqlite_schema LIMIT 1", 0, 0, 0); |
| 29092 | 31657 | sqlite3_exec(p->db, "PRAGMA vdbe_trace=ON;", 0, 0, 0); |
| 29093 | 31658 | #endif |
| 29094 | 31659 | }else{ |
| 29095 | | - p->autoEQP = (u8)booleanValue(azArg[1]); |
| 31660 | + p->mode.autoEQP = (u8)booleanValue(azArg[1]); |
| 29096 | 31661 | } |
| 29097 | 31662 | }else{ |
| 29098 | 31663 | eputz("Usage: .eqp off|on|trace|trigger|full\n"); |
| 29099 | 31664 | rc = 1; |
| 29100 | 31665 | } |
| 29101 | 31666 | }else |
| 29102 | 31667 | |
| 29103 | 31668 | #ifndef SQLITE_SHELL_FIDDLE |
| 29104 | 31669 | if( c=='e' && cli_strncmp(azArg[0], "exit", n)==0 ){ |
| 29105 | | - if( nArg>1 && (rc = (int)integerValue(azArg[1]))!=0 ) exit(rc); |
| 31670 | + if( nArg>1 && (rc = (int)integerValue(azArg[1]))!=0 ) cli_exit(rc); |
| 29106 | 31671 | rc = 2; |
| 29107 | 31672 | }else |
| 29108 | 31673 | #endif |
| 29109 | 31674 | |
| 29110 | 31675 | /* The ".explain" command is automatic now. It is largely pointless. It |
| 29111 | 31676 | ** retained purely for backwards compatibility */ |
| 29112 | 31677 | if( c=='e' && cli_strncmp(azArg[0], "explain", n)==0 ){ |
| 29113 | | - int val = 1; |
| 29114 | 31678 | if( nArg>=2 ){ |
| 29115 | 31679 | if( cli_strcmp(azArg[1],"auto")==0 ){ |
| 29116 | | - val = 99; |
| 31680 | + p->mode.autoExplain = 1; |
| 29117 | 31681 | }else{ |
| 29118 | | - val = booleanValue(azArg[1]); |
| 29119 | | - } |
| 29120 | | - } |
| 29121 | | - if( val==1 && p->mode!=MODE_Explain ){ |
| 29122 | | - p->normalMode = p->mode; |
| 29123 | | - p->mode = MODE_Explain; |
| 29124 | | - p->autoExplain = 0; |
| 29125 | | - }else if( val==0 ){ |
| 29126 | | - if( p->mode==MODE_Explain ) p->mode = p->normalMode; |
| 29127 | | - p->autoExplain = 0; |
| 29128 | | - }else if( val==99 ){ |
| 29129 | | - if( p->mode==MODE_Explain ) p->mode = p->normalMode; |
| 29130 | | - p->autoExplain = 1; |
| 31682 | + p->mode.autoExplain = booleanValue(azArg[1]); |
| 31683 | + } |
| 29131 | 31684 | } |
| 29132 | 31685 | }else |
| 29133 | 31686 | |
| 29134 | 31687 | #if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_AUTHORIZATION) |
| 29135 | 31688 | if( c=='e' && cli_strncmp(azArg[0], "expert", n)==0 ){ |
| 29136 | 31689 | if( p->bSafeMode ){ |
| 29137 | | - sqlite3_fprintf(stderr, |
| 31690 | + cli_printf(stderr, |
| 29138 | 31691 | "Cannot run experimental commands such as \"%s\" in safe mode\n", |
| 29139 | 31692 | azArg[0]); |
| 29140 | 31693 | rc = 1; |
| 29141 | 31694 | }else{ |
| 29142 | 31695 | open_db(p, 0); |
| | @@ -29190,13 +31743,13 @@ |
| 29190 | 31743 | if( zCmd[0]=='-' && zCmd[1] ) zCmd++; |
| 29191 | 31744 | } |
| 29192 | 31745 | |
| 29193 | 31746 | /* --help lists all file-controls */ |
| 29194 | 31747 | if( cli_strcmp(zCmd,"help")==0 ){ |
| 29195 | | - sqlite3_fputs("Available file-controls:\n", p->out); |
| 31748 | + cli_puts("Available file-controls:\n", p->out); |
| 29196 | 31749 | for(i=0; i<ArraySize(aCtrl); i++){ |
| 29197 | | - sqlite3_fprintf(p->out, |
| 31750 | + cli_printf(p->out, |
| 29198 | 31751 | " .filectrl %s %s\n", aCtrl[i].zCtrlName, aCtrl[i].zUsage); |
| 29199 | 31752 | } |
| 29200 | 31753 | rc = 1; |
| 29201 | 31754 | goto meta_command_exit; |
| 29202 | 31755 | } |
| | @@ -29208,19 +31761,19 @@ |
| 29208 | 31761 | if( cli_strncmp(zCmd, aCtrl[i].zCtrlName, n2)==0 ){ |
| 29209 | 31762 | if( filectrl<0 ){ |
| 29210 | 31763 | filectrl = aCtrl[i].ctrlCode; |
| 29211 | 31764 | iCtrl = i; |
| 29212 | 31765 | }else{ |
| 29213 | | - sqlite3_fprintf(stderr,"Error: ambiguous file-control: \"%s\"\n" |
| 31766 | + cli_printf(stderr,"Error: ambiguous file-control: \"%s\"\n" |
| 29214 | 31767 | "Use \".filectrl --help\" for help\n", zCmd); |
| 29215 | 31768 | rc = 1; |
| 29216 | 31769 | goto meta_command_exit; |
| 29217 | 31770 | } |
| 29218 | 31771 | } |
| 29219 | 31772 | } |
| 29220 | 31773 | if( filectrl<0 ){ |
| 29221 | | - sqlite3_fprintf(stderr,"Error: unknown file-control: %s\n" |
| 31774 | + cli_printf(stderr,"Error: unknown file-control: %s\n" |
| 29222 | 31775 | "Use \".filectrl --help\" for help\n", zCmd); |
| 29223 | 31776 | }else{ |
| 29224 | 31777 | switch(filectrl){ |
| 29225 | 31778 | case SQLITE_FCNTL_SIZE_LIMIT: { |
| 29226 | 31779 | if( nArg!=2 && nArg!=3 ) break; |
| | @@ -29260,11 +31813,11 @@ |
| 29260 | 31813 | case SQLITE_FCNTL_TEMPFILENAME: { |
| 29261 | 31814 | char *z = 0; |
| 29262 | 31815 | if( nArg!=2 ) break; |
| 29263 | 31816 | sqlite3_file_control(p->db, zSchema, filectrl, &z); |
| 29264 | 31817 | if( z ){ |
| 29265 | | - sqlite3_fprintf(p->out, "%s\n", z); |
| 31818 | + cli_printf(p->out, "%s\n", z); |
| 29266 | 31819 | sqlite3_free(z); |
| 29267 | 31820 | } |
| 29268 | 31821 | isOk = 2; |
| 29269 | 31822 | break; |
| 29270 | 31823 | } |
| | @@ -29274,81 +31827,96 @@ |
| 29274 | 31827 | x = atoi(azArg[2]); |
| 29275 | 31828 | sqlite3_file_control(p->db, zSchema, filectrl, &x); |
| 29276 | 31829 | } |
| 29277 | 31830 | x = -1; |
| 29278 | 31831 | sqlite3_file_control(p->db, zSchema, filectrl, &x); |
| 29279 | | - sqlite3_fprintf(p->out, "%d\n", x); |
| 31832 | + cli_printf(p->out, "%d\n", x); |
| 29280 | 31833 | isOk = 2; |
| 29281 | 31834 | break; |
| 29282 | 31835 | } |
| 29283 | 31836 | } |
| 29284 | 31837 | } |
| 29285 | 31838 | if( isOk==0 && iCtrl>=0 ){ |
| 29286 | | - sqlite3_fprintf(p->out, "Usage: .filectrl %s %s\n", |
| 31839 | + cli_printf(p->out, "Usage: .filectrl %s %s\n", |
| 29287 | 31840 | zCmd, aCtrl[iCtrl].zUsage); |
| 29288 | 31841 | rc = 1; |
| 29289 | 31842 | }else if( isOk==1 ){ |
| 29290 | 31843 | char zBuf[100]; |
| 29291 | 31844 | sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", iRes); |
| 29292 | | - sqlite3_fprintf(p->out, "%s\n", zBuf); |
| 31845 | + cli_printf(p->out, "%s\n", zBuf); |
| 29293 | 31846 | } |
| 29294 | 31847 | }else |
| 29295 | 31848 | |
| 29296 | 31849 | if( c=='f' && cli_strncmp(azArg[0], "fullschema", n)==0 ){ |
| 29297 | 31850 | ShellState data; |
| 29298 | 31851 | int doStats = 0; |
| 29299 | | - memcpy(&data, p, sizeof(data)); |
| 29300 | | - data.showHeader = 0; |
| 29301 | | - data.cMode = data.mode = MODE_Semi; |
| 31852 | + int hasStat[5]; |
| 31853 | + int flgs = 0; |
| 31854 | + char *zSql; |
| 29302 | 31855 | if( nArg==2 && optionMatch(azArg[1], "indent") ){ |
| 29303 | | - data.cMode = data.mode = MODE_Pretty; |
| 29304 | 31856 | nArg = 1; |
| 29305 | 31857 | } |
| 29306 | 31858 | if( nArg!=1 ){ |
| 29307 | 31859 | eputz("Usage: .fullschema ?--indent?\n"); |
| 29308 | 31860 | rc = 1; |
| 29309 | 31861 | goto meta_command_exit; |
| 29310 | 31862 | } |
| 29311 | 31863 | open_db(p, 0); |
| 29312 | | - rc = sqlite3_exec(p->db, |
| 29313 | | - "SELECT sql FROM" |
| 31864 | + zSql = sqlite3_mprintf( |
| 31865 | + "SELECT shell_format_schema(sql,%d) FROM" |
| 29314 | 31866 | " (SELECT sql sql, type type, tbl_name tbl_name, name name, rowid x" |
| 29315 | 31867 | " FROM sqlite_schema UNION ALL" |
| 29316 | 31868 | " SELECT sql, type, tbl_name, name, rowid FROM sqlite_temp_schema) " |
| 29317 | 31869 | "WHERE type!='meta' AND sql NOTNULL" |
| 29318 | | - " AND name NOT LIKE 'sqlite__%' ESCAPE '_' " |
| 29319 | | - "ORDER BY x", |
| 29320 | | - callback, &data, 0 |
| 29321 | | - ); |
| 31870 | + " AND name NOT LIKE 'sqlite__%%' ESCAPE '_' " |
| 31871 | + "ORDER BY x", flgs); |
| 31872 | + memcpy(&data, p, sizeof(data)); |
| 31873 | + data.mode.spec.bTitles = QRF_No; |
| 31874 | + data.mode.eMode = MODE_List; |
| 31875 | + data.mode.spec.eText = QRF_TEXT_Plain; |
| 31876 | + data.mode.spec.nCharLimit = 0; |
| 31877 | + data.mode.spec.zRowSep = "\n"; |
| 31878 | + rc = shell_exec(&data,zSql,0); |
| 31879 | + sqlite3_free(zSql); |
| 29322 | 31880 | if( rc==SQLITE_OK ){ |
| 31881 | + memset(hasStat, 0, sizeof(hasStat)); |
| 29323 | 31882 | sqlite3_stmt *pStmt; |
| 29324 | 31883 | rc = sqlite3_prepare_v2(p->db, |
| 29325 | | - "SELECT rowid FROM sqlite_schema" |
| 31884 | + "SELECT substr(name,12,1) FROM sqlite_schema" |
| 29326 | 31885 | " WHERE name GLOB 'sqlite_stat[134]'", |
| 29327 | 31886 | -1, &pStmt, 0); |
| 29328 | 31887 | if( rc==SQLITE_OK ){ |
| 29329 | | - doStats = sqlite3_step(pStmt)==SQLITE_ROW; |
| 29330 | | - sqlite3_finalize(pStmt); |
| 31888 | + while( sqlite3_step(pStmt)==SQLITE_ROW ){ |
| 31889 | + int k = sqlite3_column_int(pStmt,0); |
| 31890 | + assert( k==1 || k==3 || k==4 ); |
| 31891 | + hasStat[k] = 1; |
| 31892 | + doStats = 1; |
| 31893 | + } |
| 29331 | 31894 | } |
| 31895 | + sqlite3_finalize(pStmt); |
| 29332 | 31896 | } |
| 29333 | 31897 | if( doStats==0 ){ |
| 29334 | | - sqlite3_fputs("/* No STAT tables available */\n", p->out); |
| 31898 | + cli_puts("/* No STAT tables available */\n", p->out); |
| 29335 | 31899 | }else{ |
| 29336 | | - sqlite3_fputs("ANALYZE sqlite_schema;\n", p->out); |
| 29337 | | - data.cMode = data.mode = MODE_Insert; |
| 29338 | | - data.zDestTable = "sqlite_stat1"; |
| 29339 | | - shell_exec(&data, "SELECT * FROM sqlite_stat1", 0); |
| 29340 | | - data.zDestTable = "sqlite_stat4"; |
| 29341 | | - shell_exec(&data, "SELECT * FROM sqlite_stat4", 0); |
| 29342 | | - sqlite3_fputs("ANALYZE sqlite_schema;\n", p->out); |
| 31900 | + cli_puts("ANALYZE sqlite_schema;\n", p->out); |
| 31901 | + data.mode.eMode = MODE_Insert; |
| 31902 | + if( hasStat[1] ){ |
| 31903 | + data.mode.spec.zTableName = "sqlite_stat1"; |
| 31904 | + shell_exec(&data, "SELECT * FROM sqlite_stat1", 0); |
| 31905 | + } |
| 31906 | + if( hasStat[4] ){ |
| 31907 | + data.mode.spec.zTableName = "sqlite_stat4"; |
| 31908 | + shell_exec(&data, "SELECT * FROM sqlite_stat4", 0); |
| 31909 | + } |
| 31910 | + cli_puts("ANALYZE sqlite_schema;\n", p->out); |
| 29343 | 31911 | } |
| 29344 | 31912 | }else |
| 29345 | 31913 | |
| 29346 | 31914 | if( c=='h' && cli_strncmp(azArg[0], "headers", n)==0 ){ |
| 29347 | 31915 | if( nArg==2 ){ |
| 29348 | | - p->showHeader = booleanValue(azArg[1]); |
| 29349 | | - p->shellFlgs |= SHFLG_HeaderSet; |
| 31916 | + p->mode.spec.bTitles = booleanValue(azArg[1]) ? QRF_Yes : QRF_No; |
| 31917 | + p->mode.spec.eTitle = aModeInfo[p->mode.eMode].eHdr; |
| 29350 | 31918 | }else{ |
| 29351 | 31919 | eputz("Usage: .headers on|off\n"); |
| 29352 | 31920 | rc = 1; |
| 29353 | 31921 | } |
| 29354 | 31922 | }else |
| | @@ -29355,340 +31923,20 @@ |
| 29355 | 31923 | |
| 29356 | 31924 | if( c=='h' && cli_strncmp(azArg[0], "help", n)==0 ){ |
| 29357 | 31925 | if( nArg>=2 ){ |
| 29358 | 31926 | n = showHelp(p->out, azArg[1]); |
| 29359 | 31927 | if( n==0 ){ |
| 29360 | | - sqlite3_fprintf(p->out, "Nothing matches '%s'\n", azArg[1]); |
| 31928 | + cli_printf(p->out, "Nothing matches '%s'\n", azArg[1]); |
| 29361 | 31929 | } |
| 29362 | 31930 | }else{ |
| 29363 | 31931 | showHelp(p->out, 0); |
| 29364 | 31932 | } |
| 29365 | 31933 | }else |
| 29366 | 31934 | |
| 29367 | 31935 | #ifndef SQLITE_SHELL_FIDDLE |
| 29368 | 31936 | if( c=='i' && cli_strncmp(azArg[0], "import", n)==0 ){ |
| 29369 | | - char *zTable = 0; /* Insert data into this table */ |
| 29370 | | - char *zSchema = 0; /* Schema of zTable */ |
| 29371 | | - char *zFile = 0; /* Name of file to extra content from */ |
| 29372 | | - sqlite3_stmt *pStmt = NULL; /* A statement */ |
| 29373 | | - int nCol; /* Number of columns in the table */ |
| 29374 | | - i64 nByte; /* Number of bytes in an SQL string */ |
| 29375 | | - int i, j; /* Loop counters */ |
| 29376 | | - int needCommit; /* True to COMMIT or ROLLBACK at end */ |
| 29377 | | - int nSep; /* Number of bytes in p->colSeparator[] */ |
| 29378 | | - char *zSql = 0; /* An SQL statement */ |
| 29379 | | - ImportCtx sCtx; /* Reader context */ |
| 29380 | | - char *(SQLITE_CDECL *xRead)(ImportCtx*); /* Func to read one value */ |
| 29381 | | - int eVerbose = 0; /* Larger for more console output */ |
| 29382 | | - i64 nSkip = 0; /* Initial lines to skip */ |
| 29383 | | - int useOutputMode = 1; /* Use output mode to determine separators */ |
| 29384 | | - char *zCreate = 0; /* CREATE TABLE statement text */ |
| 29385 | | - |
| 29386 | | - failIfSafeMode(p, "cannot run .import in safe mode"); |
| 29387 | | - memset(&sCtx, 0, sizeof(sCtx)); |
| 29388 | | - if( p->mode==MODE_Ascii ){ |
| 29389 | | - xRead = ascii_read_one_field; |
| 29390 | | - }else{ |
| 29391 | | - xRead = csv_read_one_field; |
| 29392 | | - } |
| 29393 | | - rc = 1; |
| 29394 | | - for(i=1; i<nArg; i++){ |
| 29395 | | - char *z = azArg[i]; |
| 29396 | | - if( z[0]=='-' && z[1]=='-' ) z++; |
| 29397 | | - if( z[0]!='-' ){ |
| 29398 | | - if( zFile==0 ){ |
| 29399 | | - zFile = z; |
| 29400 | | - }else if( zTable==0 ){ |
| 29401 | | - zTable = z; |
| 29402 | | - }else{ |
| 29403 | | - sqlite3_fprintf(p->out, "ERROR: extra argument: \"%s\". Usage:\n",z); |
| 29404 | | - showHelp(p->out, "import"); |
| 29405 | | - goto meta_command_exit; |
| 29406 | | - } |
| 29407 | | - }else if( cli_strcmp(z,"-v")==0 ){ |
| 29408 | | - eVerbose++; |
| 29409 | | - }else if( cli_strcmp(z,"-schema")==0 && i<nArg-1 ){ |
| 29410 | | - zSchema = azArg[++i]; |
| 29411 | | - }else if( cli_strcmp(z,"-skip")==0 && i<nArg-1 ){ |
| 29412 | | - nSkip = integerValue(azArg[++i]); |
| 29413 | | - }else if( cli_strcmp(z,"-ascii")==0 ){ |
| 29414 | | - sCtx.cColSep = SEP_Unit[0]; |
| 29415 | | - sCtx.cRowSep = SEP_Record[0]; |
| 29416 | | - xRead = ascii_read_one_field; |
| 29417 | | - useOutputMode = 0; |
| 29418 | | - }else if( cli_strcmp(z,"-csv")==0 ){ |
| 29419 | | - sCtx.cColSep = ','; |
| 29420 | | - sCtx.cRowSep = '\n'; |
| 29421 | | - xRead = csv_read_one_field; |
| 29422 | | - useOutputMode = 0; |
| 29423 | | - }else{ |
| 29424 | | - sqlite3_fprintf(p->out, "ERROR: unknown option: \"%s\". Usage:\n", z); |
| 29425 | | - showHelp(p->out, "import"); |
| 29426 | | - goto meta_command_exit; |
| 29427 | | - } |
| 29428 | | - } |
| 29429 | | - if( zTable==0 ){ |
| 29430 | | - sqlite3_fprintf(p->out, "ERROR: missing %s argument. Usage:\n", |
| 29431 | | - zFile==0 ? "FILE" : "TABLE"); |
| 29432 | | - showHelp(p->out, "import"); |
| 29433 | | - goto meta_command_exit; |
| 29434 | | - } |
| 29435 | | - seenInterrupt = 0; |
| 29436 | | - open_db(p, 0); |
| 29437 | | - if( useOutputMode ){ |
| 29438 | | - /* If neither the --csv or --ascii options are specified, then set |
| 29439 | | - ** the column and row separator characters from the output mode. */ |
| 29440 | | - nSep = strlen30(p->colSeparator); |
| 29441 | | - if( nSep==0 ){ |
| 29442 | | - eputz("Error: non-null column separator required for import\n"); |
| 29443 | | - goto meta_command_exit; |
| 29444 | | - } |
| 29445 | | - if( nSep>1 ){ |
| 29446 | | - eputz("Error: multi-character column separators not allowed" |
| 29447 | | - " for import\n"); |
| 29448 | | - goto meta_command_exit; |
| 29449 | | - } |
| 29450 | | - nSep = strlen30(p->rowSeparator); |
| 29451 | | - if( nSep==0 ){ |
| 29452 | | - eputz("Error: non-null row separator required for import\n"); |
| 29453 | | - goto meta_command_exit; |
| 29454 | | - } |
| 29455 | | - if( nSep==2 && p->mode==MODE_Csv |
| 29456 | | - && cli_strcmp(p->rowSeparator,SEP_CrLf)==0 |
| 29457 | | - ){ |
| 29458 | | - /* When importing CSV (only), if the row separator is set to the |
| 29459 | | - ** default output row separator, change it to the default input |
| 29460 | | - ** row separator. This avoids having to maintain different input |
| 29461 | | - ** and output row separators. */ |
| 29462 | | - sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Row); |
| 29463 | | - nSep = strlen30(p->rowSeparator); |
| 29464 | | - } |
| 29465 | | - if( nSep>1 ){ |
| 29466 | | - eputz("Error: multi-character row separators not allowed" |
| 29467 | | - " for import\n"); |
| 29468 | | - goto meta_command_exit; |
| 29469 | | - } |
| 29470 | | - sCtx.cColSep = (u8)p->colSeparator[0]; |
| 29471 | | - sCtx.cRowSep = (u8)p->rowSeparator[0]; |
| 29472 | | - } |
| 29473 | | - sCtx.zFile = zFile; |
| 29474 | | - sCtx.nLine = 1; |
| 29475 | | - if( sCtx.zFile[0]=='|' ){ |
| 29476 | | -#ifdef SQLITE_OMIT_POPEN |
| 29477 | | - eputz("Error: pipes are not supported in this OS\n"); |
| 29478 | | - goto meta_command_exit; |
| 29479 | | -#else |
| 29480 | | - sCtx.in = sqlite3_popen(sCtx.zFile+1, "r"); |
| 29481 | | - sCtx.zFile = "<pipe>"; |
| 29482 | | - sCtx.xCloser = pclose; |
| 29483 | | -#endif |
| 29484 | | - }else{ |
| 29485 | | - sCtx.in = sqlite3_fopen(sCtx.zFile, "rb"); |
| 29486 | | - sCtx.xCloser = fclose; |
| 29487 | | - } |
| 29488 | | - if( sCtx.in==0 ){ |
| 29489 | | - sqlite3_fprintf(stderr,"Error: cannot open \"%s\"\n", zFile); |
| 29490 | | - goto meta_command_exit; |
| 29491 | | - } |
| 29492 | | - if( eVerbose>=2 || (eVerbose>=1 && useOutputMode) ){ |
| 29493 | | - char zSep[2]; |
| 29494 | | - zSep[1] = 0; |
| 29495 | | - zSep[0] = sCtx.cColSep; |
| 29496 | | - sqlite3_fputs("Column separator ", p->out); |
| 29497 | | - output_c_string(p->out, zSep); |
| 29498 | | - sqlite3_fputs(", row separator ", p->out); |
| 29499 | | - zSep[0] = sCtx.cRowSep; |
| 29500 | | - output_c_string(p->out, zSep); |
| 29501 | | - sqlite3_fputs("\n", p->out); |
| 29502 | | - } |
| 29503 | | - sCtx.z = sqlite3_malloc64(120); |
| 29504 | | - if( sCtx.z==0 ){ |
| 29505 | | - import_cleanup(&sCtx); |
| 29506 | | - shell_out_of_memory(); |
| 29507 | | - } |
| 29508 | | - /* Below, resources must be freed before exit. */ |
| 29509 | | - while( nSkip>0 ){ |
| 29510 | | - nSkip--; |
| 29511 | | - while( xRead(&sCtx) && sCtx.cTerm==sCtx.cColSep ){} |
| 29512 | | - } |
| 29513 | | - import_append_char(&sCtx, 0); /* To ensure sCtx.z is allocated */ |
| 29514 | | - if( sqlite3_table_column_metadata(p->db, zSchema, zTable,0,0,0,0,0,0) |
| 29515 | | - && 0==db_int(p->db, "SELECT count(*) FROM \"%w\".sqlite_schema" |
| 29516 | | - " WHERE name=%Q AND type='view'", |
| 29517 | | - zSchema ? zSchema : "main", zTable) |
| 29518 | | - ){ |
| 29519 | | - /* Table does not exist. Create it. */ |
| 29520 | | - sqlite3 *dbCols = 0; |
| 29521 | | - char *zRenames = 0; |
| 29522 | | - char *zColDefs; |
| 29523 | | - zCreate = sqlite3_mprintf("CREATE TABLE \"%w\".\"%w\"", |
| 29524 | | - zSchema ? zSchema : "main", zTable); |
| 29525 | | - while( xRead(&sCtx) ){ |
| 29526 | | - zAutoColumn(sCtx.z, &dbCols, 0); |
| 29527 | | - if( sCtx.cTerm!=sCtx.cColSep ) break; |
| 29528 | | - } |
| 29529 | | - zColDefs = zAutoColumn(0, &dbCols, &zRenames); |
| 29530 | | - if( zRenames!=0 ){ |
| 29531 | | - sqlite3_fprintf((stdin_is_interactive && p->in==stdin)? p->out : stderr, |
| 29532 | | - "Columns renamed during .import %s due to duplicates:\n" |
| 29533 | | - "%s\n", sCtx.zFile, zRenames); |
| 29534 | | - sqlite3_free(zRenames); |
| 29535 | | - } |
| 29536 | | - assert(dbCols==0); |
| 29537 | | - if( zColDefs==0 ){ |
| 29538 | | - sqlite3_fprintf(stderr,"%s: empty file\n", sCtx.zFile); |
| 29539 | | - import_cleanup(&sCtx); |
| 29540 | | - rc = 1; |
| 29541 | | - sqlite3_free(zCreate); |
| 29542 | | - goto meta_command_exit; |
| 29543 | | - } |
| 29544 | | - zCreate = sqlite3_mprintf("%z%z\n", zCreate, zColDefs); |
| 29545 | | - if( zCreate==0 ){ |
| 29546 | | - import_cleanup(&sCtx); |
| 29547 | | - shell_out_of_memory(); |
| 29548 | | - } |
| 29549 | | - if( eVerbose>=1 ){ |
| 29550 | | - sqlite3_fprintf(p->out, "%s\n", zCreate); |
| 29551 | | - } |
| 29552 | | - rc = sqlite3_exec(p->db, zCreate, 0, 0, 0); |
| 29553 | | - if( rc ){ |
| 29554 | | - sqlite3_fprintf(stderr, |
| 29555 | | - "%s failed:\n%s\n", zCreate, sqlite3_errmsg(p->db)); |
| 29556 | | - } |
| 29557 | | - sqlite3_free(zCreate); |
| 29558 | | - zCreate = 0; |
| 29559 | | - if( rc ){ |
| 29560 | | - import_cleanup(&sCtx); |
| 29561 | | - rc = 1; |
| 29562 | | - goto meta_command_exit; |
| 29563 | | - } |
| 29564 | | - } |
| 29565 | | - zSql = sqlite3_mprintf("SELECT count(*) FROM pragma_table_info(%Q,%Q);", |
| 29566 | | - zTable, zSchema); |
| 29567 | | - if( zSql==0 ){ |
| 29568 | | - import_cleanup(&sCtx); |
| 29569 | | - shell_out_of_memory(); |
| 29570 | | - } |
| 29571 | | - rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); |
| 29572 | | - sqlite3_free(zSql); |
| 29573 | | - zSql = 0; |
| 29574 | | - if( rc ){ |
| 29575 | | - if (pStmt) sqlite3_finalize(pStmt); |
| 29576 | | - shellDatabaseError(p->db); |
| 29577 | | - import_cleanup(&sCtx); |
| 29578 | | - rc = 1; |
| 29579 | | - goto meta_command_exit; |
| 29580 | | - } |
| 29581 | | - if( sqlite3_step(pStmt)==SQLITE_ROW ){ |
| 29582 | | - nCol = sqlite3_column_int(pStmt, 0); |
| 29583 | | - }else{ |
| 29584 | | - nCol = 0; |
| 29585 | | - } |
| 29586 | | - sqlite3_finalize(pStmt); |
| 29587 | | - pStmt = 0; |
| 29588 | | - if( nCol==0 ) return 0; /* no columns, no error */ |
| 29589 | | - |
| 29590 | | - nByte = 64 /* space for "INSERT INTO", "VALUES(", ")\0" */ |
| 29591 | | - + (zSchema ? strlen(zSchema)*2 + 2: 0) /* Quoted schema name */ |
| 29592 | | - + strlen(zTable)*2 + 2 /* Quoted table name */ |
| 29593 | | - + nCol*2; /* Space for ",?" for each column */ |
| 29594 | | - zSql = sqlite3_malloc64( nByte ); |
| 29595 | | - if( zSql==0 ){ |
| 29596 | | - import_cleanup(&sCtx); |
| 29597 | | - shell_out_of_memory(); |
| 29598 | | - } |
| 29599 | | - if( zSchema ){ |
| 29600 | | - sqlite3_snprintf(nByte, zSql, "INSERT INTO \"%w\".\"%w\" VALUES(?", |
| 29601 | | - zSchema, zTable); |
| 29602 | | - }else{ |
| 29603 | | - sqlite3_snprintf(nByte, zSql, "INSERT INTO \"%w\" VALUES(?", zTable); |
| 29604 | | - } |
| 29605 | | - j = strlen30(zSql); |
| 29606 | | - for(i=1; i<nCol; i++){ |
| 29607 | | - zSql[j++] = ','; |
| 29608 | | - zSql[j++] = '?'; |
| 29609 | | - } |
| 29610 | | - zSql[j++] = ')'; |
| 29611 | | - zSql[j] = 0; |
| 29612 | | - assert( j<nByte ); |
| 29613 | | - if( eVerbose>=2 ){ |
| 29614 | | - sqlite3_fprintf(p->out, "Insert using: %s\n", zSql); |
| 29615 | | - } |
| 29616 | | - rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); |
| 29617 | | - sqlite3_free(zSql); |
| 29618 | | - zSql = 0; |
| 29619 | | - if( rc ){ |
| 29620 | | - shellDatabaseError(p->db); |
| 29621 | | - if (pStmt) sqlite3_finalize(pStmt); |
| 29622 | | - import_cleanup(&sCtx); |
| 29623 | | - rc = 1; |
| 29624 | | - goto meta_command_exit; |
| 29625 | | - } |
| 29626 | | - needCommit = sqlite3_get_autocommit(p->db); |
| 29627 | | - if( needCommit ) sqlite3_exec(p->db, "BEGIN", 0, 0, 0); |
| 29628 | | - do{ |
| 29629 | | - int startLine = sCtx.nLine; |
| 29630 | | - for(i=0; i<nCol; i++){ |
| 29631 | | - char *z = xRead(&sCtx); |
| 29632 | | - /* |
| 29633 | | - ** Did we reach end-of-file before finding any columns? |
| 29634 | | - ** If so, stop instead of NULL filling the remaining columns. |
| 29635 | | - */ |
| 29636 | | - if( z==0 && i==0 ) break; |
| 29637 | | - /* |
| 29638 | | - ** Did we reach end-of-file OR end-of-line before finding any |
| 29639 | | - ** columns in ASCII mode? If so, stop instead of NULL filling |
| 29640 | | - ** the remaining columns. |
| 29641 | | - */ |
| 29642 | | - if( p->mode==MODE_Ascii && (z==0 || z[0]==0) && i==0 ) break; |
| 29643 | | - /* |
| 29644 | | - ** For CSV mode, per RFC 4180, accept EOF in lieu of final |
| 29645 | | - ** record terminator but only for last field of multi-field row. |
| 29646 | | - ** (If there are too few fields, it's not valid CSV anyway.) |
| 29647 | | - */ |
| 29648 | | - if( z==0 && (xRead==csv_read_one_field) && i==nCol-1 && i>0 ){ |
| 29649 | | - z = ""; |
| 29650 | | - } |
| 29651 | | - sqlite3_bind_text(pStmt, i+1, z, -1, SQLITE_TRANSIENT); |
| 29652 | | - if( i<nCol-1 && sCtx.cTerm!=sCtx.cColSep ){ |
| 29653 | | - sqlite3_fprintf(stderr,"%s:%d: expected %d columns but found %d" |
| 29654 | | - " - filling the rest with NULL\n", |
| 29655 | | - sCtx.zFile, startLine, nCol, i+1); |
| 29656 | | - i += 2; |
| 29657 | | - while( i<=nCol ){ sqlite3_bind_null(pStmt, i); i++; } |
| 29658 | | - } |
| 29659 | | - } |
| 29660 | | - if( sCtx.cTerm==sCtx.cColSep ){ |
| 29661 | | - do{ |
| 29662 | | - xRead(&sCtx); |
| 29663 | | - i++; |
| 29664 | | - }while( sCtx.cTerm==sCtx.cColSep ); |
| 29665 | | - sqlite3_fprintf(stderr, |
| 29666 | | - "%s:%d: expected %d columns but found %d - extras ignored\n", |
| 29667 | | - sCtx.zFile, startLine, nCol, i); |
| 29668 | | - } |
| 29669 | | - if( i>=nCol ){ |
| 29670 | | - sqlite3_step(pStmt); |
| 29671 | | - rc = sqlite3_reset(pStmt); |
| 29672 | | - if( rc!=SQLITE_OK ){ |
| 29673 | | - sqlite3_fprintf(stderr,"%s:%d: INSERT failed: %s\n", |
| 29674 | | - sCtx.zFile, startLine, sqlite3_errmsg(p->db)); |
| 29675 | | - sCtx.nErr++; |
| 29676 | | - }else{ |
| 29677 | | - sCtx.nRow++; |
| 29678 | | - } |
| 29679 | | - } |
| 29680 | | - }while( sCtx.cTerm!=EOF ); |
| 29681 | | - |
| 29682 | | - import_cleanup(&sCtx); |
| 29683 | | - sqlite3_finalize(pStmt); |
| 29684 | | - if( needCommit ) sqlite3_exec(p->db, "COMMIT", 0, 0, 0); |
| 29685 | | - if( eVerbose>0 ){ |
| 29686 | | - sqlite3_fprintf(p->out, |
| 29687 | | - "Added %d rows with %d errors using %d lines of input\n", |
| 29688 | | - sCtx.nRow, sCtx.nErr, sCtx.nLine-1); |
| 29689 | | - } |
| 31937 | + rc = dotCmdImport(p); |
| 29690 | 31938 | }else |
| 29691 | 31939 | #endif /* !defined(SQLITE_SHELL_FIDDLE) */ |
| 29692 | 31940 | |
| 29693 | 31941 | #ifndef SQLITE_UNTESTABLE |
| 29694 | 31942 | if( c=='i' && cli_strncmp(azArg[0], "imposter", n)==0 ){ |
| | @@ -29758,11 +32006,11 @@ |
| 29758 | 32006 | zCollist = sqlite3_mprintf("%z,\"%w\"", zCollist, zCol); |
| 29759 | 32007 | } |
| 29760 | 32008 | } |
| 29761 | 32009 | sqlite3_finalize(pStmt); |
| 29762 | 32010 | if( i==0 || tnum==0 ){ |
| 29763 | | - sqlite3_fprintf(stderr,"no such index: \"%s\"\n", azArg[1]); |
| 32011 | + cli_printf(stderr,"no such index: \"%s\"\n", azArg[1]); |
| 29764 | 32012 | rc = 1; |
| 29765 | 32013 | sqlite3_free(zCollist); |
| 29766 | 32014 | goto meta_command_exit; |
| 29767 | 32015 | } |
| 29768 | 32016 | if( lenPK==0 ) lenPK = 100000; |
| | @@ -29773,17 +32021,17 @@ |
| 29773 | 32021 | rc = sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, p->db, "main", 2, tnum); |
| 29774 | 32022 | if( rc==SQLITE_OK ){ |
| 29775 | 32023 | rc = sqlite3_exec(p->db, zSql, 0, 0, 0); |
| 29776 | 32024 | sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, p->db, "main", 0, 0); |
| 29777 | 32025 | if( rc ){ |
| 29778 | | - sqlite3_fprintf(stderr, |
| 32026 | + cli_printf(stderr, |
| 29779 | 32027 | "Error in [%s]: %s\n", zSql, sqlite3_errmsg(p->db)); |
| 29780 | 32028 | }else{ |
| 29781 | | - sqlite3_fprintf(stdout, "%s;\n", zSql); |
| 32029 | + cli_printf(stdout, "%s;\n", zSql); |
| 29782 | 32030 | } |
| 29783 | 32031 | }else{ |
| 29784 | | - sqlite3_fprintf(stderr,"SQLITE_TESTCTRL_IMPOSTER returns %d\n", rc); |
| 32032 | + cli_printf(stderr,"SQLITE_TESTCTRL_IMPOSTER returns %d\n", rc); |
| 29785 | 32033 | rc = 1; |
| 29786 | 32034 | } |
| 29787 | 32035 | sqlite3_free(zSql); |
| 29788 | 32036 | }else |
| 29789 | 32037 | #endif /* !defined(SQLITE_OMIT_TEST_CONTROL) */ |
| | @@ -29793,11 +32041,11 @@ |
| 29793 | 32041 | if( nArg==2 ){ |
| 29794 | 32042 | iArg = integerValue(azArg[1]); |
| 29795 | 32043 | if( iArg==0 ) iArg = -1; |
| 29796 | 32044 | } |
| 29797 | 32045 | if( (nArg!=1 && nArg!=2) || iArg<0 ){ |
| 29798 | | - sqlite3_fprintf(stderr,"%s","Usage: .intck STEPS_PER_UNLOCK\n"); |
| 32046 | + cli_printf(stderr,"%s","Usage: .intck STEPS_PER_UNLOCK\n"); |
| 29799 | 32047 | rc = 1; |
| 29800 | 32048 | goto meta_command_exit; |
| 29801 | 32049 | } |
| 29802 | 32050 | open_db(p, 0); |
| 29803 | 32051 | rc = intckDatabaseCmd(p, iArg); |
| | @@ -29814,11 +32062,11 @@ |
| 29814 | 32062 | sqlite3IoTrace = iotracePrintf; |
| 29815 | 32063 | iotrace = stdout; |
| 29816 | 32064 | }else{ |
| 29817 | 32065 | iotrace = sqlite3_fopen(azArg[1], "w"); |
| 29818 | 32066 | if( iotrace==0 ){ |
| 29819 | | - sqlite3_fprintf(stderr,"Error: cannot open \"%s\"\n", azArg[1]); |
| 32067 | + cli_printf(stderr,"Error: cannot open \"%s\"\n", azArg[1]); |
| 29820 | 32068 | sqlite3IoTrace = 0; |
| 29821 | 32069 | rc = 1; |
| 29822 | 32070 | }else{ |
| 29823 | 32071 | sqlite3IoTrace = iotracePrintf; |
| 29824 | 32072 | } |
| | @@ -29833,10 +32081,11 @@ |
| 29833 | 32081 | } aLimit[] = { |
| 29834 | 32082 | { "length", SQLITE_LIMIT_LENGTH }, |
| 29835 | 32083 | { "sql_length", SQLITE_LIMIT_SQL_LENGTH }, |
| 29836 | 32084 | { "column", SQLITE_LIMIT_COLUMN }, |
| 29837 | 32085 | { "expr_depth", SQLITE_LIMIT_EXPR_DEPTH }, |
| 32086 | + { "parser_depth", SQLITE_LIMIT_PARSER_DEPTH }, |
| 29838 | 32087 | { "compound_select", SQLITE_LIMIT_COMPOUND_SELECT }, |
| 29839 | 32088 | { "vdbe_op", SQLITE_LIMIT_VDBE_OP }, |
| 29840 | 32089 | { "function_arg", SQLITE_LIMIT_FUNCTION_ARG }, |
| 29841 | 32090 | { "attached", SQLITE_LIMIT_ATTACHED }, |
| 29842 | 32091 | { "like_pattern_length", SQLITE_LIMIT_LIKE_PATTERN_LENGTH }, |
| | @@ -29846,11 +32095,11 @@ |
| 29846 | 32095 | }; |
| 29847 | 32096 | int i, n2; |
| 29848 | 32097 | open_db(p, 0); |
| 29849 | 32098 | if( nArg==1 ){ |
| 29850 | 32099 | for(i=0; i<ArraySize(aLimit); i++){ |
| 29851 | | - sqlite3_fprintf(stdout, "%20s %d\n", aLimit[i].zLimitName, |
| 32100 | + cli_printf(stdout, "%20s %d\n", aLimit[i].zLimitName, |
| 29852 | 32101 | sqlite3_limit(p->db, aLimit[i].limitCode, -1)); |
| 29853 | 32102 | } |
| 29854 | 32103 | }else if( nArg>3 ){ |
| 29855 | 32104 | eputz("Usage: .limit NAME ?NEW-VALUE?\n"); |
| 29856 | 32105 | rc = 1; |
| | @@ -29861,28 +32110,28 @@ |
| 29861 | 32110 | for(i=0; i<ArraySize(aLimit); i++){ |
| 29862 | 32111 | if( sqlite3_strnicmp(aLimit[i].zLimitName, azArg[1], n2)==0 ){ |
| 29863 | 32112 | if( iLimit<0 ){ |
| 29864 | 32113 | iLimit = i; |
| 29865 | 32114 | }else{ |
| 29866 | | - sqlite3_fprintf(stderr,"ambiguous limit: \"%s\"\n", azArg[1]); |
| 32115 | + cli_printf(stderr,"ambiguous limit: \"%s\"\n", azArg[1]); |
| 29867 | 32116 | rc = 1; |
| 29868 | 32117 | goto meta_command_exit; |
| 29869 | 32118 | } |
| 29870 | 32119 | } |
| 29871 | 32120 | } |
| 29872 | 32121 | if( iLimit<0 ){ |
| 29873 | | - sqlite3_fprintf(stderr,"unknown limit: \"%s\"\n" |
| 32122 | + cli_printf(stderr,"unknown limit: \"%s\"\n" |
| 29874 | 32123 | "enter \".limits\" with no arguments for a list.\n", |
| 29875 | 32124 | azArg[1]); |
| 29876 | 32125 | rc = 1; |
| 29877 | 32126 | goto meta_command_exit; |
| 29878 | 32127 | } |
| 29879 | 32128 | if( nArg==3 ){ |
| 29880 | 32129 | sqlite3_limit(p->db, aLimit[iLimit].limitCode, |
| 29881 | 32130 | (int)integerValue(azArg[2])); |
| 29882 | 32131 | } |
| 29883 | | - sqlite3_fprintf(stdout, "%20s %d\n", aLimit[iLimit].zLimitName, |
| 32132 | + cli_printf(stdout, "%20s %d\n", aLimit[iLimit].zLimitName, |
| 29884 | 32133 | sqlite3_limit(p->db, aLimit[iLimit].limitCode, -1)); |
| 29885 | 32134 | } |
| 29886 | 32135 | }else |
| 29887 | 32136 | |
| 29888 | 32137 | if( c=='l' && n>2 && cli_strncmp(azArg[0], "lint", n)==0 ){ |
| | @@ -29927,189 +32176,27 @@ |
| 29927 | 32176 | " than \"on\" or \"off\"\n"); |
| 29928 | 32177 | zFile = "off"; |
| 29929 | 32178 | } |
| 29930 | 32179 | output_file_close(p->pLog); |
| 29931 | 32180 | if( cli_strcmp(zFile,"on")==0 ) zFile = "stdout"; |
| 29932 | | - p->pLog = output_file_open(zFile); |
| 32181 | + p->pLog = output_file_open(p, zFile); |
| 29933 | 32182 | } |
| 29934 | 32183 | }else |
| 29935 | 32184 | |
| 29936 | 32185 | if( c=='m' && cli_strncmp(azArg[0], "mode", n)==0 ){ |
| 29937 | | - const char *zMode = 0; |
| 29938 | | - const char *zTabname = 0; |
| 29939 | | - int i, n2; |
| 29940 | | - int chng = 0; /* 0x01: change to cmopts. 0x02: Any other change */ |
| 29941 | | - ColModeOpts cmOpts = ColModeOpts_default; |
| 29942 | | - for(i=1; i<nArg; i++){ |
| 29943 | | - const char *z = azArg[i]; |
| 29944 | | - if( optionMatch(z,"wrap") && i+1<nArg ){ |
| 29945 | | - cmOpts.iWrap = integerValue(azArg[++i]); |
| 29946 | | - chng |= 1; |
| 29947 | | - }else if( optionMatch(z,"ww") ){ |
| 29948 | | - cmOpts.bWordWrap = 1; |
| 29949 | | - chng |= 1; |
| 29950 | | - }else if( optionMatch(z,"wordwrap") && i+1<nArg ){ |
| 29951 | | - cmOpts.bWordWrap = (u8)booleanValue(azArg[++i]); |
| 29952 | | - chng |= 1; |
| 29953 | | - }else if( optionMatch(z,"quote") ){ |
| 29954 | | - cmOpts.bQuote = 1; |
| 29955 | | - chng |= 1; |
| 29956 | | - }else if( optionMatch(z,"noquote") ){ |
| 29957 | | - cmOpts.bQuote = 0; |
| 29958 | | - chng |= 1; |
| 29959 | | - }else if( optionMatch(z,"escape") && i+1<nArg ){ |
| 29960 | | - /* See similar code at tag-20250224-1 */ |
| 29961 | | - const char *zEsc = azArg[++i]; |
| 29962 | | - int k; |
| 29963 | | - for(k=0; k<ArraySize(shell_EscModeNames); k++){ |
| 29964 | | - if( sqlite3_stricmp(zEsc,shell_EscModeNames[k])==0 ){ |
| 29965 | | - p->eEscMode = k; |
| 29966 | | - chng |= 2; |
| 29967 | | - break; |
| 29968 | | - } |
| 29969 | | - } |
| 29970 | | - if( k>=ArraySize(shell_EscModeNames) ){ |
| 29971 | | - sqlite3_fprintf(stderr, "unknown control character escape mode \"%s\"" |
| 29972 | | - " - choices:", zEsc); |
| 29973 | | - for(k=0; k<ArraySize(shell_EscModeNames); k++){ |
| 29974 | | - sqlite3_fprintf(stderr, " %s", shell_EscModeNames[k]); |
| 29975 | | - } |
| 29976 | | - sqlite3_fprintf(stderr, "\n"); |
| 29977 | | - rc = 1; |
| 29978 | | - goto meta_command_exit; |
| 29979 | | - } |
| 29980 | | - }else if( zMode==0 ){ |
| 29981 | | - zMode = z; |
| 29982 | | - /* Apply defaults for qbox pseudo-mode. If that |
| 29983 | | - * overwrites already-set values, user was informed of this. |
| 29984 | | - */ |
| 29985 | | - chng |= 1; |
| 29986 | | - if( cli_strcmp(z, "qbox")==0 ){ |
| 29987 | | - ColModeOpts cmo = ColModeOpts_default_qbox; |
| 29988 | | - zMode = "box"; |
| 29989 | | - cmOpts = cmo; |
| 29990 | | - } |
| 29991 | | - }else if( zTabname==0 ){ |
| 29992 | | - zTabname = z; |
| 29993 | | - }else if( z[0]=='-' ){ |
| 29994 | | - sqlite3_fprintf(stderr,"unknown option: %s\n", z); |
| 29995 | | - eputz("options:\n" |
| 29996 | | - " --escape MODE\n" |
| 29997 | | - " --noquote\n" |
| 29998 | | - " --quote\n" |
| 29999 | | - " --wordwrap on/off\n" |
| 30000 | | - " --wrap N\n" |
| 30001 | | - " --ww\n"); |
| 30002 | | - rc = 1; |
| 30003 | | - goto meta_command_exit; |
| 30004 | | - }else{ |
| 30005 | | - sqlite3_fprintf(stderr,"extra argument: \"%s\"\n", z); |
| 30006 | | - rc = 1; |
| 30007 | | - goto meta_command_exit; |
| 30008 | | - } |
| 30009 | | - } |
| 30010 | | - if( !chng ){ |
| 30011 | | - if( p->mode==MODE_Column |
| 30012 | | - || (p->mode>=MODE_Markdown && p->mode<=MODE_Box) |
| 30013 | | - ){ |
| 30014 | | - sqlite3_fprintf(p->out, |
| 30015 | | - "current output mode: %s --wrap %d --wordwrap %s " |
| 30016 | | - "--%squote --escape %s\n", |
| 30017 | | - modeDescr[p->mode], p->cmOpts.iWrap, |
| 30018 | | - p->cmOpts.bWordWrap ? "on" : "off", |
| 30019 | | - p->cmOpts.bQuote ? "" : "no", |
| 30020 | | - shell_EscModeNames[p->eEscMode] |
| 30021 | | - ); |
| 30022 | | - }else{ |
| 30023 | | - sqlite3_fprintf(p->out, |
| 30024 | | - "current output mode: %s --escape %s\n", |
| 30025 | | - modeDescr[p->mode], |
| 30026 | | - shell_EscModeNames[p->eEscMode] |
| 30027 | | - ); |
| 30028 | | - } |
| 30029 | | - } |
| 30030 | | - if( zMode==0 ){ |
| 30031 | | - zMode = modeDescr[p->mode]; |
| 30032 | | - if( (chng&1)==0 ) cmOpts = p->cmOpts; |
| 30033 | | - } |
| 30034 | | - n2 = strlen30(zMode); |
| 30035 | | - if( cli_strncmp(zMode,"lines",n2)==0 ){ |
| 30036 | | - p->mode = MODE_Line; |
| 30037 | | - sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Row); |
| 30038 | | - }else if( cli_strncmp(zMode,"columns",n2)==0 ){ |
| 30039 | | - p->mode = MODE_Column; |
| 30040 | | - if( (p->shellFlgs & SHFLG_HeaderSet)==0 ){ |
| 30041 | | - p->showHeader = 1; |
| 30042 | | - } |
| 30043 | | - sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Row); |
| 30044 | | - p->cmOpts = cmOpts; |
| 30045 | | - }else if( cli_strncmp(zMode,"list",n2)==0 ){ |
| 30046 | | - p->mode = MODE_List; |
| 30047 | | - sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Column); |
| 30048 | | - sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Row); |
| 30049 | | - }else if( cli_strncmp(zMode,"html",n2)==0 ){ |
| 30050 | | - p->mode = MODE_Html; |
| 30051 | | - }else if( cli_strncmp(zMode,"tcl",n2)==0 ){ |
| 30052 | | - p->mode = MODE_Tcl; |
| 30053 | | - sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Space); |
| 30054 | | - sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Row); |
| 30055 | | - }else if( cli_strncmp(zMode,"csv",n2)==0 ){ |
| 30056 | | - p->mode = MODE_Csv; |
| 30057 | | - sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Comma); |
| 30058 | | - sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_CrLf); |
| 30059 | | - }else if( cli_strncmp(zMode,"tabs",n2)==0 ){ |
| 30060 | | - p->mode = MODE_List; |
| 30061 | | - sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Tab); |
| 30062 | | - }else if( cli_strncmp(zMode,"insert",n2)==0 ){ |
| 30063 | | - p->mode = MODE_Insert; |
| 30064 | | - set_table_name(p, zTabname ? zTabname : "table"); |
| 30065 | | - if( p->eEscMode==SHELL_ESC_OFF ){ |
| 30066 | | - ShellSetFlag(p, SHFLG_Newlines); |
| 30067 | | - }else{ |
| 30068 | | - ShellClearFlag(p, SHFLG_Newlines); |
| 30069 | | - } |
| 30070 | | - }else if( cli_strncmp(zMode,"quote",n2)==0 ){ |
| 30071 | | - p->mode = MODE_Quote; |
| 30072 | | - sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Comma); |
| 30073 | | - sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Row); |
| 30074 | | - }else if( cli_strncmp(zMode,"ascii",n2)==0 ){ |
| 30075 | | - p->mode = MODE_Ascii; |
| 30076 | | - sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Unit); |
| 30077 | | - sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Record); |
| 30078 | | - }else if( cli_strncmp(zMode,"markdown",n2)==0 ){ |
| 30079 | | - p->mode = MODE_Markdown; |
| 30080 | | - p->cmOpts = cmOpts; |
| 30081 | | - }else if( cli_strncmp(zMode,"table",n2)==0 ){ |
| 30082 | | - p->mode = MODE_Table; |
| 30083 | | - p->cmOpts = cmOpts; |
| 30084 | | - }else if( cli_strncmp(zMode,"box",n2)==0 ){ |
| 30085 | | - p->mode = MODE_Box; |
| 30086 | | - p->cmOpts = cmOpts; |
| 30087 | | - }else if( cli_strncmp(zMode,"count",n2)==0 ){ |
| 30088 | | - p->mode = MODE_Count; |
| 30089 | | - }else if( cli_strncmp(zMode,"off",n2)==0 ){ |
| 30090 | | - p->mode = MODE_Off; |
| 30091 | | - }else if( cli_strncmp(zMode,"json",n2)==0 ){ |
| 30092 | | - p->mode = MODE_Json; |
| 30093 | | - }else{ |
| 30094 | | - eputz("Error: mode should be one of: " |
| 30095 | | - "ascii box column csv html insert json line list markdown " |
| 30096 | | - "qbox quote table tabs tcl\n"); |
| 30097 | | - rc = 1; |
| 30098 | | - } |
| 30099 | | - p->cMode = p->mode; |
| 32186 | + rc = dotCmdMode(p); |
| 30100 | 32187 | }else |
| 30101 | 32188 | |
| 30102 | 32189 | #ifndef SQLITE_SHELL_FIDDLE |
| 30103 | 32190 | if( c=='n' && cli_strcmp(azArg[0], "nonce")==0 ){ |
| 30104 | 32191 | if( nArg!=2 ){ |
| 30105 | 32192 | eputz("Usage: .nonce NONCE\n"); |
| 30106 | 32193 | rc = 1; |
| 30107 | 32194 | }else if( p->zNonce==0 || cli_strcmp(azArg[1],p->zNonce)!=0 ){ |
| 30108 | | - sqlite3_fprintf(stderr,"line %lld: incorrect nonce: \"%s\"\n", |
| 32195 | + cli_printf(stderr,"line %lld: incorrect nonce: \"%s\"\n", |
| 30109 | 32196 | p->lineno, azArg[1]); |
| 30110 | | - exit(1); |
| 32197 | + cli_exit(1); |
| 30111 | 32198 | }else{ |
| 30112 | 32199 | p->bSafeMode = 0; |
| 30113 | 32200 | return 0; /* Return immediately to bypass the safe mode reset |
| 30114 | 32201 | ** at the end of this procedure */ |
| 30115 | 32202 | } |
| | @@ -30116,12 +32203,11 @@ |
| 30116 | 32203 | }else |
| 30117 | 32204 | #endif /* !defined(SQLITE_SHELL_FIDDLE) */ |
| 30118 | 32205 | |
| 30119 | 32206 | if( c=='n' && cli_strncmp(azArg[0], "nullvalue", n)==0 ){ |
| 30120 | 32207 | if( nArg==2 ){ |
| 30121 | | - sqlite3_snprintf(sizeof(p->nullValue), p->nullValue, |
| 30122 | | - "%.*s", (int)ArraySize(p->nullValue)-1, azArg[1]); |
| 32208 | + modeSetStr(&p->mode.spec.zNull, azArg[1]); |
| 30123 | 32209 | }else{ |
| 30124 | 32210 | eputz("Usage: .nullvalue STRING\n"); |
| 30125 | 32211 | rc = 1; |
| 30126 | 32212 | } |
| 30127 | 32213 | }else |
| | @@ -30166,15 +32252,15 @@ |
| 30166 | 32252 | p->szMax = integerValue(azArg[++iName]); |
| 30167 | 32253 | #endif /* SQLITE_OMIT_DESERIALIZE */ |
| 30168 | 32254 | }else |
| 30169 | 32255 | #endif /* !SQLITE_SHELL_FIDDLE */ |
| 30170 | 32256 | if( z[0]=='-' ){ |
| 30171 | | - sqlite3_fprintf(stderr,"unknown option: %s\n", z); |
| 32257 | + cli_printf(stderr,"unknown option: %s\n", z); |
| 30172 | 32258 | rc = 1; |
| 30173 | 32259 | goto meta_command_exit; |
| 30174 | 32260 | }else if( zFN ){ |
| 30175 | | - sqlite3_fprintf(stderr,"extra argument: \"%s\"\n", z); |
| 32261 | + cli_printf(stderr,"extra argument: \"%s\"\n", z); |
| 30176 | 32262 | rc = 1; |
| 30177 | 32263 | goto meta_command_exit; |
| 30178 | 32264 | }else{ |
| 30179 | 32265 | zFN = z; |
| 30180 | 32266 | } |
| | @@ -30221,11 +32307,11 @@ |
| 30221 | 32307 | zNewFilename = 0; |
| 30222 | 32308 | } |
| 30223 | 32309 | p->pAuxDb->zDbFilename = zNewFilename; |
| 30224 | 32310 | open_db(p, OPEN_DB_KEEPALIVE); |
| 30225 | 32311 | if( p->db==0 ){ |
| 30226 | | - sqlite3_fprintf(stderr,"Error: cannot open '%s'\n", zNewFilename); |
| 32312 | + cli_printf(stderr,"Error: cannot open '%s'\n", zNewFilename); |
| 30227 | 32313 | sqlite3_free(zNewFilename); |
| 30228 | 32314 | }else{ |
| 30229 | 32315 | p->pAuxDb->zFreeOnClose = zNewFilename; |
| 30230 | 32316 | } |
| 30231 | 32317 | } |
| | @@ -30241,149 +32327,11 @@ |
| 30241 | 32327 | && (cli_strncmp(azArg[0], "output", n)==0 |
| 30242 | 32328 | || cli_strncmp(azArg[0], "once", n)==0)) |
| 30243 | 32329 | || (c=='e' && n==5 && cli_strcmp(azArg[0],"excel")==0) |
| 30244 | 32330 | || (c=='w' && n==3 && cli_strcmp(azArg[0],"www")==0) |
| 30245 | 32331 | ){ |
| 30246 | | - char *zFile = 0; |
| 30247 | | - int i; |
| 30248 | | - int eMode = 0; /* 0: .outout/.once, 'x'=.excel, 'w'=.www */ |
| 30249 | | - int bOnce = 0; /* 0: .output, 1: .once, 2: .excel/.www */ |
| 30250 | | - int bPlain = 0; /* --plain option */ |
| 30251 | | - static const char *zBomUtf8 = "\357\273\277"; |
| 30252 | | - const char *zBom = 0; |
| 30253 | | - |
| 30254 | | - failIfSafeMode(p, "cannot run .%s in safe mode", azArg[0]); |
| 30255 | | - if( c=='e' ){ |
| 30256 | | - eMode = 'x'; |
| 30257 | | - bOnce = 2; |
| 30258 | | - }else if( c=='w' ){ |
| 30259 | | - eMode = 'w'; |
| 30260 | | - bOnce = 2; |
| 30261 | | - }else if( cli_strncmp(azArg[0],"once",n)==0 ){ |
| 30262 | | - bOnce = 1; |
| 30263 | | - } |
| 30264 | | - for(i=1; i<nArg; i++){ |
| 30265 | | - char *z = azArg[i]; |
| 30266 | | - if( z[0]=='-' ){ |
| 30267 | | - if( z[1]=='-' ) z++; |
| 30268 | | - if( cli_strcmp(z,"-bom")==0 ){ |
| 30269 | | - zBom = zBomUtf8; |
| 30270 | | - }else if( cli_strcmp(z,"-plain")==0 ){ |
| 30271 | | - bPlain = 1; |
| 30272 | | - }else if( c=='o' && cli_strcmp(z,"-x")==0 ){ |
| 30273 | | - eMode = 'x'; /* spreadsheet */ |
| 30274 | | - }else if( c=='o' && cli_strcmp(z,"-e")==0 ){ |
| 30275 | | - eMode = 'e'; /* text editor */ |
| 30276 | | - }else if( c=='o' && cli_strcmp(z,"-w")==0 ){ |
| 30277 | | - eMode = 'w'; /* Web browser */ |
| 30278 | | - }else{ |
| 30279 | | - sqlite3_fprintf(p->out, |
| 30280 | | - "ERROR: unknown option: \"%s\". Usage:\n", azArg[i]); |
| 30281 | | - showHelp(p->out, azArg[0]); |
| 30282 | | - rc = 1; |
| 30283 | | - sqlite3_free(zFile); |
| 30284 | | - goto meta_command_exit; |
| 30285 | | - } |
| 30286 | | - }else if( zFile==0 && eMode==0 ){ |
| 30287 | | - if( cli_strcmp(z, "off")==0 ){ |
| 30288 | | -#ifdef _WIN32 |
| 30289 | | - zFile = sqlite3_mprintf("nul"); |
| 30290 | | -#else |
| 30291 | | - zFile = sqlite3_mprintf("/dev/null"); |
| 30292 | | -#endif |
| 30293 | | - }else{ |
| 30294 | | - zFile = sqlite3_mprintf("%s", z); |
| 30295 | | - } |
| 30296 | | - if( zFile && zFile[0]=='|' ){ |
| 30297 | | - while( i+1<nArg ) zFile = sqlite3_mprintf("%z %s", zFile, azArg[++i]); |
| 30298 | | - break; |
| 30299 | | - } |
| 30300 | | - }else{ |
| 30301 | | - sqlite3_fprintf(p->out, |
| 30302 | | - "ERROR: extra parameter: \"%s\". Usage:\n", azArg[i]); |
| 30303 | | - showHelp(p->out, azArg[0]); |
| 30304 | | - rc = 1; |
| 30305 | | - sqlite3_free(zFile); |
| 30306 | | - goto meta_command_exit; |
| 30307 | | - } |
| 30308 | | - } |
| 30309 | | - if( zFile==0 ){ |
| 30310 | | - zFile = sqlite3_mprintf("stdout"); |
| 30311 | | - } |
| 30312 | | - shell_check_oom(zFile); |
| 30313 | | - if( bOnce ){ |
| 30314 | | - p->outCount = 2; |
| 30315 | | - }else{ |
| 30316 | | - p->outCount = 0; |
| 30317 | | - } |
| 30318 | | - output_reset(p); |
| 30319 | | -#ifndef SQLITE_NOHAVE_SYSTEM |
| 30320 | | - if( eMode=='e' || eMode=='x' || eMode=='w' ){ |
| 30321 | | - p->doXdgOpen = 1; |
| 30322 | | - outputModePush(p); |
| 30323 | | - if( eMode=='x' ){ |
| 30324 | | - /* spreadsheet mode. Output as CSV. */ |
| 30325 | | - newTempFile(p, "csv"); |
| 30326 | | - ShellClearFlag(p, SHFLG_Echo); |
| 30327 | | - p->mode = MODE_Csv; |
| 30328 | | - sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Comma); |
| 30329 | | - sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_CrLf); |
| 30330 | | -#ifdef _WIN32 |
| 30331 | | - zBom = zBomUtf8; /* Always include the BOM on Windows, as Excel does |
| 30332 | | - ** not work without it. */ |
| 30333 | | -#endif |
| 30334 | | - }else if( eMode=='w' ){ |
| 30335 | | - /* web-browser mode. */ |
| 30336 | | - newTempFile(p, "html"); |
| 30337 | | - if( !bPlain ) p->mode = MODE_Www; |
| 30338 | | - }else{ |
| 30339 | | - /* text editor mode */ |
| 30340 | | - newTempFile(p, "txt"); |
| 30341 | | - } |
| 30342 | | - sqlite3_free(zFile); |
| 30343 | | - zFile = sqlite3_mprintf("%s", p->zTempFile); |
| 30344 | | - } |
| 30345 | | -#endif /* SQLITE_NOHAVE_SYSTEM */ |
| 30346 | | - shell_check_oom(zFile); |
| 30347 | | - if( zFile[0]=='|' ){ |
| 30348 | | -#ifdef SQLITE_OMIT_POPEN |
| 30349 | | - eputz("Error: pipes are not supported in this OS\n"); |
| 30350 | | - rc = 1; |
| 30351 | | - output_redir(p, stdout); |
| 30352 | | -#else |
| 30353 | | - FILE *pfPipe = sqlite3_popen(zFile + 1, "w"); |
| 30354 | | - if( pfPipe==0 ){ |
| 30355 | | - assert( stderr!=NULL ); |
| 30356 | | - sqlite3_fprintf(stderr,"Error: cannot open pipe \"%s\"\n", zFile + 1); |
| 30357 | | - rc = 1; |
| 30358 | | - }else{ |
| 30359 | | - output_redir(p, pfPipe); |
| 30360 | | - if( zBom ) sqlite3_fputs(zBom, pfPipe); |
| 30361 | | - sqlite3_snprintf(sizeof(p->outfile), p->outfile, "%s", zFile); |
| 30362 | | - } |
| 30363 | | -#endif |
| 30364 | | - }else{ |
| 30365 | | - FILE *pfFile = output_file_open(zFile); |
| 30366 | | - if( pfFile==0 ){ |
| 30367 | | - if( cli_strcmp(zFile,"off")!=0 ){ |
| 30368 | | - assert( stderr!=NULL ); |
| 30369 | | - sqlite3_fprintf(stderr,"Error: cannot write to \"%s\"\n", zFile); |
| 30370 | | - } |
| 30371 | | - rc = 1; |
| 30372 | | - } else { |
| 30373 | | - output_redir(p, pfFile); |
| 30374 | | - if( zBom ) sqlite3_fputs(zBom, pfFile); |
| 30375 | | - if( bPlain && eMode=='w' ){ |
| 30376 | | - sqlite3_fputs( |
| 30377 | | - "<!DOCTYPE html>\n<BODY>\n<PLAINTEXT>\n", |
| 30378 | | - pfFile |
| 30379 | | - ); |
| 30380 | | - } |
| 30381 | | - sqlite3_snprintf(sizeof(p->outfile), p->outfile, "%s", zFile); |
| 30382 | | - } |
| 30383 | | - } |
| 30384 | | - sqlite3_free(zFile); |
| 32332 | + rc = dotCmdOutput(p); |
| 30385 | 32333 | }else |
| 30386 | 32334 | #endif /* !defined(SQLITE_SHELL_FIDDLE) */ |
| 30387 | 32335 | |
| 30388 | 32336 | if( c=='p' && n>=3 && cli_strncmp(azArg[0], "parameter", n)==0 ){ |
| 30389 | 32337 | open_db(p,0); |
| | @@ -30416,11 +32364,11 @@ |
| 30416 | 32364 | if( len ){ |
| 30417 | 32365 | rx = sqlite3_prepare_v2(p->db, |
| 30418 | 32366 | "SELECT key, quote(value) " |
| 30419 | 32367 | "FROM temp.sqlite_parameters;", -1, &pStmt, 0); |
| 30420 | 32368 | while( rx==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){ |
| 30421 | | - sqlite3_fprintf(p->out, |
| 32369 | + cli_printf(p->out, |
| 30422 | 32370 | "%-*s %s\n", len, sqlite3_column_text(pStmt,0), |
| 30423 | 32371 | sqlite3_column_text(pStmt,1)); |
| 30424 | 32372 | } |
| 30425 | 32373 | sqlite3_finalize(pStmt); |
| 30426 | 32374 | } |
| | @@ -30462,11 +32410,11 @@ |
| 30462 | 32410 | "VALUES(%Q,%Q);", zKey, zValue); |
| 30463 | 32411 | shell_check_oom(zSql); |
| 30464 | 32412 | rx = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); |
| 30465 | 32413 | sqlite3_free(zSql); |
| 30466 | 32414 | if( rx!=SQLITE_OK ){ |
| 30467 | | - sqlite3_fprintf(p->out, "Error: %s\n", sqlite3_errmsg(p->db)); |
| 32415 | + cli_printf(p->out, "Error: %s\n", sqlite3_errmsg(p->db)); |
| 30468 | 32416 | sqlite3_finalize(pStmt); |
| 30469 | 32417 | pStmt = 0; |
| 30470 | 32418 | rc = 1; |
| 30471 | 32419 | } |
| 30472 | 32420 | } |
| | @@ -30492,14 +32440,14 @@ |
| 30492 | 32440 | }else |
| 30493 | 32441 | |
| 30494 | 32442 | if( c=='p' && n>=3 && cli_strncmp(azArg[0], "print", n)==0 ){ |
| 30495 | 32443 | int i; |
| 30496 | 32444 | for(i=1; i<nArg; i++){ |
| 30497 | | - if( i>1 ) sqlite3_fputs(" ", p->out); |
| 30498 | | - sqlite3_fputs(azArg[i], p->out); |
| 32445 | + if( i>1 ) cli_puts(" ", p->out); |
| 32446 | + cli_puts(azArg[i], p->out); |
| 30499 | 32447 | } |
| 30500 | | - sqlite3_fputs("\n", p->out); |
| 32448 | + cli_puts("\n", p->out); |
| 30501 | 32449 | }else |
| 30502 | 32450 | |
| 30503 | 32451 | #ifndef SQLITE_OMIT_PROGRESS_CALLBACK |
| 30504 | 32452 | if( c=='p' && n>=3 && cli_strncmp(azArg[0], "progress", n)==0 ){ |
| 30505 | 32453 | int i; |
| | @@ -30532,11 +32480,11 @@ |
| 30532 | 32480 | }else{ |
| 30533 | 32481 | p->mxProgress = (int)integerValue(azArg[++i]); |
| 30534 | 32482 | } |
| 30535 | 32483 | continue; |
| 30536 | 32484 | } |
| 30537 | | - sqlite3_fprintf(stderr,"Error: unknown option: \"%s\"\n", azArg[i]); |
| 32485 | + cli_printf(stderr,"Error: unknown option: \"%s\"\n", azArg[i]); |
| 30538 | 32486 | rc = 1; |
| 30539 | 32487 | goto meta_command_exit; |
| 30540 | 32488 | }else{ |
| 30541 | 32489 | nn = (int)integerValue(z); |
| 30542 | 32490 | } |
| | @@ -30576,22 +32524,24 @@ |
| 30576 | 32524 | eputz("Error: pipes are not supported in this OS\n"); |
| 30577 | 32525 | rc = 1; |
| 30578 | 32526 | #else |
| 30579 | 32527 | p->in = sqlite3_popen(azArg[1]+1, "r"); |
| 30580 | 32528 | if( p->in==0 ){ |
| 30581 | | - sqlite3_fprintf(stderr,"Error: cannot open \"%s\"\n", azArg[1]); |
| 32529 | + cli_printf(stderr,"Error: cannot open \"%s\"\n", azArg[1]); |
| 30582 | 32530 | rc = 1; |
| 30583 | 32531 | }else{ |
| 30584 | 32532 | rc = process_input(p, "<pipe>"); |
| 30585 | 32533 | pclose(p->in); |
| 30586 | 32534 | } |
| 30587 | 32535 | #endif |
| 30588 | 32536 | }else if( (p->in = openChrSource(azArg[1]))==0 ){ |
| 30589 | | - sqlite3_fprintf(stderr,"Error: cannot open \"%s\"\n", azArg[1]); |
| 32537 | + cli_printf(stderr,"Error: cannot open \"%s\"\n", azArg[1]); |
| 30590 | 32538 | rc = 1; |
| 30591 | 32539 | }else{ |
| 30592 | | - rc = process_input(p, azArg[1]); |
| 32540 | + char *zFilename = strdup(azArg[1]); |
| 32541 | + rc = process_input(p, zFilename); |
| 32542 | + free(zFilename); |
| 30593 | 32543 | fclose(p->in); |
| 30594 | 32544 | } |
| 30595 | 32545 | p->in = inSaved; |
| 30596 | 32546 | p->lineno = savedLineno; |
| 30597 | 32547 | }else |
| | @@ -30617,11 +32567,11 @@ |
| 30617 | 32567 | rc = 1; |
| 30618 | 32568 | goto meta_command_exit; |
| 30619 | 32569 | } |
| 30620 | 32570 | rc = sqlite3_open(zSrcFile, &pSrc); |
| 30621 | 32571 | if( rc!=SQLITE_OK ){ |
| 30622 | | - sqlite3_fprintf(stderr,"Error: cannot open \"%s\"\n", zSrcFile); |
| 32572 | + cli_printf(stderr,"Error: cannot open \"%s\"\n", zSrcFile); |
| 30623 | 32573 | close_db(pSrc); |
| 30624 | 32574 | return 1; |
| 30625 | 32575 | } |
| 30626 | 32576 | open_db(p, 0); |
| 30627 | 32577 | pBackup = sqlite3_backup_init(p->db, zDb, pSrc, "main"); |
| | @@ -30655,25 +32605,25 @@ |
| 30655 | 32605 | (cli_strncmp(azArg[0], "scanstats", n)==0 || |
| 30656 | 32606 | cli_strncmp(azArg[0], "scanstatus", n)==0) |
| 30657 | 32607 | ){ |
| 30658 | 32608 | if( nArg==2 ){ |
| 30659 | 32609 | if( cli_strcmp(azArg[1], "vm")==0 ){ |
| 30660 | | - p->scanstatsOn = 3; |
| 32610 | + p->mode.scanstatsOn = 3; |
| 30661 | 32611 | }else |
| 30662 | 32612 | if( cli_strcmp(azArg[1], "est")==0 ){ |
| 30663 | | - p->scanstatsOn = 2; |
| 32613 | + p->mode.scanstatsOn = 2; |
| 30664 | 32614 | }else{ |
| 30665 | | - p->scanstatsOn = (u8)booleanValue(azArg[1]); |
| 32615 | + p->mode.scanstatsOn = (u8)booleanValue(azArg[1]); |
| 30666 | 32616 | } |
| 30667 | 32617 | open_db(p, 0); |
| 30668 | 32618 | sqlite3_db_config( |
| 30669 | | - p->db, SQLITE_DBCONFIG_STMT_SCANSTATUS, p->scanstatsOn, (int*)0 |
| 32619 | + p->db, SQLITE_DBCONFIG_STMT_SCANSTATUS, p->mode.scanstatsOn, (int*)0 |
| 30670 | 32620 | ); |
| 30671 | 32621 | #if !defined(SQLITE_ENABLE_STMT_SCANSTATUS) |
| 30672 | 32622 | eputz("Warning: .scanstats not available in this build.\n"); |
| 30673 | 32623 | #elif !defined(SQLITE_ENABLE_BYTECODE_VTAB) |
| 30674 | | - if( p->scanstatsOn==3 ){ |
| 32624 | + if( p->mode.scanstatsOn==3 ){ |
| 30675 | 32625 | eputz("Warning: \".scanstats vm\" not available in this build.\n"); |
| 30676 | 32626 | } |
| 30677 | 32627 | #endif |
| 30678 | 32628 | }else{ |
| 30679 | 32629 | eputz("Usage: .scanstats on|off|est\n"); |
| | @@ -30680,34 +32630,38 @@ |
| 30680 | 32630 | rc = 1; |
| 30681 | 32631 | } |
| 30682 | 32632 | }else |
| 30683 | 32633 | |
| 30684 | 32634 | if( c=='s' && cli_strncmp(azArg[0], "schema", n)==0 ){ |
| 30685 | | - ShellText sSelect; |
| 30686 | 32635 | ShellState data; |
| 30687 | 32636 | char *zErrMsg = 0; |
| 30688 | 32637 | const char *zDiv = "("; |
| 30689 | 32638 | const char *zName = 0; |
| 30690 | 32639 | int iSchema = 0; |
| 30691 | 32640 | int bDebug = 0; |
| 30692 | 32641 | int bNoSystemTabs = 0; |
| 32642 | + int bIndent = 0; |
| 30693 | 32643 | int ii; |
| 30694 | | - |
| 32644 | + sqlite3_str *pSql; |
| 32645 | + sqlite3_stmt *pStmt = 0; |
| 32646 | + |
| 30695 | 32647 | open_db(p, 0); |
| 30696 | 32648 | memcpy(&data, p, sizeof(data)); |
| 30697 | | - data.showHeader = 0; |
| 30698 | | - data.cMode = data.mode = MODE_Semi; |
| 30699 | | - initText(&sSelect); |
| 32649 | + data.mode.spec.bTitles = QRF_No; |
| 32650 | + data.mode.eMode = MODE_List; |
| 32651 | + data.mode.spec.eText = QRF_TEXT_Plain; |
| 32652 | + data.mode.spec.nCharLimit = 0; |
| 32653 | + data.mode.spec.zRowSep = "\n"; |
| 30700 | 32654 | for(ii=1; ii<nArg; ii++){ |
| 30701 | 32655 | if( optionMatch(azArg[ii],"indent") ){ |
| 30702 | | - data.cMode = data.mode = MODE_Pretty; |
| 32656 | + bIndent = 1; |
| 30703 | 32657 | }else if( optionMatch(azArg[ii],"debug") ){ |
| 30704 | 32658 | bDebug = 1; |
| 30705 | 32659 | }else if( optionMatch(azArg[ii],"nosys") ){ |
| 30706 | 32660 | bNoSystemTabs = 1; |
| 30707 | 32661 | }else if( azArg[ii][0]=='-' ){ |
| 30708 | | - sqlite3_fprintf(stderr,"Unknown option: \"%s\"\n", azArg[ii]); |
| 32662 | + cli_printf(stderr,"Unknown option: \"%s\"\n", azArg[ii]); |
| 30709 | 32663 | rc = 1; |
| 30710 | 32664 | goto meta_command_exit; |
| 30711 | 32665 | }else if( zName==0 ){ |
| 30712 | 32666 | zName = azArg[ii]; |
| 30713 | 32667 | }else{ |
| | @@ -30720,100 +32674,87 @@ |
| 30720 | 32674 | int isSchema = sqlite3_strlike(zName, "sqlite_master", '\\')==0 |
| 30721 | 32675 | || sqlite3_strlike(zName, "sqlite_schema", '\\')==0 |
| 30722 | 32676 | || sqlite3_strlike(zName,"sqlite_temp_master", '\\')==0 |
| 30723 | 32677 | || sqlite3_strlike(zName,"sqlite_temp_schema", '\\')==0; |
| 30724 | 32678 | if( isSchema ){ |
| 30725 | | - char *new_argv[2], *new_colv[2]; |
| 30726 | | - new_argv[0] = sqlite3_mprintf( |
| 32679 | + cli_printf(p->out, |
| 30727 | 32680 | "CREATE TABLE %s (\n" |
| 30728 | 32681 | " type text,\n" |
| 30729 | 32682 | " name text,\n" |
| 30730 | 32683 | " tbl_name text,\n" |
| 30731 | 32684 | " rootpage integer,\n" |
| 30732 | 32685 | " sql text\n" |
| 30733 | | - ")", zName); |
| 30734 | | - shell_check_oom(new_argv[0]); |
| 30735 | | - new_argv[1] = 0; |
| 30736 | | - new_colv[0] = "sql"; |
| 30737 | | - new_colv[1] = 0; |
| 30738 | | - callback(&data, 1, new_argv, new_colv); |
| 30739 | | - sqlite3_free(new_argv[0]); |
| 30740 | | - } |
| 30741 | | - } |
| 30742 | | - if( zDiv ){ |
| 30743 | | - sqlite3_stmt *pStmt = 0; |
| 30744 | | - rc = sqlite3_prepare_v2(p->db, "SELECT name FROM pragma_database_list", |
| 30745 | | - -1, &pStmt, 0); |
| 30746 | | - if( rc ){ |
| 30747 | | - shellDatabaseError(p->db); |
| 30748 | | - sqlite3_finalize(pStmt); |
| 30749 | | - rc = 1; |
| 30750 | | - goto meta_command_exit; |
| 30751 | | - } |
| 30752 | | - appendText(&sSelect, "SELECT sql FROM", 0); |
| 30753 | | - iSchema = 0; |
| 30754 | | - while( sqlite3_step(pStmt)==SQLITE_ROW ){ |
| 30755 | | - const char *zDb = (const char*)sqlite3_column_text(pStmt, 0); |
| 30756 | | - char zScNum[30]; |
| 30757 | | - sqlite3_snprintf(sizeof(zScNum), zScNum, "%d", ++iSchema); |
| 30758 | | - appendText(&sSelect, zDiv, 0); |
| 30759 | | - zDiv = " UNION ALL "; |
| 30760 | | - appendText(&sSelect, "SELECT shell_add_schema(sql,", 0); |
| 30761 | | - if( sqlite3_stricmp(zDb, "main")!=0 ){ |
| 30762 | | - appendText(&sSelect, zDb, '\''); |
| 30763 | | - }else{ |
| 30764 | | - appendText(&sSelect, "NULL", 0); |
| 30765 | | - } |
| 30766 | | - appendText(&sSelect, ",name) AS sql, type, tbl_name, name, rowid,", 0); |
| 30767 | | - appendText(&sSelect, zScNum, 0); |
| 30768 | | - appendText(&sSelect, " AS snum, ", 0); |
| 30769 | | - appendText(&sSelect, zDb, '\''); |
| 30770 | | - appendText(&sSelect, " AS sname FROM ", 0); |
| 30771 | | - appendText(&sSelect, zDb, quoteChar(zDb)); |
| 30772 | | - appendText(&sSelect, ".sqlite_schema", 0); |
| 30773 | | - } |
| 30774 | | - sqlite3_finalize(pStmt); |
| 32686 | + ");\n", zName); |
| 32687 | + } |
| 32688 | + } |
| 32689 | + rc = sqlite3_prepare_v2(p->db, "SELECT name FROM pragma_database_list", |
| 32690 | + -1, &pStmt, 0); |
| 32691 | + if( rc ){ |
| 32692 | + shellDatabaseError(p->db); |
| 32693 | + sqlite3_finalize(pStmt); |
| 32694 | + |
| 32695 | + rc = 1; |
| 32696 | + goto meta_command_exit; |
| 32697 | + } |
| 32698 | + pSql = sqlite3_str_new(p->db); |
| 32699 | + sqlite3_str_appendf(pSql, "SELECT sql FROM", 0); |
| 32700 | + iSchema = 0; |
| 32701 | + while( sqlite3_step(pStmt)==SQLITE_ROW ){ |
| 32702 | + const char *zDb = (const char*)sqlite3_column_text(pStmt, 0); |
| 32703 | + char zScNum[30]; |
| 32704 | + sqlite3_snprintf(sizeof(zScNum), zScNum, "%d", ++iSchema); |
| 32705 | + sqlite3_str_appendall(pSql, zDiv); |
| 32706 | + zDiv = " UNION ALL "; |
| 32707 | + if( sqlite3_stricmp(zDb, "main")==0 ){ |
| 32708 | + sqlite3_str_appendf(pSql, |
| 32709 | + "SELECT shell_format_schema(shell_add_schema(sql,NULL,name),%d)", |
| 32710 | + bIndent); |
| 32711 | + }else{ |
| 32712 | + sqlite3_str_appendf(pSql, |
| 32713 | + "SELECT shell_format_schema(shell_add_schema(sql,%Q,name),%d))", |
| 32714 | + zDb, bIndent); |
| 32715 | + } |
| 32716 | + sqlite3_str_appendf(pSql, |
| 32717 | + " AS sql, type, tbl_name, name, rowid, %d AS snum, %Q as sname", |
| 32718 | + ++iSchema, zDb); |
| 32719 | + sqlite3_str_appendf(pSql," FROM \"%w\".sqlite_schema", zDb); |
| 32720 | + } |
| 32721 | + sqlite3_finalize(pStmt); |
| 30775 | 32722 | #ifndef SQLITE_OMIT_INTROSPECTION_PRAGMAS |
| 30776 | | - if( zName ){ |
| 30777 | | - appendText(&sSelect, |
| 30778 | | - " UNION ALL SELECT shell_module_schema(name)," |
| 30779 | | - " 'table', name, name, name, 9e+99, 'main' FROM pragma_module_list", |
| 30780 | | - 0); |
| 30781 | | - } |
| 30782 | | -#endif |
| 30783 | | - appendText(&sSelect, ") WHERE ", 0); |
| 30784 | | - if( zName ){ |
| 30785 | | - char *zQarg = sqlite3_mprintf("%Q", zName); |
| 30786 | | - int bGlob; |
| 30787 | | - shell_check_oom(zQarg); |
| 30788 | | - bGlob = strchr(zName, '*') != 0 || strchr(zName, '?') != 0 || |
| 30789 | | - strchr(zName, '[') != 0; |
| 30790 | | - if( strchr(zName, '.') ){ |
| 30791 | | - appendText(&sSelect, "lower(printf('%s.%s',sname,tbl_name))", 0); |
| 30792 | | - }else{ |
| 30793 | | - appendText(&sSelect, "lower(tbl_name)", 0); |
| 30794 | | - } |
| 30795 | | - appendText(&sSelect, bGlob ? " GLOB " : " LIKE ", 0); |
| 30796 | | - appendText(&sSelect, zQarg, 0); |
| 30797 | | - if( !bGlob ){ |
| 30798 | | - appendText(&sSelect, " ESCAPE '\\' ", 0); |
| 30799 | | - } |
| 30800 | | - appendText(&sSelect, " AND ", 0); |
| 30801 | | - sqlite3_free(zQarg); |
| 30802 | | - } |
| 30803 | | - if( bNoSystemTabs ){ |
| 30804 | | - appendText(&sSelect, "name NOT LIKE 'sqlite__%%' ESCAPE '_' AND ", 0); |
| 30805 | | - } |
| 30806 | | - appendText(&sSelect, "sql IS NOT NULL" |
| 30807 | | - " ORDER BY snum, rowid", 0); |
| 30808 | | - if( bDebug ){ |
| 30809 | | - sqlite3_fprintf(p->out, "SQL: %s;\n", sSelect.zTxt); |
| 30810 | | - }else{ |
| 30811 | | - rc = sqlite3_exec(p->db, sSelect.zTxt, callback, &data, &zErrMsg); |
| 30812 | | - } |
| 30813 | | - freeText(&sSelect); |
| 30814 | | - } |
| 32723 | + if( zName ){ |
| 32724 | + sqlite3_str_appendall(pSql, |
| 32725 | + " UNION ALL SELECT shell_module_schema(name)," |
| 32726 | + " 'table', name, name, name, 9e+99, 'main' FROM pragma_module_list"); |
| 32727 | + } |
| 32728 | +#endif |
| 32729 | + sqlite3_str_appendf(pSql, ") WHERE ", 0); |
| 32730 | + if( zName ){ |
| 32731 | + int bGlob; |
| 32732 | + bGlob = strchr(zName, '*') != 0 || strchr(zName, '?') != 0 || |
| 32733 | + strchr(zName, '[') != 0; |
| 32734 | + if( strchr(zName, '.') ){ |
| 32735 | + sqlite3_str_appendall(pSql, "lower(format('%%s.%%s',sname,tbl_name))"); |
| 32736 | + }else{ |
| 32737 | + sqlite3_str_appendall(pSql, "lower(tbl_name)"); |
| 32738 | + } |
| 32739 | + if( bGlob ){ |
| 32740 | + sqlite3_str_appendf(pSql, " GLOB %Q AND ", zName); |
| 32741 | + }else{ |
| 32742 | + sqlite3_str_appendf(pSql, " LIKE %Q ESCAPE '\\' AND ", zName); |
| 32743 | + } |
| 32744 | + } |
| 32745 | + if( bNoSystemTabs ){ |
| 32746 | + sqlite3_str_appendf(pSql, " name NOT LIKE 'sqlite__%%' ESCALE '_' AND "); |
| 32747 | + } |
| 32748 | + sqlite3_str_appendf(pSql, "sql IS NOT NULL ORDER BY snum, rowid"); |
| 32749 | + if( bDebug ){ |
| 32750 | + cli_printf(p->out, "SQL: %s;\n", sqlite3_str_value(pSql)); |
| 32751 | + }else{ |
| 32752 | + rc = shell_exec(&data, sqlite3_str_value(pSql), &zErrMsg); |
| 32753 | + } |
| 32754 | + sqlite3_str_free(pSql); |
| 32755 | + |
| 30815 | 32756 | if( zErrMsg ){ |
| 30816 | 32757 | shellEmitError(zErrMsg); |
| 30817 | 32758 | sqlite3_free(zErrMsg); |
| 30818 | 32759 | rc = 1; |
| 30819 | 32760 | }else if( rc != SQLITE_OK ){ |
| | @@ -30865,11 +32806,11 @@ |
| 30865 | 32806 | session_not_open: |
| 30866 | 32807 | eputz("ERROR: No sessions are open\n"); |
| 30867 | 32808 | }else{ |
| 30868 | 32809 | rc = sqlite3session_attach(pSession->p, azCmd[1]); |
| 30869 | 32810 | if( rc ){ |
| 30870 | | - sqlite3_fprintf(stderr, |
| 32811 | + cli_printf(stderr, |
| 30871 | 32812 | "ERROR: sqlite3session_attach() returns %d\n",rc); |
| 30872 | 32813 | rc = 0; |
| 30873 | 32814 | } |
| 30874 | 32815 | } |
| 30875 | 32816 | }else |
| | @@ -30885,11 +32826,11 @@ |
| 30885 | 32826 | failIfSafeMode(p, "cannot run \".session %s\" in safe mode", azCmd[0]); |
| 30886 | 32827 | if( nCmd!=2 ) goto session_syntax_error; |
| 30887 | 32828 | if( pSession->p==0 ) goto session_not_open; |
| 30888 | 32829 | out = sqlite3_fopen(azCmd[1], "wb"); |
| 30889 | 32830 | if( out==0 ){ |
| 30890 | | - sqlite3_fprintf(stderr,"ERROR: cannot open \"%s\" for writing\n", |
| 32831 | + cli_printf(stderr,"ERROR: cannot open \"%s\" for writing\n", |
| 30891 | 32832 | azCmd[1]); |
| 30892 | 32833 | }else{ |
| 30893 | 32834 | int szChng; |
| 30894 | 32835 | void *pChng; |
| 30895 | 32836 | if( azCmd[0][0]=='c' ){ |
| | @@ -30896,16 +32837,16 @@ |
| 30896 | 32837 | rc = sqlite3session_changeset(pSession->p, &szChng, &pChng); |
| 30897 | 32838 | }else{ |
| 30898 | 32839 | rc = sqlite3session_patchset(pSession->p, &szChng, &pChng); |
| 30899 | 32840 | } |
| 30900 | 32841 | if( rc ){ |
| 30901 | | - sqlite3_fprintf(stdout, "Error: error code %d\n", rc); |
| 32842 | + cli_printf(stdout, "Error: error code %d\n", rc); |
| 30902 | 32843 | rc = 0; |
| 30903 | 32844 | } |
| 30904 | 32845 | if( pChng |
| 30905 | 32846 | && fwrite(pChng, szChng, 1, out)!=1 ){ |
| 30906 | | - sqlite3_fprintf(stderr, |
| 32847 | + cli_printf(stderr, |
| 30907 | 32848 | "ERROR: Failed to write entire %d-byte output\n", szChng); |
| 30908 | 32849 | } |
| 30909 | 32850 | sqlite3_free(pChng); |
| 30910 | 32851 | fclose(out); |
| 30911 | 32852 | } |
| | @@ -30929,11 +32870,11 @@ |
| 30929 | 32870 | int ii; |
| 30930 | 32871 | if( nCmd>2 ) goto session_syntax_error; |
| 30931 | 32872 | ii = nCmd==1 ? -1 : booleanValue(azCmd[1]); |
| 30932 | 32873 | if( pAuxDb->nSession ){ |
| 30933 | 32874 | ii = sqlite3session_enable(pSession->p, ii); |
| 30934 | | - sqlite3_fprintf(p->out, |
| 32875 | + cli_printf(p->out, |
| 30935 | 32876 | "session %s enable flag = %d\n", pSession->zName, ii); |
| 30936 | 32877 | } |
| 30937 | 32878 | }else |
| 30938 | 32879 | |
| 30939 | 32880 | /* .session filter GLOB .... |
| | @@ -30966,11 +32907,11 @@ |
| 30966 | 32907 | int ii; |
| 30967 | 32908 | if( nCmd>2 ) goto session_syntax_error; |
| 30968 | 32909 | ii = nCmd==1 ? -1 : booleanValue(azCmd[1]); |
| 30969 | 32910 | if( pAuxDb->nSession ){ |
| 30970 | 32911 | ii = sqlite3session_indirect(pSession->p, ii); |
| 30971 | | - sqlite3_fprintf(p->out, |
| 32912 | + cli_printf(p->out, |
| 30972 | 32913 | "session %s indirect flag = %d\n", pSession->zName, ii); |
| 30973 | 32914 | } |
| 30974 | 32915 | }else |
| 30975 | 32916 | |
| 30976 | 32917 | /* .session isempty |
| | @@ -30979,21 +32920,21 @@ |
| 30979 | 32920 | if( cli_strcmp(azCmd[0], "isempty")==0 ){ |
| 30980 | 32921 | int ii; |
| 30981 | 32922 | if( nCmd!=1 ) goto session_syntax_error; |
| 30982 | 32923 | if( pAuxDb->nSession ){ |
| 30983 | 32924 | ii = sqlite3session_isempty(pSession->p); |
| 30984 | | - sqlite3_fprintf(p->out, |
| 32925 | + cli_printf(p->out, |
| 30985 | 32926 | "session %s isempty flag = %d\n", pSession->zName, ii); |
| 30986 | 32927 | } |
| 30987 | 32928 | }else |
| 30988 | 32929 | |
| 30989 | 32930 | /* .session list |
| 30990 | 32931 | ** List all currently open sessions |
| 30991 | 32932 | */ |
| 30992 | 32933 | if( cli_strcmp(azCmd[0],"list")==0 ){ |
| 30993 | 32934 | for(i=0; i<pAuxDb->nSession; i++){ |
| 30994 | | - sqlite3_fprintf(p->out, "%d %s\n", i, pAuxDb->aSession[i].zName); |
| 32935 | + cli_printf(p->out, "%d %s\n", i, pAuxDb->aSession[i].zName); |
| 30995 | 32936 | } |
| 30996 | 32937 | }else |
| 30997 | 32938 | |
| 30998 | 32939 | /* .session open DB NAME |
| 30999 | 32940 | ** Open a new session called NAME on the attached database DB. |
| | @@ -31004,23 +32945,23 @@ |
| 31004 | 32945 | if( nCmd!=3 ) goto session_syntax_error; |
| 31005 | 32946 | zName = azCmd[2]; |
| 31006 | 32947 | if( zName[0]==0 ) goto session_syntax_error; |
| 31007 | 32948 | for(i=0; i<pAuxDb->nSession; i++){ |
| 31008 | 32949 | if( cli_strcmp(pAuxDb->aSession[i].zName,zName)==0 ){ |
| 31009 | | - sqlite3_fprintf(stderr,"Session \"%s\" already exists\n", zName); |
| 32950 | + cli_printf(stderr,"Session \"%s\" already exists\n", zName); |
| 31010 | 32951 | goto meta_command_exit; |
| 31011 | 32952 | } |
| 31012 | 32953 | } |
| 31013 | 32954 | if( pAuxDb->nSession>=ArraySize(pAuxDb->aSession) ){ |
| 31014 | | - sqlite3_fprintf(stderr, |
| 32955 | + cli_printf(stderr, |
| 31015 | 32956 | "Maximum of %d sessions\n", ArraySize(pAuxDb->aSession)); |
| 31016 | 32957 | goto meta_command_exit; |
| 31017 | 32958 | } |
| 31018 | 32959 | pSession = &pAuxDb->aSession[pAuxDb->nSession]; |
| 31019 | 32960 | rc = sqlite3session_create(p->db, azCmd[1], &pSession->p); |
| 31020 | 32961 | if( rc ){ |
| 31021 | | - sqlite3_fprintf(stderr,"Cannot open session: error code=%d\n", rc); |
| 32962 | + cli_printf(stderr,"Cannot open session: error code=%d\n", rc); |
| 31022 | 32963 | rc = 0; |
| 31023 | 32964 | goto meta_command_exit; |
| 31024 | 32965 | } |
| 31025 | 32966 | pSession->nFilter = 0; |
| 31026 | 32967 | sqlite3session_table_filter(pSession->p, session_filter, pSession); |
| | @@ -31040,20 +32981,20 @@ |
| 31040 | 32981 | if( c=='s' && n>=10 && cli_strncmp(azArg[0], "selftest-", 9)==0 ){ |
| 31041 | 32982 | if( cli_strncmp(azArg[0]+9, "boolean", n-9)==0 ){ |
| 31042 | 32983 | int i, v; |
| 31043 | 32984 | for(i=1; i<nArg; i++){ |
| 31044 | 32985 | v = booleanValue(azArg[i]); |
| 31045 | | - sqlite3_fprintf(p->out, "%s: %d 0x%x\n", azArg[i], v, v); |
| 32986 | + cli_printf(p->out, "%s: %d 0x%x\n", azArg[i], v, v); |
| 31046 | 32987 | } |
| 31047 | 32988 | } |
| 31048 | 32989 | if( cli_strncmp(azArg[0]+9, "integer", n-9)==0 ){ |
| 31049 | 32990 | int i; sqlite3_int64 v; |
| 31050 | 32991 | for(i=1; i<nArg; i++){ |
| 31051 | 32992 | char zBuf[200]; |
| 31052 | 32993 | v = integerValue(azArg[i]); |
| 31053 | 32994 | sqlite3_snprintf(sizeof(zBuf),zBuf,"%s: %lld 0x%llx\n", azArg[i],v,v); |
| 31054 | | - sqlite3_fputs(zBuf, p->out); |
| 32995 | + cli_puts(zBuf, p->out); |
| 31055 | 32996 | } |
| 31056 | 32997 | } |
| 31057 | 32998 | }else |
| 31058 | 32999 | #endif |
| 31059 | 33000 | |
| | @@ -31076,13 +33017,13 @@ |
| 31076 | 33017 | }else |
| 31077 | 33018 | if( cli_strcmp(z,"-v")==0 ){ |
| 31078 | 33019 | bVerbose++; |
| 31079 | 33020 | }else |
| 31080 | 33021 | { |
| 31081 | | - sqlite3_fprintf(stderr, |
| 33022 | + cli_printf(stderr, |
| 31082 | 33023 | "Unknown option \"%s\" on \"%s\"\n", azArg[i], azArg[0]); |
| 31083 | | - sqlite3_fputs("Should be one of: --init -v\n", stderr); |
| 33024 | + cli_puts("Should be one of: --init -v\n", stderr); |
| 31084 | 33025 | rc = 1; |
| 31085 | 33026 | goto meta_command_exit; |
| 31086 | 33027 | } |
| 31087 | 33028 | } |
| 31088 | 33029 | if( sqlite3_table_column_metadata(p->db,"main","selftest",0,0,0,0,0,0) |
| | @@ -31123,61 +33064,59 @@ |
| 31123 | 33064 | if( zOp==0 ) continue; |
| 31124 | 33065 | if( zSql==0 ) continue; |
| 31125 | 33066 | if( zAns==0 ) continue; |
| 31126 | 33067 | k = 0; |
| 31127 | 33068 | if( bVerbose>0 ){ |
| 31128 | | - sqlite3_fprintf(stdout, "%d: %s %s\n", tno, zOp, zSql); |
| 33069 | + cli_printf(stdout, "%d: %s %s\n", tno, zOp, zSql); |
| 31129 | 33070 | } |
| 31130 | 33071 | if( cli_strcmp(zOp,"memo")==0 ){ |
| 31131 | | - sqlite3_fprintf(p->out, "%s\n", zSql); |
| 33072 | + cli_printf(p->out, "%s\n", zSql); |
| 31132 | 33073 | }else |
| 31133 | 33074 | if( cli_strcmp(zOp,"run")==0 ){ |
| 31134 | 33075 | char *zErrMsg = 0; |
| 31135 | 33076 | str.n = 0; |
| 31136 | 33077 | str.zTxt[0] = 0; |
| 31137 | 33078 | rc = sqlite3_exec(p->db, zSql, captureOutputCallback, &str, &zErrMsg); |
| 31138 | 33079 | nTest++; |
| 31139 | 33080 | if( bVerbose ){ |
| 31140 | | - sqlite3_fprintf(p->out, "Result: %s\n", str.zTxt); |
| 33081 | + cli_printf(p->out, "Result: %s\n", str.zTxt); |
| 31141 | 33082 | } |
| 31142 | 33083 | if( rc || zErrMsg ){ |
| 31143 | 33084 | nErr++; |
| 31144 | 33085 | rc = 1; |
| 31145 | | - sqlite3_fprintf(p->out, "%d: error-code-%d: %s\n", tno, rc,zErrMsg); |
| 33086 | + cli_printf(p->out, "%d: error-code-%d: %s\n", tno, rc,zErrMsg); |
| 31146 | 33087 | sqlite3_free(zErrMsg); |
| 31147 | 33088 | }else if( cli_strcmp(zAns,str.zTxt)!=0 ){ |
| 31148 | 33089 | nErr++; |
| 31149 | 33090 | rc = 1; |
| 31150 | | - sqlite3_fprintf(p->out, "%d: Expected: [%s]\n", tno, zAns); |
| 31151 | | - sqlite3_fprintf(p->out, "%d: Got: [%s]\n", tno, str.zTxt); |
| 33091 | + cli_printf(p->out, "%d: Expected: [%s]\n", tno, zAns); |
| 33092 | + cli_printf(p->out, "%d: Got: [%s]\n", tno, str.zTxt); |
| 31152 | 33093 | } |
| 31153 | 33094 | } |
| 31154 | 33095 | else{ |
| 31155 | | - sqlite3_fprintf(stderr, |
| 33096 | + cli_printf(stderr, |
| 31156 | 33097 | "Unknown operation \"%s\" on selftest line %d\n", zOp, tno); |
| 31157 | 33098 | rc = 1; |
| 31158 | 33099 | break; |
| 31159 | 33100 | } |
| 31160 | 33101 | } /* End loop over rows of content from SELFTEST */ |
| 31161 | 33102 | sqlite3_finalize(pStmt); |
| 31162 | 33103 | } /* End loop over k */ |
| 31163 | 33104 | freeText(&str); |
| 31164 | | - sqlite3_fprintf(p->out, "%d errors out of %d tests\n", nErr, nTest); |
| 33105 | + cli_printf(p->out, "%d errors out of %d tests\n", nErr, nTest); |
| 31165 | 33106 | }else |
| 31166 | 33107 | |
| 31167 | 33108 | if( c=='s' && cli_strncmp(azArg[0], "separator", n)==0 ){ |
| 31168 | 33109 | if( nArg<2 || nArg>3 ){ |
| 31169 | 33110 | eputz("Usage: .separator COL ?ROW?\n"); |
| 31170 | 33111 | rc = 1; |
| 31171 | 33112 | } |
| 31172 | 33113 | if( nArg>=2 ){ |
| 31173 | | - sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, |
| 31174 | | - "%.*s", (int)ArraySize(p->colSeparator)-1, azArg[1]); |
| 33114 | + modeSetStr(&p->mode.spec.zColumnSep, azArg[1]); |
| 31175 | 33115 | } |
| 31176 | 33116 | if( nArg>=3 ){ |
| 31177 | | - sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, |
| 31178 | | - "%.*s", (int)ArraySize(p->rowSeparator)-1, azArg[2]); |
| 33117 | + modeSetStr(&p->mode.spec.zRowSep,azArg[2]); |
| 31179 | 33118 | } |
| 31180 | 33119 | }else |
| 31181 | 33120 | |
| 31182 | 33121 | if( c=='s' && n>=4 && cli_strncmp(azArg[0],"sha3sum",n)==0 ){ |
| 31183 | 33122 | const char *zLike = 0; /* Which table to checksum. 0 means everything */ |
| | @@ -31207,11 +33146,11 @@ |
| 31207 | 33146 | }else |
| 31208 | 33147 | if( cli_strcmp(z,"debug")==0 ){ |
| 31209 | 33148 | bDebug = 1; |
| 31210 | 33149 | }else |
| 31211 | 33150 | { |
| 31212 | | - sqlite3_fprintf(stderr, |
| 33151 | + cli_printf(stderr, |
| 31213 | 33152 | "Unknown option \"%s\" on \"%s\"\n", azArg[i], azArg[0]); |
| 31214 | 33153 | showHelp(p->out, azArg[0]); |
| 31215 | 33154 | rc = 1; |
| 31216 | 33155 | goto meta_command_exit; |
| 31217 | 33156 | } |
| | @@ -31286,11 +33225,11 @@ |
| 31286 | 33225 | } |
| 31287 | 33226 | shell_check_oom(zSql); |
| 31288 | 33227 | freeText(&sQuery); |
| 31289 | 33228 | freeText(&sSql); |
| 31290 | 33229 | if( bDebug ){ |
| 31291 | | - sqlite3_fprintf(p->out, "%s\n", zSql); |
| 33230 | + cli_printf(p->out, "%s\n", zSql); |
| 31292 | 33231 | }else{ |
| 31293 | 33232 | shell_exec(p, zSql, 0); |
| 31294 | 33233 | } |
| 31295 | 33234 | #if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) && !defined(SQLITE_OMIT_VIRTUALTABLE) |
| 31296 | 33235 | { |
| | @@ -31316,11 +33255,11 @@ |
| 31316 | 33255 | "||group_concat('CAST(CAST('||cname||' AS BLOB) AS TEXT)<>'||cname\n" |
| 31317 | 33256 | "|| ' AND typeof('||cname||')=''text'' ',\n" |
| 31318 | 33257 | "' OR ') as query, tname from tabcols group by tname)" |
| 31319 | 33258 | , zRevText); |
| 31320 | 33259 | shell_check_oom(zRevText); |
| 31321 | | - if( bDebug ) sqlite3_fprintf(p->out, "%s\n", zRevText); |
| 33260 | + if( bDebug ) cli_printf(p->out, "%s\n", zRevText); |
| 31322 | 33261 | lrc = sqlite3_prepare_v2(p->db, zRevText, -1, &pStmt, 0); |
| 31323 | 33262 | if( lrc!=SQLITE_OK ){ |
| 31324 | 33263 | /* assert(lrc==SQLITE_NOMEM); // might also be SQLITE_ERROR if the |
| 31325 | 33264 | ** user does cruel and unnatural things like ".limit expr_depth 0". */ |
| 31326 | 33265 | rc = 1; |
| | @@ -31329,19 +33268,19 @@ |
| 31329 | 33268 | lrc = SQLITE_ROW==sqlite3_step(pStmt); |
| 31330 | 33269 | if( lrc ){ |
| 31331 | 33270 | const char *zGenQuery = (char*)sqlite3_column_text(pStmt,0); |
| 31332 | 33271 | sqlite3_stmt *pCheckStmt; |
| 31333 | 33272 | lrc = sqlite3_prepare_v2(p->db, zGenQuery, -1, &pCheckStmt, 0); |
| 31334 | | - if( bDebug ) sqlite3_fprintf(p->out, "%s\n", zGenQuery); |
| 33273 | + if( bDebug ) cli_printf(p->out, "%s\n", zGenQuery); |
| 31335 | 33274 | if( lrc!=SQLITE_OK ){ |
| 31336 | 33275 | rc = 1; |
| 31337 | 33276 | }else{ |
| 31338 | 33277 | if( SQLITE_ROW==sqlite3_step(pCheckStmt) ){ |
| 31339 | 33278 | double countIrreversible = sqlite3_column_double(pCheckStmt, 0); |
| 31340 | 33279 | if( countIrreversible>0 ){ |
| 31341 | 33280 | int sz = (int)(countIrreversible + 0.5); |
| 31342 | | - sqlite3_fprintf(stderr, |
| 33281 | + cli_printf(stderr, |
| 31343 | 33282 | "Digest includes %d invalidly encoded text field%s.\n", |
| 31344 | 33283 | sz, (sz>1)? "s": ""); |
| 31345 | 33284 | } |
| 31346 | 33285 | } |
| 31347 | 33286 | sqlite3_finalize(pCheckStmt); |
| | @@ -31376,11 +33315,11 @@ |
| 31376 | 33315 | } |
| 31377 | 33316 | /*consoleRestore();*/ |
| 31378 | 33317 | x = zCmd!=0 ? system(zCmd) : 1; |
| 31379 | 33318 | /*consoleRenewSetup();*/ |
| 31380 | 33319 | sqlite3_free(zCmd); |
| 31381 | | - if( x ) sqlite3_fprintf(stderr,"System command returns %d\n", x); |
| 33320 | + if( x ) cli_printf(stderr,"System command returns %d\n", x); |
| 31382 | 33321 | }else |
| 31383 | 33322 | #endif /* !defined(SQLITE_NOHAVE_SYSTEM) && !defined(SQLITE_SHELL_FIDDLE) */ |
| 31384 | 33323 | |
| 31385 | 33324 | if( c=='s' && cli_strncmp(azArg[0], "show", n)==0 ){ |
| 31386 | 33325 | static const char *azBool[] = { "off", "on", "trigger", "full"}; |
| | @@ -31389,52 +33328,55 @@ |
| 31389 | 33328 | if( nArg!=1 ){ |
| 31390 | 33329 | eputz("Usage: .show\n"); |
| 31391 | 33330 | rc = 1; |
| 31392 | 33331 | goto meta_command_exit; |
| 31393 | 33332 | } |
| 31394 | | - sqlite3_fprintf(p->out, "%12.12s: %s\n","echo", |
| 31395 | | - azBool[ShellHasFlag(p, SHFLG_Echo)]); |
| 31396 | | - sqlite3_fprintf(p->out, "%12.12s: %s\n","eqp", azBool[p->autoEQP&3]); |
| 31397 | | - sqlite3_fprintf(p->out, "%12.12s: %s\n","explain", |
| 31398 | | - p->mode==MODE_Explain ? "on" : p->autoExplain ? "auto" : "off"); |
| 31399 | | - sqlite3_fprintf(p->out, "%12.12s: %s\n","headers", |
| 31400 | | - azBool[p->showHeader!=0]); |
| 31401 | | - if( p->mode==MODE_Column |
| 31402 | | - || (p->mode>=MODE_Markdown && p->mode<=MODE_Box) |
| 33333 | + cli_printf(p->out, "%12.12s: %s\n","echo", |
| 33334 | + azBool[(p->mode.mFlags & MFLG_ECHO)!=0]); |
| 33335 | + cli_printf(p->out, "%12.12s: %s\n","eqp", azBool[p->mode.autoEQP&3]); |
| 33336 | + cli_printf(p->out, "%12.12s: %s\n","explain", |
| 33337 | + p->mode.autoExplain ? "auto" : "off"); |
| 33338 | + cli_printf(p->out, "%12.12s: %s\n","headers", |
| 33339 | + azBool[p->mode.spec.bTitles==QRF_Yes]); |
| 33340 | + if( p->mode.spec.eStyle==QRF_STYLE_Column |
| 33341 | + || p->mode.spec.eStyle==QRF_STYLE_Box |
| 33342 | + || p->mode.spec.eStyle==QRF_STYLE_Table |
| 33343 | + || p->mode.spec.eStyle==QRF_STYLE_Markdown |
| 31403 | 33344 | ){ |
| 31404 | | - sqlite3_fprintf(p->out, |
| 33345 | + cli_printf(p->out, |
| 31405 | 33346 | "%12.12s: %s --wrap %d --wordwrap %s --%squote\n", "mode", |
| 31406 | | - modeDescr[p->mode], p->cmOpts.iWrap, |
| 31407 | | - p->cmOpts.bWordWrap ? "on" : "off", |
| 31408 | | - p->cmOpts.bQuote ? "" : "no"); |
| 33347 | + aModeInfo[p->mode.eMode].zName, p->mode.spec.nWrap, |
| 33348 | + p->mode.spec.bWordWrap==QRF_Yes ? "on" : "off", |
| 33349 | + p->mode.spec.eText==QRF_TEXT_Sql ? "" : "no"); |
| 31409 | 33350 | }else{ |
| 31410 | | - sqlite3_fprintf(p->out, "%12.12s: %s\n","mode", modeDescr[p->mode]); |
| 33351 | + cli_printf(p->out, "%12.12s: %s\n","mode", |
| 33352 | + aModeInfo[p->mode.eMode].zName); |
| 31411 | 33353 | } |
| 31412 | | - sqlite3_fprintf(p->out, "%12.12s: ", "nullvalue"); |
| 31413 | | - output_c_string(p->out, p->nullValue); |
| 31414 | | - sqlite3_fputs("\n", p->out); |
| 31415 | | - sqlite3_fprintf(p->out, "%12.12s: %s\n","output", |
| 33354 | + cli_printf(p->out, "%12.12s: ", "nullvalue"); |
| 33355 | + output_c_string(p->out, p->mode.spec.zNull); |
| 33356 | + cli_puts("\n", p->out); |
| 33357 | + cli_printf(p->out, "%12.12s: %s\n","output", |
| 31416 | 33358 | strlen30(p->outfile) ? p->outfile : "stdout"); |
| 31417 | | - sqlite3_fprintf(p->out, "%12.12s: ", "colseparator"); |
| 31418 | | - output_c_string(p->out, p->colSeparator); |
| 31419 | | - sqlite3_fputs("\n", p->out); |
| 31420 | | - sqlite3_fprintf(p->out, "%12.12s: ", "rowseparator"); |
| 31421 | | - output_c_string(p->out, p->rowSeparator); |
| 31422 | | - sqlite3_fputs("\n", p->out); |
| 33359 | + cli_printf(p->out, "%12.12s: ", "colseparator"); |
| 33360 | + output_c_string(p->out, p->mode.spec.zColumnSep); |
| 33361 | + cli_puts("\n", p->out); |
| 33362 | + cli_printf(p->out, "%12.12s: ", "rowseparator"); |
| 33363 | + output_c_string(p->out, p->mode.spec.zRowSep); |
| 33364 | + cli_puts("\n", p->out); |
| 31423 | 33365 | switch( p->statsOn ){ |
| 31424 | 33366 | case 0: zOut = "off"; break; |
| 31425 | 33367 | default: zOut = "on"; break; |
| 31426 | 33368 | case 2: zOut = "stmt"; break; |
| 31427 | 33369 | case 3: zOut = "vmstep"; break; |
| 31428 | 33370 | } |
| 31429 | | - sqlite3_fprintf(p->out, "%12.12s: %s\n","stats", zOut); |
| 31430 | | - sqlite3_fprintf(p->out, "%12.12s: ", "width"); |
| 31431 | | - for (i=0;i<p->nWidth;i++) { |
| 31432 | | - sqlite3_fprintf(p->out, "%d ", p->colWidth[i]); |
| 33371 | + cli_printf(p->out, "%12.12s: %s\n","stats", zOut); |
| 33372 | + cli_printf(p->out, "%12.12s: ", "width"); |
| 33373 | + for(i=0; i<p->mode.spec.nWidth; i++){ |
| 33374 | + cli_printf(p->out, "%d ", (int)p->mode.spec.aWidth[i]); |
| 31433 | 33375 | } |
| 31434 | | - sqlite3_fputs("\n", p->out); |
| 31435 | | - sqlite3_fprintf(p->out, "%12.12s: %s\n", "filename", |
| 33376 | + cli_puts("\n", p->out); |
| 33377 | + cli_printf(p->out, "%12.12s: %s\n", "filename", |
| 31436 | 33378 | p->pAuxDb->zDbFilename ? p->pAuxDb->zDbFilename : ""); |
| 31437 | 33379 | }else |
| 31438 | 33380 | |
| 31439 | 33381 | if( c=='s' && cli_strncmp(azArg[0], "stats", n)==0 ){ |
| 31440 | 33382 | if( nArg==2 ){ |
| | @@ -31542,20 +33484,20 @@ |
| 31542 | 33484 | int nPrintCol, nPrintRow; |
| 31543 | 33485 | for(i=0; i<nRow; i++){ |
| 31544 | 33486 | len = strlen30(azResult[i]); |
| 31545 | 33487 | if( len>maxlen ) maxlen = len; |
| 31546 | 33488 | } |
| 31547 | | - nPrintCol = 80/(maxlen+2); |
| 33489 | + nPrintCol = shellScreenWidth()/(maxlen+2); |
| 31548 | 33490 | if( nPrintCol<1 ) nPrintCol = 1; |
| 31549 | 33491 | nPrintRow = (nRow + nPrintCol - 1)/nPrintCol; |
| 31550 | 33492 | for(i=0; i<nPrintRow; i++){ |
| 31551 | 33493 | for(j=i; j<nRow; j+=nPrintRow){ |
| 31552 | 33494 | char *zSp = j<nPrintRow ? "" : " "; |
| 31553 | | - sqlite3_fprintf(p->out, |
| 33495 | + cli_printf(p->out, |
| 31554 | 33496 | "%s%-*s", zSp, maxlen, azResult[j] ? azResult[j]:""); |
| 31555 | 33497 | } |
| 31556 | | - sqlite3_fputs("\n", p->out); |
| 33498 | + cli_puts("\n", p->out); |
| 31557 | 33499 | } |
| 31558 | 33500 | } |
| 31559 | 33501 | |
| 31560 | 33502 | for(ii=0; ii<nRow; ii++) sqlite3_free(azResult[ii]); |
| 31561 | 33503 | sqlite3_free(azResult); |
| | @@ -31563,11 +33505,11 @@ |
| 31563 | 33505 | |
| 31564 | 33506 | #ifndef SQLITE_SHELL_FIDDLE |
| 31565 | 33507 | /* Begin redirecting output to the file "testcase-out.txt" */ |
| 31566 | 33508 | if( c=='t' && cli_strcmp(azArg[0],"testcase")==0 ){ |
| 31567 | 33509 | output_reset(p); |
| 31568 | | - p->out = output_file_open("testcase-out.txt"); |
| 33510 | + p->out = output_file_open(p, "testcase-out.txt"); |
| 31569 | 33511 | if( p->out==0 ){ |
| 31570 | 33512 | eputz("Error: cannot open 'testcase-out.txt'\n"); |
| 31571 | 33513 | } |
| 31572 | 33514 | if( nArg>=2 ){ |
| 31573 | 33515 | sqlite3_snprintf(sizeof(p->zTestcase), p->zTestcase, "%s", azArg[1]); |
| | @@ -31626,14 +33568,14 @@ |
| 31626 | 33568 | if( zCmd[0]=='-' && zCmd[1] ) zCmd++; |
| 31627 | 33569 | } |
| 31628 | 33570 | |
| 31629 | 33571 | /* --help lists all test-controls */ |
| 31630 | 33572 | if( cli_strcmp(zCmd,"help")==0 ){ |
| 31631 | | - sqlite3_fputs("Available test-controls:\n", p->out); |
| 33573 | + cli_puts("Available test-controls:\n", p->out); |
| 31632 | 33574 | for(i=0; i<ArraySize(aCtrl); i++){ |
| 31633 | 33575 | if( aCtrl[i].unSafe && !ShellHasFlag(p,SHFLG_TestingMode) ) continue; |
| 31634 | | - sqlite3_fprintf(p->out, " .testctrl %s %s\n", |
| 33576 | + cli_printf(p->out, " .testctrl %s %s\n", |
| 31635 | 33577 | aCtrl[i].zCtrlName, aCtrl[i].zUsage); |
| 31636 | 33578 | } |
| 31637 | 33579 | rc = 1; |
| 31638 | 33580 | goto meta_command_exit; |
| 31639 | 33581 | } |
| | @@ -31646,19 +33588,19 @@ |
| 31646 | 33588 | if( cli_strncmp(zCmd, aCtrl[i].zCtrlName, n2)==0 ){ |
| 31647 | 33589 | if( testctrl<0 ){ |
| 31648 | 33590 | testctrl = aCtrl[i].ctrlCode; |
| 31649 | 33591 | iCtrl = i; |
| 31650 | 33592 | }else{ |
| 31651 | | - sqlite3_fprintf(stderr,"Error: ambiguous test-control: \"%s\"\n" |
| 33593 | + cli_printf(stderr,"Error: ambiguous test-control: \"%s\"\n" |
| 31652 | 33594 | "Use \".testctrl --help\" for help\n", zCmd); |
| 31653 | 33595 | rc = 1; |
| 31654 | 33596 | goto meta_command_exit; |
| 31655 | 33597 | } |
| 31656 | 33598 | } |
| 31657 | 33599 | } |
| 31658 | 33600 | if( testctrl<0 ){ |
| 31659 | | - sqlite3_fprintf(stderr,"Error: unknown test-control: %s\n" |
| 33601 | + cli_printf(stderr,"Error: unknown test-control: %s\n" |
| 31660 | 33602 | "Use \".testctrl --help\" for help\n", zCmd); |
| 31661 | 33603 | }else{ |
| 31662 | 33604 | switch(testctrl){ |
| 31663 | 33605 | |
| 31664 | 33606 | /* Special processing for .testctrl opt MASK ... |
| | @@ -31734,17 +33676,17 @@ |
| 31734 | 33676 | int jj; |
| 31735 | 33677 | for(jj=0; jj<ArraySize(aLabel); jj++){ |
| 31736 | 33678 | if( sqlite3_stricmp(zLabel, aLabel[jj].zLabel)==0 ) break; |
| 31737 | 33679 | } |
| 31738 | 33680 | if( jj>=ArraySize(aLabel) ){ |
| 31739 | | - sqlite3_fprintf(stderr, |
| 33681 | + cli_printf(stderr, |
| 31740 | 33682 | "Error: no such optimization: \"%s\"\n", zLabel); |
| 31741 | | - sqlite3_fputs("Should be one of:", stderr); |
| 33683 | + cli_puts("Should be one of:", stderr); |
| 31742 | 33684 | for(jj=0; jj<ArraySize(aLabel); jj++){ |
| 31743 | | - sqlite3_fprintf(stderr," %s", aLabel[jj].zLabel); |
| 33685 | + cli_printf(stderr," %s", aLabel[jj].zLabel); |
| 31744 | 33686 | } |
| 31745 | | - sqlite3_fputs("\n", stderr); |
| 33687 | + cli_puts("\n", stderr); |
| 31746 | 33688 | rc = 1; |
| 31747 | 33689 | goto meta_command_exit; |
| 31748 | 33690 | } |
| 31749 | 33691 | if( useLabel=='+' ){ |
| 31750 | 33692 | newOpt &= ~aLabel[jj].mask; |
| | @@ -31758,27 +33700,27 @@ |
| 31758 | 33700 | } |
| 31759 | 33701 | for(ii=nOff=0, m=1; ii<32; ii++, m <<= 1){ |
| 31760 | 33702 | if( m & newOpt ) nOff++; |
| 31761 | 33703 | } |
| 31762 | 33704 | if( nOff<12 ){ |
| 31763 | | - sqlite3_fputs("+All", p->out); |
| 33705 | + cli_puts("+All", p->out); |
| 31764 | 33706 | for(ii=0; ii<ArraySize(aLabel); ii++){ |
| 31765 | 33707 | if( !aLabel[ii].bDsply ) continue; |
| 31766 | 33708 | if( (newOpt & aLabel[ii].mask)!=0 ){ |
| 31767 | | - sqlite3_fprintf(p->out, " -%s", aLabel[ii].zLabel); |
| 33709 | + cli_printf(p->out, " -%s", aLabel[ii].zLabel); |
| 31768 | 33710 | } |
| 31769 | 33711 | } |
| 31770 | 33712 | }else{ |
| 31771 | | - sqlite3_fputs("-All", p->out); |
| 33713 | + cli_puts("-All", p->out); |
| 31772 | 33714 | for(ii=0; ii<ArraySize(aLabel); ii++){ |
| 31773 | 33715 | if( !aLabel[ii].bDsply ) continue; |
| 31774 | 33716 | if( (newOpt & aLabel[ii].mask)==0 ){ |
| 31775 | | - sqlite3_fprintf(p->out, " +%s", aLabel[ii].zLabel); |
| 33717 | + cli_printf(p->out, " +%s", aLabel[ii].zLabel); |
| 31776 | 33718 | } |
| 31777 | 33719 | } |
| 31778 | 33720 | } |
| 31779 | | - sqlite3_fputs("\n", p->out); |
| 33721 | + cli_puts("\n", p->out); |
| 31780 | 33722 | rc2 = isOk = 3; |
| 31781 | 33723 | break; |
| 31782 | 33724 | } |
| 31783 | 33725 | |
| 31784 | 33726 | /* sqlite3_test_control(int, db, int) */ |
| | @@ -31814,11 +33756,11 @@ |
| 31814 | 33756 | if( nArg==3 || nArg==4 ){ |
| 31815 | 33757 | int ii = (int)integerValue(azArg[2]); |
| 31816 | 33758 | sqlite3 *db; |
| 31817 | 33759 | if( ii==0 && cli_strcmp(azArg[2],"random")==0 ){ |
| 31818 | 33760 | sqlite3_randomness(sizeof(ii),&ii); |
| 31819 | | - sqlite3_fprintf(stdout, "-- random seed: %d\n", ii); |
| 33761 | + cli_printf(stdout, "-- random seed: %d\n", ii); |
| 31820 | 33762 | } |
| 31821 | 33763 | if( nArg==3 ){ |
| 31822 | 33764 | db = 0; |
| 31823 | 33765 | }else{ |
| 31824 | 33766 | db = p->db; |
| | @@ -31867,11 +33809,11 @@ |
| 31867 | 33809 | break; |
| 31868 | 33810 | |
| 31869 | 33811 | case SQLITE_TESTCTRL_SEEK_COUNT: { |
| 31870 | 33812 | u64 x = 0; |
| 31871 | 33813 | rc2 = sqlite3_test_control(testctrl, p->db, &x); |
| 31872 | | - sqlite3_fprintf(p->out, "%llu\n", x); |
| 33814 | + cli_printf(p->out, "%llu\n", x); |
| 31873 | 33815 | isOk = 3; |
| 31874 | 33816 | break; |
| 31875 | 33817 | } |
| 31876 | 33818 | |
| 31877 | 33819 | #ifdef YYCOVERAGE |
| | @@ -31898,15 +33840,15 @@ |
| 31898 | 33840 | int id = 1; |
| 31899 | 33841 | while(1){ |
| 31900 | 33842 | int val = 0; |
| 31901 | 33843 | rc2 = sqlite3_test_control(testctrl, -id, &val); |
| 31902 | 33844 | if( rc2!=SQLITE_OK ) break; |
| 31903 | | - if( id>1 ) sqlite3_fputs(" ", p->out); |
| 31904 | | - sqlite3_fprintf(p->out, "%d: %d", id, val); |
| 33845 | + if( id>1 ) cli_puts(" ", p->out); |
| 33846 | + cli_printf(p->out, "%d: %d", id, val); |
| 31905 | 33847 | id++; |
| 31906 | 33848 | } |
| 31907 | | - if( id>1 ) sqlite3_fputs("\n", p->out); |
| 33849 | + if( id>1 ) cli_puts("\n", p->out); |
| 31908 | 33850 | isOk = 3; |
| 31909 | 33851 | } |
| 31910 | 33852 | break; |
| 31911 | 33853 | } |
| 31912 | 33854 | #endif |
| | @@ -31941,11 +33883,11 @@ |
| 31941 | 33883 | const char *zTestArg; |
| 31942 | 33884 | int nOp; |
| 31943 | 33885 | int ii, jj, x; |
| 31944 | 33886 | int *aOp; |
| 31945 | 33887 | if( nArg!=4 ){ |
| 31946 | | - sqlite3_fprintf(stderr, |
| 33888 | + cli_printf(stderr, |
| 31947 | 33889 | "ERROR - should be: \".testctrl bitvec_test SIZE INT-ARRAY\"\n" |
| 31948 | 33890 | ); |
| 31949 | 33891 | rc = 1; |
| 31950 | 33892 | goto meta_command_exit; |
| 31951 | 33893 | } |
| | @@ -31964,11 +33906,11 @@ |
| 31964 | 33906 | x = 0; |
| 31965 | 33907 | } |
| 31966 | 33908 | } |
| 31967 | 33909 | aOp[jj] = x; |
| 31968 | 33910 | x = sqlite3_test_control(testctrl, iSize, aOp); |
| 31969 | | - sqlite3_fprintf(p->out, "result: %d\n", x); |
| 33911 | + cli_printf(p->out, "result: %d\n", x); |
| 31970 | 33912 | free(aOp); |
| 31971 | 33913 | break; |
| 31972 | 33914 | } |
| 31973 | 33915 | case SQLITE_TESTCTRL_FAULT_INSTALL: { |
| 31974 | 33916 | int kk; |
| | @@ -31987,25 +33929,25 @@ |
| 31987 | 33929 | }else if( cli_strcmp(z,"reset")==0 ){ |
| 31988 | 33930 | faultsim_state.iCnt = faultsim_state.nSkip; |
| 31989 | 33931 | faultsim_state.nHit = 0; |
| 31990 | 33932 | sqlite3_test_control(testctrl, faultsim_callback); |
| 31991 | 33933 | }else if( cli_strcmp(z,"status")==0 ){ |
| 31992 | | - sqlite3_fprintf(p->out, "faultsim.iId: %d\n", |
| 33934 | + cli_printf(p->out, "faultsim.iId: %d\n", |
| 31993 | 33935 | faultsim_state.iId); |
| 31994 | | - sqlite3_fprintf(p->out, "faultsim.iErr: %d\n", |
| 33936 | + cli_printf(p->out, "faultsim.iErr: %d\n", |
| 31995 | 33937 | faultsim_state.iErr); |
| 31996 | | - sqlite3_fprintf(p->out, "faultsim.iCnt: %d\n", |
| 33938 | + cli_printf(p->out, "faultsim.iCnt: %d\n", |
| 31997 | 33939 | faultsim_state.iCnt); |
| 31998 | | - sqlite3_fprintf(p->out, "faultsim.nHit: %d\n", |
| 33940 | + cli_printf(p->out, "faultsim.nHit: %d\n", |
| 31999 | 33941 | faultsim_state.nHit); |
| 32000 | | - sqlite3_fprintf(p->out, "faultsim.iInterval: %d\n", |
| 33942 | + cli_printf(p->out, "faultsim.iInterval: %d\n", |
| 32001 | 33943 | faultsim_state.iInterval); |
| 32002 | | - sqlite3_fprintf(p->out, "faultsim.eVerbose: %d\n", |
| 33944 | + cli_printf(p->out, "faultsim.eVerbose: %d\n", |
| 32003 | 33945 | faultsim_state.eVerbose); |
| 32004 | | - sqlite3_fprintf(p->out, "faultsim.nRepeat: %d\n", |
| 33946 | + cli_printf(p->out, "faultsim.nRepeat: %d\n", |
| 32005 | 33947 | faultsim_state.nRepeat); |
| 32006 | | - sqlite3_fprintf(p->out, "faultsim.nSkip: %d\n", |
| 33948 | + cli_printf(p->out, "faultsim.nSkip: %d\n", |
| 32007 | 33949 | faultsim_state.nSkip); |
| 32008 | 33950 | }else if( cli_strcmp(z,"-v")==0 ){ |
| 32009 | 33951 | if( faultsim_state.eVerbose<2 ) faultsim_state.eVerbose++; |
| 32010 | 33952 | }else if( cli_strcmp(z,"-q")==0 ){ |
| 32011 | 33953 | if( faultsim_state.eVerbose>0 ) faultsim_state.eVerbose--; |
| | @@ -32020,20 +33962,20 @@ |
| 32020 | 33962 | }else if( cli_strcmp(z,"-skip")==0 && kk+1<nArg ){ |
| 32021 | 33963 | faultsim_state.nSkip = atoi(azArg[++kk]); |
| 32022 | 33964 | }else if( cli_strcmp(z,"-?")==0 || sqlite3_strglob("*help*",z)==0){ |
| 32023 | 33965 | bShowHelp = 1; |
| 32024 | 33966 | }else{ |
| 32025 | | - sqlite3_fprintf(stderr, |
| 33967 | + cli_printf(stderr, |
| 32026 | 33968 | "Unrecognized fault_install argument: \"%s\"\n", |
| 32027 | 33969 | azArg[kk]); |
| 32028 | 33970 | rc = 1; |
| 32029 | 33971 | bShowHelp = 1; |
| 32030 | 33972 | break; |
| 32031 | 33973 | } |
| 32032 | 33974 | } |
| 32033 | 33975 | if( bShowHelp ){ |
| 32034 | | - sqlite3_fputs( |
| 33976 | + cli_puts( |
| 32035 | 33977 | "Usage: .testctrl fault_install ARGS\n" |
| 32036 | 33978 | "Possible arguments:\n" |
| 32037 | 33979 | " off Disable faultsim\n" |
| 32038 | 33980 | " on Activate faultsim\n" |
| 32039 | 33981 | " reset Reset the trigger counter\n" |
| | @@ -32051,17 +33993,17 @@ |
| 32051 | 33993 | break; |
| 32052 | 33994 | } |
| 32053 | 33995 | } |
| 32054 | 33996 | } |
| 32055 | 33997 | if( isOk==0 && iCtrl>=0 ){ |
| 32056 | | - sqlite3_fprintf(p->out, |
| 33998 | + cli_printf(p->out, |
| 32057 | 33999 | "Usage: .testctrl %s %s\n", zCmd,aCtrl[iCtrl].zUsage); |
| 32058 | 34000 | rc = 1; |
| 32059 | 34001 | }else if( isOk==1 ){ |
| 32060 | | - sqlite3_fprintf(p->out, "%d\n", rc2); |
| 34002 | + cli_printf(p->out, "%d\n", rc2); |
| 32061 | 34003 | }else if( isOk==2 ){ |
| 32062 | | - sqlite3_fprintf(p->out, "0x%08x\n", rc2); |
| 34004 | + cli_printf(p->out, "0x%08x\n", rc2); |
| 32063 | 34005 | } |
| 32064 | 34006 | }else |
| 32065 | 34007 | #endif /* !defined(SQLITE_UNTESTABLE) */ |
| 32066 | 34008 | |
| 32067 | 34009 | if( c=='t' && n>4 && cli_strncmp(azArg[0], "timeout", n)==0 ){ |
| | @@ -32112,17 +34054,17 @@ |
| 32112 | 34054 | } |
| 32113 | 34055 | else if( optionMatch(z, "close") ){ |
| 32114 | 34056 | mType |= SQLITE_TRACE_CLOSE; |
| 32115 | 34057 | } |
| 32116 | 34058 | else { |
| 32117 | | - sqlite3_fprintf(stderr,"Unknown option \"%s\" on \".trace\"\n", z); |
| 34059 | + cli_printf(stderr,"Unknown option \"%s\" on \".trace\"\n", z); |
| 32118 | 34060 | rc = 1; |
| 32119 | 34061 | goto meta_command_exit; |
| 32120 | 34062 | } |
| 32121 | 34063 | }else{ |
| 32122 | 34064 | output_file_close(p->traceOut); |
| 32123 | | - p->traceOut = output_file_open(z); |
| 34065 | + p->traceOut = output_file_open(p, z); |
| 32124 | 34066 | } |
| 32125 | 34067 | } |
| 32126 | 34068 | if( p->traceOut==0 ){ |
| 32127 | 34069 | sqlite3_trace_v2(p->db, 0, 0, 0); |
| 32128 | 34070 | }else{ |
| | @@ -32157,38 +34099,38 @@ |
| 32157 | 34099 | }else |
| 32158 | 34100 | #endif |
| 32159 | 34101 | |
| 32160 | 34102 | if( c=='v' && cli_strncmp(azArg[0], "version", n)==0 ){ |
| 32161 | 34103 | char *zPtrSz = sizeof(void*)==8 ? "64-bit" : "32-bit"; |
| 32162 | | - sqlite3_fprintf(p->out, "SQLite %s %s\n" /*extra-version-info*/, |
| 34104 | + cli_printf(p->out, "SQLite %s %s\n" /*extra-version-info*/, |
| 32163 | 34105 | sqlite3_libversion(), sqlite3_sourceid()); |
| 32164 | 34106 | #if SQLITE_HAVE_ZLIB |
| 32165 | | - sqlite3_fprintf(p->out, "zlib version %s\n", zlibVersion()); |
| 34107 | + cli_printf(p->out, "zlib version %s\n", zlibVersion()); |
| 32166 | 34108 | #endif |
| 32167 | 34109 | #define CTIMEOPT_VAL_(opt) #opt |
| 32168 | 34110 | #define CTIMEOPT_VAL(opt) CTIMEOPT_VAL_(opt) |
| 32169 | 34111 | #if defined(__clang__) && defined(__clang_major__) |
| 32170 | | - sqlite3_fprintf(p->out, "clang-" CTIMEOPT_VAL(__clang_major__) "." |
| 34112 | + cli_printf(p->out, "clang-" CTIMEOPT_VAL(__clang_major__) "." |
| 32171 | 34113 | CTIMEOPT_VAL(__clang_minor__) "." |
| 32172 | 34114 | CTIMEOPT_VAL(__clang_patchlevel__) " (%s)\n", zPtrSz); |
| 32173 | 34115 | #elif defined(_MSC_VER) |
| 32174 | | - sqlite3_fprintf(p->out, "msvc-" CTIMEOPT_VAL(_MSC_VER) " (%s)\n", zPtrSz); |
| 34116 | + cli_printf(p->out, "msvc-" CTIMEOPT_VAL(_MSC_VER) " (%s)\n", zPtrSz); |
| 32175 | 34117 | #elif defined(__GNUC__) && defined(__VERSION__) |
| 32176 | | - sqlite3_fprintf(p->out, "gcc-" __VERSION__ " (%s)\n", zPtrSz); |
| 34118 | + cli_printf(p->out, "gcc-" __VERSION__ " (%s)\n", zPtrSz); |
| 32177 | 34119 | #endif |
| 32178 | 34120 | }else |
| 32179 | 34121 | |
| 32180 | 34122 | if( c=='v' && cli_strncmp(azArg[0], "vfsinfo", n)==0 ){ |
| 32181 | 34123 | const char *zDbName = nArg==2 ? azArg[1] : "main"; |
| 32182 | 34124 | sqlite3_vfs *pVfs = 0; |
| 32183 | 34125 | if( p->db ){ |
| 32184 | 34126 | sqlite3_file_control(p->db, zDbName, SQLITE_FCNTL_VFS_POINTER, &pVfs); |
| 32185 | 34127 | if( pVfs ){ |
| 32186 | | - sqlite3_fprintf(p->out, "vfs.zName = \"%s\"\n", pVfs->zName); |
| 32187 | | - sqlite3_fprintf(p->out, "vfs.iVersion = %d\n", pVfs->iVersion); |
| 32188 | | - sqlite3_fprintf(p->out, "vfs.szOsFile = %d\n", pVfs->szOsFile); |
| 32189 | | - sqlite3_fprintf(p->out, "vfs.mxPathname = %d\n", pVfs->mxPathname); |
| 34128 | + cli_printf(p->out, "vfs.zName = \"%s\"\n", pVfs->zName); |
| 34129 | + cli_printf(p->out, "vfs.iVersion = %d\n", pVfs->iVersion); |
| 34130 | + cli_printf(p->out, "vfs.szOsFile = %d\n", pVfs->szOsFile); |
| 34131 | + cli_printf(p->out, "vfs.mxPathname = %d\n", pVfs->mxPathname); |
| 32190 | 34132 | } |
| 32191 | 34133 | } |
| 32192 | 34134 | }else |
| 32193 | 34135 | |
| 32194 | 34136 | if( c=='v' && cli_strncmp(azArg[0], "vfslist", n)==0 ){ |
| | @@ -32196,17 +34138,17 @@ |
| 32196 | 34138 | sqlite3_vfs *pCurrent = 0; |
| 32197 | 34139 | if( p->db ){ |
| 32198 | 34140 | sqlite3_file_control(p->db, "main", SQLITE_FCNTL_VFS_POINTER, &pCurrent); |
| 32199 | 34141 | } |
| 32200 | 34142 | for(pVfs=sqlite3_vfs_find(0); pVfs; pVfs=pVfs->pNext){ |
| 32201 | | - sqlite3_fprintf(p->out, "vfs.zName = \"%s\"%s\n", pVfs->zName, |
| 34143 | + cli_printf(p->out, "vfs.zName = \"%s\"%s\n", pVfs->zName, |
| 32202 | 34144 | pVfs==pCurrent ? " <--- CURRENT" : ""); |
| 32203 | | - sqlite3_fprintf(p->out, "vfs.iVersion = %d\n", pVfs->iVersion); |
| 32204 | | - sqlite3_fprintf(p->out, "vfs.szOsFile = %d\n", pVfs->szOsFile); |
| 32205 | | - sqlite3_fprintf(p->out, "vfs.mxPathname = %d\n", pVfs->mxPathname); |
| 34145 | + cli_printf(p->out, "vfs.iVersion = %d\n", pVfs->iVersion); |
| 34146 | + cli_printf(p->out, "vfs.szOsFile = %d\n", pVfs->szOsFile); |
| 34147 | + cli_printf(p->out, "vfs.mxPathname = %d\n", pVfs->mxPathname); |
| 32206 | 34148 | if( pVfs->pNext ){ |
| 32207 | | - sqlite3_fputs("-----------------------------------\n", p->out); |
| 34149 | + cli_puts("-----------------------------------\n", p->out); |
| 32208 | 34150 | } |
| 32209 | 34151 | } |
| 32210 | 34152 | }else |
| 32211 | 34153 | |
| 32212 | 34154 | if( c=='v' && cli_strncmp(azArg[0], "vfsname", n)==0 ){ |
| | @@ -32213,11 +34155,11 @@ |
| 32213 | 34155 | const char *zDbName = nArg==2 ? azArg[1] : "main"; |
| 32214 | 34156 | char *zVfsName = 0; |
| 32215 | 34157 | if( p->db ){ |
| 32216 | 34158 | sqlite3_file_control(p->db, zDbName, SQLITE_FCNTL_VFSNAME, &zVfsName); |
| 32217 | 34159 | if( zVfsName ){ |
| 32218 | | - sqlite3_fprintf(p->out, "%s\n", zVfsName); |
| 34160 | + cli_printf(p->out, "%s\n", zVfsName); |
| 32219 | 34161 | sqlite3_free(zVfsName); |
| 32220 | 34162 | } |
| 32221 | 34163 | } |
| 32222 | 34164 | }else |
| 32223 | 34165 | |
| | @@ -32226,33 +34168,32 @@ |
| 32226 | 34168 | sqlite3_test_control(SQLITE_TESTCTRL_TRACEFLAGS, 3, &x); |
| 32227 | 34169 | }else |
| 32228 | 34170 | |
| 32229 | 34171 | if( c=='w' && cli_strncmp(azArg[0], "width", n)==0 ){ |
| 32230 | 34172 | int j; |
| 32231 | | - assert( nArg<=ArraySize(azArg) ); |
| 32232 | | - p->nWidth = nArg-1; |
| 32233 | | - p->colWidth = realloc(p->colWidth, (p->nWidth+1)*sizeof(int)*2); |
| 32234 | | - if( p->colWidth==0 && p->nWidth>0 ) shell_out_of_memory(); |
| 32235 | | - if( p->nWidth ) p->actualWidth = &p->colWidth[p->nWidth]; |
| 34173 | + p->mode.spec.nWidth = nArg-1; |
| 34174 | + p->mode.spec.aWidth = realloc(p->mode.spec.aWidth, |
| 34175 | + (p->mode.spec.nWidth+1)*sizeof(short int)); |
| 34176 | + shell_check_oom(p->mode.spec.aWidth); |
| 32236 | 34177 | for(j=1; j<nArg; j++){ |
| 32237 | 34178 | i64 w = integerValue(azArg[j]); |
| 32238 | | - if( w < -30000 ) w = -30000; |
| 32239 | | - if( w > +30000 ) w = +30000; |
| 32240 | | - p->colWidth[j-1] = (int)w; |
| 34179 | + if( w < -QRF_MAX_WIDTH ) w = -QRF_MAX_WIDTH; |
| 34180 | + if( w > QRF_MAX_WIDTH ) w = QRF_MAX_WIDTH; |
| 34181 | + p->mode.spec.aWidth[j-1] = (short int)w; |
| 32241 | 34182 | } |
| 32242 | 34183 | }else |
| 32243 | 34184 | |
| 32244 | 34185 | { |
| 32245 | | - sqlite3_fprintf(stderr,"Error: unknown command or invalid arguments: " |
| 34186 | + cli_printf(stderr,"Error: unknown command or invalid arguments: " |
| 32246 | 34187 | " \"%s\". Enter \".help\" for help\n", azArg[0]); |
| 32247 | 34188 | rc = 1; |
| 32248 | 34189 | } |
| 32249 | 34190 | |
| 32250 | 34191 | meta_command_exit: |
| 32251 | | - if( p->outCount ){ |
| 32252 | | - p->outCount--; |
| 32253 | | - if( p->outCount==0 ) output_reset(p); |
| 34192 | + if( p->nPopOutput ){ |
| 34193 | + p->nPopOutput--; |
| 34194 | + if( p->nPopOutput==0 ) output_reset(p); |
| 32254 | 34195 | } |
| 32255 | 34196 | p->bSafeMode = p->bSafeModePersist; |
| 32256 | 34197 | return rc; |
| 32257 | 34198 | } |
| 32258 | 34199 | |
| | @@ -32513,29 +34454,29 @@ |
| 32513 | 34454 | sqlite3_snprintf(sizeof(zPrefix), zPrefix, |
| 32514 | 34455 | "%s near line %d:", zErrorType, startline); |
| 32515 | 34456 | }else{ |
| 32516 | 34457 | sqlite3_snprintf(sizeof(zPrefix), zPrefix, "%s:", zErrorType); |
| 32517 | 34458 | } |
| 32518 | | - sqlite3_fprintf(stderr,"%s %s\n", zPrefix, zErrorTail); |
| 34459 | + cli_printf(stderr,"%s %s\n", zPrefix, zErrorTail); |
| 32519 | 34460 | sqlite3_free(zErrMsg); |
| 32520 | 34461 | zErrMsg = 0; |
| 32521 | 34462 | return 1; |
| 32522 | 34463 | }else if( ShellHasFlag(p, SHFLG_CountChanges) ){ |
| 32523 | 34464 | char zLineBuf[2000]; |
| 32524 | 34465 | sqlite3_snprintf(sizeof(zLineBuf), zLineBuf, |
| 32525 | 34466 | "changes: %lld total_changes: %lld", |
| 32526 | 34467 | sqlite3_changes64(p->db), sqlite3_total_changes64(p->db)); |
| 32527 | | - sqlite3_fprintf(p->out, "%s\n", zLineBuf); |
| 34468 | + cli_printf(p->out, "%s\n", zLineBuf); |
| 32528 | 34469 | } |
| 32529 | 34470 | |
| 32530 | 34471 | if( doAutoDetectRestore(p, zSql) ) return 1; |
| 32531 | 34472 | return 0; |
| 32532 | 34473 | } |
| 32533 | 34474 | |
| 32534 | 34475 | static void echo_group_input(ShellState *p, const char *zDo){ |
| 32535 | | - if( ShellHasFlag(p, SHFLG_Echo) ){ |
| 32536 | | - sqlite3_fprintf(p->out, "%s\n", zDo); |
| 34476 | + if( p->mode.mFlags & MFLG_ECHO ){ |
| 34477 | + cli_printf(p->out, "%s\n", zDo); |
| 32537 | 34478 | fflush(p->out); |
| 32538 | 34479 | } |
| 32539 | 34480 | } |
| 32540 | 34481 | |
| 32541 | 34482 | #ifdef SQLITE_SHELL_FIDDLE |
| | @@ -32588,26 +34529,31 @@ |
| 32588 | 34529 | i64 nAlloc = 0; /* Allocated zSql[] space */ |
| 32589 | 34530 | int rc; /* Error code */ |
| 32590 | 34531 | int errCnt = 0; /* Number of errors seen */ |
| 32591 | 34532 | i64 startline = 0; /* Line number for start of current input */ |
| 32592 | 34533 | QuickScanState qss = QSS_Start; /* Accumulated line status (so far) */ |
| 34534 | + const char *saved_zInFile; /* Prior value of p->zInFile */ |
| 34535 | + i64 saved_lineno; /* Prior value of p->lineno */ |
| 32593 | 34536 | |
| 32594 | 34537 | if( p->inputNesting==MAX_INPUT_NESTING ){ |
| 32595 | 34538 | /* This will be more informative in a later version. */ |
| 32596 | | - sqlite3_fprintf(stderr,"%s: Input nesting limit (%d) reached at line %lld." |
| 34539 | + cli_printf(stderr,"%s: Input nesting limit (%d) reached at line %lld." |
| 32597 | 34540 | " Check recursion.\n", zSrc, MAX_INPUT_NESTING, p->lineno); |
| 32598 | 34541 | return 1; |
| 32599 | 34542 | } |
| 32600 | 34543 | ++p->inputNesting; |
| 34544 | + saved_zInFile = p->zInFile; |
| 34545 | + p->zInFile = zSrc; |
| 34546 | + saved_lineno = p->lineno; |
| 32601 | 34547 | p->lineno = 0; |
| 32602 | 34548 | CONTINUE_PROMPT_RESET; |
| 32603 | 34549 | while( errCnt==0 || !bail_on_error || (p->in==0 && stdin_is_interactive) ){ |
| 32604 | 34550 | fflush(p->out); |
| 32605 | 34551 | zLine = one_input_line(p->in, zLine, nSql>0); |
| 32606 | 34552 | if( zLine==0 ){ |
| 32607 | 34553 | /* End of input */ |
| 32608 | | - if( p->in==0 && stdin_is_interactive ) sqlite3_fputs("\n", p->out); |
| 34554 | + if( p->in==0 && stdin_is_interactive ) cli_puts("\n", p->out); |
| 32609 | 34555 | break; |
| 32610 | 34556 | } |
| 32611 | 34557 | if( seenInterrupt ){ |
| 32612 | 34558 | if( p->in!=0 ) break; |
| 32613 | 34559 | seenInterrupt = 0; |
| | @@ -32660,25 +34606,29 @@ |
| 32660 | 34606 | nSql += nLine; |
| 32661 | 34607 | } |
| 32662 | 34608 | if( nSql>0x7fff0000 ){ |
| 32663 | 34609 | char zSize[100]; |
| 32664 | 34610 | sqlite3_snprintf(sizeof(zSize),zSize,"%,lld",nSql); |
| 32665 | | - sqlite3_fprintf(stderr, "%s:%lld: Input SQL is too big: %s bytes\n", |
| 34611 | + cli_printf(stderr, "%s:%lld: Input SQL is too big: %s bytes\n", |
| 32666 | 34612 | zSrc, startline, zSize); |
| 32667 | 34613 | nSql = 0; |
| 32668 | 34614 | errCnt++; |
| 32669 | 34615 | break; |
| 32670 | 34616 | }else if( nSql && QSS_SEMITERM(qss) && sqlite3_complete(zSql) ){ |
| 32671 | 34617 | echo_group_input(p, zSql); |
| 32672 | 34618 | errCnt += runOneSqlLine(p, zSql, p->in, startline); |
| 32673 | 34619 | CONTINUE_PROMPT_RESET; |
| 32674 | 34620 | nSql = 0; |
| 32675 | | - if( p->outCount ){ |
| 34621 | + if( p->nPopOutput ){ |
| 32676 | 34622 | output_reset(p); |
| 32677 | | - p->outCount = 0; |
| 34623 | + p->nPopOutput = 0; |
| 32678 | 34624 | }else{ |
| 32679 | 34625 | clearTempFile(p); |
| 34626 | + } |
| 34627 | + if( p->nPopMode ){ |
| 34628 | + modePop(p); |
| 34629 | + p->nPopMode = 0; |
| 32680 | 34630 | } |
| 32681 | 34631 | p->bSafeMode = p->bSafeModePersist; |
| 32682 | 34632 | qss = QSS_Start; |
| 32683 | 34633 | }else if( nSql && QSS_PLAINWHITE(qss) ){ |
| 32684 | 34634 | echo_group_input(p, zSql); |
| | @@ -32693,10 +34643,12 @@ |
| 32693 | 34643 | CONTINUE_PROMPT_RESET; |
| 32694 | 34644 | } |
| 32695 | 34645 | free(zSql); |
| 32696 | 34646 | free(zLine); |
| 32697 | 34647 | --p->inputNesting; |
| 34648 | + p->zInFile = saved_zInFile; |
| 34649 | + p->lineno = saved_lineno; |
| 32698 | 34650 | return errCnt>0; |
| 32699 | 34651 | } |
| 32700 | 34652 | |
| 32701 | 34653 | /* |
| 32702 | 34654 | ** Return a pathname which is the user's home directory. A |
| | @@ -32853,17 +34805,17 @@ |
| 32853 | 34805 | shell_check_oom(sqliterc); |
| 32854 | 34806 | } |
| 32855 | 34807 | p->in = sqliterc ? sqlite3_fopen(sqliterc,"rb") : 0; |
| 32856 | 34808 | if( p->in ){ |
| 32857 | 34809 | if( stdin_is_interactive ){ |
| 32858 | | - sqlite3_fprintf(stderr,"-- Loading resources from %s\n", sqliterc); |
| 34810 | + cli_printf(stderr,"-- Loading resources from %s\n", sqliterc); |
| 32859 | 34811 | } |
| 32860 | | - if( process_input(p, sqliterc) && bail_on_error ) exit(1); |
| 34812 | + if( process_input(p, sqliterc) && bail_on_error ) cli_exit(1); |
| 32861 | 34813 | fclose(p->in); |
| 32862 | 34814 | }else if( sqliterc_override!=0 ){ |
| 32863 | | - sqlite3_fprintf(stderr,"cannot open: \"%s\"\n", sqliterc); |
| 32864 | | - if( bail_on_error ) exit(1); |
| 34815 | + cli_printf(stderr,"cannot open: \"%s\"\n", sqliterc); |
| 34816 | + if( bail_on_error ) cli_exit(1); |
| 32865 | 34817 | } |
| 32866 | 34818 | p->in = inSaved; |
| 32867 | 34819 | p->lineno = savedLineno; |
| 32868 | 34820 | if( sqliterc != sqliterc_override ){ |
| 32869 | 34821 | sqlite3_free(sqliterc); |
| | @@ -32881,12 +34833,13 @@ |
| 32881 | 34833 | " -append append the database to the end of the file\n" |
| 32882 | 34834 | " -ascii set output mode to 'ascii'\n" |
| 32883 | 34835 | " -bail stop after hitting an error\n" |
| 32884 | 34836 | " -batch force batch I/O\n" |
| 32885 | 34837 | " -box set output mode to 'box'\n" |
| 32886 | | - " -column set output mode to 'column'\n" |
| 32887 | 34838 | " -cmd COMMAND run \"COMMAND\" before reading stdin\n" |
| 34839 | + " -column set output mode to 'column'\n" |
| 34840 | + " -compat YYYYMMDD set default options for date YYYYMMDD\n" |
| 32888 | 34841 | " -csv set output mode to 'csv'\n" |
| 32889 | 34842 | #if !defined(SQLITE_OMIT_DESERIALIZE) |
| 32890 | 34843 | " -deserialize open the database using sqlite3_deserialize()\n" |
| 32891 | 34844 | #endif |
| 32892 | 34845 | " -echo print inputs before execution\n" |
| | @@ -32913,18 +34866,20 @@ |
| 32913 | 34866 | #ifdef SQLITE_ENABLE_MULTIPLEX |
| 32914 | 34867 | " -multiplex enable the multiplexor VFS\n" |
| 32915 | 34868 | #endif |
| 32916 | 34869 | " -newline SEP set output row separator. Default: '\\n'\n" |
| 32917 | 34870 | " -nofollow refuse to open symbolic links to database files\n" |
| 34871 | + " -noinit Do not read the ~/.sqliterc file at startup\n" |
| 32918 | 34872 | " -nonce STRING set the safe-mode escape nonce\n" |
| 32919 | 34873 | " -no-rowid-in-view Disable rowid-in-view using sqlite3_config()\n" |
| 32920 | 34874 | " -nullvalue TEXT set text string for NULL values. Default ''\n" |
| 32921 | 34875 | " -pagecache SIZE N use N slots of SZ bytes each for page cache memory\n" |
| 32922 | 34876 | " -pcachetrace trace all page cache operations\n" |
| 32923 | 34877 | " -quote set output mode to 'quote'\n" |
| 32924 | 34878 | " -readonly open the database read-only\n" |
| 32925 | 34879 | " -safe enable safe-mode\n" |
| 34880 | + " -screenwidth N use N as the default screenwidth \n" |
| 32926 | 34881 | " -separator SEP set output column separator. Default: '|'\n" |
| 32927 | 34882 | #ifdef SQLITE_ENABLE_SORTER_REFERENCES |
| 32928 | 34883 | " -sorterref SIZE sorter references threshold size\n" |
| 32929 | 34884 | #endif |
| 32930 | 34885 | " -stats print memory stats before each finalize\n" |
| | @@ -32937,15 +34892,15 @@ |
| 32937 | 34892 | #ifdef SQLITE_HAVE_ZLIB |
| 32938 | 34893 | " -zip open the file as a ZIP Archive\n" |
| 32939 | 34894 | #endif |
| 32940 | 34895 | ; |
| 32941 | 34896 | static void usage(int showDetail){ |
| 32942 | | - sqlite3_fprintf(stderr,"Usage: %s [OPTIONS] [FILENAME [SQL...]]\n" |
| 34897 | + cli_printf(stderr,"Usage: %s [OPTIONS] [FILENAME [SQL...]]\n" |
| 32943 | 34898 | "FILENAME is the name of an SQLite database. A new database is created\n" |
| 32944 | 34899 | "if the file does not previously exist. Defaults to :memory:.\n", Argv0); |
| 32945 | 34900 | if( showDetail ){ |
| 32946 | | - sqlite3_fprintf(stderr,"OPTIONS include:\n%s", zOptions); |
| 34901 | + cli_printf(stderr,"OPTIONS include:\n%s", zOptions); |
| 32947 | 34902 | }else{ |
| 32948 | 34903 | eputz("Use the -help option for additional information\n"); |
| 32949 | 34904 | } |
| 32950 | 34905 | exit(0); |
| 32951 | 34906 | } |
| | @@ -32962,23 +34917,20 @@ |
| 32962 | 34917 | } |
| 32963 | 34918 | |
| 32964 | 34919 | /* |
| 32965 | 34920 | ** Initialize the state information in data |
| 32966 | 34921 | */ |
| 32967 | | -static void main_init(ShellState *data) { |
| 32968 | | - memset(data, 0, sizeof(*data)); |
| 32969 | | - data->normalMode = data->cMode = data->mode = MODE_List; |
| 32970 | | - data->autoExplain = 1; |
| 32971 | | -#ifdef _WIN32 |
| 32972 | | - data->crlfMode = 1; |
| 34922 | +static void main_init(ShellState *p) { |
| 34923 | + memset(p, 0, sizeof(*p)); |
| 34924 | +#if defined(COMPATIBILITY_DATE) |
| 34925 | + p->iCompat = COMPATIBILITY_DATE; |
| 34926 | +#else |
| 34927 | + p->iCompat = 20251116; |
| 32973 | 34928 | #endif |
| 32974 | | - data->pAuxDb = &data->aAuxDb[0]; |
| 32975 | | - memcpy(data->colSeparator,SEP_Column, 2); |
| 32976 | | - memcpy(data->rowSeparator,SEP_Row, 2); |
| 32977 | | - data->showHeader = 0; |
| 32978 | | - data->shellFlgs = SHFLG_Lookaside; |
| 32979 | | - sqlite3_config(SQLITE_CONFIG_LOG, shellLog, data); |
| 34929 | + p->pAuxDb = &p->aAuxDb[0]; |
| 34930 | + p->shellFlgs = SHFLG_Lookaside; |
| 34931 | + sqlite3_config(SQLITE_CONFIG_LOG, shellLog, p); |
| 32980 | 34932 | #if !defined(SQLITE_SHELL_FIDDLE) |
| 32981 | 34933 | verify_uninitialized(); |
| 32982 | 34934 | #endif |
| 32983 | 34935 | sqlite3_config(SQLITE_CONFIG_URI, 1); |
| 32984 | 34936 | sqlite3_config(SQLITE_CONFIG_MULTITHREAD); |
| | @@ -33004,23 +34956,23 @@ |
| 33004 | 34956 | SetConsoleTextAttribute(out, defaultScreenInfo.wAttributes); |
| 33005 | 34957 | #endif |
| 33006 | 34958 | } |
| 33007 | 34959 | #else |
| 33008 | 34960 | static void printBold(const char *zText){ |
| 33009 | | - sqlite3_fprintf(stdout, "\033[1m%s\033[0m", zText); |
| 34961 | + cli_printf(stdout, "\033[1m%s\033[0m", zText); |
| 33010 | 34962 | } |
| 33011 | 34963 | #endif |
| 33012 | 34964 | |
| 33013 | 34965 | /* |
| 33014 | 34966 | ** Get the argument to an --option. Throw an error and die if no argument |
| 33015 | 34967 | ** is available. |
| 33016 | 34968 | */ |
| 33017 | 34969 | static char *cmdline_option_value(int argc, char **argv, int i){ |
| 33018 | 34970 | if( i==argc ){ |
| 33019 | | - sqlite3_fprintf(stderr, |
| 34971 | + cli_printf(stderr, |
| 33020 | 34972 | "%s: Error: missing argument to %s\n", argv[0], argv[argc-1]); |
| 33021 | | - exit(1); |
| 34973 | + cli_exit(1); |
| 33022 | 34974 | } |
| 33023 | 34975 | return argv[i]; |
| 33024 | 34976 | } |
| 33025 | 34977 | |
| 33026 | 34978 | static void sayAbnormalExit(void){ |
| | @@ -33029,11 +34981,11 @@ |
| 33029 | 34981 | |
| 33030 | 34982 | /* Routine to output from vfstrace |
| 33031 | 34983 | */ |
| 33032 | 34984 | static int vfstraceOut(const char *z, void *pArg){ |
| 33033 | 34985 | ShellState *p = (ShellState*)pArg; |
| 33034 | | - sqlite3_fputs(z, p->out); |
| 34986 | + cli_puts(z, p->out); |
| 33035 | 34987 | fflush(p->out); |
| 33036 | 34988 | return 1; |
| 33037 | 34989 | } |
| 33038 | 34990 | |
| 33039 | 34991 | #ifndef SQLITE_SHELL_IS_UTF8 |
| | @@ -33067,14 +35019,16 @@ |
| 33067 | 35019 | const char *zInitFile = 0; |
| 33068 | 35020 | int i; |
| 33069 | 35021 | int rc = 0; |
| 33070 | 35022 | int warnInmemoryDb = 0; |
| 33071 | 35023 | int readStdin = 1; |
| 35024 | + int noInit = 0; /* Do not read ~/.sqliterc if true */ |
| 33072 | 35025 | int nCmd = 0; |
| 33073 | 35026 | int nOptsEnd = argc; |
| 33074 | 35027 | int bEnableVfstrace = 0; |
| 33075 | 35028 | char **azCmd = 0; |
| 35029 | + int *aiCmd = 0; |
| 33076 | 35030 | const char *zVfs = 0; /* Value of -vfs command-line option */ |
| 33077 | 35031 | #if !SQLITE_SHELL_IS_UTF8 |
| 33078 | 35032 | char **argvToFree = 0; |
| 33079 | 35033 | int argcToFree = 0; |
| 33080 | 35034 | #endif |
| | @@ -33094,11 +35048,11 @@ |
| 33094 | 35048 | #endif |
| 33095 | 35049 | #if !defined(_WIN32_WCE) |
| 33096 | 35050 | if( getenv("SQLITE_DEBUG_BREAK") ){ |
| 33097 | 35051 | if( isatty(0) && isatty(2) ){ |
| 33098 | 35052 | char zLine[100]; |
| 33099 | | - sqlite3_fprintf(stderr, |
| 35053 | + cli_printf(stderr, |
| 33100 | 35054 | "attach debugger to process %d and press ENTER to continue...", |
| 33101 | 35055 | GETPID()); |
| 33102 | 35056 | if( sqlite3_fgets(zLine, sizeof(zLine), stdin)!=0 |
| 33103 | 35057 | && cli_strcmp(zLine,"stop")==0 |
| 33104 | 35058 | ){ |
| | @@ -33126,11 +35080,11 @@ |
| 33126 | 35080 | } |
| 33127 | 35081 | #endif |
| 33128 | 35082 | |
| 33129 | 35083 | #if USE_SYSTEM_SQLITE+0!=1 |
| 33130 | 35084 | if( cli_strncmp(sqlite3_sourceid(),SQLITE_SOURCE_ID,60)!=0 ){ |
| 33131 | | - sqlite3_fprintf(stderr, |
| 35085 | + cli_printf(stderr, |
| 33132 | 35086 | "SQLite header and source version mismatch\n%s\n%s\n", |
| 33133 | 35087 | sqlite3_sourceid(), SQLITE_SOURCE_ID); |
| 33134 | 35088 | exit(1); |
| 33135 | 35089 | } |
| 33136 | 35090 | #endif |
| | @@ -33193,14 +35147,18 @@ |
| 33193 | 35147 | data.aAuxDb->zDbFilename = z; |
| 33194 | 35148 | }else{ |
| 33195 | 35149 | /* Excess arguments are interpreted as SQL (or dot-commands) and |
| 33196 | 35150 | ** mean that nothing is read from stdin */ |
| 33197 | 35151 | readStdin = 0; |
| 35152 | + stdin_is_interactive = 0; |
| 33198 | 35153 | nCmd++; |
| 33199 | 35154 | azCmd = realloc(azCmd, sizeof(azCmd[0])*nCmd); |
| 33200 | 35155 | shell_check_oom(azCmd); |
| 35156 | + aiCmd = realloc(aiCmd, sizeof(aiCmd[0])*nCmd); |
| 35157 | + shell_check_oom(azCmd); |
| 33201 | 35158 | azCmd[nCmd-1] = z; |
| 35159 | + aiCmd[nCmd-1] = i; |
| 33202 | 35160 | } |
| 33203 | 35161 | continue; |
| 33204 | 35162 | } |
| 33205 | 35163 | if( z[1]=='-' ) z++; |
| 33206 | 35164 | if( cli_strcmp(z, "-")==0 ){ |
| | @@ -33219,10 +35177,24 @@ |
| 33219 | 35177 | /* Need to check for batch mode here to so we can avoid printing |
| 33220 | 35178 | ** informational messages (like from process_sqliterc) before |
| 33221 | 35179 | ** we do the actual processing of arguments later in a second pass. |
| 33222 | 35180 | */ |
| 33223 | 35181 | stdin_is_interactive = 0; |
| 35182 | + stdout_is_console = 0; |
| 35183 | + modeChange(&data, MODE_BATCH); |
| 35184 | + }else if( cli_strcmp(z,"-screenwidth")==0 ){ |
| 35185 | + int n = atoi(cmdline_option_value(argc, argv, ++i)); |
| 35186 | + if( n<2 ){ |
| 35187 | + sqlite3_fprintf(stderr,"minimum --screenwidth is 2\n"); |
| 35188 | + exit(1); |
| 35189 | + } |
| 35190 | + stdout_tty_width = n; |
| 35191 | + }else if( cli_strcmp(z,"-compat")==0 ){ |
| 35192 | + data.iCompat = atoi(cmdline_option_value(argc, argv, ++i)); |
| 35193 | + modeFree(&data.mode); |
| 35194 | + memset(&data.mode, 0, sizeof(data.mode)); |
| 35195 | + modeDefault(&data); |
| 33224 | 35196 | }else if( cli_strcmp(z,"-utf8")==0 ){ |
| 33225 | 35197 | }else if( cli_strcmp(z,"-no-utf8")==0 ){ |
| 33226 | 35198 | }else if( cli_strcmp(z,"-no-rowid-in-view")==0 ){ |
| 33227 | 35199 | int val = 0; |
| 33228 | 35200 | sqlite3_config(SQLITE_CONFIG_ROWID_IN_VIEW, &val); |
| | @@ -33252,11 +35224,11 @@ |
| 33252 | 35224 | if( sz>0 && (sz & (sz-1))==0 ){ |
| 33253 | 35225 | /* If SIZE is a power of two, round it up by the PCACHE_HDRSZ */ |
| 33254 | 35226 | int szHdr = 0; |
| 33255 | 35227 | sqlite3_config(SQLITE_CONFIG_PCACHE_HDRSZ, &szHdr); |
| 33256 | 35228 | sz += szHdr; |
| 33257 | | - sqlite3_fprintf(stdout, "Page cache size increased to %d to accommodate" |
| 35229 | + cli_printf(stdout, "Page cache size increased to %d to accommodate" |
| 33258 | 35230 | " the %d-byte headers\n", (int)sz, szHdr); |
| 33259 | 35231 | } |
| 33260 | 35232 | verify_uninitialized(); |
| 33261 | 35233 | sqlite3_config(SQLITE_CONFIG_PAGECACHE, |
| 33262 | 35234 | (n>0 && sz>0) ? malloc(n*sz) : 0, sz, n); |
| | @@ -33313,10 +35285,12 @@ |
| 33313 | 35285 | }else if( cli_strcmp(z,"-readonly")==0 ){ |
| 33314 | 35286 | data.openFlags &= ~(SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE); |
| 33315 | 35287 | data.openFlags |= SQLITE_OPEN_READONLY; |
| 33316 | 35288 | }else if( cli_strcmp(z,"-nofollow")==0 ){ |
| 33317 | 35289 | data.openFlags |= SQLITE_OPEN_NOFOLLOW; |
| 35290 | + }else if( cli_strcmp(z,"-noinit")==0 ){ |
| 35291 | + noInit = 1; |
| 33318 | 35292 | }else if( cli_strcmp(z,"-exclusive")==0 ){ /* UNDOCUMENTED */ |
| 33319 | 35293 | data.openFlags |= SQLITE_OPEN_EXCLUSIVE; |
| 33320 | 35294 | }else if( cli_strcmp(z,"-ifexists")==0 ){ |
| 33321 | 35295 | data.openFlags &= ~(SQLITE_OPEN_CREATE); |
| 33322 | 35296 | if( data.openFlags==0 ) data.openFlags = SQLITE_OPEN_READWRITE; |
| | @@ -33367,21 +35341,21 @@ |
| 33367 | 35341 | if( zVfs ){ |
| 33368 | 35342 | sqlite3_vfs *pVfs = sqlite3_vfs_find(zVfs); |
| 33369 | 35343 | if( pVfs ){ |
| 33370 | 35344 | sqlite3_vfs_register(pVfs, 1); |
| 33371 | 35345 | }else{ |
| 33372 | | - sqlite3_fprintf(stderr,"no such VFS: \"%s\"\n", zVfs); |
| 35346 | + cli_printf(stderr,"no such VFS: \"%s\"\n", zVfs); |
| 33373 | 35347 | exit(1); |
| 33374 | 35348 | } |
| 33375 | 35349 | } |
| 33376 | 35350 | |
| 33377 | 35351 | if( data.pAuxDb->zDbFilename==0 ){ |
| 33378 | 35352 | #ifndef SQLITE_OMIT_MEMORYDB |
| 33379 | 35353 | data.pAuxDb->zDbFilename = ":memory:"; |
| 33380 | 35354 | warnInmemoryDb = argc==1; |
| 33381 | 35355 | #else |
| 33382 | | - sqlite3_fprintf(stderr, |
| 35356 | + cli_printf(stderr, |
| 33383 | 35357 | "%s: Error: no database filename specified\n", Argv0); |
| 33384 | 35358 | return 1; |
| 33385 | 35359 | #endif |
| 33386 | 35360 | } |
| 33387 | 35361 | data.out = stdout; |
| | @@ -33389,10 +35363,11 @@ |
| 33389 | 35363 | vfstrace_register("trace",0,vfstraceOut, &data, 1); |
| 33390 | 35364 | } |
| 33391 | 35365 | #ifndef SQLITE_SHELL_FIDDLE |
| 33392 | 35366 | sqlite3_appendvfs_init(0,0,0); |
| 33393 | 35367 | #endif |
| 35368 | + modeDefault(&data); |
| 33394 | 35369 | |
| 33395 | 35370 | /* Go ahead and open the database file if it already exists. If the |
| 33396 | 35371 | ** file does not exist, delay opening it. This prevents empty database |
| 33397 | 35372 | ** files from being created if a user mistypes the database name argument |
| 33398 | 35373 | ** to the sqlite command-line tool. |
| | @@ -33403,11 +35378,11 @@ |
| 33403 | 35378 | |
| 33404 | 35379 | /* Process the initialization file if there is one. If no -init option |
| 33405 | 35380 | ** is given on the command line, look for a file named ~/.sqliterc and |
| 33406 | 35381 | ** try to process it. |
| 33407 | 35382 | */ |
| 33408 | | - process_sqliterc(&data,zInitFile); |
| 35383 | + if( !noInit ) process_sqliterc(&data,zInitFile); |
| 33409 | 35384 | |
| 33410 | 35385 | /* Make a second pass through the command-line argument and set |
| 33411 | 35386 | ** options. This second pass is delayed until after the initialization |
| 33412 | 35387 | ** file is processed so that the command-line arguments will override |
| 33413 | 35388 | ** settings in the initialization file. |
| | @@ -33417,49 +35392,46 @@ |
| 33417 | 35392 | if( z[0]!='-' || i>=nOptsEnd ) continue; |
| 33418 | 35393 | if( z[1]=='-' ){ z++; } |
| 33419 | 35394 | if( cli_strcmp(z,"-init")==0 ){ |
| 33420 | 35395 | i++; |
| 33421 | 35396 | }else if( cli_strcmp(z,"-html")==0 ){ |
| 33422 | | - data.mode = MODE_Html; |
| 35397 | + modeChange(&data, MODE_Html); |
| 33423 | 35398 | }else if( cli_strcmp(z,"-list")==0 ){ |
| 33424 | | - data.mode = MODE_List; |
| 35399 | + modeChange(&data, MODE_List); |
| 33425 | 35400 | }else if( cli_strcmp(z,"-quote")==0 ){ |
| 33426 | | - data.mode = MODE_Quote; |
| 33427 | | - sqlite3_snprintf(sizeof(data.colSeparator), data.colSeparator, SEP_Comma); |
| 33428 | | - sqlite3_snprintf(sizeof(data.rowSeparator), data.rowSeparator, SEP_Row); |
| 35401 | + modeChange(&data, MODE_Quote); |
| 33429 | 35402 | }else if( cli_strcmp(z,"-line")==0 ){ |
| 33430 | | - data.mode = MODE_Line; |
| 35403 | + modeChange(&data, MODE_Line); |
| 33431 | 35404 | }else if( cli_strcmp(z,"-column")==0 ){ |
| 33432 | | - data.mode = MODE_Column; |
| 35405 | + modeChange(&data, MODE_Column); |
| 33433 | 35406 | }else if( cli_strcmp(z,"-json")==0 ){ |
| 33434 | | - data.mode = MODE_Json; |
| 35407 | + modeChange(&data, MODE_Json); |
| 33435 | 35408 | }else if( cli_strcmp(z,"-markdown")==0 ){ |
| 33436 | | - data.mode = MODE_Markdown; |
| 35409 | + modeChange(&data, MODE_Markdown); |
| 33437 | 35410 | }else if( cli_strcmp(z,"-table")==0 ){ |
| 33438 | | - data.mode = MODE_Table; |
| 35411 | + modeChange(&data, MODE_Table); |
| 33439 | 35412 | }else if( cli_strcmp(z,"-box")==0 ){ |
| 33440 | | - data.mode = MODE_Box; |
| 35413 | + modeChange(&data, MODE_Box); |
| 33441 | 35414 | }else if( cli_strcmp(z,"-csv")==0 ){ |
| 33442 | | - data.mode = MODE_Csv; |
| 33443 | | - memcpy(data.colSeparator,",",2); |
| 35415 | + modeChange(&data, MODE_Csv); |
| 33444 | 35416 | }else if( cli_strcmp(z,"-escape")==0 && i+1<argc ){ |
| 33445 | 35417 | /* See similar code at tag-20250224-1 */ |
| 33446 | 35418 | const char *zEsc = argv[++i]; |
| 33447 | 35419 | int k; |
| 33448 | | - for(k=0; k<ArraySize(shell_EscModeNames); k++){ |
| 33449 | | - if( sqlite3_stricmp(zEsc,shell_EscModeNames[k])==0 ){ |
| 33450 | | - data.eEscMode = k; |
| 35420 | + for(k=0; k<ArraySize(qrfEscNames); k++){ |
| 35421 | + if( sqlite3_stricmp(zEsc,qrfEscNames[k])==0 ){ |
| 35422 | + data.mode.spec.eEsc = k; |
| 33451 | 35423 | break; |
| 33452 | 35424 | } |
| 33453 | 35425 | } |
| 33454 | | - if( k>=ArraySize(shell_EscModeNames) ){ |
| 33455 | | - sqlite3_fprintf(stderr, "unknown control character escape mode \"%s\"" |
| 35426 | + if( k>=ArraySize(qrfEscNames) ){ |
| 35427 | + cli_printf(stderr, "unknown control character escape mode \"%s\"" |
| 33456 | 35428 | " - choices:", zEsc); |
| 33457 | | - for(k=0; k<ArraySize(shell_EscModeNames); k++){ |
| 33458 | | - sqlite3_fprintf(stderr, " %s", shell_EscModeNames[k]); |
| 35429 | + for(k=0; k<ArraySize(qrfEscNames); k++){ |
| 35430 | + cli_printf(stderr, " %s", qrfEscNames[k]); |
| 33459 | 35431 | } |
| 33460 | | - sqlite3_fprintf(stderr, "\n"); |
| 35432 | + cli_printf(stderr, "\n"); |
| 33461 | 35433 | exit(1); |
| 33462 | 35434 | } |
| 33463 | 35435 | #ifdef SQLITE_HAVE_ZLIB |
| 33464 | 35436 | }else if( cli_strcmp(z,"-zip")==0 ){ |
| 33465 | 35437 | data.openMode = SHELL_OPEN_ZIPFILE; |
| | @@ -33475,48 +35447,44 @@ |
| 33475 | 35447 | }else if( cli_strcmp(z,"-readonly")==0 ){ |
| 33476 | 35448 | data.openFlags &= ~(SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE); |
| 33477 | 35449 | data.openFlags |= SQLITE_OPEN_READONLY; |
| 33478 | 35450 | }else if( cli_strcmp(z,"-nofollow")==0 ){ |
| 33479 | 35451 | data.openFlags |= SQLITE_OPEN_NOFOLLOW; |
| 35452 | + }else if( cli_strcmp(z,"-noinit")==0 ){ |
| 35453 | + /* No-op */ |
| 33480 | 35454 | }else if( cli_strcmp(z,"-exclusive")==0 ){ /* UNDOCUMENTED */ |
| 33481 | 35455 | data.openFlags |= SQLITE_OPEN_EXCLUSIVE; |
| 33482 | 35456 | }else if( cli_strcmp(z,"-ifexists")==0 ){ |
| 33483 | 35457 | data.openFlags &= ~(SQLITE_OPEN_CREATE); |
| 33484 | 35458 | if( data.openFlags==0 ) data.openFlags = SQLITE_OPEN_READWRITE; |
| 33485 | 35459 | }else if( cli_strcmp(z,"-ascii")==0 ){ |
| 33486 | | - data.mode = MODE_Ascii; |
| 33487 | | - sqlite3_snprintf(sizeof(data.colSeparator), data.colSeparator,SEP_Unit); |
| 33488 | | - sqlite3_snprintf(sizeof(data.rowSeparator), data.rowSeparator,SEP_Record); |
| 35460 | + modeChange(&data, MODE_Ascii); |
| 33489 | 35461 | }else if( cli_strcmp(z,"-tabs")==0 ){ |
| 33490 | | - data.mode = MODE_List; |
| 33491 | | - sqlite3_snprintf(sizeof(data.colSeparator), data.colSeparator,SEP_Tab); |
| 33492 | | - sqlite3_snprintf(sizeof(data.rowSeparator), data.rowSeparator,SEP_Row); |
| 35462 | + modeChange(&data, MODE_Tabs); |
| 33493 | 35463 | }else if( cli_strcmp(z,"-separator")==0 ){ |
| 33494 | | - sqlite3_snprintf(sizeof(data.colSeparator), data.colSeparator, |
| 33495 | | - "%s",cmdline_option_value(argc,argv,++i)); |
| 35464 | + modeSetStr(&data.mode.spec.zColumnSep, |
| 35465 | + cmdline_option_value(argc,argv,++i)); |
| 33496 | 35466 | }else if( cli_strcmp(z,"-newline")==0 ){ |
| 33497 | | - sqlite3_snprintf(sizeof(data.rowSeparator), data.rowSeparator, |
| 33498 | | - "%s",cmdline_option_value(argc,argv,++i)); |
| 35467 | + modeSetStr(&data.mode.spec.zRowSep, |
| 35468 | + cmdline_option_value(argc,argv,++i)); |
| 33499 | 35469 | }else if( cli_strcmp(z,"-nullvalue")==0 ){ |
| 33500 | | - sqlite3_snprintf(sizeof(data.nullValue), data.nullValue, |
| 33501 | | - "%s",cmdline_option_value(argc,argv,++i)); |
| 35470 | + modeSetStr(&data.mode.spec.zNull, |
| 35471 | + cmdline_option_value(argc,argv,++i)); |
| 33502 | 35472 | }else if( cli_strcmp(z,"-header")==0 ){ |
| 33503 | | - data.showHeader = 1; |
| 33504 | | - ShellSetFlag(&data, SHFLG_HeaderSet); |
| 35473 | + data.mode.spec.bTitles = QRF_Yes; |
| 33505 | 35474 | }else if( cli_strcmp(z,"-noheader")==0 ){ |
| 33506 | | - data.showHeader = 0; |
| 33507 | | - ShellSetFlag(&data, SHFLG_HeaderSet); |
| 35475 | + data.mode.spec.bTitles = QRF_No; |
| 33508 | 35476 | }else if( cli_strcmp(z,"-echo")==0 ){ |
| 33509 | | - ShellSetFlag(&data, SHFLG_Echo); |
| 35477 | + data.mode.mFlags |= MFLG_ECHO; |
| 33510 | 35478 | }else if( cli_strcmp(z,"-eqp")==0 ){ |
| 33511 | | - data.autoEQP = AUTOEQP_on; |
| 35479 | + data.mode.autoEQP = AUTOEQP_on; |
| 33512 | 35480 | }else if( cli_strcmp(z,"-eqpfull")==0 ){ |
| 33513 | | - data.autoEQP = AUTOEQP_full; |
| 35481 | + data.mode.autoEQP = AUTOEQP_full; |
| 33514 | 35482 | }else if( cli_strcmp(z,"-stats")==0 ){ |
| 33515 | 35483 | data.statsOn = 1; |
| 33516 | 35484 | }else if( cli_strcmp(z,"-scanstats")==0 ){ |
| 33517 | | - data.scanstatsOn = 1; |
| 35485 | + data.mode.scanstatsOn = 1; |
| 33518 | 35486 | }else if( cli_strcmp(z,"-backslash")==0 ){ |
| 33519 | 35487 | /* Undocumented command-line option: -backslash |
| 33520 | 35488 | ** Causes C-style backslash escapes to be evaluated in SQL statements |
| 33521 | 35489 | ** prior to sending the SQL into SQLite. Useful for injecting |
| 33522 | 35490 | ** crazy bytes in the middle of SQL statements for testing and debugging. |
| | @@ -33523,20 +35491,24 @@ |
| 33523 | 35491 | */ |
| 33524 | 35492 | ShellSetFlag(&data, SHFLG_Backslash); |
| 33525 | 35493 | }else if( cli_strcmp(z,"-bail")==0 ){ |
| 33526 | 35494 | /* No-op. The bail_on_error flag should already be set. */ |
| 33527 | 35495 | }else if( cli_strcmp(z,"-version")==0 ){ |
| 33528 | | - sqlite3_fprintf(stdout, "%s %s (%d-bit)\n", |
| 35496 | + cli_printf(stdout, "%s %s (%d-bit)\n", |
| 33529 | 35497 | sqlite3_libversion(), sqlite3_sourceid(), 8*(int)sizeof(char*)); |
| 33530 | 35498 | return 0; |
| 33531 | 35499 | }else if( cli_strcmp(z,"-interactive")==0 ){ |
| 33532 | 35500 | /* Need to check for interactive override here to so that it can |
| 33533 | 35501 | ** affect console setup (for Windows only) and testing thereof. |
| 33534 | 35502 | */ |
| 33535 | 35503 | stdin_is_interactive = 1; |
| 33536 | 35504 | }else if( cli_strcmp(z,"-batch")==0 ){ |
| 33537 | 35505 | /* already handled */ |
| 35506 | + }else if( cli_strcmp(z,"-screenwidth")==0 ){ |
| 35507 | + i++; |
| 35508 | + }else if( cli_strcmp(z,"-compat")==0 ){ |
| 35509 | + i++; |
| 33538 | 35510 | }else if( cli_strcmp(z,"-utf8")==0 ){ |
| 33539 | 35511 | /* already handled */ |
| 33540 | 35512 | }else if( cli_strcmp(z,"-no-utf8")==0 ){ |
| 33541 | 35513 | /* already handled */ |
| 33542 | 35514 | }else if( cli_strcmp(z,"-no-rowid-in-view")==0 ){ |
| | @@ -33584,20 +35556,21 @@ |
| 33584 | 35556 | }else{ |
| 33585 | 35557 | open_db(&data, 0); |
| 33586 | 35558 | rc = shell_exec(&data, z, &zErrMsg); |
| 33587 | 35559 | if( zErrMsg!=0 ){ |
| 33588 | 35560 | shellEmitError(zErrMsg); |
| 35561 | + sqlite3_free(zErrMsg); |
| 33589 | 35562 | if( bail_on_error ) return rc!=0 ? rc : 1; |
| 33590 | 35563 | }else if( rc!=0 ){ |
| 33591 | | - sqlite3_fprintf(stderr,"Error: unable to process SQL \"%s\"\n", z); |
| 35564 | + cli_printf(stderr,"Error: unable to process SQL \"%s\"\n", z); |
| 33592 | 35565 | if( bail_on_error ) return rc; |
| 33593 | 35566 | } |
| 33594 | 35567 | } |
| 33595 | 35568 | #if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_HAVE_ZLIB) |
| 33596 | 35569 | }else if( cli_strncmp(z, "-A", 2)==0 ){ |
| 33597 | 35570 | if( nCmd>0 ){ |
| 33598 | | - sqlite3_fprintf(stderr,"Error: cannot mix regular SQL or dot-commands" |
| 35571 | + cli_printf(stderr,"Error: cannot mix regular SQL or dot-commands" |
| 33599 | 35572 | " with \"%s\"\n", z); |
| 33600 | 35573 | return 1; |
| 33601 | 35574 | } |
| 33602 | 35575 | open_db(&data, OPEN_DB_ZIPFILE); |
| 33603 | 35576 | if( z[2] ){ |
| | @@ -33605,22 +35578,22 @@ |
| 33605 | 35578 | arDotCommand(&data, 1, argv+(i-1), argc-(i-1)); |
| 33606 | 35579 | }else{ |
| 33607 | 35580 | arDotCommand(&data, 1, argv+i, argc-i); |
| 33608 | 35581 | } |
| 33609 | 35582 | readStdin = 0; |
| 35583 | + stdin_is_interactive = 0; |
| 33610 | 35584 | break; |
| 33611 | 35585 | #endif |
| 33612 | 35586 | }else if( cli_strcmp(z,"-safe")==0 ){ |
| 33613 | 35587 | data.bSafeMode = data.bSafeModePersist = 1; |
| 33614 | 35588 | }else if( cli_strcmp(z,"-unsafe-testing")==0 ){ |
| 33615 | 35589 | /* Acted upon in first pass. */ |
| 33616 | 35590 | }else{ |
| 33617 | | - sqlite3_fprintf(stderr,"%s: Error: unknown option: %s\n", Argv0, z); |
| 35591 | + cli_printf(stderr,"%s: Error: unknown option: %s\n", Argv0, z); |
| 33618 | 35592 | eputz("Use -help for a list of options.\n"); |
| 33619 | 35593 | return 1; |
| 33620 | 35594 | } |
| 33621 | | - data.cMode = data.mode; |
| 33622 | 35595 | } |
| 33623 | 35596 | |
| 33624 | 35597 | if( !readStdin ){ |
| 33625 | 35598 | /* Run all arguments that do not begin with '-' as if they were separate |
| 33626 | 35599 | ** command-line inputs, except for the argToSkip argument which contains |
| | @@ -33627,11 +35600,18 @@ |
| 33627 | 35600 | ** the database filename. |
| 33628 | 35601 | */ |
| 33629 | 35602 | for(i=0; i<nCmd; i++){ |
| 33630 | 35603 | echo_group_input(&data, azCmd[i]); |
| 33631 | 35604 | if( azCmd[i][0]=='.' ){ |
| 35605 | + char *zErrCtx = malloc( 64 ); |
| 35606 | + shell_check_oom(zErrCtx); |
| 35607 | + sqlite3_snprintf(64,zErrCtx,"argv[%i]:",aiCmd[i]); |
| 35608 | + data.zInFile = "<cmdline>"; |
| 35609 | + data.zErrPrefix = zErrCtx; |
| 33632 | 35610 | rc = do_meta_command(azCmd[i], &data); |
| 35611 | + free(data.zErrPrefix); |
| 35612 | + data.zErrPrefix = 0; |
| 33633 | 35613 | if( rc ){ |
| 33634 | 35614 | if( rc==2 ) rc = 0; |
| 33635 | 35615 | goto shell_main_exit; |
| 33636 | 35616 | } |
| 33637 | 35617 | }else{ |
| | @@ -33639,11 +35619,11 @@ |
| 33639 | 35619 | rc = shell_exec(&data, azCmd[i], &zErrMsg); |
| 33640 | 35620 | if( zErrMsg || rc ){ |
| 33641 | 35621 | if( zErrMsg!=0 ){ |
| 33642 | 35622 | shellEmitError(zErrMsg); |
| 33643 | 35623 | }else{ |
| 33644 | | - sqlite3_fprintf(stderr, |
| 35624 | + cli_printf(stderr, |
| 33645 | 35625 | "Error: unable to process SQL: %s\n", azCmd[i]); |
| 33646 | 35626 | } |
| 33647 | 35627 | sqlite3_free(zErrMsg); |
| 33648 | 35628 | if( rc==0 ) rc = 1; |
| 33649 | 35629 | goto shell_main_exit; |
| | @@ -33654,11 +35634,11 @@ |
| 33654 | 35634 | /* Run commands received from standard input |
| 33655 | 35635 | */ |
| 33656 | 35636 | if( stdin_is_interactive ){ |
| 33657 | 35637 | char *zHome; |
| 33658 | 35638 | char *zHistory; |
| 33659 | | - sqlite3_fprintf(stdout, |
| 35639 | + cli_printf(stdout, |
| 33660 | 35640 | "SQLite version %s %.19s\n" /*extra-version-info*/ |
| 33661 | 35641 | "Enter \".help\" for usage hints.\n", |
| 33662 | 35642 | sqlite3_libversion(), sqlite3_sourceid()); |
| 33663 | 35643 | if( warnInmemoryDb ){ |
| 33664 | 35644 | sputz(stdout, "Connected to a "); |
| | @@ -33707,10 +35687,11 @@ |
| 33707 | 35687 | expertFinish(&data, 1, 0); |
| 33708 | 35688 | } |
| 33709 | 35689 | #endif |
| 33710 | 35690 | shell_main_exit: |
| 33711 | 35691 | free(azCmd); |
| 35692 | + free(aiCmd); |
| 33712 | 35693 | set_table_name(&data, 0); |
| 33713 | 35694 | if( data.db ){ |
| 33714 | 35695 | session_close_all(&data, -1); |
| 33715 | 35696 | close_db(data.db); |
| 33716 | 35697 | } |
| | @@ -33727,21 +35708,41 @@ |
| 33727 | 35708 | clearTempFile(&data); |
| 33728 | 35709 | #if !SQLITE_SHELL_IS_UTF8 |
| 33729 | 35710 | for(i=0; i<argcToFree; i++) free(argvToFree[i]); |
| 33730 | 35711 | free(argvToFree); |
| 33731 | 35712 | #endif |
| 33732 | | - free(data.colWidth); |
| 35713 | + modeFree(&data.mode); |
| 35714 | + if( data.nSavedModes ){ |
| 35715 | + int ii; |
| 35716 | + for(ii=0; ii<data.nSavedModes; ii++){ |
| 35717 | + modeFree(&data.aSavedModes[ii].mode); |
| 35718 | + free(data.aSavedModes[ii].zTag); |
| 35719 | + } |
| 35720 | + free(data.aSavedModes); |
| 35721 | + } |
| 35722 | + free(data.zErrPrefix); |
| 33733 | 35723 | free(data.zNonce); |
| 35724 | + free(data.dot.zCopy); |
| 35725 | + free(data.dot.azArg); |
| 35726 | + free(data.dot.aiOfst); |
| 35727 | + free(data.dot.abQuot); |
| 35728 | + if( data.nTestRun ){ |
| 35729 | + sqlite3_fprintf(stdout, "%d test%s run with %d error%s\n", |
| 35730 | + data.nTestRun, data.nTestRun==1 ? "" : "s", |
| 35731 | + data.nTestErr, data.nTestErr==1 ? "" : "s"); |
| 35732 | + fflush(stdout); |
| 35733 | + rc = data.nTestErr>0; |
| 35734 | + } |
| 33734 | 35735 | /* Clear the global data structure so that valgrind will detect memory |
| 33735 | 35736 | ** leaks */ |
| 33736 | 35737 | memset(&data, 0, sizeof(data)); |
| 33737 | 35738 | if( bEnableVfstrace ){ |
| 33738 | 35739 | vfstrace_unregister("trace"); |
| 33739 | 35740 | } |
| 33740 | 35741 | #ifdef SQLITE_DEBUG |
| 33741 | 35742 | if( sqlite3_memory_used()>mem_main_enter ){ |
| 33742 | | - sqlite3_fprintf(stderr,"Memory leaked: %u bytes\n", |
| 35743 | + cli_printf(stderr,"Memory leaked: %u bytes\n", |
| 33743 | 35744 | (unsigned int)(sqlite3_memory_used()-mem_main_enter)); |
| 33744 | 35745 | } |
| 33745 | 35746 | #endif |
| 33746 | 35747 | #else /* SQLITE_SHELL_FIDDLE... */ |
| 33747 | 35748 | shell_main_exit: |
| | @@ -33777,11 +35778,11 @@ |
| 33777 | 35778 | return pVfs; |
| 33778 | 35779 | } |
| 33779 | 35780 | |
| 33780 | 35781 | /* Only for emcc experimentation purposes. */ |
| 33781 | 35782 | sqlite3 * fiddle_db_arg(sqlite3 *arg){ |
| 33782 | | - sqlite3_fprintf(stdout, "fiddle_db_arg(%p)\n", (const void*)arg); |
| 35783 | + cli_printf(stdout, "fiddle_db_arg(%p)\n", (const void*)arg); |
| 33783 | 35784 | return arg; |
| 33784 | 35785 | } |
| 33785 | 35786 | |
| 33786 | 35787 | /* |
| 33787 | 35788 | ** Intended to be called via a SharedWorker() while a separate |
| | @@ -33814,11 +35815,11 @@ |
| 33814 | 35815 | while( sqlite3_txn_state(globalDb,0)>0 ){ |
| 33815 | 35816 | /* |
| 33816 | 35817 | ** Resolve problem reported in |
| 33817 | 35818 | ** https://sqlite.org/forum/forumpost/0b41a25d65 |
| 33818 | 35819 | */ |
| 33819 | | - sqlite3_fputs("Rolling back in-progress transaction.\n", stdout); |
| 35820 | + cli_puts("Rolling back in-progress transaction.\n", stdout); |
| 33820 | 35821 | sqlite3_exec(globalDb,"ROLLBACK", 0, 0, 0); |
| 33821 | 35822 | } |
| 33822 | 35823 | rc = sqlite3_db_config(globalDb, SQLITE_DBCONFIG_RESET_DATABASE, 1, 0); |
| 33823 | 35824 | if( 0==rc ) sqlite3_exec(globalDb, "VACUUM", 0, 0, 0); |
| 33824 | 35825 | sqlite3_db_config(globalDb, SQLITE_DBCONFIG_RESET_DATABASE, 0, 0); |
| | @@ -33881,5 +35882,6 @@ |
| 33881 | 35882 | process_input(&shellState, "<stdin>"); |
| 33882 | 35883 | shellState.wasm.zInput = shellState.wasm.zPos = 0; |
| 33883 | 35884 | } |
| 33884 | 35885 | } |
| 33885 | 35886 | #endif /* SQLITE_SHELL_FIDDLE */ |
| 35887 | +/************************* End src/shell.c.in ******************/ |
| 33886 | 35888 | |