Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Command-line shell enhancements. Bail out when errors are seen in non-interactive mode. Override isatty() using -interactive or -batch command-line options. Report line number in error messages. Tickets #2009, #2045. (CVS 3490) |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA1: |
3baa04cfb91039e27f642f6f78ef761b |
User & Date: | drh 2006-10-26 14:25:58.000 |
Context
2006-10-26
| ||
18:15 | Bring CVS output into more commonly accepted practice. Tickets #2030, #1573. Add command-line options -bail and ".bail" commands. Default behavior is to continue after encountering an error. Ticket #2045. (CVS 3491) (check-in: 517712d6fb user: drh tags: trunk) | |
14:25 | Command-line shell enhancements. Bail out when errors are seen in non-interactive mode. Override isatty() using -interactive or -batch command-line options. Report line number in error messages. Tickets #2009, #2045. (CVS 3490) (check-in: 3baa04cfb9 user: drh tags: trunk) | |
00:41 | Empty queries should get no results. My recent change ( http://www.sqlite.org/cvstrac/chngview?cn=3486 ) broke test fts2a-5.3. This change should make the expected result more obvious. (CVS 3489) (check-in: cde383eb46 user: shess tags: trunk) | |
Changes
Changes to src/shell.c.
︙ | ︙ | |||
8 9 10 11 12 13 14 | ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* ** This file contains code to implement the "sqlite" command line ** utility for accessing SQLite databases. ** | | | 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* ** This file contains code to implement the "sqlite" command line ** utility for accessing SQLite databases. ** ** $Id: shell.c,v 1.151 2006/10/26 14:25:58 drh Exp $ */ #include <stdlib.h> #include <string.h> #include <stdio.h> #include <assert.h> #include "sqlite3.h" #include <ctype.h> |
︙ | ︙ | |||
56 57 58 59 60 61 62 63 64 65 66 67 68 69 | # include <io.h> #else /* Make sure isatty() has a prototype. */ extern int isatty(); #endif /* ** The following is the open SQLite database. We make a pointer ** to this database a static variable so that it can be accessed ** by the SIGINT handler to interrupt database processing. */ static sqlite3 *db = 0; | > > > > > > | 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 | # include <io.h> #else /* Make sure isatty() has a prototype. */ extern int isatty(); #endif /* ** Threat stdin as an interactive input if the following variable ** is true. Otherwise, assume stdin is connected to a file or pipe. */ static int stdin_is_interactive = 1; /* ** The following is the open SQLite database. We make a pointer ** to this database a static variable so that it can be accessed ** by the SIGINT handler to interrupt database processing. */ static sqlite3 *db = 0; |
︙ | ︙ | |||
180 181 182 183 184 185 186 | } } zLine = realloc( zLine, n+1 ); return zLine; } /* | | < < < | 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 | } } zLine = realloc( zLine, n+1 ); return zLine; } /* ** Retrieve a single line of input text. ** ** zPrior is a string of prior text retrieved. If not the empty ** string, then issue a continuation prompt. */ static char *one_input_line(const char *zPrior, FILE *in){ char *zPrompt; char *zResult; |
︙ | ︙ | |||
789 790 791 792 793 794 795 | ".show Show the current values for various settings\n" ".tables ?PATTERN? List names of tables matching a LIKE pattern\n" ".timeout MS Try opening locked tables for MS milliseconds\n" ".width NUM NUM ... Set column widths for \"column\" mode\n" ; /* Forward reference */ | | | 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 | ".show Show the current values for various settings\n" ".tables ?PATTERN? List names of tables matching a LIKE pattern\n" ".timeout MS Try opening locked tables for MS milliseconds\n" ".width NUM NUM ... Set column widths for \"column\" mode\n" ; /* Forward reference */ static int process_input(struct callback_data *p, FILE *in); /* ** Make sure the database is open. If it is not, then open it. If ** the database fails to open, print an error message and exit. */ static void open_db(struct callback_data *p){ if( p->db==0 ){ |
︙ | ︙ | |||
848 849 850 851 852 853 854 855 856 857 858 859 860 861 | } } } z[j] = c; } z[j] = 0; } /* ** If an input line begins with "." then invoke this routine to ** process that line. ** ** Return 1 to exit and 0 to continue. */ | > > > > > > > > > > > > > > > > > | 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 | } } } z[j] = c; } z[j] = 0; } /* ** Interpret zArg as a boolean value. Return either 0 or 1. */ static int booleanValue(char *zArg){ int val = atoi(zArg); int j; for(j=0; zArg[j]; j++){ zArg[j] = tolower(zArg[j]); } if( strcmp(zArg,"on")==0 ){ val = 1; }else if( strcmp(zArg,"yes")==0 ){ val = 1; } return val; } /* ** If an input line begins with "." then invoke this routine to ** process that line. ** ** Return 1 to exit and 0 to continue. */ |
︙ | ︙ | |||
953 954 955 956 957 958 959 | sqlite3_free(zErrMsg); }else{ fprintf(p->out, "COMMIT;\n"); } }else if( c=='e' && strncmp(azArg[0], "echo", n)==0 && nArg>1 ){ | < < < < < < < < < < < | < < < | < < < < < < < < | 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 | sqlite3_free(zErrMsg); }else{ fprintf(p->out, "COMMIT;\n"); } }else if( c=='e' && strncmp(azArg[0], "echo", n)==0 && nArg>1 ){ p->echoOn = booleanValue(azArg[1]); }else if( c=='e' && strncmp(azArg[0], "exit", n)==0 ){ rc = 1; }else if( c=='e' && strncmp(azArg[0], "explain", n)==0 ){ int val = nArg>=2 ? booleanValue(azArg[1]) : 1; if(val == 1) { if(!p->explainPrev.valid) { p->explainPrev.valid = 1; p->explainPrev.mode = p->mode; p->explainPrev.showHeader = p->showHeader; memcpy(p->explainPrev.colWidth,p->colWidth,sizeof(p->colWidth)); } |
︙ | ︙ | |||
1014 1015 1016 1017 1018 1019 1020 | p->explainPrev.valid = 0; p->mode = p->explainPrev.mode; p->showHeader = p->explainPrev.showHeader; memcpy(p->colWidth,p->explainPrev.colWidth,sizeof(p->colWidth)); } }else | | < < < < < < < < < < < < | | 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 | p->explainPrev.valid = 0; p->mode = p->explainPrev.mode; p->showHeader = p->explainPrev.showHeader; memcpy(p->colWidth,p->explainPrev.colWidth,sizeof(p->colWidth)); } }else if( c=='h' && (strncmp(azArg[0], "header", n)==0 || strncmp(azArg[0], "headers", n)==0 )&& nArg>1 ){ p->showHeader = booleanValue(azArg[1]); }else if( c=='h' && strncmp(azArg[0], "help", n)==0 ){ fprintf(stderr,zHelp); }else if( c=='i' && strncmp(azArg[0], "import", n)==0 && nArg>=3 ){ |
︙ | ︙ | |||
1478 1479 1480 1481 1482 1483 1484 1485 | /* ** Read input from *in and process it. If *in==0 then input ** is interactive - the user is typing it it. Otherwise, input ** is coming from a file or device. A prompt is issued and history ** is saved only if input is interactive. An interrupt signal will ** cause this routine to exit immediately, unless input is interactive. */ | > > | | > > > > > > | 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 | /* ** Read input from *in and process it. If *in==0 then input ** is interactive - the user is typing it it. Otherwise, input ** is coming from a file or device. A prompt is issued and history ** is saved only if input is interactive. An interrupt signal will ** cause this routine to exit immediately, unless input is interactive. ** ** Return the number of errors. */ static int process_input(struct callback_data *p, FILE *in){ char *zLine; char *zSql = 0; int nSql = 0; char *zErrMsg; int rc = 0; int lineno = 0; int startline = 0; while( fflush(p->out), (zLine = one_input_line(zSql, in))!=0 ){ if( seenInterrupt ){ if( in!=0 ) break; seenInterrupt = 0; } if( rc && (in!=0 || !stdin_is_interactive) ){ break; } lineno++; if( p->echoOn ) printf("%s\n", zLine); if( (zSql==0 || zSql[0]==0) && _all_whitespace(zLine) ) continue; if( zLine && zLine[0]=='.' && nSql==0 ){ int rc = do_meta_command(zLine, p); free(zLine); if( rc ) break; continue; |
︙ | ︙ | |||
1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 | nSql = strlen(zLine); zSql = malloc( nSql+1 ); if( zSql==0 ){ fprintf(stderr, "out of memory\n"); exit(1); } strcpy(zSql, zLine); } }else{ int len = strlen(zLine); zSql = realloc( zSql, nSql + len + 2 ); if( zSql==0 ){ fprintf(stderr,"%s: out of memory!\n", Argv0); exit(1); } strcpy(&zSql[nSql++], "\n"); strcpy(&zSql[nSql], zLine); nSql += len; } free(zLine); if( zSql && _ends_with_semicolon(zSql, nSql) && sqlite3_complete(zSql) ){ p->cnt = 0; open_db(p); rc = sqlite3_exec(p->db, zSql, callback, p, &zErrMsg); if( rc || zErrMsg ){ | > > | > > > > | | > | 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 | nSql = strlen(zLine); zSql = malloc( nSql+1 ); if( zSql==0 ){ fprintf(stderr, "out of memory\n"); exit(1); } strcpy(zSql, zLine); startline = lineno; } }else{ int len = strlen(zLine); zSql = realloc( zSql, nSql + len + 2 ); if( zSql==0 ){ fprintf(stderr,"%s: out of memory!\n", Argv0); exit(1); } strcpy(&zSql[nSql++], "\n"); strcpy(&zSql[nSql], zLine); nSql += len; } free(zLine); if( zSql && _ends_with_semicolon(zSql, nSql) && sqlite3_complete(zSql) ){ p->cnt = 0; open_db(p); rc = sqlite3_exec(p->db, zSql, callback, p, &zErrMsg); if( rc || zErrMsg ){ char zPrefix[100]; if( in!=0 || !stdin_is_interactive ){ sprintf(zPrefix, "SQL error near line %d:", startline); }else{ sprintf(zPrefix, "SQL error:"); } if( zErrMsg!=0 ){ printf("%s %s\n", zPrefix, zErrMsg); sqlite3_free(zErrMsg); zErrMsg = 0; }else{ printf("%s %s\n", zPrefix, sqlite3_errmsg(p->db)); } } free(zSql); zSql = 0; nSql = 0; } } if( zSql ){ if( !_all_whitespace(zSql) ) printf("Incomplete SQL: %s\n", zSql); free(zSql); } return rc; } /* ** Return a pathname which is the user's home directory. A ** 0 return indicates an error of some kind. Space to hold the ** resulting string is obtained from malloc(). The calling ** function should free the result. |
︙ | ︙ | |||
1638 1639 1640 1641 1642 1643 1644 | } sprintf(zBuf,"%s/.sqliterc",home_dir); free(home_dir); sqliterc = (const char*)zBuf; } in = fopen(sqliterc,"rb"); if( in ){ | | | 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 | } sprintf(zBuf,"%s/.sqliterc",home_dir); free(home_dir); sqliterc = (const char*)zBuf; } in = fopen(sqliterc,"rb"); if( in ){ if( stdin_is_interactive ){ printf("Loading resources from %s\n",sqliterc); } process_input(p,in); fclose(in); } free(zBuf); return; |
︙ | ︙ | |||
1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 | int main(int argc, char **argv){ char *zErrMsg = 0; struct callback_data data; const char *zInitFile = 0; char *zFirstCmd = 0; int i; #ifdef __MACOS__ argc = ccommand(&argv); #endif Argv0 = argv[0]; main_init(&data); /* Make sure we have a valid signal handler early, before anything ** else is done. */ #ifdef SIGINT signal(SIGINT, interrupt_handler); #endif /* Do an initial pass through the command-line argument to locate ** the name of the database file, the name of the initialization file, ** and the first command to execute. */ for(i=1; i<argc-1; i++){ if( argv[i][0]!='-' ) break; if( strcmp(argv[i],"-separator")==0 || strcmp(argv[i],"-nullvalue")==0 ){ i++; }else if( strcmp(argv[i],"-init")==0 ){ i++; zInitFile = argv[i]; } } | > > > > > | 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 | int main(int argc, char **argv){ char *zErrMsg = 0; struct callback_data data; const char *zInitFile = 0; char *zFirstCmd = 0; int i; int rc = 0; #ifdef __MACOS__ argc = ccommand(&argv); #endif Argv0 = argv[0]; main_init(&data); stdin_is_interactive = isatty(0); /* Make sure we have a valid signal handler early, before anything ** else is done. */ #ifdef SIGINT signal(SIGINT, interrupt_handler); #endif /* Do an initial pass through the command-line argument to locate ** the name of the database file, the name of the initialization file, ** and the first command to execute. */ for(i=1; i<argc-1; i++){ char *z; if( argv[i][0]!='-' ) break; z = argv[i]; if( z[0]=='-' && z[1]=='-' ) z++; if( strcmp(argv[i],"-separator")==0 || strcmp(argv[i],"-nullvalue")==0 ){ i++; }else if( strcmp(argv[i],"-init")==0 ){ i++; zInitFile = argv[i]; } } |
︙ | ︙ | |||
1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 | /* Make a second pass through the command-line argument and set ** options. This second pass is delayed until after the initialization ** file is processed so that the command-line arguments will override ** settings in the initialization file. */ for(i=1; i<argc && argv[i][0]=='-'; i++){ char *z = argv[i]; if( strcmp(z,"-init")==0 ){ i++; }else if( strcmp(z,"-html")==0 ){ data.mode = MODE_Html; }else if( strcmp(z,"-list")==0 ){ data.mode = MODE_List; }else if( strcmp(z,"-line")==0 ){ | > | 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 | /* Make a second pass through the command-line argument and set ** options. This second pass is delayed until after the initialization ** file is processed so that the command-line arguments will override ** settings in the initialization file. */ for(i=1; i<argc && argv[i][0]=='-'; i++){ char *z = argv[i]; if( z[1]=='-' ){ z++; } if( strcmp(z,"-init")==0 ){ i++; }else if( strcmp(z,"-html")==0 ){ data.mode = MODE_Html; }else if( strcmp(z,"-list")==0 ){ data.mode = MODE_List; }else if( strcmp(z,"-line")==0 ){ |
︙ | ︙ | |||
1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 | }else if( strcmp(z,"-noheader")==0 ){ data.showHeader = 0; }else if( strcmp(z,"-echo")==0 ){ data.echoOn = 1; }else if( strcmp(z,"-version")==0 ){ printf("%s\n", sqlite3_libversion()); return 0; }else if( strcmp(z,"-help")==0 || strcmp(z, "--help")==0 ){ usage(1); }else{ fprintf(stderr,"%s: unknown option: %s\n", Argv0, z); fprintf(stderr,"Use -help for a list of options.\n"); return 1; } | > > > > | 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 | }else if( strcmp(z,"-noheader")==0 ){ data.showHeader = 0; }else if( strcmp(z,"-echo")==0 ){ data.echoOn = 1; }else if( strcmp(z,"-version")==0 ){ printf("%s\n", sqlite3_libversion()); return 0; }else if( strcmp(z,"-interactive")==0 ){ stdin_is_interactive = 1; }else if( strcmp(z,"-batch")==0 ){ stdin_is_interactive = 0; }else if( strcmp(z,"-help")==0 || strcmp(z, "--help")==0 ){ usage(1); }else{ fprintf(stderr,"%s: unknown option: %s\n", Argv0, z); fprintf(stderr,"Use -help for a list of options.\n"); return 1; } |
︙ | ︙ | |||
1817 1818 1819 1820 1821 1822 1823 | fprintf(stderr,"SQL error: %s\n", zErrMsg); exit(1); } } }else{ /* Run commands received from standard input */ | | | | | | 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 | fprintf(stderr,"SQL error: %s\n", zErrMsg); exit(1); } } }else{ /* Run commands received from standard input */ if( stdin_is_interactive ){ char *zHome; char *zHistory = 0; printf( "SQLite version %s\n" "Enter \".help\" for instructions\n", sqlite3_libversion() ); zHome = find_home_dir(); if( zHome && (zHistory = malloc(strlen(zHome)+20))!=0 ){ sprintf(zHistory,"%s/.sqlite_history", zHome); } #if defined(HAVE_READLINE) && HAVE_READLINE==1 if( zHistory ) read_history(zHistory); #endif rc = process_input(&data, 0); if( zHistory ){ stifle_history(100); write_history(zHistory); free(zHistory); } free(zHome); }else{ rc = process_input(&data, stdin); } } set_table_name(&data, 0); if( db ){ if( sqlite3_close(db)!=SQLITE_OK ){ fprintf(stderr,"error closing database: %s\n", sqlite3_errmsg(db)); } } return rc; } |