File Coverage

src/parser.c
Criterion Covered Total %
statement 218 262 83.2
branch 224 396 56.5
condition n/a
subroutine n/a
pod n/a
total 442 658 67.1


line stmt bran cond sub pod time code
1             #include "parser.h"
2             #include "macro.h"
3             #include "state.h"
4             #include "debug.h"
5             #include "xsutil.h"
6              
7 182           AV* parse (pTHX_ HV* state, register char* p) {
8             DEBUG_OUT("line: %s", p);
9              
10             AV* ret = NULL;
11 571 100         while (*p != '\0') {
12 468 50         SKIP_WSPACE(p);
    100          
13 389 50         if (*p == '\0') break;
14              
15 389           const IV context = get_parser_context(aTHX_ state);
16 389           switch (context) {
17             case CONTEXT_GLOBAL:
18             DEBUG_OUT("context: GLOBAL\n");
19 219           p = parse_global(aTHX_ state, p);
20 389           break;
21             case CONTEXT_CREATE_TABLE:
22             DEBUG_OUT("context: CREATE TABLE\n");
23 46           p = parse_create_table(aTHX_ state, p);
24 46           break;
25             case CONTEXT_CREATE_TABLE_COLUMN:
26             DEBUG_OUT("context: CREATE TABLE (column)\n");
27 22           p = parse_create_table_column(aTHX_ state, p);
28 22           break;
29             case CONTEXT_BLOCK_COMMENT:
30             DEBUG_OUT("context: BLOCK COMMENT\n");
31 84           p = parse_block_comment(aTHX_ state, p);
32 84           break;
33             case CONTEXT_INSERT_INTO:
34             DEBUG_OUT("context: INSERT INTO\n");
35 9           p = parse_insert_into(aTHX_ state, p);
36 9           break;
37             case CONTEXT_INSERT_VALUES:
38             DEBUG_OUT("context: INSERT INTO (values)\n");
39 9 50         if (! ret) {
40 9           ret = newAV_mortal();
41             }
42 9           p = parse_insert_values(aTHX_ state, p, ret);
43 9           break;
44             default:
45 0           croak("Unexpected context:%d", (int)context);
46             }
47             }
48              
49 182           return ret;
50             }
51              
52 219           char* parse_global (pTHX_ HV* state, register char* p) {
53 636 100         while (*p != '\0') {
54             DEBUG_OUT("[GLOBAL] char: %c\n", *p);
55 494 100         if (*p == '/') {
56             // is comment ?
57 66 50         if (*(p + 1) == '*') {
58 66           p += 2;
59 66           set_parser_context(aTHX_ state, CONTEXT_BLOCK_COMMENT);
60 66           break;
61             }
62             else {
63 0           p++;
64             }
65             }
66 428 100         else if (*p == '-' && *(p + 1) == '-') {
    50          
67             // line comment
68 1180 100         while (*p != '\0' && *p != '\n') p++;
69             }
70 392 100         else if (IS_WSPACE(p)) {
71 40 50         SKIP_WSPACE(p);
    100          
72             }
73 372 100         else if (*p == 'C') {
74             // is create table?
75 8 50         if (IS_CREATE(p)) {
    100          
    50          
    50          
    50          
    50          
76 2           p += 6;
77 4 50         SKIP_WSPACE(p);
    100          
78 2 50         if (*p == '\0') break;
79 2 50         if (IS_TABLE(p)) {
    50          
    50          
    50          
    50          
80 2           p += 5;
81 4 50         SKIP_WSPACE(p);
    100          
82              
83             // set context and extract table name
84 2           set_parser_context(aTHX_ state, CONTEXT_CREATE_TABLE);
85             char* mark;
86 2 50         if (*p == '`') {
87 2           mark = ++p;
88 10 100         while (*p != '\0' && *p != '`') p++;
89             }
90             else {
91             mark = p;
92 0 0         SKIP_UNTIL_WSPACE(p);
    0          
93             }
94 2 50         if (*p == '\0') break;
95 2           set_table(aTHX_ state, mark, p - mark);
96 2           p++;
97 4 50         SKIP_WSPACE(p);
    100          
98              
99             break;
100             }
101             }
102             else {
103 6           p++;
104             }
105             }
106 364 100         else if (*p == 'I') {
107             // is insert?
108 16 50         if (IS_INSERT(p)) {
    100          
    50          
    50          
    50          
    50          
109 9           p += 6;
110 18 50         SKIP_WSPACE(p);
    100          
111 9 50         if (*p == '\0') break;
112 9 50         if (IS_INTO(p)) {
    50          
    50          
    50          
113 9           p += 4;
114 18 50         SKIP_WSPACE(p);
    100          
115              
116             // set context and extract table name
117 9           set_parser_context(aTHX_ state, CONTEXT_INSERT_INTO);
118             char* mark;
119 9 50         if (*p == '`') {
120 9           mark = ++p;
121 44 100         while (*p != '\0' && *p != '`') p++;
122             }
123             else {
124             mark = p;
125 0 0         SKIP_UNTIL_WSPACE(p);
    0          
126             }
127 9 50         if (*p == '\0') break;
128 9           set_table(aTHX_ state, mark, p - mark);
129 9           p++;
130 18 50         SKIP_WSPACE(p);
    100          
131              
132             break;
133             }
134             }
135             else {
136 7           p++;
137             }
138             }
139             else {
140 417           p++;
141             }
142             }
143 219           return p;
144             }
145              
146 84           char* parse_block_comment (pTHX_ HV* state, register char* p) {
147 4959 100         while (*p != '\0') {
148             DEBUG_OUT("[BLOCK COMMENT] char: %c\n", *p);
149 4859 100         if (*p++ == '*') {
150 68 50         if (*p++ == '/') {
151 68           restore_context(aTHX_ state);
152 152           break;
153             }
154             }
155             }
156 84           return p;
157             }
158              
159 46           char* parse_create_table (pTHX_ HV* state, register char* p) {
160 46           HV* schema = get_current_schema(aTHX_ state);
161 46           AV* columns = get_or_create_columns(aTHX_ schema);
162 50 100         while (*p != '\0') {
163             DEBUG_OUT("[CREATE TABLE] char: %c\n", *p);
164 48 100         if (*p == '/') {
165             // is comment ?
166 2 50         if (*(p + 1) == '*') {
167 2           p += 2;
168 2           set_parser_context(aTHX_ state, CONTEXT_BLOCK_COMMENT);
169 2           break;
170             }
171             }
172 46 50         else if (*p == '-' && *(p + 1) == '-') {
    0          
173             // line comment
174 0 0         while (*p != '\0' && *p != '\n') p++;
175             }
176 46 50         else if (IS_WSPACE(p)) {
177 0 0         SKIP_WSPACE(p);
    0          
178             }
179 46 100         else if (*p == '(') {
180 2           p++;
181 2           incr_nest(aTHX_ state);
182             }
183 44 50         else if (*p == ')') {
184 0           p++;
185 0           decr_nest(aTHX_ state);
186             }
187             else {
188 44           const IV nest = get_nest(aTHX_ state);
189             DEBUG_OUT("nest:%d\n", (int)nest);
190 44 100         if (nest == 0) {
191 108 100         while (*p != '\0' && *p != '(' && *p != ';' && *p != '/') p++;
    100          
    50          
192 4 100         if (*p == ';') {
193 2           p++;
194 2           set_parser_context(aTHX_ state, CONTEXT_GLOBAL);
195 2           break;
196             }
197             else {
198 2           continue;
199             }
200             }
201 40 50         else if (nest == 1) {
202 40 100         if (! IS_MAYBE_KEY(p)) {
    50          
    50          
    50          
    0          
    0          
    0          
    0          
    50          
    0          
    0          
    0          
    0          
    0          
    100          
    50          
    50          
    50          
    50          
    50          
    50          
203             char *mark;
204 34 100         if (*p == '`') {
205 14           mark = ++p;
206 102 100         while (*p != '\0' && *p != '`') p++;
207             }
208             else {
209             mark = p;
210 40 100         SKIP_UNTIL_WSPACE(p);
    50          
211             }
212 34 100         if (*p == '\0') break;
213 14           XSUTIL_AV_PUSH_NOINC(columns, newSVpvn(mark, p - mark));
214             DEBUG_OUT("column name: %.*s\n", (int)(p - mark), mark);
215 14           p++;
216             }
217 20           set_parser_context(aTHX_ state, CONTEXT_CREATE_TABLE_COLUMN);
218 20           break;
219             }
220             else {
221 4           p++;
222             }
223             }
224             }
225 46           return p;
226             }
227              
228 798           char* parse_create_table_column (pTHX_ HV* state, register char* p) {
229             while (1) {
230             DEBUG_OUT("[CREATE TABLE (column)] char: %c\n", *p);
231 776 100         if (*p == '\0') {
232             return p;
233             }
234 774 50         else if (*p == '/') {
235             // is comment ?
236 0 0         if (*(p + 1) == '*') {
237 0           p += 2;
238 0           set_parser_context(aTHX_ state, CONTEXT_BLOCK_COMMENT);
239 0           return p;
240             }
241             }
242 774 50         else if (*p == '-' && *(p + 1) == '-') {
    0          
243             // line comment
244 0 0         while (*p != '\0' && *p != '\n') p++;
245             }
246 774 100         else if (IS_WSPACE(p)) {
247 160 50         SKIP_WSPACE(p);
    100          
248             }
249 694 100         else if (*p == '(') {
250 16           p++;
251 16           incr_nest(aTHX_ state);
252             }
253 678 100         else if (*p == ')') {
254 18           const IV nest = get_nest(aTHX_ state);
255 18 100         if (nest == 0) break;
256 16           p++;
257 16           decr_nest(aTHX_ state);
258             }
259 660 100         else if (*p == '\'' || *p == '"') {
260 14           char symbol = *p++;
261 70 50         while (*p != '\0' && *p != symbol) {
    100          
262 56 100         if (*p == '\\') p++;
263 56           p++;
264             }
265 14 50         if (*p != '\0') p++;
266             }
267             else {
268 646           const IV nest = get_nest(aTHX_ state);
269             DEBUG_OUT("nest:%d\n", (int)nest);
270 646 100         if (nest == 0 && *p == ',') {
    100          
271             break;
272             }
273             else {
274 628           p++;
275             }
276             }
277             }
278              
279 20           set_parser_context(aTHX_ state, CONTEXT_CREATE_TABLE);
280 20 100         if (*p != ')') incr_nest(aTHX_ state); /* XXX: fix nest to 1 */
281 20           p++;
282 20           return p;
283             }
284              
285 9           char* parse_insert_into (pTHX_ HV* state, register char* p) {
286 21 50         while (*p != '\0') {
287             DEBUG_OUT("[INSERT INTO] char: %c\n", *p);
288 21 50         if (*p == '/') {
289             // is comment ?
290 0 0         if (*(p + 1) == '*') {
291 0           p += 2;
292 0           set_parser_context(aTHX_ state, CONTEXT_BLOCK_COMMENT);
293 0           break;
294             }
295             }
296 21 50         else if (*p == '-' && *(p + 1) == '-') {
    0          
297             // line comment
298 0 0         while (*p != '\0' && *p != '\n') p++;
299             }
300 21 100         else if (IS_WSPACE(p)) {
301 12 50         SKIP_WSPACE(p);
    100          
302             }
303 15 100         else if (*p == '(') {
304 6           p++;
305             DEBUG_OUT("SET COLUMNS (INSERT INTO)\n");
306 6           HV* schema = get_current_schema(aTHX_ state);
307 6           AV* columns = get_or_create_columns(aTHX_ schema);
308 6           av_clear(columns);
309              
310             char *mark;
311 48 100         while (*p != '\0' && *p != ')') {
312 42 50         SKIP_WSPACE(p);
    50          
313 42 50         if (*p == '`') {
314 42           mark = ++p;
315 306 100         while (*p != '\0' && *p != '`') p++;
316             }
317             else {
318             mark = p;
319 0 0         while (*p != '\0' && *p != ',' && *p != ')') p++;
    0          
320             }
321 42 50         if (*p == '\0') break;
322 42           XSUTIL_AV_PUSH_NOINC(columns, newSVpvn(mark, p - mark));
323             DEBUG_OUT("name: %.*s\n", (int)(p - mark), mark);
324 42 100         if (*++p == ',') p++;
325             }
326 6 50         if (*p == '\0') break;
327 6           p++;
328             }
329 9 50         else if (IS_VALUES(p)) {
    50          
    50          
    50          
    50          
    50          
330 9           p += 6;
331 9           set_parser_context(aTHX_ state, CONTEXT_INSERT_VALUES);
332 21           break;
333             }
334             else {
335             DEBUG_OUT("[INSERT INTO] Unexpected char: %c\n", *p);
336             }
337             }
338 9           return p;
339             }
340              
341 9           char* parse_insert_values (pTHX_ HV* state, register char* p, AV* ret) {
342 9           HV* schema = get_current_schema(aTHX_ state);
343 9           AV* columns = get_or_create_columns(aTHX_ schema);
344 54 50         while (*p != '\0') {
345             DEBUG_OUT("[INSERT INTO VALUES] char: %c\n", *p);
346 54 50         if (*p == '/') {
347             // is comment ?
348 0 0         if (*(p + 1) == '*') {
349 0           p += 2;
350 0           set_parser_context(aTHX_ state, CONTEXT_BLOCK_COMMENT);
351 0           break;
352             }
353             }
354 54 50         else if (*p == '-' && *(p + 1) == '-') {
    0          
355             // line comment
356 0 0         while (*p != '\0' && *p != '\n') p++;
357             }
358 54 50         else if (IS_WSPACE(p)) {
359 0 0         SKIP_WSPACE(p);
    0          
360             }
361 54 100         else if (*p == '(') {
362             DEBUG_OUT("SET VALUE\n");
363 18           HV* row = newHV_mortal();
364 18           p++;
365              
366             I32 column_id = 0;
367             int have_binary_literal = 0;
368 144 100         while (*p != '\0' && *p != ')') {
369             // get column name
370 126           SV** column_ref = XSUTIL_AV_FETCH(columns, column_id);
371 126 50         if (! column_ref) croak("Cannot fetch columns[%d]", column_id);
372 126           SV* column = *column_ref;
373             DEBUG_OUT("key: %s\n", SvPV_nolen(column));
374              
375             Again:
376             // extract and store value
377 126 100         if (IS_NULL_STR(p)) {
    50          
    50          
    50          
378 12 50         if (have_binary_literal)
379 0           croak("Invalid use of '_binary' keyword.");
380              
381             // null value
382 12           p += 4;
383             DEBUG_OUT("value: (NULL)\n");
384 12           XSUTIL_HV_STORE_ENT_NOINC(row, column, &PL_sv_undef);
385             }
386 114 50         else if (IS__binary(p)) {
    0          
    0          
    0          
    0          
    0          
    0          
387 0 0         if (have_binary_literal)
388 0           croak("Found duplicated '_binary' keyword.");
389              
390             // skip '_binary' character set introducer for string literals
391 0           p += 7;
392 0 0         SKIP_WSPACE(p);
    0          
393             have_binary_literal = 1;
394             goto Again;
395             }
396 114 100         else if (*p == '\'' || *p == '"') {
397             // string
398             char symbol = *p;
399 54           char* mark = ++p;
400             SV* value = NULL;
401 796 50         while (*p != '\0' && *p != symbol) {
    100          
402             // handle mySQL string literals
403 742 100         if (*p == '\\') {
404 2           char c[2] = {'\0', '\0'};
405              
406 2 50         if (value == NULL) {
407 2           value = newSVpvn(mark, p - mark);
408             }
409             else {
410 0           sv_catpvn(value, mark, p - mark);
411             }
412              
413 2           p++;
414 2           switch (*p) {
415             case '0':
416 0           c[0] = '\0';
417 0           break;
418             case 'b':
419 0           c[0] = '\b';
420 0           break;
421             case 'n':
422 0           c[0] = '\n';
423 0           break;
424             case 'r':
425 0           c[0] = '\r';
426 0           break;
427             case 't':
428 0           c[0] = '\t';
429 0           break;
430             case 'Z':
431 0           c[0] = '\x1A';
432 0           break;
433             default:
434 2           c[0] = *p;
435 2           break;
436             }
437 2           sv_catpvn(value, c, 1);
438 2           mark = p + 1;
439             }
440 742           p++;
441             }
442 54 100         if (value == NULL) {
443 52           value = newSVpvn(mark, p - mark);
444             }
445             else {
446 2           sv_catpvn(value, mark, p - mark);
447             }
448             DEBUG_OUT("value: %s (%sstring)\n",
449             SvPV_nolen(value),
450             have_binary_literal ? "binary " : "");
451 54           XSUTIL_HV_STORE_ENT_NOINC(row, column, value);
452             have_binary_literal = 0;
453             }
454             else {
455 60 50         if (have_binary_literal)
456 0           croak("Invalid use of '_binary' keyword.");
457              
458             // normal value
459             char* mark = p;
460 123 100         while (*p != '\0' && *p != ',' && *p != ')') p++;
    50          
461              
462             // store
463 60           SV* value = newSVpvn(mark, p - mark);
464             DEBUG_OUT("value: %s(normal value)\n", SvPV_nolen(value));
465 126           XSUTIL_HV_STORE_ENT_NOINC(row, column, value);
466             }
467              
468             // skip
469 180 100         while (*p != '\0' && *p != ',' && *p != ')') p++;
    100          
470 126 50         if (*p == '\0') return p;
471 126 50         SKIP_WSPACE(p);
    50          
472 126 100         if (*p == ',') {
473 108           column_id++;
474 126           p++;
475             }
476 126 50         SKIP_WSPACE(p);
    50          
477             }
478              
479 36           XSUTIL_AV_PUSH_REF(ret, (SV*)row);
480             }
481 36 100         else if (*p == ';') {
482 9           p++;
483 9           set_parser_context(aTHX_ state, CONTEXT_GLOBAL);
484 9           break;
485             }
486             else {
487 45           p++;
488             }
489             }
490             return p;
491             }
492