File Coverage

src/parser.c
Criterion Covered Total %
statement 215 253 84.9
branch 221 372 59.4
condition n/a
subroutine n/a
pod n/a
total 436 625 69.7


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: INSER INTO\n");
35 9           p = parse_insert_into(aTHX_ state, p);
36 9           break;
37             case CONTEXT_INSERT_VALUES:
38             DEBUG_OUT("context: INSER 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 144 100         while (*p != '\0' && *p != ')') {
368             // get column name
369 126           SV** column_ref = XSUTIL_AV_FETCH(columns, column_id);
370 126 50         if (! column_ref) croak("Cannot fetch columns[%d]", column_id);
371 126           SV* column = *column_ref;
372             DEBUG_OUT("key: %s\n", SvPV_nolen(column));
373              
374             // extract and store value
375 126 100         if (IS_NULL_STR(p)) {
    50          
    50          
    50          
376             // null value
377 12           p += 4;
378             DEBUG_OUT("value: (NULL)\n");
379 12           XSUTIL_HV_STORE_ENT_NOINC(row, column, &PL_sv_undef);
380             }
381 114 100         else if (*p == '\'' || *p == '"') {
382             // string
383             char symbol = *p;
384 54           char* mark = ++p;
385             SV* value = NULL;
386 796 50         while (*p != '\0' && *p != symbol) {
    100          
387             // handle mySQL string literals
388 742 100         if (*p == '\\') {
389 2           char c[2] = {'\0', '\0'};
390              
391 2 50         if (value == NULL) {
392 2           value = newSVpvn(mark, p - mark);
393             }
394             else {
395 0           sv_catpvn(value, mark, p - mark);
396             }
397              
398 2           p++;
399 2           switch (*p) {
400             case '0':
401 0           c[0] = '\0';
402 0           break;
403             case 'b':
404 0           c[0] = '\b';
405 0           break;
406             case 'n':
407 0           c[0] = '\n';
408 0           break;
409             case 'r':
410 0           c[0] = '\r';
411 0           break;
412             case 't':
413 0           c[0] = '\t';
414 0           break;
415             case 'Z':
416 0           c[0] = '\x1A';
417 0           break;
418             default:
419 2           c[0] = *p;
420 2           break;
421             }
422 2           sv_catpvn(value, c, 1);
423 2           mark = p + 1;
424             }
425 742           p++;
426             }
427 54 100         if (value == NULL) {
428 52           value = newSVpvn(mark, p - mark);
429             }
430             else {
431 2           sv_catpvn(value, mark, p - mark);
432             }
433             DEBUG_OUT("value: %s (string)\n", SvPV_nolen(value));
434 54           XSUTIL_HV_STORE_ENT_NOINC(row, column, value);
435             }
436             else {
437             // normal value
438             char* mark = p;
439 123 100         while (*p != '\0' && *p != ',' && *p != ')') p++;
    50          
440              
441             // store
442 60           SV* value = newSVpvn(mark, p - mark);
443             DEBUG_OUT("value: %s(normal value)\n", SvPV_nolen(value));
444 126           XSUTIL_HV_STORE_ENT_NOINC(row, column, value);
445             }
446              
447             // skip
448 180 100         while (*p != '\0' && *p != ',' && *p != ')') p++;
    100          
449 126 50         if (*p == '\0') return p;
450 126 50         SKIP_WSPACE(p);
    50          
451 126 100         if (*p == ',') {
452 108           column_id++;
453 126           p++;
454             }
455 126 50         SKIP_WSPACE(p);
    50          
456             }
457              
458 36           XSUTIL_AV_PUSH_REF(ret, (SV*)row);
459             }
460 36 100         else if (*p == ';') {
461 9           p++;
462 9           set_parser_context(aTHX_ state, CONTEXT_GLOBAL);
463 9           break;
464             }
465             else {
466 45           p++;
467             }
468             }
469             return p;
470             }
471