33 *
44 * Copyright (c) 2000-2006, PostgreSQL Global Development Group
55 *
6- * $PostgreSQL: pgsql/src/bin/psql/copy.c,v 1.63 2006/06/0100:15:36 tgl Exp $
6+ * $PostgreSQL: pgsql/src/bin/psql/copy.c,v 1.64 2006/06/0101:28:00 tgl Exp $
77 */
88#include "postgres_fe.h"
99#include "copy.h"
@@ -127,23 +127,23 @@ parse_slash_copy(const char *args)
127127result = pg_calloc (1 ,sizeof (struct copy_options ));
128128
129129token = strtokx (line ,whitespace ,".,()" ,"\"" ,
130- 0 , false,pset .encoding );
130+ 0 , false,false, pset .encoding );
131131if (!token )
132132gotoerror ;
133133
134134if (pg_strcasecmp (token ,"binary" )== 0 )
135135{
136136result -> binary = true;
137137token = strtokx (NULL ,whitespace ,".,()" ,"\"" ,
138- 0 , false,pset .encoding );
138+ 0 , false,false, pset .encoding );
139139if (!token )
140140gotoerror ;
141141}
142142
143143result -> table = pg_strdup (token );
144144
145145token = strtokx (NULL ,whitespace ,".,()" ,"\"" ,
146- 0 , false,pset .encoding );
146+ 0 , false,false, pset .encoding );
147147if (!token )
148148gotoerror ;
149149
@@ -156,12 +156,12 @@ parse_slash_copy(const char *args)
156156/* handle schema . table */
157157xstrcat (& result -> table ,token );
158158token = strtokx (NULL ,whitespace ,".,()" ,"\"" ,
159- 0 , false,pset .encoding );
159+ 0 , false,false, pset .encoding );
160160if (!token )
161161gotoerror ;
162162xstrcat (& result -> table ,token );
163163token = strtokx (NULL ,whitespace ,".,()" ,"\"" ,
164- 0 , false,pset .encoding );
164+ 0 , false,false, pset .encoding );
165165if (!token )
166166gotoerror ;
167167}
@@ -173,12 +173,12 @@ parse_slash_copy(const char *args)
173173for (;;)
174174{
175175token = strtokx (NULL ,whitespace ,".,()" ,"\"" ,
176- 0 , false,pset .encoding );
176+ 0 , false,false, pset .encoding );
177177if (!token || strchr (".,()" ,token [0 ]))
178178gotoerror ;
179179xstrcat (& result -> column_list ,token );
180180token = strtokx (NULL ,whitespace ,".,()" ,"\"" ,
181- 0 , false,pset .encoding );
181+ 0 , false,false, pset .encoding );
182182if (!token )
183183gotoerror ;
184184xstrcat (& result -> column_list ,token );
@@ -188,7 +188,7 @@ parse_slash_copy(const char *args)
188188gotoerror ;
189189}
190190token = strtokx (NULL ,whitespace ,".,()" ,"\"" ,
191- 0 , false,pset .encoding );
191+ 0 , false,false, pset .encoding );
192192if (!token )
193193gotoerror ;
194194}
@@ -199,13 +199,13 @@ parse_slash_copy(const char *args)
199199if (pg_strcasecmp (token ,"with" )== 0 )
200200{
201201token = strtokx (NULL ,whitespace ,NULL ,NULL ,
202- 0 , false,pset .encoding );
202+ 0 , false,false, pset .encoding );
203203if (!token || pg_strcasecmp (token ,"oids" )!= 0 )
204204gotoerror ;
205205result -> oids = true;
206206
207207token = strtokx (NULL ,whitespace ,NULL ,NULL ,
208- 0 , false,pset .encoding );
208+ 0 , false,false, pset .encoding );
209209if (!token )
210210gotoerror ;
211211}
@@ -218,7 +218,7 @@ parse_slash_copy(const char *args)
218218gotoerror ;
219219
220220token = strtokx (NULL ,whitespace ,NULL ,"'" ,
221- nonstd_backslash , true,pset .encoding );
221+ 0 , false , true,pset .encoding );
222222if (!token )
223223gotoerror ;
224224
@@ -242,27 +242,27 @@ parse_slash_copy(const char *args)
242242}
243243
244244token = strtokx (NULL ,whitespace ,NULL ,NULL ,
245- 0 , false,pset .encoding );
245+ 0 , false,false, pset .encoding );
246246
247247/*
248248 * Allows old COPY syntax for backward compatibility 2002-06-19
249249 */
250250if (token && pg_strcasecmp (token ,"using" )== 0 )
251251{
252252token = strtokx (NULL ,whitespace ,NULL ,NULL ,
253- 0 , false,pset .encoding );
253+ 0 , false,false, pset .encoding );
254254if (!(token && pg_strcasecmp (token ,"delimiters" )== 0 ))
255255gotoerror ;
256256}
257257if (token && pg_strcasecmp (token ,"delimiters" )== 0 )
258258{
259259token = strtokx (NULL ,whitespace ,NULL ,"'" ,
260- nonstd_backslash , false,pset .encoding );
260+ nonstd_backslash ,true, false,pset .encoding );
261261if (!token )
262262gotoerror ;
263263result -> delim = pg_strdup (token );
264264token = strtokx (NULL ,whitespace ,NULL ,NULL ,
265- 0 , false,pset .encoding );
265+ 0 , false,false, pset .encoding );
266266}
267267
268268if (token )
@@ -273,7 +273,7 @@ parse_slash_copy(const char *args)
273273 */
274274if (pg_strcasecmp (token ,"with" )== 0 )
275275token = strtokx (NULL ,whitespace ,NULL ,NULL ,
276- 0 , false,pset .encoding );
276+ 0 , false,false, pset .encoding );
277277
278278while (token )
279279{
@@ -292,10 +292,10 @@ parse_slash_copy(const char *args)
292292else if (pg_strcasecmp (token ,"delimiter" )== 0 )
293293{
294294token = strtokx (NULL ,whitespace ,NULL ,"'" ,
295- nonstd_backslash , false,pset .encoding );
295+ nonstd_backslash ,true, false,pset .encoding );
296296if (token && pg_strcasecmp (token ,"as" )== 0 )
297297token = strtokx (NULL ,whitespace ,NULL ,"'" ,
298- nonstd_backslash , false,pset .encoding );
298+ nonstd_backslash ,true, false,pset .encoding );
299299if (token )
300300result -> delim = pg_strdup (token );
301301else
@@ -304,10 +304,10 @@ parse_slash_copy(const char *args)
304304else if (pg_strcasecmp (token ,"null" )== 0 )
305305{
306306token = strtokx (NULL ,whitespace ,NULL ,"'" ,
307- nonstd_backslash , false,pset .encoding );
307+ nonstd_backslash ,true, false,pset .encoding );
308308if (token && pg_strcasecmp (token ,"as" )== 0 )
309309token = strtokx (NULL ,whitespace ,NULL ,"'" ,
310- nonstd_backslash , false,pset .encoding );
310+ nonstd_backslash ,true, false,pset .encoding );
311311if (token )
312312result -> null = pg_strdup (token );
313313else
@@ -316,10 +316,10 @@ parse_slash_copy(const char *args)
316316else if (pg_strcasecmp (token ,"quote" )== 0 )
317317{
318318token = strtokx (NULL ,whitespace ,NULL ,"'" ,
319- nonstd_backslash , false,pset .encoding );
319+ nonstd_backslash ,true, false,pset .encoding );
320320if (token && pg_strcasecmp (token ,"as" )== 0 )
321321token = strtokx (NULL ,whitespace ,NULL ,"'" ,
322- nonstd_backslash , false,pset .encoding );
322+ nonstd_backslash ,true, false,pset .encoding );
323323if (token )
324324result -> quote = pg_strdup (token );
325325else
@@ -328,10 +328,10 @@ parse_slash_copy(const char *args)
328328else if (pg_strcasecmp (token ,"escape" )== 0 )
329329{
330330token = strtokx (NULL ,whitespace ,NULL ,"'" ,
331- nonstd_backslash , false,pset .encoding );
331+ nonstd_backslash ,true, false,pset .encoding );
332332if (token && pg_strcasecmp (token ,"as" )== 0 )
333333token = strtokx (NULL ,whitespace ,NULL ,"'" ,
334- nonstd_backslash , false,pset .encoding );
334+ nonstd_backslash ,true, false,pset .encoding );
335335if (token )
336336result -> escape = pg_strdup (token );
337337else
@@ -340,23 +340,23 @@ parse_slash_copy(const char *args)
340340else if (pg_strcasecmp (token ,"force" )== 0 )
341341{
342342token = strtokx (NULL ,whitespace ,"," ,"\"" ,
343- 0 , false,pset .encoding );
343+ 0 , false,false, pset .encoding );
344344if (pg_strcasecmp (token ,"quote" )== 0 )
345345{
346346/* handle column list */
347347fetch_next = false;
348348for (;;)
349349{
350350token = strtokx (NULL ,whitespace ,"," ,"\"" ,
351- 0 , false,pset .encoding );
351+ 0 , false,false, pset .encoding );
352352if (!token || strchr ("," ,token [0 ]))
353353gotoerror ;
354354if (!result -> force_quote_list )
355355result -> force_quote_list = pg_strdup (token );
356356else
357357xstrcat (& result -> force_quote_list ,token );
358358token = strtokx (NULL ,whitespace ,"," ,"\"" ,
359- 0 , false,pset .encoding );
359+ 0 , false,false, pset .encoding );
360360if (!token || token [0 ]!= ',' )
361361break ;
362362xstrcat (& result -> force_quote_list ,token );
@@ -365,23 +365,23 @@ parse_slash_copy(const char *args)
365365else if (pg_strcasecmp (token ,"not" )== 0 )
366366{
367367token = strtokx (NULL ,whitespace ,"," ,"\"" ,
368- 0 , false,pset .encoding );
368+ 0 , false,false, pset .encoding );
369369if (pg_strcasecmp (token ,"null" )!= 0 )
370370gotoerror ;
371371/* handle column list */
372372fetch_next = false;
373373for (;;)
374374{
375375token = strtokx (NULL ,whitespace ,"," ,"\"" ,
376- 0 , false,pset .encoding );
376+ 0 , false,false, pset .encoding );
377377if (!token || strchr ("," ,token [0 ]))
378378gotoerror ;
379379if (!result -> force_notnull_list )
380380result -> force_notnull_list = pg_strdup (token );
381381else
382382xstrcat (& result -> force_notnull_list ,token );
383383token = strtokx (NULL ,whitespace ,"," ,"\"" ,
384- 0 , false,pset .encoding );
384+ 0 , false,false, pset .encoding );
385385if (!token || token [0 ]!= ',' )
386386break ;
387387xstrcat (& result -> force_notnull_list ,token );
@@ -395,7 +395,7 @@ parse_slash_copy(const char *args)
395395
396396if (fetch_next )
397397token = strtokx (NULL ,whitespace ,NULL ,NULL ,
398- 0 , false,pset .encoding );
398+ 0 , false,false, pset .encoding );
399399}
400400}
401401
@@ -415,6 +415,22 @@ parse_slash_copy(const char *args)
415415}
416416
417417
418+ /*
419+ * Handle one of the "string" options of COPY. If the user gave a quoted
420+ * string, pass it to the backend as-is; if it wasn't quoted then quote
421+ * and escape it.
422+ */
423+ static void
424+ emit_copy_option (PQExpBuffer query ,const char * keyword ,const char * option )
425+ {
426+ appendPQExpBufferStr (query ,keyword );
427+ if (option [0 ]== '\'' ||
428+ ((option [0 ]== 'E' || option [0 ]== 'e' )&& option [1 ]== '\'' ))
429+ appendPQExpBufferStr (query ,option );
430+ else
431+ appendStringLiteralConn (query ,option ,pset .db );
432+ }
433+
418434
419435/*
420436 * Execute a \copy command (frontend copy). We have to open a file, then
@@ -462,29 +478,11 @@ do_copy(const char *args)
462478
463479/* Uses old COPY syntax for backward compatibility 2002-06-19 */
464480if (options -> delim )
465- {
466- /* if user gave a quoted string, use it as-is */
467- if (options -> delim [0 ]== '\'' )
468- appendPQExpBuffer (& query ," USING DELIMITERS %s" ,options -> delim );
469- else
470- {
471- appendPQExpBuffer (& query ," USING DELIMITERS " );
472- appendStringLiteralConn (& query ,options -> delim ,pset .db );
473- }
474- }
481+ emit_copy_option (& query ," USING DELIMITERS " ,options -> delim );
475482
476483/* There is no backward-compatible CSV syntax */
477484if (options -> null )
478- {
479- /* if user gave a quoted string, use it as-is */
480- if (options -> null [0 ]== '\'' )
481- appendPQExpBuffer (& query ," WITH NULL AS %s" ,options -> null );
482- else
483- {
484- appendPQExpBuffer (& query ," WITH NULL AS " );
485- appendStringLiteralConn (& query ,options -> null ,pset .db );
486- }
487- }
485+ emit_copy_option (& query ," WITH NULL AS " ,options -> null );
488486
489487if (options -> csv_mode )
490488appendPQExpBuffer (& query ," CSV" );
@@ -493,28 +491,10 @@ do_copy(const char *args)
493491appendPQExpBuffer (& query ," HEADER" );
494492
495493if (options -> quote )
496- {
497- /* if user gave a quoted string, use it as-is */
498- if (options -> quote [0 ]== '\'' )
499- appendPQExpBuffer (& query ," QUOTE AS %s" ,options -> quote );
500- else
501- {
502- appendPQExpBuffer (& query ," QUOTE AS " );
503- appendStringLiteralConn (& query ,options -> quote ,pset .db );
504- }
505- }
494+ emit_copy_option (& query ," QUOTE AS " ,options -> quote );
506495
507496if (options -> escape )
508- {
509- /* if user gave a quoted string, use it as-is */
510- if (options -> escape [0 ]== '\'' )
511- appendPQExpBuffer (& query ," ESCAPE AS %s" ,options -> escape );
512- else
513- {
514- appendPQExpBuffer (& query ," ESCAPE AS " );
515- appendStringLiteralConn (& query ,options -> escape ,pset .db );
516- }
517- }
497+ emit_copy_option (& query ," ESCAPE AS " ,options -> escape );
518498
519499if (options -> force_quote_list )
520500appendPQExpBuffer (& query ," FORCE QUOTE %s" ,options -> force_quote_list );