?? sql.c
字號:
/* * 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){ if ( check_first ) { if ( isCmdTerm(token) ) return; } do { readToken (token); } while ( !isCmdTerm(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++; 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--; } } } readToken (token); }}static void parseSubProgram (tokenInfo *const token){ tokenInfo *const name = newToken (); /* * 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; */ 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)) { /* Read datatype */ readToken (token); /* * Read token after which could be the * command terminator if a prototype */ readToken (token); } } if( isCmdTerm (token) ) { makeSqlTag (name, SQLTAG_PROTOTYPE); } else { while (!(isKeyword (token, KEYWORD_is) || isKeyword (token, KEYWORD_begin) || isCmdTerm (token) ) ) { /* read return type */ readToken (token); } 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); } } deleteToken (name);}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){ 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; */ while (! isKeyword (token, KEYWORD_then)) readToken (token); parseStatements (token); /* * parseStatements returns when it finds an END, an IF * statement should be followed by an IF (ANSI anyway) * so read the following IF as well */ readToken (token); break; case KEYWORD_loop: case KEYWORD_case: case KEYWORD_for: /* * LOOP... * END LOOP; * * FOR loop_name AS cursor_name CURSOR FOR ... * END FOR; */ readToken (token); parseStatements (token); break; case KEYWORD_declare: case KEYWORD_begin: parseBlock (token, TRUE); 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 (! (isKeyword (token, KEYWORD_end) || (isType (token, TOKEN_SEMICOLON))) ) { readToken (token); } } /* * We assumed earlier all statements ended with a semi-colon, * see comment above, now, only read if the current token * is not a semi-colon */ if ( isType (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); /* * 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); while (! isKeyword (token, KEYWORD_end)) { parseStatements (token); } 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))
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -