Fix one-off leak by allocating PropertyDialogElements on the stack
[geany-mirror.git] / tagmanager / sql.c
blob518b36211a8dd61161fb7d06bf6a7491efa78ac0
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.
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 "entry.h"
23 #include "keyword.h"
24 #include "parse.h"
25 #include "main.h"
26 #include "read.h"
27 #include "vstring.h"
30 * On-line PL/SQL Reference Guide:
31 * http://info-it.umsystem.edu/oradocs/doc/server/doc/PLS23/toc.htm
33 * Sample PL/SQL code is available from:
34 * http://www.orafaq.com/faqscrpt.htm#GENPLSQL
38 * MACROS
40 #define isType(token,t) (boolean) ((token)->type == (t))
41 #define isKeyword(token,k) (boolean) ((token)->keyword == (k))
44 * DATA DECLARATIONS
47 typedef enum eException { ExceptionNone, ExceptionEOF } exception_t;
49 /* Used to specify type of keyword.
51 typedef enum eKeywordId {
52 KEYWORD_NONE = -1,
53 KEYWORD_is,
54 KEYWORD_begin,
55 KEYWORD_body,
56 KEYWORD_cursor,
57 KEYWORD_declare,
58 KEYWORD_end,
59 KEYWORD_function,
60 KEYWORD_if,
61 KEYWORD_loop,
62 KEYWORD_case,
63 KEYWORD_for,
64 KEYWORD_call,
65 KEYWORD_package,
66 KEYWORD_pragma,
67 KEYWORD_procedure,
68 KEYWORD_record,
69 KEYWORD_object,
70 KEYWORD_ref,
71 KEYWORD_rem,
72 KEYWORD_return,
73 KEYWORD_returns,
74 KEYWORD_subtype,
75 KEYWORD_table,
76 KEYWORD_trigger,
77 KEYWORD_type,
78 KEYWORD_index,
79 KEYWORD_event,
80 KEYWORD_publication,
81 KEYWORD_service,
82 KEYWORD_domain,
83 KEYWORD_datatype,
84 KEYWORD_result,
85 KEYWORD_when,
86 KEYWORD_then,
87 KEYWORD_variable,
88 KEYWORD_exception,
89 KEYWORD_at,
90 KEYWORD_on,
91 KEYWORD_primary,
92 KEYWORD_references,
93 KEYWORD_unique,
94 KEYWORD_check,
95 KEYWORD_constraint,
96 KEYWORD_foreign,
97 KEYWORD_ml_table,
98 KEYWORD_ml_conn,
99 KEYWORD_local,
100 KEYWORD_temporary,
101 KEYWORD_drop,
102 KEYWORD_view,
103 KEYWORD_synonym,
104 KEYWORD_handler
105 } keywordId;
107 /* Used to determine whether keyword is valid for the token language and
108 * what its ID is.
110 typedef struct sKeywordDesc {
111 const char *name;
112 keywordId id;
113 } keywordDesc;
115 typedef enum eTokenType {
116 TOKEN_UNDEFINED,
117 TOKEN_BLOCK_LABEL_BEGIN,
118 TOKEN_BLOCK_LABEL_END,
119 TOKEN_CHARACTER,
120 TOKEN_CLOSE_PAREN,
121 TOKEN_SEMICOLON,
122 TOKEN_COMMA,
123 TOKEN_IDENTIFIER,
124 TOKEN_KEYWORD,
125 TOKEN_OPEN_PAREN,
126 TOKEN_OPERATOR,
127 TOKEN_OTHER,
128 TOKEN_STRING,
129 TOKEN_PERIOD
130 } tokenType;
132 typedef struct sTokenInfo {
133 tokenType type;
134 keywordId keyword;
135 vString * string;
136 vString * scope;
137 unsigned long lineNumber;
138 MIOPos filePosition;
139 } tokenInfo;
142 * DATA DEFINITIONS
145 static langType Lang_sql;
147 static jmp_buf Exception;
149 typedef enum {
150 SQLTAG_CURSOR,
151 SQLTAG_PROTOTYPE,
152 SQLTAG_FUNCTION,
153 SQLTAG_FIELD,
154 SQLTAG_LOCAL_VARIABLE,
155 SQLTAG_BLOCK_LABEL,
156 SQLTAG_PACKAGE,
157 SQLTAG_PROCEDURE,
158 SQLTAG_RECORD,
159 SQLTAG_SUBTYPE,
160 SQLTAG_TABLE,
161 SQLTAG_TRIGGER,
162 SQLTAG_VARIABLE,
163 SQLTAG_INDEX,
164 SQLTAG_EVENT,
165 SQLTAG_PUBLICATION,
166 SQLTAG_SERVICE,
167 SQLTAG_DOMAIN,
168 SQLTAG_VIEW,
169 SQLTAG_SYNONYM,
170 SQLTAG_MLTABLE,
171 SQLTAG_MLCONN,
172 SQLTAG_COUNT
173 } sqlKind;
175 static kindOption SqlKinds [] = {
176 { TRUE, 'c', "cursor", "cursors" },
177 { FALSE, 'd', "prototype", "prototypes" },
178 { TRUE, 'f', "function", "functions" },
179 { TRUE, 'F', "field", "record fields" },
180 { FALSE, 'l', "local", "local variables" },
181 { TRUE, 'L', "label", "block label" },
182 { TRUE, 'P', "package", "packages" },
183 { TRUE, 'n', "namespace", "procedures" },
184 { FALSE, 'r', "record", "records" },
185 { TRUE, 's', "subtype", "subtypes" },
186 { TRUE, 't', "class", "tables" },
187 { TRUE, 'T', "macro", "triggers" },
188 { TRUE, 'v', "variable", "variables" },
189 { TRUE, 'i', "struct", "indexes" },
190 { TRUE, 'e', "event", "events" },
191 { TRUE, 'U', "publication", "publications" },
192 { TRUE, 'R', "service", "services" },
193 { TRUE, 'D', "domain", "domains" },
194 { TRUE, 'm', "member", "views" },
195 { TRUE, 'n', "synonym", "synonyms" },
196 { TRUE, 'x', "mltable", "MobiLink Table Scripts" },
197 { TRUE, 'y', "mlconn", "MobiLink Conn Scripts" }
200 static const keywordDesc SqlKeywordTable [] = {
201 /* keyword keyword ID */
202 { "as", KEYWORD_is },
203 { "begin", KEYWORD_begin },
204 { "body", KEYWORD_body },
205 { "cursor", KEYWORD_cursor },
206 { "declare", KEYWORD_declare },
207 { "end", KEYWORD_end },
208 { "function", KEYWORD_function },
209 { "if", KEYWORD_if },
210 { "is", KEYWORD_is },
211 { "loop", KEYWORD_loop },
212 { "case", KEYWORD_case },
213 { "for", KEYWORD_for },
214 { "call", KEYWORD_call },
215 { "package", KEYWORD_package },
216 { "pragma", KEYWORD_pragma },
217 { "procedure", KEYWORD_procedure },
218 { "record", KEYWORD_record },
219 { "object", KEYWORD_object },
220 { "ref", KEYWORD_ref },
221 { "rem", KEYWORD_rem },
222 { "return", KEYWORD_return },
223 { "returns", KEYWORD_returns },
224 { "subtype", KEYWORD_subtype },
225 { "table", KEYWORD_table },
226 { "trigger", KEYWORD_trigger },
227 { "type", KEYWORD_type },
228 { "index", KEYWORD_index },
229 { "event", KEYWORD_event },
230 { "publication", KEYWORD_publication },
231 { "service", KEYWORD_service },
232 { "domain", KEYWORD_domain },
233 { "datatype", KEYWORD_datatype },
234 { "result", KEYWORD_result },
235 { "when", KEYWORD_when },
236 { "then", KEYWORD_then },
237 { "variable", KEYWORD_variable },
238 { "exception", KEYWORD_exception },
239 { "at", KEYWORD_at },
240 { "on", KEYWORD_on },
241 { "primary", KEYWORD_primary },
242 { "references", KEYWORD_references },
243 { "unique", KEYWORD_unique },
244 { "check", KEYWORD_check },
245 { "constraint", KEYWORD_constraint },
246 { "foreign", KEYWORD_foreign },
247 { "ml_add_table_script", KEYWORD_ml_table },
248 { "ml_add_connection_script", KEYWORD_ml_conn },
249 { "local", KEYWORD_local },
250 { "temporary", KEYWORD_temporary },
251 { "drop", KEYWORD_drop },
252 { "view", KEYWORD_view },
253 { "synonym", KEYWORD_synonym },
254 { "handler", KEYWORD_handler }
258 * FUNCTION DECLARATIONS
261 static void parseBlock (tokenInfo *const token, const boolean local);
262 static void makeConstTag (tokenInfo *const token, const sqlKind kind);
265 * DEBUG function
268 static void dispToken (tokenInfo *const token, const char * location)
270 #ifdef DEBUG
271 if ( isKeyword(token, KEYWORD_NONE) )
273 if ( isType(token, TOKEN_IDENTIFIER) || isType(token, TOKEN_STRING) )
275 printf( "\n%s: token string t:%s s:%s l:%lu p:%d\n"
276 , location
277 , vStringValue(token->string)
278 , vStringValue(token->scope)
279 , token->lineNumber
280 , token->bufferPosition
282 } else {
283 printf( "\n%s: token t:%d s:%s l:%lu p:%d\n"
284 , location
285 , token->type
286 , vStringValue(token->scope)
287 , token->lineNumber
288 , token->bufferPosition
291 } else {
292 printf( "\n%s: keyword:%s k:%d s:%s l:%lu p:%d\n"
293 , location
294 , vStringValue(token->string)
295 , token->keyword
296 , vStringValue(token->scope)
297 , token->lineNumber
298 , token->bufferPosition
301 #endif
305 * FUNCTION DEFINITIONS
308 static boolean isIdentChar1 (const int c)
310 /* Other databases are less restrictive on the first character of
311 * an identifier.
312 * isIdentChar1 is used to identify the first character of an
313 * identifier, so we are removing some restrictions. */
314 return (boolean)
315 (isalpha (c) || c == '@' || c == '_' );
318 static boolean isIdentChar (const int c)
320 return (boolean)
321 (isalpha (c) || isdigit (c) || c == '$' ||
322 c == '@' || c == '_' || c == '#');
325 static void buildSqlKeywordHash (void)
327 const size_t count = sizeof (SqlKeywordTable) /
328 sizeof (SqlKeywordTable [0]);
329 size_t i;
330 for (i = 0 ; i < count ; ++i)
332 const keywordDesc* const p = &SqlKeywordTable [i];
333 addKeyword (p->name, Lang_sql, (int) p->id);
337 static tokenInfo *newToken (void)
339 tokenInfo *const token = xMalloc(1, tokenInfo);
341 token->type = TOKEN_UNDEFINED;
342 token->keyword = KEYWORD_NONE;
343 token->string = vStringNew ();
344 token->scope = vStringNew ();
346 return token;
349 static void deleteToken (tokenInfo *const token)
351 vStringDelete (token->string);
352 eFree (token);
356 * Tag generation functions
359 static void makeSqlTag (tokenInfo *const token, const sqlKind kind)
361 vString * fulltag;
363 if (SqlKinds [kind].enabled)
366 * If a scope has been added to the token, change the token
367 * string to include the scope when making the tag.
369 if ( vStringLength(token->scope) > 0 )
371 fulltag = vStringNew ();
372 vStringCopy(fulltag, token->scope);
373 vStringCatS (fulltag, ".");
374 vStringCatS (fulltag, vStringValue(token->string));
375 vStringTerminate(fulltag);
376 vStringCopy(token->string, fulltag);
377 vStringDelete (fulltag);
379 makeConstTag (token, kind);
383 static void makeConstTag (tokenInfo *const token, const sqlKind kind)
385 if (SqlKinds [kind].enabled)
387 const char *const name = vStringValue (token->string);
388 tagEntryInfo e;
389 initTagEntry (&e, name);
391 e.lineNumber = token->lineNumber;
392 e.filePosition = token->filePosition;
393 e.kindName = SqlKinds [kind].name;
394 e.kind = SqlKinds [kind].letter;
396 makeTagEntry (&e);
401 * Parsing functions
404 static int skipToCharacter (const int c)
406 int d;
409 d = fileGetc ();
410 } while (d != EOF && d != c);
411 return d;
414 static void parseString (vString *const string, const int delimiter)
416 boolean end = FALSE;
417 int c;
418 while (! end)
420 c = fileGetc ();
421 /* printf( "\nps: %c\n", c ); */
422 if (c == EOF)
423 end = TRUE;
424 else if (c == delimiter)
425 end = TRUE;
426 else
427 vStringPut (string, c);
429 vStringTerminate (string);
432 /* Read a C identifier beginning with "firstChar" and places it into "name".
434 static void parseIdentifier (vString *const string, const int firstChar)
436 int c = firstChar;
437 Assert (isIdentChar1 (c));
440 vStringPut (string, c);
441 c = fileGetc ();
442 } while (isIdentChar (c));
443 vStringTerminate (string);
444 if (!isspace (c))
445 fileUngetc (c); /* unget non-identifier character */
448 static keywordId analyzeToken (vString *const name)
450 static vString *keyword = NULL;
451 if (keyword == NULL)
452 keyword = vStringNew ();
453 vStringCopyToLower (keyword, name);
454 return (keywordId) lookupKeyword (vStringValue (keyword), Lang_sql);
457 static void readToken (tokenInfo *const token)
459 int c;
461 token->type = TOKEN_UNDEFINED;
462 token->keyword = KEYWORD_NONE;
463 vStringClear (token->string);
465 getNextChar:
468 c = fileGetc ();
469 /* printf( "\nrtc: %c\n", c ); */
471 * Added " to the list of ignores, not sure what this
472 * might break but it gets by this issue:
473 * create table "t1" (...)
476 while (c == '\t' || c == ' ' || c == '\n');
478 switch (c)
480 case EOF: longjmp (Exception, (int)ExceptionEOF); break;
481 case '(': token->type = TOKEN_OPEN_PAREN; break;
482 case ')': token->type = TOKEN_CLOSE_PAREN; break;
483 case ';': token->type = TOKEN_SEMICOLON; break;
484 case '.': token->type = TOKEN_PERIOD; break;
485 case ',': token->type = TOKEN_COMMA; break;
487 case '\'':
488 case '"':
489 token->type = TOKEN_STRING;
490 parseString (token->string, c);
491 token->lineNumber = getSourceLineNumber ();
492 token->filePosition = getInputFilePosition ();
493 break;
495 case '-':
496 c = fileGetc ();
497 if (c == '-') /* is this the start of a comment? */
499 skipToCharacter ('\n');
500 goto getNextChar;
502 else
504 if (!isspace (c))
505 fileUngetc (c);
506 token->type = TOKEN_OPERATOR;
508 break;
510 case '<':
511 case '>':
513 const int initial = c;
514 int d = fileGetc ();
515 if (d == initial)
517 if (initial == '<')
518 token->type = TOKEN_BLOCK_LABEL_BEGIN;
519 else
520 token->type = TOKEN_BLOCK_LABEL_END;
522 else
524 fileUngetc (d);
525 token->type = TOKEN_UNDEFINED;
527 break;
530 case '/':
532 int d = fileGetc ();
533 if (d != '*') /* is this the start of a comment? */
534 fileUngetc (d);
535 else
539 skipToCharacter ('*');
540 c = fileGetc ();
541 if (c == '/')
542 break;
543 else
544 fileUngetc (c);
545 } while (c != EOF && c != '\0');
546 goto getNextChar;
548 break;
551 default:
552 if (! isIdentChar1 (c))
553 token->type = TOKEN_UNDEFINED;
554 else
556 parseIdentifier (token->string, c);
557 token->lineNumber = getSourceLineNumber ();
558 token->filePosition = getInputFilePosition ();
559 token->keyword = analyzeToken (token->string);
560 if (isKeyword (token, KEYWORD_rem))
562 vStringClear (token->string);
563 skipToCharacter ('\n');
564 goto getNextChar;
566 else if (isKeyword (token, KEYWORD_NONE))
567 token->type = TOKEN_IDENTIFIER;
568 else
569 token->type = TOKEN_KEYWORD;
571 break;
573 /*dispToken(token, "rte");*/
577 * Token parsing functions
580 /* - unused - I don't know (enrico)
581 static void addContext (tokenInfo* const parent, const tokenInfo* const child)
583 if (vStringLength (parent->string) > 0)
585 vStringCatS (parent->string, ".");
587 vStringCatS (parent->string, vStringValue(child->string));
588 vStringTerminate(parent->string);
591 static void addToScope (tokenInfo* const token, vString* const extra)
593 if (vStringLength (token->scope) > 0)
595 vStringCatS (token->scope, ".");
597 vStringCatS (token->scope, vStringValue(extra));
598 vStringTerminate(token->scope);
602 * Scanning functions
605 static void findToken (tokenInfo *const token, const tokenType type)
607 while (! isType (token, type))
609 readToken (token);
613 static void skipArgumentList (tokenInfo *const token)
615 int nest_level = 0;
618 * Other databases can have arguments with fully declared
619 * datatypes:
620 * ( name varchar(30), text binary(10) )
621 * So we must check for nested open and closing parantheses
624 if (isType (token, TOKEN_OPEN_PAREN)) /* arguments? */
626 nest_level++;
627 /*findToken (token, TOKEN_CLOSE_PAREN);*/
628 while (! (isType (token, TOKEN_CLOSE_PAREN) && (nest_level == 0)))
630 readToken (token);
631 if (isType (token, TOKEN_OPEN_PAREN))
633 nest_level++;
635 if (isType (token, TOKEN_CLOSE_PAREN))
637 if (nest_level > 0)
639 nest_level--;
642 } /*while*/
643 readToken (token);
647 static void parseSubProgram (tokenInfo *const token)
649 tokenInfo *const name = newToken ();
652 * Prototype:
653 * FUNCTION func_name RETURN integer;
654 * PROCEDURE proc_name( parameters );
655 * Procedure
656 * FUNCTION GET_ML_USERNAME RETURN VARCHAR2
657 * IS
658 * BEGIN
659 * RETURN v_sync_user_id;
660 * END GET_ML_USERNAME;
662 * PROCEDURE proc_name( parameters )
663 * IS
664 * BEGIN
665 * END;
666 * CREATE PROCEDURE proc_name( parameters )
667 * EXTERNAL NAME ... ;
668 * CREATE PROCEDURE proc_name( parameters )
669 * BEGIN
670 * END;
672 * CREATE FUNCTION f_GetClassName(
673 * IN @object VARCHAR(128)
674 * ,IN @code VARCHAR(128)
676 * RETURNS VARCHAR(200)
677 * DETERMINISTIC
678 * BEGIN
680 * IF( @object = 'user_state' ) THEN
681 * SET something = something;
682 * END IF;
684 * RETURN @name;
685 * END;
687 const sqlKind kind = isKeyword (token, KEYWORD_function) ?
688 SQLTAG_FUNCTION : SQLTAG_PROCEDURE;
689 Assert (isKeyword (token, KEYWORD_function) ||
690 isKeyword (token, KEYWORD_procedure));
691 readToken (name);
692 readToken (token);
693 if (isType (token, TOKEN_PERIOD))
695 readToken (name);
696 readToken (token);
698 skipArgumentList (token);
700 if (kind == SQLTAG_FUNCTION)
702 if (isKeyword (token, KEYWORD_return))
704 /* Read RETURN */
705 readToken (token);
706 /* Read datatype */
707 readToken (token);
710 if( isType (token, TOKEN_SEMICOLON) )
712 makeSqlTag (name, SQLTAG_PROTOTYPE);
713 } else {
714 while (!(isKeyword (token, KEYWORD_is) ||
715 isKeyword (token, KEYWORD_begin) ||
716 isType (token, TOKEN_SEMICOLON)
719 readToken (token); /* read return type */
720 if (isKeyword (token, KEYWORD_is) ||
721 isKeyword (token, KEYWORD_begin) )
723 addToScope(token, name->string);
724 if (isType (name, TOKEN_IDENTIFIER) ||
725 isType (name, TOKEN_STRING))
726 makeSqlTag (name, kind);
728 /*dispToken(name, "SubProgram: parseBlock name");*/
729 /*dispToken(token, "SubProgram: parseBlock token");*/
730 parseBlock (token, TRUE);
731 vStringClear (token->scope);
734 deleteToken (name);
737 static void parseRecord (tokenInfo *const token)
739 /* Make it a bit forgiving, this is called from
740 * multiple functions, parseTable, parseType */
741 if (!isType (token, TOKEN_OPEN_PAREN))
742 readToken (token);
744 Assert (isType (token, TOKEN_OPEN_PAREN));
747 if ( isType (token, TOKEN_COMMA) || isType (token, TOKEN_OPEN_PAREN) )
748 readToken (token);
751 * Create table statements can end with various constraints
752 * which must be excluded from the SQLTAG_FIELD.
753 * create table t1 (
754 * c1 integer,
755 * c2 char(30),
756 * c3 numeric(10,5),
757 * c4 integer,
758 * constraint whatever,
759 * primary key(c1),
760 * foreign key (),
761 * check ()
764 if (! (isKeyword(token, KEYWORD_primary) ||
765 isKeyword(token, KEYWORD_references) ||
766 isKeyword(token, KEYWORD_unique) ||
767 isKeyword(token, KEYWORD_check) ||
768 isKeyword(token, KEYWORD_constraint) ||
769 isKeyword(token, KEYWORD_foreign) ) )
771 if (isType (token, TOKEN_IDENTIFIER) ||
772 isType (token, TOKEN_STRING))
773 makeSqlTag (token, SQLTAG_FIELD);
776 while (!(isType (token, TOKEN_COMMA) ||
777 isType (token, TOKEN_CLOSE_PAREN) ||
778 isType (token, TOKEN_OPEN_PAREN)
781 readToken (token);
783 * A table structure can look like this:
784 * create table t1 (
785 * c1 integer,
786 * c2 char(30),
787 * c3 numeric(10,5),
788 * c4 integer
790 * We can't just look for a COMMA or CLOSE_PAREN
791 * since that will not deal with the numeric(10,5)
792 * case. So we need to skip the argument list
793 * when we find an open paren.
795 if (isType (token, TOKEN_OPEN_PAREN))
797 /* Reads to the next token after the TOKEN_CLOSE_PAREN */
798 skipArgumentList(token);
801 } while (! isType (token, TOKEN_CLOSE_PAREN));
804 static void parseType (tokenInfo *const token)
806 tokenInfo *const name = newToken ();
807 vString * saveScope = vStringNew ();
809 vStringCopy(saveScope, token->scope);
810 /* If a scope has been set, add it to the name */
811 addToScope (name, token->scope);
812 readToken (name);
813 if (isType (name, TOKEN_IDENTIFIER))
815 readToken (token);
816 if (isKeyword (token, KEYWORD_is))
818 readToken (token);
819 addToScope (token, name->string);
820 switch (token->keyword)
822 case KEYWORD_record:
823 case KEYWORD_object:
824 makeSqlTag (name, SQLTAG_RECORD);
825 parseRecord (token);
826 break;
828 case KEYWORD_table:
829 makeSqlTag (name, SQLTAG_TABLE);
830 break;
832 case KEYWORD_ref:
833 readToken (token);
834 if (isKeyword (token, KEYWORD_cursor))
835 makeSqlTag (name, SQLTAG_CURSOR);
836 break;
838 default: break;
840 vStringClear (token->scope);
843 vStringCopy(token->scope, saveScope);
844 deleteToken (name);
845 vStringDelete(saveScope);
848 static void parseSimple (tokenInfo *const token, const sqlKind kind)
850 readToken (token);
851 if (isType (token, TOKEN_IDENTIFIER) ||
852 isType (token, TOKEN_STRING))
853 makeSqlTag (token, kind);
856 static void parseDeclare (tokenInfo *const token, const boolean local)
859 * PL/SQL declares are of this format:
860 * IS|AS
861 * [declare]
862 * CURSOR curname ...
863 * varname1 datatype;
864 * varname2 datatype;
865 * varname3 datatype;
866 * begin
869 if (isKeyword (token, KEYWORD_declare))
870 readToken (token);
871 while (! isKeyword (token, KEYWORD_begin) && ! isKeyword (token, KEYWORD_end))
873 switch (token->keyword)
875 case KEYWORD_cursor: parseSimple (token, SQLTAG_CURSOR); break;
876 case KEYWORD_function: parseSubProgram (token); break;
877 case KEYWORD_procedure: parseSubProgram (token); break;
878 case KEYWORD_subtype: parseSimple (token, SQLTAG_SUBTYPE); break;
879 case KEYWORD_trigger: parseSimple (token, SQLTAG_TRIGGER); break;
880 case KEYWORD_type: parseType (token); break;
882 default:
883 if (isType (token, TOKEN_IDENTIFIER))
885 if (local)
887 makeSqlTag (token, SQLTAG_LOCAL_VARIABLE);
888 } else {
889 makeSqlTag (token, SQLTAG_VARIABLE);
892 break;
894 findToken (token, TOKEN_SEMICOLON);
895 readToken (token);
899 static void parseDeclareANSI (tokenInfo *const token, const boolean local)
901 tokenInfo *const type = newToken ();
903 * ANSI declares are of this format:
904 * BEGIN
905 * DECLARE varname1 datatype;
906 * DECLARE varname2 datatype;
907 * ...
909 * This differ from PL/SQL where DECLARE preceeds the BEGIN block
910 * and the DECLARE keyword is not repeated.
912 while (isKeyword (token, KEYWORD_declare))
914 readToken (token);
915 readToken (type);
917 if (isKeyword (type, KEYWORD_cursor))
918 makeSqlTag (token, SQLTAG_CURSOR);
919 else if (isKeyword (token, KEYWORD_local) &&
920 isKeyword (type, KEYWORD_temporary))
923 * DECLARE LOCAL TEMPORARY TABLE table_name (
924 * c1 int,
925 * c2 int
926 * );
928 readToken (token);
929 if (isKeyword (token, KEYWORD_table))
931 readToken (token);
932 if (isType(token, TOKEN_IDENTIFIER) ||
933 isType(token, TOKEN_STRING) )
935 makeSqlTag (token, SQLTAG_TABLE);
939 else if (isType (token, TOKEN_IDENTIFIER) ||
940 isType (token, TOKEN_STRING))
942 if (local)
943 makeSqlTag (token, SQLTAG_LOCAL_VARIABLE);
944 else
945 makeSqlTag (token, SQLTAG_VARIABLE);
947 findToken (token, TOKEN_SEMICOLON);
948 readToken (token);
950 deleteToken (type);
953 static void parseLabel (tokenInfo *const token)
956 * A label has this format:
957 * <<tobacco_dependency>>
958 * DECLARE
959 * v_senator VARCHAR2(100) := 'THURMOND, JESSE';
960 * BEGIN
961 * IF total_contributions (v_senator, 'TOBACCO') > 25000
962 * THEN
963 * <<alochol_dependency>>
964 * DECLARE
965 * v_senator VARCHAR2(100) := 'WHATEVERIT, TAKES';
966 * BEGIN
967 * ...
970 Assert (isType (token, TOKEN_BLOCK_LABEL_BEGIN));
971 readToken (token);
972 if (isType (token, TOKEN_IDENTIFIER))
974 makeSqlTag (token, SQLTAG_BLOCK_LABEL);
975 readToken (token); /* read end of label */
979 static void parseStatements (tokenInfo *const token)
983 if (isType (token, TOKEN_BLOCK_LABEL_BEGIN))
984 parseLabel (token);
985 else
987 switch (token->keyword)
990 * EXCEPTION
991 * <exception handler>;
993 * Where an exception handler could be:
994 * BEGIN
995 * WHEN OTHERS THEN
996 * x := x + 3;
997 * END;
998 * In this case we need to skip this keyword and
999 * move on to the next token without reading until
1000 * TOKEN_SEMICOLON;
1002 case KEYWORD_exception:
1003 readToken (token);
1004 continue;
1007 * WHEN statements can be used in exception clauses
1008 * and CASE statements. The CASE statement should skip
1009 * these given below we skip over to an END statement.
1010 * But for an exception clause, we can have:
1011 * EXCEPTION
1012 * WHEN OTHERS THEN
1013 * BEGIN
1014 * x := x + 3;
1015 * END;
1016 * If we skip to the TOKEN_SEMICOLON, we miss the begin
1017 * of a nested BEGIN END block. So read the next token
1018 * after the THEN and restart the LOOP.
1020 case KEYWORD_when:
1021 while (! isKeyword (token, KEYWORD_then))
1022 readToken (token);
1023 readToken (token);
1024 continue;
1027 * We do not want to look for a ; since for an empty
1028 * IF block, that would skip over the END.
1029 * IF...THEN
1030 * END IF;
1032 case KEYWORD_if:
1033 while (! isKeyword (token, KEYWORD_then))
1034 readToken (token);
1035 /*readToken (token);*/
1036 parseStatements (token);
1037 break;
1040 * LOOP...
1041 * END LOOP;
1043 * FOR loop_name AS cursor_name CURSOR FOR ...
1044 * END FOR;
1046 case KEYWORD_loop:
1047 case KEYWORD_case:
1048 case KEYWORD_for:
1049 readToken (token);
1050 parseStatements (token);
1051 break;
1053 case KEYWORD_declare:
1054 case KEYWORD_begin:
1055 parseBlock (token, TRUE);
1056 break;
1058 default:
1059 readToken (token);
1060 break;
1062 findToken (token, TOKEN_SEMICOLON);
1064 readToken (token);
1065 } while (! isKeyword (token, KEYWORD_end));
1068 static void parseBlock (tokenInfo *const token, const boolean local)
1070 if (isType (token, TOKEN_BLOCK_LABEL_BEGIN))
1072 parseLabel (token);
1073 readToken (token);
1075 if (! isKeyword (token, KEYWORD_begin))
1077 readToken (token);
1078 /*dispToken(token, "parseBlock calling parseDeclare");*/
1079 parseDeclare (token, local);
1081 if (isKeyword (token, KEYWORD_begin))
1083 readToken (token);
1084 parseDeclareANSI (token, local);
1085 while (! isKeyword (token, KEYWORD_end))
1086 parseStatements (token);
1087 findToken (token, TOKEN_SEMICOLON);
1091 static void parsePackage (tokenInfo *const token)
1094 * Packages can be specified in a number of ways:
1095 * CREATE OR REPLACE PACKAGE pkg_name AS
1096 * or
1097 * CREATE OR REPLACE PACKAGE owner.pkg_name AS
1098 * or by specifying a package body
1099 * CREATE OR REPLACE PACKAGE BODY pkg_name AS
1100 * CREATE OR REPLACE PACKAGE BODY owner.pkg_name AS
1102 tokenInfo *const name = newToken ();
1103 readToken (name);
1104 if (isKeyword (name, KEYWORD_body))
1105 readToken (name);
1106 /*dispToken(token, "parsePackage after BODY");*/
1107 /* Chceck for owner.pkg_name */
1108 while (! isKeyword (token, KEYWORD_is))
1110 readToken (token);
1111 if ( isType(token, TOKEN_PERIOD) )
1113 readToken (name);
1114 /*dispToken(name, "parsePackage new name");*/
1117 dispToken(name, "parsePackage name");
1118 if (isKeyword (token, KEYWORD_is))
1120 /*dispToken(token, "parsePackage processing IS");*/
1121 if (isType (name, TOKEN_IDENTIFIER) ||
1122 isType (name, TOKEN_STRING))
1123 makeSqlTag (name, SQLTAG_PACKAGE);
1124 parseBlock (token, FALSE);
1126 findToken (token, TOKEN_SEMICOLON);
1127 deleteToken (name);
1130 static void parseTable (tokenInfo *const token)
1132 tokenInfo *const name = newToken ();
1134 /* This deals with these formats
1135 * create table t1 (c1 int);
1136 * create global tempoary table t2 (c1 int);
1137 * create table "t3" (c1 int);
1138 * create table bob.t4 (c1 int);
1139 * create table bob."t5" (c1 int);
1140 * create table "bob"."t6" (c1 int);
1141 * create table bob."t7" (c1 int);
1142 * Proxy tables use this format:
1143 * create existing table bob."t7" AT '...'; */
1145 readToken (name);
1146 readToken (token);
1147 if (isType (token, TOKEN_PERIOD))
1149 readToken (name);
1150 readToken (token);
1152 if (isType (token, TOKEN_OPEN_PAREN))
1154 if (isType (name, TOKEN_IDENTIFIER) ||
1155 isType (name, TOKEN_STRING))
1157 makeSqlTag (name, SQLTAG_TABLE);
1158 vStringCopy(token->scope, name->string);
1159 parseRecord (token);
1160 vStringClear (token->scope);
1162 } else if (isKeyword (token, KEYWORD_at))
1164 if (isType (name, TOKEN_IDENTIFIER))
1166 makeSqlTag (name, SQLTAG_TABLE);
1169 findToken (token, TOKEN_SEMICOLON);
1170 deleteToken (name);
1173 static void parseIndex (tokenInfo *const token)
1175 tokenInfo *const name = newToken ();
1176 tokenInfo *const owner = newToken ();
1178 /* This deals with these formats
1179 * create index i1 on t1(c1) create index "i2" on t1(c1)
1180 * create virtual unique clustered index "i3" on t1(c1)
1181 * create unique clustered index "i4" on t1(c1)
1182 * create clustered index "i5" on t1(c1)
1183 * create bitmap index "i6" on t1(c1) */
1185 readToken (name);
1186 readToken (token);
1187 if (isType (token, TOKEN_PERIOD))
1189 readToken (name);
1190 readToken (token);
1192 if ( isKeyword (token, KEYWORD_on) &&
1193 (isType (name, TOKEN_IDENTIFIER) || isType (name, TOKEN_STRING) ) )
1195 readToken (owner);
1196 readToken (token);
1197 if (isType (token, TOKEN_PERIOD))
1199 readToken (owner);
1200 readToken (token);
1202 addToScope(name, owner->string);
1203 makeSqlTag (name, SQLTAG_INDEX);
1205 findToken (token, TOKEN_SEMICOLON);
1206 deleteToken (name);
1207 deleteToken (owner);
1210 static void parseEvent (tokenInfo *const token)
1212 tokenInfo *const name = newToken ();
1214 /* This deals with these formats
1215 * create event e1 handler begin end;
1216 * create event "e2" handler begin end;
1217 * create event dba."e3" handler begin end;
1218 * create event "dba"."e4" handler begin end; */
1220 readToken (name);
1221 readToken (token);
1222 if (isType (token, TOKEN_PERIOD))
1224 readToken (name);
1226 while (! (isKeyword (token, KEYWORD_handler) ||
1227 (isType (token, TOKEN_SEMICOLON))) )
1229 readToken (token);
1232 if ( isKeyword (token, KEYWORD_handler) ||
1233 isType (token, TOKEN_SEMICOLON) )
1235 makeSqlTag (name, SQLTAG_EVENT);
1238 if (isKeyword (token, KEYWORD_handler))
1240 readToken (token);
1241 if ( isKeyword (token, KEYWORD_begin) )
1243 parseBlock (token, TRUE);
1245 findToken (token, TOKEN_SEMICOLON);
1247 deleteToken (name);
1250 static void parseTrigger (tokenInfo *const token)
1252 tokenInfo *const name = newToken ();
1253 tokenInfo *const table = newToken ();
1255 /* This deals with these formats
1256 * create or replace trigger tr1 begin end;
1257 * create trigger "tr2" begin end;
1258 * drop trigger "droptr1";
1259 * create trigger "tr3" CALL sp_something();
1260 * create trigger "owner"."tr4" begin end;
1261 * create trigger "tr5" not valid;
1262 * create trigger "tr6" begin end; */
1264 readToken (name);
1265 readToken (token);
1266 if (isType (token, TOKEN_PERIOD))
1268 readToken (name);
1269 readToken (token);
1272 while (! (isKeyword (token, KEYWORD_on) ||
1273 ( isType (token, TOKEN_SEMICOLON))) )
1275 readToken (token);
1278 if (! isType (token, TOKEN_SEMICOLON) )
1280 readToken (table);
1281 readToken (token);
1282 if (isType (token, TOKEN_PERIOD))
1284 readToken (table);
1285 readToken (token);
1288 while (! (isKeyword (token, KEYWORD_begin) ||
1289 (isKeyword (token, KEYWORD_call)) ||
1290 ( isType (token, TOKEN_SEMICOLON))) )
1292 readToken (token);
1293 if ( isKeyword (token, KEYWORD_declare) )
1295 addToScope(token, name->string);
1296 parseDeclare(token, TRUE);
1297 vStringClear(token->scope);
1301 if ( isKeyword (token, KEYWORD_begin) ||
1302 isKeyword (token, KEYWORD_call) )
1304 addToScope(name, table->string);
1305 makeSqlTag (name, SQLTAG_TRIGGER);
1306 addToScope(token, table->string);
1307 if ( isKeyword (token, KEYWORD_begin) )
1309 parseBlock (token, TRUE);
1311 vStringClear(token->scope);
1315 if (! isType (token, TOKEN_SEMICOLON) )
1317 findToken (token, TOKEN_SEMICOLON);
1319 deleteToken (name);
1320 deleteToken (table);
1323 static void parsePublication (tokenInfo *const token)
1325 tokenInfo *const name = newToken ();
1327 /* This deals with these formats
1328 * create or replace publication pu1 ()
1329 * create publication "pu2" ()
1330 * create publication dba."pu3" ()
1331 * create publication "dba"."pu4" () */
1333 readToken (name);
1334 readToken (token);
1335 if (isType (token, TOKEN_PERIOD))
1337 readToken (name);
1338 readToken (token);
1340 if (isType (token, TOKEN_OPEN_PAREN))
1342 if (isType (name, TOKEN_IDENTIFIER) ||
1343 isType (name, TOKEN_STRING))
1345 makeSqlTag (name, SQLTAG_PUBLICATION);
1348 findToken (token, TOKEN_SEMICOLON);
1349 deleteToken (name);
1352 static void parseService (tokenInfo *const token)
1354 tokenInfo *const name = newToken ();
1356 /* This deals with these formats
1357 * CREATE SERVICE s1 TYPE 'HTML'
1358 * AUTHORIZATION OFF USER DBA AS
1359 * SELECT *
1360 * FROM SYS.SYSTABLE;
1361 * CREATE SERVICE "s2" TYPE 'HTML'
1362 * AUTHORIZATION OFF USER DBA AS
1363 * CALL sp_Something(); */
1365 readToken (name);
1366 readToken (token);
1367 if (isKeyword (token, KEYWORD_type))
1369 if (isType (name, TOKEN_IDENTIFIER) ||
1370 isType (name, TOKEN_STRING))
1372 makeSqlTag (name, SQLTAG_SERVICE);
1375 findToken (token, TOKEN_SEMICOLON);
1376 deleteToken (name);
1379 static void parseDomain (tokenInfo *const token)
1381 tokenInfo *const name = newToken ();
1383 /* This deals with these formats
1384 * CREATE DOMAIN|DATATYPE [AS] your_name ...; */
1386 readToken (name);
1387 if (isKeyword (name, KEYWORD_is))
1389 readToken (name);
1391 readToken (token);
1392 if (isType (name, TOKEN_IDENTIFIER) ||
1393 isType (name, TOKEN_STRING))
1395 makeSqlTag (name, SQLTAG_DOMAIN);
1397 findToken (token, TOKEN_SEMICOLON);
1398 deleteToken (name);
1401 static void parseDrop (tokenInfo *const token)
1403 /* This deals with these formats
1404 * DROP TABLE|PROCEDURE|DOMAIN|DATATYPE name;
1406 * Just simply skip over these statements.
1407 * They are often confused with PROCEDURE prototypes
1408 * since the syntax is similar, this effectively deals with
1409 * the issue for all types. */
1411 /*dispToken(token, "parseDrop");*/
1412 findToken (token, TOKEN_SEMICOLON);
1415 static void parseVariable (tokenInfo *const token)
1417 tokenInfo *const name = newToken ();
1419 /* This deals with these formats
1420 * create variable varname1 integer;
1421 * create variable @varname2 integer;
1422 * create variable "varname3" integer;
1423 * drop variable @varname3; */
1425 readToken (name);
1426 readToken (token);
1427 if ( (isType (name, TOKEN_IDENTIFIER) || isType (name, TOKEN_STRING))
1428 && !isType (token, TOKEN_SEMICOLON) )
1430 makeSqlTag (name, SQLTAG_VARIABLE);
1432 if (! isType (token, TOKEN_SEMICOLON) )
1433 findToken (token, TOKEN_SEMICOLON);
1435 deleteToken (name);
1438 static void parseSynonym (tokenInfo *const token)
1440 tokenInfo *const name = newToken ();
1442 /* This deals with these formats
1443 * create variable varname1 integer;
1444 * create variable @varname2 integer;
1445 * create variable "varname3" integer;
1446 * drop variable @varname3; */
1448 readToken (name);
1449 readToken (token);
1450 if ( (isType (name, TOKEN_IDENTIFIER) || isType (name, TOKEN_STRING))
1451 && isKeyword (token, KEYWORD_for) )
1453 makeSqlTag (name, SQLTAG_SYNONYM);
1455 if (! isType (token, TOKEN_SEMICOLON) )
1456 findToken (token, TOKEN_SEMICOLON);
1458 deleteToken (name);
1461 static void parseView (tokenInfo *const token)
1463 tokenInfo *const name = newToken ();
1465 /* This deals with these formats
1466 * create variable varname1 integer;
1467 * create variable @varname2 integer;
1468 * create variable "varname3" integer;
1469 * drop variable @varname3; */
1471 readToken (name);
1472 readToken (token);
1473 if (isType (token, TOKEN_PERIOD))
1475 readToken (name);
1476 readToken (token);
1478 if ( isType (token, TOKEN_OPEN_PAREN) )
1480 skipArgumentList(token);
1484 while (!(isKeyword (token, KEYWORD_is) ||
1485 isType (token, TOKEN_SEMICOLON)
1488 readToken (token);
1491 if ( (isType (name, TOKEN_IDENTIFIER) || isType (name, TOKEN_STRING))
1492 && isKeyword (token, KEYWORD_is) )
1494 makeSqlTag (name, SQLTAG_VIEW);
1497 if (! isType (token, TOKEN_SEMICOLON) )
1498 findToken (token, TOKEN_SEMICOLON);
1500 deleteToken (name);
1503 static void parseMLTable (tokenInfo *const token)
1505 tokenInfo *const version = newToken ();
1506 tokenInfo *const table = newToken ();
1507 tokenInfo *const event = newToken ();
1509 /* This deals with these formats
1510 * call ml_add_table_script( 'version', 'table_name', 'event',
1511 * 'some SQL statement'
1512 * ); */
1514 readToken (token);
1515 if ( isType (token, TOKEN_OPEN_PAREN) )
1517 readToken (version);
1518 readToken (token);
1519 while (!(isType (token, TOKEN_COMMA) ||
1520 isType (token, TOKEN_CLOSE_PAREN)
1523 readToken (token);
1526 if (isType (token, TOKEN_COMMA))
1528 readToken (table);
1529 readToken (token);
1530 while (!(isType (token, TOKEN_COMMA) ||
1531 isType (token, TOKEN_CLOSE_PAREN)
1534 readToken (token);
1537 if (isType (token, TOKEN_COMMA))
1539 readToken (event);
1541 if (isType (version, TOKEN_STRING) &&
1542 isType (table, TOKEN_STRING) &&
1543 isType (event, TOKEN_STRING) )
1545 addToScope(version, table->string);
1546 addToScope(version, event->string);
1547 makeSqlTag (version, SQLTAG_MLTABLE);
1550 if( !isType (token, TOKEN_CLOSE_PAREN) )
1551 findToken (token, TOKEN_CLOSE_PAREN);
1555 if (! isType (token, TOKEN_SEMICOLON) )
1556 findToken (token, TOKEN_SEMICOLON);
1558 deleteToken (version);
1559 deleteToken (table);
1560 deleteToken (event);
1563 static void parseMLConn (tokenInfo *const token)
1565 tokenInfo *const version = newToken ();
1566 tokenInfo *const event = newToken ();
1568 /* This deals with these formats
1569 * call ml_add_connection_script( 'version', 'event',
1570 * 'some SQL statement'
1571 * ); */
1573 readToken (token);
1574 if ( isType (token, TOKEN_OPEN_PAREN) )
1576 readToken (version);
1577 readToken (token);
1578 while (!(isType (token, TOKEN_COMMA) ||
1579 isType (token, TOKEN_CLOSE_PAREN)
1582 readToken (token);
1585 if (isType (token, TOKEN_COMMA))
1587 readToken (event);
1589 if (isType (version, TOKEN_STRING) &&
1590 isType (event, TOKEN_STRING) )
1592 addToScope(version, event->string);
1593 makeSqlTag (version, SQLTAG_MLCONN);
1596 if( !isType (token, TOKEN_CLOSE_PAREN) )
1597 findToken (token, TOKEN_CLOSE_PAREN);
1601 if (! isType (token, TOKEN_SEMICOLON) )
1602 findToken (token, TOKEN_SEMICOLON);
1604 deleteToken (version);
1605 deleteToken (event);
1609 static void parseSqlFile (tokenInfo *const token)
1613 readToken (token);
1614 /*dispToken(token, "psf");*/
1616 if (isType (token, TOKEN_BLOCK_LABEL_BEGIN))
1617 parseLabel (token);
1618 else switch (token->keyword)
1620 case KEYWORD_begin: parseBlock (token, FALSE); break;
1621 case KEYWORD_cursor: parseSimple (token, SQLTAG_CURSOR); break;
1622 case KEYWORD_datatype: parseDomain (token); break;
1623 case KEYWORD_declare: parseBlock (token, FALSE); break;
1624 case KEYWORD_domain: parseDomain (token); break;
1625 case KEYWORD_drop: parseDrop (token); break;
1626 case KEYWORD_event: parseEvent (token); break;
1627 case KEYWORD_function: parseSubProgram (token); break;
1628 case KEYWORD_index: parseIndex (token); break;
1629 case KEYWORD_ml_table: parseMLTable (token); break;
1630 case KEYWORD_ml_conn: parseMLConn (token); break;
1631 case KEYWORD_package: parsePackage (token); break;
1632 case KEYWORD_procedure: parseSubProgram (token); break;
1633 case KEYWORD_publication: parsePublication (token); break;
1634 case KEYWORD_service: parseService (token); break;
1635 case KEYWORD_subtype: parseSimple (token, SQLTAG_SUBTYPE); break;
1636 case KEYWORD_synonym: parseSynonym (token); break;
1637 case KEYWORD_table: parseTable (token); break;
1638 case KEYWORD_trigger: parseTrigger (token); break;
1639 case KEYWORD_type: parseType (token); break;
1640 case KEYWORD_variable: parseVariable (token); break;
1641 case KEYWORD_view: parseView (token); break;
1642 default: break;
1644 } while (! isKeyword (token, KEYWORD_end));
1647 static void initialize (const langType language)
1649 Assert (sizeof (SqlKinds) / sizeof (SqlKinds [0]) == SQLTAG_COUNT);
1650 Lang_sql = language;
1651 buildSqlKeywordHash ();
1654 static void findSqlTags (void)
1656 tokenInfo *const token = newToken ();
1657 exception_t exception = (exception_t) (setjmp (Exception));
1658 while (exception == ExceptionNone)
1659 parseSqlFile (token);
1660 deleteToken (token);
1663 extern parserDefinition* SqlParser (void)
1665 static const char *const extensions [] = { "sql", NULL };
1666 parserDefinition* def = parserNew ("SQL");
1667 def->kinds = SqlKinds;
1668 def->kindCount = KIND_COUNT (SqlKinds);
1669 def->extensions = extensions;
1670 def->parser = findSqlTags;
1671 def->initialize = initialize;
1672 return def;
1675 /* vi:set tabstop=8 shiftwidth=4: */