/sql.c
C | 2371 lines | 1564 code | 209 blank | 598 comment | 259 complexity | a85152c03064c2719a3442134b11326e MD5 | raw file
Possible License(s): GPL-2.0
- /*
- * $Id$
- *
- * Copyright (c) 2002-2003, Darren Hiebert
- *
- * This source code is released for free distribution under the terms of the
- * GNU General Public License.
- *
- * This module contains functions for generating tags for PL/SQL language
- * files.
- */
- /*
- * INCLUDE FILES
- */
- #include "general.h" /* must always come first */
- #include <ctype.h> /* to define isalpha () */
- #include <setjmp.h>
- #ifdef DEBUG
- #include <stdio.h>
- #endif
- #include "debug.h"
- #include "entry.h"
- #include "keyword.h"
- #include "parse.h"
- #include "read.h"
- #include "routines.h"
- #include "vstring.h"
- /*
- * On-line "Oracle Database PL/SQL Language Reference":
- * http://download.oracle.com/docs/cd/B28359_01/appdev.111/b28370/toc.htm
- *
- * Sample PL/SQL code is available from:
- * http://www.orafaq.com/faqscrpt.htm#GENPLSQL
- *
- * On-line SQL Anywhere Documentation
- * http://www.ianywhere.com/developer/product_manuals/sqlanywhere/index.html
- */
- /*
- * MACROS
- */
- #define isType(token,t) (boolean) ((token)->type == (t))
- #define isKeyword(token,k) (boolean) ((token)->keyword == (k))
- /*
- * DATA DECLARATIONS
- */
- typedef enum eException { ExceptionNone, ExceptionEOF } exception_t;
- /*
- * Used to specify type of keyword.
- */
- typedef enum eKeywordId {
- KEYWORD_NONE = -1,
- KEYWORD_is,
- KEYWORD_begin,
- KEYWORD_body,
- KEYWORD_cursor,
- KEYWORD_declare,
- KEYWORD_end,
- KEYWORD_function,
- KEYWORD_if,
- KEYWORD_else,
- KEYWORD_elseif,
- KEYWORD_endif,
- KEYWORD_loop,
- KEYWORD_while,
- KEYWORD_case,
- KEYWORD_for,
- KEYWORD_do,
- KEYWORD_call,
- KEYWORD_package,
- KEYWORD_pragma,
- KEYWORD_procedure,
- KEYWORD_record,
- KEYWORD_object,
- KEYWORD_ref,
- KEYWORD_rem,
- KEYWORD_return,
- KEYWORD_returns,
- KEYWORD_subtype,
- KEYWORD_table,
- KEYWORD_trigger,
- KEYWORD_type,
- KEYWORD_index,
- KEYWORD_event,
- KEYWORD_publication,
- KEYWORD_service,
- KEYWORD_domain,
- KEYWORD_datatype,
- KEYWORD_result,
- KEYWORD_url,
- KEYWORD_internal,
- KEYWORD_external,
- KEYWORD_when,
- KEYWORD_then,
- KEYWORD_variable,
- KEYWORD_exception,
- KEYWORD_at,
- KEYWORD_on,
- KEYWORD_primary,
- KEYWORD_references,
- KEYWORD_unique,
- KEYWORD_check,
- KEYWORD_constraint,
- KEYWORD_foreign,
- KEYWORD_ml_table,
- KEYWORD_ml_table_lang,
- KEYWORD_ml_table_dnet,
- KEYWORD_ml_table_java,
- KEYWORD_ml_table_chk,
- KEYWORD_ml_conn,
- KEYWORD_ml_conn_lang,
- KEYWORD_ml_conn_dnet,
- KEYWORD_ml_conn_java,
- KEYWORD_ml_conn_chk,
- KEYWORD_ml_prop,
- KEYWORD_local,
- KEYWORD_temporary,
- KEYWORD_drop,
- KEYWORD_view,
- KEYWORD_synonym,
- KEYWORD_handler,
- KEYWORD_comment,
- KEYWORD_create,
- KEYWORD_go
- } keywordId;
- /*
- * Used to determine whether keyword is valid for the token language and
- * what its ID is.
- */
- typedef struct sKeywordDesc {
- const char *name;
- keywordId id;
- } keywordDesc;
- typedef enum eTokenType {
- TOKEN_UNDEFINED,
- TOKEN_BLOCK_LABEL_BEGIN,
- TOKEN_BLOCK_LABEL_END,
- TOKEN_CHARACTER,
- TOKEN_CLOSE_PAREN,
- TOKEN_COLON,
- TOKEN_SEMICOLON,
- TOKEN_COMMA,
- TOKEN_IDENTIFIER,
- TOKEN_KEYWORD,
- TOKEN_OPEN_PAREN,
- TOKEN_OPERATOR,
- TOKEN_OTHER,
- TOKEN_STRING,
- TOKEN_PERIOD,
- TOKEN_OPEN_CURLY,
- TOKEN_CLOSE_CURLY,
- TOKEN_OPEN_SQUARE,
- TOKEN_CLOSE_SQUARE,
- TOKEN_TILDE,
- TOKEN_FORWARD_SLASH,
- TOKEN_EQUAL
- } tokenType;
- typedef struct sTokenInfoSQL {
- tokenType type;
- keywordId keyword;
- vString * string;
- vString * scope;
- int begin_end_nest_lvl;
- unsigned long lineNumber;
- fpos_t filePosition;
- } tokenInfo;
- /*
- * DATA DEFINITIONS
- */
- static langType Lang_sql;
- static jmp_buf Exception;
- typedef enum {
- SQLTAG_CURSOR,
- SQLTAG_PROTOTYPE,
- SQLTAG_FUNCTION,
- SQLTAG_FIELD,
- SQLTAG_LOCAL_VARIABLE,
- SQLTAG_BLOCK_LABEL,
- SQLTAG_PACKAGE,
- SQLTAG_PROCEDURE,
- SQLTAG_RECORD,
- SQLTAG_SUBTYPE,
- SQLTAG_TABLE,
- SQLTAG_TRIGGER,
- SQLTAG_VARIABLE,
- SQLTAG_INDEX,
- SQLTAG_EVENT,
- SQLTAG_PUBLICATION,
- SQLTAG_SERVICE,
- SQLTAG_DOMAIN,
- SQLTAG_VIEW,
- SQLTAG_SYNONYM,
- SQLTAG_MLTABLE,
- SQLTAG_MLCONN,
- SQLTAG_MLPROP,
- SQLTAG_COUNT
- } sqlKind;
- static kindOption SqlKinds [] = {
- { TRUE, 'c', "cursor", "cursors" },
- { FALSE, 'd', "prototype", "prototypes" },
- { TRUE, 'f', "function", "functions" },
- { TRUE, 'F', "field", "record fields" },
- { FALSE, 'l', "local", "local variables" },
- { TRUE, 'L', "label", "block label" },
- { TRUE, 'P', "package", "packages" },
- { TRUE, 'p', "procedure", "procedures" },
- { FALSE, 'r', "record", "records" },
- { TRUE, 's', "subtype", "subtypes" },
- { TRUE, 't', "table", "tables" },
- { TRUE, 'T', "trigger", "triggers" },
- { TRUE, 'v', "variable", "variables" },
- { TRUE, 'i', "index", "indexes" },
- { TRUE, 'e', "event", "events" },
- { TRUE, 'U', "publication", "publications" },
- { TRUE, 'R', "service", "services" },
- { TRUE, 'D', "domain", "domains" },
- { TRUE, 'V', "view", "views" },
- { TRUE, 'n', "synonym", "synonyms" },
- { TRUE, 'x', "mltable", "MobiLink Table Scripts" },
- { TRUE, 'y', "mlconn", "MobiLink Conn Scripts" },
- { TRUE, 'z', "mlprop", "MobiLink Properties " }
- };
- static const keywordDesc SqlKeywordTable [] = {
- /* keyword keyword ID */
- { "as", KEYWORD_is },
- { "is", KEYWORD_is },
- { "begin", KEYWORD_begin },
- { "body", KEYWORD_body },
- { "cursor", KEYWORD_cursor },
- { "declare", KEYWORD_declare },
- { "end", KEYWORD_end },
- { "function", KEYWORD_function },
- { "if", KEYWORD_if },
- { "else", KEYWORD_else },
- { "elseif", KEYWORD_elseif },
- { "endif", KEYWORD_endif },
- { "loop", KEYWORD_loop },
- { "while", KEYWORD_while },
- { "case", KEYWORD_case },
- { "for", KEYWORD_for },
- { "do", KEYWORD_do },
- { "call", KEYWORD_call },
- { "package", KEYWORD_package },
- { "pragma", KEYWORD_pragma },
- { "procedure", KEYWORD_procedure },
- { "record", KEYWORD_record },
- { "object", KEYWORD_object },
- { "ref", KEYWORD_ref },
- { "rem", KEYWORD_rem },
- { "return", KEYWORD_return },
- { "returns", KEYWORD_returns },
- { "subtype", KEYWORD_subtype },
- { "table", KEYWORD_table },
- { "trigger", KEYWORD_trigger },
- { "type", KEYWORD_type },
- { "index", KEYWORD_index },
- { "event", KEYWORD_event },
- { "publication", KEYWORD_publication },
- { "service", KEYWORD_service },
- { "domain", KEYWORD_domain },
- { "datatype", KEYWORD_datatype },
- { "result", KEYWORD_result },
- { "url", KEYWORD_url },
- { "internal", KEYWORD_internal },
- { "external", KEYWORD_external },
- { "when", KEYWORD_when },
- { "then", KEYWORD_then },
- { "variable", KEYWORD_variable },
- { "exception", KEYWORD_exception },
- { "at", KEYWORD_at },
- { "on", KEYWORD_on },
- { "primary", KEYWORD_primary },
- { "references", KEYWORD_references },
- { "unique", KEYWORD_unique },
- { "check", KEYWORD_check },
- { "constraint", KEYWORD_constraint },
- { "foreign", KEYWORD_foreign },
- { "ml_add_table_script", KEYWORD_ml_table },
- { "ml_add_lang_table_script", KEYWORD_ml_table_lang },
- { "ml_add_dnet_table_script", KEYWORD_ml_table_dnet },
- { "ml_add_java_table_script", KEYWORD_ml_table_java },
- { "ml_add_lang_table_script_chk", KEYWORD_ml_table_chk },
- { "ml_add_connection_script", KEYWORD_ml_conn },
- { "ml_add_lang_connection_script", KEYWORD_ml_conn_lang },
- { "ml_add_dnet_connection_script", KEYWORD_ml_conn_dnet },
- { "ml_add_java_connection_script", KEYWORD_ml_conn_java },
- { "ml_add_lang_conn_script_chk", KEYWORD_ml_conn_chk },
- { "ml_add_property", KEYWORD_ml_prop },
- { "local", KEYWORD_local },
- { "temporary", KEYWORD_temporary },
- { "drop", KEYWORD_drop },
- { "view", KEYWORD_view },
- { "synonym", KEYWORD_synonym },
- { "handler", KEYWORD_handler },
- { "comment", KEYWORD_comment },
- { "create", KEYWORD_create },
- { "go", KEYWORD_go }
- };
- /*
- * FUNCTION DECLARATIONS
- */
- /* Recursive calls */
- static void parseBlock (tokenInfo *const token, const boolean local);
- static void parseDeclare (tokenInfo *const token, const boolean local);
- static void parseKeywords (tokenInfo *const token);
- static void parseSqlFile (tokenInfo *const token);
- /*
- * FUNCTION DEFINITIONS
- */
- static boolean isIdentChar1 (const int c)
- {
- /*
- * Other databases are less restrictive on the first character of
- * an identifier.
- * isIdentChar1 is used to identify the first character of an
- * identifier, so we are removing some restrictions.
- */
- return (boolean)
- (isalpha (c) || c == '@' || c == '_' );
- }
- static boolean isIdentChar (const int c)
- {
- return (boolean)
- (isalpha (c) || isdigit (c) || c == '$' ||
- c == '@' || c == '_' || c == '#');
- }
- static boolean isCmdTerm (tokenInfo *const token)
- {
- DebugStatement (
- debugPrintf (DEBUG_PARSE
- , "\n isCmdTerm: token same tt:%d tk:%d\n"
- , token->type
- , token->keyword
- );
- );
- /*
- * Based on the various customer sites I have been at
- * the most common command delimiters are
- * ;
- * ~
- * /
- * go
- * This routine will check for any of these, more
- * can easily be added by modifying readToken and
- * either adding the character to:
- * enum eTokenType
- * enum eTokenType
- */
- return ( isType (token, TOKEN_SEMICOLON) ||
- isType (token, TOKEN_TILDE) ||
- isType (token, TOKEN_FORWARD_SLASH) ||
- isKeyword (token, KEYWORD_go)
- );
- }
- static boolean isMatchedEnd(tokenInfo *const token, int nest_lvl)
- {
- boolean terminated = FALSE;
- /*
- * Since different forms of SQL allow the use of
- * BEGIN
- * ...
- * END
- * blocks, some statements may not be terminated using
- * the standard delimiters:
- * ;
- * ~
- * /
- * go
- * This routine will check to see if we encounter and END
- * for the matching nest level of BEGIN ... END statements.
- * If we find one, then we can assume, the statement was terminated
- * since we have fallen through to the END statement of the BEGIN
- * block.
- */
- if ( nest_lvl > 0 && isKeyword (token, KEYWORD_end) )
- {
- if ( token->begin_end_nest_lvl == nest_lvl )
- terminated = TRUE;
- }
- return terminated;
- }
- static void buildSqlKeywordHash (void)
- {
- const size_t count = sizeof (SqlKeywordTable) /
- sizeof (SqlKeywordTable [0]);
- size_t i;
- for (i = 0 ; i < count ; ++i)
- {
- const keywordDesc* const p = &SqlKeywordTable [i];
- addKeyword (p->name, Lang_sql, (int) p->id);
- }
- }
- static tokenInfo *newToken (void)
- {
- tokenInfo *const token = xMalloc (1, tokenInfo);
- token->type = TOKEN_UNDEFINED;
- token->keyword = KEYWORD_NONE;
- token->string = vStringNew ();
- token->scope = vStringNew ();
- token->begin_end_nest_lvl = 0;
- token->lineNumber = getSourceLineNumber ();
- token->filePosition = getInputFilePosition ();
- return token;
- }
- static void deleteToken (tokenInfo *const token)
- {
- vStringDelete (token->string);
- vStringDelete (token->scope);
- eFree (token);
- }
- /*
- * Tag generation functions
- */
- static void makeConstTag (tokenInfo *const token, const sqlKind kind)
- {
- if (SqlKinds [kind].enabled)
- {
- const char *const name = vStringValue (token->string);
- tagEntryInfo e;
- initTagEntry (&e, name);
- e.lineNumber = token->lineNumber;
- e.filePosition = token->filePosition;
- e.kindName = SqlKinds [kind].name;
- e.kind = SqlKinds [kind].letter;
- makeTagEntry (&e);
- }
- }
- static void makeSqlTag (tokenInfo *const token, const sqlKind kind)
- {
- vString * fulltag;
- if (SqlKinds [kind].enabled)
- {
- /*
- * If a scope has been added to the token, change the token
- * string to include the scope when making the tag.
- */
- if ( vStringLength(token->scope) > 0 )
- {
- fulltag = vStringNew ();
- vStringCopy(fulltag, token->scope);
- vStringCatS (fulltag, ".");
- vStringCatS (fulltag, vStringValue(token->string));
- vStringTerminate(fulltag);
- vStringCopy(token->string, fulltag);
- vStringDelete (fulltag);
- }
- makeConstTag (token, kind);
- }
- }
- /*
- * Parsing functions
- */
- static void parseString (vString *const string, const int delimiter)
- {
- boolean end = FALSE;
- while (! end)
- {
- int c = fileGetc ();
- if (c == EOF)
- end = TRUE;
- /*
- else if (c == '\\')
- {
- c = fileGetc(); // This maybe a ' or ". //
- vStringPut(string, c);
- }
- */
- else if (c == delimiter)
- end = TRUE;
- else
- vStringPut (string, c);
- }
- vStringTerminate (string);
- }
- /* Read a C identifier beginning with "firstChar" and places it into "name".
- */
- static void parseIdentifier (vString *const string, const int firstChar)
- {
- int c = firstChar;
- Assert (isIdentChar1 (c));
- do
- {
- vStringPut (string, c);
- c = fileGetc ();
- } while (isIdentChar (c));
- vStringTerminate (string);
- if (!isspace (c))
- fileUngetc (c); /* unget non-identifier character */
- }
- static void readToken (tokenInfo *const token)
- {
- int c;
- token->type = TOKEN_UNDEFINED;
- token->keyword = KEYWORD_NONE;
- vStringClear (token->string);
- getNextChar:
- do
- {
- c = fileGetc ();
- token->lineNumber = getSourceLineNumber ();
- token->filePosition = getInputFilePosition ();
- /*
- * Added " to the list of ignores, not sure what this
- * might break but it gets by this issue:
- * create table "t1" (...)
- *
- * Darren, the code passes all my tests for both
- * Oracle and SQL Anywhere, but maybe you can tell me
- * what this may effect.
- */
- }
- while (c == '\t' || c == ' ' || c == '\n');
- switch (c)
- {
- case EOF: longjmp (Exception, (int)ExceptionEOF); break;
- case '(': token->type = TOKEN_OPEN_PAREN; break;
- case ')': token->type = TOKEN_CLOSE_PAREN; break;
- case ':': token->type = TOKEN_COLON; break;
- case ';': token->type = TOKEN_SEMICOLON; break;
- case '.': token->type = TOKEN_PERIOD; break;
- case ',': token->type = TOKEN_COMMA; break;
- case '{': token->type = TOKEN_OPEN_CURLY; break;
- case '}': token->type = TOKEN_CLOSE_CURLY; break;
- case '~': token->type = TOKEN_TILDE; break;
- case '[': token->type = TOKEN_OPEN_SQUARE; break;
- case ']': token->type = TOKEN_CLOSE_SQUARE; break;
- case '=': token->type = TOKEN_EQUAL; break;
- case '\'':
- case '"':
- token->type = TOKEN_STRING;
- parseString (token->string, c);
- token->lineNumber = getSourceLineNumber ();
- token->filePosition = getInputFilePosition ();
- break;
- case '-':
- c = fileGetc ();
- if (c == '-') /* -- is this the start of a comment? */
- {
- fileSkipToCharacter ('\n');
- goto getNextChar;
- }
- else
- {
- if (!isspace (c))
- fileUngetc (c);
- token->type = TOKEN_OPERATOR;
- }
- break;
- case '<':
- case '>':
- {
- const int initial = c;
- int d = fileGetc ();
- if (d == initial)
- {
- if (initial == '<')
- token->type = TOKEN_BLOCK_LABEL_BEGIN;
- else
- token->type = TOKEN_BLOCK_LABEL_END;
- }
- else
- {
- fileUngetc (d);
- token->type = TOKEN_UNDEFINED;
- }
- break;
- }
- case '\\':
- c = fileGetc ();
- if (c != '\\' && c != '"' && c != '\'' && !isspace (c))
- fileUngetc (c);
- token->type = TOKEN_CHARACTER;
- token->lineNumber = getSourceLineNumber ();
- token->filePosition = getInputFilePosition ();
- break;
- case '/':
- {
- int d = fileGetc ();
- if ( (d != '*') && /* is this the start of a comment? */
- (d != '/') ) /* is a one line comment? */
- {
- token->type = TOKEN_FORWARD_SLASH;
- fileUngetc (d);
- }
- else
- {
- if (d == '*')
- {
- do
- {
- fileSkipToCharacter ('*');
- c = fileGetc ();
- if (c == '/')
- break;
- else
- fileUngetc (c);
- } while (c != EOF && c != '\0');
- goto getNextChar;
- }
- else if (d == '/') /* is this the start of a comment? */
- {
- fileSkipToCharacter ('\n');
- goto getNextChar;
- }
- }
- break;
- }
- default:
- if (! isIdentChar1 (c))
- token->type = TOKEN_UNDEFINED;
- else
- {
- parseIdentifier (token->string, c);
- token->lineNumber = getSourceLineNumber ();
- token->filePosition = getInputFilePosition ();
- token->keyword = analyzeToken (token->string, Lang_sql);
- if (isKeyword (token, KEYWORD_rem))
- {
- vStringClear (token->string);
- fileSkipToCharacter ('\n');
- goto getNextChar;
- }
- else if (isKeyword (token, KEYWORD_NONE))
- token->type = TOKEN_IDENTIFIER;
- else
- token->type = TOKEN_KEYWORD;
- }
- break;
- }
- }
- /*
- * Token parsing functions
- */
- /*
- * static void addContext (tokenInfo* const parent, const tokenInfo* const child)
- * {
- * if (vStringLength (parent->string) > 0)
- * {
- * vStringCatS (parent->string, ".");
- * }
- * vStringCatS (parent->string, vStringValue(child->string));
- * vStringTerminate(parent->string);
- * }
- */
- static void addToScope (tokenInfo* const token, vString* const extra)
- {
- if (vStringLength (token->scope) > 0)
- {
- vStringCatS (token->scope, ".");
- }
- vStringCatS (token->scope, vStringValue(extra));
- vStringTerminate(token->scope);
- }
- /*
- * Scanning functions
- */
- static void findToken (tokenInfo *const token, const tokenType type)
- {
- while (! isType (token, type))
- {
- readToken (token);
- }
- }
- static void findCmdTerm (tokenInfo *const token, const boolean check_first)
- {
- int begin_end_nest_lvl = token->begin_end_nest_lvl;
- if ( check_first )
- {
- if ( isCmdTerm(token) )
- return;
- }
- do
- {
- readToken (token);
- } while ( !isCmdTerm(token) && !isMatchedEnd(token, begin_end_nest_lvl) );
- }
- static void skipToMatched(tokenInfo *const token)
- {
- int nest_level = 0;
- tokenType open_token;
- tokenType close_token;
- switch (token->type)
- {
- case TOKEN_OPEN_PAREN:
- open_token = TOKEN_OPEN_PAREN;
- close_token = TOKEN_CLOSE_PAREN;
- break;
- case TOKEN_OPEN_CURLY:
- open_token = TOKEN_OPEN_CURLY;
- close_token = TOKEN_CLOSE_CURLY;
- break;
- case TOKEN_OPEN_SQUARE:
- open_token = TOKEN_OPEN_SQUARE;
- close_token = TOKEN_CLOSE_SQUARE;
- break;
- default:
- return;
- }
- /*
- * This routine will skip to a matching closing token.
- * It will also handle nested tokens like the (, ) below.
- * ( name varchar(30), text binary(10) )
- */
- if (isType (token, open_token))
- {
- nest_level++;
- while (! (isType (token, close_token) && (nest_level == 0)))
- {
- readToken (token);
- if (isType (token, open_token))
- {
- nest_level++;
- }
- if (isType (token, close_token))
- {
- if (nest_level > 0)
- {
- nest_level--;
- }
- }
- }
- readToken (token);
- }
- }
- static void copyToken (tokenInfo *const dest, tokenInfo *const src)
- {
- dest->lineNumber = src->lineNumber;
- dest->filePosition = src->filePosition;
- dest->type = src->type;
- dest->keyword = src->keyword;
- vStringCopy(dest->string, src->string);
- vStringCopy(dest->scope, src->scope);
- }
- static void skipArgumentList (tokenInfo *const token)
- {
- /*
- * Other databases can have arguments with fully declared
- * datatypes:
- * ( name varchar(30), text binary(10) )
- * So we must check for nested open and closing parantheses
- */
- if (isType (token, TOKEN_OPEN_PAREN)) /* arguments? */
- {
- skipToMatched (token);
- }
- }
- static void parseSubProgram (tokenInfo *const token)
- {
- tokenInfo *const name = newToken ();
- vString * saveScope = vStringNew ();
- /*
- * This must handle both prototypes and the body of
- * the procedures.
- *
- * Prototype:
- * FUNCTION func_name RETURN integer;
- * PROCEDURE proc_name( parameters );
- * Procedure
- * FUNCTION GET_ML_USERNAME RETURN VARCHAR2
- * IS
- * BEGIN
- * RETURN v_sync_user_id;
- * END GET_ML_USERNAME;
- *
- * PROCEDURE proc_name( parameters )
- * IS
- * BEGIN
- * END;
- * CREATE PROCEDURE proc_name( parameters )
- * EXTERNAL NAME ... ;
- * CREATE PROCEDURE proc_name( parameters )
- * BEGIN
- * END;
- *
- * CREATE FUNCTION f_GetClassName(
- * IN @object VARCHAR(128)
- * ,IN @code VARCHAR(128)
- * )
- * RETURNS VARCHAR(200)
- * DETERMINISTIC
- * BEGIN
- *
- * IF( @object = 'user_state' ) THEN
- * SET something = something;
- * END IF;
- *
- * RETURN @name;
- * END;
- *
- * Note, a Package adds scope to the items within.
- * create or replace package demo_pkg is
- * test_var number;
- * function test_func return varchar2;
- * function more.test_func2 return varchar2;
- * end demo_pkg;
- * So the tags generated here, contain the package name:
- * demo_pkg.test_var
- * demo_pkg.test_func
- * demo_pkg.more.test_func2
- */
- const sqlKind kind = isKeyword (token, KEYWORD_function) ?
- SQLTAG_FUNCTION : SQLTAG_PROCEDURE;
- Assert (isKeyword (token, KEYWORD_function) ||
- isKeyword (token, KEYWORD_procedure));
- vStringCopy(saveScope, token->scope);
- readToken (token);
- copyToken (name, token);
- readToken (token);
- if (isType (token, TOKEN_PERIOD))
- {
- /*
- * If this is an Oracle package, then the token->scope should
- * already be set. If this is the case, also add this value to the
- * scope.
- * If this is not an Oracle package, chances are the scope should be
- * blank and the value just read is the OWNER or CREATOR of the
- * function and should not be considered part of the scope.
- */
- if ( vStringLength(saveScope) > 0 )
- {
- addToScope(token, name->string);
- }
- readToken (token);
- copyToken (name, token);
- readToken (token);
- }
- if (isType (token, TOKEN_OPEN_PAREN))
- {
- /* Reads to the next token after the TOKEN_CLOSE_PAREN */
- skipArgumentList(token);
- }
- if (kind == SQLTAG_FUNCTION)
- {
- if (isKeyword (token, KEYWORD_return) || isKeyword (token, KEYWORD_returns))
- {
- /* Read datatype */
- readToken (token);
- /*
- * Read token after which could be the
- * command terminator if a prototype
- * or an open parantheses
- */
- readToken (token);
- if (isType (token, TOKEN_OPEN_PAREN))
- {
- /* Reads to the next token after the TOKEN_CLOSE_PAREN */
- skipArgumentList(token);
- }
- }
- }
- if( isCmdTerm (token) )
- {
- makeSqlTag (name, SQLTAG_PROTOTYPE);
- }
- else
- {
- while (!(isKeyword (token, KEYWORD_is) ||
- isKeyword (token, KEYWORD_begin) ||
- isKeyword (token, KEYWORD_at) ||
- isKeyword (token, KEYWORD_internal) ||
- isKeyword (token, KEYWORD_external) ||
- isKeyword (token, KEYWORD_url) ||
- isType (token, TOKEN_EQUAL) ||
- isCmdTerm (token)
- )
- )
- {
- if ( isKeyword (token, KEYWORD_result) )
- {
- readToken (token);
- if (isType (token, TOKEN_OPEN_PAREN))
- {
- /* Reads to the next token after the TOKEN_CLOSE_PAREN */
- skipArgumentList(token);
- }
- } else {
- readToken (token);
- }
- }
- if (isKeyword (token, KEYWORD_at) ||
- isKeyword (token, KEYWORD_url) ||
- isKeyword (token, KEYWORD_internal) ||
- isKeyword (token, KEYWORD_external) )
- {
- addToScope(token, name->string);
- if (isType (name, TOKEN_IDENTIFIER) ||
- isType (name, TOKEN_STRING) ||
- !isKeyword (token, KEYWORD_NONE)
- )
- makeSqlTag (name, kind);
- vStringClear (token->scope);
- }
- if ( isType (token, TOKEN_EQUAL) )
- readToken (token);
- if ( isKeyword (token, KEYWORD_declare) )
- parseDeclare (token, FALSE);
- if (isKeyword (token, KEYWORD_is) ||
- isKeyword (token, KEYWORD_begin) )
- {
- addToScope(token, name->string);
- if (isType (name, TOKEN_IDENTIFIER) ||
- isType (name, TOKEN_STRING) ||
- !isKeyword (token, KEYWORD_NONE)
- )
- makeSqlTag (name, kind);
- parseBlock (token, TRUE);
- vStringClear (token->scope);
- }
- }
- vStringCopy(token->scope, saveScope);
- deleteToken (name);
- vStringDelete(saveScope);
- }
- static void parseRecord (tokenInfo *const token)
- {
- /*
- * Make it a bit forgiving, this is called from
- * multiple functions, parseTable, parseType
- */
- if (!isType (token, TOKEN_OPEN_PAREN))
- readToken (token);
- Assert (isType (token, TOKEN_OPEN_PAREN));
- do
- {
- if ( isType (token, TOKEN_COMMA) || isType (token, TOKEN_OPEN_PAREN) )
- readToken (token);
- /*
- * Create table statements can end with various constraints
- * which must be excluded from the SQLTAG_FIELD.
- * create table t1 (
- * c1 integer,
- * c2 char(30),
- * c3 numeric(10,5),
- * c4 integer,
- * constraint whatever,
- * primary key(c1),
- * foreign key (),
- * check ()
- * )
- */
- if (! (isKeyword(token, KEYWORD_primary) ||
- isKeyword(token, KEYWORD_references) ||
- isKeyword(token, KEYWORD_unique) ||
- isKeyword(token, KEYWORD_check) ||
- isKeyword(token, KEYWORD_constraint) ||
- isKeyword(token, KEYWORD_foreign) ) )
- {
- if (isType (token, TOKEN_IDENTIFIER) ||
- isType (token, TOKEN_STRING))
- makeSqlTag (token, SQLTAG_FIELD);
- }
- while (!(isType (token, TOKEN_COMMA) ||
- isType (token, TOKEN_CLOSE_PAREN) ||
- isType (token, TOKEN_OPEN_PAREN)
- ))
- {
- readToken (token);
- /*
- * A table structure can look like this:
- * create table t1 (
- * c1 integer,
- * c2 char(30),
- * c3 numeric(10,5),
- * c4 integer
- * )
- * We can't just look for a COMMA or CLOSE_PAREN
- * since that will not deal with the numeric(10,5)
- * case. So we need to skip the argument list
- * when we find an open paren.
- */
- if (isType (token, TOKEN_OPEN_PAREN))
- {
- /* Reads to the next token after the TOKEN_CLOSE_PAREN */
- skipArgumentList(token);
- }
- }
- } while (! isType (token, TOKEN_CLOSE_PAREN));
- }
- static void parseType (tokenInfo *const token)
- {
- tokenInfo *const name = newToken ();
- vString * saveScope = vStringNew ();
- vStringCopy(saveScope, token->scope);
- /* If a scope has been set, add it to the name */
- addToScope (name, token->scope);
- readToken (name);
- if (isType (name, TOKEN_IDENTIFIER))
- {
- readToken (token);
- if (isKeyword (token, KEYWORD_is))
- {
- readToken (token);
- addToScope (token, name->string);
- switch (token->keyword)
- {
- case KEYWORD_record:
- case KEYWORD_object:
- makeSqlTag (name, SQLTAG_RECORD);
- parseRecord (token);
- break;
- case KEYWORD_table:
- makeSqlTag (name, SQLTAG_TABLE);
- break;
- case KEYWORD_ref:
- readToken (token);
- if (isKeyword (token, KEYWORD_cursor))
- makeSqlTag (name, SQLTAG_CURSOR);
- break;
- default: break;
- }
- vStringClear (token->scope);
- }
- }
- vStringCopy(token->scope, saveScope);
- deleteToken (name);
- vStringDelete(saveScope);
- }
- static void parseSimple (tokenInfo *const token, const sqlKind kind)
- {
- /* This will simply make the tagname from the first word found */
- readToken (token);
- if (isType (token, TOKEN_IDENTIFIER) ||
- isType (token, TOKEN_STRING))
- makeSqlTag (token, kind);
- }
- static void parseDeclare (tokenInfo *const token, const boolean local)
- {
- /*
- * PL/SQL declares are of this format:
- * IS|AS
- * [declare]
- * CURSOR curname ...
- * varname1 datatype;
- * varname2 datatype;
- * varname3 datatype;
- * begin
- */
- if (isKeyword (token, KEYWORD_declare))
- readToken (token);
- while (! isKeyword (token, KEYWORD_begin) && ! isKeyword (token, KEYWORD_end))
- {
- switch (token->keyword)
- {
- case KEYWORD_cursor: parseSimple (token, SQLTAG_CURSOR); break;
- case KEYWORD_function: parseSubProgram (token); break;
- case KEYWORD_procedure: parseSubProgram (token); break;
- case KEYWORD_subtype: parseSimple (token, SQLTAG_SUBTYPE); break;
- case KEYWORD_trigger: parseSimple (token, SQLTAG_TRIGGER); break;
- case KEYWORD_type: parseType (token); break;
- default:
- if (isType (token, TOKEN_IDENTIFIER))
- {
- if (local)
- {
- makeSqlTag (token, SQLTAG_LOCAL_VARIABLE);
- }
- else
- {
- makeSqlTag (token, SQLTAG_VARIABLE);
- }
- }
- break;
- }
- findToken (token, TOKEN_SEMICOLON);
- readToken (token);
- }
- }
- static void parseDeclareANSI (tokenInfo *const token, const boolean local)
- {
- tokenInfo *const type = newToken ();
- /*
- * ANSI declares are of this format:
- * BEGIN
- * DECLARE varname1 datatype;
- * DECLARE varname2 datatype;
- * ...
- *
- * This differ from PL/SQL where DECLARE preceeds the BEGIN block
- * and the DECLARE keyword is not repeated.
- */
- while (isKeyword (token, KEYWORD_declare))
- {
- readToken (token);
- readToken (type);
- if (isKeyword (type, KEYWORD_cursor))
- makeSqlTag (token, SQLTAG_CURSOR);
- else if (isKeyword (token, KEYWORD_local) &&
- isKeyword (type, KEYWORD_temporary))
- {
- /*
- * DECLARE LOCAL TEMPORARY TABLE table_name (
- * c1 int,
- * c2 int
- * );
- */
- readToken (token);
- if (isKeyword (token, KEYWORD_table))
- {
- readToken (token);
- if (isType(token, TOKEN_IDENTIFIER) ||
- isType(token, TOKEN_STRING) )
- {
- makeSqlTag (token, SQLTAG_TABLE);
- }
- }
- }
- else if (isType (token, TOKEN_IDENTIFIER) ||
- isType (token, TOKEN_STRING))
- {
- if (local)
- makeSqlTag (token, SQLTAG_LOCAL_VARIABLE);
- else
- makeSqlTag (token, SQLTAG_VARIABLE);
- }
- findToken (token, TOKEN_SEMICOLON);
- readToken (token);
- }
- deleteToken (type);
- }
- static void parseLabel (tokenInfo *const token)
- {
- /*
- * A label has this format:
- * <<tobacco_dependency>>
- * DECLARE
- * v_senator VARCHAR2(100) := 'THURMOND, JESSE';
- * BEGIN
- * IF total_contributions (v_senator, 'TOBACCO') > 25000
- * THEN
- * <<alochol_dependency>>
- * DECLARE
- * v_senator VARCHAR2(100) := 'WHATEVERIT, TAKES';
- * BEGIN
- * ...
- */
- Assert (isType (token, TOKEN_BLOCK_LABEL_BEGIN));
- readToken (token);
- if (isType (token, TOKEN_IDENTIFIER))
- {
- makeSqlTag (token, SQLTAG_BLOCK_LABEL);
- readToken (token); /* read end of label */
- }
- }
- static void parseStatements (tokenInfo *const token, const boolean exit_on_endif )
- {
- boolean isAnsi = TRUE;
- boolean stmtTerm = FALSE;
- do
- {
- if (isType (token, TOKEN_BLOCK_LABEL_BEGIN))
- parseLabel (token);
- else
- {
- switch (token->keyword)
- {
- case KEYWORD_exception:
- /*
- * EXCEPTION
- * <exception handler>;
- *
- * Where an exception handler could be:
- * BEGIN
- * WHEN OTHERS THEN
- * x := x + 3;
- * END;
- * In this case we need to skip this keyword and
- * move on to the next token without reading until
- * TOKEN_SEMICOLON;
- */
- readToken (token);
- continue;
- case KEYWORD_when:
- /*
- * WHEN statements can be used in exception clauses
- * and CASE statements. The CASE statement should skip
- * these given below we skip over to an END statement.
- * But for an exception clause, we can have:
- * EXCEPTION
- * WHEN OTHERS THEN
- * BEGIN
- * x := x + 3;
- * END;
- * If we skip to the TOKEN_SEMICOLON, we miss the begin
- * of a nested BEGIN END block. So read the next token
- * after the THEN and restart the LOOP.
- */
- while (! isKeyword (token, KEYWORD_then))
- readToken (token);
- readToken (token);
- continue;
- case KEYWORD_if:
- /*
- * We do not want to look for a ; since for an empty
- * IF block, it would skip over the END.
- * IF...THEN
- * END IF;
- *
- * IF...THEN
- * ELSE
- * END IF;
- *
- * IF...THEN
- * ELSEIF...THEN
- * ELSE
- * END IF;
- *
- * or non-ANSI
- * IF ...
- * BEGIN
- * END
- */
- while ( ! isKeyword (token, KEYWORD_then) &&
- ! isKeyword (token, KEYWORD_begin) )
- {
- readToken (token);
- }
- if( isKeyword (token, KEYWORD_begin ) )
- {
- isAnsi = FALSE;
- parseBlock(token, FALSE);
- /*
- * Handle the non-Ansi IF blocks.
- * parseBlock consumes the END, so if the next
- * token in a command terminator (like GO)
- * we know we are done with this statement.
- */
- if ( isCmdTerm (token) )
- stmtTerm = TRUE;
- }
- else
- {
- readToken (token);
- while( ! (isKeyword (token, KEYWORD_end ) ||
- isKeyword (token, KEYWORD_endif ) )
- )
- {
- if ( isKeyword (token, KEYWORD_else) ||
- isKeyword (token, KEYWORD_elseif) )
- readToken (token);
- parseStatements (token, TRUE);
- if ( isCmdTerm(token) )
- readToken (token);
- }
- /*
- * parseStatements returns when it finds an END, an IF
- * should follow the END for ANSI anyway.
- * IF...THEN
- * END IF;
- */
- if( isKeyword (token, KEYWORD_end ) )
- readToken (token);
- if( isKeyword (token, KEYWORD_if ) || isKeyword (token, KEYWORD_endif ) )
- {
- readToken (token);
- if ( isCmdTerm(token) )
- stmtTerm = TRUE;
- }
- else
- {
- /*
- * Well we need to do something here.
- * There are lots of different END statements
- * END;
- * END CASE;
- * ENDIF;
- * ENDCASE;
- */
- }
- }
- break;
- case KEYWORD_loop:
- case KEYWORD_case:
- case KEYWORD_for:
- /*
- * LOOP...
- * END LOOP;
- *
- * CASE
- * WHEN '1' THEN
- * END CASE;
- *
- * FOR loop_name AS cursor_name CURSOR FOR ...
- * DO
- * END FOR;
- */
- if( isKeyword (token, KEYWORD_for ) )
- {
- /* loop name */
- readToken (token);
- /* AS */
- readToken (token);
- while ( ! isKeyword (token, KEYWORD_is) )
- {
- /*
- * If this is not an AS keyword this is
- * not a proper FOR statement and should
- * simply be ignored
- */
- return;
- }
- while ( ! isKeyword (token, KEYWORD_do) )
- readToken (token);
- }
- readToken (token);
- while( ! isKeyword (token, KEYWORD_end ) )
- {
- /*
- if ( isKeyword (token, KEYWORD_else) ||
- isKeyword (token, KEYWORD_elseif) )
- readToken (token);
- */
- parseStatements (token, FALSE);
- if ( isCmdTerm(token) )
- readToken (token);
- }
- if( isKeyword (token, KEYWORD_end ) )
- readToken (token);
- /*
- * Typically ended with
- * END LOOP [loop name];
- * END CASE
- * END FOR [loop name];
- */
- if ( isKeyword (token, KEYWORD_loop) ||
- isKeyword (token, KEYWORD_case) ||
- isKeyword (token, KEYWORD_for) )
- readToken (token);
- if ( isCmdTerm(token) )
- stmtTerm = TRUE;
- break;
- case KEYWORD_create:
- readToken (token);
- parseKeywords(token);
- break;
- case KEYWORD_declare:
- case KEYWORD_begin:
- parseBlock (token, TRUE);
- break;
- case KEYWORD_end:
- break;
- default:
- readToken (token);
- break;
- }
- /*
- * Not all statements must end in a semi-colon
- * begin
- * if current publisher <> 'publish' then
- * signal UE_FailStatement
- * end if
- * end;
- * The last statement prior to an end ("signal" above) does
- * not need a semi-colon, nor does the end if, since it is
- * also the last statement prior to the end of the block.
- *
- * So we must read to the first semi-colon or an END block
- */
- while ( ! stmtTerm &&
- ! ( isKeyword (token, KEYWORD_end) ||
- (isCmdTerm(token)) )
- )
- {
- if ( isKeyword (token, KEYWORD_endif) &&
- exit_on_endif )
- return;
- if (isType (token, TOKEN_COLON) )
- {
- /*
- * A : can signal a loop name
- * myloop:
- * LOOP
- * LEAVE myloop;
- * END LOOP;
- * Unfortunately, labels do not have a
- * cmd terminator, therefore we have to check
- * if the next token is a keyword and process
- * it accordingly.
- */
- readToken (token);
- if ( isKeyword (token, KEYWORD_loop) ||
- isKeyword (token, KEYWORD_while) ||
- isKeyword (token, KEYWORD_for) )
- /* parseStatements (token); */
- return;
- }
- readToken (token);
- if (isType (token, TOKEN_OPEN_PAREN) ||
- isType (token, TOKEN_OPEN_CURLY) ||
- isType (token, TOKEN_OPEN_SQUARE) )
- skipToMatched (token);
- /*
- * Since we know how to parse various statements
- * if we detect them, parse them to completion
- */
- if (isType (token, TOKEN_BLOCK_LABEL_BEGIN) ||
- isKeyword (token, KEYWORD_exception) ||
- isKeyword (token, KEYWORD_loop) ||
- isKeyword (token, KEYWORD_case) ||
- isKeyword (token, KEYWORD_for) ||
- isKeyword (token, KEYWORD_begin) )
- parseStatements (token, FALSE);
- else if (isKeyword (token, KEYWORD_if))
- parseStatements (token, TRUE);
- }
- }
- /*
- * We assumed earlier all statements ended with a command terminator.
- * See comment above, now, only read if the current token
- * is not a command terminator.
- */
- if ( isCmdTerm(token) && ! stmtTerm )
- stmtTerm = TRUE;
-
- } while (! isKeyword (token, KEYWORD_end) &&
- ! (exit_on_endif && isKeyword (token, KEYWORD_endif) ) &&
- ! stmtTerm );
- }
- static void parseBlock (tokenInfo *const token, const boolean local)
- {
- if (isType (token, TOKEN_BLOCK_LABEL_BEGIN))
- {
- parseLabel (token);
- readToken (token);
- }
- if (! isKeyword (token, KEYWORD_begin))
- {
- readToken (token);
- /*
- * These are Oracle style declares which generally come
- * between an IS/AS and BEGIN block.
- */
- parseDeclare (token, local);
- }
- if (isKeyword (token, KEYWORD_begin))
- {
- readToken (token);
- /*
- * Check for ANSI declarations which always follow
- * a BEGIN statement. This routine will not advance
- * the token if none are found.
- */
- parseDeclareANSI (token, local);
- token->begin_end_nest_lvl++;
- while (! isKeyword (token, KEYWORD_end))
- {
- parseStatements (token, FALSE);
- if ( isCmdTerm(token) )
- readToken (token);
- }
- token->begin_end_nest_lvl--;
- /*
- * Read the next token (we will assume
- * it is the command delimiter)
- */
- readToken (token);
-
- /*
- * Check if the END block is terminated
- */
- if ( !isCmdTerm (token) )
- {
- /*
- * Not sure what to do here at the moment.
- * I think the routine that calls parseBlock
- * must expect the next token has already
- * been read since it is possible this
- * token is not a command delimiter.
- */
- /* findCmdTerm (token, FALSE); */
- }
- }
- }
- static void parsePackage (tokenInfo *const token)
- {
- /*
- * Packages can be specified in a number of ways:
- * CREATE OR REPLACE PACKAGE pkg_name AS
- * or
- * CREATE OR REPLACE PACKAGE owner.pkg_name AS
- * or by specifying a package body
- * CREATE OR REPLACE PACKAGE BODY pkg_name AS
- * CREATE OR REPLACE PACKAGE BODY owner.pkg_name AS
- */
- tokenInfo *const name = newToken ();
- readToken (name);
- if (isKeyword (name, KEYWORD_body))
- {
- /*
- * Ignore the BODY tag since we will process
- * the body or prototypes in the same manner
- */
- readToken (name);
- }
- /* Check for owner.pkg_name */
- while (! isKeyword (token, KEYWORD_is))
- {
- readToken (token);
- if ( isType(token, TOKEN_PERIOD) )
- {
- readToken (name);
- }
- }
- if (isKeyword (token, KEYWORD_is))
- {
- if (isType (name, TOKEN_IDENTIFIER) ||
- isType (name, TOKEN_STRING))
- makeSqlTag (name, SQLTAG_PACKAGE);
- addToScope (token, name->string);
- parseBlock (token, FALSE);
- vStringClear (token->scope);
- }
- findCmdTerm (token, FALSE);
- deleteToken (name);
- }
- static void parseTable (tokenInfo *const token)
- {
- tokenInfo *const name = newToken ();
- /*
- * This deals with these formats:
- * create table t1 (c1 int);
- * create global tempoary table t2 (c1 int);
- * create table "t3" (c1 int);
- * create table bob.t4 (c1 int);
- * create table bob."t5" (c1 int);
- * create table "bob"."t6" (c1 int);
- * create table bob."t7" (c1 int);
- * Proxy tables use this format:
- * create existing table bob."t7" AT '...';
- * SQL Server and Sybase formats
- * create table OnlyTable (
- * create table dbo.HasOwner (
- * create table [dbo].[HasOwnerSquare] (
- * create table master.dbo.HasDb (
- * create table master..HasDbNoOwner (
- * create table [master].dbo.[HasDbAndOwnerSquare] (
- * create table [master]..[HasDbNoOwnerSquare] (
- */
- /* This could be a database, owner or table name */
- readToken (name);
- if (isType (name, TOKEN_OPEN_SQUARE))
- {
- readToken (name);
- /* Read close square */
- readToken (token);
- }
- readToken (token);
- if (isType (token, TOKEN_PERIOD))
- {
- /*
- * This could be a owner or table name.
- * But this is also a special case since the table can be
- * referenced with a blank owner:
- * dbname..tablename
- */
- readToken (name);
- if (isType (name, TOKEN_OPEN_SQUARE))
- {
- readToken (name);
- /* Read close square */
- readToken (token);
- }
- /* Check if a blank name was provided */
- if (isType (name, TOKEN_PERIOD))
- {
- readToken (name);
- if (isType (name, TOKEN_OPEN_SQUARE))
- {
- readToken (name);
- /* Read close square */
- readToken (token);
- }
- }
- readToken (token);
- if (isType (token, TOKEN_PERIOD))
- {
- /* This can only be the table name */
- readToken (name);
- if (isType (name, TOKEN_OPEN_SQUARE))
- {
- readToken (name);
- /* Read close square */
- readToken (token);
- }
- readToken (token);
- }
- }
- if (isType (token, TOKEN_OPEN_PAREN))
- {
- if (isType (name, TOKEN_IDENTIFIER) ||
- isType (name, TOKEN_STRING))
- {
- makeSqlTag (name, SQLTAG_TABLE);
- vStringCopy(token->scope, name->string);
- parseRecord (token);
- vStringClear (token->scope);
- }
- }
- else if (isKeyword (token, KEYWORD_at))
- {
- if (isType (name, TOKEN_IDENTIFIER))
- {
- makeSqlTag (name, SQLTAG_TABLE);
- }
- }
- findCmdTerm (token, FALSE);
- deleteToken (name);
- }
- static void parseIndex (tokenInfo *const token)
- {
- tokenInfo *const name = newToken ();
- tokenInfo *const owner = newToken ();
- /*
- * This deals with these formats
- * create index i1 on t1(c1) create index "i2" on t1(c1)
- * create virtual unique clustered index "i3" on t1(c1)
- * create unique clustered index "i4" on t1(c1)
- * create clustered index "i5" on t1(c1)
- * create bitmap index "i6" on t1(c1)
- */
- readToken (name);
- readToken (token);
- if (isType (token, TOKEN_PERIOD))
- {
- readToken (name);
- readToken (token);
- }
- if ( isKeyword (token, KEYWORD_on) &&
- (isType (name, TOKEN_IDENTIFIER) || isType (name, TOKEN_STRING) ) )
- {
- readToken (owner);
- readToken (token);
- if (isType (token, TOKEN_PERIOD))
- {
- readToken (owner);
- readToken (token);
- }
- addToScope(name, owner->string);
- makeSqlTag (name, SQLTAG_INDEX);
- }
- findCmdTerm (token, FALSE);
- deleteToken (name);
- deleteToken (owner);
- }
- static void parseEvent (tokenInfo *const token)
- {
- tokenInfo *const name = newToken ();
- /*
- * This deals with these formats
- * create event e1 handler begin end;
- * create event "e2" handler begin end;
- * create event dba."e3" handler begin end;
- * create event "dba"."e4" handler begin end;
- */
- readToken (name);
- readToken (token);
- if (isType (token, TOKEN_PERIOD))
- {
- readToken (name);
- }
- while (! (isKeyword (token, KEYWORD_handler) ||
- (isType (token, TOKEN_SEMICOLON))) )
- {
- readToken (token);
- }
- if ( isKeyword (token, KEYWORD_handler) ||
- isType (token, TOKEN_SEMICOLON) )
- {
- makeSqlTag (name, SQLTAG_EVENT);
- }
- if (isKeyword (token, KEYWORD_handler))
- {
- readToken (token);
- if ( isKeyword (token, KEYWORD_begin) )
- {
- parseBlock (token, TRUE);
- }
- findCmdTerm (token, TRUE);
- }
- deleteToken (name);
- }
- static void parseTrigger (tokenInfo *const token)
- {
- tokenInfo *const name = newToken ();
- tokenInfo *const table = newToken ();
- /*
- * This deals with these formats
- * create or replace trigger tr1 begin end;
- * create trigger "tr2" begin end;
- * drop trigger "droptr1";
- * create trigger "tr3" CALL sp_something();
- * create trigger "owner"."tr4" begin end;
- * create trigger "tr5" not valid;
- * create trigger "tr6" begin end;
- */
- readToken (name);
- readToken (token);
- if (isType (token, TOKEN_PERIOD))
- {
- readToken (name);
- readToken (token);
- }
- while ( !isKeyword (token, KEYWORD_on) &&
- !isCmdTerm (token) )
- {
- readToken (token);
- }
- /*if (! isType (token, TOKEN_SEMICOLON) ) */
- if (! isCmdTerm (token) )
- {
- readToken (table);
- readToken (token);
- if (isType (token, TOKEN_PERIOD))
- {
- readToken (table);
- readToken (token);
- }
- while (! (isKeyword (token, KEYWORD_begin) ||
- (isKeyword (token, KEYWORD_call)) ||
- ( isCmdTerm (token))) )
- {
- if ( isKeyword (token, KEYWORD_declare) )
- {
- addToScope(token, name->string);
- parseDeclare(token, TRUE);
- vStringClear(token->scope);
- }
- else
- readToken (token);
- }
- if ( isKeyword (token, KEYWORD_begin) ||
- isKeyword (token, KEYWORD_call) )
- {
- addToScope(name, table->string);
- makeSqlTag (name, SQLTAG_TRIGGER);
- addToScope(token, table->string);
- if ( isKeyword (token, KEYWORD_begin) )
- {
- parseBlock (token, TRUE);
- }
- vStringClear(token->scope);
- }
- }
- findCmdTerm (token, TRUE);
- deleteToken (name);
- deleteToken (table);
- }
- static void parsePublication (tokenInfo *const token)
- {
- tokenInfo *const name = newToken ();
- /*
- * This deals with these formats
- * create or replace publication pu1 ()
- * create publication "pu2" ()
- * create publication dba."pu3" ()
- * create publication "dba"."pu4" ()
- */
- readToken (name);
- readToken (token);
- if (isType (token, TOKEN_PERIOD))
- {
- readToken (name);
- readToken (token);
- }
- if (isType (token, TOKEN_OPEN_PAREN))
- {
- if (isType (name, TOKEN_IDENTIFIER) ||
- isType (name, TOKEN_STRING))
- {
- makeSqlTag (name, SQLTAG_PUBLICATION);
- }
- }
- findCmdTerm (token, FALSE);
- deleteToken (name);
- }
- static void parseService (tokenInfo *const token)
- {
- tokenInfo *const name = newToken ();
- /*
- * This deals with these formats
- * CREATE SERVICE s1 TYPE 'HTML'
- * AUTHORIZATION OFF USER DBA AS
- * SELECT *
- * FROM SYS.SYSTABLE;
- * CREATE SERVICE "s2" TYPE 'HTML'
- * AUTHORIZATION OFF USER DBA AS
- * CALL sp_Something();
- */
- readToken (name);
- readToken (token);
- if (isKeyword (token, KEYWORD_type))
- {
- if (isType (name, TOKEN_IDENTIFIER) ||
- isType (name, TOKEN_STRING))
- {
- makeSqlTag (name, SQLTAG_SERVICE);
- }
- }
- findCmdTerm (token, FALSE);
- deleteToken (name);
- }
- static void parseDomain (tokenInfo *const token)
- {
- tokenInfo *const name = newToken ();
- /*
- * This deals with these formats
- * CREATE DOMAIN|DATATYPE [AS] your_name ...;
- */
- readToken (name);
- if (isKeyword (name, KEYWORD_is))
- {
- readToken (name);
- }
- readToken (token);
- if (isType (name, TOKEN_IDENTIFIER) ||
- isType (name, TOKEN_STRING))
- {
- makeSqlTag (name, SQLTAG_DOMAIN);
- }
- findCmdTerm (token, FALSE);
- deleteToken (name);
- }
- static void parseDrop (tokenInfo *const token)
- {
- /*
- * This deals with these formats
- * DROP TABLE|PROCEDURE|DOMAIN|DATATYPE name;
- *
- * Just simply skip over these statements.
- * They are often confused with PROCEDURE prototypes
- * since the syntax is similar, this effectively deals with
- * the issue for all types.
- */
- findCmdTerm (token, FALSE);
- }
- static void parseVariable (tokenInfo *const token)
- {
- tokenInfo *const name = newToken ();
- /*
- * This deals with these formats
- * create variable varname1 integer;
- * create variable @varname2 integer;
- * create variable "varname3" integer;
- * drop variable @varname3;
- */
- readToken (name);
- readToken (token);
- if ( (isType (name, TOKEN_IDENTIFIER) || isType (name, TOKEN_STRING))
- && !isType (token, TOKEN_SEMICOLON) )
- {
- makeSqlTag (name, SQLTAG_VARIABLE);
- }
- findCmdTerm (token, TRUE);
- deleteToken (name);
- }
- static void parseSynonym (tokenInfo *const token)
- {
- tokenInfo *const name = newToken ();
- /*
- * This deals with these formats
- * create variable varname1 integer;
- * create variable @varname2 integer;
- * create variable "varname3" integer;
- * drop variable @varname3;
- */
- readToken (name);
- readToken (token);
- if ( (isType (name, TOKEN_IDENTIFIER) || isType (name, TOKEN_STRING))
- && isKeyword (token, KEYWORD_for) )
- {
- makeSqlTag (name, SQLTAG_SYNONYM);
- }
- findCmdTerm (token, TRUE);
- deleteToken (name);
- }
- static void parseView (tokenInfo *const token)
- {
- tokenInfo *const name = newToken ();
- /*
- * This deals with these formats
- * create variable varname1 integer;
- * create variable @varname2 integer;
- * create variable "varname3" integer;
- * drop variable @varname3;
- */
- readToken (name);
- readToken (token);
- if (isType (token, TOKEN_PERIOD))
- {
- readToken (name);
- readToken (token);
- }
- if ( isType (token, TOKEN_OPEN_PAREN) )
- {
- skipArgumentList(token);
- }
- while (!(isKeyword (token, KEYWORD_is) ||
- isType (token, TOKEN_SEMICOLON)
- ))
- {
- readToken (token);
- }
- if ( (isType (name, TOKEN_IDENTIFIER) || isType (name, TOKEN_STRING))
- && isKeyword (token, KEYWORD_is) )
- {
- makeSqlTag (name, SQLTAG_VIEW);
- }
- findCmdTerm (token, TRUE);
- deleteToken (name);
- }
- static void parseMLTable (tokenInfo *const token)
- {
- tokenInfo *const version = newToken ();
- tokenInfo *const table = newToken ();
- tokenInfo *const event = newToken ();
- /*
- * This deals with these formats
- * call dbo.ml_add_table_script( 'version', 'table_name', 'event',
- * 'some SQL statement'
- * );
- */
- readToken (token);
- if ( isType (token, TOKEN_OPEN_PAREN) )
- {
- readToken (version);
- readToken (token);
- while (!(isType (token, TOKEN_COMMA) ||
- isType (token, TOKEN_CLOSE_PAREN)
- ))
- {
- readToken (token);
- }
- if (isType (token, TOKEN_COMMA))
- {
- readToken (table);
- readToken (token);
- while (!(isType (token, TOKEN_COMMA) ||
- isType (token, TOKEN_CLOSE_PAREN)
- ))
- {
- readToken (token);
- }
- if (isType (token, TOKEN_COMMA))
- {
- readToken (event);
- if (isType (version, TOKEN_STRING) &&
- isType (table, TOKEN_STRING) &&
- isType (event, TOKEN_STRING) )
- {
- addToScope(version, table->string);
- addToScope(version, event->string);
- makeSqlTag (version, SQLTAG_MLTABLE);
- }
- }
- if( !isType (token, TOKEN_CLOSE_PAREN) )
- findToken (token, TOKEN_CLOSE_PAREN);
- }
- }
- findCmdTerm (token, TRUE);
- deleteToken (version);
- deleteToken (table);
- deleteToken (event);
- }
- static void parseMLConn (tokenInfo *const token)
- {
- tokenInfo *const version = newToken ();
- tokenInfo *const event = newToken ();
- /*
- * This deals with these formats
- * call ml_add_connection_script( 'version', 'event',
- * 'some SQL statement'
- * );
- */
- readToken (token);
- if ( isType (token, TOKEN_OPEN_PAREN) )
- {
- readToken (version);
- readToken (token);
- while (!(isType (token, TOKEN_COMMA) ||
- isType (token, TOKEN_CLOSE_PAREN)
- ))
- {
- readToken (token);
- }
- if (isType (token, TOKEN_COMMA))
- {
- readToken (event);
- if (isType (version, TOKEN_STRING) &&
- isType (event, TOKEN_STRING) )
- {
- addToScope(version, event->string);
- makeSqlTag (version, SQLTAG_MLCONN);
- }
- }
- if( !isType (token, TOKEN_CLOSE_PAREN) )
- findToken (token, TOKEN_CLOSE_PAREN);
- }
- findCmdTerm (token, TRUE);
- deleteToken (version);
- deleteToken (event);
- }
- static void parseMLProp (tokenInfo *const token)
- {
- tokenInfo *const component = newToken ();
- tokenInfo *const prop_set_name = newToken ();
- tokenInfo *const prop_name = newToken ();
- /*
- * This deals with these formats
- * ml_add_property (
- * 'comp_name',
- * 'prop_set_name',
- * 'prop_name',
- * 'prop_value'
- * )
- */
- readToken (token);
- if ( isType (token, TOKEN_OPEN_PAREN) )
- {
- readToken (component);
- readToken (token);
- while (!(isType (token, TOKEN_COMMA) ||
- isType (token, TOKEN_CLOSE_PAREN)
- ))
- {
- readToken (token);
- }
- if (isType (token, TOKEN_COMMA))
- {
- readToken (prop_set_name);
- readToken (token);
- while (!(isType (token, TOKEN_COMMA) ||
- isType (token, TOKEN_CLOSE_PAREN)
- ))
- {
- readToken (token);
- }
- if (isType (token, TOKEN_COMMA))
- {
- readToken (prop_name);
- if (isType (component, TOKEN_STRING) &&
- isType (prop_set_name, TOKEN_STRING) &&
- isType (prop_name, TOKEN_STRING) )
- {
- addToScope(component, prop_set_name->string);
- addToScope(component, prop_name->string);
- makeSqlTag (component, SQLTAG_MLPROP);
- }
- }
- if( !isType (token, TOKEN_CLOSE_PAREN) )
- findToken (token, TOKEN_CLOSE_PAREN);
- }
- }
- findCmdTerm (token, TRUE);
- deleteToken (component);
- deleteToken (prop_set_name);
- deleteToken (prop_name);
- }
- static void parseComment (tokenInfo *const token)
- {
- /*
- * This deals with this statement:
- * COMMENT TO PRESERVE FORMAT ON PROCEDURE "DBA"."test" IS
- * {create PROCEDURE DBA."test"()
- * BEGIN
- * signal dave;
- * END
- * }
- * ;
- * The comment can contain anything between the CURLY
- * braces
- * COMMENT ON USER "admin" IS
- * 'Administration Group'
- * ;
- * Or it could be a simple string with no curly braces
- */
- while (! isKeyword (token, KEYWORD_is))
- {
- readToken (token);
- }
- readToken (token);
- if ( isType(token, TOKEN_OPEN_CURLY) )
- {
- findToken (token, TOKEN_CLOSE_CURLY);
- }
- findCmdTerm (token, TRUE);
- }
- static void parseKeywords (tokenInfo *const token)
- {
- switch (token->keyword)
- {
- case KEYWORD_begin: parseBlock (token, FALSE); break;
- case KEYWORD_comment: parseComment (token); break;
- case KEYWORD_cursor: parseSimple (token, SQLTAG_CURSOR); break;
- case KEYWORD_datatype: parseDomain (token); break;
- case KEYWORD_declare: parseBlock (token, FALSE); break;
- case KEYWORD_domain: parseDomain (token); break;
- case KEYWORD_drop: parseDrop (token); break;
- case KEYWORD_event: parseEvent (token); break;
- case KEYWORD_function: parseSubProgram (token); break;
- case KEYWORD_if: parseStatements (token, FALSE); break;
- case KEYWORD_index: parseIndex (token); break;
- case KEYWORD_ml_table: parseMLTable (token); break;
- case KEYWORD_ml_table_lang: parseMLTable (token); break;
- case KEYWORD_ml_table_dnet: parseMLTable (token); break;
- case KEYWORD_ml_table_java: parseMLTable (token); break;
- case KEYWORD_ml_table_chk: parseMLTable (token); break;
- case KEYWORD_ml_conn: parseMLConn (token); break;
- case KEYWORD_ml_conn_lang: parseMLConn (token); break;
- case KEYWORD_ml_conn_dnet: parseMLConn (token); break;
- case KEYWORD_ml_conn_java: parseMLConn (token); break;
- case KEYWORD_ml_conn_chk: parseMLConn (token); break;
- case KEYWORD_ml_prop: parseMLProp (token); break;
- case KEYWORD_package: parsePackage (token); break;
- case KEYWORD_procedure: parseSubProgram (token); break;
- case KEYWORD_publication: parsePublication (token); break;
- case KEYWORD_service: parseService (token); break;
- case KEYWORD_subtype: parseSimple (token, SQLTAG_SUBTYPE); break;
- case KEYWORD_synonym: parseSynonym (token); break;
- case KEYWORD_table: parseTable (token); break;
- case KEYWORD_trigger: parseTrigger (token); break;
- case KEYWORD_type: parseType (token); break;
- case KEYWORD_variable: parseVariable (token); break;
- case KEYWORD_view: parseView (token); break;
- default: break;
- }
- }
- static void parseSqlFile (tokenInfo *const token)
- {
- do
- {
- readToken (token);
- if (isType (token, TOKEN_BLOCK_LABEL_BEGIN))
- parseLabel (token);
- else
- parseKeywords (token);
- } while (! isKeyword (token, KEYWORD_end));
- }
- static void initialize (const langType language)
- {
- Assert (sizeof (SqlKinds) / sizeof (SqlKinds [0]) == SQLTAG_COUNT);
- Lang_sql = language;
- buildSqlKeywordHash ();
- }
- static void findSqlTags (void)
- {
- tokenInfo *const token = newToken ();
- exception_t exception = (exception_t) (setjmp (Exception));
- while (exception == ExceptionNone)
- parseSqlFile (token);
- deleteToken (token);
- }
- extern parserDefinition* SqlParser (void)
- {
- static const char *const extensions [] = { "sql", NULL };
- parserDefinition* def = parserNew ("SQL");
- def->kinds = SqlKinds;
- def->kindCount = KIND_COUNT (SqlKinds);
- def->extensions = extensions;
- def->parser = findSqlTags;
- def->initialize = initialize;
- return def;
- }
- /* vi:set tabstop=4 shiftwidth=4 noexpandtab: */