static struct copy_options * parse_slash_copy(const char *args) { struct copy_options *result; char* token; const char *whitespace = " \t\n\r"; char nonstd_backslash = standard_strings() ? 0 : '\\'; if (!args) { psql_error("\\copy: arguments required\n"); return NULL; } result = pg_calloc(1, sizeof(struct copy_options)); result->before_tofrom = pg_strdup(""); /* initialize for appending */ token = strtokx(args, whitespace, ".,()", "\"", 0, false, false, pset.encoding); if (!token) goto error; /* The following can be removed when we drop 7.3 syntax support */ if (pg_strcasecmp(token, "binary") == 0) { xstrcat(&result->before_tofrom, token); token = strtokx(NULL, whitespace, ".,()", "\"", 0, false, false, pset.encoding); if (!token) goto error; } /* Handle COPY (SELECT) case */ if (token[0] == '(') { int parens = 1; while (parens > 0) { xstrcat(&result->before_tofrom, " "); xstrcat(&result->before_tofrom, token); token = strtokx(NULL, whitespace, "()", "\"'", nonstd_backslash, true, false, pset.encoding); if (!token) goto error; if (token[0] == '(') parens++; else if (token[0] == ')') parens--; } } xstrcat(&result->before_tofrom, " "); xstrcat(&result->before_tofrom, token); token = strtokx(NULL, whitespace, ".,()", "\"", 0, false, false, pset.encoding); if (!token) goto error; /* * strtokx() will not have returned a multi-character token starting with * '.', so we don't need strcmp() here. Likewise for '(', etc, below. */ if (token[0] == '.') { /* handle schema . table */ xstrcat(&result->before_tofrom, token); token = strtokx(NULL, whitespace, ".,()", "\"", 0, false, false, pset.encoding); if (!token) goto error; xstrcat(&result->before_tofrom, token); token = strtokx(NULL, whitespace, ".,()", "\"", 0, false, false, pset.encoding); if (!token) goto error; } if (token[0] == '(') { /* handle parenthesized column list */ for (;;) { xstrcat(&result->before_tofrom, " "); xstrcat(&result->before_tofrom, token); token = strtokx(NULL, whitespace, "()", "\"", 0, false, false, pset.encoding); if (!token) goto error; if (token[0] == ')') break; } xstrcat(&result->before_tofrom, " "); xstrcat(&result->before_tofrom, token); token = strtokx(NULL, whitespace, ".,()", "\"", 0, false, false, pset.encoding); if (!token) goto error; } if (pg_strcasecmp(token, "from") == 0) result->from = true; else if (pg_strcasecmp(token, "to") == 0) result->from = false; else goto error; token = strtokx(NULL, whitespace, NULL, "'", 0, false, true, pset.encoding); if (!token) goto error; if (pg_strcasecmp(token, "stdin") == 0 || pg_strcasecmp(token, "stdout") == 0) { result->psql_inout = false; result->file = NULL; } else if (pg_strcasecmp(token, "pstdin") == 0 || pg_strcasecmp(token, "pstdout") == 0) { result->psql_inout = true; result->file = NULL; } else { result->psql_inout = false; result->file = pg_strdup(token); expand_tilde(&result->file); } /* Collect the rest of the line (COPY options) */ token = strtokx(NULL, "", NULL, NULL, 0, false, false, pset.encoding); if (token) result->after_tofrom = pg_strdup(token); return result; error: if (token) psql_error("\\copy: parse error at \"%s\"\n", token); else psql_error("\\copy: parse error at end of line\n"); free_copy_options(result); return NULL; }
/* * ParseStageOptions takes the given copy options, parses the additional options * needed for the \stage command, and sets them in the copy options structure. * The additional parsed options are the table name and the column list. */ copy_options * ParseStageOptions(copy_options *copyOptions) { copy_options *stageOptions = NULL; const char *whitespace = " \t\n\r"; char *tableName = NULL; char *columnList = NULL; char *token = NULL; const char *beforeToFrom = copyOptions->before_tofrom; Assert(beforeToFrom != NULL); token = strtokx(beforeToFrom, whitespace, ".,()", "\"", 0, false, false, pset.encoding); /* * We should have errored out earlier if the token were null. Similarly, we * should have errored out on the "\stage (select) to" case. */ Assert(token != NULL); Assert(token[0] != '('); /* we do not support PostgreSQL's 7.3 syntax */ if (pg_strcasecmp(token, "binary") == 0) { psql_error("\\stage: binary keyword before to/from is not supported\n"); Assert(false); } /* init table name and append either the table name or schema name */ tableName = pg_strdup(""); xstrcat(&tableName, token); /* check for the schema.table use case */ token = strtokx(NULL, whitespace, ".,()", "\"", 0, false, false, pset.encoding); if (token != NULL && token[0] == '.') { /* append the dot token */ xstrcat(&tableName, token); token = strtokx(NULL, whitespace, ".,()", "\"", 0, false, false, pset.encoding); Assert(token != NULL); /* append the table name token */ xstrcat(&tableName, token); token = strtokx(NULL, whitespace, ".,()", "\"", 0, false, false, pset.encoding); } /* check for the column list use case */ if (token != NULL && token[0] == '(') { /* init column list, and add columns */ columnList = pg_strdup(""); for (;;) { xstrcat(&columnList, " "); xstrcat(&columnList, token); token = strtokx(NULL, whitespace, "()", "\"", 0, false, false, pset.encoding); Assert(token != NULL); if (token[0] == ')') { break; } } xstrcat(&columnList, " "); xstrcat(&columnList, token); } /* finally set additional stage options */ stageOptions = copyOptions; stageOptions->tableName = tableName; stageOptions->columnList = columnList; return stageOptions; }
/* * parse_slash_copy parses copy options from the given meta-command line. The * function then returns a dynamically allocated structure with the options, or * Null on parsing error. */ copy_options * parse_slash_copy(const char *args) { struct copy_options *result; char *token; const char *whitespace = " \t\n\r"; char nonstd_backslash = standard_strings() ? 0 : '\\'; if (!args) { psql_error("\\copy: arguments required\n"); return NULL; } result = pg_malloc0(sizeof(struct copy_options)); result->before_tofrom = pg_strdup(""); /* initialize for appending */ token = strtokx(args, whitespace, ".,()", "\"", 0, false, false, pset.encoding); if (!token) goto error; /* The following can be removed when we drop 7.3 syntax support */ if (pg_strcasecmp(token, "binary") == 0) { xstrcat(&result->before_tofrom, token); token = strtokx(NULL, whitespace, ".,()", "\"", 0, false, false, pset.encoding); if (!token) goto error; } /* Handle COPY (SELECT) case */ if (token[0] == '(') { int parens = 1; while (parens > 0) { xstrcat(&result->before_tofrom, " "); xstrcat(&result->before_tofrom, token); token = strtokx(NULL, whitespace, "()", "\"'", nonstd_backslash, true, false, pset.encoding); if (!token) goto error; if (token[0] == '(') parens++; else if (token[0] == ')') parens--; } } xstrcat(&result->before_tofrom, " "); xstrcat(&result->before_tofrom, token); token = strtokx(NULL, whitespace, ".,()", "\"", 0, false, false, pset.encoding); if (!token) goto error; /* * strtokx() will not have returned a multi-character token starting with * '.', so we don't need strcmp() here. Likewise for '(', etc, below. */ if (token[0] == '.') { /* handle schema . table */ xstrcat(&result->before_tofrom, token); token = strtokx(NULL, whitespace, ".,()", "\"", 0, false, false, pset.encoding); if (!token) goto error; xstrcat(&result->before_tofrom, token); token = strtokx(NULL, whitespace, ".,()", "\"", 0, false, false, pset.encoding); if (!token) goto error; } if (token[0] == '(') { /* handle parenthesized column list */ for (;;) { xstrcat(&result->before_tofrom, " "); xstrcat(&result->before_tofrom, token); token = strtokx(NULL, whitespace, "()", "\"", 0, false, false, pset.encoding); if (!token) goto error; if (token[0] == ')') break; } xstrcat(&result->before_tofrom, " "); xstrcat(&result->before_tofrom, token); token = strtokx(NULL, whitespace, ".,()", "\"", 0, false, false, pset.encoding); if (!token) goto error; } if (pg_strcasecmp(token, "from") == 0) result->from = true; else if (pg_strcasecmp(token, "to") == 0) result->from = false; else goto error; /* { 'filename' | PROGRAM 'command' | STDIN | STDOUT | PSTDIN | PSTDOUT } */ token = strtokx(NULL, whitespace, ";", "'", 0, false, false, pset.encoding); if (!token) goto error; if (pg_strcasecmp(token, "program") == 0) { int toklen; token = strtokx(NULL, whitespace, ";", "'", 0, false, false, pset.encoding); if (!token) goto error; /* * The shell command must be quoted. This isn't fool-proof, but * catches most quoting errors. */ toklen = strlen(token); if (token[0] != '\'' || toklen < 2 || token[toklen - 1] != '\'') goto error; strip_quotes(token, '\'', 0, pset.encoding); result->program = true; result->file = pg_strdup(token); } else if (pg_strcasecmp(token, "stdin") == 0 || pg_strcasecmp(token, "stdout") == 0) { result->file = NULL; } else if (pg_strcasecmp(token, "pstdin") == 0 || pg_strcasecmp(token, "pstdout") == 0) { result->psql_inout = true; result->file = NULL; } else { /* filename can be optionally quoted */ strip_quotes(token, '\'', 0, pset.encoding); result->file = pg_strdup(token); expand_tilde(&result->file); } /* Collect the rest of the line (COPY options) */ token = strtokx(NULL, "", NULL, NULL, 0, false, false, pset.encoding); if (token) result->after_tofrom = pg_strdup(token); /* set data staging options to null */ result->tableName = NULL; result->columnList = NULL; return result; error: if (token) psql_error("\\copy: parse error at \"%s\"\n", token); else psql_error("\\copy: parse error at end of line\n"); free_copy_options(result); return NULL; }
static struct copy_options * parse_slash_copy(const char *args) { struct copy_options *result; char *line; char *token; const char *whitespace = " \t\n\r"; char nonstd_backslash = standard_strings() ? 0 : '\\'; if (args) line = pg_strdup(args); else { psql_error("\\copy: arguments required\n"); return NULL; } result = pg_calloc(1, sizeof(struct copy_options)); token = strtokx(line, whitespace, ".,()", "\"", 0, false, false, pset.encoding); if (!token) goto error; if (pg_strcasecmp(token, "binary") == 0) { result->binary = true; token = strtokx(NULL, whitespace, ".,()", "\"", 0, false, false, pset.encoding); if (!token) goto error; } result->table = pg_strdup(token); /* Handle COPY (SELECT) case */ if (token[0] == '(') { int parens = 1; while (parens > 0) { token = strtokx(NULL, whitespace, "()", "\"'", nonstd_backslash, true, false, pset.encoding); if (!token) goto error; if (token[0] == '(') parens++; else if (token[0] == ')') parens--; xstrcat(&result->table, " "); xstrcat(&result->table, token); } } token = strtokx(NULL, whitespace, ".,()", "\"", 0, false, false, pset.encoding); if (!token) goto error; /* * strtokx() will not have returned a multi-character token starting with * '.', so we don't need strcmp() here. Likewise for '(', etc, below. */ if (token[0] == '.') { /* handle schema . table */ xstrcat(&result->table, token); token = strtokx(NULL, whitespace, ".,()", "\"", 0, false, false, pset.encoding); if (!token) goto error; xstrcat(&result->table, token); token = strtokx(NULL, whitespace, ".,()", "\"", 0, false, false, pset.encoding); if (!token) goto error; } if (token[0] == '(') { /* handle parenthesized column list */ result->column_list = pg_strdup(token); for (;;) { token = strtokx(NULL, whitespace, ".,()", "\"", 0, false, false, pset.encoding); if (!token || strchr(".,()", token[0])) goto error; xstrcat(&result->column_list, token); token = strtokx(NULL, whitespace, ".,()", "\"", 0, false, false, pset.encoding); if (!token) goto error; xstrcat(&result->column_list, token); if (token[0] == ')') break; if (token[0] != ',') goto error; } token = strtokx(NULL, whitespace, ".,()", "\"", 0, false, false, pset.encoding); if (!token) goto error; } /* * Allows old COPY syntax for backward compatibility 2002-06-19 */ if (pg_strcasecmp(token, "with") == 0) { token = strtokx(NULL, whitespace, NULL, NULL, 0, false, false, pset.encoding); if (!token || pg_strcasecmp(token, "oids") != 0) goto error; result->oids = true; token = strtokx(NULL, whitespace, NULL, NULL, 0, false, false, pset.encoding); if (!token) goto error; } if (pg_strcasecmp(token, "from") == 0) result->from = true; else if (pg_strcasecmp(token, "to") == 0) result->from = false; else goto error; token = strtokx(NULL, whitespace, NULL, "'", 0, false, true, pset.encoding); if (!token) goto error; if (pg_strcasecmp(token, "stdin") == 0 || pg_strcasecmp(token, "stdout") == 0) { result->psql_inout = false; result->file = NULL; } else if (pg_strcasecmp(token, "pstdin") == 0 || pg_strcasecmp(token, "pstdout") == 0) { result->psql_inout = true; result->file = NULL; } else { result->psql_inout = false; result->file = pg_strdup(token); expand_tilde(&result->file); } token = strtokx(NULL, whitespace, NULL, NULL, 0, false, false, pset.encoding); /* * Allows old COPY syntax for backward compatibility. */ if (token && pg_strcasecmp(token, "using") == 0) { token = strtokx(NULL, whitespace, NULL, NULL, 0, false, false, pset.encoding); if (!(token && pg_strcasecmp(token, "delimiters") == 0)) goto error; } if (token && pg_strcasecmp(token, "delimiters") == 0) { token = strtokx(NULL, whitespace, NULL, "'", nonstd_backslash, true, false, pset.encoding); if (!token) goto error; result->delim = pg_strdup(token); token = strtokx(NULL, whitespace, NULL, NULL, 0, false, false, pset.encoding); } if (token) { /* * WITH is optional. Also, the backend will allow WITH followed by * nothing, so we do too. */ if (pg_strcasecmp(token, "with") == 0) token = strtokx(NULL, whitespace, NULL, NULL, 0, false, false, pset.encoding); while (token) { bool fetch_next; fetch_next = true; if (pg_strcasecmp(token, "oids") == 0) result->oids = true; else if (pg_strcasecmp(token, "binary") == 0) result->binary = true; else if (pg_strcasecmp(token, "csv") == 0) result->csv_mode = true; else if (pg_strcasecmp(token, "header") == 0) result->header = true; else if (pg_strcasecmp(token, "delimiter") == 0) { token = strtokx(NULL, whitespace, NULL, "'", nonstd_backslash, true, false, pset.encoding); if (token && pg_strcasecmp(token, "as") == 0) token = strtokx(NULL, whitespace, NULL, "'", nonstd_backslash, true, false, pset.encoding); if (token) result->delim = pg_strdup(token); else goto error; } else if (pg_strcasecmp(token, "null") == 0) { token = strtokx(NULL, whitespace, NULL, "'", nonstd_backslash, true, false, pset.encoding); if (token && pg_strcasecmp(token, "as") == 0) token = strtokx(NULL, whitespace, NULL, "'", nonstd_backslash, true, false, pset.encoding); if (token) result->null = pg_strdup(token); else goto error; } else if (pg_strcasecmp(token, "quote") == 0) { token = strtokx(NULL, whitespace, NULL, "'", nonstd_backslash, true, false, pset.encoding); if (token && pg_strcasecmp(token, "as") == 0) token = strtokx(NULL, whitespace, NULL, "'", nonstd_backslash, true, false, pset.encoding); if (token) result->quote = pg_strdup(token); else goto error; } else if (pg_strcasecmp(token, "escape") == 0) { token = strtokx(NULL, whitespace, NULL, "'", nonstd_backslash, true, false, pset.encoding); if (token && pg_strcasecmp(token, "as") == 0) token = strtokx(NULL, whitespace, NULL, "'", nonstd_backslash, true, false, pset.encoding); if (token) result->escape = pg_strdup(token); else goto error; } else if (pg_strcasecmp(token, "force") == 0) { token = strtokx(NULL, whitespace, ",", "\"", 0, false, false, pset.encoding); if (pg_strcasecmp(token, "quote") == 0) { /* handle column list */ fetch_next = false; for (;;) { token = strtokx(NULL, whitespace, ",", "\"", 0, false, false, pset.encoding); if (!token || strchr(",", token[0])) goto error; if (!result->force_quote_list) result->force_quote_list = pg_strdup(token); else xstrcat(&result->force_quote_list, token); token = strtokx(NULL, whitespace, ",", "\"", 0, false, false, pset.encoding); if (!token || token[0] != ',') break; xstrcat(&result->force_quote_list, token); } } else if (pg_strcasecmp(token, "not") == 0) { token = strtokx(NULL, whitespace, ",", "\"", 0, false, false, pset.encoding); if (pg_strcasecmp(token, "null") != 0) goto error; /* handle column list */ fetch_next = false; for (;;) { token = strtokx(NULL, whitespace, ",", "\"", 0, false, false, pset.encoding); if (!token || strchr(",", token[0])) goto error; if (!result->force_notnull_list) result->force_notnull_list = pg_strdup(token); else xstrcat(&result->force_notnull_list, token); token = strtokx(NULL, whitespace, ",", "\"", 0, false, false, pset.encoding); if (!token || token[0] != ',') break; xstrcat(&result->force_notnull_list, token); } } else goto error; } else goto error; if (fetch_next) token = strtokx(NULL, whitespace, NULL, NULL, 0, false, false, pset.encoding); } } free(line); return result; error: if (token) psql_error("\\copy: parse error at \"%s\"\n", token); else psql_error("\\copy: parse error at end of line\n"); free_copy_options(result); free(line); return NULL; }