Skip to content

Commit fc0e13b

Browse files
authored
add support for FOR ORDINALITY and NESTED in JSON_TABLE (#1493)
1 parent a5b0092 commit fc0e13b

File tree

4 files changed

+102
-14
lines changed

4 files changed

+102
-14
lines changed

src/ast/mod.rs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -54,13 +54,14 @@ pub use self::query::{
5454
ExceptSelectItem, ExcludeSelectItem, ExprWithAlias, Fetch, ForClause, ForJson, ForXml,
5555
FormatClause, GroupByExpr, GroupByWithModifier, IdentWithAlias, IlikeSelectItem, Interpolate,
5656
InterpolateExpr, Join, JoinConstraint, JoinOperator, JsonTableColumn,
57-
JsonTableColumnErrorHandling, LateralView, LockClause, LockType, MatchRecognizePattern,
58-
MatchRecognizeSymbol, Measure, NamedWindowDefinition, NamedWindowExpr, NonBlock, Offset,
59-
OffsetRows, OrderBy, OrderByExpr, PivotValueSource, ProjectionSelect, Query, RenameSelectItem,
60-
RepetitionQuantifier, ReplaceSelectElement, ReplaceSelectItem, RowsPerMatch, Select,
61-
SelectInto, SelectItem, SetExpr, SetOperator, SetQuantifier, Setting, SymbolDefinition, Table,
62-
TableAlias, TableFactor, TableFunctionArgs, TableVersion, TableWithJoins, Top, TopQuantity,
63-
ValueTableMode, Values, WildcardAdditionalOptions, With, WithFill,
57+
JsonTableColumnErrorHandling, JsonTableNamedColumn, JsonTableNestedColumn, LateralView,
58+
LockClause, LockType, MatchRecognizePattern, MatchRecognizeSymbol, Measure,
59+
NamedWindowDefinition, NamedWindowExpr, NonBlock, Offset, OffsetRows, OrderBy, OrderByExpr,
60+
PivotValueSource, ProjectionSelect, Query, RenameSelectItem, RepetitionQuantifier,
61+
ReplaceSelectElement, ReplaceSelectItem, RowsPerMatch, Select, SelectInto, SelectItem, SetExpr,
62+
SetOperator, SetQuantifier, Setting, SymbolDefinition, Table, TableAlias, TableFactor,
63+
TableFunctionArgs, TableVersion, TableWithJoins, Top, TopQuantity, ValueTableMode, Values,
64+
WildcardAdditionalOptions, With, WithFill,
6465
};
6566

6667
pub use self::trigger::{

src/ast/query.rs

Lines changed: 68 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2286,19 +2286,84 @@ impl fmt::Display for ForJson {
22862286
}
22872287

22882288
/// A single column definition in MySQL's `JSON_TABLE` table valued function.
2289+
///
2290+
/// See
2291+
/// - [MySQL's JSON_TABLE documentation](https://dev.mysql.com/doc/refman/8.0/en/json-table-functions.html#function_json-table)
2292+
/// - [Oracle's JSON_TABLE documentation](https://docs.oracle.com/en/database/oracle/oracle-database/21/sqlrf/JSON_TABLE.html)
2293+
/// - [MariaDB's JSON_TABLE documentation](https://mariadb.com/kb/en/json_table/)
2294+
///
22892295
/// ```sql
22902296
/// SELECT *
22912297
/// FROM JSON_TABLE(
22922298
/// '["a", "b"]',
22932299
/// '$[*]' COLUMNS (
2294-
/// value VARCHAR(20) PATH '$'
2300+
/// name FOR ORDINALITY,
2301+
/// value VARCHAR(20) PATH '$',
2302+
/// NESTED PATH '$[*]' COLUMNS (
2303+
/// value VARCHAR(20) PATH '$'
2304+
/// )
22952305
/// )
22962306
/// ) AS jt;
22972307
/// ```
22982308
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
22992309
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
23002310
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
2301-
pub struct JsonTableColumn {
2311+
pub enum JsonTableColumn {
2312+
/// A named column with a JSON path
2313+
Named(JsonTableNamedColumn),
2314+
/// The FOR ORDINALITY column, which is a special column that returns the index of the current row in a JSON array.
2315+
ForOrdinality(Ident),
2316+
/// A set of nested columns, which extracts data from a nested JSON array.
2317+
Nested(JsonTableNestedColumn),
2318+
}
2319+
2320+
impl fmt::Display for JsonTableColumn {
2321+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2322+
match self {
2323+
JsonTableColumn::Named(json_table_named_column) => {
2324+
write!(f, "{json_table_named_column}")
2325+
}
2326+
JsonTableColumn::ForOrdinality(ident) => write!(f, "{} FOR ORDINALITY", ident),
2327+
JsonTableColumn::Nested(json_table_nested_column) => {
2328+
write!(f, "{json_table_nested_column}")
2329+
}
2330+
}
2331+
}
2332+
}
2333+
2334+
/// A nested column in a JSON_TABLE column list
2335+
///
2336+
/// See <https://mariadb.com/kb/en/json_table/#nested-paths>
2337+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
2338+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
2339+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
2340+
pub struct JsonTableNestedColumn {
2341+
pub path: Value,
2342+
pub columns: Vec<JsonTableColumn>,
2343+
}
2344+
2345+
impl fmt::Display for JsonTableNestedColumn {
2346+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2347+
write!(
2348+
f,
2349+
"NESTED PATH {} COLUMNS ({})",
2350+
self.path,
2351+
display_comma_separated(&self.columns)
2352+
)
2353+
}
2354+
}
2355+
2356+
/// A single column definition in MySQL's `JSON_TABLE` table valued function.
2357+
///
2358+
/// See <https://mariadb.com/kb/en/json_table/#path-columns>
2359+
///
2360+
/// ```sql
2361+
/// value VARCHAR(20) PATH '$'
2362+
/// ```
2363+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
2364+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
2365+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
2366+
pub struct JsonTableNamedColumn {
23022367
/// The name of the column to be extracted.
23032368
pub name: Ident,
23042369
/// The type of the column to be extracted.
@@ -2313,7 +2378,7 @@ pub struct JsonTableColumn {
23132378
pub on_error: Option<JsonTableColumnErrorHandling>,
23142379
}
23152380

2316-
impl fmt::Display for JsonTableColumn {
2381+
impl fmt::Display for JsonTableNamedColumn {
23172382
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
23182383
write!(
23192384
f,

src/parser/mod.rs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10466,7 +10466,23 @@ impl<'a> Parser<'a> {
1046610466
/// Parses MySQL's JSON_TABLE column definition.
1046710467
/// For example: `id INT EXISTS PATH '$' DEFAULT '0' ON EMPTY ERROR ON ERROR`
1046810468
pub fn parse_json_table_column_def(&mut self) -> Result<JsonTableColumn, ParserError> {
10469+
if self.parse_keyword(Keyword::NESTED) {
10470+
let _has_path_keyword = self.parse_keyword(Keyword::PATH);
10471+
let path = self.parse_value()?;
10472+
self.expect_keyword(Keyword::COLUMNS)?;
10473+
let columns = self.parse_parenthesized(|p| {
10474+
p.parse_comma_separated(Self::parse_json_table_column_def)
10475+
})?;
10476+
return Ok(JsonTableColumn::Nested(JsonTableNestedColumn {
10477+
path,
10478+
columns,
10479+
}));
10480+
}
1046910481
let name = self.parse_identifier(false)?;
10482+
if self.parse_keyword(Keyword::FOR) {
10483+
self.expect_keyword(Keyword::ORDINALITY)?;
10484+
return Ok(JsonTableColumn::ForOrdinality(name));
10485+
}
1047010486
let r#type = self.parse_data_type()?;
1047110487
let exists = self.parse_keyword(Keyword::EXISTS);
1047210488
self.expect_keyword(Keyword::PATH)?;
@@ -10481,14 +10497,14 @@ impl<'a> Parser<'a> {
1048110497
on_error = Some(error_handling);
1048210498
}
1048310499
}
10484-
Ok(JsonTableColumn {
10500+
Ok(JsonTableColumn::Named(JsonTableNamedColumn {
1048510501
name,
1048610502
r#type,
1048710503
path,
1048810504
exists,
1048910505
on_empty,
1049010506
on_error,
10491-
})
10507+
}))
1049210508
}
1049310509

1049410510
fn parse_json_table_column_error_handling(

tests/sqlparser_mysql.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2773,6 +2773,12 @@ fn parse_json_table() {
27732773
r#"SELECT * FROM JSON_TABLE('[1,2]', '$[*]' COLUMNS(x INT PATH '$' ERROR ON EMPTY)) AS t"#,
27742774
);
27752775
mysql().verified_only_select(r#"SELECT * FROM JSON_TABLE('[1,2]', '$[*]' COLUMNS(x INT PATH '$' ERROR ON EMPTY DEFAULT '0' ON ERROR)) AS t"#);
2776+
mysql().verified_only_select(
2777+
r#"SELECT jt.* FROM JSON_TABLE('["Alice", "Bob", "Charlie"]', '$[*]' COLUMNS(row_num FOR ORDINALITY, name VARCHAR(50) PATH '$')) AS jt"#,
2778+
);
2779+
mysql().verified_only_select(
2780+
r#"SELECT * FROM JSON_TABLE('[ {"a": 1, "b": [11,111]}, {"a": 2, "b": [22,222]}, {"a":3}]', '$[*]' COLUMNS(a INT PATH '$.a', NESTED PATH '$.b[*]' COLUMNS (b INT PATH '$'))) AS jt"#,
2781+
);
27762782
assert_eq!(
27772783
mysql()
27782784
.verified_only_select(
@@ -2784,14 +2790,14 @@ fn parse_json_table() {
27842790
json_expr: Expr::Value(Value::SingleQuotedString("[1,2]".to_string())),
27852791
json_path: Value::SingleQuotedString("$[*]".to_string()),
27862792
columns: vec![
2787-
JsonTableColumn {
2793+
JsonTableColumn::Named(JsonTableNamedColumn {
27882794
name: Ident::new("x"),
27892795
r#type: DataType::Int(None),
27902796
path: Value::SingleQuotedString("$".to_string()),
27912797
exists: false,
27922798
on_empty: Some(JsonTableColumnErrorHandling::Default(Value::SingleQuotedString("0".to_string()))),
27932799
on_error: Some(JsonTableColumnErrorHandling::Null),
2794-
},
2800+
}),
27952801
],
27962802
alias: Some(TableAlias {
27972803
name: Ident::new("t"),

0 commit comments

Comments
 (0)