FreeBasic: Update keywords
[geany-mirror.git] / tagmanager / ctags / sql.c
blobeec7975beaaef7db8d16ddef1bce6a4b30a25c36
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 "Oracle Database PL/SQL Language Reference":
33 * http://download.oracle.com/docs/cd/B28359_01/appdev.111/b28370/toc.htm
35 * Sample PL/SQL code is available from:
36 * http://www.orafaq.com/faqscrpt.htm#GENPLSQL
38 * On-line SQL Anywhere Documentation
39 * http://www.ianywhere.com/developer/product_manuals/sqlanywhere/index.html
43 * MACROS
45 #define isType(token,t) (boolean) ((token)->type == (t))
46 #define isKeyword(token,k) (boolean) ((token)->keyword == (k))
49 * DATA DECLARATIONS
52 typedef enum eException { ExceptionNone, ExceptionEOF } exception_t;
55 * Used to specify type of keyword.
57 typedef enum eKeywordId {
58 KEYWORD_NONE = -1,
59 KEYWORD_is,
60 KEYWORD_begin,
61 KEYWORD_body,
62 KEYWORD_cursor,
63 KEYWORD_declare,
64 KEYWORD_end,
65 KEYWORD_function,
66 KEYWORD_if,
67 KEYWORD_else,
68 KEYWORD_elseif,
69 KEYWORD_endif,
70 KEYWORD_loop,
71 KEYWORD_while,
72 KEYWORD_case,
73 KEYWORD_for,
74 KEYWORD_do,
75 KEYWORD_call,
76 KEYWORD_package,
77 KEYWORD_pragma,
78 KEYWORD_procedure,
79 KEYWORD_record,
80 KEYWORD_object,
81 KEYWORD_ref,
82 KEYWORD_rem,
83 KEYWORD_return,
84 KEYWORD_returns,
85 KEYWORD_subtype,
86 KEYWORD_table,
87 KEYWORD_trigger,
88 KEYWORD_type,
89 KEYWORD_index,
90 KEYWORD_event,
91 KEYWORD_publication,
92 KEYWORD_service,
93 KEYWORD_domain,
94 KEYWORD_datatype,
95 KEYWORD_result,
96 KEYWORD_url,
97 KEYWORD_internal,
98 KEYWORD_external,
99 KEYWORD_when,
100 KEYWORD_then,
101 KEYWORD_variable,
102 KEYWORD_exception,
103 KEYWORD_at,
104 KEYWORD_on,
105 KEYWORD_primary,
106 KEYWORD_references,
107 KEYWORD_unique,
108 KEYWORD_check,
109 KEYWORD_constraint,
110 KEYWORD_foreign,
111 KEYWORD_ml_table,
112 KEYWORD_ml_table_lang,
113 KEYWORD_ml_table_dnet,
114 KEYWORD_ml_table_java,
115 KEYWORD_ml_table_chk,
116 KEYWORD_ml_conn,
117 KEYWORD_ml_conn_lang,
118 KEYWORD_ml_conn_dnet,
119 KEYWORD_ml_conn_java,
120 KEYWORD_ml_conn_chk,
121 KEYWORD_ml_prop,
122 KEYWORD_local,
123 KEYWORD_temporary,
124 KEYWORD_drop,
125 KEYWORD_view,
126 KEYWORD_synonym,
127 KEYWORD_handler,
128 KEYWORD_comment,
129 KEYWORD_create,
130 KEYWORD_go
131 } keywordId;
134 * Used to determine whether keyword is valid for the token language and
135 * what its ID is.
137 typedef struct sKeywordDesc {
138 const char *name;
139 keywordId id;
140 } keywordDesc;
142 typedef enum eTokenType {
143 TOKEN_UNDEFINED,
144 TOKEN_BLOCK_LABEL_BEGIN,
145 TOKEN_BLOCK_LABEL_END,
146 TOKEN_CHARACTER,
147 TOKEN_CLOSE_PAREN,
148 TOKEN_COLON,
149 TOKEN_SEMICOLON,
150 TOKEN_COMMA,
151 TOKEN_IDENTIFIER,
152 TOKEN_KEYWORD,
153 TOKEN_OPEN_PAREN,
154 TOKEN_OPERATOR,
155 TOKEN_OTHER,
156 TOKEN_STRING,
157 TOKEN_PERIOD,
158 TOKEN_OPEN_CURLY,
159 TOKEN_CLOSE_CURLY,
160 TOKEN_OPEN_SQUARE,
161 TOKEN_CLOSE_SQUARE,
162 TOKEN_TILDE,
163 TOKEN_FORWARD_SLASH,
164 TOKEN_EQUAL
165 } tokenType;
167 typedef struct sTokenInfoSQL {
168 tokenType type;
169 keywordId keyword;
170 vString * string;
171 vString * scope;
172 int scopeKind;
173 int begin_end_nest_lvl;
174 unsigned long lineNumber;
175 MIOPos 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, 'n', "namespace", "procedures" },
222 { FALSE, 'r', "record", "records" },
223 { TRUE, 's', "subtype", "subtypes" },
224 { TRUE, 't', "class", "tables" },
225 { TRUE, 'T', "macro", "triggers" },
226 { TRUE, 'v', "variable", "variables" },
227 { TRUE, 'i', "struct", "indexes" },
228 { TRUE, 'e', "event", "events" },
229 { TRUE, 'U', "publication", "publications" },
230 { TRUE, 'R', "service", "services" },
231 { TRUE, 'D', "domain", "domains" },
232 { TRUE, 'm', "member", "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->scopeKind = SQLTAG_COUNT;
429 token->begin_end_nest_lvl = 0;
430 token->lineNumber = getSourceLineNumber ();
431 token->filePosition = getInputFilePosition ();
433 return token;
436 static void deleteToken (tokenInfo *const token)
438 vStringDelete (token->string);
439 vStringDelete (token->scope);
440 eFree (token);
443 static int analyzeToken (vString *const name, langType language)
445 vString *keyword = vStringNew ();
446 int result;
447 vStringCopyToLower (keyword, name);
448 result = lookupKeyword (vStringValue (keyword), language);
449 vStringDelete (keyword);
450 return result;
454 * Tag generation functions
457 static void makeSqlTag (tokenInfo *const token, const sqlKind kind)
459 if (SqlKinds [kind].enabled)
461 const char *const name = vStringValue (token->string);
462 tagEntryInfo e;
463 initTagEntry (&e, name);
465 e.lineNumber = token->lineNumber;
466 e.filePosition = token->filePosition;
467 e.kindName = SqlKinds [kind].name;
468 e.kind = SqlKinds [kind].letter;
470 if (vStringLength (token->scope) > 0)
472 Assert (token->scopeKind < SQLTAG_COUNT);
473 e.extensionFields.scope[0] = SqlKinds [token->scopeKind].name;
474 e.extensionFields.scope[1] = vStringValue (token->scope);
477 makeTagEntry (&e);
482 * Parsing functions
485 static void parseString (vString *const string, const int delimiter)
487 boolean end = FALSE;
488 while (! end)
490 int c = fileGetc ();
491 if (c == EOF)
492 end = TRUE;
494 else if (c == '\\')
496 c = fileGetc(); // This maybe a ' or ". //
497 vStringPut(string, c);
500 else if (c == delimiter)
501 end = TRUE;
502 else
503 vStringPut (string, c);
505 vStringTerminate (string);
508 /* Read a C identifier beginning with "firstChar" and places it into "name".
510 static void parseIdentifier (vString *const string, const int firstChar)
512 int c = firstChar;
513 Assert (isIdentChar1 (c));
516 vStringPut (string, c);
517 c = fileGetc ();
518 } while (isIdentChar (c));
519 vStringTerminate (string);
520 if (!isspace (c))
521 fileUngetc (c); /* unget non-identifier character */
524 static void readToken (tokenInfo *const token)
526 int c;
528 token->type = TOKEN_UNDEFINED;
529 token->keyword = KEYWORD_NONE;
530 vStringClear (token->string);
532 getNextChar:
535 c = fileGetc ();
536 token->lineNumber = getSourceLineNumber ();
537 token->filePosition = getInputFilePosition ();
539 * Added " to the list of ignores, not sure what this
540 * might break but it gets by this issue:
541 * create table "t1" (...)
543 * Darren, the code passes all my tests for both
544 * Oracle and SQL Anywhere, but maybe you can tell me
545 * what this may effect.
548 while (c == '\t' || c == ' ' || c == '\n');
550 switch (c)
552 case EOF: longjmp (Exception, (int)ExceptionEOF); break;
553 case '(': token->type = TOKEN_OPEN_PAREN; break;
554 case ')': token->type = TOKEN_CLOSE_PAREN; break;
555 case ':': token->type = TOKEN_COLON; break;
556 case ';': token->type = TOKEN_SEMICOLON; break;
557 case '.': token->type = TOKEN_PERIOD; break;
558 case ',': token->type = TOKEN_COMMA; break;
559 case '{': token->type = TOKEN_OPEN_CURLY; break;
560 case '}': token->type = TOKEN_CLOSE_CURLY; break;
561 case '~': token->type = TOKEN_TILDE; break;
562 case '[': token->type = TOKEN_OPEN_SQUARE; break;
563 case ']': token->type = TOKEN_CLOSE_SQUARE; break;
564 case '=': token->type = TOKEN_EQUAL; break;
566 case '\'':
567 case '"':
568 token->type = TOKEN_STRING;
569 parseString (token->string, c);
570 token->lineNumber = getSourceLineNumber ();
571 token->filePosition = getInputFilePosition ();
572 break;
574 case '-':
575 c = fileGetc ();
576 if (c == '-') /* -- is this the start of a comment? */
578 fileSkipToCharacter ('\n');
579 goto getNextChar;
581 else
583 if (!isspace (c))
584 fileUngetc (c);
585 token->type = TOKEN_OPERATOR;
587 break;
589 case '<':
590 case '>':
592 const int initial = c;
593 int d = fileGetc ();
594 if (d == initial)
596 if (initial == '<')
597 token->type = TOKEN_BLOCK_LABEL_BEGIN;
598 else
599 token->type = TOKEN_BLOCK_LABEL_END;
601 else
603 fileUngetc (d);
604 token->type = TOKEN_UNDEFINED;
606 break;
609 case '\\':
610 c = fileGetc ();
611 if (c != '\\' && c != '"' && c != '\'' && !isspace (c))
612 fileUngetc (c);
613 token->type = TOKEN_CHARACTER;
614 token->lineNumber = getSourceLineNumber ();
615 token->filePosition = getInputFilePosition ();
616 break;
618 case '/':
620 int d = fileGetc ();
621 if ( (d != '*') && /* is this the start of a comment? */
622 (d != '/') ) /* is a one line comment? */
624 token->type = TOKEN_FORWARD_SLASH;
625 fileUngetc (d);
627 else
629 if (d == '*')
633 fileSkipToCharacter ('*');
634 c = fileGetc ();
635 if (c == '/')
636 break;
637 else
638 fileUngetc (c);
639 } while (c != EOF && c != '\0');
640 goto getNextChar;
642 else if (d == '/') /* is this the start of a comment? */
644 fileSkipToCharacter ('\n');
645 goto getNextChar;
648 break;
651 default:
652 if (! isIdentChar1 (c))
653 token->type = TOKEN_UNDEFINED;
654 else
656 parseIdentifier (token->string, c);
657 token->lineNumber = getSourceLineNumber ();
658 token->filePosition = getInputFilePosition ();
659 token->keyword = analyzeToken (token->string, Lang_sql);
660 if (isKeyword (token, KEYWORD_rem))
662 vStringClear (token->string);
663 fileSkipToCharacter ('\n');
664 goto getNextChar;
666 else if (isKeyword (token, KEYWORD_NONE))
667 token->type = TOKEN_IDENTIFIER;
668 else
669 token->type = TOKEN_KEYWORD;
671 break;
676 * reads an indentifier, possibly quoted:
677 * identifier
678 * "identifier"
679 * [identifier]
681 static void readIdentifier (tokenInfo *const token)
683 readToken (token);
684 if (isType (token, TOKEN_OPEN_SQUARE))
686 tokenInfo *const close_square = newToken ();
688 readToken (token);
689 /* eat close swuare */
690 readToken (close_square);
691 deleteToken (close_square);
696 * Token parsing functions
700 * static void addContext (tokenInfo* const parent, const tokenInfo* const child)
702 * if (vStringLength (parent->string) > 0)
704 * vStringCatS (parent->string, ".");
706 * vStringCatS (parent->string, vStringValue(child->string));
707 * vStringTerminate(parent->string);
711 static void addToScope (tokenInfo* const token, vString* const extra, sqlKind kind)
713 if (vStringLength (token->scope) > 0)
715 vStringCatS (token->scope, ".");
717 vStringCatS (token->scope, vStringValue(extra));
718 vStringTerminate(token->scope);
719 token->scopeKind = kind;
723 * Scanning functions
726 static void findToken (tokenInfo *const token, const tokenType type)
728 while (! isType (token, type))
730 readToken (token);
734 static void findCmdTerm (tokenInfo *const token, const boolean check_first)
736 int begin_end_nest_lvl = token->begin_end_nest_lvl;
738 if ( check_first )
740 if ( isCmdTerm(token) )
741 return;
745 readToken (token);
746 } while ( !isCmdTerm(token) && !isMatchedEnd(token, begin_end_nest_lvl) );
749 static void skipToMatched(tokenInfo *const token)
751 int nest_level = 0;
752 tokenType open_token;
753 tokenType close_token;
755 switch (token->type)
757 case TOKEN_OPEN_PAREN:
758 open_token = TOKEN_OPEN_PAREN;
759 close_token = TOKEN_CLOSE_PAREN;
760 break;
761 case TOKEN_OPEN_CURLY:
762 open_token = TOKEN_OPEN_CURLY;
763 close_token = TOKEN_CLOSE_CURLY;
764 break;
765 case TOKEN_OPEN_SQUARE:
766 open_token = TOKEN_OPEN_SQUARE;
767 close_token = TOKEN_CLOSE_SQUARE;
768 break;
769 default:
770 return;
774 * This routine will skip to a matching closing token.
775 * It will also handle nested tokens like the (, ) below.
776 * ( name varchar(30), text binary(10) )
779 if (isType (token, open_token))
781 nest_level++;
782 while (! (isType (token, close_token) && (nest_level == 0)))
784 readToken (token);
785 if (isType (token, open_token))
787 nest_level++;
789 if (isType (token, close_token))
791 if (nest_level > 0)
793 nest_level--;
797 readToken (token);
801 static void copyToken (tokenInfo *const dest, tokenInfo *const src)
803 dest->lineNumber = src->lineNumber;
804 dest->filePosition = src->filePosition;
805 dest->type = src->type;
806 dest->keyword = src->keyword;
807 vStringCopy(dest->string, src->string);
808 vStringCopy(dest->scope, src->scope);
809 dest->scopeKind = src->scopeKind;
812 static void skipArgumentList (tokenInfo *const token)
815 * Other databases can have arguments with fully declared
816 * datatypes:
817 * ( name varchar(30), text binary(10) )
818 * So we must check for nested open and closing parantheses
821 if (isType (token, TOKEN_OPEN_PAREN)) /* arguments? */
823 skipToMatched (token);
827 static void parseSubProgram (tokenInfo *const token)
829 tokenInfo *const name = newToken ();
830 vString * saveScope = vStringNew ();
831 sqlKind saveScopeKind;
834 * This must handle both prototypes and the body of
835 * the procedures.
837 * Prototype:
838 * FUNCTION func_name RETURN integer;
839 * PROCEDURE proc_name( parameters );
840 * Procedure
841 * FUNCTION GET_ML_USERNAME RETURN VARCHAR2
842 * IS
843 * BEGIN
844 * RETURN v_sync_user_id;
845 * END GET_ML_USERNAME;
847 * PROCEDURE proc_name( parameters )
848 * IS
849 * BEGIN
850 * END;
851 * CREATE PROCEDURE proc_name( parameters )
852 * EXTERNAL NAME ... ;
853 * CREATE PROCEDURE proc_name( parameters )
854 * BEGIN
855 * END;
857 * CREATE FUNCTION f_GetClassName(
858 * IN @object VARCHAR(128)
859 * ,IN @code VARCHAR(128)
861 * RETURNS VARCHAR(200)
862 * DETERMINISTIC
863 * BEGIN
865 * IF( @object = 'user_state' ) THEN
866 * SET something = something;
867 * END IF;
869 * RETURN @name;
870 * END;
872 * Note, a Package adds scope to the items within.
873 * create or replace package demo_pkg is
874 * test_var number;
875 * function test_func return varchar2;
876 * function more.test_func2 return varchar2;
877 * end demo_pkg;
878 * So the tags generated here, contain the package name:
879 * demo_pkg.test_var
880 * demo_pkg.test_func
881 * demo_pkg.more.test_func2
883 const sqlKind kind = isKeyword (token, KEYWORD_function) ?
884 SQLTAG_FUNCTION : SQLTAG_PROCEDURE;
885 Assert (isKeyword (token, KEYWORD_function) ||
886 isKeyword (token, KEYWORD_procedure));
888 vStringCopy(saveScope, token->scope);
889 saveScopeKind = token->scopeKind;
890 readToken (token);
891 copyToken (name, token);
892 readToken (token);
894 if (isType (token, TOKEN_PERIOD))
897 * If this is an Oracle package, then the token->scope should
898 * already be set. If this is the case, also add this value to the
899 * scope.
900 * If this is not an Oracle package, chances are the scope should be
901 * blank and the value just read is the OWNER or CREATOR of the
902 * function and should not be considered part of the scope.
904 if ( vStringLength(saveScope) > 0 )
906 addToScope(token, name->string, kind);
908 readToken (token);
909 copyToken (name, token);
910 readToken (token);
912 if (isType (token, TOKEN_OPEN_PAREN))
914 /* Reads to the next token after the TOKEN_CLOSE_PAREN */
915 skipArgumentList(token);
918 if (kind == SQLTAG_FUNCTION)
920 if (isKeyword (token, KEYWORD_return) || isKeyword (token, KEYWORD_returns))
922 /* Read datatype */
923 readToken (token);
925 * Read token after which could be the
926 * command terminator if a prototype
927 * or an open parantheses
929 readToken (token);
930 if (isType (token, TOKEN_OPEN_PAREN))
932 /* Reads to the next token after the TOKEN_CLOSE_PAREN */
933 skipArgumentList(token);
937 if( isCmdTerm (token) )
939 makeSqlTag (name, SQLTAG_PROTOTYPE);
941 else
943 while (!(isKeyword (token, KEYWORD_is) ||
944 isKeyword (token, KEYWORD_begin) ||
945 isKeyword (token, KEYWORD_at) ||
946 isKeyword (token, KEYWORD_internal) ||
947 isKeyword (token, KEYWORD_external) ||
948 isKeyword (token, KEYWORD_url) ||
949 isType (token, TOKEN_EQUAL) ||
950 isCmdTerm (token)
954 if ( isKeyword (token, KEYWORD_result) )
956 readToken (token);
957 if (isType (token, TOKEN_OPEN_PAREN))
959 /* Reads to the next token after the TOKEN_CLOSE_PAREN */
960 skipArgumentList(token);
962 } else {
963 readToken (token);
966 if (isKeyword (token, KEYWORD_at) ||
967 isKeyword (token, KEYWORD_url) ||
968 isKeyword (token, KEYWORD_internal) ||
969 isKeyword (token, KEYWORD_external) )
971 addToScope(token, name->string, kind);
972 if (isType (name, TOKEN_IDENTIFIER) ||
973 isType (name, TOKEN_STRING) ||
974 !isKeyword (token, KEYWORD_NONE)
976 makeSqlTag (name, kind);
978 vStringClear (token->scope);
979 token->scopeKind = SQLTAG_COUNT;
981 if ( isType (token, TOKEN_EQUAL) )
982 readToken (token);
984 if ( isKeyword (token, KEYWORD_declare) )
985 parseDeclare (token, FALSE);
987 if (isKeyword (token, KEYWORD_is) ||
988 isKeyword (token, KEYWORD_begin) )
990 addToScope(token, name->string, kind);
991 if (isType (name, TOKEN_IDENTIFIER) ||
992 isType (name, TOKEN_STRING) ||
993 !isKeyword (token, KEYWORD_NONE)
995 makeSqlTag (name, kind);
997 parseBlock (token, TRUE);
998 vStringClear (token->scope);
999 token->scopeKind = SQLTAG_COUNT;
1002 vStringCopy(token->scope, saveScope);
1003 token->scopeKind = saveScopeKind;
1004 deleteToken (name);
1005 vStringDelete(saveScope);
1008 static void parseRecord (tokenInfo *const token)
1011 * Make it a bit forgiving, this is called from
1012 * multiple functions, parseTable, parseType
1014 if (!isType (token, TOKEN_OPEN_PAREN))
1015 readToken (token);
1017 Assert (isType (token, TOKEN_OPEN_PAREN));
1020 if ( isType (token, TOKEN_COMMA) || isType (token, TOKEN_OPEN_PAREN) )
1021 readToken (token);
1024 * Create table statements can end with various constraints
1025 * which must be excluded from the SQLTAG_FIELD.
1026 * create table t1 (
1027 * c1 integer,
1028 * c2 char(30),
1029 * c3 numeric(10,5),
1030 * c4 integer,
1031 * constraint whatever,
1032 * primary key(c1),
1033 * foreign key (),
1034 * check ()
1037 if (! (isKeyword(token, KEYWORD_primary) ||
1038 isKeyword(token, KEYWORD_references) ||
1039 isKeyword(token, KEYWORD_unique) ||
1040 isKeyword(token, KEYWORD_check) ||
1041 isKeyword(token, KEYWORD_constraint) ||
1042 isKeyword(token, KEYWORD_foreign) ) )
1044 if (isType (token, TOKEN_IDENTIFIER) ||
1045 isType (token, TOKEN_STRING))
1046 makeSqlTag (token, SQLTAG_FIELD);
1049 while (!(isType (token, TOKEN_COMMA) ||
1050 isType (token, TOKEN_CLOSE_PAREN) ||
1051 isType (token, TOKEN_OPEN_PAREN)
1054 readToken (token);
1056 * A table structure can look like this:
1057 * create table t1 (
1058 * c1 integer,
1059 * c2 char(30),
1060 * c3 numeric(10,5),
1061 * c4 integer
1063 * We can't just look for a COMMA or CLOSE_PAREN
1064 * since that will not deal with the numeric(10,5)
1065 * case. So we need to skip the argument list
1066 * when we find an open paren.
1068 if (isType (token, TOKEN_OPEN_PAREN))
1070 /* Reads to the next token after the TOKEN_CLOSE_PAREN */
1071 skipArgumentList(token);
1074 } while (! isType (token, TOKEN_CLOSE_PAREN));
1077 static void parseType (tokenInfo *const token)
1079 tokenInfo *const name = newToken ();
1080 vString * saveScope = vStringNew ();
1081 sqlKind saveScopeKind;
1083 vStringCopy(saveScope, token->scope);
1084 /* If a scope has been set, add it to the name */
1085 addToScope (name, token->scope, token->scopeKind);
1086 saveScopeKind = token->scopeKind;
1087 readToken (name);
1088 if (isType (name, TOKEN_IDENTIFIER))
1090 readToken (token);
1091 if (isKeyword (token, KEYWORD_is))
1093 readToken (token);
1094 switch (token->keyword)
1096 case KEYWORD_record:
1097 case KEYWORD_object:
1098 makeSqlTag (name, SQLTAG_RECORD);
1099 addToScope (token, name->string, SQLTAG_RECORD);
1100 parseRecord (token);
1101 break;
1103 case KEYWORD_table:
1104 makeSqlTag (name, SQLTAG_TABLE);
1105 break;
1107 case KEYWORD_ref:
1108 readToken (token);
1109 if (isKeyword (token, KEYWORD_cursor))
1110 makeSqlTag (name, SQLTAG_CURSOR);
1111 break;
1113 default: break;
1115 vStringClear (token->scope);
1116 token->scopeKind = SQLTAG_COUNT;
1119 vStringCopy(token->scope, saveScope);
1120 token->scopeKind = saveScopeKind;
1121 deleteToken (name);
1122 vStringDelete(saveScope);
1125 static void parseSimple (tokenInfo *const token, const sqlKind kind)
1127 /* This will simply make the tagname from the first word found */
1128 readToken (token);
1129 if (isType (token, TOKEN_IDENTIFIER) ||
1130 isType (token, TOKEN_STRING))
1131 makeSqlTag (token, kind);
1134 static void parseDeclare (tokenInfo *const token, const boolean local)
1137 * PL/SQL declares are of this format:
1138 * IS|AS
1139 * [declare]
1140 * CURSOR curname ...
1141 * varname1 datatype;
1142 * varname2 datatype;
1143 * varname3 datatype;
1144 * begin
1147 if (isKeyword (token, KEYWORD_declare))
1148 readToken (token);
1149 while (! isKeyword (token, KEYWORD_begin) && ! isKeyword (token, KEYWORD_end))
1151 switch (token->keyword)
1153 case KEYWORD_cursor: parseSimple (token, SQLTAG_CURSOR); break;
1154 case KEYWORD_function: parseSubProgram (token); break;
1155 case KEYWORD_procedure: parseSubProgram (token); break;
1156 case KEYWORD_subtype: parseSimple (token, SQLTAG_SUBTYPE); break;
1157 case KEYWORD_trigger: parseSimple (token, SQLTAG_TRIGGER); break;
1158 case KEYWORD_type: parseType (token); break;
1160 default:
1161 if (isType (token, TOKEN_IDENTIFIER))
1163 if (local)
1165 makeSqlTag (token, SQLTAG_LOCAL_VARIABLE);
1167 else
1169 makeSqlTag (token, SQLTAG_VARIABLE);
1172 break;
1174 findToken (token, TOKEN_SEMICOLON);
1175 readToken (token);
1179 static void parseDeclareANSI (tokenInfo *const token, const boolean local)
1181 tokenInfo *const type = newToken ();
1183 * ANSI declares are of this format:
1184 * BEGIN
1185 * DECLARE varname1 datatype;
1186 * DECLARE varname2 datatype;
1187 * ...
1189 * This differ from PL/SQL where DECLARE preceeds the BEGIN block
1190 * and the DECLARE keyword is not repeated.
1192 while (isKeyword (token, KEYWORD_declare))
1194 readToken (token);
1195 readToken (type);
1197 if (isKeyword (type, KEYWORD_cursor))
1198 makeSqlTag (token, SQLTAG_CURSOR);
1199 else if (isKeyword (token, KEYWORD_local) &&
1200 isKeyword (type, KEYWORD_temporary))
1203 * DECLARE LOCAL TEMPORARY TABLE table_name (
1204 * c1 int,
1205 * c2 int
1206 * );
1208 readToken (token);
1209 if (isKeyword (token, KEYWORD_table))
1211 readToken (token);
1212 if (isType(token, TOKEN_IDENTIFIER) ||
1213 isType(token, TOKEN_STRING) )
1215 makeSqlTag (token, SQLTAG_TABLE);
1219 else if (isType (token, TOKEN_IDENTIFIER) ||
1220 isType (token, TOKEN_STRING))
1222 if (local)
1223 makeSqlTag (token, SQLTAG_LOCAL_VARIABLE);
1224 else
1225 makeSqlTag (token, SQLTAG_VARIABLE);
1227 findToken (token, TOKEN_SEMICOLON);
1228 readToken (token);
1230 deleteToken (type);
1233 static void parseLabel (tokenInfo *const token)
1236 * A label has this format:
1237 * <<tobacco_dependency>>
1238 * DECLARE
1239 * v_senator VARCHAR2(100) := 'THURMOND, JESSE';
1240 * BEGIN
1241 * IF total_contributions (v_senator, 'TOBACCO') > 25000
1242 * THEN
1243 * <<alochol_dependency>>
1244 * DECLARE
1245 * v_senator VARCHAR2(100) := 'WHATEVERIT, TAKES';
1246 * BEGIN
1247 * ...
1250 Assert (isType (token, TOKEN_BLOCK_LABEL_BEGIN));
1251 readToken (token);
1252 if (isType (token, TOKEN_IDENTIFIER))
1254 makeSqlTag (token, SQLTAG_BLOCK_LABEL);
1255 readToken (token); /* read end of label */
1259 static void parseStatements (tokenInfo *const token, const boolean exit_on_endif )
1261 boolean stmtTerm = FALSE;
1265 if (isType (token, TOKEN_BLOCK_LABEL_BEGIN))
1266 parseLabel (token);
1267 else
1269 switch (token->keyword)
1271 case KEYWORD_exception:
1273 * EXCEPTION
1274 * <exception handler>;
1276 * Where an exception handler could be:
1277 * BEGIN
1278 * WHEN OTHERS THEN
1279 * x := x + 3;
1280 * END;
1281 * In this case we need to skip this keyword and
1282 * move on to the next token without reading until
1283 * TOKEN_SEMICOLON;
1285 readToken (token);
1286 continue;
1288 case KEYWORD_when:
1290 * WHEN statements can be used in exception clauses
1291 * and CASE statements. The CASE statement should skip
1292 * these given below we skip over to an END statement.
1293 * But for an exception clause, we can have:
1294 * EXCEPTION
1295 * WHEN OTHERS THEN
1296 * BEGIN
1297 * x := x + 3;
1298 * END;
1299 * If we skip to the TOKEN_SEMICOLON, we miss the begin
1300 * of a nested BEGIN END block. So read the next token
1301 * after the THEN and restart the LOOP.
1303 while (! isKeyword (token, KEYWORD_then))
1304 readToken (token);
1306 readToken (token);
1307 continue;
1309 case KEYWORD_if:
1311 * We do not want to look for a ; since for an empty
1312 * IF block, it would skip over the END.
1313 * IF...THEN
1314 * END IF;
1316 * IF...THEN
1317 * ELSE
1318 * END IF;
1320 * IF...THEN
1321 * ELSEIF...THEN
1322 * ELSE
1323 * END IF;
1325 * or non-ANSI
1326 * IF ...
1327 * BEGIN
1328 * END
1330 while ( ! isKeyword (token, KEYWORD_then) &&
1331 ! isKeyword (token, KEYWORD_begin) )
1333 readToken (token);
1336 if( isKeyword (token, KEYWORD_begin ) )
1338 parseBlock(token, FALSE);
1341 * Handle the non-Ansi IF blocks.
1342 * parseBlock consumes the END, so if the next
1343 * token in a command terminator (like GO)
1344 * we know we are done with this statement.
1346 if ( isCmdTerm (token) )
1347 stmtTerm = TRUE;
1349 else
1351 readToken (token);
1353 while( ! (isKeyword (token, KEYWORD_end ) ||
1354 isKeyword (token, KEYWORD_endif ) )
1357 if ( isKeyword (token, KEYWORD_else) ||
1358 isKeyword (token, KEYWORD_elseif) )
1359 readToken (token);
1361 parseStatements (token, TRUE);
1363 if ( isCmdTerm(token) )
1364 readToken (token);
1369 * parseStatements returns when it finds an END, an IF
1370 * should follow the END for ANSI anyway.
1371 * IF...THEN
1372 * END IF;
1374 if( isKeyword (token, KEYWORD_end ) )
1375 readToken (token);
1377 if( isKeyword (token, KEYWORD_if ) || isKeyword (token, KEYWORD_endif ) )
1379 readToken (token);
1380 if ( isCmdTerm(token) )
1381 stmtTerm = TRUE;
1383 else
1386 * Well we need to do something here.
1387 * There are lots of different END statements
1388 * END;
1389 * END CASE;
1390 * ENDIF;
1391 * ENDCASE;
1395 break;
1397 case KEYWORD_loop:
1398 case KEYWORD_case:
1399 case KEYWORD_for:
1401 * LOOP...
1402 * END LOOP;
1404 * CASE
1405 * WHEN '1' THEN
1406 * END CASE;
1408 * FOR loop_name AS cursor_name CURSOR FOR ...
1409 * DO
1410 * END FOR;
1412 if( isKeyword (token, KEYWORD_for ) )
1414 /* loop name */
1415 readToken (token);
1416 /* AS */
1417 readToken (token);
1419 while ( ! isKeyword (token, KEYWORD_is) )
1422 * If this is not an AS keyword this is
1423 * not a proper FOR statement and should
1424 * simply be ignored
1426 return;
1429 while ( ! isKeyword (token, KEYWORD_do) )
1430 readToken (token);
1434 readToken (token);
1435 while( ! isKeyword (token, KEYWORD_end ) )
1438 if ( isKeyword (token, KEYWORD_else) ||
1439 isKeyword (token, KEYWORD_elseif) )
1440 readToken (token);
1443 parseStatements (token, FALSE);
1445 if ( isCmdTerm(token) )
1446 readToken (token);
1450 if( isKeyword (token, KEYWORD_end ) )
1451 readToken (token);
1454 * Typically ended with
1455 * END LOOP [loop name];
1456 * END CASE
1457 * END FOR [loop name];
1459 if ( isKeyword (token, KEYWORD_loop) ||
1460 isKeyword (token, KEYWORD_case) ||
1461 isKeyword (token, KEYWORD_for) )
1462 readToken (token);
1464 if ( isCmdTerm(token) )
1465 stmtTerm = TRUE;
1467 break;
1469 case KEYWORD_create:
1470 readToken (token);
1471 parseKeywords(token);
1472 break;
1474 case KEYWORD_declare:
1475 case KEYWORD_begin:
1476 parseBlock (token, TRUE);
1477 break;
1479 case KEYWORD_end:
1480 break;
1482 default:
1483 readToken (token);
1484 break;
1487 * Not all statements must end in a semi-colon
1488 * begin
1489 * if current publisher <> 'publish' then
1490 * signal UE_FailStatement
1491 * end if
1492 * end;
1493 * The last statement prior to an end ("signal" above) does
1494 * not need a semi-colon, nor does the end if, since it is
1495 * also the last statement prior to the end of the block.
1497 * So we must read to the first semi-colon or an END block
1499 while ( ! stmtTerm &&
1500 ! ( isKeyword (token, KEYWORD_end) ||
1501 (isCmdTerm(token)) )
1504 if ( isKeyword (token, KEYWORD_endif) &&
1505 exit_on_endif )
1506 return;
1508 if (isType (token, TOKEN_COLON) )
1511 * A : can signal a loop name
1512 * myloop:
1513 * LOOP
1514 * LEAVE myloop;
1515 * END LOOP;
1516 * Unfortunately, labels do not have a
1517 * cmd terminator, therefore we have to check
1518 * if the next token is a keyword and process
1519 * it accordingly.
1521 readToken (token);
1522 if ( isKeyword (token, KEYWORD_loop) ||
1523 isKeyword (token, KEYWORD_while) ||
1524 isKeyword (token, KEYWORD_for) )
1525 /* parseStatements (token); */
1526 return;
1529 readToken (token);
1531 if (isType (token, TOKEN_OPEN_PAREN) ||
1532 isType (token, TOKEN_OPEN_CURLY) ||
1533 isType (token, TOKEN_OPEN_SQUARE) )
1534 skipToMatched (token);
1537 * Since we know how to parse various statements
1538 * if we detect them, parse them to completion
1540 if (isType (token, TOKEN_BLOCK_LABEL_BEGIN) ||
1541 isKeyword (token, KEYWORD_exception) ||
1542 isKeyword (token, KEYWORD_loop) ||
1543 isKeyword (token, KEYWORD_case) ||
1544 isKeyword (token, KEYWORD_for) ||
1545 isKeyword (token, KEYWORD_begin) )
1546 parseStatements (token, FALSE);
1547 else if (isKeyword (token, KEYWORD_if))
1548 parseStatements (token, TRUE);
1553 * We assumed earlier all statements ended with a command terminator.
1554 * See comment above, now, only read if the current token
1555 * is not a command terminator.
1557 if ( isCmdTerm(token) && ! stmtTerm )
1558 stmtTerm = TRUE;
1560 } while (! isKeyword (token, KEYWORD_end) &&
1561 ! (exit_on_endif && isKeyword (token, KEYWORD_endif) ) &&
1562 ! stmtTerm );
1565 static void parseBlock (tokenInfo *const token, const boolean local)
1567 if (isType (token, TOKEN_BLOCK_LABEL_BEGIN))
1569 parseLabel (token);
1570 readToken (token);
1572 if (! isKeyword (token, KEYWORD_begin))
1574 readToken (token);
1576 * These are Oracle style declares which generally come
1577 * between an IS/AS and BEGIN block.
1579 parseDeclare (token, local);
1581 if (isKeyword (token, KEYWORD_begin))
1583 readToken (token);
1585 * Check for ANSI declarations which always follow
1586 * a BEGIN statement. This routine will not advance
1587 * the token if none are found.
1589 parseDeclareANSI (token, local);
1590 token->begin_end_nest_lvl++;
1591 while (! isKeyword (token, KEYWORD_end))
1593 parseStatements (token, FALSE);
1595 if ( isCmdTerm(token) )
1596 readToken (token);
1598 token->begin_end_nest_lvl--;
1601 * Read the next token (we will assume
1602 * it is the command delimiter)
1604 readToken (token);
1607 * Check if the END block is terminated
1609 if ( !isCmdTerm (token) )
1612 * Not sure what to do here at the moment.
1613 * I think the routine that calls parseBlock
1614 * must expect the next token has already
1615 * been read since it is possible this
1616 * token is not a command delimiter.
1618 /* findCmdTerm (token, FALSE); */
1623 static void parsePackage (tokenInfo *const token)
1626 * Packages can be specified in a number of ways:
1627 * CREATE OR REPLACE PACKAGE pkg_name AS
1628 * or
1629 * CREATE OR REPLACE PACKAGE owner.pkg_name AS
1630 * or by specifying a package body
1631 * CREATE OR REPLACE PACKAGE BODY pkg_name AS
1632 * CREATE OR REPLACE PACKAGE BODY owner.pkg_name AS
1634 tokenInfo *const name = newToken ();
1635 readIdentifier (name);
1636 if (isKeyword (name, KEYWORD_body))
1639 * Ignore the BODY tag since we will process
1640 * the body or prototypes in the same manner
1642 readIdentifier (name);
1644 /* Check for owner.pkg_name */
1645 while (! isKeyword (token, KEYWORD_is))
1647 readToken (token);
1648 if ( isType(token, TOKEN_PERIOD) )
1650 readIdentifier (name);
1653 if (isKeyword (token, KEYWORD_is))
1655 if (isType (name, TOKEN_IDENTIFIER) ||
1656 isType (name, TOKEN_STRING))
1657 makeSqlTag (name, SQLTAG_PACKAGE);
1658 addToScope (token, name->string, SQLTAG_PACKAGE);
1659 parseBlock (token, FALSE);
1660 vStringClear (token->scope);
1661 token->scopeKind = SQLTAG_COUNT;
1663 findCmdTerm (token, FALSE);
1664 deleteToken (name);
1667 static void parseTable (tokenInfo *const token)
1669 tokenInfo *const name = newToken ();
1672 * This deals with these formats:
1673 * create table t1 (c1 int);
1674 * create global tempoary table t2 (c1 int);
1675 * create table "t3" (c1 int);
1676 * create table bob.t4 (c1 int);
1677 * create table bob."t5" (c1 int);
1678 * create table "bob"."t6" (c1 int);
1679 * create table bob."t7" (c1 int);
1680 * Proxy tables use this format:
1681 * create existing table bob."t7" AT '...';
1682 * SQL Server and Sybase formats
1683 * create table OnlyTable (
1684 * create table dbo.HasOwner (
1685 * create table [dbo].[HasOwnerSquare] (
1686 * create table master.dbo.HasDb (
1687 * create table master..HasDbNoOwner (
1688 * create table [master].dbo.[HasDbAndOwnerSquare] (
1689 * create table [master]..[HasDbNoOwnerSquare] (
1692 /* This could be a database, owner or table name */
1693 readIdentifier (name);
1694 readToken (token);
1695 if (isType (token, TOKEN_PERIOD))
1698 * This could be a owner or table name.
1699 * But this is also a special case since the table can be
1700 * referenced with a blank owner:
1701 * dbname..tablename
1703 readIdentifier (name);
1704 /* Check if a blank name was provided */
1705 if (isType (name, TOKEN_PERIOD))
1707 readIdentifier (name);
1709 readToken (token);
1710 if (isType (token, TOKEN_PERIOD))
1712 /* This can only be the table name */
1713 readIdentifier (name);
1714 readToken (token);
1717 if (isType (token, TOKEN_OPEN_PAREN))
1719 if (isType (name, TOKEN_IDENTIFIER) ||
1720 isType (name, TOKEN_STRING))
1722 makeSqlTag (name, SQLTAG_TABLE);
1723 vStringCopy(token->scope, name->string);
1724 token->scopeKind = SQLTAG_TABLE;
1725 parseRecord (token);
1726 vStringClear (token->scope);
1727 token->scopeKind = SQLTAG_COUNT;
1730 else if (isKeyword (token, KEYWORD_at))
1732 if (isType (name, TOKEN_IDENTIFIER))
1734 makeSqlTag (name, SQLTAG_TABLE);
1737 findCmdTerm (token, FALSE);
1738 deleteToken (name);
1741 static void parseIndex (tokenInfo *const token)
1743 tokenInfo *const name = newToken ();
1744 tokenInfo *const owner = newToken ();
1747 * This deals with these formats
1748 * create index i1 on t1(c1) create index "i2" on t1(c1)
1749 * create virtual unique clustered index "i3" on t1(c1)
1750 * create unique clustered index "i4" on t1(c1)
1751 * create clustered index "i5" on t1(c1)
1752 * create bitmap index "i6" on t1(c1)
1755 readIdentifier (name);
1756 readToken (token);
1757 if (isType (token, TOKEN_PERIOD))
1759 readIdentifier (name);
1760 readToken (token);
1762 if ( isKeyword (token, KEYWORD_on) &&
1763 (isType (name, TOKEN_IDENTIFIER) || isType (name, TOKEN_STRING) ) )
1765 readIdentifier (owner);
1766 readToken (token);
1767 if (isType (token, TOKEN_PERIOD))
1769 readIdentifier (owner);
1770 readToken (token);
1772 addToScope(name, owner->string, SQLTAG_TABLE /* FIXME? */);
1773 makeSqlTag (name, SQLTAG_INDEX);
1775 findCmdTerm (token, FALSE);
1776 deleteToken (name);
1777 deleteToken (owner);
1780 static void parseEvent (tokenInfo *const token)
1782 tokenInfo *const name = newToken ();
1785 * This deals with these formats
1786 * create event e1 handler begin end;
1787 * create event "e2" handler begin end;
1788 * create event dba."e3" handler begin end;
1789 * create event "dba"."e4" handler begin end;
1792 readIdentifier (name);
1793 readToken (token);
1794 if (isType (token, TOKEN_PERIOD))
1796 readIdentifier (name);
1798 while (! (isKeyword (token, KEYWORD_handler) ||
1799 (isType (token, TOKEN_SEMICOLON))) )
1801 readToken (token);
1804 if ( isKeyword (token, KEYWORD_handler) ||
1805 isType (token, TOKEN_SEMICOLON) )
1807 makeSqlTag (name, SQLTAG_EVENT);
1810 if (isKeyword (token, KEYWORD_handler))
1812 readToken (token);
1813 if ( isKeyword (token, KEYWORD_begin) )
1815 parseBlock (token, TRUE);
1817 findCmdTerm (token, TRUE);
1819 deleteToken (name);
1822 static void parseTrigger (tokenInfo *const token)
1824 tokenInfo *const name = newToken ();
1825 tokenInfo *const table = newToken ();
1828 * This deals with these formats
1829 * create or replace trigger tr1 begin end;
1830 * create trigger "tr2" begin end;
1831 * drop trigger "droptr1";
1832 * create trigger "tr3" CALL sp_something();
1833 * create trigger "owner"."tr4" begin end;
1834 * create trigger "tr5" not valid;
1835 * create trigger "tr6" begin end;
1838 readIdentifier (name);
1839 readToken (token);
1840 if (isType (token, TOKEN_PERIOD))
1842 readIdentifier (name);
1843 readToken (token);
1846 while ( !isKeyword (token, KEYWORD_on) &&
1847 !isCmdTerm (token) )
1849 readToken (token);
1852 /*if (! isType (token, TOKEN_SEMICOLON) ) */
1853 if (! isCmdTerm (token) )
1855 readToken (table);
1856 readToken (token);
1857 if (isType (token, TOKEN_PERIOD))
1859 readToken (table);
1860 readToken (token);
1863 while (! (isKeyword (token, KEYWORD_begin) ||
1864 (isKeyword (token, KEYWORD_call)) ||
1865 ( isCmdTerm (token))) )
1867 if ( isKeyword (token, KEYWORD_declare) )
1869 addToScope(token, name->string, SQLTAG_TRIGGER);
1870 parseDeclare(token, TRUE);
1871 vStringClear(token->scope);
1872 token->scopeKind = SQLTAG_COUNT;
1874 else
1875 readToken (token);
1878 if ( isKeyword (token, KEYWORD_begin) ||
1879 isKeyword (token, KEYWORD_call) )
1881 addToScope(name, table->string, SQLTAG_TABLE);
1882 makeSqlTag (name, SQLTAG_TRIGGER);
1883 addToScope(token, table->string, SQLTAG_TABLE);
1884 if ( isKeyword (token, KEYWORD_begin) )
1886 parseBlock (token, TRUE);
1888 vStringClear(token->scope);
1889 token->scopeKind = SQLTAG_COUNT;
1893 findCmdTerm (token, TRUE);
1894 deleteToken (name);
1895 deleteToken (table);
1898 static void parsePublication (tokenInfo *const token)
1900 tokenInfo *const name = newToken ();
1903 * This deals with these formats
1904 * create or replace publication pu1 ()
1905 * create publication "pu2" ()
1906 * create publication dba."pu3" ()
1907 * create publication "dba"."pu4" ()
1910 readIdentifier (name);
1911 readToken (token);
1912 if (isType (token, TOKEN_PERIOD))
1914 readIdentifier (name);
1915 readToken (token);
1917 if (isType (token, TOKEN_OPEN_PAREN))
1919 if (isType (name, TOKEN_IDENTIFIER) ||
1920 isType (name, TOKEN_STRING))
1922 makeSqlTag (name, SQLTAG_PUBLICATION);
1925 findCmdTerm (token, FALSE);
1926 deleteToken (name);
1929 static void parseService (tokenInfo *const token)
1931 tokenInfo *const name = newToken ();
1934 * This deals with these formats
1935 * CREATE SERVICE s1 TYPE 'HTML'
1936 * AUTHORIZATION OFF USER DBA AS
1937 * SELECT *
1938 * FROM SYS.SYSTABLE;
1939 * CREATE SERVICE "s2" TYPE 'HTML'
1940 * AUTHORIZATION OFF USER DBA AS
1941 * CALL sp_Something();
1944 readIdentifier (name);
1945 readToken (token);
1946 if (isKeyword (token, KEYWORD_type))
1948 if (isType (name, TOKEN_IDENTIFIER) ||
1949 isType (name, TOKEN_STRING))
1951 makeSqlTag (name, SQLTAG_SERVICE);
1954 findCmdTerm (token, FALSE);
1955 deleteToken (name);
1958 static void parseDomain (tokenInfo *const token)
1960 tokenInfo *const name = newToken ();
1963 * This deals with these formats
1964 * CREATE DOMAIN|DATATYPE [AS] your_name ...;
1967 readIdentifier (name);
1968 if (isKeyword (name, KEYWORD_is))
1970 readIdentifier (name);
1972 readToken (token);
1973 if (isType (name, TOKEN_IDENTIFIER) ||
1974 isType (name, TOKEN_STRING))
1976 makeSqlTag (name, SQLTAG_DOMAIN);
1978 findCmdTerm (token, FALSE);
1979 deleteToken (name);
1982 static void parseDrop (tokenInfo *const token)
1985 * This deals with these formats
1986 * DROP TABLE|PROCEDURE|DOMAIN|DATATYPE name;
1988 * Just simply skip over these statements.
1989 * They are often confused with PROCEDURE prototypes
1990 * since the syntax is similar, this effectively deals with
1991 * the issue for all types.
1994 findCmdTerm (token, FALSE);
1997 static void parseVariable (tokenInfo *const token)
1999 tokenInfo *const name = newToken ();
2002 * This deals with these formats
2003 * create variable varname1 integer;
2004 * create variable @varname2 integer;
2005 * create variable "varname3" integer;
2006 * drop variable @varname3;
2009 readIdentifier (name);
2010 readToken (token);
2011 if ( (isType (name, TOKEN_IDENTIFIER) || isType (name, TOKEN_STRING))
2012 && !isType (token, TOKEN_SEMICOLON) )
2014 makeSqlTag (name, SQLTAG_VARIABLE);
2016 findCmdTerm (token, TRUE);
2018 deleteToken (name);
2021 static void parseSynonym (tokenInfo *const token)
2023 tokenInfo *const name = newToken ();
2026 * This deals with these formats
2027 * create variable varname1 integer;
2028 * create variable @varname2 integer;
2029 * create variable "varname3" integer;
2030 * drop variable @varname3;
2033 readIdentifier (name);
2034 readToken (token);
2035 if ( (isType (name, TOKEN_IDENTIFIER) || isType (name, TOKEN_STRING))
2036 && isKeyword (token, KEYWORD_for) )
2038 makeSqlTag (name, SQLTAG_SYNONYM);
2040 findCmdTerm (token, TRUE);
2042 deleteToken (name);
2045 static void parseView (tokenInfo *const token)
2047 tokenInfo *const name = newToken ();
2050 * This deals with these formats
2051 * create variable varname1 integer;
2052 * create variable @varname2 integer;
2053 * create variable "varname3" integer;
2054 * drop variable @varname3;
2057 readIdentifier (name);
2058 readToken (token);
2059 if (isType (token, TOKEN_PERIOD))
2061 readIdentifier (name);
2062 readToken (token);
2064 if ( isType (token, TOKEN_OPEN_PAREN) )
2066 skipArgumentList(token);
2070 while (!(isKeyword (token, KEYWORD_is) ||
2071 isType (token, TOKEN_SEMICOLON)
2074 readToken (token);
2077 if ( (isType (name, TOKEN_IDENTIFIER) || isType (name, TOKEN_STRING))
2078 && isKeyword (token, KEYWORD_is) )
2080 makeSqlTag (name, SQLTAG_VIEW);
2083 findCmdTerm (token, TRUE);
2085 deleteToken (name);
2088 static void parseMLTable (tokenInfo *const token)
2090 tokenInfo *const version = newToken ();
2091 tokenInfo *const table = newToken ();
2092 tokenInfo *const event = newToken ();
2095 * This deals with these formats
2096 * call dbo.ml_add_table_script( 'version', 'table_name', 'event',
2097 * 'some SQL statement'
2098 * );
2101 readToken (token);
2102 if ( isType (token, TOKEN_OPEN_PAREN) )
2104 readToken (version);
2105 readToken (token);
2106 while (!(isType (token, TOKEN_COMMA) ||
2107 isType (token, TOKEN_CLOSE_PAREN)
2110 readToken (token);
2113 if (isType (token, TOKEN_COMMA))
2115 readToken (table);
2116 readToken (token);
2117 while (!(isType (token, TOKEN_COMMA) ||
2118 isType (token, TOKEN_CLOSE_PAREN)
2121 readToken (token);
2124 if (isType (token, TOKEN_COMMA))
2126 readToken (event);
2128 if (isType (version, TOKEN_STRING) &&
2129 isType (table, TOKEN_STRING) &&
2130 isType (event, TOKEN_STRING) )
2132 addToScope(version, table->string, SQLTAG_TABLE);
2133 addToScope(version, event->string, SQLTAG_EVENT);
2134 makeSqlTag (version, SQLTAG_MLTABLE);
2137 if( !isType (token, TOKEN_CLOSE_PAREN) )
2138 findToken (token, TOKEN_CLOSE_PAREN);
2142 findCmdTerm (token, TRUE);
2144 deleteToken (version);
2145 deleteToken (table);
2146 deleteToken (event);
2149 static void parseMLConn (tokenInfo *const token)
2151 tokenInfo *const version = newToken ();
2152 tokenInfo *const event = newToken ();
2155 * This deals with these formats
2156 * call ml_add_connection_script( 'version', 'event',
2157 * 'some SQL statement'
2158 * );
2161 readToken (token);
2162 if ( isType (token, TOKEN_OPEN_PAREN) )
2164 readToken (version);
2165 readToken (token);
2166 while (!(isType (token, TOKEN_COMMA) ||
2167 isType (token, TOKEN_CLOSE_PAREN)
2170 readToken (token);
2173 if (isType (token, TOKEN_COMMA))
2175 readToken (event);
2177 if (isType (version, TOKEN_STRING) &&
2178 isType (event, TOKEN_STRING) )
2180 addToScope(version, event->string, SQLTAG_EVENT);
2181 makeSqlTag (version, SQLTAG_MLCONN);
2184 if( !isType (token, TOKEN_CLOSE_PAREN) )
2185 findToken (token, TOKEN_CLOSE_PAREN);
2189 findCmdTerm (token, TRUE);
2191 deleteToken (version);
2192 deleteToken (event);
2195 static void parseMLProp (tokenInfo *const token)
2197 tokenInfo *const component = newToken ();
2198 tokenInfo *const prop_set_name = newToken ();
2199 tokenInfo *const prop_name = newToken ();
2202 * This deals with these formats
2203 * ml_add_property (
2204 * 'comp_name',
2205 * 'prop_set_name',
2206 * 'prop_name',
2207 * 'prop_value'
2211 readToken (token);
2212 if ( isType (token, TOKEN_OPEN_PAREN) )
2214 readToken (component);
2215 readToken (token);
2216 while (!(isType (token, TOKEN_COMMA) ||
2217 isType (token, TOKEN_CLOSE_PAREN)
2220 readToken (token);
2223 if (isType (token, TOKEN_COMMA))
2225 readToken (prop_set_name);
2226 readToken (token);
2227 while (!(isType (token, TOKEN_COMMA) ||
2228 isType (token, TOKEN_CLOSE_PAREN)
2231 readToken (token);
2234 if (isType (token, TOKEN_COMMA))
2236 readToken (prop_name);
2238 if (isType (component, TOKEN_STRING) &&
2239 isType (prop_set_name, TOKEN_STRING) &&
2240 isType (prop_name, TOKEN_STRING) )
2242 addToScope(component, prop_set_name->string, SQLTAG_MLPROP /* FIXME */);
2243 addToScope(component, prop_name->string, SQLTAG_MLPROP /* FIXME */);
2244 makeSqlTag (component, SQLTAG_MLPROP);
2247 if( !isType (token, TOKEN_CLOSE_PAREN) )
2248 findToken (token, TOKEN_CLOSE_PAREN);
2252 findCmdTerm (token, TRUE);
2254 deleteToken (component);
2255 deleteToken (prop_set_name);
2256 deleteToken (prop_name);
2259 static void parseComment (tokenInfo *const token)
2262 * This deals with this statement:
2263 * COMMENT TO PRESERVE FORMAT ON PROCEDURE "DBA"."test" IS
2264 * {create PROCEDURE DBA."test"()
2265 * BEGIN
2266 * signal dave;
2267 * END
2270 * The comment can contain anything between the CURLY
2271 * braces
2272 * COMMENT ON USER "admin" IS
2273 * 'Administration Group'
2275 * Or it could be a simple string with no curly braces
2277 while (! isKeyword (token, KEYWORD_is))
2279 readToken (token);
2281 readToken (token);
2282 if ( isType(token, TOKEN_OPEN_CURLY) )
2284 findToken (token, TOKEN_CLOSE_CURLY);
2287 findCmdTerm (token, TRUE);
2291 static void parseKeywords (tokenInfo *const token)
2293 switch (token->keyword)
2295 case KEYWORD_begin: parseBlock (token, FALSE); break;
2296 case KEYWORD_comment: parseComment (token); break;
2297 case KEYWORD_cursor: parseSimple (token, SQLTAG_CURSOR); break;
2298 case KEYWORD_datatype: parseDomain (token); break;
2299 case KEYWORD_declare: parseBlock (token, FALSE); break;
2300 case KEYWORD_domain: parseDomain (token); break;
2301 case KEYWORD_drop: parseDrop (token); break;
2302 case KEYWORD_event: parseEvent (token); break;
2303 case KEYWORD_function: parseSubProgram (token); break;
2304 case KEYWORD_if: parseStatements (token, FALSE); break;
2305 case KEYWORD_index: parseIndex (token); break;
2306 case KEYWORD_ml_table: parseMLTable (token); break;
2307 case KEYWORD_ml_table_lang: parseMLTable (token); break;
2308 case KEYWORD_ml_table_dnet: parseMLTable (token); break;
2309 case KEYWORD_ml_table_java: parseMLTable (token); break;
2310 case KEYWORD_ml_table_chk: parseMLTable (token); break;
2311 case KEYWORD_ml_conn: parseMLConn (token); break;
2312 case KEYWORD_ml_conn_lang: parseMLConn (token); break;
2313 case KEYWORD_ml_conn_dnet: parseMLConn (token); break;
2314 case KEYWORD_ml_conn_java: parseMLConn (token); break;
2315 case KEYWORD_ml_conn_chk: parseMLConn (token); break;
2316 case KEYWORD_ml_prop: parseMLProp (token); break;
2317 case KEYWORD_package: parsePackage (token); break;
2318 case KEYWORD_procedure: parseSubProgram (token); break;
2319 case KEYWORD_publication: parsePublication (token); break;
2320 case KEYWORD_service: parseService (token); break;
2321 case KEYWORD_subtype: parseSimple (token, SQLTAG_SUBTYPE); break;
2322 case KEYWORD_synonym: parseSynonym (token); break;
2323 case KEYWORD_table: parseTable (token); break;
2324 case KEYWORD_trigger: parseTrigger (token); break;
2325 case KEYWORD_type: parseType (token); break;
2326 case KEYWORD_variable: parseVariable (token); break;
2327 case KEYWORD_view: parseView (token); break;
2328 default: break;
2332 static void parseSqlFile (tokenInfo *const token)
2336 readToken (token);
2338 if (isType (token, TOKEN_BLOCK_LABEL_BEGIN))
2339 parseLabel (token);
2340 else
2341 parseKeywords (token);
2342 } while (! isKeyword (token, KEYWORD_end));
2345 static void initialize (const langType language)
2347 Assert (sizeof (SqlKinds) / sizeof (SqlKinds [0]) == SQLTAG_COUNT);
2348 Lang_sql = language;
2349 buildSqlKeywordHash ();
2352 static void findSqlTags (void)
2354 tokenInfo *const token = newToken ();
2355 exception_t exception = (exception_t) (setjmp (Exception));
2357 while (exception == ExceptionNone)
2358 parseSqlFile (token);
2360 deleteToken (token);
2363 extern parserDefinition* SqlParser (void)
2365 static const char *const extensions [] = { "sql", NULL };
2366 parserDefinition* def = parserNew ("SQL");
2367 def->kinds = SqlKinds;
2368 def->kindCount = KIND_COUNT (SqlKinds);
2369 def->extensions = extensions;
2370 def->parser = findSqlTags;
2371 def->initialize = initialize;
2372 return def;
2375 /* vi:set tabstop=4 shiftwidth=4 noexpandtab: */