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 PL/SQL Reference Guide:
33 * http://info-it.umsystem.edu/oradocs/doc/server/doc/PLS23/toc.htm
35 * Sample PL/SQL code is available from:
36 * http://www.orafaq.com/faqscrpt.htm#GENPLSQL
42 #define isType(token,t) (boolean) ((token)->type == (t))
43 #define isKeyword(token,k) (boolean) ((token)->keyword == (k))
49 typedef enum eException
{ ExceptionNone
, ExceptionEOF
} exception_t
;
51 /* Used to specify type of keyword.
53 typedef enum eKeywordId
{
109 /* Used to determine whether keyword is valid for the token language and
112 typedef struct sKeywordDesc
{
117 typedef enum eTokenType
{
119 TOKEN_BLOCK_LABEL_BEGIN
,
120 TOKEN_BLOCK_LABEL_END
,
134 typedef struct sTokenInfo
{
139 unsigned long lineNumber
;
141 int bufferPosition
; /* buffer position of line containing name */
148 static langType Lang_sql
;
150 static jmp_buf Exception
;
157 SQLTAG_LOCAL_VARIABLE
,
178 static kindOption SqlKinds
[] = {
179 { TRUE
, 'c', "cursor", "cursors" },
180 { FALSE
, 'd', "prototype", "prototypes" },
181 { TRUE
, 'f', "function", "functions" },
182 { TRUE
, 'F', "field", "record fields" },
183 { FALSE
, 'l', "local", "local variables" },
184 { TRUE
, 'L', "label", "block label" },
185 { TRUE
, 'P', "package", "packages" },
186 { TRUE
, 'p', "procedure", "procedures" },
187 { FALSE
, 'r', "record", "records" },
188 { TRUE
, 's', "subtype", "subtypes" },
189 { TRUE
, 't', "table", "tables" },
190 { TRUE
, 'T', "trigger", "triggers" },
191 { TRUE
, 'v', "variable", "variables" },
192 { TRUE
, 'i', "index", "indexes" },
193 { TRUE
, 'e', "event", "events" },
194 { TRUE
, 'U', "publication", "publications" },
195 { TRUE
, 'R', "service", "services" },
196 { TRUE
, 'D', "domain", "domains" },
197 { TRUE
, 'V', "view", "views" },
198 { TRUE
, 'n', "synonym", "synonyms" },
199 { TRUE
, 'x', "mltable", "MobiLink Table Scripts" },
200 { TRUE
, 'y', "mlconn", "MobiLink Conn Scripts" }
203 static const keywordDesc SqlKeywordTable
[] = {
204 /* keyword keyword ID */
205 { "as", KEYWORD_is
},
206 { "begin", KEYWORD_begin
},
207 { "body", KEYWORD_body
},
208 { "cursor", KEYWORD_cursor
},
209 { "declare", KEYWORD_declare
},
210 { "end", KEYWORD_end
},
211 { "function", KEYWORD_function
},
212 { "if", KEYWORD_if
},
213 { "is", KEYWORD_is
},
214 { "loop", KEYWORD_loop
},
215 { "case", KEYWORD_case
},
216 { "for", KEYWORD_for
},
217 { "call", KEYWORD_call
},
218 { "package", KEYWORD_package
},
219 { "pragma", KEYWORD_pragma
},
220 { "procedure", KEYWORD_procedure
},
221 { "record", KEYWORD_record
},
222 { "object", KEYWORD_object
},
223 { "ref", KEYWORD_ref
},
224 { "rem", KEYWORD_rem
},
225 { "return", KEYWORD_return
},
226 { "returns", KEYWORD_returns
},
227 { "subtype", KEYWORD_subtype
},
228 { "table", KEYWORD_table
},
229 { "trigger", KEYWORD_trigger
},
230 { "type", KEYWORD_type
},
231 { "index", KEYWORD_index
},
232 { "event", KEYWORD_event
},
233 { "publication", KEYWORD_publication
},
234 { "service", KEYWORD_service
},
235 { "domain", KEYWORD_domain
},
236 { "datatype", KEYWORD_datatype
},
237 { "result", KEYWORD_result
},
238 { "when", KEYWORD_when
},
239 { "then", KEYWORD_then
},
240 { "variable", KEYWORD_variable
},
241 { "exception", KEYWORD_exception
},
242 { "at", KEYWORD_at
},
243 { "on", KEYWORD_on
},
244 { "primary", KEYWORD_primary
},
245 { "references", KEYWORD_references
},
246 { "unique", KEYWORD_unique
},
247 { "check", KEYWORD_check
},
248 { "constraint", KEYWORD_constraint
},
249 { "foreign", KEYWORD_foreign
},
250 { "ml_add_table_script", KEYWORD_ml_table
},
251 { "ml_add_connection_script", KEYWORD_ml_conn
},
252 { "local", KEYWORD_local
},
253 { "temporary", KEYWORD_temporary
},
254 { "drop", KEYWORD_drop
},
255 { "view", KEYWORD_view
},
256 { "synonym", KEYWORD_synonym
},
257 { "handler", KEYWORD_handler
}
261 * FUNCTION DECLARATIONS
264 static void parseBlock (tokenInfo
*const token
, const boolean local
);
265 static void makeConstTag (tokenInfo
*const token
, const sqlKind kind
);
271 static void dispToken (tokenInfo
*const token
, char * location
)
274 if ( isKeyword(token
, KEYWORD_NONE
) )
276 if ( isType(token
, TOKEN_IDENTIFIER
) || isType(token
, TOKEN_STRING
) )
278 printf( "\n%s: token string t:%s s:%s l:%lu p:%d\n"
280 , vStringValue(token
->string
)
281 , vStringValue(token
->scope
)
283 , token
->bufferPosition
286 printf( "\n%s: token t:%d s:%s l:%lu p:%d\n"
289 , vStringValue(token
->scope
)
291 , token
->bufferPosition
295 printf( "\n%s: keyword:%s k:%d s:%s l:%lu p:%d\n"
297 , vStringValue(token
->string
)
299 , vStringValue(token
->scope
)
301 , token
->bufferPosition
308 * FUNCTION DEFINITIONS
311 static boolean
isIdentChar1 (const int c
)
313 /* Other databases are less restrictive on the first character of
315 * isIdentChar1 is used to identify the first character of an
316 * identifier, so we are removing some restrictions. */
318 (isalpha (c
) || c
== '@' || c
== '_' );
321 static boolean
isIdentChar (const int c
)
324 (isalpha (c
) || isdigit (c
) || c
== '$' ||
325 c
== '@' || c
== '_' || c
== '#');
328 static void buildSqlKeywordHash (void)
330 const size_t count
= sizeof (SqlKeywordTable
) /
331 sizeof (SqlKeywordTable
[0]);
333 for (i
= 0 ; i
< count
; ++i
)
335 const keywordDesc
* const p
= &SqlKeywordTable
[i
];
336 addKeyword (p
->name
, Lang_sql
, (int) p
->id
);
340 static tokenInfo
*newToken (void)
342 tokenInfo
*const token
= xMalloc(1, tokenInfo
);
344 token
->type
= TOKEN_UNDEFINED
;
345 token
->keyword
= KEYWORD_NONE
;
346 token
->string
= vStringNew ();
347 token
->scope
= vStringNew ();
352 static void deleteToken (tokenInfo
*const token
)
354 vStringDelete (token
->string
);
359 * Tag generation functions
362 static void makeSqlTag (tokenInfo
*const token
, const sqlKind kind
)
366 if (SqlKinds
[kind
].enabled
)
369 * If a scope has been added to the token, change the token
370 * string to include the scope when making the tag.
372 if ( vStringLength(token
->scope
) > 0 )
374 fulltag
= vStringNew ();
375 vStringCopy(fulltag
, token
->scope
);
376 vStringCatS (fulltag
, ".");
377 vStringCatS (fulltag
, vStringValue(token
->string
));
378 vStringTerminate(fulltag
);
379 vStringCopy(token
->string
, fulltag
);
380 vStringDelete (fulltag
);
382 makeConstTag (token
, kind
);
386 static void makeConstTag (tokenInfo
*const token
, const sqlKind kind
)
388 if (SqlKinds
[kind
].enabled
)
390 const char *const name
= vStringValue (token
->string
);
392 initTagEntry (&e
, name
);
394 e
.lineNumber
= token
->lineNumber
;
396 e
.filePosition
= token
->filePosition
;
398 e
.bufferPosition
= token
->bufferPosition
;
399 e
.kindName
= SqlKinds
[kind
].name
;
400 e
.kind
= SqlKinds
[kind
].letter
;
410 static int skipToCharacter (const int c
)
416 } while (d
!= EOF
&& d
!= c
);
420 static void parseString (vString
*const string
, const int delimiter
)
427 /* printf( "\nps: %c\n", c ); */
430 else if (c
== delimiter
)
433 vStringPut (string
, c
);
435 vStringTerminate (string
);
438 /* Read a C identifier beginning with "firstChar" and places it into "name".
440 static void parseIdentifier (vString
*const string
, const int firstChar
)
443 Assert (isIdentChar1 (c
));
446 vStringPut (string
, c
);
448 } while (isIdentChar (c
));
449 vStringTerminate (string
);
451 fileUngetc (c
); /* unget non-identifier character */
454 static keywordId
analyzeToken (vString
*const name
)
456 static vString
*keyword
= NULL
;
458 keyword
= vStringNew ();
459 vStringCopyToLower (keyword
, name
);
460 return (keywordId
) lookupKeyword (vStringValue (keyword
), Lang_sql
);
463 static void readToken (tokenInfo
*const token
)
467 token
->type
= TOKEN_UNDEFINED
;
468 token
->keyword
= KEYWORD_NONE
;
469 vStringClear (token
->string
);
475 /* printf( "\nrtc: %c\n", c ); */
477 * Added " to the list of ignores, not sure what this
478 * might break but it gets by this issue:
479 * create table "t1" (...)
482 while (c
== '\t' || c
== ' ' || c
== '\n');
486 case EOF
: longjmp (Exception
, (int)ExceptionEOF
); break;
487 case '(': token
->type
= TOKEN_OPEN_PAREN
; break;
488 case ')': token
->type
= TOKEN_CLOSE_PAREN
; break;
489 case ';': token
->type
= TOKEN_SEMICOLON
; break;
490 case '.': token
->type
= TOKEN_PERIOD
; break;
491 case ',': token
->type
= TOKEN_COMMA
; break;
495 token
->type
= TOKEN_STRING
;
496 parseString (token
->string
, c
);
497 token
->lineNumber
= getSourceLineNumber ();
499 token
->filePosition
= getInputFilePosition ();
501 token
->bufferPosition
= getInputBufferPosition ();
506 if (c
== '-') /* is this the start of a comment? */
508 skipToCharacter ('\n');
515 token
->type
= TOKEN_OPERATOR
;
522 const int initial
= c
;
527 token
->type
= TOKEN_BLOCK_LABEL_BEGIN
;
529 token
->type
= TOKEN_BLOCK_LABEL_END
;
534 token
->type
= TOKEN_UNDEFINED
;
542 if (d
!= '*') /* is this the start of a comment? */
548 skipToCharacter ('*');
554 } while (c
!= EOF
&& c
!= '\0');
561 if (! isIdentChar1 (c
))
562 token
->type
= TOKEN_UNDEFINED
;
565 parseIdentifier (token
->string
, c
);
566 token
->lineNumber
= getSourceLineNumber ();
568 token
->filePosition
= getInputFilePosition ();
570 token
->bufferPosition
= getInputBufferPosition ();
571 token
->keyword
= analyzeToken (token
->string
);
572 if (isKeyword (token
, KEYWORD_rem
))
574 vStringClear (token
->string
);
575 skipToCharacter ('\n');
578 else if (isKeyword (token
, KEYWORD_NONE
))
579 token
->type
= TOKEN_IDENTIFIER
;
581 token
->type
= TOKEN_KEYWORD
;
585 /*dispToken(token, "rte");*/
589 * Token parsing functions
592 /* - unused - I don't know (enrico)
593 static void addContext (tokenInfo* const parent, const tokenInfo* const child)
595 if (vStringLength (parent->string) > 0)
597 vStringCatS (parent->string, ".");
599 vStringCatS (parent->string, vStringValue(child->string));
600 vStringTerminate(parent->string);
603 static void addToScope (tokenInfo
* const token
, vString
* const extra
)
605 if (vStringLength (token
->scope
) > 0)
607 vStringCatS (token
->scope
, ".");
609 vStringCatS (token
->scope
, vStringValue(extra
));
610 vStringTerminate(token
->scope
);
617 static void findToken (tokenInfo
*const token
, const tokenType type
)
619 while (! isType (token
, type
))
625 static void skipArgumentList (tokenInfo
*const token
)
630 * Other databases can have arguments with fully declared
632 * ( name varchar(30), text binary(10) )
633 * So we must check for nested open and closing parantheses
636 if (isType (token
, TOKEN_OPEN_PAREN
)) /* arguments? */
639 /*findToken (token, TOKEN_CLOSE_PAREN);*/
640 while (! (isType (token
, TOKEN_CLOSE_PAREN
) && (nest_level
== 0)))
643 if (isType (token
, TOKEN_OPEN_PAREN
))
647 if (isType (token
, TOKEN_CLOSE_PAREN
))
659 static void parseSubProgram (tokenInfo
*const token
)
661 tokenInfo
*const name
= newToken ();
665 * FUNCTION func_name RETURN integer;
666 * PROCEDURE proc_name( parameters );
668 * FUNCTION GET_ML_USERNAME RETURN VARCHAR2
671 * RETURN v_sync_user_id;
672 * END GET_ML_USERNAME;
674 * PROCEDURE proc_name( parameters )
678 * CREATE PROCEDURE proc_name( parameters )
679 * EXTERNAL NAME ... ;
680 * CREATE PROCEDURE proc_name( parameters )
684 * CREATE FUNCTION f_GetClassName(
685 * IN @object VARCHAR(128)
686 * ,IN @code VARCHAR(128)
688 * RETURNS VARCHAR(200)
692 * IF( @object = 'user_state' ) THEN
693 * SET something = something;
699 const sqlKind kind
= isKeyword (token
, KEYWORD_function
) ?
700 SQLTAG_FUNCTION
: SQLTAG_PROCEDURE
;
701 Assert (isKeyword (token
, KEYWORD_function
) ||
702 isKeyword (token
, KEYWORD_procedure
));
705 if (isType (token
, TOKEN_PERIOD
))
710 skipArgumentList (token
);
712 if (kind
== SQLTAG_FUNCTION
)
714 if (isKeyword (token
, KEYWORD_return
))
722 if( isType (token
, TOKEN_SEMICOLON
) )
724 makeSqlTag (name
, SQLTAG_PROTOTYPE
);
726 while (!(isKeyword (token
, KEYWORD_is
) ||
727 isKeyword (token
, KEYWORD_begin
) ||
728 isType (token
, TOKEN_SEMICOLON
)
731 readToken (token
); /* read return type */
732 if (isKeyword (token
, KEYWORD_is
) ||
733 isKeyword (token
, KEYWORD_begin
) )
735 addToScope(token
, name
->string
);
736 if (isType (name
, TOKEN_IDENTIFIER
) ||
737 isType (name
, TOKEN_STRING
))
738 makeSqlTag (name
, kind
);
740 /*dispToken(name, "SubProgram: parseBlock name");*/
741 /*dispToken(token, "SubProgram: parseBlock token");*/
742 parseBlock (token
, TRUE
);
743 vStringClear (token
->scope
);
749 static void parseRecord (tokenInfo
*const token
)
751 /* Make it a bit forgiving, this is called from
752 * multiple functions, parseTable, parseType */
753 if (!isType (token
, TOKEN_OPEN_PAREN
))
756 Assert (isType (token
, TOKEN_OPEN_PAREN
));
759 if ( isType (token
, TOKEN_COMMA
) || isType (token
, TOKEN_OPEN_PAREN
) )
763 * Create table statements can end with various constraints
764 * which must be excluded from the SQLTAG_FIELD.
770 * constraint whatever,
776 if (! (isKeyword(token
, KEYWORD_primary
) ||
777 isKeyword(token
, KEYWORD_references
) ||
778 isKeyword(token
, KEYWORD_unique
) ||
779 isKeyword(token
, KEYWORD_check
) ||
780 isKeyword(token
, KEYWORD_constraint
) ||
781 isKeyword(token
, KEYWORD_foreign
) ) )
783 if (isType (token
, TOKEN_IDENTIFIER
) ||
784 isType (token
, TOKEN_STRING
))
785 makeSqlTag (token
, SQLTAG_FIELD
);
788 while (!(isType (token
, TOKEN_COMMA
) ||
789 isType (token
, TOKEN_CLOSE_PAREN
) ||
790 isType (token
, TOKEN_OPEN_PAREN
)
795 * A table structure can look like this:
802 * We can't just look for a COMMA or CLOSE_PAREN
803 * since that will not deal with the numeric(10,5)
804 * case. So we need to skip the argument list
805 * when we find an open paren.
807 if (isType (token
, TOKEN_OPEN_PAREN
))
809 /* Reads to the next token after the TOKEN_CLOSE_PAREN */
810 skipArgumentList(token
);
813 } while (! isType (token
, TOKEN_CLOSE_PAREN
));
816 static void parseType (tokenInfo
*const token
)
818 tokenInfo
*const name
= newToken ();
819 vString
* saveScope
= vStringNew ();
821 vStringCopy(saveScope
, token
->scope
);
822 /* If a scope has been set, add it to the name */
823 addToScope (name
, token
->scope
);
825 if (isType (name
, TOKEN_IDENTIFIER
))
828 if (isKeyword (token
, KEYWORD_is
))
831 addToScope (token
, name
->string
);
832 switch (token
->keyword
)
836 makeSqlTag (name
, SQLTAG_RECORD
);
841 makeSqlTag (name
, SQLTAG_TABLE
);
846 if (isKeyword (token
, KEYWORD_cursor
))
847 makeSqlTag (name
, SQLTAG_CURSOR
);
852 vStringClear (token
->scope
);
855 vStringCopy(token
->scope
, saveScope
);
857 vStringDelete(saveScope
);
860 static void parseSimple (tokenInfo
*const token
, const sqlKind kind
)
863 if (isType (token
, TOKEN_IDENTIFIER
) ||
864 isType (token
, TOKEN_STRING
))
865 makeSqlTag (token
, kind
);
868 static void parseDeclare (tokenInfo
*const token
, const boolean local
)
871 * PL/SQL declares are of this format:
881 if (isKeyword (token
, KEYWORD_declare
))
883 while (! isKeyword (token
, KEYWORD_begin
) && ! isKeyword (token
, KEYWORD_end
))
885 switch (token
->keyword
)
887 case KEYWORD_cursor
: parseSimple (token
, SQLTAG_CURSOR
); break;
888 case KEYWORD_function
: parseSubProgram (token
); break;
889 case KEYWORD_procedure
: parseSubProgram (token
); break;
890 case KEYWORD_subtype
: parseSimple (token
, SQLTAG_SUBTYPE
); break;
891 case KEYWORD_trigger
: parseSimple (token
, SQLTAG_TRIGGER
); break;
892 case KEYWORD_type
: parseType (token
); break;
895 if (isType (token
, TOKEN_IDENTIFIER
))
899 makeSqlTag (token
, SQLTAG_LOCAL_VARIABLE
);
901 makeSqlTag (token
, SQLTAG_VARIABLE
);
906 findToken (token
, TOKEN_SEMICOLON
);
911 static void parseDeclareANSI (tokenInfo
*const token
, const boolean local
)
913 tokenInfo
*const type
= newToken ();
915 * ANSI declares are of this format:
917 * DECLARE varname1 datatype;
918 * DECLARE varname2 datatype;
921 * This differ from PL/SQL where DECLARE preceeds the BEGIN block
922 * and the DECLARE keyword is not repeated.
924 while (isKeyword (token
, KEYWORD_declare
))
929 if (isKeyword (type
, KEYWORD_cursor
))
930 makeSqlTag (token
, SQLTAG_CURSOR
);
931 else if (isKeyword (token
, KEYWORD_local
) &&
932 isKeyword (type
, KEYWORD_temporary
))
935 * DECLARE LOCAL TEMPORARY TABLE table_name (
941 if (isKeyword (token
, KEYWORD_table
))
944 if (isType(token
, TOKEN_IDENTIFIER
) ||
945 isType(token
, TOKEN_STRING
) )
947 makeSqlTag (token
, SQLTAG_TABLE
);
951 else if (isType (token
, TOKEN_IDENTIFIER
) ||
952 isType (token
, TOKEN_STRING
))
955 makeSqlTag (token
, SQLTAG_LOCAL_VARIABLE
);
957 makeSqlTag (token
, SQLTAG_VARIABLE
);
959 findToken (token
, TOKEN_SEMICOLON
);
965 static void parseLabel (tokenInfo
*const token
)
968 * A label has this format:
969 * <<tobacco_dependency>>
971 * v_senator VARCHAR2(100) := 'THURMOND, JESSE';
973 * IF total_contributions (v_senator, 'TOBACCO') > 25000
975 * <<alochol_dependency>>
977 * v_senator VARCHAR2(100) := 'WHATEVERIT, TAKES';
982 Assert (isType (token
, TOKEN_BLOCK_LABEL_BEGIN
));
984 if (isType (token
, TOKEN_IDENTIFIER
))
986 makeSqlTag (token
, SQLTAG_BLOCK_LABEL
);
987 readToken (token
); /* read end of label */
991 static void parseStatements (tokenInfo
*const token
)
995 if (isType (token
, TOKEN_BLOCK_LABEL_BEGIN
))
999 switch (token
->keyword
)
1003 * <exception handler>;
1005 * Where an exception handler could be:
1010 * In this case we need to skip this keyword and
1011 * move on to the next token without reading until
1014 case KEYWORD_exception
:
1019 * WHEN statements can be used in exception clauses
1020 * and CASE statements. The CASE statement should skip
1021 * these given below we skip over to an END statement.
1022 * But for an exception clause, we can have:
1028 * If we skip to the TOKEN_SEMICOLON, we miss the begin
1029 * of a nested BEGIN END block. So read the next token
1030 * after the THEN and restart the LOOP.
1033 while (! isKeyword (token
, KEYWORD_then
))
1039 * We do not want to look for a ; since for an empty
1040 * IF block, that would skip over the END.
1045 while (! isKeyword (token
, KEYWORD_then
))
1047 /*readToken (token);*/
1048 parseStatements (token
);
1055 * FOR loop_name AS cursor_name CURSOR FOR ...
1062 parseStatements (token
);
1065 case KEYWORD_declare
:
1067 parseBlock (token
, TRUE
);
1074 findToken (token
, TOKEN_SEMICOLON
);
1077 } while (! isKeyword (token
, KEYWORD_end
));
1080 static void parseBlock (tokenInfo
*const token
, const boolean local
)
1082 if (isType (token
, TOKEN_BLOCK_LABEL_BEGIN
))
1087 if (! isKeyword (token
, KEYWORD_begin
))
1090 /*dispToken(token, "parseBlock calling parseDeclare");*/
1091 parseDeclare (token
, local
);
1093 if (isKeyword (token
, KEYWORD_begin
))
1096 parseDeclareANSI (token
, local
);
1097 while (! isKeyword (token
, KEYWORD_end
))
1098 parseStatements (token
);
1099 findToken (token
, TOKEN_SEMICOLON
);
1103 static void parsePackage (tokenInfo
*const token
)
1106 * Packages can be specified in a number of ways:
1107 * CREATE OR REPLACE PACKAGE pkg_name AS
1109 * CREATE OR REPLACE PACKAGE owner.pkg_name AS
1110 * or by specifying a package body
1111 * CREATE OR REPLACE PACKAGE BODY pkg_name AS
1112 * CREATE OR REPLACE PACKAGE BODY owner.pkg_name AS
1114 tokenInfo
*const name
= newToken ();
1116 if (isKeyword (name
, KEYWORD_body
))
1118 /*dispToken(token, "parsePackage after BODY");*/
1119 /* Chceck for owner.pkg_name */
1120 while (! isKeyword (token
, KEYWORD_is
))
1123 if ( isType(token
, TOKEN_PERIOD
) )
1126 /*dispToken(name, "parsePackage new name");*/
1129 dispToken(name
, "parsePackage name");
1130 if (isKeyword (token
, KEYWORD_is
))
1132 /*dispToken(token, "parsePackage processing IS");*/
1133 if (isType (name
, TOKEN_IDENTIFIER
) ||
1134 isType (name
, TOKEN_STRING
))
1135 makeSqlTag (name
, SQLTAG_PACKAGE
);
1136 parseBlock (token
, FALSE
);
1138 findToken (token
, TOKEN_SEMICOLON
);
1142 static void parseTable (tokenInfo
*const token
)
1144 tokenInfo
*const name
= newToken ();
1146 /* This deals with these formats
1147 * create table t1 (c1 int);
1148 * create global tempoary table t2 (c1 int);
1149 * create table "t3" (c1 int);
1150 * create table bob.t4 (c1 int);
1151 * create table bob."t5" (c1 int);
1152 * create table "bob"."t6" (c1 int);
1153 * create table bob."t7" (c1 int);
1154 * Proxy tables use this format:
1155 * create existing table bob."t7" AT '...'; */
1159 if (isType (token
, TOKEN_PERIOD
))
1164 if (isType (token
, TOKEN_OPEN_PAREN
))
1166 if (isType (name
, TOKEN_IDENTIFIER
) ||
1167 isType (name
, TOKEN_STRING
))
1169 makeSqlTag (name
, SQLTAG_TABLE
);
1170 vStringCopy(token
->scope
, name
->string
);
1171 parseRecord (token
);
1172 vStringClear (token
->scope
);
1174 } else if (isKeyword (token
, KEYWORD_at
))
1176 if (isType (name
, TOKEN_IDENTIFIER
))
1178 makeSqlTag (name
, SQLTAG_TABLE
);
1181 findToken (token
, TOKEN_SEMICOLON
);
1185 static void parseIndex (tokenInfo
*const token
)
1187 tokenInfo
*const name
= newToken ();
1188 tokenInfo
*const owner
= newToken ();
1190 /* This deals with these formats
1191 * create index i1 on t1(c1) create index "i2" on t1(c1)
1192 * create virtual unique clustered index "i3" on t1(c1)
1193 * create unique clustered index "i4" on t1(c1)
1194 * create clustered index "i5" on t1(c1)
1195 * create bitmap index "i6" on t1(c1) */
1199 if (isType (token
, TOKEN_PERIOD
))
1204 if ( isKeyword (token
, KEYWORD_on
) &&
1205 (isType (name
, TOKEN_IDENTIFIER
) || isType (name
, TOKEN_STRING
) ) )
1209 if (isType (token
, TOKEN_PERIOD
))
1214 addToScope(name
, owner
->string
);
1215 makeSqlTag (name
, SQLTAG_INDEX
);
1217 findToken (token
, TOKEN_SEMICOLON
);
1219 deleteToken (owner
);
1222 static void parseEvent (tokenInfo
*const token
)
1224 tokenInfo
*const name
= newToken ();
1226 /* This deals with these formats
1227 * create event e1 handler begin end;
1228 * create event "e2" handler begin end;
1229 * create event dba."e3" handler begin end;
1230 * create event "dba"."e4" handler begin end; */
1234 if (isType (token
, TOKEN_PERIOD
))
1238 while (! (isKeyword (token
, KEYWORD_handler
) ||
1239 (isType (token
, TOKEN_SEMICOLON
))) )
1244 if ( isKeyword (token
, KEYWORD_handler
) ||
1245 isType (token
, TOKEN_SEMICOLON
) )
1247 makeSqlTag (name
, SQLTAG_EVENT
);
1250 if (isKeyword (token
, KEYWORD_handler
))
1253 if ( isKeyword (token
, KEYWORD_begin
) )
1255 parseBlock (token
, TRUE
);
1257 findToken (token
, TOKEN_SEMICOLON
);
1262 static void parseTrigger (tokenInfo
*const token
)
1264 tokenInfo
*const name
= newToken ();
1265 tokenInfo
*const table
= newToken ();
1267 /* This deals with these formats
1268 * create or replace trigger tr1 begin end;
1269 * create trigger "tr2" begin end;
1270 * drop trigger "droptr1";
1271 * create trigger "tr3" CALL sp_something();
1272 * create trigger "owner"."tr4" begin end;
1273 * create trigger "tr5" not valid;
1274 * create trigger "tr6" begin end; */
1278 if (isType (token
, TOKEN_PERIOD
))
1284 while (! (isKeyword (token
, KEYWORD_on
) ||
1285 ( isType (token
, TOKEN_SEMICOLON
))) )
1290 if (! isType (token
, TOKEN_SEMICOLON
) )
1294 if (isType (token
, TOKEN_PERIOD
))
1300 while (! (isKeyword (token
, KEYWORD_begin
) ||
1301 (isKeyword (token
, KEYWORD_call
)) ||
1302 ( isType (token
, TOKEN_SEMICOLON
))) )
1305 if ( isKeyword (token
, KEYWORD_declare
) )
1307 addToScope(token
, name
->string
);
1308 parseDeclare(token
, TRUE
);
1309 vStringClear(token
->scope
);
1313 if ( isKeyword (token
, KEYWORD_begin
) ||
1314 isKeyword (token
, KEYWORD_call
) )
1316 addToScope(name
, table
->string
);
1317 makeSqlTag (name
, SQLTAG_TRIGGER
);
1318 addToScope(token
, table
->string
);
1319 if ( isKeyword (token
, KEYWORD_begin
) )
1321 parseBlock (token
, TRUE
);
1323 vStringClear(token
->scope
);
1327 if (! isType (token
, TOKEN_SEMICOLON
) )
1329 findToken (token
, TOKEN_SEMICOLON
);
1332 deleteToken (table
);
1335 static void parsePublication (tokenInfo
*const token
)
1337 tokenInfo
*const name
= newToken ();
1339 /* This deals with these formats
1340 * create or replace publication pu1 ()
1341 * create publication "pu2" ()
1342 * create publication dba."pu3" ()
1343 * create publication "dba"."pu4" () */
1347 if (isType (token
, TOKEN_PERIOD
))
1352 if (isType (token
, TOKEN_OPEN_PAREN
))
1354 if (isType (name
, TOKEN_IDENTIFIER
) ||
1355 isType (name
, TOKEN_STRING
))
1357 makeSqlTag (name
, SQLTAG_PUBLICATION
);
1360 findToken (token
, TOKEN_SEMICOLON
);
1364 static void parseService (tokenInfo
*const token
)
1366 tokenInfo
*const name
= newToken ();
1368 /* This deals with these formats
1369 * CREATE SERVICE s1 TYPE 'HTML'
1370 * AUTHORIZATION OFF USER DBA AS
1372 * FROM SYS.SYSTABLE;
1373 * CREATE SERVICE "s2" TYPE 'HTML'
1374 * AUTHORIZATION OFF USER DBA AS
1375 * CALL sp_Something(); */
1379 if (isKeyword (token
, KEYWORD_type
))
1381 if (isType (name
, TOKEN_IDENTIFIER
) ||
1382 isType (name
, TOKEN_STRING
))
1384 makeSqlTag (name
, SQLTAG_SERVICE
);
1387 findToken (token
, TOKEN_SEMICOLON
);
1391 static void parseDomain (tokenInfo
*const token
)
1393 tokenInfo
*const name
= newToken ();
1395 /* This deals with these formats
1396 * CREATE DOMAIN|DATATYPE [AS] your_name ...; */
1399 if (isKeyword (name
, KEYWORD_is
))
1404 if (isType (name
, TOKEN_IDENTIFIER
) ||
1405 isType (name
, TOKEN_STRING
))
1407 makeSqlTag (name
, SQLTAG_DOMAIN
);
1409 findToken (token
, TOKEN_SEMICOLON
);
1413 static void parseDrop (tokenInfo
*const token
)
1415 /* This deals with these formats
1416 * DROP TABLE|PROCEDURE|DOMAIN|DATATYPE name;
1418 * Just simply skip over these statements.
1419 * They are often confused with PROCEDURE prototypes
1420 * since the syntax is similar, this effectively deals with
1421 * the issue for all types. */
1423 /*dispToken(token, "parseDrop");*/
1424 findToken (token
, TOKEN_SEMICOLON
);
1427 static void parseVariable (tokenInfo
*const token
)
1429 tokenInfo
*const name
= newToken ();
1431 /* This deals with these formats
1432 * create variable varname1 integer;
1433 * create variable @varname2 integer;
1434 * create variable "varname3" integer;
1435 * drop variable @varname3; */
1439 if ( (isType (name
, TOKEN_IDENTIFIER
) || isType (name
, TOKEN_STRING
))
1440 && !isType (token
, TOKEN_SEMICOLON
) )
1442 makeSqlTag (name
, SQLTAG_VARIABLE
);
1444 if (! isType (token
, TOKEN_SEMICOLON
) )
1445 findToken (token
, TOKEN_SEMICOLON
);
1450 static void parseSynonym (tokenInfo
*const token
)
1452 tokenInfo
*const name
= newToken ();
1454 /* This deals with these formats
1455 * create variable varname1 integer;
1456 * create variable @varname2 integer;
1457 * create variable "varname3" integer;
1458 * drop variable @varname3; */
1462 if ( (isType (name
, TOKEN_IDENTIFIER
) || isType (name
, TOKEN_STRING
))
1463 && isKeyword (token
, KEYWORD_for
) )
1465 makeSqlTag (name
, SQLTAG_SYNONYM
);
1467 if (! isType (token
, TOKEN_SEMICOLON
) )
1468 findToken (token
, TOKEN_SEMICOLON
);
1473 static void parseView (tokenInfo
*const token
)
1475 tokenInfo
*const name
= newToken ();
1477 /* This deals with these formats
1478 * create variable varname1 integer;
1479 * create variable @varname2 integer;
1480 * create variable "varname3" integer;
1481 * drop variable @varname3; */
1485 if (isType (token
, TOKEN_PERIOD
))
1490 if ( isType (token
, TOKEN_OPEN_PAREN
) )
1492 skipArgumentList(token
);
1496 while (!(isKeyword (token
, KEYWORD_is
) ||
1497 isType (token
, TOKEN_SEMICOLON
)
1503 if ( (isType (name
, TOKEN_IDENTIFIER
) || isType (name
, TOKEN_STRING
))
1504 && isKeyword (token
, KEYWORD_is
) )
1506 makeSqlTag (name
, SQLTAG_VIEW
);
1509 if (! isType (token
, TOKEN_SEMICOLON
) )
1510 findToken (token
, TOKEN_SEMICOLON
);
1515 static void parseMLTable (tokenInfo
*const token
)
1517 tokenInfo
*const version
= newToken ();
1518 tokenInfo
*const table
= newToken ();
1519 tokenInfo
*const event
= newToken ();
1521 /* This deals with these formats
1522 * call ml_add_table_script( 'version', 'table_name', 'event',
1523 * 'some SQL statement'
1527 if ( isType (token
, TOKEN_OPEN_PAREN
) )
1529 readToken (version
);
1531 while (!(isType (token
, TOKEN_COMMA
) ||
1532 isType (token
, TOKEN_CLOSE_PAREN
)
1538 if (isType (token
, TOKEN_COMMA
))
1542 while (!(isType (token
, TOKEN_COMMA
) ||
1543 isType (token
, TOKEN_CLOSE_PAREN
)
1549 if (isType (token
, TOKEN_COMMA
))
1553 if (isType (version
, TOKEN_STRING
) &&
1554 isType (table
, TOKEN_STRING
) &&
1555 isType (event
, TOKEN_STRING
) )
1557 addToScope(version
, table
->string
);
1558 addToScope(version
, event
->string
);
1559 makeSqlTag (version
, SQLTAG_MLTABLE
);
1562 if( !isType (token
, TOKEN_CLOSE_PAREN
) )
1563 findToken (token
, TOKEN_CLOSE_PAREN
);
1567 if (! isType (token
, TOKEN_SEMICOLON
) )
1568 findToken (token
, TOKEN_SEMICOLON
);
1570 deleteToken (version
);
1571 deleteToken (table
);
1572 deleteToken (event
);
1575 static void parseMLConn (tokenInfo
*const token
)
1577 tokenInfo
*const version
= newToken ();
1578 tokenInfo
*const event
= newToken ();
1580 /* This deals with these formats
1581 * call ml_add_connection_script( 'version', 'event',
1582 * 'some SQL statement'
1586 if ( isType (token
, TOKEN_OPEN_PAREN
) )
1588 readToken (version
);
1590 while (!(isType (token
, TOKEN_COMMA
) ||
1591 isType (token
, TOKEN_CLOSE_PAREN
)
1597 if (isType (token
, TOKEN_COMMA
))
1601 if (isType (version
, TOKEN_STRING
) &&
1602 isType (event
, TOKEN_STRING
) )
1604 addToScope(version
, event
->string
);
1605 makeSqlTag (version
, SQLTAG_MLCONN
);
1608 if( !isType (token
, TOKEN_CLOSE_PAREN
) )
1609 findToken (token
, TOKEN_CLOSE_PAREN
);
1613 if (! isType (token
, TOKEN_SEMICOLON
) )
1614 findToken (token
, TOKEN_SEMICOLON
);
1616 deleteToken (version
);
1617 deleteToken (event
);
1621 static void parseSqlFile (tokenInfo
*const token
)
1626 /*dispToken(token, "psf");*/
1628 if (isType (token
, TOKEN_BLOCK_LABEL_BEGIN
))
1630 else switch (token
->keyword
)
1632 case KEYWORD_begin
: parseBlock (token
, FALSE
); break;
1633 case KEYWORD_cursor
: parseSimple (token
, SQLTAG_CURSOR
); break;
1634 case KEYWORD_datatype
: parseDomain (token
); break;
1635 case KEYWORD_declare
: parseBlock (token
, FALSE
); break;
1636 case KEYWORD_domain
: parseDomain (token
); break;
1637 case KEYWORD_drop
: parseDrop (token
); break;
1638 case KEYWORD_event
: parseEvent (token
); break;
1639 case KEYWORD_function
: parseSubProgram (token
); break;
1640 case KEYWORD_index
: parseIndex (token
); break;
1641 case KEYWORD_ml_table
: parseMLTable (token
); break;
1642 case KEYWORD_ml_conn
: parseMLConn (token
); break;
1643 case KEYWORD_package
: parsePackage (token
); break;
1644 case KEYWORD_procedure
: parseSubProgram (token
); break;
1645 case KEYWORD_publication
: parsePublication (token
); break;
1646 case KEYWORD_service
: parseService (token
); break;
1647 case KEYWORD_subtype
: parseSimple (token
, SQLTAG_SUBTYPE
); break;
1648 case KEYWORD_synonym
: parseSynonym (token
); break;
1649 case KEYWORD_table
: parseTable (token
); break;
1650 case KEYWORD_trigger
: parseTrigger (token
); break;
1651 case KEYWORD_type
: parseType (token
); break;
1652 case KEYWORD_variable
: parseVariable (token
); break;
1653 case KEYWORD_view
: parseView (token
); break;
1656 } while (! isKeyword (token
, KEYWORD_end
));
1659 static void initialize (const langType language
)
1661 Assert (sizeof (SqlKinds
) / sizeof (SqlKinds
[0]) == SQLTAG_COUNT
);
1662 Lang_sql
= language
;
1663 buildSqlKeywordHash ();
1666 static void findSqlTags (void)
1668 tokenInfo
*const token
= newToken ();
1669 exception_t exception
= (exception_t
) (setjmp (Exception
));
1670 while (exception
== ExceptionNone
)
1671 parseSqlFile (token
);
1672 deleteToken (token
);
1675 extern parserDefinition
* SqlParser (void)
1677 static const char *const extensions
[] = { "sql", NULL
};
1678 parserDefinition
* def
= parserNew ("SQL");
1679 def
->kinds
= SqlKinds
;
1680 def
->kindCount
= KIND_COUNT (SqlKinds
);
1681 def
->extensions
= extensions
;
1682 def
->parser
= findSqlTags
;
1683 def
->initialize
= initialize
;
1687 /* vi:set tabstop=8 shiftwidth=4: */