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.
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 () */
30 * On-line PL/SQL Reference Guide:
31 * http://info-it.umsystem.edu/oradocs/doc/server/doc/PLS23/toc.htm
33 * Sample PL/SQL code is available from:
34 * http://www.orafaq.com/faqscrpt.htm#GENPLSQL
40 #define isType(token,t) (boolean) ((token)->type == (t))
41 #define isKeyword(token,k) (boolean) ((token)->keyword == (k))
47 typedef enum eException
{ ExceptionNone
, ExceptionEOF
} exception_t
;
49 /* Used to specify type of keyword.
51 typedef enum eKeywordId
{
107 /* Used to determine whether keyword is valid for the token language and
110 typedef struct sKeywordDesc
{
115 typedef enum eTokenType
{
117 TOKEN_BLOCK_LABEL_BEGIN
,
118 TOKEN_BLOCK_LABEL_END
,
132 typedef struct sTokenInfo
{
137 unsigned long lineNumber
;
145 static langType Lang_sql
;
147 static jmp_buf Exception
;
154 SQLTAG_LOCAL_VARIABLE
,
175 static kindOption SqlKinds
[] = {
176 { TRUE
, 'c', "cursor", "cursors" },
177 { FALSE
, 'd', "prototype", "prototypes" },
178 { TRUE
, 'f', "function", "functions" },
179 { TRUE
, 'F', "field", "record fields" },
180 { FALSE
, 'l', "local", "local variables" },
181 { TRUE
, 'L', "label", "block label" },
182 { TRUE
, 'P', "package", "packages" },
183 { TRUE
, 'n', "namespace", "procedures" },
184 { FALSE
, 'r', "record", "records" },
185 { TRUE
, 's', "subtype", "subtypes" },
186 { TRUE
, 't', "class", "tables" },
187 { TRUE
, 'T', "macro", "triggers" },
188 { TRUE
, 'v', "variable", "variables" },
189 { TRUE
, 'i', "struct", "indexes" },
190 { TRUE
, 'e', "event", "events" },
191 { TRUE
, 'U', "publication", "publications" },
192 { TRUE
, 'R', "service", "services" },
193 { TRUE
, 'D', "domain", "domains" },
194 { TRUE
, 'm', "member", "views" },
195 { TRUE
, 'n', "synonym", "synonyms" },
196 { TRUE
, 'x', "mltable", "MobiLink Table Scripts" },
197 { TRUE
, 'y', "mlconn", "MobiLink Conn Scripts" }
200 static const keywordDesc SqlKeywordTable
[] = {
201 /* keyword keyword ID */
202 { "as", KEYWORD_is
},
203 { "begin", KEYWORD_begin
},
204 { "body", KEYWORD_body
},
205 { "cursor", KEYWORD_cursor
},
206 { "declare", KEYWORD_declare
},
207 { "end", KEYWORD_end
},
208 { "function", KEYWORD_function
},
209 { "if", KEYWORD_if
},
210 { "is", KEYWORD_is
},
211 { "loop", KEYWORD_loop
},
212 { "case", KEYWORD_case
},
213 { "for", KEYWORD_for
},
214 { "call", KEYWORD_call
},
215 { "package", KEYWORD_package
},
216 { "pragma", KEYWORD_pragma
},
217 { "procedure", KEYWORD_procedure
},
218 { "record", KEYWORD_record
},
219 { "object", KEYWORD_object
},
220 { "ref", KEYWORD_ref
},
221 { "rem", KEYWORD_rem
},
222 { "return", KEYWORD_return
},
223 { "returns", KEYWORD_returns
},
224 { "subtype", KEYWORD_subtype
},
225 { "table", KEYWORD_table
},
226 { "trigger", KEYWORD_trigger
},
227 { "type", KEYWORD_type
},
228 { "index", KEYWORD_index
},
229 { "event", KEYWORD_event
},
230 { "publication", KEYWORD_publication
},
231 { "service", KEYWORD_service
},
232 { "domain", KEYWORD_domain
},
233 { "datatype", KEYWORD_datatype
},
234 { "result", KEYWORD_result
},
235 { "when", KEYWORD_when
},
236 { "then", KEYWORD_then
},
237 { "variable", KEYWORD_variable
},
238 { "exception", KEYWORD_exception
},
239 { "at", KEYWORD_at
},
240 { "on", KEYWORD_on
},
241 { "primary", KEYWORD_primary
},
242 { "references", KEYWORD_references
},
243 { "unique", KEYWORD_unique
},
244 { "check", KEYWORD_check
},
245 { "constraint", KEYWORD_constraint
},
246 { "foreign", KEYWORD_foreign
},
247 { "ml_add_table_script", KEYWORD_ml_table
},
248 { "ml_add_connection_script", KEYWORD_ml_conn
},
249 { "local", KEYWORD_local
},
250 { "temporary", KEYWORD_temporary
},
251 { "drop", KEYWORD_drop
},
252 { "view", KEYWORD_view
},
253 { "synonym", KEYWORD_synonym
},
254 { "handler", KEYWORD_handler
}
258 * FUNCTION DECLARATIONS
261 static void parseBlock (tokenInfo
*const token
, const boolean local
);
262 static void makeConstTag (tokenInfo
*const token
, const sqlKind kind
);
268 static void dispToken (tokenInfo
*const token
, const char * location
)
271 if ( isKeyword(token
, KEYWORD_NONE
) )
273 if ( isType(token
, TOKEN_IDENTIFIER
) || isType(token
, TOKEN_STRING
) )
275 printf( "\n%s: token string t:%s s:%s l:%lu p:%d\n"
277 , vStringValue(token
->string
)
278 , vStringValue(token
->scope
)
280 , token
->bufferPosition
283 printf( "\n%s: token t:%d s:%s l:%lu p:%d\n"
286 , vStringValue(token
->scope
)
288 , token
->bufferPosition
292 printf( "\n%s: keyword:%s k:%d s:%s l:%lu p:%d\n"
294 , vStringValue(token
->string
)
296 , vStringValue(token
->scope
)
298 , token
->bufferPosition
305 * FUNCTION DEFINITIONS
308 static boolean
isIdentChar1 (const int c
)
310 /* Other databases are less restrictive on the first character of
312 * isIdentChar1 is used to identify the first character of an
313 * identifier, so we are removing some restrictions. */
315 (isalpha (c
) || c
== '@' || c
== '_' );
318 static boolean
isIdentChar (const int c
)
321 (isalpha (c
) || isdigit (c
) || c
== '$' ||
322 c
== '@' || c
== '_' || c
== '#');
325 static void buildSqlKeywordHash (void)
327 const size_t count
= sizeof (SqlKeywordTable
) /
328 sizeof (SqlKeywordTable
[0]);
330 for (i
= 0 ; i
< count
; ++i
)
332 const keywordDesc
* const p
= &SqlKeywordTable
[i
];
333 addKeyword (p
->name
, Lang_sql
, (int) p
->id
);
337 static tokenInfo
*newToken (void)
339 tokenInfo
*const token
= xMalloc(1, tokenInfo
);
341 token
->type
= TOKEN_UNDEFINED
;
342 token
->keyword
= KEYWORD_NONE
;
343 token
->string
= vStringNew ();
344 token
->scope
= vStringNew ();
349 static void deleteToken (tokenInfo
*const token
)
351 vStringDelete (token
->string
);
356 * Tag generation functions
359 static void makeSqlTag (tokenInfo
*const token
, const sqlKind kind
)
363 if (SqlKinds
[kind
].enabled
)
366 * If a scope has been added to the token, change the token
367 * string to include the scope when making the tag.
369 if ( vStringLength(token
->scope
) > 0 )
371 fulltag
= vStringNew ();
372 vStringCopy(fulltag
, token
->scope
);
373 vStringCatS (fulltag
, ".");
374 vStringCatS (fulltag
, vStringValue(token
->string
));
375 vStringTerminate(fulltag
);
376 vStringCopy(token
->string
, fulltag
);
377 vStringDelete (fulltag
);
379 makeConstTag (token
, kind
);
383 static void makeConstTag (tokenInfo
*const token
, const sqlKind kind
)
385 if (SqlKinds
[kind
].enabled
)
387 const char *const name
= vStringValue (token
->string
);
389 initTagEntry (&e
, name
);
391 e
.lineNumber
= token
->lineNumber
;
392 e
.filePosition
= token
->filePosition
;
393 e
.kindName
= SqlKinds
[kind
].name
;
394 e
.kind
= SqlKinds
[kind
].letter
;
404 static int skipToCharacter (const int c
)
410 } while (d
!= EOF
&& d
!= c
);
414 static void parseString (vString
*const string
, const int delimiter
)
421 /* printf( "\nps: %c\n", c ); */
424 else if (c
== delimiter
)
427 vStringPut (string
, c
);
429 vStringTerminate (string
);
432 /* Read a C identifier beginning with "firstChar" and places it into "name".
434 static void parseIdentifier (vString
*const string
, const int firstChar
)
437 Assert (isIdentChar1 (c
));
440 vStringPut (string
, c
);
442 } while (isIdentChar (c
));
443 vStringTerminate (string
);
445 fileUngetc (c
); /* unget non-identifier character */
448 static keywordId
analyzeToken (vString
*const name
)
450 static vString
*keyword
= NULL
;
452 keyword
= vStringNew ();
453 vStringCopyToLower (keyword
, name
);
454 return (keywordId
) lookupKeyword (vStringValue (keyword
), Lang_sql
);
457 static void readToken (tokenInfo
*const token
)
461 token
->type
= TOKEN_UNDEFINED
;
462 token
->keyword
= KEYWORD_NONE
;
463 vStringClear (token
->string
);
469 /* printf( "\nrtc: %c\n", c ); */
471 * Added " to the list of ignores, not sure what this
472 * might break but it gets by this issue:
473 * create table "t1" (...)
476 while (c
== '\t' || c
== ' ' || c
== '\n');
480 case EOF
: longjmp (Exception
, (int)ExceptionEOF
); break;
481 case '(': token
->type
= TOKEN_OPEN_PAREN
; break;
482 case ')': token
->type
= TOKEN_CLOSE_PAREN
; break;
483 case ';': token
->type
= TOKEN_SEMICOLON
; break;
484 case '.': token
->type
= TOKEN_PERIOD
; break;
485 case ',': token
->type
= TOKEN_COMMA
; break;
489 token
->type
= TOKEN_STRING
;
490 parseString (token
->string
, c
);
491 token
->lineNumber
= getSourceLineNumber ();
492 token
->filePosition
= getInputFilePosition ();
497 if (c
== '-') /* is this the start of a comment? */
499 skipToCharacter ('\n');
506 token
->type
= TOKEN_OPERATOR
;
513 const int initial
= c
;
518 token
->type
= TOKEN_BLOCK_LABEL_BEGIN
;
520 token
->type
= TOKEN_BLOCK_LABEL_END
;
525 token
->type
= TOKEN_UNDEFINED
;
533 if (d
!= '*') /* is this the start of a comment? */
539 skipToCharacter ('*');
545 } while (c
!= EOF
&& c
!= '\0');
552 if (! isIdentChar1 (c
))
553 token
->type
= TOKEN_UNDEFINED
;
556 parseIdentifier (token
->string
, c
);
557 token
->lineNumber
= getSourceLineNumber ();
558 token
->filePosition
= getInputFilePosition ();
559 token
->keyword
= analyzeToken (token
->string
);
560 if (isKeyword (token
, KEYWORD_rem
))
562 vStringClear (token
->string
);
563 skipToCharacter ('\n');
566 else if (isKeyword (token
, KEYWORD_NONE
))
567 token
->type
= TOKEN_IDENTIFIER
;
569 token
->type
= TOKEN_KEYWORD
;
573 /*dispToken(token, "rte");*/
577 * Token parsing functions
580 /* - unused - I don't know (enrico)
581 static void addContext (tokenInfo* const parent, const tokenInfo* const child)
583 if (vStringLength (parent->string) > 0)
585 vStringCatS (parent->string, ".");
587 vStringCatS (parent->string, vStringValue(child->string));
588 vStringTerminate(parent->string);
591 static void addToScope (tokenInfo
* const token
, vString
* const extra
)
593 if (vStringLength (token
->scope
) > 0)
595 vStringCatS (token
->scope
, ".");
597 vStringCatS (token
->scope
, vStringValue(extra
));
598 vStringTerminate(token
->scope
);
605 static void findToken (tokenInfo
*const token
, const tokenType type
)
607 while (! isType (token
, type
))
613 static void skipArgumentList (tokenInfo
*const token
)
618 * Other databases can have arguments with fully declared
620 * ( name varchar(30), text binary(10) )
621 * So we must check for nested open and closing parantheses
624 if (isType (token
, TOKEN_OPEN_PAREN
)) /* arguments? */
627 /*findToken (token, TOKEN_CLOSE_PAREN);*/
628 while (! (isType (token
, TOKEN_CLOSE_PAREN
) && (nest_level
== 0)))
631 if (isType (token
, TOKEN_OPEN_PAREN
))
635 if (isType (token
, TOKEN_CLOSE_PAREN
))
647 static void parseSubProgram (tokenInfo
*const token
)
649 tokenInfo
*const name
= newToken ();
653 * FUNCTION func_name RETURN integer;
654 * PROCEDURE proc_name( parameters );
656 * FUNCTION GET_ML_USERNAME RETURN VARCHAR2
659 * RETURN v_sync_user_id;
660 * END GET_ML_USERNAME;
662 * PROCEDURE proc_name( parameters )
666 * CREATE PROCEDURE proc_name( parameters )
667 * EXTERNAL NAME ... ;
668 * CREATE PROCEDURE proc_name( parameters )
672 * CREATE FUNCTION f_GetClassName(
673 * IN @object VARCHAR(128)
674 * ,IN @code VARCHAR(128)
676 * RETURNS VARCHAR(200)
680 * IF( @object = 'user_state' ) THEN
681 * SET something = something;
687 const sqlKind kind
= isKeyword (token
, KEYWORD_function
) ?
688 SQLTAG_FUNCTION
: SQLTAG_PROCEDURE
;
689 Assert (isKeyword (token
, KEYWORD_function
) ||
690 isKeyword (token
, KEYWORD_procedure
));
693 if (isType (token
, TOKEN_PERIOD
))
698 skipArgumentList (token
);
700 if (kind
== SQLTAG_FUNCTION
)
702 if (isKeyword (token
, KEYWORD_return
))
710 if( isType (token
, TOKEN_SEMICOLON
) )
712 makeSqlTag (name
, SQLTAG_PROTOTYPE
);
714 while (!(isKeyword (token
, KEYWORD_is
) ||
715 isKeyword (token
, KEYWORD_begin
) ||
716 isType (token
, TOKEN_SEMICOLON
)
719 readToken (token
); /* read return type */
720 if (isKeyword (token
, KEYWORD_is
) ||
721 isKeyword (token
, KEYWORD_begin
) )
723 addToScope(token
, name
->string
);
724 if (isType (name
, TOKEN_IDENTIFIER
) ||
725 isType (name
, TOKEN_STRING
))
726 makeSqlTag (name
, kind
);
728 /*dispToken(name, "SubProgram: parseBlock name");*/
729 /*dispToken(token, "SubProgram: parseBlock token");*/
730 parseBlock (token
, TRUE
);
731 vStringClear (token
->scope
);
737 static void parseRecord (tokenInfo
*const token
)
739 /* Make it a bit forgiving, this is called from
740 * multiple functions, parseTable, parseType */
741 if (!isType (token
, TOKEN_OPEN_PAREN
))
744 Assert (isType (token
, TOKEN_OPEN_PAREN
));
747 if ( isType (token
, TOKEN_COMMA
) || isType (token
, TOKEN_OPEN_PAREN
) )
751 * Create table statements can end with various constraints
752 * which must be excluded from the SQLTAG_FIELD.
758 * constraint whatever,
764 if (! (isKeyword(token
, KEYWORD_primary
) ||
765 isKeyword(token
, KEYWORD_references
) ||
766 isKeyword(token
, KEYWORD_unique
) ||
767 isKeyword(token
, KEYWORD_check
) ||
768 isKeyword(token
, KEYWORD_constraint
) ||
769 isKeyword(token
, KEYWORD_foreign
) ) )
771 if (isType (token
, TOKEN_IDENTIFIER
) ||
772 isType (token
, TOKEN_STRING
))
773 makeSqlTag (token
, SQLTAG_FIELD
);
776 while (!(isType (token
, TOKEN_COMMA
) ||
777 isType (token
, TOKEN_CLOSE_PAREN
) ||
778 isType (token
, TOKEN_OPEN_PAREN
)
783 * A table structure can look like this:
790 * We can't just look for a COMMA or CLOSE_PAREN
791 * since that will not deal with the numeric(10,5)
792 * case. So we need to skip the argument list
793 * when we find an open paren.
795 if (isType (token
, TOKEN_OPEN_PAREN
))
797 /* Reads to the next token after the TOKEN_CLOSE_PAREN */
798 skipArgumentList(token
);
801 } while (! isType (token
, TOKEN_CLOSE_PAREN
));
804 static void parseType (tokenInfo
*const token
)
806 tokenInfo
*const name
= newToken ();
807 vString
* saveScope
= vStringNew ();
809 vStringCopy(saveScope
, token
->scope
);
810 /* If a scope has been set, add it to the name */
811 addToScope (name
, token
->scope
);
813 if (isType (name
, TOKEN_IDENTIFIER
))
816 if (isKeyword (token
, KEYWORD_is
))
819 addToScope (token
, name
->string
);
820 switch (token
->keyword
)
824 makeSqlTag (name
, SQLTAG_RECORD
);
829 makeSqlTag (name
, SQLTAG_TABLE
);
834 if (isKeyword (token
, KEYWORD_cursor
))
835 makeSqlTag (name
, SQLTAG_CURSOR
);
840 vStringClear (token
->scope
);
843 vStringCopy(token
->scope
, saveScope
);
845 vStringDelete(saveScope
);
848 static void parseSimple (tokenInfo
*const token
, const sqlKind kind
)
851 if (isType (token
, TOKEN_IDENTIFIER
) ||
852 isType (token
, TOKEN_STRING
))
853 makeSqlTag (token
, kind
);
856 static void parseDeclare (tokenInfo
*const token
, const boolean local
)
859 * PL/SQL declares are of this format:
869 if (isKeyword (token
, KEYWORD_declare
))
871 while (! isKeyword (token
, KEYWORD_begin
) && ! isKeyword (token
, KEYWORD_end
))
873 switch (token
->keyword
)
875 case KEYWORD_cursor
: parseSimple (token
, SQLTAG_CURSOR
); break;
876 case KEYWORD_function
: parseSubProgram (token
); break;
877 case KEYWORD_procedure
: parseSubProgram (token
); break;
878 case KEYWORD_subtype
: parseSimple (token
, SQLTAG_SUBTYPE
); break;
879 case KEYWORD_trigger
: parseSimple (token
, SQLTAG_TRIGGER
); break;
880 case KEYWORD_type
: parseType (token
); break;
883 if (isType (token
, TOKEN_IDENTIFIER
))
887 makeSqlTag (token
, SQLTAG_LOCAL_VARIABLE
);
889 makeSqlTag (token
, SQLTAG_VARIABLE
);
894 findToken (token
, TOKEN_SEMICOLON
);
899 static void parseDeclareANSI (tokenInfo
*const token
, const boolean local
)
901 tokenInfo
*const type
= newToken ();
903 * ANSI declares are of this format:
905 * DECLARE varname1 datatype;
906 * DECLARE varname2 datatype;
909 * This differ from PL/SQL where DECLARE preceeds the BEGIN block
910 * and the DECLARE keyword is not repeated.
912 while (isKeyword (token
, KEYWORD_declare
))
917 if (isKeyword (type
, KEYWORD_cursor
))
918 makeSqlTag (token
, SQLTAG_CURSOR
);
919 else if (isKeyword (token
, KEYWORD_local
) &&
920 isKeyword (type
, KEYWORD_temporary
))
923 * DECLARE LOCAL TEMPORARY TABLE table_name (
929 if (isKeyword (token
, KEYWORD_table
))
932 if (isType(token
, TOKEN_IDENTIFIER
) ||
933 isType(token
, TOKEN_STRING
) )
935 makeSqlTag (token
, SQLTAG_TABLE
);
939 else if (isType (token
, TOKEN_IDENTIFIER
) ||
940 isType (token
, TOKEN_STRING
))
943 makeSqlTag (token
, SQLTAG_LOCAL_VARIABLE
);
945 makeSqlTag (token
, SQLTAG_VARIABLE
);
947 findToken (token
, TOKEN_SEMICOLON
);
953 static void parseLabel (tokenInfo
*const token
)
956 * A label has this format:
957 * <<tobacco_dependency>>
959 * v_senator VARCHAR2(100) := 'THURMOND, JESSE';
961 * IF total_contributions (v_senator, 'TOBACCO') > 25000
963 * <<alochol_dependency>>
965 * v_senator VARCHAR2(100) := 'WHATEVERIT, TAKES';
970 Assert (isType (token
, TOKEN_BLOCK_LABEL_BEGIN
));
972 if (isType (token
, TOKEN_IDENTIFIER
))
974 makeSqlTag (token
, SQLTAG_BLOCK_LABEL
);
975 readToken (token
); /* read end of label */
979 static void parseStatements (tokenInfo
*const token
)
983 if (isType (token
, TOKEN_BLOCK_LABEL_BEGIN
))
987 switch (token
->keyword
)
991 * <exception handler>;
993 * Where an exception handler could be:
998 * In this case we need to skip this keyword and
999 * move on to the next token without reading until
1002 case KEYWORD_exception
:
1007 * WHEN statements can be used in exception clauses
1008 * and CASE statements. The CASE statement should skip
1009 * these given below we skip over to an END statement.
1010 * But for an exception clause, we can have:
1016 * If we skip to the TOKEN_SEMICOLON, we miss the begin
1017 * of a nested BEGIN END block. So read the next token
1018 * after the THEN and restart the LOOP.
1021 while (! isKeyword (token
, KEYWORD_then
))
1027 * We do not want to look for a ; since for an empty
1028 * IF block, that would skip over the END.
1033 while (! isKeyword (token
, KEYWORD_then
))
1035 /*readToken (token);*/
1036 parseStatements (token
);
1043 * FOR loop_name AS cursor_name CURSOR FOR ...
1050 parseStatements (token
);
1053 case KEYWORD_declare
:
1055 parseBlock (token
, TRUE
);
1062 findToken (token
, TOKEN_SEMICOLON
);
1065 } while (! isKeyword (token
, KEYWORD_end
));
1068 static void parseBlock (tokenInfo
*const token
, const boolean local
)
1070 if (isType (token
, TOKEN_BLOCK_LABEL_BEGIN
))
1075 if (! isKeyword (token
, KEYWORD_begin
))
1078 /*dispToken(token, "parseBlock calling parseDeclare");*/
1079 parseDeclare (token
, local
);
1081 if (isKeyword (token
, KEYWORD_begin
))
1084 parseDeclareANSI (token
, local
);
1085 while (! isKeyword (token
, KEYWORD_end
))
1086 parseStatements (token
);
1087 findToken (token
, TOKEN_SEMICOLON
);
1091 static void parsePackage (tokenInfo
*const token
)
1094 * Packages can be specified in a number of ways:
1095 * CREATE OR REPLACE PACKAGE pkg_name AS
1097 * CREATE OR REPLACE PACKAGE owner.pkg_name AS
1098 * or by specifying a package body
1099 * CREATE OR REPLACE PACKAGE BODY pkg_name AS
1100 * CREATE OR REPLACE PACKAGE BODY owner.pkg_name AS
1102 tokenInfo
*const name
= newToken ();
1104 if (isKeyword (name
, KEYWORD_body
))
1106 /*dispToken(token, "parsePackage after BODY");*/
1107 /* Chceck for owner.pkg_name */
1108 while (! isKeyword (token
, KEYWORD_is
))
1111 if ( isType(token
, TOKEN_PERIOD
) )
1114 /*dispToken(name, "parsePackage new name");*/
1117 dispToken(name
, "parsePackage name");
1118 if (isKeyword (token
, KEYWORD_is
))
1120 /*dispToken(token, "parsePackage processing IS");*/
1121 if (isType (name
, TOKEN_IDENTIFIER
) ||
1122 isType (name
, TOKEN_STRING
))
1123 makeSqlTag (name
, SQLTAG_PACKAGE
);
1124 parseBlock (token
, FALSE
);
1126 findToken (token
, TOKEN_SEMICOLON
);
1130 static void parseTable (tokenInfo
*const token
)
1132 tokenInfo
*const name
= newToken ();
1134 /* This deals with these formats
1135 * create table t1 (c1 int);
1136 * create global tempoary table t2 (c1 int);
1137 * create table "t3" (c1 int);
1138 * create table bob.t4 (c1 int);
1139 * create table bob."t5" (c1 int);
1140 * create table "bob"."t6" (c1 int);
1141 * create table bob."t7" (c1 int);
1142 * Proxy tables use this format:
1143 * create existing table bob."t7" AT '...'; */
1147 if (isType (token
, TOKEN_PERIOD
))
1152 if (isType (token
, TOKEN_OPEN_PAREN
))
1154 if (isType (name
, TOKEN_IDENTIFIER
) ||
1155 isType (name
, TOKEN_STRING
))
1157 makeSqlTag (name
, SQLTAG_TABLE
);
1158 vStringCopy(token
->scope
, name
->string
);
1159 parseRecord (token
);
1160 vStringClear (token
->scope
);
1162 } else if (isKeyword (token
, KEYWORD_at
))
1164 if (isType (name
, TOKEN_IDENTIFIER
))
1166 makeSqlTag (name
, SQLTAG_TABLE
);
1169 findToken (token
, TOKEN_SEMICOLON
);
1173 static void parseIndex (tokenInfo
*const token
)
1175 tokenInfo
*const name
= newToken ();
1176 tokenInfo
*const owner
= newToken ();
1178 /* This deals with these formats
1179 * create index i1 on t1(c1) create index "i2" on t1(c1)
1180 * create virtual unique clustered index "i3" on t1(c1)
1181 * create unique clustered index "i4" on t1(c1)
1182 * create clustered index "i5" on t1(c1)
1183 * create bitmap index "i6" on t1(c1) */
1187 if (isType (token
, TOKEN_PERIOD
))
1192 if ( isKeyword (token
, KEYWORD_on
) &&
1193 (isType (name
, TOKEN_IDENTIFIER
) || isType (name
, TOKEN_STRING
) ) )
1197 if (isType (token
, TOKEN_PERIOD
))
1202 addToScope(name
, owner
->string
);
1203 makeSqlTag (name
, SQLTAG_INDEX
);
1205 findToken (token
, TOKEN_SEMICOLON
);
1207 deleteToken (owner
);
1210 static void parseEvent (tokenInfo
*const token
)
1212 tokenInfo
*const name
= newToken ();
1214 /* This deals with these formats
1215 * create event e1 handler begin end;
1216 * create event "e2" handler begin end;
1217 * create event dba."e3" handler begin end;
1218 * create event "dba"."e4" handler begin end; */
1222 if (isType (token
, TOKEN_PERIOD
))
1226 while (! (isKeyword (token
, KEYWORD_handler
) ||
1227 (isType (token
, TOKEN_SEMICOLON
))) )
1232 if ( isKeyword (token
, KEYWORD_handler
) ||
1233 isType (token
, TOKEN_SEMICOLON
) )
1235 makeSqlTag (name
, SQLTAG_EVENT
);
1238 if (isKeyword (token
, KEYWORD_handler
))
1241 if ( isKeyword (token
, KEYWORD_begin
) )
1243 parseBlock (token
, TRUE
);
1245 findToken (token
, TOKEN_SEMICOLON
);
1250 static void parseTrigger (tokenInfo
*const token
)
1252 tokenInfo
*const name
= newToken ();
1253 tokenInfo
*const table
= newToken ();
1255 /* This deals with these formats
1256 * create or replace trigger tr1 begin end;
1257 * create trigger "tr2" begin end;
1258 * drop trigger "droptr1";
1259 * create trigger "tr3" CALL sp_something();
1260 * create trigger "owner"."tr4" begin end;
1261 * create trigger "tr5" not valid;
1262 * create trigger "tr6" begin end; */
1266 if (isType (token
, TOKEN_PERIOD
))
1272 while (! (isKeyword (token
, KEYWORD_on
) ||
1273 ( isType (token
, TOKEN_SEMICOLON
))) )
1278 if (! isType (token
, TOKEN_SEMICOLON
) )
1282 if (isType (token
, TOKEN_PERIOD
))
1288 while (! (isKeyword (token
, KEYWORD_begin
) ||
1289 (isKeyword (token
, KEYWORD_call
)) ||
1290 ( isType (token
, TOKEN_SEMICOLON
))) )
1293 if ( isKeyword (token
, KEYWORD_declare
) )
1295 addToScope(token
, name
->string
);
1296 parseDeclare(token
, TRUE
);
1297 vStringClear(token
->scope
);
1301 if ( isKeyword (token
, KEYWORD_begin
) ||
1302 isKeyword (token
, KEYWORD_call
) )
1304 addToScope(name
, table
->string
);
1305 makeSqlTag (name
, SQLTAG_TRIGGER
);
1306 addToScope(token
, table
->string
);
1307 if ( isKeyword (token
, KEYWORD_begin
) )
1309 parseBlock (token
, TRUE
);
1311 vStringClear(token
->scope
);
1315 if (! isType (token
, TOKEN_SEMICOLON
) )
1317 findToken (token
, TOKEN_SEMICOLON
);
1320 deleteToken (table
);
1323 static void parsePublication (tokenInfo
*const token
)
1325 tokenInfo
*const name
= newToken ();
1327 /* This deals with these formats
1328 * create or replace publication pu1 ()
1329 * create publication "pu2" ()
1330 * create publication dba."pu3" ()
1331 * create publication "dba"."pu4" () */
1335 if (isType (token
, TOKEN_PERIOD
))
1340 if (isType (token
, TOKEN_OPEN_PAREN
))
1342 if (isType (name
, TOKEN_IDENTIFIER
) ||
1343 isType (name
, TOKEN_STRING
))
1345 makeSqlTag (name
, SQLTAG_PUBLICATION
);
1348 findToken (token
, TOKEN_SEMICOLON
);
1352 static void parseService (tokenInfo
*const token
)
1354 tokenInfo
*const name
= newToken ();
1356 /* This deals with these formats
1357 * CREATE SERVICE s1 TYPE 'HTML'
1358 * AUTHORIZATION OFF USER DBA AS
1360 * FROM SYS.SYSTABLE;
1361 * CREATE SERVICE "s2" TYPE 'HTML'
1362 * AUTHORIZATION OFF USER DBA AS
1363 * CALL sp_Something(); */
1367 if (isKeyword (token
, KEYWORD_type
))
1369 if (isType (name
, TOKEN_IDENTIFIER
) ||
1370 isType (name
, TOKEN_STRING
))
1372 makeSqlTag (name
, SQLTAG_SERVICE
);
1375 findToken (token
, TOKEN_SEMICOLON
);
1379 static void parseDomain (tokenInfo
*const token
)
1381 tokenInfo
*const name
= newToken ();
1383 /* This deals with these formats
1384 * CREATE DOMAIN|DATATYPE [AS] your_name ...; */
1387 if (isKeyword (name
, KEYWORD_is
))
1392 if (isType (name
, TOKEN_IDENTIFIER
) ||
1393 isType (name
, TOKEN_STRING
))
1395 makeSqlTag (name
, SQLTAG_DOMAIN
);
1397 findToken (token
, TOKEN_SEMICOLON
);
1401 static void parseDrop (tokenInfo
*const token
)
1403 /* This deals with these formats
1404 * DROP TABLE|PROCEDURE|DOMAIN|DATATYPE name;
1406 * Just simply skip over these statements.
1407 * They are often confused with PROCEDURE prototypes
1408 * since the syntax is similar, this effectively deals with
1409 * the issue for all types. */
1411 /*dispToken(token, "parseDrop");*/
1412 findToken (token
, TOKEN_SEMICOLON
);
1415 static void parseVariable (tokenInfo
*const token
)
1417 tokenInfo
*const name
= newToken ();
1419 /* This deals with these formats
1420 * create variable varname1 integer;
1421 * create variable @varname2 integer;
1422 * create variable "varname3" integer;
1423 * drop variable @varname3; */
1427 if ( (isType (name
, TOKEN_IDENTIFIER
) || isType (name
, TOKEN_STRING
))
1428 && !isType (token
, TOKEN_SEMICOLON
) )
1430 makeSqlTag (name
, SQLTAG_VARIABLE
);
1432 if (! isType (token
, TOKEN_SEMICOLON
) )
1433 findToken (token
, TOKEN_SEMICOLON
);
1438 static void parseSynonym (tokenInfo
*const token
)
1440 tokenInfo
*const name
= newToken ();
1442 /* This deals with these formats
1443 * create variable varname1 integer;
1444 * create variable @varname2 integer;
1445 * create variable "varname3" integer;
1446 * drop variable @varname3; */
1450 if ( (isType (name
, TOKEN_IDENTIFIER
) || isType (name
, TOKEN_STRING
))
1451 && isKeyword (token
, KEYWORD_for
) )
1453 makeSqlTag (name
, SQLTAG_SYNONYM
);
1455 if (! isType (token
, TOKEN_SEMICOLON
) )
1456 findToken (token
, TOKEN_SEMICOLON
);
1461 static void parseView (tokenInfo
*const token
)
1463 tokenInfo
*const name
= newToken ();
1465 /* This deals with these formats
1466 * create variable varname1 integer;
1467 * create variable @varname2 integer;
1468 * create variable "varname3" integer;
1469 * drop variable @varname3; */
1473 if (isType (token
, TOKEN_PERIOD
))
1478 if ( isType (token
, TOKEN_OPEN_PAREN
) )
1480 skipArgumentList(token
);
1484 while (!(isKeyword (token
, KEYWORD_is
) ||
1485 isType (token
, TOKEN_SEMICOLON
)
1491 if ( (isType (name
, TOKEN_IDENTIFIER
) || isType (name
, TOKEN_STRING
))
1492 && isKeyword (token
, KEYWORD_is
) )
1494 makeSqlTag (name
, SQLTAG_VIEW
);
1497 if (! isType (token
, TOKEN_SEMICOLON
) )
1498 findToken (token
, TOKEN_SEMICOLON
);
1503 static void parseMLTable (tokenInfo
*const token
)
1505 tokenInfo
*const version
= newToken ();
1506 tokenInfo
*const table
= newToken ();
1507 tokenInfo
*const event
= newToken ();
1509 /* This deals with these formats
1510 * call ml_add_table_script( 'version', 'table_name', 'event',
1511 * 'some SQL statement'
1515 if ( isType (token
, TOKEN_OPEN_PAREN
) )
1517 readToken (version
);
1519 while (!(isType (token
, TOKEN_COMMA
) ||
1520 isType (token
, TOKEN_CLOSE_PAREN
)
1526 if (isType (token
, TOKEN_COMMA
))
1530 while (!(isType (token
, TOKEN_COMMA
) ||
1531 isType (token
, TOKEN_CLOSE_PAREN
)
1537 if (isType (token
, TOKEN_COMMA
))
1541 if (isType (version
, TOKEN_STRING
) &&
1542 isType (table
, TOKEN_STRING
) &&
1543 isType (event
, TOKEN_STRING
) )
1545 addToScope(version
, table
->string
);
1546 addToScope(version
, event
->string
);
1547 makeSqlTag (version
, SQLTAG_MLTABLE
);
1550 if( !isType (token
, TOKEN_CLOSE_PAREN
) )
1551 findToken (token
, TOKEN_CLOSE_PAREN
);
1555 if (! isType (token
, TOKEN_SEMICOLON
) )
1556 findToken (token
, TOKEN_SEMICOLON
);
1558 deleteToken (version
);
1559 deleteToken (table
);
1560 deleteToken (event
);
1563 static void parseMLConn (tokenInfo
*const token
)
1565 tokenInfo
*const version
= newToken ();
1566 tokenInfo
*const event
= newToken ();
1568 /* This deals with these formats
1569 * call ml_add_connection_script( 'version', 'event',
1570 * 'some SQL statement'
1574 if ( isType (token
, TOKEN_OPEN_PAREN
) )
1576 readToken (version
);
1578 while (!(isType (token
, TOKEN_COMMA
) ||
1579 isType (token
, TOKEN_CLOSE_PAREN
)
1585 if (isType (token
, TOKEN_COMMA
))
1589 if (isType (version
, TOKEN_STRING
) &&
1590 isType (event
, TOKEN_STRING
) )
1592 addToScope(version
, event
->string
);
1593 makeSqlTag (version
, SQLTAG_MLCONN
);
1596 if( !isType (token
, TOKEN_CLOSE_PAREN
) )
1597 findToken (token
, TOKEN_CLOSE_PAREN
);
1601 if (! isType (token
, TOKEN_SEMICOLON
) )
1602 findToken (token
, TOKEN_SEMICOLON
);
1604 deleteToken (version
);
1605 deleteToken (event
);
1609 static void parseSqlFile (tokenInfo
*const token
)
1614 /*dispToken(token, "psf");*/
1616 if (isType (token
, TOKEN_BLOCK_LABEL_BEGIN
))
1618 else switch (token
->keyword
)
1620 case KEYWORD_begin
: parseBlock (token
, FALSE
); break;
1621 case KEYWORD_cursor
: parseSimple (token
, SQLTAG_CURSOR
); break;
1622 case KEYWORD_datatype
: parseDomain (token
); break;
1623 case KEYWORD_declare
: parseBlock (token
, FALSE
); break;
1624 case KEYWORD_domain
: parseDomain (token
); break;
1625 case KEYWORD_drop
: parseDrop (token
); break;
1626 case KEYWORD_event
: parseEvent (token
); break;
1627 case KEYWORD_function
: parseSubProgram (token
); break;
1628 case KEYWORD_index
: parseIndex (token
); break;
1629 case KEYWORD_ml_table
: parseMLTable (token
); break;
1630 case KEYWORD_ml_conn
: parseMLConn (token
); break;
1631 case KEYWORD_package
: parsePackage (token
); break;
1632 case KEYWORD_procedure
: parseSubProgram (token
); break;
1633 case KEYWORD_publication
: parsePublication (token
); break;
1634 case KEYWORD_service
: parseService (token
); break;
1635 case KEYWORD_subtype
: parseSimple (token
, SQLTAG_SUBTYPE
); break;
1636 case KEYWORD_synonym
: parseSynonym (token
); break;
1637 case KEYWORD_table
: parseTable (token
); break;
1638 case KEYWORD_trigger
: parseTrigger (token
); break;
1639 case KEYWORD_type
: parseType (token
); break;
1640 case KEYWORD_variable
: parseVariable (token
); break;
1641 case KEYWORD_view
: parseView (token
); break;
1644 } while (! isKeyword (token
, KEYWORD_end
));
1647 static void initialize (const langType language
)
1649 Assert (sizeof (SqlKinds
) / sizeof (SqlKinds
[0]) == SQLTAG_COUNT
);
1650 Lang_sql
= language
;
1651 buildSqlKeywordHash ();
1654 static void findSqlTags (void)
1656 tokenInfo
*const token
= newToken ();
1657 exception_t exception
= (exception_t
) (setjmp (Exception
));
1658 while (exception
== ExceptionNone
)
1659 parseSqlFile (token
);
1660 deleteToken (token
);
1663 extern parserDefinition
* SqlParser (void)
1665 static const char *const extensions
[] = { "sql", NULL
};
1666 parserDefinition
* def
= parserNew ("SQL");
1667 def
->kinds
= SqlKinds
;
1668 def
->kindCount
= KIND_COUNT (SqlKinds
);
1669 def
->extensions
= extensions
;
1670 def
->parser
= findSqlTags
;
1671 def
->initialize
= initialize
;
1675 /* vi:set tabstop=8 shiftwidth=4: */