Making "inline" behave like an attribute. Fixes #1
[arduino-ctags.git] / sql.c
blob6c0c76a58f51a3c83758b903ced7d9f4b4f51178
1 /*
2 * $Id: sql.c 745 2009-10-27 02:42:55Z dfishburn $
4 * Copyright (c) 2002-2003, Darren Hiebert
6 * This source code is released for free distribution under the terms of the
7 * GNU General Public License.
9 * This module contains functions for generating tags for PL/SQL language
10 * files.
14 * INCLUDE FILES
16 #include "general.h" /* must always come first */
18 #include <ctype.h> /* to define isalpha () */
19 #include <setjmp.h>
20 #ifdef DEBUG
21 #include <stdio.h>
22 #endif
24 #include "debug.h"
25 #include "entry.h"
26 #include "keyword.h"
27 #include "parse.h"
28 #include "read.h"
29 #include "routines.h"
30 #include "vstring.h"
33 * On-line "Oracle Database PL/SQL Language Reference":
34 * http://download.oracle.com/docs/cd/B28359_01/appdev.111/b28370/toc.htm
36 * Sample PL/SQL code is available from:
37 * http://www.orafaq.com/faqscrpt.htm#GENPLSQL
39 * On-line SQL Anywhere Documentation
40 * http://www.ianywhere.com/developer/product_manuals/sqlanywhere/index.html
44 * MACROS
46 #define isType(token,t) (boolean) ((token)->type == (t))
47 #define isKeyword(token,k) (boolean) ((token)->keyword == (k))
50 * DATA DECLARATIONS
53 typedef enum eException { ExceptionNone, ExceptionEOF } exception_t;
56 * Used to specify type of keyword.
58 typedef enum eKeywordId {
59 KEYWORD_NONE = -1,
60 KEYWORD_is,
61 KEYWORD_begin,
62 KEYWORD_body,
63 KEYWORD_cursor,
64 KEYWORD_declare,
65 KEYWORD_end,
66 KEYWORD_function,
67 KEYWORD_if,
68 KEYWORD_else,
69 KEYWORD_elseif,
70 KEYWORD_endif,
71 KEYWORD_loop,
72 KEYWORD_while,
73 KEYWORD_case,
74 KEYWORD_for,
75 KEYWORD_do,
76 KEYWORD_call,
77 KEYWORD_package,
78 KEYWORD_pragma,
79 KEYWORD_procedure,
80 KEYWORD_record,
81 KEYWORD_object,
82 KEYWORD_ref,
83 KEYWORD_rem,
84 KEYWORD_return,
85 KEYWORD_returns,
86 KEYWORD_subtype,
87 KEYWORD_table,
88 KEYWORD_trigger,
89 KEYWORD_type,
90 KEYWORD_index,
91 KEYWORD_event,
92 KEYWORD_publication,
93 KEYWORD_service,
94 KEYWORD_domain,
95 KEYWORD_datatype,
96 KEYWORD_result,
97 KEYWORD_url,
98 KEYWORD_internal,
99 KEYWORD_external,
100 KEYWORD_when,
101 KEYWORD_then,
102 KEYWORD_variable,
103 KEYWORD_exception,
104 KEYWORD_at,
105 KEYWORD_on,
106 KEYWORD_primary,
107 KEYWORD_references,
108 KEYWORD_unique,
109 KEYWORD_check,
110 KEYWORD_constraint,
111 KEYWORD_foreign,
112 KEYWORD_ml_table,
113 KEYWORD_ml_table_lang,
114 KEYWORD_ml_table_dnet,
115 KEYWORD_ml_table_java,
116 KEYWORD_ml_table_chk,
117 KEYWORD_ml_conn,
118 KEYWORD_ml_conn_lang,
119 KEYWORD_ml_conn_dnet,
120 KEYWORD_ml_conn_java,
121 KEYWORD_ml_conn_chk,
122 KEYWORD_ml_prop,
123 KEYWORD_local,
124 KEYWORD_temporary,
125 KEYWORD_drop,
126 KEYWORD_view,
127 KEYWORD_synonym,
128 KEYWORD_handler,
129 KEYWORD_comment,
130 KEYWORD_create,
131 KEYWORD_go
132 } keywordId;
135 * Used to determine whether keyword is valid for the token language and
136 * what its ID is.
138 typedef struct sKeywordDesc {
139 const char *name;
140 keywordId id;
141 } keywordDesc;
143 typedef enum eTokenType {
144 TOKEN_UNDEFINED,
145 TOKEN_BLOCK_LABEL_BEGIN,
146 TOKEN_BLOCK_LABEL_END,
147 TOKEN_CHARACTER,
148 TOKEN_CLOSE_PAREN,
149 TOKEN_COLON,
150 TOKEN_SEMICOLON,
151 TOKEN_COMMA,
152 TOKEN_IDENTIFIER,
153 TOKEN_KEYWORD,
154 TOKEN_OPEN_PAREN,
155 TOKEN_OPERATOR,
156 TOKEN_OTHER,
157 TOKEN_STRING,
158 TOKEN_PERIOD,
159 TOKEN_OPEN_CURLY,
160 TOKEN_CLOSE_CURLY,
161 TOKEN_OPEN_SQUARE,
162 TOKEN_CLOSE_SQUARE,
163 TOKEN_TILDE,
164 TOKEN_FORWARD_SLASH,
165 TOKEN_EQUAL
166 } tokenType;
168 typedef struct sTokenInfoSQL {
169 tokenType type;
170 keywordId keyword;
171 vString * string;
172 vString * scope;
173 int begin_end_nest_lvl;
174 unsigned long lineNumber;
175 fpos_t filePosition;
176 } tokenInfo;
179 * DATA DEFINITIONS
182 static langType Lang_sql;
184 static jmp_buf Exception;
186 typedef enum {
187 SQLTAG_CURSOR,
188 SQLTAG_PROTOTYPE,
189 SQLTAG_FUNCTION,
190 SQLTAG_FIELD,
191 SQLTAG_LOCAL_VARIABLE,
192 SQLTAG_BLOCK_LABEL,
193 SQLTAG_PACKAGE,
194 SQLTAG_PROCEDURE,
195 SQLTAG_RECORD,
196 SQLTAG_SUBTYPE,
197 SQLTAG_TABLE,
198 SQLTAG_TRIGGER,
199 SQLTAG_VARIABLE,
200 SQLTAG_INDEX,
201 SQLTAG_EVENT,
202 SQLTAG_PUBLICATION,
203 SQLTAG_SERVICE,
204 SQLTAG_DOMAIN,
205 SQLTAG_VIEW,
206 SQLTAG_SYNONYM,
207 SQLTAG_MLTABLE,
208 SQLTAG_MLCONN,
209 SQLTAG_MLPROP,
210 SQLTAG_COUNT
211 } sqlKind;
213 static kindOption SqlKinds [] = {
214 { TRUE, 'c', "cursor", "cursors" },
215 { FALSE, 'd', "prototype", "prototypes" },
216 { TRUE, 'f', "function", "functions" },
217 { TRUE, 'F', "field", "record fields" },
218 { FALSE, 'l', "local", "local variables" },
219 { TRUE, 'L', "label", "block label" },
220 { TRUE, 'P', "package", "packages" },
221 { TRUE, 'p', "procedure", "procedures" },
222 { FALSE, 'r', "record", "records" },
223 { TRUE, 's', "subtype", "subtypes" },
224 { TRUE, 't', "table", "tables" },
225 { TRUE, 'T', "trigger", "triggers" },
226 { TRUE, 'v', "variable", "variables" },
227 { TRUE, 'i', "index", "indexes" },
228 { TRUE, 'e', "event", "events" },
229 { TRUE, 'U', "publication", "publications" },
230 { TRUE, 'R', "service", "services" },
231 { TRUE, 'D', "domain", "domains" },
232 { TRUE, 'V', "view", "views" },
233 { TRUE, 'n', "synonym", "synonyms" },
234 { TRUE, 'x', "mltable", "MobiLink Table Scripts" },
235 { TRUE, 'y', "mlconn", "MobiLink Conn Scripts" },
236 { TRUE, 'z', "mlprop", "MobiLink Properties " }
239 static const keywordDesc SqlKeywordTable [] = {
240 /* keyword keyword ID */
241 { "as", KEYWORD_is },
242 { "is", KEYWORD_is },
243 { "begin", KEYWORD_begin },
244 { "body", KEYWORD_body },
245 { "cursor", KEYWORD_cursor },
246 { "declare", KEYWORD_declare },
247 { "end", KEYWORD_end },
248 { "function", KEYWORD_function },
249 { "if", KEYWORD_if },
250 { "else", KEYWORD_else },
251 { "elseif", KEYWORD_elseif },
252 { "endif", KEYWORD_endif },
253 { "loop", KEYWORD_loop },
254 { "while", KEYWORD_while },
255 { "case", KEYWORD_case },
256 { "for", KEYWORD_for },
257 { "do", KEYWORD_do },
258 { "call", KEYWORD_call },
259 { "package", KEYWORD_package },
260 { "pragma", KEYWORD_pragma },
261 { "procedure", KEYWORD_procedure },
262 { "record", KEYWORD_record },
263 { "object", KEYWORD_object },
264 { "ref", KEYWORD_ref },
265 { "rem", KEYWORD_rem },
266 { "return", KEYWORD_return },
267 { "returns", KEYWORD_returns },
268 { "subtype", KEYWORD_subtype },
269 { "table", KEYWORD_table },
270 { "trigger", KEYWORD_trigger },
271 { "type", KEYWORD_type },
272 { "index", KEYWORD_index },
273 { "event", KEYWORD_event },
274 { "publication", KEYWORD_publication },
275 { "service", KEYWORD_service },
276 { "domain", KEYWORD_domain },
277 { "datatype", KEYWORD_datatype },
278 { "result", KEYWORD_result },
279 { "url", KEYWORD_url },
280 { "internal", KEYWORD_internal },
281 { "external", KEYWORD_external },
282 { "when", KEYWORD_when },
283 { "then", KEYWORD_then },
284 { "variable", KEYWORD_variable },
285 { "exception", KEYWORD_exception },
286 { "at", KEYWORD_at },
287 { "on", KEYWORD_on },
288 { "primary", KEYWORD_primary },
289 { "references", KEYWORD_references },
290 { "unique", KEYWORD_unique },
291 { "check", KEYWORD_check },
292 { "constraint", KEYWORD_constraint },
293 { "foreign", KEYWORD_foreign },
294 { "ml_add_table_script", KEYWORD_ml_table },
295 { "ml_add_lang_table_script", KEYWORD_ml_table_lang },
296 { "ml_add_dnet_table_script", KEYWORD_ml_table_dnet },
297 { "ml_add_java_table_script", KEYWORD_ml_table_java },
298 { "ml_add_lang_table_script_chk", KEYWORD_ml_table_chk },
299 { "ml_add_connection_script", KEYWORD_ml_conn },
300 { "ml_add_lang_connection_script", KEYWORD_ml_conn_lang },
301 { "ml_add_dnet_connection_script", KEYWORD_ml_conn_dnet },
302 { "ml_add_java_connection_script", KEYWORD_ml_conn_java },
303 { "ml_add_lang_conn_script_chk", KEYWORD_ml_conn_chk },
304 { "ml_add_property", KEYWORD_ml_prop },
305 { "local", KEYWORD_local },
306 { "temporary", KEYWORD_temporary },
307 { "drop", KEYWORD_drop },
308 { "view", KEYWORD_view },
309 { "synonym", KEYWORD_synonym },
310 { "handler", KEYWORD_handler },
311 { "comment", KEYWORD_comment },
312 { "create", KEYWORD_create },
313 { "go", KEYWORD_go }
317 * FUNCTION DECLARATIONS
320 /* Recursive calls */
321 static void parseBlock (tokenInfo *const token, const boolean local);
322 static void parseDeclare (tokenInfo *const token, const boolean local);
323 static void parseKeywords (tokenInfo *const token);
324 static void parseSqlFile (tokenInfo *const token);
327 * FUNCTION DEFINITIONS
330 static boolean isIdentChar1 (const int c)
333 * Other databases are less restrictive on the first character of
334 * an identifier.
335 * isIdentChar1 is used to identify the first character of an
336 * identifier, so we are removing some restrictions.
338 return (boolean)
339 (isalpha (c) || c == '@' || c == '_' );
342 static boolean isIdentChar (const int c)
344 return (boolean)
345 (isalpha (c) || isdigit (c) || c == '$' ||
346 c == '@' || c == '_' || c == '#');
349 static boolean isCmdTerm (tokenInfo *const token)
351 DebugStatement (
352 debugPrintf (DEBUG_PARSE
353 , "\n isCmdTerm: token same tt:%d tk:%d\n"
354 , token->type
355 , token->keyword
360 * Based on the various customer sites I have been at
361 * the most common command delimiters are
365 * go
366 * This routine will check for any of these, more
367 * can easily be added by modifying readToken and
368 * either adding the character to:
369 * enum eTokenType
370 * enum eTokenType
372 return ( isType (token, TOKEN_SEMICOLON) ||
373 isType (token, TOKEN_TILDE) ||
374 isType (token, TOKEN_FORWARD_SLASH) ||
375 isKeyword (token, KEYWORD_go)
379 static boolean isMatchedEnd(tokenInfo *const token, int nest_lvl)
381 boolean terminated = FALSE;
383 * Since different forms of SQL allow the use of
384 * BEGIN
385 * ...
386 * END
387 * blocks, some statements may not be terminated using
388 * the standard delimiters:
392 * go
393 * This routine will check to see if we encounter and END
394 * for the matching nest level of BEGIN ... END statements.
395 * If we find one, then we can assume, the statement was terminated
396 * since we have fallen through to the END statement of the BEGIN
397 * block.
399 if ( nest_lvl > 0 && isKeyword (token, KEYWORD_end) )
401 if ( token->begin_end_nest_lvl == nest_lvl )
402 terminated = TRUE;
405 return terminated;
408 static void buildSqlKeywordHash (void)
410 const size_t count = sizeof (SqlKeywordTable) /
411 sizeof (SqlKeywordTable [0]);
412 size_t i;
413 for (i = 0 ; i < count ; ++i)
415 const keywordDesc* const p = &SqlKeywordTable [i];
416 addKeyword (p->name, Lang_sql, (int) p->id);
420 static tokenInfo *newToken (void)
422 tokenInfo *const token = xMalloc (1, tokenInfo);
424 token->type = TOKEN_UNDEFINED;
425 token->keyword = KEYWORD_NONE;
426 token->string = vStringNew ();
427 token->scope = vStringNew ();
428 token->begin_end_nest_lvl = 0;
429 token->lineNumber = getSourceLineNumber ();
430 token->filePosition = getInputFilePosition ();
432 return token;
435 static void deleteToken (tokenInfo *const token)
437 vStringDelete (token->string);
438 vStringDelete (token->scope);
439 eFree (token);
443 * Tag generation functions
446 static void makeConstTag (tokenInfo *const token, const sqlKind kind)
448 if (SqlKinds [kind].enabled)
450 const char *const name = vStringValue (token->string);
451 tagEntryInfo e;
452 initTagEntry (&e, name);
454 e.lineNumber = token->lineNumber;
455 e.filePosition = token->filePosition;
456 e.kindName = SqlKinds [kind].name;
457 e.kind = SqlKinds [kind].letter;
459 makeTagEntry (&e);
463 static void makeSqlTag (tokenInfo *const token, const sqlKind kind)
465 vString * fulltag;
467 if (SqlKinds [kind].enabled)
470 * If a scope has been added to the token, change the token
471 * string to include the scope when making the tag.
473 if ( vStringLength(token->scope) > 0 )
475 fulltag = vStringNew ();
476 vStringCopy(fulltag, token->scope);
477 vStringCatS (fulltag, ".");
478 vStringCatS (fulltag, vStringValue(token->string));
479 vStringTerminate(fulltag);
480 vStringCopy(token->string, fulltag);
481 vStringDelete (fulltag);
483 makeConstTag (token, kind);
488 * Parsing functions
491 static void parseString (vString *const string, const int delimiter)
493 boolean end = FALSE;
494 while (! end)
496 int c = fileGetc ();
497 if (c == EOF)
498 end = TRUE;
500 else if (c == '\\')
502 c = fileGetc(); // This maybe a ' or ". //
503 vStringPut(string, c);
506 else if (c == delimiter)
507 end = TRUE;
508 else
509 vStringPut (string, c);
511 vStringTerminate (string);
514 /* Read a C identifier beginning with "firstChar" and places it into "name".
516 static void parseIdentifier (vString *const string, const int firstChar)
518 int c = firstChar;
519 Assert (isIdentChar1 (c));
522 vStringPut (string, c);
523 c = fileGetc ();
524 } while (isIdentChar (c));
525 vStringTerminate (string);
526 if (!isspace (c))
527 fileUngetc (c); /* unget non-identifier character */
530 static void readToken (tokenInfo *const token)
532 int c;
534 token->type = TOKEN_UNDEFINED;
535 token->keyword = KEYWORD_NONE;
536 vStringClear (token->string);
538 getNextChar:
541 c = fileGetc ();
542 token->lineNumber = getSourceLineNumber ();
543 token->filePosition = getInputFilePosition ();
545 * Added " to the list of ignores, not sure what this
546 * might break but it gets by this issue:
547 * create table "t1" (...)
549 * Darren, the code passes all my tests for both
550 * Oracle and SQL Anywhere, but maybe you can tell me
551 * what this may effect.
554 while (c == '\t' || c == ' ' || c == '\n');
556 switch (c)
558 case EOF: longjmp (Exception, (int)ExceptionEOF); break;
559 case '(': token->type = TOKEN_OPEN_PAREN; break;
560 case ')': token->type = TOKEN_CLOSE_PAREN; break;
561 case ':': token->type = TOKEN_COLON; break;
562 case ';': token->type = TOKEN_SEMICOLON; break;
563 case '.': token->type = TOKEN_PERIOD; break;
564 case ',': token->type = TOKEN_COMMA; break;
565 case '{': token->type = TOKEN_OPEN_CURLY; break;
566 case '}': token->type = TOKEN_CLOSE_CURLY; break;
567 case '~': token->type = TOKEN_TILDE; break;
568 case '[': token->type = TOKEN_OPEN_SQUARE; break;
569 case ']': token->type = TOKEN_CLOSE_SQUARE; break;
570 case '=': token->type = TOKEN_EQUAL; break;
572 case '\'':
573 case '"':
574 token->type = TOKEN_STRING;
575 parseString (token->string, c);
576 token->lineNumber = getSourceLineNumber ();
577 token->filePosition = getInputFilePosition ();
578 break;
580 case '-':
581 c = fileGetc ();
582 if (c == '-') /* -- is this the start of a comment? */
584 fileSkipToCharacter ('\n');
585 goto getNextChar;
587 else
589 if (!isspace (c))
590 fileUngetc (c);
591 token->type = TOKEN_OPERATOR;
593 break;
595 case '<':
596 case '>':
598 const int initial = c;
599 int d = fileGetc ();
600 if (d == initial)
602 if (initial == '<')
603 token->type = TOKEN_BLOCK_LABEL_BEGIN;
604 else
605 token->type = TOKEN_BLOCK_LABEL_END;
607 else
609 fileUngetc (d);
610 token->type = TOKEN_UNDEFINED;
612 break;
615 case '\\':
616 c = fileGetc ();
617 if (c != '\\' && c != '"' && c != '\'' && !isspace (c))
618 fileUngetc (c);
619 token->type = TOKEN_CHARACTER;
620 token->lineNumber = getSourceLineNumber ();
621 token->filePosition = getInputFilePosition ();
622 break;
624 case '/':
626 int d = fileGetc ();
627 if ( (d != '*') && /* is this the start of a comment? */
628 (d != '/') ) /* is a one line comment? */
630 token->type = TOKEN_FORWARD_SLASH;
631 fileUngetc (d);
633 else
635 if (d == '*')
639 fileSkipToCharacter ('*');
640 c = fileGetc ();
641 if (c == '/')
642 break;
643 else
644 fileUngetc (c);
645 } while (c != EOF && c != '\0');
646 goto getNextChar;
648 else if (d == '/') /* is this the start of a comment? */
650 fileSkipToCharacter ('\n');
651 goto getNextChar;
654 break;
657 default:
658 if (! isIdentChar1 (c))
659 token->type = TOKEN_UNDEFINED;
660 else
662 parseIdentifier (token->string, c);
663 token->lineNumber = getSourceLineNumber ();
664 token->filePosition = getInputFilePosition ();
665 token->keyword = analyzeToken (token->string, Lang_sql);
666 if (isKeyword (token, KEYWORD_rem))
668 vStringClear (token->string);
669 fileSkipToCharacter ('\n');
670 goto getNextChar;
672 else if (isKeyword (token, KEYWORD_NONE))
673 token->type = TOKEN_IDENTIFIER;
674 else
675 token->type = TOKEN_KEYWORD;
677 break;
682 * Token parsing functions
686 * static void addContext (tokenInfo* const parent, const tokenInfo* const child)
688 * if (vStringLength (parent->string) > 0)
690 * vStringCatS (parent->string, ".");
692 * vStringCatS (parent->string, vStringValue(child->string));
693 * vStringTerminate(parent->string);
697 static void addToScope (tokenInfo* const token, vString* const extra)
699 if (vStringLength (token->scope) > 0)
701 vStringCatS (token->scope, ".");
703 vStringCatS (token->scope, vStringValue(extra));
704 vStringTerminate(token->scope);
708 * Scanning functions
711 static void findToken (tokenInfo *const token, const tokenType type)
713 while (! isType (token, type))
715 readToken (token);
719 static void findCmdTerm (tokenInfo *const token, const boolean check_first)
721 int begin_end_nest_lvl = token->begin_end_nest_lvl;
723 if ( check_first )
725 if ( isCmdTerm(token) )
726 return;
730 readToken (token);
731 } while ( !isCmdTerm(token) && !isMatchedEnd(token, begin_end_nest_lvl) );
734 static void skipToMatched(tokenInfo *const token)
736 int nest_level = 0;
737 tokenType open_token;
738 tokenType close_token;
740 switch (token->type)
742 case TOKEN_OPEN_PAREN:
743 open_token = TOKEN_OPEN_PAREN;
744 close_token = TOKEN_CLOSE_PAREN;
745 break;
746 case TOKEN_OPEN_CURLY:
747 open_token = TOKEN_OPEN_CURLY;
748 close_token = TOKEN_CLOSE_CURLY;
749 break;
750 case TOKEN_OPEN_SQUARE:
751 open_token = TOKEN_OPEN_SQUARE;
752 close_token = TOKEN_CLOSE_SQUARE;
753 break;
754 default:
755 return;
759 * This routine will skip to a matching closing token.
760 * It will also handle nested tokens like the (, ) below.
761 * ( name varchar(30), text binary(10) )
764 if (isType (token, open_token))
766 nest_level++;
767 while (! (isType (token, close_token) && (nest_level == 0)))
769 readToken (token);
770 if (isType (token, open_token))
772 nest_level++;
774 if (isType (token, close_token))
776 if (nest_level > 0)
778 nest_level--;
782 readToken (token);
786 static void skipArgumentList (tokenInfo *const token)
789 * Other databases can have arguments with fully declared
790 * datatypes:
791 * ( name varchar(30), text binary(10) )
792 * So we must check for nested open and closing parentheses
795 if (isType (token, TOKEN_OPEN_PAREN)) /* arguments? */
797 skipToMatched (token);
801 static void parseSubProgram (tokenInfo *const token)
803 tokenInfo *const name = newToken ();
806 * This must handle both prototypes and the body of
807 * the procedures.
809 * Prototype:
810 * FUNCTION func_name RETURN integer;
811 * PROCEDURE proc_name( parameters );
812 * Procedure
813 * FUNCTION GET_ML_USERNAME RETURN VARCHAR2
814 * IS
815 * BEGIN
816 * RETURN v_sync_user_id;
817 * END GET_ML_USERNAME;
819 * PROCEDURE proc_name( parameters )
820 * IS
821 * BEGIN
822 * END;
823 * CREATE PROCEDURE proc_name( parameters )
824 * EXTERNAL NAME ... ;
825 * CREATE PROCEDURE proc_name( parameters )
826 * BEGIN
827 * END;
829 * CREATE FUNCTION f_GetClassName(
830 * IN @object VARCHAR(128)
831 * ,IN @code VARCHAR(128)
833 * RETURNS VARCHAR(200)
834 * DETERMINISTIC
835 * BEGIN
837 * IF( @object = 'user_state' ) THEN
838 * SET something = something;
839 * END IF;
841 * RETURN @name;
842 * END;
844 const sqlKind kind = isKeyword (token, KEYWORD_function) ?
845 SQLTAG_FUNCTION : SQLTAG_PROCEDURE;
846 Assert (isKeyword (token, KEYWORD_function) ||
847 isKeyword (token, KEYWORD_procedure));
848 readToken (name);
849 readToken (token);
850 if (isType (token, TOKEN_PERIOD))
852 readToken (name);
853 readToken (token);
855 if (isType (token, TOKEN_OPEN_PAREN))
857 /* Reads to the next token after the TOKEN_CLOSE_PAREN */
858 skipArgumentList(token);
861 if (kind == SQLTAG_FUNCTION)
863 if (isKeyword (token, KEYWORD_return) || isKeyword (token, KEYWORD_returns))
865 /* Read datatype */
866 readToken (token);
868 * Read token after which could be the
869 * command terminator if a prototype
870 * or an open parenthesis
872 readToken (token);
873 if (isType (token, TOKEN_OPEN_PAREN))
875 /* Reads to the next token after the TOKEN_CLOSE_PAREN */
876 skipArgumentList(token);
880 if( isCmdTerm (token) )
882 makeSqlTag (name, SQLTAG_PROTOTYPE);
884 else
886 while (!(isKeyword (token, KEYWORD_is) ||
887 isKeyword (token, KEYWORD_begin) ||
888 isKeyword (token, KEYWORD_at) ||
889 isKeyword (token, KEYWORD_internal) ||
890 isKeyword (token, KEYWORD_external) ||
891 isKeyword (token, KEYWORD_url) ||
892 isType (token, TOKEN_EQUAL) ||
893 isCmdTerm (token)
897 if ( isKeyword (token, KEYWORD_result) )
899 readToken (token);
900 if (isType (token, TOKEN_OPEN_PAREN))
902 /* Reads to the next token after the TOKEN_CLOSE_PAREN */
903 skipArgumentList(token);
905 } else {
906 readToken (token);
909 if (isKeyword (token, KEYWORD_at) ||
910 isKeyword (token, KEYWORD_url) ||
911 isKeyword (token, KEYWORD_internal) ||
912 isKeyword (token, KEYWORD_external) )
914 addToScope(token, name->string);
915 if (isType (name, TOKEN_IDENTIFIER) ||
916 isType (name, TOKEN_STRING) ||
917 !isKeyword (token, KEYWORD_NONE)
919 makeSqlTag (name, kind);
921 vStringClear (token->scope);
923 if ( isType (token, TOKEN_EQUAL) )
924 readToken (token);
926 if ( isKeyword (token, KEYWORD_declare) )
927 parseDeclare (token, FALSE);
929 if (isKeyword (token, KEYWORD_is) ||
930 isKeyword (token, KEYWORD_begin) )
932 addToScope(token, name->string);
933 if (isType (name, TOKEN_IDENTIFIER) ||
934 isType (name, TOKEN_STRING) ||
935 !isKeyword (token, KEYWORD_NONE)
937 makeSqlTag (name, kind);
939 parseBlock (token, TRUE);
940 vStringClear (token->scope);
943 deleteToken (name);
946 static void parseRecord (tokenInfo *const token)
949 * Make it a bit forgiving, this is called from
950 * multiple functions, parseTable, parseType
952 if (!isType (token, TOKEN_OPEN_PAREN))
953 readToken (token);
955 Assert (isType (token, TOKEN_OPEN_PAREN));
958 if ( isType (token, TOKEN_COMMA) || isType (token, TOKEN_OPEN_PAREN) )
959 readToken (token);
962 * Create table statements can end with various constraints
963 * which must be excluded from the SQLTAG_FIELD.
964 * create table t1 (
965 * c1 integer,
966 * c2 char(30),
967 * c3 numeric(10,5),
968 * c4 integer,
969 * constraint whatever,
970 * primary key(c1),
971 * foreign key (),
972 * check ()
975 if (! (isKeyword(token, KEYWORD_primary) ||
976 isKeyword(token, KEYWORD_references) ||
977 isKeyword(token, KEYWORD_unique) ||
978 isKeyword(token, KEYWORD_check) ||
979 isKeyword(token, KEYWORD_constraint) ||
980 isKeyword(token, KEYWORD_foreign) ) )
982 if (isType (token, TOKEN_IDENTIFIER) ||
983 isType (token, TOKEN_STRING))
984 makeSqlTag (token, SQLTAG_FIELD);
987 while (!(isType (token, TOKEN_COMMA) ||
988 isType (token, TOKEN_CLOSE_PAREN) ||
989 isType (token, TOKEN_OPEN_PAREN)
992 readToken (token);
994 * A table structure can look like this:
995 * create table t1 (
996 * c1 integer,
997 * c2 char(30),
998 * c3 numeric(10,5),
999 * c4 integer
1001 * We can't just look for a COMMA or CLOSE_PAREN
1002 * since that will not deal with the numeric(10,5)
1003 * case. So we need to skip the argument list
1004 * when we find an open paren.
1006 if (isType (token, TOKEN_OPEN_PAREN))
1008 /* Reads to the next token after the TOKEN_CLOSE_PAREN */
1009 skipArgumentList(token);
1012 } while (! isType (token, TOKEN_CLOSE_PAREN));
1015 static void parseType (tokenInfo *const token)
1017 tokenInfo *const name = newToken ();
1018 vString * saveScope = vStringNew ();
1020 vStringCopy(saveScope, token->scope);
1021 /* If a scope has been set, add it to the name */
1022 addToScope (name, token->scope);
1023 readToken (name);
1024 if (isType (name, TOKEN_IDENTIFIER))
1026 readToken (token);
1027 if (isKeyword (token, KEYWORD_is))
1029 readToken (token);
1030 addToScope (token, name->string);
1031 switch (token->keyword)
1033 case KEYWORD_record:
1034 case KEYWORD_object:
1035 makeSqlTag (name, SQLTAG_RECORD);
1036 parseRecord (token);
1037 break;
1039 case KEYWORD_table:
1040 makeSqlTag (name, SQLTAG_TABLE);
1041 break;
1043 case KEYWORD_ref:
1044 readToken (token);
1045 if (isKeyword (token, KEYWORD_cursor))
1046 makeSqlTag (name, SQLTAG_CURSOR);
1047 break;
1049 default: break;
1051 vStringClear (token->scope);
1054 vStringCopy(token->scope, saveScope);
1055 deleteToken (name);
1056 vStringDelete(saveScope);
1059 static void parseSimple (tokenInfo *const token, const sqlKind kind)
1061 /* This will simply make the tagname from the first word found */
1062 readToken (token);
1063 if (isType (token, TOKEN_IDENTIFIER) ||
1064 isType (token, TOKEN_STRING))
1065 makeSqlTag (token, kind);
1068 static void parseDeclare (tokenInfo *const token, const boolean local)
1071 * PL/SQL declares are of this format:
1072 * IS|AS
1073 * [declare]
1074 * CURSOR curname ...
1075 * varname1 datatype;
1076 * varname2 datatype;
1077 * varname3 datatype;
1078 * begin
1081 if (isKeyword (token, KEYWORD_declare))
1082 readToken (token);
1083 while (! isKeyword (token, KEYWORD_begin) && ! isKeyword (token, KEYWORD_end))
1085 switch (token->keyword)
1087 case KEYWORD_cursor: parseSimple (token, SQLTAG_CURSOR); break;
1088 case KEYWORD_function: parseSubProgram (token); break;
1089 case KEYWORD_procedure: parseSubProgram (token); break;
1090 case KEYWORD_subtype: parseSimple (token, SQLTAG_SUBTYPE); break;
1091 case KEYWORD_trigger: parseSimple (token, SQLTAG_TRIGGER); break;
1092 case KEYWORD_type: parseType (token); break;
1094 default:
1095 if (isType (token, TOKEN_IDENTIFIER))
1097 if (local)
1099 makeSqlTag (token, SQLTAG_LOCAL_VARIABLE);
1101 else
1103 makeSqlTag (token, SQLTAG_VARIABLE);
1106 break;
1108 findToken (token, TOKEN_SEMICOLON);
1109 readToken (token);
1113 static void parseDeclareANSI (tokenInfo *const token, const boolean local)
1115 tokenInfo *const type = newToken ();
1117 * ANSI declares are of this format:
1118 * BEGIN
1119 * DECLARE varname1 datatype;
1120 * DECLARE varname2 datatype;
1121 * ...
1123 * This differ from PL/SQL where DECLARE preceeds the BEGIN block
1124 * and the DECLARE keyword is not repeated.
1126 while (isKeyword (token, KEYWORD_declare))
1128 readToken (token);
1129 readToken (type);
1131 if (isKeyword (type, KEYWORD_cursor))
1132 makeSqlTag (token, SQLTAG_CURSOR);
1133 else if (isKeyword (token, KEYWORD_local) &&
1134 isKeyword (type, KEYWORD_temporary))
1137 * DECLARE LOCAL TEMPORARY TABLE table_name (
1138 * c1 int,
1139 * c2 int
1140 * );
1142 readToken (token);
1143 if (isKeyword (token, KEYWORD_table))
1145 readToken (token);
1146 if (isType(token, TOKEN_IDENTIFIER) ||
1147 isType(token, TOKEN_STRING) )
1149 makeSqlTag (token, SQLTAG_TABLE);
1153 else if (isType (token, TOKEN_IDENTIFIER) ||
1154 isType (token, TOKEN_STRING))
1156 if (local)
1157 makeSqlTag (token, SQLTAG_LOCAL_VARIABLE);
1158 else
1159 makeSqlTag (token, SQLTAG_VARIABLE);
1161 findToken (token, TOKEN_SEMICOLON);
1162 readToken (token);
1164 deleteToken (type);
1167 static void parseLabel (tokenInfo *const token)
1170 * A label has this format:
1171 * <<tobacco_dependency>>
1172 * DECLARE
1173 * v_senator VARCHAR2(100) := 'THURMOND, JESSE';
1174 * BEGIN
1175 * IF total_contributions (v_senator, 'TOBACCO') > 25000
1176 * THEN
1177 * <<alochol_dependency>>
1178 * DECLARE
1179 * v_senator VARCHAR2(100) := 'WHATEVERIT, TAKES';
1180 * BEGIN
1181 * ...
1184 Assert (isType (token, TOKEN_BLOCK_LABEL_BEGIN));
1185 readToken (token);
1186 if (isType (token, TOKEN_IDENTIFIER))
1188 makeSqlTag (token, SQLTAG_BLOCK_LABEL);
1189 readToken (token); /* read end of label */
1193 static void parseStatements (tokenInfo *const token, const boolean exit_on_endif )
1195 boolean isAnsi = TRUE;
1196 boolean stmtTerm = FALSE;
1200 if (isType (token, TOKEN_BLOCK_LABEL_BEGIN))
1201 parseLabel (token);
1202 else
1204 switch (token->keyword)
1206 case KEYWORD_exception:
1208 * EXCEPTION
1209 * <exception handler>;
1211 * Where an exception handler could be:
1212 * BEGIN
1213 * WHEN OTHERS THEN
1214 * x := x + 3;
1215 * END;
1216 * In this case we need to skip this keyword and
1217 * move on to the next token without reading until
1218 * TOKEN_SEMICOLON;
1220 readToken (token);
1221 continue;
1223 case KEYWORD_when:
1225 * WHEN statements can be used in exception clauses
1226 * and CASE statements. The CASE statement should skip
1227 * these given below we skip over to an END statement.
1228 * But for an exception clause, we can have:
1229 * EXCEPTION
1230 * WHEN OTHERS THEN
1231 * BEGIN
1232 * x := x + 3;
1233 * END;
1234 * If we skip to the TOKEN_SEMICOLON, we miss the begin
1235 * of a nested BEGIN END block. So read the next token
1236 * after the THEN and restart the LOOP.
1238 while (! isKeyword (token, KEYWORD_then))
1239 readToken (token);
1241 readToken (token);
1242 continue;
1244 case KEYWORD_if:
1246 * We do not want to look for a ; since for an empty
1247 * IF block, it would skip over the END.
1248 * IF...THEN
1249 * END IF;
1251 * IF...THEN
1252 * ELSE
1253 * END IF;
1255 * IF...THEN
1256 * ELSEIF...THEN
1257 * ELSE
1258 * END IF;
1260 * or non-ANSI
1261 * IF ...
1262 * BEGIN
1263 * END
1265 while ( ! isKeyword (token, KEYWORD_then) &&
1266 ! isKeyword (token, KEYWORD_begin) )
1268 readToken (token);
1271 if( isKeyword (token, KEYWORD_begin ) )
1273 isAnsi = FALSE;
1274 parseBlock(token, FALSE);
1277 * Handle the non-Ansi IF blocks.
1278 * parseBlock consumes the END, so if the next
1279 * token in a command terminator (like GO)
1280 * we know we are done with this statement.
1282 if ( isCmdTerm (token) )
1283 stmtTerm = TRUE;
1285 else
1287 readToken (token);
1289 while( ! (isKeyword (token, KEYWORD_end ) ||
1290 isKeyword (token, KEYWORD_endif ) )
1293 if ( isKeyword (token, KEYWORD_else) ||
1294 isKeyword (token, KEYWORD_elseif) )
1295 readToken (token);
1297 parseStatements (token, TRUE);
1299 if ( isCmdTerm(token) )
1300 readToken (token);
1305 * parseStatements returns when it finds an END, an IF
1306 * should follow the END for ANSI anyway.
1307 * IF...THEN
1308 * END IF;
1310 if( isKeyword (token, KEYWORD_end ) )
1311 readToken (token);
1313 if( isKeyword (token, KEYWORD_if ) || isKeyword (token, KEYWORD_endif ) )
1315 readToken (token);
1316 if ( isCmdTerm(token) )
1317 stmtTerm = TRUE;
1319 else
1322 * Well we need to do something here.
1323 * There are lots of different END statements
1324 * END;
1325 * END CASE;
1326 * ENDIF;
1327 * ENDCASE;
1331 break;
1333 case KEYWORD_loop:
1334 case KEYWORD_case:
1335 case KEYWORD_for:
1337 * LOOP...
1338 * END LOOP;
1340 * CASE
1341 * WHEN '1' THEN
1342 * END CASE;
1344 * FOR loop_name AS cursor_name CURSOR FOR ...
1345 * DO
1346 * END FOR;
1348 if( isKeyword (token, KEYWORD_for ) )
1350 /* loop name */
1351 readToken (token);
1352 /* AS */
1353 readToken (token);
1355 while ( ! isKeyword (token, KEYWORD_is) )
1358 * If this is not an AS keyword this is
1359 * not a proper FOR statement and should
1360 * simply be ignored
1362 return;
1365 while ( ! isKeyword (token, KEYWORD_do) )
1366 readToken (token);
1370 readToken (token);
1371 while( ! isKeyword (token, KEYWORD_end ) )
1374 if ( isKeyword (token, KEYWORD_else) ||
1375 isKeyword (token, KEYWORD_elseif) )
1376 readToken (token);
1379 parseStatements (token, FALSE);
1381 if ( isCmdTerm(token) )
1382 readToken (token);
1386 if( isKeyword (token, KEYWORD_end ) )
1387 readToken (token);
1390 * Typically ended with
1391 * END LOOP [loop name];
1392 * END CASE
1393 * END FOR [loop name];
1395 if ( isKeyword (token, KEYWORD_loop) ||
1396 isKeyword (token, KEYWORD_case) ||
1397 isKeyword (token, KEYWORD_for) )
1398 readToken (token);
1400 if ( isCmdTerm(token) )
1401 stmtTerm = TRUE;
1403 break;
1405 case KEYWORD_create:
1406 readToken (token);
1407 parseKeywords(token);
1408 break;
1410 case KEYWORD_declare:
1411 case KEYWORD_begin:
1412 parseBlock (token, TRUE);
1413 break;
1415 case KEYWORD_end:
1416 break;
1418 default:
1419 readToken (token);
1420 break;
1423 * Not all statements must end in a semi-colon
1424 * begin
1425 * if current publisher <> 'publish' then
1426 * signal UE_FailStatement
1427 * end if
1428 * end;
1429 * The last statement prior to an end ("signal" above) does
1430 * not need a semi-colon, nor does the end if, since it is
1431 * also the last statement prior to the end of the block.
1433 * So we must read to the first semi-colon or an END block
1435 while ( ! stmtTerm &&
1436 ! ( isKeyword (token, KEYWORD_end) ||
1437 (isCmdTerm(token)) )
1440 if ( isKeyword (token, KEYWORD_endif) &&
1441 exit_on_endif )
1442 return;
1444 if (isType (token, TOKEN_COLON) )
1447 * A : can signal a loop name
1448 * myloop:
1449 * LOOP
1450 * LEAVE myloop;
1451 * END LOOP;
1452 * Unfortunately, labels do not have a
1453 * cmd terminator, therefore we have to check
1454 * if the next token is a keyword and process
1455 * it accordingly.
1457 readToken (token);
1458 if ( isKeyword (token, KEYWORD_loop) ||
1459 isKeyword (token, KEYWORD_while) ||
1460 isKeyword (token, KEYWORD_for) )
1461 /* parseStatements (token); */
1462 return;
1465 readToken (token);
1467 if (isType (token, TOKEN_OPEN_PAREN) ||
1468 isType (token, TOKEN_OPEN_CURLY) ||
1469 isType (token, TOKEN_OPEN_SQUARE) )
1470 skipToMatched (token);
1473 * Since we know how to parse various statements
1474 * if we detect them, parse them to completion
1476 if (isType (token, TOKEN_BLOCK_LABEL_BEGIN) ||
1477 isKeyword (token, KEYWORD_exception) ||
1478 isKeyword (token, KEYWORD_loop) ||
1479 isKeyword (token, KEYWORD_case) ||
1480 isKeyword (token, KEYWORD_for) ||
1481 isKeyword (token, KEYWORD_begin) )
1482 parseStatements (token, FALSE);
1483 else if (isKeyword (token, KEYWORD_if))
1484 parseStatements (token, TRUE);
1489 * We assumed earlier all statements ended with a command terminator.
1490 * See comment above, now, only read if the current token
1491 * is not a command terminator.
1493 if ( isCmdTerm(token) && ! stmtTerm )
1494 stmtTerm = TRUE;
1496 } while (! isKeyword (token, KEYWORD_end) &&
1497 ! (exit_on_endif && isKeyword (token, KEYWORD_endif) ) &&
1498 ! stmtTerm );
1501 static void parseBlock (tokenInfo *const token, const boolean local)
1503 if (isType (token, TOKEN_BLOCK_LABEL_BEGIN))
1505 parseLabel (token);
1506 readToken (token);
1508 if (! isKeyword (token, KEYWORD_begin))
1510 readToken (token);
1512 * These are Oracle style declares which generally come
1513 * between an IS/AS and BEGIN block.
1515 parseDeclare (token, local);
1517 if (isKeyword (token, KEYWORD_begin))
1519 readToken (token);
1521 * Check for ANSI declarations which always follow
1522 * a BEGIN statement. This routine will not advance
1523 * the token if none are found.
1525 parseDeclareANSI (token, local);
1526 token->begin_end_nest_lvl++;
1527 while (! isKeyword (token, KEYWORD_end))
1529 parseStatements (token, FALSE);
1531 if ( isCmdTerm(token) )
1532 readToken (token);
1534 token->begin_end_nest_lvl--;
1537 * Read the next token (we will assume
1538 * it is the command delimiter)
1540 readToken (token);
1543 * Check if the END block is terminated
1545 if ( !isCmdTerm (token) )
1548 * Not sure what to do here at the moment.
1549 * I think the routine that calls parseBlock
1550 * must expect the next token has already
1551 * been read since it is possible this
1552 * token is not a command delimiter.
1554 /* findCmdTerm (token, FALSE); */
1559 static void parsePackage (tokenInfo *const token)
1562 * Packages can be specified in a number of ways:
1563 * CREATE OR REPLACE PACKAGE pkg_name AS
1564 * or
1565 * CREATE OR REPLACE PACKAGE owner.pkg_name AS
1566 * or by specifying a package body
1567 * CREATE OR REPLACE PACKAGE BODY pkg_name AS
1568 * CREATE OR REPLACE PACKAGE BODY owner.pkg_name AS
1570 tokenInfo *const name = newToken ();
1571 readToken (name);
1572 if (isKeyword (name, KEYWORD_body))
1575 * Ignore the BODY tag since we will process
1576 * the body or prototypes in the same manner
1578 readToken (name);
1580 /* Check for owner.pkg_name */
1581 while (! isKeyword (token, KEYWORD_is))
1583 readToken (token);
1584 if ( isType(token, TOKEN_PERIOD) )
1586 readToken (name);
1589 if (isKeyword (token, KEYWORD_is))
1591 if (isType (name, TOKEN_IDENTIFIER) ||
1592 isType (name, TOKEN_STRING))
1593 makeSqlTag (name, SQLTAG_PACKAGE);
1594 parseBlock (token, FALSE);
1596 findCmdTerm (token, FALSE);
1597 deleteToken (name);
1600 static void parseTable (tokenInfo *const token)
1602 tokenInfo *const name = newToken ();
1605 * This deals with these formats:
1606 * create table t1 (c1 int);
1607 * create global tempoary table t2 (c1 int);
1608 * create table "t3" (c1 int);
1609 * create table bob.t4 (c1 int);
1610 * create table bob."t5" (c1 int);
1611 * create table "bob"."t6" (c1 int);
1612 * create table bob."t7" (c1 int);
1613 * Proxy tables use this format:
1614 * create existing table bob."t7" AT '...';
1615 * SQL Server and Sybase formats
1616 * create table OnlyTable (
1617 * create table dbo.HasOwner (
1618 * create table [dbo].[HasOwnerSquare] (
1619 * create table master.dbo.HasDb (
1620 * create table master..HasDbNoOwner (
1621 * create table [master].dbo.[HasDbAndOwnerSquare] (
1622 * create table [master]..[HasDbNoOwnerSquare] (
1625 /* This could be a database, owner or table name */
1626 readToken (name);
1627 if (isType (name, TOKEN_OPEN_SQUARE))
1629 readToken (name);
1630 /* Read close square */
1631 readToken (token);
1633 readToken (token);
1634 if (isType (token, TOKEN_PERIOD))
1637 * This could be a owner or table name.
1638 * But this is also a special case since the table can be
1639 * referenced with a blank owner:
1640 * dbname..tablename
1642 readToken (name);
1643 if (isType (name, TOKEN_OPEN_SQUARE))
1645 readToken (name);
1646 /* Read close square */
1647 readToken (token);
1649 /* Check if a blank name was provided */
1650 if (isType (name, TOKEN_PERIOD))
1652 readToken (name);
1653 if (isType (name, TOKEN_OPEN_SQUARE))
1655 readToken (name);
1656 /* Read close square */
1657 readToken (token);
1660 readToken (token);
1661 if (isType (token, TOKEN_PERIOD))
1663 /* This can only be the table name */
1664 readToken (name);
1665 if (isType (name, TOKEN_OPEN_SQUARE))
1667 readToken (name);
1668 /* Read close square */
1669 readToken (token);
1671 readToken (token);
1674 if (isType (token, TOKEN_OPEN_PAREN))
1676 if (isType (name, TOKEN_IDENTIFIER) ||
1677 isType (name, TOKEN_STRING))
1679 makeSqlTag (name, SQLTAG_TABLE);
1680 vStringCopy(token->scope, name->string);
1681 parseRecord (token);
1682 vStringClear (token->scope);
1685 else if (isKeyword (token, KEYWORD_at))
1687 if (isType (name, TOKEN_IDENTIFIER))
1689 makeSqlTag (name, SQLTAG_TABLE);
1692 findCmdTerm (token, FALSE);
1693 deleteToken (name);
1696 static void parseIndex (tokenInfo *const token)
1698 tokenInfo *const name = newToken ();
1699 tokenInfo *const owner = newToken ();
1702 * This deals with these formats
1703 * create index i1 on t1(c1) create index "i2" on t1(c1)
1704 * create virtual unique clustered index "i3" on t1(c1)
1705 * create unique clustered index "i4" on t1(c1)
1706 * create clustered index "i5" on t1(c1)
1707 * create bitmap index "i6" on t1(c1)
1710 readToken (name);
1711 readToken (token);
1712 if (isType (token, TOKEN_PERIOD))
1714 readToken (name);
1715 readToken (token);
1717 if ( isKeyword (token, KEYWORD_on) &&
1718 (isType (name, TOKEN_IDENTIFIER) || isType (name, TOKEN_STRING) ) )
1720 readToken (owner);
1721 readToken (token);
1722 if (isType (token, TOKEN_PERIOD))
1724 readToken (owner);
1725 readToken (token);
1727 addToScope(name, owner->string);
1728 makeSqlTag (name, SQLTAG_INDEX);
1730 findCmdTerm (token, FALSE);
1731 deleteToken (name);
1732 deleteToken (owner);
1735 static void parseEvent (tokenInfo *const token)
1737 tokenInfo *const name = newToken ();
1740 * This deals with these formats
1741 * create event e1 handler begin end;
1742 * create event "e2" handler begin end;
1743 * create event dba."e3" handler begin end;
1744 * create event "dba"."e4" handler begin end;
1747 readToken (name);
1748 readToken (token);
1749 if (isType (token, TOKEN_PERIOD))
1751 readToken (name);
1753 while (! (isKeyword (token, KEYWORD_handler) ||
1754 (isType (token, TOKEN_SEMICOLON))) )
1756 readToken (token);
1759 if ( isKeyword (token, KEYWORD_handler) ||
1760 isType (token, TOKEN_SEMICOLON) )
1762 makeSqlTag (name, SQLTAG_EVENT);
1765 if (isKeyword (token, KEYWORD_handler))
1767 readToken (token);
1768 if ( isKeyword (token, KEYWORD_begin) )
1770 parseBlock (token, TRUE);
1772 findCmdTerm (token, TRUE);
1774 deleteToken (name);
1777 static void parseTrigger (tokenInfo *const token)
1779 tokenInfo *const name = newToken ();
1780 tokenInfo *const table = newToken ();
1783 * This deals with these formats
1784 * create or replace trigger tr1 begin end;
1785 * create trigger "tr2" begin end;
1786 * drop trigger "droptr1";
1787 * create trigger "tr3" CALL sp_something();
1788 * create trigger "owner"."tr4" begin end;
1789 * create trigger "tr5" not valid;
1790 * create trigger "tr6" begin end;
1793 readToken (name);
1794 readToken (token);
1795 if (isType (token, TOKEN_PERIOD))
1797 readToken (name);
1798 readToken (token);
1801 while ( !isKeyword (token, KEYWORD_on) &&
1802 !isCmdTerm (token) )
1804 readToken (token);
1807 /*if (! isType (token, TOKEN_SEMICOLON) ) */
1808 if (! isCmdTerm (token) )
1810 readToken (table);
1811 readToken (token);
1812 if (isType (token, TOKEN_PERIOD))
1814 readToken (table);
1815 readToken (token);
1818 while (! (isKeyword (token, KEYWORD_begin) ||
1819 (isKeyword (token, KEYWORD_call)) ||
1820 ( isCmdTerm (token))) )
1822 if ( isKeyword (token, KEYWORD_declare) )
1824 addToScope(token, name->string);
1825 parseDeclare(token, TRUE);
1826 vStringClear(token->scope);
1828 else
1829 readToken (token);
1832 if ( isKeyword (token, KEYWORD_begin) ||
1833 isKeyword (token, KEYWORD_call) )
1835 addToScope(name, table->string);
1836 makeSqlTag (name, SQLTAG_TRIGGER);
1837 addToScope(token, table->string);
1838 if ( isKeyword (token, KEYWORD_begin) )
1840 parseBlock (token, TRUE);
1842 vStringClear(token->scope);
1846 findCmdTerm (token, TRUE);
1847 deleteToken (name);
1848 deleteToken (table);
1851 static void parsePublication (tokenInfo *const token)
1853 tokenInfo *const name = newToken ();
1856 * This deals with these formats
1857 * create or replace publication pu1 ()
1858 * create publication "pu2" ()
1859 * create publication dba."pu3" ()
1860 * create publication "dba"."pu4" ()
1863 readToken (name);
1864 readToken (token);
1865 if (isType (token, TOKEN_PERIOD))
1867 readToken (name);
1868 readToken (token);
1870 if (isType (token, TOKEN_OPEN_PAREN))
1872 if (isType (name, TOKEN_IDENTIFIER) ||
1873 isType (name, TOKEN_STRING))
1875 makeSqlTag (name, SQLTAG_PUBLICATION);
1878 findCmdTerm (token, FALSE);
1879 deleteToken (name);
1882 static void parseService (tokenInfo *const token)
1884 tokenInfo *const name = newToken ();
1887 * This deals with these formats
1888 * CREATE SERVICE s1 TYPE 'HTML'
1889 * AUTHORIZATION OFF USER DBA AS
1890 * SELECT *
1891 * FROM SYS.SYSTABLE;
1892 * CREATE SERVICE "s2" TYPE 'HTML'
1893 * AUTHORIZATION OFF USER DBA AS
1894 * CALL sp_Something();
1897 readToken (name);
1898 readToken (token);
1899 if (isKeyword (token, KEYWORD_type))
1901 if (isType (name, TOKEN_IDENTIFIER) ||
1902 isType (name, TOKEN_STRING))
1904 makeSqlTag (name, SQLTAG_SERVICE);
1907 findCmdTerm (token, FALSE);
1908 deleteToken (name);
1911 static void parseDomain (tokenInfo *const token)
1913 tokenInfo *const name = newToken ();
1916 * This deals with these formats
1917 * CREATE DOMAIN|DATATYPE [AS] your_name ...;
1920 readToken (name);
1921 if (isKeyword (name, KEYWORD_is))
1923 readToken (name);
1925 readToken (token);
1926 if (isType (name, TOKEN_IDENTIFIER) ||
1927 isType (name, TOKEN_STRING))
1929 makeSqlTag (name, SQLTAG_DOMAIN);
1931 findCmdTerm (token, FALSE);
1932 deleteToken (name);
1935 static void parseDrop (tokenInfo *const token)
1938 * This deals with these formats
1939 * DROP TABLE|PROCEDURE|DOMAIN|DATATYPE name;
1941 * Just simply skip over these statements.
1942 * They are often confused with PROCEDURE prototypes
1943 * since the syntax is similar, this effectively deals with
1944 * the issue for all types.
1947 findCmdTerm (token, FALSE);
1950 static void parseVariable (tokenInfo *const token)
1952 tokenInfo *const name = newToken ();
1955 * This deals with these formats
1956 * create variable varname1 integer;
1957 * create variable @varname2 integer;
1958 * create variable "varname3" integer;
1959 * drop variable @varname3;
1962 readToken (name);
1963 readToken (token);
1964 if ( (isType (name, TOKEN_IDENTIFIER) || isType (name, TOKEN_STRING))
1965 && !isType (token, TOKEN_SEMICOLON) )
1967 makeSqlTag (name, SQLTAG_VARIABLE);
1969 findCmdTerm (token, TRUE);
1971 deleteToken (name);
1974 static void parseSynonym (tokenInfo *const token)
1976 tokenInfo *const name = newToken ();
1979 * This deals with these formats
1980 * create variable varname1 integer;
1981 * create variable @varname2 integer;
1982 * create variable "varname3" integer;
1983 * drop variable @varname3;
1986 readToken (name);
1987 readToken (token);
1988 if ( (isType (name, TOKEN_IDENTIFIER) || isType (name, TOKEN_STRING))
1989 && isKeyword (token, KEYWORD_for) )
1991 makeSqlTag (name, SQLTAG_SYNONYM);
1993 findCmdTerm (token, TRUE);
1995 deleteToken (name);
1998 static void parseView (tokenInfo *const token)
2000 tokenInfo *const name = newToken ();
2003 * This deals with these formats
2004 * create variable varname1 integer;
2005 * create variable @varname2 integer;
2006 * create variable "varname3" integer;
2007 * drop variable @varname3;
2010 readToken (name);
2011 readToken (token);
2012 if (isType (token, TOKEN_PERIOD))
2014 readToken (name);
2015 readToken (token);
2017 if ( isType (token, TOKEN_OPEN_PAREN) )
2019 skipArgumentList(token);
2023 while (!(isKeyword (token, KEYWORD_is) ||
2024 isType (token, TOKEN_SEMICOLON)
2027 readToken (token);
2030 if ( (isType (name, TOKEN_IDENTIFIER) || isType (name, TOKEN_STRING))
2031 && isKeyword (token, KEYWORD_is) )
2033 makeSqlTag (name, SQLTAG_VIEW);
2036 findCmdTerm (token, TRUE);
2038 deleteToken (name);
2041 static void parseMLTable (tokenInfo *const token)
2043 tokenInfo *const version = newToken ();
2044 tokenInfo *const table = newToken ();
2045 tokenInfo *const event = newToken ();
2048 * This deals with these formats
2049 * call dbo.ml_add_table_script( 'version', 'table_name', 'event',
2050 * 'some SQL statement'
2051 * );
2054 readToken (token);
2055 if ( isType (token, TOKEN_OPEN_PAREN) )
2057 readToken (version);
2058 readToken (token);
2059 while (!(isType (token, TOKEN_COMMA) ||
2060 isType (token, TOKEN_CLOSE_PAREN)
2063 readToken (token);
2066 if (isType (token, TOKEN_COMMA))
2068 readToken (table);
2069 readToken (token);
2070 while (!(isType (token, TOKEN_COMMA) ||
2071 isType (token, TOKEN_CLOSE_PAREN)
2074 readToken (token);
2077 if (isType (token, TOKEN_COMMA))
2079 readToken (event);
2081 if (isType (version, TOKEN_STRING) &&
2082 isType (table, TOKEN_STRING) &&
2083 isType (event, TOKEN_STRING) )
2085 addToScope(version, table->string);
2086 addToScope(version, event->string);
2087 makeSqlTag (version, SQLTAG_MLTABLE);
2090 if( !isType (token, TOKEN_CLOSE_PAREN) )
2091 findToken (token, TOKEN_CLOSE_PAREN);
2095 findCmdTerm (token, TRUE);
2097 deleteToken (version);
2098 deleteToken (table);
2099 deleteToken (event);
2102 static void parseMLConn (tokenInfo *const token)
2104 tokenInfo *const version = newToken ();
2105 tokenInfo *const event = newToken ();
2108 * This deals with these formats
2109 * call ml_add_connection_script( 'version', 'event',
2110 * 'some SQL statement'
2111 * );
2114 readToken (token);
2115 if ( isType (token, TOKEN_OPEN_PAREN) )
2117 readToken (version);
2118 readToken (token);
2119 while (!(isType (token, TOKEN_COMMA) ||
2120 isType (token, TOKEN_CLOSE_PAREN)
2123 readToken (token);
2126 if (isType (token, TOKEN_COMMA))
2128 readToken (event);
2130 if (isType (version, TOKEN_STRING) &&
2131 isType (event, TOKEN_STRING) )
2133 addToScope(version, event->string);
2134 makeSqlTag (version, SQLTAG_MLCONN);
2137 if( !isType (token, TOKEN_CLOSE_PAREN) )
2138 findToken (token, TOKEN_CLOSE_PAREN);
2142 findCmdTerm (token, TRUE);
2144 deleteToken (version);
2145 deleteToken (event);
2148 static void parseMLProp (tokenInfo *const token)
2150 tokenInfo *const component = newToken ();
2151 tokenInfo *const prop_set_name = newToken ();
2152 tokenInfo *const prop_name = newToken ();
2155 * This deals with these formats
2156 * ml_add_property (
2157 * 'comp_name',
2158 * 'prop_set_name',
2159 * 'prop_name',
2160 * 'prop_value'
2164 readToken (token);
2165 if ( isType (token, TOKEN_OPEN_PAREN) )
2167 readToken (component);
2168 readToken (token);
2169 while (!(isType (token, TOKEN_COMMA) ||
2170 isType (token, TOKEN_CLOSE_PAREN)
2173 readToken (token);
2176 if (isType (token, TOKEN_COMMA))
2178 readToken (prop_set_name);
2179 readToken (token);
2180 while (!(isType (token, TOKEN_COMMA) ||
2181 isType (token, TOKEN_CLOSE_PAREN)
2184 readToken (token);
2187 if (isType (token, TOKEN_COMMA))
2189 readToken (prop_name);
2191 if (isType (component, TOKEN_STRING) &&
2192 isType (prop_set_name, TOKEN_STRING) &&
2193 isType (prop_name, TOKEN_STRING) )
2195 addToScope(component, prop_set_name->string);
2196 addToScope(component, prop_name->string);
2197 makeSqlTag (component, SQLTAG_MLPROP);
2200 if( !isType (token, TOKEN_CLOSE_PAREN) )
2201 findToken (token, TOKEN_CLOSE_PAREN);
2205 findCmdTerm (token, TRUE);
2207 deleteToken (component);
2208 deleteToken (prop_set_name);
2209 deleteToken (prop_name);
2212 static void parseComment (tokenInfo *const token)
2215 * This deals with this statement:
2216 * COMMENT TO PRESERVE FORMAT ON PROCEDURE "DBA"."test" IS
2217 * {create PROCEDURE DBA."test"()
2218 * BEGIN
2219 * signal dave;
2220 * END
2223 * The comment can contain anything between the CURLY
2224 * braces
2225 * COMMENT ON USER "admin" IS
2226 * 'Administration Group'
2228 * Or it could be a simple string with no curly braces
2230 while (! isKeyword (token, KEYWORD_is))
2232 readToken (token);
2234 readToken (token);
2235 if ( isType(token, TOKEN_OPEN_CURLY) )
2237 findToken (token, TOKEN_CLOSE_CURLY);
2240 findCmdTerm (token, TRUE);
2244 static void parseKeywords (tokenInfo *const token)
2246 switch (token->keyword)
2248 case KEYWORD_begin: parseBlock (token, FALSE); break;
2249 case KEYWORD_comment: parseComment (token); break;
2250 case KEYWORD_cursor: parseSimple (token, SQLTAG_CURSOR); break;
2251 case KEYWORD_datatype: parseDomain (token); break;
2252 case KEYWORD_declare: parseBlock (token, FALSE); break;
2253 case KEYWORD_domain: parseDomain (token); break;
2254 case KEYWORD_drop: parseDrop (token); break;
2255 case KEYWORD_event: parseEvent (token); break;
2256 case KEYWORD_function: parseSubProgram (token); break;
2257 case KEYWORD_if: parseStatements (token, FALSE); break;
2258 case KEYWORD_index: parseIndex (token); break;
2259 case KEYWORD_ml_table: parseMLTable (token); break;
2260 case KEYWORD_ml_table_lang: parseMLTable (token); break;
2261 case KEYWORD_ml_table_dnet: parseMLTable (token); break;
2262 case KEYWORD_ml_table_java: parseMLTable (token); break;
2263 case KEYWORD_ml_table_chk: parseMLTable (token); break;
2264 case KEYWORD_ml_conn: parseMLConn (token); break;
2265 case KEYWORD_ml_conn_lang: parseMLConn (token); break;
2266 case KEYWORD_ml_conn_dnet: parseMLConn (token); break;
2267 case KEYWORD_ml_conn_java: parseMLConn (token); break;
2268 case KEYWORD_ml_conn_chk: parseMLConn (token); break;
2269 case KEYWORD_ml_prop: parseMLProp (token); break;
2270 case KEYWORD_package: parsePackage (token); break;
2271 case KEYWORD_procedure: parseSubProgram (token); break;
2272 case KEYWORD_publication: parsePublication (token); break;
2273 case KEYWORD_service: parseService (token); break;
2274 case KEYWORD_subtype: parseSimple (token, SQLTAG_SUBTYPE); break;
2275 case KEYWORD_synonym: parseSynonym (token); break;
2276 case KEYWORD_table: parseTable (token); break;
2277 case KEYWORD_trigger: parseTrigger (token); break;
2278 case KEYWORD_type: parseType (token); break;
2279 case KEYWORD_variable: parseVariable (token); break;
2280 case KEYWORD_view: parseView (token); break;
2281 default: break;
2285 static void parseSqlFile (tokenInfo *const token)
2289 readToken (token);
2291 if (isType (token, TOKEN_BLOCK_LABEL_BEGIN))
2292 parseLabel (token);
2293 else
2294 parseKeywords (token);
2295 } while (! isKeyword (token, KEYWORD_end));
2298 static void initialize (const langType language)
2300 Assert (sizeof (SqlKinds) / sizeof (SqlKinds [0]) == SQLTAG_COUNT);
2301 Lang_sql = language;
2302 buildSqlKeywordHash ();
2305 static void findSqlTags (void)
2307 tokenInfo *const token = newToken ();
2308 exception_t exception = (exception_t) (setjmp (Exception));
2310 while (exception == ExceptionNone)
2311 parseSqlFile (token);
2313 deleteToken (token);
2316 extern parserDefinition* SqlParser (void)
2318 static const char *const extensions [] = { "sql", NULL };
2319 parserDefinition* def = parserNew ("SQL");
2320 def->kinds = SqlKinds;
2321 def->kindCount = KIND_COUNT (SqlKinds);
2322 def->extensions = extensions;
2323 def->parser = findSqlTags;
2324 def->initialize = initialize;
2325 return def;
2328 /* vi:set tabstop=4 shiftwidth=4 noexpandtab: */