Skip to content

Commit 8df98a9

Browse files
committed
MySQL: Add support for SET statements
1 parent 27b4008 commit 8df98a9

6 files changed

Lines changed: 376 additions & 88 deletions

File tree

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
*.out
3232
*.app
3333

34-
34+
set_mysql_example
3535
mysql_example
3636
pgsql_example
3737
libpgsqlparser.a

Makefile

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ MYSQL_PARSER_INCLUDE_DIR = $(INCLUDE_DIR)/mysql_parser
3636
MYSQL_TARGET_LIB_NAME = mysqlparser
3737
MYSQL_TARGET_LIB = $(PROJECT_ROOT)/lib$(MYSQL_TARGET_LIB_NAME).a
3838
MYSQL_EXAMPLE_EXE = $(PROJECT_ROOT)/mysql_example
39+
MYSQL_SET_EXAMPLE_EXE = $(PROJECT_ROOT)/set_mysql_example
3940

4041
MYSQL_BISON_C_FILE = mysql_parser.tab.c
4142
MYSQL_BISON_H_FILE = mysql_parser.tab.h
@@ -48,8 +49,9 @@ MYSQL_FLEX_C = $(MYSQL_PARSER_SRC_DIR)/$(MYSQL_FLEX_C_FILE)
4849
MYSQL_LIB_OBJS = \
4950
$(MYSQL_BISON_C:.c=.o) \
5051
$(MYSQL_FLEX_C:.c=.o) \
51-
$(MYSQL_PARSER_SRC_DIR)/mysql_parser.o
52+
$(MYSQL_PARSER_SRC_DIR)/mysql_parser.o # Renamed from mysql_sql_parser.o
5253
MYSQL_EXAMPLE_OBJS = $(PROJECT_ROOT)/examples/main_mysql_example.o
54+
MYSQL_SET_EXAMPLE_OBJS = $(PROJECT_ROOT)/examples/set_mysql_example.o
5355

5456

5557
.PHONY: all clean examples pgsql mysql
@@ -59,7 +61,7 @@ all: pgsql mysql examples
5961
pgsql: $(PGSQL_TARGET_LIB)
6062
mysql: $(MYSQL_TARGET_LIB)
6163

62-
examples: $(PGSQL_EXAMPLE_EXE) $(MYSQL_EXAMPLE_EXE)
64+
examples: $(PGSQL_EXAMPLE_EXE) $(MYSQL_EXAMPLE_EXE) $(MYSQL_SET_EXAMPLE_EXE)
6365

6466
# --- PostgreSQL Rules ---
6567
$(PGSQL_TARGET_LIB): $(PGSQL_LIB_OBJS)
@@ -97,6 +99,11 @@ $(MYSQL_EXAMPLE_EXE): $(MYSQL_EXAMPLE_OBJS) $(MYSQL_TARGET_LIB)
9799
$(LINKER) $(CXXFLAGS) -o $@ $(MYSQL_EXAMPLE_OBJS) -L$(PROJECT_ROOT) -l$(MYSQL_TARGET_LIB_NAME)
98100
@echo "Created MySQL example $@"
99101

102+
# Rule for MySQL SET example executable <<< NEW
103+
$(MYSQL_SET_EXAMPLE_EXE): $(MYSQL_SET_EXAMPLE_OBJS) $(MYSQL_TARGET_LIB)
104+
$(LINKER) $(CXXFLAGS) -o $@ $(MYSQL_SET_EXAMPLE_OBJS) -L$(PROJECT_ROOT) -l$(MYSQL_TARGET_LIB_NAME)
105+
@echo "Created MySQL SET statement example $@"
106+
100107
$(MYSQL_BISON_H) $(MYSQL_BISON_C): $(MYSQL_PARSER_SRC_DIR)/mysql_parser.y $(MYSQL_PARSER_INCLUDE_DIR)/mysql_ast.h $(MYSQL_PARSER_INCLUDE_DIR)/mysql_parser.h
101108
cd $(MYSQL_PARSER_SRC_DIR) && bison -d -v --report=all -o $(MYSQL_BISON_C_FILE) --defines=$(MYSQL_BISON_H_FILE) mysql_parser.y
102109

@@ -115,10 +122,14 @@ $(MYSQL_PARSER_SRC_DIR)/mysql_parser.o: $(MYSQL_PARSER_SRC_DIR)/mysql_parser.cpp
115122
$(PROJECT_ROOT)/examples/main_mysql_example.o: $(PROJECT_ROOT)/examples/main_mysql_example.cpp $(MYSQL_PARSER_INCLUDE_DIR)/mysql_parser.h $(MYSQL_PARSER_INCLUDE_DIR)/mysql_ast.h
116123
$(CXX) $(CXXFLAGS) $(CPPFLAGS) -c $< -o $@
117124

125+
# Rule for MySQL SET example main.o <<< NEW
126+
$(PROJECT_ROOT)/examples/set_mysql_example.o: $(PROJECT_ROOT)/examples/set_mysql_example.cpp $(MYSQL_PARSER_INCLUDE_DIR)/mysql_parser.h $(MYSQL_PARSER_INCLUDE_DIR)/mysql_ast.h
127+
$(CXX) $(CXXFLAGS) $(CPPFLAGS) -c $< -o $@
128+
118129

119130
clean:
120-
rm -f $(PGSQL_TARGET_LIB) $(PGSQL_EXAMPLE_EXE) $(MYSQL_TARGET_LIB) $(MYSQL_EXAMPLE_EXE)
121-
rm -f $(PGSQL_LIB_OBJS) $(PGSQL_EXAMPLE_OBJS) $(MYSQL_LIB_OBJS) $(MYSQL_EXAMPLE_OBJS)
131+
rm -f $(PGSQL_TARGET_LIB) $(PGSQL_EXAMPLE_EXE) $(MYSQL_TARGET_LIB) $(MYSQL_EXAMPLE_EXE) $(MYSQL_SET_EXAMPLE_EXE)
132+
rm -f $(PGSQL_LIB_OBJS) $(PGSQL_EXAMPLE_OBJS) $(MYSQL_LIB_OBJS) $(MYSQL_EXAMPLE_OBJS) $(MYSQL_SET_EXAMPLE_OBJS)
122133
rm -f $(PGSQL_BISON_C) $(PGSQL_BISON_H) $(PGSQL_FLEX_C)
123134
rm -f $(MYSQL_BISON_C) $(MYSQL_BISON_H) $(MYSQL_FLEX_C)
124135
rm -f $(PGSQL_PARSER_SRC_DIR)/pgsql_parser.output $(PGSQL_PARSER_SRC_DIR)/pgsql_parser.report

examples/set_mysql_example.cpp

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
#include "mysql_parser/mysql_parser.h" // Ensure this path is correct for your include setup
2+
#include <iostream>
3+
#include <vector>
4+
#include <string>
5+
6+
int main() {
7+
MysqlParser::Parser parser; // Using the MysqlParser namespace
8+
9+
std::vector<std::string> set_queries = {
10+
// Basic User Variable Assignments
11+
"SET @my_user_var = 'hello world';",
12+
"SET @anotherVar = 12345;",
13+
"SET @thirdVar = `ident_value`;", // Using identifier as value
14+
"SET @complex_var = @@global.max_connections;", // Setting user var to sys var value (expr placeholder)
15+
16+
// System Variable Assignments
17+
"SET global max_connections = 1000;",
18+
"SET session sort_buffer_size = 200000;",
19+
"SET GLOBAL sort_buffer_size = 400000;", // Case-insensitivity for scope
20+
"SET SESSION wait_timeout = 180;",
21+
"SET @@global.tmp_table_size = 32000000;",
22+
"SET @@session.net_write_timeout = 120;",
23+
"SET @@net_read_timeout = 60;", // Implicit SESSION scope for @@
24+
"SET max_allowed_packet = 64000000;", // Implicit SESSION scope for simple sysvar
25+
26+
// PERSIST / PERSIST_ONLY (if supported by your current grammar for scope)
27+
"SET persist character_set_server = 'utf8mb4';",
28+
"SET persist_only innodb_buffer_pool_size = '1G';", // String literal for value
29+
30+
// SET NAMES and CHARACTER SET
31+
"SET NAMES 'utf8mb4';",
32+
"SET NAMES `latin1`;",
33+
"SET NAMES DEFAULT;",
34+
"SET NAMES 'gbk' COLLATE 'gbk_chinese_ci';",
35+
"SET CHARACTER SET 'utf8';",
36+
"SET CHARACTER SET DEFAULT;",
37+
38+
// Comma-separated list (testing set_option_list)
39+
"SET @a = 1, @b = 'two', global max_heap_table_size = 128000000;",
40+
"SET sql_mode = 'STRICT_TRANS_TABLES', character_set_client = 'utf8mb4';",
41+
42+
// Statements without trailing semicolon (should work with optional_semicolon)
43+
"SET @no_semicolon = 'works'",
44+
45+
// Test cases for potential errors or unsupported expressions
46+
"SET @myvar = some_function(1, 'a');", // 'some_function(...)' is just an identifier for expression_placeholder for now
47+
"SET global invalid-variable = 100;", // Invalid identifier char (if not quoted)
48+
"SET @unterminated_string = 'oops",
49+
"SET =", // Syntax error
50+
"SET names utf8 collate ;" // Missing collation name
51+
};
52+
53+
for (const auto& query : set_queries) {
54+
std::cout << "------------------------------------------\n";
55+
std::cout << "Parsing MySQL SET query: " << query << std::endl;
56+
57+
parser.clearErrors();
58+
std::unique_ptr<MysqlParser::AstNode> ast = parser.parse(query);
59+
60+
if (ast) {
61+
std::cout << "Parsing successful!" << std::endl;
62+
MysqlParser::print_ast(ast.get());
63+
} else {
64+
std::cout << "Parsing failed." << std::endl;
65+
const auto& errors = parser.getErrors();
66+
if (errors.empty()) {
67+
std::cout << " (No specific error messages, check parser logic or mysql_yyerror)" << std::endl;
68+
} else {
69+
for (const auto& error : errors) {
70+
std::cout << " Error: " << error << std::endl;
71+
}
72+
}
73+
}
74+
}
75+
return 0;
76+
}

include/mysql_parser/mysql_ast.h

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,15 @@ enum class NodeType {
1616
NODE_INSERT_STATEMENT,
1717
NODE_IDENTIFIER,
1818
NODE_STRING_LITERAL,
19-
NODE_ASTERISK
19+
NODE_ASTERISK,
20+
NODE_SET_STATEMENT,
21+
NODE_VARIABLE_ASSIGNMENT, // for @var = expr or sysvar = expr
22+
NODE_USER_VARIABLE, // for @varname
23+
NODE_SYSTEM_VARIABLE, // for sysvar (can have scope)
24+
NODE_VARIABLE_SCOPE, // GLOBAL, SESSION, PERSIST, PERSIST_ONLY
25+
NODE_EXPRESSION_PLACEHOLDER,// Placeholder for complex expressions
26+
NODE_SET_NAMES, // for SET NAMES
27+
NODE_SET_CHARSET // for SET CHARSET
2028
};
2129

2230
struct AstNode {
@@ -58,6 +66,14 @@ inline void print_ast(const AstNode* node, int indent = 0) {
5866
case NodeType::NODE_IDENTIFIER: type_str = "IDENTIFIER"; break;
5967
case NodeType::NODE_STRING_LITERAL: type_str = "STRING_LITERAL"; break;
6068
case NodeType::NODE_ASTERISK: type_str = "ASTERISK"; break;
69+
case NodeType::NODE_SET_STATEMENT: type_str = "SET_STATEMENT"; break;
70+
case NodeType::NODE_VARIABLE_ASSIGNMENT: type_str = "VAR_ASSIGN"; break;
71+
case NodeType::NODE_USER_VARIABLE: type_str = "USER_VAR"; break;
72+
case NodeType::NODE_SYSTEM_VARIABLE: type_str = "SYSTEM_VAR"; break;
73+
case NodeType::NODE_VARIABLE_SCOPE: type_str = "VAR_SCOPE"; break;
74+
case NodeType::NODE_EXPRESSION_PLACEHOLDER: type_str = "EXPR_PLACEHOLDER"; break;
75+
case NodeType::NODE_SET_NAMES: type_str = "SET_NAMES"; break;
76+
case NodeType::NODE_SET_CHARSET: type_str = "SET_CHARSET"; break;
6177
default: type_str = "UNHANDLED_TYPE(" + std::to_string(static_cast<int>(node->type)) + ")"; break;
6278
}
6379
std::cout << "Type: " << type_str << ", Value: '" << node->value << "'" << std::endl;

src/mysql_parser/mysql_lexer.l

Lines changed: 37 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -38,42 +38,52 @@ union MYSQL_YYSTYPE; // Forward declare for YY_DECL (Bison defines this in mysql
3838
"INSERT" { return TOKEN_INSERT; }
3939
"INTO" { return TOKEN_INTO; }
4040
"VALUES" { return TOKEN_VALUES; }
41-
42-
"QUIT" {
43-
yylval_param->str_val = new std::string(yytext);
44-
return TOKEN_QUIT;
41+
"QUIT" { yylval_param->str_val = new std::string(yytext); return TOKEN_QUIT; }
42+
43+
"SET" { return TOKEN_SET; }
44+
"NAMES" { return TOKEN_NAMES; }
45+
"CHARACTER" { return TOKEN_CHARACTER; }
46+
"GLOBAL" { return TOKEN_GLOBAL; }
47+
"SESSION" { return TOKEN_SESSION; }
48+
"PERSIST" { return TOKEN_PERSIST; }
49+
"PERSIST_ONLY" { return TOKEN_PERSIST_ONLY;}
50+
"DEFAULT" { return TOKEN_DEFAULT; }
51+
"COLLATE" { return TOKEN_COLLATE; }
52+
53+
"`" { yylval_param->str_val = new std::string(); BEGIN(BTIDENT); }
54+
55+
"@@global." { return TOKEN_GLOBAL_VAR_PREFIX; }
56+
"@@session." { return TOKEN_SESSION_VAR_PREFIX; }
57+
"@@local." { return TOKEN_SESSION_VAR_PREFIX; } /* Alias for session */
58+
"@@persisted." { /* Placeholder if you add specific handling */ return TOKEN_PERSIST_VAR_PREFIX; }
59+
"@@" { return TOKEN_DOUBLESPECIAL; }
60+
"@" { return TOKEN_SPECIAL; }
61+
62+
[a-zA-Z_][a-zA-Z0-9_]* { /* Does not include hyphen for now */
63+
yylval_param->str_val = new std::string(yytext);
64+
return TOKEN_IDENTIFIER;
4565
}
4666

47-
"`" {
48-
yylval_param->str_val = new std::string(); /* Start empty for backticked id */
49-
BEGIN(BTIDENT);
50-
}
51-
[a-zA-Z_][a-zA-Z0-9_]* {
52-
yylval_param->str_val = new std::string(yytext);
53-
return TOKEN_IDENTIFIER;
54-
}
55-
56-
"'" {
57-
yylval_param->str_val = new std::string("'");
58-
BEGIN(SQSTRING);
59-
}
60-
"\"" {
61-
yylval_param->str_val = new std::string("\"");
62-
BEGIN(DQSTRING);
63-
}
67+
"'" { yylval_param->str_val = new std::string("'"); BEGIN(SQSTRING); }
68+
"\"" { yylval_param->str_val = new std::string("\""); BEGIN(DQSTRING); }
6469

6570
"*" { return TOKEN_ASTERISK; }
6671
"(" { return TOKEN_LPAREN; }
6772
")" { return TOKEN_RPAREN; }
6873
";" { return TOKEN_SEMICOLON; }
74+
"=" { return TOKEN_EQUAL; }
75+
"." { return TOKEN_DOT; }
76+
"," { return TOKEN_COMMA; }
77+
78+
[0-9]+("."[0-9]+)? { yylval_param->str_val = new std::string(yytext); return TOKEN_NUMBER_LITERAL;}
6979

70-
. {
80+
. {
7181
char err_msg[100];
7282
snprintf(err_msg, sizeof(err_msg), "Lexer: Unknown character: '%s'", yytext);
73-
if (parser_context) {
83+
if (parser_context) {
7484
parser_context->internal_add_error(err_msg);
7585
} else {
76-
fprintf(stderr, "%s\n", err_msg);
86+
fprintf(stderr, "%s\n", err_msg);
7787
}
7888
}
7989
}
@@ -91,7 +101,7 @@ union MYSQL_YYSTYPE; // Forward declare for YY_DECL (Bison defines this in mysql
91101
"\\\\" { *(yylval_param->str_val) += "\\"; }
92102
"''" { *(yylval_param->str_val) += "'"; }
93103
"'" { *(yylval_param->str_val) += "'"; BEGIN(INITIAL); return TOKEN_STRING_LITERAL; }
94-
<<EOF>> { if(parser_context) parser_context->internal_add_error("Unterminated single-quoted string"); BEGIN(INITIAL); return YY_NULL; /* Return 0 or YY_NULL for EOF/error */ }
104+
<<EOF>> { if(parser_context) parser_context->internal_add_error("Unterminated single-quoted string"); BEGIN(INITIAL); return YY_NULL; }
95105
}
96106

97107
<DQSTRING>{
@@ -104,15 +114,12 @@ union MYSQL_YYSTYPE; // Forward declare for YY_DECL (Bison defines this in mysql
104114
}
105115

106116
<BTIDENT>{
107-
"`" { BEGIN(INITIAL); return TOKEN_IDENTIFIER; }
117+
"`" { BEGIN(INITIAL); return TOKEN_IDENTIFIER; } // Returns the accumulated string
108118
"``" { *(yylval_param->str_val) += '`'; }
109119
[^`\n]+ { *(yylval_param->str_val) += yytext; }
110-
\n { if(parser_context) parser_context->internal_add_error("Newline in backticked identifier"); BEGIN(INITIAL); /* No explicit return, lexer will find next token or error */ }
120+
\n { if(parser_context) parser_context->internal_add_error("Newline in backticked identifier"); BEGIN(INITIAL); /* No explicit return */ }
111121
<<EOF>> { if(parser_context) parser_context->internal_add_error("Unterminated backticked identifier"); BEGIN(INITIAL); return YY_NULL; }
112122
}
113123

114124
%%
115125

116-
// yywrap is not strictly needed due to %option noyywrap
117-
// int mysql_yywrap(yyscan_t scanner) { return 1; }
118-

0 commit comments

Comments
 (0)