Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * parser.c
4 : : * Main entry point/driver for PostgreSQL grammar
5 : : *
6 : : * This should match src/backend/parser/parser.c, except that we do not
7 : : * need to bother with re-entrant interfaces.
8 : : *
9 : : * Note: ECPG doesn't report error location like the backend does.
10 : : * This file will need work if we ever want it to.
11 : : *
12 : : *
13 : : * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
14 : : * Portions Copyright (c) 1994, Regents of the University of California
15 : : *
16 : : * IDENTIFICATION
17 : : * src/interfaces/ecpg/preproc/parser.c
18 : : *
19 : : *-------------------------------------------------------------------------
20 : : */
21 : :
22 : : #include "postgres_fe.h"
23 : :
24 : : #include "preproc_extern.h"
25 : : #include "preproc.h"
26 : :
27 : :
28 : : static bool have_lookahead; /* is lookahead info valid? */
29 : : static int lookahead_token; /* one-token lookahead */
30 : : static YYSTYPE lookahead_yylval; /* yylval for lookahead token */
31 : : static YYLTYPE lookahead_yylloc; /* yylloc for lookahead token */
32 : : static char *lookahead_yytext; /* start current token */
33 : :
34 : : static bool check_uescapechar(unsigned char escape);
35 : : static bool ecpg_isspace(char ch);
36 : :
37 : :
38 : : /*
39 : : * Intermediate filter between parser and base lexer (base_yylex in scan.l).
40 : : *
41 : : * This filter is needed because in some cases the standard SQL grammar
42 : : * requires more than one token lookahead. We reduce these cases to one-token
43 : : * lookahead by replacing tokens here, in order to keep the grammar LALR(1).
44 : : *
45 : : * Using a filter is simpler than trying to recognize multiword tokens
46 : : * directly in scan.l, because we'd have to allow for comments between the
47 : : * words. Furthermore it's not clear how to do that without re-introducing
48 : : * scanner backtrack, which would cost more performance than this filter
49 : : * layer does.
50 : : *
51 : : * We also use this filter to convert UIDENT and USCONST sequences into
52 : : * plain IDENT and SCONST tokens. While that could be handled by additional
53 : : * productions in the main grammar, it's more efficient to do it like this.
54 : : */
55 : : int
6015 tgl@sss.pgh.pa.us 56 :CBC 35431 : filtered_base_yylex(void)
57 : : {
58 : : int cur_token;
59 : : int next_token;
60 : : YYSTYPE cur_yylval;
61 : : YYLTYPE cur_yylloc;
62 : : char *cur_yytext;
63 : :
64 : : /* Get next token --- we might already have it */
65 [ + + ]: 35431 : if (have_lookahead)
66 : : {
67 : 57 : cur_token = lookahead_token;
68 : 57 : base_yylval = lookahead_yylval;
69 : 57 : base_yylloc = lookahead_yylloc;
2681 70 : 57 : base_yytext = lookahead_yytext;
6015 71 : 57 : have_lookahead = false;
72 : : }
73 : : else
74 : 35374 : cur_token = base_yylex();
75 : :
76 : : /*
77 : : * If this token isn't one that requires lookahead, just return it.
78 : : */
79 [ + + ]: 35431 : switch (cur_token)
80 : : {
382 alvherre@alvh.no-ip. 81 : 58 : case FORMAT:
82 : : case NOT:
83 : : case NULLS_P:
84 : : case WITH:
85 : : case WITHOUT:
86 : : case UIDENT:
87 : : case USCONST:
3337 tgl@sss.pgh.pa.us 88 : 58 : break;
89 : 35373 : default:
90 : 35373 : return cur_token;
91 : : }
92 : :
93 : : /* Save and restore lexer output variables around the call */
94 : 58 : cur_yylval = base_yylval;
95 : 58 : cur_yylloc = base_yylloc;
2681 96 : 58 : cur_yytext = base_yytext;
97 : :
98 : : /* Get next token, saving outputs into lookahead variables */
3337 99 : 58 : next_token = base_yylex();
100 : :
101 : 58 : lookahead_token = next_token;
102 : 58 : lookahead_yylval = base_yylval;
103 : 58 : lookahead_yylloc = base_yylloc;
2681 104 : 58 : lookahead_yytext = base_yytext;
105 : :
3337 106 : 58 : base_yylval = cur_yylval;
107 : 58 : base_yylloc = cur_yylloc;
2681 108 : 58 : base_yytext = cur_yytext;
109 : :
3337 110 : 58 : have_lookahead = true;
111 : :
112 : : /* Replace cur_token if needed, based on lookahead */
113 [ + + + + : 58 : switch (cur_token)
+ + - ]
114 : : {
382 alvherre@alvh.no-ip. 115 [ + - ]: 5 : case FORMAT:
116 : : /* Replace FORMAT by FORMAT_LA if it's followed by JSON */
117 : : switch (next_token)
118 : : {
119 : 5 : case JSON:
120 : 5 : cur_token = FORMAT_LA;
121 : 5 : break;
122 : : }
123 : 5 : break;
124 : :
3322 tgl@sss.pgh.pa.us 125 [ - + ]: 36 : case NOT:
126 : : /* Replace NOT by NOT_LA if it's followed by BETWEEN, IN, etc */
127 : : switch (next_token)
128 : : {
3322 tgl@sss.pgh.pa.us 129 :UBC 0 : case BETWEEN:
130 : : case IN_P:
131 : : case LIKE:
132 : : case ILIKE:
133 : : case SIMILAR:
134 : 0 : cur_token = NOT_LA;
135 : 0 : break;
136 : : }
3322 tgl@sss.pgh.pa.us 137 :CBC 36 : break;
138 : :
3337 139 [ + - ]: 2 : case NULLS_P:
140 : : /* Replace NULLS_P by NULLS_LA if it's followed by FIRST or LAST */
141 : : switch (next_token)
142 : : {
6015 143 : 2 : case FIRST_P:
144 : : case LAST_P:
3337 145 : 2 : cur_token = NULLS_LA;
6015 146 : 2 : break;
147 : : }
148 : 2 : break;
149 : :
150 [ + + ]: 8 : case WITH:
151 : : /* Replace WITH by WITH_LA if it's followed by TIME or ORDINALITY */
152 : : switch (next_token)
153 : : {
5647 peter_e@gmx.net 154 : 1 : case TIME:
155 : : case ORDINALITY:
3337 tgl@sss.pgh.pa.us 156 : 1 : cur_token = WITH_LA;
6015 157 : 1 : break;
158 : : }
159 : 8 : break;
160 : :
382 alvherre@alvh.no-ip. 161 [ + + ]: 4 : case WITHOUT:
162 : : /* Replace WITHOUT by WITHOUT_LA if it's followed by TIME */
163 : : switch (next_token)
164 : : {
376 165 : 1 : case TIME:
382 166 : 1 : cur_token = WITHOUT_LA;
167 : 1 : break;
168 : : }
169 : 4 : break;
1553 tgl@sss.pgh.pa.us 170 : 3 : case UIDENT:
171 : : case USCONST:
172 : : /* Look ahead for UESCAPE */
173 [ + + ]: 3 : if (next_token == UESCAPE)
174 : : {
175 : : /* Yup, so get third token, which had better be SCONST */
176 : : const char *escstr;
177 : :
178 : : /*
179 : : * Again save and restore lexer output variables around the
180 : : * call
181 : : */
182 : 1 : cur_yylval = base_yylval;
183 : 1 : cur_yylloc = base_yylloc;
184 : 1 : cur_yytext = base_yytext;
185 : :
186 : : /* Get third token */
187 : 1 : next_token = base_yylex();
188 : :
189 [ - + ]: 1 : if (next_token != SCONST)
1553 tgl@sss.pgh.pa.us 190 :UBC 0 : mmerror(PARSE_ERROR, ET_ERROR, "UESCAPE must be followed by a simple string literal");
191 : :
192 : : /*
193 : : * Save and check escape string, which the scanner returns
194 : : * with quotes
195 : : */
1553 tgl@sss.pgh.pa.us 196 :CBC 1 : escstr = base_yylval.str;
197 [ + - - + ]: 1 : if (strlen(escstr) != 3 || !check_uescapechar(escstr[1]))
1553 tgl@sss.pgh.pa.us 198 :UBC 0 : mmerror(PARSE_ERROR, ET_ERROR, "invalid Unicode escape character");
199 : :
1553 tgl@sss.pgh.pa.us 200 :CBC 1 : base_yylval = cur_yylval;
201 : 1 : base_yylloc = cur_yylloc;
202 : 1 : base_yytext = cur_yytext;
203 : :
204 : : /* Combine 3 tokens into 1 */
205 : 1 : base_yylval.str = psprintf("%s UESCAPE %s", base_yylval.str, escstr);
206 : :
207 : : /* Clear have_lookahead, thereby consuming all three tokens */
208 : 1 : have_lookahead = false;
209 : : }
210 : :
211 [ + + ]: 3 : if (cur_token == UIDENT)
212 : 1 : cur_token = IDENT;
213 [ + - ]: 2 : else if (cur_token == USCONST)
214 : 2 : cur_token = SCONST;
215 : 3 : break;
216 : : }
217 : :
6015 218 : 58 : return cur_token;
219 : : }
220 : :
221 : : /*
222 : : * check_uescapechar() and ecpg_isspace() should match their equivalents
223 : : * in pgc.l.
224 : : */
225 : :
226 : : /* is 'escape' acceptable as Unicode escape character (UESCAPE syntax) ? */
227 : : static bool
1553 228 : 1 : check_uescapechar(unsigned char escape)
229 : : {
230 [ + - ]: 1 : if (isxdigit(escape)
231 [ + - ]: 1 : || escape == '+'
232 [ + - ]: 1 : || escape == '\''
233 [ + - ]: 1 : || escape == '"'
234 [ - + ]: 1 : || ecpg_isspace(escape))
1553 tgl@sss.pgh.pa.us 235 :UBC 0 : return false;
236 : : else
1553 tgl@sss.pgh.pa.us 237 :CBC 1 : return true;
238 : : }
239 : :
240 : : /*
241 : : * ecpg_isspace() --- return true if flex scanner considers char whitespace
242 : : */
243 : : static bool
244 : 1 : ecpg_isspace(char ch)
245 : : {
246 [ + - + - ]: 1 : if (ch == ' ' ||
247 [ + - ]: 1 : ch == '\t' ||
248 [ + - ]: 1 : ch == '\n' ||
249 [ - + ]: 1 : ch == '\r' ||
250 : : ch == '\f')
1553 tgl@sss.pgh.pa.us 251 :UBC 0 : return true;
1553 tgl@sss.pgh.pa.us 252 :CBC 1 : return false;
253 : : }
|