]> granicus.if.org Git - postgresql/blob - src/pl/plpgsql/src/scan.l
PL/pgSQL functions can return sets. Neil Conway's patch, modified so
[postgresql] / src / pl / plpgsql / src / scan.l
1 %{
2 /**********************************************************************
3  * scan.l               - Scanner for the PL/pgSQL
4  *                        procedural language
5  *
6  * IDENTIFICATION
7  *    $Header: /cvsroot/pgsql/src/pl/plpgsql/src/Attic/scan.l,v 1.22 2002/08/30 00:28:41 tgl Exp $
8  *
9  *    This software is copyrighted by Jan Wieck - Hamburg.
10  *
11  *    The author hereby grants permission  to  use,  copy,  modify,
12  *    distribute,  and  license this software and its documentation
13  *    for any purpose, provided that existing copyright notices are
14  *    retained  in  all  copies  and  that  this notice is included
15  *    verbatim in any distributions. No written agreement, license,
16  *    or  royalty  fee  is required for any of the authorized uses.
17  *    Modifications to this software may be  copyrighted  by  their
18  *    author  and  need  not  follow  the licensing terms described
19  *    here, provided that the new terms are  clearly  indicated  on
20  *    the first page of each file where they apply.
21  *
22  *    IN NO EVENT SHALL THE AUTHOR OR DISTRIBUTORS BE LIABLE TO ANY
23  *    PARTY  FOR  DIRECT,   INDIRECT,   SPECIAL,   INCIDENTAL,   OR
24  *    CONSEQUENTIAL   DAMAGES  ARISING  OUT  OF  THE  USE  OF  THIS
25  *    SOFTWARE, ITS DOCUMENTATION, OR ANY DERIVATIVES THEREOF, EVEN
26  *    IF  THE  AUTHOR  HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH
27  *    DAMAGE.
28  *
29  *    THE  AUTHOR  AND  DISTRIBUTORS  SPECIFICALLY   DISCLAIM   ANY
30  *    WARRANTIES,  INCLUDING,  BUT  NOT  LIMITED  TO,  THE  IMPLIED
31  *    WARRANTIES  OF  MERCHANTABILITY,  FITNESS  FOR  A  PARTICULAR
32  *    PURPOSE,  AND NON-INFRINGEMENT.  THIS SOFTWARE IS PROVIDED ON
33  *    AN "AS IS" BASIS, AND THE AUTHOR  AND  DISTRIBUTORS  HAVE  NO
34  *    OBLIGATION   TO   PROVIDE   MAINTENANCE,   SUPPORT,  UPDATES,
35  *    ENHANCEMENTS, OR MODIFICATIONS.
36  *
37  **********************************************************************/
38
39 #include "plpgsql.h"
40 #include "pl.tab.h"
41
42
43 static char     *plpgsql_source;
44 static int      plpgsql_bytes_left;
45 static int      scanner_functype;
46 static int      scanner_typereported;
47 static int      pushback_token;
48 static bool have_pushback_token;
49 static int      lookahead_token;        
50 static bool have_lookahead_token;
51
52 int     plpgsql_SpaceScanned = 0;
53
54 static void plpgsql_input(char *buf, int *result, int max);
55
56 #define YY_INPUT(buf,res,max)   plpgsql_input(buf, &res, max)
57 %}
58
59 %option 8bit
60 %option never-interactive
61 %option nounput
62 %option noyywrap
63
64 %option yylineno
65 %option case-insensitive
66
67
68 %x      IN_STRING IN_COMMENT
69
70 digit                   [0-9]
71 letter                  [\200-\377_A-Za-z]
72 letter_or_digit [\200-\377_A-Za-z0-9]
73
74 quoted_ident    (\"[^\"]*\")+
75
76 identifier              ({letter}{letter_or_digit}*|{quoted_ident})
77
78 space                   [ \t\n\r\f]
79
80 %%
81     /* ----------
82      * Local variable in scanner to remember where
83      * a string or comment started
84      * ----------
85      */
86     int start_lineno = 0;
87
88     /* ----------
89      * Reset the state when entering the scanner
90      * ----------
91      */
92     BEGIN INITIAL;
93     plpgsql_SpaceScanned = 0;
94
95     /* ----------
96      * On the first call to a new source report the
97      * functions type (T_FUNCTION or T_TRIGGER)
98      * ----------
99      */
100         if (!scanner_typereported)
101         {
102                 scanner_typereported = 1;
103                 return scanner_functype;
104         }
105
106     /* ----------
107      * The keyword rules
108      * ----------
109      */
110 :=                              { return K_ASSIGN;                      }
111 =                               { return K_ASSIGN;                      }
112 \.\.                    { return K_DOTDOT;                      }
113 alias                   { return K_ALIAS;                       }
114 begin                   { return K_BEGIN;                       }
115 close                   { return K_CLOSE;                       }
116 constant                { return K_CONSTANT;            }
117 cursor                  { return K_CURSOR;                      }
118 debug                   { return K_DEBUG;                       }
119 declare                 { return K_DECLARE;                     }
120 default                 { return K_DEFAULT;                     }
121 diagnostics             { return K_DIAGNOSTICS;         }
122 else                    { return K_ELSE;                        }
123 elsif           { return K_ELSIF;           }
124 end                             { return K_END;                         }
125 exception               { return K_EXCEPTION;           }
126 execute                 { return K_EXECUTE;                     }
127 exit                    { return K_EXIT;                        }
128 fetch                   { return K_FETCH;                       }
129 for                             { return K_FOR;                         }
130 from                    { return K_FROM;                        }
131 get                             { return K_GET;                         }
132 if                              { return K_IF;                          }
133 in                              { return K_IN;                          }
134 info                    { return K_INFO;                        }
135 into                    { return K_INTO;                        }
136 is                              { return K_IS;                          }
137 log                             { return K_LOG;                         }
138 loop                    { return K_LOOP;                        }
139 next                    { return K_NEXT;                        }
140 not                             { return K_NOT;                         }
141 notice                  { return K_NOTICE;                      }
142 null                    { return K_NULL;                        }
143 open                    { return K_OPEN;                        }
144 perform                 { return K_PERFORM;                     }
145 raise                   { return K_RAISE;                       }
146 record                  { return K_RECORD;                      }
147 rename                  { return K_RENAME;                      }
148 result_oid              { return K_RESULT_OID;          }
149 return                  { return K_RETURN;                      }
150 reverse                 { return K_REVERSE;                     }
151 row_count               { return K_ROW_COUNT;           }
152 select                  { return K_SELECT;                      }
153 then                    { return K_THEN;                        }
154 to                              { return K_TO;                          }
155 type                    { return K_TYPE;                        }
156 warning                 { return K_WARNING;                     }
157 when                    { return K_WHEN;                        }
158 while                   { return K_WHILE;                       }
159
160 ^#option                { return O_OPTION;                      }
161 dump                    { return O_DUMP;                        }
162
163
164     /* ----------
165      * Special word rules
166      * ----------
167      */
168 {identifier}                                    { return plpgsql_parse_word(yytext);    }
169 {identifier}{space}*\.{space}*{identifier}      { return plpgsql_parse_dblword(yytext); }
170 {identifier}{space}*\.{space}*{identifier}{space}*\.{space}*{identifier}        { return plpgsql_parse_tripword(yytext); }
171 {identifier}{space}*%TYPE               { return plpgsql_parse_wordtype(yytext);        }
172 {identifier}{space}*\.{space}*{identifier}{space}*%TYPE { return plpgsql_parse_dblwordtype(yytext); }
173 {identifier}{space}*%ROWTYPE    { return plpgsql_parse_wordrowtype(yytext);     }
174
175 \${digit}+                                              { return plpgsql_parse_word(yytext);    }
176 \${digit}+{space}*\.{space}*{identifier}        { return plpgsql_parse_dblword(yytext); }
177 \${digit}+{space}*\.{space}*{identifier}{space}*\.{space}*{identifier}  { return plpgsql_parse_tripword(yytext); }
178 \${digit}+{space}*%TYPE                 { return plpgsql_parse_wordtype(yytext);        }
179 \${digit}+{space}*\.{space}*{identifier}{space}*%TYPE   { return plpgsql_parse_dblwordtype(yytext); }
180 \${digit}+{space}*%ROWTYPE              { return plpgsql_parse_wordrowtype(yytext);     }
181
182 {digit}+                { return T_NUMBER;                      }
183
184 \".                             {
185                                 plpgsql_error_lineno = yylineno;
186                                 elog(ERROR, "unterminated quoted identifier");
187                         }
188
189     /* ----------
190      * Ignore whitespaces but remember this happened
191      * ----------
192      */
193 {space}+                { plpgsql_SpaceScanned = 1;             }
194
195     /* ----------
196      * Eat up comments
197      * ----------
198      */
199 --[^\r\n]*              ;
200
201 \/\*                    { start_lineno = yylineno;
202                           BEGIN IN_COMMENT;
203                         }
204 <IN_COMMENT>\*\/        { BEGIN INITIAL; plpgsql_SpaceScanned = 1; }
205 <IN_COMMENT>\n          ;
206 <IN_COMMENT>.           ;
207 <IN_COMMENT><<EOF>>     {
208                                 plpgsql_error_lineno = start_lineno;
209                                 elog(ERROR, "unterminated comment");
210                         }
211
212     /* ----------
213      * Collect anything inside of ''s and return one STRING
214      * ----------
215      */
216 '                       { start_lineno = yylineno;
217                           BEGIN IN_STRING;
218                           yymore();
219                         }
220 <IN_STRING>\\.          |
221 <IN_STRING>''           { yymore();                             }
222 <IN_STRING>'            { BEGIN INITIAL;
223                           return T_STRING;
224                         }
225 <IN_STRING><<EOF>>      {
226                                 plpgsql_error_lineno = start_lineno;
227                                 elog(ERROR, "unterminated string");
228                         }
229 <IN_STRING>[^'\\]*      { yymore();                             }
230
231     /* ----------
232      * Any unmatched character is returned as is
233      * ----------
234      */
235 .                       { return yytext[0];                     }
236
237 %%
238
239
240 static void
241 plpgsql_input(char *buf, int *result, int max)
242 {
243     int         n = max;
244
245     if (n > plpgsql_bytes_left)
246         n = plpgsql_bytes_left;
247
248     if (n == 0)
249         {
250         *result = YY_NULL;
251                 return;
252     }
253
254     *result = n;
255     memcpy(buf, plpgsql_source, n);
256     plpgsql_source += n;
257     plpgsql_bytes_left -= n;
258 }
259
260 /*
261  * This is the yylex routine called from outside. It exists to provide
262  * a pushback facility, as well as to allow us to parse syntax that
263  * requires more than one token of lookahead.
264  */
265 int
266 plpgsql_yylex(void)
267 {
268         int cur_token;
269
270         if (have_pushback_token)
271         {
272                 have_pushback_token = false;
273                 cur_token = pushback_token;
274         }
275         else if (have_lookahead_token)
276         {
277                 have_lookahead_token = false;
278                 cur_token = lookahead_token;
279         }
280         else
281                 cur_token = yylex();
282
283         /* Do we need to look ahead for a possible multiword token? */
284         switch (cur_token)
285         {
286                 /* RETURN NEXT must be reduced to a single token */
287                 case K_RETURN:
288                         if (!have_lookahead_token)
289                         {
290                                 lookahead_token = yylex();
291                                 have_lookahead_token = true;
292                         }
293                         if (lookahead_token == K_NEXT)
294                         {
295                                 have_lookahead_token = false;
296                                 cur_token = K_RETURN_NEXT;
297                         }
298                         break;
299
300                 default:
301                         break;
302         }
303
304         return cur_token;
305 }
306
307 /*
308  * Push back a single token to be re-read by next plpgsql_yylex() call.
309  */
310 void
311 plpgsql_push_back_token(int token)
312 {
313         if (have_pushback_token)
314                 elog(ERROR, "plpgsql_push_back_token: can't push back multiple tokens");
315         pushback_token = token;
316         have_pushback_token = true;
317 }
318
319
320 /*
321  * Initialize the scanner for new input.
322  */
323 void
324 plpgsql_setinput(char *source, int functype)
325 {
326     yyrestart(NULL);
327     yylineno = 1;
328
329     plpgsql_source = source;
330
331         /*----------
332          * Hack: skip any initial newline, so that in the common coding layout
333          *              CREATE FUNCTION ... AS '
334          *                      code body
335          *              ' LANGUAGE 'plpgsql';
336          * we will think "line 1" is what the programmer thinks of as line 1.
337          *----------
338          */
339     if (*plpgsql_source == '\r')
340         plpgsql_source++;
341     if (*plpgsql_source == '\n')
342         plpgsql_source++;
343
344     plpgsql_bytes_left = strlen(plpgsql_source);
345
346     scanner_functype     = functype;
347     scanner_typereported = 0;
348
349         have_pushback_token = false;
350         have_lookahead_token = false;
351 }