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