PageRenderTime 69ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/sql.c

http://github.com/fishman/ctags
C | 2371 lines | 1564 code | 209 blank | 598 comment | 259 complexity | a85152c03064c2719a3442134b11326e MD5 | raw file
Possible License(s): GPL-2.0
  1. /*
  2. * $Id$
  3. *
  4. * Copyright (c) 2002-2003, Darren Hiebert
  5. *
  6. * This source code is released for free distribution under the terms of the
  7. * GNU General Public License.
  8. *
  9. * This module contains functions for generating tags for PL/SQL language
  10. * files.
  11. */
  12. /*
  13. * INCLUDE FILES
  14. */
  15. #include "general.h" /* must always come first */
  16. #include <ctype.h> /* to define isalpha () */
  17. #include <setjmp.h>
  18. #ifdef DEBUG
  19. #include <stdio.h>
  20. #endif
  21. #include "debug.h"
  22. #include "entry.h"
  23. #include "keyword.h"
  24. #include "parse.h"
  25. #include "read.h"
  26. #include "routines.h"
  27. #include "vstring.h"
  28. /*
  29. * On-line "Oracle Database PL/SQL Language Reference":
  30. * http://download.oracle.com/docs/cd/B28359_01/appdev.111/b28370/toc.htm
  31. *
  32. * Sample PL/SQL code is available from:
  33. * http://www.orafaq.com/faqscrpt.htm#GENPLSQL
  34. *
  35. * On-line SQL Anywhere Documentation
  36. * http://www.ianywhere.com/developer/product_manuals/sqlanywhere/index.html
  37. */
  38. /*
  39. * MACROS
  40. */
  41. #define isType(token,t) (boolean) ((token)->type == (t))
  42. #define isKeyword(token,k) (boolean) ((token)->keyword == (k))
  43. /*
  44. * DATA DECLARATIONS
  45. */
  46. typedef enum eException { ExceptionNone, ExceptionEOF } exception_t;
  47. /*
  48. * Used to specify type of keyword.
  49. */
  50. typedef enum eKeywordId {
  51. KEYWORD_NONE = -1,
  52. KEYWORD_is,
  53. KEYWORD_begin,
  54. KEYWORD_body,
  55. KEYWORD_cursor,
  56. KEYWORD_declare,
  57. KEYWORD_end,
  58. KEYWORD_function,
  59. KEYWORD_if,
  60. KEYWORD_else,
  61. KEYWORD_elseif,
  62. KEYWORD_endif,
  63. KEYWORD_loop,
  64. KEYWORD_while,
  65. KEYWORD_case,
  66. KEYWORD_for,
  67. KEYWORD_do,
  68. KEYWORD_call,
  69. KEYWORD_package,
  70. KEYWORD_pragma,
  71. KEYWORD_procedure,
  72. KEYWORD_record,
  73. KEYWORD_object,
  74. KEYWORD_ref,
  75. KEYWORD_rem,
  76. KEYWORD_return,
  77. KEYWORD_returns,
  78. KEYWORD_subtype,
  79. KEYWORD_table,
  80. KEYWORD_trigger,
  81. KEYWORD_type,
  82. KEYWORD_index,
  83. KEYWORD_event,
  84. KEYWORD_publication,
  85. KEYWORD_service,
  86. KEYWORD_domain,
  87. KEYWORD_datatype,
  88. KEYWORD_result,
  89. KEYWORD_url,
  90. KEYWORD_internal,
  91. KEYWORD_external,
  92. KEYWORD_when,
  93. KEYWORD_then,
  94. KEYWORD_variable,
  95. KEYWORD_exception,
  96. KEYWORD_at,
  97. KEYWORD_on,
  98. KEYWORD_primary,
  99. KEYWORD_references,
  100. KEYWORD_unique,
  101. KEYWORD_check,
  102. KEYWORD_constraint,
  103. KEYWORD_foreign,
  104. KEYWORD_ml_table,
  105. KEYWORD_ml_table_lang,
  106. KEYWORD_ml_table_dnet,
  107. KEYWORD_ml_table_java,
  108. KEYWORD_ml_table_chk,
  109. KEYWORD_ml_conn,
  110. KEYWORD_ml_conn_lang,
  111. KEYWORD_ml_conn_dnet,
  112. KEYWORD_ml_conn_java,
  113. KEYWORD_ml_conn_chk,
  114. KEYWORD_ml_prop,
  115. KEYWORD_local,
  116. KEYWORD_temporary,
  117. KEYWORD_drop,
  118. KEYWORD_view,
  119. KEYWORD_synonym,
  120. KEYWORD_handler,
  121. KEYWORD_comment,
  122. KEYWORD_create,
  123. KEYWORD_go
  124. } keywordId;
  125. /*
  126. * Used to determine whether keyword is valid for the token language and
  127. * what its ID is.
  128. */
  129. typedef struct sKeywordDesc {
  130. const char *name;
  131. keywordId id;
  132. } keywordDesc;
  133. typedef enum eTokenType {
  134. TOKEN_UNDEFINED,
  135. TOKEN_BLOCK_LABEL_BEGIN,
  136. TOKEN_BLOCK_LABEL_END,
  137. TOKEN_CHARACTER,
  138. TOKEN_CLOSE_PAREN,
  139. TOKEN_COLON,
  140. TOKEN_SEMICOLON,
  141. TOKEN_COMMA,
  142. TOKEN_IDENTIFIER,
  143. TOKEN_KEYWORD,
  144. TOKEN_OPEN_PAREN,
  145. TOKEN_OPERATOR,
  146. TOKEN_OTHER,
  147. TOKEN_STRING,
  148. TOKEN_PERIOD,
  149. TOKEN_OPEN_CURLY,
  150. TOKEN_CLOSE_CURLY,
  151. TOKEN_OPEN_SQUARE,
  152. TOKEN_CLOSE_SQUARE,
  153. TOKEN_TILDE,
  154. TOKEN_FORWARD_SLASH,
  155. TOKEN_EQUAL
  156. } tokenType;
  157. typedef struct sTokenInfoSQL {
  158. tokenType type;
  159. keywordId keyword;
  160. vString * string;
  161. vString * scope;
  162. int begin_end_nest_lvl;
  163. unsigned long lineNumber;
  164. fpos_t filePosition;
  165. } tokenInfo;
  166. /*
  167. * DATA DEFINITIONS
  168. */
  169. static langType Lang_sql;
  170. static jmp_buf Exception;
  171. typedef enum {
  172. SQLTAG_CURSOR,
  173. SQLTAG_PROTOTYPE,
  174. SQLTAG_FUNCTION,
  175. SQLTAG_FIELD,
  176. SQLTAG_LOCAL_VARIABLE,
  177. SQLTAG_BLOCK_LABEL,
  178. SQLTAG_PACKAGE,
  179. SQLTAG_PROCEDURE,
  180. SQLTAG_RECORD,
  181. SQLTAG_SUBTYPE,
  182. SQLTAG_TABLE,
  183. SQLTAG_TRIGGER,
  184. SQLTAG_VARIABLE,
  185. SQLTAG_INDEX,
  186. SQLTAG_EVENT,
  187. SQLTAG_PUBLICATION,
  188. SQLTAG_SERVICE,
  189. SQLTAG_DOMAIN,
  190. SQLTAG_VIEW,
  191. SQLTAG_SYNONYM,
  192. SQLTAG_MLTABLE,
  193. SQLTAG_MLCONN,
  194. SQLTAG_MLPROP,
  195. SQLTAG_COUNT
  196. } sqlKind;
  197. static kindOption SqlKinds [] = {
  198. { TRUE, 'c', "cursor", "cursors" },
  199. { FALSE, 'd', "prototype", "prototypes" },
  200. { TRUE, 'f', "function", "functions" },
  201. { TRUE, 'F', "field", "record fields" },
  202. { FALSE, 'l', "local", "local variables" },
  203. { TRUE, 'L', "label", "block label" },
  204. { TRUE, 'P', "package", "packages" },
  205. { TRUE, 'p', "procedure", "procedures" },
  206. { FALSE, 'r', "record", "records" },
  207. { TRUE, 's', "subtype", "subtypes" },
  208. { TRUE, 't', "table", "tables" },
  209. { TRUE, 'T', "trigger", "triggers" },
  210. { TRUE, 'v', "variable", "variables" },
  211. { TRUE, 'i', "index", "indexes" },
  212. { TRUE, 'e', "event", "events" },
  213. { TRUE, 'U', "publication", "publications" },
  214. { TRUE, 'R', "service", "services" },
  215. { TRUE, 'D', "domain", "domains" },
  216. { TRUE, 'V', "view", "views" },
  217. { TRUE, 'n', "synonym", "synonyms" },
  218. { TRUE, 'x', "mltable", "MobiLink Table Scripts" },
  219. { TRUE, 'y', "mlconn", "MobiLink Conn Scripts" },
  220. { TRUE, 'z', "mlprop", "MobiLink Properties " }
  221. };
  222. static const keywordDesc SqlKeywordTable [] = {
  223. /* keyword keyword ID */
  224. { "as", KEYWORD_is },
  225. { "is", KEYWORD_is },
  226. { "begin", KEYWORD_begin },
  227. { "body", KEYWORD_body },
  228. { "cursor", KEYWORD_cursor },
  229. { "declare", KEYWORD_declare },
  230. { "end", KEYWORD_end },
  231. { "function", KEYWORD_function },
  232. { "if", KEYWORD_if },
  233. { "else", KEYWORD_else },
  234. { "elseif", KEYWORD_elseif },
  235. { "endif", KEYWORD_endif },
  236. { "loop", KEYWORD_loop },
  237. { "while", KEYWORD_while },
  238. { "case", KEYWORD_case },
  239. { "for", KEYWORD_for },
  240. { "do", KEYWORD_do },
  241. { "call", KEYWORD_call },
  242. { "package", KEYWORD_package },
  243. { "pragma", KEYWORD_pragma },
  244. { "procedure", KEYWORD_procedure },
  245. { "record", KEYWORD_record },
  246. { "object", KEYWORD_object },
  247. { "ref", KEYWORD_ref },
  248. { "rem", KEYWORD_rem },
  249. { "return", KEYWORD_return },
  250. { "returns", KEYWORD_returns },
  251. { "subtype", KEYWORD_subtype },
  252. { "table", KEYWORD_table },
  253. { "trigger", KEYWORD_trigger },
  254. { "type", KEYWORD_type },
  255. { "index", KEYWORD_index },
  256. { "event", KEYWORD_event },
  257. { "publication", KEYWORD_publication },
  258. { "service", KEYWORD_service },
  259. { "domain", KEYWORD_domain },
  260. { "datatype", KEYWORD_datatype },
  261. { "result", KEYWORD_result },
  262. { "url", KEYWORD_url },
  263. { "internal", KEYWORD_internal },
  264. { "external", KEYWORD_external },
  265. { "when", KEYWORD_when },
  266. { "then", KEYWORD_then },
  267. { "variable", KEYWORD_variable },
  268. { "exception", KEYWORD_exception },
  269. { "at", KEYWORD_at },
  270. { "on", KEYWORD_on },
  271. { "primary", KEYWORD_primary },
  272. { "references", KEYWORD_references },
  273. { "unique", KEYWORD_unique },
  274. { "check", KEYWORD_check },
  275. { "constraint", KEYWORD_constraint },
  276. { "foreign", KEYWORD_foreign },
  277. { "ml_add_table_script", KEYWORD_ml_table },
  278. { "ml_add_lang_table_script", KEYWORD_ml_table_lang },
  279. { "ml_add_dnet_table_script", KEYWORD_ml_table_dnet },
  280. { "ml_add_java_table_script", KEYWORD_ml_table_java },
  281. { "ml_add_lang_table_script_chk", KEYWORD_ml_table_chk },
  282. { "ml_add_connection_script", KEYWORD_ml_conn },
  283. { "ml_add_lang_connection_script", KEYWORD_ml_conn_lang },
  284. { "ml_add_dnet_connection_script", KEYWORD_ml_conn_dnet },
  285. { "ml_add_java_connection_script", KEYWORD_ml_conn_java },
  286. { "ml_add_lang_conn_script_chk", KEYWORD_ml_conn_chk },
  287. { "ml_add_property", KEYWORD_ml_prop },
  288. { "local", KEYWORD_local },
  289. { "temporary", KEYWORD_temporary },
  290. { "drop", KEYWORD_drop },
  291. { "view", KEYWORD_view },
  292. { "synonym", KEYWORD_synonym },
  293. { "handler", KEYWORD_handler },
  294. { "comment", KEYWORD_comment },
  295. { "create", KEYWORD_create },
  296. { "go", KEYWORD_go }
  297. };
  298. /*
  299. * FUNCTION DECLARATIONS
  300. */
  301. /* Recursive calls */
  302. static void parseBlock (tokenInfo *const token, const boolean local);
  303. static void parseDeclare (tokenInfo *const token, const boolean local);
  304. static void parseKeywords (tokenInfo *const token);
  305. static void parseSqlFile (tokenInfo *const token);
  306. /*
  307. * FUNCTION DEFINITIONS
  308. */
  309. static boolean isIdentChar1 (const int c)
  310. {
  311. /*
  312. * Other databases are less restrictive on the first character of
  313. * an identifier.
  314. * isIdentChar1 is used to identify the first character of an
  315. * identifier, so we are removing some restrictions.
  316. */
  317. return (boolean)
  318. (isalpha (c) || c == '@' || c == '_' );
  319. }
  320. static boolean isIdentChar (const int c)
  321. {
  322. return (boolean)
  323. (isalpha (c) || isdigit (c) || c == '$' ||
  324. c == '@' || c == '_' || c == '#');
  325. }
  326. static boolean isCmdTerm (tokenInfo *const token)
  327. {
  328. DebugStatement (
  329. debugPrintf (DEBUG_PARSE
  330. , "\n isCmdTerm: token same tt:%d tk:%d\n"
  331. , token->type
  332. , token->keyword
  333. );
  334. );
  335. /*
  336. * Based on the various customer sites I have been at
  337. * the most common command delimiters are
  338. * ;
  339. * ~
  340. * /
  341. * go
  342. * This routine will check for any of these, more
  343. * can easily be added by modifying readToken and
  344. * either adding the character to:
  345. * enum eTokenType
  346. * enum eTokenType
  347. */
  348. return ( isType (token, TOKEN_SEMICOLON) ||
  349. isType (token, TOKEN_TILDE) ||
  350. isType (token, TOKEN_FORWARD_SLASH) ||
  351. isKeyword (token, KEYWORD_go)
  352. );
  353. }
  354. static boolean isMatchedEnd(tokenInfo *const token, int nest_lvl)
  355. {
  356. boolean terminated = FALSE;
  357. /*
  358. * Since different forms of SQL allow the use of
  359. * BEGIN
  360. * ...
  361. * END
  362. * blocks, some statements may not be terminated using
  363. * the standard delimiters:
  364. * ;
  365. * ~
  366. * /
  367. * go
  368. * This routine will check to see if we encounter and END
  369. * for the matching nest level of BEGIN ... END statements.
  370. * If we find one, then we can assume, the statement was terminated
  371. * since we have fallen through to the END statement of the BEGIN
  372. * block.
  373. */
  374. if ( nest_lvl > 0 && isKeyword (token, KEYWORD_end) )
  375. {
  376. if ( token->begin_end_nest_lvl == nest_lvl )
  377. terminated = TRUE;
  378. }
  379. return terminated;
  380. }
  381. static void buildSqlKeywordHash (void)
  382. {
  383. const size_t count = sizeof (SqlKeywordTable) /
  384. sizeof (SqlKeywordTable [0]);
  385. size_t i;
  386. for (i = 0 ; i < count ; ++i)
  387. {
  388. const keywordDesc* const p = &SqlKeywordTable [i];
  389. addKeyword (p->name, Lang_sql, (int) p->id);
  390. }
  391. }
  392. static tokenInfo *newToken (void)
  393. {
  394. tokenInfo *const token = xMalloc (1, tokenInfo);
  395. token->type = TOKEN_UNDEFINED;
  396. token->keyword = KEYWORD_NONE;
  397. token->string = vStringNew ();
  398. token->scope = vStringNew ();
  399. token->begin_end_nest_lvl = 0;
  400. token->lineNumber = getSourceLineNumber ();
  401. token->filePosition = getInputFilePosition ();
  402. return token;
  403. }
  404. static void deleteToken (tokenInfo *const token)
  405. {
  406. vStringDelete (token->string);
  407. vStringDelete (token->scope);
  408. eFree (token);
  409. }
  410. /*
  411. * Tag generation functions
  412. */
  413. static void makeConstTag (tokenInfo *const token, const sqlKind kind)
  414. {
  415. if (SqlKinds [kind].enabled)
  416. {
  417. const char *const name = vStringValue (token->string);
  418. tagEntryInfo e;
  419. initTagEntry (&e, name);
  420. e.lineNumber = token->lineNumber;
  421. e.filePosition = token->filePosition;
  422. e.kindName = SqlKinds [kind].name;
  423. e.kind = SqlKinds [kind].letter;
  424. makeTagEntry (&e);
  425. }
  426. }
  427. static void makeSqlTag (tokenInfo *const token, const sqlKind kind)
  428. {
  429. vString * fulltag;
  430. if (SqlKinds [kind].enabled)
  431. {
  432. /*
  433. * If a scope has been added to the token, change the token
  434. * string to include the scope when making the tag.
  435. */
  436. if ( vStringLength(token->scope) > 0 )
  437. {
  438. fulltag = vStringNew ();
  439. vStringCopy(fulltag, token->scope);
  440. vStringCatS (fulltag, ".");
  441. vStringCatS (fulltag, vStringValue(token->string));
  442. vStringTerminate(fulltag);
  443. vStringCopy(token->string, fulltag);
  444. vStringDelete (fulltag);
  445. }
  446. makeConstTag (token, kind);
  447. }
  448. }
  449. /*
  450. * Parsing functions
  451. */
  452. static void parseString (vString *const string, const int delimiter)
  453. {
  454. boolean end = FALSE;
  455. while (! end)
  456. {
  457. int c = fileGetc ();
  458. if (c == EOF)
  459. end = TRUE;
  460. /*
  461. else if (c == '\\')
  462. {
  463. c = fileGetc(); // This maybe a ' or ". //
  464. vStringPut(string, c);
  465. }
  466. */
  467. else if (c == delimiter)
  468. end = TRUE;
  469. else
  470. vStringPut (string, c);
  471. }
  472. vStringTerminate (string);
  473. }
  474. /* Read a C identifier beginning with "firstChar" and places it into "name".
  475. */
  476. static void parseIdentifier (vString *const string, const int firstChar)
  477. {
  478. int c = firstChar;
  479. Assert (isIdentChar1 (c));
  480. do
  481. {
  482. vStringPut (string, c);
  483. c = fileGetc ();
  484. } while (isIdentChar (c));
  485. vStringTerminate (string);
  486. if (!isspace (c))
  487. fileUngetc (c); /* unget non-identifier character */
  488. }
  489. static void readToken (tokenInfo *const token)
  490. {
  491. int c;
  492. token->type = TOKEN_UNDEFINED;
  493. token->keyword = KEYWORD_NONE;
  494. vStringClear (token->string);
  495. getNextChar:
  496. do
  497. {
  498. c = fileGetc ();
  499. token->lineNumber = getSourceLineNumber ();
  500. token->filePosition = getInputFilePosition ();
  501. /*
  502. * Added " to the list of ignores, not sure what this
  503. * might break but it gets by this issue:
  504. * create table "t1" (...)
  505. *
  506. * Darren, the code passes all my tests for both
  507. * Oracle and SQL Anywhere, but maybe you can tell me
  508. * what this may effect.
  509. */
  510. }
  511. while (c == '\t' || c == ' ' || c == '\n');
  512. switch (c)
  513. {
  514. case EOF: longjmp (Exception, (int)ExceptionEOF); break;
  515. case '(': token->type = TOKEN_OPEN_PAREN; break;
  516. case ')': token->type = TOKEN_CLOSE_PAREN; break;
  517. case ':': token->type = TOKEN_COLON; break;
  518. case ';': token->type = TOKEN_SEMICOLON; break;
  519. case '.': token->type = TOKEN_PERIOD; break;
  520. case ',': token->type = TOKEN_COMMA; break;
  521. case '{': token->type = TOKEN_OPEN_CURLY; break;
  522. case '}': token->type = TOKEN_CLOSE_CURLY; break;
  523. case '~': token->type = TOKEN_TILDE; break;
  524. case '[': token->type = TOKEN_OPEN_SQUARE; break;
  525. case ']': token->type = TOKEN_CLOSE_SQUARE; break;
  526. case '=': token->type = TOKEN_EQUAL; break;
  527. case '\'':
  528. case '"':
  529. token->type = TOKEN_STRING;
  530. parseString (token->string, c);
  531. token->lineNumber = getSourceLineNumber ();
  532. token->filePosition = getInputFilePosition ();
  533. break;
  534. case '-':
  535. c = fileGetc ();
  536. if (c == '-') /* -- is this the start of a comment? */
  537. {
  538. fileSkipToCharacter ('\n');
  539. goto getNextChar;
  540. }
  541. else
  542. {
  543. if (!isspace (c))
  544. fileUngetc (c);
  545. token->type = TOKEN_OPERATOR;
  546. }
  547. break;
  548. case '<':
  549. case '>':
  550. {
  551. const int initial = c;
  552. int d = fileGetc ();
  553. if (d == initial)
  554. {
  555. if (initial == '<')
  556. token->type = TOKEN_BLOCK_LABEL_BEGIN;
  557. else
  558. token->type = TOKEN_BLOCK_LABEL_END;
  559. }
  560. else
  561. {
  562. fileUngetc (d);
  563. token->type = TOKEN_UNDEFINED;
  564. }
  565. break;
  566. }
  567. case '\\':
  568. c = fileGetc ();
  569. if (c != '\\' && c != '"' && c != '\'' && !isspace (c))
  570. fileUngetc (c);
  571. token->type = TOKEN_CHARACTER;
  572. token->lineNumber = getSourceLineNumber ();
  573. token->filePosition = getInputFilePosition ();
  574. break;
  575. case '/':
  576. {
  577. int d = fileGetc ();
  578. if ( (d != '*') && /* is this the start of a comment? */
  579. (d != '/') ) /* is a one line comment? */
  580. {
  581. token->type = TOKEN_FORWARD_SLASH;
  582. fileUngetc (d);
  583. }
  584. else
  585. {
  586. if (d == '*')
  587. {
  588. do
  589. {
  590. fileSkipToCharacter ('*');
  591. c = fileGetc ();
  592. if (c == '/')
  593. break;
  594. else
  595. fileUngetc (c);
  596. } while (c != EOF && c != '\0');
  597. goto getNextChar;
  598. }
  599. else if (d == '/') /* is this the start of a comment? */
  600. {
  601. fileSkipToCharacter ('\n');
  602. goto getNextChar;
  603. }
  604. }
  605. break;
  606. }
  607. default:
  608. if (! isIdentChar1 (c))
  609. token->type = TOKEN_UNDEFINED;
  610. else
  611. {
  612. parseIdentifier (token->string, c);
  613. token->lineNumber = getSourceLineNumber ();
  614. token->filePosition = getInputFilePosition ();
  615. token->keyword = analyzeToken (token->string, Lang_sql);
  616. if (isKeyword (token, KEYWORD_rem))
  617. {
  618. vStringClear (token->string);
  619. fileSkipToCharacter ('\n');
  620. goto getNextChar;
  621. }
  622. else if (isKeyword (token, KEYWORD_NONE))
  623. token->type = TOKEN_IDENTIFIER;
  624. else
  625. token->type = TOKEN_KEYWORD;
  626. }
  627. break;
  628. }
  629. }
  630. /*
  631. * Token parsing functions
  632. */
  633. /*
  634. * static void addContext (tokenInfo* const parent, const tokenInfo* const child)
  635. * {
  636. * if (vStringLength (parent->string) > 0)
  637. * {
  638. * vStringCatS (parent->string, ".");
  639. * }
  640. * vStringCatS (parent->string, vStringValue(child->string));
  641. * vStringTerminate(parent->string);
  642. * }
  643. */
  644. static void addToScope (tokenInfo* const token, vString* const extra)
  645. {
  646. if (vStringLength (token->scope) > 0)
  647. {
  648. vStringCatS (token->scope, ".");
  649. }
  650. vStringCatS (token->scope, vStringValue(extra));
  651. vStringTerminate(token->scope);
  652. }
  653. /*
  654. * Scanning functions
  655. */
  656. static void findToken (tokenInfo *const token, const tokenType type)
  657. {
  658. while (! isType (token, type))
  659. {
  660. readToken (token);
  661. }
  662. }
  663. static void findCmdTerm (tokenInfo *const token, const boolean check_first)
  664. {
  665. int begin_end_nest_lvl = token->begin_end_nest_lvl;
  666. if ( check_first )
  667. {
  668. if ( isCmdTerm(token) )
  669. return;
  670. }
  671. do
  672. {
  673. readToken (token);
  674. } while ( !isCmdTerm(token) && !isMatchedEnd(token, begin_end_nest_lvl) );
  675. }
  676. static void skipToMatched(tokenInfo *const token)
  677. {
  678. int nest_level = 0;
  679. tokenType open_token;
  680. tokenType close_token;
  681. switch (token->type)
  682. {
  683. case TOKEN_OPEN_PAREN:
  684. open_token = TOKEN_OPEN_PAREN;
  685. close_token = TOKEN_CLOSE_PAREN;
  686. break;
  687. case TOKEN_OPEN_CURLY:
  688. open_token = TOKEN_OPEN_CURLY;
  689. close_token = TOKEN_CLOSE_CURLY;
  690. break;
  691. case TOKEN_OPEN_SQUARE:
  692. open_token = TOKEN_OPEN_SQUARE;
  693. close_token = TOKEN_CLOSE_SQUARE;
  694. break;
  695. default:
  696. return;
  697. }
  698. /*
  699. * This routine will skip to a matching closing token.
  700. * It will also handle nested tokens like the (, ) below.
  701. * ( name varchar(30), text binary(10) )
  702. */
  703. if (isType (token, open_token))
  704. {
  705. nest_level++;
  706. while (! (isType (token, close_token) && (nest_level == 0)))
  707. {
  708. readToken (token);
  709. if (isType (token, open_token))
  710. {
  711. nest_level++;
  712. }
  713. if (isType (token, close_token))
  714. {
  715. if (nest_level > 0)
  716. {
  717. nest_level--;
  718. }
  719. }
  720. }
  721. readToken (token);
  722. }
  723. }
  724. static void copyToken (tokenInfo *const dest, tokenInfo *const src)
  725. {
  726. dest->lineNumber = src->lineNumber;
  727. dest->filePosition = src->filePosition;
  728. dest->type = src->type;
  729. dest->keyword = src->keyword;
  730. vStringCopy(dest->string, src->string);
  731. vStringCopy(dest->scope, src->scope);
  732. }
  733. static void skipArgumentList (tokenInfo *const token)
  734. {
  735. /*
  736. * Other databases can have arguments with fully declared
  737. * datatypes:
  738. * ( name varchar(30), text binary(10) )
  739. * So we must check for nested open and closing parantheses
  740. */
  741. if (isType (token, TOKEN_OPEN_PAREN)) /* arguments? */
  742. {
  743. skipToMatched (token);
  744. }
  745. }
  746. static void parseSubProgram (tokenInfo *const token)
  747. {
  748. tokenInfo *const name = newToken ();
  749. vString * saveScope = vStringNew ();
  750. /*
  751. * This must handle both prototypes and the body of
  752. * the procedures.
  753. *
  754. * Prototype:
  755. * FUNCTION func_name RETURN integer;
  756. * PROCEDURE proc_name( parameters );
  757. * Procedure
  758. * FUNCTION GET_ML_USERNAME RETURN VARCHAR2
  759. * IS
  760. * BEGIN
  761. * RETURN v_sync_user_id;
  762. * END GET_ML_USERNAME;
  763. *
  764. * PROCEDURE proc_name( parameters )
  765. * IS
  766. * BEGIN
  767. * END;
  768. * CREATE PROCEDURE proc_name( parameters )
  769. * EXTERNAL NAME ... ;
  770. * CREATE PROCEDURE proc_name( parameters )
  771. * BEGIN
  772. * END;
  773. *
  774. * CREATE FUNCTION f_GetClassName(
  775. * IN @object VARCHAR(128)
  776. * ,IN @code VARCHAR(128)
  777. * )
  778. * RETURNS VARCHAR(200)
  779. * DETERMINISTIC
  780. * BEGIN
  781. *
  782. * IF( @object = 'user_state' ) THEN
  783. * SET something = something;
  784. * END IF;
  785. *
  786. * RETURN @name;
  787. * END;
  788. *
  789. * Note, a Package adds scope to the items within.
  790. * create or replace package demo_pkg is
  791. * test_var number;
  792. * function test_func return varchar2;
  793. * function more.test_func2 return varchar2;
  794. * end demo_pkg;
  795. * So the tags generated here, contain the package name:
  796. * demo_pkg.test_var
  797. * demo_pkg.test_func
  798. * demo_pkg.more.test_func2
  799. */
  800. const sqlKind kind = isKeyword (token, KEYWORD_function) ?
  801. SQLTAG_FUNCTION : SQLTAG_PROCEDURE;
  802. Assert (isKeyword (token, KEYWORD_function) ||
  803. isKeyword (token, KEYWORD_procedure));
  804. vStringCopy(saveScope, token->scope);
  805. readToken (token);
  806. copyToken (name, token);
  807. readToken (token);
  808. if (isType (token, TOKEN_PERIOD))
  809. {
  810. /*
  811. * If this is an Oracle package, then the token->scope should
  812. * already be set. If this is the case, also add this value to the
  813. * scope.
  814. * If this is not an Oracle package, chances are the scope should be
  815. * blank and the value just read is the OWNER or CREATOR of the
  816. * function and should not be considered part of the scope.
  817. */
  818. if ( vStringLength(saveScope) > 0 )
  819. {
  820. addToScope(token, name->string);
  821. }
  822. readToken (token);
  823. copyToken (name, token);
  824. readToken (token);
  825. }
  826. if (isType (token, TOKEN_OPEN_PAREN))
  827. {
  828. /* Reads to the next token after the TOKEN_CLOSE_PAREN */
  829. skipArgumentList(token);
  830. }
  831. if (kind == SQLTAG_FUNCTION)
  832. {
  833. if (isKeyword (token, KEYWORD_return) || isKeyword (token, KEYWORD_returns))
  834. {
  835. /* Read datatype */
  836. readToken (token);
  837. /*
  838. * Read token after which could be the
  839. * command terminator if a prototype
  840. * or an open parantheses
  841. */
  842. readToken (token);
  843. if (isType (token, TOKEN_OPEN_PAREN))
  844. {
  845. /* Reads to the next token after the TOKEN_CLOSE_PAREN */
  846. skipArgumentList(token);
  847. }
  848. }
  849. }
  850. if( isCmdTerm (token) )
  851. {
  852. makeSqlTag (name, SQLTAG_PROTOTYPE);
  853. }
  854. else
  855. {
  856. while (!(isKeyword (token, KEYWORD_is) ||
  857. isKeyword (token, KEYWORD_begin) ||
  858. isKeyword (token, KEYWORD_at) ||
  859. isKeyword (token, KEYWORD_internal) ||
  860. isKeyword (token, KEYWORD_external) ||
  861. isKeyword (token, KEYWORD_url) ||
  862. isType (token, TOKEN_EQUAL) ||
  863. isCmdTerm (token)
  864. )
  865. )
  866. {
  867. if ( isKeyword (token, KEYWORD_result) )
  868. {
  869. readToken (token);
  870. if (isType (token, TOKEN_OPEN_PAREN))
  871. {
  872. /* Reads to the next token after the TOKEN_CLOSE_PAREN */
  873. skipArgumentList(token);
  874. }
  875. } else {
  876. readToken (token);
  877. }
  878. }
  879. if (isKeyword (token, KEYWORD_at) ||
  880. isKeyword (token, KEYWORD_url) ||
  881. isKeyword (token, KEYWORD_internal) ||
  882. isKeyword (token, KEYWORD_external) )
  883. {
  884. addToScope(token, name->string);
  885. if (isType (name, TOKEN_IDENTIFIER) ||
  886. isType (name, TOKEN_STRING) ||
  887. !isKeyword (token, KEYWORD_NONE)
  888. )
  889. makeSqlTag (name, kind);
  890. vStringClear (token->scope);
  891. }
  892. if ( isType (token, TOKEN_EQUAL) )
  893. readToken (token);
  894. if ( isKeyword (token, KEYWORD_declare) )
  895. parseDeclare (token, FALSE);
  896. if (isKeyword (token, KEYWORD_is) ||
  897. isKeyword (token, KEYWORD_begin) )
  898. {
  899. addToScope(token, name->string);
  900. if (isType (name, TOKEN_IDENTIFIER) ||
  901. isType (name, TOKEN_STRING) ||
  902. !isKeyword (token, KEYWORD_NONE)
  903. )
  904. makeSqlTag (name, kind);
  905. parseBlock (token, TRUE);
  906. vStringClear (token->scope);
  907. }
  908. }
  909. vStringCopy(token->scope, saveScope);
  910. deleteToken (name);
  911. vStringDelete(saveScope);
  912. }
  913. static void parseRecord (tokenInfo *const token)
  914. {
  915. /*
  916. * Make it a bit forgiving, this is called from
  917. * multiple functions, parseTable, parseType
  918. */
  919. if (!isType (token, TOKEN_OPEN_PAREN))
  920. readToken (token);
  921. Assert (isType (token, TOKEN_OPEN_PAREN));
  922. do
  923. {
  924. if ( isType (token, TOKEN_COMMA) || isType (token, TOKEN_OPEN_PAREN) )
  925. readToken (token);
  926. /*
  927. * Create table statements can end with various constraints
  928. * which must be excluded from the SQLTAG_FIELD.
  929. * create table t1 (
  930. * c1 integer,
  931. * c2 char(30),
  932. * c3 numeric(10,5),
  933. * c4 integer,
  934. * constraint whatever,
  935. * primary key(c1),
  936. * foreign key (),
  937. * check ()
  938. * )
  939. */
  940. if (! (isKeyword(token, KEYWORD_primary) ||
  941. isKeyword(token, KEYWORD_references) ||
  942. isKeyword(token, KEYWORD_unique) ||
  943. isKeyword(token, KEYWORD_check) ||
  944. isKeyword(token, KEYWORD_constraint) ||
  945. isKeyword(token, KEYWORD_foreign) ) )
  946. {
  947. if (isType (token, TOKEN_IDENTIFIER) ||
  948. isType (token, TOKEN_STRING))
  949. makeSqlTag (token, SQLTAG_FIELD);
  950. }
  951. while (!(isType (token, TOKEN_COMMA) ||
  952. isType (token, TOKEN_CLOSE_PAREN) ||
  953. isType (token, TOKEN_OPEN_PAREN)
  954. ))
  955. {
  956. readToken (token);
  957. /*
  958. * A table structure can look like this:
  959. * create table t1 (
  960. * c1 integer,
  961. * c2 char(30),
  962. * c3 numeric(10,5),
  963. * c4 integer
  964. * )
  965. * We can't just look for a COMMA or CLOSE_PAREN
  966. * since that will not deal with the numeric(10,5)
  967. * case. So we need to skip the argument list
  968. * when we find an open paren.
  969. */
  970. if (isType (token, TOKEN_OPEN_PAREN))
  971. {
  972. /* Reads to the next token after the TOKEN_CLOSE_PAREN */
  973. skipArgumentList(token);
  974. }
  975. }
  976. } while (! isType (token, TOKEN_CLOSE_PAREN));
  977. }
  978. static void parseType (tokenInfo *const token)
  979. {
  980. tokenInfo *const name = newToken ();
  981. vString * saveScope = vStringNew ();
  982. vStringCopy(saveScope, token->scope);
  983. /* If a scope has been set, add it to the name */
  984. addToScope (name, token->scope);
  985. readToken (name);
  986. if (isType (name, TOKEN_IDENTIFIER))
  987. {
  988. readToken (token);
  989. if (isKeyword (token, KEYWORD_is))
  990. {
  991. readToken (token);
  992. addToScope (token, name->string);
  993. switch (token->keyword)
  994. {
  995. case KEYWORD_record:
  996. case KEYWORD_object:
  997. makeSqlTag (name, SQLTAG_RECORD);
  998. parseRecord (token);
  999. break;
  1000. case KEYWORD_table:
  1001. makeSqlTag (name, SQLTAG_TABLE);
  1002. break;
  1003. case KEYWORD_ref:
  1004. readToken (token);
  1005. if (isKeyword (token, KEYWORD_cursor))
  1006. makeSqlTag (name, SQLTAG_CURSOR);
  1007. break;
  1008. default: break;
  1009. }
  1010. vStringClear (token->scope);
  1011. }
  1012. }
  1013. vStringCopy(token->scope, saveScope);
  1014. deleteToken (name);
  1015. vStringDelete(saveScope);
  1016. }
  1017. static void parseSimple (tokenInfo *const token, const sqlKind kind)
  1018. {
  1019. /* This will simply make the tagname from the first word found */
  1020. readToken (token);
  1021. if (isType (token, TOKEN_IDENTIFIER) ||
  1022. isType (token, TOKEN_STRING))
  1023. makeSqlTag (token, kind);
  1024. }
  1025. static void parseDeclare (tokenInfo *const token, const boolean local)
  1026. {
  1027. /*
  1028. * PL/SQL declares are of this format:
  1029. * IS|AS
  1030. * [declare]
  1031. * CURSOR curname ...
  1032. * varname1 datatype;
  1033. * varname2 datatype;
  1034. * varname3 datatype;
  1035. * begin
  1036. */
  1037. if (isKeyword (token, KEYWORD_declare))
  1038. readToken (token);
  1039. while (! isKeyword (token, KEYWORD_begin) && ! isKeyword (token, KEYWORD_end))
  1040. {
  1041. switch (token->keyword)
  1042. {
  1043. case KEYWORD_cursor: parseSimple (token, SQLTAG_CURSOR); break;
  1044. case KEYWORD_function: parseSubProgram (token); break;
  1045. case KEYWORD_procedure: parseSubProgram (token); break;
  1046. case KEYWORD_subtype: parseSimple (token, SQLTAG_SUBTYPE); break;
  1047. case KEYWORD_trigger: parseSimple (token, SQLTAG_TRIGGER); break;
  1048. case KEYWORD_type: parseType (token); break;
  1049. default:
  1050. if (isType (token, TOKEN_IDENTIFIER))
  1051. {
  1052. if (local)
  1053. {
  1054. makeSqlTag (token, SQLTAG_LOCAL_VARIABLE);
  1055. }
  1056. else
  1057. {
  1058. makeSqlTag (token, SQLTAG_VARIABLE);
  1059. }
  1060. }
  1061. break;
  1062. }
  1063. findToken (token, TOKEN_SEMICOLON);
  1064. readToken (token);
  1065. }
  1066. }
  1067. static void parseDeclareANSI (tokenInfo *const token, const boolean local)
  1068. {
  1069. tokenInfo *const type = newToken ();
  1070. /*
  1071. * ANSI declares are of this format:
  1072. * BEGIN
  1073. * DECLARE varname1 datatype;
  1074. * DECLARE varname2 datatype;
  1075. * ...
  1076. *
  1077. * This differ from PL/SQL where DECLARE preceeds the BEGIN block
  1078. * and the DECLARE keyword is not repeated.
  1079. */
  1080. while (isKeyword (token, KEYWORD_declare))
  1081. {
  1082. readToken (token);
  1083. readToken (type);
  1084. if (isKeyword (type, KEYWORD_cursor))
  1085. makeSqlTag (token, SQLTAG_CURSOR);
  1086. else if (isKeyword (token, KEYWORD_local) &&
  1087. isKeyword (type, KEYWORD_temporary))
  1088. {
  1089. /*
  1090. * DECLARE LOCAL TEMPORARY TABLE table_name (
  1091. * c1 int,
  1092. * c2 int
  1093. * );
  1094. */
  1095. readToken (token);
  1096. if (isKeyword (token, KEYWORD_table))
  1097. {
  1098. readToken (token);
  1099. if (isType(token, TOKEN_IDENTIFIER) ||
  1100. isType(token, TOKEN_STRING) )
  1101. {
  1102. makeSqlTag (token, SQLTAG_TABLE);
  1103. }
  1104. }
  1105. }
  1106. else if (isType (token, TOKEN_IDENTIFIER) ||
  1107. isType (token, TOKEN_STRING))
  1108. {
  1109. if (local)
  1110. makeSqlTag (token, SQLTAG_LOCAL_VARIABLE);
  1111. else
  1112. makeSqlTag (token, SQLTAG_VARIABLE);
  1113. }
  1114. findToken (token, TOKEN_SEMICOLON);
  1115. readToken (token);
  1116. }
  1117. deleteToken (type);
  1118. }
  1119. static void parseLabel (tokenInfo *const token)
  1120. {
  1121. /*
  1122. * A label has this format:
  1123. * <<tobacco_dependency>>
  1124. * DECLARE
  1125. * v_senator VARCHAR2(100) := 'THURMOND, JESSE';
  1126. * BEGIN
  1127. * IF total_contributions (v_senator, 'TOBACCO') > 25000
  1128. * THEN
  1129. * <<alochol_dependency>>
  1130. * DECLARE
  1131. * v_senator VARCHAR2(100) := 'WHATEVERIT, TAKES';
  1132. * BEGIN
  1133. * ...
  1134. */
  1135. Assert (isType (token, TOKEN_BLOCK_LABEL_BEGIN));
  1136. readToken (token);
  1137. if (isType (token, TOKEN_IDENTIFIER))
  1138. {
  1139. makeSqlTag (token, SQLTAG_BLOCK_LABEL);
  1140. readToken (token); /* read end of label */
  1141. }
  1142. }
  1143. static void parseStatements (tokenInfo *const token, const boolean exit_on_endif )
  1144. {
  1145. boolean isAnsi = TRUE;
  1146. boolean stmtTerm = FALSE;
  1147. do
  1148. {
  1149. if (isType (token, TOKEN_BLOCK_LABEL_BEGIN))
  1150. parseLabel (token);
  1151. else
  1152. {
  1153. switch (token->keyword)
  1154. {
  1155. case KEYWORD_exception:
  1156. /*
  1157. * EXCEPTION
  1158. * <exception handler>;
  1159. *
  1160. * Where an exception handler could be:
  1161. * BEGIN
  1162. * WHEN OTHERS THEN
  1163. * x := x + 3;
  1164. * END;
  1165. * In this case we need to skip this keyword and
  1166. * move on to the next token without reading until
  1167. * TOKEN_SEMICOLON;
  1168. */
  1169. readToken (token);
  1170. continue;
  1171. case KEYWORD_when:
  1172. /*
  1173. * WHEN statements can be used in exception clauses
  1174. * and CASE statements. The CASE statement should skip
  1175. * these given below we skip over to an END statement.
  1176. * But for an exception clause, we can have:
  1177. * EXCEPTION
  1178. * WHEN OTHERS THEN
  1179. * BEGIN
  1180. * x := x + 3;
  1181. * END;
  1182. * If we skip to the TOKEN_SEMICOLON, we miss the begin
  1183. * of a nested BEGIN END block. So read the next token
  1184. * after the THEN and restart the LOOP.
  1185. */
  1186. while (! isKeyword (token, KEYWORD_then))
  1187. readToken (token);
  1188. readToken (token);
  1189. continue;
  1190. case KEYWORD_if:
  1191. /*
  1192. * We do not want to look for a ; since for an empty
  1193. * IF block, it would skip over the END.
  1194. * IF...THEN
  1195. * END IF;
  1196. *
  1197. * IF...THEN
  1198. * ELSE
  1199. * END IF;
  1200. *
  1201. * IF...THEN
  1202. * ELSEIF...THEN
  1203. * ELSE
  1204. * END IF;
  1205. *
  1206. * or non-ANSI
  1207. * IF ...
  1208. * BEGIN
  1209. * END
  1210. */
  1211. while ( ! isKeyword (token, KEYWORD_then) &&
  1212. ! isKeyword (token, KEYWORD_begin) )
  1213. {
  1214. readToken (token);
  1215. }
  1216. if( isKeyword (token, KEYWORD_begin ) )
  1217. {
  1218. isAnsi = FALSE;
  1219. parseBlock(token, FALSE);
  1220. /*
  1221. * Handle the non-Ansi IF blocks.
  1222. * parseBlock consumes the END, so if the next
  1223. * token in a command terminator (like GO)
  1224. * we know we are done with this statement.
  1225. */
  1226. if ( isCmdTerm (token) )
  1227. stmtTerm = TRUE;
  1228. }
  1229. else
  1230. {
  1231. readToken (token);
  1232. while( ! (isKeyword (token, KEYWORD_end ) ||
  1233. isKeyword (token, KEYWORD_endif ) )
  1234. )
  1235. {
  1236. if ( isKeyword (token, KEYWORD_else) ||
  1237. isKeyword (token, KEYWORD_elseif) )
  1238. readToken (token);
  1239. parseStatements (token, TRUE);
  1240. if ( isCmdTerm(token) )
  1241. readToken (token);
  1242. }
  1243. /*
  1244. * parseStatements returns when it finds an END, an IF
  1245. * should follow the END for ANSI anyway.
  1246. * IF...THEN
  1247. * END IF;
  1248. */
  1249. if( isKeyword (token, KEYWORD_end ) )
  1250. readToken (token);
  1251. if( isKeyword (token, KEYWORD_if ) || isKeyword (token, KEYWORD_endif ) )
  1252. {
  1253. readToken (token);
  1254. if ( isCmdTerm(token) )
  1255. stmtTerm = TRUE;
  1256. }
  1257. else
  1258. {
  1259. /*
  1260. * Well we need to do something here.
  1261. * There are lots of different END statements
  1262. * END;
  1263. * END CASE;
  1264. * ENDIF;
  1265. * ENDCASE;
  1266. */
  1267. }
  1268. }
  1269. break;
  1270. case KEYWORD_loop:
  1271. case KEYWORD_case:
  1272. case KEYWORD_for:
  1273. /*
  1274. * LOOP...
  1275. * END LOOP;
  1276. *
  1277. * CASE
  1278. * WHEN '1' THEN
  1279. * END CASE;
  1280. *
  1281. * FOR loop_name AS cursor_name CURSOR FOR ...
  1282. * DO
  1283. * END FOR;
  1284. */
  1285. if( isKeyword (token, KEYWORD_for ) )
  1286. {
  1287. /* loop name */
  1288. readToken (token);
  1289. /* AS */
  1290. readToken (token);
  1291. while ( ! isKeyword (token, KEYWORD_is) )
  1292. {
  1293. /*
  1294. * If this is not an AS keyword this is
  1295. * not a proper FOR statement and should
  1296. * simply be ignored
  1297. */
  1298. return;
  1299. }
  1300. while ( ! isKeyword (token, KEYWORD_do) )
  1301. readToken (token);
  1302. }
  1303. readToken (token);
  1304. while( ! isKeyword (token, KEYWORD_end ) )
  1305. {
  1306. /*
  1307. if ( isKeyword (token, KEYWORD_else) ||
  1308. isKeyword (token, KEYWORD_elseif) )
  1309. readToken (token);
  1310. */
  1311. parseStatements (token, FALSE);
  1312. if ( isCmdTerm(token) )
  1313. readToken (token);
  1314. }
  1315. if( isKeyword (token, KEYWORD_end ) )
  1316. readToken (token);
  1317. /*
  1318. * Typically ended with
  1319. * END LOOP [loop name];
  1320. * END CASE
  1321. * END FOR [loop name];
  1322. */
  1323. if ( isKeyword (token, KEYWORD_loop) ||
  1324. isKeyword (token, KEYWORD_case) ||
  1325. isKeyword (token, KEYWORD_for) )
  1326. readToken (token);
  1327. if ( isCmdTerm(token) )
  1328. stmtTerm = TRUE;
  1329. break;
  1330. case KEYWORD_create:
  1331. readToken (token);
  1332. parseKeywords(token);
  1333. break;
  1334. case KEYWORD_declare:
  1335. case KEYWORD_begin:
  1336. parseBlock (token, TRUE);
  1337. break;
  1338. case KEYWORD_end:
  1339. break;
  1340. default:
  1341. readToken (token);
  1342. break;
  1343. }
  1344. /*
  1345. * Not all statements must end in a semi-colon
  1346. * begin
  1347. * if current publisher <> 'publish' then
  1348. * signal UE_FailStatement
  1349. * end if
  1350. * end;
  1351. * The last statement prior to an end ("signal" above) does
  1352. * not need a semi-colon, nor does the end if, since it is
  1353. * also the last statement prior to the end of the block.
  1354. *
  1355. * So we must read to the first semi-colon or an END block
  1356. */
  1357. while ( ! stmtTerm &&
  1358. ! ( isKeyword (token, KEYWORD_end) ||
  1359. (isCmdTerm(token)) )
  1360. )
  1361. {
  1362. if ( isKeyword (token, KEYWORD_endif) &&
  1363. exit_on_endif )
  1364. return;
  1365. if (isType (token, TOKEN_COLON) )
  1366. {
  1367. /*
  1368. * A : can signal a loop name
  1369. * myloop:
  1370. * LOOP
  1371. * LEAVE myloop;
  1372. * END LOOP;
  1373. * Unfortunately, labels do not have a
  1374. * cmd terminator, therefore we have to check
  1375. * if the next token is a keyword and process
  1376. * it accordingly.
  1377. */
  1378. readToken (token);
  1379. if ( isKeyword (token, KEYWORD_loop) ||
  1380. isKeyword (token, KEYWORD_while) ||
  1381. isKeyword (token, KEYWORD_for) )
  1382. /* parseStatements (token); */
  1383. return;
  1384. }
  1385. readToken (token);
  1386. if (isType (token, TOKEN_OPEN_PAREN) ||
  1387. isType (token, TOKEN_OPEN_CURLY) ||
  1388. isType (token, TOKEN_OPEN_SQUARE) )
  1389. skipToMatched (token);
  1390. /*
  1391. * Since we know how to parse various statements
  1392. * if we detect them, parse them to completion
  1393. */
  1394. if (isType (token, TOKEN_BLOCK_LABEL_BEGIN) ||
  1395. isKeyword (token, KEYWORD_exception) ||
  1396. isKeyword (token, KEYWORD_loop) ||
  1397. isKeyword (token, KEYWORD_case) ||
  1398. isKeyword (token, KEYWORD_for) ||
  1399. isKeyword (token, KEYWORD_begin) )
  1400. parseStatements (token, FALSE);
  1401. else if (isKeyword (token, KEYWORD_if))
  1402. parseStatements (token, TRUE);
  1403. }
  1404. }
  1405. /*
  1406. * We assumed earlier all statements ended with a command terminator.
  1407. * See comment above, now, only read if the current token
  1408. * is not a command terminator.
  1409. */
  1410. if ( isCmdTerm(token) && ! stmtTerm )
  1411. stmtTerm = TRUE;
  1412. } while (! isKeyword (token, KEYWORD_end) &&
  1413. ! (exit_on_endif && isKeyword (token, KEYWORD_endif) ) &&
  1414. ! stmtTerm );
  1415. }
  1416. static void parseBlock (tokenInfo *const token, const boolean local)
  1417. {
  1418. if (isType (token, TOKEN_BLOCK_LABEL_BEGIN))
  1419. {
  1420. parseLabel (token);
  1421. readToken (token);
  1422. }
  1423. if (! isKeyword (token, KEYWORD_begin))
  1424. {
  1425. readToken (token);
  1426. /*
  1427. * These are Oracle style declares which generally come
  1428. * between an IS/AS and BEGIN block.
  1429. */
  1430. parseDeclare (token, local);
  1431. }
  1432. if (isKeyword (token, KEYWORD_begin))
  1433. {
  1434. readToken (token);
  1435. /*
  1436. * Check for ANSI declarations which always follow
  1437. * a BEGIN statement. This routine will not advance
  1438. * the token if none are found.
  1439. */
  1440. parseDeclareANSI (token, local);
  1441. token->begin_end_nest_lvl++;
  1442. while (! isKeyword (token, KEYWORD_end))
  1443. {
  1444. parseStatements (token, FALSE);
  1445. if ( isCmdTerm(token) )
  1446. readToken (token);
  1447. }
  1448. token->begin_end_nest_lvl--;
  1449. /*
  1450. * Read the next token (we will assume
  1451. * it is the command delimiter)
  1452. */
  1453. readToken (token);
  1454. /*
  1455. * Check if the END block is terminated
  1456. */
  1457. if ( !isCmdTerm (token) )
  1458. {
  1459. /*
  1460. * Not sure what to do here at the moment.
  1461. * I think the routine that calls parseBlock
  1462. * must expect the next token has already
  1463. * been read since it is possible this
  1464. * token is not a command delimiter.
  1465. */
  1466. /* findCmdTerm (token, FALSE); */
  1467. }
  1468. }
  1469. }
  1470. static void parsePackage (tokenInfo *const token)
  1471. {
  1472. /*
  1473. * Packages can be specified in a number of ways:
  1474. * CREATE OR REPLACE PACKAGE pkg_name AS
  1475. * or
  1476. * CREATE OR REPLACE PACKAGE owner.pkg_name AS
  1477. * or by specifying a package body
  1478. * CREATE OR REPLACE PACKAGE BODY pkg_name AS
  1479. * CREATE OR REPLACE PACKAGE BODY owner.pkg_name AS
  1480. */
  1481. tokenInfo *const name = newToken ();
  1482. readToken (name);
  1483. if (isKeyword (name, KEYWORD_body))
  1484. {
  1485. /*
  1486. * Ignore the BODY tag since we will process
  1487. * the body or prototypes in the same manner
  1488. */
  1489. readToken (name);
  1490. }
  1491. /* Check for owner.pkg_name */
  1492. while (! isKeyword (token, KEYWORD_is))
  1493. {
  1494. readToken (token);
  1495. if ( isType(token, TOKEN_PERIOD) )
  1496. {
  1497. readToken (name);
  1498. }
  1499. }
  1500. if (isKeyword (token, KEYWORD_is))
  1501. {
  1502. if (isType (name, TOKEN_IDENTIFIER) ||
  1503. isType (name, TOKEN_STRING))
  1504. makeSqlTag (name, SQLTAG_PACKAGE);
  1505. addToScope (token, name->string);
  1506. parseBlock (token, FALSE);
  1507. vStringClear (token->scope);
  1508. }
  1509. findCmdTerm (token, FALSE);
  1510. deleteToken (name);
  1511. }
  1512. static void parseTable (tokenInfo *const token)
  1513. {
  1514. tokenInfo *const name = newToken ();
  1515. /*
  1516. * This deals with these formats:
  1517. * create table t1 (c1 int);
  1518. * create global tempoary table t2 (c1 int);
  1519. * create table "t3" (c1 int);
  1520. * create table bob.t4 (c1 int);
  1521. * create table bob."t5" (c1 int);
  1522. * create table "bob"."t6" (c1 int);
  1523. * create table bob."t7" (c1 int);
  1524. * Proxy tables use this format:
  1525. * create existing table bob."t7" AT '...';
  1526. * SQL Server and Sybase formats
  1527. * create table OnlyTable (
  1528. * create table dbo.HasOwner (
  1529. * create table [dbo].[HasOwnerSquare] (
  1530. * create table master.dbo.HasDb (
  1531. * create table master..HasDbNoOwner (
  1532. * create table [master].dbo.[HasDbAndOwnerSquare] (
  1533. * create table [master]..[HasDbNoOwnerSquare] (
  1534. */
  1535. /* This could be a database, owner or table name */
  1536. readToken (name);
  1537. if (isType (name, TOKEN_OPEN_SQUARE))
  1538. {
  1539. readToken (name);
  1540. /* Read close square */
  1541. readToken (token);
  1542. }
  1543. readToken (token);
  1544. if (isType (token, TOKEN_PERIOD))
  1545. {
  1546. /*
  1547. * This could be a owner or table name.
  1548. * But this is also a special case since the table can be
  1549. * referenced with a blank owner:
  1550. * dbname..tablename
  1551. */
  1552. readToken (name);
  1553. if (isType (name, TOKEN_OPEN_SQUARE))
  1554. {
  1555. readToken (name);
  1556. /* Read close square */
  1557. readToken (token);
  1558. }
  1559. /* Check if a blank name was provided */
  1560. if (isType (name, TOKEN_PERIOD))
  1561. {
  1562. readToken (name);
  1563. if (isType (name, TOKEN_OPEN_SQUARE))
  1564. {
  1565. readToken (name);
  1566. /* Read close square */
  1567. readToken (token);
  1568. }
  1569. }
  1570. readToken (token);
  1571. if (isType (token, TOKEN_PERIOD))
  1572. {
  1573. /* This can only be the table name */
  1574. readToken (name);
  1575. if (isType (name, TOKEN_OPEN_SQUARE))
  1576. {
  1577. readToken (name);
  1578. /* Read close square */
  1579. readToken (token);
  1580. }
  1581. readToken (token);
  1582. }
  1583. }
  1584. if (isType (token, TOKEN_OPEN_PAREN))
  1585. {
  1586. if (isType (name, TOKEN_IDENTIFIER) ||
  1587. isType (name, TOKEN_STRING))
  1588. {
  1589. makeSqlTag (name, SQLTAG_TABLE);
  1590. vStringCopy(token->scope, name->string);
  1591. parseRecord (token);
  1592. vStringClear (token->scope);
  1593. }
  1594. }
  1595. else if (isKeyword (token, KEYWORD_at))
  1596. {
  1597. if (isType (name, TOKEN_IDENTIFIER))
  1598. {
  1599. makeSqlTag (name, SQLTAG_TABLE);
  1600. }
  1601. }
  1602. findCmdTerm (token, FALSE);
  1603. deleteToken (name);
  1604. }
  1605. static void parseIndex (tokenInfo *const token)
  1606. {
  1607. tokenInfo *const name = newToken ();
  1608. tokenInfo *const owner = newToken ();
  1609. /*
  1610. * This deals with these formats
  1611. * create index i1 on t1(c1) create index "i2" on t1(c1)
  1612. * create virtual unique clustered index "i3" on t1(c1)
  1613. * create unique clustered index "i4" on t1(c1)
  1614. * create clustered index "i5" on t1(c1)
  1615. * create bitmap index "i6" on t1(c1)
  1616. */
  1617. readToken (name);
  1618. readToken (token);
  1619. if (isType (token, TOKEN_PERIOD))
  1620. {
  1621. readToken (name);
  1622. readToken (token);
  1623. }
  1624. if ( isKeyword (token, KEYWORD_on) &&
  1625. (isType (name, TOKEN_IDENTIFIER) || isType (name, TOKEN_STRING) ) )
  1626. {
  1627. readToken (owner);
  1628. readToken (token);
  1629. if (isType (token, TOKEN_PERIOD))
  1630. {
  1631. readToken (owner);
  1632. readToken (token);
  1633. }
  1634. addToScope(name, owner->string);
  1635. makeSqlTag (name, SQLTAG_INDEX);
  1636. }
  1637. findCmdTerm (token, FALSE);
  1638. deleteToken (name);
  1639. deleteToken (owner);
  1640. }
  1641. static void parseEvent (tokenInfo *const token)
  1642. {
  1643. tokenInfo *const name = newToken ();
  1644. /*
  1645. * This deals with these formats
  1646. * create event e1 handler begin end;
  1647. * create event "e2" handler begin end;
  1648. * create event dba."e3" handler begin end;
  1649. * create event "dba"."e4" handler begin end;
  1650. */
  1651. readToken (name);
  1652. readToken (token);
  1653. if (isType (token, TOKEN_PERIOD))
  1654. {
  1655. readToken (name);
  1656. }
  1657. while (! (isKeyword (token, KEYWORD_handler) ||
  1658. (isType (token, TOKEN_SEMICOLON))) )
  1659. {
  1660. readToken (token);
  1661. }
  1662. if ( isKeyword (token, KEYWORD_handler) ||
  1663. isType (token, TOKEN_SEMICOLON) )
  1664. {
  1665. makeSqlTag (name, SQLTAG_EVENT);
  1666. }
  1667. if (isKeyword (token, KEYWORD_handler))
  1668. {
  1669. readToken (token);
  1670. if ( isKeyword (token, KEYWORD_begin) )
  1671. {
  1672. parseBlock (token, TRUE);
  1673. }
  1674. findCmdTerm (token, TRUE);
  1675. }
  1676. deleteToken (name);
  1677. }
  1678. static void parseTrigger (tokenInfo *const token)
  1679. {
  1680. tokenInfo *const name = newToken ();
  1681. tokenInfo *const table = newToken ();
  1682. /*
  1683. * This deals with these formats
  1684. * create or replace trigger tr1 begin end;
  1685. * create trigger "tr2" begin end;
  1686. * drop trigger "droptr1";
  1687. * create trigger "tr3" CALL sp_something();
  1688. * create trigger "owner"."tr4" begin end;
  1689. * create trigger "tr5" not valid;
  1690. * create trigger "tr6" begin end;
  1691. */
  1692. readToken (name);
  1693. readToken (token);
  1694. if (isType (token, TOKEN_PERIOD))
  1695. {
  1696. readToken (name);
  1697. readToken (token);
  1698. }
  1699. while ( !isKeyword (token, KEYWORD_on) &&
  1700. !isCmdTerm (token) )
  1701. {
  1702. readToken (token);
  1703. }
  1704. /*if (! isType (token, TOKEN_SEMICOLON) ) */
  1705. if (! isCmdTerm (token) )
  1706. {
  1707. readToken (table);
  1708. readToken (token);
  1709. if (isType (token, TOKEN_PERIOD))
  1710. {
  1711. readToken (table);
  1712. readToken (token);
  1713. }
  1714. while (! (isKeyword (token, KEYWORD_begin) ||
  1715. (isKeyword (token, KEYWORD_call)) ||
  1716. ( isCmdTerm (token))) )
  1717. {
  1718. if ( isKeyword (token, KEYWORD_declare) )
  1719. {
  1720. addToScope(token, name->string);
  1721. parseDeclare(token, TRUE);
  1722. vStringClear(token->scope);
  1723. }
  1724. else
  1725. readToken (token);
  1726. }
  1727. if ( isKeyword (token, KEYWORD_begin) ||
  1728. isKeyword (token, KEYWORD_call) )
  1729. {
  1730. addToScope(name, table->string);
  1731. makeSqlTag (name, SQLTAG_TRIGGER);
  1732. addToScope(token, table->string);
  1733. if ( isKeyword (token, KEYWORD_begin) )
  1734. {
  1735. parseBlock (token, TRUE);
  1736. }
  1737. vStringClear(token->scope);
  1738. }
  1739. }
  1740. findCmdTerm (token, TRUE);
  1741. deleteToken (name);
  1742. deleteToken (table);
  1743. }
  1744. static void parsePublication (tokenInfo *const token)
  1745. {
  1746. tokenInfo *const name = newToken ();
  1747. /*
  1748. * This deals with these formats
  1749. * create or replace publication pu1 ()
  1750. * create publication "pu2" ()
  1751. * create publication dba."pu3" ()
  1752. * create publication "dba"."pu4" ()
  1753. */
  1754. readToken (name);
  1755. readToken (token);
  1756. if (isType (token, TOKEN_PERIOD))
  1757. {
  1758. readToken (name);
  1759. readToken (token);
  1760. }
  1761. if (isType (token, TOKEN_OPEN_PAREN))
  1762. {
  1763. if (isType (name, TOKEN_IDENTIFIER) ||
  1764. isType (name, TOKEN_STRING))
  1765. {
  1766. makeSqlTag (name, SQLTAG_PUBLICATION);
  1767. }
  1768. }
  1769. findCmdTerm (token, FALSE);
  1770. deleteToken (name);
  1771. }
  1772. static void parseService (tokenInfo *const token)
  1773. {
  1774. tokenInfo *const name = newToken ();
  1775. /*
  1776. * This deals with these formats
  1777. * CREATE SERVICE s1 TYPE 'HTML'
  1778. * AUTHORIZATION OFF USER DBA AS
  1779. * SELECT *
  1780. * FROM SYS.SYSTABLE;
  1781. * CREATE SERVICE "s2" TYPE 'HTML'
  1782. * AUTHORIZATION OFF USER DBA AS
  1783. * CALL sp_Something();
  1784. */
  1785. readToken (name);
  1786. readToken (token);
  1787. if (isKeyword (token, KEYWORD_type))
  1788. {
  1789. if (isType (name, TOKEN_IDENTIFIER) ||
  1790. isType (name, TOKEN_STRING))
  1791. {
  1792. makeSqlTag (name, SQLTAG_SERVICE);
  1793. }
  1794. }
  1795. findCmdTerm (token, FALSE);
  1796. deleteToken (name);
  1797. }
  1798. static void parseDomain (tokenInfo *const token)
  1799. {
  1800. tokenInfo *const name = newToken ();
  1801. /*
  1802. * This deals with these formats
  1803. * CREATE DOMAIN|DATATYPE [AS] your_name ...;
  1804. */
  1805. readToken (name);
  1806. if (isKeyword (name, KEYWORD_is))
  1807. {
  1808. readToken (name);
  1809. }
  1810. readToken (token);
  1811. if (isType (name, TOKEN_IDENTIFIER) ||
  1812. isType (name, TOKEN_STRING))
  1813. {
  1814. makeSqlTag (name, SQLTAG_DOMAIN);
  1815. }
  1816. findCmdTerm (token, FALSE);
  1817. deleteToken (name);
  1818. }
  1819. static void parseDrop (tokenInfo *const token)
  1820. {
  1821. /*
  1822. * This deals with these formats
  1823. * DROP TABLE|PROCEDURE|DOMAIN|DATATYPE name;
  1824. *
  1825. * Just simply skip over these statements.
  1826. * They are often confused with PROCEDURE prototypes
  1827. * since the syntax is similar, this effectively deals with
  1828. * the issue for all types.
  1829. */
  1830. findCmdTerm (token, FALSE);
  1831. }
  1832. static void parseVariable (tokenInfo *const token)
  1833. {
  1834. tokenInfo *const name = newToken ();
  1835. /*
  1836. * This deals with these formats
  1837. * create variable varname1 integer;
  1838. * create variable @varname2 integer;
  1839. * create variable "varname3" integer;
  1840. * drop variable @varname3;
  1841. */
  1842. readToken (name);
  1843. readToken (token);
  1844. if ( (isType (name, TOKEN_IDENTIFIER) || isType (name, TOKEN_STRING))
  1845. && !isType (token, TOKEN_SEMICOLON) )
  1846. {
  1847. makeSqlTag (name, SQLTAG_VARIABLE);
  1848. }
  1849. findCmdTerm (token, TRUE);
  1850. deleteToken (name);
  1851. }
  1852. static void parseSynonym (tokenInfo *const token)
  1853. {
  1854. tokenInfo *const name = newToken ();
  1855. /*
  1856. * This deals with these formats
  1857. * create variable varname1 integer;
  1858. * create variable @varname2 integer;
  1859. * create variable "varname3" integer;
  1860. * drop variable @varname3;
  1861. */
  1862. readToken (name);
  1863. readToken (token);
  1864. if ( (isType (name, TOKEN_IDENTIFIER) || isType (name, TOKEN_STRING))
  1865. && isKeyword (token, KEYWORD_for) )
  1866. {
  1867. makeSqlTag (name, SQLTAG_SYNONYM);
  1868. }
  1869. findCmdTerm (token, TRUE);
  1870. deleteToken (name);
  1871. }
  1872. static void parseView (tokenInfo *const token)
  1873. {
  1874. tokenInfo *const name = newToken ();
  1875. /*
  1876. * This deals with these formats
  1877. * create variable varname1 integer;
  1878. * create variable @varname2 integer;
  1879. * create variable "varname3" integer;
  1880. * drop variable @varname3;
  1881. */
  1882. readToken (name);
  1883. readToken (token);
  1884. if (isType (token, TOKEN_PERIOD))
  1885. {
  1886. readToken (name);
  1887. readToken (token);
  1888. }
  1889. if ( isType (token, TOKEN_OPEN_PAREN) )
  1890. {
  1891. skipArgumentList(token);
  1892. }
  1893. while (!(isKeyword (token, KEYWORD_is) ||
  1894. isType (token, TOKEN_SEMICOLON)
  1895. ))
  1896. {
  1897. readToken (token);
  1898. }
  1899. if ( (isType (name, TOKEN_IDENTIFIER) || isType (name, TOKEN_STRING))
  1900. && isKeyword (token, KEYWORD_is) )
  1901. {
  1902. makeSqlTag (name, SQLTAG_VIEW);
  1903. }
  1904. findCmdTerm (token, TRUE);
  1905. deleteToken (name);
  1906. }
  1907. static void parseMLTable (tokenInfo *const token)
  1908. {
  1909. tokenInfo *const version = newToken ();
  1910. tokenInfo *const table = newToken ();
  1911. tokenInfo *const event = newToken ();
  1912. /*
  1913. * This deals with these formats
  1914. * call dbo.ml_add_table_script( 'version', 'table_name', 'event',
  1915. * 'some SQL statement'
  1916. * );
  1917. */
  1918. readToken (token);
  1919. if ( isType (token, TOKEN_OPEN_PAREN) )
  1920. {
  1921. readToken (version);
  1922. readToken (token);
  1923. while (!(isType (token, TOKEN_COMMA) ||
  1924. isType (token, TOKEN_CLOSE_PAREN)
  1925. ))
  1926. {
  1927. readToken (token);
  1928. }
  1929. if (isType (token, TOKEN_COMMA))
  1930. {
  1931. readToken (table);
  1932. readToken (token);
  1933. while (!(isType (token, TOKEN_COMMA) ||
  1934. isType (token, TOKEN_CLOSE_PAREN)
  1935. ))
  1936. {
  1937. readToken (token);
  1938. }
  1939. if (isType (token, TOKEN_COMMA))
  1940. {
  1941. readToken (event);
  1942. if (isType (version, TOKEN_STRING) &&
  1943. isType (table, TOKEN_STRING) &&
  1944. isType (event, TOKEN_STRING) )
  1945. {
  1946. addToScope(version, table->string);
  1947. addToScope(version, event->string);
  1948. makeSqlTag (version, SQLTAG_MLTABLE);
  1949. }
  1950. }
  1951. if( !isType (token, TOKEN_CLOSE_PAREN) )
  1952. findToken (token, TOKEN_CLOSE_PAREN);
  1953. }
  1954. }
  1955. findCmdTerm (token, TRUE);
  1956. deleteToken (version);
  1957. deleteToken (table);
  1958. deleteToken (event);
  1959. }
  1960. static void parseMLConn (tokenInfo *const token)
  1961. {
  1962. tokenInfo *const version = newToken ();
  1963. tokenInfo *const event = newToken ();
  1964. /*
  1965. * This deals with these formats
  1966. * call ml_add_connection_script( 'version', 'event',
  1967. * 'some SQL statement'
  1968. * );
  1969. */
  1970. readToken (token);
  1971. if ( isType (token, TOKEN_OPEN_PAREN) )
  1972. {
  1973. readToken (version);
  1974. readToken (token);
  1975. while (!(isType (token, TOKEN_COMMA) ||
  1976. isType (token, TOKEN_CLOSE_PAREN)
  1977. ))
  1978. {
  1979. readToken (token);
  1980. }
  1981. if (isType (token, TOKEN_COMMA))
  1982. {
  1983. readToken (event);
  1984. if (isType (version, TOKEN_STRING) &&
  1985. isType (event, TOKEN_STRING) )
  1986. {
  1987. addToScope(version, event->string);
  1988. makeSqlTag (version, SQLTAG_MLCONN);
  1989. }
  1990. }
  1991. if( !isType (token, TOKEN_CLOSE_PAREN) )
  1992. findToken (token, TOKEN_CLOSE_PAREN);
  1993. }
  1994. findCmdTerm (token, TRUE);
  1995. deleteToken (version);
  1996. deleteToken (event);
  1997. }
  1998. static void parseMLProp (tokenInfo *const token)
  1999. {
  2000. tokenInfo *const component = newToken ();
  2001. tokenInfo *const prop_set_name = newToken ();
  2002. tokenInfo *const prop_name = newToken ();
  2003. /*
  2004. * This deals with these formats
  2005. * ml_add_property (
  2006. * 'comp_name',
  2007. * 'prop_set_name',
  2008. * 'prop_name',
  2009. * 'prop_value'
  2010. * )
  2011. */
  2012. readToken (token);
  2013. if ( isType (token, TOKEN_OPEN_PAREN) )
  2014. {
  2015. readToken (component);
  2016. readToken (token);
  2017. while (!(isType (token, TOKEN_COMMA) ||
  2018. isType (token, TOKEN_CLOSE_PAREN)
  2019. ))
  2020. {
  2021. readToken (token);
  2022. }
  2023. if (isType (token, TOKEN_COMMA))
  2024. {
  2025. readToken (prop_set_name);
  2026. readToken (token);
  2027. while (!(isType (token, TOKEN_COMMA) ||
  2028. isType (token, TOKEN_CLOSE_PAREN)
  2029. ))
  2030. {
  2031. readToken (token);
  2032. }
  2033. if (isType (token, TOKEN_COMMA))
  2034. {
  2035. readToken (prop_name);
  2036. if (isType (component, TOKEN_STRING) &&
  2037. isType (prop_set_name, TOKEN_STRING) &&
  2038. isType (prop_name, TOKEN_STRING) )
  2039. {
  2040. addToScope(component, prop_set_name->string);
  2041. addToScope(component, prop_name->string);
  2042. makeSqlTag (component, SQLTAG_MLPROP);
  2043. }
  2044. }
  2045. if( !isType (token, TOKEN_CLOSE_PAREN) )
  2046. findToken (token, TOKEN_CLOSE_PAREN);
  2047. }
  2048. }
  2049. findCmdTerm (token, TRUE);
  2050. deleteToken (component);
  2051. deleteToken (prop_set_name);
  2052. deleteToken (prop_name);
  2053. }
  2054. static void parseComment (tokenInfo *const token)
  2055. {
  2056. /*
  2057. * This deals with this statement:
  2058. * COMMENT TO PRESERVE FORMAT ON PROCEDURE "DBA"."test" IS
  2059. * {create PROCEDURE DBA."test"()
  2060. * BEGIN
  2061. * signal dave;
  2062. * END
  2063. * }
  2064. * ;
  2065. * The comment can contain anything between the CURLY
  2066. * braces
  2067. * COMMENT ON USER "admin" IS
  2068. * 'Administration Group'
  2069. * ;
  2070. * Or it could be a simple string with no curly braces
  2071. */
  2072. while (! isKeyword (token, KEYWORD_is))
  2073. {
  2074. readToken (token);
  2075. }
  2076. readToken (token);
  2077. if ( isType(token, TOKEN_OPEN_CURLY) )
  2078. {
  2079. findToken (token, TOKEN_CLOSE_CURLY);
  2080. }
  2081. findCmdTerm (token, TRUE);
  2082. }
  2083. static void parseKeywords (tokenInfo *const token)
  2084. {
  2085. switch (token->keyword)
  2086. {
  2087. case KEYWORD_begin: parseBlock (token, FALSE); break;
  2088. case KEYWORD_comment: parseComment (token); break;
  2089. case KEYWORD_cursor: parseSimple (token, SQLTAG_CURSOR); break;
  2090. case KEYWORD_datatype: parseDomain (token); break;
  2091. case KEYWORD_declare: parseBlock (token, FALSE); break;
  2092. case KEYWORD_domain: parseDomain (token); break;
  2093. case KEYWORD_drop: parseDrop (token); break;
  2094. case KEYWORD_event: parseEvent (token); break;
  2095. case KEYWORD_function: parseSubProgram (token); break;
  2096. case KEYWORD_if: parseStatements (token, FALSE); break;
  2097. case KEYWORD_index: parseIndex (token); break;
  2098. case KEYWORD_ml_table: parseMLTable (token); break;
  2099. case KEYWORD_ml_table_lang: parseMLTable (token); break;
  2100. case KEYWORD_ml_table_dnet: parseMLTable (token); break;
  2101. case KEYWORD_ml_table_java: parseMLTable (token); break;
  2102. case KEYWORD_ml_table_chk: parseMLTable (token); break;
  2103. case KEYWORD_ml_conn: parseMLConn (token); break;
  2104. case KEYWORD_ml_conn_lang: parseMLConn (token); break;
  2105. case KEYWORD_ml_conn_dnet: parseMLConn (token); break;
  2106. case KEYWORD_ml_conn_java: parseMLConn (token); break;
  2107. case KEYWORD_ml_conn_chk: parseMLConn (token); break;
  2108. case KEYWORD_ml_prop: parseMLProp (token); break;
  2109. case KEYWORD_package: parsePackage (token); break;
  2110. case KEYWORD_procedure: parseSubProgram (token); break;
  2111. case KEYWORD_publication: parsePublication (token); break;
  2112. case KEYWORD_service: parseService (token); break;
  2113. case KEYWORD_subtype: parseSimple (token, SQLTAG_SUBTYPE); break;
  2114. case KEYWORD_synonym: parseSynonym (token); break;
  2115. case KEYWORD_table: parseTable (token); break;
  2116. case KEYWORD_trigger: parseTrigger (token); break;
  2117. case KEYWORD_type: parseType (token); break;
  2118. case KEYWORD_variable: parseVariable (token); break;
  2119. case KEYWORD_view: parseView (token); break;
  2120. default: break;
  2121. }
  2122. }
  2123. static void parseSqlFile (tokenInfo *const token)
  2124. {
  2125. do
  2126. {
  2127. readToken (token);
  2128. if (isType (token, TOKEN_BLOCK_LABEL_BEGIN))
  2129. parseLabel (token);
  2130. else
  2131. parseKeywords (token);
  2132. } while (! isKeyword (token, KEYWORD_end));
  2133. }
  2134. static void initialize (const langType language)
  2135. {
  2136. Assert (sizeof (SqlKinds) / sizeof (SqlKinds [0]) == SQLTAG_COUNT);
  2137. Lang_sql = language;
  2138. buildSqlKeywordHash ();
  2139. }
  2140. static void findSqlTags (void)
  2141. {
  2142. tokenInfo *const token = newToken ();
  2143. exception_t exception = (exception_t) (setjmp (Exception));
  2144. while (exception == ExceptionNone)
  2145. parseSqlFile (token);
  2146. deleteToken (token);
  2147. }
  2148. extern parserDefinition* SqlParser (void)
  2149. {
  2150. static const char *const extensions [] = { "sql", NULL };
  2151. parserDefinition* def = parserNew ("SQL");
  2152. def->kinds = SqlKinds;
  2153. def->kindCount = KIND_COUNT (SqlKinds);
  2154. def->extensions = extensions;
  2155. def->parser = findSqlTags;
  2156. def->initialize = initialize;
  2157. return def;
  2158. }
  2159. /* vi:set tabstop=4 shiftwidth=4 noexpandtab: */