Update for last 2 commits.
[geany-mirror.git] / tagmanager / sql.c
blob02733ccc7ae61cb409793f72dcf2300cfb1e37cb
1 /*
2 * $Id$
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 "entry.h"
25 #include "keyword.h"
26 #include "parse.h"
27 #include "main.h"
28 #include "read.h"
29 #include "vstring.h"
32 * On-line PL/SQL Reference Guide:
33 * http://info-it.umsystem.edu/oradocs/doc/server/doc/PLS23/toc.htm
35 * Sample PL/SQL code is available from:
36 * http://www.orafaq.com/faqscrpt.htm#GENPLSQL
40 * MACROS
42 #define isType(token,t) (boolean) ((token)->type == (t))
43 #define isKeyword(token,k) (boolean) ((token)->keyword == (k))
46 * DATA DECLARATIONS
49 typedef enum eException { ExceptionNone, ExceptionEOF } exception_t;
51 /* Used to specify type of keyword.
53 typedef enum eKeywordId {
54 KEYWORD_NONE = -1,
55 KEYWORD_is,
56 KEYWORD_begin,
57 KEYWORD_body,
58 KEYWORD_cursor,
59 KEYWORD_declare,
60 KEYWORD_end,
61 KEYWORD_function,
62 KEYWORD_if,
63 KEYWORD_loop,
64 KEYWORD_case,
65 KEYWORD_for,
66 KEYWORD_call,
67 KEYWORD_package,
68 KEYWORD_pragma,
69 KEYWORD_procedure,
70 KEYWORD_record,
71 KEYWORD_object,
72 KEYWORD_ref,
73 KEYWORD_rem,
74 KEYWORD_return,
75 KEYWORD_returns,
76 KEYWORD_subtype,
77 KEYWORD_table,
78 KEYWORD_trigger,
79 KEYWORD_type,
80 KEYWORD_index,
81 KEYWORD_event,
82 KEYWORD_publication,
83 KEYWORD_service,
84 KEYWORD_domain,
85 KEYWORD_datatype,
86 KEYWORD_result,
87 KEYWORD_when,
88 KEYWORD_then,
89 KEYWORD_variable,
90 KEYWORD_exception,
91 KEYWORD_at,
92 KEYWORD_on,
93 KEYWORD_primary,
94 KEYWORD_references,
95 KEYWORD_unique,
96 KEYWORD_check,
97 KEYWORD_constraint,
98 KEYWORD_foreign,
99 KEYWORD_ml_table,
100 KEYWORD_ml_conn,
101 KEYWORD_local,
102 KEYWORD_temporary,
103 KEYWORD_drop,
104 KEYWORD_view,
105 KEYWORD_synonym,
106 KEYWORD_handler
107 } keywordId;
109 /* Used to determine whether keyword is valid for the token language and
110 * what its ID is.
112 typedef struct sKeywordDesc {
113 const char *name;
114 keywordId id;
115 } keywordDesc;
117 typedef enum eTokenType {
118 TOKEN_UNDEFINED,
119 TOKEN_BLOCK_LABEL_BEGIN,
120 TOKEN_BLOCK_LABEL_END,
121 TOKEN_CHARACTER,
122 TOKEN_CLOSE_PAREN,
123 TOKEN_SEMICOLON,
124 TOKEN_COMMA,
125 TOKEN_IDENTIFIER,
126 TOKEN_KEYWORD,
127 TOKEN_OPEN_PAREN,
128 TOKEN_OPERATOR,
129 TOKEN_OTHER,
130 TOKEN_STRING,
131 TOKEN_PERIOD
132 } tokenType;
134 typedef struct sTokenInfo {
135 tokenType type;
136 keywordId keyword;
137 vString * string;
138 vString * scope;
139 unsigned long lineNumber;
140 fpos_t filePosition;
141 int bufferPosition; /* buffer position of line containing name */
142 } tokenInfo;
145 * DATA DEFINITIONS
148 static langType Lang_sql;
150 static jmp_buf Exception;
152 typedef enum {
153 SQLTAG_CURSOR,
154 SQLTAG_PROTOTYPE,
155 SQLTAG_FUNCTION,
156 SQLTAG_FIELD,
157 SQLTAG_LOCAL_VARIABLE,
158 SQLTAG_BLOCK_LABEL,
159 SQLTAG_PACKAGE,
160 SQLTAG_PROCEDURE,
161 SQLTAG_RECORD,
162 SQLTAG_SUBTYPE,
163 SQLTAG_TABLE,
164 SQLTAG_TRIGGER,
165 SQLTAG_VARIABLE,
166 SQLTAG_INDEX,
167 SQLTAG_EVENT,
168 SQLTAG_PUBLICATION,
169 SQLTAG_SERVICE,
170 SQLTAG_DOMAIN,
171 SQLTAG_VIEW,
172 SQLTAG_SYNONYM,
173 SQLTAG_MLTABLE,
174 SQLTAG_MLCONN,
175 SQLTAG_COUNT
176 } sqlKind;
178 static kindOption SqlKinds [] = {
179 { TRUE, 'c', "cursor", "cursors" },
180 { FALSE, 'd', "prototype", "prototypes" },
181 { TRUE, 'f', "function", "functions" },
182 { TRUE, 'F', "field", "record fields" },
183 { FALSE, 'l', "local", "local variables" },
184 { TRUE, 'L', "label", "block label" },
185 { TRUE, 'P', "package", "packages" },
186 { TRUE, 'p', "procedure", "procedures" },
187 { FALSE, 'r', "record", "records" },
188 { TRUE, 's', "subtype", "subtypes" },
189 { TRUE, 't', "table", "tables" },
190 { TRUE, 'T', "trigger", "triggers" },
191 { TRUE, 'v', "variable", "variables" },
192 { TRUE, 'i', "index", "indexes" },
193 { TRUE, 'e', "event", "events" },
194 { TRUE, 'U', "publication", "publications" },
195 { TRUE, 'R', "service", "services" },
196 { TRUE, 'D', "domain", "domains" },
197 { TRUE, 'V', "view", "views" },
198 { TRUE, 'n', "synonym", "synonyms" },
199 { TRUE, 'x', "mltable", "MobiLink Table Scripts" },
200 { TRUE, 'y', "mlconn", "MobiLink Conn Scripts" }
203 static const keywordDesc SqlKeywordTable [] = {
204 /* keyword keyword ID */
205 { "as", KEYWORD_is },
206 { "begin", KEYWORD_begin },
207 { "body", KEYWORD_body },
208 { "cursor", KEYWORD_cursor },
209 { "declare", KEYWORD_declare },
210 { "end", KEYWORD_end },
211 { "function", KEYWORD_function },
212 { "if", KEYWORD_if },
213 { "is", KEYWORD_is },
214 { "loop", KEYWORD_loop },
215 { "case", KEYWORD_case },
216 { "for", KEYWORD_for },
217 { "call", KEYWORD_call },
218 { "package", KEYWORD_package },
219 { "pragma", KEYWORD_pragma },
220 { "procedure", KEYWORD_procedure },
221 { "record", KEYWORD_record },
222 { "object", KEYWORD_object },
223 { "ref", KEYWORD_ref },
224 { "rem", KEYWORD_rem },
225 { "return", KEYWORD_return },
226 { "returns", KEYWORD_returns },
227 { "subtype", KEYWORD_subtype },
228 { "table", KEYWORD_table },
229 { "trigger", KEYWORD_trigger },
230 { "type", KEYWORD_type },
231 { "index", KEYWORD_index },
232 { "event", KEYWORD_event },
233 { "publication", KEYWORD_publication },
234 { "service", KEYWORD_service },
235 { "domain", KEYWORD_domain },
236 { "datatype", KEYWORD_datatype },
237 { "result", KEYWORD_result },
238 { "when", KEYWORD_when },
239 { "then", KEYWORD_then },
240 { "variable", KEYWORD_variable },
241 { "exception", KEYWORD_exception },
242 { "at", KEYWORD_at },
243 { "on", KEYWORD_on },
244 { "primary", KEYWORD_primary },
245 { "references", KEYWORD_references },
246 { "unique", KEYWORD_unique },
247 { "check", KEYWORD_check },
248 { "constraint", KEYWORD_constraint },
249 { "foreign", KEYWORD_foreign },
250 { "ml_add_table_script", KEYWORD_ml_table },
251 { "ml_add_connection_script", KEYWORD_ml_conn },
252 { "local", KEYWORD_local },
253 { "temporary", KEYWORD_temporary },
254 { "drop", KEYWORD_drop },
255 { "view", KEYWORD_view },
256 { "synonym", KEYWORD_synonym },
257 { "handler", KEYWORD_handler }
261 * FUNCTION DECLARATIONS
264 static void parseBlock (tokenInfo *const token, const boolean local);
265 static void makeConstTag (tokenInfo *const token, const sqlKind kind);
268 * DEBUG function
271 static void dispToken (tokenInfo *const token, char * location)
273 #ifdef DEBUG
274 if ( isKeyword(token, KEYWORD_NONE) )
276 if ( isType(token, TOKEN_IDENTIFIER) || isType(token, TOKEN_STRING) )
278 printf( "\n%s: token string t:%s s:%s l:%lu p:%d\n"
279 , location
280 , vStringValue(token->string)
281 , vStringValue(token->scope)
282 , token->lineNumber
283 , token->bufferPosition
285 } else {
286 printf( "\n%s: token t:%d s:%s l:%lu p:%d\n"
287 , location
288 , token->type
289 , vStringValue(token->scope)
290 , token->lineNumber
291 , token->bufferPosition
294 } else {
295 printf( "\n%s: keyword:%s k:%d s:%s l:%lu p:%d\n"
296 , location
297 , vStringValue(token->string)
298 , token->keyword
299 , vStringValue(token->scope)
300 , token->lineNumber
301 , token->bufferPosition
304 #endif
308 * FUNCTION DEFINITIONS
311 static boolean isIdentChar1 (const int c)
313 /* Other databases are less restrictive on the first character of
314 * an identifier.
315 * isIdentChar1 is used to identify the first character of an
316 * identifier, so we are removing some restrictions. */
317 return (boolean)
318 (isalpha (c) || c == '@' || c == '_' );
321 static boolean isIdentChar (const int c)
323 return (boolean)
324 (isalpha (c) || isdigit (c) || c == '$' ||
325 c == '@' || c == '_' || c == '#');
328 static void buildSqlKeywordHash (void)
330 const size_t count = sizeof (SqlKeywordTable) /
331 sizeof (SqlKeywordTable [0]);
332 size_t i;
333 for (i = 0 ; i < count ; ++i)
335 const keywordDesc* const p = &SqlKeywordTable [i];
336 addKeyword (p->name, Lang_sql, (int) p->id);
340 static tokenInfo *newToken (void)
342 tokenInfo *const token = xMalloc(1, tokenInfo);
344 token->type = TOKEN_UNDEFINED;
345 token->keyword = KEYWORD_NONE;
346 token->string = vStringNew ();
347 token->scope = vStringNew ();
349 return token;
352 static void deleteToken (tokenInfo *const token)
354 vStringDelete (token->string);
355 eFree (token);
359 * Tag generation functions
362 static void makeSqlTag (tokenInfo *const token, const sqlKind kind)
364 vString * fulltag;
366 if (SqlKinds [kind].enabled)
369 * If a scope has been added to the token, change the token
370 * string to include the scope when making the tag.
372 if ( vStringLength(token->scope) > 0 )
374 fulltag = vStringNew ();
375 vStringCopy(fulltag, token->scope);
376 vStringCatS (fulltag, ".");
377 vStringCatS (fulltag, vStringValue(token->string));
378 vStringTerminate(fulltag);
379 vStringCopy(token->string, fulltag);
380 vStringDelete (fulltag);
382 makeConstTag (token, kind);
386 static void makeConstTag (tokenInfo *const token, const sqlKind kind)
388 if (SqlKinds [kind].enabled)
390 const char *const name = vStringValue (token->string);
391 tagEntryInfo e;
392 initTagEntry (&e, name);
394 e.lineNumber = token->lineNumber;
395 if (useFile())
396 e.filePosition = token->filePosition;
397 else
398 e.bufferPosition = token->bufferPosition;
399 e.kindName = SqlKinds [kind].name;
400 e.kind = SqlKinds [kind].letter;
402 makeTagEntry (&e);
407 * Parsing functions
410 static int skipToCharacter (const int c)
412 int d;
415 d = fileGetc ();
416 } while (d != EOF && d != c);
417 return d;
420 static void parseString (vString *const string, const int delimiter)
422 boolean end = FALSE;
423 int c;
424 while (! end)
426 c = fileGetc ();
427 /* printf( "\nps: %c\n", c ); */
428 if (c == EOF)
429 end = TRUE;
430 else if (c == delimiter)
431 end = TRUE;
432 else
433 vStringPut (string, c);
435 vStringTerminate (string);
438 /* Read a C identifier beginning with "firstChar" and places it into "name".
440 static void parseIdentifier (vString *const string, const int firstChar)
442 int c = firstChar;
443 Assert (isIdentChar1 (c));
446 vStringPut (string, c);
447 c = fileGetc ();
448 } while (isIdentChar (c));
449 vStringTerminate (string);
450 if (!isspace (c))
451 fileUngetc (c); /* unget non-identifier character */
454 static keywordId analyzeToken (vString *const name)
456 static vString *keyword = NULL;
457 if (keyword == NULL)
458 keyword = vStringNew ();
459 vStringCopyToLower (keyword, name);
460 return (keywordId) lookupKeyword (vStringValue (keyword), Lang_sql);
463 static void readToken (tokenInfo *const token)
465 int c;
467 token->type = TOKEN_UNDEFINED;
468 token->keyword = KEYWORD_NONE;
469 vStringClear (token->string);
471 getNextChar:
474 c = fileGetc ();
475 /* printf( "\nrtc: %c\n", c ); */
477 * Added " to the list of ignores, not sure what this
478 * might break but it gets by this issue:
479 * create table "t1" (...)
482 while (c == '\t' || c == ' ' || c == '\n');
484 switch (c)
486 case EOF: longjmp (Exception, (int)ExceptionEOF); break;
487 case '(': token->type = TOKEN_OPEN_PAREN; break;
488 case ')': token->type = TOKEN_CLOSE_PAREN; break;
489 case ';': token->type = TOKEN_SEMICOLON; break;
490 case '.': token->type = TOKEN_PERIOD; break;
491 case ',': token->type = TOKEN_COMMA; break;
493 case '\'':
494 case '"':
495 token->type = TOKEN_STRING;
496 parseString (token->string, c);
497 token->lineNumber = getSourceLineNumber ();
498 if (useFile())
499 token->filePosition = getInputFilePosition ();
500 else
501 token->bufferPosition = getInputBufferPosition ();
502 break;
504 case '-':
505 c = fileGetc ();
506 if (c == '-') /* is this the start of a comment? */
508 skipToCharacter ('\n');
509 goto getNextChar;
511 else
513 if (!isspace (c))
514 fileUngetc (c);
515 token->type = TOKEN_OPERATOR;
517 break;
519 case '<':
520 case '>':
522 const int initial = c;
523 int d = fileGetc ();
524 if (d == initial)
526 if (initial == '<')
527 token->type = TOKEN_BLOCK_LABEL_BEGIN;
528 else
529 token->type = TOKEN_BLOCK_LABEL_END;
531 else
533 fileUngetc (d);
534 token->type = TOKEN_UNDEFINED;
536 break;
539 case '/':
541 int d = fileGetc ();
542 if (d != '*') /* is this the start of a comment? */
543 fileUngetc (d);
544 else
548 skipToCharacter ('*');
549 c = fileGetc ();
550 if (c == '/')
551 break;
552 else
553 fileUngetc (c);
554 } while (c != EOF && c != '\0');
555 goto getNextChar;
557 break;
560 default:
561 if (! isIdentChar1 (c))
562 token->type = TOKEN_UNDEFINED;
563 else
565 parseIdentifier (token->string, c);
566 token->lineNumber = getSourceLineNumber ();
567 if (useFile())
568 token->filePosition = getInputFilePosition ();
569 else
570 token->bufferPosition = getInputBufferPosition ();
571 token->keyword = analyzeToken (token->string);
572 if (isKeyword (token, KEYWORD_rem))
574 vStringClear (token->string);
575 skipToCharacter ('\n');
576 goto getNextChar;
578 else if (isKeyword (token, KEYWORD_NONE))
579 token->type = TOKEN_IDENTIFIER;
580 else
581 token->type = TOKEN_KEYWORD;
583 break;
585 /*dispToken(token, "rte");*/
589 * Token parsing functions
592 /* - unused - I don't know (enrico)
593 static void addContext (tokenInfo* const parent, const tokenInfo* const child)
595 if (vStringLength (parent->string) > 0)
597 vStringCatS (parent->string, ".");
599 vStringCatS (parent->string, vStringValue(child->string));
600 vStringTerminate(parent->string);
603 static void addToScope (tokenInfo* const token, vString* const extra)
605 if (vStringLength (token->scope) > 0)
607 vStringCatS (token->scope, ".");
609 vStringCatS (token->scope, vStringValue(extra));
610 vStringTerminate(token->scope);
614 * Scanning functions
617 static void findToken (tokenInfo *const token, const tokenType type)
619 while (! isType (token, type))
621 readToken (token);
625 static void skipArgumentList (tokenInfo *const token)
627 int nest_level = 0;
630 * Other databases can have arguments with fully declared
631 * datatypes:
632 * ( name varchar(30), text binary(10) )
633 * So we must check for nested open and closing parantheses
636 if (isType (token, TOKEN_OPEN_PAREN)) /* arguments? */
638 nest_level++;
639 /*findToken (token, TOKEN_CLOSE_PAREN);*/
640 while (! (isType (token, TOKEN_CLOSE_PAREN) && (nest_level == 0)))
642 readToken (token);
643 if (isType (token, TOKEN_OPEN_PAREN))
645 nest_level++;
647 if (isType (token, TOKEN_CLOSE_PAREN))
649 if (nest_level > 0)
651 nest_level--;
654 } /*while*/
655 readToken (token);
659 static void parseSubProgram (tokenInfo *const token)
661 tokenInfo *const name = newToken ();
664 * Prototype:
665 * FUNCTION func_name RETURN integer;
666 * PROCEDURE proc_name( parameters );
667 * Procedure
668 * FUNCTION GET_ML_USERNAME RETURN VARCHAR2
669 * IS
670 * BEGIN
671 * RETURN v_sync_user_id;
672 * END GET_ML_USERNAME;
674 * PROCEDURE proc_name( parameters )
675 * IS
676 * BEGIN
677 * END;
678 * CREATE PROCEDURE proc_name( parameters )
679 * EXTERNAL NAME ... ;
680 * CREATE PROCEDURE proc_name( parameters )
681 * BEGIN
682 * END;
684 * CREATE FUNCTION f_GetClassName(
685 * IN @object VARCHAR(128)
686 * ,IN @code VARCHAR(128)
688 * RETURNS VARCHAR(200)
689 * DETERMINISTIC
690 * BEGIN
692 * IF( @object = 'user_state' ) THEN
693 * SET something = something;
694 * END IF;
696 * RETURN @name;
697 * END;
699 const sqlKind kind = isKeyword (token, KEYWORD_function) ?
700 SQLTAG_FUNCTION : SQLTAG_PROCEDURE;
701 Assert (isKeyword (token, KEYWORD_function) ||
702 isKeyword (token, KEYWORD_procedure));
703 readToken (name);
704 readToken (token);
705 if (isType (token, TOKEN_PERIOD))
707 readToken (name);
708 readToken (token);
710 skipArgumentList (token);
712 if (kind == SQLTAG_FUNCTION)
714 if (isKeyword (token, KEYWORD_return))
716 /* Read RETURN */
717 readToken (token);
718 /* Read datatype */
719 readToken (token);
722 if( isType (token, TOKEN_SEMICOLON) )
724 makeSqlTag (name, SQLTAG_PROTOTYPE);
725 } else {
726 while (!(isKeyword (token, KEYWORD_is) ||
727 isKeyword (token, KEYWORD_begin) ||
728 isType (token, TOKEN_SEMICOLON)
731 readToken (token); /* read return type */
732 if (isKeyword (token, KEYWORD_is) ||
733 isKeyword (token, KEYWORD_begin) )
735 addToScope(token, name->string);
736 if (isType (name, TOKEN_IDENTIFIER) ||
737 isType (name, TOKEN_STRING))
738 makeSqlTag (name, kind);
740 /*dispToken(name, "SubProgram: parseBlock name");*/
741 /*dispToken(token, "SubProgram: parseBlock token");*/
742 parseBlock (token, TRUE);
743 vStringClear (token->scope);
746 deleteToken (name);
749 static void parseRecord (tokenInfo *const token)
751 /* Make it a bit forgiving, this is called from
752 * multiple functions, parseTable, parseType */
753 if (!isType (token, TOKEN_OPEN_PAREN))
754 readToken (token);
756 Assert (isType (token, TOKEN_OPEN_PAREN));
759 if ( isType (token, TOKEN_COMMA) || isType (token, TOKEN_OPEN_PAREN) )
760 readToken (token);
763 * Create table statements can end with various constraints
764 * which must be excluded from the SQLTAG_FIELD.
765 * create table t1 (
766 * c1 integer,
767 * c2 char(30),
768 * c3 numeric(10,5),
769 * c4 integer,
770 * constraint whatever,
771 * primary key(c1),
772 * foreign key (),
773 * check ()
776 if (! (isKeyword(token, KEYWORD_primary) ||
777 isKeyword(token, KEYWORD_references) ||
778 isKeyword(token, KEYWORD_unique) ||
779 isKeyword(token, KEYWORD_check) ||
780 isKeyword(token, KEYWORD_constraint) ||
781 isKeyword(token, KEYWORD_foreign) ) )
783 if (isType (token, TOKEN_IDENTIFIER) ||
784 isType (token, TOKEN_STRING))
785 makeSqlTag (token, SQLTAG_FIELD);
788 while (!(isType (token, TOKEN_COMMA) ||
789 isType (token, TOKEN_CLOSE_PAREN) ||
790 isType (token, TOKEN_OPEN_PAREN)
793 readToken (token);
795 * A table structure can look like this:
796 * create table t1 (
797 * c1 integer,
798 * c2 char(30),
799 * c3 numeric(10,5),
800 * c4 integer
802 * We can't just look for a COMMA or CLOSE_PAREN
803 * since that will not deal with the numeric(10,5)
804 * case. So we need to skip the argument list
805 * when we find an open paren.
807 if (isType (token, TOKEN_OPEN_PAREN))
809 /* Reads to the next token after the TOKEN_CLOSE_PAREN */
810 skipArgumentList(token);
813 } while (! isType (token, TOKEN_CLOSE_PAREN));
816 static void parseType (tokenInfo *const token)
818 tokenInfo *const name = newToken ();
819 vString * saveScope = vStringNew ();
821 vStringCopy(saveScope, token->scope);
822 /* If a scope has been set, add it to the name */
823 addToScope (name, token->scope);
824 readToken (name);
825 if (isType (name, TOKEN_IDENTIFIER))
827 readToken (token);
828 if (isKeyword (token, KEYWORD_is))
830 readToken (token);
831 addToScope (token, name->string);
832 switch (token->keyword)
834 case KEYWORD_record:
835 case KEYWORD_object:
836 makeSqlTag (name, SQLTAG_RECORD);
837 parseRecord (token);
838 break;
840 case KEYWORD_table:
841 makeSqlTag (name, SQLTAG_TABLE);
842 break;
844 case KEYWORD_ref:
845 readToken (token);
846 if (isKeyword (token, KEYWORD_cursor))
847 makeSqlTag (name, SQLTAG_CURSOR);
848 break;
850 default: break;
852 vStringClear (token->scope);
855 vStringCopy(token->scope, saveScope);
856 deleteToken (name);
857 vStringDelete(saveScope);
860 static void parseSimple (tokenInfo *const token, const sqlKind kind)
862 readToken (token);
863 if (isType (token, TOKEN_IDENTIFIER) ||
864 isType (token, TOKEN_STRING))
865 makeSqlTag (token, kind);
868 static void parseDeclare (tokenInfo *const token, const boolean local)
871 * PL/SQL declares are of this format:
872 * IS|AS
873 * [declare]
874 * CURSOR curname ...
875 * varname1 datatype;
876 * varname2 datatype;
877 * varname3 datatype;
878 * begin
881 if (isKeyword (token, KEYWORD_declare))
882 readToken (token);
883 while (! isKeyword (token, KEYWORD_begin) && ! isKeyword (token, KEYWORD_end))
885 switch (token->keyword)
887 case KEYWORD_cursor: parseSimple (token, SQLTAG_CURSOR); break;
888 case KEYWORD_function: parseSubProgram (token); break;
889 case KEYWORD_procedure: parseSubProgram (token); break;
890 case KEYWORD_subtype: parseSimple (token, SQLTAG_SUBTYPE); break;
891 case KEYWORD_trigger: parseSimple (token, SQLTAG_TRIGGER); break;
892 case KEYWORD_type: parseType (token); break;
894 default:
895 if (isType (token, TOKEN_IDENTIFIER))
897 if (local)
899 makeSqlTag (token, SQLTAG_LOCAL_VARIABLE);
900 } else {
901 makeSqlTag (token, SQLTAG_VARIABLE);
904 break;
906 findToken (token, TOKEN_SEMICOLON);
907 readToken (token);
911 static void parseDeclareANSI (tokenInfo *const token, const boolean local)
913 tokenInfo *const type = newToken ();
915 * ANSI declares are of this format:
916 * BEGIN
917 * DECLARE varname1 datatype;
918 * DECLARE varname2 datatype;
919 * ...
921 * This differ from PL/SQL where DECLARE preceeds the BEGIN block
922 * and the DECLARE keyword is not repeated.
924 while (isKeyword (token, KEYWORD_declare))
926 readToken (token);
927 readToken (type);
929 if (isKeyword (type, KEYWORD_cursor))
930 makeSqlTag (token, SQLTAG_CURSOR);
931 else if (isKeyword (token, KEYWORD_local) &&
932 isKeyword (type, KEYWORD_temporary))
935 * DECLARE LOCAL TEMPORARY TABLE table_name (
936 * c1 int,
937 * c2 int
938 * );
940 readToken (token);
941 if (isKeyword (token, KEYWORD_table))
943 readToken (token);
944 if (isType(token, TOKEN_IDENTIFIER) ||
945 isType(token, TOKEN_STRING) )
947 makeSqlTag (token, SQLTAG_TABLE);
951 else if (isType (token, TOKEN_IDENTIFIER) ||
952 isType (token, TOKEN_STRING))
954 if (local)
955 makeSqlTag (token, SQLTAG_LOCAL_VARIABLE);
956 else
957 makeSqlTag (token, SQLTAG_VARIABLE);
959 findToken (token, TOKEN_SEMICOLON);
960 readToken (token);
962 deleteToken (type);
965 static void parseLabel (tokenInfo *const token)
968 * A label has this format:
969 * <<tobacco_dependency>>
970 * DECLARE
971 * v_senator VARCHAR2(100) := 'THURMOND, JESSE';
972 * BEGIN
973 * IF total_contributions (v_senator, 'TOBACCO') > 25000
974 * THEN
975 * <<alochol_dependency>>
976 * DECLARE
977 * v_senator VARCHAR2(100) := 'WHATEVERIT, TAKES';
978 * BEGIN
979 * ...
982 Assert (isType (token, TOKEN_BLOCK_LABEL_BEGIN));
983 readToken (token);
984 if (isType (token, TOKEN_IDENTIFIER))
986 makeSqlTag (token, SQLTAG_BLOCK_LABEL);
987 readToken (token); /* read end of label */
991 static void parseStatements (tokenInfo *const token)
995 if (isType (token, TOKEN_BLOCK_LABEL_BEGIN))
996 parseLabel (token);
997 else
999 switch (token->keyword)
1002 * EXCEPTION
1003 * <exception handler>;
1005 * Where an exception handler could be:
1006 * BEGIN
1007 * WHEN OTHERS THEN
1008 * x := x + 3;
1009 * END;
1010 * In this case we need to skip this keyword and
1011 * move on to the next token without reading until
1012 * TOKEN_SEMICOLON;
1014 case KEYWORD_exception:
1015 readToken (token);
1016 continue;
1019 * WHEN statements can be used in exception clauses
1020 * and CASE statements. The CASE statement should skip
1021 * these given below we skip over to an END statement.
1022 * But for an exception clause, we can have:
1023 * EXCEPTION
1024 * WHEN OTHERS THEN
1025 * BEGIN
1026 * x := x + 3;
1027 * END;
1028 * If we skip to the TOKEN_SEMICOLON, we miss the begin
1029 * of a nested BEGIN END block. So read the next token
1030 * after the THEN and restart the LOOP.
1032 case KEYWORD_when:
1033 while (! isKeyword (token, KEYWORD_then))
1034 readToken (token);
1035 readToken (token);
1036 continue;
1039 * We do not want to look for a ; since for an empty
1040 * IF block, that would skip over the END.
1041 * IF...THEN
1042 * END IF;
1044 case KEYWORD_if:
1045 while (! isKeyword (token, KEYWORD_then))
1046 readToken (token);
1047 /*readToken (token);*/
1048 parseStatements (token);
1049 break;
1052 * LOOP...
1053 * END LOOP;
1055 * FOR loop_name AS cursor_name CURSOR FOR ...
1056 * END FOR;
1058 case KEYWORD_loop:
1059 case KEYWORD_case:
1060 case KEYWORD_for:
1061 readToken (token);
1062 parseStatements (token);
1063 break;
1065 case KEYWORD_declare:
1066 case KEYWORD_begin:
1067 parseBlock (token, TRUE);
1068 break;
1070 default:
1071 readToken (token);
1072 break;
1074 findToken (token, TOKEN_SEMICOLON);
1076 readToken (token);
1077 } while (! isKeyword (token, KEYWORD_end));
1080 static void parseBlock (tokenInfo *const token, const boolean local)
1082 if (isType (token, TOKEN_BLOCK_LABEL_BEGIN))
1084 parseLabel (token);
1085 readToken (token);
1087 if (! isKeyword (token, KEYWORD_begin))
1089 readToken (token);
1090 /*dispToken(token, "parseBlock calling parseDeclare");*/
1091 parseDeclare (token, local);
1093 if (isKeyword (token, KEYWORD_begin))
1095 readToken (token);
1096 parseDeclareANSI (token, local);
1097 while (! isKeyword (token, KEYWORD_end))
1098 parseStatements (token);
1099 findToken (token, TOKEN_SEMICOLON);
1103 static void parsePackage (tokenInfo *const token)
1106 * Packages can be specified in a number of ways:
1107 * CREATE OR REPLACE PACKAGE pkg_name AS
1108 * or
1109 * CREATE OR REPLACE PACKAGE owner.pkg_name AS
1110 * or by specifying a package body
1111 * CREATE OR REPLACE PACKAGE BODY pkg_name AS
1112 * CREATE OR REPLACE PACKAGE BODY owner.pkg_name AS
1114 tokenInfo *const name = newToken ();
1115 readToken (name);
1116 if (isKeyword (name, KEYWORD_body))
1117 readToken (name);
1118 /*dispToken(token, "parsePackage after BODY");*/
1119 /* Chceck for owner.pkg_name */
1120 while (! isKeyword (token, KEYWORD_is))
1122 readToken (token);
1123 if ( isType(token, TOKEN_PERIOD) )
1125 readToken (name);
1126 /*dispToken(name, "parsePackage new name");*/
1129 dispToken(name, "parsePackage name");
1130 if (isKeyword (token, KEYWORD_is))
1132 /*dispToken(token, "parsePackage processing IS");*/
1133 if (isType (name, TOKEN_IDENTIFIER) ||
1134 isType (name, TOKEN_STRING))
1135 makeSqlTag (name, SQLTAG_PACKAGE);
1136 parseBlock (token, FALSE);
1138 findToken (token, TOKEN_SEMICOLON);
1139 deleteToken (name);
1142 static void parseTable (tokenInfo *const token)
1144 tokenInfo *const name = newToken ();
1146 /* This deals with these formats
1147 * create table t1 (c1 int);
1148 * create global tempoary table t2 (c1 int);
1149 * create table "t3" (c1 int);
1150 * create table bob.t4 (c1 int);
1151 * create table bob."t5" (c1 int);
1152 * create table "bob"."t6" (c1 int);
1153 * create table bob."t7" (c1 int);
1154 * Proxy tables use this format:
1155 * create existing table bob."t7" AT '...'; */
1157 readToken (name);
1158 readToken (token);
1159 if (isType (token, TOKEN_PERIOD))
1161 readToken (name);
1162 readToken (token);
1164 if (isType (token, TOKEN_OPEN_PAREN))
1166 if (isType (name, TOKEN_IDENTIFIER) ||
1167 isType (name, TOKEN_STRING))
1169 makeSqlTag (name, SQLTAG_TABLE);
1170 vStringCopy(token->scope, name->string);
1171 parseRecord (token);
1172 vStringClear (token->scope);
1174 } else if (isKeyword (token, KEYWORD_at))
1176 if (isType (name, TOKEN_IDENTIFIER))
1178 makeSqlTag (name, SQLTAG_TABLE);
1181 findToken (token, TOKEN_SEMICOLON);
1182 deleteToken (name);
1185 static void parseIndex (tokenInfo *const token)
1187 tokenInfo *const name = newToken ();
1188 tokenInfo *const owner = newToken ();
1190 /* This deals with these formats
1191 * create index i1 on t1(c1) create index "i2" on t1(c1)
1192 * create virtual unique clustered index "i3" on t1(c1)
1193 * create unique clustered index "i4" on t1(c1)
1194 * create clustered index "i5" on t1(c1)
1195 * create bitmap index "i6" on t1(c1) */
1197 readToken (name);
1198 readToken (token);
1199 if (isType (token, TOKEN_PERIOD))
1201 readToken (name);
1202 readToken (token);
1204 if ( isKeyword (token, KEYWORD_on) &&
1205 (isType (name, TOKEN_IDENTIFIER) || isType (name, TOKEN_STRING) ) )
1207 readToken (owner);
1208 readToken (token);
1209 if (isType (token, TOKEN_PERIOD))
1211 readToken (owner);
1212 readToken (token);
1214 addToScope(name, owner->string);
1215 makeSqlTag (name, SQLTAG_INDEX);
1217 findToken (token, TOKEN_SEMICOLON);
1218 deleteToken (name);
1219 deleteToken (owner);
1222 static void parseEvent (tokenInfo *const token)
1224 tokenInfo *const name = newToken ();
1226 /* This deals with these formats
1227 * create event e1 handler begin end;
1228 * create event "e2" handler begin end;
1229 * create event dba."e3" handler begin end;
1230 * create event "dba"."e4" handler begin end; */
1232 readToken (name);
1233 readToken (token);
1234 if (isType (token, TOKEN_PERIOD))
1236 readToken (name);
1238 while (! (isKeyword (token, KEYWORD_handler) ||
1239 (isType (token, TOKEN_SEMICOLON))) )
1241 readToken (token);
1244 if ( isKeyword (token, KEYWORD_handler) ||
1245 isType (token, TOKEN_SEMICOLON) )
1247 makeSqlTag (name, SQLTAG_EVENT);
1250 if (isKeyword (token, KEYWORD_handler))
1252 readToken (token);
1253 if ( isKeyword (token, KEYWORD_begin) )
1255 parseBlock (token, TRUE);
1257 findToken (token, TOKEN_SEMICOLON);
1259 deleteToken (name);
1262 static void parseTrigger (tokenInfo *const token)
1264 tokenInfo *const name = newToken ();
1265 tokenInfo *const table = newToken ();
1267 /* This deals with these formats
1268 * create or replace trigger tr1 begin end;
1269 * create trigger "tr2" begin end;
1270 * drop trigger "droptr1";
1271 * create trigger "tr3" CALL sp_something();
1272 * create trigger "owner"."tr4" begin end;
1273 * create trigger "tr5" not valid;
1274 * create trigger "tr6" begin end; */
1276 readToken (name);
1277 readToken (token);
1278 if (isType (token, TOKEN_PERIOD))
1280 readToken (name);
1281 readToken (token);
1284 while (! (isKeyword (token, KEYWORD_on) ||
1285 ( isType (token, TOKEN_SEMICOLON))) )
1287 readToken (token);
1290 if (! isType (token, TOKEN_SEMICOLON) )
1292 readToken (table);
1293 readToken (token);
1294 if (isType (token, TOKEN_PERIOD))
1296 readToken (table);
1297 readToken (token);
1300 while (! (isKeyword (token, KEYWORD_begin) ||
1301 (isKeyword (token, KEYWORD_call)) ||
1302 ( isType (token, TOKEN_SEMICOLON))) )
1304 readToken (token);
1305 if ( isKeyword (token, KEYWORD_declare) )
1307 addToScope(token, name->string);
1308 parseDeclare(token, TRUE);
1309 vStringClear(token->scope);
1313 if ( isKeyword (token, KEYWORD_begin) ||
1314 isKeyword (token, KEYWORD_call) )
1316 addToScope(name, table->string);
1317 makeSqlTag (name, SQLTAG_TRIGGER);
1318 addToScope(token, table->string);
1319 if ( isKeyword (token, KEYWORD_begin) )
1321 parseBlock (token, TRUE);
1323 vStringClear(token->scope);
1327 if (! isType (token, TOKEN_SEMICOLON) )
1329 findToken (token, TOKEN_SEMICOLON);
1331 deleteToken (name);
1332 deleteToken (table);
1335 static void parsePublication (tokenInfo *const token)
1337 tokenInfo *const name = newToken ();
1339 /* This deals with these formats
1340 * create or replace publication pu1 ()
1341 * create publication "pu2" ()
1342 * create publication dba."pu3" ()
1343 * create publication "dba"."pu4" () */
1345 readToken (name);
1346 readToken (token);
1347 if (isType (token, TOKEN_PERIOD))
1349 readToken (name);
1350 readToken (token);
1352 if (isType (token, TOKEN_OPEN_PAREN))
1354 if (isType (name, TOKEN_IDENTIFIER) ||
1355 isType (name, TOKEN_STRING))
1357 makeSqlTag (name, SQLTAG_PUBLICATION);
1360 findToken (token, TOKEN_SEMICOLON);
1361 deleteToken (name);
1364 static void parseService (tokenInfo *const token)
1366 tokenInfo *const name = newToken ();
1368 /* This deals with these formats
1369 * CREATE SERVICE s1 TYPE 'HTML'
1370 * AUTHORIZATION OFF USER DBA AS
1371 * SELECT *
1372 * FROM SYS.SYSTABLE;
1373 * CREATE SERVICE "s2" TYPE 'HTML'
1374 * AUTHORIZATION OFF USER DBA AS
1375 * CALL sp_Something(); */
1377 readToken (name);
1378 readToken (token);
1379 if (isKeyword (token, KEYWORD_type))
1381 if (isType (name, TOKEN_IDENTIFIER) ||
1382 isType (name, TOKEN_STRING))
1384 makeSqlTag (name, SQLTAG_SERVICE);
1387 findToken (token, TOKEN_SEMICOLON);
1388 deleteToken (name);
1391 static void parseDomain (tokenInfo *const token)
1393 tokenInfo *const name = newToken ();
1395 /* This deals with these formats
1396 * CREATE DOMAIN|DATATYPE [AS] your_name ...; */
1398 readToken (name);
1399 if (isKeyword (name, KEYWORD_is))
1401 readToken (name);
1403 readToken (token);
1404 if (isType (name, TOKEN_IDENTIFIER) ||
1405 isType (name, TOKEN_STRING))
1407 makeSqlTag (name, SQLTAG_DOMAIN);
1409 findToken (token, TOKEN_SEMICOLON);
1410 deleteToken (name);
1413 static void parseDrop (tokenInfo *const token)
1415 /* This deals with these formats
1416 * DROP TABLE|PROCEDURE|DOMAIN|DATATYPE name;
1418 * Just simply skip over these statements.
1419 * They are often confused with PROCEDURE prototypes
1420 * since the syntax is similar, this effectively deals with
1421 * the issue for all types. */
1423 /*dispToken(token, "parseDrop");*/
1424 findToken (token, TOKEN_SEMICOLON);
1427 static void parseVariable (tokenInfo *const token)
1429 tokenInfo *const name = newToken ();
1431 /* This deals with these formats
1432 * create variable varname1 integer;
1433 * create variable @varname2 integer;
1434 * create variable "varname3" integer;
1435 * drop variable @varname3; */
1437 readToken (name);
1438 readToken (token);
1439 if ( (isType (name, TOKEN_IDENTIFIER) || isType (name, TOKEN_STRING))
1440 && !isType (token, TOKEN_SEMICOLON) )
1442 makeSqlTag (name, SQLTAG_VARIABLE);
1444 if (! isType (token, TOKEN_SEMICOLON) )
1445 findToken (token, TOKEN_SEMICOLON);
1447 deleteToken (name);
1450 static void parseSynonym (tokenInfo *const token)
1452 tokenInfo *const name = newToken ();
1454 /* This deals with these formats
1455 * create variable varname1 integer;
1456 * create variable @varname2 integer;
1457 * create variable "varname3" integer;
1458 * drop variable @varname3; */
1460 readToken (name);
1461 readToken (token);
1462 if ( (isType (name, TOKEN_IDENTIFIER) || isType (name, TOKEN_STRING))
1463 && isKeyword (token, KEYWORD_for) )
1465 makeSqlTag (name, SQLTAG_SYNONYM);
1467 if (! isType (token, TOKEN_SEMICOLON) )
1468 findToken (token, TOKEN_SEMICOLON);
1470 deleteToken (name);
1473 static void parseView (tokenInfo *const token)
1475 tokenInfo *const name = newToken ();
1477 /* This deals with these formats
1478 * create variable varname1 integer;
1479 * create variable @varname2 integer;
1480 * create variable "varname3" integer;
1481 * drop variable @varname3; */
1483 readToken (name);
1484 readToken (token);
1485 if (isType (token, TOKEN_PERIOD))
1487 readToken (name);
1488 readToken (token);
1490 if ( isType (token, TOKEN_OPEN_PAREN) )
1492 skipArgumentList(token);
1496 while (!(isKeyword (token, KEYWORD_is) ||
1497 isType (token, TOKEN_SEMICOLON)
1500 readToken (token);
1503 if ( (isType (name, TOKEN_IDENTIFIER) || isType (name, TOKEN_STRING))
1504 && isKeyword (token, KEYWORD_is) )
1506 makeSqlTag (name, SQLTAG_VIEW);
1509 if (! isType (token, TOKEN_SEMICOLON) )
1510 findToken (token, TOKEN_SEMICOLON);
1512 deleteToken (name);
1515 static void parseMLTable (tokenInfo *const token)
1517 tokenInfo *const version = newToken ();
1518 tokenInfo *const table = newToken ();
1519 tokenInfo *const event = newToken ();
1521 /* This deals with these formats
1522 * call ml_add_table_script( 'version', 'table_name', 'event',
1523 * 'some SQL statement'
1524 * ); */
1526 readToken (token);
1527 if ( isType (token, TOKEN_OPEN_PAREN) )
1529 readToken (version);
1530 readToken (token);
1531 while (!(isType (token, TOKEN_COMMA) ||
1532 isType (token, TOKEN_CLOSE_PAREN)
1535 readToken (token);
1538 if (isType (token, TOKEN_COMMA))
1540 readToken (table);
1541 readToken (token);
1542 while (!(isType (token, TOKEN_COMMA) ||
1543 isType (token, TOKEN_CLOSE_PAREN)
1546 readToken (token);
1549 if (isType (token, TOKEN_COMMA))
1551 readToken (event);
1553 if (isType (version, TOKEN_STRING) &&
1554 isType (table, TOKEN_STRING) &&
1555 isType (event, TOKEN_STRING) )
1557 addToScope(version, table->string);
1558 addToScope(version, event->string);
1559 makeSqlTag (version, SQLTAG_MLTABLE);
1562 if( !isType (token, TOKEN_CLOSE_PAREN) )
1563 findToken (token, TOKEN_CLOSE_PAREN);
1567 if (! isType (token, TOKEN_SEMICOLON) )
1568 findToken (token, TOKEN_SEMICOLON);
1570 deleteToken (version);
1571 deleteToken (table);
1572 deleteToken (event);
1575 static void parseMLConn (tokenInfo *const token)
1577 tokenInfo *const version = newToken ();
1578 tokenInfo *const event = newToken ();
1580 /* This deals with these formats
1581 * call ml_add_connection_script( 'version', 'event',
1582 * 'some SQL statement'
1583 * ); */
1585 readToken (token);
1586 if ( isType (token, TOKEN_OPEN_PAREN) )
1588 readToken (version);
1589 readToken (token);
1590 while (!(isType (token, TOKEN_COMMA) ||
1591 isType (token, TOKEN_CLOSE_PAREN)
1594 readToken (token);
1597 if (isType (token, TOKEN_COMMA))
1599 readToken (event);
1601 if (isType (version, TOKEN_STRING) &&
1602 isType (event, TOKEN_STRING) )
1604 addToScope(version, event->string);
1605 makeSqlTag (version, SQLTAG_MLCONN);
1608 if( !isType (token, TOKEN_CLOSE_PAREN) )
1609 findToken (token, TOKEN_CLOSE_PAREN);
1613 if (! isType (token, TOKEN_SEMICOLON) )
1614 findToken (token, TOKEN_SEMICOLON);
1616 deleteToken (version);
1617 deleteToken (event);
1621 static void parseSqlFile (tokenInfo *const token)
1625 readToken (token);
1626 /*dispToken(token, "psf");*/
1628 if (isType (token, TOKEN_BLOCK_LABEL_BEGIN))
1629 parseLabel (token);
1630 else switch (token->keyword)
1632 case KEYWORD_begin: parseBlock (token, FALSE); break;
1633 case KEYWORD_cursor: parseSimple (token, SQLTAG_CURSOR); break;
1634 case KEYWORD_datatype: parseDomain (token); break;
1635 case KEYWORD_declare: parseBlock (token, FALSE); break;
1636 case KEYWORD_domain: parseDomain (token); break;
1637 case KEYWORD_drop: parseDrop (token); break;
1638 case KEYWORD_event: parseEvent (token); break;
1639 case KEYWORD_function: parseSubProgram (token); break;
1640 case KEYWORD_index: parseIndex (token); break;
1641 case KEYWORD_ml_table: parseMLTable (token); break;
1642 case KEYWORD_ml_conn: parseMLConn (token); break;
1643 case KEYWORD_package: parsePackage (token); break;
1644 case KEYWORD_procedure: parseSubProgram (token); break;
1645 case KEYWORD_publication: parsePublication (token); break;
1646 case KEYWORD_service: parseService (token); break;
1647 case KEYWORD_subtype: parseSimple (token, SQLTAG_SUBTYPE); break;
1648 case KEYWORD_synonym: parseSynonym (token); break;
1649 case KEYWORD_table: parseTable (token); break;
1650 case KEYWORD_trigger: parseTrigger (token); break;
1651 case KEYWORD_type: parseType (token); break;
1652 case KEYWORD_variable: parseVariable (token); break;
1653 case KEYWORD_view: parseView (token); break;
1654 default: break;
1656 } while (! isKeyword (token, KEYWORD_end));
1659 static void initialize (const langType language)
1661 Assert (sizeof (SqlKinds) / sizeof (SqlKinds [0]) == SQLTAG_COUNT);
1662 Lang_sql = language;
1663 buildSqlKeywordHash ();
1666 static void findSqlTags (void)
1668 tokenInfo *const token = newToken ();
1669 exception_t exception = (exception_t) (setjmp (Exception));
1670 while (exception == ExceptionNone)
1671 parseSqlFile (token);
1672 deleteToken (token);
1675 extern parserDefinition* SqlParser (void)
1677 static const char *const extensions [] = { "sql", NULL };
1678 parserDefinition* def = parserNew ("SQL");
1679 def->kinds = SqlKinds;
1680 def->kindCount = KIND_COUNT (SqlKinds);
1681 def->extensions = extensions;
1682 def->parser = findSqlTags;
1683 def->initialize = initialize;
1684 return def;
1687 /* vi:set tabstop=8 shiftwidth=4: */