diff --git a/src/backend/parser/scan.l b/src/backend/parser/scan.l index caab9a002cf075298accde359be5d39c02e1e89d..b788a42bc98db10aea20c7ec4478ca806de83a9c 100644 --- a/src/backend/parser/scan.l +++ b/src/backend/parser/scan.l @@ -10,7 +10,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/scan.l,v 1.114 2004/02/21 00:34:52 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/parser/scan.l,v 1.115 2004/02/24 21:45:18 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -37,6 +37,7 @@ extern YYSTYPE yylval; static int xcdepth = 0; /* depth of nesting in slash-star comments */ +static char *dolqstart; /* current $foo$ quote start string */ /* * literalbuf is used to accumulate literal values when multiple rules @@ -74,6 +75,7 @@ unsigned char unescape_single_char(unsigned char c); %option 8bit %option never-interactive +%option nodefault %option nounput %option noyywrap %option prefix="base_yy" @@ -94,6 +96,7 @@ unsigned char unescape_single_char(unsigned char c); * <xd> delimited identifiers (double-quoted identifiers) * <xh> hexadecimal numeric string * <xq> quoted strings + * <xdolq> $foo$ quoted strings */ %x xb @@ -101,6 +104,7 @@ unsigned char unescape_single_char(unsigned char c); %x xd %x xh %x xq +%x xdolq /* * In order to make the world safe for Windows and Mac clients as well as @@ -175,6 +179,17 @@ xqescape [\\][^0-7] xqoctesc [\\][0-7]{1,3} xqcat {quote}{whitespace_with_newline}{quote} +/* $foo$ style quotes ("dollar quoting") + * The quoted string starts with $foo$ where "foo" is an optional string + * in the form of an identifier, except that it may not contain "$", + * and extends to the first occurrence of an identical string. + * There is *no* processing of the quoted text. + */ +dolq_start [A-Za-z\200-\377_] +dolq_cont [A-Za-z\200-\377_0-9] +dolqdelim \$({dolq_start}{dolq_cont}*)?\$ +dolqinside [^$]+ + /* Double quote * Allows embedded spaces and other special characters into identifiers. */ @@ -242,7 +257,8 @@ param \${integer} other . /* - * Quoted strings must allow some special characters such as single-quote + * Dollar quoted strings are totally opaque, and no escaping is done on them. + * Other quoted strings must allow some special characters such as single-quote * and newline. * Embedded single-quotes are implemented both in the SQL standard * style of two adjacent single quotes "''" and in the Postgres/Java style @@ -388,8 +404,46 @@ other . <xq>{xqcat} { /* ignore */ } +<xq>. { + /* This is only needed for \ just before EOF */ + addlitchar(yytext[0]); + } <xq><<EOF>> { yyerror("unterminated quoted string"); } +{dolqdelim} { + token_start = yytext; + dolqstart = pstrdup(yytext); + BEGIN(xdolq); + startlit(); + } +<xdolq>{dolqdelim} { + if (strcmp(yytext, dolqstart) == 0) + { + pfree(dolqstart); + BEGIN(INITIAL); + yylval.str = litbufdup(); + return SCONST; + } + else + { + /* + * When we fail to match $...$ to dolqstart, transfer + * the $... part to the output, but put back the final + * $ for rescanning. Consider $delim$...$junk$delim$ + */ + addlit(yytext, yyleng-1); + yyless(yyleng-1); + } + } +<xdolq>{dolqinside} { + addlit(yytext, yyleng); + } +<xdolq>. { + /* This is only needed for $ inside the quoted text */ + addlitchar(yytext[0]); + } +<xdolq><<EOF>> { yyerror("unterminated dollar-quoted string"); } + {xdstart} { token_start = yytext; BEGIN(xd); @@ -407,7 +461,7 @@ other . yylval.str = ident; return IDENT; } -<xd>{xddouble} { +<xd>{xddouble} { addlitchar('"'); } <xd>{xdinside} { diff --git a/src/bin/psql/prompt.c b/src/bin/psql/prompt.c index a11881bc9a7fdfd610168d37de79bd76fc5a4b77..0a0d317739b106c608cb147ae2a6d64f425fb865 100644 --- a/src/bin/psql/prompt.c +++ b/src/bin/psql/prompt.c @@ -3,7 +3,7 @@ * * Copyright (c) 2000-2003, PostgreSQL Global Development Group * - * $PostgreSQL: pgsql/src/bin/psql/prompt.c,v 1.34 2004/01/25 03:07:22 neilc Exp $ + * $PostgreSQL: pgsql/src/bin/psql/prompt.c,v 1.35 2004/02/24 21:45:18 tgl Exp $ */ #include "postgres_fe.h" #include "prompt.h" @@ -85,6 +85,7 @@ get_prompt(promptStatus_t status) case PROMPT_CONTINUE: case PROMPT_SINGLEQUOTE: case PROMPT_DOUBLEQUOTE: + case PROMPT_DOLLARQUOTE: case PROMPT_COMMENT: case PROMPT_PAREN: prompt_name = "PROMPT2"; @@ -199,6 +200,9 @@ get_prompt(promptStatus_t status) case PROMPT_DOUBLEQUOTE: buf[0] = '"'; break; + case PROMPT_DOLLARQUOTE: + buf[0] = '$'; + break; case PROMPT_COMMENT: buf[0] = '*'; break; diff --git a/src/bin/psql/prompt.h b/src/bin/psql/prompt.h index 433f70a3af847ae323efc00113173cf59e66f9aa..338371a478ab0df54f2ac95acf2d3e27fd1c776b 100644 --- a/src/bin/psql/prompt.h +++ b/src/bin/psql/prompt.h @@ -3,7 +3,7 @@ * * Copyright (c) 2000-2003, PostgreSQL Global Development Group * - * $PostgreSQL: pgsql/src/bin/psql/prompt.h,v 1.13 2003/11/29 19:52:07 pgsql Exp $ + * $PostgreSQL: pgsql/src/bin/psql/prompt.h,v 1.14 2004/02/24 21:45:18 tgl Exp $ */ #ifndef PROMPT_H #define PROMPT_H @@ -15,6 +15,7 @@ typedef enum _promptStatus PROMPT_COMMENT, PROMPT_SINGLEQUOTE, PROMPT_DOUBLEQUOTE, + PROMPT_DOLLARQUOTE, PROMPT_PAREN, PROMPT_COPY } promptStatus_t; diff --git a/src/bin/psql/psqlscan.l b/src/bin/psql/psqlscan.l index 46886b2f9403698db23191f8e469aedae0a0d94f..5eb3f40fc8e81e24c30c000f06e230674bc69eab 100644 --- a/src/bin/psql/psqlscan.l +++ b/src/bin/psql/psqlscan.l @@ -31,7 +31,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/bin/psql/psqlscan.l,v 1.1 2004/02/19 19:40:09 tgl Exp $ + * $PostgreSQL: pgsql/src/bin/psql/psqlscan.l,v 1.2 2004/02/24 21:45:18 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -92,6 +92,7 @@ typedef struct PsqlScanStateData int start_state; /* saved YY_START */ int paren_depth; /* depth of nesting in parentheses */ int xcdepth; /* depth of nesting in slash-star comments */ + char *dolqstart; /* current $foo$ quote start string */ } PsqlScanStateData; static PsqlScanState cur_state; /* current state while active */ @@ -123,6 +124,7 @@ static void emit(const char *txt, int len); %option 8bit %option never-interactive +%option nodefault %option nounput %option noyywrap @@ -151,6 +153,7 @@ static void emit(const char *txt, int len); * <xd> delimited identifiers (double-quoted identifiers) * <xh> hexadecimal numeric string * <xq> quoted strings + * <xdolq> $foo$ quoted strings */ %x xb @@ -158,6 +161,7 @@ static void emit(const char *txt, int len); %x xd %x xh %x xq +%x xdolq /* Additional exclusive states for psql only: lex backslash commands */ %x xslashcmd %x xslasharg @@ -241,6 +245,17 @@ xqescape [\\][^0-7] xqoctesc [\\][0-7]{1,3} xqcat {quote}{whitespace_with_newline}{quote} +/* $foo$ style quotes ("dollar quoting") + * The quoted string starts with $foo$ where "foo" is an optional string + * in the form of an identifier, except that it may not contain "$", + * and extends to the first occurrence of an identical string. + * There is *no* processing of the quoted text. + */ +dolq_start [A-Za-z\200-\377_] +dolq_cont [A-Za-z\200-\377_0-9] +dolqdelim \$({dolq_start}{dolq_cont}*)?\$ +dolqinside [^$]+ + /* Double quote * Allows embedded spaces and other special characters into identifiers. */ @@ -308,7 +323,8 @@ param \${integer} other . /* - * Quoted strings must allow some special characters such as single-quote + * Dollar quoted strings are totally opaque, and no escaping is done on them. + * Other quoted strings must allow some special characters such as single-quote * and newline. * Embedded single-quotes are implemented both in the SQL standard * style of two adjacent single quotes "''" and in the Postgres/Java style @@ -427,6 +443,41 @@ other . <xq>{xqcat} { ECHO; } +<xq>. { + /* This is only needed for \ just before EOF */ + ECHO; + } + +{dolqdelim} { + cur_state->dolqstart = pg_strdup(yytext); + BEGIN(xdolq); + ECHO; + } +<xdolq>{dolqdelim} { + if (strcmp(yytext, cur_state->dolqstart) == 0) + { + free(cur_state->dolqstart); + cur_state->dolqstart = NULL; + BEGIN(INITIAL); + } + else + { + /* + * When we fail to match $...$ to dolqstart, transfer + * the $... part to the output, but put back the final + * $ for rescanning. Consider $delim$...$junk$delim$ + */ + yyless(yyleng-1); + } + ECHO; + } +<xdolq>{dolqinside} { + ECHO; + } +<xdolq>. { + /* This is only needed for $ inside the quoted text */ + ECHO; + } {xdstart} { BEGIN(xd); @@ -436,7 +487,7 @@ other . BEGIN(INITIAL); ECHO; } -<xd>{xddouble} { +<xd>{xddouble} { ECHO; } <xd>{xdinside} { @@ -754,7 +805,7 @@ other . "\\". { emit(yytext + 1, 1); } -{other} { ECHO; } +{other}|\n { ECHO; } } @@ -766,7 +817,7 @@ other . "`" { return LEXRES_OK; } -{other} { ECHO; } +{other}|\n { ECHO; } } @@ -811,7 +862,7 @@ other . BEGIN(xslashdefaultarg); } -{other} { ECHO; } +{other}|\n { ECHO; } } @@ -833,7 +884,7 @@ other . "\\\\" { return LEXRES_OK; } -{other} { +{other}|\n { yyless(0); return LEXRES_OK; } @@ -865,6 +916,8 @@ psql_scan_destroy(PsqlScanState state) { psql_scan_finish(state); + psql_scan_reset(state); + free(state); } @@ -1008,6 +1061,10 @@ psql_scan(PsqlScanState state, result = PSCAN_INCOMPLETE; *prompt = PROMPT_SINGLEQUOTE; break; + case xdolq: + result = PSCAN_INCOMPLETE; + *prompt = PROMPT_DOLLARQUOTE; + break; default: /* can't get here */ fprintf(stderr, "invalid YY_START\n"); @@ -1082,6 +1139,9 @@ psql_scan_reset(PsqlScanState state) state->start_state = INITIAL; state->paren_depth = 0; state->xcdepth = 0; /* not really necessary */ + if (state->dolqstart) + free(state->dolqstart); + state->dolqstart = NULL; } /*