Sync whitespace in parsers
[geany-mirror.git] / ctags / parsers / sql.c
blobdc57be45bb01ba354acebb8a9a4909251eeffe72
1 /*
2 * Copyright (c) 2002-2003, Darren Hiebert
4 * This source code is released for free distribution under the terms of the
5 * GNU General Public License version 2 or (at your option) any later version.
7 * This module contains functions for generating tags for PL/SQL language
8 * files.
9 */
12 * INCLUDE FILES
14 #include "general.h" /* must always come first */
16 #include <ctype.h> /* to define isalpha () */
17 #include <setjmp.h>
18 #ifdef DEBUG
19 #include <stdio.h>
20 #endif
22 #include "debug.h"
23 #include "entry.h"
24 #include "keyword.h"
25 #include "parse.h"
26 #include "read.h"
27 #include "routines.h"
28 #include "vstring.h"
31 * On-line "Oracle Database PL/SQL Language Reference":
32 * http://download.oracle.com/docs/cd/B28359_01/appdev.111/b28370/toc.htm
34 * Sample PL/SQL code is available from:
35 * http://www.orafaq.com/faqscrpt.htm#GENPLSQL
37 * On-line SQL Anywhere Documentation
38 * http://www.ianywhere.com/developer/product_manuals/sqlanywhere/index.html
42 * MACROS
44 #define isType(token,t) (boolean) ((token)->type == (t))
45 #define isKeyword(token,k) (boolean) ((token)->keyword == (k))
48 * DATA DECLARATIONS
51 typedef enum eException { ExceptionNone, ExceptionEOF } exception_t;
54 * Used to specify type of keyword.
56 typedef enum eKeywordId {
57 KEYWORD_NONE = -1,
58 KEYWORD_is,
59 KEYWORD_begin,
60 KEYWORD_body,
61 KEYWORD_cursor,
62 KEYWORD_declare,
63 KEYWORD_end,
64 KEYWORD_function,
65 KEYWORD_if,
66 KEYWORD_else,
67 KEYWORD_elseif,
68 KEYWORD_endif,
69 KEYWORD_loop,
70 KEYWORD_while,
71 KEYWORD_case,
72 KEYWORD_for,
73 KEYWORD_do,
74 KEYWORD_call,
75 KEYWORD_package,
76 KEYWORD_pragma,
77 KEYWORD_procedure,
78 KEYWORD_record,
79 KEYWORD_object,
80 KEYWORD_ref,
81 KEYWORD_rem,
82 KEYWORD_return,
83 KEYWORD_returns,
84 KEYWORD_subtype,
85 KEYWORD_table,
86 KEYWORD_trigger,
87 KEYWORD_type,
88 KEYWORD_index,
89 KEYWORD_event,
90 KEYWORD_publication,
91 KEYWORD_service,
92 KEYWORD_domain,
93 KEYWORD_datatype,
94 KEYWORD_result,
95 KEYWORD_url,
96 KEYWORD_internal,
97 KEYWORD_external,
98 KEYWORD_when,
99 KEYWORD_then,
100 KEYWORD_variable,
101 KEYWORD_exception,
102 KEYWORD_at,
103 KEYWORD_on,
104 KEYWORD_primary,
105 KEYWORD_references,
106 KEYWORD_unique,
107 KEYWORD_check,
108 KEYWORD_constraint,
109 KEYWORD_foreign,
110 KEYWORD_ml_table,
111 KEYWORD_ml_table_lang,
112 KEYWORD_ml_table_dnet,
113 KEYWORD_ml_table_java,
114 KEYWORD_ml_table_chk,
115 KEYWORD_ml_conn,
116 KEYWORD_ml_conn_lang,
117 KEYWORD_ml_conn_dnet,
118 KEYWORD_ml_conn_java,
119 KEYWORD_ml_conn_chk,
120 KEYWORD_ml_prop,
121 KEYWORD_local,
122 KEYWORD_temporary,
123 KEYWORD_drop,
124 KEYWORD_view,
125 KEYWORD_synonym,
126 KEYWORD_handler,
127 KEYWORD_comment,
128 KEYWORD_create,
129 KEYWORD_go
130 } keywordId;
132 typedef enum eTokenType {
133 TOKEN_UNDEFINED,
134 TOKEN_BLOCK_LABEL_BEGIN,
135 TOKEN_BLOCK_LABEL_END,
136 TOKEN_CHARACTER,
137 TOKEN_CLOSE_PAREN,
138 TOKEN_COLON,
139 TOKEN_SEMICOLON,
140 TOKEN_COMMA,
141 TOKEN_IDENTIFIER,
142 TOKEN_KEYWORD,
143 TOKEN_OPEN_PAREN,
144 TOKEN_OPERATOR,
145 TOKEN_OTHER,
146 TOKEN_STRING,
147 TOKEN_PERIOD,
148 TOKEN_OPEN_CURLY,
149 TOKEN_CLOSE_CURLY,
150 TOKEN_OPEN_SQUARE,
151 TOKEN_CLOSE_SQUARE,
152 TOKEN_TILDE,
153 TOKEN_FORWARD_SLASH,
154 TOKEN_EQUAL
155 } tokenType;
157 typedef struct sTokenInfoSQL {
158 tokenType type;
159 keywordId keyword;
160 vString * string;
161 vString * scope;
162 int scopeKind;
163 int begin_end_nest_lvl;
164 unsigned long lineNumber;
165 MIOPos filePosition;
166 } tokenInfo;
169 * DATA DEFINITIONS
172 static langType Lang_sql;
174 static jmp_buf Exception;
176 typedef enum {
177 SQLTAG_CURSOR,
178 SQLTAG_PROTOTYPE,
179 SQLTAG_FUNCTION,
180 SQLTAG_FIELD,
181 SQLTAG_LOCAL_VARIABLE,
182 SQLTAG_BLOCK_LABEL,
183 SQLTAG_PACKAGE,
184 SQLTAG_PROCEDURE,
185 SQLTAG_RECORD,
186 SQLTAG_SUBTYPE,
187 SQLTAG_TABLE,
188 SQLTAG_TRIGGER,
189 SQLTAG_VARIABLE,
190 SQLTAG_INDEX,
191 SQLTAG_EVENT,
192 SQLTAG_PUBLICATION,
193 SQLTAG_SERVICE,
194 SQLTAG_DOMAIN,
195 SQLTAG_VIEW,
196 SQLTAG_SYNONYM,
197 SQLTAG_MLTABLE,
198 SQLTAG_MLCONN,
199 SQLTAG_MLPROP,
200 SQLTAG_COUNT
201 } sqlKind;
203 static kindOption SqlKinds [] = {
204 { TRUE, 'c', "cursor", "cursors" },
205 { FALSE, 'd', "prototype", "prototypes" },
206 { TRUE, 'f', "function", "functions" },
207 { TRUE, 'F', "field", "record fields" },
208 { FALSE, 'l', "local", "local variables" },
209 { TRUE, 'L', "label", "block label" },
210 { TRUE, 'P', "package", "packages" },
211 { TRUE, 'p', "procedure", "procedures" },
212 { FALSE, 'r', "record", "records" },
213 { TRUE, 's', "subtype", "subtypes" },
214 { TRUE, 't', "table", "tables" },
215 { TRUE, 'T', "trigger", "triggers" },
216 { TRUE, 'v', "variable", "variables" },
217 { TRUE, 'i', "index", "indexes" },
218 { TRUE, 'e', "event", "events" },
219 { TRUE, 'U', "publication", "publications" },
220 { TRUE, 'R', "service", "services" },
221 { TRUE, 'D', "domain", "domains" },
222 { TRUE, 'V', "view", "views" },
223 { TRUE, 'n', "synonym", "synonyms" },
224 { TRUE, 'x', "mltable", "MobiLink Table Scripts" },
225 { TRUE, 'y', "mlconn", "MobiLink Conn Scripts" },
226 { TRUE, 'z', "mlprop", "MobiLink Properties " }
229 static const keywordTable SqlKeywordTable [] = {
230 /* keyword keyword ID */
231 { "as", KEYWORD_is },
232 { "is", KEYWORD_is },
233 { "begin", KEYWORD_begin },
234 { "body", KEYWORD_body },
235 { "cursor", KEYWORD_cursor },
236 { "declare", KEYWORD_declare },
237 { "end", KEYWORD_end },
238 { "function", KEYWORD_function },
239 { "if", KEYWORD_if },
240 { "else", KEYWORD_else },
241 { "elseif", KEYWORD_elseif },
242 { "endif", KEYWORD_endif },
243 { "loop", KEYWORD_loop },
244 { "while", KEYWORD_while },
245 { "case", KEYWORD_case },
246 { "for", KEYWORD_for },
247 { "do", KEYWORD_do },
248 { "call", KEYWORD_call },
249 { "package", KEYWORD_package },
250 { "pragma", KEYWORD_pragma },
251 { "procedure", KEYWORD_procedure },
252 { "record", KEYWORD_record },
253 { "object", KEYWORD_object },
254 { "ref", KEYWORD_ref },
255 { "rem", KEYWORD_rem },
256 { "return", KEYWORD_return },
257 { "returns", KEYWORD_returns },
258 { "subtype", KEYWORD_subtype },
259 { "table", KEYWORD_table },
260 { "trigger", KEYWORD_trigger },
261 { "type", KEYWORD_type },
262 { "index", KEYWORD_index },
263 { "event", KEYWORD_event },
264 { "publication", KEYWORD_publication },
265 { "service", KEYWORD_service },
266 { "domain", KEYWORD_domain },
267 { "datatype", KEYWORD_datatype },
268 { "result", KEYWORD_result },
269 { "url", KEYWORD_url },
270 { "internal", KEYWORD_internal },
271 { "external", KEYWORD_external },
272 { "when", KEYWORD_when },
273 { "then", KEYWORD_then },
274 { "variable", KEYWORD_variable },
275 { "exception", KEYWORD_exception },
276 { "at", KEYWORD_at },
277 { "on", KEYWORD_on },
278 { "primary", KEYWORD_primary },
279 { "references", KEYWORD_references },
280 { "unique", KEYWORD_unique },
281 { "check", KEYWORD_check },
282 { "constraint", KEYWORD_constraint },
283 { "foreign", KEYWORD_foreign },
284 { "ml_add_table_script", KEYWORD_ml_table },
285 { "ml_add_lang_table_script", KEYWORD_ml_table_lang },
286 { "ml_add_dnet_table_script", KEYWORD_ml_table_dnet },
287 { "ml_add_java_table_script", KEYWORD_ml_table_java },
288 { "ml_add_lang_table_script_chk", KEYWORD_ml_table_chk },
289 { "ml_add_connection_script", KEYWORD_ml_conn },
290 { "ml_add_lang_connection_script", KEYWORD_ml_conn_lang },
291 { "ml_add_dnet_connection_script", KEYWORD_ml_conn_dnet },
292 { "ml_add_java_connection_script", KEYWORD_ml_conn_java },
293 { "ml_add_lang_conn_script_chk", KEYWORD_ml_conn_chk },
294 { "ml_add_property", KEYWORD_ml_prop },
295 { "local", KEYWORD_local },
296 { "temporary", KEYWORD_temporary },
297 { "drop", KEYWORD_drop },
298 { "view", KEYWORD_view },
299 { "synonym", KEYWORD_synonym },
300 { "handler", KEYWORD_handler },
301 { "comment", KEYWORD_comment },
302 { "create", KEYWORD_create },
303 { "go", KEYWORD_go }
307 * FUNCTION DECLARATIONS
310 /* Recursive calls */
311 static void parseBlock (tokenInfo *const token, const boolean local);
312 static void parseDeclare (tokenInfo *const token, const boolean local);
313 static void parseKeywords (tokenInfo *const token);
314 static void parseSqlFile (tokenInfo *const token);
317 * FUNCTION DEFINITIONS
320 static boolean isIdentChar1 (const int c)
323 * Other databases are less restrictive on the first character of
324 * an identifier.
325 * isIdentChar1 is used to identify the first character of an
326 * identifier, so we are removing some restrictions.
328 return (boolean)
329 (isalpha (c) || c == '@' || c == '_' );
332 static boolean isIdentChar (const int c)
334 return (boolean)
335 (isalpha (c) || isdigit (c) || c == '$' ||
336 c == '@' || c == '_' || c == '#');
339 static boolean isCmdTerm (tokenInfo *const token)
341 DebugStatement (
342 debugPrintf (DEBUG_PARSE
343 , "\n isCmdTerm: token same tt:%d tk:%d\n"
344 , token->type
345 , token->keyword
350 * Based on the various customer sites I have been at
351 * the most common command delimiters are
355 * go
356 * This routine will check for any of these, more
357 * can easily be added by modifying readToken and
358 * either adding the character to:
359 * enum eTokenType
360 * enum eTokenType
362 return (isType (token, TOKEN_SEMICOLON) ||
363 isType (token, TOKEN_TILDE) ||
364 isType (token, TOKEN_FORWARD_SLASH) ||
365 isKeyword (token, KEYWORD_go));
368 static boolean isMatchedEnd(tokenInfo *const token, int nest_lvl)
370 boolean terminated = FALSE;
372 * Since different forms of SQL allow the use of
373 * BEGIN
374 * ...
375 * END
376 * blocks, some statements may not be terminated using
377 * the standard delimiters:
381 * go
382 * This routine will check to see if we encounter and END
383 * for the matching nest level of BEGIN ... END statements.
384 * If we find one, then we can assume, the statement was terminated
385 * since we have fallen through to the END statement of the BEGIN
386 * block.
388 if ( nest_lvl > 0 && isKeyword (token, KEYWORD_end) )
390 if ( token->begin_end_nest_lvl == nest_lvl )
391 terminated = TRUE;
394 return terminated;
397 static tokenInfo *newToken (void)
399 tokenInfo *const token = xMalloc (1, tokenInfo);
401 token->type = TOKEN_UNDEFINED;
402 token->keyword = KEYWORD_NONE;
403 token->string = vStringNew ();
404 token->scope = vStringNew ();
405 token->scopeKind = SQLTAG_COUNT;
406 token->begin_end_nest_lvl = 0;
407 token->lineNumber = getInputLineNumber ();
408 token->filePosition = getInputFilePosition ();
410 return token;
413 static void deleteToken (tokenInfo *const token)
415 vStringDelete (token->string);
416 vStringDelete (token->scope);
417 eFree (token);
420 static int analyzeToken (vString *const name, langType language)
422 vString *keyword = vStringNew ();
423 int result;
424 vStringCopyToLower (keyword, name);
425 result = lookupKeyword (vStringValue (keyword), language);
426 vStringDelete (keyword);
427 return result;
431 * Tag generation functions
434 static void makeSqlTag (tokenInfo *const token, const sqlKind kind)
436 if (SqlKinds [kind].enabled)
438 const char *const name = vStringValue (token->string);
439 tagEntryInfo e;
440 initTagEntry (&e, name, &(SqlKinds [kind]));
442 e.lineNumber = token->lineNumber;
443 e.filePosition = token->filePosition;
445 if (vStringLength (token->scope) > 0)
447 Assert (token->scopeKind < SQLTAG_COUNT);
448 e.extensionFields.scopeKind = &(SqlKinds [token->scopeKind]);
449 e.extensionFields.scopeName = vStringValue (token->scope);
452 makeTagEntry (&e);
457 * Parsing functions
460 static void parseString (vString *const string, const int delimiter)
462 boolean end = FALSE;
463 while (! end)
465 int c = getcFromInputFile ();
466 if (c == EOF)
467 end = TRUE;
469 else if (c == '\\')
471 c = getcFromInputFile(); // This maybe a ' or ". //
472 vStringPut(string, c);
475 else if (c == delimiter)
476 end = TRUE;
477 else
478 vStringPut (string, c);
480 vStringTerminate (string);
483 /* Read a C identifier beginning with "firstChar" and places it into "name".
485 static void parseIdentifier (vString *const string, const int firstChar)
487 int c = firstChar;
488 Assert (isIdentChar1 (c));
491 vStringPut (string, c);
492 c = getcFromInputFile ();
493 } while (isIdentChar (c));
494 vStringTerminate (string);
495 if (!isspace (c))
496 ungetcToInputFile (c); /* unget non-identifier character */
499 static void readToken (tokenInfo *const token)
501 int c;
503 token->type = TOKEN_UNDEFINED;
504 token->keyword = KEYWORD_NONE;
505 vStringClear (token->string);
507 getNextChar:
510 c = getcFromInputFile ();
511 token->lineNumber = getInputLineNumber ();
512 token->filePosition = getInputFilePosition ();
514 * Added " to the list of ignores, not sure what this
515 * might break but it gets by this issue:
516 * create table "t1" (...)
518 * Darren, the code passes all my tests for both
519 * Oracle and SQL Anywhere, but maybe you can tell me
520 * what this may effect.
523 while (c == '\t' || c == ' ' || c == '\n');
525 switch (c)
527 case EOF: longjmp (Exception, (int)ExceptionEOF); break;
528 case '(': token->type = TOKEN_OPEN_PAREN; break;
529 case ')': token->type = TOKEN_CLOSE_PAREN; break;
530 case ':': token->type = TOKEN_COLON; break;
531 case ';': token->type = TOKEN_SEMICOLON; break;
532 case '.': token->type = TOKEN_PERIOD; break;
533 case ',': token->type = TOKEN_COMMA; break;
534 case '{': token->type = TOKEN_OPEN_CURLY; break;
535 case '}': token->type = TOKEN_CLOSE_CURLY; break;
536 case '~': token->type = TOKEN_TILDE; break;
537 case '[': token->type = TOKEN_OPEN_SQUARE; break;
538 case ']': token->type = TOKEN_CLOSE_SQUARE; break;
539 case '=': token->type = TOKEN_EQUAL; break;
541 case '\'':
542 case '"':
543 token->type = TOKEN_STRING;
544 parseString (token->string, c);
545 token->lineNumber = getInputLineNumber ();
546 token->filePosition = getInputFilePosition ();
547 break;
549 case '-':
550 c = getcFromInputFile ();
551 if (c == '-') /* -- is this the start of a comment? */
553 skipToCharacterInInputFile ('\n');
554 goto getNextChar;
556 else
558 if (!isspace (c))
559 ungetcToInputFile (c);
560 token->type = TOKEN_OPERATOR;
562 break;
564 case '<':
565 case '>':
567 const int initial = c;
568 int d = getcFromInputFile ();
569 if (d == initial)
571 if (initial == '<')
572 token->type = TOKEN_BLOCK_LABEL_BEGIN;
573 else
574 token->type = TOKEN_BLOCK_LABEL_END;
576 else
578 ungetcToInputFile (d);
579 token->type = TOKEN_UNDEFINED;
581 break;
584 case '\\':
585 c = getcFromInputFile ();
586 if (c != '\\' && c != '"' && c != '\'' && !isspace (c))
587 ungetcToInputFile (c);
588 token->type = TOKEN_CHARACTER;
589 token->lineNumber = getInputLineNumber ();
590 token->filePosition = getInputFilePosition ();
591 break;
593 case '/':
595 int d = getcFromInputFile ();
596 if ((d != '*') && /* is this the start of a comment? */
597 (d != '/')) /* is a one line comment? */
599 token->type = TOKEN_FORWARD_SLASH;
600 ungetcToInputFile (d);
602 else
604 if (d == '*')
608 skipToCharacterInInputFile ('*');
609 c = getcFromInputFile ();
610 if (c == '/')
611 break;
612 else
613 ungetcToInputFile (c);
614 } while (c != EOF && c != '\0');
615 goto getNextChar;
617 else if (d == '/') /* is this the start of a comment? */
619 skipToCharacterInInputFile ('\n');
620 goto getNextChar;
623 break;
626 default:
627 if (! isIdentChar1 (c))
628 token->type = TOKEN_UNDEFINED;
629 else
631 parseIdentifier (token->string, c);
632 token->lineNumber = getInputLineNumber ();
633 token->filePosition = getInputFilePosition ();
634 token->keyword = analyzeToken (token->string, Lang_sql);
635 if (isKeyword (token, KEYWORD_rem))
637 vStringClear (token->string);
638 skipToCharacterInInputFile ('\n');
639 goto getNextChar;
641 else if (isKeyword (token, KEYWORD_NONE))
642 token->type = TOKEN_IDENTIFIER;
643 else
644 token->type = TOKEN_KEYWORD;
646 break;
651 * reads an identifier, possibly quoted:
652 * identifier
653 * "identifier"
654 * [identifier]
656 static void readIdentifier (tokenInfo *const token)
658 readToken (token);
659 if (isType (token, TOKEN_OPEN_SQUARE))
661 tokenInfo *const close_square = newToken ();
663 readToken (token);
664 /* eat close square */
665 readToken (close_square);
666 deleteToken (close_square);
671 * Token parsing functions
675 * static void addContext (tokenInfo* const parent, const tokenInfo* const child)
677 * if (vStringLength (parent->string) > 0)
679 * vStringCatS (parent->string, ".");
681 * vStringCatS (parent->string, vStringValue(child->string));
682 * vStringTerminate(parent->string);
686 static void addToScope (tokenInfo* const token, vString* const extra, sqlKind kind)
688 if (vStringLength (token->scope) > 0)
690 vStringCatS (token->scope, ".");
692 vStringCatS (token->scope, vStringValue(extra));
693 vStringTerminate(token->scope);
694 token->scopeKind = kind;
698 * Scanning functions
701 static void findToken (tokenInfo *const token, const tokenType type)
703 while (! isType (token, type))
705 readToken (token);
709 static void findCmdTerm (tokenInfo *const token, const boolean check_first)
711 int begin_end_nest_lvl = token->begin_end_nest_lvl;
713 if (check_first)
715 if (isCmdTerm(token))
716 return;
720 readToken (token);
721 } while ( !isCmdTerm(token) && !isMatchedEnd(token, begin_end_nest_lvl) );
724 static void skipToMatched(tokenInfo *const token)
726 int nest_level = 0;
727 tokenType open_token;
728 tokenType close_token;
730 switch (token->type)
732 case TOKEN_OPEN_PAREN:
733 open_token = TOKEN_OPEN_PAREN;
734 close_token = TOKEN_CLOSE_PAREN;
735 break;
736 case TOKEN_OPEN_CURLY:
737 open_token = TOKEN_OPEN_CURLY;
738 close_token = TOKEN_CLOSE_CURLY;
739 break;
740 case TOKEN_OPEN_SQUARE:
741 open_token = TOKEN_OPEN_SQUARE;
742 close_token = TOKEN_CLOSE_SQUARE;
743 break;
744 default:
745 return;
749 * This routine will skip to a matching closing token.
750 * It will also handle nested tokens like the (, ) below.
751 * ( name varchar(30), text binary(10) )
754 if (isType (token, open_token))
756 nest_level++;
757 while (! (isType (token, close_token) && (nest_level == 0)))
759 readToken (token);
760 if (isType (token, open_token))
762 nest_level++;
764 if (isType (token, close_token))
766 if (nest_level > 0)
768 nest_level--;
772 readToken (token);
776 static void copyToken (tokenInfo *const dest, tokenInfo *const src)
778 dest->lineNumber = src->lineNumber;
779 dest->filePosition = src->filePosition;
780 dest->type = src->type;
781 dest->keyword = src->keyword;
782 vStringCopy(dest->string, src->string);
783 vStringCopy(dest->scope, src->scope);
784 dest->scopeKind = src->scopeKind;
787 static void skipArgumentList (tokenInfo *const token)
790 * Other databases can have arguments with fully declared
791 * datatypes:
792 * ( name varchar(30), text binary(10) )
793 * So we must check for nested open and closing parentheses
796 if (isType (token, TOKEN_OPEN_PAREN)) /* arguments? */
798 skipToMatched (token);
802 static void parseSubProgram (tokenInfo *const token)
804 tokenInfo *const name = newToken ();
805 vString * saveScope = vStringNew ();
806 sqlKind saveScopeKind;
809 * This must handle both prototypes and the body of
810 * the procedures.
812 * Prototype:
813 * FUNCTION func_name RETURN integer;
814 * PROCEDURE proc_name( parameters );
815 * Procedure
816 * FUNCTION GET_ML_USERNAME RETURN VARCHAR2
817 * IS
818 * BEGIN
819 * RETURN v_sync_user_id;
820 * END GET_ML_USERNAME;
822 * PROCEDURE proc_name( parameters )
823 * IS
824 * BEGIN
825 * END;
826 * CREATE PROCEDURE proc_name( parameters )
827 * EXTERNAL NAME ... ;
828 * CREATE PROCEDURE proc_name( parameters )
829 * BEGIN
830 * END;
832 * CREATE FUNCTION f_GetClassName(
833 * IN @object VARCHAR(128)
834 * ,IN @code VARCHAR(128)
836 * RETURNS VARCHAR(200)
837 * DETERMINISTIC
838 * BEGIN
840 * IF( @object = 'user_state' ) THEN
841 * SET something = something;
842 * END IF;
844 * RETURN @name;
845 * END;
847 * Note, a Package adds scope to the items within.
848 * create or replace package demo_pkg is
849 * test_var number;
850 * function test_func return varchar2;
851 * function more.test_func2 return varchar2;
852 * end demo_pkg;
853 * So the tags generated here, contain the package name:
854 * demo_pkg.test_var
855 * demo_pkg.test_func
856 * demo_pkg.more.test_func2
858 const sqlKind kind = isKeyword (token, KEYWORD_function) ?
859 SQLTAG_FUNCTION : SQLTAG_PROCEDURE;
860 Assert (isKeyword (token, KEYWORD_function) ||
861 isKeyword (token, KEYWORD_procedure));
863 vStringCopy(saveScope, token->scope);
864 saveScopeKind = token->scopeKind;
865 readToken (token);
866 copyToken (name, token);
867 readToken (token);
869 if (isType (token, TOKEN_PERIOD))
872 * If this is an Oracle package, then the token->scope should
873 * already be set. If this is the case, also add this value to the
874 * scope.
875 * If this is not an Oracle package, chances are the scope should be
876 * blank and the value just read is the OWNER or CREATOR of the
877 * function and should not be considered part of the scope.
879 if (vStringLength(saveScope) > 0)
881 addToScope(token, name->string, kind);
883 readToken (token);
884 copyToken (name, token);
885 readToken (token);
887 if (isType (token, TOKEN_OPEN_PAREN))
889 /* Reads to the next token after the TOKEN_CLOSE_PAREN */
890 skipArgumentList(token);
893 if (kind == SQLTAG_FUNCTION)
895 if (isKeyword (token, KEYWORD_return) ||
896 isKeyword (token, KEYWORD_returns))
898 /* Read datatype */
899 readToken (token);
901 * Read token after which could be the
902 * command terminator if a prototype
903 * or an open parenthesis
905 readToken (token);
906 if (isType (token, TOKEN_OPEN_PAREN))
908 /* Reads to the next token after the TOKEN_CLOSE_PAREN */
909 skipArgumentList(token);
913 if (isCmdTerm (token))
915 makeSqlTag (name, SQLTAG_PROTOTYPE);
917 else
919 while (!(isKeyword (token, KEYWORD_is) ||
920 isKeyword (token, KEYWORD_begin) ||
921 isKeyword (token, KEYWORD_at) ||
922 isKeyword (token, KEYWORD_internal) ||
923 isKeyword (token, KEYWORD_external) ||
924 isKeyword (token, KEYWORD_url) ||
925 isType (token, TOKEN_EQUAL) ||
926 isCmdTerm (token)
930 if (isKeyword (token, KEYWORD_result))
932 readToken (token);
933 if (isType (token, TOKEN_OPEN_PAREN))
935 /* Reads to the next token after the TOKEN_CLOSE_PAREN */
936 skipArgumentList(token);
938 } else {
939 readToken (token);
942 if (isKeyword (token, KEYWORD_at) ||
943 isKeyword (token, KEYWORD_url) ||
944 isKeyword (token, KEYWORD_internal) ||
945 isKeyword (token, KEYWORD_external))
947 addToScope(token, name->string, kind);
948 if (isType (name, TOKEN_IDENTIFIER) ||
949 isType (name, TOKEN_STRING) ||
950 !isKeyword (token, KEYWORD_NONE)
952 makeSqlTag (name, kind);
954 vStringClear (token->scope);
955 token->scopeKind = SQLTAG_COUNT;
957 if (isType (token, TOKEN_EQUAL))
958 readToken (token);
960 if (isKeyword (token, KEYWORD_declare))
961 parseDeclare (token, FALSE);
963 if (isKeyword (token, KEYWORD_is) ||
964 isKeyword (token, KEYWORD_begin))
966 addToScope(token, name->string, kind);
967 if (isType (name, TOKEN_IDENTIFIER) ||
968 isType (name, TOKEN_STRING) ||
969 !isKeyword (token, KEYWORD_NONE)
971 makeSqlTag (name, kind);
973 parseBlock (token, TRUE);
974 vStringClear (token->scope);
975 token->scopeKind = SQLTAG_COUNT;
978 vStringCopy(token->scope, saveScope);
979 token->scopeKind = saveScopeKind;
980 deleteToken (name);
981 vStringDelete(saveScope);
984 static void parseRecord (tokenInfo *const token)
987 * Make it a bit forgiving, this is called from
988 * multiple functions, parseTable, parseType
990 if (!isType (token, TOKEN_OPEN_PAREN))
991 readToken (token);
993 Assert (isType (token, TOKEN_OPEN_PAREN));
996 if ( isType (token, TOKEN_COMMA) || isType (token, TOKEN_OPEN_PAREN) )
997 readToken (token);
1000 * Create table statements can end with various constraints
1001 * which must be excluded from the SQLTAG_FIELD.
1002 * create table t1 (
1003 * c1 integer,
1004 * c2 char(30),
1005 * c3 numeric(10,5),
1006 * c4 integer,
1007 * constraint whatever,
1008 * primary key(c1),
1009 * foreign key (),
1010 * check ()
1013 if (! (isKeyword(token, KEYWORD_primary) ||
1014 isKeyword(token, KEYWORD_references) ||
1015 isKeyword(token, KEYWORD_unique) ||
1016 isKeyword(token, KEYWORD_check) ||
1017 isKeyword(token, KEYWORD_constraint) ||
1018 isKeyword(token, KEYWORD_foreign) ) )
1020 /* keyword test above is redundant as only a TOKEN_KEYWORD could
1021 * match any isKeyword() anyway */
1022 if (isType (token, TOKEN_IDENTIFIER) ||
1023 isType (token, TOKEN_STRING))
1024 makeSqlTag (token, SQLTAG_FIELD);
1027 while (!(isType (token, TOKEN_COMMA) ||
1028 isType (token, TOKEN_CLOSE_PAREN) ||
1029 isType (token, TOKEN_OPEN_PAREN)
1032 readToken (token);
1034 * A table structure can look like this:
1035 * create table t1 (
1036 * c1 integer,
1037 * c2 char(30),
1038 * c3 numeric(10,5),
1039 * c4 integer
1041 * We can't just look for a COMMA or CLOSE_PAREN
1042 * since that will not deal with the numeric(10,5)
1043 * case. So we need to skip the argument list
1044 * when we find an open paren.
1046 if (isType (token, TOKEN_OPEN_PAREN))
1048 /* Reads to the next token after the TOKEN_CLOSE_PAREN */
1049 skipArgumentList(token);
1052 } while (! isType (token, TOKEN_CLOSE_PAREN));
1055 static void parseType (tokenInfo *const token)
1057 tokenInfo *const name = newToken ();
1058 vString * saveScope = vStringNew ();
1059 sqlKind saveScopeKind;
1061 vStringCopy(saveScope, token->scope);
1062 /* If a scope has been set, add it to the name */
1063 addToScope (name, token->scope, token->scopeKind);
1064 saveScopeKind = token->scopeKind;
1065 readToken (name);
1066 if (isType (name, TOKEN_IDENTIFIER))
1068 readToken (token);
1069 if (isKeyword (token, KEYWORD_is))
1071 readToken (token);
1072 switch (token->keyword)
1074 case KEYWORD_record:
1075 case KEYWORD_object:
1076 makeSqlTag (name, SQLTAG_RECORD);
1077 addToScope (token, name->string, SQLTAG_RECORD);
1078 parseRecord (token);
1079 break;
1081 case KEYWORD_table:
1082 makeSqlTag (name, SQLTAG_TABLE);
1083 break;
1085 case KEYWORD_ref:
1086 readToken (token);
1087 if (isKeyword (token, KEYWORD_cursor))
1088 makeSqlTag (name, SQLTAG_CURSOR);
1089 break;
1091 default: break;
1093 vStringClear (token->scope);
1094 token->scopeKind = SQLTAG_COUNT;
1097 vStringCopy(token->scope, saveScope);
1098 token->scopeKind = saveScopeKind;
1099 deleteToken (name);
1100 vStringDelete(saveScope);
1103 static void parseSimple (tokenInfo *const token, const sqlKind kind)
1105 /* This will simply make the tagname from the first word found */
1106 readToken (token);
1107 if (isType (token, TOKEN_IDENTIFIER) ||
1108 isType (token, TOKEN_STRING))
1109 makeSqlTag (token, kind);
1112 static void parseDeclare (tokenInfo *const token, const boolean local)
1115 * PL/SQL declares are of this format:
1116 * IS|AS
1117 * [declare]
1118 * CURSOR curname ...
1119 * varname1 datatype;
1120 * varname2 datatype;
1121 * varname3 datatype;
1122 * begin
1125 if (isKeyword (token, KEYWORD_declare))
1126 readToken (token);
1127 while (! isKeyword (token, KEYWORD_begin) && ! isKeyword (token, KEYWORD_end))
1129 switch (token->keyword)
1131 case KEYWORD_cursor: parseSimple (token, SQLTAG_CURSOR); break;
1132 case KEYWORD_function: parseSubProgram (token); break;
1133 case KEYWORD_procedure: parseSubProgram (token); break;
1134 case KEYWORD_subtype: parseSimple (token, SQLTAG_SUBTYPE); break;
1135 case KEYWORD_trigger: parseSimple (token, SQLTAG_TRIGGER); break;
1136 case KEYWORD_type: parseType (token); break;
1138 default:
1139 if (isType (token, TOKEN_IDENTIFIER))
1141 if (local)
1143 makeSqlTag (token, SQLTAG_LOCAL_VARIABLE);
1145 else
1147 makeSqlTag (token, SQLTAG_VARIABLE);
1150 break;
1152 findToken (token, TOKEN_SEMICOLON);
1153 readToken (token);
1157 static void parseDeclareANSI (tokenInfo *const token, const boolean local)
1159 tokenInfo *const type = newToken ();
1161 * ANSI declares are of this format:
1162 * BEGIN
1163 * DECLARE varname1 datatype;
1164 * DECLARE varname2 datatype;
1165 * ...
1167 * This differ from PL/SQL where DECLARE precedes the BEGIN block
1168 * and the DECLARE keyword is not repeated.
1170 while (isKeyword (token, KEYWORD_declare))
1172 readToken (token);
1173 readToken (type);
1175 if (isKeyword (type, KEYWORD_cursor))
1176 makeSqlTag (token, SQLTAG_CURSOR);
1177 else if (isKeyword (token, KEYWORD_local) &&
1178 isKeyword (type, KEYWORD_temporary))
1181 * DECLARE LOCAL TEMPORARY TABLE table_name (
1182 * c1 int,
1183 * c2 int
1184 * );
1186 readToken (token);
1187 if (isKeyword (token, KEYWORD_table))
1189 readToken (token);
1190 if (isType(token, TOKEN_IDENTIFIER) ||
1191 isType(token, TOKEN_STRING))
1193 makeSqlTag (token, SQLTAG_TABLE);
1197 else if (isType (token, TOKEN_IDENTIFIER) ||
1198 isType (token, TOKEN_STRING))
1200 if (local)
1201 makeSqlTag (token, SQLTAG_LOCAL_VARIABLE);
1202 else
1203 makeSqlTag (token, SQLTAG_VARIABLE);
1205 findToken (token, TOKEN_SEMICOLON);
1206 readToken (token);
1208 deleteToken (type);
1211 static void parseLabel (tokenInfo *const token)
1214 * A label has this format:
1215 * <<tobacco_dependency>>
1216 * DECLARE
1217 * v_senator VARCHAR2(100) := 'THURMOND, JESSE';
1218 * BEGIN
1219 * IF total_contributions (v_senator, 'TOBACCO') > 25000
1220 * THEN
1221 * <<alochol_dependency>>
1222 * DECLARE
1223 * v_senator VARCHAR2(100) := 'WHATEVERIT, TAKES';
1224 * BEGIN
1225 * ...
1228 Assert (isType (token, TOKEN_BLOCK_LABEL_BEGIN));
1229 readToken (token);
1230 if (isType (token, TOKEN_IDENTIFIER))
1232 makeSqlTag (token, SQLTAG_BLOCK_LABEL);
1233 readToken (token); /* read end of label */
1237 static void parseStatements (tokenInfo *const token, const boolean exit_on_endif )
1239 /* boolean isAnsi = TRUE; */
1240 boolean stmtTerm = FALSE;
1244 if (isType (token, TOKEN_BLOCK_LABEL_BEGIN))
1245 parseLabel (token);
1246 else
1248 switch (token->keyword)
1250 case KEYWORD_exception:
1252 * EXCEPTION
1253 * <exception handler>;
1255 * Where an exception handler could be:
1256 * BEGIN
1257 * WHEN OTHERS THEN
1258 * x := x + 3;
1259 * END;
1260 * In this case we need to skip this keyword and
1261 * move on to the next token without reading until
1262 * TOKEN_SEMICOLON;
1264 readToken (token);
1265 continue;
1267 case KEYWORD_when:
1269 * WHEN statements can be used in exception clauses
1270 * and CASE statements. The CASE statement should skip
1271 * these given below we skip over to an END statement.
1272 * But for an exception clause, we can have:
1273 * EXCEPTION
1274 * WHEN OTHERS THEN
1275 * BEGIN
1276 * x := x + 3;
1277 * END;
1278 * If we skip to the TOKEN_SEMICOLON, we miss the begin
1279 * of a nested BEGIN END block. So read the next token
1280 * after the THEN and restart the LOOP.
1282 while (! isKeyword (token, KEYWORD_then))
1283 readToken (token);
1285 readToken (token);
1286 continue;
1288 case KEYWORD_if:
1290 * We do not want to look for a ; since for an empty
1291 * IF block, it would skip over the END.
1292 * IF...THEN
1293 * END IF;
1295 * IF...THEN
1296 * ELSE
1297 * END IF;
1299 * IF...THEN
1300 * ELSEIF...THEN
1301 * ELSE
1302 * END IF;
1304 * or non-ANSI
1305 * IF ...
1306 * BEGIN
1307 * END
1309 while ( ! isKeyword (token, KEYWORD_then) &&
1310 ! isKeyword (token, KEYWORD_begin) )
1312 readToken (token);
1315 if (isKeyword (token, KEYWORD_begin))
1317 /* isAnsi = FALSE; */
1318 parseBlock(token, FALSE);
1321 * Handle the non-Ansi IF blocks.
1322 * parseBlock consumes the END, so if the next
1323 * token in a command terminator (like GO)
1324 * we know we are done with this statement.
1326 if (isCmdTerm (token))
1327 stmtTerm = TRUE;
1329 else
1331 readToken (token);
1333 while( ! (isKeyword (token, KEYWORD_end ) ||
1334 isKeyword (token, KEYWORD_endif ) )
1337 if ( isKeyword (token, KEYWORD_else) ||
1338 isKeyword (token, KEYWORD_elseif) )
1339 readToken (token);
1341 parseStatements (token, TRUE);
1343 if (isCmdTerm(token))
1344 readToken (token);
1349 * parseStatements returns when it finds an END, an IF
1350 * should follow the END for ANSI anyway.
1351 * IF...THEN
1352 * END IF;
1354 if (isKeyword (token, KEYWORD_end))
1355 readToken (token);
1357 if( isKeyword (token, KEYWORD_if ) || isKeyword (token, KEYWORD_endif ) )
1359 readToken (token);
1360 if (isCmdTerm(token))
1361 stmtTerm = TRUE;
1363 else
1366 * Well we need to do something here.
1367 * There are lots of different END statements
1368 * END;
1369 * END CASE;
1370 * ENDIF;
1371 * ENDCASE;
1375 break;
1377 case KEYWORD_loop:
1378 case KEYWORD_case:
1379 case KEYWORD_for:
1381 * LOOP...
1382 * END LOOP;
1384 * CASE
1385 * WHEN '1' THEN
1386 * END CASE;
1388 * FOR loop_name AS cursor_name CURSOR FOR ...
1389 * DO
1390 * END FOR;
1392 if (isKeyword (token, KEYWORD_for))
1394 /* loop name */
1395 readToken (token);
1396 /* AS */
1397 readToken (token);
1399 while ( ! isKeyword (token, KEYWORD_is) )
1402 * If this is not an AS keyword this is
1403 * not a proper FOR statement and should
1404 * simply be ignored
1406 return;
1409 while ( ! isKeyword (token, KEYWORD_do) )
1410 readToken (token);
1414 readToken (token);
1415 while( ! isKeyword (token, KEYWORD_end ) )
1418 if ( isKeyword (token, KEYWORD_else) ||
1419 isKeyword (token, KEYWORD_elseif) )
1420 readToken (token);
1423 parseStatements (token, FALSE);
1425 if (isCmdTerm(token))
1426 readToken (token);
1430 if (isKeyword (token, KEYWORD_end ))
1431 readToken (token);
1434 * Typically ended with
1435 * END LOOP [loop name];
1436 * END CASE
1437 * END FOR [loop name];
1439 if ( isKeyword (token, KEYWORD_loop) ||
1440 isKeyword (token, KEYWORD_case) ||
1441 isKeyword (token, KEYWORD_for) )
1442 readToken (token);
1444 if (isCmdTerm(token))
1445 stmtTerm = TRUE;
1447 break;
1449 case KEYWORD_create:
1450 readToken (token);
1451 parseKeywords(token);
1452 break;
1454 case KEYWORD_declare:
1455 case KEYWORD_begin:
1456 parseBlock (token, TRUE);
1457 break;
1459 case KEYWORD_end:
1460 break;
1462 default:
1463 readToken (token);
1464 break;
1467 * Not all statements must end in a semi-colon
1468 * begin
1469 * if current publisher <> 'publish' then
1470 * signal UE_FailStatement
1471 * end if
1472 * end;
1473 * The last statement prior to an end ("signal" above) does
1474 * not need a semi-colon, nor does the end if, since it is
1475 * also the last statement prior to the end of the block.
1477 * So we must read to the first semi-colon or an END block
1479 while ( ! stmtTerm &&
1480 ! ( isKeyword (token, KEYWORD_end) ||
1481 (isCmdTerm(token)) )
1484 if ( isKeyword (token, KEYWORD_endif) &&
1485 exit_on_endif )
1486 return;
1488 if (isType (token, TOKEN_COLON) )
1491 * A : can signal a loop name
1492 * myloop:
1493 * LOOP
1494 * LEAVE myloop;
1495 * END LOOP;
1496 * Unfortunately, labels do not have a
1497 * cmd terminator, therefore we have to check
1498 * if the next token is a keyword and process
1499 * it accordingly.
1501 readToken (token);
1502 if ( isKeyword (token, KEYWORD_loop) ||
1503 isKeyword (token, KEYWORD_while) ||
1504 isKeyword (token, KEYWORD_for) )
1505 /* parseStatements (token); */
1506 return;
1509 readToken (token);
1511 if (isType (token, TOKEN_OPEN_PAREN) ||
1512 isType (token, TOKEN_OPEN_CURLY) ||
1513 isType (token, TOKEN_OPEN_SQUARE) )
1514 skipToMatched (token);
1517 * Since we know how to parse various statements
1518 * if we detect them, parse them to completion
1520 if (isType (token, TOKEN_BLOCK_LABEL_BEGIN) ||
1521 isKeyword (token, KEYWORD_exception) ||
1522 isKeyword (token, KEYWORD_loop) ||
1523 isKeyword (token, KEYWORD_case) ||
1524 isKeyword (token, KEYWORD_for) ||
1525 isKeyword (token, KEYWORD_begin))
1526 parseStatements (token, FALSE);
1527 else if (isKeyword (token, KEYWORD_if))
1528 parseStatements (token, TRUE);
1533 * We assumed earlier all statements ended with a command terminator.
1534 * See comment above, now, only read if the current token
1535 * is not a command terminator.
1537 if (isCmdTerm(token) && ! stmtTerm)
1538 stmtTerm = TRUE;
1540 } while (! isKeyword (token, KEYWORD_end) &&
1541 ! (exit_on_endif && isKeyword (token, KEYWORD_endif) ) &&
1542 ! stmtTerm );
1545 static void parseBlock (tokenInfo *const token, const boolean local)
1547 if (isType (token, TOKEN_BLOCK_LABEL_BEGIN))
1549 parseLabel (token);
1550 readToken (token);
1552 if (! isKeyword (token, KEYWORD_begin))
1554 readToken (token);
1556 * These are Oracle style declares which generally come
1557 * between an IS/AS and BEGIN block.
1559 parseDeclare (token, local);
1561 if (isKeyword (token, KEYWORD_begin))
1563 readToken (token);
1565 * Check for ANSI declarations which always follow
1566 * a BEGIN statement. This routine will not advance
1567 * the token if none are found.
1569 parseDeclareANSI (token, local);
1570 token->begin_end_nest_lvl++;
1571 while (! isKeyword (token, KEYWORD_end))
1573 parseStatements (token, FALSE);
1575 if (isCmdTerm(token))
1576 readToken (token);
1578 token->begin_end_nest_lvl--;
1581 * Read the next token (we will assume
1582 * it is the command delimiter)
1584 readToken (token);
1587 * Check if the END block is terminated
1589 if (! isCmdTerm (token))
1592 * Not sure what to do here at the moment.
1593 * I think the routine that calls parseBlock
1594 * must expect the next token has already
1595 * been read since it is possible this
1596 * token is not a command delimiter.
1598 /* findCmdTerm (token, FALSE); */
1603 static void parsePackage (tokenInfo *const token)
1606 * Packages can be specified in a number of ways:
1607 * CREATE OR REPLACE PACKAGE pkg_name AS
1608 * or
1609 * CREATE OR REPLACE PACKAGE owner.pkg_name AS
1610 * or by specifying a package body
1611 * CREATE OR REPLACE PACKAGE BODY pkg_name AS
1612 * CREATE OR REPLACE PACKAGE BODY owner.pkg_name AS
1614 tokenInfo *const name = newToken ();
1615 readIdentifier (name);
1616 if (isKeyword (name, KEYWORD_body))
1619 * Ignore the BODY tag since we will process
1620 * the body or prototypes in the same manner
1622 readIdentifier (name);
1624 /* Check for owner.pkg_name */
1625 while (! isKeyword (token, KEYWORD_is))
1627 readToken (token);
1628 if ( isType(token, TOKEN_PERIOD) )
1630 readIdentifier (name);
1633 if (isKeyword (token, KEYWORD_is))
1635 if (isType (name, TOKEN_IDENTIFIER) ||
1636 isType (name, TOKEN_STRING))
1637 makeSqlTag (name, SQLTAG_PACKAGE);
1638 addToScope (token, name->string, SQLTAG_PACKAGE);
1639 parseBlock (token, FALSE);
1640 vStringClear (token->scope);
1641 token->scopeKind = SQLTAG_COUNT;
1643 findCmdTerm (token, FALSE);
1644 deleteToken (name);
1647 static void parseTable (tokenInfo *const token)
1649 tokenInfo *const name = newToken ();
1652 * This deals with these formats:
1653 * create table t1 (c1 int);
1654 * create global temporary table t2 (c1 int);
1655 * create table "t3" (c1 int);
1656 * create table bob.t4 (c1 int);
1657 * create table bob."t5" (c1 int);
1658 * create table "bob"."t6" (c1 int);
1659 * create table bob."t7" (c1 int);
1660 * Proxy tables use this format:
1661 * create existing table bob."t7" AT '...';
1662 * SQL Server and Sybase formats
1663 * create table OnlyTable (
1664 * create table dbo.HasOwner (
1665 * create table [dbo].[HasOwnerSquare] (
1666 * create table master.dbo.HasDb (
1667 * create table master..HasDbNoOwner (
1668 * create table [master].dbo.[HasDbAndOwnerSquare] (
1669 * create table [master]..[HasDbNoOwnerSquare] (
1672 /* This could be a database, owner or table name */
1673 readIdentifier (name);
1674 readToken (token);
1675 if (isType (token, TOKEN_PERIOD))
1678 * This could be a owner or table name.
1679 * But this is also a special case since the table can be
1680 * referenced with a blank owner:
1681 * dbname..tablename
1683 readIdentifier (name);
1684 /* Check if a blank name was provided */
1685 if (isType (name, TOKEN_PERIOD))
1687 readIdentifier (name);
1689 readToken (token);
1690 if (isType (token, TOKEN_PERIOD))
1692 /* This can only be the table name */
1693 readIdentifier (name);
1694 readToken (token);
1697 if (isType (token, TOKEN_OPEN_PAREN))
1699 if (isType (name, TOKEN_IDENTIFIER) ||
1700 isType (name, TOKEN_STRING))
1702 makeSqlTag (name, SQLTAG_TABLE);
1703 vStringCopy(token->scope, name->string);
1704 token->scopeKind = SQLTAG_TABLE;
1705 parseRecord (token);
1706 vStringClear (token->scope);
1707 token->scopeKind = SQLTAG_COUNT;
1710 else if (isKeyword (token, KEYWORD_at))
1712 if (isType (name, TOKEN_IDENTIFIER))
1714 makeSqlTag (name, SQLTAG_TABLE);
1717 findCmdTerm (token, FALSE);
1718 deleteToken (name);
1721 static void parseIndex (tokenInfo *const token)
1723 tokenInfo *const name = newToken ();
1724 tokenInfo *const owner = newToken ();
1727 * This deals with these formats
1728 * create index i1 on t1(c1) create index "i2" on t1(c1)
1729 * create virtual unique clustered index "i3" on t1(c1)
1730 * create unique clustered index "i4" on t1(c1)
1731 * create clustered index "i5" on t1(c1)
1732 * create bitmap index "i6" on t1(c1)
1735 readIdentifier (name);
1736 readToken (token);
1737 if (isType (token, TOKEN_PERIOD))
1739 readIdentifier (name);
1740 readToken (token);
1742 if (isKeyword (token, KEYWORD_on) &&
1743 (isType (name, TOKEN_IDENTIFIER) ||
1744 isType (name, TOKEN_STRING)))
1746 readIdentifier (owner);
1747 readToken (token);
1748 if (isType (token, TOKEN_PERIOD))
1750 readIdentifier (owner);
1751 readToken (token);
1753 addToScope(name, owner->string, SQLTAG_TABLE /* FIXME? */);
1754 makeSqlTag (name, SQLTAG_INDEX);
1756 findCmdTerm (token, FALSE);
1757 deleteToken (name);
1758 deleteToken (owner);
1761 static void parseEvent (tokenInfo *const token)
1763 tokenInfo *const name = newToken ();
1766 * This deals with these formats
1767 * create event e1 handler begin end;
1768 * create event "e2" handler begin end;
1769 * create event dba."e3" handler begin end;
1770 * create event "dba"."e4" handler begin end;
1773 readIdentifier (name);
1774 readToken (token);
1775 if (isType (token, TOKEN_PERIOD))
1777 readIdentifier (name);
1779 while (! (isKeyword (token, KEYWORD_handler) ||
1780 (isType (token, TOKEN_SEMICOLON))) )
1782 readToken (token);
1785 if (isKeyword (token, KEYWORD_handler) ||
1786 isType (token, TOKEN_SEMICOLON))
1788 makeSqlTag (name, SQLTAG_EVENT);
1791 if (isKeyword (token, KEYWORD_handler))
1793 readToken (token);
1794 if (isKeyword (token, KEYWORD_begin))
1796 parseBlock (token, TRUE);
1798 findCmdTerm (token, TRUE);
1800 deleteToken (name);
1803 static void parseTrigger (tokenInfo *const token)
1805 tokenInfo *const name = newToken ();
1806 tokenInfo *const table = newToken ();
1809 * This deals with these formats
1810 * create or replace trigger tr1 begin end;
1811 * create trigger "tr2" begin end;
1812 * drop trigger "droptr1";
1813 * create trigger "tr3" CALL sp_something();
1814 * create trigger "owner"."tr4" begin end;
1815 * create trigger "tr5" not valid;
1816 * create trigger "tr6" begin end;
1819 readIdentifier (name);
1820 readToken (token);
1821 if (isType (token, TOKEN_PERIOD))
1823 readIdentifier (name);
1824 readToken (token);
1827 while ( !isKeyword (token, KEYWORD_on) &&
1828 !isCmdTerm (token) )
1830 readToken (token);
1833 /*if (! isType (token, TOKEN_SEMICOLON) ) */
1834 if (! isCmdTerm (token))
1836 readToken (table);
1837 readToken (token);
1838 if (isType (token, TOKEN_PERIOD))
1840 readToken (table);
1841 readToken (token);
1844 while (! (isKeyword (token, KEYWORD_begin) ||
1845 (isKeyword (token, KEYWORD_call)) ||
1846 ( isCmdTerm (token))) )
1848 if (isKeyword (token, KEYWORD_declare))
1850 addToScope(token, name->string, SQLTAG_TRIGGER);
1851 parseDeclare(token, TRUE);
1852 vStringClear(token->scope);
1853 token->scopeKind = SQLTAG_COUNT;
1855 else
1856 readToken (token);
1859 if (isKeyword (token, KEYWORD_begin) ||
1860 isKeyword (token, KEYWORD_call))
1862 addToScope(name, table->string, SQLTAG_TABLE);
1863 makeSqlTag (name, SQLTAG_TRIGGER);
1864 addToScope(token, table->string, SQLTAG_TABLE);
1865 if (isKeyword (token, KEYWORD_begin))
1867 parseBlock (token, TRUE);
1869 vStringClear(token->scope);
1870 token->scopeKind = SQLTAG_COUNT;
1874 findCmdTerm (token, TRUE);
1875 deleteToken (name);
1876 deleteToken (table);
1879 static void parsePublication (tokenInfo *const token)
1881 tokenInfo *const name = newToken ();
1884 * This deals with these formats
1885 * create or replace publication pu1 ()
1886 * create publication "pu2" ()
1887 * create publication dba."pu3" ()
1888 * create publication "dba"."pu4" ()
1891 readIdentifier (name);
1892 readToken (token);
1893 if (isType (token, TOKEN_PERIOD))
1895 readIdentifier (name);
1896 readToken (token);
1898 if (isType (token, TOKEN_OPEN_PAREN))
1900 if (isType (name, TOKEN_IDENTIFIER) ||
1901 isType (name, TOKEN_STRING))
1903 makeSqlTag (name, SQLTAG_PUBLICATION);
1906 findCmdTerm (token, FALSE);
1907 deleteToken (name);
1910 static void parseService (tokenInfo *const token)
1912 tokenInfo *const name = newToken ();
1915 * This deals with these formats
1916 * CREATE SERVICE s1 TYPE 'HTML'
1917 * AUTHORIZATION OFF USER DBA AS
1918 * SELECT *
1919 * FROM SYS.SYSTABLE;
1920 * CREATE SERVICE "s2" TYPE 'HTML'
1921 * AUTHORIZATION OFF USER DBA AS
1922 * CALL sp_Something();
1925 readIdentifier (name);
1926 readToken (token);
1927 if (isKeyword (token, KEYWORD_type))
1929 if (isType (name, TOKEN_IDENTIFIER) ||
1930 isType (name, TOKEN_STRING))
1932 makeSqlTag (name, SQLTAG_SERVICE);
1935 findCmdTerm (token, FALSE);
1936 deleteToken (name);
1939 static void parseDomain (tokenInfo *const token)
1941 tokenInfo *const name = newToken ();
1944 * This deals with these formats
1945 * CREATE DOMAIN|DATATYPE [AS] your_name ...;
1948 readIdentifier (name);
1949 if (isKeyword (name, KEYWORD_is))
1951 readIdentifier (name);
1953 readToken (token);
1954 if (isType (name, TOKEN_IDENTIFIER) ||
1955 isType (name, TOKEN_STRING))
1957 makeSqlTag (name, SQLTAG_DOMAIN);
1959 findCmdTerm (token, FALSE);
1960 deleteToken (name);
1963 static void parseDrop (tokenInfo *const token)
1966 * This deals with these formats
1967 * DROP TABLE|PROCEDURE|DOMAIN|DATATYPE name;
1969 * Just simply skip over these statements.
1970 * They are often confused with PROCEDURE prototypes
1971 * since the syntax is similar, this effectively deals with
1972 * the issue for all types.
1975 findCmdTerm (token, FALSE);
1978 static void parseVariable (tokenInfo *const token)
1980 tokenInfo *const name = newToken ();
1983 * This deals with these formats
1984 * create variable varname1 integer;
1985 * create variable @varname2 integer;
1986 * create variable "varname3" integer;
1987 * drop variable @varname3;
1990 readIdentifier (name);
1991 readToken (token);
1992 if ( (isType (name, TOKEN_IDENTIFIER) || isType (name, TOKEN_STRING))
1993 && !isType (token, TOKEN_SEMICOLON) )
1995 makeSqlTag (name, SQLTAG_VARIABLE);
1997 findCmdTerm (token, TRUE);
1999 deleteToken (name);
2002 static void parseSynonym (tokenInfo *const token)
2004 tokenInfo *const name = newToken ();
2007 * This deals with these formats
2008 * create variable varname1 integer;
2009 * create variable @varname2 integer;
2010 * create variable "varname3" integer;
2011 * drop variable @varname3;
2014 readIdentifier (name);
2015 readToken (token);
2016 if ( (isType (name, TOKEN_IDENTIFIER) || isType (name, TOKEN_STRING))
2017 && isKeyword (token, KEYWORD_for) )
2019 makeSqlTag (name, SQLTAG_SYNONYM);
2021 findCmdTerm (token, TRUE);
2023 deleteToken (name);
2026 static void parseView (tokenInfo *const token)
2028 tokenInfo *const name = newToken ();
2031 * This deals with these formats
2032 * create variable varname1 integer;
2033 * create variable @varname2 integer;
2034 * create variable "varname3" integer;
2035 * drop variable @varname3;
2038 readIdentifier (name);
2039 readToken (token);
2040 if (isType (token, TOKEN_PERIOD))
2042 readIdentifier (name);
2043 readToken (token);
2045 if (isType (token, TOKEN_OPEN_PAREN))
2047 skipArgumentList(token);
2050 while (!(isKeyword (token, KEYWORD_is) ||
2051 isType (token, TOKEN_SEMICOLON)
2054 readToken (token);
2057 if ( (isType (name, TOKEN_IDENTIFIER) || isType (name, TOKEN_STRING))
2058 && isKeyword (token, KEYWORD_is) )
2060 makeSqlTag (name, SQLTAG_VIEW);
2063 findCmdTerm (token, TRUE);
2065 deleteToken (name);
2068 static void parseMLTable (tokenInfo *const token)
2070 tokenInfo *const version = newToken ();
2071 tokenInfo *const table = newToken ();
2072 tokenInfo *const event = newToken ();
2075 * This deals with these formats
2076 * call dbo.ml_add_table_script( 'version', 'table_name', 'event',
2077 * 'some SQL statement'
2078 * );
2081 readToken (token);
2082 if (isType (token, TOKEN_OPEN_PAREN))
2084 readToken (version);
2085 readToken (token);
2086 while (!(isType (token, TOKEN_COMMA) ||
2087 isType (token, TOKEN_CLOSE_PAREN)
2090 readToken (token);
2093 if (isType (token, TOKEN_COMMA))
2095 readToken (table);
2096 readToken (token);
2097 while (!(isType (token, TOKEN_COMMA) ||
2098 isType (token, TOKEN_CLOSE_PAREN)
2101 readToken (token);
2104 if (isType (token, TOKEN_COMMA))
2106 readToken (event);
2108 if (isType (version, TOKEN_STRING) &&
2109 isType (table, TOKEN_STRING) &&
2110 isType (event, TOKEN_STRING))
2112 addToScope(version, table->string, SQLTAG_TABLE);
2113 addToScope(version, event->string, SQLTAG_EVENT);
2114 makeSqlTag (version, SQLTAG_MLTABLE);
2117 if (! isType (token, TOKEN_CLOSE_PAREN))
2118 findToken (token, TOKEN_CLOSE_PAREN);
2122 findCmdTerm (token, TRUE);
2124 deleteToken (version);
2125 deleteToken (table);
2126 deleteToken (event);
2129 static void parseMLConn (tokenInfo *const token)
2131 tokenInfo *const version = newToken ();
2132 tokenInfo *const event = newToken ();
2135 * This deals with these formats
2136 * call ml_add_connection_script( 'version', 'event',
2137 * 'some SQL statement'
2138 * );
2141 readToken (token);
2142 if (isType (token, TOKEN_OPEN_PAREN))
2144 readToken (version);
2145 readToken (token);
2146 while (!(isType (token, TOKEN_COMMA) ||
2147 isType (token, TOKEN_CLOSE_PAREN)
2150 readToken (token);
2153 if (isType (token, TOKEN_COMMA))
2155 readToken (event);
2157 if (isType (version, TOKEN_STRING) &&
2158 isType (event, TOKEN_STRING))
2160 addToScope(version, event->string, SQLTAG_EVENT);
2161 makeSqlTag (version, SQLTAG_MLCONN);
2164 if (! isType (token, TOKEN_CLOSE_PAREN))
2165 findToken (token, TOKEN_CLOSE_PAREN);
2169 findCmdTerm (token, TRUE);
2171 deleteToken (version);
2172 deleteToken (event);
2175 static void parseMLProp (tokenInfo *const token)
2177 tokenInfo *const component = newToken ();
2178 tokenInfo *const prop_set_name = newToken ();
2179 tokenInfo *const prop_name = newToken ();
2182 * This deals with these formats
2183 * ml_add_property (
2184 * 'comp_name',
2185 * 'prop_set_name',
2186 * 'prop_name',
2187 * 'prop_value'
2191 readToken (token);
2192 if (isType (token, TOKEN_OPEN_PAREN))
2194 readToken (component);
2195 readToken (token);
2196 while (!(isType (token, TOKEN_COMMA) ||
2197 isType (token, TOKEN_CLOSE_PAREN)
2200 readToken (token);
2203 if (isType (token, TOKEN_COMMA))
2205 readToken (prop_set_name);
2206 readToken (token);
2207 while (!(isType (token, TOKEN_COMMA) ||
2208 isType (token, TOKEN_CLOSE_PAREN)
2211 readToken (token);
2214 if (isType (token, TOKEN_COMMA))
2216 readToken (prop_name);
2218 if (isType (component, TOKEN_STRING) &&
2219 isType (prop_set_name, TOKEN_STRING) &&
2220 isType (prop_name, TOKEN_STRING))
2222 addToScope(component, prop_set_name->string, SQLTAG_MLPROP /* FIXME */);
2223 addToScope(component, prop_name->string, SQLTAG_MLPROP /* FIXME */);
2224 makeSqlTag (component, SQLTAG_MLPROP);
2227 if (! isType (token, TOKEN_CLOSE_PAREN))
2228 findToken (token, TOKEN_CLOSE_PAREN);
2232 findCmdTerm (token, TRUE);
2234 deleteToken (component);
2235 deleteToken (prop_set_name);
2236 deleteToken (prop_name);
2239 static void parseComment (tokenInfo *const token)
2242 * This deals with this statement:
2243 * COMMENT TO PRESERVE FORMAT ON PROCEDURE "DBA"."test" IS
2244 * {create PROCEDURE DBA."test"()
2245 * BEGIN
2246 * signal dave;
2247 * END
2250 * The comment can contain anything between the CURLY
2251 * braces
2252 * COMMENT ON USER "admin" IS
2253 * 'Administration Group'
2255 * Or it could be a simple string with no curly braces
2257 while (! isKeyword (token, KEYWORD_is))
2259 readToken (token);
2261 readToken (token);
2262 if (isType(token, TOKEN_OPEN_CURLY))
2264 findToken (token, TOKEN_CLOSE_CURLY);
2267 findCmdTerm (token, TRUE);
2271 static void parseKeywords (tokenInfo *const token)
2273 switch (token->keyword)
2275 case KEYWORD_begin: parseBlock (token, FALSE); break;
2276 case KEYWORD_comment: parseComment (token); break;
2277 case KEYWORD_cursor: parseSimple (token, SQLTAG_CURSOR); break;
2278 case KEYWORD_datatype: parseDomain (token); break;
2279 case KEYWORD_declare: parseBlock (token, FALSE); break;
2280 case KEYWORD_domain: parseDomain (token); break;
2281 case KEYWORD_drop: parseDrop (token); break;
2282 case KEYWORD_event: parseEvent (token); break;
2283 case KEYWORD_function: parseSubProgram (token); break;
2284 case KEYWORD_if: parseStatements (token, FALSE); break;
2285 case KEYWORD_index: parseIndex (token); break;
2286 case KEYWORD_ml_table: parseMLTable (token); break;
2287 case KEYWORD_ml_table_lang: parseMLTable (token); break;
2288 case KEYWORD_ml_table_dnet: parseMLTable (token); break;
2289 case KEYWORD_ml_table_java: parseMLTable (token); break;
2290 case KEYWORD_ml_table_chk: parseMLTable (token); break;
2291 case KEYWORD_ml_conn: parseMLConn (token); break;
2292 case KEYWORD_ml_conn_lang: parseMLConn (token); break;
2293 case KEYWORD_ml_conn_dnet: parseMLConn (token); break;
2294 case KEYWORD_ml_conn_java: parseMLConn (token); break;
2295 case KEYWORD_ml_conn_chk: parseMLConn (token); break;
2296 case KEYWORD_ml_prop: parseMLProp (token); break;
2297 case KEYWORD_package: parsePackage (token); break;
2298 case KEYWORD_procedure: parseSubProgram (token); break;
2299 case KEYWORD_publication: parsePublication (token); break;
2300 case KEYWORD_service: parseService (token); break;
2301 case KEYWORD_subtype: parseSimple (token, SQLTAG_SUBTYPE); break;
2302 case KEYWORD_synonym: parseSynonym (token); break;
2303 case KEYWORD_table: parseTable (token); break;
2304 case KEYWORD_trigger: parseTrigger (token); break;
2305 case KEYWORD_type: parseType (token); break;
2306 case KEYWORD_variable: parseVariable (token); break;
2307 case KEYWORD_view: parseView (token); break;
2308 default: break;
2312 static void parseSqlFile (tokenInfo *const token)
2316 readToken (token);
2318 if (isType (token, TOKEN_BLOCK_LABEL_BEGIN))
2319 parseLabel (token);
2320 else
2321 parseKeywords (token);
2322 } while (! isKeyword (token, KEYWORD_end));
2325 static void initialize (const langType language)
2327 Assert (ARRAY_SIZE (SqlKinds) == SQLTAG_COUNT);
2328 Lang_sql = language;
2331 static void findSqlTags (void)
2333 tokenInfo *const token = newToken ();
2334 exception_t exception = (exception_t) (setjmp (Exception));
2336 while (exception == ExceptionNone)
2337 parseSqlFile (token);
2339 deleteToken (token);
2342 extern parserDefinition* SqlParser (void)
2344 static const char *const extensions [] = { "sql", NULL };
2345 parserDefinition* def = parserNewFull ("SQL", KIND_FILE_ALT);
2346 def->kinds = SqlKinds;
2347 def->kindCount = ARRAY_SIZE (SqlKinds);
2348 def->extensions = extensions;
2349 def->parser = findSqlTags;
2350 def->initialize = initialize;
2351 def->keywordTable = SqlKeywordTable;
2352 def->keywordCount = ARRAY_SIZE (SqlKeywordTable);
2353 return def;
2356 /* vi:set tabstop=4 shiftwidth=4 noexpandtab: */