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 () */
31 * On-line "Oracle Database PL/SQL Language Reference":
32 * http://download.oracle.com/docs/cd/B28359_01/appdev.111/b28370/toc.htm
34 * Sample PL/SQL code is available from:
35 * http://www.orafaq.com/faqscrpt.htm#GENPLSQL
37 * On-line SQL Anywhere Documentation
38 * http://www.ianywhere.com/developer/product_manuals/sqlanywhere/index.html
44 #define isType(token,t) (bool) ((token)->type == (t))
45 #define isKeyword(token,k) (bool) ((token)->keyword == (k))
51 typedef enum eException
{ ExceptionNone
, ExceptionEOF
} exception_t
;
54 * Used to specify type of keyword.
110 KEYWORD_ml_table_lang
,
111 KEYWORD_ml_table_dnet
,
112 KEYWORD_ml_table_java
,
113 KEYWORD_ml_table_chk
,
115 KEYWORD_ml_conn_lang
,
116 KEYWORD_ml_conn_dnet
,
117 KEYWORD_ml_conn_java
,
130 typedef int keywordId
; /* to allow KEYWORD_NONE */
132 typedef enum eTokenType
{
134 TOKEN_BLOCK_LABEL_BEGIN
,
135 TOKEN_BLOCK_LABEL_END
,
157 typedef struct sTokenInfoSQL
{
163 int begin_end_nest_lvl
;
164 unsigned long lineNumber
;
172 static langType Lang_sql
;
174 static jmp_buf Exception
;
181 SQLTAG_LOCAL_VARIABLE
,
203 static kindOption SqlKinds
[] = {
204 { true, 'c', "cursor", "cursors" },
205 { false, 'd', "prototype", "prototypes" },
206 { true, 'f', "function", "functions" },
207 { true, 'F', "field", "record fields" },
208 { false, 'l', "local", "local variables" },
209 { true, 'L', "label", "block label" },
210 { true, 'P', "package", "packages" },
211 { true, 'p', "procedure", "procedures" },
212 { false, 'r', "record", "records" },
213 { true, 's', "subtype", "subtypes" },
214 { true, 't', "table", "tables" },
215 { true, 'T', "trigger", "triggers" },
216 { true, 'v', "variable", "variables" },
217 { true, 'i', "index", "indexes" },
218 { true, 'e', "event", "events" },
219 { true, 'U', "publication", "publications" },
220 { true, 'R', "service", "services" },
221 { true, 'D', "domain", "domains" },
222 { true, 'V', "view", "views" },
223 { true, 'n', "synonym", "synonyms" },
224 { true, 'x', "mltable", "MobiLink Table Scripts" },
225 { true, 'y', "mlconn", "MobiLink Conn Scripts" },
226 { true, 'z', "mlprop", "MobiLink Properties " }
229 static const keywordTable SqlKeywordTable
[] = {
230 /* keyword keyword ID */
231 { "as", KEYWORD_is
},
232 { "is", KEYWORD_is
},
233 { "begin", KEYWORD_begin
},
234 { "body", KEYWORD_body
},
235 { "cursor", KEYWORD_cursor
},
236 { "declare", KEYWORD_declare
},
237 { "end", KEYWORD_end
},
238 { "function", KEYWORD_function
},
239 { "if", KEYWORD_if
},
240 { "else", KEYWORD_else
},
241 { "elseif", KEYWORD_elseif
},
242 { "endif", KEYWORD_endif
},
243 { "loop", KEYWORD_loop
},
244 { "while", KEYWORD_while
},
245 { "case", KEYWORD_case
},
246 { "for", KEYWORD_for
},
247 { "do", KEYWORD_do
},
248 { "call", KEYWORD_call
},
249 { "package", KEYWORD_package
},
250 { "pragma", KEYWORD_pragma
},
251 { "procedure", KEYWORD_procedure
},
252 { "record", KEYWORD_record
},
253 { "object", KEYWORD_object
},
254 { "ref", KEYWORD_ref
},
255 { "rem", KEYWORD_rem
},
256 { "return", KEYWORD_return
},
257 { "returns", KEYWORD_returns
},
258 { "subtype", KEYWORD_subtype
},
259 { "table", KEYWORD_table
},
260 { "trigger", KEYWORD_trigger
},
261 { "type", KEYWORD_type
},
262 { "index", KEYWORD_index
},
263 { "event", KEYWORD_event
},
264 { "publication", KEYWORD_publication
},
265 { "service", KEYWORD_service
},
266 { "domain", KEYWORD_domain
},
267 { "datatype", KEYWORD_datatype
},
268 { "result", KEYWORD_result
},
269 { "url", KEYWORD_url
},
270 { "internal", KEYWORD_internal
},
271 { "external", KEYWORD_external
},
272 { "when", KEYWORD_when
},
273 { "then", KEYWORD_then
},
274 { "variable", KEYWORD_variable
},
275 { "exception", KEYWORD_exception
},
276 { "at", KEYWORD_at
},
277 { "on", KEYWORD_on
},
278 { "primary", KEYWORD_primary
},
279 { "references", KEYWORD_references
},
280 { "unique", KEYWORD_unique
},
281 { "check", KEYWORD_check
},
282 { "constraint", KEYWORD_constraint
},
283 { "foreign", KEYWORD_foreign
},
284 { "ml_add_table_script", KEYWORD_ml_table
},
285 { "ml_add_lang_table_script", KEYWORD_ml_table_lang
},
286 { "ml_add_dnet_table_script", KEYWORD_ml_table_dnet
},
287 { "ml_add_java_table_script", KEYWORD_ml_table_java
},
288 { "ml_add_lang_table_script_chk", KEYWORD_ml_table_chk
},
289 { "ml_add_connection_script", KEYWORD_ml_conn
},
290 { "ml_add_lang_connection_script", KEYWORD_ml_conn_lang
},
291 { "ml_add_dnet_connection_script", KEYWORD_ml_conn_dnet
},
292 { "ml_add_java_connection_script", KEYWORD_ml_conn_java
},
293 { "ml_add_lang_conn_script_chk", KEYWORD_ml_conn_chk
},
294 { "ml_add_property", KEYWORD_ml_prop
},
295 { "local", KEYWORD_local
},
296 { "temporary", KEYWORD_temporary
},
297 { "drop", KEYWORD_drop
},
298 { "view", KEYWORD_view
},
299 { "synonym", KEYWORD_synonym
},
300 { "handler", KEYWORD_handler
},
301 { "comment", KEYWORD_comment
},
302 { "create", KEYWORD_create
},
307 * FUNCTION DECLARATIONS
310 /* Recursive calls */
311 static void parseBlock (tokenInfo
*const token
, const bool local
);
312 static void parseDeclare (tokenInfo
*const token
, const bool local
);
313 static void parseKeywords (tokenInfo
*const token
);
314 static void parseSqlFile (tokenInfo
*const token
);
317 * FUNCTION DEFINITIONS
320 static bool isIdentChar1 (const int c
)
323 * Other databases are less restrictive on the first character of
325 * isIdentChar1 is used to identify the first character of an
326 * identifier, so we are removing some restrictions.
329 (isalpha (c
) || c
== '@' || c
== '_' );
332 static bool isIdentChar (const int c
)
335 (isalpha (c
) || isdigit (c
) || c
== '$' ||
336 c
== '@' || c
== '_' || c
== '#');
339 static bool isCmdTerm (tokenInfo
*const token
)
342 debugPrintf (DEBUG_PARSE
343 , "\n isCmdTerm: token same tt:%d tk:%d\n"
350 * Based on the various customer sites I have been at
351 * the most common command delimiters are
356 * This routine will check for any of these, more
357 * can easily be added by modifying readToken and
358 * either adding the character to:
362 return (isType (token
, TOKEN_SEMICOLON
) ||
363 isType (token
, TOKEN_TILDE
) ||
364 isType (token
, TOKEN_FORWARD_SLASH
) ||
365 isKeyword (token
, KEYWORD_go
));
368 static bool isMatchedEnd(tokenInfo
*const token
, int nest_lvl
)
370 bool terminated
= false;
372 * Since different forms of SQL allow the use of
376 * blocks, some statements may not be terminated using
377 * the standard delimiters:
382 * This routine will check to see if we encounter and END
383 * for the matching nest level of BEGIN ... END statements.
384 * If we find one, then we can assume, the statement was terminated
385 * since we have fallen through to the END statement of the BEGIN
388 if ( nest_lvl
> 0 && isKeyword (token
, KEYWORD_end
) )
390 if ( token
->begin_end_nest_lvl
== nest_lvl
)
397 static tokenInfo
*newToken (void)
399 tokenInfo
*const token
= xMalloc (1, tokenInfo
);
401 token
->type
= TOKEN_UNDEFINED
;
402 token
->keyword
= KEYWORD_NONE
;
403 token
->string
= vStringNew ();
404 token
->scope
= vStringNew ();
405 token
->scopeKind
= SQLTAG_COUNT
;
406 token
->begin_end_nest_lvl
= 0;
407 token
->lineNumber
= getInputLineNumber ();
408 token
->filePosition
= getInputFilePosition ();
413 static void deleteToken (tokenInfo
*const token
)
415 vStringDelete (token
->string
);
416 vStringDelete (token
->scope
);
420 static int analyzeToken (vString
*const name
, langType language
)
422 vString
*keyword
= vStringNew ();
424 vStringCopyToLower (keyword
, name
);
425 result
= lookupKeyword (vStringValue (keyword
), language
);
426 vStringDelete (keyword
);
431 * Tag generation functions
434 static void makeSqlTag (tokenInfo
*const token
, const sqlKind kind
)
436 if (SqlKinds
[kind
].enabled
)
438 const char *const name
= vStringValue (token
->string
);
440 initTagEntry (&e
, name
, &(SqlKinds
[kind
]));
442 e
.lineNumber
= token
->lineNumber
;
443 e
.filePosition
= token
->filePosition
;
445 if (vStringLength (token
->scope
) > 0)
447 Assert (token
->scopeKind
< SQLTAG_COUNT
);
448 e
.extensionFields
.scopeKind
= &(SqlKinds
[token
->scopeKind
]);
449 e
.extensionFields
.scopeName
= vStringValue (token
->scope
);
460 static void parseString (vString
*const string
, const int delimiter
)
465 int c
= getcFromInputFile ();
471 c = getcFromInputFile(); // This maybe a ' or ". //
472 vStringPut(string, c);
475 else if (c
== delimiter
)
478 vStringPut (string
, c
);
482 /* Read a C identifier beginning with "firstChar" and places it into "name".
484 static void parseIdentifier (vString
*const string
, const int firstChar
)
487 Assert (isIdentChar1 (c
));
490 vStringPut (string
, c
);
491 c
= getcFromInputFile ();
492 } while (isIdentChar (c
));
494 ungetcToInputFile (c
); /* unget non-identifier character */
497 static void readToken (tokenInfo
*const token
)
501 token
->type
= TOKEN_UNDEFINED
;
502 token
->keyword
= KEYWORD_NONE
;
503 vStringClear (token
->string
);
508 c
= getcFromInputFile ();
509 token
->lineNumber
= getInputLineNumber ();
510 token
->filePosition
= getInputFilePosition ();
512 * Added " to the list of ignores, not sure what this
513 * might break but it gets by this issue:
514 * create table "t1" (...)
516 * Darren, the code passes all my tests for both
517 * Oracle and SQL Anywhere, but maybe you can tell me
518 * what this may effect.
521 while (c
== '\t' || c
== ' ' || c
== '\n');
525 case EOF
: longjmp (Exception
, (int)ExceptionEOF
); break;
526 case '(': token
->type
= TOKEN_OPEN_PAREN
; break;
527 case ')': token
->type
= TOKEN_CLOSE_PAREN
; break;
528 case ':': token
->type
= TOKEN_COLON
; break;
529 case ';': token
->type
= TOKEN_SEMICOLON
; break;
530 case '.': token
->type
= TOKEN_PERIOD
; break;
531 case ',': token
->type
= TOKEN_COMMA
; break;
532 case '{': token
->type
= TOKEN_OPEN_CURLY
; break;
533 case '}': token
->type
= TOKEN_CLOSE_CURLY
; break;
534 case '~': token
->type
= TOKEN_TILDE
; break;
535 case '[': token
->type
= TOKEN_OPEN_SQUARE
; break;
536 case ']': token
->type
= TOKEN_CLOSE_SQUARE
; break;
537 case '=': token
->type
= TOKEN_EQUAL
; break;
541 token
->type
= TOKEN_STRING
;
542 parseString (token
->string
, c
);
543 token
->lineNumber
= getInputLineNumber ();
544 token
->filePosition
= getInputFilePosition ();
548 c
= getcFromInputFile ();
549 if (c
== '-') /* -- is this the start of a comment? */
551 skipToCharacterInInputFile ('\n');
557 ungetcToInputFile (c
);
558 token
->type
= TOKEN_OPERATOR
;
565 const int initial
= c
;
566 int d
= getcFromInputFile ();
570 token
->type
= TOKEN_BLOCK_LABEL_BEGIN
;
572 token
->type
= TOKEN_BLOCK_LABEL_END
;
576 ungetcToInputFile (d
);
577 token
->type
= TOKEN_UNDEFINED
;
583 c
= getcFromInputFile ();
584 if (c
!= '\\' && c
!= '"' && c
!= '\'' && !isspace (c
))
585 ungetcToInputFile (c
);
586 token
->type
= TOKEN_CHARACTER
;
587 token
->lineNumber
= getInputLineNumber ();
588 token
->filePosition
= getInputFilePosition ();
593 int d
= getcFromInputFile ();
594 if ((d
!= '*') && /* is this the start of a comment? */
595 (d
!= '/')) /* is a one line comment? */
597 token
->type
= TOKEN_FORWARD_SLASH
;
598 ungetcToInputFile (d
);
606 skipToCharacterInInputFile ('*');
607 c
= getcFromInputFile ();
611 ungetcToInputFile (c
);
612 } while (c
!= EOF
&& c
!= '\0');
615 else if (d
== '/') /* is this the start of a comment? */
617 skipToCharacterInInputFile ('\n');
625 if (! isIdentChar1 (c
))
626 token
->type
= TOKEN_UNDEFINED
;
629 parseIdentifier (token
->string
, c
);
630 token
->lineNumber
= getInputLineNumber ();
631 token
->filePosition
= getInputFilePosition ();
632 token
->keyword
= analyzeToken (token
->string
, Lang_sql
);
633 if (isKeyword (token
, KEYWORD_rem
))
635 vStringClear (token
->string
);
636 skipToCharacterInInputFile ('\n');
639 else if (isKeyword (token
, KEYWORD_NONE
))
640 token
->type
= TOKEN_IDENTIFIER
;
642 token
->type
= TOKEN_KEYWORD
;
649 * reads an identifier, possibly quoted:
654 static void readIdentifier (tokenInfo
*const token
)
657 if (isType (token
, TOKEN_OPEN_SQUARE
))
659 tokenInfo
*const close_square
= newToken ();
662 /* eat close square */
663 readToken (close_square
);
664 deleteToken (close_square
);
669 * Token parsing functions
673 * static void addContext (tokenInfo* const parent, const tokenInfo* const child)
675 * if (vStringLength (parent->string) > 0)
677 * vStringCatS (parent->string, ".");
679 * vStringCatS (parent->string, vStringValue(child->string));
683 static void addToScope (tokenInfo
* const token
, vString
* const extra
, sqlKind kind
)
685 if (vStringLength (token
->scope
) > 0)
687 vStringCatS (token
->scope
, ".");
689 vStringCatS (token
->scope
, vStringValue(extra
));
690 token
->scopeKind
= kind
;
697 static void findToken (tokenInfo
*const token
, const tokenType type
)
699 while (! isType (token
, type
))
705 static void findCmdTerm (tokenInfo
*const token
, const bool check_first
)
707 int begin_end_nest_lvl
= token
->begin_end_nest_lvl
;
711 if (isCmdTerm(token
))
717 } while ( !isCmdTerm(token
) && !isMatchedEnd(token
, begin_end_nest_lvl
) );
720 static void skipToMatched(tokenInfo
*const token
)
723 tokenType open_token
;
724 tokenType close_token
;
728 case TOKEN_OPEN_PAREN
:
729 open_token
= TOKEN_OPEN_PAREN
;
730 close_token
= TOKEN_CLOSE_PAREN
;
732 case TOKEN_OPEN_CURLY
:
733 open_token
= TOKEN_OPEN_CURLY
;
734 close_token
= TOKEN_CLOSE_CURLY
;
736 case TOKEN_OPEN_SQUARE
:
737 open_token
= TOKEN_OPEN_SQUARE
;
738 close_token
= TOKEN_CLOSE_SQUARE
;
745 * This routine will skip to a matching closing token.
746 * It will also handle nested tokens like the (, ) below.
747 * ( name varchar(30), text binary(10) )
750 if (isType (token
, open_token
))
753 while (! (isType (token
, close_token
) && (nest_level
== 0)))
756 if (isType (token
, open_token
))
760 if (isType (token
, close_token
))
772 static void copyToken (tokenInfo
*const dest
, tokenInfo
*const src
)
774 dest
->lineNumber
= src
->lineNumber
;
775 dest
->filePosition
= src
->filePosition
;
776 dest
->type
= src
->type
;
777 dest
->keyword
= src
->keyword
;
778 vStringCopy(dest
->string
, src
->string
);
779 vStringCopy(dest
->scope
, src
->scope
);
780 dest
->scopeKind
= src
->scopeKind
;
783 static void skipArgumentList (tokenInfo
*const token
)
786 * Other databases can have arguments with fully declared
788 * ( name varchar(30), text binary(10) )
789 * So we must check for nested open and closing parentheses
792 if (isType (token
, TOKEN_OPEN_PAREN
)) /* arguments? */
794 skipToMatched (token
);
798 static void parseSubProgram (tokenInfo
*const token
)
800 tokenInfo
*const name
= newToken ();
801 vString
* saveScope
= vStringNew ();
802 sqlKind saveScopeKind
;
805 * This must handle both prototypes and the body of
809 * FUNCTION func_name RETURN integer;
810 * PROCEDURE proc_name( parameters );
812 * FUNCTION GET_ML_USERNAME RETURN VARCHAR2
815 * RETURN v_sync_user_id;
816 * END GET_ML_USERNAME;
818 * PROCEDURE proc_name( parameters )
822 * CREATE PROCEDURE proc_name( parameters )
823 * EXTERNAL NAME ... ;
824 * CREATE PROCEDURE proc_name( parameters )
828 * CREATE FUNCTION f_GetClassName(
829 * IN @object VARCHAR(128)
830 * ,IN @code VARCHAR(128)
832 * RETURNS VARCHAR(200)
836 * IF( @object = 'user_state' ) THEN
837 * SET something = something;
843 * Note, a Package adds scope to the items within.
844 * create or replace package demo_pkg is
846 * function test_func return varchar2;
847 * function more.test_func2 return varchar2;
849 * So the tags generated here, contain the package name:
852 * demo_pkg.more.test_func2
854 const sqlKind kind
= isKeyword (token
, KEYWORD_function
) ?
855 SQLTAG_FUNCTION
: SQLTAG_PROCEDURE
;
856 Assert (isKeyword (token
, KEYWORD_function
) ||
857 isKeyword (token
, KEYWORD_procedure
));
859 vStringCopy(saveScope
, token
->scope
);
860 saveScopeKind
= token
->scopeKind
;
862 copyToken (name
, token
);
865 if (isType (token
, TOKEN_PERIOD
))
868 * If this is an Oracle package, then the token->scope should
869 * already be set. If this is the case, also add this value to the
871 * If this is not an Oracle package, chances are the scope should be
872 * blank and the value just read is the OWNER or CREATOR of the
873 * function and should not be considered part of the scope.
875 if (vStringLength(saveScope
) > 0)
877 addToScope(token
, name
->string
, kind
);
880 copyToken (name
, token
);
883 if (isType (token
, TOKEN_OPEN_PAREN
))
885 /* Reads to the next token after the TOKEN_CLOSE_PAREN */
886 skipArgumentList(token
);
889 if (kind
== SQLTAG_FUNCTION
)
891 if (isKeyword (token
, KEYWORD_return
) ||
892 isKeyword (token
, KEYWORD_returns
))
897 * Read token after which could be the
898 * command terminator if a prototype
899 * or an open parenthesis
902 if (isType (token
, TOKEN_OPEN_PAREN
))
904 /* Reads to the next token after the TOKEN_CLOSE_PAREN */
905 skipArgumentList(token
);
909 if (isCmdTerm (token
))
911 makeSqlTag (name
, SQLTAG_PROTOTYPE
);
915 while (!(isKeyword (token
, KEYWORD_is
) ||
916 isKeyword (token
, KEYWORD_begin
) ||
917 isKeyword (token
, KEYWORD_at
) ||
918 isKeyword (token
, KEYWORD_internal
) ||
919 isKeyword (token
, KEYWORD_external
) ||
920 isKeyword (token
, KEYWORD_url
) ||
921 isType (token
, TOKEN_EQUAL
) ||
926 if (isKeyword (token
, KEYWORD_result
))
929 if (isType (token
, TOKEN_OPEN_PAREN
))
931 /* Reads to the next token after the TOKEN_CLOSE_PAREN */
932 skipArgumentList(token
);
938 if (isKeyword (token
, KEYWORD_at
) ||
939 isKeyword (token
, KEYWORD_url
) ||
940 isKeyword (token
, KEYWORD_internal
) ||
941 isKeyword (token
, KEYWORD_external
))
943 addToScope(token
, name
->string
, kind
);
944 if (isType (name
, TOKEN_IDENTIFIER
) ||
945 isType (name
, TOKEN_STRING
) ||
946 !isKeyword (token
, KEYWORD_NONE
)
948 makeSqlTag (name
, kind
);
950 vStringClear (token
->scope
);
951 token
->scopeKind
= SQLTAG_COUNT
;
953 if (isType (token
, TOKEN_EQUAL
))
956 if (isKeyword (token
, KEYWORD_declare
))
957 parseDeclare (token
, false);
959 if (isKeyword (token
, KEYWORD_is
) ||
960 isKeyword (token
, KEYWORD_begin
))
962 addToScope(token
, name
->string
, kind
);
963 if (isType (name
, TOKEN_IDENTIFIER
) ||
964 isType (name
, TOKEN_STRING
) ||
965 !isKeyword (token
, KEYWORD_NONE
)
967 makeSqlTag (name
, kind
);
969 parseBlock (token
, true);
970 vStringClear (token
->scope
);
971 token
->scopeKind
= SQLTAG_COUNT
;
974 vStringCopy(token
->scope
, saveScope
);
975 token
->scopeKind
= saveScopeKind
;
977 vStringDelete(saveScope
);
980 static void parseRecord (tokenInfo
*const token
)
983 * Make it a bit forgiving, this is called from
984 * multiple functions, parseTable, parseType
986 if (!isType (token
, TOKEN_OPEN_PAREN
))
989 Assert (isType (token
, TOKEN_OPEN_PAREN
));
992 if ( isType (token
, TOKEN_COMMA
) || isType (token
, TOKEN_OPEN_PAREN
) )
996 * Create table statements can end with various constraints
997 * which must be excluded from the SQLTAG_FIELD.
1003 * constraint whatever,
1009 if (! (isKeyword(token
, KEYWORD_primary
) ||
1010 isKeyword(token
, KEYWORD_references
) ||
1011 isKeyword(token
, KEYWORD_unique
) ||
1012 isKeyword(token
, KEYWORD_check
) ||
1013 isKeyword(token
, KEYWORD_constraint
) ||
1014 isKeyword(token
, KEYWORD_foreign
) ) )
1016 /* keyword test above is redundant as only a TOKEN_KEYWORD could
1017 * match any isKeyword() anyway */
1018 if (isType (token
, TOKEN_IDENTIFIER
) ||
1019 isType (token
, TOKEN_STRING
))
1020 makeSqlTag (token
, SQLTAG_FIELD
);
1023 while (!(isType (token
, TOKEN_COMMA
) ||
1024 isType (token
, TOKEN_CLOSE_PAREN
) ||
1025 isType (token
, TOKEN_OPEN_PAREN
)
1030 * A table structure can look like this:
1037 * We can't just look for a COMMA or CLOSE_PAREN
1038 * since that will not deal with the numeric(10,5)
1039 * case. So we need to skip the argument list
1040 * when we find an open paren.
1042 if (isType (token
, TOKEN_OPEN_PAREN
))
1044 /* Reads to the next token after the TOKEN_CLOSE_PAREN */
1045 skipArgumentList(token
);
1048 } while (! isType (token
, TOKEN_CLOSE_PAREN
));
1051 static void parseType (tokenInfo
*const token
)
1053 tokenInfo
*const name
= newToken ();
1054 vString
* saveScope
= vStringNew ();
1055 sqlKind saveScopeKind
;
1057 vStringCopy(saveScope
, token
->scope
);
1058 /* If a scope has been set, add it to the name */
1059 addToScope (name
, token
->scope
, token
->scopeKind
);
1060 saveScopeKind
= token
->scopeKind
;
1062 if (isType (name
, TOKEN_IDENTIFIER
))
1065 if (isKeyword (token
, KEYWORD_is
))
1068 switch (token
->keyword
)
1070 case KEYWORD_record
:
1071 case KEYWORD_object
:
1072 makeSqlTag (name
, SQLTAG_RECORD
);
1073 addToScope (token
, name
->string
, SQLTAG_RECORD
);
1074 parseRecord (token
);
1078 makeSqlTag (name
, SQLTAG_TABLE
);
1083 if (isKeyword (token
, KEYWORD_cursor
))
1084 makeSqlTag (name
, SQLTAG_CURSOR
);
1089 vStringClear (token
->scope
);
1090 token
->scopeKind
= SQLTAG_COUNT
;
1093 vStringCopy(token
->scope
, saveScope
);
1094 token
->scopeKind
= saveScopeKind
;
1096 vStringDelete(saveScope
);
1099 static void parseSimple (tokenInfo
*const token
, const sqlKind kind
)
1101 /* This will simply make the tagname from the first word found */
1103 if (isType (token
, TOKEN_IDENTIFIER
) ||
1104 isType (token
, TOKEN_STRING
))
1105 makeSqlTag (token
, kind
);
1108 static void parseDeclare (tokenInfo
*const token
, const bool local
)
1111 * PL/SQL declares are of this format:
1114 * CURSOR curname ...
1115 * varname1 datatype;
1116 * varname2 datatype;
1117 * varname3 datatype;
1121 if (isKeyword (token
, KEYWORD_declare
))
1123 while (! isKeyword (token
, KEYWORD_begin
) && ! isKeyword (token
, KEYWORD_end
))
1125 switch (token
->keyword
)
1127 case KEYWORD_cursor
: parseSimple (token
, SQLTAG_CURSOR
); break;
1128 case KEYWORD_function
: parseSubProgram (token
); break;
1129 case KEYWORD_procedure
: parseSubProgram (token
); break;
1130 case KEYWORD_subtype
: parseSimple (token
, SQLTAG_SUBTYPE
); break;
1131 case KEYWORD_trigger
: parseSimple (token
, SQLTAG_TRIGGER
); break;
1132 case KEYWORD_type
: parseType (token
); break;
1135 if (isType (token
, TOKEN_IDENTIFIER
))
1139 makeSqlTag (token
, SQLTAG_LOCAL_VARIABLE
);
1143 makeSqlTag (token
, SQLTAG_VARIABLE
);
1148 findToken (token
, TOKEN_SEMICOLON
);
1153 static void parseDeclareANSI (tokenInfo
*const token
, const bool local
)
1155 tokenInfo
*const type
= newToken ();
1157 * ANSI declares are of this format:
1159 * DECLARE varname1 datatype;
1160 * DECLARE varname2 datatype;
1163 * This differ from PL/SQL where DECLARE precedes the BEGIN block
1164 * and the DECLARE keyword is not repeated.
1166 while (isKeyword (token
, KEYWORD_declare
))
1171 if (isKeyword (type
, KEYWORD_cursor
))
1172 makeSqlTag (token
, SQLTAG_CURSOR
);
1173 else if (isKeyword (token
, KEYWORD_local
) &&
1174 isKeyword (type
, KEYWORD_temporary
))
1177 * DECLARE LOCAL TEMPORARY TABLE table_name (
1183 if (isKeyword (token
, KEYWORD_table
))
1186 if (isType(token
, TOKEN_IDENTIFIER
) ||
1187 isType(token
, TOKEN_STRING
))
1189 makeSqlTag (token
, SQLTAG_TABLE
);
1193 else if (isType (token
, TOKEN_IDENTIFIER
) ||
1194 isType (token
, TOKEN_STRING
))
1197 makeSqlTag (token
, SQLTAG_LOCAL_VARIABLE
);
1199 makeSqlTag (token
, SQLTAG_VARIABLE
);
1201 findToken (token
, TOKEN_SEMICOLON
);
1207 static void parseLabel (tokenInfo
*const token
)
1210 * A label has this format:
1211 * <<tobacco_dependency>>
1213 * v_senator VARCHAR2(100) := 'THURMOND, JESSE';
1215 * IF total_contributions (v_senator, 'TOBACCO') > 25000
1217 * <<alochol_dependency>>
1219 * v_senator VARCHAR2(100) := 'WHATEVERIT, TAKES';
1224 Assert (isType (token
, TOKEN_BLOCK_LABEL_BEGIN
));
1226 if (isType (token
, TOKEN_IDENTIFIER
))
1228 makeSqlTag (token
, SQLTAG_BLOCK_LABEL
);
1229 readToken (token
); /* read end of label */
1233 static void parseStatements (tokenInfo
*const token
, const bool exit_on_endif
)
1235 /* bool isAnsi = true; */
1236 bool stmtTerm
= false;
1240 if (isType (token
, TOKEN_BLOCK_LABEL_BEGIN
))
1244 switch (token
->keyword
)
1246 case KEYWORD_exception
:
1249 * <exception handler>;
1251 * Where an exception handler could be:
1256 * In this case we need to skip this keyword and
1257 * move on to the next token without reading until
1265 * WHEN statements can be used in exception clauses
1266 * and CASE statements. The CASE statement should skip
1267 * these given below we skip over to an END statement.
1268 * But for an exception clause, we can have:
1274 * If we skip to the TOKEN_SEMICOLON, we miss the begin
1275 * of a nested BEGIN END block. So read the next token
1276 * after the THEN and restart the LOOP.
1278 while (! isKeyword (token
, KEYWORD_then
))
1286 * We do not want to look for a ; since for an empty
1287 * IF block, it would skip over the END.
1305 while ( ! isKeyword (token
, KEYWORD_then
) &&
1306 ! isKeyword (token
, KEYWORD_begin
) )
1311 if (isKeyword (token
, KEYWORD_begin
))
1313 /* isAnsi = false; */
1314 parseBlock(token
, false);
1317 * Handle the non-Ansi IF blocks.
1318 * parseBlock consumes the END, so if the next
1319 * token in a command terminator (like GO)
1320 * we know we are done with this statement.
1322 if (isCmdTerm (token
))
1329 while( ! (isKeyword (token
, KEYWORD_end
) ||
1330 isKeyword (token
, KEYWORD_endif
) )
1333 if ( isKeyword (token
, KEYWORD_else
) ||
1334 isKeyword (token
, KEYWORD_elseif
) )
1337 parseStatements (token
, true);
1339 if (isCmdTerm(token
))
1345 * parseStatements returns when it finds an END, an IF
1346 * should follow the END for ANSI anyway.
1350 if (isKeyword (token
, KEYWORD_end
))
1353 if( isKeyword (token
, KEYWORD_if
) || isKeyword (token
, KEYWORD_endif
) )
1356 if (isCmdTerm(token
))
1362 * Well we need to do something here.
1363 * There are lots of different END statements
1384 * FOR loop_name AS cursor_name CURSOR FOR ...
1388 if (isKeyword (token
, KEYWORD_for
))
1395 while ( ! isKeyword (token
, KEYWORD_is
) )
1398 * If this is not an AS keyword this is
1399 * not a proper FOR statement and should
1405 while ( ! isKeyword (token
, KEYWORD_do
) )
1411 while( ! isKeyword (token
, KEYWORD_end
) )
1414 if ( isKeyword (token, KEYWORD_else) ||
1415 isKeyword (token, KEYWORD_elseif) )
1419 parseStatements (token
, false);
1421 if (isCmdTerm(token
))
1426 if (isKeyword (token
, KEYWORD_end
))
1430 * Typically ended with
1431 * END LOOP [loop name];
1433 * END FOR [loop name];
1435 if ( isKeyword (token
, KEYWORD_loop
) ||
1436 isKeyword (token
, KEYWORD_case
) ||
1437 isKeyword (token
, KEYWORD_for
) )
1440 if (isCmdTerm(token
))
1445 case KEYWORD_create
:
1447 parseKeywords(token
);
1450 case KEYWORD_declare
:
1452 parseBlock (token
, true);
1463 * Not all statements must end in a semi-colon
1465 * if current publisher <> 'publish' then
1466 * signal UE_FailStatement
1469 * The last statement prior to an end ("signal" above) does
1470 * not need a semi-colon, nor does the end if, since it is
1471 * also the last statement prior to the end of the block.
1473 * So we must read to the first semi-colon or an END block
1475 while ( ! stmtTerm
&&
1476 ! ( isKeyword (token
, KEYWORD_end
) ||
1477 (isCmdTerm(token
)) )
1480 if ( isKeyword (token
, KEYWORD_endif
) &&
1484 if (isType (token
, TOKEN_COLON
) )
1487 * A : can signal a loop name
1492 * Unfortunately, labels do not have a
1493 * cmd terminator, therefore we have to check
1494 * if the next token is a keyword and process
1498 if ( isKeyword (token
, KEYWORD_loop
) ||
1499 isKeyword (token
, KEYWORD_while
) ||
1500 isKeyword (token
, KEYWORD_for
) )
1501 /* parseStatements (token); */
1507 if (isType (token
, TOKEN_OPEN_PAREN
) ||
1508 isType (token
, TOKEN_OPEN_CURLY
) ||
1509 isType (token
, TOKEN_OPEN_SQUARE
) )
1510 skipToMatched (token
);
1513 * Since we know how to parse various statements
1514 * if we detect them, parse them to completion
1516 if (isType (token
, TOKEN_BLOCK_LABEL_BEGIN
) ||
1517 isKeyword (token
, KEYWORD_exception
) ||
1518 isKeyword (token
, KEYWORD_loop
) ||
1519 isKeyword (token
, KEYWORD_case
) ||
1520 isKeyword (token
, KEYWORD_for
) ||
1521 isKeyword (token
, KEYWORD_begin
))
1522 parseStatements (token
, false);
1523 else if (isKeyword (token
, KEYWORD_if
))
1524 parseStatements (token
, true);
1529 * We assumed earlier all statements ended with a command terminator.
1530 * See comment above, now, only read if the current token
1531 * is not a command terminator.
1533 if (isCmdTerm(token
) && ! stmtTerm
)
1536 } while (! isKeyword (token
, KEYWORD_end
) &&
1537 ! (exit_on_endif
&& isKeyword (token
, KEYWORD_endif
) ) &&
1541 static void parseBlock (tokenInfo
*const token
, const bool local
)
1543 if (isType (token
, TOKEN_BLOCK_LABEL_BEGIN
))
1548 if (! isKeyword (token
, KEYWORD_begin
))
1552 * These are Oracle style declares which generally come
1553 * between an IS/AS and BEGIN block.
1555 parseDeclare (token
, local
);
1557 if (isKeyword (token
, KEYWORD_begin
))
1561 * Check for ANSI declarations which always follow
1562 * a BEGIN statement. This routine will not advance
1563 * the token if none are found.
1565 parseDeclareANSI (token
, local
);
1566 token
->begin_end_nest_lvl
++;
1567 while (! isKeyword (token
, KEYWORD_end
))
1569 parseStatements (token
, false);
1571 if (isCmdTerm(token
))
1574 token
->begin_end_nest_lvl
--;
1577 * Read the next token (we will assume
1578 * it is the command delimiter)
1583 * Check if the END block is terminated
1585 if (! isCmdTerm (token
))
1588 * Not sure what to do here at the moment.
1589 * I think the routine that calls parseBlock
1590 * must expect the next token has already
1591 * been read since it is possible this
1592 * token is not a command delimiter.
1594 /* findCmdTerm (token, false); */
1599 static void parsePackage (tokenInfo
*const token
)
1602 * Packages can be specified in a number of ways:
1603 * CREATE OR REPLACE PACKAGE pkg_name AS
1605 * CREATE OR REPLACE PACKAGE owner.pkg_name AS
1606 * or by specifying a package body
1607 * CREATE OR REPLACE PACKAGE BODY pkg_name AS
1608 * CREATE OR REPLACE PACKAGE BODY owner.pkg_name AS
1610 tokenInfo
*const name
= newToken ();
1611 readIdentifier (name
);
1612 if (isKeyword (name
, KEYWORD_body
))
1615 * Ignore the BODY tag since we will process
1616 * the body or prototypes in the same manner
1618 readIdentifier (name
);
1620 /* Check for owner.pkg_name */
1621 while (! isKeyword (token
, KEYWORD_is
))
1624 if ( isType(token
, TOKEN_PERIOD
) )
1626 readIdentifier (name
);
1629 if (isKeyword (token
, KEYWORD_is
))
1631 if (isType (name
, TOKEN_IDENTIFIER
) ||
1632 isType (name
, TOKEN_STRING
))
1633 makeSqlTag (name
, SQLTAG_PACKAGE
);
1634 addToScope (token
, name
->string
, SQLTAG_PACKAGE
);
1635 parseBlock (token
, false);
1636 vStringClear (token
->scope
);
1637 token
->scopeKind
= SQLTAG_COUNT
;
1639 findCmdTerm (token
, false);
1643 static void parseTable (tokenInfo
*const token
)
1645 tokenInfo
*const name
= newToken ();
1648 * This deals with these formats:
1649 * create table t1 (c1 int);
1650 * create global temporary table t2 (c1 int);
1651 * create table "t3" (c1 int);
1652 * create table bob.t4 (c1 int);
1653 * create table bob."t5" (c1 int);
1654 * create table "bob"."t6" (c1 int);
1655 * create table bob."t7" (c1 int);
1656 * Proxy tables use this format:
1657 * create existing table bob."t7" AT '...';
1658 * SQL Server and Sybase formats
1659 * create table OnlyTable (
1660 * create table dbo.HasOwner (
1661 * create table [dbo].[HasOwnerSquare] (
1662 * create table master.dbo.HasDb (
1663 * create table master..HasDbNoOwner (
1664 * create table [master].dbo.[HasDbAndOwnerSquare] (
1665 * create table [master]..[HasDbNoOwnerSquare] (
1668 /* This could be a database, owner or table name */
1669 readIdentifier (name
);
1671 if (isType (token
, TOKEN_PERIOD
))
1674 * This could be a owner or table name.
1675 * But this is also a special case since the table can be
1676 * referenced with a blank owner:
1679 readIdentifier (name
);
1680 /* Check if a blank name was provided */
1681 if (isType (name
, TOKEN_PERIOD
))
1683 readIdentifier (name
);
1686 if (isType (token
, TOKEN_PERIOD
))
1688 /* This can only be the table name */
1689 readIdentifier (name
);
1693 if (isType (token
, TOKEN_OPEN_PAREN
))
1695 if (isType (name
, TOKEN_IDENTIFIER
) ||
1696 isType (name
, TOKEN_STRING
))
1698 makeSqlTag (name
, SQLTAG_TABLE
);
1699 vStringCopy(token
->scope
, name
->string
);
1700 token
->scopeKind
= SQLTAG_TABLE
;
1701 parseRecord (token
);
1702 vStringClear (token
->scope
);
1703 token
->scopeKind
= SQLTAG_COUNT
;
1706 else if (isKeyword (token
, KEYWORD_at
))
1708 if (isType (name
, TOKEN_IDENTIFIER
))
1710 makeSqlTag (name
, SQLTAG_TABLE
);
1713 findCmdTerm (token
, false);
1717 static void parseIndex (tokenInfo
*const token
)
1719 tokenInfo
*const name
= newToken ();
1720 tokenInfo
*const owner
= newToken ();
1723 * This deals with these formats
1724 * create index i1 on t1(c1) create index "i2" on t1(c1)
1725 * create virtual unique clustered index "i3" on t1(c1)
1726 * create unique clustered index "i4" on t1(c1)
1727 * create clustered index "i5" on t1(c1)
1728 * create bitmap index "i6" on t1(c1)
1731 readIdentifier (name
);
1733 if (isType (token
, TOKEN_PERIOD
))
1735 readIdentifier (name
);
1738 if (isKeyword (token
, KEYWORD_on
) &&
1739 (isType (name
, TOKEN_IDENTIFIER
) ||
1740 isType (name
, TOKEN_STRING
)))
1742 readIdentifier (owner
);
1744 if (isType (token
, TOKEN_PERIOD
))
1746 readIdentifier (owner
);
1749 addToScope(name
, owner
->string
, SQLTAG_TABLE
/* FIXME? */);
1750 makeSqlTag (name
, SQLTAG_INDEX
);
1752 findCmdTerm (token
, false);
1754 deleteToken (owner
);
1757 static void parseEvent (tokenInfo
*const token
)
1759 tokenInfo
*const name
= newToken ();
1762 * This deals with these formats
1763 * create event e1 handler begin end;
1764 * create event "e2" handler begin end;
1765 * create event dba."e3" handler begin end;
1766 * create event "dba"."e4" handler begin end;
1769 readIdentifier (name
);
1771 if (isType (token
, TOKEN_PERIOD
))
1773 readIdentifier (name
);
1775 while (! (isKeyword (token
, KEYWORD_handler
) ||
1776 (isType (token
, TOKEN_SEMICOLON
))) )
1781 if (isKeyword (token
, KEYWORD_handler
) ||
1782 isType (token
, TOKEN_SEMICOLON
))
1784 makeSqlTag (name
, SQLTAG_EVENT
);
1787 if (isKeyword (token
, KEYWORD_handler
))
1790 if (isKeyword (token
, KEYWORD_begin
))
1792 parseBlock (token
, true);
1794 findCmdTerm (token
, true);
1799 static void parseTrigger (tokenInfo
*const token
)
1801 tokenInfo
*const name
= newToken ();
1802 tokenInfo
*const table
= newToken ();
1805 * This deals with these formats
1806 * create or replace trigger tr1 begin end;
1807 * create trigger "tr2" begin end;
1808 * drop trigger "droptr1";
1809 * create trigger "tr3" CALL sp_something();
1810 * create trigger "owner"."tr4" begin end;
1811 * create trigger "tr5" not valid;
1812 * create trigger "tr6" begin end;
1815 readIdentifier (name
);
1817 if (isType (token
, TOKEN_PERIOD
))
1819 readIdentifier (name
);
1823 while ( !isKeyword (token
, KEYWORD_on
) &&
1824 !isCmdTerm (token
) )
1829 /*if (! isType (token, TOKEN_SEMICOLON) ) */
1830 if (! isCmdTerm (token
))
1834 if (isType (token
, TOKEN_PERIOD
))
1840 while (! (isKeyword (token
, KEYWORD_begin
) ||
1841 (isKeyword (token
, KEYWORD_call
)) ||
1842 ( isCmdTerm (token
))) )
1844 if (isKeyword (token
, KEYWORD_declare
))
1846 addToScope(token
, name
->string
, SQLTAG_TRIGGER
);
1847 parseDeclare(token
, true);
1848 vStringClear(token
->scope
);
1849 token
->scopeKind
= SQLTAG_COUNT
;
1855 if (isKeyword (token
, KEYWORD_begin
) ||
1856 isKeyword (token
, KEYWORD_call
))
1858 addToScope(name
, table
->string
, SQLTAG_TABLE
);
1859 makeSqlTag (name
, SQLTAG_TRIGGER
);
1860 addToScope(token
, table
->string
, SQLTAG_TABLE
);
1861 if (isKeyword (token
, KEYWORD_begin
))
1863 parseBlock (token
, true);
1865 vStringClear(token
->scope
);
1866 token
->scopeKind
= SQLTAG_COUNT
;
1870 findCmdTerm (token
, true);
1872 deleteToken (table
);
1875 static void parsePublication (tokenInfo
*const token
)
1877 tokenInfo
*const name
= newToken ();
1880 * This deals with these formats
1881 * create or replace publication pu1 ()
1882 * create publication "pu2" ()
1883 * create publication dba."pu3" ()
1884 * create publication "dba"."pu4" ()
1887 readIdentifier (name
);
1889 if (isType (token
, TOKEN_PERIOD
))
1891 readIdentifier (name
);
1894 if (isType (token
, TOKEN_OPEN_PAREN
))
1896 if (isType (name
, TOKEN_IDENTIFIER
) ||
1897 isType (name
, TOKEN_STRING
))
1899 makeSqlTag (name
, SQLTAG_PUBLICATION
);
1902 findCmdTerm (token
, false);
1906 static void parseService (tokenInfo
*const token
)
1908 tokenInfo
*const name
= newToken ();
1911 * This deals with these formats
1912 * CREATE SERVICE s1 TYPE 'HTML'
1913 * AUTHORIZATION OFF USER DBA AS
1915 * FROM SYS.SYSTABLE;
1916 * CREATE SERVICE "s2" TYPE 'HTML'
1917 * AUTHORIZATION OFF USER DBA AS
1918 * CALL sp_Something();
1921 readIdentifier (name
);
1923 if (isKeyword (token
, KEYWORD_type
))
1925 if (isType (name
, TOKEN_IDENTIFIER
) ||
1926 isType (name
, TOKEN_STRING
))
1928 makeSqlTag (name
, SQLTAG_SERVICE
);
1931 findCmdTerm (token
, false);
1935 static void parseDomain (tokenInfo
*const token
)
1937 tokenInfo
*const name
= newToken ();
1940 * This deals with these formats
1941 * CREATE DOMAIN|DATATYPE [AS] your_name ...;
1944 readIdentifier (name
);
1945 if (isKeyword (name
, KEYWORD_is
))
1947 readIdentifier (name
);
1950 if (isType (name
, TOKEN_IDENTIFIER
) ||
1951 isType (name
, TOKEN_STRING
))
1953 makeSqlTag (name
, SQLTAG_DOMAIN
);
1955 findCmdTerm (token
, false);
1959 static void parseDrop (tokenInfo
*const token
)
1962 * This deals with these formats
1963 * DROP TABLE|PROCEDURE|DOMAIN|DATATYPE name;
1965 * Just simply skip over these statements.
1966 * They are often confused with PROCEDURE prototypes
1967 * since the syntax is similar, this effectively deals with
1968 * the issue for all types.
1971 findCmdTerm (token
, false);
1974 static void parseVariable (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;
1986 readIdentifier (name
);
1988 if ( (isType (name
, TOKEN_IDENTIFIER
) || isType (name
, TOKEN_STRING
))
1989 && !isType (token
, TOKEN_SEMICOLON
) )
1991 makeSqlTag (name
, SQLTAG_VARIABLE
);
1993 findCmdTerm (token
, true);
1998 static void parseSynonym (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;
2010 readIdentifier (name
);
2012 if ( (isType (name
, TOKEN_IDENTIFIER
) || isType (name
, TOKEN_STRING
))
2013 && isKeyword (token
, KEYWORD_for
) )
2015 makeSqlTag (name
, SQLTAG_SYNONYM
);
2017 findCmdTerm (token
, true);
2022 static void parseView (tokenInfo
*const token
)
2024 tokenInfo
*const name
= newToken ();
2027 * This deals with these formats
2028 * create variable varname1 integer;
2029 * create variable @varname2 integer;
2030 * create variable "varname3" integer;
2031 * drop variable @varname3;
2034 readIdentifier (name
);
2036 if (isType (token
, TOKEN_PERIOD
))
2038 readIdentifier (name
);
2041 if (isType (token
, TOKEN_OPEN_PAREN
))
2043 skipArgumentList(token
);
2046 while (!(isKeyword (token
, KEYWORD_is
) ||
2047 isType (token
, TOKEN_SEMICOLON
)
2053 if ( (isType (name
, TOKEN_IDENTIFIER
) || isType (name
, TOKEN_STRING
))
2054 && isKeyword (token
, KEYWORD_is
) )
2056 makeSqlTag (name
, SQLTAG_VIEW
);
2059 findCmdTerm (token
, true);
2064 static void parseMLTable (tokenInfo
*const token
)
2066 tokenInfo
*const version
= newToken ();
2067 tokenInfo
*const table
= newToken ();
2068 tokenInfo
*const event
= newToken ();
2071 * This deals with these formats
2072 * call dbo.ml_add_table_script( 'version', 'table_name', 'event',
2073 * 'some SQL statement'
2078 if (isType (token
, TOKEN_OPEN_PAREN
))
2080 readToken (version
);
2082 while (!(isType (token
, TOKEN_COMMA
) ||
2083 isType (token
, TOKEN_CLOSE_PAREN
)
2089 if (isType (token
, TOKEN_COMMA
))
2093 while (!(isType (token
, TOKEN_COMMA
) ||
2094 isType (token
, TOKEN_CLOSE_PAREN
)
2100 if (isType (token
, TOKEN_COMMA
))
2104 if (isType (version
, TOKEN_STRING
) &&
2105 isType (table
, TOKEN_STRING
) &&
2106 isType (event
, TOKEN_STRING
))
2108 addToScope(version
, table
->string
, SQLTAG_TABLE
);
2109 addToScope(version
, event
->string
, SQLTAG_EVENT
);
2110 makeSqlTag (version
, SQLTAG_MLTABLE
);
2113 if (! isType (token
, TOKEN_CLOSE_PAREN
))
2114 findToken (token
, TOKEN_CLOSE_PAREN
);
2118 findCmdTerm (token
, true);
2120 deleteToken (version
);
2121 deleteToken (table
);
2122 deleteToken (event
);
2125 static void parseMLConn (tokenInfo
*const token
)
2127 tokenInfo
*const version
= newToken ();
2128 tokenInfo
*const event
= newToken ();
2131 * This deals with these formats
2132 * call ml_add_connection_script( 'version', 'event',
2133 * 'some SQL statement'
2138 if (isType (token
, TOKEN_OPEN_PAREN
))
2140 readToken (version
);
2142 while (!(isType (token
, TOKEN_COMMA
) ||
2143 isType (token
, TOKEN_CLOSE_PAREN
)
2149 if (isType (token
, TOKEN_COMMA
))
2153 if (isType (version
, TOKEN_STRING
) &&
2154 isType (event
, TOKEN_STRING
))
2156 addToScope(version
, event
->string
, SQLTAG_EVENT
);
2157 makeSqlTag (version
, SQLTAG_MLCONN
);
2160 if (! isType (token
, TOKEN_CLOSE_PAREN
))
2161 findToken (token
, TOKEN_CLOSE_PAREN
);
2165 findCmdTerm (token
, true);
2167 deleteToken (version
);
2168 deleteToken (event
);
2171 static void parseMLProp (tokenInfo
*const token
)
2173 tokenInfo
*const component
= newToken ();
2174 tokenInfo
*const prop_set_name
= newToken ();
2175 tokenInfo
*const prop_name
= newToken ();
2178 * This deals with these formats
2188 if (isType (token
, TOKEN_OPEN_PAREN
))
2190 readToken (component
);
2192 while (!(isType (token
, TOKEN_COMMA
) ||
2193 isType (token
, TOKEN_CLOSE_PAREN
)
2199 if (isType (token
, TOKEN_COMMA
))
2201 readToken (prop_set_name
);
2203 while (!(isType (token
, TOKEN_COMMA
) ||
2204 isType (token
, TOKEN_CLOSE_PAREN
)
2210 if (isType (token
, TOKEN_COMMA
))
2212 readToken (prop_name
);
2214 if (isType (component
, TOKEN_STRING
) &&
2215 isType (prop_set_name
, TOKEN_STRING
) &&
2216 isType (prop_name
, TOKEN_STRING
))
2218 addToScope(component
, prop_set_name
->string
, SQLTAG_MLPROP
/* FIXME */);
2219 addToScope(component
, prop_name
->string
, SQLTAG_MLPROP
/* FIXME */);
2220 makeSqlTag (component
, SQLTAG_MLPROP
);
2223 if (! isType (token
, TOKEN_CLOSE_PAREN
))
2224 findToken (token
, TOKEN_CLOSE_PAREN
);
2228 findCmdTerm (token
, true);
2230 deleteToken (component
);
2231 deleteToken (prop_set_name
);
2232 deleteToken (prop_name
);
2235 static void parseComment (tokenInfo
*const token
)
2238 * This deals with this statement:
2239 * COMMENT TO PRESERVE FORMAT ON PROCEDURE "DBA"."test" IS
2240 * {create PROCEDURE DBA."test"()
2246 * The comment can contain anything between the CURLY
2248 * COMMENT ON USER "admin" IS
2249 * 'Administration Group'
2251 * Or it could be a simple string with no curly braces
2253 while (! isKeyword (token
, KEYWORD_is
))
2258 if (isType(token
, TOKEN_OPEN_CURLY
))
2260 findToken (token
, TOKEN_CLOSE_CURLY
);
2263 findCmdTerm (token
, true);
2267 static void parseKeywords (tokenInfo
*const token
)
2269 switch (token
->keyword
)
2271 case KEYWORD_begin
: parseBlock (token
, false); break;
2272 case KEYWORD_comment
: parseComment (token
); break;
2273 case KEYWORD_cursor
: parseSimple (token
, SQLTAG_CURSOR
); break;
2274 case KEYWORD_datatype
: parseDomain (token
); break;
2275 case KEYWORD_declare
: parseBlock (token
, false); break;
2276 case KEYWORD_domain
: parseDomain (token
); break;
2277 case KEYWORD_drop
: parseDrop (token
); break;
2278 case KEYWORD_event
: parseEvent (token
); break;
2279 case KEYWORD_function
: parseSubProgram (token
); break;
2280 case KEYWORD_if
: parseStatements (token
, false); break;
2281 case KEYWORD_index
: parseIndex (token
); break;
2282 case KEYWORD_ml_table
: parseMLTable (token
); break;
2283 case KEYWORD_ml_table_lang
: parseMLTable (token
); break;
2284 case KEYWORD_ml_table_dnet
: parseMLTable (token
); break;
2285 case KEYWORD_ml_table_java
: parseMLTable (token
); break;
2286 case KEYWORD_ml_table_chk
: parseMLTable (token
); break;
2287 case KEYWORD_ml_conn
: parseMLConn (token
); break;
2288 case KEYWORD_ml_conn_lang
: parseMLConn (token
); break;
2289 case KEYWORD_ml_conn_dnet
: parseMLConn (token
); break;
2290 case KEYWORD_ml_conn_java
: parseMLConn (token
); break;
2291 case KEYWORD_ml_conn_chk
: parseMLConn (token
); break;
2292 case KEYWORD_ml_prop
: parseMLProp (token
); break;
2293 case KEYWORD_package
: parsePackage (token
); break;
2294 case KEYWORD_procedure
: parseSubProgram (token
); break;
2295 case KEYWORD_publication
: parsePublication (token
); break;
2296 case KEYWORD_service
: parseService (token
); break;
2297 case KEYWORD_subtype
: parseSimple (token
, SQLTAG_SUBTYPE
); break;
2298 case KEYWORD_synonym
: parseSynonym (token
); break;
2299 case KEYWORD_table
: parseTable (token
); break;
2300 case KEYWORD_trigger
: parseTrigger (token
); break;
2301 case KEYWORD_type
: parseType (token
); break;
2302 case KEYWORD_variable
: parseVariable (token
); break;
2303 case KEYWORD_view
: parseView (token
); break;
2308 static void parseSqlFile (tokenInfo
*const token
)
2314 if (isType (token
, TOKEN_BLOCK_LABEL_BEGIN
))
2317 parseKeywords (token
);
2318 } while (! isKeyword (token
, KEYWORD_end
));
2321 static void initialize (const langType language
)
2323 Assert (ARRAY_SIZE (SqlKinds
) == SQLTAG_COUNT
);
2324 Lang_sql
= language
;
2327 static void findSqlTags (void)
2329 tokenInfo
*const token
= newToken ();
2330 exception_t exception
= (exception_t
) (setjmp (Exception
));
2332 while (exception
== ExceptionNone
)
2333 parseSqlFile (token
);
2335 deleteToken (token
);
2338 extern parserDefinition
* SqlParser (void)
2340 static const char *const extensions
[] = { "sql", NULL
};
2341 parserDefinition
* def
= parserNewFull ("SQL", KIND_FILE_ALT
);
2342 def
->kinds
= SqlKinds
;
2343 def
->kindCount
= ARRAY_SIZE (SqlKinds
);
2344 def
->extensions
= extensions
;
2345 def
->parser
= findSqlTags
;
2346 def
->initialize
= initialize
;
2347 def
->keywordTable
= SqlKeywordTable
;
2348 def
->keywordCount
= ARRAY_SIZE (SqlKeywordTable
);