Re: [PATCH 4/4] Add tests to dblink covering use of COPY TO FUNCTION - Mailing list pgsql-hackers

From Andrew Dunstan
Subject Re: [PATCH 4/4] Add tests to dblink covering use of COPY TO FUNCTION
Date
Msg-id 4B0B46E5.803@dunslane.net
Whole thread Raw
In response to Re: [PATCH 4/4] Add tests to dblink covering use of COPY TO FUNCTION  (Greg Smith <greg@2ndquadrant.com>)
Responses Re: [PATCH 4/4] Add tests to dblink covering use of COPY TO FUNCTION
List pgsql-hackers

Greg Smith wrote:
> I haven't heard anything from Andrew about ragged CVS import either.
> I think that ultimately those features are useful, but just exceed
> what the existing code could be hacked to handle cleanly.

The patch is attached for your edification/amusement. I have backpatched
it to 8.4 for the client that needed it, and it's working just fine. I
didn't pursue it when it was clear that it was not going to be accepted.
COPY returning text[] would allow us to achieve the same thing, a bit
more verbosely, but it would be a lot more work to develop.

cheers

andrew
Index: src/backend/commands/copy.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/commands/copy.c,v
retrieving revision 1.316
diff -c -r1.316 copy.c
*** src/backend/commands/copy.c    29 Jul 2009 20:56:18 -0000    1.316
--- src/backend/commands/copy.c    13 Sep 2009 02:57:16 -0000
***************
*** 116,121 ****
--- 116,122 ----
      char       *escape;            /* CSV escape char (must be 1 byte) */
      bool       *force_quote_flags;        /* per-column CSV FQ flags */
      bool       *force_notnull_flags;    /* per-column CSV FNN flags */
+     bool        ragged;         /* allow ragged CSV input? */

      /* these are just for error messages, see copy_in_error_callback */
      const char *cur_relname;    /* table name for error messages */
***************
*** 822,827 ****
--- 823,836 ----
                           errmsg("conflicting or redundant options")));
              force_notnull = (List *) defel->arg;
          }
+         else if (strcmp(defel->defname, "ragged") == 0)
+         {
+             if (cstate->ragged)
+                 ereport(ERROR,
+                         (errcode(ERRCODE_SYNTAX_ERROR),
+                          errmsg("conflicting or redundant options")));
+             cstate->ragged = intVal(defel->arg);
+         }
          else
              elog(ERROR, "option \"%s\" not recognized",
                   defel->defname);
***************
*** 948,953 ****
--- 957,972 ----
                  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                errmsg("COPY force not null only available using COPY FROM")));

+     /* Check ragged */
+     if (!cstate->csv_mode && cstate->ragged)
+         ereport(ERROR,
+                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                  errmsg("COPY ragged available only in CSV mode")));
+     if (cstate->ragged && !is_from)
+         ereport(ERROR,
+                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+               errmsg("COPY  ragged only available using COPY FROM")));
+
      /* Don't allow the delimiter to appear in the null string. */
      if (strchr(cstate->null_print, cstate->delim[0]) != NULL)
          ereport(ERROR,
***************
*** 2951,2964 ****
          int            input_len;

          /* Make sure space remains in fieldvals[] */
!         if (fieldno >= maxfields)
              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;
!         fieldvals[fieldno] = output_ptr;

          /*
           * Scan data for field,
--- 2970,2984 ----
          int            input_len;

          /* Make sure space remains in fieldvals[] */
!         if (fieldno >= maxfields && ! cstate->ragged)
              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;
!         if (fieldno < maxfields)
!             fieldvals[fieldno] = output_ptr;

          /*
           * Scan data for field,
***************
*** 3045,3051 ****
          /* Check whether raw input matched null marker */
          input_len = end_ptr - start_ptr;
          if (!saw_quote && input_len == cstate->null_print_len &&
!             strncmp(start_ptr, cstate->null_print, input_len) == 0)
              fieldvals[fieldno] = NULL;

          fieldno++;
--- 3065,3072 ----
          /* Check whether raw input matched null marker */
          input_len = end_ptr - start_ptr;
          if (!saw_quote && input_len == cstate->null_print_len &&
!             strncmp(start_ptr, cstate->null_print, input_len) == 0 &&
!             fieldno < maxfields)
              fieldvals[fieldno] = NULL;

          fieldno++;
***************
*** 3059,3065 ****
      Assert(*output_ptr == '\0');
      cstate->attribute_buf.len = (output_ptr - cstate->attribute_buf.data);

!     return fieldno;
  }


--- 3080,3092 ----
      Assert(*output_ptr == '\0');
      cstate->attribute_buf.len = (output_ptr - cstate->attribute_buf.data);

!     /* for ragged input, set field null for underflowed fields */
!     if (cstate->ragged)
!         while (fieldno  < maxfields)
!             fieldvals[fieldno++] = NULL;
!
!
!     return cstate->ragged ? maxfields  : fieldno;
  }


Index: src/backend/parser/gram.y
===================================================================
RCS file: /cvsroot/pgsql/src/backend/parser/gram.y,v
retrieving revision 2.677
diff -c -r2.677 gram.y
*** src/backend/parser/gram.y    18 Aug 2009 23:40:20 -0000    2.677
--- src/backend/parser/gram.y    13 Sep 2009 02:57:17 -0000
***************
*** 504,510 ****

      QUOTE

!     RANGE READ REAL REASSIGN RECHECK RECURSIVE REFERENCES REINDEX
      RELATIVE_P RELEASE RENAME REPEATABLE REPLACE REPLICA RESET RESTART
      RESTRICT RETURNING RETURNS REVOKE RIGHT ROLE ROLLBACK ROW ROWS RULE

--- 504,510 ----

      QUOTE

!     RAGGED RANGE READ REAL REASSIGN RECHECK RECURSIVE REFERENCES REINDEX
      RELATIVE_P RELEASE RENAME REPEATABLE REPLACE REPLICA RESET RESTART
      RESTRICT RETURNING RETURNS REVOKE RIGHT ROLE ROLLBACK ROW ROWS RULE

***************
*** 2050,2055 ****
--- 2050,2059 ----
                  {
                      $$ = makeDefElem("force_notnull", (Node *)$4);
                  }
+             | RAGGED
+                 {
+                     $$ = makeDefElem("ragged",(Node *)makeInteger(TRUE));
+                 }
          ;

  /* The following exist for backward compatibility */
***************
*** 10373,10378 ****
--- 10377,10383 ----
              | PROCEDURAL
              | PROCEDURE
              | QUOTE
+             | RAGGED
              | RANGE
              | READ
              | REASSIGN
Index: src/bin/psql/copy.c
===================================================================
RCS file: /cvsroot/pgsql/src/bin/psql/copy.c,v
retrieving revision 1.82
diff -c -r1.82 copy.c
*** src/bin/psql/copy.c    7 Aug 2009 20:16:11 -0000    1.82
--- src/bin/psql/copy.c    13 Sep 2009 02:57:17 -0000
***************
*** 34,40 ****
   * The documented syntax is:
   *    \copy tablename [(columnlist)] from|to filename
   *      [ with ] [ binary ] [ oids ] [ delimiter [as] char ] [ null [as] string ]
!  *      [ csv  [ header ] [ quote [ AS ] string ]  escape [as] string
   *        [ force not null column [, ...] | force quote column [, ...] | * ] ]
   *
   *    \copy ( select stmt ) to filename
--- 34,40 ----
   * The documented syntax is:
   *    \copy tablename [(columnlist)] from|to filename
   *      [ with ] [ binary ] [ oids ] [ delimiter [as] char ] [ null [as] string ]
!  *      [ csv  [ header ] [ quote [ AS ] string ]  escape [as] string [ ragged ]
   *        [ force not null column [, ...] | force quote column [, ...] | * ] ]
   *
   *    \copy ( select stmt ) to filename
***************
*** 69,74 ****
--- 69,75 ----
      char       *escape;
      char       *force_quote_list;
      char       *force_notnull_list;
+     bool        ragged;
  };


***************
*** 268,273 ****
--- 269,276 ----
                  result->csv_mode = true;
              else if (pg_strcasecmp(token, "header") == 0)
                  result->header = true;
+             else if (pg_strcasecmp(token, "ragged") == 0)
+                 result->ragged = true;
              else if (pg_strcasecmp(token, "delimiter") == 0)
              {
                  if (result->delim)
***************
*** 477,482 ****
--- 480,488 ----
      if (options->header)
          appendPQExpBuffer(&query, " HEADER");

+     if (options->ragged)
+         appendPQExpBuffer(&query, " RAGGED");
+
      if (options->quote)
          emit_copy_option(&query, " QUOTE AS ", options->quote);

Index: src/bin/psql/tab-complete.c
===================================================================
RCS file: /cvsroot/pgsql/src/bin/psql/tab-complete.c,v
retrieving revision 1.185
diff -c -r1.185 tab-complete.c
*** src/bin/psql/tab-complete.c    2 Aug 2009 22:14:52 -0000    1.185
--- src/bin/psql/tab-complete.c    13 Sep 2009 02:57:18 -0000
***************
*** 1249,1255 ****
                pg_strcasecmp(prev3_wd, "TO") == 0))
      {
          static const char *const list_CSV[] =
!         {"HEADER", "QUOTE", "ESCAPE", "FORCE QUOTE", NULL};

          COMPLETE_WITH_LIST(list_CSV);
      }
--- 1249,1255 ----
                pg_strcasecmp(prev3_wd, "TO") == 0))
      {
          static const char *const list_CSV[] =
!             {"HEADER", "QUOTE", "ESCAPE", "FORCE QUOTE", "RAGGED", NULL};

          COMPLETE_WITH_LIST(list_CSV);
      }
Index: src/include/parser/kwlist.h
===================================================================
RCS file: /cvsroot/pgsql/src/include/parser/kwlist.h,v
retrieving revision 1.2
diff -c -r1.2 kwlist.h
*** src/include/parser/kwlist.h    6 Apr 2009 08:42:53 -0000    1.2
--- src/include/parser/kwlist.h    13 Sep 2009 02:57:18 -0000
***************
*** 294,299 ****
--- 294,300 ----
  PG_KEYWORD("procedural", PROCEDURAL, UNRESERVED_KEYWORD)
  PG_KEYWORD("procedure", PROCEDURE, UNRESERVED_KEYWORD)
  PG_KEYWORD("quote", QUOTE, UNRESERVED_KEYWORD)
+ PG_KEYWORD("ragged", RAGGED, UNRESERVED_KEYWORD)
  PG_KEYWORD("range", RANGE, UNRESERVED_KEYWORD)
  PG_KEYWORD("read", READ, UNRESERVED_KEYWORD)
  PG_KEYWORD("real", REAL, COL_NAME_KEYWORD)

pgsql-hackers by date:

Previous
From: Caleb Cushing
Date:
Subject: Re: named generic constraints [feature request]
Next
From: Itagaki Takahiro
Date:
Subject: SE-PgSQL patch review