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 () */
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
,
134 * Used to determine whether keyword is valid for the token language and
137 typedef struct sKeywordDesc
{
142 typedef enum eTokenType
{
144 TOKEN_BLOCK_LABEL_BEGIN
,
145 TOKEN_BLOCK_LABEL_END
,
167 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
, '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
},
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
->scopeKind
= SQLTAG_COUNT
;
429 token
->begin_end_nest_lvl
= 0;
430 token
->lineNumber
= getSourceLineNumber ();
431 token
->filePosition
= getInputFilePosition ();
436 static void deleteToken (tokenInfo
*const token
)
438 vStringDelete (token
->string
);
439 vStringDelete (token
->scope
);
443 static int analyzeToken (vString
*const name
, langType language
)
445 vString
*keyword
= vStringNew ();
447 vStringCopyToLower (keyword
, name
);
448 result
= lookupKeyword (vStringValue (keyword
), language
);
449 vStringDelete (keyword
);
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
);
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
);
485 static void parseString (vString
*const string
, const int delimiter
)
496 c = fileGetc(); // This maybe a ' or ". //
497 vStringPut(string, c);
500 else if (c
== delimiter
)
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
)
513 Assert (isIdentChar1 (c
));
516 vStringPut (string
, c
);
518 } while (isIdentChar (c
));
519 vStringTerminate (string
);
521 fileUngetc (c
); /* unget non-identifier character */
524 static void readToken (tokenInfo
*const token
)
528 token
->type
= TOKEN_UNDEFINED
;
529 token
->keyword
= KEYWORD_NONE
;
530 vStringClear (token
->string
);
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');
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;
568 token
->type
= TOKEN_STRING
;
569 parseString (token
->string
, c
);
570 token
->lineNumber
= getSourceLineNumber ();
571 token
->filePosition
= getInputFilePosition ();
576 if (c
== '-') /* -- is this the start of a comment? */
578 fileSkipToCharacter ('\n');
585 token
->type
= TOKEN_OPERATOR
;
592 const int initial
= c
;
597 token
->type
= TOKEN_BLOCK_LABEL_BEGIN
;
599 token
->type
= TOKEN_BLOCK_LABEL_END
;
604 token
->type
= TOKEN_UNDEFINED
;
611 if (c
!= '\\' && c
!= '"' && c
!= '\'' && !isspace (c
))
613 token
->type
= TOKEN_CHARACTER
;
614 token
->lineNumber
= getSourceLineNumber ();
615 token
->filePosition
= getInputFilePosition ();
621 if ( (d
!= '*') && /* is this the start of a comment? */
622 (d
!= '/') ) /* is a one line comment? */
624 token
->type
= TOKEN_FORWARD_SLASH
;
633 fileSkipToCharacter ('*');
639 } while (c
!= EOF
&& c
!= '\0');
642 else if (d
== '/') /* is this the start of a comment? */
644 fileSkipToCharacter ('\n');
652 if (! isIdentChar1 (c
))
653 token
->type
= TOKEN_UNDEFINED
;
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');
666 else if (isKeyword (token
, KEYWORD_NONE
))
667 token
->type
= TOKEN_IDENTIFIER
;
669 token
->type
= TOKEN_KEYWORD
;
676 * reads an indentifier, possibly quoted:
681 static void readIdentifier (tokenInfo
*const token
)
684 if (isType (token
, TOKEN_OPEN_SQUARE
))
686 tokenInfo
*const close_square
= newToken ();
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
;
726 static void findToken (tokenInfo
*const token
, const tokenType type
)
728 while (! isType (token
, type
))
734 static void findCmdTerm (tokenInfo
*const token
, const boolean check_first
)
736 int begin_end_nest_lvl
= token
->begin_end_nest_lvl
;
740 if ( isCmdTerm(token
) )
746 } while ( !isCmdTerm(token
) && !isMatchedEnd(token
, begin_end_nest_lvl
) );
749 static void skipToMatched(tokenInfo
*const token
)
752 tokenType open_token
;
753 tokenType close_token
;
757 case TOKEN_OPEN_PAREN
:
758 open_token
= TOKEN_OPEN_PAREN
;
759 close_token
= TOKEN_CLOSE_PAREN
;
761 case TOKEN_OPEN_CURLY
:
762 open_token
= TOKEN_OPEN_CURLY
;
763 close_token
= TOKEN_CLOSE_CURLY
;
765 case TOKEN_OPEN_SQUARE
:
766 open_token
= TOKEN_OPEN_SQUARE
;
767 close_token
= TOKEN_CLOSE_SQUARE
;
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
))
782 while (! (isType (token
, close_token
) && (nest_level
== 0)))
785 if (isType (token
, open_token
))
789 if (isType (token
, close_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
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
838 * FUNCTION func_name RETURN integer;
839 * PROCEDURE proc_name( parameters );
841 * FUNCTION GET_ML_USERNAME RETURN VARCHAR2
844 * RETURN v_sync_user_id;
845 * END GET_ML_USERNAME;
847 * PROCEDURE proc_name( parameters )
851 * CREATE PROCEDURE proc_name( parameters )
852 * EXTERNAL NAME ... ;
853 * CREATE PROCEDURE proc_name( parameters )
857 * CREATE FUNCTION f_GetClassName(
858 * IN @object VARCHAR(128)
859 * ,IN @code VARCHAR(128)
861 * RETURNS VARCHAR(200)
865 * IF( @object = 'user_state' ) THEN
866 * SET something = something;
872 * Note, a Package adds scope to the items within.
873 * create or replace package demo_pkg is
875 * function test_func return varchar2;
876 * function more.test_func2 return varchar2;
878 * So the tags generated here, contain the package name:
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
;
891 copyToken (name
, 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
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
);
909 copyToken (name
, 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
))
925 * Read token after which could be the
926 * command terminator if a prototype
927 * or an open parantheses
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
);
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
) ||
954 if ( isKeyword (token
, KEYWORD_result
) )
957 if (isType (token
, TOKEN_OPEN_PAREN
))
959 /* Reads to the next token after the TOKEN_CLOSE_PAREN */
960 skipArgumentList(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
) )
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
;
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
))
1017 Assert (isType (token
, TOKEN_OPEN_PAREN
));
1020 if ( isType (token
, TOKEN_COMMA
) || isType (token
, TOKEN_OPEN_PAREN
) )
1024 * Create table statements can end with various constraints
1025 * which must be excluded from the SQLTAG_FIELD.
1031 * constraint whatever,
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
)
1056 * A table structure can look like this:
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
;
1088 if (isType (name
, TOKEN_IDENTIFIER
))
1091 if (isKeyword (token
, KEYWORD_is
))
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
);
1104 makeSqlTag (name
, SQLTAG_TABLE
);
1109 if (isKeyword (token
, KEYWORD_cursor
))
1110 makeSqlTag (name
, SQLTAG_CURSOR
);
1115 vStringClear (token
->scope
);
1116 token
->scopeKind
= SQLTAG_COUNT
;
1119 vStringCopy(token
->scope
, saveScope
);
1120 token
->scopeKind
= saveScopeKind
;
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 */
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:
1140 * CURSOR curname ...
1141 * varname1 datatype;
1142 * varname2 datatype;
1143 * varname3 datatype;
1147 if (isKeyword (token
, KEYWORD_declare
))
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;
1161 if (isType (token
, TOKEN_IDENTIFIER
))
1165 makeSqlTag (token
, SQLTAG_LOCAL_VARIABLE
);
1169 makeSqlTag (token
, SQLTAG_VARIABLE
);
1174 findToken (token
, TOKEN_SEMICOLON
);
1179 static void parseDeclareANSI (tokenInfo
*const token
, const boolean local
)
1181 tokenInfo
*const type
= newToken ();
1183 * ANSI declares are of this format:
1185 * DECLARE varname1 datatype;
1186 * DECLARE varname2 datatype;
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
))
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 (
1209 if (isKeyword (token
, KEYWORD_table
))
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
))
1223 makeSqlTag (token
, SQLTAG_LOCAL_VARIABLE
);
1225 makeSqlTag (token
, SQLTAG_VARIABLE
);
1227 findToken (token
, TOKEN_SEMICOLON
);
1233 static void parseLabel (tokenInfo
*const token
)
1236 * A label has this format:
1237 * <<tobacco_dependency>>
1239 * v_senator VARCHAR2(100) := 'THURMOND, JESSE';
1241 * IF total_contributions (v_senator, 'TOBACCO') > 25000
1243 * <<alochol_dependency>>
1245 * v_senator VARCHAR2(100) := 'WHATEVERIT, TAKES';
1250 Assert (isType (token
, TOKEN_BLOCK_LABEL_BEGIN
));
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
))
1269 switch (token
->keyword
)
1271 case KEYWORD_exception
:
1274 * <exception handler>;
1276 * Where an exception handler could be:
1281 * In this case we need to skip this keyword and
1282 * move on to the next token without reading until
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:
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
))
1311 * We do not want to look for a ; since for an empty
1312 * IF block, it would skip over the END.
1330 while ( ! isKeyword (token
, KEYWORD_then
) &&
1331 ! isKeyword (token
, KEYWORD_begin
) )
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
) )
1353 while( ! (isKeyword (token
, KEYWORD_end
) ||
1354 isKeyword (token
, KEYWORD_endif
) )
1357 if ( isKeyword (token
, KEYWORD_else
) ||
1358 isKeyword (token
, KEYWORD_elseif
) )
1361 parseStatements (token
, TRUE
);
1363 if ( isCmdTerm(token
) )
1369 * parseStatements returns when it finds an END, an IF
1370 * should follow the END for ANSI anyway.
1374 if( isKeyword (token
, KEYWORD_end
) )
1377 if( isKeyword (token
, KEYWORD_if
) || isKeyword (token
, KEYWORD_endif
) )
1380 if ( isCmdTerm(token
) )
1386 * Well we need to do something here.
1387 * There are lots of different END statements
1408 * FOR loop_name AS cursor_name CURSOR FOR ...
1412 if( isKeyword (token
, KEYWORD_for
) )
1419 while ( ! isKeyword (token
, KEYWORD_is
) )
1422 * If this is not an AS keyword this is
1423 * not a proper FOR statement and should
1429 while ( ! isKeyword (token
, KEYWORD_do
) )
1435 while( ! isKeyword (token
, KEYWORD_end
) )
1438 if ( isKeyword (token, KEYWORD_else) ||
1439 isKeyword (token, KEYWORD_elseif) )
1443 parseStatements (token
, FALSE
);
1445 if ( isCmdTerm(token
) )
1450 if( isKeyword (token
, KEYWORD_end
) )
1454 * Typically ended with
1455 * END LOOP [loop name];
1457 * END FOR [loop name];
1459 if ( isKeyword (token
, KEYWORD_loop
) ||
1460 isKeyword (token
, KEYWORD_case
) ||
1461 isKeyword (token
, KEYWORD_for
) )
1464 if ( isCmdTerm(token
) )
1469 case KEYWORD_create
:
1471 parseKeywords(token
);
1474 case KEYWORD_declare
:
1476 parseBlock (token
, TRUE
);
1487 * Not all statements must end in a semi-colon
1489 * if current publisher <> 'publish' then
1490 * signal UE_FailStatement
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
) &&
1508 if (isType (token
, TOKEN_COLON
) )
1511 * A : can signal a loop name
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
1522 if ( isKeyword (token
, KEYWORD_loop
) ||
1523 isKeyword (token
, KEYWORD_while
) ||
1524 isKeyword (token
, KEYWORD_for
) )
1525 /* parseStatements (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
)
1560 } while (! isKeyword (token
, KEYWORD_end
) &&
1561 ! (exit_on_endif
&& isKeyword (token
, KEYWORD_endif
) ) &&
1565 static void parseBlock (tokenInfo
*const token
, const boolean local
)
1567 if (isType (token
, TOKEN_BLOCK_LABEL_BEGIN
))
1572 if (! isKeyword (token
, KEYWORD_begin
))
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
))
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
) )
1598 token
->begin_end_nest_lvl
--;
1601 * Read the next token (we will assume
1602 * it is the command delimiter)
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
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
))
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
);
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
);
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:
1703 readIdentifier (name
);
1704 /* Check if a blank name was provided */
1705 if (isType (name
, TOKEN_PERIOD
))
1707 readIdentifier (name
);
1710 if (isType (token
, TOKEN_PERIOD
))
1712 /* This can only be the table name */
1713 readIdentifier (name
);
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
);
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
);
1757 if (isType (token
, TOKEN_PERIOD
))
1759 readIdentifier (name
);
1762 if ( isKeyword (token
, KEYWORD_on
) &&
1763 (isType (name
, TOKEN_IDENTIFIER
) || isType (name
, TOKEN_STRING
) ) )
1765 readIdentifier (owner
);
1767 if (isType (token
, TOKEN_PERIOD
))
1769 readIdentifier (owner
);
1772 addToScope(name
, owner
->string
, SQLTAG_TABLE
/* FIXME? */);
1773 makeSqlTag (name
, SQLTAG_INDEX
);
1775 findCmdTerm (token
, FALSE
);
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
);
1794 if (isType (token
, TOKEN_PERIOD
))
1796 readIdentifier (name
);
1798 while (! (isKeyword (token
, KEYWORD_handler
) ||
1799 (isType (token
, TOKEN_SEMICOLON
))) )
1804 if ( isKeyword (token
, KEYWORD_handler
) ||
1805 isType (token
, TOKEN_SEMICOLON
) )
1807 makeSqlTag (name
, SQLTAG_EVENT
);
1810 if (isKeyword (token
, KEYWORD_handler
))
1813 if ( isKeyword (token
, KEYWORD_begin
) )
1815 parseBlock (token
, TRUE
);
1817 findCmdTerm (token
, TRUE
);
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
);
1840 if (isType (token
, TOKEN_PERIOD
))
1842 readIdentifier (name
);
1846 while ( !isKeyword (token
, KEYWORD_on
) &&
1847 !isCmdTerm (token
) )
1852 /*if (! isType (token, TOKEN_SEMICOLON) ) */
1853 if (! isCmdTerm (token
) )
1857 if (isType (token
, TOKEN_PERIOD
))
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
;
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
);
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
);
1912 if (isType (token
, TOKEN_PERIOD
))
1914 readIdentifier (name
);
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
);
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
1938 * FROM SYS.SYSTABLE;
1939 * CREATE SERVICE "s2" TYPE 'HTML'
1940 * AUTHORIZATION OFF USER DBA AS
1941 * CALL sp_Something();
1944 readIdentifier (name
);
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
);
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
);
1973 if (isType (name
, TOKEN_IDENTIFIER
) ||
1974 isType (name
, TOKEN_STRING
))
1976 makeSqlTag (name
, SQLTAG_DOMAIN
);
1978 findCmdTerm (token
, FALSE
);
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
);
2011 if ( (isType (name
, TOKEN_IDENTIFIER
) || isType (name
, TOKEN_STRING
))
2012 && !isType (token
, TOKEN_SEMICOLON
) )
2014 makeSqlTag (name
, SQLTAG_VARIABLE
);
2016 findCmdTerm (token
, TRUE
);
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
);
2035 if ( (isType (name
, TOKEN_IDENTIFIER
) || isType (name
, TOKEN_STRING
))
2036 && isKeyword (token
, KEYWORD_for
) )
2038 makeSqlTag (name
, SQLTAG_SYNONYM
);
2040 findCmdTerm (token
, TRUE
);
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
);
2059 if (isType (token
, TOKEN_PERIOD
))
2061 readIdentifier (name
);
2064 if ( isType (token
, TOKEN_OPEN_PAREN
) )
2066 skipArgumentList(token
);
2070 while (!(isKeyword (token
, KEYWORD_is
) ||
2071 isType (token
, TOKEN_SEMICOLON
)
2077 if ( (isType (name
, TOKEN_IDENTIFIER
) || isType (name
, TOKEN_STRING
))
2078 && isKeyword (token
, KEYWORD_is
) )
2080 makeSqlTag (name
, SQLTAG_VIEW
);
2083 findCmdTerm (token
, TRUE
);
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'
2102 if ( isType (token
, TOKEN_OPEN_PAREN
) )
2104 readToken (version
);
2106 while (!(isType (token
, TOKEN_COMMA
) ||
2107 isType (token
, TOKEN_CLOSE_PAREN
)
2113 if (isType (token
, TOKEN_COMMA
))
2117 while (!(isType (token
, TOKEN_COMMA
) ||
2118 isType (token
, TOKEN_CLOSE_PAREN
)
2124 if (isType (token
, TOKEN_COMMA
))
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'
2162 if ( isType (token
, TOKEN_OPEN_PAREN
) )
2164 readToken (version
);
2166 while (!(isType (token
, TOKEN_COMMA
) ||
2167 isType (token
, TOKEN_CLOSE_PAREN
)
2173 if (isType (token
, TOKEN_COMMA
))
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
2212 if ( isType (token
, TOKEN_OPEN_PAREN
) )
2214 readToken (component
);
2216 while (!(isType (token
, TOKEN_COMMA
) ||
2217 isType (token
, TOKEN_CLOSE_PAREN
)
2223 if (isType (token
, TOKEN_COMMA
))
2225 readToken (prop_set_name
);
2227 while (!(isType (token
, TOKEN_COMMA
) ||
2228 isType (token
, TOKEN_CLOSE_PAREN
)
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"()
2270 * The comment can contain anything between the CURLY
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
))
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;
2332 static void parseSqlFile (tokenInfo
*const token
)
2338 if (isType (token
, TOKEN_BLOCK_LABEL_BEGIN
))
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
;
2375 /* vi:set tabstop=4 shiftwidth=4 noexpandtab: */