Skip to content

Commit ba7f191

Browse files
committed
Added support for more complex SELECT statements
1 parent f021273 commit ba7f191

5 files changed

Lines changed: 1134 additions & 383 deletions

File tree

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
CXX = g++
33
LINKER = g++
44

5-
CXXFLAGS = -std=c++17 -Wall -g
5+
CXXFLAGS = -std=c++17 -Wall -g -O2
66
CPPFLAGS = -I$(PROJECT_ROOT)/include
77

88
PROJECT_ROOT = .

examples/main_mysql_example.cpp

Lines changed: 152 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -2,49 +2,154 @@
22
#include <iostream>
33
#include <vector>
44
#include <string>
5+
#include <memory> // Required for std::unique_ptr
56

6-
void parse_and_print(MysqlParser::Parser& parser, const std::string& query_type, const std::string& query) {
7-
std::cout << "------------------------------------------\n";
8-
std::cout << "Parsing MySQL " << query_type << " query: " << query << std::endl;
7+
bool verbose_print = true;
8+
unsigned long int queries = 0;
99

10-
parser.clearErrors();
10+
// Function to parse a query and print the AST or errors
11+
void parse_and_print(MysqlParser::Parser& parser, const std::string& query_type, const std::string& query) {
12+
queries++;
13+
if (verbose_print) {
14+
std::cout << "------------------------------------------\n";
15+
std::cout << "Parsing MySQL " << query_type << " query: " << query << std::endl;
16+
}
17+
//std::cout << "AAA" << std::endl;
18+
parser.clearErrors(); // Clear any errors from previous parses
19+
//std::cout << "BBB" << std::endl;
1120
std::unique_ptr<MysqlParser::AstNode> ast = parser.parse(query);
21+
//std::cout << "CCC" << std::endl;
1222

23+
if (verbose_print == false) {
24+
return;
25+
}
1326
if (ast) {
1427
std::cout << "Parsing successful!" << std::endl;
15-
MysqlParser::print_ast(ast.get());
28+
MysqlParser::print_ast(ast.get()); // Print the AST
1629
} else {
1730
std::cout << "Parsing failed." << std::endl;
1831
const auto& errors = parser.getErrors();
1932
if (errors.empty()) {
20-
std::cout << " (No specific error messages, check parser logic or mysql_yyerror)" << std::endl;
33+
std::cout << " (No specific error messages, check parser logic or mysql_yyerror implementation)" << std::endl;
2134
} else {
2235
for (const auto& error : errors) {
2336
std::cout << " Error: " << error << std::endl;
2437
}
2538
}
2639
}
40+
std::cout << "------------------------------------------\n\n";
2741
}
2842

2943
int main() {
3044
MysqlParser::Parser parser;
3145

3246
std::vector<std::string> select_queries = {
47+
// Basic SELECTs (from original)
3348
"SELECT name FROM users;",
3449
"SELECT * FROM `orders`;",
35-
"SELECT * FROM tablenameB" // No semicolon
50+
"SELECT `col1`, `col2` FROM tablenameB", // No semicolon
51+
52+
// SELECT with ALIAS
53+
"SELECT column1 AS first_column, column2 AS second_column FROM my_table;",
54+
"SELECT column1 first_column, column2 second_column FROM my_table;", // Implicit AS
55+
56+
// SELECT with WHERE
57+
"SELECT product_name, price FROM products WHERE category = 'Electronics';",
58+
"SELECT * FROM employees WHERE salary > 50000 AND department_id = 3;",
59+
60+
// SELECT with ORDER BY
61+
"SELECT student_name, score FROM results ORDER BY score DESC;",
62+
"SELECT item, quantity FROM inventory ORDER BY item ASC, quantity DESC;",
63+
64+
// SELECT with LIMIT
65+
"SELECT event_name FROM event_log LIMIT 10;",
66+
"SELECT message FROM messages ORDER BY created_at DESC LIMIT 5, 10;", // LIMIT offset, count
67+
68+
// SELECT with GROUP BY
69+
"SELECT department, COUNT(*) AS num_employees FROM employees GROUP BY department;",
70+
"SELECT product_category, AVG(price) AS avg_price FROM products GROUP BY product_category;",
71+
72+
// SELECT with GROUP BY and HAVING
73+
"SELECT department, COUNT(*) AS num_employees FROM employees GROUP BY department HAVING COUNT(*) > 10;",
74+
"SELECT product_category, AVG(price) AS avg_price FROM products GROUP BY product_category HAVING AVG(price) > 100.00;",
75+
76+
// Basic JOIN clauses
77+
"SELECT c.customer_name, o.order_id FROM customers c JOIN orders o ON c.customer_id = o.customer_id;",
78+
"SELECT s.name, p.product_name FROM suppliers s INNER JOIN products p ON s.supplier_id = p.supplier_id;",
79+
"SELECT e.name, d.department_name FROM employees e LEFT JOIN departments d ON e.department_id = d.department_id;",
80+
"SELECT e.name, p.project_name FROM employees e RIGHT OUTER JOIN projects p ON e.employee_id = p.lead_employee_id;",
81+
"SELECT c1.name, c2.name AS city_pair FROM cities c1 CROSS JOIN cities c2 WHERE c1.id <> c2.id;", // Comma join will be CROSS JOIN
82+
"SELECT c1.name, c2.name AS city_pair_comma FROM cities c1, cities c2 WHERE c1.id <> c2.id;",
83+
"SELECT e.name, d.name FROM employees e NATURAL JOIN departments d;", // Natural Join
84+
"SELECT s.student_name, c.course_name FROM students s NATURAL LEFT JOIN courses c;",
85+
86+
// JOIN with USING
87+
"SELECT c.customer_name, o.order_date FROM customers c JOIN orders o USING (customer_id);",
88+
"SELECT a.val, b.val FROM tableA a LEFT JOIN tableB b USING (id, common_column);",
89+
90+
// More complex JOINs (multiple joins)
91+
"SELECT c.name, o.order_date, p.product_name, oi.quantity "
92+
"FROM customers c "
93+
"JOIN orders o ON c.customer_id = o.customer_id "
94+
"JOIN order_items oi ON o.order_id = oi.order_id "
95+
"JOIN products p ON oi.product_id = p.product_id "
96+
"WHERE c.country = 'USA' ORDER BY o.order_date DESC;",
97+
98+
// Subquery in FROM clause (Derived Table)
99+
"SELECT dt.category_name, dt.total_sales "
100+
"FROM (SELECT category, SUM(sales_amount) AS total_sales FROM sales GROUP BY category) AS dt "
101+
"WHERE dt.total_sales > 10000;",
102+
103+
"SELECT emp_details.name, emp_details.dept "
104+
"FROM (SELECT e.name, d.department_name AS dept FROM employees e JOIN departments d ON e.dept_id = d.id) emp_details "
105+
"ORDER BY emp_details.name;",
106+
107+
"SELECT * FROM (SELECT id FROM t1) AS derived_t1 JOIN (SELECT id FROM t2) AS derived_t2 ON derived_t1.id = derived_t2.id;",
108+
109+
110+
// SELECT ... INTO OUTFILE / DUMPFILE
111+
"SELECT user_id, username, email INTO OUTFILE '/tmp/users.txt' FROM user_accounts WHERE is_active = 1;",
112+
"SELECT * INTO OUTFILE '/tmp/products_export.csv' FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '\"' LINES TERMINATED BY '\\n' FROM products;",
113+
"SELECT data_column INTO DUMPFILE '/tmp/data_blob.dat' FROM large_objects WHERE id = 42;",
114+
"SELECT col1, col2 INTO OUTFILE 'test_char_set.txt' CHARACTER SET 'utf8mb4' FIELDS ENCLOSED BY '`' FROM my_data;",
115+
116+
117+
// SELECT ... INTO variables
118+
"SELECT COUNT(*), MAX(salary) INTO @user_count, @max_sal FROM employees;",
119+
"SELECT name, email INTO @emp_name, @emp_email FROM employees WHERE id = 101;",
120+
121+
// SELECT ... FOR UPDATE / FOR SHARE
122+
"SELECT account_balance FROM accounts WHERE account_id = 123 FOR UPDATE;",
123+
"SELECT product_name, quantity FROM inventory WHERE product_id = 789 FOR SHARE;",
124+
"SELECT c.name, o.status FROM customers c JOIN orders o ON c.id = o.customer_id WHERE o.id = 500 FOR UPDATE OF c, o;",
125+
"SELECT item_name FROM stock_items WHERE category = 'electronics' FOR SHARE NOWAIT;",
126+
"SELECT * FROM pending_tasks FOR UPDATE SKIP LOCKED;",
127+
"SELECT id FROM users WHERE status = 'pending' FOR SHARE OF users SKIP LOCKED;",
128+
129+
130+
// Combined query
131+
"SELECT c.name AS customer_name, COUNT(o.order_id) AS total_orders, SUM(oi.price * oi.quantity) AS total_spent "
132+
"FROM customers AS c "
133+
"LEFT JOIN orders AS o ON c.customer_id = o.customer_id "
134+
"JOIN order_items AS oi ON o.order_id = oi.order_id "
135+
"WHERE c.registration_date > '2022-01-01' "
136+
"GROUP BY c.customer_id, c.name "
137+
"HAVING COUNT(o.order_id) > 2 AND SUM(oi.price * oi.quantity) > 500 "
138+
"ORDER BY total_spent DESC, customer_name ASC "
139+
"LIMIT 10, 5 "
140+
"FOR UPDATE OF c, o NOWAIT;"
36141
};
37142

38143
std::vector<std::string> insert_queries = {
39144
"INSERT INTO products VALUES ('a new gadget');",
40-
"INSERT INTO logs VALUES (\"Error message with double quotes\")", // No semicolon
145+
"INSERT INTO logs VALUES (\"Error message with double quotes\")",
41146
"INSERT INTO `special-table` VALUES ('escaped value \\'single quote\\' and \\\\ backslash');"
42147
};
43148

44149
std::vector<std::string> set_queries = {
45150
"SET @my_user_var = 'hello world';",
46151
"SET @anotherVar = 12345;",
47-
"SET global max_connections = 1000", // No semicolon
152+
"SET global max_connections = 1000",
48153
"SET @@session.net_write_timeout = 120;",
49154
"SET NAMES 'utf8mb4' COLLATE 'utf8mb4_unicode_ci';",
50155
"SET CHARACTER SET DEFAULT",
@@ -56,49 +161,47 @@ int main() {
56161
"DELETE FROM customers WHERE customer_id = 101;",
57162
"DELETE LOW_PRIORITY FROM orders WHERE order_date < '2023-01-01'",
58163
"DELETE QUICK IGNORE FROM logs WHERE log_level = 'DEBUG' ORDER BY timestamp DESC LIMIT 1000;",
59-
"DELETE FROM events WHERE event_name = `expired-event`", // Backticked identifier for value
60-
61-
// Multi-table DELETE statements (simplified, based on current grammar)
62-
"DELETE t1 FROM table1 AS t1, table2 AS t2 WHERE t1.id = t2.ref_id;", // Needs table_reference_list_placeholder to be more robust
63-
"DELETE FROM t1, t2 USING table1 AS t1 INNER JOIN table2 AS t2 ON t1.key = t2.key WHERE t1.value > 100;", // Also simplified
164+
"DELETE FROM events WHERE event_name = `expired-event`",
64165

65-
// DELETE without semicolon
66-
"DELETE FROM old_records WHERE last_accessed < '2020-01-01'",
67-
68-
// Potentially problematic or error cases for DELETE
69-
"DELETE quick low_priority from test_table", // Order of options might matter or not be fully supported yet
70-
"DELETE FROM table1 WHERE id = ", // Incomplete WHERE
71-
"DELETE tbl1 tbl2 FROM table_references" // Common MySQL multi-table, current grammar might simplify tbl1, tbl2 part
166+
// Multi-table DELETE statements
167+
"DELETE t1 FROM table1 AS t1, table2 AS t2 WHERE t1.id = t2.ref_id;",
168+
"DELETE FROM t1, t2 USING table1 AS t1 INNER JOIN table2 AS t2 ON t1.key = t2.key WHERE t1.value > 100;",
169+
"DELETE FROM old_records WHERE last_accessed < '2020-01-01'"
72170
};
73171

74-
75-
std::cout << "\n======= SELECT QUERIES =======\n";
76-
for (const auto& query : select_queries) {
77-
parse_and_print(parser, "SELECT", query);
78-
}
79-
80-
std::cout << "\n======= INSERT QUERIES =======\n";
81-
for (const auto& query : insert_queries) {
82-
parse_and_print(parser, "INSERT", query);
83-
}
84-
85-
std::cout << "\n======= SET QUERIES =======\n";
86-
for (const auto& query : set_queries) {
87-
parse_and_print(parser, "SET", query);
88-
}
89-
90-
std::cout << "\n======= DELETE QUERIES =======\n";
91-
for (const auto& query : delete_queries) {
92-
parse_and_print(parser, "DELETE", query);
172+
const int iterations = 10000;
173+
for (int i = 0; i < iterations; i++) {
174+
if (verbose_print == true) std::cout << "\n======= SELECT QUERIES =======\n";
175+
for (const auto& query : select_queries) {
176+
parse_and_print(parser, "SELECT", query);
177+
}
178+
if (verbose_print == true) std::cout << "\n======= INSERT QUERIES =======\n";
179+
for (const auto& query : insert_queries) {
180+
parse_and_print(parser, "INSERT", query);
181+
}
182+
183+
if (verbose_print == true) std::cout << "\n======= SET QUERIES =======\n";
184+
for (const auto& query : set_queries) {
185+
parse_and_print(parser, "SET", query);
186+
}
187+
188+
if (verbose_print == true) std::cout << "\n======= DELETE QUERIES =======\n";
189+
for (const auto& query : delete_queries) {
190+
parse_and_print(parser, "DELETE", query);
191+
}
192+
193+
// Example of a known failing query (due to function call in expression_placeholder)
194+
if (verbose_print == true) std::cout << "\n======= KNOWN FAILING SET QUERY (Function Call) =======\n";
195+
parse_and_print(parser, "SET", "SET @myvar = some_function(1, 'a');");
196+
197+
if (verbose_print == true) std::cout << "\n======= KNOWN FAILING SET QUERY (Invalid Identifier) =======\n";
198+
parse_and_print(parser, "SET", "SET global invalid-variable = 100;");
199+
if (verbose_print == true) { verbose_print=false; }
200+
if ((i+1) % 1000 == 0) {
201+
std::cout << i+1 << std::endl;
202+
}
93203
}
94-
95-
// Example of a known failing query (due to function call in expression_placeholder)
96-
std::cout << "\n======= KNOWN FAILING SET QUERY (Function Call) =======\n";
97-
parse_and_print(parser, "SET", "SET @myvar = some_function(1, 'a');");
98-
99-
std::cout << "\n======= KNOWN FAILING SET QUERY (Invalid Identifier) =======\n";
100-
parse_and_print(parser, "SET", "SET global invalid-variable = 100;");
101-
102-
204+
std::cout << "Queries: " << queries << std::endl;
103205
return 0;
104206
}
207+

0 commit comments

Comments
 (0)