📄 copy.c
字号:
/* * psql - the PostgreSQL interactive terminal * * Copyright (c) 2000-2005, PostgreSQL Global Development Group * * $PostgreSQL: pgsql/src/bin/psql/copy.c,v 1.58 2005/10/15 02:49:40 momjian Exp $ */#include "postgres_fe.h"#include "copy.h"#include <errno.h>#include <signal.h>#include <sys/stat.h>#ifndef WIN32#include <unistd.h> /* for isatty */#else#include <io.h> /* I think */#endif#include "libpq-fe.h"#include "pqexpbuffer.h"#include "pqsignal.h"#include "settings.h"#include "common.h"#include "prompt.h"#include "stringutils.h"#if defined(WIN32) && (!defined(__MINGW32__))#define __S_ISTYPE(mode, mask) (((mode) & S_IFMT) == (mask))#define S_ISDIR(mode) __S_ISTYPE((mode), S_IFDIR)#endif/* * parse_slash_copy * -- parses \copy command line * * The documented preferred syntax is: * \copy tablename [(columnlist)] from|to filename * [ with ] [ oids ] [ delimiter [as] char ] [ null [as] string ] * (binary is not here yet) * * The pre-7.3 syntax was: * \copy tablename [(columnlist)] [with oids] from|to filename * [ [using] delimiters char ] [ with null as string ] * * The actual accepted syntax is a rather unholy combination of these, * plus some undocumented flexibility (for instance, the clauses after * WITH can appear in any order). The accepted syntax matches what * the backend grammar actually accepts (see backend/parser/gram.y). * * table name can be double-quoted and can have a schema part. * column names can be double-quoted. * filename, char, and string can be single-quoted like SQL literals. * * returns a malloc'ed structure with the options, or NULL on parsing error */struct copy_options{ char *table; char *column_list; char *file; /* NULL = stdin/stdout */ bool psql_inout; /* true = use psql stdin/stdout */ bool from; bool binary; bool oids; bool csv_mode; bool header; char *delim; char *null; char *quote; char *escape; char *force_quote_list; char *force_notnull_list;};static voidfree_copy_options(struct copy_options * ptr){ if (!ptr) return; free(ptr->table); free(ptr->column_list); free(ptr->file); free(ptr->delim); free(ptr->null); free(ptr->quote); free(ptr->escape); free(ptr->force_quote_list); free(ptr->force_notnull_list); free(ptr);}/* concatenate "more" onto "var", freeing the original value of *var */static voidxstrcat(char **var, const char *more){ char *newvar; newvar = pg_malloc(strlen(*var) + strlen(more) + 1); strcpy(newvar, *var); strcat(newvar, more); free(*var); *var = newvar;}static struct copy_options *parse_slash_copy(const char *args){ struct copy_options *result; char *line; char *token; const char *whitespace = " \t\n\r"; 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, pset.encoding); if (!token) goto error;#ifdef NOT_USED /* this is not implemented yet */ if (pg_strcasecmp(token, "binary") == 0) { result->binary = true; token = strtokx(NULL, whitespace, ".,()", "\"", 0, false, pset.encoding); if (!token) goto error; }#endif result->table = pg_strdup(token); token = strtokx(NULL, whitespace, ".,()", "\"", 0, 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, pset.encoding); if (!token) goto error; xstrcat(&result->table, token); token = strtokx(NULL, whitespace, ".,()", "\"", 0, 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, pset.encoding); if (!token || strchr(".,()", token[0])) goto error; xstrcat(&result->column_list, token); token = strtokx(NULL, whitespace, ".,()", "\"", 0, 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, 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, pset.encoding); if (!token || pg_strcasecmp(token, "oids") != 0) goto error; result->oids = true; token = strtokx(NULL, whitespace, NULL, NULL, 0, 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, "'", '\\', 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, pset.encoding); /* * Allows old COPY syntax for backward compatibility 2002-06-19 */ if (token && pg_strcasecmp(token, "using") == 0) { token = strtokx(NULL, whitespace, NULL, NULL, 0, false, pset.encoding); if (!(token && pg_strcasecmp(token, "delimiters") == 0)) goto error; } if (token && pg_strcasecmp(token, "delimiters") == 0) { token = strtokx(NULL, whitespace, NULL, "'", '\\', false, pset.encoding); if (!token) goto error; result->delim = pg_strdup(token); token = strtokx(NULL, whitespace, NULL, NULL, 0, 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, pset.encoding); while (token) { bool fetch_next; fetch_next = true; /* someday allow BINARY here */ if (pg_strcasecmp(token, "oids") == 0) result->oids = 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, "'", '\\', false, pset.encoding); if (token && pg_strcasecmp(token, "as") == 0) token = strtokx(NULL, whitespace, NULL, "'", '\\', 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, "'", '\\', false, pset.encoding); if (token && pg_strcasecmp(token, "as") == 0) token = strtokx(NULL, whitespace, NULL, "'", '\\', 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, "'", '\\', false, pset.encoding); if (token && pg_strcasecmp(token, "as") == 0) token = strtokx(NULL, whitespace, NULL, "'", '\\', 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, "'", '\\', false, pset.encoding); if (token && pg_strcasecmp(token, "as") == 0) token = strtokx(NULL, whitespace, NULL, "'", '\\', 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, pset.encoding); if (pg_strcasecmp(token, "quote") == 0) { /* handle column list */ fetch_next = false; for (;;) { token = strtokx(NULL, whitespace, ",", "\"", 0, 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, 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, pset.encoding); if (pg_strcasecmp(token, "null") != 0) goto error; /* handle column list */ fetch_next = false;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -