Skip to content

Commit f8c3e73

Browse files
committed
Adds top-statements PREPARE, EXECUTE and DEALLOCATE for Postgres-specific feature prepared statement.
1 parent f053383 commit f8c3e73

File tree

4 files changed

+233
-2
lines changed

4 files changed

+233
-2
lines changed

src/ast/mod.rs

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -551,12 +551,34 @@ pub enum Statement {
551551
Rollback { chain: bool },
552552
/// CREATE SCHEMA
553553
CreateSchema { schema_name: ObjectName },
554-
555554
/// ASSERT <condition> [AS <message>]
556555
Assert {
557556
condition: Expr,
558557
message: Option<Expr>,
559558
},
559+
/// DEALLOCATE [ PREPARE ] { name | ALL }
560+
///
561+
/// Note: this is a PostgreSQL-specific statement.
562+
Deallocate {
563+
name: Option<ObjectName>,
564+
all: bool,
565+
prepare: bool,
566+
},
567+
/// EXECUTE name [ ( parameter [, ...] ) ]
568+
///
569+
/// Note: this is a PostgreSQL-specific statement.
570+
Execute {
571+
name: ObjectName,
572+
parameters: Vec<Expr>,
573+
},
574+
/// PREPARE name [ ( data_type [, ...] ) ] AS statement
575+
///
576+
/// Note: this is a PostgreSQL-specific statement.
577+
Prepare {
578+
name: ObjectName,
579+
data_types: Vec<DataType>,
580+
statement: Box<Statement>,
581+
},
560582
}
561583

562584
impl fmt::Display for Statement {
@@ -824,6 +846,36 @@ impl fmt::Display for Statement {
824846
}
825847
Ok(())
826848
}
849+
Statement::Deallocate { name, all, prepare } => {
850+
f.write_str("DEALLOCATE ")?;
851+
if *prepare {
852+
f.write_str("PREPARE ")?;
853+
}
854+
if *all {
855+
f.write_str("ALL")
856+
} else {
857+
write!(f, "{}", name.as_ref().unwrap())
858+
}
859+
}
860+
Statement::Execute { name, parameters } => {
861+
write!(f, "EXECUTE {}", name)?;
862+
if !parameters.is_empty() {
863+
write!(f, "({})", display_comma_separated(parameters))?;
864+
}
865+
Ok(())
866+
}
867+
Statement::Prepare {
868+
name,
869+
data_types,
870+
statement,
871+
} => {
872+
write!(f, "PREPARE {} ", name)?;
873+
if !data_types.is_empty() {
874+
write!(f, "({}) ", display_comma_separated(data_types))?;
875+
}
876+
877+
write!(f, "AS {}", statement)
878+
}
827879
}
828880
}
829881
}

src/dialect/postgresql.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ impl Dialect for PostgreSqlDialect {
2020
// See https://www.postgresql.org/docs/11/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS
2121
// We don't yet support identifiers beginning with "letters with
2222
// diacritical marks and non-Latin letters"
23-
(ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || ch == '_'
23+
(ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || ch == '_' || ch == '$'
2424
}
2525

2626
fn is_identifier_part(&self, ch: char) -> bool {

src/parser.rs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,11 @@ impl Parser {
149149
Keyword::COMMIT => Ok(self.parse_commit()?),
150150
Keyword::ROLLBACK => Ok(self.parse_rollback()?),
151151
Keyword::ASSERT => Ok(self.parse_assert()?),
152+
// `PREPARE`, `EXECUTE` and `DEALLOCATE` are Postgres-specific
153+
// syntaxes. They are used for Postgres prepared statement.
154+
Keyword::DEALLOCATE => Ok(self.parse_deallocate()?),
155+
Keyword::EXECUTE => Ok(self.parse_execute()?),
156+
Keyword::PREPARE => Ok(self.parse_prepare()?),
152157
_ => self.expected("an SQL statement", Token::Word(w)),
153158
},
154159
Token::LParen => {
@@ -2386,6 +2391,49 @@ impl Parser {
23862391
Ok(false)
23872392
}
23882393
}
2394+
2395+
fn parse_deallocate(&mut self) -> Result<Statement, ParserError> {
2396+
let prepare = self.parse_keyword(Keyword::PREPARE);
2397+
let all = self.parse_keyword(Keyword::ALL);
2398+
2399+
let name = if all {
2400+
None
2401+
} else {
2402+
Some(self.parse_object_name()?)
2403+
};
2404+
2405+
Ok(Statement::Deallocate { name, all, prepare })
2406+
}
2407+
2408+
fn parse_execute(&mut self) -> Result<Statement, ParserError> {
2409+
let name = self.parse_object_name()?;
2410+
2411+
let mut parameters = vec![];
2412+
if self.expect_token(&Token::LParen).is_ok() {
2413+
parameters = self.parse_comma_separated(Parser::parse_expr)?;
2414+
self.expect_token(&Token::RParen)?;
2415+
}
2416+
2417+
Ok(Statement::Execute { name, parameters })
2418+
}
2419+
2420+
fn parse_prepare(&mut self) -> Result<Statement, ParserError> {
2421+
let name = self.parse_object_name()?;
2422+
2423+
let mut data_types = vec![];
2424+
if self.expect_token(&Token::LParen).is_ok() {
2425+
data_types = self.parse_comma_separated(Parser::parse_data_type)?;
2426+
self.expect_token(&Token::RParen)?;
2427+
}
2428+
2429+
self.expect_keyword(Keyword::AS)?;
2430+
let statement = Box::new(self.parse_statement()?);
2431+
Ok(Statement::Prepare {
2432+
name,
2433+
data_types,
2434+
statement,
2435+
})
2436+
}
23892437
}
23902438

23912439
impl Word {

tests/sqlparser_postgres.rs

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -423,6 +423,137 @@ fn parse_show() {
423423
)
424424
}
425425

426+
#[test]
427+
fn parse_deallocate() {
428+
let stmt = pg().verified_stmt("DEALLOCATE a");
429+
assert_eq!(
430+
stmt,
431+
Statement::Deallocate {
432+
name: Some(ObjectName(vec!["a".into()])),
433+
all: false,
434+
prepare: false,
435+
}
436+
);
437+
438+
let stmt = pg().verified_stmt("DEALLOCATE ALL");
439+
assert_eq!(
440+
stmt,
441+
Statement::Deallocate {
442+
name: None,
443+
all: true,
444+
prepare: false,
445+
}
446+
);
447+
448+
let stmt = pg().verified_stmt("DEALLOCATE PREPARE a");
449+
assert_eq!(
450+
stmt,
451+
Statement::Deallocate {
452+
name: Some(ObjectName(vec!["a".into()])),
453+
all: false,
454+
prepare: true,
455+
}
456+
);
457+
458+
let stmt = pg().verified_stmt("DEALLOCATE PREPARE ALL");
459+
assert_eq!(
460+
stmt,
461+
Statement::Deallocate {
462+
name: None,
463+
all: true,
464+
prepare: true,
465+
}
466+
);
467+
}
468+
469+
#[test]
470+
fn parse_execute() {
471+
let stmt = pg().verified_stmt("EXECUTE a");
472+
assert_eq!(
473+
stmt,
474+
Statement::Execute {
475+
name: ObjectName(vec!["a".into()]),
476+
parameters: vec![],
477+
}
478+
);
479+
480+
let stmt = pg().verified_stmt("EXECUTE a(1, 't')");
481+
assert_eq!(
482+
stmt,
483+
Statement::Execute {
484+
name: ObjectName(vec!["a".into()]),
485+
parameters: vec![
486+
Expr::Value(Value::Number("1".to_string())),
487+
Expr::Value(Value::SingleQuotedString("t".to_string()))
488+
],
489+
}
490+
);
491+
}
492+
493+
#[test]
494+
fn parse_prepare() {
495+
let stmt = pg().verified_stmt("PREPARE a AS INSERT INTO customers VALUES ($1, $2, $3)");
496+
let sub_stmt = match stmt {
497+
Statement::Prepare {
498+
name,
499+
data_types,
500+
statement,
501+
..
502+
} => {
503+
assert_eq!(name, ObjectName(vec!["a".into()]));
504+
assert!(data_types.is_empty());
505+
506+
statement
507+
}
508+
_ => unreachable!(),
509+
};
510+
match sub_stmt.as_ref() {
511+
Statement::Insert {
512+
table_name,
513+
columns,
514+
source,
515+
..
516+
} => {
517+
assert_eq!(table_name.to_string(), "customers");
518+
assert!(columns.is_empty());
519+
520+
let expected_values = [vec![
521+
Expr::Identifier("$1".into()),
522+
Expr::Identifier("$2".into()),
523+
Expr::Identifier("$3".into()),
524+
]];
525+
match &source.body {
526+
SetExpr::Values(Values(values)) => assert_eq!(values.as_slice(), &expected_values),
527+
_ => unreachable!(),
528+
}
529+
}
530+
_ => unreachable!(),
531+
};
532+
533+
let stmt = pg()
534+
.verified_stmt("PREPARE a (INT, TEXT) AS SELECT * FROM customers WHERE customers.id = $1");
535+
let sub_stmt = match stmt {
536+
Statement::Prepare {
537+
name,
538+
data_types,
539+
statement,
540+
..
541+
} => {
542+
assert_eq!(name, ObjectName(vec!["a".into()]));
543+
assert_eq!(data_types, vec![DataType::Int, DataType::Text]);
544+
545+
statement
546+
}
547+
_ => unreachable!(),
548+
};
549+
assert_eq!(
550+
sub_stmt,
551+
Box::new(Statement::Query(Box::new(pg().verified_query(
552+
"SELECT * FROM customers WHERE customers.id = $1"
553+
))))
554+
);
555+
}
556+
426557
fn pg() -> TestedDialects {
427558
TestedDialects {
428559
dialects: vec![Box::new(PostgreSqlDialect {})],

0 commit comments

Comments
 (0)