Remove file_fdw.
authorShigeru Hanada <[email protected]>
Tue, 23 Nov 2010 06:42:02 +0000 (15:42 +0900)
committerShigeru Hanada <[email protected]>
Tue, 23 Nov 2010 06:42:02 +0000 (15:42 +0900)
Also defer check of handler until the actual scan of foreign table.

18 files changed:
doc/src/sgml/ref/alter_foreign_data_wrapper.sgml
doc/src/sgml/ref/create_foreign_data_wrapper.sgml
src/backend/commands/foreigncmds.c
src/backend/executor/nodeForeignscan.c
src/backend/foreign/Makefile
src/backend/foreign/file_fdw.c [deleted file]
src/backend/foreign/file_parser.c [deleted file]
src/backend/foreign/file_parser.h [deleted file]
src/backend/foreign/foreign.c
src/include/catalog/pg_proc.h
src/test/regress/expected/.gitignore
src/test/regress/expected/foreign_data.out
src/test/regress/input/file_fdw.source [deleted file]
src/test/regress/output/file_fdw.source [deleted file]
src/test/regress/parallel_schedule
src/test/regress/serial_schedule
src/test/regress/sql/.gitignore
src/test/regress/sql/foreign_data.sql

index b9f7b48dccf945a908660a1607f40f4e44aca3a9..ead2c2ebcc1fc3530ac15c0743cb23b989c5db6f 100644 (file)
@@ -103,9 +103,8 @@ ALTER FOREIGN DATA WRAPPER <replaceable class="parameter">name</replaceable> OWN
       longer have a handler function.
      </para>
      <para>
-      Note that it is not allowed to remove handler of a foreign-data wrapper
-      which has any foreign table.  Therefore, it is necessary to drop all
-      foreign tables which use the foreign-data wrapper.
+      Note that foreign tables which uses a foreign-data wrapper with no 
+      handler can't be used in a SELECT statement.
      </para>
     </listitem>
    </varlistentry>
index 29816d7c0bb2a7af35e13d4f089b51ceb33bec14..e61a725de34c8981263f4213ca6a6553e83c2254 100644 (file)
@@ -89,9 +89,7 @@ CREATE FOREIGN DATA WRAPPER <replaceable class="parameter">name</replaceable>
       <replaceable class="parameter">handler</replaceable> is the
       name of a previously registered function that will be called to
       retrieve a set of functions for foreign tables.
-      If no handler function or <literal>NO HANDLER</literal> is specified,
-      then the forign-data wrapper cannot be used for <command>CREATE FOREIGN
-      TABLE</command>. The validator function must take no arguments.
+      The validator function must take no arguments.
       The return type must be <type>fdw_handler</type>.
      </para>
     </listitem>
index f393b2fef4028c6274234d654581454e334f0bab..f5741467fee8db81f1329ff8ee84be452128116f 100644 (file)
@@ -495,66 +495,6 @@ CreateForeignDataWrapper(CreateFdwStmt *stmt)
 }
 
 
-/*
- * Check whether the foreign-data wrapper have any foreign table.
- *
- * pg_foreign_table and pg_foreign_server are accessed via heap_xxx() because 
- * they have no suitable index.
- */
-static bool
-have_foreign_table(Oid fdwId)
-{
-   bool            found = false;
-   Relation        srvrel;
-   Relation        ftrel;
-   ScanKeyData     srvkey[1];
-   HeapScanDesc    srvscan;
-   HeapTuple       srvtup;
-
-   /*
-    * Open pg_foreign_server and search tuples which uses the foreign-data
-    * wrapper identified with fdwId.
-    */
-   srvrel = heap_open(ForeignServerRelationId, AccessShareLock);
-   ftrel = heap_open(ForeignTableRelationId, AccessShareLock);
-   ScanKeyInit(&srvkey[0],
-               Anum_pg_foreign_server_srvfdw,
-               BTEqualStrategyNumber, F_OIDEQ,
-               ObjectIdGetDatum(fdwId));
-   srvscan = heap_beginscan(srvrel, SnapshotNow, 1, srvkey);
-
-   while (HeapTupleIsValid(srvtup = heap_getnext(srvscan, ForwardScanDirection)))
-   {
-       ScanKeyData     ftkey[1];
-       HeapScanDesc    ftscan;
-       HeapTuple       fttup;
-
-       ScanKeyInit(&ftkey[0],
-                   Anum_pg_foreign_table_ftserver,
-                   BTEqualStrategyNumber, F_OIDEQ,
-                   ObjectIdGetDatum(HeapTupleGetOid(srvtup)));
-       ftscan = heap_beginscan(ftrel, SnapshotNow, 1, ftkey);
-
-       /* only one valid tuple can finish this check */
-       if (HeapTupleIsValid(fttup = heap_getnext(ftscan, ForwardScanDirection)))
-       {
-           found = true;
-           heap_endscan(ftscan);
-           break;
-       }
-
-       heap_endscan(ftscan);
-   }
-
-   /* cleanup */
-   heap_endscan(srvscan);
-   heap_close(srvrel, AccessShareLock);
-   heap_close(ftrel, AccessShareLock);
-
-   return found;
-}
-
-
 /*
  * Alter foreign-data wrapper
  */
@@ -633,21 +573,13 @@ AlterForeignDataWrapper(AlterFdwStmt *stmt)
        repl_repl[Anum_pg_foreign_data_wrapper_fdwhandler - 1] = true;
 
        /*
-        * Foreign-data wrapper with "NO HANDLER" cannot have foreign table.
-        * If any foreign table uses this foreign-data wrapper, make this
-        * statement fail.
+        * It could be that the behavior of accessing foreign table changes
+        * with the new handler.  Warn about this.
         */
-       if (defhandler->arg == NULL && have_foreign_table(fdwId))
-           elog(ERROR, "cannot unset handler because foreign table depend on it");
-
-       /* Check whether the handler has valid Iterate routine. */
-       if (fdwhandler != InvalidOid)
-       {
-           FdwRoutine     *routine = GetFdwRoutine(fdwhandler);
-           if (routine->Iterate != NULL)
-               ereport(ERROR,
-                       (errmsg("HANDLER must have valid Iterate routine")));
-       }
+       if (defhandler->arg)
+           ereport(WARNING,
+            (errmsg("changing the foreign-data wrapper handler would change "
+                    "the behavior of accessing foreign tables")));
    }
    else
    {
@@ -1373,13 +1305,6 @@ CreateForeignTable(CreateForeignTableStmt *stmt, Oid relid)
    if (aclresult != ACLCHECK_OK)
        aclcheck_error(aclresult, ACL_KIND_FDW, fdw->fdwname);
 
-   /*
-    * Check that the foreign-data wrapper have valid handler to access this
-    * foreign table.
-    */
-   if (fdw->fdwhandler == InvalidOid)
-       elog(ERROR, "foreign-data wrapper handler is required to create foreign table");
-
    /*
     * Insert tuple into pg_foreign_table.
     */
index b5a5be89e2d055e3ffcbc2a95525248c6d67bf4c..77c108ce40f2fee658bac6560471ab424a4dbb16 100644 (file)
@@ -51,6 +51,9 @@ ForeignNext(ForeignScanState *node)
    Assert(node->ss.ps.state->es_direction == ForwardScanDirection);
 
    /* tupleslot will be filled by Iterate. */
+   if (node->routine->Iterate == NULL)
+       ereport(ERROR,
+               (errmsg("foreign-data wrapper must support Iterate to scan foreign table")));
    node->routine->Iterate(node);
 
    /* Set tableoid if the tuple was valid. */
index 1cbc8eb64dca37de6bbd30210a98350699975a9f..85aa857d3a7e0f872562abfef511cb34a5e31c1e 100644 (file)
@@ -12,6 +12,6 @@ subdir = src/backend/foreign
 top_builddir = ../../..
 include $(top_builddir)/src/Makefile.global
 
-OBJS= foreign.o file_fdw.o file_parser.o
+OBJS= foreign.o
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/foreign/file_fdw.c b/src/backend/foreign/file_fdw.c
deleted file mode 100644 (file)
index 43e3206..0000000
+++ /dev/null
@@ -1,444 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * file_fdw.c
- *       foreign-datga wrapper for server-side flat files.
- *
- * Copyright (c) 2010, PostgreSQL Global Development Group
- *
- * IDENTIFICATION
- *       $PostgreSQL$
- *
- *-------------------------------------------------------------------------
- */
-
-#include "postgres.h"
-
-#include "access/reloptions.h"
-#include "catalog/pg_foreign_table.h"
-#include "catalog/pg_foreign_server.h"
-#include "commands/defrem.h"
-#include "foreign/foreign.h"
-#include "funcapi.h"
-#include "miscadmin.h"
-#include "nodes/makefuncs.h"
-#include "optimizer/cost.h"
-#include "parser/parsetree.h"
-#include "storage/fd.h"
-#include "utils/builtins.h"
-
-#include "file_parser.h"
-
-/*
- * Describes the valid options for objects which uses this wrapper.
- */
-struct FileFdwOption
-{
-   const char *optname;
-   Oid         optcontext;     /* Oid of catalog in which option may appear */
-};
-
-/*
- * Valid options for file_fdw.
- * These options are based on the options for COPY FROM command.
- *
- * Note: If you are adding new option for user mapping, you need to modify
- * fileBeginScan().  See comments of the function for detail.
- */
-static struct FileFdwOption valid_options[] = {
-   /* File options */
-   { "filename",       ForeignTableRelationId },
-
-   /* Format options */
-   /* Note: 'oids' option comes from the definition of the foreign table. */
-   { "format",         ForeignTableRelationId },
-   { "header",         ForeignTableRelationId },
-   { "delimiter",      ForeignTableRelationId },
-   { "quote",          ForeignTableRelationId },
-   { "escape",         ForeignTableRelationId },
-   { "null",           ForeignTableRelationId },
-   { "force_not_null", AttributeRelationId },
-
-   /* Centinel */
-   { NULL,         InvalidOid }
-};
-
-/*
- * SQL functions
- */
-extern Datum file_fdw_validator(PG_FUNCTION_ARGS);
-extern Datum file_fdw_handler(PG_FUNCTION_ARGS);
-
-/*
- * FDW routines
- */
-static void fileBeginScan(ForeignScanState *scanstate);
-static void fileIterate(ForeignScanState *scanstate);
-static void fileClose(ForeignScanState *scanstate);
-static void fileReOpen(ForeignScanState *scanstate);
-static void fileEstimateCosts(ForeignPath *path, PlannerInfo *root, RelOptInfo *baserel);
-
-/*
- * Check if the provided option is one of valid options.
- * context is the Oid of the catalog the option came from, or 0 if we
- * don't care.
- */
-static bool
-is_valid_option(const char *option, Oid context)
-{
-   struct FileFdwOption *opt;
-
-   for (opt = valid_options; opt->optname; opt++)
-       if (context == opt->optcontext && strcmp(opt->optname, option) == 0)
-           return true;
-   return false;
-}
-
-/*
- * Validate the generic option given to FOREIGN DATA WRAPPER, SERVER, USER
- * MAPPING or FOREIGN TABLE which use file_fdw.
- * Raise an ERROR if the option or its value is considered
- * invalid.
- */
-Datum
-file_fdw_validator(PG_FUNCTION_ARGS)
-{
-   List       *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
-   Oid         catalog = PG_GETARG_OID(1);
-
-   ListCell   *cell;
-
-   char       *format = NULL;
-   char       *delimiter = NULL;
-   char       *quote = NULL;
-   char       *escape = NULL;
-   char       *null = NULL;
-   bool        oids;
-   bool        header;
-
-   foreach(cell, options_list)
-   {
-       DefElem    *def = lfirst(cell);
-
-       if (!is_valid_option(def->defname, catalog))
-       {
-           struct FileFdwOption *opt;
-           StringInfoData buf;
-
-           /*
-            * Unknown option specified, complain about it. Provide a hint
-            * with list of valid options for the object.
-            */
-           initStringInfo(&buf);
-           for (opt = valid_options; opt->optname; opt++)
-               if (catalog == opt->optcontext)
-                   appendStringInfo(&buf, "%s%s", (buf.len > 0) ? ", " : "",
-                                    opt->optname);
-
-           ereport(ERROR,
-                   (errcode(ERRCODE_FDW_INVALID_OPTION_NAME),
-                    errmsg("invalid option \"%s\"", def->defname),
-               errhint("Valid options in this context are: %s", buf.data)));
-
-           PG_RETURN_BOOL(false);
-       }
-
-       if (strcmp(def->defname, "format") == 0)
-       {
-           if (pg_strcasecmp(strVal(def->arg), "csv") != 0 &&
-               pg_strcasecmp(strVal(def->arg), "text") != 0)
-               ereport(ERROR,
-                       (errcode(ERRCODE_FDW_INVALID_ATTRIBUTE_VALUE),
-                        errmsg("format must be csv or text")));
-           format = strVal(def->arg);
-       }
-       else if (strcmp(def->defname, "oids") == 0)
-       {
-           oids = defGetBoolean(def);
-       }
-       else if (strcmp(def->defname, "header") == 0)
-       {
-           header = defGetBoolean(def);
-       }
-       else if (strcmp(def->defname, "delimiter") == 0)
-       {
-           if (strlen(strVal(def->arg)) != 1)
-               ereport(ERROR,
-                       (errcode(ERRCODE_FDW_INVALID_ATTRIBUTE_VALUE),
-                        errmsg("delimiter must be a single one-byte1 character")));
-           if (strchr(strVal(def->arg), '\r') != NULL ||
-               strchr(strVal(def->arg), '\n') != NULL)
-               ereport(ERROR,
-                       (errcode(ERRCODE_FDW_INVALID_ATTRIBUTE_VALUE),
-                        errmsg("delimiter cannot be newline or carriage return")));
-           delimiter = strVal(def->arg);
-       }
-       else if (strcmp(def->defname, "quote") == 0)
-       {
-           if (strlen(strVal(def->arg)) != 1)
-               ereport(ERROR,
-                       (errcode(ERRCODE_FDW_INVALID_ATTRIBUTE_VALUE),
-                        errmsg("quote must be 1 byte")));
-           quote = strVal(def->arg);
-       }
-       else if (strcmp(def->defname, "escape") == 0)
-       {
-           if (strlen(strVal(def->arg)) != 1)
-               ereport(ERROR,
-                       (errcode(ERRCODE_FDW_INVALID_ATTRIBUTE_VALUE),
-                        errmsg("escape must be 1 byte")));
-           escape = strVal(def->arg);
-       }
-       else if (strcmp(def->defname, "null") == 0)
-       {
-           if (strchr(strVal(def->arg), '\r') != NULL ||
-               strchr(strVal(def->arg), '\n') != NULL)
-               ereport(ERROR,
-                       (errcode(ERRCODE_FDW_INVALID_ATTRIBUTE_VALUE),
-                        errmsg("null representation cannot use newline or carriage return")));
-           null = strVal(def->arg);
-       }
-       else if (strcmp(def->defname, "force_not_null") == 0)
-       {
-           /* is valid boolean string? */
-           defGetBoolean(def);
-       }
-   }
-
-   /* Check options which depend on the file format. */
-   if (format != NULL && pg_strcasecmp(format, "text") == 0)
-   {
-       if (delimiter && strchr("\\.abcdefghijklmnopqrstuvwxyz0123456789",
-                  delimiter[0]) != NULL)
-           ereport(ERROR,
-                   (errcode(ERRCODE_FDW_INVALID_ATTRIBUTE_VALUE),
-                    errmsg("delimiter cannot be \"%s\"", delimiter)));
-
-       if (escape != NULL)
-           ereport(ERROR,
-                   (errcode(ERRCODE_FDW_INVALID_ATTRIBUTE_VALUE),
-                    errmsg("escape available only in CSV mode")));
-   }
-   else if (format != NULL && pg_strcasecmp(format, "csv") == 0)
-   {
-       if (null != NULL && quote != NULL && strchr(null, quote[0]) != NULL)
-           ereport(ERROR,
-                   (errcode(ERRCODE_FDW_INVALID_ATTRIBUTE_VALUE),
-                    errmsg("quote must not appear in the NULL specification")));
-   }
-
-   if (delimiter != NULL && quote != NULL)
-       if (strcmp(delimiter, quote) == 0)
-           ereport(ERROR,
-                   (errcode(ERRCODE_FDW_INVALID_ATTRIBUTE_VALUE),
-                    errmsg("delimiter and quote must be different")));
-
-   if (null != NULL && delimiter != NULL)
-       if (strchr(null, delimiter[0]) != NULL)
-           ereport(ERROR,
-                   (errcode(ERRCODE_FDW_INVALID_ATTRIBUTE_VALUE),
-                    errmsg("delimiter must not appear in the NULL specification")));
-
-   PG_RETURN_BOOL(true);
-}
-
-/*
- * return foreign-data wrapper handler object to execute foreign-data wrapper
- * routines.
- */
-PG_FUNCTION_INFO_V1(file_fdw_handler);
-Datum
-file_fdw_handler(PG_FUNCTION_ARGS)
-{
-   static FdwRoutine file_fdw_routine =
-   {
-       NULL,               /* ConnectServer */
-       NULL,               /* FreeFSConnection */
-       fileEstimateCosts,
-       NULL,               /* Open */
-       fileBeginScan,
-       fileIterate,
-       fileClose,
-       fileReOpen,
-   };
-
-   PG_RETURN_POINTER(&file_fdw_routine);
-}
-
-/*
- * BeginScan()
- *   - initiate access to the file with creating FileState
- *
- * Parameters for parsing file such as filename and format are passed via
- * generic options of FDW-related objects; foreign-data wrapper, server and
- * foreign table.  User mapping is not used to get options because there is no
- * valid option in context of user mapping.
- */
-static void
-fileBeginScan(ForeignScanState *scanstate)
-{
-   ForeignTable   *table;
-   ForeignServer  *server;
-   ForeignDataWrapper *wrapper;
-   List           *options;
-   FileState       fstate;
-
-   elog(DEBUG3, "%s called", __FUNCTION__);
-
-   /* Extract options from FDW objects */
-   table = GetForeignTable(scanstate->ss.ss_currentRelation->rd_id);
-   server = GetForeignServer(table->serverid);
-   wrapper = GetForeignDataWrapper(server->fdwid);
-   options = NIL;
-   options = list_concat(options, wrapper->options);
-   options = list_concat(options, server->options);
-   options = list_concat(options, table->options);
-
-   /* oid is the only option which is not from FDW object. */
-   if (scanstate->ss.ss_currentRelation->rd_rel->relhasoids)
-   {
-       DefElem        *oid_option;
-       oid_option = makeDefElem(pstrdup("oids"),
-                                (Node *) makeString(pstrdup("true")));
-       options = lappend(options, oid_option);
-   }
-
-   /* create FileState and set default settings */
-   fstate = FileStateCreate(scanstate->ss.ss_currentRelation, options);
-
-   /* pack file information into reply and pass it to subsequent functions */
-   scanstate->reply = (FdwReply *) fstate;
-}
-
-/*
- * Iterate()
- *   - create HeapTuple from the record in the file.
- */
-static void
-fileIterate(ForeignScanState *scanstate)
-{
-   FileState       fstate = (FileState) scanstate->reply;
-   TupleTableSlot *slot = scanstate->ss.ss_ScanTupleSlot;
-   HeapTuple       tuple = NULL;
-
-   elog(DEBUG3, "%s called for \"%s\"",
-        __FUNCTION__,
-        FileStateGetFilename(fstate));
-
-   /* get next tuple from the file */
-   tuple = FileStateGetNext(fstate, CurrentMemoryContext);
-
-   /*
-    * If next tuple has been found, store it into the slot.  Otherwise,
-    * clear the slot to tell executor that we have reached EOF.
-    */
-   if (HeapTupleIsValid(tuple))
-   {
-       ExecStoreTuple(tuple, slot, InvalidBuffer, true);
-   }
-   else
-       ExecClearTuple(slot);
-}
-
-/*
- * Finish scanning foreign table and dispose objects used for this scan.
- */
-static void
-fileClose(ForeignScanState *scanstate)
-{
-   FileState       fstate = (FileState) scanstate->reply;
-
-   elog(DEBUG3, "%s called", __FUNCTION__);
-
-   FileStateFree(fstate);
-}
-
-/*
- * Execute query with new parameter.
- */
-static void
-fileReOpen(ForeignScanState *scanstate)
-{
-   FileState       fstate = (FileState) scanstate->reply;
-
-   elog(DEBUG3, "%s called for \"%s\"",
-        __FUNCTION__,
-        FileStateGetFilename(fstate));
-
-   FileStateReset(fstate);
-}
-
-/*
- * Estimate costs of scanning on a foreign table.
- */
-static void
-fileEstimateCosts(ForeignPath *path, PlannerInfo *root, RelOptInfo *baserel)
-{
-   RangeTblEntry  *rte;
-   ForeignTable   *table;
-   int             n;
-   const char    **keywords;
-   const char    **values;
-   int             i;
-   char           *filename = NULL;
-   struct stat     stat;
-   BlockNumber     pages;
-   double          run_cost = 0;
-   double          startup_cost = 0;
-   double          cpu_per_tuple;
-
-   elog(DEBUG3, "%s called", __FUNCTION__);
-
-   /* get filename from generic option of the foreign table */
-   rte = planner_rt_fetch(baserel->relid, root);
-   table = GetForeignTable(rte->relid);
-   keywords = palloc(sizeof(char *) * list_length(table->options));
-   values = palloc(sizeof(char *) * list_length(table->options));
-   n = flatten_generic_options(table->options, keywords, values);
-
-   for (i = 0; i < n; i++)
-   {
-       if (strcmp(keywords[i], "filename") == 0)
-       {
-           filename = pstrdup(values[i]);
-           break;
-       }
-   }
-
-   pfree(keywords);
-   pfree(values);
-
-   /* at least filename must be specified */
-   if (filename == NULL)
-   {
-       ereport(ERROR,
-               (errcode(ERRCODE_FDW_UNABLE_TO_CREATE_REPLY),
-                errmsg("generic option \"filename\" is required")));
-   }
-
-   /* get size of the file */
-   if (lstat(filename, &stat) == -1)
-   {
-       ereport(ERROR,
-               (errcode_for_file_access(),
-                errmsg("could not stat file \"%s\": %m", filename)));
-   }
-   pfree(filename);
-
-   /*
-    * The way to estimate costs is almost same as cost_seqscan(), but there
-    * are some differences:
-    * - DISK costs are estimated from file size.
-    * - CPU costs are 2x of seq scan, for overhead of parsing records.
-    */
-   pages = stat.st_size / BLCKSZ;
-   run_cost += seq_page_cost * pages;
-
-   startup_cost += baserel->baserestrictcost.startup;
-   cpu_per_tuple = cpu_tuple_cost + baserel->baserestrictcost.per_tuple;
-   cpu_per_tuple *= 2;
-   run_cost += cpu_per_tuple * baserel->tuples;
-
-   path->path.startup_cost = startup_cost;
-   path->path.total_cost = startup_cost + run_cost;
-}
-
diff --git a/src/backend/foreign/file_parser.c b/src/backend/foreign/file_parser.c
deleted file mode 100644 (file)
index f90c29b..0000000
+++ /dev/null
@@ -1,1902 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * file_parser.c
- *     Implements the parser of CSV/Text format file.  Heavily based
- *     on src/backend/commands/copy.c .
- *
- * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- *
- * IDENTIFICATION
- *   src/backend/foreign/file_parser.c
- *
- *-------------------------------------------------------------------------
- */
-#include "postgres.h"
-
-#include "access/xact.h"
-#include "access/reloptions.h"
-#include "commands/defrem.h"
-#include "executor/execdesc.h"
-#include "executor/executor.h"
-#include "mb/pg_wchar.h"
-#include "miscadmin.h"
-#include "rewrite/rewriteHandler.h"
-#include "utils/builtins.h"
-#include "utils/lsyscache.h"
-#include "utils/memutils.h"
-#include "utils/relcache.h"
-#include "utils/snapmgr.h"
-#include "utils/syscache.h"
-
-#include "file_parser.h"
-
-#define ISOCTAL(c) (((c) >= '0') && ((c) <= '7'))
-#define OCTVALUE(c) ((c) - '0')
-
-/*
- * Represents the end-of-line terminator type of the input
- */
-typedef enum EolType
-{
-   EOL_UNKNOWN,
-   EOL_NL,
-   EOL_CR,
-   EOL_CRNL
-} EolType;
-
-/*
- * This struct contains all the state variables used throughout parsing a file
- * which is in format of CSV or TEXT.
- *
- * Multi-byte encodings: all supported client-side encodings encode multi-byte
- * characters by having the first byte's high bit set. Subsequent bytes of the
- * character can have the high bit not set. When scanning data in such an
- * encoding to look for a match to a single-byte (ie ASCII) character, we must
- * use the full pg_encoding_mblen() machinery to skip over multibyte
- * characters, else we might find a false match to a trailing byte. In
- * supported server encodings, there is no possibility of a false match, and
- * it's faster to make useless comparisons to trailing bytes than it is to
- * invoke pg_encoding_mblen() to skip over them. encoding_embeds_ascii is TRUE
- * when we have to do it the hard way.
- */
-typedef struct FileStateData
-{
-   /* low-level state data */
-   FILE       *file;
-   EolType     eol_type;       /* EOL type of input */
-   int         client_encoding;    /* remote side's character encoding */
-   bool        need_transcoding;       /* client encoding diff from server? */
-   bool        encoding_embeds_ascii;  /* ASCII can be non-first byte? */
-   uint64      processed;      /* # of tuples processed */
-   bool        done;           /* read all records in the file? */
-
-   /* generic options from the FDW-related objects */
-   Relation    rel;            /* foreign table */
-   List       *attnumlist;     /* integer list of attnums to copy */
-   char       *filename;       /* filename, or NULL for STDIN/STDOUT */
-   bool        oids;           /* include OIDs? */
-   bool        csv_mode;       /* Comma Separated Value format? */
-   bool        header_line;    /* CSV header line? */
-   char       *null_print;     /* NULL marker string (server encoding!) */
-   int         null_print_len; /* length of same */
-   char       *null_print_client;      /* same converted to client encoding */
-   char       *delim;          /* column delimiter (must be 1 byte) */
-   char       *quote;          /* CSV quote char (must be 1 byte) */
-   char       *escape;         /* CSV escape char (must be 1 byte) */
-   bool       *force_notnull_flags;    /* per-column CSV FNN flags */
-
-   /* these are just for error messages, see file_error_callback */
-   ErrorContextCallback errcontext;
-   const char *cur_relname;    /* table name for error messages */
-   int         cur_lineno;     /* line number for error messages */
-   const char *cur_attname;    /* current att for error messages */
-   const char *cur_attval;     /* current att value for error messages */
-
-   /*
-    * These variables are used to reduce overhead in textual parsing.
-    *
-    * attribute_buf holds the separated, de-escaped text for each field of
-    * the current line.  The FileReadAttributes functions return arrays of
-    * pointers into this buffer.  We avoid palloc/pfree overhead by re-using
-    * the buffer on each cycle.
-    */
-   StringInfoData attribute_buf;
-
-   /*
-    * These variables are used to reduce overhead of memory allocation in the
-    * loop over FileStateGetNext().
-    */
-   Datum      *values;
-   bool       *nulls;
-   int         nfields;
-   char      **field_strings;
-
-   /*
-    * The definition of input functions and default expressions are stored
-    * in these variables.
-    */
-   EState     *estate;
-   FmgrInfo   *in_functions;
-   Oid        *typioparams;
-   int        *defmap;
-   ExprState **defexprs;       /* array of default att expressions */
-   AttrNumber  num_defaults;
-
-   /*
-    * Similarly, line_buf holds the whole input line being processed. The
-    * input cycle is first to read the whole line into line_buf, convert it
-    * to server encoding there, and then extract the individual attribute
-    * fields into attribute_buf.  line_buf is preserved unmodified so that we
-    * can display it in error messages if appropriate.
-    */
-   StringInfoData line_buf;
-   bool        line_buf_converted;     /* converted to server encoding? */
-
-   /*
-    * Finally, raw_buf holds raw data read from the data source (file or
-    * client connection).  FileReadLine parses this data sufficiently to
-    * locate line boundaries, then transfers the data to line_buf and
-    * converts it.  Note: we guarantee that there is a \0 at
-    * raw_buf[raw_buf_len].
-    */
-#define RAW_BUF_SIZE 65536     /* we palloc RAW_BUF_SIZE+1 bytes */
-   char       *raw_buf;
-   int         raw_buf_index;  /* next byte to process */
-   int         raw_buf_len;    /* total # of bytes stored */
-} FileStateData;
-
-
-/*
- * These macros centralize code used to process line_buf and raw_buf buffers.
- * They are macros because they often do continue/break control and to avoid
- * function call overhead in tight Iterate() loops.
- *
- * We must use "if (1)" because the usual "do {...} while(0)" wrapper would
- * prevent the continue/break processing from working. We end the "if (1)"
- * with "else ((void) 0)" to ensure the "if" does not unintentionally match
- * any "else" in the calling code, and to avoid any compiler warnings about
- * empty statements.  See http://www.cit.gu.edu.au/~anthony/info/C/C.macros.
- */
-
-/*
- * This keeps the character read at the top of the loop in the buffer
- * even if there is more than one read-ahead.
- */
-#define IF_NEED_REFILL_AND_NOT_EOF_CONTINUE(extralen) \
-if (1) \
-{ \
-   if (raw_buf_ptr + (extralen) >= raw_buf_len && !hit_eof) \
-   { \
-       raw_buf_ptr = prev_raw_ptr; /* undo fetch */ \
-       need_data = true; \
-       continue; \
-   } \
-} else ((void) 0)
-
-/* This consumes the remainder of the buffer and breaks */
-#define IF_NEED_REFILL_AND_EOF_BREAK(extralen) \
-if (1) \
-{ \
-   if (raw_buf_ptr + (extralen) >= raw_buf_len && hit_eof) \
-   { \
-       if (extralen) \
-           raw_buf_ptr = raw_buf_len; /* consume the partial character */ \
-       /* backslash just before EOF, treat as data char */ \
-       result = true; \
-       break; \
-   } \
-} else ((void) 0)
-
-/*
- * Transfer any approved data to line_buf; must do this to be sure
- * there is some room in raw_buf.
- */
-#define REFILL_LINEBUF \
-if (1) \
-{ \
-   if (raw_buf_ptr > fstate->raw_buf_index) \
-   { \
-       appendBinaryStringInfo(&fstate->line_buf, \
-                            fstate->raw_buf + fstate->raw_buf_index, \
-                              raw_buf_ptr - fstate->raw_buf_index); \
-       fstate->raw_buf_index = raw_buf_ptr; \
-   } \
-} else ((void) 0)
-
-/* Undo any read-ahead and jump out of the block. */
-#define NO_END_OF_COPY_GOTO \
-if (1) \
-{ \
-   raw_buf_ptr = prev_raw_ptr + 1; \
-   goto not_end_of_copy; \
-} else ((void) 0)
-
-/* non-export function prototypes */
-static bool FileReadLine(FileState fstate);
-static bool FileReadLineText(FileState fstate);
-static int FileReadAttributesText(FileState fstate);
-static int FileReadAttributesCSV(FileState fstate);
-static List *FileGetAttnums(TupleDesc tupDesc, Relation rel,
-              List *attnamelist);
-static char *limit_printout_length(const char *str);
-static void file_error_callback(void *arg);
-
-/*
- * FileGetData reads data from the source (file or frontend)
- *
- * We attempt to read at least minread, and at most maxread, bytes from
- * the source. The actual number of bytes read is returned; if this is
- * less than minread, EOF was detected.
- *
- * NB: no data conversion is applied here.
- */
-static int
-FileGetData(FileState fstate, void *databuf, int minread, int maxread)
-{
-   int         bytesread = 0;
-
-   bytesread = fread(databuf, 1, maxread, fstate->file);
-   if (ferror(fstate->file))
-       ereport(ERROR,
-               (errcode_for_file_access(),
-                errmsg("could not read from file \"%s\": %m",
-                       fstate->filename)));
-
-   return bytesread;
-}
-
-
-/*
- * FileLoadRawBuf loads some more data into raw_buf
- *
- * Returns TRUE if able to obtain at least one more byte, else FALSE.
- *
- * If raw_buf_index < raw_buf_len, the unprocessed bytes are transferred
- * down to the start of the buffer and then we load more data after that.
- * This case is used only when a frontend multibyte character crosses a
- * bufferload boundary.
- */
-static bool
-FileLoadRawBuf(FileState fstate)
-{
-   int         nbytes;
-   int         inbytes;
-
-   if (fstate->raw_buf_index < fstate->raw_buf_len)
-   {
-       /* Copy down the unprocessed data */
-       nbytes = fstate->raw_buf_len - fstate->raw_buf_index;
-       memmove(fstate->raw_buf, fstate->raw_buf + fstate->raw_buf_index,
-               nbytes);
-   }
-   else
-       nbytes = 0;             /* no data need be saved */
-
-   inbytes = FileGetData(fstate, fstate->raw_buf + nbytes,
-                         1, RAW_BUF_SIZE - nbytes);
-   nbytes += inbytes;
-   fstate->raw_buf[nbytes] = '\0';
-   fstate->raw_buf_index = 0;
-   fstate->raw_buf_len = nbytes;
-   return (inbytes > 0);
-}
-
-
-/*
- * FileStateCreate() makes a FileState for a foreign scan on the relation rel.
- */
-FileState
-FileStateCreate(Relation rel, List *options)
-{
-   FileState   fstate;
-   bool        format_specified = false;
-   ListCell   *option;
-   TupleDesc   tupDesc;
-   int         num_phys_attrs;
-   Form_pg_attribute *attr;
-   AttrNumber  attr_count;
-   int         attnum;
-   ResultRelInfo *resultRelInfo;
-
-   /* Allocate workspace and zero all fields */
-   fstate = (FileStateData *) palloc0(sizeof(FileStateData));
-
-   /* Extract information about the file and its format from options */
-   foreach(option, options)
-   {
-       DefElem    *defel = (DefElem *) lfirst(option);
-
-       if (strcmp(defel->defname, "filename") == 0)
-       {
-           fstate->filename = defGetString(defel);
-       }
-       else if (strcmp(defel->defname, "format") == 0)
-       {
-           char       *fmt = defGetString(defel);
-
-           if (format_specified)
-               ereport(ERROR,
-                       (errcode(ERRCODE_SYNTAX_ERROR),
-                        errmsg("conflicting or redundant options")));
-           format_specified = true;
-           if (strcmp(fmt, "text") == 0)
-                /* default format */ ;
-           else if (strcmp(fmt, "csv") == 0)
-               fstate->csv_mode = true;
-           else
-               ereport(ERROR,
-                       (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                        errmsg("format \"%s\" not recognized", fmt)));
-       }
-       else if (strcmp(defel->defname, "oids") == 0)
-       {
-           if (fstate->oids)
-               ereport(ERROR,
-                       (errcode(ERRCODE_SYNTAX_ERROR),
-                        errmsg("conflicting or redundant options")));
-           fstate->oids = defGetBoolean(defel);
-       }
-       else if (strcmp(defel->defname, "delimiter") == 0)
-       {
-           if (fstate->delim)
-               ereport(ERROR,
-                       (errcode(ERRCODE_SYNTAX_ERROR),
-                        errmsg("conflicting or redundant options")));
-           fstate->delim = defGetString(defel);
-       }
-       else if (strcmp(defel->defname, "null") == 0)
-       {
-           if (fstate->null_print)
-               ereport(ERROR,
-                       (errcode(ERRCODE_SYNTAX_ERROR),
-                        errmsg("conflicting or redundant options")));
-           fstate->null_print = defGetString(defel);
-       }
-       else if (strcmp(defel->defname, "header") == 0)
-       {
-           if (fstate->header_line)
-               ereport(ERROR,
-                       (errcode(ERRCODE_SYNTAX_ERROR),
-                        errmsg("conflicting or redundant options")));
-           fstate->header_line = defGetBoolean(defel);
-       }
-       else if (strcmp(defel->defname, "quote") == 0)
-       {
-           if (fstate->quote)
-               ereport(ERROR,
-                       (errcode(ERRCODE_SYNTAX_ERROR),
-                        errmsg("conflicting or redundant options")));
-           fstate->quote = defGetString(defel);
-       }
-       else if (strcmp(defel->defname, "escape") == 0)
-       {
-           if (fstate->escape)
-               ereport(ERROR,
-                       (errcode(ERRCODE_SYNTAX_ERROR),
-                        errmsg("conflicting or redundant options")));
-           fstate->escape = defGetString(defel);
-       }
-       else
-           ereport(ERROR,
-                   (errcode(ERRCODE_SYNTAX_ERROR),
-                    errmsg("option \"%s\" not recognized",
-                           defel->defname)));
-   }
-
-   if (fstate->filename == NULL)
-       ereport(ERROR,
-               (errcode(ERRCODE_FDW_UNALBE_TO_CREATE_EXECUTION),
-                errmsg("generic option filename is required")));
-
-   /* Set defaults for omitted options */
-   if (!fstate->delim)
-       fstate->delim = fstate->csv_mode ? "," : "\t";
-
-   if (!fstate->null_print)
-       fstate->null_print = fstate->csv_mode ? "" : "\\N";
-   fstate->null_print_len = strlen(fstate->null_print);
-
-   if (fstate->csv_mode)
-   {
-       if (!fstate->quote)
-           fstate->quote = "\"";
-       if (!fstate->escape)
-           fstate->escape = fstate->quote;
-   }
-
-   /*
-    * Disallow unsafe delimiter characters in non-CSV mode.  We can't allow
-    * backslash because it would be ambiguous.  We can't allow the other
-    * cases because data characters matching the delimiter must be
-    * backslashed, and certain backslash combinations are interpreted
-    * non-literally by COPY IN.  Disallowing all lower case ASCII letters is
-    * more than strictly necessary, but seems best for consistency and
-    * future-proofing.  Likewise we disallow all digits though only octal
-    * digits are actually dangerous.
-    */
-   if (!fstate->csv_mode &&
-       strchr("\\.abcdefghijklmnopqrstuvwxyz0123456789",
-              fstate->delim[0]) != NULL)
-       ereport(ERROR,
-               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                errmsg("delimiter cannot be \"%s\"", fstate->delim)));
-
-   /* Check header */
-   if (!fstate->csv_mode && fstate->header_line)
-       ereport(ERROR,
-               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                errmsg("HEADER available only in CSV mode")));
-
-   /* Check quote */
-   if (!fstate->csv_mode && fstate->quote != NULL)
-       ereport(ERROR,
-               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                errmsg("quote available only in CSV mode")));
-
-   if (fstate->csv_mode && strlen(fstate->quote) != 1)
-       ereport(ERROR,
-               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                errmsg("quote must be a single one-byte character")));
-
-   if (fstate->csv_mode && fstate->delim[0] == fstate->quote[0])
-       ereport(ERROR,
-               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                errmsg("delimiter and quote must be different")));
-
-   /* Check escape */
-   if (!fstate->csv_mode && fstate->escape != NULL)
-       ereport(ERROR,
-               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                errmsg("escape available only in CSV mode")));
-
-   if (fstate->csv_mode && strlen(fstate->escape) != 1)
-       ereport(ERROR,
-               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                errmsg("escape must be a single one-byte character")));
-
-   /* Don't allow the delimiter to appear in the null string. */
-   if (strchr(fstate->null_print, fstate->delim[0]) != NULL)
-       ereport(ERROR,
-               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-       errmsg("delimiter must not appear in the NULL specification")));
-
-   /* Don't allow the CSV quote char to appear in the null string. */
-   if (fstate->csv_mode &&
-       strchr(fstate->null_print, fstate->quote[0]) != NULL)
-       ereport(ERROR,
-               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                errmsg("CSV quote character must not appear in the NULL specification")));
-
-   /* Disallow reading server-side files except to superusers. */
-   if (!superuser())
-       ereport(ERROR,
-               (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-                errmsg("must be superuser to read from a file")));
-
-   fstate->rel = rel;
-   tupDesc = RelationGetDescr(fstate->rel);
-
-   /* Don't allow read OIDs from a table without them */
-   if (fstate->oids && !fstate->rel->rd_rel->relhasoids)
-       ereport(ERROR,
-               (errcode(ERRCODE_UNDEFINED_COLUMN),
-                errmsg("table \"%s\" does not have OIDs",
-                       RelationGetRelationName(fstate->rel))));
-
-   /* Generate or convert list of attributes to process */
-   fstate->attnumlist = FileGetAttnums(tupDesc, fstate->rel, NIL);
-
-   num_phys_attrs = tupDesc->natts;
-
-   /* Convert force_not_null options in each columns to per-column flags. */
-   if (fstate->csv_mode)
-   {
-       int i;
-       Form_pg_attribute *attrs = tupDesc->attrs;
-
-       /* slot for dropped column are necessary */
-       fstate->force_notnull_flags =
-           (bool *) palloc0(num_phys_attrs * sizeof(bool));
-
-       for (i = 0; i < num_phys_attrs; i++)
-       {
-           HeapTuple   tuple;
-           Datum       datum;
-           bool        isnull;
-           List       *options;
-           ListCell   *lc;
-
-           if (attrs[i]->attisdropped)
-               continue;
-
-           tuple = SearchSysCache2(ATTNUM, rel->rd_id, i + 1);
-           if (!HeapTupleIsValid(tuple))
-               ereport(ERROR,
-                       (errcode(ERRCODE_UNDEFINED_COLUMN),
-                        errmsg("attnum \"%d\" of relation \"%s\" does not exist",
-                               i + 1, RelationGetRelationName(fstate->rel))));
-
-           datum = SysCacheGetAttr(ATTNUM, tuple,
-                                   Anum_pg_attribute_attgenoptions,
-                                   &isnull);
-           if (!isnull)
-           {
-               options = untransformRelOptions(datum);
-               foreach (lc, options)
-               {
-                   DefElem    *def = lfirst(lc);
-                   if (strcmp(def->defname, "force_not_null") == 0)
-                   {
-                       fstate->force_notnull_flags[i] = defGetBoolean(def);
-                       break;
-                   }
-               }
-           }
-
-           ReleaseSysCache(tuple);
-       }
-   }
-
-   /* Set up variables to avoid per-attribute overhead. */
-   initStringInfo(&fstate->attribute_buf);
-   initStringInfo(&fstate->line_buf);
-   fstate->line_buf_converted = false;
-   fstate->raw_buf = (char *) palloc(RAW_BUF_SIZE + 1);
-   fstate->raw_buf_index = fstate->raw_buf_len = 0;
-   fstate->processed = 0;
-   fstate->done = false;
-
-   /*
-    * Set up encoding conversion info.  Even if the client and server
-    * encodings are the same, we must apply pg_client_to_server() to validate
-    * data in multibyte encodings.
-    */
-   fstate->client_encoding = pg_get_client_encoding();
-   fstate->need_transcoding =
-       (fstate->client_encoding != GetDatabaseEncoding() ||
-        pg_database_encoding_max_length() > 1);
-   /* See Multibyte encoding comment above */
-   fstate->encoding_embeds_ascii = PG_ENCODING_IS_CLIENT_ONLY(fstate->client_encoding);
-
-   fstate->estate = CreateExecutorState(); /* for ExecConstraints() */
-
-   tupDesc = RelationGetDescr(fstate->rel);
-   attr = tupDesc->attrs;
-   num_phys_attrs = tupDesc->natts;
-   attr_count = list_length(fstate->attnumlist);
-   fstate->num_defaults = 0;
-
-   /* We need a ResultRelInfo to check constraints. */
-   resultRelInfo = makeNode(ResultRelInfo);
-   resultRelInfo->ri_RangeTableIndex = 1;      /* dummy */
-   resultRelInfo->ri_RelationDesc = fstate->rel;
-
-   fstate->estate->es_result_relations = resultRelInfo;
-   fstate->estate->es_num_result_relations = 1;
-   fstate->estate->es_result_relation_info = resultRelInfo;
-
-   /*
-    * Pick up the required catalog information for each attribute in the
-    * relation, including the input function, the element type (to pass to
-    * the input function), and info about defaults and constraints.
-    */
-   fstate->in_functions = (FmgrInfo *) palloc(num_phys_attrs * sizeof(FmgrInfo));
-   fstate->typioparams = (Oid *) palloc(num_phys_attrs * sizeof(Oid));
-   fstate->defmap = (int *) palloc(num_phys_attrs * sizeof(int));
-   fstate->defexprs = (ExprState **) palloc(num_phys_attrs * sizeof(ExprState *));
-
-   for (attnum = 1; attnum <= num_phys_attrs; attnum++)
-   {
-       Oid         in_func_oid;
-
-       /* We don't need info for dropped attributes */
-       if (attr[attnum - 1]->attisdropped)
-           continue;
-
-       /* Fetch the input function and typioparam info */
-       getTypeInputInfo(attr[attnum - 1]->atttypid,
-                        &in_func_oid, &fstate->typioparams[attnum - 1]);
-       fmgr_info(in_func_oid, &fstate->in_functions[attnum - 1]);
-
-       /* Get default info if needed */
-       if (!list_member_int(fstate->attnumlist, attnum))
-       {
-           /* attribute is NOT to be copied from input */
-           /* use default value if one exists */
-           Node       *defexpr = build_column_default(fstate->rel, attnum);
-
-           if (defexpr != NULL)
-           {
-               fstate->defexprs[fstate->num_defaults] =
-                   ExecPrepareExpr((Expr *) defexpr, fstate->estate);
-               fstate->defmap[fstate->num_defaults] = attnum - 1;
-               fstate->num_defaults++;
-           }
-       }
-   }
-
-   fstate->values = (Datum *) palloc(num_phys_attrs * sizeof(Datum));
-   fstate->nulls = (bool *) palloc(num_phys_attrs * sizeof(bool));
-
-   /* create workspace for FileReadAttributes results */
-   fstate->nfields = fstate->oids ? (attr_count + 1) : attr_count;
-   fstate->field_strings = (char **) palloc(fstate->nfields * sizeof(char *));
-
-   /* Initialize state variables */
-   fstate->eol_type = EOL_UNKNOWN;
-   fstate->cur_relname = RelationGetRelationName(fstate->rel);
-   fstate->cur_lineno = 0;
-   fstate->cur_attname = NULL;
-   fstate->cur_attval = NULL;
-
-   /* Set up callback to identify error line number */
-   fstate->errcontext.callback = file_error_callback;
-   fstate->errcontext.arg = (void *) fstate;
-   fstate->errcontext.previous = error_context_stack;
-   error_context_stack = &fstate->errcontext;
-
-   return fstate;
-}
-
-
-void
-FileStateFree(FileState fstate)
-{
-   if (fstate == NULL)
-       return;
-
-   /* Done, clean up */
-   error_context_stack = fstate->errcontext.previous;
-
-   pfree(fstate->values);
-   pfree(fstate->nulls);
-   pfree(fstate->field_strings);
-
-   pfree(fstate->in_functions);
-   pfree(fstate->typioparams);
-   pfree(fstate->defmap);
-   pfree(fstate->defexprs);
-
-   ExecResetTupleTable(fstate->estate->es_tupleTable, false);
-
-   FreeExecutorState(fstate->estate);
-
-   if (fstate->file != NULL)
-       if (FreeFile(fstate->file))
-           ereport(ERROR,
-                   (errcode_for_file_access(),
-                    errmsg("could not read from file \"%s\": %m",
-                           fstate->filename)));
-
-   /* Clean up storage (probably not really necessary) */
-   pfree(fstate->attribute_buf.data);
-   pfree(fstate->line_buf.data);
-   pfree(fstate->raw_buf);
-   pfree(fstate);
-}
-
-
-/*
- * error context callback for CSV/Text file parsing
- */
-static void
-file_error_callback(void *arg)
-{
-   FileState   fstate = (FileState) arg;
-
-   if (fstate->cur_attname && fstate->cur_attval)
-   {
-       /* error is relevant to a particular column */
-       char       *attval;
-
-       attval = limit_printout_length(fstate->cur_attval);
-       errcontext("scanning %s, line %d, column %s: \"%s\"",
-                  fstate->cur_relname, fstate->cur_lineno,
-                  fstate->cur_attname, attval);
-       pfree(attval);
-   }
-   else if (fstate->cur_attname)
-   {
-       /* error is relevant to a particular column, value is NULL */
-       errcontext("scanning %s, line %d, column %s: null input",
-                  fstate->cur_relname, fstate->cur_lineno,
-                  fstate->cur_attname);
-   }
-   else
-   {
-       /* error is relevant to a particular line */
-       if (fstate->line_buf_converted || !fstate->need_transcoding)
-       {
-           char       *lineval;
-
-           lineval = limit_printout_length(fstate->line_buf.data);
-           errcontext("scanning %s, line %d: \"%s\"",
-                      fstate->cur_relname, fstate->cur_lineno, lineval);
-           pfree(lineval);
-       }
-       else
-       {
-           /*
-            * Here, the line buffer is still in a foreign encoding, and
-            * indeed it's quite likely that the error is precisely a
-            * failure to do encoding conversion (ie, bad data).  We dare
-            * not try to convert it, and at present there's no way to
-            * regurgitate it without conversion.  So we have to punt and
-            * just report the line number.
-            */
-           errcontext("scanning %s, line %d",
-                      fstate->cur_relname, fstate->cur_lineno);
-       }
-   }
-}
-
-/*
- * Make sure we don't print an unreasonable amount of data in a message.
- *
- * It would seem a lot easier to just use the sprintf "precision" limit to
- * truncate the string.  However, some versions of glibc have a bug/misfeature
- * that vsnprintf will always fail (return -1) if it is asked to truncate
- * a string that contains invalid byte sequences for the current encoding.
- * So, do our own truncation.  We return a pstrdup'd copy of the input.
- */
-static char *
-limit_printout_length(const char *str)
-{
-#define MAX_COPY_DATA_DISPLAY 100
-
-   int         slen = strlen(str);
-   int         len;
-   char       *res;
-
-   /* Fast path if definitely okay */
-   if (slen <= MAX_COPY_DATA_DISPLAY)
-       return pstrdup(str);
-
-   /* Apply encoding-dependent truncation */
-   len = pg_mbcliplen(str, slen, MAX_COPY_DATA_DISPLAY);
-
-   /*
-    * Truncate, and add "..." to show we truncated the input.
-    */
-   res = (char *) palloc(len + 4);
-   memcpy(res, str, len);
-   strcpy(res + len, "...");
-
-   return res;
-}
-
-/*
- * Read a record from the file and generate HeapTuple.
- */
-HeapTuple
-FileStateGetNext(FileState fstate, MemoryContext tupleContext)
-{
-   HeapTuple   tuple = NULL;
-   TupleDesc   tupDesc = RelationGetDescr(fstate->rel);
-   Form_pg_attribute *attr = tupDesc->attrs;
-   AttrNumber  num_phys_attrs = tupDesc->natts;
-   int         i;
-   Oid         loaded_oid = InvalidOid;
-   ExprContext *econtext;
-   MemoryContext oldcontext = CurrentMemoryContext;
-   ListCell   *cur;
-   int         fldct;
-   int         fieldno;
-   char       *string;
-
-   /* Initialize the state of the file on the first call. */
-   if (fstate->file == NULL)
-   {
-       struct stat st;
-
-       /*
-        * Open the file in read mode and check that the file is a regular file.
-        */
-       fstate->file = AllocateFile(fstate->filename, PG_BINARY_R);
-
-       if (fstate->file == NULL)
-           ereport(ERROR,
-                   (errcode_for_file_access(),
-                    errmsg("could not open file \"%s\" for reading: %m",
-                           fstate->filename)));
-
-       fstat(fileno(fstate->file), &st);
-       if (S_ISDIR(st.st_mode))
-           ereport(ERROR,
-                   (errcode(ERRCODE_WRONG_OBJECT_TYPE),
-                    errmsg("\"%s\" is a directory", fstate->filename)));
-
-       /* on input just throw the header line away */
-       if (fstate->header_line)
-       {
-           fstate->cur_lineno++;
-           fstate->done = FileReadLine(fstate);
-       }
-   }
-
-   /* Return NULL once reached EOF. */
-   if (fstate->done)
-       goto done;
-
-   /*
-    * Generate HeapTuple from each line of CSV/Text file.
-    */
-   CHECK_FOR_INTERRUPTS();
-
-   fstate->cur_lineno++;
-
-   /* Reset the per-tuple exprcontext */
-   ResetPerTupleExprContext(fstate->estate);
-
-   /* Switch into its memory context */
-   MemoryContextSwitchTo(GetPerTupleMemoryContext(fstate->estate));
-
-   /* Initialize all values for row to NULL */
-   MemSet(fstate->values, 0, num_phys_attrs * sizeof(Datum));
-   MemSet(fstate->nulls, true, num_phys_attrs * sizeof(bool));
-
-   /* Actually read the line into memory here */
-   fstate->done = FileReadLine(fstate);
-
-   /*
-    * EOF at start of line means we're done.  If we see EOF after
-    * some characters, we act as though it was newline followed by
-    * EOF, ie, process the line and then exit loop on next iteration.
-    */
-   if (fstate->done && fstate->line_buf.len == 0)
-       goto done;
-
-   /* Parse the line into de-escaped field values */
-   if (fstate->csv_mode)
-       fldct = FileReadAttributesCSV(fstate);
-   else
-       fldct = FileReadAttributesText(fstate);
-   fieldno = 0;
-
-   /* Read the OID field if present */
-   if (fstate->oids)
-   {
-       if (fieldno >= fldct)
-           ereport(ERROR,
-                   (errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
-                    errmsg("missing data for OID column")));
-       string = fstate->field_strings[fieldno++];
-
-       if (string == NULL)
-           ereport(ERROR,
-                   (errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
-                    errmsg("null OID in COPY data")));
-       else
-       {
-           fstate->cur_attname = "oid";
-           fstate->cur_attval = string;
-           loaded_oid = DatumGetObjectId(DirectFunctionCall1(oidin,
-                                          CStringGetDatum(string)));
-           if (loaded_oid == InvalidOid)
-               ereport(ERROR,
-                       (errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
-                        errmsg("invalid OID in data")));
-           fstate->cur_attname = NULL;
-           fstate->cur_attval = NULL;
-       }
-   }
-
-   /* Loop to read the user attributes on the line. */
-   foreach(cur, fstate->attnumlist)
-   {
-       int         attnum = lfirst_int(cur);
-       int         m = attnum - 1;
-
-       if (fieldno >= fldct)
-           ereport(ERROR,
-                   (errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
-                    errmsg("missing data for column \"%s\"",
-                           NameStr(attr[m]->attname))));
-       string = fstate->field_strings[fieldno++];
-
-       if (fstate->csv_mode && string == NULL &&
-           fstate->force_notnull_flags[m])
-       {
-           /* Go ahead and read the NULL string */
-           string = fstate->null_print;
-       }
-
-       fstate->cur_attname = NameStr(attr[m]->attname);
-       fstate->cur_attval = string;
-       fstate->values[m] = InputFunctionCall(&fstate->in_functions[m],
-                                             string,
-                                             fstate->typioparams[m],
-                                             attr[m]->atttypmod);
-       if (string != NULL)
-           fstate->nulls[m] = false;
-       fstate->cur_attname = NULL;
-       fstate->cur_attval = NULL;
-   }
-
-   Assert(fieldno == fstate->nfields);
-
-   /*
-    * Now compute and insert any defaults available for the columns not
-    * provided by the input data.  Anything not processed here or above
-    * will remain NULL.
-    */
-   econtext = GetPerTupleExprContext(fstate->estate);
-   for (i = 0; i < fstate->num_defaults; i++)
-   {
-       fstate->values[fstate->defmap[i]] =
-           ExecEvalExpr(fstate->defexprs[i], econtext,
-                        &fstate->nulls[fstate->defmap[i]], NULL);
-   }
-
-   /*
-    * The tuple is allocated in the specific memory context if caller wants.
-   */
-   if (tupleContext != NULL)
-       MemoryContextSwitchTo(tupleContext);
-
-   /* And now we can form the input tuple. */
-   tuple = heap_form_tuple(tupDesc, fstate->values, fstate->nulls);
-
-   if (fstate->oids)
-       HeapTupleSetOid(tuple, loaded_oid);
-
-   /* OK, return the tuple */
-   fstate->processed++;
-
-done:
-   MemoryContextSwitchTo(oldcontext);
-
-   return tuple;
-}
-
-
-/*
- * Read the next input line and stash it in line_buf, with conversion to
- * server encoding.
- *
- * Result is true if read was terminated by EOF, false if terminated
- * by newline. The terminating newline or EOF marker is not included
- * in the final value of line_buf.
- */
-static bool
-FileReadLine(FileState fstate)
-{
-   bool        result;
-
-   resetStringInfo(&fstate->line_buf);
-
-   /* Mark that encoding conversion hasn't occurred yet */
-   fstate->line_buf_converted = false;
-
-   /* Parse data and transfer into line_buf */
-   result = FileReadLineText(fstate);
-
-   if (!result)
-   {
-       /*
-        * If we didn't hit EOF, then we must have transferred the EOL marker
-        * to line_buf along with the data.  Get rid of it.
-        */
-       switch (fstate->eol_type)
-       {
-           case EOL_NL:
-               Assert(fstate->line_buf.len >= 1);
-               Assert(fstate->line_buf.data[fstate->line_buf.len - 1] == '\n');
-               fstate->line_buf.len--;
-               fstate->line_buf.data[fstate->line_buf.len] = '\0';
-               break;
-           case EOL_CR:
-               Assert(fstate->line_buf.len >= 1);
-               Assert(fstate->line_buf.data[fstate->line_buf.len - 1] == '\r');
-               fstate->line_buf.len--;
-               fstate->line_buf.data[fstate->line_buf.len] = '\0';
-               break;
-           case EOL_CRNL:
-               Assert(fstate->line_buf.len >= 2);
-               Assert(fstate->line_buf.data[fstate->line_buf.len - 2] == '\r');
-               Assert(fstate->line_buf.data[fstate->line_buf.len - 1] == '\n');
-               fstate->line_buf.len -= 2;
-               fstate->line_buf.data[fstate->line_buf.len] = '\0';
-               break;
-           case EOL_UNKNOWN:
-               /* shouldn't get here */
-               Assert(false);
-               break;
-       }
-   }
-
-   /* Done reading the line.  Convert it to server encoding. */
-   if (fstate->need_transcoding)
-   {
-       char       *cvt;
-
-       cvt = pg_client_to_server(fstate->line_buf.data,
-                                 fstate->line_buf.len);
-       if (cvt != fstate->line_buf.data)
-       {
-           /* transfer converted data back to line_buf */
-           resetStringInfo(&fstate->line_buf);
-           appendBinaryStringInfo(&fstate->line_buf, cvt, strlen(cvt));
-           pfree(cvt);
-       }
-   }
-
-   /* Now it's safe to use the buffer in error messages */
-   fstate->line_buf_converted = true;
-
-   return result;
-}
-
-/*
- * FileReadLineText - inner loop of FileReadLine for text mode
- */
-static bool
-FileReadLineText(FileState fstate)
-{
-   char       *raw_buf;
-   int         raw_buf_ptr;
-   int         raw_buf_len;
-   bool        need_data = false;
-   bool        hit_eof = false;
-   bool        result = false;
-   char        mblen_str[2];
-
-   /* CSV variables */
-   bool        first_char_in_line = true;
-   bool        in_quote = false,
-               last_was_esc = false;
-   char        quotec = '\0';
-   char        escapec = '\0';
-
-   if (fstate->csv_mode)
-   {
-       quotec = fstate->quote[0];
-       escapec = fstate->escape[0];
-       /* ignore special escape processing if it's the same as quotec */
-       if (quotec == escapec)
-           escapec = '\0';
-   }
-
-   mblen_str[1] = '\0';
-
-   /*
-    * The objective of this loop is to transfer the entire next input line
-    * into line_buf.  Hence, we only care for detecting newlines (\r and/or
-    * \n) and the end-of-copy marker (\.).
-    *
-    * In CSV mode, \r and \n inside a quoted field are just part of the data
-    * value and are put in line_buf.  We keep just enough state to know if we
-    * are currently in a quoted field or not.
-    *
-    * These four characters, and the CSV escape and quote characters, are
-    * assumed the same in frontend and backend encodings.
-    *
-    * For speed, we try to move data from raw_buf to line_buf in chunks
-    * rather than one character at a time.  raw_buf_ptr points to the next
-    * character to examine; any characters from raw_buf_index to raw_buf_ptr
-    * have been determined to be part of the line, but not yet transferred to
-    * line_buf.
-    *
-    * For a little extra speed within the loop, we copy raw_buf and
-    * raw_buf_len into local variables.
-    */
-   raw_buf = fstate->raw_buf;
-   raw_buf_ptr = fstate->raw_buf_index;
-   raw_buf_len = fstate->raw_buf_len;
-
-   for (;;)
-   {
-       int         prev_raw_ptr;
-       char        c;
-
-       /*
-        * Load more data if needed.  Ideally we would just force four bytes
-        * of read-ahead and avoid the many calls to
-        * IF_NEED_REFILL_AND_NOT_EOF_CONTINUE().
-        * One optimization would be to read-ahead four byte here but it
-        * hardly seems worth it, considering the size of the buffer.
-        */
-       if (raw_buf_ptr >= raw_buf_len || need_data)
-       {
-           REFILL_LINEBUF;
-
-           /*
-            * Try to read some more data.  This will certainly reset
-            * raw_buf_index to zero, and raw_buf_ptr must go with it.
-            */
-           if (!FileLoadRawBuf(fstate))
-               hit_eof = true;
-           raw_buf_ptr = 0;
-           raw_buf_len = fstate->raw_buf_len;
-
-           /*
-            * If we are completely out of data, break out of the loop,
-            * reporting EOF.
-            */
-           if (raw_buf_len <= 0)
-           {
-               result = true;
-               break;
-           }
-           need_data = false;
-       }
-
-       /* OK to fetch a character */
-       prev_raw_ptr = raw_buf_ptr;
-       c = raw_buf[raw_buf_ptr++];
-
-       if (fstate->csv_mode)
-       {
-           /*
-            * If character is '\\' or '\r', we may need to look ahead below.
-            * Force fetch of the next character if we don't already have it.
-            * We need to do this before changing CSV state, in case one of
-            * these characters is also the quote or escape character.
-            *
-            * Note: old-protocol does not like forced prefetch, but it's OK
-            * here since we cannot validly be at EOF.
-            */
-           if (c == '\\' || c == '\r')
-           {
-               IF_NEED_REFILL_AND_NOT_EOF_CONTINUE(0);
-           }
-
-           /*
-            * Dealing with quotes and escapes here is mildly tricky. If the
-            * quote char is also the escape char, there's no problem - we
-            * just use the char as a toggle. If they are different, we need
-            * to ensure that we only take account of an escape inside a
-            * quoted field and immediately preceding a quote char, and not
-            * the second in a escape-escape sequence.
-            */
-           if (in_quote && c == escapec)
-               last_was_esc = !last_was_esc;
-           if (c == quotec && !last_was_esc)
-               in_quote = !in_quote;
-           if (c != escapec)
-               last_was_esc = false;
-
-           /*
-            * Updating the line count for embedded CR and/or LF chars is
-            * necessarily a little fragile - this test is probably about the
-            * best we can do.  (XXX it's arguable whether we should do this
-            * at all --- is cur_lineno a physical or logical count?)
-            */
-           if (in_quote && c == (fstate->eol_type == EOL_NL ? '\n' : '\r'))
-               fstate->cur_lineno++;
-       }
-
-       /* Process \r */
-       if (c == '\r' && (!fstate->csv_mode || !in_quote))
-       {
-           /* Check for \r\n on first line, _and_ handle \r\n. */
-           if (fstate->eol_type == EOL_UNKNOWN ||
-               fstate->eol_type == EOL_CRNL)
-           {
-               /*
-                * If need more data, go back to loop top to load it.
-                *
-                * Note that if we are at EOF, c will wind up as '\0' because
-                * of the guaranteed pad of raw_buf.
-                */
-               IF_NEED_REFILL_AND_NOT_EOF_CONTINUE(0);
-
-               /* get next char */
-               c = raw_buf[raw_buf_ptr];
-
-               if (c == '\n')
-               {
-                   raw_buf_ptr++;      /* eat newline */
-                   fstate->eol_type = EOL_CRNL;        /* in case not set yet */
-               }
-               else
-               {
-                   /* found \r, but no \n */
-                   if (fstate->eol_type == EOL_CRNL)
-                       ereport(ERROR,
-                               (errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
-                                !fstate->csv_mode ?
-                           errmsg("literal carriage return found in data") :
-                           errmsg("unquoted carriage return found in data"),
-                                !fstate->csv_mode ?
-                       errhint("Use \"\\r\" to represent carriage return.") :
-                                errhint("Use quoted CSV field to represent carriage return.")));
-
-                   /*
-                    * if we got here, it is the first line and we didn't find
-                    * \n, so don't consume the peeked character
-                    */
-                   fstate->eol_type = EOL_CR;
-               }
-           }
-           else if (fstate->eol_type == EOL_NL)
-               ereport(ERROR,
-                       (errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
-                        !fstate->csv_mode ?
-                        errmsg("literal carriage return found in data") :
-                        errmsg("unquoted carriage return found in data"),
-                        !fstate->csv_mode ?
-                      errhint("Use \"\\r\" to represent carriage return.") :
-                        errhint("Use quoted CSV field to represent carriage return.")));
-           /* If reach here, we have found the line terminator */
-           break;
-       }
-
-       /* Process \n */
-       if (c == '\n' && (!fstate->csv_mode || !in_quote))
-       {
-           if (fstate->eol_type == EOL_CR || fstate->eol_type == EOL_CRNL)
-               ereport(ERROR,
-                       (errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
-                        !fstate->csv_mode ?
-                        errmsg("literal newline found in data") :
-                        errmsg("unquoted newline found in data"),
-                        !fstate->csv_mode ?
-                        errhint("Use \"\\n\" to represent newline.") :
-                    errhint("Use quoted CSV field to represent newline.")));
-           fstate->eol_type = EOL_NL;  /* in case not set yet */
-           /* If reach here, we have found the line terminator */
-           break;
-       }
-
-       /*
-        * In CSV mode, we only recognize \. alone on a line.  This is because
-        * \. is a valid CSV data value.
-        */
-       if (c == '\\' && (!fstate->csv_mode || first_char_in_line))
-       {
-           char        c2;
-
-           IF_NEED_REFILL_AND_NOT_EOF_CONTINUE(0);
-           IF_NEED_REFILL_AND_EOF_BREAK(0);
-
-           /* -----
-            * get next character
-            * Note: we do not change c so if it isn't \., we can fall
-            * through and continue processing for client encoding.
-            * -----
-            */
-           c2 = raw_buf[raw_buf_ptr];
-
-           if (c2 == '.')
-           {
-               raw_buf_ptr++;  /* consume the '.' */
-
-               /*
-                * Note: if we loop back for more data here, it does not
-                * matter that the CSV state change checks are re-executed; we
-                * will come back here with no important state changed.
-                */
-               if (fstate->eol_type == EOL_CRNL)
-               {
-                   /* Get the next character */
-                   IF_NEED_REFILL_AND_NOT_EOF_CONTINUE(0);
-                   /* if hit_eof, c2 will become '\0' */
-                   c2 = raw_buf[raw_buf_ptr++];
-
-                   if (c2 == '\n')
-                   {
-                       if (!fstate->csv_mode)
-                           ereport(ERROR,
-                                   (errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
-                                    errmsg("end-of-copy marker does not match previous newline style")));
-                       else
-                           NO_END_OF_COPY_GOTO;
-                   }
-                   else if (c2 != '\r')
-                   {
-                       if (!fstate->csv_mode)
-                           ereport(ERROR,
-                                   (errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
-                                    errmsg("end-of-copy marker corrupt")));
-                       else
-                           NO_END_OF_COPY_GOTO;
-                   }
-               }
-
-               /* Get the next character */
-               IF_NEED_REFILL_AND_NOT_EOF_CONTINUE(0);
-               /* if hit_eof, c2 will become '\0' */
-               c2 = raw_buf[raw_buf_ptr++];
-
-               if (c2 != '\r' && c2 != '\n')
-               {
-                   if (!fstate->csv_mode)
-                       ereport(ERROR,
-                               (errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
-                                errmsg("end-of-copy marker corrupt")));
-                   else
-                       NO_END_OF_COPY_GOTO;
-               }
-
-               if ((fstate->eol_type == EOL_NL && c2 != '\n') ||
-                   (fstate->eol_type == EOL_CRNL && c2 != '\n') ||
-                   (fstate->eol_type == EOL_CR && c2 != '\r'))
-               {
-                   ereport(ERROR,
-                           (errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
-                            errmsg("end-of-copy marker does not match previous newline style")));
-               }
-
-               /*
-                * Transfer only the data before the \. into line_buf, then
-                * discard the data and the \. sequence.
-                */
-               if (prev_raw_ptr > fstate->raw_buf_index)
-                   appendBinaryStringInfo(&fstate->line_buf,
-                                    fstate->raw_buf + fstate->raw_buf_index,
-                                      prev_raw_ptr - fstate->raw_buf_index);
-               fstate->raw_buf_index = raw_buf_ptr;
-               result = true;  /* report EOF */
-               break;
-           }
-           else if (!fstate->csv_mode)
-
-               /*
-                * If we are here, it means we found a backslash followed by
-                * something other than a period.  In non-CSV mode, anything
-                * after a backslash is special, so we skip over that second
-                * character too.  If we didn't do that \\. would be
-                * considered an eof-of copy, while in non-CSV mode it is a
-                * literal backslash followed by a period.  In CSV mode,
-                * backslashes are not special, so we want to process the
-                * character after the backslash just like a normal character,
-                * so we don't increment in those cases.
-                */
-               raw_buf_ptr++;
-       }
-
-       /*
-        * This label is for CSV cases where \. appears at the start of a
-        * line, but there is more text after it, meaning it was a data value.
-        * We are more strict for \. in CSV mode because \. could be a data
-        * value, while in non-CSV mode, \. cannot be a data value.
-        */
-not_end_of_copy:
-
-       /*
-        * Process all bytes of a multi-byte character as a group.
-        *
-        * We only support multi-byte sequences where the first byte has the
-        * high-bit set, so as an optimization we can avoid this block
-        * entirely if it is not set.
-        */
-       if (fstate->encoding_embeds_ascii && IS_HIGHBIT_SET(c))
-       {
-           int         mblen;
-
-           mblen_str[0] = c;
-           /* All our encodings only read the first byte to get the length */
-           mblen = pg_encoding_mblen(fstate->client_encoding, mblen_str);
-           IF_NEED_REFILL_AND_NOT_EOF_CONTINUE(mblen - 1);
-           IF_NEED_REFILL_AND_EOF_BREAK(mblen - 1);
-           raw_buf_ptr += mblen - 1;
-       }
-       first_char_in_line = false;
-   }                           /* end of outer loop */
-
-   /*
-    * Transfer any still-uncopied data to line_buf.
-    */
-   REFILL_LINEBUF;
-
-   /* write buffer state back */
-   fstate->raw_buf_index = raw_buf_ptr;
-   fstate->raw_buf_len = raw_buf_len;
-
-   return result;
-}
-
-/*
- * Return decimal value for a hexadecimal digit
- */
-static int
-GetDecimalFromHex(char hex)
-{
-   if (isdigit((unsigned char) hex))
-       return hex - '0';
-   else
-       return tolower((unsigned char) hex) - 'a' + 10;
-}
-
-/*
- * Parse the current line into separate attributes (fields),
- * performing de-escaping as needed.
- *
- * The input is in line_buf.  We use attribute_buf to hold the result
- * strings.  fstate->field_strings[k] is set to point to the k'th attribute
- * string, or NULL when the input matches the null marker string.  (Note that
- * the caller cannot check for nulls since the returned string would be the
- * post-de-escaping equivalent, which may look the same as some valid data
- * string.)
- *
- * delim is the column delimiter string (must be just one byte for now).
- * null_print is the null marker string.  Note that this is compared to
- * the pre-de-escaped input string.
- *
- * The return value is the number of fields actually read. (We error out
- * if this would exceed maxfields, which is the length of
- * fstate->field_strings[].)
- */
-static int
-FileReadAttributesText(FileState fstate)
-{
-   char        delimc = fstate->delim[0];
-   int         fieldno;
-   char       *output_ptr;
-   char       *cur_ptr;
-   char       *line_end_ptr;
-
-   /*
-    * We need a special case for zero-column tables: check that the input
-    * line is empty, and return.
-    */
-   if (fstate->nfields <= 0)
-   {
-       if (fstate->line_buf.len != 0)
-           ereport(ERROR,
-                   (errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
-                    errmsg("extra data after last expected column")));
-       return 0;
-   }
-
-   resetStringInfo(&fstate->attribute_buf);
-
-   /*
-    * The de-escaped attributes will certainly not be longer than the input
-    * data line, so we can just force attribute_buf to be large enough and
-    * then transfer data without any checks for enough space.  We need to do
-    * it this way because enlarging attribute_buf mid-stream would invalidate
-    * pointers already stored into fstate->field_strings[].
-    */
-   if (fstate->attribute_buf.maxlen <= fstate->line_buf.len)
-       enlargeStringInfo(&fstate->attribute_buf, fstate->line_buf.len);
-   output_ptr = fstate->attribute_buf.data;
-
-   /* set pointer variables for loop */
-   cur_ptr = fstate->line_buf.data;
-   line_end_ptr = fstate->line_buf.data + fstate->line_buf.len;
-
-   /* Outer loop iterates over fields */
-   fieldno = 0;
-   for (;;)
-   {
-       bool        found_delim = false;
-       char       *start_ptr;
-       char       *end_ptr;
-       int         input_len;
-       bool        saw_non_ascii = false;
-
-       /* Make sure space remains in fstate->field_strings[] */
-       if (fieldno >= fstate->nfields)
-           ereport(ERROR,
-                   (errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
-                    errmsg("extra data after last expected column")));
-
-       /* Remember start of field on both input and output sides */
-       start_ptr = cur_ptr;
-       fstate->field_strings[fieldno] = output_ptr;
-
-       /* Scan data for field */
-       for (;;)
-       {
-           char        c;
-
-           end_ptr = cur_ptr;
-           if (cur_ptr >= line_end_ptr)
-               break;
-           c = *cur_ptr++;
-           if (c == delimc)
-           {
-               found_delim = true;
-               break;
-           }
-           if (c == '\\')
-           {
-               if (cur_ptr >= line_end_ptr)
-                   break;
-               c = *cur_ptr++;
-               switch (c)
-               {
-                   case '0':
-                   case '1':
-                   case '2':
-                   case '3':
-                   case '4':
-                   case '5':
-                   case '6':
-                   case '7':
-                       {
-                           /* handle \013 */
-                           int         val;
-
-                           val = OCTVALUE(c);
-                           if (cur_ptr < line_end_ptr)
-                           {
-                               c = *cur_ptr;
-                               if (ISOCTAL(c))
-                               {
-                                   cur_ptr++;
-                                   val = (val << 3) + OCTVALUE(c);
-                                   if (cur_ptr < line_end_ptr)
-                                   {
-                                       c = *cur_ptr;
-                                       if (ISOCTAL(c))
-                                       {
-                                           cur_ptr++;
-                                           val = (val << 3) + OCTVALUE(c);
-                                       }
-                                   }
-                               }
-                           }
-                           c = val & 0377;
-                           if (c == '\0' || IS_HIGHBIT_SET(c))
-                               saw_non_ascii = true;
-                       }
-                       break;
-                   case 'x':
-                       /* Handle \x3F */
-                       if (cur_ptr < line_end_ptr)
-                       {
-                           char        hexchar = *cur_ptr;
-
-                           if (isxdigit((unsigned char) hexchar))
-                           {
-                               int         val = GetDecimalFromHex(hexchar);
-
-                               cur_ptr++;
-                               if (cur_ptr < line_end_ptr)
-                               {
-                                   hexchar = *cur_ptr;
-                                   if (isxdigit((unsigned char) hexchar))
-                                   {
-                                       cur_ptr++;
-                                       val = (val << 4) + GetDecimalFromHex(hexchar);
-                                   }
-                               }
-                               c = val & 0xff;
-                               if (c == '\0' || IS_HIGHBIT_SET(c))
-                                   saw_non_ascii = true;
-                           }
-                       }
-                       break;
-                   case 'b':
-                       c = '\b';
-                       break;
-                   case 'f':
-                       c = '\f';
-                       break;
-                   case 'n':
-                       c = '\n';
-                       break;
-                   case 'r':
-                       c = '\r';
-                       break;
-                   case 't':
-                       c = '\t';
-                       break;
-                   case 'v':
-                       c = '\v';
-                       break;
-
-                       /*
-                        * in all other cases, take the char after '\'
-                        * literally
-                        */
-               }
-           }
-
-           /* Add c to output string */
-           *output_ptr++ = c;
-       }
-
-       /* Terminate attribute value in output area */
-       *output_ptr++ = '\0';
-
-       /*
-        * If we de-escaped a non-7-bit-ASCII char, make sure we still have
-        * valid data for the db encoding. Avoid calling strlen here for the
-        * sake of efficiency.
-        */
-       if (saw_non_ascii)
-       {
-           char       *fld = fstate->field_strings[fieldno];
-
-           pg_verifymbstr(fld, output_ptr - (fld + 1), false);
-       }
-
-       /* Check whether raw input matched null marker */
-       input_len = end_ptr - start_ptr;
-       if (input_len == fstate->null_print_len &&
-           strncmp(start_ptr, fstate->null_print, input_len) == 0)
-           fstate->field_strings[fieldno] = NULL;
-
-       fieldno++;
-       /* Done if we hit EOL instead of a delim */
-       if (!found_delim)
-           break;
-   }
-
-   /* Clean up state of attribute_buf */
-   output_ptr--;
-   Assert(*output_ptr == '\0');
-   fstate->attribute_buf.len = (output_ptr - fstate->attribute_buf.data);
-
-   return fieldno;
-}
-
-/*
- * Parse the current line into separate attributes (fields),
- * performing de-escaping as needed.  This has exactly the same API as
- * FileReadAttributesText, except we parse the fields according to
- * "standard" (i.e. common) CSV usage.
- */
-static int
-FileReadAttributesCSV(FileState fstate)
-{
-   char        delimc = fstate->delim[0];
-   char        quotec = fstate->quote[0];
-   char        escapec = fstate->escape[0];
-   int         fieldno;
-   char       *output_ptr;
-   char       *cur_ptr;
-   char       *line_end_ptr;
-
-   /*
-    * We need a special case for zero-column tables: check that the input
-    * line is empty, and return.
-    */
-   if (fstate->nfields <= 0)
-   {
-       if (fstate->line_buf.len != 0)
-           ereport(ERROR,
-                   (errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
-                    errmsg("extra data after last expected column")));
-       return 0;
-   }
-
-   resetStringInfo(&fstate->attribute_buf);
-
-   /*
-    * The de-escaped attributes will certainly not be longer than the input
-    * data line, so we can just force attribute_buf to be large enough and
-    * then transfer data without any checks for enough space.  We need to do
-    * it this way because enlarging attribute_buf mid-stream would invalidate
-    * pointers already stored into fstate->field_strings[].
-    */
-   if (fstate->attribute_buf.maxlen <= fstate->line_buf.len)
-       enlargeStringInfo(&fstate->attribute_buf, fstate->line_buf.len);
-   output_ptr = fstate->attribute_buf.data;
-
-   /* set pointer variables for loop */
-   cur_ptr = fstate->line_buf.data;
-   line_end_ptr = fstate->line_buf.data + fstate->line_buf.len;
-
-   /* Outer loop iterates over fields */
-   fieldno = 0;
-   for (;;)
-   {
-       bool        found_delim = false;
-       bool        saw_quote = false;
-       char       *start_ptr;
-       char       *end_ptr;
-       int         input_len;
-
-       /* Make sure space remains in fstate->field_strings[] */
-       if (fieldno >= fstate->nfields)
-           ereport(ERROR,
-                   (errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
-                    errmsg("extra data after last expected column")));
-
-       /* Remember start of field on both input and output sides */
-       start_ptr = cur_ptr;
-       fstate->field_strings[fieldno] = output_ptr;
-
-       /*
-        * Scan data for field,
-        *
-        * The loop starts in "not quote" mode and then toggles between that
-        * and "in quote" mode. The loop exits normally if it is in "not
-        * quote" mode and a delimiter or line end is seen.
-        */
-       for (;;)
-       {
-           char        c;
-
-           /* Not in quote */
-           for (;;)
-           {
-               end_ptr = cur_ptr;
-               if (cur_ptr >= line_end_ptr)
-                   goto endfield;
-               c = *cur_ptr++;
-               /* unquoted field delimiter */
-               if (c == delimc)
-               {
-                   found_delim = true;
-                   goto endfield;
-               }
-               /* start of quoted field (or part of field) */
-               if (c == quotec)
-               {
-                   saw_quote = true;
-                   break;
-               }
-               /* Add c to output string */
-               *output_ptr++ = c;
-           }
-
-           /* In quote */
-           for (;;)
-           {
-               end_ptr = cur_ptr;
-               if (cur_ptr >= line_end_ptr)
-                   ereport(ERROR,
-                           (errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
-                            errmsg("unterminated CSV quoted field")));
-
-               c = *cur_ptr++;
-
-               /* escape within a quoted field */
-               if (c == escapec)
-               {
-                   /*
-                    * peek at the next char if available, and escape it if it
-                    * is an escape char or a quote char
-                    */
-                   if (cur_ptr < line_end_ptr)
-                   {
-                       char        nextc = *cur_ptr;
-
-                       if (nextc == escapec || nextc == quotec)
-                       {
-                           *output_ptr++ = nextc;
-                           cur_ptr++;
-                           continue;
-                       }
-                   }
-               }
-
-               /*
-                * end of quoted field. Must do this test after testing for
-                * escape in case quote char and escape char are the same
-                * (which is the common case).
-                */
-               if (c == quotec)
-                   break;
-
-               /* Add c to output string */
-               *output_ptr++ = c;
-           }
-       }
-endfield:
-
-       /* Terminate attribute value in output area */
-       *output_ptr++ = '\0';
-
-       /* Check whether raw input matched null marker */
-       input_len = end_ptr - start_ptr;
-       if (!saw_quote && input_len == fstate->null_print_len &&
-           strncmp(start_ptr, fstate->null_print, input_len) == 0)
-           fstate->field_strings[fieldno] = NULL;
-
-       fieldno++;
-       /* Done if we hit EOL instead of a delim */
-       if (!found_delim)
-           break;
-   }
-
-   /* Clean up state of attribute_buf */
-   output_ptr--;
-   Assert(*output_ptr == '\0');
-   fstate->attribute_buf.len = (output_ptr - fstate->attribute_buf.data);
-
-   return fieldno;
-}
-
-
-/*
- * FileGetAttnums - build an integer list of attnums to be copied
- *
- * The input attnamelist is either the user-specified column list,
- * or NIL if there was none (in which case we want all the non-dropped
- * columns).
- *
- * rel can be NULL ... it's only used for error reports.
- */
-static List *
-FileGetAttnums(TupleDesc tupDesc, Relation rel, List *attnamelist)
-{
-   List       *attnums = NIL;
-
-   if (attnamelist == NIL)
-   {
-       /* Generate default column list */
-       Form_pg_attribute *attr = tupDesc->attrs;
-       int         attr_count = tupDesc->natts;
-       int         i;
-
-       for (i = 0; i < attr_count; i++)
-       {
-           if (attr[i]->attisdropped)
-               continue;
-           attnums = lappend_int(attnums, i + 1);
-       }
-   }
-   else
-   {
-       /* Validate the user-supplied list and extract attnums */
-       ListCell   *l;
-
-       foreach(l, attnamelist)
-       {
-           char       *name = strVal(lfirst(l));
-           int         attnum;
-           int         i;
-
-           /* Lookup column name */
-           attnum = InvalidAttrNumber;
-           for (i = 0; i < tupDesc->natts; i++)
-           {
-               if (tupDesc->attrs[i]->attisdropped)
-                   continue;
-               if (namestrcmp(&(tupDesc->attrs[i]->attname), name) == 0)
-               {
-                   attnum = tupDesc->attrs[i]->attnum;
-                   break;
-               }
-           }
-           if (attnum == InvalidAttrNumber)
-           {
-               if (rel != NULL)
-                   ereport(ERROR,
-                           (errcode(ERRCODE_UNDEFINED_COLUMN),
-                   errmsg("column \"%s\" of relation \"%s\" does not exist",
-                          name, RelationGetRelationName(rel))));
-               else
-                   ereport(ERROR,
-                           (errcode(ERRCODE_UNDEFINED_COLUMN),
-                            errmsg("column \"%s\" does not exist",
-                                   name)));
-           }
-           /* Check for duplicates */
-           if (list_member_int(attnums, attnum))
-               ereport(ERROR,
-                       (errcode(ERRCODE_DUPLICATE_COLUMN),
-                        errmsg("column \"%s\" specified more than once",
-                               name)));
-           attnums = lappend_int(attnums, attnum);
-       }
-   }
-
-   return attnums;
-}
-
-void
-FileStateReset(FileState fstate)
-{
-   /* If the file has not been opened, it is virtually reseted already. */
-   if (fstate->file == NULL)
-       return;
-
-   if (fseek(fstate->file, 0, SEEK_SET) == -1)
-       ereport(ERROR,
-               (errcode_for_file_access(),
-                errmsg("could not seek file \"%s\": %m", fstate->filename)));
-
-   /* reset internal state of reading file */
-   fstate->processed = 0;
-   fstate->done = false;
-   fstate->cur_lineno = 0;
-   fstate->raw_buf_index = fstate->raw_buf_len = 0;
-   resetStringInfo(&fstate->line_buf);
-   resetStringInfo(&fstate->attribute_buf);
-}
-
-const char *
-FileStateGetFilename(FileState fstate)
-{
-   return fstate->filename;
-}
diff --git a/src/backend/foreign/file_parser.h b/src/backend/foreign/file_parser.h
deleted file mode 100644 (file)
index 665f747..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * file_parser.h
- *   Implements the parrser of CSV/Text format file.
- *
- *
- * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
- *
- * src/backend/foreign/file_parser.h
- *
- *-------------------------------------------------------------------------
- */
-#ifndef FILE_PARSER_H
-#define FILE_PARSER_H
-
-typedef struct FileStateData *FileState;
-
-/*
- *
- */
-FileState FileStateCreate(Relation rel, List *options);
-HeapTuple FileStateGetNext(FileState cstate, MemoryContext tupleContext);
-void FileStateReset(FileState cstate);
-void FileStateFree(FileState cstate);
-
-/*
- * FileState access methods
- */
-const char *FileStateGetFilename(FileState cstate);
-
-
-#endif   /* FILE_PARSER_H */
index 847d05c4bdbfe360779c779a34cbfc27effcbfd1..7834d30679dd7ddada496bdf8dd52bf3f7ddb853 100644 (file)
@@ -467,6 +467,9 @@ GetFdwRoutine(Oid fdwhandler)
    Datum                   result;
    FdwRoutine             *routine;
 
+   if (fdwhandler == InvalidOid)
+       elog(ERROR, "foreign-data wrapper has no handler");
+
    fmgr_info(fdwhandler, &flinfo);
    InitFunctionCallInfoData(fcinfo, &flinfo, 0, NULL, NULL);
    result = FunctionCallInvoke(&fcinfo);
@@ -495,10 +498,6 @@ GetFdwRoutineByRelId(Oid relid)
    Oid                     serverid;
    Oid                     fdwid;
    Oid                     fdwhandler;
-   FmgrInfo                flinfo;
-   FunctionCallInfoData    fcinfo;
-   Datum                   result;
-   FdwRoutine             *routine;
 
    /* Get function OID for the foreign table. */
    tp = SearchSysCache1(FOREIGNTABLEREL, ObjectIdGetDatum(relid));
@@ -522,19 +521,7 @@ GetFdwRoutineByRelId(Oid relid)
    fdwhandler = fdwform->fdwhandler;
    ReleaseSysCache(tp);
 
-   /* Get FdwRoutine by invoking fdwhandler function. */
-   fmgr_info(fdwhandler, &flinfo);
-   InitFunctionCallInfoData(fcinfo, &flinfo, 0, NULL, NULL);
-   result = FunctionCallInvoke(&fcinfo);
-
-   if (fcinfo.isnull ||
-       (routine = (FdwRoutine *) DatumGetPointer(result)) == NULL)
-   {
-       elog(ERROR, "function %u returned NULL", flinfo.fn_oid);
-       routine = NULL; /* keep compiler quiet */
-   }
-
-   return routine;
+   return GetFdwRoutine(fdwhandler);
 }
 
 /*
index 62ab1093dd5704cea4731ea8f92f2320c9a8409a..4cf4f72de97c0c65bd44dbee92e910a238cd8a16 100644 (file)
@@ -3818,8 +3818,6 @@ DATA(insert OID = 3034 ( pg_relation_filepath PGNSP PGUID 12 1 0 0 f f f t f s 1
 DESCR("file path of relation");
 
 DATA(insert OID = 2316 ( postgresql_fdw_validator PGNSP PGUID 12 1 0 0 f f f t f i 2 0 16 "1009 26" _null_ _null_ _null_ _null_ postgresql_fdw_validator _null_ _null_ _null_));
-DATA(insert OID = 3120 ( file_fdw_validator PGNSP PGUID 12 1 0 0 f f f t f i 2 0 16 "1009 26" _null_ _null_ _null_ _null_ file_fdw_validator _null_ _null_ _null_));
-DATA(insert OID = 3121 ( file_fdw_handler PGNSP PGUID   12 1 0 0 f f f t f i 0 0 3115 "" _null_ _null_ _null_ _null_ file_fdw_handler _null_ _null_ _null_));
 
 DATA(insert OID = 2290 (  record_in            PGNSP PGUID 12 1 0 0 f f f t f s 3 0 2249 "2275 26 23" _null_ _null_ _null_ _null_  record_in _null_ _null_ _null_ ));
 DESCR("I/O");
index e15dbdec62f84837ab7c92b3eaf510cb5e3a7e6d..93c56c85a09a598cdef93f0fc7626bcc0d1670df 100644 (file)
@@ -2,7 +2,6 @@
 /copy.out
 /create_function_1.out
 /create_function_2.out
-/file_fdw.out
 /largeobject.out
 /largeobject_1.out
 /misc.out
index f3ae1d540d8ccac8095849e5381f9cf696e977ac..028afdbea318c916568b1728fae2694d5a644b9e 100644 (file)
@@ -16,15 +16,13 @@ CREATE ROLE unprivileged_role;
 CREATE SCHEMA foreign_schema;
 CREATE FOREIGN DATA WRAPPER dummy;
 CREATE FOREIGN DATA WRAPPER postgresql VALIDATOR postgresql_fdw_validator;
-CREATE FOREIGN DATA WRAPPER csv VALIDATOR file_fdw_validator HANDLER file_fdw_handler;
 -- At this point we should have 2 built-in wrappers and no servers.
 SELECT fdwname, fdwvalidator::regproc, fdwhandler::regproc, fdwoptions FROM pg_foreign_data_wrapper ORDER BY fdwname;
-  fdwname   |       fdwvalidator       |    fdwhandler    | fdwoptions 
-------------+--------------------------+------------------+------------
- csv        | file_fdw_validator       | file_fdw_handler | 
- dummy      | -                        | -                | 
- postgresql | postgresql_fdw_validator | -                | 
-(3 rows)
+  fdwname   |       fdwvalidator       | fdwhandler | fdwoptions 
+------------+--------------------------+------------+------------
+ dummy      | -                        | -          | 
+ postgresql | postgresql_fdw_validator | -          | 
+(2 rows)
 
 SELECT srvname, srvoptions FROM pg_foreign_server;
  srvname | srvoptions 
@@ -41,42 +39,39 @@ CREATE FOREIGN DATA WRAPPER foo VALIDATOR bar;            -- ERROR
 ERROR:  function bar(text[], oid) does not exist
 CREATE FOREIGN DATA WRAPPER foo;
 \dew
-                        List of foreign-data wrappers
-    Name    |       Owner       |     Handler      |        Validator         
-------------+-------------------+------------------+--------------------------
- csv        | foreign_data_user | file_fdw_handler | file_fdw_validator
- dummy      | foreign_data_user | -                | -
- foo        | foreign_data_user | -                | -
- postgresql | foreign_data_user | -                | postgresql_fdw_validator
-(4 rows)
+                    List of foreign-data wrappers
+    Name    |       Owner       | Handler |        Validator         
+------------+-------------------+---------+--------------------------
+ dummy      | foreign_data_user | -       | -
+ foo        | foreign_data_user | -       | -
+ postgresql | foreign_data_user | -       | postgresql_fdw_validator
+(3 rows)
 
 CREATE FOREIGN DATA WRAPPER foo; -- duplicate
 ERROR:  foreign-data wrapper "foo" already exists
 DROP FOREIGN DATA WRAPPER foo;
 CREATE FOREIGN DATA WRAPPER foo OPTIONS (testing '1');
 \dew+
-                                         List of foreign-data wrappers
-    Name    |       Owner       |     Handler      |        Validator         | Access privileges |   Options   
-------------+-------------------+------------------+--------------------------+-------------------+-------------
- csv        | foreign_data_user | file_fdw_handler | file_fdw_validator       |                   | 
- dummy      | foreign_data_user | -                | -                        |                   | 
- foo        | foreign_data_user | -                | -                        |                   | {testing=1}
- postgresql | foreign_data_user | -                | postgresql_fdw_validator |                   | 
-(4 rows)
+                                     List of foreign-data wrappers
+    Name    |       Owner       | Handler |        Validator         | Access privileges |   Options   
+------------+-------------------+---------+--------------------------+-------------------+-------------
+ dummy      | foreign_data_user | -       | -                        |                   | 
+ foo        | foreign_data_user | -       | -                        |                   | {testing=1}
+ postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   | 
+(3 rows)
 
 DROP FOREIGN DATA WRAPPER foo;
 CREATE FOREIGN DATA WRAPPER foo OPTIONS (testing '1', testing '2');   -- ERROR
 ERROR:  option "testing" provided more than once
 CREATE FOREIGN DATA WRAPPER foo OPTIONS (testing '1', another '2');
 \dew+
-                                              List of foreign-data wrappers
-    Name    |       Owner       |     Handler      |        Validator         | Access privileges |        Options        
-------------+-------------------+------------------+--------------------------+-------------------+-----------------------
- csv        | foreign_data_user | file_fdw_handler | file_fdw_validator       |                   | 
- dummy      | foreign_data_user | -                | -                        |                   | 
- foo        | foreign_data_user | -                | -                        |                   | {testing=1,another=2}
- postgresql | foreign_data_user | -                | postgresql_fdw_validator |                   | 
-(4 rows)
+                                          List of foreign-data wrappers
+    Name    |       Owner       | Handler |        Validator         | Access privileges |        Options        
+------------+-------------------+---------+--------------------------+-------------------+-----------------------
+ dummy      | foreign_data_user | -       | -                        |                   | 
+ foo        | foreign_data_user | -       | -                        |                   | {testing=1,another=2}
+ postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   | 
+(3 rows)
 
 DROP FOREIGN DATA WRAPPER foo;
 SET ROLE regress_test_role;
@@ -86,14 +81,13 @@ HINT:  Must be superuser to create a foreign-data wrapper.
 RESET ROLE;
 CREATE FOREIGN DATA WRAPPER foo VALIDATOR postgresql_fdw_validator;
 \dew+
-                                       List of foreign-data wrappers
-    Name    |       Owner       |     Handler      |        Validator         | Access privileges | Options 
-------------+-------------------+------------------+--------------------------+-------------------+---------
- csv        | foreign_data_user | file_fdw_handler | file_fdw_validator       |                   | 
- dummy      | foreign_data_user | -                | -                        |                   | 
- foo        | foreign_data_user | -                | postgresql_fdw_validator |                   | 
- postgresql | foreign_data_user | -                | postgresql_fdw_validator |                   | 
-(4 rows)
+                                   List of foreign-data wrappers
+    Name    |       Owner       | Handler |        Validator         | Access privileges | Options 
+------------+-------------------+---------+--------------------------+-------------------+---------
+ dummy      | foreign_data_user | -       | -                        |                   | 
+ foo        | foreign_data_user | -       | postgresql_fdw_validator |                   | 
+ postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   | 
+(3 rows)
 
 -- ALTER FOREIGN DATA WRAPPER
 ALTER FOREIGN DATA WRAPPER foo;                             -- ERROR
@@ -104,14 +98,13 @@ ALTER FOREIGN DATA WRAPPER foo VALIDATOR bar;               -- ERROR
 ERROR:  function bar(text[], oid) does not exist
 ALTER FOREIGN DATA WRAPPER foo NO VALIDATOR;
 \dew+
-                                       List of foreign-data wrappers
-    Name    |       Owner       |     Handler      |        Validator         | Access privileges | Options 
-------------+-------------------+------------------+--------------------------+-------------------+---------
- csv        | foreign_data_user | file_fdw_handler | file_fdw_validator       |                   | 
- dummy      | foreign_data_user | -                | -                        |                   | 
- foo        | foreign_data_user | -                | -                        |                   | 
- postgresql | foreign_data_user | -                | postgresql_fdw_validator |                   | 
-(4 rows)
+                                   List of foreign-data wrappers
+    Name    |       Owner       | Handler |        Validator         | Access privileges | Options 
+------------+-------------------+---------+--------------------------+-------------------+---------
+ dummy      | foreign_data_user | -       | -                        |                   | 
+ foo        | foreign_data_user | -       | -                        |                   | 
+ postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   | 
+(3 rows)
 
 ALTER FOREIGN DATA WRAPPER foo OPTIONS (a '1', b '2');
 ALTER FOREIGN DATA WRAPPER foo OPTIONS (SET c '4');         -- ERROR
@@ -120,38 +113,35 @@ ALTER FOREIGN DATA WRAPPER foo OPTIONS (DROP c);            -- ERROR
 ERROR:  option "c" not found
 ALTER FOREIGN DATA WRAPPER foo OPTIONS (ADD x '1', DROP x);
 \dew+
-                                        List of foreign-data wrappers
-    Name    |       Owner       |     Handler      |        Validator         | Access privileges |  Options  
-------------+-------------------+------------------+--------------------------+-------------------+-----------
- csv        | foreign_data_user | file_fdw_handler | file_fdw_validator       |                   | 
- dummy      | foreign_data_user | -                | -                        |                   | 
- foo        | foreign_data_user | -                | -                        |                   | {a=1,b=2}
- postgresql | foreign_data_user | -                | postgresql_fdw_validator |                   | 
-(4 rows)
+                                    List of foreign-data wrappers
+    Name    |       Owner       | Handler |        Validator         | Access privileges |  Options  
+------------+-------------------+---------+--------------------------+-------------------+-----------
+ dummy      | foreign_data_user | -       | -                        |                   | 
+ foo        | foreign_data_user | -       | -                        |                   | {a=1,b=2}
+ postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   | 
+(3 rows)
 
 ALTER FOREIGN DATA WRAPPER foo OPTIONS (DROP a, SET b '3', ADD c '4');
 \dew+
-                                        List of foreign-data wrappers
-    Name    |       Owner       |     Handler      |        Validator         | Access privileges |  Options  
-------------+-------------------+------------------+--------------------------+-------------------+-----------
- csv        | foreign_data_user | file_fdw_handler | file_fdw_validator       |                   | 
- dummy      | foreign_data_user | -                | -                        |                   | 
- foo        | foreign_data_user | -                | -                        |                   | {b=3,c=4}
- postgresql | foreign_data_user | -                | postgresql_fdw_validator |                   | 
-(4 rows)
+                                    List of foreign-data wrappers
+    Name    |       Owner       | Handler |        Validator         | Access privileges |  Options  
+------------+-------------------+---------+--------------------------+-------------------+-----------
+ dummy      | foreign_data_user | -       | -                        |                   | 
+ foo        | foreign_data_user | -       | -                        |                   | {b=3,c=4}
+ postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   | 
+(3 rows)
 
 ALTER FOREIGN DATA WRAPPER foo OPTIONS (a '2');
 ALTER FOREIGN DATA WRAPPER foo OPTIONS (b '4');             -- ERROR
 ERROR:  option "b" provided more than once
 \dew+
-                                          List of foreign-data wrappers
-    Name    |       Owner       |     Handler      |        Validator         | Access privileges |    Options    
-------------+-------------------+------------------+--------------------------+-------------------+---------------
- csv        | foreign_data_user | file_fdw_handler | file_fdw_validator       |                   | 
- dummy      | foreign_data_user | -                | -                        |                   | 
- foo        | foreign_data_user | -                | -                        |                   | {b=3,c=4,a=2}
- postgresql | foreign_data_user | -                | postgresql_fdw_validator |                   | 
-(4 rows)
+                                      List of foreign-data wrappers
+    Name    |       Owner       | Handler |        Validator         | Access privileges |    Options    
+------------+-------------------+---------+--------------------------+-------------------+---------------
+ dummy      | foreign_data_user | -       | -                        |                   | 
+ foo        | foreign_data_user | -       | -                        |                   | {b=3,c=4,a=2}
+ postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   | 
+(3 rows)
 
 SET ROLE regress_test_role;
 ALTER FOREIGN DATA WRAPPER foo OPTIONS (ADD d '5');         -- ERROR
@@ -160,14 +150,13 @@ HINT:  Must be superuser to alter a foreign-data wrapper.
 SET ROLE regress_test_role_super;
 ALTER FOREIGN DATA WRAPPER foo OPTIONS (ADD d '5');
 \dew+
-                                            List of foreign-data wrappers
-    Name    |       Owner       |     Handler      |        Validator         | Access privileges |      Options      
-------------+-------------------+------------------+--------------------------+-------------------+-------------------
- csv        | foreign_data_user | file_fdw_handler | file_fdw_validator       |                   | 
- dummy      | foreign_data_user | -                | -                        |                   | 
- foo        | foreign_data_user | -                | -                        |                   | {b=3,c=4,a=2,d=5}
- postgresql | foreign_data_user | -                | postgresql_fdw_validator |                   | 
-(4 rows)
+                                        List of foreign-data wrappers
+    Name    |       Owner       | Handler |        Validator         | Access privileges |      Options      
+------------+-------------------+---------+--------------------------+-------------------+-------------------
+ dummy      | foreign_data_user | -       | -                        |                   | 
+ foo        | foreign_data_user | -       | -                        |                   | {b=3,c=4,a=2,d=5}
+ postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   | 
+(3 rows)
 
 ALTER FOREIGN DATA WRAPPER foo OWNER TO regress_test_role;  -- ERROR
 ERROR:  permission denied to change owner of foreign-data wrapper "foo"
@@ -180,14 +169,13 @@ ERROR:  permission denied to alter foreign-data wrapper "foo"
 HINT:  Must be superuser to alter a foreign-data wrapper.
 RESET ROLE;
 \dew+
-                                               List of foreign-data wrappers
-    Name    |          Owner          |     Handler      |        Validator         | Access privileges |      Options      
-------------+-------------------------+------------------+--------------------------+-------------------+-------------------
- csv        | foreign_data_user       | file_fdw_handler | file_fdw_validator       |                   | 
- dummy      | foreign_data_user       | -                | -                        |                   | 
- foo        | regress_test_role_super | -                | -                        |                   | {b=3,c=4,a=2,d=5}
- postgresql | foreign_data_user       | -                | postgresql_fdw_validator |                   | 
-(4 rows)
+                                           List of foreign-data wrappers
+    Name    |          Owner          | Handler |        Validator         | Access privileges |      Options      
+------------+-------------------------+---------+--------------------------+-------------------+-------------------
+ dummy      | foreign_data_user       | -       | -                        |                   | 
+ foo        | regress_test_role_super | -       | -                        |                   | {b=3,c=4,a=2,d=5}
+ postgresql | foreign_data_user       | -       | postgresql_fdw_validator |                   | 
+(3 rows)
 
 -- DROP FOREIGN DATA WRAPPER
 DROP FOREIGN DATA WRAPPER nonexistent;                      -- ERROR
@@ -195,14 +183,13 @@ ERROR:  foreign-data wrapper "nonexistent" does not exist
 DROP FOREIGN DATA WRAPPER IF EXISTS nonexistent;
 NOTICE:  foreign-data wrapper "nonexistent" does not exist, skipping
 \dew+
-                                               List of foreign-data wrappers
-    Name    |          Owner          |     Handler      |        Validator         | Access privileges |      Options      
-------------+-------------------------+------------------+--------------------------+-------------------+-------------------
- csv        | foreign_data_user       | file_fdw_handler | file_fdw_validator       |                   | 
- dummy      | foreign_data_user       | -                | -                        |                   | 
- foo        | regress_test_role_super | -                | -                        |                   | {b=3,c=4,a=2,d=5}
- postgresql | foreign_data_user       | -                | postgresql_fdw_validator |                   | 
-(4 rows)
+                                           List of foreign-data wrappers
+    Name    |          Owner          | Handler |        Validator         | Access privileges |      Options      
+------------+-------------------------+---------+--------------------------+-------------------+-------------------
+ dummy      | foreign_data_user       | -       | -                        |                   | 
+ foo        | regress_test_role_super | -       | -                        |                   | {b=3,c=4,a=2,d=5}
+ postgresql | foreign_data_user       | -       | postgresql_fdw_validator |                   | 
+(3 rows)
 
 DROP ROLE regress_test_role_super;                          -- ERROR
 ERROR:  role "regress_test_role_super" cannot be dropped because some objects depend on it
@@ -216,26 +203,24 @@ ALTER ROLE regress_test_role_super SUPERUSER;
 DROP FOREIGN DATA WRAPPER foo;
 DROP ROLE regress_test_role_super;
 \dew+
-                                       List of foreign-data wrappers
-    Name    |       Owner       |     Handler      |        Validator         | Access privileges | Options 
-------------+-------------------+------------------+--------------------------+-------------------+---------
- csv        | foreign_data_user | file_fdw_handler | file_fdw_validator       |                   | 
- dummy      | foreign_data_user | -                | -                        |                   | 
- postgresql | foreign_data_user | -                | postgresql_fdw_validator |                   | 
-(3 rows)
+                                   List of foreign-data wrappers
+    Name    |       Owner       | Handler |        Validator         | Access privileges | Options 
+------------+-------------------+---------+--------------------------+-------------------+---------
+ dummy      | foreign_data_user | -       | -                        |                   | 
+ postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   | 
+(2 rows)
 
 CREATE FOREIGN DATA WRAPPER foo;
 CREATE SERVER s1 FOREIGN DATA WRAPPER foo;
 CREATE USER MAPPING FOR current_user SERVER s1;
 \dew+
-                                       List of foreign-data wrappers
-    Name    |       Owner       |     Handler      |        Validator         | Access privileges | Options 
-------------+-------------------+------------------+--------------------------+-------------------+---------
- csv        | foreign_data_user | file_fdw_handler | file_fdw_validator       |                   | 
- dummy      | foreign_data_user | -                | -                        |                   | 
- foo        | foreign_data_user | -                | -                        |                   | 
- postgresql | foreign_data_user | -                | postgresql_fdw_validator |                   | 
-(4 rows)
+                                   List of foreign-data wrappers
+    Name    |       Owner       | Handler |        Validator         | Access privileges | Options 
+------------+-------------------+---------+--------------------------+-------------------+---------
+ dummy      | foreign_data_user | -       | -                        |                   | 
+ foo        | foreign_data_user | -       | -                        |                   | 
+ postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   | 
+(3 rows)
 
 \des+
                                     List of foreign servers
@@ -266,13 +251,12 @@ NOTICE:  drop cascades to 2 other objects
 DETAIL:  drop cascades to server s1
 drop cascades to user mapping for foreign_data_user
 \dew+
-                                       List of foreign-data wrappers
-    Name    |       Owner       |     Handler      |        Validator         | Access privileges | Options 
-------------+-------------------+------------------+--------------------------+-------------------+---------
- csv        | foreign_data_user | file_fdw_handler | file_fdw_validator       |                   | 
- dummy      | foreign_data_user | -                | -                        |                   | 
- postgresql | foreign_data_user | -                | postgresql_fdw_validator |                   | 
-(3 rows)
+                                   List of foreign-data wrappers
+    Name    |       Owner       | Handler |        Validator         | Access privileges | Options 
+------------+-------------------+---------+--------------------------+-------------------+---------
+ dummy      | foreign_data_user | -       | -                        |                   | 
+ postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   | 
+(2 rows)
 
 \des+
                               List of foreign servers
@@ -367,7 +351,7 @@ CREATE SERVER t2 FOREIGN DATA WRAPPER foo;
 
 RESET ROLE;
 REVOKE regress_test_indirect FROM regress_test_role;
-CREATE SERVER sc FOREIGN DATA WRAPPER csv;
+CREATE SERVER sc FOREIGN DATA WRAPPER dummy;
 -- ALTER SERVER
 ALTER SERVER s0;                                            -- ERROR
 ERROR:  syntax error at or near ";"
@@ -394,7 +378,7 @@ GRANT USAGE ON FOREIGN SERVER s6 TO regress_test_role2 WITH GRANT OPTION;
       |                   |                      | regress_test_role2=U*/foreign_data_user |        |         | 
  s7   | foreign_data_user | foo                  |                                         | oracle | 17.0    | {host=a,dbname=b}
  s8   | foreign_data_user | postgresql           |                                         |        |         | {host=localhost,dbname=s8db}
- sc   | foreign_data_user | csv                  |                                         |        |         | 
+ sc   | foreign_data_user | dummy                |                                         |        |         | 
  t1   | regress_test_role | foo                  |                                         |        |         | 
  t2   | regress_test_role | foo                  |                                         |        |         | 
 (11 rows)
@@ -446,7 +430,7 @@ privileges for foreign-data wrapper foo
       |                       |                      | regress_test_role2=U*/foreign_data_user |        |         | 
  s7   | foreign_data_user     | foo                  |                                         | oracle | 17.0    | {host=a,dbname=b}
  s8   | foreign_data_user     | postgresql           |                                         |        |         | {dbname=db1,connect_timeout=30}
- sc   | foreign_data_user     | csv                  |                                         |        |         | 
+ sc   | foreign_data_user     | dummy                |                                         |        |         | 
  t1   | regress_test_role     | foo                  |                                         |        |         | 
  t2   | regress_test_role     | foo                  |                                         |        |         | 
 (11 rows)
@@ -468,7 +452,7 @@ NOTICE:  server "nonexistent" does not exist, skipping
  s6   | foreign_data_user     | foo
  s7   | foreign_data_user     | foo
  s8   | foreign_data_user     | postgresql
- sc   | foreign_data_user     | csv
+ sc   | foreign_data_user     | dummy
  t1   | regress_test_role     | foo
  t2   | regress_test_role     | foo
 (11 rows)
@@ -489,7 +473,7 @@ RESET ROLE;
  s6   | foreign_data_user | foo
  s7   | foreign_data_user | foo
  s8   | foreign_data_user | postgresql
- sc   | foreign_data_user | csv
+ sc   | foreign_data_user | dummy
  t1   | regress_test_role | foo
  t2   | regress_test_role | foo
 (10 rows)
@@ -508,7 +492,7 @@ RESET ROLE;
  s6   | foreign_data_user | foo
  s7   | foreign_data_user | foo
  s8   | foreign_data_user | postgresql
- sc   | foreign_data_user | csv
+ sc   | foreign_data_user | dummy
  t1   | regress_test_role | foo
  t2   | regress_test_role | foo
 (9 rows)
@@ -536,7 +520,7 @@ NOTICE:  drop cascades to user mapping for foreign_data_user
  s6   | foreign_data_user | foo
  s7   | foreign_data_user | foo
  s8   | foreign_data_user | postgresql
- sc   | foreign_data_user | csv
+ sc   | foreign_data_user | dummy
  t1   | regress_test_role | foo
  t2   | regress_test_role | foo
 (8 rows)
@@ -813,11 +797,10 @@ DROP FOREIGN TABLE foreign_schema.foreign_table_1;
 SELECT * FROM information_schema.foreign_data_wrappers ORDER BY 1, 2;
  foreign_data_wrapper_catalog | foreign_data_wrapper_name | authorization_identifier | library_name | foreign_data_wrapper_language 
 ------------------------------+---------------------------+--------------------------+--------------+-------------------------------
- regression                   | csv                       | foreign_data_user        |              | c
  regression                   | dummy                     | foreign_data_user        |              | c
  regression                   | foo                       | foreign_data_user        |              | c
  regression                   | postgresql                | foreign_data_user        |              | c
-(4 rows)
+(3 rows)
 
 SELECT * FROM information_schema.foreign_data_wrapper_options ORDER BY 1, 2, 3;
  foreign_data_wrapper_catalog | foreign_data_wrapper_name | option_name  | option_value 
@@ -832,7 +815,7 @@ SELECT * FROM information_schema.foreign_servers ORDER BY 1, 2;
  regression             | s5                  | regression                   | foo                       |                     | 15.0                   | regress_test_role
  regression             | s6                  | regression                   | foo                       |                     | 16.0                   | regress_test_indirect
  regression             | s8                  | regression                   | postgresql                |                     |                        | foreign_data_user
- regression             | sc                  | regression                   | csv                       |                     |                        | foreign_data_user
+ regression             | sc                  | regression                   | dummy                     |                     |                        | foreign_data_user
  regression             | t1                  | regression                   | foo                       |                     |                        | regress_test_indirect
  regression             | t2                  | regression                   | foo                       |                     |                        | regress_test_role
 (7 rows)
@@ -1180,12 +1163,11 @@ DETAIL:  privileges for foreign-data wrapper postgresql
 REVOKE ALL ON FOREIGN DATA WRAPPER postgresql FROM unprivileged_role;
 DROP ROLE unprivileged_role;
 DROP ROLE regress_test_role2;
-DROP FOREIGN DATA WRAPPER csv CASCADE;
+DROP FOREIGN DATA WRAPPER dummy CASCADE;
 NOTICE:  drop cascades to 2 other objects
 DETAIL:  drop cascades to server sc
 drop cascades to user mapping for public
 DROP FOREIGN DATA WRAPPER postgresql CASCADE;
-DROP FOREIGN DATA WRAPPER dummy CASCADE;
 \c
 DROP ROLE foreign_data_user;
 -- At this point we should have no wrappers, no servers, and no mappings.
diff --git a/src/test/regress/input/file_fdw.source b/src/test/regress/input/file_fdw.source
deleted file mode 100644 (file)
index c886d92..0000000
+++ /dev/null
@@ -1,79 +0,0 @@
---
--- Test foreign-data wrapper file_fdw.
---
-
--- Clean up in case a prior regression run failed
-
--- Suppress NOTICE messages when roles don't exist
-SET client_min_messages TO 'error';
-
-DROP ROLE IF EXISTS file_fdw_superuser, file_fdw_user, no_priv_user, no_mapping_user;
-
-RESET client_min_messages;
-
-CREATE ROLE file_fdw_superuser LOGIN SUPERUSER; -- is a superuser
-CREATE ROLE file_fdw_user LOGIN;                -- has priv and user mapping
-CREATE ROLE no_priv_user LOGIN;                 -- has priv but no user mapping
-CREATE ROLE no_mapping_user LOGIN;              -- has user mapping but no priv
-
--- file_fdw_superuser owns fdw-related objects 
-SET ROLE file_fdw_superuser;
-CREATE FOREIGN DATA WRAPPER file_fdw VALIDATOR file_fdw_validator HANDLER file_fdw_handler;
-CREATE SERVER file_server FOREIGN DATA WRAPPER file_fdw;
-
--- privilege tests
-SET ROLE file_fdw_user;
-CREATE FOREIGN DATA WRAPPER file_fdw2 VALIDATOR file_fdw_validator HANDLER file_fdw_handler;   -- ERROR
-CREATE SERVER file_server2 FOREIGN DATA WRAPPER file_fdw;   -- ERROR
-CREATE USER MAPPING FOR file_fdw_user SERVER file_server;   -- ERROR
-
-SET ROLE file_fdw_superuser;
-GRANT USAGE ON FOREIGN SERVER file_server TO file_fdw_user;
-
-SET ROLE file_fdw_user;
-CREATE USER MAPPING FOR file_fdw_user SERVER file_server;
-
--- create user mappings and grant privilege to test users
-SET ROLE file_fdw_superuser;
-CREATE USER MAPPING FOR file_fdw_superuser SERVER file_server;
-GRANT USAGE ON FOREIGN SERVER file_server TO no_mapping_user;
-CREATE USER MAPPING FOR no_priv_user SERVER file_server;
-
--- validator tests
-CREATE FOREIGN TABLE tbl () SERVER file_server OPTIONS (format 'xml');  -- ERROR
-CREATE FOREIGN TABLE tbl () SERVER file_server OPTIONS (format 'text', delimiter 'a');      -- ERROR
-CREATE FOREIGN TABLE tbl () SERVER file_server OPTIONS (format 'text', escape '-');         -- ERROR
-CREATE FOREIGN TABLE tbl () SERVER file_server OPTIONS (format 'csv', quote '-', null '=-=');   -- ERROR
-CREATE FOREIGN TABLE tbl () SERVER file_server OPTIONS (format 'csv', delimiter '-', null '=-=');    -- ERROR
-CREATE FOREIGN TABLE tbl () SERVER file_server OPTIONS (format 'csv', delimiter '-', quote '-');    -- ERROR
-CREATE FOREIGN TABLE agg_text (
-   a   int2,
-   b   float4
-) SERVER file_server
-OPTIONS (format 'text', filename '@abs_srcdir@/data/agg.data', delimiter ' ', null '\N');
-GRANT SELECT ON agg_text TO file_fdw_user;
-GRANT SELECT ON agg_text TO no_mapping_user;
-CREATE FOREIGN TABLE agg_csv (
-   a   int2,
-   b   float4
-) SERVER file_server WITH OIDS
-OPTIONS (format 'csv', filename '@abs_srcdir@/data/agg.csv', header 'true', delimiter ';', quote '@', escape '"', null '');
-
--- basic query tests
-SELECT * FROM agg_text WHERE b > 10.0 ORDER BY a;
-SELECT oid, * FROM agg_csv ORDER BY a;
-
--- privilege tests
-SET ROLE file_fdw_superuser;
-SELECT * FROM agg_text ORDER BY a;
-SET ROLE file_fdw_user;
-SELECT * FROM agg_text ORDER BY a;   -- ERROR
-SET ROLE no_priv_user;
-SELECT * FROM agg_text ORDER BY a;   -- ERROR
-SET ROLE no_mapping_user;
-SELECT * FROM agg_text ORDER BY a;   -- ERROR
-
--- cleanup
-RESET ROLE;
-DROP FOREIGN DATA WRAPPER file_fdw CASCADE;
-DROP ROLE IF EXISTS file_fdw_superuser, file_fdw_user, no_priv_user, no_mapping_user;
diff --git a/src/test/regress/output/file_fdw.source b/src/test/regress/output/file_fdw.source
deleted file mode 100644 (file)
index 055c42a..0000000
+++ /dev/null
@@ -1,106 +0,0 @@
---
--- Test foreign-data wrapper file_fdw.
---
--- Clean up in case a prior regression run failed
--- Suppress NOTICE messages when roles don't exist
-SET client_min_messages TO 'error';
-DROP ROLE IF EXISTS file_fdw_superuser, file_fdw_user, no_priv_user, no_mapping_user;
-RESET client_min_messages;
-CREATE ROLE file_fdw_superuser LOGIN SUPERUSER; -- is a superuser
-CREATE ROLE file_fdw_user LOGIN;                -- has priv and user mapping
-CREATE ROLE no_priv_user LOGIN;                 -- has priv but no user mapping
-CREATE ROLE no_mapping_user LOGIN;              -- has user mapping but no priv
--- file_fdw_superuser owns fdw-related objects 
-SET ROLE file_fdw_superuser;
-CREATE FOREIGN DATA WRAPPER file_fdw VALIDATOR file_fdw_validator HANDLER file_fdw_handler;
-CREATE SERVER file_server FOREIGN DATA WRAPPER file_fdw;
--- privilege tests
-SET ROLE file_fdw_user;
-CREATE FOREIGN DATA WRAPPER file_fdw2 VALIDATOR file_fdw_validator HANDLER file_fdw_handler;   -- ERROR
-ERROR:  permission denied to create foreign-data wrapper "file_fdw2"
-HINT:  Must be superuser to create a foreign-data wrapper.
-CREATE SERVER file_server2 FOREIGN DATA WRAPPER file_fdw;   -- ERROR
-ERROR:  permission denied for foreign-data wrapper file_fdw
-CREATE USER MAPPING FOR file_fdw_user SERVER file_server;   -- ERROR
-ERROR:  permission denied for foreign server file_server
-SET ROLE file_fdw_superuser;
-GRANT USAGE ON FOREIGN SERVER file_server TO file_fdw_user;
-SET ROLE file_fdw_user;
-CREATE USER MAPPING FOR file_fdw_user SERVER file_server;
--- create user mappings and grant privilege to test users
-SET ROLE file_fdw_superuser;
-CREATE USER MAPPING FOR file_fdw_superuser SERVER file_server;
-GRANT USAGE ON FOREIGN SERVER file_server TO no_mapping_user;
-CREATE USER MAPPING FOR no_priv_user SERVER file_server;
--- validator tests
-CREATE FOREIGN TABLE tbl () SERVER file_server OPTIONS (format 'xml');  -- ERROR
-ERROR:  format must be csv or text
-CREATE FOREIGN TABLE tbl () SERVER file_server OPTIONS (format 'text', delimiter 'a');      -- ERROR
-ERROR:  delimiter cannot be "a"
-CREATE FOREIGN TABLE tbl () SERVER file_server OPTIONS (format 'text', escape '-');         -- ERROR
-ERROR:  escape available only in CSV mode
-CREATE FOREIGN TABLE tbl () SERVER file_server OPTIONS (format 'csv', quote '-', null '=-=');   -- ERROR
-ERROR:  quote must not appear in the NULL specification
-CREATE FOREIGN TABLE tbl () SERVER file_server OPTIONS (format 'csv', delimiter '-', null '=-=');    -- ERROR
-ERROR:  delimiter must not appear in the NULL specification
-CREATE FOREIGN TABLE tbl () SERVER file_server OPTIONS (format 'csv', delimiter '-', quote '-');    -- ERROR
-ERROR:  delimiter and quote must be different
-CREATE FOREIGN TABLE agg_text (
-   a   int2,
-   b   float4
-) SERVER file_server
-OPTIONS (format 'text', filename '@abs_srcdir@/data/agg.data', delimiter ' ', null '\N');
-GRANT SELECT ON agg_text TO file_fdw_user;
-GRANT SELECT ON agg_text TO no_mapping_user;
-CREATE FOREIGN TABLE agg_csv (
-   a   int2,
-   b   float4
-) SERVER file_server WITH OIDS
-OPTIONS (format 'csv', filename '@abs_srcdir@/data/agg.csv', header 'true', delimiter ';', quote '@', escape '"', null '');
--- basic query tests
-SELECT * FROM agg_text WHERE b > 10.0 ORDER BY a;
-  a  |   b    
------+--------
-  42 | 324.78
- 100 | 99.097
-(2 rows)
-
-SELECT oid, * FROM agg_csv ORDER BY a;
-  oid   |  a  |    b    
---------+-----+---------
- 344909 |   0 | 0.09561
- 344910 |  42 |  324.78
- 344908 | 100 |  99.097
-(3 rows)
-
--- privilege tests
-SET ROLE file_fdw_superuser;
-SELECT * FROM agg_text ORDER BY a;
-  a  |    b    
------+---------
-   0 | 0.09561
-  42 |  324.78
-  56 |     7.8
- 100 |  99.097
-(4 rows)
-
-SET ROLE file_fdw_user;
-SELECT * FROM agg_text ORDER BY a;   -- ERROR
-ERROR:  must be superuser to read from a file
-SET ROLE no_priv_user;
-SELECT * FROM agg_text ORDER BY a;   -- ERROR
-ERROR:  permission denied for relation agg_text
-SET ROLE no_mapping_user;
-SELECT * FROM agg_text ORDER BY a;   -- ERROR
-ERROR:  user mapping not found for "no_mapping_user"
--- cleanup
-RESET ROLE;
-DROP FOREIGN DATA WRAPPER file_fdw CASCADE;
-NOTICE:  drop cascades to 6 other objects
-DETAIL:  drop cascades to server file_server
-drop cascades to user mapping for file_fdw_user
-drop cascades to user mapping for file_fdw_superuser
-drop cascades to user mapping for no_priv_user
-drop cascades to foreign table agg_text
-drop cascades to foreign table agg_csv
-DROP ROLE IF EXISTS file_fdw_superuser, file_fdw_user, no_priv_user, no_mapping_user;
index bcd1cf3d060def4d279044145e972ac972df75a9..3b99e867efefcad9052685a7ba1c4ad4fd1967dc 100644 (file)
@@ -85,8 +85,6 @@ test: rules
 # Another group of parallel tests
 # ----------
 test: select_views portals_p2 foreign_key cluster dependency guc bitmapops combocid tsearch tsdicts foreign_data window xmlmap functional_deps
-# file_fdw cannnot run concurrently with foreign_data
-test: file_fdw
 
 # ----------
 # Another group of parallel tests
index 808d1b72e887c5dfdf27857d3b58e8d39afd7dad..b348f0e1a9415955909a5aa5f2ef6d8abd65b27a 100644 (file)
@@ -105,7 +105,6 @@ test: foreign_data
 test: window
 test: xmlmap
 test: functional_deps
-test: file_fdw
 test: plancache
 test: limit
 test: plpgsql
index 2605a50539e0d67e0cab9130e067e3acfb37ffbf..46c8112094c9e761eb0c1ab91d7fe4270895f04f 100644 (file)
@@ -2,7 +2,6 @@
 /copy.sql
 /create_function_1.sql
 /create_function_2.sql
-/file_fdw.sql
 /largeobject.sql
 /misc.sql
 /security_label.sql
index 5d7396b5778ff314195f59ad521ba462aa8ad6f3..6d9385ea5b1518fa9b6b615783df6b49074668cb 100644 (file)
@@ -24,7 +24,6 @@ CREATE SCHEMA foreign_schema;
 
 CREATE FOREIGN DATA WRAPPER dummy;
 CREATE FOREIGN DATA WRAPPER postgresql VALIDATOR postgresql_fdw_validator;
-CREATE FOREIGN DATA WRAPPER csv VALIDATOR file_fdw_validator HANDLER file_fdw_handler;
 
 -- At this point we should have 2 built-in wrappers and no servers.
 SELECT fdwname, fdwvalidator::regproc, fdwhandler::regproc, fdwoptions FROM pg_foreign_data_wrapper ORDER BY fdwname;
@@ -150,7 +149,7 @@ CREATE SERVER t2 FOREIGN DATA WRAPPER foo;
 RESET ROLE;
 REVOKE regress_test_indirect FROM regress_test_role;
 
-CREATE SERVER sc FOREIGN DATA WRAPPER csv;
+CREATE SERVER sc FOREIGN DATA WRAPPER dummy;
 
 -- ALTER SERVER
 ALTER SERVER s0;                                            -- ERROR
@@ -473,9 +472,8 @@ DROP ROLE unprivileged_role;                                -- ERROR
 REVOKE ALL ON FOREIGN DATA WRAPPER postgresql FROM unprivileged_role;
 DROP ROLE unprivileged_role;
 DROP ROLE regress_test_role2;
-DROP FOREIGN DATA WRAPPER csv CASCADE;
-DROP FOREIGN DATA WRAPPER postgresql CASCADE;
 DROP FOREIGN DATA WRAPPER dummy CASCADE;
+DROP FOREIGN DATA WRAPPER postgresql CASCADE;
 \c
 DROP ROLE foreign_data_user;