Change COPY CSV keyword to be:
authorBruce Momjian
Wed, 21 Apr 2004 00:34:18 +0000 (00:34 +0000)
committerBruce Momjian
Wed, 21 Apr 2004 00:34:18 +0000 (00:34 +0000)
FORCE QUOTE to force quotes
FORCE NOT NULL to quote null input values

doc/src/sgml/ref/copy.sgml
doc/src/sgml/ref/psql-ref.sgml
src/backend/commands/copy.c
src/backend/parser/gram.y
src/backend/parser/keywords.c
src/bin/psql/copy.c

index 7d53d5d3aad2820da1cf211544d7ecba1aef8383..cc86af1ad16a3fd852bb194fcf9ec1dbeaeb8fff 100644 (file)
@@ -1,5 +1,5 @@
 
 
@@ -29,7 +29,7 @@ COPY tablename [ ( 
           [ NULL [ AS ] 'null string' ]
           [ CSV [ QUOTE [ AS ] 'quote' ] 
                 [ ESCAPE [ AS ] 'escape' ]
-                [ LITERAcolumn [, ...] ]
+                [ FORCE NOT NULcolumn [, ...] ]
 
 COPY tablename [ ( column [, ...] ) ]
     TO { 'filename' | STDOUT }
@@ -40,7 +40,7 @@ COPY tablename [ ( 
           [ NULL [ AS ] 'null string' ]
           [ CSV [ QUOTE [ AS ] 'quote' ] 
                 [ ESCAPE [ AS ] 'escape' ]
-                [ FORCE column [, ...] ]
+                [ FORCE QUOTE column [, ...] ]
 
  
  
@@ -185,10 +185,10 @@ COPY tablename [ ( 
     CSV
     
      
-      Enables Comma Separated Variable (CSV) mode.  (Also called
-      Comma Separated Value).  It sets the default DELIMITER to 
-      comma, and QUOTE and ESCAPE values to 
-      double-quote.
+      Enables Comma Separated Variable (CSV) mode. (Also
+      called Comma Separated Value). It sets the default
+      DELIMITER to comma, and QUOTE and
+      ESCAPE values to double-quote.
      
     
    
@@ -207,38 +207,33 @@ COPY tablename [ ( 
     escape
     
      
-      Specifies the character that should appear before a QUOTE
-      data character value in CSV mode.  The default is the
-      QUOTE value (usually double-quote).
+      Specifies the character that should appear before a
+      QUOTE data character value in CSV mode.
+      The default is the QUOTE value (usually double-quote).
      
     
    
 
    
-    FORCE
+    FORCE QUOTE
     
      
-      In CSV COPY TO mode, forces quoting
-      to be used for all non-NULL values in each specified 
-      column.  NULL output is never quoted.
+      In CSV COPY TO mode, forces quoting to be
+      used for all non-NULL values in each specified column.
+      NULL output is never quoted.
      
     
    
 
    
-    LITERAL
+    FORCE NOT NULL
     
      
-      In CSV COPY FROM mode, for each column specified,
-      do not do a null string comparison;  instead load the value 
-      literally.  QUOTE and ESCAPE processing are still
-      performed.
-     
-     
-      If the null string is '' (the default 
-      in CSV mode), a missing input value (delimiter, 
-      delimiter), will load as a zero-length string.  Delimiter, quote, 
-      quote, delimiter is always treated as a zero-length string on input.
+      In CSV COPY FROM mode, process each
+      specified column as though it were quoted and hance not a
+      NULL value. For the default null string in
+      CSV mode (''), this causes a missing
+      values to be input as a zero-length strings.
      
     
    
@@ -483,7 +478,7 @@ COPY tablename [ ( 
     suffixed by the QUOTE character, and any occurrence
     within the value of a QUOTE character or the
     ESCAPE character is preceded by the escape character.
-    You can also use FORCE to force quotes when outputting
+    You can also use FORCE QUOTE to force quotes when outputting
     non-NULL values in specific columns.
    
 
@@ -496,7 +491,7 @@ COPY tablename [ ( 
     is quoted. Therefore, using the default settings, a NULL is
     written as an unquoted empty string, while an empty string is
     written with double quotes (""). Reading values follows 
-    similar rules. You can use LITERAL to prevent NULL
+    similar rules. You can use FORCE NOT NULL to prevent NULL
     input comparisons for specific columns.
    
 
index c41080e1bac63f907422917832ea0b55ca5e1ad0..99c0dbd95e857142cd69583c3926d0feaedba3a2 100644 (file)
@@ -1,5 +1,5 @@
 
 
@@ -713,8 +713,8 @@ testdb=>
             [ null [as]  'string' ]
             [ csv [ quote [as]  'character' ]
                            [ escape [as]  'character' ]
-                           [ force column_list ]
-                           [ literacolumn_list ] ]
+                           [ force quote column_list ]
+                           [ force not nulcolumn_list ] ]
         
 
         
index b532c158bcfd20eed7783a068e78274de5c13cbe..74025ad041bea088ce9da915e41d394a68d9b302 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/commands/copy.c,v 1.222 2004/04/19 21:58:02 momjian Exp $
+ *   $PostgreSQL: pgsql/src/backend/commands/copy.c,v 1.223 2004/04/21 00:34:18 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -132,10 +132,10 @@ static bool line_buf_converted;
 /* non-export function prototypes */
 static void CopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
       char *delim, char *null_print, bool csv_mode, char *quote, char *escape,
-      List *force_atts);
+      List *force_quote_atts);
 static void CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
         char *delim, char *null_print, bool csv_mode, char *quote, char *escape,
-        List *literal_atts);
+        List *force_notnull_atts);
 static bool CopyReadLine(void);
 static char *CopyReadAttribute(const char *delim, const char *null_print,
                               CopyReadResult *result, bool *isnull);
@@ -695,10 +695,10 @@ DoCopy(const CopyStmt *stmt)
    char       *quote = NULL;
    char       *escape = NULL;
    char       *null_print = NULL;
-   List       *force = NIL;
-   List       *literal = NIL;
-   List       *force_atts = NIL;
-   List       *literal_atts = NIL;
+   List       *force_quote = NIL;
+   List       *force_notnull = NIL;
+   List       *force_quote_atts = NIL;
+   List       *force_notnull_atts = NIL;
    Relation    rel;
    AclMode     required_access = (is_from ? ACL_INSERT : ACL_SELECT);
    AclResult   aclresult;
@@ -764,21 +764,21 @@ DoCopy(const CopyStmt *stmt)
                         errmsg("conflicting or redundant options")));
            escape = strVal(defel->arg);
        }
-       else if (strcmp(defel->defname, "force") == 0)
+       else if (strcmp(defel->defname, "force_quote") == 0)
        {
-           if (force)
+           if (force_quote)
                ereport(ERROR,
                        (errcode(ERRCODE_SYNTAX_ERROR),
                         errmsg("conflicting or redundant options")));
-           force = (List *)defel->arg;
+           force_quote = (List *)defel->arg;
        }
-       else if (strcmp(defel->defname, "literal") == 0)
+       else if (strcmp(defel->defname, "force_notnull") == 0)
        {
-           if (literal)
+           if (force_notnull)
                ereport(ERROR,
                        (errcode(ERRCODE_SYNTAX_ERROR),
                         errmsg("conflicting or redundant options")));
-           literal = (List *)defel->arg;
+           force_notnull = (List *)defel->arg;
        }
        else
            elog(ERROR, "option \"%s\" not recognized",
@@ -850,28 +850,28 @@ DoCopy(const CopyStmt *stmt)
                 errmsg("COPY escape must be a single character")));
 
    /*
-    * Check force
+    * Check force_quote
     */
-   if (!csv_mode && force != NIL)
+   if (!csv_mode && force_quote != NIL)
        ereport(ERROR,
                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                errmsg("COPY force available only in CSV mode")));
-   if (force != NIL && is_from)
+                errmsg("COPY force quote available only in CSV mode")));
+   if (force_quote != NIL && is_from)
        ereport(ERROR,
                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                errmsg("COPY force only available using COPY TO")));
+                errmsg("COPY force quote only available using COPY TO")));
 
    /*
-    * Check literal
+    * Check force_notnull
     */
-   if (!csv_mode && literal != NIL)
+   if (!csv_mode && force_notnull != NIL)
        ereport(ERROR,
                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                errmsg("COPY literal available only in CSV mode")));
-   if (literal != NIL && !is_from)
+                errmsg("COPY force not null available only in CSV mode")));
+   if (force_notnull != NIL && !is_from)
        ereport(ERROR,
                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                errmsg("COPY literal only available using COPY FROM")));
+                errmsg("COPY force not null only available using COPY FROM")));
 
    /*
     * Don't allow the delimiter to appear in the null string.
@@ -928,47 +928,47 @@ DoCopy(const CopyStmt *stmt)
    attnumlist = CopyGetAttnums(rel, attnamelist);
 
    /*
-    * Check that FORCE references valid COPY columns
+    * Check that FORCE QUOTE references valid COPY columns
     */
-   if (force)
+   if (force_quote)
    {
        TupleDesc   tupDesc = RelationGetDescr(rel);
        Form_pg_attribute *attr = tupDesc->attrs;
        List       *cur;
 
-       force_atts = CopyGetAttnums(rel, force);
+       force_quote_atts = CopyGetAttnums(rel, force_quote);
 
-       foreach(cur, force_atts)
+       foreach(cur, force_quote_atts)
        {
            int         attnum = lfirsti(cur);
 
            if (!intMember(attnum, attnumlist))
                ereport(ERROR,
                        (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
-                        errmsg("FORCE column \"%s\" not referenced by COPY",
+                        errmsg("FORCE QUOTE column \"%s\" not referenced by COPY",
                                NameStr(attr[attnum - 1]->attname))));
        }
    }
    
    /*
-    * Check that LITERAL references valid COPY columns
+    * Check that FORCE NOT NULL references valid COPY columns
     */
-   if (literal)
+   if (force_notnull)
    {
        List       *cur;
        TupleDesc   tupDesc = RelationGetDescr(rel);
        Form_pg_attribute *attr = tupDesc->attrs;
 
-       literal_atts = CopyGetAttnums(rel, literal);
+       force_notnull_atts = CopyGetAttnums(rel, force_notnull);
 
-       foreach(cur, literal_atts)
+       foreach(cur, force_notnull_atts)
        {
            int         attnum = lfirsti(cur);
 
            if (!intMember(attnum, attnumlist))
                ereport(ERROR,
                        (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
-                        errmsg("LITERAL column \"%s\" not referenced by COPY",
+                        errmsg("FORCE NOT NULL column \"%s\" not referenced by COPY",
                                NameStr(attr[attnum - 1]->attname))));
        }
    }
@@ -1037,7 +1037,7 @@ DoCopy(const CopyStmt *stmt)
            }
        }
        CopyFrom(rel, attnumlist, binary, oids, delim, null_print, csv_mode,
-                quote, escape, literal_atts);
+                quote, escape, force_notnull_atts);
    }
    else
    {                           /* copy from database to file */
@@ -1100,7 +1100,7 @@ DoCopy(const CopyStmt *stmt)
            }
        }
        CopyTo(rel, attnumlist, binary, oids, delim, null_print, csv_mode,
-               quote, escape, force_atts);
+               quote, escape, force_quote_atts);
    }
 
    if (!pipe)
@@ -1133,7 +1133,7 @@ DoCopy(const CopyStmt *stmt)
 static void
 CopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
       char *delim, char *null_print, bool csv_mode, char *quote,
-      char *escape, List *force_atts)
+      char *escape, List *force_quote_atts)
 {
    HeapTuple   tuple;
    TupleDesc   tupDesc;
@@ -1180,7 +1180,7 @@ CopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
                              &isvarlena[attnum - 1]);
        fmgr_info(out_func_oid, &out_functions[attnum - 1]);
 
-       if (intMember(attnum, force_atts))
+       if (intMember(attnum, force_quote_atts))
            force_quote[attnum - 1] = true;
        else
            force_quote[attnum - 1] = false;
@@ -1434,7 +1434,7 @@ limit_printout_length(StringInfo buf)
 static void
 CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
         char *delim, char *null_print, bool csv_mode, char *quote,
-        char *escape, List *literal_atts)
+        char *escape, List *force_notnull_atts)
 {
    HeapTuple   tuple;
    TupleDesc   tupDesc;
@@ -1447,7 +1447,7 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
    Oid        *elements;
    Oid         oid_in_element;
    ExprState **constraintexprs;
-   bool       *literal_nullstr;
+   bool       *force_notnull;
    bool        hasConstraints = false;
    int         attnum;
    int         i;
@@ -1509,7 +1509,7 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
    defmap = (int *) palloc((num_phys_attrs + 1) * sizeof(int));
    defexprs = (ExprState **) palloc((num_phys_attrs + 1) * sizeof(ExprState *));
    constraintexprs = (ExprState **) palloc0((num_phys_attrs + 1) * sizeof(ExprState *));
-   literal_nullstr = (bool *) palloc((num_phys_attrs + 1) * sizeof(bool));
+   force_notnull = (bool *) palloc((num_phys_attrs + 1) * sizeof(bool));
 
    for (attnum = 1; attnum <= num_phys_attrs; attnum++)
    {
@@ -1526,10 +1526,10 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
                             &in_func_oid, &elements[attnum - 1]);
        fmgr_info(in_func_oid, &in_functions[attnum - 1]);
 
-       if (intMember(attnum, literal_atts))
-           literal_nullstr[attnum - 1] = true;
+       if (intMember(attnum, force_notnull_atts))
+           force_notnull[attnum - 1] = true;
        else
-           literal_nullstr[attnum - 1] = false;
+           force_notnull[attnum - 1] = false;
        
        /* Get default info if needed */
        if (!intMember(attnum, attnumlist))
@@ -1748,7 +1748,7 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
                    string = CopyReadAttribute(delim, null_print, 
                                               &result, &isnull);
 
-               if (csv_mode && isnull && literal_nullstr[m])
+               if (csv_mode && isnull && force_notnull[m])
                {
                    string = null_print;    /* set to NULL string */
                    isnull = false;
@@ -1947,7 +1947,7 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
    pfree(defmap);
    pfree(defexprs);
    pfree(constraintexprs);
-   pfree(literal_nullstr);
+   pfree(force_notnull);
 
    ExecDropTupleTable(tupleTable, true);
 
@@ -2558,14 +2558,13 @@ CopyAttributeOut(char *server_string, char *delim)
  */
 static void
 CopyAttributeOutCSV(char *server_string, char *delim, char *quote,
-                   char *escape, bool force_quote)
+                   char *escape, bool use_quote)
 {
    char       *string;
    char        c;
    char        delimc = delim[0];
    char        quotec = quote[0];
    char        escapec = escape[0];
-   bool        need_quote = force_quote;
    char        *test_string;
    bool        same_encoding;
    int         mblen;
@@ -2583,23 +2582,23 @@ CopyAttributeOutCSV(char *server_string, char *delim, char *quote,
     */
 
    for(test_string = string; 
-       !need_quote && (c = *test_string) != '\0'; 
+       !use_quote && (c = *test_string) != '\0'; 
        test_string += mblen)
    {
        if (c == delimc || c == quotec || c == '\n' || c == '\r')
-           need_quote = true;
+           use_quote = true;
        if (!same_encoding)
            mblen = pg_encoding_mblen(client_encoding, test_string);
        else
            mblen = 1;
    }
 
-   if (need_quote)
+   if (use_quote)
        CopySendChar(quotec);
 
    for (; (c = *string) != '\0'; string += mblen)
    {
-       if (need_quote && (c == quotec || c == escapec))
+       if (use_quote && (c == quotec || c == escapec))
            CopySendChar(escapec);
 
        CopySendChar(c);
@@ -2615,7 +2614,7 @@ CopyAttributeOutCSV(char *server_string, char *delim, char *quote,
            mblen = 1;
    }
 
-   if (need_quote)
+   if (use_quote)
        CopySendChar(quotec);
 }
 
index 461a39dbb706c183d3c6b1feaf0286a15bf284db..de8597ed9efaa9068359c0ca8d033bae0cf80e55 100644 (file)
@@ -11,7 +11,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.451 2004/04/19 17:22:30 momjian Exp $
+ *   $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.452 2004/04/21 00:34:18 momjian Exp $
  *
  * HISTORY
  *   AUTHOR            DATE            MAJOR EVENT
@@ -370,7 +370,7 @@ static void doNegateFloat(Value *v);
    KEY
 
    LANCOMPILER LANGUAGE LARGE_P LAST_P LEADING LEFT LEVEL LIKE LIMIT
-   LISTEN LITERAL LOAD LOCAL LOCALTIME LOCALTIMESTAMP LOCATION
+   LISTEN LOAD LOCAL LOCALTIME LOCALTIMESTAMP LOCATION
    LOCK_P
 
    MATCH MAXVALUE MINUTE_P MINVALUE MODE MONTH_P MOVE
@@ -1374,13 +1374,13 @@ copy_opt_item:
                {
                    $$ = makeDefElem("escape", (Node *)makeString($3));
                }
-           | FORCE columnList
+           | FORCE QUOTE columnList
                {
-                   $$ = makeDefElem("force", (Node *)$2);
+                   $$ = makeDefElem("force_quote", (Node *)$3);
                }
-           | LITERAL columnList
+           | FORCE NOT NULL_P columnList
                {
-                   $$ = makeDefElem("literal", (Node *)$2);
+                   $$ = makeDefElem("force_notnull", (Node *)$4);
                }
        ;
 
@@ -7496,7 +7496,6 @@ unreserved_keyword:
            | LAST_P
            | LEVEL
            | LISTEN
-           | LITERAL
            | LOAD
            | LOCAL
            | LOCATION
index 613d22ac30c421a256b6064093c72abca190c2fc..a3c765a1952857128ff19cb034d9ada14cf69d16 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/parser/keywords.c,v 1.148 2004/04/19 17:22:31 momjian Exp $
+ *   $PostgreSQL: pgsql/src/backend/parser/keywords.c,v 1.149 2004/04/21 00:34:18 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -187,7 +187,6 @@ static const ScanKeyword ScanKeywords[] = {
    {"like", LIKE},
    {"limit", LIMIT},
    {"listen", LISTEN},
-   {"literal", LITERAL},
    {"load", LOAD},
    {"local", LOCAL},
    {"localtime", LOCALTIME},
index 0241b8eda27f708f0ea87e6320f785170b27202e..da91fe8c714a7960d7923e2c76c73a554e3c83ae 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 2000-2003, PostgreSQL Global Development Group
  *
- * $PostgreSQL: pgsql/src/bin/psql/copy.c,v 1.45 2004/04/19 17:42:58 momjian Exp $
+ * $PostgreSQL: pgsql/src/bin/psql/copy.c,v 1.46 2004/04/21 00:34:18 momjian Exp $
  */
 #include "postgres_fe.h"
 #include "copy.h"
@@ -71,8 +71,8 @@ struct copy_options
    char       *null;
    char       *quote;
    char       *escape;
-   char       *force_list;
-   char       *literal_list;
+   char       *force_quote_list;
+   char       *force_notnull_list;
 };
 
 
@@ -88,8 +88,8 @@ free_copy_options(struct copy_options * ptr)
    free(ptr->null);
    free(ptr->quote);
    free(ptr->escape);
-   free(ptr->force_list);
-   free(ptr->literal_list);
+   free(ptr->force_quote_list);
+   free(ptr->force_notnull_list);
    free(ptr);
 }
 
@@ -344,45 +344,56 @@ parse_slash_copy(const char *args)
            }
            else if (strcasecmp(token, "force") == 0)
            {
-               /* handle column list */
-               fetch_next = false;
-               for (;;)
+               token = strtokx(NULL, whitespace, ",", "\"",
+                               0, false, pset.encoding);
+               if (strcasecmp(token, "quote") == 0)
                {
-                   token = strtokx(NULL, whitespace, ",", "\"",
-                                   0, false, pset.encoding);
-                   if (!token || strchr(",", token[0]))
-                       goto error;
-                   if (!result->force_list)
-                       result->force_list = pg_strdup(token);
-                   else
-                       xstrcat(&result->force_list, token);
-                   token = strtokx(NULL, whitespace, ",", "\"",
-                                   0, false, pset.encoding);
-                   if (!token || token[0] != ',')
-                       break;
-                   xstrcat(&result->force_list, token);
+                   /* handle column list */
+                   fetch_next = false;
+                   for (;;)
+                   {
+                       token = strtokx(NULL, whitespace, ",", "\"",
+                                       0, false, pset.encoding);
+                       if (!token || strchr(",", token[0]))
+                           goto error;
+                       if (!result->force_quote_list)
+                           result->force_quote_list = pg_strdup(token);
+                       else
+                           xstrcat(&result->force_quote_list, token);
+                       token = strtokx(NULL, whitespace, ",", "\"",
+                                       0, false, pset.encoding);
+                       if (!token || token[0] != ',')
+                           break;
+                       xstrcat(&result->force_quote_list, token);
+                   }
                }
-           }
-           else if (strcasecmp(token, "literal") == 0)
-           {
-               /* handle column list */
-               fetch_next = false;
-               for (;;)
+               else if (strcasecmp(token, "not") == 0)
                {
                    token = strtokx(NULL, whitespace, ",", "\"",
                                    0, false, pset.encoding);
-                   if (!token || strchr(",", token[0]))
+                   if (strcasecmp(token, "null") != 0)
                        goto error;
-                   if (!result->literal_list)
-                       result->literal_list = pg_strdup(token);
-                   else
-                       xstrcat(&result->literal_list, token);
-                   token = strtokx(NULL, whitespace, ",", "\"",
-                                   0, false, pset.encoding);
-                   if (!token || token[0] != ',')
-                       break;
-                   xstrcat(&result->literal_list, token);
+                   /* handle column list */
+                   fetch_next = false;
+                   for (;;)
+                   {
+                       token = strtokx(NULL, whitespace, ",", "\"",
+                                       0, false, pset.encoding);
+                       if (!token || strchr(",", token[0]))
+                           goto error;
+                       if (!result->force_notnull_list)
+                           result->force_notnull_list = pg_strdup(token);
+                       else
+                           xstrcat(&result->force_notnull_list, token);
+                       token = strtokx(NULL, whitespace, ",", "\"",
+                                       0, false, pset.encoding);
+                       if (!token || token[0] != ',')
+                           break;
+                       xstrcat(&result->force_notnull_list, token);
+                   }
                }
+               else
+                   goto error;
            }
            else
                goto error;
@@ -493,14 +504,14 @@ do_copy(const char *args)
            appendPQExpBuffer(&query, " ESCAPE AS '%s'", options->escape);
    }
 
-   if (options->force_list)
+   if (options->force_quote_list)
    {
-       appendPQExpBuffer(&query, " FORCE %s", options->force_list);
+       appendPQExpBuffer(&query, " FORCE QUOTE %s", options->force_quote_list);
    }
 
-   if (options->literal_list)
+   if (options->force_notnull_list)
    {
-       appendPQExpBuffer(&query, " LITERAL %s", options->literal_list);
+       appendPQExpBuffer(&query, " FORCE NOT NULL %s", options->force_notnull_list);
    }
 
    if (options->from)