2 * $Id: sql.c 745 2009-10-27 02:42:55Z dfishburn $
4 * Copyright (c) 2002-2003, Darren Hiebert
6 * This source code is released for free distribution under the terms of the
7 * GNU General Public License.
9 * This module contains functions for generating tags for PL/SQL language
16 #include "general.h" /* must always come first */
18 #include <ctype.h> /* to define isalpha () */
33 * On-line "Oracle Database PL/SQL Language Reference":
34 * http://download.oracle.com/docs/cd/B28359_01/appdev.111/b28370/toc.htm
36 * Sample PL/SQL code is available from:
37 * http://www.orafaq.com/faqscrpt.htm#GENPLSQL
39 * On-line SQL Anywhere Documentation
40 * http://www.ianywhere.com/developer/product_manuals/sqlanywhere/index.html
46 #define isType(token,t) (boolean) ((token)->type == (t))
47 #define isKeyword(token,k) (boolean) ((token)->keyword == (k))
53 typedef enum eException
{ ExceptionNone
, ExceptionEOF
} exception_t
;
56 * Used to specify type of keyword.
58 typedef enum eKeywordId
{
113 KEYWORD_ml_table_lang
,
114 KEYWORD_ml_table_dnet
,
115 KEYWORD_ml_table_java
,
116 KEYWORD_ml_table_chk
,
118 KEYWORD_ml_conn_lang
,
119 KEYWORD_ml_conn_dnet
,
120 KEYWORD_ml_conn_java
,
135 * Used to determine whether keyword is valid for the token language and
138 typedef struct sKeywordDesc
{
143 typedef enum eTokenType
{
145 TOKEN_BLOCK_LABEL_BEGIN
,
146 TOKEN_BLOCK_LABEL_END
,
168 typedef struct sTokenInfoSQL
{
173 int begin_end_nest_lvl
;
174 unsigned long lineNumber
;
182 static langType Lang_sql
;
184 static jmp_buf Exception
;
191 SQLTAG_LOCAL_VARIABLE
,
213 static kindOption SqlKinds
[] = {
214 { TRUE
, 'c', "cursor", "cursors" },
215 { FALSE
, 'd', "prototype", "prototypes" },
216 { TRUE
, 'f', "function", "functions" },
217 { TRUE
, 'F', "field", "record fields" },
218 { FALSE
, 'l', "local", "local variables" },
219 { TRUE
, 'L', "label", "block label" },
220 { TRUE
, 'P', "package", "packages" },
221 { TRUE
, 'p', "procedure", "procedures" },
222 { FALSE
, 'r', "record", "records" },
223 { TRUE
, 's', "subtype", "subtypes" },
224 { TRUE
, 't', "table", "tables" },
225 { TRUE
, 'T', "trigger", "triggers" },
226 { TRUE
, 'v', "variable", "variables" },
227 { TRUE
, 'i', "index", "indexes" },
228 { TRUE
, 'e', "event", "events" },
229 { TRUE
, 'U', "publication", "publications" },
230 { TRUE
, 'R', "service", "services" },
231 { TRUE
, 'D', "domain", "domains" },
232 { TRUE
, 'V', "view", "views" },
233 { TRUE
, 'n', "synonym", "synonyms" },
234 { TRUE
, 'x', "mltable", "MobiLink Table Scripts" },
235 { TRUE
, 'y', "mlconn", "MobiLink Conn Scripts" },
236 { TRUE
, 'z', "mlprop", "MobiLink Properties " }
239 static const keywordDesc SqlKeywordTable
[] = {
240 /* keyword keyword ID */
241 { "as", KEYWORD_is
},
242 { "is", KEYWORD_is
},
243 { "begin", KEYWORD_begin
},
244 { "body", KEYWORD_body
},
245 { "cursor", KEYWORD_cursor
},
246 { "declare", KEYWORD_declare
},
247 { "end", KEYWORD_end
},
248 { "function", KEYWORD_function
},
249 { "if", KEYWORD_if
},
250 { "else", KEYWORD_else
},
251 { "elseif", KEYWORD_elseif
},
252 { "endif", KEYWORD_endif
},
253 { "loop", KEYWORD_loop
},
254 { "while", KEYWORD_while
},
255 { "case", KEYWORD_case
},
256 { "for", KEYWORD_for
},
257 { "do", KEYWORD_do
},
258 { "call", KEYWORD_call
},
259 { "package", KEYWORD_package
},
260 { "pragma", KEYWORD_pragma
},
261 { "procedure", KEYWORD_procedure
},
262 { "record", KEYWORD_record
},
263 { "object", KEYWORD_object
},
264 { "ref", KEYWORD_ref
},
265 { "rem", KEYWORD_rem
},
266 { "return", KEYWORD_return
},
267 { "returns", KEYWORD_returns
},
268 { "subtype", KEYWORD_subtype
},
269 { "table", KEYWORD_table
},
270 { "trigger", KEYWORD_trigger
},
271 { "type", KEYWORD_type
},
272 { "index", KEYWORD_index
},
273 { "event", KEYWORD_event
},
274 { "publication", KEYWORD_publication
},
275 { "service", KEYWORD_service
},
276 { "domain", KEYWORD_domain
},
277 { "datatype", KEYWORD_datatype
},
278 { "result", KEYWORD_result
},
279 { "url", KEYWORD_url
},
280 { "internal", KEYWORD_internal
},
281 { "external", KEYWORD_external
},
282 { "when", KEYWORD_when
},
283 { "then", KEYWORD_then
},
284 { "variable", KEYWORD_variable
},
285 { "exception", KEYWORD_exception
},
286 { "at", KEYWORD_at
},
287 { "on", KEYWORD_on
},
288 { "primary", KEYWORD_primary
},
289 { "references", KEYWORD_references
},
290 { "unique", KEYWORD_unique
},
291 { "check", KEYWORD_check
},
292 { "constraint", KEYWORD_constraint
},
293 { "foreign", KEYWORD_foreign
},
294 { "ml_add_table_script", KEYWORD_ml_table
},
295 { "ml_add_lang_table_script", KEYWORD_ml_table_lang
},
296 { "ml_add_dnet_table_script", KEYWORD_ml_table_dnet
},
297 { "ml_add_java_table_script", KEYWORD_ml_table_java
},
298 { "ml_add_lang_table_script_chk", KEYWORD_ml_table_chk
},
299 { "ml_add_connection_script", KEYWORD_ml_conn
},
300 { "ml_add_lang_connection_script", KEYWORD_ml_conn_lang
},
301 { "ml_add_dnet_connection_script", KEYWORD_ml_conn_dnet
},
302 { "ml_add_java_connection_script", KEYWORD_ml_conn_java
},
303 { "ml_add_lang_conn_script_chk", KEYWORD_ml_conn_chk
},
304 { "ml_add_property", KEYWORD_ml_prop
},
305 { "local", KEYWORD_local
},
306 { "temporary", KEYWORD_temporary
},
307 { "drop", KEYWORD_drop
},
308 { "view", KEYWORD_view
},
309 { "synonym", KEYWORD_synonym
},
310 { "handler", KEYWORD_handler
},
311 { "comment", KEYWORD_comment
},
312 { "create", KEYWORD_create
},
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
335 * isIdentChar1 is used to identify the first character of an
336 * identifier, so we are removing some restrictions.
339 (isalpha (c
) || c
== '@' || c
== '_' );
342 static boolean
isIdentChar (const int c
)
345 (isalpha (c
) || isdigit (c
) || c
== '$' ||
346 c
== '@' || c
== '_' || c
== '#');
349 static boolean
isCmdTerm (tokenInfo
*const token
)
352 debugPrintf (DEBUG_PARSE
353 , "\n isCmdTerm: token same tt:%d tk:%d\n"
360 * Based on the various customer sites I have been at
361 * the most common command delimiters are
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:
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
387 * blocks, some statements may not be terminated using
388 * the standard delimiters:
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
399 if ( nest_lvl
> 0 && isKeyword (token
, KEYWORD_end
) )
401 if ( token
->begin_end_nest_lvl
== nest_lvl
)
408 static void buildSqlKeywordHash (void)
410 const size_t count
= sizeof (SqlKeywordTable
) /
411 sizeof (SqlKeywordTable
[0]);
413 for (i
= 0 ; i
< count
; ++i
)
415 const keywordDesc
* const p
= &SqlKeywordTable
[i
];
416 addKeyword (p
->name
, Lang_sql
, (int) p
->id
);
420 static tokenInfo
*newToken (void)
422 tokenInfo
*const token
= xMalloc (1, tokenInfo
);
424 token
->type
= TOKEN_UNDEFINED
;
425 token
->keyword
= KEYWORD_NONE
;
426 token
->string
= vStringNew ();
427 token
->scope
= vStringNew ();
428 token
->begin_end_nest_lvl
= 0;
429 token
->lineNumber
= getSourceLineNumber ();
430 token
->filePosition
= getInputFilePosition ();
435 static void deleteToken (tokenInfo
*const token
)
437 vStringDelete (token
->string
);
438 vStringDelete (token
->scope
);
443 * Tag generation functions
446 static void makeConstTag (tokenInfo
*const token
, const sqlKind kind
)
448 if (SqlKinds
[kind
].enabled
)
450 const char *const name
= vStringValue (token
->string
);
452 initTagEntry (&e
, name
);
454 e
.lineNumber
= token
->lineNumber
;
455 e
.filePosition
= token
->filePosition
;
456 e
.kindName
= SqlKinds
[kind
].name
;
457 e
.kind
= SqlKinds
[kind
].letter
;
463 static void makeSqlTag (tokenInfo
*const token
, const sqlKind kind
)
467 if (SqlKinds
[kind
].enabled
)
470 * If a scope has been added to the token, change the token
471 * string to include the scope when making the tag.
473 if ( vStringLength(token
->scope
) > 0 )
475 fulltag
= vStringNew ();
476 vStringCopy(fulltag
, token
->scope
);
477 vStringCatS (fulltag
, ".");
478 vStringCatS (fulltag
, vStringValue(token
->string
));
479 vStringTerminate(fulltag
);
480 vStringCopy(token
->string
, fulltag
);
481 vStringDelete (fulltag
);
483 makeConstTag (token
, kind
);
491 static void parseString (vString
*const string
, const int delimiter
)
502 c = fileGetc(); // This maybe a ' or ". //
503 vStringPut(string, c);
506 else if (c
== delimiter
)
509 vStringPut (string
, c
);
511 vStringTerminate (string
);
514 /* Read a C identifier beginning with "firstChar" and places it into "name".
516 static void parseIdentifier (vString
*const string
, const int firstChar
)
519 Assert (isIdentChar1 (c
));
522 vStringPut (string
, c
);
524 } while (isIdentChar (c
));
525 vStringTerminate (string
);
527 fileUngetc (c
); /* unget non-identifier character */
530 static void readToken (tokenInfo
*const token
)
534 token
->type
= TOKEN_UNDEFINED
;
535 token
->keyword
= KEYWORD_NONE
;
536 vStringClear (token
->string
);
542 token
->lineNumber
= getSourceLineNumber ();
543 token
->filePosition
= getInputFilePosition ();
545 * Added " to the list of ignores, not sure what this
546 * might break but it gets by this issue:
547 * create table "t1" (...)
549 * Darren, the code passes all my tests for both
550 * Oracle and SQL Anywhere, but maybe you can tell me
551 * what this may effect.
554 while (c
== '\t' || c
== ' ' || c
== '\n');
558 case EOF
: longjmp (Exception
, (int)ExceptionEOF
); break;
559 case '(': token
->type
= TOKEN_OPEN_PAREN
; break;
560 case ')': token
->type
= TOKEN_CLOSE_PAREN
; break;
561 case ':': token
->type
= TOKEN_COLON
; break;
562 case ';': token
->type
= TOKEN_SEMICOLON
; break;
563 case '.': token
->type
= TOKEN_PERIOD
; break;
564 case ',': token
->type
= TOKEN_COMMA
; break;
565 case '{': token
->type
= TOKEN_OPEN_CURLY
; break;
566 case '}': token
->type
= TOKEN_CLOSE_CURLY
; break;
567 case '~': token
->type
= TOKEN_TILDE
; break;
568 case '[': token
->type
= TOKEN_OPEN_SQUARE
; break;
569 case ']': token
->type
= TOKEN_CLOSE_SQUARE
; break;
570 case '=': token
->type
= TOKEN_EQUAL
; break;
574 token
->type
= TOKEN_STRING
;
575 parseString (token
->string
, c
);
576 token
->lineNumber
= getSourceLineNumber ();
577 token
->filePosition
= getInputFilePosition ();
582 if (c
== '-') /* -- is this the start of a comment? */
584 fileSkipToCharacter ('\n');
591 token
->type
= TOKEN_OPERATOR
;
598 const int initial
= c
;
603 token
->type
= TOKEN_BLOCK_LABEL_BEGIN
;
605 token
->type
= TOKEN_BLOCK_LABEL_END
;
610 token
->type
= TOKEN_UNDEFINED
;
617 if (c
!= '\\' && c
!= '"' && c
!= '\'' && !isspace (c
))
619 token
->type
= TOKEN_CHARACTER
;
620 token
->lineNumber
= getSourceLineNumber ();
621 token
->filePosition
= getInputFilePosition ();
627 if ( (d
!= '*') && /* is this the start of a comment? */
628 (d
!= '/') ) /* is a one line comment? */
630 token
->type
= TOKEN_FORWARD_SLASH
;
639 fileSkipToCharacter ('*');
645 } while (c
!= EOF
&& c
!= '\0');
648 else if (d
== '/') /* is this the start of a comment? */
650 fileSkipToCharacter ('\n');
658 if (! isIdentChar1 (c
))
659 token
->type
= TOKEN_UNDEFINED
;
662 parseIdentifier (token
->string
, c
);
663 token
->lineNumber
= getSourceLineNumber ();
664 token
->filePosition
= getInputFilePosition ();
665 token
->keyword
= analyzeToken (token
->string
, Lang_sql
);
666 if (isKeyword (token
, KEYWORD_rem
))
668 vStringClear (token
->string
);
669 fileSkipToCharacter ('\n');
672 else if (isKeyword (token
, KEYWORD_NONE
))
673 token
->type
= TOKEN_IDENTIFIER
;
675 token
->type
= TOKEN_KEYWORD
;
682 * Token parsing functions
686 * static void addContext (tokenInfo* const parent, const tokenInfo* const child)
688 * if (vStringLength (parent->string) > 0)
690 * vStringCatS (parent->string, ".");
692 * vStringCatS (parent->string, vStringValue(child->string));
693 * vStringTerminate(parent->string);
697 static void addToScope (tokenInfo
* const token
, vString
* const extra
)
699 if (vStringLength (token
->scope
) > 0)
701 vStringCatS (token
->scope
, ".");
703 vStringCatS (token
->scope
, vStringValue(extra
));
704 vStringTerminate(token
->scope
);
711 static void findToken (tokenInfo
*const token
, const tokenType type
)
713 while (! isType (token
, type
))
719 static void findCmdTerm (tokenInfo
*const token
, const boolean check_first
)
721 int begin_end_nest_lvl
= token
->begin_end_nest_lvl
;
725 if ( isCmdTerm(token
) )
731 } while ( !isCmdTerm(token
) && !isMatchedEnd(token
, begin_end_nest_lvl
) );
734 static void skipToMatched(tokenInfo
*const token
)
737 tokenType open_token
;
738 tokenType close_token
;
742 case TOKEN_OPEN_PAREN
:
743 open_token
= TOKEN_OPEN_PAREN
;
744 close_token
= TOKEN_CLOSE_PAREN
;
746 case TOKEN_OPEN_CURLY
:
747 open_token
= TOKEN_OPEN_CURLY
;
748 close_token
= TOKEN_CLOSE_CURLY
;
750 case TOKEN_OPEN_SQUARE
:
751 open_token
= TOKEN_OPEN_SQUARE
;
752 close_token
= TOKEN_CLOSE_SQUARE
;
759 * This routine will skip to a matching closing token.
760 * It will also handle nested tokens like the (, ) below.
761 * ( name varchar(30), text binary(10) )
764 if (isType (token
, open_token
))
767 while (! (isType (token
, close_token
) && (nest_level
== 0)))
770 if (isType (token
, open_token
))
774 if (isType (token
, close_token
))
786 static void skipArgumentList (tokenInfo
*const token
)
789 * Other databases can have arguments with fully declared
791 * ( name varchar(30), text binary(10) )
792 * So we must check for nested open and closing parentheses
795 if (isType (token
, TOKEN_OPEN_PAREN
)) /* arguments? */
797 skipToMatched (token
);
801 static void parseSubProgram (tokenInfo
*const token
)
803 tokenInfo
*const name
= newToken ();
806 * This must handle both prototypes and the body of
810 * FUNCTION func_name RETURN integer;
811 * PROCEDURE proc_name( parameters );
813 * FUNCTION GET_ML_USERNAME RETURN VARCHAR2
816 * RETURN v_sync_user_id;
817 * END GET_ML_USERNAME;
819 * PROCEDURE proc_name( parameters )
823 * CREATE PROCEDURE proc_name( parameters )
824 * EXTERNAL NAME ... ;
825 * CREATE PROCEDURE proc_name( parameters )
829 * CREATE FUNCTION f_GetClassName(
830 * IN @object VARCHAR(128)
831 * ,IN @code VARCHAR(128)
833 * RETURNS VARCHAR(200)
837 * IF( @object = 'user_state' ) THEN
838 * SET something = something;
844 const sqlKind kind
= isKeyword (token
, KEYWORD_function
) ?
845 SQLTAG_FUNCTION
: SQLTAG_PROCEDURE
;
846 Assert (isKeyword (token
, KEYWORD_function
) ||
847 isKeyword (token
, KEYWORD_procedure
));
850 if (isType (token
, TOKEN_PERIOD
))
855 if (isType (token
, TOKEN_OPEN_PAREN
))
857 /* Reads to the next token after the TOKEN_CLOSE_PAREN */
858 skipArgumentList(token
);
861 if (kind
== SQLTAG_FUNCTION
)
863 if (isKeyword (token
, KEYWORD_return
) || isKeyword (token
, KEYWORD_returns
))
868 * Read token after which could be the
869 * command terminator if a prototype
870 * or an open parenthesis
873 if (isType (token
, TOKEN_OPEN_PAREN
))
875 /* Reads to the next token after the TOKEN_CLOSE_PAREN */
876 skipArgumentList(token
);
880 if( isCmdTerm (token
) )
882 makeSqlTag (name
, SQLTAG_PROTOTYPE
);
886 while (!(isKeyword (token
, KEYWORD_is
) ||
887 isKeyword (token
, KEYWORD_begin
) ||
888 isKeyword (token
, KEYWORD_at
) ||
889 isKeyword (token
, KEYWORD_internal
) ||
890 isKeyword (token
, KEYWORD_external
) ||
891 isKeyword (token
, KEYWORD_url
) ||
892 isType (token
, TOKEN_EQUAL
) ||
897 if ( isKeyword (token
, KEYWORD_result
) )
900 if (isType (token
, TOKEN_OPEN_PAREN
))
902 /* Reads to the next token after the TOKEN_CLOSE_PAREN */
903 skipArgumentList(token
);
909 if (isKeyword (token
, KEYWORD_at
) ||
910 isKeyword (token
, KEYWORD_url
) ||
911 isKeyword (token
, KEYWORD_internal
) ||
912 isKeyword (token
, KEYWORD_external
) )
914 addToScope(token
, name
->string
);
915 if (isType (name
, TOKEN_IDENTIFIER
) ||
916 isType (name
, TOKEN_STRING
) ||
917 !isKeyword (token
, KEYWORD_NONE
)
919 makeSqlTag (name
, kind
);
921 vStringClear (token
->scope
);
923 if ( isType (token
, TOKEN_EQUAL
) )
926 if ( isKeyword (token
, KEYWORD_declare
) )
927 parseDeclare (token
, FALSE
);
929 if (isKeyword (token
, KEYWORD_is
) ||
930 isKeyword (token
, KEYWORD_begin
) )
932 addToScope(token
, name
->string
);
933 if (isType (name
, TOKEN_IDENTIFIER
) ||
934 isType (name
, TOKEN_STRING
) ||
935 !isKeyword (token
, KEYWORD_NONE
)
937 makeSqlTag (name
, kind
);
939 parseBlock (token
, TRUE
);
940 vStringClear (token
->scope
);
946 static void parseRecord (tokenInfo
*const token
)
949 * Make it a bit forgiving, this is called from
950 * multiple functions, parseTable, parseType
952 if (!isType (token
, TOKEN_OPEN_PAREN
))
955 Assert (isType (token
, TOKEN_OPEN_PAREN
));
958 if ( isType (token
, TOKEN_COMMA
) || isType (token
, TOKEN_OPEN_PAREN
) )
962 * Create table statements can end with various constraints
963 * which must be excluded from the SQLTAG_FIELD.
969 * constraint whatever,
975 if (! (isKeyword(token
, KEYWORD_primary
) ||
976 isKeyword(token
, KEYWORD_references
) ||
977 isKeyword(token
, KEYWORD_unique
) ||
978 isKeyword(token
, KEYWORD_check
) ||
979 isKeyword(token
, KEYWORD_constraint
) ||
980 isKeyword(token
, KEYWORD_foreign
) ) )
982 if (isType (token
, TOKEN_IDENTIFIER
) ||
983 isType (token
, TOKEN_STRING
))
984 makeSqlTag (token
, SQLTAG_FIELD
);
987 while (!(isType (token
, TOKEN_COMMA
) ||
988 isType (token
, TOKEN_CLOSE_PAREN
) ||
989 isType (token
, TOKEN_OPEN_PAREN
)
994 * A table structure can look like this:
1001 * We can't just look for a COMMA or CLOSE_PAREN
1002 * since that will not deal with the numeric(10,5)
1003 * case. So we need to skip the argument list
1004 * when we find an open paren.
1006 if (isType (token
, TOKEN_OPEN_PAREN
))
1008 /* Reads to the next token after the TOKEN_CLOSE_PAREN */
1009 skipArgumentList(token
);
1012 } while (! isType (token
, TOKEN_CLOSE_PAREN
));
1015 static void parseType (tokenInfo
*const token
)
1017 tokenInfo
*const name
= newToken ();
1018 vString
* saveScope
= vStringNew ();
1020 vStringCopy(saveScope
, token
->scope
);
1021 /* If a scope has been set, add it to the name */
1022 addToScope (name
, token
->scope
);
1024 if (isType (name
, TOKEN_IDENTIFIER
))
1027 if (isKeyword (token
, KEYWORD_is
))
1030 addToScope (token
, name
->string
);
1031 switch (token
->keyword
)
1033 case KEYWORD_record
:
1034 case KEYWORD_object
:
1035 makeSqlTag (name
, SQLTAG_RECORD
);
1036 parseRecord (token
);
1040 makeSqlTag (name
, SQLTAG_TABLE
);
1045 if (isKeyword (token
, KEYWORD_cursor
))
1046 makeSqlTag (name
, SQLTAG_CURSOR
);
1051 vStringClear (token
->scope
);
1054 vStringCopy(token
->scope
, saveScope
);
1056 vStringDelete(saveScope
);
1059 static void parseSimple (tokenInfo
*const token
, const sqlKind kind
)
1061 /* This will simply make the tagname from the first word found */
1063 if (isType (token
, TOKEN_IDENTIFIER
) ||
1064 isType (token
, TOKEN_STRING
))
1065 makeSqlTag (token
, kind
);
1068 static void parseDeclare (tokenInfo
*const token
, const boolean local
)
1071 * PL/SQL declares are of this format:
1074 * CURSOR curname ...
1075 * varname1 datatype;
1076 * varname2 datatype;
1077 * varname3 datatype;
1081 if (isKeyword (token
, KEYWORD_declare
))
1083 while (! isKeyword (token
, KEYWORD_begin
) && ! isKeyword (token
, KEYWORD_end
))
1085 switch (token
->keyword
)
1087 case KEYWORD_cursor
: parseSimple (token
, SQLTAG_CURSOR
); break;
1088 case KEYWORD_function
: parseSubProgram (token
); break;
1089 case KEYWORD_procedure
: parseSubProgram (token
); break;
1090 case KEYWORD_subtype
: parseSimple (token
, SQLTAG_SUBTYPE
); break;
1091 case KEYWORD_trigger
: parseSimple (token
, SQLTAG_TRIGGER
); break;
1092 case KEYWORD_type
: parseType (token
); break;
1095 if (isType (token
, TOKEN_IDENTIFIER
))
1099 makeSqlTag (token
, SQLTAG_LOCAL_VARIABLE
);
1103 makeSqlTag (token
, SQLTAG_VARIABLE
);
1108 findToken (token
, TOKEN_SEMICOLON
);
1113 static void parseDeclareANSI (tokenInfo
*const token
, const boolean local
)
1115 tokenInfo
*const type
= newToken ();
1117 * ANSI declares are of this format:
1119 * DECLARE varname1 datatype;
1120 * DECLARE varname2 datatype;
1123 * This differ from PL/SQL where DECLARE preceeds the BEGIN block
1124 * and the DECLARE keyword is not repeated.
1126 while (isKeyword (token
, KEYWORD_declare
))
1131 if (isKeyword (type
, KEYWORD_cursor
))
1132 makeSqlTag (token
, SQLTAG_CURSOR
);
1133 else if (isKeyword (token
, KEYWORD_local
) &&
1134 isKeyword (type
, KEYWORD_temporary
))
1137 * DECLARE LOCAL TEMPORARY TABLE table_name (
1143 if (isKeyword (token
, KEYWORD_table
))
1146 if (isType(token
, TOKEN_IDENTIFIER
) ||
1147 isType(token
, TOKEN_STRING
) )
1149 makeSqlTag (token
, SQLTAG_TABLE
);
1153 else if (isType (token
, TOKEN_IDENTIFIER
) ||
1154 isType (token
, TOKEN_STRING
))
1157 makeSqlTag (token
, SQLTAG_LOCAL_VARIABLE
);
1159 makeSqlTag (token
, SQLTAG_VARIABLE
);
1161 findToken (token
, TOKEN_SEMICOLON
);
1167 static void parseLabel (tokenInfo
*const token
)
1170 * A label has this format:
1171 * <<tobacco_dependency>>
1173 * v_senator VARCHAR2(100) := 'THURMOND, JESSE';
1175 * IF total_contributions (v_senator, 'TOBACCO') > 25000
1177 * <<alochol_dependency>>
1179 * v_senator VARCHAR2(100) := 'WHATEVERIT, TAKES';
1184 Assert (isType (token
, TOKEN_BLOCK_LABEL_BEGIN
));
1186 if (isType (token
, TOKEN_IDENTIFIER
))
1188 makeSqlTag (token
, SQLTAG_BLOCK_LABEL
);
1189 readToken (token
); /* read end of label */
1193 static void parseStatements (tokenInfo
*const token
, const boolean exit_on_endif
)
1195 boolean isAnsi
= TRUE
;
1196 boolean stmtTerm
= FALSE
;
1200 if (isType (token
, TOKEN_BLOCK_LABEL_BEGIN
))
1204 switch (token
->keyword
)
1206 case KEYWORD_exception
:
1209 * <exception handler>;
1211 * Where an exception handler could be:
1216 * In this case we need to skip this keyword and
1217 * move on to the next token without reading until
1225 * WHEN statements can be used in exception clauses
1226 * and CASE statements. The CASE statement should skip
1227 * these given below we skip over to an END statement.
1228 * But for an exception clause, we can have:
1234 * If we skip to the TOKEN_SEMICOLON, we miss the begin
1235 * of a nested BEGIN END block. So read the next token
1236 * after the THEN and restart the LOOP.
1238 while (! isKeyword (token
, KEYWORD_then
))
1246 * We do not want to look for a ; since for an empty
1247 * IF block, it would skip over the END.
1265 while ( ! isKeyword (token
, KEYWORD_then
) &&
1266 ! isKeyword (token
, KEYWORD_begin
) )
1271 if( isKeyword (token
, KEYWORD_begin
) )
1274 parseBlock(token
, FALSE
);
1277 * Handle the non-Ansi IF blocks.
1278 * parseBlock consumes the END, so if the next
1279 * token in a command terminator (like GO)
1280 * we know we are done with this statement.
1282 if ( isCmdTerm (token
) )
1289 while( ! (isKeyword (token
, KEYWORD_end
) ||
1290 isKeyword (token
, KEYWORD_endif
) )
1293 if ( isKeyword (token
, KEYWORD_else
) ||
1294 isKeyword (token
, KEYWORD_elseif
) )
1297 parseStatements (token
, TRUE
);
1299 if ( isCmdTerm(token
) )
1305 * parseStatements returns when it finds an END, an IF
1306 * should follow the END for ANSI anyway.
1310 if( isKeyword (token
, KEYWORD_end
) )
1313 if( isKeyword (token
, KEYWORD_if
) || isKeyword (token
, KEYWORD_endif
) )
1316 if ( isCmdTerm(token
) )
1322 * Well we need to do something here.
1323 * There are lots of different END statements
1344 * FOR loop_name AS cursor_name CURSOR FOR ...
1348 if( isKeyword (token
, KEYWORD_for
) )
1355 while ( ! isKeyword (token
, KEYWORD_is
) )
1358 * If this is not an AS keyword this is
1359 * not a proper FOR statement and should
1365 while ( ! isKeyword (token
, KEYWORD_do
) )
1371 while( ! isKeyword (token
, KEYWORD_end
) )
1374 if ( isKeyword (token, KEYWORD_else) ||
1375 isKeyword (token, KEYWORD_elseif) )
1379 parseStatements (token
, FALSE
);
1381 if ( isCmdTerm(token
) )
1386 if( isKeyword (token
, KEYWORD_end
) )
1390 * Typically ended with
1391 * END LOOP [loop name];
1393 * END FOR [loop name];
1395 if ( isKeyword (token
, KEYWORD_loop
) ||
1396 isKeyword (token
, KEYWORD_case
) ||
1397 isKeyword (token
, KEYWORD_for
) )
1400 if ( isCmdTerm(token
) )
1405 case KEYWORD_create
:
1407 parseKeywords(token
);
1410 case KEYWORD_declare
:
1412 parseBlock (token
, TRUE
);
1423 * Not all statements must end in a semi-colon
1425 * if current publisher <> 'publish' then
1426 * signal UE_FailStatement
1429 * The last statement prior to an end ("signal" above) does
1430 * not need a semi-colon, nor does the end if, since it is
1431 * also the last statement prior to the end of the block.
1433 * So we must read to the first semi-colon or an END block
1435 while ( ! stmtTerm
&&
1436 ! ( isKeyword (token
, KEYWORD_end
) ||
1437 (isCmdTerm(token
)) )
1440 if ( isKeyword (token
, KEYWORD_endif
) &&
1444 if (isType (token
, TOKEN_COLON
) )
1447 * A : can signal a loop name
1452 * Unfortunately, labels do not have a
1453 * cmd terminator, therefore we have to check
1454 * if the next token is a keyword and process
1458 if ( isKeyword (token
, KEYWORD_loop
) ||
1459 isKeyword (token
, KEYWORD_while
) ||
1460 isKeyword (token
, KEYWORD_for
) )
1461 /* parseStatements (token); */
1467 if (isType (token
, TOKEN_OPEN_PAREN
) ||
1468 isType (token
, TOKEN_OPEN_CURLY
) ||
1469 isType (token
, TOKEN_OPEN_SQUARE
) )
1470 skipToMatched (token
);
1473 * Since we know how to parse various statements
1474 * if we detect them, parse them to completion
1476 if (isType (token
, TOKEN_BLOCK_LABEL_BEGIN
) ||
1477 isKeyword (token
, KEYWORD_exception
) ||
1478 isKeyword (token
, KEYWORD_loop
) ||
1479 isKeyword (token
, KEYWORD_case
) ||
1480 isKeyword (token
, KEYWORD_for
) ||
1481 isKeyword (token
, KEYWORD_begin
) )
1482 parseStatements (token
, FALSE
);
1483 else if (isKeyword (token
, KEYWORD_if
))
1484 parseStatements (token
, TRUE
);
1489 * We assumed earlier all statements ended with a command terminator.
1490 * See comment above, now, only read if the current token
1491 * is not a command terminator.
1493 if ( isCmdTerm(token
) && ! stmtTerm
)
1496 } while (! isKeyword (token
, KEYWORD_end
) &&
1497 ! (exit_on_endif
&& isKeyword (token
, KEYWORD_endif
) ) &&
1501 static void parseBlock (tokenInfo
*const token
, const boolean local
)
1503 if (isType (token
, TOKEN_BLOCK_LABEL_BEGIN
))
1508 if (! isKeyword (token
, KEYWORD_begin
))
1512 * These are Oracle style declares which generally come
1513 * between an IS/AS and BEGIN block.
1515 parseDeclare (token
, local
);
1517 if (isKeyword (token
, KEYWORD_begin
))
1521 * Check for ANSI declarations which always follow
1522 * a BEGIN statement. This routine will not advance
1523 * the token if none are found.
1525 parseDeclareANSI (token
, local
);
1526 token
->begin_end_nest_lvl
++;
1527 while (! isKeyword (token
, KEYWORD_end
))
1529 parseStatements (token
, FALSE
);
1531 if ( isCmdTerm(token
) )
1534 token
->begin_end_nest_lvl
--;
1537 * Read the next token (we will assume
1538 * it is the command delimiter)
1543 * Check if the END block is terminated
1545 if ( !isCmdTerm (token
) )
1548 * Not sure what to do here at the moment.
1549 * I think the routine that calls parseBlock
1550 * must expect the next token has already
1551 * been read since it is possible this
1552 * token is not a command delimiter.
1554 /* findCmdTerm (token, FALSE); */
1559 static void parsePackage (tokenInfo
*const token
)
1562 * Packages can be specified in a number of ways:
1563 * CREATE OR REPLACE PACKAGE pkg_name AS
1565 * CREATE OR REPLACE PACKAGE owner.pkg_name AS
1566 * or by specifying a package body
1567 * CREATE OR REPLACE PACKAGE BODY pkg_name AS
1568 * CREATE OR REPLACE PACKAGE BODY owner.pkg_name AS
1570 tokenInfo
*const name
= newToken ();
1572 if (isKeyword (name
, KEYWORD_body
))
1575 * Ignore the BODY tag since we will process
1576 * the body or prototypes in the same manner
1580 /* Check for owner.pkg_name */
1581 while (! isKeyword (token
, KEYWORD_is
))
1584 if ( isType(token
, TOKEN_PERIOD
) )
1589 if (isKeyword (token
, KEYWORD_is
))
1591 if (isType (name
, TOKEN_IDENTIFIER
) ||
1592 isType (name
, TOKEN_STRING
))
1593 makeSqlTag (name
, SQLTAG_PACKAGE
);
1594 parseBlock (token
, FALSE
);
1596 findCmdTerm (token
, FALSE
);
1600 static void parseTable (tokenInfo
*const token
)
1602 tokenInfo
*const name
= newToken ();
1605 * This deals with these formats:
1606 * create table t1 (c1 int);
1607 * create global tempoary table t2 (c1 int);
1608 * create table "t3" (c1 int);
1609 * create table bob.t4 (c1 int);
1610 * create table bob."t5" (c1 int);
1611 * create table "bob"."t6" (c1 int);
1612 * create table bob."t7" (c1 int);
1613 * Proxy tables use this format:
1614 * create existing table bob."t7" AT '...';
1615 * SQL Server and Sybase formats
1616 * create table OnlyTable (
1617 * create table dbo.HasOwner (
1618 * create table [dbo].[HasOwnerSquare] (
1619 * create table master.dbo.HasDb (
1620 * create table master..HasDbNoOwner (
1621 * create table [master].dbo.[HasDbAndOwnerSquare] (
1622 * create table [master]..[HasDbNoOwnerSquare] (
1625 /* This could be a database, owner or table name */
1627 if (isType (name
, TOKEN_OPEN_SQUARE
))
1630 /* Read close square */
1634 if (isType (token
, TOKEN_PERIOD
))
1637 * This could be a owner or table name.
1638 * But this is also a special case since the table can be
1639 * referenced with a blank owner:
1643 if (isType (name
, TOKEN_OPEN_SQUARE
))
1646 /* Read close square */
1649 /* Check if a blank name was provided */
1650 if (isType (name
, TOKEN_PERIOD
))
1653 if (isType (name
, TOKEN_OPEN_SQUARE
))
1656 /* Read close square */
1661 if (isType (token
, TOKEN_PERIOD
))
1663 /* This can only be the table name */
1665 if (isType (name
, TOKEN_OPEN_SQUARE
))
1668 /* Read close square */
1674 if (isType (token
, TOKEN_OPEN_PAREN
))
1676 if (isType (name
, TOKEN_IDENTIFIER
) ||
1677 isType (name
, TOKEN_STRING
))
1679 makeSqlTag (name
, SQLTAG_TABLE
);
1680 vStringCopy(token
->scope
, name
->string
);
1681 parseRecord (token
);
1682 vStringClear (token
->scope
);
1685 else if (isKeyword (token
, KEYWORD_at
))
1687 if (isType (name
, TOKEN_IDENTIFIER
))
1689 makeSqlTag (name
, SQLTAG_TABLE
);
1692 findCmdTerm (token
, FALSE
);
1696 static void parseIndex (tokenInfo
*const token
)
1698 tokenInfo
*const name
= newToken ();
1699 tokenInfo
*const owner
= newToken ();
1702 * This deals with these formats
1703 * create index i1 on t1(c1) create index "i2" on t1(c1)
1704 * create virtual unique clustered index "i3" on t1(c1)
1705 * create unique clustered index "i4" on t1(c1)
1706 * create clustered index "i5" on t1(c1)
1707 * create bitmap index "i6" on t1(c1)
1712 if (isType (token
, TOKEN_PERIOD
))
1717 if ( isKeyword (token
, KEYWORD_on
) &&
1718 (isType (name
, TOKEN_IDENTIFIER
) || isType (name
, TOKEN_STRING
) ) )
1722 if (isType (token
, TOKEN_PERIOD
))
1727 addToScope(name
, owner
->string
);
1728 makeSqlTag (name
, SQLTAG_INDEX
);
1730 findCmdTerm (token
, FALSE
);
1732 deleteToken (owner
);
1735 static void parseEvent (tokenInfo
*const token
)
1737 tokenInfo
*const name
= newToken ();
1740 * This deals with these formats
1741 * create event e1 handler begin end;
1742 * create event "e2" handler begin end;
1743 * create event dba."e3" handler begin end;
1744 * create event "dba"."e4" handler begin end;
1749 if (isType (token
, TOKEN_PERIOD
))
1753 while (! (isKeyword (token
, KEYWORD_handler
) ||
1754 (isType (token
, TOKEN_SEMICOLON
))) )
1759 if ( isKeyword (token
, KEYWORD_handler
) ||
1760 isType (token
, TOKEN_SEMICOLON
) )
1762 makeSqlTag (name
, SQLTAG_EVENT
);
1765 if (isKeyword (token
, KEYWORD_handler
))
1768 if ( isKeyword (token
, KEYWORD_begin
) )
1770 parseBlock (token
, TRUE
);
1772 findCmdTerm (token
, TRUE
);
1777 static void parseTrigger (tokenInfo
*const token
)
1779 tokenInfo
*const name
= newToken ();
1780 tokenInfo
*const table
= newToken ();
1783 * This deals with these formats
1784 * create or replace trigger tr1 begin end;
1785 * create trigger "tr2" begin end;
1786 * drop trigger "droptr1";
1787 * create trigger "tr3" CALL sp_something();
1788 * create trigger "owner"."tr4" begin end;
1789 * create trigger "tr5" not valid;
1790 * create trigger "tr6" begin end;
1795 if (isType (token
, TOKEN_PERIOD
))
1801 while ( !isKeyword (token
, KEYWORD_on
) &&
1802 !isCmdTerm (token
) )
1807 /*if (! isType (token, TOKEN_SEMICOLON) ) */
1808 if (! isCmdTerm (token
) )
1812 if (isType (token
, TOKEN_PERIOD
))
1818 while (! (isKeyword (token
, KEYWORD_begin
) ||
1819 (isKeyword (token
, KEYWORD_call
)) ||
1820 ( isCmdTerm (token
))) )
1822 if ( isKeyword (token
, KEYWORD_declare
) )
1824 addToScope(token
, name
->string
);
1825 parseDeclare(token
, TRUE
);
1826 vStringClear(token
->scope
);
1832 if ( isKeyword (token
, KEYWORD_begin
) ||
1833 isKeyword (token
, KEYWORD_call
) )
1835 addToScope(name
, table
->string
);
1836 makeSqlTag (name
, SQLTAG_TRIGGER
);
1837 addToScope(token
, table
->string
);
1838 if ( isKeyword (token
, KEYWORD_begin
) )
1840 parseBlock (token
, TRUE
);
1842 vStringClear(token
->scope
);
1846 findCmdTerm (token
, TRUE
);
1848 deleteToken (table
);
1851 static void parsePublication (tokenInfo
*const token
)
1853 tokenInfo
*const name
= newToken ();
1856 * This deals with these formats
1857 * create or replace publication pu1 ()
1858 * create publication "pu2" ()
1859 * create publication dba."pu3" ()
1860 * create publication "dba"."pu4" ()
1865 if (isType (token
, TOKEN_PERIOD
))
1870 if (isType (token
, TOKEN_OPEN_PAREN
))
1872 if (isType (name
, TOKEN_IDENTIFIER
) ||
1873 isType (name
, TOKEN_STRING
))
1875 makeSqlTag (name
, SQLTAG_PUBLICATION
);
1878 findCmdTerm (token
, FALSE
);
1882 static void parseService (tokenInfo
*const token
)
1884 tokenInfo
*const name
= newToken ();
1887 * This deals with these formats
1888 * CREATE SERVICE s1 TYPE 'HTML'
1889 * AUTHORIZATION OFF USER DBA AS
1891 * FROM SYS.SYSTABLE;
1892 * CREATE SERVICE "s2" TYPE 'HTML'
1893 * AUTHORIZATION OFF USER DBA AS
1894 * CALL sp_Something();
1899 if (isKeyword (token
, KEYWORD_type
))
1901 if (isType (name
, TOKEN_IDENTIFIER
) ||
1902 isType (name
, TOKEN_STRING
))
1904 makeSqlTag (name
, SQLTAG_SERVICE
);
1907 findCmdTerm (token
, FALSE
);
1911 static void parseDomain (tokenInfo
*const token
)
1913 tokenInfo
*const name
= newToken ();
1916 * This deals with these formats
1917 * CREATE DOMAIN|DATATYPE [AS] your_name ...;
1921 if (isKeyword (name
, KEYWORD_is
))
1926 if (isType (name
, TOKEN_IDENTIFIER
) ||
1927 isType (name
, TOKEN_STRING
))
1929 makeSqlTag (name
, SQLTAG_DOMAIN
);
1931 findCmdTerm (token
, FALSE
);
1935 static void parseDrop (tokenInfo
*const token
)
1938 * This deals with these formats
1939 * DROP TABLE|PROCEDURE|DOMAIN|DATATYPE name;
1941 * Just simply skip over these statements.
1942 * They are often confused with PROCEDURE prototypes
1943 * since the syntax is similar, this effectively deals with
1944 * the issue for all types.
1947 findCmdTerm (token
, FALSE
);
1950 static void parseVariable (tokenInfo
*const token
)
1952 tokenInfo
*const name
= newToken ();
1955 * This deals with these formats
1956 * create variable varname1 integer;
1957 * create variable @varname2 integer;
1958 * create variable "varname3" integer;
1959 * drop variable @varname3;
1964 if ( (isType (name
, TOKEN_IDENTIFIER
) || isType (name
, TOKEN_STRING
))
1965 && !isType (token
, TOKEN_SEMICOLON
) )
1967 makeSqlTag (name
, SQLTAG_VARIABLE
);
1969 findCmdTerm (token
, TRUE
);
1974 static void parseSynonym (tokenInfo
*const token
)
1976 tokenInfo
*const name
= newToken ();
1979 * This deals with these formats
1980 * create variable varname1 integer;
1981 * create variable @varname2 integer;
1982 * create variable "varname3" integer;
1983 * drop variable @varname3;
1988 if ( (isType (name
, TOKEN_IDENTIFIER
) || isType (name
, TOKEN_STRING
))
1989 && isKeyword (token
, KEYWORD_for
) )
1991 makeSqlTag (name
, SQLTAG_SYNONYM
);
1993 findCmdTerm (token
, TRUE
);
1998 static void parseView (tokenInfo
*const token
)
2000 tokenInfo
*const name
= newToken ();
2003 * This deals with these formats
2004 * create variable varname1 integer;
2005 * create variable @varname2 integer;
2006 * create variable "varname3" integer;
2007 * drop variable @varname3;
2012 if (isType (token
, TOKEN_PERIOD
))
2017 if ( isType (token
, TOKEN_OPEN_PAREN
) )
2019 skipArgumentList(token
);
2023 while (!(isKeyword (token
, KEYWORD_is
) ||
2024 isType (token
, TOKEN_SEMICOLON
)
2030 if ( (isType (name
, TOKEN_IDENTIFIER
) || isType (name
, TOKEN_STRING
))
2031 && isKeyword (token
, KEYWORD_is
) )
2033 makeSqlTag (name
, SQLTAG_VIEW
);
2036 findCmdTerm (token
, TRUE
);
2041 static void parseMLTable (tokenInfo
*const token
)
2043 tokenInfo
*const version
= newToken ();
2044 tokenInfo
*const table
= newToken ();
2045 tokenInfo
*const event
= newToken ();
2048 * This deals with these formats
2049 * call dbo.ml_add_table_script( 'version', 'table_name', 'event',
2050 * 'some SQL statement'
2055 if ( isType (token
, TOKEN_OPEN_PAREN
) )
2057 readToken (version
);
2059 while (!(isType (token
, TOKEN_COMMA
) ||
2060 isType (token
, TOKEN_CLOSE_PAREN
)
2066 if (isType (token
, TOKEN_COMMA
))
2070 while (!(isType (token
, TOKEN_COMMA
) ||
2071 isType (token
, TOKEN_CLOSE_PAREN
)
2077 if (isType (token
, TOKEN_COMMA
))
2081 if (isType (version
, TOKEN_STRING
) &&
2082 isType (table
, TOKEN_STRING
) &&
2083 isType (event
, TOKEN_STRING
) )
2085 addToScope(version
, table
->string
);
2086 addToScope(version
, event
->string
);
2087 makeSqlTag (version
, SQLTAG_MLTABLE
);
2090 if( !isType (token
, TOKEN_CLOSE_PAREN
) )
2091 findToken (token
, TOKEN_CLOSE_PAREN
);
2095 findCmdTerm (token
, TRUE
);
2097 deleteToken (version
);
2098 deleteToken (table
);
2099 deleteToken (event
);
2102 static void parseMLConn (tokenInfo
*const token
)
2104 tokenInfo
*const version
= newToken ();
2105 tokenInfo
*const event
= newToken ();
2108 * This deals with these formats
2109 * call ml_add_connection_script( 'version', 'event',
2110 * 'some SQL statement'
2115 if ( isType (token
, TOKEN_OPEN_PAREN
) )
2117 readToken (version
);
2119 while (!(isType (token
, TOKEN_COMMA
) ||
2120 isType (token
, TOKEN_CLOSE_PAREN
)
2126 if (isType (token
, TOKEN_COMMA
))
2130 if (isType (version
, TOKEN_STRING
) &&
2131 isType (event
, TOKEN_STRING
) )
2133 addToScope(version
, event
->string
);
2134 makeSqlTag (version
, SQLTAG_MLCONN
);
2137 if( !isType (token
, TOKEN_CLOSE_PAREN
) )
2138 findToken (token
, TOKEN_CLOSE_PAREN
);
2142 findCmdTerm (token
, TRUE
);
2144 deleteToken (version
);
2145 deleteToken (event
);
2148 static void parseMLProp (tokenInfo
*const token
)
2150 tokenInfo
*const component
= newToken ();
2151 tokenInfo
*const prop_set_name
= newToken ();
2152 tokenInfo
*const prop_name
= newToken ();
2155 * This deals with these formats
2165 if ( isType (token
, TOKEN_OPEN_PAREN
) )
2167 readToken (component
);
2169 while (!(isType (token
, TOKEN_COMMA
) ||
2170 isType (token
, TOKEN_CLOSE_PAREN
)
2176 if (isType (token
, TOKEN_COMMA
))
2178 readToken (prop_set_name
);
2180 while (!(isType (token
, TOKEN_COMMA
) ||
2181 isType (token
, TOKEN_CLOSE_PAREN
)
2187 if (isType (token
, TOKEN_COMMA
))
2189 readToken (prop_name
);
2191 if (isType (component
, TOKEN_STRING
) &&
2192 isType (prop_set_name
, TOKEN_STRING
) &&
2193 isType (prop_name
, TOKEN_STRING
) )
2195 addToScope(component
, prop_set_name
->string
);
2196 addToScope(component
, prop_name
->string
);
2197 makeSqlTag (component
, SQLTAG_MLPROP
);
2200 if( !isType (token
, TOKEN_CLOSE_PAREN
) )
2201 findToken (token
, TOKEN_CLOSE_PAREN
);
2205 findCmdTerm (token
, TRUE
);
2207 deleteToken (component
);
2208 deleteToken (prop_set_name
);
2209 deleteToken (prop_name
);
2212 static void parseComment (tokenInfo
*const token
)
2215 * This deals with this statement:
2216 * COMMENT TO PRESERVE FORMAT ON PROCEDURE "DBA"."test" IS
2217 * {create PROCEDURE DBA."test"()
2223 * The comment can contain anything between the CURLY
2225 * COMMENT ON USER "admin" IS
2226 * 'Administration Group'
2228 * Or it could be a simple string with no curly braces
2230 while (! isKeyword (token
, KEYWORD_is
))
2235 if ( isType(token
, TOKEN_OPEN_CURLY
) )
2237 findToken (token
, TOKEN_CLOSE_CURLY
);
2240 findCmdTerm (token
, TRUE
);
2244 static void parseKeywords (tokenInfo
*const token
)
2246 switch (token
->keyword
)
2248 case KEYWORD_begin
: parseBlock (token
, FALSE
); break;
2249 case KEYWORD_comment
: parseComment (token
); break;
2250 case KEYWORD_cursor
: parseSimple (token
, SQLTAG_CURSOR
); break;
2251 case KEYWORD_datatype
: parseDomain (token
); break;
2252 case KEYWORD_declare
: parseBlock (token
, FALSE
); break;
2253 case KEYWORD_domain
: parseDomain (token
); break;
2254 case KEYWORD_drop
: parseDrop (token
); break;
2255 case KEYWORD_event
: parseEvent (token
); break;
2256 case KEYWORD_function
: parseSubProgram (token
); break;
2257 case KEYWORD_if
: parseStatements (token
, FALSE
); break;
2258 case KEYWORD_index
: parseIndex (token
); break;
2259 case KEYWORD_ml_table
: parseMLTable (token
); break;
2260 case KEYWORD_ml_table_lang
: parseMLTable (token
); break;
2261 case KEYWORD_ml_table_dnet
: parseMLTable (token
); break;
2262 case KEYWORD_ml_table_java
: parseMLTable (token
); break;
2263 case KEYWORD_ml_table_chk
: parseMLTable (token
); break;
2264 case KEYWORD_ml_conn
: parseMLConn (token
); break;
2265 case KEYWORD_ml_conn_lang
: parseMLConn (token
); break;
2266 case KEYWORD_ml_conn_dnet
: parseMLConn (token
); break;
2267 case KEYWORD_ml_conn_java
: parseMLConn (token
); break;
2268 case KEYWORD_ml_conn_chk
: parseMLConn (token
); break;
2269 case KEYWORD_ml_prop
: parseMLProp (token
); break;
2270 case KEYWORD_package
: parsePackage (token
); break;
2271 case KEYWORD_procedure
: parseSubProgram (token
); break;
2272 case KEYWORD_publication
: parsePublication (token
); break;
2273 case KEYWORD_service
: parseService (token
); break;
2274 case KEYWORD_subtype
: parseSimple (token
, SQLTAG_SUBTYPE
); break;
2275 case KEYWORD_synonym
: parseSynonym (token
); break;
2276 case KEYWORD_table
: parseTable (token
); break;
2277 case KEYWORD_trigger
: parseTrigger (token
); break;
2278 case KEYWORD_type
: parseType (token
); break;
2279 case KEYWORD_variable
: parseVariable (token
); break;
2280 case KEYWORD_view
: parseView (token
); break;
2285 static void parseSqlFile (tokenInfo
*const token
)
2291 if (isType (token
, TOKEN_BLOCK_LABEL_BEGIN
))
2294 parseKeywords (token
);
2295 } while (! isKeyword (token
, KEYWORD_end
));
2298 static void initialize (const langType language
)
2300 Assert (sizeof (SqlKinds
) / sizeof (SqlKinds
[0]) == SQLTAG_COUNT
);
2301 Lang_sql
= language
;
2302 buildSqlKeywordHash ();
2305 static void findSqlTags (void)
2307 tokenInfo
*const token
= newToken ();
2308 exception_t exception
= (exception_t
) (setjmp (Exception
));
2310 while (exception
== ExceptionNone
)
2311 parseSqlFile (token
);
2313 deleteToken (token
);
2316 extern parserDefinition
* SqlParser (void)
2318 static const char *const extensions
[] = { "sql", NULL
};
2319 parserDefinition
* def
= parserNew ("SQL");
2320 def
->kinds
= SqlKinds
;
2321 def
->kindCount
= KIND_COUNT (SqlKinds
);
2322 def
->extensions
= extensions
;
2323 def
->parser
= findSqlTags
;
2324 def
->initialize
= initialize
;
2328 /* vi:set tabstop=4 shiftwidth=4 noexpandtab: */