/*---------------------------------------------------------------- * sql_copy.c: * Insert statement which corresponds to COPY statement. * * Change logs * 2000.05.10: Add WITH NULL AS 'X' to COPY command. * 2001.04.15: Delete parse_copy_insert/select() * 2001.04.15: Add setSQLCA() in parse_copy(). * 2001.06.11: Fix dealing with the backslash character. * 2002.10.27: Fix CSV formatted data. * * * *----------------------------------------------------------------- * This software is copyrighted by SAKAIDA Masaaki - Osaka,Japan. * The author hereby grants permission to use, copy, modify, * distribute, and license this software and its documentation * for any purpose, provided that existing copyright notices are * retained in all copies and that this notice is included * verbatim in any distributions. No written agreement, license, * or royalty fee is required for any of the authorized uses. * Modifications to this software may be copyrighted by their * author and need not follow the licensing terms described * here, provided that the new terms are clearly indicated on * the first page of each file where they apply. * IN NO EVENT SHALL THE AUTHOR OR DISTRIBUTORS BE LIABLE TO ANY * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR * CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS * SOFTWARE, ITS DOCUMENTATION, OR ANY DERIVATIVES THEREOF, EVEN * IF THE AUTHOR HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH * DAMAGE. * THE AUTHOR AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR * PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE IS PROVIDED ON * AN "AS IS" BASIS, AND THE AUTHOR AND DISTRIBUTORS HAVE NO * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, * ENHANCEMENTS, OR MODIFICATIONS. *----------------------------------------------------------------- */ #include #include #include #include /*----------------------------------------------------------------- * The definition of sql_fetch. *---------------------------------------------------------------*/ #include "sql_copy.h" #include "psql_sub.h" #include "utils.h" #include "sql_errno.h" /*----------------------------------------------------------------- * The function list. *----------------------------------------------------------------- * int exec_copy_insert() : exec batch insert. * int exec_copy_select() : exec batch declare cursor for select. * int parse_copyt() : parse copy statement */ /* * ############################################################### * The global routine of sql_fetch. * ############################################################### */ int exec_copy_insert(PSconnSet * pset, char *insert_stmt, const bool mustprompt, FILE * fp) /***************************************************************** * exec Insert statement which corresponds to COPY statement. * * return(0) : normal end * return(-1) : error ****************************************************************/ { bool copydone = false; bool linedone; bool fd_start = true, wq_start = false; char *insertbuf; char *s, *pstart, *ptoken; char token[COPYBUFSIZ]; char ch = pset->opt.fieldSep[0]; int buflen; int c = 0; int ret = -1; int pset_quiet = pset->quiet; int count = 0; /*--------- malloc insert buf -------------*/ insertbuf = wrapMalloc(COPYBUFSIZ + strlen(insert_stmt) + 256); /*--------- start TRANSACTION -------------*/ if (!SendQuery(pset, "begin")) { if (!pset->quiet) printSQLerror(get_Ferr(), pset->sqlca); goto err_return; } /*--------- print promt -------------------*/ if (mustprompt) { fputs("Enter info followed by a newline\n", stdout); fputs("End with a backslash and a " "period on a line by itself.\n", stdout); } /*--------- for each input line -----------*/ while (!copydone) { if (mustprompt) { fputs(">> ", stdout); fflush(stdout); } linedone = false; /*--- set Insert stmt ------*/ strcpy(insertbuf, insert_stmt); strcat(insertbuf, " values("); pstart = insertbuf; pstart += strlen(insertbuf); /*----- for each buffer -----*/ while (!linedone) { s = pstart; ptoken = token; ch = pset->opt.fieldSep[0]; wq_start = false; for (buflen = COPYBUFSIZ; buflen > 1; buflen--) { c = getc(fp); /*----- Single quotation -----*/ if (c == '\'') { *ptoken++ = '\\'; *ptoken++ = '\''; ch = c; continue; } /*----- Doubel quotation starts -----*/ if (ch == pset->opt.fieldSep[0] && c == '"') { wq_start = true; /* start =====>*/ *ptoken++ = c; ch = '\0'; continue; } if (ch == '"' && c == '"') { ch = '\0'; continue; } if (wq_start) { if (ch == '"' && (c == pset->opt.fieldSep[0] || c == '\n' || c == EOF) ) { wq_start = false; /* stop <===== */ *ptoken++ = c; ch = c; } else { *ptoken++ = c; ch = c; continue; } } /*----- Field Separator -----*/ if (ch != '\\' && (c == pset->opt.fieldSep[0] || c == '\n' || c == EOF) ) { *ptoken = '\0'; /* NULL string */ if (strcmp(token, pset->opt.nullstr) == 0) { strcpy(s, "NULL"); s += 4; } /* Double quotation("..." format) */ else if(token[0] == '"' && token[strlen(token)-2] == '"') { *s++ = '\''; strcpy(s, token+1); s += (strlen(token) - 3); *s++ = '\''; } else { *s++ = '\''; strcpy(s, token); s += strlen(token); *s++ = '\''; } ptoken = token; ch = c; if (c == pset->opt.fieldSep[0]) { *s++ = ','; continue; } else { *s++ = ')'; *s++ = ';'; linedone = true; count++; break; } } *ptoken++ = c; ch = c; } *s = '\0'; if (c == EOF) { copydone = true; break; } /*------- EOF mark --------------*/ if (!strncmp(pstart, "'\\.'", 4) || !strncmp(pstart, "'.'", 3)) break; /*------- exec Insert stmt -------*/ pset->quiet = true; if (!SendQuery(pset, insertbuf)) { pset->quiet = pset_quiet; if (!pset->quiet) { fprintf(pset->fout, "Line %d: %s\n", count, insertbuf); printSQLerror(get_Ferr(), pset->sqlca); } goto err_return; } pset->quiet = pset_quiet; } if (!strncmp(pstart, "'\\.'", 4) || !strncmp(pstart, "'.'", 3)) break; } ret = 0; err_return: SendQuery(pset, "end"); free(insertbuf); return (ret); } int exec_copy_select(PSconnSet * pset, char *select_stmt, FILE * fp) /***************************************************************** * exec Declare copy_cur00 cursor for Select statement * which corresponds to COPY statement. * * return(0) : normal end * return(-1) : error ****************************************************************/ { #define CURNAME "copy_cur00" #define FETCHNUMBER "300" /* fetch FETCHNUMBER in copy_cur00 */ char *selectbuf; char fetchbuf[80]; int ret = -1; int pset_quiet = pset->quiet; FILE *pset_fout = pset->fout; pset->quiet = true; /*--------- malloc insert buf -------------*/ selectbuf = wrapMalloc(strlen(select_stmt) + strlen(CURNAME) + 22); /*--------- start TRANSACTION -------------*/ if (!SendQuery(pset, "begin")) { if (!pset->quiet) printSQLerror(get_Ferr(), pset->sqlca); goto err_return; } /*--------- declare cursor ----------------*/ strcpy(selectbuf, "declare "); strcat(selectbuf, CURNAME); strcat(selectbuf, " cursor for "); strcat(selectbuf, select_stmt); if (!SendQuery(pset, selectbuf)) { if (!pset->quiet) printSQLerror(get_Ferr(), pset->sqlca); goto err_return; } /*--------- fetch statement ---------------*/ strcpy(fetchbuf, "fetch "); strcat(fetchbuf, FETCHNUMBER); strcat(fetchbuf, " in "); strcat(fetchbuf, CURNAME); /*--------- for each input line -----------*/ while (1) { /*------- exec fetch stmt --------*/ pset->fout = fp; if (!SendQuery(pset, fetchbuf)) { pset->fout = pset_fout; if (!pset->quiet) printSQLerror(get_Ferr(), pset->sqlca); goto err_return; } pset->fout = pset_fout; /*-------- number of tuples -------*/ if (pset->sqlca->sqlerrd[2] == 0) break; } ret = 0; err_return: free(selectbuf); SendQuery(pset, "end"); pset->quiet = pset_quiet; return (ret); } int parse_copy(PSconnSet * pset, char *copy_stmt, char **sql, FILE ** fp, char *delim, char *nullstring) /******************************************************************* * The syntactic analysis of copy statement. * * ex. * input : * copy_stmt="copy table_name[(col1,col2,..)] * [from | to] [STDIN|STDOUT|file_name] * [[USING] DELIMITER 'delim'] * [WITH NULL AS 'nullstring'] * * output * sql : "select col1,col2,.. from teable_name" * "insert into table_name(col1,col2,..)" * fp : FILE pointer * delim : delimiter ('\t' or ',') * nullstring : '\N' etc. * * return( 0) : copy statement * return(-1) : syntax error * return(-2) : logic error ******************************************************************/ { char *p, *p2; char *pt1, *pt2; /* "table_name" start/end pointer */ char *pc1, *pc2; /* "col1,col2," start/end pointer */ int colslen = 0; char *pi1, *pi2; /* "from|to" start/end pointer */ char *pf1, *pf2; /* "FILE" start/end pointer */ int ret; delim[0] = '\t'; delim[1] = '\0'; nullstring[0] = '\0'; /*------------ check COPY statement --------------*/ if (copy_stmt == NULL) return (-1); /*------------ search "copy" --------------------*/ p = copy_stmt; p = skipSpace(p); p2 = skipToken(p); if (!(p2 - p == 4 && strUcmp(p, "COPY", 4) == 0)) return (-1); /*------------ search table_name -----------------*/ p = p2; p = skipSpace(p); p2 = skipToken(p); pt1 = p; /* table_name start pointer */ pt2 = p2; /* table_name end pointer */ /*----------- search COL names -------------------*/ pc1 = strchr(pt1, '('); if (pc1 != NULL) { pc1 = pc1 + 1; /* COL_names start pointer */ if ((pc2 = strchr(pc1, ')')) == NULL) return (-1); colslen = pc2 - pc1; p2 = pc2 + 2; } else return(-1); /*------------ search "from|to" ------------------*/ p = p2; p = skipSpace(p); p2 = skipToken(p); if (!((p2 - p == 4 && strUcmp(p, "FROM", 4) == 0) || (p2 - p == 2 && strUcmp(p, "TO", 2) == 0)) ) { setSQLCA(pset->sqlca, SQL_INVALID_STMT, "Error: FROM or TO not found.", 0, 0); return (-2); } pi1 = p; /* "from|to" start pointer */ pi2 = p2; /* "from|to" end pointer */ /*------------ search FILE -----------------------*/ p = p2; p = skipSpace(p); p2 = skipToken(p); if (p2 - p == 0) { setSQLCA(pset->sqlca, SQL_INVALID_STMT, "Error: FILE not found.", 0, 0); return (-2); } pf1 = p; if (*p == '\'') pf1++; pf2 = p2; if (*p == '\'') pf2--; /*------------ serch USING ------------------------*/ p = p2; p = skipSpace(p); p2 = skipToken(p); if ( (p2 - p == 5 && strUcmp(p, "USING", 5) == 0) || (strUcmp(p, "DELIMITER", 9) == 0) ) { /*---- serch DELIMITERS -------------------*/ if (p2 - p == 5 && strUcmp(p, "USING", 5) == 0) { p = p2; p = skipSpace(p); p2 = skipToken(p); if (!(strUcmp(p, "DELIMITER", 9) == 0)) { setSQLCA(pset->sqlca, SQL_INVALID_STMT, "Error: DELIMITERS not found.", 0, 0); return (-2); } } p = p2; p = skipSpace(p); p2 = skipToken(p); if (*p == '\'') p++; if (strncmp(p, "\\t", 2) == 0) delim[0] = '\t'; else delim[0] = *p; delim[1] = '\0'; if ((p2 = strchr( p+1, '\'' )) == NULL) { setSQLCA(pset->sqlca, SQL_INVALID_STMT, "Error: Single quote not found.", 0, 0); return(-2); } p2++; p = p2; p = skipSpace(p); p2 = skipToken(p); } /*------------ serch WITH NULL AS '\N' ------------*/ ret = 0; if (p2 - p == 4 && strUcmp(p, "WITH", 4) == 0) { p = p2; p = skipSpace(p); p2 = skipToken(p); if (p2 - p == 4 && strUcmp(p, "NULL", 4) == 0) { p = p2; p = skipSpace(p); p2 = skipToken(p); if (p2 - p == 2 && strUcmp(p, "AS", 2) == 0) { p = p2; p = skipSpace(p); p2 = skipToken(p); if (*p == '\'') p++; strncpy(nullstring, p, 10); p2 = strchr(nullstring,'\''); if (p2 != NULL) *p2 = '\0'; } else ret = -1; } else ret = -1; } else if (p2 - p >= 1) ret = -1; if (ret == -1 ) { setSQLCA(pset->sqlca, SQL_INVALID_STMT, "Error: WITH NULL AS not found.", 0, 0); return(-2); } /*------------ check FILE -------------------------*/ if (strUcmp(pi1, "FROM", 4) == 0 && (pf2 - pf1 == 6 && strUcmp(pf1, "STDOUT", 6) == 0)) { setSQLCA(pset->sqlca, SQL_INVALID_STMT, "Error: can not use 'STDOUT'.", 0, 0); return (-2); } if (strUcmp(pi1, "TO", 2) == 0 && (pf2 - pf1 == 5 && strUcmp(pf1, "STDIN", 5) == 0)) { setSQLCA(pset->sqlca, SQL_INVALID_STMT, "Error: can not use 'STDIN'.", 0, 0); return (-2); } /*------------ FILE pointer ----------------------*/ if (pf2 - pf1 == 6 && strUcmp(pf1, "STDOUT", 6) == 0) *fp = stdout; else if (pf2 - pf1 == 5 && strUcmp(pf1, "STDIN", 5) == 0) *fp = stdin; else { char *fname; fname = wrapMalloc((pf2 - pf1) + 1); fname[0] = '\0'; strncat(fname, pf1, (pf2 - pf1)); if (strUcmp(pi1, "FROM", 4) == 0 && (*fp = fopen(fname, "r")) == NULL) { setSQLCA(pset->sqlca, SQL_INVALID_STMT, getSQLerrmc("can not open the file(%s).", fname), 0, 0); free(fname); return (-2); } else if (strUcmp(pi1, "TO", 2) == 0) { if ((*fp = fopen(fname, "r")) != NULL) { setSQLCA(pset->sqlca, SQL_INVALID_STMT, getSQLerrmc("file(%s) already exist.", fname), 0, 0); free(fname); return (-2); } if ((*fp = fopen(fname, "w")) == NULL) { setSQLCA(pset->sqlca, SQL_INVALID_STMT, getSQLerrmc("can not open the file(%s).", fname), 0, 0); free(fname); return (-2); } } free(fname); } /*------------ make SQL statement ----------------*/ if (strUcmp(pi1, "FROM", 4) == 0) { *sql = wrapMalloc(32 + (pt2 - pt1) + (colslen + 2) + 1); /*-- "insert into table(col1,col2,..)" ---*/ strcpy(*sql, "insert into "); strncat(*sql, pt1, (pt2 - pt1)); if (colslen != 0) { strcat(*sql, "("); strncat(*sql, pc1, (pc2 - pc1)); strcat(*sql, ")"); } } else { /*----------- TO --------------------------*/ *sql = wrapMalloc(32 + (pt2 - pt1) + colslen + 1); /*--- "select col1,col2,.. from table" ----*/ strcpy(*sql, "select "); if (colslen != 0) strncat(*sql, pc1, (pc2 - pc1)); else strcat(*sql, "*"); strcat(*sql, " from "); strncat(*sql, pt1, (pt2 - pt1)); } return (0); }