2006-02-22 13:49:08 +00:00
|
|
|
/*
|
|
|
|
* $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 "entry.h"
|
|
|
|
#include "keyword.h"
|
|
|
|
#include "parse.h"
|
|
|
|
#include "main.h"
|
|
|
|
#include "read.h"
|
|
|
|
#include "vstring.h"
|
|
|
|
|
|
|
|
/*
|
|
|
|
* On-line PL/SQL Reference Guide:
|
|
|
|
* http://info-it.umsystem.edu/oradocs/doc/server/doc/PLS23/toc.htm
|
|
|
|
*
|
|
|
|
* Sample PL/SQL code is available from:
|
|
|
|
* http://www.orafaq.com/faqscrpt.htm#GENPLSQL
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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_loop,
|
|
|
|
KEYWORD_case,
|
|
|
|
KEYWORD_for,
|
|
|
|
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_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_conn,
|
|
|
|
KEYWORD_local,
|
|
|
|
KEYWORD_temporary,
|
|
|
|
KEYWORD_drop,
|
|
|
|
KEYWORD_view,
|
|
|
|
KEYWORD_synonym,
|
|
|
|
KEYWORD_handler
|
|
|
|
} 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_SEMICOLON,
|
|
|
|
TOKEN_COMMA,
|
|
|
|
TOKEN_IDENTIFIER,
|
|
|
|
TOKEN_KEYWORD,
|
|
|
|
TOKEN_OPEN_PAREN,
|
|
|
|
TOKEN_OPERATOR,
|
|
|
|
TOKEN_OTHER,
|
|
|
|
TOKEN_STRING,
|
|
|
|
TOKEN_PERIOD
|
|
|
|
} tokenType;
|
|
|
|
|
|
|
|
typedef struct sTokenInfo {
|
|
|
|
tokenType type;
|
|
|
|
keywordId keyword;
|
|
|
|
vString * string;
|
|
|
|
vString * scope;
|
|
|
|
unsigned long lineNumber;
|
2011-03-05 22:40:50 +00:00
|
|
|
MIOPos filePosition;
|
2006-02-22 13:49:08 +00:00
|
|
|
} 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_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" },
|
2011-04-10 14:52:57 +00:00
|
|
|
{ TRUE, 'n', "namespace", "procedures" },
|
2006-02-22 13:49:08 +00:00
|
|
|
{ FALSE, 'r', "record", "records" },
|
|
|
|
{ TRUE, 's', "subtype", "subtypes" },
|
2011-04-10 14:52:57 +00:00
|
|
|
{ TRUE, 't', "class", "tables" },
|
|
|
|
{ TRUE, 'T', "macro", "triggers" },
|
2006-02-22 13:49:08 +00:00
|
|
|
{ TRUE, 'v', "variable", "variables" },
|
2011-04-10 14:52:57 +00:00
|
|
|
{ TRUE, 'i', "struct", "indexes" },
|
2006-02-22 13:49:08 +00:00
|
|
|
{ TRUE, 'e', "event", "events" },
|
|
|
|
{ TRUE, 'U', "publication", "publications" },
|
|
|
|
{ TRUE, 'R', "service", "services" },
|
|
|
|
{ TRUE, 'D', "domain", "domains" },
|
2011-04-10 14:52:57 +00:00
|
|
|
{ TRUE, 'm', "member", "views" },
|
2006-02-22 13:49:08 +00:00
|
|
|
{ TRUE, 'n', "synonym", "synonyms" },
|
|
|
|
{ TRUE, 'x', "mltable", "MobiLink Table Scripts" },
|
|
|
|
{ TRUE, 'y', "mlconn", "MobiLink Conn Scripts" }
|
|
|
|
};
|
|
|
|
|
|
|
|
static const keywordDesc SqlKeywordTable [] = {
|
|
|
|
/* keyword keyword ID */
|
|
|
|
{ "as", KEYWORD_is },
|
|
|
|
{ "begin", KEYWORD_begin },
|
|
|
|
{ "body", KEYWORD_body },
|
|
|
|
{ "cursor", KEYWORD_cursor },
|
|
|
|
{ "declare", KEYWORD_declare },
|
|
|
|
{ "end", KEYWORD_end },
|
|
|
|
{ "function", KEYWORD_function },
|
|
|
|
{ "if", KEYWORD_if },
|
|
|
|
{ "is", KEYWORD_is },
|
|
|
|
{ "loop", KEYWORD_loop },
|
|
|
|
{ "case", KEYWORD_case },
|
|
|
|
{ "for", KEYWORD_for },
|
|
|
|
{ "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 },
|
|
|
|
{ "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_connection_script", KEYWORD_ml_conn },
|
|
|
|
{ "local", KEYWORD_local },
|
|
|
|
{ "temporary", KEYWORD_temporary },
|
|
|
|
{ "drop", KEYWORD_drop },
|
|
|
|
{ "view", KEYWORD_view },
|
|
|
|
{ "synonym", KEYWORD_synonym },
|
|
|
|
{ "handler", KEYWORD_handler }
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* FUNCTION DECLARATIONS
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void parseBlock (tokenInfo *const token, const boolean local);
|
|
|
|
static void makeConstTag (tokenInfo *const token, const sqlKind kind);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* DEBUG function
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void dispToken (tokenInfo *const token, char * location)
|
|
|
|
{
|
|
|
|
#ifdef DEBUG
|
|
|
|
if ( isKeyword(token, KEYWORD_NONE) )
|
|
|
|
{
|
|
|
|
if ( isType(token, TOKEN_IDENTIFIER) || isType(token, TOKEN_STRING) )
|
|
|
|
{
|
2008-11-07 14:37:17 +00:00
|
|
|
printf( "\n%s: token string t:%s s:%s l:%lu p:%d\n"
|
2006-02-22 13:49:08 +00:00
|
|
|
, location
|
|
|
|
, vStringValue(token->string)
|
|
|
|
, vStringValue(token->scope)
|
|
|
|
, token->lineNumber
|
2008-11-07 14:37:17 +00:00
|
|
|
, token->bufferPosition
|
2006-02-22 13:49:08 +00:00
|
|
|
);
|
|
|
|
} else {
|
2008-11-07 14:37:17 +00:00
|
|
|
printf( "\n%s: token t:%d s:%s l:%lu p:%d\n"
|
2006-02-22 13:49:08 +00:00
|
|
|
, location
|
|
|
|
, token->type
|
|
|
|
, vStringValue(token->scope)
|
|
|
|
, token->lineNumber
|
2008-11-07 14:37:17 +00:00
|
|
|
, token->bufferPosition
|
2006-02-22 13:49:08 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
} else {
|
2008-11-07 14:37:17 +00:00
|
|
|
printf( "\n%s: keyword:%s k:%d s:%s l:%lu p:%d\n"
|
2006-02-22 13:49:08 +00:00
|
|
|
, location
|
|
|
|
, vStringValue(token->string)
|
|
|
|
, token->keyword
|
|
|
|
, vStringValue(token->scope)
|
|
|
|
, token->lineNumber
|
2008-11-07 14:37:17 +00:00
|
|
|
, token->bufferPosition
|
2006-02-22 13:49:08 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* FUNCTION DEFINITIONS
|
|
|
|
*/
|
|
|
|
|
|
|
|
static boolean isIdentChar1 (const int c)
|
|
|
|
{
|
2008-02-27 13:17:29 +00:00
|
|
|
/* 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. */
|
2006-02-22 13:49:08 +00:00
|
|
|
return (boolean)
|
|
|
|
(isalpha (c) || c == '@' || c == '_' );
|
|
|
|
}
|
|
|
|
|
|
|
|
static boolean isIdentChar (const int c)
|
|
|
|
{
|
|
|
|
return (boolean)
|
|
|
|
(isalpha (c) || isdigit (c) || c == '$' ||
|
|
|
|
c == '@' || c == '_' || c == '#');
|
|
|
|
}
|
|
|
|
|
|
|
|
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 ();
|
|
|
|
|
|
|
|
return token;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void deleteToken (tokenInfo *const token)
|
|
|
|
{
|
|
|
|
vStringDelete (token->string);
|
|
|
|
eFree (token);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Tag generation functions
|
|
|
|
*/
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
2011-03-05 22:40:50 +00:00
|
|
|
e.filePosition = token->filePosition;
|
2006-02-22 13:49:08 +00:00
|
|
|
e.kindName = SqlKinds [kind].name;
|
|
|
|
e.kind = SqlKinds [kind].letter;
|
|
|
|
|
|
|
|
makeTagEntry (&e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Parsing functions
|
|
|
|
*/
|
|
|
|
|
|
|
|
static int skipToCharacter (const int c)
|
|
|
|
{
|
|
|
|
int d;
|
|
|
|
do
|
|
|
|
{
|
|
|
|
d = fileGetc ();
|
|
|
|
} while (d != EOF && d != c);
|
|
|
|
return d;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void parseString (vString *const string, const int delimiter)
|
|
|
|
{
|
|
|
|
boolean end = FALSE;
|
|
|
|
int c;
|
|
|
|
while (! end)
|
|
|
|
{
|
|
|
|
c = fileGetc ();
|
2008-02-27 13:17:29 +00:00
|
|
|
/* printf( "\nps: %c\n", c ); */
|
2006-02-22 13:49:08 +00:00
|
|
|
if (c == EOF)
|
|
|
|
end = TRUE;
|
|
|
|
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 keywordId analyzeToken (vString *const name)
|
|
|
|
{
|
|
|
|
static vString *keyword = NULL;
|
|
|
|
if (keyword == NULL)
|
|
|
|
keyword = vStringNew ();
|
|
|
|
vStringCopyToLower (keyword, name);
|
|
|
|
return (keywordId) lookupKeyword (vStringValue (keyword), Lang_sql);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void readToken (tokenInfo *const token)
|
|
|
|
{
|
|
|
|
int c;
|
|
|
|
|
|
|
|
token->type = TOKEN_UNDEFINED;
|
|
|
|
token->keyword = KEYWORD_NONE;
|
|
|
|
vStringClear (token->string);
|
|
|
|
|
|
|
|
getNextChar:
|
|
|
|
do
|
|
|
|
{
|
|
|
|
c = fileGetc ();
|
2008-02-27 13:17:29 +00:00
|
|
|
/* printf( "\nrtc: %c\n", c ); */
|
2006-02-22 13:49:08 +00:00
|
|
|
/*
|
|
|
|
* Added " to the list of ignores, not sure what this
|
|
|
|
* might break but it gets by this issue:
|
|
|
|
* create table "t1" (...)
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
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_SEMICOLON; break;
|
|
|
|
case '.': token->type = TOKEN_PERIOD; break;
|
|
|
|
case ',': token->type = TOKEN_COMMA; break;
|
|
|
|
|
|
|
|
case '\'':
|
|
|
|
case '"':
|
|
|
|
token->type = TOKEN_STRING;
|
|
|
|
parseString (token->string, c);
|
|
|
|
token->lineNumber = getSourceLineNumber ();
|
2011-03-05 22:40:50 +00:00
|
|
|
token->filePosition = getInputFilePosition ();
|
2006-02-22 13:49:08 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case '-':
|
|
|
|
c = fileGetc ();
|
|
|
|
if (c == '-') /* is this the start of a comment? */
|
|
|
|
{
|
|
|
|
skipToCharacter ('\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 '/':
|
|
|
|
{
|
|
|
|
int d = fileGetc ();
|
|
|
|
if (d != '*') /* is this the start of a comment? */
|
|
|
|
fileUngetc (d);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
do
|
|
|
|
{
|
|
|
|
skipToCharacter ('*');
|
|
|
|
c = fileGetc ();
|
|
|
|
if (c == '/')
|
|
|
|
break;
|
|
|
|
else
|
|
|
|
fileUngetc (c);
|
2007-05-12 12:25:11 +00:00
|
|
|
} while (c != EOF && c != '\0');
|
2006-02-22 13:49:08 +00:00
|
|
|
goto getNextChar;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
if (! isIdentChar1 (c))
|
|
|
|
token->type = TOKEN_UNDEFINED;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
parseIdentifier (token->string, c);
|
|
|
|
token->lineNumber = getSourceLineNumber ();
|
2011-03-05 22:40:50 +00:00
|
|
|
token->filePosition = getInputFilePosition ();
|
2006-02-22 13:49:08 +00:00
|
|
|
token->keyword = analyzeToken (token->string);
|
|
|
|
if (isKeyword (token, KEYWORD_rem))
|
|
|
|
{
|
|
|
|
vStringClear (token->string);
|
|
|
|
skipToCharacter ('\n');
|
|
|
|
goto getNextChar;
|
|
|
|
}
|
|
|
|
else if (isKeyword (token, KEYWORD_NONE))
|
|
|
|
token->type = TOKEN_IDENTIFIER;
|
|
|
|
else
|
|
|
|
token->type = TOKEN_KEYWORD;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2008-02-27 13:17:29 +00:00
|
|
|
/*dispToken(token, "rte");*/
|
2006-02-22 13:49:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Token parsing functions
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* - unused - I don't know (enrico)
|
|
|
|
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 skipArgumentList (tokenInfo *const token)
|
|
|
|
{
|
|
|
|
int nest_level = 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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? */
|
|
|
|
{
|
|
|
|
nest_level++;
|
2008-02-27 13:17:29 +00:00
|
|
|
/*findToken (token, TOKEN_CLOSE_PAREN);*/
|
2006-02-22 13:49:08 +00:00
|
|
|
while (! (isType (token, TOKEN_CLOSE_PAREN) && (nest_level == 0)))
|
|
|
|
{
|
|
|
|
readToken (token);
|
|
|
|
if (isType (token, TOKEN_OPEN_PAREN))
|
|
|
|
{
|
|
|
|
nest_level++;
|
|
|
|
}
|
|
|
|
if (isType (token, TOKEN_CLOSE_PAREN))
|
|
|
|
{
|
|
|
|
if (nest_level > 0)
|
|
|
|
{
|
|
|
|
nest_level--;
|
|
|
|
}
|
|
|
|
}
|
2008-02-27 13:17:29 +00:00
|
|
|
} /*while*/
|
2006-02-22 13:49:08 +00:00
|
|
|
readToken (token);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void parseSubProgram (tokenInfo *const token)
|
|
|
|
{
|
|
|
|
tokenInfo *const name = newToken ();
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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;
|
|
|
|
*/
|
|
|
|
const sqlKind kind = isKeyword (token, KEYWORD_function) ?
|
|
|
|
SQLTAG_FUNCTION : SQLTAG_PROCEDURE;
|
|
|
|
Assert (isKeyword (token, KEYWORD_function) ||
|
|
|
|
isKeyword (token, KEYWORD_procedure));
|
|
|
|
readToken (name);
|
|
|
|
readToken (token);
|
|
|
|
if (isType (token, TOKEN_PERIOD))
|
|
|
|
{
|
|
|
|
readToken (name);
|
|
|
|
readToken (token);
|
|
|
|
}
|
|
|
|
skipArgumentList (token);
|
|
|
|
|
|
|
|
if (kind == SQLTAG_FUNCTION)
|
|
|
|
{
|
|
|
|
if (isKeyword (token, KEYWORD_return))
|
|
|
|
{
|
2008-02-27 13:17:29 +00:00
|
|
|
/* Read RETURN */
|
2006-02-22 13:49:08 +00:00
|
|
|
readToken (token);
|
2008-02-27 13:17:29 +00:00
|
|
|
/* Read datatype */
|
2006-02-22 13:49:08 +00:00
|
|
|
readToken (token);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if( isType (token, TOKEN_SEMICOLON) )
|
|
|
|
{
|
|
|
|
makeSqlTag (name, SQLTAG_PROTOTYPE);
|
|
|
|
} else {
|
|
|
|
while (!(isKeyword (token, KEYWORD_is) ||
|
|
|
|
isKeyword (token, KEYWORD_begin) ||
|
|
|
|
isType (token, TOKEN_SEMICOLON)
|
|
|
|
)
|
|
|
|
)
|
|
|
|
readToken (token); /* read return type */
|
|
|
|
if (isKeyword (token, KEYWORD_is) ||
|
|
|
|
isKeyword (token, KEYWORD_begin) )
|
|
|
|
{
|
|
|
|
addToScope(token, name->string);
|
|
|
|
if (isType (name, TOKEN_IDENTIFIER) ||
|
|
|
|
isType (name, TOKEN_STRING))
|
|
|
|
makeSqlTag (name, kind);
|
|
|
|
|
2008-02-27 13:17:29 +00:00
|
|
|
/*dispToken(name, "SubProgram: parseBlock name");*/
|
|
|
|
/*dispToken(token, "SubProgram: parseBlock token");*/
|
2006-02-22 13:49:08 +00:00
|
|
|
parseBlock (token, TRUE);
|
|
|
|
vStringClear (token->scope);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
deleteToken (name);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void parseRecord (tokenInfo *const token)
|
|
|
|
{
|
2008-02-27 13:17:29 +00:00
|
|
|
/* Make it a bit forgiving, this is called from
|
|
|
|
* multiple functions, parseTable, parseType */
|
2006-02-22 13:49:08 +00:00
|
|
|
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))
|
|
|
|
{
|
2008-02-27 13:17:29 +00:00
|
|
|
/* Reads to the next token after the TOKEN_CLOSE_PAREN */
|
2006-02-22 13:49:08 +00:00
|
|
|
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);
|
2008-02-27 13:17:29 +00:00
|
|
|
/* If a scope has been set, add it to the name */
|
2006-02-22 13:49:08 +00:00
|
|
|
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)
|
|
|
|
{
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
do
|
|
|
|
{
|
|
|
|
if (isType (token, TOKEN_BLOCK_LABEL_BEGIN))
|
|
|
|
parseLabel (token);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
switch (token->keyword)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* 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;
|
|
|
|
*/
|
|
|
|
case KEYWORD_exception:
|
|
|
|
readToken (token);
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
case KEYWORD_when:
|
|
|
|
while (! isKeyword (token, KEYWORD_then))
|
|
|
|
readToken (token);
|
|
|
|
readToken (token);
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We do not want to look for a ; since for an empty
|
|
|
|
* IF block, that would skip over the END.
|
|
|
|
* IF...THEN
|
|
|
|
* END IF;
|
|
|
|
*/
|
|
|
|
case KEYWORD_if:
|
|
|
|
while (! isKeyword (token, KEYWORD_then))
|
|
|
|
readToken (token);
|
2008-02-27 13:17:29 +00:00
|
|
|
/*readToken (token);*/
|
2006-02-22 13:49:08 +00:00
|
|
|
parseStatements (token);
|
|
|
|
break;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* LOOP...
|
|
|
|
* END LOOP;
|
|
|
|
*
|
|
|
|
* FOR loop_name AS cursor_name CURSOR FOR ...
|
|
|
|
* END FOR;
|
|
|
|
*/
|
|
|
|
case KEYWORD_loop:
|
|
|
|
case KEYWORD_case:
|
|
|
|
case KEYWORD_for:
|
|
|
|
readToken (token);
|
|
|
|
parseStatements (token);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case KEYWORD_declare:
|
|
|
|
case KEYWORD_begin:
|
|
|
|
parseBlock (token, TRUE);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
readToken (token);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
findToken (token, TOKEN_SEMICOLON);
|
|
|
|
}
|
|
|
|
readToken (token);
|
|
|
|
} while (! isKeyword (token, KEYWORD_end));
|
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
2008-02-27 13:17:29 +00:00
|
|
|
/*dispToken(token, "parseBlock calling parseDeclare");*/
|
2006-02-22 13:49:08 +00:00
|
|
|
parseDeclare (token, local);
|
|
|
|
}
|
|
|
|
if (isKeyword (token, KEYWORD_begin))
|
|
|
|
{
|
|
|
|
readToken (token);
|
|
|
|
parseDeclareANSI (token, local);
|
|
|
|
while (! isKeyword (token, KEYWORD_end))
|
|
|
|
parseStatements (token);
|
|
|
|
findToken (token, TOKEN_SEMICOLON);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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))
|
|
|
|
readToken (name);
|
2008-02-27 13:17:29 +00:00
|
|
|
/*dispToken(token, "parsePackage after BODY");*/
|
|
|
|
/* Chceck for owner.pkg_name */
|
2006-02-22 13:49:08 +00:00
|
|
|
while (! isKeyword (token, KEYWORD_is))
|
|
|
|
{
|
|
|
|
readToken (token);
|
|
|
|
if ( isType(token, TOKEN_PERIOD) )
|
|
|
|
{
|
|
|
|
readToken (name);
|
2008-02-27 13:17:29 +00:00
|
|
|
/*dispToken(name, "parsePackage new name");*/
|
2006-02-22 13:49:08 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
dispToken(name, "parsePackage name");
|
|
|
|
if (isKeyword (token, KEYWORD_is))
|
|
|
|
{
|
2008-02-27 13:17:29 +00:00
|
|
|
/*dispToken(token, "parsePackage processing IS");*/
|
2006-02-22 13:49:08 +00:00
|
|
|
if (isType (name, TOKEN_IDENTIFIER) ||
|
|
|
|
isType (name, TOKEN_STRING))
|
|
|
|
makeSqlTag (name, SQLTAG_PACKAGE);
|
|
|
|
parseBlock (token, FALSE);
|
|
|
|
}
|
|
|
|
findToken (token, TOKEN_SEMICOLON);
|
|
|
|
deleteToken (name);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void parseTable (tokenInfo *const token)
|
|
|
|
{
|
|
|
|
tokenInfo *const name = newToken ();
|
|
|
|
|
2008-02-27 13:17:29 +00:00
|
|
|
/* 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 '...'; */
|
2006-02-22 13:49:08 +00:00
|
|
|
|
|
|
|
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_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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
findToken (token, TOKEN_SEMICOLON);
|
|
|
|
deleteToken (name);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void parseIndex (tokenInfo *const token)
|
|
|
|
{
|
|
|
|
tokenInfo *const name = newToken ();
|
|
|
|
tokenInfo *const owner = newToken ();
|
|
|
|
|
2008-02-27 13:17:29 +00:00
|
|
|
/* 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) */
|
2006-02-22 13:49:08 +00:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
findToken (token, TOKEN_SEMICOLON);
|
|
|
|
deleteToken (name);
|
|
|
|
deleteToken (owner);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void parseEvent (tokenInfo *const token)
|
|
|
|
{
|
|
|
|
tokenInfo *const name = newToken ();
|
|
|
|
|
2008-02-27 13:17:29 +00:00
|
|
|
/* 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; */
|
2006-02-22 13:49:08 +00:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
findToken (token, TOKEN_SEMICOLON);
|
|
|
|
}
|
|
|
|
deleteToken (name);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void parseTrigger (tokenInfo *const token)
|
|
|
|
{
|
|
|
|
tokenInfo *const name = newToken ();
|
|
|
|
tokenInfo *const table = newToken ();
|
|
|
|
|
2008-02-27 13:17:29 +00:00
|
|
|
/* 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; */
|
2006-02-22 13:49:08 +00:00
|
|
|
|
|
|
|
readToken (name);
|
|
|
|
readToken (token);
|
|
|
|
if (isType (token, TOKEN_PERIOD))
|
|
|
|
{
|
|
|
|
readToken (name);
|
|
|
|
readToken (token);
|
|
|
|
}
|
|
|
|
|
|
|
|
while (! (isKeyword (token, KEYWORD_on) ||
|
|
|
|
( isType (token, TOKEN_SEMICOLON))) )
|
|
|
|
{
|
|
|
|
readToken (token);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (! isType (token, TOKEN_SEMICOLON) )
|
|
|
|
{
|
|
|
|
readToken (table);
|
|
|
|
readToken (token);
|
|
|
|
if (isType (token, TOKEN_PERIOD))
|
|
|
|
{
|
|
|
|
readToken (table);
|
|
|
|
readToken (token);
|
|
|
|
}
|
|
|
|
|
|
|
|
while (! (isKeyword (token, KEYWORD_begin) ||
|
|
|
|
(isKeyword (token, KEYWORD_call)) ||
|
|
|
|
( isType (token, TOKEN_SEMICOLON))) )
|
|
|
|
{
|
|
|
|
readToken (token);
|
|
|
|
if ( isKeyword (token, KEYWORD_declare) )
|
|
|
|
{
|
|
|
|
addToScope(token, name->string);
|
|
|
|
parseDeclare(token, TRUE);
|
|
|
|
vStringClear(token->scope);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (! isType (token, TOKEN_SEMICOLON) )
|
|
|
|
{
|
|
|
|
findToken (token, TOKEN_SEMICOLON);
|
|
|
|
}
|
|
|
|
deleteToken (name);
|
|
|
|
deleteToken (table);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void parsePublication (tokenInfo *const token)
|
|
|
|
{
|
|
|
|
tokenInfo *const name = newToken ();
|
|
|
|
|
2008-02-27 13:17:29 +00:00
|
|
|
/* This deals with these formats
|
|
|
|
* create or replace publication pu1 ()
|
|
|
|
* create publication "pu2" ()
|
|
|
|
* create publication dba."pu3" ()
|
|
|
|
* create publication "dba"."pu4" () */
|
2006-02-22 13:49:08 +00:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
findToken (token, TOKEN_SEMICOLON);
|
|
|
|
deleteToken (name);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void parseService (tokenInfo *const token)
|
|
|
|
{
|
|
|
|
tokenInfo *const name = newToken ();
|
|
|
|
|
2008-02-27 13:17:29 +00:00
|
|
|
/* 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(); */
|
2006-02-22 13:49:08 +00:00
|
|
|
|
|
|
|
readToken (name);
|
|
|
|
readToken (token);
|
|
|
|
if (isKeyword (token, KEYWORD_type))
|
|
|
|
{
|
|
|
|
if (isType (name, TOKEN_IDENTIFIER) ||
|
|
|
|
isType (name, TOKEN_STRING))
|
|
|
|
{
|
|
|
|
makeSqlTag (name, SQLTAG_SERVICE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
findToken (token, TOKEN_SEMICOLON);
|
|
|
|
deleteToken (name);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void parseDomain (tokenInfo *const token)
|
|
|
|
{
|
|
|
|
tokenInfo *const name = newToken ();
|
|
|
|
|
2008-02-27 13:17:29 +00:00
|
|
|
/* This deals with these formats
|
|
|
|
* CREATE DOMAIN|DATATYPE [AS] your_name ...; */
|
2006-02-22 13:49:08 +00:00
|
|
|
|
|
|
|
readToken (name);
|
|
|
|
if (isKeyword (name, KEYWORD_is))
|
|
|
|
{
|
|
|
|
readToken (name);
|
|
|
|
}
|
|
|
|
readToken (token);
|
|
|
|
if (isType (name, TOKEN_IDENTIFIER) ||
|
|
|
|
isType (name, TOKEN_STRING))
|
|
|
|
{
|
|
|
|
makeSqlTag (name, SQLTAG_DOMAIN);
|
|
|
|
}
|
|
|
|
findToken (token, TOKEN_SEMICOLON);
|
|
|
|
deleteToken (name);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void parseDrop (tokenInfo *const token)
|
|
|
|
{
|
2008-02-27 13:17:29 +00:00
|
|
|
/* 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. */
|
|
|
|
|
|
|
|
/*dispToken(token, "parseDrop");*/
|
2006-02-22 13:49:08 +00:00
|
|
|
findToken (token, TOKEN_SEMICOLON);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void parseVariable (tokenInfo *const token)
|
|
|
|
{
|
|
|
|
tokenInfo *const name = newToken ();
|
|
|
|
|
2008-02-27 13:17:29 +00:00
|
|
|
/* This deals with these formats
|
|
|
|
* create variable varname1 integer;
|
|
|
|
* create variable @varname2 integer;
|
|
|
|
* create variable "varname3" integer;
|
|
|
|
* drop variable @varname3; */
|
2006-02-22 13:49:08 +00:00
|
|
|
|
|
|
|
readToken (name);
|
|
|
|
readToken (token);
|
|
|
|
if ( (isType (name, TOKEN_IDENTIFIER) || isType (name, TOKEN_STRING))
|
|
|
|
&& !isType (token, TOKEN_SEMICOLON) )
|
|
|
|
{
|
|
|
|
makeSqlTag (name, SQLTAG_VARIABLE);
|
|
|
|
}
|
|
|
|
if (! isType (token, TOKEN_SEMICOLON) )
|
|
|
|
findToken (token, TOKEN_SEMICOLON);
|
|
|
|
|
|
|
|
deleteToken (name);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void parseSynonym (tokenInfo *const token)
|
|
|
|
{
|
|
|
|
tokenInfo *const name = newToken ();
|
|
|
|
|
2008-02-27 13:17:29 +00:00
|
|
|
/* This deals with these formats
|
|
|
|
* create variable varname1 integer;
|
|
|
|
* create variable @varname2 integer;
|
|
|
|
* create variable "varname3" integer;
|
|
|
|
* drop variable @varname3; */
|
2006-02-22 13:49:08 +00:00
|
|
|
|
|
|
|
readToken (name);
|
|
|
|
readToken (token);
|
|
|
|
if ( (isType (name, TOKEN_IDENTIFIER) || isType (name, TOKEN_STRING))
|
|
|
|
&& isKeyword (token, KEYWORD_for) )
|
|
|
|
{
|
|
|
|
makeSqlTag (name, SQLTAG_SYNONYM);
|
|
|
|
}
|
|
|
|
if (! isType (token, TOKEN_SEMICOLON) )
|
|
|
|
findToken (token, TOKEN_SEMICOLON);
|
|
|
|
|
|
|
|
deleteToken (name);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void parseView (tokenInfo *const token)
|
|
|
|
{
|
|
|
|
tokenInfo *const name = newToken ();
|
|
|
|
|
2008-02-27 13:17:29 +00:00
|
|
|
/* This deals with these formats
|
|
|
|
* create variable varname1 integer;
|
|
|
|
* create variable @varname2 integer;
|
|
|
|
* create variable "varname3" integer;
|
|
|
|
* drop variable @varname3; */
|
2006-02-22 13:49:08 +00:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (! isType (token, TOKEN_SEMICOLON) )
|
|
|
|
findToken (token, TOKEN_SEMICOLON);
|
|
|
|
|
|
|
|
deleteToken (name);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void parseMLTable (tokenInfo *const token)
|
|
|
|
{
|
|
|
|
tokenInfo *const version = newToken ();
|
|
|
|
tokenInfo *const table = newToken ();
|
|
|
|
tokenInfo *const event = newToken ();
|
|
|
|
|
2008-02-27 13:17:29 +00:00
|
|
|
/* This deals with these formats
|
|
|
|
* call ml_add_table_script( 'version', 'table_name', 'event',
|
|
|
|
* 'some SQL statement'
|
|
|
|
* ); */
|
2006-02-22 13:49:08 +00:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (! isType (token, TOKEN_SEMICOLON) )
|
|
|
|
findToken (token, TOKEN_SEMICOLON);
|
|
|
|
|
|
|
|
deleteToken (version);
|
|
|
|
deleteToken (table);
|
|
|
|
deleteToken (event);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void parseMLConn (tokenInfo *const token)
|
|
|
|
{
|
|
|
|
tokenInfo *const version = newToken ();
|
|
|
|
tokenInfo *const event = newToken ();
|
|
|
|
|
2008-02-27 13:17:29 +00:00
|
|
|
/* This deals with these formats
|
|
|
|
* call ml_add_connection_script( 'version', 'event',
|
|
|
|
* 'some SQL statement'
|
|
|
|
* ); */
|
2006-02-22 13:49:08 +00:00
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (! isType (token, TOKEN_SEMICOLON) )
|
|
|
|
findToken (token, TOKEN_SEMICOLON);
|
|
|
|
|
|
|
|
deleteToken (version);
|
|
|
|
deleteToken (event);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void parseSqlFile (tokenInfo *const token)
|
|
|
|
{
|
|
|
|
do
|
|
|
|
{
|
|
|
|
readToken (token);
|
2008-02-27 13:17:29 +00:00
|
|
|
/*dispToken(token, "psf");*/
|
2006-02-22 13:49:08 +00:00
|
|
|
|
|
|
|
if (isType (token, TOKEN_BLOCK_LABEL_BEGIN))
|
|
|
|
parseLabel (token);
|
|
|
|
else switch (token->keyword)
|
|
|
|
{
|
|
|
|
case KEYWORD_begin: parseBlock (token, FALSE); 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_index: parseIndex (token); break;
|
|
|
|
case KEYWORD_ml_table: parseMLTable (token); break;
|
|
|
|
case KEYWORD_ml_conn: parseMLConn (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;
|
|
|
|
}
|
|
|
|
} 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=8 shiftwidth=4: */
|