Repository navigation
PostgreSQL: Support CREATE FOREIGN TABLE - #2515
Conversation
Table-level OPTIONS reuses the list parser lifted out of CREATE SERVER.
Without parens Display emitted a () the input never had, and a persistence modifier was accepted then dropped. Also covers OPTIONS in the span.
OR ALTER, MULTISET, PERSISTENT and the view params were still accepted and then dropped. The error now points at the modifier rather than the table name.
Matching any ParserError would also pass if the guard stopped being reached. LOCAL, SET and the view params were never exercised.
Also says why the or_replace disjunct is kept despite being unreachable today.
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #2515 +/- ##
==========================================
- Coverage 81.02% 81.01% -0.02%
==========================================
Files 42 42
Lines 33455 33541 +86
Branches 33455 33541 +86
==========================================
+ Hits 27108 27173 +65
- Misses 2789 2800 +11
- Partials 3558 3568 +10 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
|
||
| #[test] | ||
| fn test_create_foreign_table_span_includes_option_keys() { | ||
| let dialect = &crate::dialect::PostgreSqlDialect {}; | ||
| let sql = "CREATE FOREIGN TABLE ft (a INT) SERVER s OPTIONS (schema_name 'public')"; | ||
| let mut test = SpanTest::new(dialect, sql); | ||
|
|
||
| // Ends at the option key, not the statement: a quoted option value is an | ||
| // Ident with an empty span, so it contributes nothing to the union. | ||
| let stmt = test.0.parse_statement().unwrap(); | ||
| assert_eq!( | ||
| test.get_source(stmt.span()), | ||
| "ft (a INT) SERVER s OPTIONS (schema_name" | ||
| ); | ||
| } |
There was a problem hiding this comment.
You should not pin known-wrong behaviour in tests. This span stops at (schema_name because parse_identifier gives quoted identifiers an empty span.
| #[test] | |
| fn test_create_foreign_table_span_includes_option_keys() { | |
| let dialect = &crate::dialect::PostgreSqlDialect {}; | |
| let sql = "CREATE FOREIGN TABLE ft (a INT) SERVER s OPTIONS (schema_name 'public')"; | |
| let mut test = SpanTest::new(dialect, sql); | |
| // Ends at the option key, not the statement: a quoted option value is an | |
| // Ident with an empty span, so it contributes nothing to the union. | |
| let stmt = test.0.parse_statement().unwrap(); | |
| assert_eq!( | |
| test.get_source(stmt.span()), | |
| "ft (a INT) SERVER s OPTIONS (schema_name" | |
| ); | |
| } |
…eign-table # Conflicts: # src/ast/spans.rs
| #[test] | ||
| fn test_create_foreign_table_span_includes_quoted_option_value() { | ||
| let dialect = &crate::dialect::PostgreSqlDialect {}; | ||
| let sql = "CREATE FOREIGN TABLE ft (a INT) SERVER s OPTIONS (schema_name 'public')"; | ||
| let mut test = SpanTest::new(dialect, sql); | ||
|
|
||
| // Span reaches through the quoted value but not the closing paren. | ||
| let stmt = test.0.parse_statement().unwrap(); | ||
| assert_eq!( | ||
| test.get_source(stmt.span()), | ||
| "ft (a INT) SERVER s OPTIONS (schema_name 'public'" | ||
| ); | ||
| } |
There was a problem hiding this comment.
You should assert the option spans directly instead of the statement span. The statement span still leaves out CREATE FOREIGN TABLE and the closing ), so this test pins known-wrong behaviour again. The version below fails if parse_identifier drops the span of a quoted identifier.
| #[test] | |
| fn test_create_foreign_table_span_includes_quoted_option_value() { | |
| let dialect = &crate::dialect::PostgreSqlDialect {}; | |
| let sql = "CREATE FOREIGN TABLE ft (a INT) SERVER s OPTIONS (schema_name 'public')"; | |
| let mut test = SpanTest::new(dialect, sql); | |
| // Span reaches through the quoted value but not the closing paren. | |
| let stmt = test.0.parse_statement().unwrap(); | |
| assert_eq!( | |
| test.get_source(stmt.span()), | |
| "ft (a INT) SERVER s OPTIONS (schema_name 'public'" | |
| ); | |
| } | |
| #[test] | |
| fn test_create_foreign_table_option_spans() { | |
| let dialect = &crate::dialect::PostgreSqlDialect {}; | |
| let sql = r#"CREATE FOREIGN TABLE ft (a INT) SERVER s OPTIONS ("schema_name" 'public')"#; | |
| let mut test = SpanTest::new(dialect, sql); | |
| let options = match test.0.parse_statement().unwrap() { | |
| Statement::CreateForeignTable(stmt) => stmt.options.unwrap(), | |
| stmt => panic!("expected CREATE FOREIGN TABLE, got {stmt:?}"), | |
| }; | |
| assert_eq!(test.get_source(options[0].key.span), r#""schema_name""#); | |
| assert_eq!(test.get_source(options[0].value.span), "'public'"); | |
| } |
There was a problem hiding this comment.
done in 88cbb5d, asserting the key and value spans directly.
There was a problem hiding this comment.
Thanks for splitting CREATE FOREIGN TABLE out of #2317 and finishing up the PR, @fmguerreiro.
If you have some time, we would really appreciate your help with reviewing other open PRs, which is where the project is most short-handed right now. Since you seem interested in PostgreSQL, running the fuzz_postgres_accepts harness would help a lot too. It feeds generated SQL to PostgreSQL's own grammar through pg_query and reports every statement PostgreSQL accepts that PostgreSqlDialect rejects. You can start it from the fuzz directory with
cargo +nightly fuzz run fuzz_postgres_accepts fuzz_seeds -- -max_total_time=600and docs/fuzzing.md has the details. Minimized reproductions of what it finds, as issues or small fix PRs, are worth much more to us at this point than new syntax.
Parses
CREATE FOREIGN TABLE [IF NOT EXISTS] name (columns) SERVER server [OPTIONS (...)](docs).Split out of #2317 at @LucaCappelletti94's request. That PR now carries
CREATE FOREIGN DATA WRAPPERonly.Table-level
OPTIONSreuses the list parser pulled out ofparse_pg_create_server, which #2317 also moves; whichever lands second needs a rebase there.Per-column
OPTIONS (...),INHERITS,PARTITION OFandALTER FOREIGN TABLEare out of scope; the rustdoc notes that.