version 1.6, 2013/06/09 14:51:57 |
version 1.7, 2013/09/21 17:43:28 |
|
|
# include <readline/history.h> |
# include <readline/history.h> |
#endif |
#endif |
#if !defined(HAVE_EDITLINE) && (!defined(HAVE_READLINE) || HAVE_READLINE!=1) |
#if !defined(HAVE_EDITLINE) && (!defined(HAVE_READLINE) || HAVE_READLINE!=1) |
# define readline(p) local_getline(p,stdin,0) |
|
# define add_history(X) |
# define add_history(X) |
# define read_history(X) |
# define read_history(X) |
# define write_history(X) |
# define write_history(X) |
|
|
#define isatty(h) _isatty(h) |
#define isatty(h) _isatty(h) |
#define access(f,m) _access((f),(m)) |
#define access(f,m) _access((f),(m)) |
#undef popen |
#undef popen |
#define popen(a,b) _popen((a),(b)) |
#define popen _popen |
#undef pclose |
#undef pclose |
#define pclose(x) _pclose(x) |
#define pclose _pclose |
#else |
#else |
/* Make sure isatty() has a prototype. |
/* Make sure isatty() has a prototype. |
*/ |
*/ |
extern int isatty(int); |
extern int isatty(int); |
#endif |
#endif |
|
|
|
/* popen and pclose are not C89 functions and so are sometimes omitted from |
|
** the <stdio.h> header */ |
|
FILE *popen(const char*,const char*); |
|
int pclose(FILE*); |
|
|
#if defined(_WIN32_WCE) |
#if defined(_WIN32_WCE) |
/* Windows CE (arm-wince-mingw32ce-gcc) does not provide isatty() |
/* Windows CE (arm-wince-mingw32ce-gcc) does not provide isatty() |
* thus we always assume that we have a console. That can be |
* thus we always assume that we have a console. That can be |
|
|
** to the text. NULL is returned at end of file, or if malloc() |
** to the text. NULL is returned at end of file, or if malloc() |
** fails. |
** fails. |
** |
** |
** The interface is like "readline" but no command-line editing |
** If zLine is not NULL then it is a malloced buffer returned from |
** is done. |
** a previous call to this routine that may be reused. |
*/ |
*/ |
static char *local_getline(char *zPrompt, FILE *in, int csvFlag){ |
static char *local_getline(char *zLine, FILE *in){ |
char *zLine; |
int nLine = zLine==0 ? 0 : 100; |
int nLine; |
int n = 0; |
int n; |
|
int inQuote = 0; |
|
|
|
if( zPrompt && *zPrompt ){ |
|
printf("%s",zPrompt); |
|
fflush(stdout); |
|
} |
|
nLine = 100; |
|
zLine = malloc( nLine ); |
|
if( zLine==0 ) return 0; |
|
n = 0; |
|
while( 1 ){ |
while( 1 ){ |
if( n+100>nLine ){ |
if( n+100>nLine ){ |
nLine = nLine*2 + 100; |
nLine = nLine*2 + 100; |
|
|
zLine[n] = 0; |
zLine[n] = 0; |
break; |
break; |
} |
} |
while( zLine[n] ){ |
while( zLine[n] ) n++; |
if( zLine[n]=='"' ) inQuote = !inQuote; |
if( n>0 && zLine[n-1]=='\n' ){ |
n++; |
|
} |
|
if( n>0 && zLine[n-1]=='\n' && (!inQuote || !csvFlag) ){ |
|
n--; |
n--; |
if( n>0 && zLine[n-1]=='\r' ) n--; |
if( n>0 && zLine[n-1]=='\r' ) n--; |
zLine[n] = 0; |
zLine[n] = 0; |
break; |
break; |
} |
} |
} |
} |
zLine = realloc( zLine, n+1 ); |
|
return zLine; |
return zLine; |
} |
} |
|
|
/* |
/* |
** Retrieve a single line of input text. |
** Retrieve a single line of input text. |
** |
** |
** zPrior is a string of prior text retrieved. If not the empty |
** If in==0 then read from standard input and prompt before each line. |
** string, then issue a continuation prompt. |
** If isContinuation is true, then a continuation prompt is appropriate. |
|
** If isContinuation is zero, then the main prompt should be used. |
|
** |
|
** If zPrior is not NULL then it is a buffer from a prior call to this |
|
** routine that can be reused. |
|
** |
|
** The result is stored in space obtained from malloc() and must either |
|
** be freed by the caller or else passed back into this routine via the |
|
** zPrior argument for reuse. |
*/ |
*/ |
static char *one_input_line(const char *zPrior, FILE *in){ |
static char *one_input_line(FILE *in, char *zPrior, int isContinuation){ |
char *zPrompt; |
char *zPrompt; |
char *zResult; |
char *zResult; |
if( in!=0 ){ |
if( in!=0 ){ |
return local_getline(0, in, 0); |
zResult = local_getline(zPrior, in); |
} |
|
if( zPrior && zPrior[0] ){ |
|
zPrompt = continuePrompt; |
|
}else{ |
}else{ |
zPrompt = mainPrompt; |
zPrompt = isContinuation ? continuePrompt : mainPrompt; |
} |
|
zResult = readline(zPrompt); |
|
#if defined(HAVE_READLINE) && HAVE_READLINE==1 |
#if defined(HAVE_READLINE) && HAVE_READLINE==1 |
if( zResult && *zResult ) add_history(zResult); |
free(zPrior); |
|
zResult = readline(zPrompt); |
|
if( zResult && *zResult ) add_history(zResult); |
|
#else |
|
printf("%s", zPrompt); |
|
fflush(stdout); |
|
zResult = local_getline(zPrior, stdin); |
#endif |
#endif |
|
} |
return zResult; |
return zResult; |
} |
} |
|
|
|
|
fprintf(pArg->out, "Sort Operations: %d\n", iCur); |
fprintf(pArg->out, "Sort Operations: %d\n", iCur); |
iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_AUTOINDEX, bReset); |
iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_AUTOINDEX, bReset); |
fprintf(pArg->out, "Autoindex Inserts: %d\n", iCur); |
fprintf(pArg->out, "Autoindex Inserts: %d\n", iCur); |
|
iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_VM_STEP, bReset); |
|
fprintf(pArg->out, "Virtual Machine Steps: %d\n", iCur); |
} |
} |
|
|
return 0; |
return 0; |
|
|
** \t -> tab |
** \t -> tab |
** \n -> newline |
** \n -> newline |
** \r -> carriage return |
** \r -> carriage return |
|
** \" -> " |
** \NNN -> ascii character NNN in octal |
** \NNN -> ascii character NNN in octal |
** \\ -> backslash |
** \\ -> backslash |
*/ |
*/ |
|
|
c = '\t'; |
c = '\t'; |
}else if( c=='r' ){ |
}else if( c=='r' ){ |
c = '\r'; |
c = '\r'; |
|
}else if( c=='\\' ){ |
|
c = '\\'; |
}else if( c>='0' && c<='7' ){ |
}else if( c>='0' && c<='7' ){ |
c -= '0'; |
c -= '0'; |
if( z[i+1]>='0' && z[i+1]<='7' ){ |
if( z[i+1]>='0' && z[i+1]<='7' ){ |
|
|
} |
} |
|
|
/* |
/* |
** Interpret zArg as a boolean value. Return either 0 or 1. |
** Return the value of a hexadecimal digit. Return -1 if the input |
|
** is not a hex digit. |
*/ |
*/ |
static int booleanValue(char *zArg){ |
static int hexDigitValue(char c){ |
int i; |
if( c>='0' && c<='9' ) return c - '0'; |
for(i=0; zArg[i]>='0' && zArg[i]<='9'; i++){} |
if( c>='a' && c<='f' ) return c - 'a' + 10; |
if( i>0 && zArg[i]==0 ) return atoi(zArg); |
if( c>='A' && c<='F' ) return c - 'A' + 10; |
if( sqlite3_stricmp(zArg, "on")==0 || sqlite3_stricmp(zArg,"yes")==0 ){ |
return -1; |
return 1; |
|
} |
|
if( sqlite3_stricmp(zArg, "off")==0 || sqlite3_stricmp(zArg,"no")==0 ){ |
|
return 0; |
|
} |
|
fprintf(stderr, "ERROR: Not a boolean value: \"%s\". Assuming \"no\".\n", |
|
zArg); |
|
return 0; |
|
} |
} |
|
|
/* |
/* |
|
|
}else if( zArg[0]=='+' ){ |
}else if( zArg[0]=='+' ){ |
zArg++; |
zArg++; |
} |
} |
while( isdigit(zArg[0]) ){ |
if( zArg[0]=='0' && zArg[1]=='x' ){ |
v = v*10 + zArg[0] - '0'; |
int x; |
zArg++; |
zArg += 2; |
|
while( (x = hexDigitValue(zArg[0]))>=0 ){ |
|
v = (v<<4) + x; |
|
zArg++; |
|
} |
|
}else{ |
|
while( IsDigit(zArg[0]) ){ |
|
v = v*10 + zArg[0] - '0'; |
|
zArg++; |
|
} |
} |
} |
for(i=0; i<sizeof(aMult)/sizeof(aMult[0]); i++){ |
for(i=0; i<ArraySize(aMult); i++){ |
if( sqlite3_stricmp(aMult[i].zSuffix, zArg)==0 ){ |
if( sqlite3_stricmp(aMult[i].zSuffix, zArg)==0 ){ |
v *= aMult[i].iMult; |
v *= aMult[i].iMult; |
break; |
break; |
|
|
} |
} |
|
|
/* |
/* |
|
** Interpret zArg as either an integer or a boolean value. Return 1 or 0 |
|
** for TRUE and FALSE. Return the integer value if appropriate. |
|
*/ |
|
static int booleanValue(char *zArg){ |
|
int i; |
|
if( zArg[0]=='0' && zArg[1]=='x' ){ |
|
for(i=2; hexDigitValue(zArg[i])>=0; i++){} |
|
}else{ |
|
for(i=0; zArg[i]>='0' && zArg[i]<='9'; i++){} |
|
} |
|
if( i>0 && zArg[i]==0 ) return (int)(integerValue(zArg) & 0xffffffff); |
|
if( sqlite3_stricmp(zArg, "on")==0 || sqlite3_stricmp(zArg,"yes")==0 ){ |
|
return 1; |
|
} |
|
if( sqlite3_stricmp(zArg, "off")==0 || sqlite3_stricmp(zArg,"no")==0 ){ |
|
return 0; |
|
} |
|
fprintf(stderr, "ERROR: Not a boolean value: \"%s\". Assuming \"no\".\n", |
|
zArg); |
|
return 0; |
|
} |
|
|
|
/* |
** Close an output file, assuming it is not stderr or stdout |
** Close an output file, assuming it is not stderr or stdout |
*/ |
*/ |
static void output_file_close(FILE *f){ |
static void output_file_close(FILE *f){ |
|
|
} |
} |
|
|
/* |
/* |
|
** An object used to read a CSV file |
|
*/ |
|
typedef struct CSVReader CSVReader; |
|
struct CSVReader { |
|
const char *zFile; /* Name of the input file */ |
|
FILE *in; /* Read the CSV text from this input stream */ |
|
char *z; /* Accumulated text for a field */ |
|
int n; /* Number of bytes in z */ |
|
int nAlloc; /* Space allocated for z[] */ |
|
int nLine; /* Current line number */ |
|
int cTerm; /* Character that terminated the most recent field */ |
|
int cSeparator; /* The separator character. (Usually ",") */ |
|
}; |
|
|
|
/* Append a single byte to z[] */ |
|
static void csv_append_char(CSVReader *p, int c){ |
|
if( p->n+1>=p->nAlloc ){ |
|
p->nAlloc += p->nAlloc + 100; |
|
p->z = sqlite3_realloc(p->z, p->nAlloc); |
|
if( p->z==0 ){ |
|
fprintf(stderr, "out of memory\n"); |
|
exit(1); |
|
} |
|
} |
|
p->z[p->n++] = (char)c; |
|
} |
|
|
|
/* Read a single field of CSV text. Compatible with rfc4180 and extended |
|
** with the option of having a separator other than ",". |
|
** |
|
** + Input comes from p->in. |
|
** + Store results in p->z of length p->n. Space to hold p->z comes |
|
** from sqlite3_malloc(). |
|
** + Use p->cSep as the separator. The default is ",". |
|
** + Keep track of the line number in p->nLine. |
|
** + Store the character that terminates the field in p->cTerm. Store |
|
** EOF on end-of-file. |
|
** + Report syntax errors on stderr |
|
*/ |
|
static char *csv_read_one_field(CSVReader *p){ |
|
int c, pc; |
|
int cSep = p->cSeparator; |
|
p->n = 0; |
|
c = fgetc(p->in); |
|
if( c==EOF || seenInterrupt ){ |
|
p->cTerm = EOF; |
|
return 0; |
|
} |
|
if( c=='"' ){ |
|
int startLine = p->nLine; |
|
int cQuote = c; |
|
pc = 0; |
|
while( 1 ){ |
|
c = fgetc(p->in); |
|
if( c=='\n' ) p->nLine++; |
|
if( c==cQuote ){ |
|
if( pc==cQuote ){ |
|
pc = 0; |
|
continue; |
|
} |
|
} |
|
if( (c==cSep && pc==cQuote) |
|
|| (c=='\n' && pc==cQuote) |
|
|| (c=='\n' && pc=='\r' && p->n>=2 && p->z[p->n-2]==cQuote) |
|
|| (c==EOF && pc==cQuote) |
|
){ |
|
do{ p->n--; }while( p->z[p->n]!=cQuote ); |
|
p->cTerm = c; |
|
break; |
|
} |
|
if( pc==cQuote && c!='\r' ){ |
|
fprintf(stderr, "%s:%d: unescaped %c character\n", |
|
p->zFile, p->nLine, cQuote); |
|
} |
|
if( c==EOF ){ |
|
fprintf(stderr, "%s:%d: unterminated %c-quoted field\n", |
|
p->zFile, startLine, cQuote); |
|
p->cTerm = EOF; |
|
break; |
|
} |
|
csv_append_char(p, c); |
|
pc = c; |
|
} |
|
}else{ |
|
while( c!=EOF && c!=cSep && c!='\n' ){ |
|
csv_append_char(p, c); |
|
c = fgetc(p->in); |
|
} |
|
if( c=='\n' ){ |
|
p->nLine++; |
|
if( p->n>1 && p->z[p->n-1]=='\r' ) p->n--; |
|
} |
|
p->cTerm = c; |
|
} |
|
if( p->z ) p->z[p->n] = 0; |
|
return p->z; |
|
} |
|
|
|
/* |
** If an input line begins with "." then invoke this routine to |
** If an input line begins with "." then invoke this routine to |
** process that line. |
** process that line. |
** |
** |
|
|
if( zLine[i]=='\'' || zLine[i]=='"' ){ |
if( zLine[i]=='\'' || zLine[i]=='"' ){ |
int delim = zLine[i++]; |
int delim = zLine[i++]; |
azArg[nArg++] = &zLine[i]; |
azArg[nArg++] = &zLine[i]; |
while( zLine[i] && zLine[i]!=delim ){ i++; } |
while( zLine[i] && zLine[i]!=delim ){ |
|
if( zLine[i]=='\\' && delim=='"' && zLine[i+1]!=0 ) i++; |
|
i++; |
|
} |
if( zLine[i]==delim ){ |
if( zLine[i]==delim ){ |
zLine[i++] = 0; |
zLine[i++] = 0; |
} |
} |
|
|
if( c=='b' && n>=3 && strncmp(azArg[0], "backup", n)==0 ){ |
if( c=='b' && n>=3 && strncmp(azArg[0], "backup", n)==0 ){ |
const char *zDestFile = 0; |
const char *zDestFile = 0; |
const char *zDb = 0; |
const char *zDb = 0; |
const char *zKey = 0; |
|
sqlite3 *pDest; |
sqlite3 *pDest; |
sqlite3_backup *pBackup; |
sqlite3_backup *pBackup; |
int j; |
int j; |
|
|
const char *z = azArg[j]; |
const char *z = azArg[j]; |
if( z[0]=='-' ){ |
if( z[0]=='-' ){ |
while( z[0]=='-' ) z++; |
while( z[0]=='-' ) z++; |
if( strcmp(z,"key")==0 && j<nArg-1 ){ |
/* No options to process at this time */ |
zKey = azArg[++j]; |
|
}else |
|
{ |
{ |
fprintf(stderr, "unknown option: %s\n", azArg[j]); |
fprintf(stderr, "unknown option: %s\n", azArg[j]); |
return 1; |
return 1; |
|
|
sqlite3_close(pDest); |
sqlite3_close(pDest); |
return 1; |
return 1; |
} |
} |
#ifdef SQLITE_HAS_CODEC |
|
sqlite3_key(pDest, zKey, (int)strlen(zKey)); |
|
#else |
|
(void)zKey; |
|
#endif |
|
open_db(p); |
open_db(p); |
pBackup = sqlite3_backup_init(pDest, "main", p->db, zDb); |
pBackup = sqlite3_backup_init(pDest, "main", p->db, zDb); |
if( pBackup==0 ){ |
if( pBackup==0 ){ |
|
|
}else |
}else |
|
|
if( c=='e' && strncmp(azArg[0], "exit", n)==0 ){ |
if( c=='e' && strncmp(azArg[0], "exit", n)==0 ){ |
if( nArg>1 && (rc = atoi(azArg[1]))!=0 ) exit(rc); |
if( nArg>1 && (rc = (int)integerValue(azArg[1]))!=0 ) exit(rc); |
rc = 2; |
rc = 2; |
}else |
}else |
|
|
|
|
|
|
if( c=='i' && strncmp(azArg[0], "import", n)==0 && nArg==3 ){ |
if( c=='i' && strncmp(azArg[0], "import", n)==0 && nArg==3 ){ |
char *zTable = azArg[2]; /* Insert data into this table */ |
char *zTable = azArg[2]; /* Insert data into this table */ |
char *zFile = azArg[1]; /* The file from which to extract data */ |
char *zFile = azArg[1]; /* Name of file to extra content from */ |
sqlite3_stmt *pStmt = NULL; /* A statement */ |
sqlite3_stmt *pStmt = NULL; /* A statement */ |
int nCol; /* Number of columns in the table */ |
int nCol; /* Number of columns in the table */ |
int nByte; /* Number of bytes in an SQL string */ |
int nByte; /* Number of bytes in an SQL string */ |
int i, j; /* Loop counters */ |
int i, j; /* Loop counters */ |
|
int needCommit; /* True to COMMIT or ROLLBACK at end */ |
int nSep; /* Number of bytes in p->separator[] */ |
int nSep; /* Number of bytes in p->separator[] */ |
char *zSql; /* An SQL statement */ |
char *zSql; /* An SQL statement */ |
char *zLine; /* A single line of input from the file */ |
CSVReader sCsv; /* Reader context */ |
char **azCol; /* zLine[] broken up into columns */ |
int (*xCloser)(FILE*); /* Procedure to close th3 connection */ |
char *zCommit; /* How to commit changes */ |
|
FILE *in; /* The input file */ |
|
int lineno = 0; /* Line number of input file */ |
|
|
|
|
seenInterrupt = 0; |
|
memset(&sCsv, 0, sizeof(sCsv)); |
open_db(p); |
open_db(p); |
nSep = strlen30(p->separator); |
nSep = strlen30(p->separator); |
if( nSep==0 ){ |
if( nSep==0 ){ |
fprintf(stderr, "Error: non-null separator required for import\n"); |
fprintf(stderr, "Error: non-null separator required for import\n"); |
return 1; |
return 1; |
} |
} |
|
if( nSep>1 ){ |
|
fprintf(stderr, "Error: multi-character separators not allowed" |
|
" for import\n"); |
|
return 1; |
|
} |
|
sCsv.zFile = zFile; |
|
sCsv.nLine = 1; |
|
if( sCsv.zFile[0]=='|' ){ |
|
sCsv.in = popen(sCsv.zFile+1, "r"); |
|
sCsv.zFile = "<pipe>"; |
|
xCloser = pclose; |
|
}else{ |
|
sCsv.in = fopen(sCsv.zFile, "rb"); |
|
xCloser = fclose; |
|
} |
|
if( sCsv.in==0 ){ |
|
fprintf(stderr, "Error: cannot open \"%s\"\n", zFile); |
|
return 1; |
|
} |
|
sCsv.cSeparator = p->separator[0]; |
zSql = sqlite3_mprintf("SELECT * FROM %s", zTable); |
zSql = sqlite3_mprintf("SELECT * FROM %s", zTable); |
if( zSql==0 ){ |
if( zSql==0 ){ |
fprintf(stderr, "Error: out of memory\n"); |
fprintf(stderr, "Error: out of memory\n"); |
|
xCloser(sCsv.in); |
return 1; |
return 1; |
} |
} |
nByte = strlen30(zSql); |
nByte = strlen30(zSql); |
rc = sqlite3_prepare(p->db, zSql, -1, &pStmt, 0); |
rc = sqlite3_prepare(p->db, zSql, -1, &pStmt, 0); |
|
if( rc && sqlite3_strglob("no such table: *", sqlite3_errmsg(db))==0 ){ |
|
char *zCreate = sqlite3_mprintf("CREATE TABLE %s", zTable); |
|
char cSep = '('; |
|
while( csv_read_one_field(&sCsv) ){ |
|
zCreate = sqlite3_mprintf("%z%c\n \"%s\" TEXT", zCreate, cSep, sCsv.z); |
|
cSep = ','; |
|
if( sCsv.cTerm!=sCsv.cSeparator ) break; |
|
} |
|
if( cSep=='(' ){ |
|
sqlite3_free(zCreate); |
|
sqlite3_free(sCsv.z); |
|
xCloser(sCsv.in); |
|
fprintf(stderr,"%s: empty file\n", sCsv.zFile); |
|
return 1; |
|
} |
|
zCreate = sqlite3_mprintf("%z\n)", zCreate); |
|
rc = sqlite3_exec(p->db, zCreate, 0, 0, 0); |
|
sqlite3_free(zCreate); |
|
if( rc ){ |
|
fprintf(stderr, "CREATE TABLE %s(...) failed: %s\n", zTable, |
|
sqlite3_errmsg(db)); |
|
sqlite3_free(sCsv.z); |
|
xCloser(sCsv.in); |
|
return 1; |
|
} |
|
rc = sqlite3_prepare(p->db, zSql, -1, &pStmt, 0); |
|
} |
sqlite3_free(zSql); |
sqlite3_free(zSql); |
if( rc ){ |
if( rc ){ |
if (pStmt) sqlite3_finalize(pStmt); |
if (pStmt) sqlite3_finalize(pStmt); |
fprintf(stderr,"Error: %s\n", sqlite3_errmsg(db)); |
fprintf(stderr,"Error: %s\n", sqlite3_errmsg(db)); |
|
xCloser(sCsv.in); |
return 1; |
return 1; |
} |
} |
nCol = sqlite3_column_count(pStmt); |
nCol = sqlite3_column_count(pStmt); |
sqlite3_finalize(pStmt); |
sqlite3_finalize(pStmt); |
pStmt = 0; |
pStmt = 0; |
if( nCol==0 ) return 0; /* no columns, no error */ |
if( nCol==0 ) return 0; /* no columns, no error */ |
zSql = malloc( nByte + 20 + nCol*2 ); |
zSql = sqlite3_malloc( nByte*2 + 20 + nCol*2 ); |
if( zSql==0 ){ |
if( zSql==0 ){ |
fprintf(stderr, "Error: out of memory\n"); |
fprintf(stderr, "Error: out of memory\n"); |
|
xCloser(sCsv.in); |
return 1; |
return 1; |
} |
} |
sqlite3_snprintf(nByte+20, zSql, "INSERT INTO %s VALUES(?", zTable); |
sqlite3_snprintf(nByte+20, zSql, "INSERT INTO \"%w\" VALUES(?", zTable); |
j = strlen30(zSql); |
j = strlen30(zSql); |
for(i=1; i<nCol; i++){ |
for(i=1; i<nCol; i++){ |
zSql[j++] = ','; |
zSql[j++] = ','; |
|
|
zSql[j++] = ')'; |
zSql[j++] = ')'; |
zSql[j] = 0; |
zSql[j] = 0; |
rc = sqlite3_prepare(p->db, zSql, -1, &pStmt, 0); |
rc = sqlite3_prepare(p->db, zSql, -1, &pStmt, 0); |
free(zSql); |
sqlite3_free(zSql); |
if( rc ){ |
if( rc ){ |
fprintf(stderr, "Error: %s\n", sqlite3_errmsg(db)); |
fprintf(stderr, "Error: %s\n", sqlite3_errmsg(db)); |
if (pStmt) sqlite3_finalize(pStmt); |
if (pStmt) sqlite3_finalize(pStmt); |
|
xCloser(sCsv.in); |
return 1; |
return 1; |
} |
} |
in = fopen(zFile, "rb"); |
needCommit = sqlite3_get_autocommit(db); |
if( in==0 ){ |
if( needCommit ) sqlite3_exec(db, "BEGIN", 0, 0, 0); |
fprintf(stderr, "Error: cannot open \"%s\"\n", zFile); |
do{ |
sqlite3_finalize(pStmt); |
int startLine = sCsv.nLine; |
return 1; |
for(i=0; i<nCol; i++){ |
} |
char *z = csv_read_one_field(&sCsv); |
azCol = malloc( sizeof(azCol[0])*(nCol+1) ); |
if( z==0 && i==0 ) break; |
if( azCol==0 ){ |
sqlite3_bind_text(pStmt, i+1, z, -1, SQLITE_TRANSIENT); |
fprintf(stderr, "Error: out of memory\n"); |
if( i<nCol-1 && sCsv.cTerm!=sCsv.cSeparator ){ |
fclose(in); |
fprintf(stderr, "%s:%d: expected %d columns but found %d - " |
sqlite3_finalize(pStmt); |
"filling the rest with NULL\n", |
return 1; |
sCsv.zFile, startLine, nCol, i+1); |
} |
|
sqlite3_exec(p->db, "BEGIN", 0, 0, 0); |
|
zCommit = "COMMIT"; |
|
while( (zLine = local_getline(0, in, 1))!=0 ){ |
|
char *z, c; |
|
int inQuote = 0; |
|
lineno++; |
|
azCol[0] = zLine; |
|
for(i=0, z=zLine; (c = *z)!=0; z++){ |
|
if( c=='"' ) inQuote = !inQuote; |
|
if( c=='\n' ) lineno++; |
|
if( !inQuote && c==p->separator[0] && strncmp(z,p->separator,nSep)==0 ){ |
|
*z = 0; |
|
i++; |
i++; |
if( i<nCol ){ |
while( i<nCol ){ sqlite3_bind_null(pStmt, i); i++; } |
azCol[i] = &z[nSep]; |
|
z += nSep-1; |
|
} |
|
} |
} |
} /* end for */ |
|
*z = 0; |
|
if( i+1!=nCol ){ |
|
fprintf(stderr, |
|
"Error: %s line %d: expected %d columns of data but found %d\n", |
|
zFile, lineno, nCol, i+1); |
|
zCommit = "ROLLBACK"; |
|
free(zLine); |
|
rc = 1; |
|
break; /* from while */ |
|
} |
} |
for(i=0; i<nCol; i++){ |
if( sCsv.cTerm==sCsv.cSeparator ){ |
if( azCol[i][0]=='"' ){ |
do{ |
int k; |
csv_read_one_field(&sCsv); |
for(z=azCol[i], j=1, k=0; z[j]; j++){ |
i++; |
if( z[j]=='"' ){ j++; if( z[j]==0 ) break; } |
}while( sCsv.cTerm==sCsv.cSeparator ); |
z[k++] = z[j]; |
fprintf(stderr, "%s:%d: expected %d columns but found %d - " |
} |
"extras ignored\n", |
z[k] = 0; |
sCsv.zFile, startLine, nCol, i); |
|
} |
|
if( i>=nCol ){ |
|
sqlite3_step(pStmt); |
|
rc = sqlite3_reset(pStmt); |
|
if( rc!=SQLITE_OK ){ |
|
fprintf(stderr, "%s:%d: INSERT failed: %s\n", sCsv.zFile, startLine, |
|
sqlite3_errmsg(db)); |
} |
} |
sqlite3_bind_text(pStmt, i+1, azCol[i], -1, SQLITE_STATIC); |
|
} |
} |
sqlite3_step(pStmt); |
}while( sCsv.cTerm!=EOF ); |
rc = sqlite3_reset(pStmt); |
|
free(zLine); |
xCloser(sCsv.in); |
if( rc!=SQLITE_OK ){ |
sqlite3_free(sCsv.z); |
fprintf(stderr,"Error: %s\n", sqlite3_errmsg(db)); |
|
zCommit = "ROLLBACK"; |
|
rc = 1; |
|
break; /* from while */ |
|
} |
|
} /* end while */ |
|
free(azCol); |
|
fclose(in); |
|
sqlite3_finalize(pStmt); |
sqlite3_finalize(pStmt); |
sqlite3_exec(p->db, zCommit, 0, 0, 0); |
if( needCommit ) sqlite3_exec(db, "COMMIT", 0, 0, 0); |
}else |
}else |
|
|
if( c=='i' && strncmp(azArg[0], "indices", n)==0 && nArg<3 ){ |
if( c=='i' && strncmp(azArg[0], "indices", n)==0 && nArg<3 ){ |
|
|
} |
} |
}else |
}else |
|
|
|
#ifdef SQLITE_DEBUG |
|
/* Undocumented commands for internal testing. Subject to change |
|
** without notice. */ |
|
if( c=='s' && n>=10 && strncmp(azArg[0], "selftest-", 9)==0 ){ |
|
if( strncmp(azArg[0]+9, "boolean", n-9)==0 ){ |
|
int i, v; |
|
for(i=1; i<nArg; i++){ |
|
v = booleanValue(azArg[i]); |
|
fprintf(p->out, "%s: %d 0x%x\n", azArg[i], v, v); |
|
} |
|
} |
|
if( strncmp(azArg[0]+9, "integer", n-9)==0 ){ |
|
int i; sqlite3_int64 v; |
|
for(i=1; i<nArg; i++){ |
|
char zBuf[200]; |
|
v = integerValue(azArg[i]); |
|
sqlite3_snprintf(sizeof(zBuf), zBuf, "%s: %lld 0x%llx\n", azArg[i], v, v); |
|
fprintf(p->out, "%s", zBuf); |
|
} |
|
} |
|
}else |
|
#endif |
|
|
if( c=='s' && strncmp(azArg[0], "separator", n)==0 && nArg==2 ){ |
if( c=='s' && strncmp(azArg[0], "separator", n)==0 && nArg==2 ){ |
sqlite3_snprintf(sizeof(p->separator), p->separator, |
sqlite3_snprintf(sizeof(p->separator), p->separator, |
"%.*s", (int)sizeof(p->separator)-1, azArg[1]); |
"%.*s", (int)sizeof(p->separator)-1, azArg[1]); |
|
|
} |
} |
} |
} |
} |
} |
if( testctrl<0 ) testctrl = atoi(azArg[1]); |
if( testctrl<0 ) testctrl = (int)integerValue(azArg[1]); |
if( (testctrl<SQLITE_TESTCTRL_FIRST) || (testctrl>SQLITE_TESTCTRL_LAST) ){ |
if( (testctrl<SQLITE_TESTCTRL_FIRST) || (testctrl>SQLITE_TESTCTRL_LAST) ){ |
fprintf(stderr,"Error: invalid testctrl option: %s\n", azArg[1]); |
fprintf(stderr,"Error: invalid testctrl option: %s\n", azArg[1]); |
}else{ |
}else{ |
|
|
/* sqlite3_test_control(int, uint) */ |
/* sqlite3_test_control(int, uint) */ |
case SQLITE_TESTCTRL_PENDING_BYTE: |
case SQLITE_TESTCTRL_PENDING_BYTE: |
if( nArg==3 ){ |
if( nArg==3 ){ |
unsigned int opt = (unsigned int)integerValue(azArg[2]); |
unsigned int opt = (unsigned int)integerValue(azArg[2]); |
rc = sqlite3_test_control(testctrl, opt); |
rc = sqlite3_test_control(testctrl, opt); |
fprintf(p->out, "%d (0x%08x)\n", rc, rc); |
fprintf(p->out, "%d (0x%08x)\n", rc, rc); |
} else { |
} else { |
|
|
case SQLITE_TESTCTRL_ASSERT: |
case SQLITE_TESTCTRL_ASSERT: |
case SQLITE_TESTCTRL_ALWAYS: |
case SQLITE_TESTCTRL_ALWAYS: |
if( nArg==3 ){ |
if( nArg==3 ){ |
int opt = atoi(azArg[2]); |
int opt = booleanValue(azArg[2]); |
rc = sqlite3_test_control(testctrl, opt); |
rc = sqlite3_test_control(testctrl, opt); |
fprintf(p->out, "%d (0x%08x)\n", rc, rc); |
fprintf(p->out, "%d (0x%08x)\n", rc, rc); |
} else { |
} else { |
|
|
|
|
if( c=='t' && n>4 && strncmp(azArg[0], "timeout", n)==0 && nArg==2 ){ |
if( c=='t' && n>4 && strncmp(azArg[0], "timeout", n)==0 && nArg==2 ){ |
open_db(p); |
open_db(p); |
sqlite3_busy_timeout(p->db, atoi(azArg[1])); |
sqlite3_busy_timeout(p->db, (int)integerValue(azArg[1])); |
}else |
}else |
|
|
if( HAS_TIMER && c=='t' && n>=5 && strncmp(azArg[0], "timer", n)==0 |
if( HAS_TIMER && c=='t' && n>=5 && strncmp(azArg[0], "timer", n)==0 |
|
|
int j; |
int j; |
assert( nArg<=ArraySize(azArg) ); |
assert( nArg<=ArraySize(azArg) ); |
for(j=1; j<nArg && j<ArraySize(p->colWidth); j++){ |
for(j=1; j<nArg && j<ArraySize(p->colWidth); j++){ |
p->colWidth[j-1] = atoi(azArg[j]); |
p->colWidth[j-1] = (int)integerValue(azArg[j]); |
} |
} |
}else |
}else |
|
|
|
|
** Return TRUE if a semicolon occurs anywhere in the first N characters |
** Return TRUE if a semicolon occurs anywhere in the first N characters |
** of string z[]. |
** of string z[]. |
*/ |
*/ |
static int _contains_semicolon(const char *z, int N){ |
static int line_contains_semicolon(const char *z, int N){ |
int i; |
int i; |
for(i=0; i<N; i++){ if( z[i]==';' ) return 1; } |
for(i=0; i<N; i++){ if( z[i]==';' ) return 1; } |
return 0; |
return 0; |
|
|
** than a semi-colon. The SQL Server style "go" command is understood |
** than a semi-colon. The SQL Server style "go" command is understood |
** as is the Oracle "/". |
** as is the Oracle "/". |
*/ |
*/ |
static int _is_command_terminator(const char *zLine){ |
static int line_is_command_terminator(const char *zLine){ |
while( IsSpace(zLine[0]) ){ zLine++; }; |
while( IsSpace(zLine[0]) ){ zLine++; }; |
if( zLine[0]=='/' && _all_whitespace(&zLine[1]) ){ |
if( zLine[0]=='/' && _all_whitespace(&zLine[1]) ){ |
return 1; /* Oracle */ |
return 1; /* Oracle */ |
|
|
** Return true if zSql is a complete SQL statement. Return false if it |
** Return true if zSql is a complete SQL statement. Return false if it |
** ends in the middle of a string literal or C-style comment. |
** ends in the middle of a string literal or C-style comment. |
*/ |
*/ |
static int _is_complete(char *zSql, int nSql){ |
static int line_is_complete(char *zSql, int nSql){ |
int rc; |
int rc; |
if( zSql==0 ) return 1; |
if( zSql==0 ) return 1; |
zSql[nSql] = ';'; |
zSql[nSql] = ';'; |
|
|
** Return the number of errors. |
** Return the number of errors. |
*/ |
*/ |
static int process_input(struct callback_data *p, FILE *in){ |
static int process_input(struct callback_data *p, FILE *in){ |
char *zLine = 0; |
char *zLine = 0; /* A single input line */ |
char *zSql = 0; |
char *zSql = 0; /* Accumulated SQL text */ |
int nSql = 0; |
int nLine; /* Length of current line */ |
int nSqlPrior = 0; |
int nSql = 0; /* Bytes of zSql[] used */ |
char *zErrMsg; |
int nAlloc = 0; /* Allocated zSql[] space */ |
int rc; |
int nSqlPrior = 0; /* Bytes of zSql[] used by prior line */ |
int errCnt = 0; |
char *zErrMsg; /* Error message returned */ |
int lineno = 0; |
int rc; /* Error code */ |
int startline = 0; |
int errCnt = 0; /* Number of errors seen */ |
|
int lineno = 0; /* Current line number */ |
|
int startline = 0; /* Line number for start of current input */ |
|
|
while( errCnt==0 || !bail_on_error || (in==0 && stdin_is_interactive) ){ |
while( errCnt==0 || !bail_on_error || (in==0 && stdin_is_interactive) ){ |
fflush(p->out); |
fflush(p->out); |
free(zLine); |
zLine = one_input_line(in, zLine, nSql>0); |
zLine = one_input_line(zSql, in); |
|
if( zLine==0 ){ |
if( zLine==0 ){ |
/* End of input */ |
/* End of input */ |
if( stdin_is_interactive ) printf("\n"); |
if( stdin_is_interactive ) printf("\n"); |
|
|
seenInterrupt = 0; |
seenInterrupt = 0; |
} |
} |
lineno++; |
lineno++; |
if( (zSql==0 || zSql[0]==0) && _all_whitespace(zLine) ) continue; |
if( nSql==0 && _all_whitespace(zLine) ) continue; |
if( zLine && zLine[0]=='.' && nSql==0 ){ |
if( zLine && zLine[0]=='.' && nSql==0 ){ |
if( p->echoOn ) printf("%s\n", zLine); |
if( p->echoOn ) printf("%s\n", zLine); |
rc = do_meta_command(zLine, p); |
rc = do_meta_command(zLine, p); |
|
|
} |
} |
continue; |
continue; |
} |
} |
if( _is_command_terminator(zLine) && _is_complete(zSql, nSql) ){ |
if( line_is_command_terminator(zLine) && line_is_complete(zSql, nSql) ){ |
memcpy(zLine,";",2); |
memcpy(zLine,";",2); |
} |
} |
|
nLine = strlen30(zLine); |
|
if( nSql+nLine+2>=nAlloc ){ |
|
nAlloc = nSql+nLine+100; |
|
zSql = realloc(zSql, nAlloc); |
|
if( zSql==0 ){ |
|
fprintf(stderr, "Error: out of memory\n"); |
|
exit(1); |
|
} |
|
} |
nSqlPrior = nSql; |
nSqlPrior = nSql; |
if( zSql==0 ){ |
if( nSql==0 ){ |
int i; |
int i; |
for(i=0; zLine[i] && IsSpace(zLine[i]); i++){} |
for(i=0; zLine[i] && IsSpace(zLine[i]); i++){} |
if( zLine[i]!=0 ){ |
assert( nAlloc>0 && zSql!=0 ); |
nSql = strlen30(zLine); |
memcpy(zSql, zLine+i, nLine+1-i); |
zSql = malloc( nSql+3 ); |
startline = lineno; |
if( zSql==0 ){ |
nSql = nLine-i; |
fprintf(stderr, "Error: out of memory\n"); |
|
exit(1); |
|
} |
|
memcpy(zSql, zLine, nSql+1); |
|
startline = lineno; |
|
} |
|
}else{ |
}else{ |
int len = strlen30(zLine); |
|
zSql = realloc( zSql, nSql + len + 4 ); |
|
if( zSql==0 ){ |
|
fprintf(stderr,"Error: out of memory\n"); |
|
exit(1); |
|
} |
|
zSql[nSql++] = '\n'; |
zSql[nSql++] = '\n'; |
memcpy(&zSql[nSql], zLine, len+1); |
memcpy(zSql+nSql, zLine, nLine+1); |
nSql += len; |
nSql += nLine; |
} |
} |
if( zSql && _contains_semicolon(&zSql[nSqlPrior], nSql-nSqlPrior) |
if( nSql && line_contains_semicolon(&zSql[nSqlPrior], nSql-nSqlPrior) |
&& sqlite3_complete(zSql) ){ |
&& sqlite3_complete(zSql) ){ |
p->cnt = 0; |
p->cnt = 0; |
open_db(p); |
open_db(p); |
|
|
} |
} |
errCnt++; |
errCnt++; |
} |
} |
free(zSql); |
|
zSql = 0; |
|
nSql = 0; |
nSql = 0; |
}else if( zSql && _all_whitespace(zSql) ){ |
}else if( nSql && _all_whitespace(zSql) ){ |
free(zSql); |
|
zSql = 0; |
|
nSql = 0; |
nSql = 0; |
} |
} |
} |
} |
if( zSql ){ |
if( nSql ){ |
if( !_all_whitespace(zSql) ){ |
if( !_all_whitespace(zSql) ){ |
fprintf(stderr, "Error: incomplete SQL: %s\n", zSql); |
fprintf(stderr, "Error: incomplete SQL: %s\n", zSql); |
} |
} |
|
|
stdin_is_interactive = 0; |
stdin_is_interactive = 0; |
}else if( strcmp(z,"-heap")==0 ){ |
}else if( strcmp(z,"-heap")==0 ){ |
#if defined(SQLITE_ENABLE_MEMSYS3) || defined(SQLITE_ENABLE_MEMSYS5) |
#if defined(SQLITE_ENABLE_MEMSYS3) || defined(SQLITE_ENABLE_MEMSYS5) |
int j, c; |
|
const char *zSize; |
const char *zSize; |
sqlite3_int64 szHeap; |
sqlite3_int64 szHeap; |
|
|