Line data Source code
1 : // Copyright (c) 2019-2025 Made to Order Software Corp. All Rights Reserved
2 : //
3 : // https://snapwebsites.org/project/prinbee
4 : // contact@m2osw.com
5 : //
6 : // This program is free software: you can redistribute it and/or modify
7 : // it under the terms of the GNU General Public License as published by
8 : // the Free Software Foundation, either version 3 of the License, or
9 : // (at your option) any later version.
10 : //
11 : // This program is distributed in the hope that it will be useful,
12 : // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 : // GNU General Public License for more details.
15 : //
16 : // You should have received a copy of the GNU General Public License
17 : // along with this program. If not, see <https://www.gnu.org/licenses/>.
18 :
19 : // prinbee
20 : //
21 : #include <prinbee/pbql/parser.h>
22 :
23 : #include <prinbee/exception.h>
24 :
25 :
26 : // self
27 : //
28 : #include "catch_main.h"
29 :
30 :
31 : // snaplogger
32 : //
33 : #include <snaplogger/message.h>
34 :
35 :
36 : // snapdev
37 : //
38 : #include <snapdev/not_reached.h>
39 : #include <snapdev/string_replace_many.h>
40 : #include <snapdev/to_lower.h>
41 :
42 :
43 : // C++
44 : //
45 : //#include <bitset>
46 : //#include <fstream>
47 : //#include <iomanip>
48 :
49 :
50 : // C
51 : //
52 : //#include <sys/stat.h>
53 : //#include <sys/types.h>
54 :
55 :
56 : // last include
57 : //
58 : #include <snapdev/poison.h>
59 :
60 :
61 :
62 : namespace
63 : {
64 :
65 :
66 :
67 112 : std::string escape_quotes(std::string const & s)
68 : {
69 : // double quotes which is the way to escape quotes in SQL
70 : //
71 448 : return snapdev::string_replace_many(s, {{"'", "''"}});
72 112 : }
73 :
74 :
75 256 : char const * optional_equal()
76 : {
77 256 : switch(rand() % 5)
78 : {
79 49 : case 0:
80 49 : return " ";
81 :
82 41 : case 1:
83 41 : return "=";
84 :
85 53 : case 2:
86 53 : return " =";
87 :
88 58 : case 3:
89 58 : return "= ";
90 :
91 55 : case 4:
92 55 : return " = ";
93 :
94 : }
95 0 : snapdev::NOT_REACHED();
96 : }
97 :
98 :
99 :
100 : } // no name namespace
101 :
102 :
103 :
104 2 : CATCH_TEST_CASE("parser", "[parser][pbql]")
105 : {
106 4 : CATCH_START_SECTION("parser: begin + select + commit/rollback")
107 : {
108 5 : for(int state(0); state < 4; ++state) // COMMIT / COMMIT + IF / ROLLBACK / ROLLBACK + IF
109 : {
110 16 : for(int work(0); work < 3; ++work)
111 : {
112 96 : for(int type(0); type < 7; ++type)
113 : {
114 : // BEGIN
115 252 : std::string script("BEGIN");
116 :
117 : // WORK/TRANSACTION
118 84 : if(work == 1)
119 : {
120 28 : script += " WORK";
121 : }
122 56 : else if(work == 2)
123 : {
124 28 : script += " TRANSACTION";
125 : }
126 :
127 : // ON
128 84 : if(type >= 3)
129 : {
130 48 : script += " ON";
131 : }
132 :
133 : // SCHEMA/DATA
134 84 : prinbee::pbql::transaction_t expected_transaction(prinbee::pbql::transaction_t::TRANSACTION_UNDEFINED);
135 84 : if(type == 1 || type == 3 || type == 5)
136 : {
137 36 : script += " SCHEMA";
138 36 : expected_transaction = prinbee::pbql::transaction_t::TRANSACTION_SCHEMA;
139 : }
140 48 : else if(type == 2 || type == 4 || type == 6)
141 : {
142 36 : script += " DATA";
143 36 : expected_transaction = prinbee::pbql::transaction_t::TRANSACTION_DATA;
144 : }
145 :
146 : // no IF for a BEGIN, type 5 and 6 are not use here
147 :
148 84 : script += ";\n";
149 :
150 : // COMMIT or ROLLBACK
151 84 : script += state < 2 ? "COMMIT" : "ROLLBACK";
152 :
153 : // WORK/TRANSACTION (the test would be better if the COMMIT/ROLLBACK would use a different set of parameters than the BEGIN...)
154 84 : if(work == 1)
155 : {
156 28 : script += " WORK";
157 : }
158 56 : else if(work == 2)
159 : {
160 28 : script += " TRANSACTION";
161 : }
162 :
163 : // ON
164 84 : if(type >= 3)
165 : {
166 48 : script += " ON";
167 : }
168 :
169 : // SCHEMA/DATA
170 : //prinbee::pbql::transaction_t expected_transaction(prinbee::pbql::transaction_t::TRANSACTION_UNDEFINED);
171 84 : if(type == 1 || type == 3 || type == 5)
172 : {
173 36 : script += " SCHEMA";
174 : //expected_transaction = prinbee::pbql::transaction_t::TRANSACTION_SCHEMA;
175 : }
176 48 : else if(type == 2 || type == 4 || type == 6)
177 : {
178 36 : script += " DATA";
179 : //expected_transaction = prinbee::pbql::transaction_t::TRANSACTION_DATA;
180 : }
181 :
182 : // IF <condition>
183 84 : if(type == 5 || type == 6)
184 : {
185 24 : script += " IF a > b";
186 24 : if(type == 6)
187 : {
188 12 : script += " OTHERWISE ";
189 12 : script += state >= 2 ? "COMMIT" : "ROLLBACK"; // test flip from above
190 : }
191 : }
192 :
193 84 : script += ";\n";
194 :
195 168 : SNAP_LOG_WARNING << "script [" << script << "]" << SNAP_LOG_SEND;
196 :
197 84 : prinbee::pbql::lexer::pointer_t lexer(std::make_shared<prinbee::pbql::lexer>());
198 84 : lexer->set_input(std::make_shared<prinbee::pbql::input>(script, "begin-test.pbql"));
199 84 : prinbee::pbql::parser::pointer_t parser(std::make_shared<prinbee::pbql::parser>(lexer));
200 84 : prinbee::pbql::command::vector_t const & commands(parser->parse());
201 :
202 84 : CATCH_REQUIRE(commands.size() == 2);
203 :
204 : // BEGIN
205 84 : CATCH_REQUIRE(commands[0]->get_command() == prinbee::pbql::command_t::COMMAND_BEGIN);
206 : // SCHEMA/DATA
207 84 : CATCH_REQUIRE(commands[0]->is_defined_as(prinbee::pbql::param_t::PARAM_TYPE) == prinbee::pbql::param_type_t::PARAM_TYPE_INT64);
208 84 : CATCH_REQUIRE(commands[0]->get_int64(prinbee::pbql::param_t::PARAM_TYPE) == static_cast<std::int64_t>(expected_transaction));
209 :
210 : // COMMIT / ROLLBACK
211 84 : CATCH_REQUIRE(commands[1]->get_command() == (state < 2 ? prinbee::pbql::command_t::COMMAND_COMMIT : prinbee::pbql::command_t::COMMAND_ROLLBACK));
212 : // SCHEMA/DATA
213 84 : CATCH_REQUIRE(commands[1]->is_defined_as(prinbee::pbql::param_t::PARAM_TYPE) == prinbee::pbql::param_type_t::PARAM_TYPE_INT64);
214 84 : CATCH_REQUIRE(commands[1]->get_int64(prinbee::pbql::param_t::PARAM_TYPE) == static_cast<std::int64_t>(expected_transaction));
215 84 : if(type == 5 || type == 6)
216 : {
217 24 : CATCH_REQUIRE(commands[1]->is_defined_as(prinbee::pbql::param_t::PARAM_CONDITION) == prinbee::pbql::param_type_t::PARAM_TYPE_STRING);
218 24 : CATCH_REQUIRE(commands[1]->get_string(prinbee::pbql::param_t::PARAM_CONDITION) == "a>b");
219 24 : }
220 : else
221 : {
222 60 : CATCH_REQUIRE(commands[1]->is_defined_as(prinbee::pbql::param_t::PARAM_CONDITION) == prinbee::pbql::param_type_t::PARAM_TYPE_UNKNOWN);
223 : }
224 84 : }
225 : }
226 : }
227 : }
228 3 : CATCH_END_SECTION()
229 :
230 4 : CATCH_START_SECTION("parser: create context")
231 : {
232 1 : int counter(1);
233 3 : for(int exists(0); exists < 2; ++exists)
234 : {
235 10 : for(int using_path(0); using_path < 4; ++using_path)
236 : {
237 64 : for(int owner(0); owner < 7; ++owner)
238 : {
239 224 : for(int comment(0); comment < 3; ++comment)
240 : {
241 : // CREATE CONTEXT
242 504 : std::string script("CREATE CONTEXT ");
243 :
244 : // IF NOT EXISTS
245 168 : if(exists != 0)
246 : {
247 84 : script += "IF NOT EXISTS ";
248 : }
249 :
250 : // <context-name>
251 168 : std::string context_name(SNAP_CATCH2_NAMESPACE::random_string(1, 97, SNAP_CATCH2_NAMESPACE::character_t::CHARACTER_LABEL));
252 168 : context_name += '_';
253 168 : context_name += std::to_string(counter);
254 168 : script += context_name;
255 :
256 : // USING '<context-path>'
257 168 : std::string context_path;
258 420 : for(int segment(0); segment < using_path; ++segment)
259 : {
260 252 : if(!context_path.empty())
261 : {
262 126 : context_path += '/';
263 : }
264 252 : context_path += SNAP_CATCH2_NAMESPACE::random_string(1, 100, SNAP_CATCH2_NAMESPACE::character_t::CHARACTER_LABEL);
265 : }
266 168 : if(!context_path.empty())
267 : {
268 126 : script += " USING '";
269 126 : script += context_path;
270 126 : script += '\'';
271 : }
272 :
273 : // WITH ( ... )
274 168 : std::string ownership;
275 168 : std::string group_member;
276 168 : std::string description;
277 168 : if(owner != 0 || comment != 0)
278 : {
279 160 : script += " WITH (";
280 160 : bool quoted_owner(rand() & 1);
281 160 : int order((rand() & 1) + 1);
282 160 : char const * sep("");
283 480 : for(int with(0); with < 2; ++with, order ^= 3)
284 : {
285 320 : if((order & 1) != 0)
286 : {
287 160 : if(owner != 0)
288 : {
289 : // WITH ( OWNER )
290 144 : script += sep;
291 144 : script += "OWNER";
292 144 : script += optional_equal();
293 144 : if(quoted_owner)
294 : {
295 68 : script += '\'';
296 : }
297 144 : if((owner & 1) == 0)
298 : {
299 72 : ownership = SNAP_CATCH2_NAMESPACE::random_string(1, 32, SNAP_CATCH2_NAMESPACE::character_t::CHARACTER_LABEL);
300 : }
301 : else
302 : {
303 72 : ownership = std::to_string(rand() & 0x7fff);
304 : }
305 144 : script += ownership;
306 144 : if(owner < 5)
307 : {
308 96 : if((owner & 2) == 0)
309 : {
310 48 : group_member = SNAP_CATCH2_NAMESPACE::random_string(1, 32, SNAP_CATCH2_NAMESPACE::character_t::CHARACTER_LABEL);
311 : }
312 : else
313 : {
314 48 : group_member = std::to_string(rand() & 0x7fff);
315 : }
316 96 : script += ':';
317 96 : script += group_member;
318 : }
319 144 : if(quoted_owner)
320 : {
321 68 : script += '\'';
322 : }
323 144 : sep = rand() & 1 ? ", " : ",";
324 : }
325 : }
326 320 : if((order & 2) != 0)
327 : {
328 160 : if(comment != 0)
329 : {
330 : // WITH ( COMMENT )
331 112 : description = SNAP_CATCH2_NAMESPACE::random_string(1, 500);
332 112 : script += sep;
333 112 : script += "COMMENT";
334 112 : script += optional_equal();
335 112 : script += '\'';
336 112 : script += escape_quotes(description);
337 112 : script += '\'';
338 112 : sep = rand() & 1 ? ", " : ",";
339 : }
340 : }
341 : }
342 160 : script += ')';
343 : }
344 168 : script += ';';
345 :
346 : //SNAP_LOG_WARNING << "script [" << script << "]" << SNAP_LOG_SEND;
347 :
348 168 : prinbee::pbql::lexer::pointer_t lexer(std::make_shared<prinbee::pbql::lexer>());
349 168 : lexer->set_input(std::make_shared<prinbee::pbql::input>(script, "create-context-test.pbql"));
350 168 : prinbee::pbql::parser::pointer_t parser(std::make_shared<prinbee::pbql::parser>(lexer));
351 168 : prinbee::pbql::command::vector_t const & commands(parser->parse());
352 :
353 168 : CATCH_REQUIRE(commands.size() == 1);
354 : // CREATE CONTEXT
355 168 : CATCH_REQUIRE(commands[0]->get_command() == prinbee::pbql::command_t::COMMAND_CREATE_CONTEXT);
356 : // [IF NOT EXISTS]
357 168 : CATCH_REQUIRE(commands[0]->is_defined_as(prinbee::pbql::param_t::PARAM_IF_EXISTS) == prinbee::pbql::param_type_t::PARAM_TYPE_BOOL);
358 168 : CATCH_REQUIRE(commands[0]->get_bool(prinbee::pbql::param_t::PARAM_IF_EXISTS) == (exists == 0));
359 : // <context-name>
360 168 : CATCH_REQUIRE(commands[0]->is_defined_as(prinbee::pbql::param_t::PARAM_NAME) == prinbee::pbql::param_type_t::PARAM_TYPE_STRING);
361 168 : CATCH_REQUIRE(commands[0]->get_string(prinbee::pbql::param_t::PARAM_NAME) == snapdev::to_lower(context_name));
362 : // [USING <context-path>]
363 168 : CATCH_REQUIRE(commands[0]->is_defined_as(prinbee::pbql::param_t::PARAM_PATH) == prinbee::pbql::param_type_t::PARAM_TYPE_STRING);
364 168 : if(context_path.empty())
365 : {
366 42 : CATCH_REQUIRE(commands[0]->get_string(prinbee::pbql::param_t::PARAM_PATH) == snapdev::to_lower(context_name));
367 : }
368 : else
369 : {
370 126 : CATCH_REQUIRE(commands[0]->get_string(prinbee::pbql::param_t::PARAM_PATH) == snapdev::to_lower(context_path));
371 : }
372 : // WITH ( OWNER [']<user>[:<group>]['] )
373 168 : CATCH_REQUIRE(commands[0]->is_defined_as(prinbee::pbql::param_t::PARAM_USER) == prinbee::pbql::param_type_t::PARAM_TYPE_STRING);
374 168 : CATCH_REQUIRE(commands[0]->get_string(prinbee::pbql::param_t::PARAM_USER) == ownership);
375 168 : CATCH_REQUIRE(commands[0]->is_defined_as(prinbee::pbql::param_t::PARAM_GROUP) == prinbee::pbql::param_type_t::PARAM_TYPE_STRING);
376 168 : CATCH_REQUIRE(commands[0]->get_string(prinbee::pbql::param_t::PARAM_GROUP) == group_member);
377 : // WITH ( COMMENT '<description>' )
378 168 : CATCH_REQUIRE(commands[0]->is_defined_as(prinbee::pbql::param_t::PARAM_DESCRIPTION) == prinbee::pbql::param_type_t::PARAM_TYPE_STRING);
379 168 : CATCH_REQUIRE(commands[0]->get_string(prinbee::pbql::param_t::PARAM_DESCRIPTION) == description);
380 :
381 : // to track the number of attempts in the context name
382 : //
383 168 : ++counter;
384 168 : }
385 : }
386 : }
387 : }
388 : }
389 3 : CATCH_END_SECTION()
390 2 : }
391 :
392 :
393 3 : CATCH_TEST_CASE("parser_error", "[parser][pbql][error]")
394 : {
395 5 : CATCH_START_SECTION("parser_error: missing lexer")
396 : {
397 1 : prinbee::pbql::lexer::pointer_t lexer;
398 4 : CATCH_REQUIRE_THROWS_MATCHES(
399 : std::make_shared<prinbee::pbql::parser>(lexer)
400 : , prinbee::logic_error
401 : , Catch::Matchers::ExceptionMessage(
402 : "logic_error: lexer missing."));
403 1 : }
404 4 : CATCH_END_SECTION()
405 :
406 5 : CATCH_START_SECTION("parser_error: create context errors")
407 : {
408 : {
409 : // CREATE CONTEXT <identifier>
410 3 : std::string script("CREATE CONTEXT 123;");
411 :
412 1 : prinbee::pbql::lexer::pointer_t lexer(std::make_shared<prinbee::pbql::lexer>());
413 1 : lexer->set_input(std::make_shared<prinbee::pbql::input>(script, "create-context-test.pbql"));
414 1 : prinbee::pbql::parser::pointer_t parser(std::make_shared<prinbee::pbql::parser>(lexer));
415 :
416 3 : CATCH_REQUIRE_THROWS_MATCHES(
417 : parser->parse()
418 : , prinbee::invalid_token
419 : , Catch::Matchers::ExceptionMessage(
420 : "prinbee_exception: create-context-test.pbql:1:16: expected an identifier after CREATE CONTEXT."));
421 1 : }
422 :
423 : {
424 : // CREATE CONTEXT IF <NOT>
425 3 : std::string script("CREATE CONTEXT IF FOO;");
426 :
427 1 : prinbee::pbql::lexer::pointer_t lexer(std::make_shared<prinbee::pbql::lexer>());
428 1 : lexer->set_input(std::make_shared<prinbee::pbql::input>(script, "create-context-test.pbql"));
429 1 : prinbee::pbql::parser::pointer_t parser(std::make_shared<prinbee::pbql::parser>(lexer));
430 :
431 3 : CATCH_REQUIRE_THROWS_MATCHES(
432 : parser->parse()
433 : , prinbee::invalid_token
434 : , Catch::Matchers::ExceptionMessage(
435 : "prinbee_exception: create-context-test.pbql:1:19: expected the NOT identifier after CREATE CONTEXT IF, not \"FOO\"."));
436 1 : }
437 :
438 : {
439 : // CREATE CONTEXT IF NOT EXIST<S>
440 3 : std::string script("CREATE CONTEXT IF NOT EXIST;");
441 :
442 1 : prinbee::pbql::lexer::pointer_t lexer(std::make_shared<prinbee::pbql::lexer>());
443 1 : lexer->set_input(std::make_shared<prinbee::pbql::input>(script, "create-context-test.pbql"));
444 1 : prinbee::pbql::parser::pointer_t parser(std::make_shared<prinbee::pbql::parser>(lexer));
445 :
446 3 : CATCH_REQUIRE_THROWS_MATCHES(
447 : parser->parse()
448 : , prinbee::invalid_token
449 : , Catch::Matchers::ExceptionMessage(
450 : "prinbee_exception: create-context-test.pbql:1:23: expected the EXISTS identifier after CREATE CONTEXT IF NOT, not \"EXIST\"."));
451 1 : }
452 :
453 : {
454 : // CREATE CONTEXT IF NOT EXISTS <identifier>
455 3 : std::string script("CREATE CONTEXT IF NOT EXISTS 123;");
456 :
457 1 : prinbee::pbql::lexer::pointer_t lexer(std::make_shared<prinbee::pbql::lexer>());
458 1 : lexer->set_input(std::make_shared<prinbee::pbql::input>(script, "create-context-test.pbql"));
459 1 : prinbee::pbql::parser::pointer_t parser(std::make_shared<prinbee::pbql::parser>(lexer));
460 :
461 3 : CATCH_REQUIRE_THROWS_MATCHES(
462 : parser->parse()
463 : , prinbee::invalid_token
464 : , Catch::Matchers::ExceptionMessage(
465 : "prinbee_exception: create-context-test.pbql:1:30: expected a IDENTIFIER after CREATE CONTEXT IF NOT EXISTS, not a INTEGER."));
466 1 : }
467 :
468 : {
469 : // CREATE CONTEXT my_context USING 123;
470 3 : std::string script("CREATE CONTEXT my_context USING 123;");
471 :
472 1 : prinbee::pbql::lexer::pointer_t lexer(std::make_shared<prinbee::pbql::lexer>());
473 1 : lexer->set_input(std::make_shared<prinbee::pbql::input>(script, "create-context-test.pbql"));
474 1 : prinbee::pbql::parser::pointer_t parser(std::make_shared<prinbee::pbql::parser>(lexer));
475 :
476 3 : CATCH_REQUIRE_THROWS_MATCHES(
477 : parser->parse()
478 : , prinbee::invalid_token
479 : , Catch::Matchers::ExceptionMessage(
480 : "prinbee_exception: create-context-test.pbql:1:33: expected a path after the USING keyword of CREATE CONTEXT."));
481 1 : }
482 :
483 : {
484 : // CREATE CONTEXT my_context USING 'path' <USING>;
485 3 : std::string script("CREATE CONTEXT my_context USING 'path' USING;");
486 :
487 1 : prinbee::pbql::lexer::pointer_t lexer(std::make_shared<prinbee::pbql::lexer>());
488 1 : lexer->set_input(std::make_shared<prinbee::pbql::input>(script, "create-context-test.pbql"));
489 1 : prinbee::pbql::parser::pointer_t parser(std::make_shared<prinbee::pbql::parser>(lexer));
490 :
491 3 : CATCH_REQUIRE_THROWS_MATCHES(
492 : parser->parse()
493 : , prinbee::invalid_token
494 : , Catch::Matchers::ExceptionMessage(
495 : "prinbee_exception: create-context-test.pbql:1:40: USING keyword found twice after CREATE CONTEXT."));
496 1 : }
497 :
498 : {
499 : // CREATE CONTEXT my_context USING '<empty>';
500 3 : std::string script("CREATE CONTEXT my_context USING '';");
501 :
502 1 : prinbee::pbql::lexer::pointer_t lexer(std::make_shared<prinbee::pbql::lexer>());
503 1 : lexer->set_input(std::make_shared<prinbee::pbql::input>(script, "create-context-test.pbql"));
504 1 : prinbee::pbql::parser::pointer_t parser(std::make_shared<prinbee::pbql::parser>(lexer));
505 :
506 3 : CATCH_REQUIRE_THROWS_MATCHES(
507 : parser->parse()
508 : , prinbee::invalid_token
509 : , Catch::Matchers::ExceptionMessage(
510 : "prinbee_exception: create-context-test.pbql:1:33: expected a non-empty path after the USING keyword of CREATE CONTEXT."));
511 1 : }
512 :
513 : {
514 : // CREATE CONTEXT my_context WITH <comment>;
515 3 : std::string script("CREATE CONTEXT my_context WITH comment;");
516 :
517 1 : prinbee::pbql::lexer::pointer_t lexer(std::make_shared<prinbee::pbql::lexer>());
518 1 : lexer->set_input(std::make_shared<prinbee::pbql::input>(script, "create-context-test.pbql"));
519 1 : prinbee::pbql::parser::pointer_t parser(std::make_shared<prinbee::pbql::parser>(lexer));
520 :
521 3 : CATCH_REQUIRE_THROWS_MATCHES(
522 : parser->parse()
523 : , prinbee::invalid_token
524 : , Catch::Matchers::ExceptionMessage(
525 : "prinbee_exception: create-context-test.pbql:1:32: WITH feature definitions must be defined between parenthesis, '(' missing in CREATE CONTEXT."));
526 1 : }
527 :
528 : {
529 : // CREATE CONTEXT my_context WITH ( <123>;
530 3 : std::string script("CREATE CONTEXT my_context WITH ( 123;");
531 :
532 1 : prinbee::pbql::lexer::pointer_t lexer(std::make_shared<prinbee::pbql::lexer>());
533 1 : lexer->set_input(std::make_shared<prinbee::pbql::input>(script, "create-context-test.pbql"));
534 1 : prinbee::pbql::parser::pointer_t parser(std::make_shared<prinbee::pbql::parser>(lexer));
535 :
536 3 : CATCH_REQUIRE_THROWS_MATCHES(
537 : parser->parse()
538 : , prinbee::invalid_token
539 : , Catch::Matchers::ExceptionMessage(
540 : "prinbee_exception: create-context-test.pbql:1:34: WITH feature definitions must be named using an identifier in CREATE CONTEXT."));
541 1 : }
542 :
543 : {
544 : // CREATE CONTEXT my_context WITH ( OWNER name:'group';
545 3 : std::string script("CREATE CONTEXT my_context WITH ( OWNER name:'group';");
546 :
547 1 : prinbee::pbql::lexer::pointer_t lexer(std::make_shared<prinbee::pbql::lexer>());
548 1 : lexer->set_input(std::make_shared<prinbee::pbql::input>(script, "create-context-test.pbql"));
549 1 : prinbee::pbql::parser::pointer_t parser(std::make_shared<prinbee::pbql::parser>(lexer));
550 :
551 3 : CATCH_REQUIRE_THROWS_MATCHES(
552 : parser->parse()
553 : , prinbee::invalid_token
554 : , Catch::Matchers::ExceptionMessage(
555 : "prinbee_exception: create-context-test.pbql:1:46: "
556 : "expected a group name after ':' in CREATE CONTEXT ... WITH ( OWNER <user>:<group> ), not a STRING."));
557 1 : }
558 :
559 : {
560 : // CREATE CONTEXT my_context WITH ( OWNER name:group, OWNER;
561 3 : std::string script("CREATE CONTEXT my_context WITH ( OWNER name:group, OWNER;");
562 :
563 1 : prinbee::pbql::lexer::pointer_t lexer(std::make_shared<prinbee::pbql::lexer>());
564 1 : lexer->set_input(std::make_shared<prinbee::pbql::input>(script, "create-context-test.pbql"));
565 1 : prinbee::pbql::parser::pointer_t parser(std::make_shared<prinbee::pbql::parser>(lexer));
566 :
567 3 : CATCH_REQUIRE_THROWS_MATCHES(
568 : parser->parse()
569 : , prinbee::invalid_token
570 : , Catch::Matchers::ExceptionMessage(
571 : "prinbee_exception: create-context-test.pbql:1:58: "
572 : "WITH OWNER found twice after CREATE CONTEXT."));
573 1 : }
574 :
575 : {
576 : // CREATE CONTEXT my_context WITH ( OWNER = 3.5;
577 3 : std::string script("CREATE CONTEXT my_context WITH ( OWNER = 3.5;");
578 :
579 1 : prinbee::pbql::lexer::pointer_t lexer(std::make_shared<prinbee::pbql::lexer>());
580 1 : lexer->set_input(std::make_shared<prinbee::pbql::input>(script, "create-context-test.pbql"));
581 1 : prinbee::pbql::parser::pointer_t parser(std::make_shared<prinbee::pbql::parser>(lexer));
582 :
583 3 : CATCH_REQUIRE_THROWS_MATCHES(
584 : parser->parse()
585 : , prinbee::invalid_token
586 : , Catch::Matchers::ExceptionMessage(
587 : "prinbee_exception: create-context-test.pbql:1:42: "
588 : "expected a string or an identifier after WITH ( OWNER <owner>[:<group>] )."));
589 1 : }
590 :
591 : {
592 : // CREATE CONTEXT my_context WITH ( COMMENT TRUE;
593 3 : std::string script("CREATE CONTEXT my_context WITH ( COMMENT TRUE;");
594 :
595 1 : prinbee::pbql::lexer::pointer_t lexer(std::make_shared<prinbee::pbql::lexer>());
596 1 : lexer->set_input(std::make_shared<prinbee::pbql::input>(script, "create-context-test.pbql"));
597 1 : prinbee::pbql::parser::pointer_t parser(std::make_shared<prinbee::pbql::parser>(lexer));
598 :
599 3 : CATCH_REQUIRE_THROWS_MATCHES(
600 : parser->parse()
601 : , prinbee::invalid_token
602 : , Catch::Matchers::ExceptionMessage(
603 : "prinbee_exception: create-context-test.pbql:1:42: "
604 : "expected a string for <description> in CREATE CONTEXT ... WITH ( COMMENT <description> ) got a IDENTIFIER."));
605 1 : }
606 :
607 : {
608 : // CREATE CONTEXT my_context WITH ( COMMENT 'good', COMMENT;
609 3 : std::string script("CREATE CONTEXT my_context WITH ( COMMENT 'good', COMMENT;");
610 :
611 1 : prinbee::pbql::lexer::pointer_t lexer(std::make_shared<prinbee::pbql::lexer>());
612 1 : lexer->set_input(std::make_shared<prinbee::pbql::input>(script, "create-context-test.pbql"));
613 1 : prinbee::pbql::parser::pointer_t parser(std::make_shared<prinbee::pbql::parser>(lexer));
614 :
615 3 : CATCH_REQUIRE_THROWS_MATCHES(
616 : parser->parse()
617 : , prinbee::invalid_token
618 : , Catch::Matchers::ExceptionMessage(
619 : "prinbee_exception: create-context-test.pbql:1:58: "
620 : "WITH COMMENT found twice after CREATE CONTEXT."));
621 1 : }
622 :
623 : {
624 : // CREATE CONTEXT my_context WITH ( COMMENT 'good'?;
625 3 : std::string script("CREATE CONTEXT my_context WITH ( COMMENT 'good' 123;");
626 :
627 1 : prinbee::pbql::lexer::pointer_t lexer(std::make_shared<prinbee::pbql::lexer>());
628 1 : lexer->set_input(std::make_shared<prinbee::pbql::input>(script, "create-context-test.pbql"));
629 1 : prinbee::pbql::parser::pointer_t parser(std::make_shared<prinbee::pbql::parser>(lexer));
630 :
631 3 : CATCH_REQUIRE_THROWS_MATCHES(
632 : parser->parse()
633 : , prinbee::invalid_token
634 : , Catch::Matchers::ExceptionMessage(
635 : "prinbee_exception: create-context-test.pbql:1:49: "
636 : "expected a comma to separate feature definitions in CREATE CONTEXT."));
637 1 : }
638 : }
639 4 : CATCH_END_SECTION()
640 :
641 5 : CATCH_START_SECTION("parser_error: BEGIN/COMMIT/ROLLBACK mistakes")
642 : {
643 : {
644 : // BEGIN ON <123>
645 3 : std::string script("BEGIN ON 123;");
646 :
647 1 : prinbee::pbql::lexer::pointer_t lexer(std::make_shared<prinbee::pbql::lexer>());
648 1 : lexer->set_input(std::make_shared<prinbee::pbql::input>(script, "transaction-test.pbql"));
649 1 : prinbee::pbql::parser::pointer_t parser(std::make_shared<prinbee::pbql::parser>(lexer));
650 :
651 3 : CATCH_REQUIRE_THROWS_MATCHES(
652 : parser->parse()
653 : , prinbee::invalid_token
654 : , Catch::Matchers::ExceptionMessage(
655 : "prinbee_exception: transaction-test.pbql:1:10: expected identifier SCHEMA or DATA after BEGIN ON."));
656 1 : }
657 :
658 : {
659 : // BEGIN ON TABLE
660 3 : std::string script("BEGIN ON TABLE;");
661 :
662 1 : prinbee::pbql::lexer::pointer_t lexer(std::make_shared<prinbee::pbql::lexer>());
663 1 : lexer->set_input(std::make_shared<prinbee::pbql::input>(script, "transaction-test.pbql"));
664 1 : prinbee::pbql::parser::pointer_t parser(std::make_shared<prinbee::pbql::parser>(lexer));
665 :
666 3 : CATCH_REQUIRE_THROWS_MATCHES(
667 : parser->parse()
668 : , prinbee::invalid_token
669 : , Catch::Matchers::ExceptionMessage(
670 : "prinbee_exception: transaction-test.pbql:1:10: expected identifier SCHEMA or DATA after BEGIN ON."));
671 1 : }
672 :
673 : {
674 : // BEGIN ON SCHEMA IF
675 3 : std::string script("BEGIN ON SCHEMA IF;");
676 :
677 1 : prinbee::pbql::lexer::pointer_t lexer(std::make_shared<prinbee::pbql::lexer>());
678 1 : lexer->set_input(std::make_shared<prinbee::pbql::input>(script, "transaction-test.pbql"));
679 1 : prinbee::pbql::parser::pointer_t parser(std::make_shared<prinbee::pbql::parser>(lexer));
680 :
681 3 : CATCH_REQUIRE_THROWS_MATCHES(
682 : parser->parse()
683 : , prinbee::invalid_token
684 : , Catch::Matchers::ExceptionMessage(
685 : "prinbee_exception: transaction-test.pbql:1:17: expected ';' at the end of 'BEGIN' command; not IDENTIFIER IF."));
686 1 : }
687 :
688 : {
689 : // COMMIT WORK ON SCHEMA WHEN
690 3 : std::string script("COMMIT WORK ON SCHEMA WHEN;");
691 :
692 1 : prinbee::pbql::lexer::pointer_t lexer(std::make_shared<prinbee::pbql::lexer>());
693 1 : lexer->set_input(std::make_shared<prinbee::pbql::input>(script, "transaction-test.pbql"));
694 1 : prinbee::pbql::parser::pointer_t parser(std::make_shared<prinbee::pbql::parser>(lexer));
695 :
696 3 : CATCH_REQUIRE_THROWS_MATCHES(
697 : parser->parse()
698 : , prinbee::invalid_token
699 : , Catch::Matchers::ExceptionMessage(
700 : "prinbee_exception: transaction-test.pbql:1:23: expected IF clause or ';' at the end of a COMMIT or ROLLBACK."));
701 1 : }
702 :
703 : {
704 : // COMMIT TRANSACTION ON SCHEMA IF a <> b THEN
705 3 : std::string script("COMMIT TRANSACTION ON SCHEMA IF a <> b THEN;");
706 :
707 1 : prinbee::pbql::lexer::pointer_t lexer(std::make_shared<prinbee::pbql::lexer>());
708 1 : lexer->set_input(std::make_shared<prinbee::pbql::input>(script, "transaction-test.pbql"));
709 1 : prinbee::pbql::parser::pointer_t parser(std::make_shared<prinbee::pbql::parser>(lexer));
710 :
711 3 : CATCH_REQUIRE_THROWS_MATCHES(
712 : parser->parse()
713 : , prinbee::invalid_token
714 : , Catch::Matchers::ExceptionMessage(
715 : "prinbee_exception: transaction-test.pbql:1:40: expected OTHERWISE after the IF expression of COMMIT or ROLLBACK."));
716 1 : }
717 :
718 : {
719 : // COMMIT TRANSACTION ON SCHEMA IF a <> b THEN
720 3 : std::string script("COMMIT TRANSACTION ON SCHEMA IF a <> b OTHERWISE 123;");
721 :
722 1 : prinbee::pbql::lexer::pointer_t lexer(std::make_shared<prinbee::pbql::lexer>());
723 1 : lexer->set_input(std::make_shared<prinbee::pbql::input>(script, "transaction-test.pbql"));
724 1 : prinbee::pbql::parser::pointer_t parser(std::make_shared<prinbee::pbql::parser>(lexer));
725 :
726 3 : CATCH_REQUIRE_THROWS_MATCHES(
727 : parser->parse()
728 : , prinbee::invalid_token
729 : , Catch::Matchers::ExceptionMessage(
730 : "prinbee_exception: transaction-test.pbql:1:50: expected ROLLBACK after OTHERWISE for command COMMIT."));
731 1 : }
732 :
733 : {
734 : // COMMIT TRANSACTION ON SCHEMA IF a <> b THEN
735 3 : std::string script("ROLLBACK TRANSACTION ON DATA IF a = b OTHERWISE 123;");
736 :
737 1 : prinbee::pbql::lexer::pointer_t lexer(std::make_shared<prinbee::pbql::lexer>());
738 1 : lexer->set_input(std::make_shared<prinbee::pbql::input>(script, "transaction-test.pbql"));
739 1 : prinbee::pbql::parser::pointer_t parser(std::make_shared<prinbee::pbql::parser>(lexer));
740 :
741 3 : CATCH_REQUIRE_THROWS_MATCHES(
742 : parser->parse()
743 : , prinbee::invalid_token
744 : , Catch::Matchers::ExceptionMessage(
745 : "prinbee_exception: transaction-test.pbql:1:49: expected COMMIT after OTHERWISE for command ROLLBACK."));
746 1 : }
747 : }
748 4 : CATCH_END_SECTION()
749 3 : }
750 :
751 :
752 :
753 : // vim: ts=4 sw=4 et
|