Skip to content

Commit d21f261

Browse files
committed
fix: handle insert update delete and select within unknown
1 parent 2d130d7 commit d21f261

File tree

5 files changed

+119
-8
lines changed

5 files changed

+119
-8
lines changed

crates/pg_statement_splitter/src/lib.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,34 @@ mod tests {
7272
]);
7373
}
7474

75+
#[test]
76+
fn insert_with_select() {
77+
Tester::from("\ninsert into tbl (id) select 1\n\nselect 3")
78+
.expect_statements(vec!["insert into tbl (id) select 1", "select 3"]);
79+
}
80+
81+
#[test]
82+
fn case() {
83+
Tester::from("select case when select 2 then 1 else 0 end")
84+
.expect_statements(vec!["select case when select 2 then 1 else 0 end"]);
85+
}
86+
87+
#[test]
88+
fn create_rule() {
89+
Tester::from(
90+
"create rule log_employee_insert as
91+
on insert to employees
92+
do also insert into employee_log (action, employee_id, log_time)
93+
values ('insert', new.id, now());",
94+
)
95+
.expect_statements(vec![
96+
"create rule log_employee_insert as
97+
on insert to employees
98+
do also insert into employee_log (action, employee_id, log_time)
99+
values ('insert', new.id, now());",
100+
]);
101+
}
102+
75103
#[test]
76104
fn insert_into() {
77105
Tester::from("randomness\ninsert into tbl (id) values (1)\nselect 3").expect_statements(

crates/pg_statement_splitter/src/parser.rs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
mod common;
22
mod data;
3+
mod ddl;
34
mod dml;
45

56
pub use common::source;
@@ -143,6 +144,24 @@ impl Parser {
143144
}
144145
}
145146

147+
fn look_back(&self) -> Option<&Token> {
148+
// we need to look back to the last relevant token
149+
let mut look_back_pos = self.next_pos - 1;
150+
loop {
151+
let token = self.tokens.get(look_back_pos);
152+
153+
if look_back_pos == 0 || token.is_none() {
154+
return None;
155+
}
156+
157+
if !is_irrelevant_token(token.unwrap()) {
158+
return token;
159+
}
160+
161+
look_back_pos -= 1;
162+
}
163+
}
164+
146165
/// checks if the current token is of `kind` and advances if true
147166
/// returns true if the current token is of `kind`
148167
pub fn eat(&mut self, kind: SyntaxKind) -> bool {
@@ -164,7 +183,7 @@ impl Parser {
164183

165184
/// collects an SyntaxError with an `error` message at the current position
166185
fn error_at(&mut self, error: String) {
167-
todo!();
186+
todo!("{error}");
168187
}
169188
}
170189

crates/pg_statement_splitter/src/parser/common.rs

Lines changed: 58 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use pg_lexer::{SyntaxKind, Token, TokenType};
22

33
use super::{
44
data::at_statement_start,
5+
ddl::create,
56
dml::{cte, delete, insert, select, update},
67
Parser,
78
};
@@ -48,6 +49,9 @@ pub(crate) fn statement(p: &mut Parser) {
4849
SyntaxKind::DeleteP => {
4950
delete(p);
5051
}
52+
SyntaxKind::Create => {
53+
create(p);
54+
}
5155
_ => {
5256
unknown(p);
5357
}
@@ -71,6 +75,22 @@ pub(crate) fn parenthesis(p: &mut Parser) {
7175
}
7276
}
7377

78+
pub(crate) fn case(p: &mut Parser) {
79+
p.expect(SyntaxKind::Case);
80+
81+
loop {
82+
match p.peek().kind {
83+
SyntaxKind::EndP => {
84+
p.advance();
85+
break;
86+
}
87+
_ => {
88+
p.advance();
89+
}
90+
}
91+
}
92+
}
93+
7494
pub(crate) fn unknown(p: &mut Parser) {
7595
loop {
7696
match p.peek() {
@@ -87,19 +107,52 @@ pub(crate) fn unknown(p: &mut Parser) {
87107
} => {
88108
break;
89109
}
110+
Token {
111+
kind: SyntaxKind::Case,
112+
..
113+
} => {
114+
case(p);
115+
}
90116
Token {
91117
kind: SyntaxKind::Ascii40,
92118
..
93119
} => {
94120
parenthesis(p);
95121
}
96-
t => {
97-
if at_statement_start(t.kind) {
122+
t => match at_statement_start(t.kind) {
123+
Some(SyntaxKind::Select) => {
124+
// we need to check for `as` here to not break on `select as`
125+
if p.look_back().map(|t| t.kind) != Some(SyntaxKind::As) {
126+
break;
127+
}
128+
p.advance();
129+
}
130+
Some(SyntaxKind::Insert) | Some(SyntaxKind::Update) | Some(SyntaxKind::DeleteP) => {
131+
let prev = p.look_back().map(|t| t.kind);
132+
if [
133+
// for create trigger
134+
SyntaxKind::After,
135+
// for create rule
136+
SyntaxKind::On,
137+
// for create rule
138+
SyntaxKind::Also,
139+
// for create rule
140+
SyntaxKind::Instead,
141+
]
142+
.iter()
143+
.all(|x| Some(x) != prev.as_ref())
144+
{
145+
break;
146+
}
147+
p.advance();
148+
}
149+
Some(_) => {
98150
break;
99151
}
100-
101-
p.advance();
102-
}
152+
None => {
153+
p.advance();
154+
}
155+
},
103156
}
104157
}
105158
}
Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
use pg_lexer::SyntaxKind;
22

3+
// All tokens listed here must be explicitly handled in the `unknown` function to ensure that we do
4+
// not break in the middle of another statement that contains a statement start token.
35
static STATEMENT_START_TOKENS: &[SyntaxKind] = &[
46
SyntaxKind::With,
57
SyntaxKind::Select,
@@ -9,6 +11,6 @@ static STATEMENT_START_TOKENS: &[SyntaxKind] = &[
911
SyntaxKind::Create,
1012
];
1113

12-
pub(crate) fn at_statement_start(kind: SyntaxKind) -> bool {
13-
STATEMENT_START_TOKENS.contains(&kind)
14+
pub(crate) fn at_statement_start(kind: SyntaxKind) -> Option<SyntaxKind> {
15+
STATEMENT_START_TOKENS.iter().find(|&x| x == &kind).cloned()
1416
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
use pg_lexer::SyntaxKind;
2+
3+
use super::{common::unknown, Parser};
4+
5+
pub(crate) fn create(p: &mut Parser) {
6+
p.expect(SyntaxKind::Create);
7+
8+
unknown(p);
9+
}

0 commit comments

Comments
 (0)