@@ -1271,6 +1271,37 @@ pub enum TableFactor {
12711271 symbols : Vec < SymbolDefinition > ,
12721272 alias : Option < TableAlias > ,
12731273 } ,
1274+ /// The `XMLTABLE` table-valued function.
1275+ /// Part of the SQL standard, supported by PostgreSQL, Oracle, and DB2.
1276+ ///
1277+ /// <https://www.postgresql.org/docs/15/functions-xml.html#FUNCTIONS-XML-PROCESSING>
1278+ ///
1279+ /// ```sql
1280+ /// SELECT xmltable.*
1281+ /// FROM xmldata,
1282+ /// XMLTABLE('//ROWS/ROW'
1283+ /// PASSING data
1284+ /// COLUMNS id int PATH '@id',
1285+ /// ordinality FOR ORDINALITY,
1286+ /// "COUNTRY_NAME" text,
1287+ /// country_id text PATH 'COUNTRY_ID',
1288+ /// size_sq_km float PATH 'SIZE[@unit = "sq_km"]',
1289+ /// size_other text PATH 'concat(SIZE[@unit!="sq_km"], " ", SIZE[@unit!="sq_km"]/@unit)',
1290+ /// premier_name text PATH 'PREMIER_NAME' DEFAULT 'not specified'
1291+ /// );
1292+ /// ````
1293+ XmlTable {
1294+ /// Optional XMLNAMESPACES clause (empty if not present)
1295+ namespaces : Vec < XmlNamespaceDefinition > ,
1296+ /// The row-generating XPath expression.
1297+ row_expression : Expr ,
1298+ /// The PASSING clause specifying the document expression.
1299+ passing : XmlPassingClause ,
1300+ /// The columns to be extracted from each generated row.
1301+ columns : Vec < XmlTableColumn > ,
1302+ /// The alias for the table.
1303+ alias : Option < TableAlias > ,
1304+ } ,
12741305}
12751306
12761307/// The table sample modifier options
@@ -1936,6 +1967,31 @@ impl fmt::Display for TableFactor {
19361967 }
19371968 Ok ( ( ) )
19381969 }
1970+ TableFactor :: XmlTable {
1971+ row_expression,
1972+ passing,
1973+ columns,
1974+ alias,
1975+ namespaces,
1976+ } => {
1977+ write ! ( f, "XMLTABLE(" ) ?;
1978+ if !namespaces. is_empty ( ) {
1979+ write ! (
1980+ f,
1981+ "XMLNAMESPACES({}), " ,
1982+ display_comma_separated( namespaces)
1983+ ) ?;
1984+ }
1985+ write ! (
1986+ f,
1987+ "{row_expression}{passing} COLUMNS {columns})" ,
1988+ columns = display_comma_separated( columns)
1989+ ) ?;
1990+ if let Some ( alias) = alias {
1991+ write ! ( f, " AS {alias}" ) ?;
1992+ }
1993+ Ok ( ( ) )
1994+ }
19391995 }
19401996 }
19411997}
@@ -3082,3 +3138,133 @@ pub enum UpdateTableFromKind {
30823138 /// For Example: `UPDATE SET t1.name='aaa' FROM t1`
30833139 AfterSet ( Vec < TableWithJoins > ) ,
30843140}
3141+
3142+ /// Defines the options for an XmlTable column: Named or ForOrdinality
3143+ #[ derive( Debug , Clone , PartialEq , PartialOrd , Eq , Ord , Hash ) ]
3144+ #[ cfg_attr( feature = "visitor" , derive( Visit , VisitMut ) ) ]
3145+ #[ cfg_attr( feature = "serde" , derive( Serialize , Deserialize ) ) ]
3146+ pub enum XmlTableColumnOption {
3147+ /// A named column with a type, optional path, and default value.
3148+ NamedInfo {
3149+ /// The type of the column to be extracted.
3150+ r#type : DataType ,
3151+ /// The path to the column to be extracted. If None, defaults to the column name.
3152+ path : Option < Expr > ,
3153+ /// Default value if path does not match
3154+ default : Option < Expr > ,
3155+ /// Whether the column is nullable (NULL=true, NOT NULL=false)
3156+ nullable : bool ,
3157+ } ,
3158+ /// The FOR ORDINALITY marker
3159+ ForOrdinality ,
3160+ }
3161+
3162+ /// A single column definition in XMLTABLE
3163+ ///
3164+ /// ```sql
3165+ /// COLUMNS
3166+ /// id int PATH '@id',
3167+ /// ordinality FOR ORDINALITY,
3168+ /// "COUNTRY_NAME" text,
3169+ /// country_id text PATH 'COUNTRY_ID',
3170+ /// size_sq_km float PATH 'SIZE[@unit = "sq_km"]',
3171+ /// size_other text PATH 'concat(SIZE[@unit!="sq_km"], " ", SIZE[@unit!="sq_km"]/@unit)',
3172+ /// premier_name text PATH 'PREMIER_NAME' DEFAULT 'not specified'
3173+ /// ```
3174+ #[ derive( Debug , Clone , PartialEq , PartialOrd , Eq , Ord , Hash ) ]
3175+ #[ cfg_attr( feature = "visitor" , derive( Visit , VisitMut ) ) ]
3176+ #[ cfg_attr( feature = "serde" , derive( Serialize , Deserialize ) ) ]
3177+ pub struct XmlTableColumn {
3178+ /// The name of the column.
3179+ pub name : Ident ,
3180+ /// Column options: type/path/default or FOR ORDINALITY
3181+ pub option : XmlTableColumnOption ,
3182+ }
3183+
3184+ impl fmt:: Display for XmlTableColumn {
3185+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
3186+ write ! ( f, "{}" , self . name) ?;
3187+ match & self . option {
3188+ XmlTableColumnOption :: NamedInfo {
3189+ r#type,
3190+ path,
3191+ default,
3192+ nullable,
3193+ } => {
3194+ write ! ( f, " {}" , r#type) ?;
3195+ if let Some ( p) = path {
3196+ write ! ( f, " PATH {}" , p) ?;
3197+ }
3198+ if let Some ( d) = default {
3199+ write ! ( f, " DEFAULT {}" , d) ?;
3200+ }
3201+ if !* nullable {
3202+ write ! ( f, " NOT NULL" ) ?;
3203+ }
3204+ Ok ( ( ) )
3205+ }
3206+ XmlTableColumnOption :: ForOrdinality => {
3207+ write ! ( f, " FOR ORDINALITY" )
3208+ }
3209+ }
3210+ }
3211+ }
3212+
3213+ /// Argument passed in the XMLTABLE PASSING clause
3214+ #[ derive( Debug , Clone , PartialEq , PartialOrd , Eq , Ord , Hash ) ]
3215+ #[ cfg_attr( feature = "visitor" , derive( Visit , VisitMut ) ) ]
3216+ #[ cfg_attr( feature = "serde" , derive( Serialize , Deserialize ) ) ]
3217+ pub struct XmlPassingArgument {
3218+ pub expr : Expr ,
3219+ pub alias : Option < Ident > ,
3220+ pub by_value : bool , // True if BY VALUE is specified
3221+ }
3222+
3223+ impl fmt:: Display for XmlPassingArgument {
3224+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
3225+ if self . by_value {
3226+ write ! ( f, "BY VALUE " ) ?;
3227+ }
3228+ write ! ( f, "{}" , self . expr) ?;
3229+ if let Some ( alias) = & self . alias {
3230+ write ! ( f, " AS {}" , alias) ?;
3231+ }
3232+ Ok ( ( ) )
3233+ }
3234+ }
3235+
3236+ /// The PASSING clause for XMLTABLE
3237+ #[ derive( Debug , Clone , PartialEq , PartialOrd , Eq , Ord , Hash ) ]
3238+ #[ cfg_attr( feature = "visitor" , derive( Visit , VisitMut ) ) ]
3239+ #[ cfg_attr( feature = "serde" , derive( Serialize , Deserialize ) ) ]
3240+ pub struct XmlPassingClause {
3241+ pub arguments : Vec < XmlPassingArgument > ,
3242+ }
3243+
3244+ impl fmt:: Display for XmlPassingClause {
3245+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
3246+ if !self . arguments . is_empty ( ) {
3247+ write ! ( f, " PASSING {}" , display_comma_separated( & self . arguments) ) ?;
3248+ }
3249+ Ok ( ( ) )
3250+ }
3251+ }
3252+
3253+ /// Represents a single XML namespace definition in the XMLNAMESPACES clause.
3254+ ///
3255+ /// `namespace_uri AS namespace_name`
3256+ #[ derive( Debug , Clone , PartialEq , PartialOrd , Eq , Ord , Hash ) ]
3257+ #[ cfg_attr( feature = "visitor" , derive( Visit , VisitMut ) ) ]
3258+ #[ cfg_attr( feature = "serde" , derive( Serialize , Deserialize ) ) ]
3259+ pub struct XmlNamespaceDefinition {
3260+ /// The namespace URI (a text expression).
3261+ pub uri : Expr ,
3262+ /// The alias for the namespace (a simple identifier).
3263+ pub name : Ident ,
3264+ }
3265+
3266+ impl fmt:: Display for XmlNamespaceDefinition {
3267+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
3268+ write ! ( f, "{} AS {}" , self . uri, self . name)
3269+ }
3270+ }
0 commit comments