2 * Copyright (c) 2002-2003, Darren Hiebert
4 * This source code is released for free distribution under the terms of the
5 * GNU General Public License version 2 or (at your option) any later version.
7 * This module contains functions for generating tags for PL/SQL language
14 #include "general.h" /* must always come first */
16 #include <ctype.h> /* to define isalpha () */
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
45 #define isType(token,t) (boolean) ((token)->type == (t))
46 #define isKeyword(token,k) (boolean) ((token)->keyword == (k))
52 typedef enum eException
{ ExceptionNone
, ExceptionEOF
} exception_t
;
55 * Used to specify type of keyword.
57 typedef enum eKeywordId
{
112 KEYWORD_ml_table_lang
,
113 KEYWORD_ml_table_dnet
,
114 KEYWORD_ml_table_java
,
115 KEYWORD_ml_table_chk
,
117 KEYWORD_ml_conn_lang
,
118 KEYWORD_ml_conn_dnet
,
119 KEYWORD_ml_conn_java
,
133 typedef enum eTokenType
{
135 TOKEN_BLOCK_LABEL_BEGIN
,
136 TOKEN_BLOCK_LABEL_END
,
158 typedef struct sTokenInfoSQL
{
164 int begin_end_nest_lvl
;
165 unsigned long lineNumber
;
173 static langType Lang_sql
;
175 static jmp_buf Exception
;
182 SQLTAG_LOCAL_VARIABLE
,
204 static kindOption SqlKinds
[] = {
205 { TRUE
, 'c', "cursor", "cursors" },
206 { FALSE
, 'd', "prototype", "prototypes" },
207 { TRUE
, 'f', "function", "functions" },
208 { TRUE
, 'F', "field", "record fields" },
209 { FALSE
, 'l', "local", "local variables" },
210 { TRUE
, 'L', "label", "block label" },
211 { TRUE
, 'P', "package", "packages" },
212 { TRUE
, 'p', "procedure", "procedures" },
213 { FALSE
, 'r', "record", "records" },
214 { TRUE
, 's', "subtype", "subtypes" },
215 { TRUE
, 't', "table", "tables" },
216 { TRUE
, 'T', "trigger", "triggers" },
217 { TRUE
, 'v', "variable", "variables" },
218 { TRUE
, 'i', "index", "indexes" },
219 { TRUE
, 'e', "event", "events" },
220 { TRUE
, 'U', "publication", "publications" },
221 { TRUE
, 'R', "service", "services" },
222 { TRUE
, 'D', "domain", "domains" },
223 { TRUE
, 'V', "view", "views" },
224 { TRUE
, 'n', "synonym", "synonyms" },
225 { TRUE
, 'x', "mltable", "MobiLink Table Scripts" },
226 { TRUE
, 'y', "mlconn", "MobiLink Conn Scripts" },
227 { TRUE
, 'z', "mlprop", "MobiLink Properties " }
230 static const keywordTable SqlKeywordTable
[] = {
231 /* keyword keyword ID */
232 { "as", KEYWORD_is
},
233 { "is", KEYWORD_is
},
234 { "begin", KEYWORD_begin
},
235 { "body", KEYWORD_body
},
236 { "cursor", KEYWORD_cursor
},
237 { "declare", KEYWORD_declare
},
238 { "end", KEYWORD_end
},
239 { "function", KEYWORD_function
},
240 { "if", KEYWORD_if
},
241 { "else", KEYWORD_else
},
242 { "elseif", KEYWORD_elseif
},
243 { "endif", KEYWORD_endif
},
244 { "loop", KEYWORD_loop
},
245 { "while", KEYWORD_while
},
246 { "case", KEYWORD_case
},
247 { "for", KEYWORD_for
},
248 { "do", KEYWORD_do
},
249 { "call", KEYWORD_call
},
250 { "package", KEYWORD_package
},
251 { "pragma", KEYWORD_pragma
},
252 { "procedure", KEYWORD_procedure
},
253 { "record", KEYWORD_record
},
254 { "object", KEYWORD_object
},
255 { "ref", KEYWORD_ref
},
256 { "rem", KEYWORD_rem
},
257 { "return", KEYWORD_return
},
258 { "returns", KEYWORD_returns
},
259 { "subtype", KEYWORD_subtype
},
260 { "table", KEYWORD_table
},
261 { "trigger", KEYWORD_trigger
},
262 { "type", KEYWORD_type
},
263 { "index", KEYWORD_index
},
264 { "event", KEYWORD_event
},
265 { "publication", KEYWORD_publication
},
266 { "service", KEYWORD_service
},
267 { "domain", KEYWORD_domain
},
268 { "datatype", KEYWORD_datatype
},
269 { "result", KEYWORD_result
},
270 { "url", KEYWORD_url
},
271 { "internal", KEYWORD_internal
},
272 { "external", KEYWORD_external
},
273 { "when", KEYWORD_when
},
274 { "then", KEYWORD_then
},
275 { "variable", KEYWORD_variable
},
276 { "exception", KEYWORD_exception
},
277 { "at", KEYWORD_at
},
278 { "on", KEYWORD_on
},
279 { "primary", KEYWORD_primary
},
280 { "references", KEYWORD_references
},
281 { "unique", KEYWORD_unique
},
282 { "check", KEYWORD_check
},
283 { "constraint", KEYWORD_constraint
},
284 { "foreign", KEYWORD_foreign
},
285 { "ml_add_table_script", KEYWORD_ml_table
},
286 { "ml_add_lang_table_script", KEYWORD_ml_table_lang
},
287 { "ml_add_dnet_table_script", KEYWORD_ml_table_dnet
},
288 { "ml_add_java_table_script", KEYWORD_ml_table_java
},
289 { "ml_add_lang_table_script_chk", KEYWORD_ml_table_chk
},
290 { "ml_add_connection_script", KEYWORD_ml_conn
},
291 { "ml_add_lang_connection_script", KEYWORD_ml_conn_lang
},
292 { "ml_add_dnet_connection_script", KEYWORD_ml_conn_dnet
},
293 { "ml_add_java_connection_script", KEYWORD_ml_conn_java
},
294 { "ml_add_lang_conn_script_chk", KEYWORD_ml_conn_chk
},
295 { "ml_add_property", KEYWORD_ml_prop
},
296 { "local", KEYWORD_local
},
297 { "temporary", KEYWORD_temporary
},
298 { "drop", KEYWORD_drop
},
299 { "view", KEYWORD_view
},
300 { "synonym", KEYWORD_synonym
},
301 { "handler", KEYWORD_handler
},
302 { "comment", KEYWORD_comment
},
303 { "create", KEYWORD_create
},
308 * FUNCTION DECLARATIONS
311 /* Recursive calls */
312 static void parseBlock (tokenInfo
*const token
, const boolean local
);
313 static void parseDeclare (tokenInfo
*const token
, const boolean local
);
314 static void parseKeywords (tokenInfo
*const token
);
315 static void parseSqlFile (tokenInfo
*const token
);
318 * FUNCTION DEFINITIONS
321 static boolean
isIdentChar1 (const int c
)
324 * Other databases are less restrictive on the first character of
326 * isIdentChar1 is used to identify the first character of an
327 * identifier, so we are removing some restrictions.
330 (isalpha (c
) || c
== '@' || c
== '_' );
333 static boolean
isIdentChar (const int c
)
336 (isalpha (c
) || isdigit (c
) || c
== '$' ||
337 c
== '@' || c
== '_' || c
== '#');
340 static boolean
isCmdTerm (tokenInfo
*const token
)
343 debugPrintf (DEBUG_PARSE
344 , "\n isCmdTerm: token same tt:%d tk:%d\n"
351 * Based on the various customer sites I have been at
352 * the most common command delimiters are
357 * This routine will check for any of these, more
358 * can easily be added by modifying readToken and
359 * either adding the character to:
363 return ( isType (token
, TOKEN_SEMICOLON
) ||
364 isType (token
, TOKEN_TILDE
) ||
365 isType (token
, TOKEN_FORWARD_SLASH
) ||
366 isKeyword (token
, KEYWORD_go
)
370 static boolean
isMatchedEnd(tokenInfo
*const token
, int nest_lvl
)
372 boolean terminated
= FALSE
;
374 * Since different forms of SQL allow the use of
378 * blocks, some statements may not be terminated using
379 * the standard delimiters:
384 * This routine will check to see if we encounter and END
385 * for the matching nest level of BEGIN ... END statements.
386 * If we find one, then we can assume, the statement was terminated
387 * since we have fallen through to the END statement of the BEGIN
390 if ( nest_lvl
> 0 && isKeyword (token
, KEYWORD_end
) )
392 if ( token
->begin_end_nest_lvl
== nest_lvl
)
399 static tokenInfo
*newToken (void)
401 tokenInfo
*const token
= xMalloc (1, tokenInfo
);
403 token
->type
= TOKEN_UNDEFINED
;
404 token
->keyword
= KEYWORD_NONE
;
405 token
->string
= vStringNew ();
406 token
->scope
= vStringNew ();
407 token
->scopeKind
= SQLTAG_COUNT
;
408 token
->begin_end_nest_lvl
= 0;
409 token
->lineNumber
= getSourceLineNumber ();
410 token
->filePosition
= getInputFilePosition ();
415 static void deleteToken (tokenInfo
*const token
)
417 vStringDelete (token
->string
);
418 vStringDelete (token
->scope
);
422 static int analyzeToken (vString
*const name
, langType language
)
424 vString
*keyword
= vStringNew ();
426 vStringCopyToLower (keyword
, name
);
427 result
= lookupKeyword (vStringValue (keyword
), language
);
428 vStringDelete (keyword
);
433 * Tag generation functions
436 static void makeSqlTag (tokenInfo
*const token
, const sqlKind kind
)
438 if (SqlKinds
[kind
].enabled
)
440 const char *const name
= vStringValue (token
->string
);
442 initTagEntry (&e
, name
);
444 e
.lineNumber
= token
->lineNumber
;
445 e
.filePosition
= token
->filePosition
;
446 e
.kindName
= SqlKinds
[kind
].name
;
447 e
.kind
= SqlKinds
[kind
].letter
;
449 if (vStringLength (token
->scope
) > 0)
451 Assert (token
->scopeKind
< SQLTAG_COUNT
);
452 e
.extensionFields
.scope
[0] = SqlKinds
[token
->scopeKind
].name
;
453 e
.extensionFields
.scope
[1] = vStringValue (token
->scope
);
464 static void parseString (vString
*const string
, const int delimiter
)
469 int c
= getcFromInputFile ();
475 c = getcFromInputFile(); // This maybe a ' or ". //
476 vStringPut(string, c);
479 else if (c
== delimiter
)
482 vStringPut (string
, c
);
484 vStringTerminate (string
);
487 /* Read a C identifier beginning with "firstChar" and places it into "name".
489 static void parseIdentifier (vString
*const string
, const int firstChar
)
492 Assert (isIdentChar1 (c
));
495 vStringPut (string
, c
);
496 c
= getcFromInputFile ();
497 } while (isIdentChar (c
));
498 vStringTerminate (string
);
500 ungetcToInputFile (c
); /* unget non-identifier character */
503 static void readToken (tokenInfo
*const token
)
507 token
->type
= TOKEN_UNDEFINED
;
508 token
->keyword
= KEYWORD_NONE
;
509 vStringClear (token
->string
);
514 c
= getcFromInputFile ();
515 token
->lineNumber
= getSourceLineNumber ();
516 token
->filePosition
= getInputFilePosition ();
518 * Added " to the list of ignores, not sure what this
519 * might break but it gets by this issue:
520 * create table "t1" (...)
522 * Darren, the code passes all my tests for both
523 * Oracle and SQL Anywhere, but maybe you can tell me
524 * what this may effect.
527 while (c
== '\t' || c
== ' ' || c
== '\n');
531 case EOF
: longjmp (Exception
, (int)ExceptionEOF
); break;
532 case '(': token
->type
= TOKEN_OPEN_PAREN
; break;
533 case ')': token
->type
= TOKEN_CLOSE_PAREN
; break;
534 case ':': token
->type
= TOKEN_COLON
; break;
535 case ';': token
->type
= TOKEN_SEMICOLON
; break;
536 case '.': token
->type
= TOKEN_PERIOD
; break;
537 case ',': token
->type
= TOKEN_COMMA
; break;
538 case '{': token
->type
= TOKEN_OPEN_CURLY
; break;
539 case '}': token
->type
= TOKEN_CLOSE_CURLY
; break;
540 case '~': token
->type
= TOKEN_TILDE
; break;
541 case '[': token
->type
= TOKEN_OPEN_SQUARE
; break;
542 case ']': token
->type
= TOKEN_CLOSE_SQUARE
; break;
543 case '=': token
->type
= TOKEN_EQUAL
; break;
547 token
->type
= TOKEN_STRING
;
548 parseString (token
->string
, c
);
549 token
->lineNumber
= getSourceLineNumber ();
550 token
->filePosition
= getInputFilePosition ();
554 c
= getcFromInputFile ();
555 if (c
== '-') /* -- is this the start of a comment? */
557 fileSkipToCharacter ('\n');
563 ungetcToInputFile (c
);
564 token
->type
= TOKEN_OPERATOR
;
571 const int initial
= c
;
572 int d
= getcFromInputFile ();
576 token
->type
= TOKEN_BLOCK_LABEL_BEGIN
;
578 token
->type
= TOKEN_BLOCK_LABEL_END
;
582 ungetcToInputFile (d
);
583 token
->type
= TOKEN_UNDEFINED
;
589 c
= getcFromInputFile ();
590 if (c
!= '\\' && c
!= '"' && c
!= '\'' && !isspace (c
))
591 ungetcToInputFile (c
);
592 token
->type
= TOKEN_CHARACTER
;
593 token
->lineNumber
= getSourceLineNumber ();
594 token
->filePosition
= getInputFilePosition ();
599 int d
= getcFromInputFile ();
600 if ( (d
!= '*') && /* is this the start of a comment? */
601 (d
!= '/') ) /* is a one line comment? */
603 token
->type
= TOKEN_FORWARD_SLASH
;
604 ungetcToInputFile (d
);
612 fileSkipToCharacter ('*');
613 c
= getcFromInputFile ();
617 ungetcToInputFile (c
);
618 } while (c
!= EOF
&& c
!= '\0');
621 else if (d
== '/') /* is this the start of a comment? */
623 fileSkipToCharacter ('\n');
631 if (! isIdentChar1 (c
))
632 token
->type
= TOKEN_UNDEFINED
;
635 parseIdentifier (token
->string
, c
);
636 token
->lineNumber
= getSourceLineNumber ();
637 token
->filePosition
= getInputFilePosition ();
638 token
->keyword
= analyzeToken (token
->string
, Lang_sql
);
639 if (isKeyword (token
, KEYWORD_rem
))
641 vStringClear (token
->string
);
642 fileSkipToCharacter ('\n');
645 else if (isKeyword (token
, KEYWORD_NONE
))
646 token
->type
= TOKEN_IDENTIFIER
;
648 token
->type
= TOKEN_KEYWORD
;
655 * reads an identifier, possibly quoted:
660 static void readIdentifier (tokenInfo
*const token
)
663 if (isType (token
, TOKEN_OPEN_SQUARE
))
665 tokenInfo
*const close_square
= newToken ();
668 /* eat close swuare */
669 readToken (close_square
);
670 deleteToken (close_square
);
675 * Token parsing functions
679 * static void addContext (tokenInfo* const parent, const tokenInfo* const child)
681 * if (vStringLength (parent->string) > 0)
683 * vStringCatS (parent->string, ".");
685 * vStringCatS (parent->string, vStringValue(child->string));
686 * vStringTerminate(parent->string);
690 static void addToScope (tokenInfo
* const token
, vString
* const extra
, sqlKind kind
)
692 if (vStringLength (token
->scope
) > 0)
694 vStringCatS (token
->scope
, ".");
696 vStringCatS (token
->scope
, vStringValue(extra
));
697 vStringTerminate(token
->scope
);
698 token
->scopeKind
= kind
;
705 static void findToken (tokenInfo
*const token
, const tokenType type
)
707 while (! isType (token
, type
))
713 static void findCmdTerm (tokenInfo
*const token
, const boolean check_first
)
715 int begin_end_nest_lvl
= token
->begin_end_nest_lvl
;
719 if ( isCmdTerm(token
) )
725 } while ( !isCmdTerm(token
) && !isMatchedEnd(token
, begin_end_nest_lvl
) );
728 static void skipToMatched(tokenInfo
*const token
)
731 tokenType open_token
;
732 tokenType close_token
;
736 case TOKEN_OPEN_PAREN
:
737 open_token
= TOKEN_OPEN_PAREN
;
738 close_token
= TOKEN_CLOSE_PAREN
;
740 case TOKEN_OPEN_CURLY
:
741 open_token
= TOKEN_OPEN_CURLY
;
742 close_token
= TOKEN_CLOSE_CURLY
;
744 case TOKEN_OPEN_SQUARE
:
745 open_token
= TOKEN_OPEN_SQUARE
;
746 close_token
= TOKEN_CLOSE_SQUARE
;
753 * This routine will skip to a matching closing token.
754 * It will also handle nested tokens like the (, ) below.
755 * ( name varchar(30), text binary(10) )
758 if (isType (token
, open_token
))
761 while (! (isType (token
, close_token
) && (nest_level
== 0)))
764 if (isType (token
, open_token
))
768 if (isType (token
, close_token
))
780 static void copyToken (tokenInfo
*const dest
, tokenInfo
*const src
)
782 dest
->lineNumber
= src
->lineNumber
;
783 dest
->filePosition
= src
->filePosition
;
784 dest
->type
= src
->type
;
785 dest
->keyword
= src
->keyword
;
786 vStringCopy(dest
->string
, src
->string
);
787 vStringCopy(dest
->scope
, src
->scope
);
788 dest
->scopeKind
= src
->scopeKind
;
791 static void skipArgumentList (tokenInfo
*const token
)
794 * Other databases can have arguments with fully declared
796 * ( name varchar(30), text binary(10) )
797 * So we must check for nested open and closing parantheses
800 if (isType (token
, TOKEN_OPEN_PAREN
)) /* arguments? */
802 skipToMatched (token
);
806 static void parseSubProgram (tokenInfo
*const token
)
808 tokenInfo
*const name
= newToken ();
809 vString
* saveScope
= vStringNew ();
810 sqlKind saveScopeKind
;
813 * This must handle both prototypes and the body of
817 * FUNCTION func_name RETURN integer;
818 * PROCEDURE proc_name( parameters );
820 * FUNCTION GET_ML_USERNAME RETURN VARCHAR2
823 * RETURN v_sync_user_id;
824 * END GET_ML_USERNAME;
826 * PROCEDURE proc_name( parameters )
830 * CREATE PROCEDURE proc_name( parameters )
831 * EXTERNAL NAME ... ;
832 * CREATE PROCEDURE proc_name( parameters )
836 * CREATE FUNCTION f_GetClassName(
837 * IN @object VARCHAR(128)
838 * ,IN @code VARCHAR(128)
840 * RETURNS VARCHAR(200)
844 * IF( @object = 'user_state' ) THEN
845 * SET something = something;
851 * Note, a Package adds scope to the items within.
852 * create or replace package demo_pkg is
854 * function test_func return varchar2;
855 * function more.test_func2 return varchar2;
857 * So the tags generated here, contain the package name:
860 * demo_pkg.more.test_func2
862 const sqlKind kind
= isKeyword (token
, KEYWORD_function
) ?
863 SQLTAG_FUNCTION
: SQLTAG_PROCEDURE
;
864 Assert (isKeyword (token
, KEYWORD_function
) ||
865 isKeyword (token
, KEYWORD_procedure
));
867 vStringCopy(saveScope
, token
->scope
);
868 saveScopeKind
= token
->scopeKind
;
870 copyToken (name
, token
);
873 if (isType (token
, TOKEN_PERIOD
))
876 * If this is an Oracle package, then the token->scope should
877 * already be set. If this is the case, also add this value to the
879 * If this is not an Oracle package, chances are the scope should be
880 * blank and the value just read is the OWNER or CREATOR of the
881 * function and should not be considered part of the scope.
883 if ( vStringLength(saveScope
) > 0 )
885 addToScope(token
, name
->string
, kind
);
888 copyToken (name
, token
);
891 if (isType (token
, TOKEN_OPEN_PAREN
))
893 /* Reads to the next token after the TOKEN_CLOSE_PAREN */
894 skipArgumentList(token
);
897 if (kind
== SQLTAG_FUNCTION
)
899 if (isKeyword (token
, KEYWORD_return
) || isKeyword (token
, KEYWORD_returns
))
904 * Read token after which could be the
905 * command terminator if a prototype
906 * or an open parantheses
909 if (isType (token
, TOKEN_OPEN_PAREN
))
911 /* Reads to the next token after the TOKEN_CLOSE_PAREN */
912 skipArgumentList(token
);
916 if( isCmdTerm (token
) )
918 makeSqlTag (name
, SQLTAG_PROTOTYPE
);
922 while (!(isKeyword (token
, KEYWORD_is
) ||
923 isKeyword (token
, KEYWORD_begin
) ||
924 isKeyword (token
, KEYWORD_at
) ||
925 isKeyword (token
, KEYWORD_internal
) ||
926 isKeyword (token
, KEYWORD_external
) ||
927 isKeyword (token
, KEYWORD_url
) ||
928 isType (token
, TOKEN_EQUAL
) ||
933 if ( isKeyword (token
, KEYWORD_result
) )
936 if (isType (token
, TOKEN_OPEN_PAREN
))
938 /* Reads to the next token after the TOKEN_CLOSE_PAREN */
939 skipArgumentList(token
);
945 if (isKeyword (token
, KEYWORD_at
) ||
946 isKeyword (token
, KEYWORD_url
) ||
947 isKeyword (token
, KEYWORD_internal
) ||
948 isKeyword (token
, KEYWORD_external
) )
950 addToScope(token
, name
->string
, kind
);
951 if (isType (name
, TOKEN_IDENTIFIER
) ||
952 isType (name
, TOKEN_STRING
) ||
953 !isKeyword (token
, KEYWORD_NONE
)
955 makeSqlTag (name
, kind
);
957 vStringClear (token
->scope
);
958 token
->scopeKind
= SQLTAG_COUNT
;
960 if ( isType (token
, TOKEN_EQUAL
) )
963 if ( isKeyword (token
, KEYWORD_declare
) )
964 parseDeclare (token
, FALSE
);
966 if (isKeyword (token
, KEYWORD_is
) ||
967 isKeyword (token
, KEYWORD_begin
) )
969 addToScope(token
, name
->string
, kind
);
970 if (isType (name
, TOKEN_IDENTIFIER
) ||
971 isType (name
, TOKEN_STRING
) ||
972 !isKeyword (token
, KEYWORD_NONE
)
974 makeSqlTag (name
, kind
);
976 parseBlock (token
, TRUE
);
977 vStringClear (token
->scope
);
978 token
->scopeKind
= SQLTAG_COUNT
;
981 vStringCopy(token
->scope
, saveScope
);
982 token
->scopeKind
= saveScopeKind
;
984 vStringDelete(saveScope
);
987 static void parseRecord (tokenInfo
*const token
)
990 * Make it a bit forgiving, this is called from
991 * multiple functions, parseTable, parseType
993 if (!isType (token
, TOKEN_OPEN_PAREN
))
996 Assert (isType (token
, TOKEN_OPEN_PAREN
));
999 if ( isType (token
, TOKEN_COMMA
) || isType (token
, TOKEN_OPEN_PAREN
) )
1003 * Create table statements can end with various constraints
1004 * which must be excluded from the SQLTAG_FIELD.
1010 * constraint whatever,
1016 if (! (isKeyword(token
, KEYWORD_primary
) ||
1017 isKeyword(token
, KEYWORD_references
) ||
1018 isKeyword(token
, KEYWORD_unique
) ||
1019 isKeyword(token
, KEYWORD_check
) ||
1020 isKeyword(token
, KEYWORD_constraint
) ||
1021 isKeyword(token
, KEYWORD_foreign
) ) )
1023 if (isType (token
, TOKEN_IDENTIFIER
) ||
1024 isType (token
, TOKEN_STRING
))
1025 makeSqlTag (token
, SQLTAG_FIELD
);
1028 while (!(isType (token
, TOKEN_COMMA
) ||
1029 isType (token
, TOKEN_CLOSE_PAREN
) ||
1030 isType (token
, TOKEN_OPEN_PAREN
)
1035 * A table structure can look like this:
1042 * We can't just look for a COMMA or CLOSE_PAREN
1043 * since that will not deal with the numeric(10,5)
1044 * case. So we need to skip the argument list
1045 * when we find an open paren.
1047 if (isType (token
, TOKEN_OPEN_PAREN
))
1049 /* Reads to the next token after the TOKEN_CLOSE_PAREN */
1050 skipArgumentList(token
);
1053 } while (! isType (token
, TOKEN_CLOSE_PAREN
));
1056 static void parseType (tokenInfo
*const token
)
1058 tokenInfo
*const name
= newToken ();
1059 vString
* saveScope
= vStringNew ();
1060 sqlKind saveScopeKind
;
1062 vStringCopy(saveScope
, token
->scope
);
1063 /* If a scope has been set, add it to the name */
1064 addToScope (name
, token
->scope
, token
->scopeKind
);
1065 saveScopeKind
= token
->scopeKind
;
1067 if (isType (name
, TOKEN_IDENTIFIER
))
1070 if (isKeyword (token
, KEYWORD_is
))
1073 switch (token
->keyword
)
1075 case KEYWORD_record
:
1076 case KEYWORD_object
:
1077 makeSqlTag (name
, SQLTAG_RECORD
);
1078 addToScope (token
, name
->string
, SQLTAG_RECORD
);
1079 parseRecord (token
);
1083 makeSqlTag (name
, SQLTAG_TABLE
);
1088 if (isKeyword (token
, KEYWORD_cursor
))
1089 makeSqlTag (name
, SQLTAG_CURSOR
);
1094 vStringClear (token
->scope
);
1095 token
->scopeKind
= SQLTAG_COUNT
;
1098 vStringCopy(token
->scope
, saveScope
);
1099 token
->scopeKind
= saveScopeKind
;
1101 vStringDelete(saveScope
);
1104 static void parseSimple (tokenInfo
*const token
, const sqlKind kind
)
1106 /* This will simply make the tagname from the first word found */
1108 if (isType (token
, TOKEN_IDENTIFIER
) ||
1109 isType (token
, TOKEN_STRING
))
1110 makeSqlTag (token
, kind
);
1113 static void parseDeclare (tokenInfo
*const token
, const boolean local
)
1116 * PL/SQL declares are of this format:
1119 * CURSOR curname ...
1120 * varname1 datatype;
1121 * varname2 datatype;
1122 * varname3 datatype;
1126 if (isKeyword (token
, KEYWORD_declare
))
1128 while (! isKeyword (token
, KEYWORD_begin
) && ! isKeyword (token
, KEYWORD_end
))
1130 switch (token
->keyword
)
1132 case KEYWORD_cursor
: parseSimple (token
, SQLTAG_CURSOR
); break;
1133 case KEYWORD_function
: parseSubProgram (token
); break;
1134 case KEYWORD_procedure
: parseSubProgram (token
); break;
1135 case KEYWORD_subtype
: parseSimple (token
, SQLTAG_SUBTYPE
); break;
1136 case KEYWORD_trigger
: parseSimple (token
, SQLTAG_TRIGGER
); break;
1137 case KEYWORD_type
: parseType (token
); break;
1140 if (isType (token
, TOKEN_IDENTIFIER
))
1144 makeSqlTag (token
, SQLTAG_LOCAL_VARIABLE
);
1148 makeSqlTag (token
, SQLTAG_VARIABLE
);
1153 findToken (token
, TOKEN_SEMICOLON
);
1158 static void parseDeclareANSI (tokenInfo
*const token
, const boolean local
)
1160 tokenInfo
*const type
= newToken ();
1162 * ANSI declares are of this format:
1164 * DECLARE varname1 datatype;
1165 * DECLARE varname2 datatype;
1168 * This differ from PL/SQL where DECLARE precedes the BEGIN block
1169 * and the DECLARE keyword is not repeated.
1171 while (isKeyword (token
, KEYWORD_declare
))
1176 if (isKeyword (type
, KEYWORD_cursor
))
1177 makeSqlTag (token
, SQLTAG_CURSOR
);
1178 else if (isKeyword (token
, KEYWORD_local
) &&
1179 isKeyword (type
, KEYWORD_temporary
))
1182 * DECLARE LOCAL TEMPORARY TABLE table_name (
1188 if (isKeyword (token
, KEYWORD_table
))
1191 if (isType(token
, TOKEN_IDENTIFIER
) ||
1192 isType(token
, TOKEN_STRING
) )
1194 makeSqlTag (token
, SQLTAG_TABLE
);
1198 else if (isType (token
, TOKEN_IDENTIFIER
) ||
1199 isType (token
, TOKEN_STRING
))
1202 makeSqlTag (token
, SQLTAG_LOCAL_VARIABLE
);
1204 makeSqlTag (token
, SQLTAG_VARIABLE
);
1206 findToken (token
, TOKEN_SEMICOLON
);
1212 static void parseLabel (tokenInfo
*const token
)
1215 * A label has this format:
1216 * <<tobacco_dependency>>
1218 * v_senator VARCHAR2(100) := 'THURMOND, JESSE';
1220 * IF total_contributions (v_senator, 'TOBACCO') > 25000
1222 * <<alochol_dependency>>
1224 * v_senator VARCHAR2(100) := 'WHATEVERIT, TAKES';
1229 Assert (isType (token
, TOKEN_BLOCK_LABEL_BEGIN
));
1231 if (isType (token
, TOKEN_IDENTIFIER
))
1233 makeSqlTag (token
, SQLTAG_BLOCK_LABEL
);
1234 readToken (token
); /* read end of label */
1238 static void parseStatements (tokenInfo
*const token
, const boolean exit_on_endif
)
1240 boolean stmtTerm
= FALSE
;
1244 if (isType (token
, TOKEN_BLOCK_LABEL_BEGIN
))
1248 switch (token
->keyword
)
1250 case KEYWORD_exception
:
1253 * <exception handler>;
1255 * Where an exception handler could be:
1260 * In this case we need to skip this keyword and
1261 * move on to the next token without reading until
1269 * WHEN statements can be used in exception clauses
1270 * and CASE statements. The CASE statement should skip
1271 * these given below we skip over to an END statement.
1272 * But for an exception clause, we can have:
1278 * If we skip to the TOKEN_SEMICOLON, we miss the begin
1279 * of a nested BEGIN END block. So read the next token
1280 * after the THEN and restart the LOOP.
1282 while (! isKeyword (token
, KEYWORD_then
))
1290 * We do not want to look for a ; since for an empty
1291 * IF block, it would skip over the END.
1309 while ( ! isKeyword (token
, KEYWORD_then
) &&
1310 ! isKeyword (token
, KEYWORD_begin
) )
1315 if( isKeyword (token
, KEYWORD_begin
) )
1317 parseBlock(token
, FALSE
);
1320 * Handle the non-Ansi IF blocks.
1321 * parseBlock consumes the END, so if the next
1322 * token in a command terminator (like GO)
1323 * we know we are done with this statement.
1325 if ( isCmdTerm (token
) )
1332 while( ! (isKeyword (token
, KEYWORD_end
) ||
1333 isKeyword (token
, KEYWORD_endif
) )
1336 if ( isKeyword (token
, KEYWORD_else
) ||
1337 isKeyword (token
, KEYWORD_elseif
) )
1340 parseStatements (token
, TRUE
);
1342 if ( isCmdTerm(token
) )
1348 * parseStatements returns when it finds an END, an IF
1349 * should follow the END for ANSI anyway.
1353 if( isKeyword (token
, KEYWORD_end
) )
1356 if( isKeyword (token
, KEYWORD_if
) || isKeyword (token
, KEYWORD_endif
) )
1359 if ( isCmdTerm(token
) )
1365 * Well we need to do something here.
1366 * There are lots of different END statements
1387 * FOR loop_name AS cursor_name CURSOR FOR ...
1391 if( isKeyword (token
, KEYWORD_for
) )
1398 while ( ! isKeyword (token
, KEYWORD_is
) )
1401 * If this is not an AS keyword this is
1402 * not a proper FOR statement and should
1408 while ( ! isKeyword (token
, KEYWORD_do
) )
1414 while( ! isKeyword (token
, KEYWORD_end
) )
1417 if ( isKeyword (token, KEYWORD_else) ||
1418 isKeyword (token, KEYWORD_elseif) )
1422 parseStatements (token
, FALSE
);
1424 if ( isCmdTerm(token
) )
1429 if( isKeyword (token
, KEYWORD_end
) )
1433 * Typically ended with
1434 * END LOOP [loop name];
1436 * END FOR [loop name];
1438 if ( isKeyword (token
, KEYWORD_loop
) ||
1439 isKeyword (token
, KEYWORD_case
) ||
1440 isKeyword (token
, KEYWORD_for
) )
1443 if ( isCmdTerm(token
) )
1448 case KEYWORD_create
:
1450 parseKeywords(token
);
1453 case KEYWORD_declare
:
1455 parseBlock (token
, TRUE
);
1466 * Not all statements must end in a semi-colon
1468 * if current publisher <> 'publish' then
1469 * signal UE_FailStatement
1472 * The last statement prior to an end ("signal" above) does
1473 * not need a semi-colon, nor does the end if, since it is
1474 * also the last statement prior to the end of the block.
1476 * So we must read to the first semi-colon or an END block
1478 while ( ! stmtTerm
&&
1479 ! ( isKeyword (token
, KEYWORD_end
) ||
1480 (isCmdTerm(token
)) )
1483 if ( isKeyword (token
, KEYWORD_endif
) &&
1487 if (isType (token
, TOKEN_COLON
) )
1490 * A : can signal a loop name
1495 * Unfortunately, labels do not have a
1496 * cmd terminator, therefore we have to check
1497 * if the next token is a keyword and process
1501 if ( isKeyword (token
, KEYWORD_loop
) ||
1502 isKeyword (token
, KEYWORD_while
) ||
1503 isKeyword (token
, KEYWORD_for
) )
1504 /* parseStatements (token); */
1510 if (isType (token
, TOKEN_OPEN_PAREN
) ||
1511 isType (token
, TOKEN_OPEN_CURLY
) ||
1512 isType (token
, TOKEN_OPEN_SQUARE
) )
1513 skipToMatched (token
);
1516 * Since we know how to parse various statements
1517 * if we detect them, parse them to completion
1519 if (isType (token
, TOKEN_BLOCK_LABEL_BEGIN
) ||
1520 isKeyword (token
, KEYWORD_exception
) ||
1521 isKeyword (token
, KEYWORD_loop
) ||
1522 isKeyword (token
, KEYWORD_case
) ||
1523 isKeyword (token
, KEYWORD_for
) ||
1524 isKeyword (token
, KEYWORD_begin
) )
1525 parseStatements (token
, FALSE
);
1526 else if (isKeyword (token
, KEYWORD_if
))
1527 parseStatements (token
, TRUE
);
1532 * We assumed earlier all statements ended with a command terminator.
1533 * See comment above, now, only read if the current token
1534 * is not a command terminator.
1536 if ( isCmdTerm(token
) && ! stmtTerm
)
1539 } while (! isKeyword (token
, KEYWORD_end
) &&
1540 ! (exit_on_endif
&& isKeyword (token
, KEYWORD_endif
) ) &&
1544 static void parseBlock (tokenInfo
*const token
, const boolean local
)
1546 if (isType (token
, TOKEN_BLOCK_LABEL_BEGIN
))
1551 if (! isKeyword (token
, KEYWORD_begin
))
1555 * These are Oracle style declares which generally come
1556 * between an IS/AS and BEGIN block.
1558 parseDeclare (token
, local
);
1560 if (isKeyword (token
, KEYWORD_begin
))
1564 * Check for ANSI declarations which always follow
1565 * a BEGIN statement. This routine will not advance
1566 * the token if none are found.
1568 parseDeclareANSI (token
, local
);
1569 token
->begin_end_nest_lvl
++;
1570 while (! isKeyword (token
, KEYWORD_end
))
1572 parseStatements (token
, FALSE
);
1574 if ( isCmdTerm(token
) )
1577 token
->begin_end_nest_lvl
--;
1580 * Read the next token (we will assume
1581 * it is the command delimiter)
1586 * Check if the END block is terminated
1588 if ( !isCmdTerm (token
) )
1591 * Not sure what to do here at the moment.
1592 * I think the routine that calls parseBlock
1593 * must expect the next token has already
1594 * been read since it is possible this
1595 * token is not a command delimiter.
1597 /* findCmdTerm (token, FALSE); */
1602 static void parsePackage (tokenInfo
*const token
)
1605 * Packages can be specified in a number of ways:
1606 * CREATE OR REPLACE PACKAGE pkg_name AS
1608 * CREATE OR REPLACE PACKAGE owner.pkg_name AS
1609 * or by specifying a package body
1610 * CREATE OR REPLACE PACKAGE BODY pkg_name AS
1611 * CREATE OR REPLACE PACKAGE BODY owner.pkg_name AS
1613 tokenInfo
*const name
= newToken ();
1614 readIdentifier (name
);
1615 if (isKeyword (name
, KEYWORD_body
))
1618 * Ignore the BODY tag since we will process
1619 * the body or prototypes in the same manner
1621 readIdentifier (name
);
1623 /* Check for owner.pkg_name */
1624 while (! isKeyword (token
, KEYWORD_is
))
1627 if ( isType(token
, TOKEN_PERIOD
) )
1629 readIdentifier (name
);
1632 if (isKeyword (token
, KEYWORD_is
))
1634 if (isType (name
, TOKEN_IDENTIFIER
) ||
1635 isType (name
, TOKEN_STRING
))
1636 makeSqlTag (name
, SQLTAG_PACKAGE
);
1637 addToScope (token
, name
->string
, SQLTAG_PACKAGE
);
1638 parseBlock (token
, FALSE
);
1639 vStringClear (token
->scope
);
1640 token
->scopeKind
= SQLTAG_COUNT
;
1642 findCmdTerm (token
, FALSE
);
1646 static void parseTable (tokenInfo
*const token
)
1648 tokenInfo
*const name
= newToken ();
1651 * This deals with these formats:
1652 * create table t1 (c1 int);
1653 * create global tempoary table t2 (c1 int);
1654 * create table "t3" (c1 int);
1655 * create table bob.t4 (c1 int);
1656 * create table bob."t5" (c1 int);
1657 * create table "bob"."t6" (c1 int);
1658 * create table bob."t7" (c1 int);
1659 * Proxy tables use this format:
1660 * create existing table bob."t7" AT '...';
1661 * SQL Server and Sybase formats
1662 * create table OnlyTable (
1663 * create table dbo.HasOwner (
1664 * create table [dbo].[HasOwnerSquare] (
1665 * create table master.dbo.HasDb (
1666 * create table master..HasDbNoOwner (
1667 * create table [master].dbo.[HasDbAndOwnerSquare] (
1668 * create table [master]..[HasDbNoOwnerSquare] (
1671 /* This could be a database, owner or table name */
1672 readIdentifier (name
);
1674 if (isType (token
, TOKEN_PERIOD
))
1677 * This could be a owner or table name.
1678 * But this is also a special case since the table can be
1679 * referenced with a blank owner:
1682 readIdentifier (name
);
1683 /* Check if a blank name was provided */
1684 if (isType (name
, TOKEN_PERIOD
))
1686 readIdentifier (name
);
1689 if (isType (token
, TOKEN_PERIOD
))
1691 /* This can only be the table name */
1692 readIdentifier (name
);
1696 if (isType (token
, TOKEN_OPEN_PAREN
))
1698 if (isType (name
, TOKEN_IDENTIFIER
) ||
1699 isType (name
, TOKEN_STRING
))
1701 makeSqlTag (name
, SQLTAG_TABLE
);
1702 vStringCopy(token
->scope
, name
->string
);
1703 token
->scopeKind
= SQLTAG_TABLE
;
1704 parseRecord (token
);
1705 vStringClear (token
->scope
);
1706 token
->scopeKind
= SQLTAG_COUNT
;
1709 else if (isKeyword (token
, KEYWORD_at
))
1711 if (isType (name
, TOKEN_IDENTIFIER
))
1713 makeSqlTag (name
, SQLTAG_TABLE
);
1716 findCmdTerm (token
, FALSE
);
1720 static void parseIndex (tokenInfo
*const token
)
1722 tokenInfo
*const name
= newToken ();
1723 tokenInfo
*const owner
= newToken ();
1726 * This deals with these formats
1727 * create index i1 on t1(c1) create index "i2" on t1(c1)
1728 * create virtual unique clustered index "i3" on t1(c1)
1729 * create unique clustered index "i4" on t1(c1)
1730 * create clustered index "i5" on t1(c1)
1731 * create bitmap index "i6" on t1(c1)
1734 readIdentifier (name
);
1736 if (isType (token
, TOKEN_PERIOD
))
1738 readIdentifier (name
);
1741 if ( isKeyword (token
, KEYWORD_on
) &&
1742 (isType (name
, TOKEN_IDENTIFIER
) || isType (name
, TOKEN_STRING
) ) )
1744 readIdentifier (owner
);
1746 if (isType (token
, TOKEN_PERIOD
))
1748 readIdentifier (owner
);
1751 addToScope(name
, owner
->string
, SQLTAG_TABLE
/* FIXME? */);
1752 makeSqlTag (name
, SQLTAG_INDEX
);
1754 findCmdTerm (token
, FALSE
);
1756 deleteToken (owner
);
1759 static void parseEvent (tokenInfo
*const token
)
1761 tokenInfo
*const name
= newToken ();
1764 * This deals with these formats
1765 * create event e1 handler begin end;
1766 * create event "e2" handler begin end;
1767 * create event dba."e3" handler begin end;
1768 * create event "dba"."e4" handler begin end;
1771 readIdentifier (name
);
1773 if (isType (token
, TOKEN_PERIOD
))
1775 readIdentifier (name
);
1777 while (! (isKeyword (token
, KEYWORD_handler
) ||
1778 (isType (token
, TOKEN_SEMICOLON
))) )
1783 if ( isKeyword (token
, KEYWORD_handler
) ||
1784 isType (token
, TOKEN_SEMICOLON
) )
1786 makeSqlTag (name
, SQLTAG_EVENT
);
1789 if (isKeyword (token
, KEYWORD_handler
))
1792 if ( isKeyword (token
, KEYWORD_begin
) )
1794 parseBlock (token
, TRUE
);
1796 findCmdTerm (token
, TRUE
);
1801 static void parseTrigger (tokenInfo
*const token
)
1803 tokenInfo
*const name
= newToken ();
1804 tokenInfo
*const table
= newToken ();
1807 * This deals with these formats
1808 * create or replace trigger tr1 begin end;
1809 * create trigger "tr2" begin end;
1810 * drop trigger "droptr1";
1811 * create trigger "tr3" CALL sp_something();
1812 * create trigger "owner"."tr4" begin end;
1813 * create trigger "tr5" not valid;
1814 * create trigger "tr6" begin end;
1817 readIdentifier (name
);
1819 if (isType (token
, TOKEN_PERIOD
))
1821 readIdentifier (name
);
1825 while ( !isKeyword (token
, KEYWORD_on
) &&
1826 !isCmdTerm (token
) )
1831 /*if (! isType (token, TOKEN_SEMICOLON) ) */
1832 if (! isCmdTerm (token
) )
1836 if (isType (token
, TOKEN_PERIOD
))
1842 while (! (isKeyword (token
, KEYWORD_begin
) ||
1843 (isKeyword (token
, KEYWORD_call
)) ||
1844 ( isCmdTerm (token
))) )
1846 if ( isKeyword (token
, KEYWORD_declare
) )
1848 addToScope(token
, name
->string
, SQLTAG_TRIGGER
);
1849 parseDeclare(token
, TRUE
);
1850 vStringClear(token
->scope
);
1851 token
->scopeKind
= SQLTAG_COUNT
;
1857 if ( isKeyword (token
, KEYWORD_begin
) ||
1858 isKeyword (token
, KEYWORD_call
) )
1860 addToScope(name
, table
->string
, SQLTAG_TABLE
);
1861 makeSqlTag (name
, SQLTAG_TRIGGER
);
1862 addToScope(token
, table
->string
, SQLTAG_TABLE
);
1863 if ( isKeyword (token
, KEYWORD_begin
) )
1865 parseBlock (token
, TRUE
);
1867 vStringClear(token
->scope
);
1868 token
->scopeKind
= SQLTAG_COUNT
;
1872 findCmdTerm (token
, TRUE
);
1874 deleteToken (table
);
1877 static void parsePublication (tokenInfo
*const token
)
1879 tokenInfo
*const name
= newToken ();
1882 * This deals with these formats
1883 * create or replace publication pu1 ()
1884 * create publication "pu2" ()
1885 * create publication dba."pu3" ()
1886 * create publication "dba"."pu4" ()
1889 readIdentifier (name
);
1891 if (isType (token
, TOKEN_PERIOD
))
1893 readIdentifier (name
);
1896 if (isType (token
, TOKEN_OPEN_PAREN
))
1898 if (isType (name
, TOKEN_IDENTIFIER
) ||
1899 isType (name
, TOKEN_STRING
))
1901 makeSqlTag (name
, SQLTAG_PUBLICATION
);
1904 findCmdTerm (token
, FALSE
);
1908 static void parseService (tokenInfo
*const token
)
1910 tokenInfo
*const name
= newToken ();
1913 * This deals with these formats
1914 * CREATE SERVICE s1 TYPE 'HTML'
1915 * AUTHORIZATION OFF USER DBA AS
1917 * FROM SYS.SYSTABLE;
1918 * CREATE SERVICE "s2" TYPE 'HTML'
1919 * AUTHORIZATION OFF USER DBA AS
1920 * CALL sp_Something();
1923 readIdentifier (name
);
1925 if (isKeyword (token
, KEYWORD_type
))
1927 if (isType (name
, TOKEN_IDENTIFIER
) ||
1928 isType (name
, TOKEN_STRING
))
1930 makeSqlTag (name
, SQLTAG_SERVICE
);
1933 findCmdTerm (token
, FALSE
);
1937 static void parseDomain (tokenInfo
*const token
)
1939 tokenInfo
*const name
= newToken ();
1942 * This deals with these formats
1943 * CREATE DOMAIN|DATATYPE [AS] your_name ...;
1946 readIdentifier (name
);
1947 if (isKeyword (name
, KEYWORD_is
))
1949 readIdentifier (name
);
1952 if (isType (name
, TOKEN_IDENTIFIER
) ||
1953 isType (name
, TOKEN_STRING
))
1955 makeSqlTag (name
, SQLTAG_DOMAIN
);
1957 findCmdTerm (token
, FALSE
);
1961 static void parseDrop (tokenInfo
*const token
)
1964 * This deals with these formats
1965 * DROP TABLE|PROCEDURE|DOMAIN|DATATYPE name;
1967 * Just simply skip over these statements.
1968 * They are often confused with PROCEDURE prototypes
1969 * since the syntax is similar, this effectively deals with
1970 * the issue for all types.
1973 findCmdTerm (token
, FALSE
);
1976 static void parseVariable (tokenInfo
*const token
)
1978 tokenInfo
*const name
= newToken ();
1981 * This deals with these formats
1982 * create variable varname1 integer;
1983 * create variable @varname2 integer;
1984 * create variable "varname3" integer;
1985 * drop variable @varname3;
1988 readIdentifier (name
);
1990 if ( (isType (name
, TOKEN_IDENTIFIER
) || isType (name
, TOKEN_STRING
))
1991 && !isType (token
, TOKEN_SEMICOLON
) )
1993 makeSqlTag (name
, SQLTAG_VARIABLE
);
1995 findCmdTerm (token
, TRUE
);
2000 static void parseSynonym (tokenInfo
*const token
)
2002 tokenInfo
*const name
= newToken ();
2005 * This deals with these formats
2006 * create variable varname1 integer;
2007 * create variable @varname2 integer;
2008 * create variable "varname3" integer;
2009 * drop variable @varname3;
2012 readIdentifier (name
);
2014 if ( (isType (name
, TOKEN_IDENTIFIER
) || isType (name
, TOKEN_STRING
))
2015 && isKeyword (token
, KEYWORD_for
) )
2017 makeSqlTag (name
, SQLTAG_SYNONYM
);
2019 findCmdTerm (token
, TRUE
);
2024 static void parseView (tokenInfo
*const token
)
2026 tokenInfo
*const name
= newToken ();
2029 * This deals with these formats
2030 * create variable varname1 integer;
2031 * create variable @varname2 integer;
2032 * create variable "varname3" integer;
2033 * drop variable @varname3;
2036 readIdentifier (name
);
2038 if (isType (token
, TOKEN_PERIOD
))
2040 readIdentifier (name
);
2043 if ( isType (token
, TOKEN_OPEN_PAREN
) )
2045 skipArgumentList(token
);
2049 while (!(isKeyword (token
, KEYWORD_is
) ||
2050 isType (token
, TOKEN_SEMICOLON
)
2056 if ( (isType (name
, TOKEN_IDENTIFIER
) || isType (name
, TOKEN_STRING
))
2057 && isKeyword (token
, KEYWORD_is
) )
2059 makeSqlTag (name
, SQLTAG_VIEW
);
2062 findCmdTerm (token
, TRUE
);
2067 static void parseMLTable (tokenInfo
*const token
)
2069 tokenInfo
*const version
= newToken ();
2070 tokenInfo
*const table
= newToken ();
2071 tokenInfo
*const event
= newToken ();
2074 * This deals with these formats
2075 * call dbo.ml_add_table_script( 'version', 'table_name', 'event',
2076 * 'some SQL statement'
2081 if ( isType (token
, TOKEN_OPEN_PAREN
) )
2083 readToken (version
);
2085 while (!(isType (token
, TOKEN_COMMA
) ||
2086 isType (token
, TOKEN_CLOSE_PAREN
)
2092 if (isType (token
, TOKEN_COMMA
))
2096 while (!(isType (token
, TOKEN_COMMA
) ||
2097 isType (token
, TOKEN_CLOSE_PAREN
)
2103 if (isType (token
, TOKEN_COMMA
))
2107 if (isType (version
, TOKEN_STRING
) &&
2108 isType (table
, TOKEN_STRING
) &&
2109 isType (event
, TOKEN_STRING
) )
2111 addToScope(version
, table
->string
, SQLTAG_TABLE
);
2112 addToScope(version
, event
->string
, SQLTAG_EVENT
);
2113 makeSqlTag (version
, SQLTAG_MLTABLE
);
2116 if( !isType (token
, TOKEN_CLOSE_PAREN
) )
2117 findToken (token
, TOKEN_CLOSE_PAREN
);
2121 findCmdTerm (token
, TRUE
);
2123 deleteToken (version
);
2124 deleteToken (table
);
2125 deleteToken (event
);
2128 static void parseMLConn (tokenInfo
*const token
)
2130 tokenInfo
*const version
= newToken ();
2131 tokenInfo
*const event
= newToken ();
2134 * This deals with these formats
2135 * call ml_add_connection_script( 'version', 'event',
2136 * 'some SQL statement'
2141 if ( isType (token
, TOKEN_OPEN_PAREN
) )
2143 readToken (version
);
2145 while (!(isType (token
, TOKEN_COMMA
) ||
2146 isType (token
, TOKEN_CLOSE_PAREN
)
2152 if (isType (token
, TOKEN_COMMA
))
2156 if (isType (version
, TOKEN_STRING
) &&
2157 isType (event
, TOKEN_STRING
) )
2159 addToScope(version
, event
->string
, SQLTAG_EVENT
);
2160 makeSqlTag (version
, SQLTAG_MLCONN
);
2163 if( !isType (token
, TOKEN_CLOSE_PAREN
) )
2164 findToken (token
, TOKEN_CLOSE_PAREN
);
2168 findCmdTerm (token
, TRUE
);
2170 deleteToken (version
);
2171 deleteToken (event
);
2174 static void parseMLProp (tokenInfo
*const token
)
2176 tokenInfo
*const component
= newToken ();
2177 tokenInfo
*const prop_set_name
= newToken ();
2178 tokenInfo
*const prop_name
= newToken ();
2181 * This deals with these formats
2191 if ( isType (token
, TOKEN_OPEN_PAREN
) )
2193 readToken (component
);
2195 while (!(isType (token
, TOKEN_COMMA
) ||
2196 isType (token
, TOKEN_CLOSE_PAREN
)
2202 if (isType (token
, TOKEN_COMMA
))
2204 readToken (prop_set_name
);
2206 while (!(isType (token
, TOKEN_COMMA
) ||
2207 isType (token
, TOKEN_CLOSE_PAREN
)
2213 if (isType (token
, TOKEN_COMMA
))
2215 readToken (prop_name
);
2217 if (isType (component
, TOKEN_STRING
) &&
2218 isType (prop_set_name
, TOKEN_STRING
) &&
2219 isType (prop_name
, TOKEN_STRING
) )
2221 addToScope(component
, prop_set_name
->string
, SQLTAG_MLPROP
/* FIXME */);
2222 addToScope(component
, prop_name
->string
, SQLTAG_MLPROP
/* FIXME */);
2223 makeSqlTag (component
, SQLTAG_MLPROP
);
2226 if( !isType (token
, TOKEN_CLOSE_PAREN
) )
2227 findToken (token
, TOKEN_CLOSE_PAREN
);
2231 findCmdTerm (token
, TRUE
);
2233 deleteToken (component
);
2234 deleteToken (prop_set_name
);
2235 deleteToken (prop_name
);
2238 static void parseComment (tokenInfo
*const token
)
2241 * This deals with this statement:
2242 * COMMENT TO PRESERVE FORMAT ON PROCEDURE "DBA"."test" IS
2243 * {create PROCEDURE DBA."test"()
2249 * The comment can contain anything between the CURLY
2251 * COMMENT ON USER "admin" IS
2252 * 'Administration Group'
2254 * Or it could be a simple string with no curly braces
2256 while (! isKeyword (token
, KEYWORD_is
))
2261 if ( isType(token
, TOKEN_OPEN_CURLY
) )
2263 findToken (token
, TOKEN_CLOSE_CURLY
);
2266 findCmdTerm (token
, TRUE
);
2270 static void parseKeywords (tokenInfo
*const token
)
2272 switch (token
->keyword
)
2274 case KEYWORD_begin
: parseBlock (token
, FALSE
); break;
2275 case KEYWORD_comment
: parseComment (token
); break;
2276 case KEYWORD_cursor
: parseSimple (token
, SQLTAG_CURSOR
); break;
2277 case KEYWORD_datatype
: parseDomain (token
); break;
2278 case KEYWORD_declare
: parseBlock (token
, FALSE
); break;
2279 case KEYWORD_domain
: parseDomain (token
); break;
2280 case KEYWORD_drop
: parseDrop (token
); break;
2281 case KEYWORD_event
: parseEvent (token
); break;
2282 case KEYWORD_function
: parseSubProgram (token
); break;
2283 case KEYWORD_if
: parseStatements (token
, FALSE
); break;
2284 case KEYWORD_index
: parseIndex (token
); break;
2285 case KEYWORD_ml_table
: parseMLTable (token
); break;
2286 case KEYWORD_ml_table_lang
: parseMLTable (token
); break;
2287 case KEYWORD_ml_table_dnet
: parseMLTable (token
); break;
2288 case KEYWORD_ml_table_java
: parseMLTable (token
); break;
2289 case KEYWORD_ml_table_chk
: parseMLTable (token
); break;
2290 case KEYWORD_ml_conn
: parseMLConn (token
); break;
2291 case KEYWORD_ml_conn_lang
: parseMLConn (token
); break;
2292 case KEYWORD_ml_conn_dnet
: parseMLConn (token
); break;
2293 case KEYWORD_ml_conn_java
: parseMLConn (token
); break;
2294 case KEYWORD_ml_conn_chk
: parseMLConn (token
); break;
2295 case KEYWORD_ml_prop
: parseMLProp (token
); break;
2296 case KEYWORD_package
: parsePackage (token
); break;
2297 case KEYWORD_procedure
: parseSubProgram (token
); break;
2298 case KEYWORD_publication
: parsePublication (token
); break;
2299 case KEYWORD_service
: parseService (token
); break;
2300 case KEYWORD_subtype
: parseSimple (token
, SQLTAG_SUBTYPE
); break;
2301 case KEYWORD_synonym
: parseSynonym (token
); break;
2302 case KEYWORD_table
: parseTable (token
); break;
2303 case KEYWORD_trigger
: parseTrigger (token
); break;
2304 case KEYWORD_type
: parseType (token
); break;
2305 case KEYWORD_variable
: parseVariable (token
); break;
2306 case KEYWORD_view
: parseView (token
); break;
2311 static void parseSqlFile (tokenInfo
*const token
)
2317 if (isType (token
, TOKEN_BLOCK_LABEL_BEGIN
))
2320 parseKeywords (token
);
2321 } while (! isKeyword (token
, KEYWORD_end
));
2324 static void initialize (const langType language
)
2326 Assert (ARRAY_SIZE (SqlKinds
) == SQLTAG_COUNT
);
2327 Lang_sql
= language
;
2330 static void findSqlTags (void)
2332 tokenInfo
*const token
= newToken ();
2333 exception_t exception
= (exception_t
) (setjmp (Exception
));
2335 while (exception
== ExceptionNone
)
2336 parseSqlFile (token
);
2338 deleteToken (token
);
2341 extern parserDefinition
* SqlParser (void)
2343 static const char *const extensions
[] = { "sql", NULL
};
2344 parserDefinition
* def
= parserNew ("SQL");
2345 def
->kinds
= SqlKinds
;
2346 def
->kindCount
= ARRAY_SIZE (SqlKinds
);
2347 def
->extensions
= extensions
;
2348 def
->parser
= findSqlTags
;
2349 def
->initialize
= initialize
;
2350 def
->keywordTable
= SqlKeywordTable
;
2351 def
->keywordCount
= ARRAY_SIZE (SqlKeywordTable
);
2355 /* vi:set tabstop=4 shiftwidth=4 noexpandtab: */