Skip to content

Commit b2ed564

Browse files
committed
Integrate UPDATE deep parser with emitter and classifier
Add emit_update_stmt and emit_update_set_clause to emitter. Replace extract_update() Tier 2 extractor with parse_update() Tier 1 parser in classifier dispatch. Handles MySQL multi-table emission without FROM keyword and PostgreSQL FROM clause with keyword.
1 parent 7c9a1fe commit b2ed564

3 files changed

Lines changed: 127 additions & 1 deletion

File tree

include/sql_parser/emitter.h

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,10 @@ class Emitter {
6868
case NodeType::NODE_STMT_OPTIONS: emit_stmt_options(node); break;
6969
case NodeType::NODE_UPDATE_SET_ITEM: emit_update_set_item(node); break;
7070

71+
// ---- UPDATE statement ----
72+
case NodeType::NODE_UPDATE_STMT: emit_update_stmt(node); break;
73+
case NodeType::NODE_UPDATE_SET_CLAUSE: emit_update_set_clause(node); break;
74+
7175
// ---- Table references ----
7276
case NodeType::NODE_TABLE_REF: emit_table_ref(node); break;
7377
case NodeType::NODE_ALIAS: emit_alias(node); break;
@@ -585,6 +589,91 @@ class Emitter {
585589
}
586590
}
587591

592+
// ---- UPDATE ----
593+
594+
void emit_update_stmt(const AstNode* node) {
595+
sb_.append("UPDATE");
596+
597+
for (const AstNode* child = node->first_child; child; child = child->next_sibling) {
598+
switch (child->type) {
599+
case NodeType::NODE_STMT_OPTIONS:
600+
sb_.append_char(' ');
601+
emit_node(child);
602+
break;
603+
case NodeType::NODE_TABLE_REF:
604+
sb_.append_char(' ');
605+
emit_node(child);
606+
break;
607+
case NodeType::NODE_FROM_CLAUSE:
608+
{
609+
// Check if FROM_CLAUSE is before SET_CLAUSE (MySQL multi-table)
610+
// or after (PostgreSQL FROM)
611+
bool is_before_set = false;
612+
for (const AstNode* s = child->next_sibling; s; s = s->next_sibling) {
613+
if (s->type == NodeType::NODE_UPDATE_SET_CLAUSE) {
614+
is_before_set = true;
615+
break;
616+
}
617+
}
618+
if (is_before_set) {
619+
// MySQL multi-table: emit table refs without FROM keyword
620+
sb_.append_char(' ');
621+
bool first = true;
622+
for (const AstNode* c = child->first_child; c; c = c->next_sibling) {
623+
if (c->type == NodeType::NODE_JOIN_CLAUSE) {
624+
sb_.append_char(' ');
625+
emit_node(c);
626+
} else {
627+
if (!first) sb_.append(", ");
628+
first = false;
629+
emit_node(c);
630+
}
631+
}
632+
} else {
633+
// PostgreSQL FROM clause (emit_from_clause adds " FROM " prefix)
634+
emit_node(child);
635+
}
636+
}
637+
break;
638+
case NodeType::NODE_UPDATE_SET_CLAUSE:
639+
sb_.append(" SET ");
640+
emit_update_set_clause_inner(child);
641+
break;
642+
case NodeType::NODE_WHERE_CLAUSE:
643+
emit_node(child);
644+
break;
645+
case NodeType::NODE_ORDER_BY_CLAUSE:
646+
emit_node(child);
647+
break;
648+
case NodeType::NODE_LIMIT_CLAUSE:
649+
emit_node(child);
650+
break;
651+
case NodeType::NODE_RETURNING_CLAUSE:
652+
sb_.append_char(' ');
653+
emit_node(child);
654+
break;
655+
default:
656+
sb_.append_char(' ');
657+
emit_node(child);
658+
break;
659+
}
660+
}
661+
}
662+
663+
void emit_update_set_clause(const AstNode* node) {
664+
sb_.append("SET ");
665+
emit_update_set_clause_inner(node);
666+
}
667+
668+
void emit_update_set_clause_inner(const AstNode* node) {
669+
bool first = true;
670+
for (const AstNode* child = node->first_child; child; child = child->next_sibling) {
671+
if (!first) sb_.append(", ");
672+
first = false;
673+
emit_node(child);
674+
}
675+
}
676+
588677
// ---- Expressions ----
589678

590679
void emit_binary_op(const AstNode* node) {

include/sql_parser/parser.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ class Parser {
5454
ParseResult parse_select();
5555
ParseResult parse_set();
5656
ParseResult parse_insert(bool is_replace = false);
57+
ParseResult parse_update();
5758

5859
// Tier 2 extractors
5960
ParseResult extract_insert(const Token& first);

src/sql_parser/parser.cpp

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include "sql_parser/set_parser.h"
44
#include "sql_parser/select_parser.h"
55
#include "sql_parser/insert_parser.h"
6+
#include "sql_parser/update_parser.h"
67

78
namespace sql_parser {
89

@@ -38,7 +39,7 @@ ParseResult Parser<D>::classify_and_dispatch() {
3839
case TokenType::TK_SELECT: return parse_select();
3940
case TokenType::TK_SET: return parse_set();
4041
case TokenType::TK_INSERT: return parse_insert(false);
41-
case TokenType::TK_UPDATE: return extract_update(first);
42+
case TokenType::TK_UPDATE: return parse_update();
4243
case TokenType::TK_DELETE: return extract_delete(first);
4344
case TokenType::TK_REPLACE: return parse_insert(true);
4445
case TokenType::TK_BEGIN:
@@ -141,6 +142,41 @@ ParseResult Parser<D>::parse_insert(bool is_replace) {
141142
return r;
142143
}
143144

145+
template <Dialect D>
146+
ParseResult Parser<D>::parse_update() {
147+
ParseResult r;
148+
r.stmt_type = StmtType::UPDATE;
149+
150+
UpdateParser<D> update_parser(tokenizer_, arena_);
151+
AstNode* ast = update_parser.parse();
152+
153+
if (ast) {
154+
r.status = ParseResult::OK;
155+
r.ast = ast;
156+
157+
// Extract table_name/schema_name from AST for backward compatibility
158+
for (const AstNode* child = ast->first_child; child; child = child->next_sibling) {
159+
if (child->type == NodeType::NODE_TABLE_REF) {
160+
const AstNode* name_node = child->first_child;
161+
if (name_node && name_node->type == NodeType::NODE_QUALIFIED_NAME) {
162+
const AstNode* schema = name_node->first_child;
163+
const AstNode* table = schema ? schema->next_sibling : nullptr;
164+
if (schema) r.schema_name = schema->value();
165+
if (table) r.table_name = table->value();
166+
} else if (name_node && name_node->type == NodeType::NODE_IDENTIFIER) {
167+
r.table_name = name_node->value();
168+
}
169+
break;
170+
}
171+
}
172+
} else {
173+
r.status = ParseResult::PARTIAL;
174+
}
175+
176+
scan_to_end(r);
177+
return r;
178+
}
179+
144180
// ---- Helpers ----
145181

146182
template <Dialect D>

0 commit comments

Comments
 (0)