Rename fileUngetc() to ungetcToInputFile()
[geany-mirror.git] / ctags / parsers / sql.c
blob1febae6791692c8c6032aea95fe81ec045d05309
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 "main.h"
27 #include "read.h"
28 #include "routines.h"
29 #include "vstring.h"
32 * On-line "Oracle Database PL/SQL Language Reference":
33 * http://download.oracle.com/docs/cd/B28359_01/appdev.111/b28370/toc.htm
35 * Sample PL/SQL code is available from:
36 * http://www.orafaq.com/faqscrpt.htm#GENPLSQL
38 * On-line SQL Anywhere Documentation
39 * http://www.ianywhere.com/developer/product_manuals/sqlanywhere/index.html
43 * MACROS
45 #define isType(token,t) (boolean) ((token)->type == (t))
46 #define isKeyword(token,k) (boolean) ((token)->keyword == (k))
49 * DATA DECLARATIONS
52 typedef enum eException { ExceptionNone, ExceptionEOF } exception_t;
55 * Used to specify type of keyword.
57 typedef enum eKeywordId {
58 KEYWORD_NONE = -1,
59 KEYWORD_is,
60 KEYWORD_begin,
61 KEYWORD_body,
62 KEYWORD_cursor,
63 KEYWORD_declare,
64 KEYWORD_end,
65 KEYWORD_function,
66 KEYWORD_if,
67 KEYWORD_else,
68 KEYWORD_elseif,
69 KEYWORD_endif,
70 KEYWORD_loop,
71 KEYWORD_while,
72 KEYWORD_case,
73 KEYWORD_for,
74 KEYWORD_do,
75 KEYWORD_call,
76 KEYWORD_package,
77 KEYWORD_pragma,
78 KEYWORD_procedure,
79 KEYWORD_record,
80 KEYWORD_object,
81 KEYWORD_ref,
82 KEYWORD_rem,
83 KEYWORD_return,
84 KEYWORD_returns,
85 KEYWORD_subtype,
86 KEYWORD_table,
87 KEYWORD_trigger,
88 KEYWORD_type,
89 KEYWORD_index,
90 KEYWORD_event,
91 KEYWORD_publication,
92 KEYWORD_service,
93 KEYWORD_domain,
94 KEYWORD_datatype,
95 KEYWORD_result,
96 KEYWORD_url,
97 KEYWORD_internal,
98 KEYWORD_external,
99 KEYWORD_when,
100 KEYWORD_then,
101 KEYWORD_variable,
102 KEYWORD_exception,
103 KEYWORD_at,
104 KEYWORD_on,
105 KEYWORD_primary,
106 KEYWORD_references,
107 KEYWORD_unique,
108 KEYWORD_check,
109 KEYWORD_constraint,
110 KEYWORD_foreign,
111 KEYWORD_ml_table,
112 KEYWORD_ml_table_lang,
113 KEYWORD_ml_table_dnet,
114 KEYWORD_ml_table_java,
115 KEYWORD_ml_table_chk,
116 KEYWORD_ml_conn,
117 KEYWORD_ml_conn_lang,
118 KEYWORD_ml_conn_dnet,
119 KEYWORD_ml_conn_java,
120 KEYWORD_ml_conn_chk,
121 KEYWORD_ml_prop,
122 KEYWORD_local,
123 KEYWORD_temporary,
124 KEYWORD_drop,
125 KEYWORD_view,
126 KEYWORD_synonym,
127 KEYWORD_handler,
128 KEYWORD_comment,
129 KEYWORD_create,
130 KEYWORD_go
131 } keywordId;
133 typedef enum eTokenType {
134 TOKEN_UNDEFINED,
135 TOKEN_BLOCK_LABEL_BEGIN,
136 TOKEN_BLOCK_LABEL_END,
137 TOKEN_CHARACTER,
138 TOKEN_CLOSE_PAREN,
139 TOKEN_COLON,
140 TOKEN_SEMICOLON,
141 TOKEN_COMMA,
142 TOKEN_IDENTIFIER,
143 TOKEN_KEYWORD,
144 TOKEN_OPEN_PAREN,
145 TOKEN_OPERATOR,
146 TOKEN_OTHER,
147 TOKEN_STRING,
148 TOKEN_PERIOD,
149 TOKEN_OPEN_CURLY,
150 TOKEN_CLOSE_CURLY,
151 TOKEN_OPEN_SQUARE,
152 TOKEN_CLOSE_SQUARE,
153 TOKEN_TILDE,
154 TOKEN_FORWARD_SLASH,
155 TOKEN_EQUAL
156 } tokenType;
158 typedef struct sTokenInfoSQL {
159 tokenType type;
160 keywordId keyword;
161 vString * string;
162 vString * scope;
163 int scopeKind;
164 int begin_end_nest_lvl;
165 unsigned long lineNumber;
166 MIOPos filePosition;
167 } tokenInfo;
170 * DATA DEFINITIONS
173 static langType Lang_sql;
175 static jmp_buf Exception;
177 typedef enum {
178 SQLTAG_CURSOR,
179 SQLTAG_PROTOTYPE,
180 SQLTAG_FUNCTION,
181 SQLTAG_FIELD,
182 SQLTAG_LOCAL_VARIABLE,
183 SQLTAG_BLOCK_LABEL,
184 SQLTAG_PACKAGE,
185 SQLTAG_PROCEDURE,
186 SQLTAG_RECORD,
187 SQLTAG_SUBTYPE,
188 SQLTAG_TABLE,
189 SQLTAG_TRIGGER,
190 SQLTAG_VARIABLE,
191 SQLTAG_INDEX,
192 SQLTAG_EVENT,
193 SQLTAG_PUBLICATION,
194 SQLTAG_SERVICE,
195 SQLTAG_DOMAIN,
196 SQLTAG_VIEW,
197 SQLTAG_SYNONYM,
198 SQLTAG_MLTABLE,
199 SQLTAG_MLCONN,
200 SQLTAG_MLPROP,
201 SQLTAG_COUNT
202 } sqlKind;
204 static kindOption SqlKinds [] = {
205 { TRUE, 'c', "cursor", "cursors" },
206 { FALSE, 'd', "prototype", "prototypes" },
207 { TRUE, 'f', "function", "functions" },
208 { TRUE, 'F', "field", "record fields" },
209 { FALSE, 'l', "local", "local variables" },
210 { TRUE, 'L', "label", "block label" },
211 { TRUE, 'P', "package", "packages" },
212 { TRUE, 'p', "procedure", "procedures" },
213 { FALSE, 'r', "record", "records" },
214 { TRUE, 's', "subtype", "subtypes" },
215 { TRUE, 't', "table", "tables" },
216 { TRUE, 'T', "trigger", "triggers" },
217 { TRUE, 'v', "variable", "variables" },
218 { TRUE, 'i', "index", "indexes" },
219 { TRUE, 'e', "event", "events" },
220 { TRUE, 'U', "publication", "publications" },
221 { TRUE, 'R', "service", "services" },
222 { TRUE, 'D', "domain", "domains" },
223 { TRUE, 'V', "view", "views" },
224 { TRUE, 'n', "synonym", "synonyms" },
225 { TRUE, 'x', "mltable", "MobiLink Table Scripts" },
226 { TRUE, 'y', "mlconn", "MobiLink Conn Scripts" },
227 { TRUE, 'z', "mlprop", "MobiLink Properties " }
230 static const keywordTable SqlKeywordTable [] = {
231 /* keyword keyword ID */
232 { "as", KEYWORD_is },
233 { "is", KEYWORD_is },
234 { "begin", KEYWORD_begin },
235 { "body", KEYWORD_body },
236 { "cursor", KEYWORD_cursor },
237 { "declare", KEYWORD_declare },
238 { "end", KEYWORD_end },
239 { "function", KEYWORD_function },
240 { "if", KEYWORD_if },
241 { "else", KEYWORD_else },
242 { "elseif", KEYWORD_elseif },
243 { "endif", KEYWORD_endif },
244 { "loop", KEYWORD_loop },
245 { "while", KEYWORD_while },
246 { "case", KEYWORD_case },
247 { "for", KEYWORD_for },
248 { "do", KEYWORD_do },
249 { "call", KEYWORD_call },
250 { "package", KEYWORD_package },
251 { "pragma", KEYWORD_pragma },
252 { "procedure", KEYWORD_procedure },
253 { "record", KEYWORD_record },
254 { "object", KEYWORD_object },
255 { "ref", KEYWORD_ref },
256 { "rem", KEYWORD_rem },
257 { "return", KEYWORD_return },
258 { "returns", KEYWORD_returns },
259 { "subtype", KEYWORD_subtype },
260 { "table", KEYWORD_table },
261 { "trigger", KEYWORD_trigger },
262 { "type", KEYWORD_type },
263 { "index", KEYWORD_index },
264 { "event", KEYWORD_event },
265 { "publication", KEYWORD_publication },
266 { "service", KEYWORD_service },
267 { "domain", KEYWORD_domain },
268 { "datatype", KEYWORD_datatype },
269 { "result", KEYWORD_result },
270 { "url", KEYWORD_url },
271 { "internal", KEYWORD_internal },
272 { "external", KEYWORD_external },
273 { "when", KEYWORD_when },
274 { "then", KEYWORD_then },
275 { "variable", KEYWORD_variable },
276 { "exception", KEYWORD_exception },
277 { "at", KEYWORD_at },
278 { "on", KEYWORD_on },
279 { "primary", KEYWORD_primary },
280 { "references", KEYWORD_references },
281 { "unique", KEYWORD_unique },
282 { "check", KEYWORD_check },
283 { "constraint", KEYWORD_constraint },
284 { "foreign", KEYWORD_foreign },
285 { "ml_add_table_script", KEYWORD_ml_table },
286 { "ml_add_lang_table_script", KEYWORD_ml_table_lang },
287 { "ml_add_dnet_table_script", KEYWORD_ml_table_dnet },
288 { "ml_add_java_table_script", KEYWORD_ml_table_java },
289 { "ml_add_lang_table_script_chk", KEYWORD_ml_table_chk },
290 { "ml_add_connection_script", KEYWORD_ml_conn },
291 { "ml_add_lang_connection_script", KEYWORD_ml_conn_lang },
292 { "ml_add_dnet_connection_script", KEYWORD_ml_conn_dnet },
293 { "ml_add_java_connection_script", KEYWORD_ml_conn_java },
294 { "ml_add_lang_conn_script_chk", KEYWORD_ml_conn_chk },
295 { "ml_add_property", KEYWORD_ml_prop },
296 { "local", KEYWORD_local },
297 { "temporary", KEYWORD_temporary },
298 { "drop", KEYWORD_drop },
299 { "view", KEYWORD_view },
300 { "synonym", KEYWORD_synonym },
301 { "handler", KEYWORD_handler },
302 { "comment", KEYWORD_comment },
303 { "create", KEYWORD_create },
304 { "go", KEYWORD_go }
308 * FUNCTION DECLARATIONS
311 /* Recursive calls */
312 static void parseBlock (tokenInfo *const token, const boolean local);
313 static void parseDeclare (tokenInfo *const token, const boolean local);
314 static void parseKeywords (tokenInfo *const token);
315 static void parseSqlFile (tokenInfo *const token);
318 * FUNCTION DEFINITIONS
321 static boolean isIdentChar1 (const int c)
324 * Other databases are less restrictive on the first character of
325 * an identifier.
326 * isIdentChar1 is used to identify the first character of an
327 * identifier, so we are removing some restrictions.
329 return (boolean)
330 (isalpha (c) || c == '@' || c == '_' );
333 static boolean isIdentChar (const int c)
335 return (boolean)
336 (isalpha (c) || isdigit (c) || c == '$' ||
337 c == '@' || c == '_' || c == '#');
340 static boolean isCmdTerm (tokenInfo *const token)
342 DebugStatement (
343 debugPrintf (DEBUG_PARSE
344 , "\n isCmdTerm: token same tt:%d tk:%d\n"
345 , token->type
346 , token->keyword
351 * Based on the various customer sites I have been at
352 * the most common command delimiters are
356 * go
357 * This routine will check for any of these, more
358 * can easily be added by modifying readToken and
359 * either adding the character to:
360 * enum eTokenType
361 * enum eTokenType
363 return ( isType (token, TOKEN_SEMICOLON) ||
364 isType (token, TOKEN_TILDE) ||
365 isType (token, TOKEN_FORWARD_SLASH) ||
366 isKeyword (token, KEYWORD_go)
370 static boolean isMatchedEnd(tokenInfo *const token, int nest_lvl)
372 boolean terminated = FALSE;
374 * Since different forms of SQL allow the use of
375 * BEGIN
376 * ...
377 * END
378 * blocks, some statements may not be terminated using
379 * the standard delimiters:
383 * go
384 * This routine will check to see if we encounter and END
385 * for the matching nest level of BEGIN ... END statements.
386 * If we find one, then we can assume, the statement was terminated
387 * since we have fallen through to the END statement of the BEGIN
388 * block.
390 if ( nest_lvl > 0 && isKeyword (token, KEYWORD_end) )
392 if ( token->begin_end_nest_lvl == nest_lvl )
393 terminated = TRUE;
396 return terminated;
399 static tokenInfo *newToken (void)
401 tokenInfo *const token = xMalloc (1, tokenInfo);
403 token->type = TOKEN_UNDEFINED;
404 token->keyword = KEYWORD_NONE;
405 token->string = vStringNew ();
406 token->scope = vStringNew ();
407 token->scopeKind = SQLTAG_COUNT;
408 token->begin_end_nest_lvl = 0;
409 token->lineNumber = getSourceLineNumber ();
410 token->filePosition = getInputFilePosition ();
412 return token;
415 static void deleteToken (tokenInfo *const token)
417 vStringDelete (token->string);
418 vStringDelete (token->scope);
419 eFree (token);
422 static int analyzeToken (vString *const name, langType language)
424 vString *keyword = vStringNew ();
425 int result;
426 vStringCopyToLower (keyword, name);
427 result = lookupKeyword (vStringValue (keyword), language);
428 vStringDelete (keyword);
429 return result;
433 * Tag generation functions
436 static void makeSqlTag (tokenInfo *const token, const sqlKind kind)
438 if (SqlKinds [kind].enabled)
440 const char *const name = vStringValue (token->string);
441 tagEntryInfo e;
442 initTagEntry (&e, name);
444 e.lineNumber = token->lineNumber;
445 e.filePosition = token->filePosition;
446 e.kindName = SqlKinds [kind].name;
447 e.kind = SqlKinds [kind].letter;
449 if (vStringLength (token->scope) > 0)
451 Assert (token->scopeKind < SQLTAG_COUNT);
452 e.extensionFields.scope[0] = SqlKinds [token->scopeKind].name;
453 e.extensionFields.scope[1] = vStringValue (token->scope);
456 makeTagEntry (&e);
461 * Parsing functions
464 static void parseString (vString *const string, const int delimiter)
466 boolean end = FALSE;
467 while (! end)
469 int c = getcFromInputFile ();
470 if (c == EOF)
471 end = TRUE;
473 else if (c == '\\')
475 c = getcFromInputFile(); // This maybe a ' or ". //
476 vStringPut(string, c);
479 else if (c == delimiter)
480 end = TRUE;
481 else
482 vStringPut (string, c);
484 vStringTerminate (string);
487 /* Read a C identifier beginning with "firstChar" and places it into "name".
489 static void parseIdentifier (vString *const string, const int firstChar)
491 int c = firstChar;
492 Assert (isIdentChar1 (c));
495 vStringPut (string, c);
496 c = getcFromInputFile ();
497 } while (isIdentChar (c));
498 vStringTerminate (string);
499 if (!isspace (c))
500 ungetcToInputFile (c); /* unget non-identifier character */
503 static void readToken (tokenInfo *const token)
505 int c;
507 token->type = TOKEN_UNDEFINED;
508 token->keyword = KEYWORD_NONE;
509 vStringClear (token->string);
511 getNextChar:
514 c = getcFromInputFile ();
515 token->lineNumber = getSourceLineNumber ();
516 token->filePosition = getInputFilePosition ();
518 * Added " to the list of ignores, not sure what this
519 * might break but it gets by this issue:
520 * create table "t1" (...)
522 * Darren, the code passes all my tests for both
523 * Oracle and SQL Anywhere, but maybe you can tell me
524 * what this may effect.
527 while (c == '\t' || c == ' ' || c == '\n');
529 switch (c)
531 case EOF: longjmp (Exception, (int)ExceptionEOF); break;
532 case '(': token->type = TOKEN_OPEN_PAREN; break;
533 case ')': token->type = TOKEN_CLOSE_PAREN; break;
534 case ':': token->type = TOKEN_COLON; break;
535 case ';': token->type = TOKEN_SEMICOLON; break;
536 case '.': token->type = TOKEN_PERIOD; break;
537 case ',': token->type = TOKEN_COMMA; break;
538 case '{': token->type = TOKEN_OPEN_CURLY; break;
539 case '}': token->type = TOKEN_CLOSE_CURLY; break;
540 case '~': token->type = TOKEN_TILDE; break;
541 case '[': token->type = TOKEN_OPEN_SQUARE; break;
542 case ']': token->type = TOKEN_CLOSE_SQUARE; break;
543 case '=': token->type = TOKEN_EQUAL; break;
545 case '\'':
546 case '"':
547 token->type = TOKEN_STRING;
548 parseString (token->string, c);
549 token->lineNumber = getSourceLineNumber ();
550 token->filePosition = getInputFilePosition ();
551 break;
553 case '-':
554 c = getcFromInputFile ();
555 if (c == '-') /* -- is this the start of a comment? */
557 fileSkipToCharacter ('\n');
558 goto getNextChar;
560 else
562 if (!isspace (c))
563 ungetcToInputFile (c);
564 token->type = TOKEN_OPERATOR;
566 break;
568 case '<':
569 case '>':
571 const int initial = c;
572 int d = getcFromInputFile ();
573 if (d == initial)
575 if (initial == '<')
576 token->type = TOKEN_BLOCK_LABEL_BEGIN;
577 else
578 token->type = TOKEN_BLOCK_LABEL_END;
580 else
582 ungetcToInputFile (d);
583 token->type = TOKEN_UNDEFINED;
585 break;
588 case '\\':
589 c = getcFromInputFile ();
590 if (c != '\\' && c != '"' && c != '\'' && !isspace (c))
591 ungetcToInputFile (c);
592 token->type = TOKEN_CHARACTER;
593 token->lineNumber = getSourceLineNumber ();
594 token->filePosition = getInputFilePosition ();
595 break;
597 case '/':
599 int d = getcFromInputFile ();
600 if ( (d != '*') && /* is this the start of a comment? */
601 (d != '/') ) /* is a one line comment? */
603 token->type = TOKEN_FORWARD_SLASH;
604 ungetcToInputFile (d);
606 else
608 if (d == '*')
612 fileSkipToCharacter ('*');
613 c = getcFromInputFile ();
614 if (c == '/')
615 break;
616 else
617 ungetcToInputFile (c);
618 } while (c != EOF && c != '\0');
619 goto getNextChar;
621 else if (d == '/') /* is this the start of a comment? */
623 fileSkipToCharacter ('\n');
624 goto getNextChar;
627 break;
630 default:
631 if (! isIdentChar1 (c))
632 token->type = TOKEN_UNDEFINED;
633 else
635 parseIdentifier (token->string, c);
636 token->lineNumber = getSourceLineNumber ();
637 token->filePosition = getInputFilePosition ();
638 token->keyword = analyzeToken (token->string, Lang_sql);
639 if (isKeyword (token, KEYWORD_rem))
641 vStringClear (token->string);
642 fileSkipToCharacter ('\n');
643 goto getNextChar;
645 else if (isKeyword (token, KEYWORD_NONE))
646 token->type = TOKEN_IDENTIFIER;
647 else
648 token->type = TOKEN_KEYWORD;
650 break;
655 * reads an identifier, possibly quoted:
656 * identifier
657 * "identifier"
658 * [identifier]
660 static void readIdentifier (tokenInfo *const token)
662 readToken (token);
663 if (isType (token, TOKEN_OPEN_SQUARE))
665 tokenInfo *const close_square = newToken ();
667 readToken (token);
668 /* eat close swuare */
669 readToken (close_square);
670 deleteToken (close_square);
675 * Token parsing functions
679 * static void addContext (tokenInfo* const parent, const tokenInfo* const child)
681 * if (vStringLength (parent->string) > 0)
683 * vStringCatS (parent->string, ".");
685 * vStringCatS (parent->string, vStringValue(child->string));
686 * vStringTerminate(parent->string);
690 static void addToScope (tokenInfo* const token, vString* const extra, sqlKind kind)
692 if (vStringLength (token->scope) > 0)
694 vStringCatS (token->scope, ".");
696 vStringCatS (token->scope, vStringValue(extra));
697 vStringTerminate(token->scope);
698 token->scopeKind = kind;
702 * Scanning functions
705 static void findToken (tokenInfo *const token, const tokenType type)
707 while (! isType (token, type))
709 readToken (token);
713 static void findCmdTerm (tokenInfo *const token, const boolean check_first)
715 int begin_end_nest_lvl = token->begin_end_nest_lvl;
717 if ( check_first )
719 if ( isCmdTerm(token) )
720 return;
724 readToken (token);
725 } while ( !isCmdTerm(token) && !isMatchedEnd(token, begin_end_nest_lvl) );
728 static void skipToMatched(tokenInfo *const token)
730 int nest_level = 0;
731 tokenType open_token;
732 tokenType close_token;
734 switch (token->type)
736 case TOKEN_OPEN_PAREN:
737 open_token = TOKEN_OPEN_PAREN;
738 close_token = TOKEN_CLOSE_PAREN;
739 break;
740 case TOKEN_OPEN_CURLY:
741 open_token = TOKEN_OPEN_CURLY;
742 close_token = TOKEN_CLOSE_CURLY;
743 break;
744 case TOKEN_OPEN_SQUARE:
745 open_token = TOKEN_OPEN_SQUARE;
746 close_token = TOKEN_CLOSE_SQUARE;
747 break;
748 default:
749 return;
753 * This routine will skip to a matching closing token.
754 * It will also handle nested tokens like the (, ) below.
755 * ( name varchar(30), text binary(10) )
758 if (isType (token, open_token))
760 nest_level++;
761 while (! (isType (token, close_token) && (nest_level == 0)))
763 readToken (token);
764 if (isType (token, open_token))
766 nest_level++;
768 if (isType (token, close_token))
770 if (nest_level > 0)
772 nest_level--;
776 readToken (token);
780 static void copyToken (tokenInfo *const dest, tokenInfo *const src)
782 dest->lineNumber = src->lineNumber;
783 dest->filePosition = src->filePosition;
784 dest->type = src->type;
785 dest->keyword = src->keyword;
786 vStringCopy(dest->string, src->string);
787 vStringCopy(dest->scope, src->scope);
788 dest->scopeKind = src->scopeKind;
791 static void skipArgumentList (tokenInfo *const token)
794 * Other databases can have arguments with fully declared
795 * datatypes:
796 * ( name varchar(30), text binary(10) )
797 * So we must check for nested open and closing parantheses
800 if (isType (token, TOKEN_OPEN_PAREN)) /* arguments? */
802 skipToMatched (token);
806 static void parseSubProgram (tokenInfo *const token)
808 tokenInfo *const name = newToken ();
809 vString * saveScope = vStringNew ();
810 sqlKind saveScopeKind;
813 * This must handle both prototypes and the body of
814 * the procedures.
816 * Prototype:
817 * FUNCTION func_name RETURN integer;
818 * PROCEDURE proc_name( parameters );
819 * Procedure
820 * FUNCTION GET_ML_USERNAME RETURN VARCHAR2
821 * IS
822 * BEGIN
823 * RETURN v_sync_user_id;
824 * END GET_ML_USERNAME;
826 * PROCEDURE proc_name( parameters )
827 * IS
828 * BEGIN
829 * END;
830 * CREATE PROCEDURE proc_name( parameters )
831 * EXTERNAL NAME ... ;
832 * CREATE PROCEDURE proc_name( parameters )
833 * BEGIN
834 * END;
836 * CREATE FUNCTION f_GetClassName(
837 * IN @object VARCHAR(128)
838 * ,IN @code VARCHAR(128)
840 * RETURNS VARCHAR(200)
841 * DETERMINISTIC
842 * BEGIN
844 * IF( @object = 'user_state' ) THEN
845 * SET something = something;
846 * END IF;
848 * RETURN @name;
849 * END;
851 * Note, a Package adds scope to the items within.
852 * create or replace package demo_pkg is
853 * test_var number;
854 * function test_func return varchar2;
855 * function more.test_func2 return varchar2;
856 * end demo_pkg;
857 * So the tags generated here, contain the package name:
858 * demo_pkg.test_var
859 * demo_pkg.test_func
860 * demo_pkg.more.test_func2
862 const sqlKind kind = isKeyword (token, KEYWORD_function) ?
863 SQLTAG_FUNCTION : SQLTAG_PROCEDURE;
864 Assert (isKeyword (token, KEYWORD_function) ||
865 isKeyword (token, KEYWORD_procedure));
867 vStringCopy(saveScope, token->scope);
868 saveScopeKind = token->scopeKind;
869 readToken (token);
870 copyToken (name, token);
871 readToken (token);
873 if (isType (token, TOKEN_PERIOD))
876 * If this is an Oracle package, then the token->scope should
877 * already be set. If this is the case, also add this value to the
878 * scope.
879 * If this is not an Oracle package, chances are the scope should be
880 * blank and the value just read is the OWNER or CREATOR of the
881 * function and should not be considered part of the scope.
883 if ( vStringLength(saveScope) > 0 )
885 addToScope(token, name->string, kind);
887 readToken (token);
888 copyToken (name, token);
889 readToken (token);
891 if (isType (token, TOKEN_OPEN_PAREN))
893 /* Reads to the next token after the TOKEN_CLOSE_PAREN */
894 skipArgumentList(token);
897 if (kind == SQLTAG_FUNCTION)
899 if (isKeyword (token, KEYWORD_return) || isKeyword (token, KEYWORD_returns))
901 /* Read datatype */
902 readToken (token);
904 * Read token after which could be the
905 * command terminator if a prototype
906 * or an open parantheses
908 readToken (token);
909 if (isType (token, TOKEN_OPEN_PAREN))
911 /* Reads to the next token after the TOKEN_CLOSE_PAREN */
912 skipArgumentList(token);
916 if( isCmdTerm (token) )
918 makeSqlTag (name, SQLTAG_PROTOTYPE);
920 else
922 while (!(isKeyword (token, KEYWORD_is) ||
923 isKeyword (token, KEYWORD_begin) ||
924 isKeyword (token, KEYWORD_at) ||
925 isKeyword (token, KEYWORD_internal) ||
926 isKeyword (token, KEYWORD_external) ||
927 isKeyword (token, KEYWORD_url) ||
928 isType (token, TOKEN_EQUAL) ||
929 isCmdTerm (token)
933 if ( isKeyword (token, KEYWORD_result) )
935 readToken (token);
936 if (isType (token, TOKEN_OPEN_PAREN))
938 /* Reads to the next token after the TOKEN_CLOSE_PAREN */
939 skipArgumentList(token);
941 } else {
942 readToken (token);
945 if (isKeyword (token, KEYWORD_at) ||
946 isKeyword (token, KEYWORD_url) ||
947 isKeyword (token, KEYWORD_internal) ||
948 isKeyword (token, KEYWORD_external) )
950 addToScope(token, name->string, kind);
951 if (isType (name, TOKEN_IDENTIFIER) ||
952 isType (name, TOKEN_STRING) ||
953 !isKeyword (token, KEYWORD_NONE)
955 makeSqlTag (name, kind);
957 vStringClear (token->scope);
958 token->scopeKind = SQLTAG_COUNT;
960 if ( isType (token, TOKEN_EQUAL) )
961 readToken (token);
963 if ( isKeyword (token, KEYWORD_declare) )
964 parseDeclare (token, FALSE);
966 if (isKeyword (token, KEYWORD_is) ||
967 isKeyword (token, KEYWORD_begin) )
969 addToScope(token, name->string, kind);
970 if (isType (name, TOKEN_IDENTIFIER) ||
971 isType (name, TOKEN_STRING) ||
972 !isKeyword (token, KEYWORD_NONE)
974 makeSqlTag (name, kind);
976 parseBlock (token, TRUE);
977 vStringClear (token->scope);
978 token->scopeKind = SQLTAG_COUNT;
981 vStringCopy(token->scope, saveScope);
982 token->scopeKind = saveScopeKind;
983 deleteToken (name);
984 vStringDelete(saveScope);
987 static void parseRecord (tokenInfo *const token)
990 * Make it a bit forgiving, this is called from
991 * multiple functions, parseTable, parseType
993 if (!isType (token, TOKEN_OPEN_PAREN))
994 readToken (token);
996 Assert (isType (token, TOKEN_OPEN_PAREN));
999 if ( isType (token, TOKEN_COMMA) || isType (token, TOKEN_OPEN_PAREN) )
1000 readToken (token);
1003 * Create table statements can end with various constraints
1004 * which must be excluded from the SQLTAG_FIELD.
1005 * create table t1 (
1006 * c1 integer,
1007 * c2 char(30),
1008 * c3 numeric(10,5),
1009 * c4 integer,
1010 * constraint whatever,
1011 * primary key(c1),
1012 * foreign key (),
1013 * check ()
1016 if (! (isKeyword(token, KEYWORD_primary) ||
1017 isKeyword(token, KEYWORD_references) ||
1018 isKeyword(token, KEYWORD_unique) ||
1019 isKeyword(token, KEYWORD_check) ||
1020 isKeyword(token, KEYWORD_constraint) ||
1021 isKeyword(token, KEYWORD_foreign) ) )
1023 if (isType (token, TOKEN_IDENTIFIER) ||
1024 isType (token, TOKEN_STRING))
1025 makeSqlTag (token, SQLTAG_FIELD);
1028 while (!(isType (token, TOKEN_COMMA) ||
1029 isType (token, TOKEN_CLOSE_PAREN) ||
1030 isType (token, TOKEN_OPEN_PAREN)
1033 readToken (token);
1035 * A table structure can look like this:
1036 * create table t1 (
1037 * c1 integer,
1038 * c2 char(30),
1039 * c3 numeric(10,5),
1040 * c4 integer
1042 * We can't just look for a COMMA or CLOSE_PAREN
1043 * since that will not deal with the numeric(10,5)
1044 * case. So we need to skip the argument list
1045 * when we find an open paren.
1047 if (isType (token, TOKEN_OPEN_PAREN))
1049 /* Reads to the next token after the TOKEN_CLOSE_PAREN */
1050 skipArgumentList(token);
1053 } while (! isType (token, TOKEN_CLOSE_PAREN));
1056 static void parseType (tokenInfo *const token)
1058 tokenInfo *const name = newToken ();
1059 vString * saveScope = vStringNew ();
1060 sqlKind saveScopeKind;
1062 vStringCopy(saveScope, token->scope);
1063 /* If a scope has been set, add it to the name */
1064 addToScope (name, token->scope, token->scopeKind);
1065 saveScopeKind = token->scopeKind;
1066 readToken (name);
1067 if (isType (name, TOKEN_IDENTIFIER))
1069 readToken (token);
1070 if (isKeyword (token, KEYWORD_is))
1072 readToken (token);
1073 switch (token->keyword)
1075 case KEYWORD_record:
1076 case KEYWORD_object:
1077 makeSqlTag (name, SQLTAG_RECORD);
1078 addToScope (token, name->string, SQLTAG_RECORD);
1079 parseRecord (token);
1080 break;
1082 case KEYWORD_table:
1083 makeSqlTag (name, SQLTAG_TABLE);
1084 break;
1086 case KEYWORD_ref:
1087 readToken (token);
1088 if (isKeyword (token, KEYWORD_cursor))
1089 makeSqlTag (name, SQLTAG_CURSOR);
1090 break;
1092 default: break;
1094 vStringClear (token->scope);
1095 token->scopeKind = SQLTAG_COUNT;
1098 vStringCopy(token->scope, saveScope);
1099 token->scopeKind = saveScopeKind;
1100 deleteToken (name);
1101 vStringDelete(saveScope);
1104 static void parseSimple (tokenInfo *const token, const sqlKind kind)
1106 /* This will simply make the tagname from the first word found */
1107 readToken (token);
1108 if (isType (token, TOKEN_IDENTIFIER) ||
1109 isType (token, TOKEN_STRING))
1110 makeSqlTag (token, kind);
1113 static void parseDeclare (tokenInfo *const token, const boolean local)
1116 * PL/SQL declares are of this format:
1117 * IS|AS
1118 * [declare]
1119 * CURSOR curname ...
1120 * varname1 datatype;
1121 * varname2 datatype;
1122 * varname3 datatype;
1123 * begin
1126 if (isKeyword (token, KEYWORD_declare))
1127 readToken (token);
1128 while (! isKeyword (token, KEYWORD_begin) && ! isKeyword (token, KEYWORD_end))
1130 switch (token->keyword)
1132 case KEYWORD_cursor: parseSimple (token, SQLTAG_CURSOR); break;
1133 case KEYWORD_function: parseSubProgram (token); break;
1134 case KEYWORD_procedure: parseSubProgram (token); break;
1135 case KEYWORD_subtype: parseSimple (token, SQLTAG_SUBTYPE); break;
1136 case KEYWORD_trigger: parseSimple (token, SQLTAG_TRIGGER); break;
1137 case KEYWORD_type: parseType (token); break;
1139 default:
1140 if (isType (token, TOKEN_IDENTIFIER))
1142 if (local)
1144 makeSqlTag (token, SQLTAG_LOCAL_VARIABLE);
1146 else
1148 makeSqlTag (token, SQLTAG_VARIABLE);
1151 break;
1153 findToken (token, TOKEN_SEMICOLON);
1154 readToken (token);
1158 static void parseDeclareANSI (tokenInfo *const token, const boolean local)
1160 tokenInfo *const type = newToken ();
1162 * ANSI declares are of this format:
1163 * BEGIN
1164 * DECLARE varname1 datatype;
1165 * DECLARE varname2 datatype;
1166 * ...
1168 * This differ from PL/SQL where DECLARE precedes the BEGIN block
1169 * and the DECLARE keyword is not repeated.
1171 while (isKeyword (token, KEYWORD_declare))
1173 readToken (token);
1174 readToken (type);
1176 if (isKeyword (type, KEYWORD_cursor))
1177 makeSqlTag (token, SQLTAG_CURSOR);
1178 else if (isKeyword (token, KEYWORD_local) &&
1179 isKeyword (type, KEYWORD_temporary))
1182 * DECLARE LOCAL TEMPORARY TABLE table_name (
1183 * c1 int,
1184 * c2 int
1185 * );
1187 readToken (token);
1188 if (isKeyword (token, KEYWORD_table))
1190 readToken (token);
1191 if (isType(token, TOKEN_IDENTIFIER) ||
1192 isType(token, TOKEN_STRING) )
1194 makeSqlTag (token, SQLTAG_TABLE);
1198 else if (isType (token, TOKEN_IDENTIFIER) ||
1199 isType (token, TOKEN_STRING))
1201 if (local)
1202 makeSqlTag (token, SQLTAG_LOCAL_VARIABLE);
1203 else
1204 makeSqlTag (token, SQLTAG_VARIABLE);
1206 findToken (token, TOKEN_SEMICOLON);
1207 readToken (token);
1209 deleteToken (type);
1212 static void parseLabel (tokenInfo *const token)
1215 * A label has this format:
1216 * <<tobacco_dependency>>
1217 * DECLARE
1218 * v_senator VARCHAR2(100) := 'THURMOND, JESSE';
1219 * BEGIN
1220 * IF total_contributions (v_senator, 'TOBACCO') > 25000
1221 * THEN
1222 * <<alochol_dependency>>
1223 * DECLARE
1224 * v_senator VARCHAR2(100) := 'WHATEVERIT, TAKES';
1225 * BEGIN
1226 * ...
1229 Assert (isType (token, TOKEN_BLOCK_LABEL_BEGIN));
1230 readToken (token);
1231 if (isType (token, TOKEN_IDENTIFIER))
1233 makeSqlTag (token, SQLTAG_BLOCK_LABEL);
1234 readToken (token); /* read end of label */
1238 static void parseStatements (tokenInfo *const token, const boolean exit_on_endif )
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 parseBlock(token, FALSE);
1320 * Handle the non-Ansi IF blocks.
1321 * parseBlock consumes the END, so if the next
1322 * token in a command terminator (like GO)
1323 * we know we are done with this statement.
1325 if ( isCmdTerm (token) )
1326 stmtTerm = TRUE;
1328 else
1330 readToken (token);
1332 while( ! (isKeyword (token, KEYWORD_end ) ||
1333 isKeyword (token, KEYWORD_endif ) )
1336 if ( isKeyword (token, KEYWORD_else) ||
1337 isKeyword (token, KEYWORD_elseif) )
1338 readToken (token);
1340 parseStatements (token, TRUE);
1342 if ( isCmdTerm(token) )
1343 readToken (token);
1348 * parseStatements returns when it finds an END, an IF
1349 * should follow the END for ANSI anyway.
1350 * IF...THEN
1351 * END IF;
1353 if( isKeyword (token, KEYWORD_end ) )
1354 readToken (token);
1356 if( isKeyword (token, KEYWORD_if ) || isKeyword (token, KEYWORD_endif ) )
1358 readToken (token);
1359 if ( isCmdTerm(token) )
1360 stmtTerm = TRUE;
1362 else
1365 * Well we need to do something here.
1366 * There are lots of different END statements
1367 * END;
1368 * END CASE;
1369 * ENDIF;
1370 * ENDCASE;
1374 break;
1376 case KEYWORD_loop:
1377 case KEYWORD_case:
1378 case KEYWORD_for:
1380 * LOOP...
1381 * END LOOP;
1383 * CASE
1384 * WHEN '1' THEN
1385 * END CASE;
1387 * FOR loop_name AS cursor_name CURSOR FOR ...
1388 * DO
1389 * END FOR;
1391 if( isKeyword (token, KEYWORD_for ) )
1393 /* loop name */
1394 readToken (token);
1395 /* AS */
1396 readToken (token);
1398 while ( ! isKeyword (token, KEYWORD_is) )
1401 * If this is not an AS keyword this is
1402 * not a proper FOR statement and should
1403 * simply be ignored
1405 return;
1408 while ( ! isKeyword (token, KEYWORD_do) )
1409 readToken (token);
1413 readToken (token);
1414 while( ! isKeyword (token, KEYWORD_end ) )
1417 if ( isKeyword (token, KEYWORD_else) ||
1418 isKeyword (token, KEYWORD_elseif) )
1419 readToken (token);
1422 parseStatements (token, FALSE);
1424 if ( isCmdTerm(token) )
1425 readToken (token);
1429 if( isKeyword (token, KEYWORD_end ) )
1430 readToken (token);
1433 * Typically ended with
1434 * END LOOP [loop name];
1435 * END CASE
1436 * END FOR [loop name];
1438 if ( isKeyword (token, KEYWORD_loop) ||
1439 isKeyword (token, KEYWORD_case) ||
1440 isKeyword (token, KEYWORD_for) )
1441 readToken (token);
1443 if ( isCmdTerm(token) )
1444 stmtTerm = TRUE;
1446 break;
1448 case KEYWORD_create:
1449 readToken (token);
1450 parseKeywords(token);
1451 break;
1453 case KEYWORD_declare:
1454 case KEYWORD_begin:
1455 parseBlock (token, TRUE);
1456 break;
1458 case KEYWORD_end:
1459 break;
1461 default:
1462 readToken (token);
1463 break;
1466 * Not all statements must end in a semi-colon
1467 * begin
1468 * if current publisher <> 'publish' then
1469 * signal UE_FailStatement
1470 * end if
1471 * end;
1472 * The last statement prior to an end ("signal" above) does
1473 * not need a semi-colon, nor does the end if, since it is
1474 * also the last statement prior to the end of the block.
1476 * So we must read to the first semi-colon or an END block
1478 while ( ! stmtTerm &&
1479 ! ( isKeyword (token, KEYWORD_end) ||
1480 (isCmdTerm(token)) )
1483 if ( isKeyword (token, KEYWORD_endif) &&
1484 exit_on_endif )
1485 return;
1487 if (isType (token, TOKEN_COLON) )
1490 * A : can signal a loop name
1491 * myloop:
1492 * LOOP
1493 * LEAVE myloop;
1494 * END LOOP;
1495 * Unfortunately, labels do not have a
1496 * cmd terminator, therefore we have to check
1497 * if the next token is a keyword and process
1498 * it accordingly.
1500 readToken (token);
1501 if ( isKeyword (token, KEYWORD_loop) ||
1502 isKeyword (token, KEYWORD_while) ||
1503 isKeyword (token, KEYWORD_for) )
1504 /* parseStatements (token); */
1505 return;
1508 readToken (token);
1510 if (isType (token, TOKEN_OPEN_PAREN) ||
1511 isType (token, TOKEN_OPEN_CURLY) ||
1512 isType (token, TOKEN_OPEN_SQUARE) )
1513 skipToMatched (token);
1516 * Since we know how to parse various statements
1517 * if we detect them, parse them to completion
1519 if (isType (token, TOKEN_BLOCK_LABEL_BEGIN) ||
1520 isKeyword (token, KEYWORD_exception) ||
1521 isKeyword (token, KEYWORD_loop) ||
1522 isKeyword (token, KEYWORD_case) ||
1523 isKeyword (token, KEYWORD_for) ||
1524 isKeyword (token, KEYWORD_begin) )
1525 parseStatements (token, FALSE);
1526 else if (isKeyword (token, KEYWORD_if))
1527 parseStatements (token, TRUE);
1532 * We assumed earlier all statements ended with a command terminator.
1533 * See comment above, now, only read if the current token
1534 * is not a command terminator.
1536 if ( isCmdTerm(token) && ! stmtTerm )
1537 stmtTerm = TRUE;
1539 } while (! isKeyword (token, KEYWORD_end) &&
1540 ! (exit_on_endif && isKeyword (token, KEYWORD_endif) ) &&
1541 ! stmtTerm );
1544 static void parseBlock (tokenInfo *const token, const boolean local)
1546 if (isType (token, TOKEN_BLOCK_LABEL_BEGIN))
1548 parseLabel (token);
1549 readToken (token);
1551 if (! isKeyword (token, KEYWORD_begin))
1553 readToken (token);
1555 * These are Oracle style declares which generally come
1556 * between an IS/AS and BEGIN block.
1558 parseDeclare (token, local);
1560 if (isKeyword (token, KEYWORD_begin))
1562 readToken (token);
1564 * Check for ANSI declarations which always follow
1565 * a BEGIN statement. This routine will not advance
1566 * the token if none are found.
1568 parseDeclareANSI (token, local);
1569 token->begin_end_nest_lvl++;
1570 while (! isKeyword (token, KEYWORD_end))
1572 parseStatements (token, FALSE);
1574 if ( isCmdTerm(token) )
1575 readToken (token);
1577 token->begin_end_nest_lvl--;
1580 * Read the next token (we will assume
1581 * it is the command delimiter)
1583 readToken (token);
1586 * Check if the END block is terminated
1588 if ( !isCmdTerm (token) )
1591 * Not sure what to do here at the moment.
1592 * I think the routine that calls parseBlock
1593 * must expect the next token has already
1594 * been read since it is possible this
1595 * token is not a command delimiter.
1597 /* findCmdTerm (token, FALSE); */
1602 static void parsePackage (tokenInfo *const token)
1605 * Packages can be specified in a number of ways:
1606 * CREATE OR REPLACE PACKAGE pkg_name AS
1607 * or
1608 * CREATE OR REPLACE PACKAGE owner.pkg_name AS
1609 * or by specifying a package body
1610 * CREATE OR REPLACE PACKAGE BODY pkg_name AS
1611 * CREATE OR REPLACE PACKAGE BODY owner.pkg_name AS
1613 tokenInfo *const name = newToken ();
1614 readIdentifier (name);
1615 if (isKeyword (name, KEYWORD_body))
1618 * Ignore the BODY tag since we will process
1619 * the body or prototypes in the same manner
1621 readIdentifier (name);
1623 /* Check for owner.pkg_name */
1624 while (! isKeyword (token, KEYWORD_is))
1626 readToken (token);
1627 if ( isType(token, TOKEN_PERIOD) )
1629 readIdentifier (name);
1632 if (isKeyword (token, KEYWORD_is))
1634 if (isType (name, TOKEN_IDENTIFIER) ||
1635 isType (name, TOKEN_STRING))
1636 makeSqlTag (name, SQLTAG_PACKAGE);
1637 addToScope (token, name->string, SQLTAG_PACKAGE);
1638 parseBlock (token, FALSE);
1639 vStringClear (token->scope);
1640 token->scopeKind = SQLTAG_COUNT;
1642 findCmdTerm (token, FALSE);
1643 deleteToken (name);
1646 static void parseTable (tokenInfo *const token)
1648 tokenInfo *const name = newToken ();
1651 * This deals with these formats:
1652 * create table t1 (c1 int);
1653 * create global tempoary table t2 (c1 int);
1654 * create table "t3" (c1 int);
1655 * create table bob.t4 (c1 int);
1656 * create table bob."t5" (c1 int);
1657 * create table "bob"."t6" (c1 int);
1658 * create table bob."t7" (c1 int);
1659 * Proxy tables use this format:
1660 * create existing table bob."t7" AT '...';
1661 * SQL Server and Sybase formats
1662 * create table OnlyTable (
1663 * create table dbo.HasOwner (
1664 * create table [dbo].[HasOwnerSquare] (
1665 * create table master.dbo.HasDb (
1666 * create table master..HasDbNoOwner (
1667 * create table [master].dbo.[HasDbAndOwnerSquare] (
1668 * create table [master]..[HasDbNoOwnerSquare] (
1671 /* This could be a database, owner or table name */
1672 readIdentifier (name);
1673 readToken (token);
1674 if (isType (token, TOKEN_PERIOD))
1677 * This could be a owner or table name.
1678 * But this is also a special case since the table can be
1679 * referenced with a blank owner:
1680 * dbname..tablename
1682 readIdentifier (name);
1683 /* Check if a blank name was provided */
1684 if (isType (name, TOKEN_PERIOD))
1686 readIdentifier (name);
1688 readToken (token);
1689 if (isType (token, TOKEN_PERIOD))
1691 /* This can only be the table name */
1692 readIdentifier (name);
1693 readToken (token);
1696 if (isType (token, TOKEN_OPEN_PAREN))
1698 if (isType (name, TOKEN_IDENTIFIER) ||
1699 isType (name, TOKEN_STRING))
1701 makeSqlTag (name, SQLTAG_TABLE);
1702 vStringCopy(token->scope, name->string);
1703 token->scopeKind = SQLTAG_TABLE;
1704 parseRecord (token);
1705 vStringClear (token->scope);
1706 token->scopeKind = SQLTAG_COUNT;
1709 else if (isKeyword (token, KEYWORD_at))
1711 if (isType (name, TOKEN_IDENTIFIER))
1713 makeSqlTag (name, SQLTAG_TABLE);
1716 findCmdTerm (token, FALSE);
1717 deleteToken (name);
1720 static void parseIndex (tokenInfo *const token)
1722 tokenInfo *const name = newToken ();
1723 tokenInfo *const owner = newToken ();
1726 * This deals with these formats
1727 * create index i1 on t1(c1) create index "i2" on t1(c1)
1728 * create virtual unique clustered index "i3" on t1(c1)
1729 * create unique clustered index "i4" on t1(c1)
1730 * create clustered index "i5" on t1(c1)
1731 * create bitmap index "i6" on t1(c1)
1734 readIdentifier (name);
1735 readToken (token);
1736 if (isType (token, TOKEN_PERIOD))
1738 readIdentifier (name);
1739 readToken (token);
1741 if ( isKeyword (token, KEYWORD_on) &&
1742 (isType (name, TOKEN_IDENTIFIER) || isType (name, TOKEN_STRING) ) )
1744 readIdentifier (owner);
1745 readToken (token);
1746 if (isType (token, TOKEN_PERIOD))
1748 readIdentifier (owner);
1749 readToken (token);
1751 addToScope(name, owner->string, SQLTAG_TABLE /* FIXME? */);
1752 makeSqlTag (name, SQLTAG_INDEX);
1754 findCmdTerm (token, FALSE);
1755 deleteToken (name);
1756 deleteToken (owner);
1759 static void parseEvent (tokenInfo *const token)
1761 tokenInfo *const name = newToken ();
1764 * This deals with these formats
1765 * create event e1 handler begin end;
1766 * create event "e2" handler begin end;
1767 * create event dba."e3" handler begin end;
1768 * create event "dba"."e4" handler begin end;
1771 readIdentifier (name);
1772 readToken (token);
1773 if (isType (token, TOKEN_PERIOD))
1775 readIdentifier (name);
1777 while (! (isKeyword (token, KEYWORD_handler) ||
1778 (isType (token, TOKEN_SEMICOLON))) )
1780 readToken (token);
1783 if ( isKeyword (token, KEYWORD_handler) ||
1784 isType (token, TOKEN_SEMICOLON) )
1786 makeSqlTag (name, SQLTAG_EVENT);
1789 if (isKeyword (token, KEYWORD_handler))
1791 readToken (token);
1792 if ( isKeyword (token, KEYWORD_begin) )
1794 parseBlock (token, TRUE);
1796 findCmdTerm (token, TRUE);
1798 deleteToken (name);
1801 static void parseTrigger (tokenInfo *const token)
1803 tokenInfo *const name = newToken ();
1804 tokenInfo *const table = newToken ();
1807 * This deals with these formats
1808 * create or replace trigger tr1 begin end;
1809 * create trigger "tr2" begin end;
1810 * drop trigger "droptr1";
1811 * create trigger "tr3" CALL sp_something();
1812 * create trigger "owner"."tr4" begin end;
1813 * create trigger "tr5" not valid;
1814 * create trigger "tr6" begin end;
1817 readIdentifier (name);
1818 readToken (token);
1819 if (isType (token, TOKEN_PERIOD))
1821 readIdentifier (name);
1822 readToken (token);
1825 while ( !isKeyword (token, KEYWORD_on) &&
1826 !isCmdTerm (token) )
1828 readToken (token);
1831 /*if (! isType (token, TOKEN_SEMICOLON) ) */
1832 if (! isCmdTerm (token) )
1834 readToken (table);
1835 readToken (token);
1836 if (isType (token, TOKEN_PERIOD))
1838 readToken (table);
1839 readToken (token);
1842 while (! (isKeyword (token, KEYWORD_begin) ||
1843 (isKeyword (token, KEYWORD_call)) ||
1844 ( isCmdTerm (token))) )
1846 if ( isKeyword (token, KEYWORD_declare) )
1848 addToScope(token, name->string, SQLTAG_TRIGGER);
1849 parseDeclare(token, TRUE);
1850 vStringClear(token->scope);
1851 token->scopeKind = SQLTAG_COUNT;
1853 else
1854 readToken (token);
1857 if ( isKeyword (token, KEYWORD_begin) ||
1858 isKeyword (token, KEYWORD_call) )
1860 addToScope(name, table->string, SQLTAG_TABLE);
1861 makeSqlTag (name, SQLTAG_TRIGGER);
1862 addToScope(token, table->string, SQLTAG_TABLE);
1863 if ( isKeyword (token, KEYWORD_begin) )
1865 parseBlock (token, TRUE);
1867 vStringClear(token->scope);
1868 token->scopeKind = SQLTAG_COUNT;
1872 findCmdTerm (token, TRUE);
1873 deleteToken (name);
1874 deleteToken (table);
1877 static void parsePublication (tokenInfo *const token)
1879 tokenInfo *const name = newToken ();
1882 * This deals with these formats
1883 * create or replace publication pu1 ()
1884 * create publication "pu2" ()
1885 * create publication dba."pu3" ()
1886 * create publication "dba"."pu4" ()
1889 readIdentifier (name);
1890 readToken (token);
1891 if (isType (token, TOKEN_PERIOD))
1893 readIdentifier (name);
1894 readToken (token);
1896 if (isType (token, TOKEN_OPEN_PAREN))
1898 if (isType (name, TOKEN_IDENTIFIER) ||
1899 isType (name, TOKEN_STRING))
1901 makeSqlTag (name, SQLTAG_PUBLICATION);
1904 findCmdTerm (token, FALSE);
1905 deleteToken (name);
1908 static void parseService (tokenInfo *const token)
1910 tokenInfo *const name = newToken ();
1913 * This deals with these formats
1914 * CREATE SERVICE s1 TYPE 'HTML'
1915 * AUTHORIZATION OFF USER DBA AS
1916 * SELECT *
1917 * FROM SYS.SYSTABLE;
1918 * CREATE SERVICE "s2" TYPE 'HTML'
1919 * AUTHORIZATION OFF USER DBA AS
1920 * CALL sp_Something();
1923 readIdentifier (name);
1924 readToken (token);
1925 if (isKeyword (token, KEYWORD_type))
1927 if (isType (name, TOKEN_IDENTIFIER) ||
1928 isType (name, TOKEN_STRING))
1930 makeSqlTag (name, SQLTAG_SERVICE);
1933 findCmdTerm (token, FALSE);
1934 deleteToken (name);
1937 static void parseDomain (tokenInfo *const token)
1939 tokenInfo *const name = newToken ();
1942 * This deals with these formats
1943 * CREATE DOMAIN|DATATYPE [AS] your_name ...;
1946 readIdentifier (name);
1947 if (isKeyword (name, KEYWORD_is))
1949 readIdentifier (name);
1951 readToken (token);
1952 if (isType (name, TOKEN_IDENTIFIER) ||
1953 isType (name, TOKEN_STRING))
1955 makeSqlTag (name, SQLTAG_DOMAIN);
1957 findCmdTerm (token, FALSE);
1958 deleteToken (name);
1961 static void parseDrop (tokenInfo *const token)
1964 * This deals with these formats
1965 * DROP TABLE|PROCEDURE|DOMAIN|DATATYPE name;
1967 * Just simply skip over these statements.
1968 * They are often confused with PROCEDURE prototypes
1969 * since the syntax is similar, this effectively deals with
1970 * the issue for all types.
1973 findCmdTerm (token, FALSE);
1976 static void parseVariable (tokenInfo *const token)
1978 tokenInfo *const name = newToken ();
1981 * This deals with these formats
1982 * create variable varname1 integer;
1983 * create variable @varname2 integer;
1984 * create variable "varname3" integer;
1985 * drop variable @varname3;
1988 readIdentifier (name);
1989 readToken (token);
1990 if ( (isType (name, TOKEN_IDENTIFIER) || isType (name, TOKEN_STRING))
1991 && !isType (token, TOKEN_SEMICOLON) )
1993 makeSqlTag (name, SQLTAG_VARIABLE);
1995 findCmdTerm (token, TRUE);
1997 deleteToken (name);
2000 static void parseSynonym (tokenInfo *const token)
2002 tokenInfo *const name = newToken ();
2005 * This deals with these formats
2006 * create variable varname1 integer;
2007 * create variable @varname2 integer;
2008 * create variable "varname3" integer;
2009 * drop variable @varname3;
2012 readIdentifier (name);
2013 readToken (token);
2014 if ( (isType (name, TOKEN_IDENTIFIER) || isType (name, TOKEN_STRING))
2015 && isKeyword (token, KEYWORD_for) )
2017 makeSqlTag (name, SQLTAG_SYNONYM);
2019 findCmdTerm (token, TRUE);
2021 deleteToken (name);
2024 static void parseView (tokenInfo *const token)
2026 tokenInfo *const name = newToken ();
2029 * This deals with these formats
2030 * create variable varname1 integer;
2031 * create variable @varname2 integer;
2032 * create variable "varname3" integer;
2033 * drop variable @varname3;
2036 readIdentifier (name);
2037 readToken (token);
2038 if (isType (token, TOKEN_PERIOD))
2040 readIdentifier (name);
2041 readToken (token);
2043 if ( isType (token, TOKEN_OPEN_PAREN) )
2045 skipArgumentList(token);
2049 while (!(isKeyword (token, KEYWORD_is) ||
2050 isType (token, TOKEN_SEMICOLON)
2053 readToken (token);
2056 if ( (isType (name, TOKEN_IDENTIFIER) || isType (name, TOKEN_STRING))
2057 && isKeyword (token, KEYWORD_is) )
2059 makeSqlTag (name, SQLTAG_VIEW);
2062 findCmdTerm (token, TRUE);
2064 deleteToken (name);
2067 static void parseMLTable (tokenInfo *const token)
2069 tokenInfo *const version = newToken ();
2070 tokenInfo *const table = newToken ();
2071 tokenInfo *const event = newToken ();
2074 * This deals with these formats
2075 * call dbo.ml_add_table_script( 'version', 'table_name', 'event',
2076 * 'some SQL statement'
2077 * );
2080 readToken (token);
2081 if ( isType (token, TOKEN_OPEN_PAREN) )
2083 readToken (version);
2084 readToken (token);
2085 while (!(isType (token, TOKEN_COMMA) ||
2086 isType (token, TOKEN_CLOSE_PAREN)
2089 readToken (token);
2092 if (isType (token, TOKEN_COMMA))
2094 readToken (table);
2095 readToken (token);
2096 while (!(isType (token, TOKEN_COMMA) ||
2097 isType (token, TOKEN_CLOSE_PAREN)
2100 readToken (token);
2103 if (isType (token, TOKEN_COMMA))
2105 readToken (event);
2107 if (isType (version, TOKEN_STRING) &&
2108 isType (table, TOKEN_STRING) &&
2109 isType (event, TOKEN_STRING) )
2111 addToScope(version, table->string, SQLTAG_TABLE);
2112 addToScope(version, event->string, SQLTAG_EVENT);
2113 makeSqlTag (version, SQLTAG_MLTABLE);
2116 if( !isType (token, TOKEN_CLOSE_PAREN) )
2117 findToken (token, TOKEN_CLOSE_PAREN);
2121 findCmdTerm (token, TRUE);
2123 deleteToken (version);
2124 deleteToken (table);
2125 deleteToken (event);
2128 static void parseMLConn (tokenInfo *const token)
2130 tokenInfo *const version = newToken ();
2131 tokenInfo *const event = newToken ();
2134 * This deals with these formats
2135 * call ml_add_connection_script( 'version', 'event',
2136 * 'some SQL statement'
2137 * );
2140 readToken (token);
2141 if ( isType (token, TOKEN_OPEN_PAREN) )
2143 readToken (version);
2144 readToken (token);
2145 while (!(isType (token, TOKEN_COMMA) ||
2146 isType (token, TOKEN_CLOSE_PAREN)
2149 readToken (token);
2152 if (isType (token, TOKEN_COMMA))
2154 readToken (event);
2156 if (isType (version, TOKEN_STRING) &&
2157 isType (event, TOKEN_STRING) )
2159 addToScope(version, event->string, SQLTAG_EVENT);
2160 makeSqlTag (version, SQLTAG_MLCONN);
2163 if( !isType (token, TOKEN_CLOSE_PAREN) )
2164 findToken (token, TOKEN_CLOSE_PAREN);
2168 findCmdTerm (token, TRUE);
2170 deleteToken (version);
2171 deleteToken (event);
2174 static void parseMLProp (tokenInfo *const token)
2176 tokenInfo *const component = newToken ();
2177 tokenInfo *const prop_set_name = newToken ();
2178 tokenInfo *const prop_name = newToken ();
2181 * This deals with these formats
2182 * ml_add_property (
2183 * 'comp_name',
2184 * 'prop_set_name',
2185 * 'prop_name',
2186 * 'prop_value'
2190 readToken (token);
2191 if ( isType (token, TOKEN_OPEN_PAREN) )
2193 readToken (component);
2194 readToken (token);
2195 while (!(isType (token, TOKEN_COMMA) ||
2196 isType (token, TOKEN_CLOSE_PAREN)
2199 readToken (token);
2202 if (isType (token, TOKEN_COMMA))
2204 readToken (prop_set_name);
2205 readToken (token);
2206 while (!(isType (token, TOKEN_COMMA) ||
2207 isType (token, TOKEN_CLOSE_PAREN)
2210 readToken (token);
2213 if (isType (token, TOKEN_COMMA))
2215 readToken (prop_name);
2217 if (isType (component, TOKEN_STRING) &&
2218 isType (prop_set_name, TOKEN_STRING) &&
2219 isType (prop_name, TOKEN_STRING) )
2221 addToScope(component, prop_set_name->string, SQLTAG_MLPROP /* FIXME */);
2222 addToScope(component, prop_name->string, SQLTAG_MLPROP /* FIXME */);
2223 makeSqlTag (component, SQLTAG_MLPROP);
2226 if( !isType (token, TOKEN_CLOSE_PAREN) )
2227 findToken (token, TOKEN_CLOSE_PAREN);
2231 findCmdTerm (token, TRUE);
2233 deleteToken (component);
2234 deleteToken (prop_set_name);
2235 deleteToken (prop_name);
2238 static void parseComment (tokenInfo *const token)
2241 * This deals with this statement:
2242 * COMMENT TO PRESERVE FORMAT ON PROCEDURE "DBA"."test" IS
2243 * {create PROCEDURE DBA."test"()
2244 * BEGIN
2245 * signal dave;
2246 * END
2249 * The comment can contain anything between the CURLY
2250 * braces
2251 * COMMENT ON USER "admin" IS
2252 * 'Administration Group'
2254 * Or it could be a simple string with no curly braces
2256 while (! isKeyword (token, KEYWORD_is))
2258 readToken (token);
2260 readToken (token);
2261 if ( isType(token, TOKEN_OPEN_CURLY) )
2263 findToken (token, TOKEN_CLOSE_CURLY);
2266 findCmdTerm (token, TRUE);
2270 static void parseKeywords (tokenInfo *const token)
2272 switch (token->keyword)
2274 case KEYWORD_begin: parseBlock (token, FALSE); break;
2275 case KEYWORD_comment: parseComment (token); break;
2276 case KEYWORD_cursor: parseSimple (token, SQLTAG_CURSOR); break;
2277 case KEYWORD_datatype: parseDomain (token); break;
2278 case KEYWORD_declare: parseBlock (token, FALSE); break;
2279 case KEYWORD_domain: parseDomain (token); break;
2280 case KEYWORD_drop: parseDrop (token); break;
2281 case KEYWORD_event: parseEvent (token); break;
2282 case KEYWORD_function: parseSubProgram (token); break;
2283 case KEYWORD_if: parseStatements (token, FALSE); break;
2284 case KEYWORD_index: parseIndex (token); break;
2285 case KEYWORD_ml_table: parseMLTable (token); break;
2286 case KEYWORD_ml_table_lang: parseMLTable (token); break;
2287 case KEYWORD_ml_table_dnet: parseMLTable (token); break;
2288 case KEYWORD_ml_table_java: parseMLTable (token); break;
2289 case KEYWORD_ml_table_chk: parseMLTable (token); break;
2290 case KEYWORD_ml_conn: parseMLConn (token); break;
2291 case KEYWORD_ml_conn_lang: parseMLConn (token); break;
2292 case KEYWORD_ml_conn_dnet: parseMLConn (token); break;
2293 case KEYWORD_ml_conn_java: parseMLConn (token); break;
2294 case KEYWORD_ml_conn_chk: parseMLConn (token); break;
2295 case KEYWORD_ml_prop: parseMLProp (token); break;
2296 case KEYWORD_package: parsePackage (token); break;
2297 case KEYWORD_procedure: parseSubProgram (token); break;
2298 case KEYWORD_publication: parsePublication (token); break;
2299 case KEYWORD_service: parseService (token); break;
2300 case KEYWORD_subtype: parseSimple (token, SQLTAG_SUBTYPE); break;
2301 case KEYWORD_synonym: parseSynonym (token); break;
2302 case KEYWORD_table: parseTable (token); break;
2303 case KEYWORD_trigger: parseTrigger (token); break;
2304 case KEYWORD_type: parseType (token); break;
2305 case KEYWORD_variable: parseVariable (token); break;
2306 case KEYWORD_view: parseView (token); break;
2307 default: break;
2311 static void parseSqlFile (tokenInfo *const token)
2315 readToken (token);
2317 if (isType (token, TOKEN_BLOCK_LABEL_BEGIN))
2318 parseLabel (token);
2319 else
2320 parseKeywords (token);
2321 } while (! isKeyword (token, KEYWORD_end));
2324 static void initialize (const langType language)
2326 Assert (ARRAY_SIZE (SqlKinds) == SQLTAG_COUNT);
2327 Lang_sql = language;
2330 static void findSqlTags (void)
2332 tokenInfo *const token = newToken ();
2333 exception_t exception = (exception_t) (setjmp (Exception));
2335 while (exception == ExceptionNone)
2336 parseSqlFile (token);
2338 deleteToken (token);
2341 extern parserDefinition* SqlParser (void)
2343 static const char *const extensions [] = { "sql", NULL };
2344 parserDefinition* def = parserNew ("SQL");
2345 def->kinds = SqlKinds;
2346 def->kindCount = ARRAY_SIZE (SqlKinds);
2347 def->extensions = extensions;
2348 def->parser = findSqlTags;
2349 def->initialize = initialize;
2350 def->keywordTable = SqlKeywordTable;
2351 def->keywordCount = ARRAY_SIZE (SqlKeywordTable);
2352 return def;
2355 /* vi:set tabstop=4 shiftwidth=4 noexpandtab: */