|
11 | 11 | // limitations under the License.
|
12 | 12 |
|
13 | 13 | #[cfg(not(feature = "std"))]
|
14 |
| -use alloc::{boxed::Box, vec::Vec}; |
| 14 | +use alloc::{boxed::Box, string::String, vec::Vec}; |
15 | 15 |
|
| 16 | +use core::fmt::{self, Display}; |
16 | 17 | #[cfg(feature = "serde")]
|
17 | 18 | use serde::{Deserialize, Serialize};
|
18 | 19 | #[cfg(feature = "visitor")]
|
19 | 20 | use sqlparser_derive::{Visit, VisitMut};
|
20 | 21 |
|
| 22 | +pub use super::ddl::{ColumnDef, TableConstraint}; |
| 23 | + |
21 | 24 | use super::{
|
22 |
| - Expr, FromTable, Ident, InsertAliases, MysqlInsertPriority, ObjectName, OnInsert, OrderByExpr, |
23 |
| - Query, SelectItem, SqliteOnConflict, TableWithJoins, |
| 25 | + display_comma_separated, display_separated, Expr, FileFormat, FromTable, HiveDistributionStyle, |
| 26 | + HiveFormat, HiveIOFormat, HiveRowFormat, Ident, InsertAliases, MysqlInsertPriority, ObjectName, |
| 27 | + OnCommit, OnInsert, OrderByExpr, Query, SelectItem, SqlOption, SqliteOnConflict, |
| 28 | + TableWithJoins, |
24 | 29 | };
|
25 | 30 |
|
| 31 | +/// CREATE INDEX statement. |
| 32 | +#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] |
| 33 | +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] |
| 34 | +#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] |
| 35 | +pub struct CreateIndex { |
| 36 | + /// index name |
| 37 | + pub name: Option<ObjectName>, |
| 38 | + #[cfg_attr(feature = "visitor", visit(with = "visit_relation"))] |
| 39 | + pub table_name: ObjectName, |
| 40 | + pub using: Option<Ident>, |
| 41 | + pub columns: Vec<OrderByExpr>, |
| 42 | + pub unique: bool, |
| 43 | + pub concurrently: bool, |
| 44 | + pub if_not_exists: bool, |
| 45 | + pub include: Vec<Ident>, |
| 46 | + pub nulls_distinct: Option<bool>, |
| 47 | + pub predicate: Option<Expr>, |
| 48 | +} |
| 49 | +/// CREATE TABLE statement. |
| 50 | +#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] |
| 51 | +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] |
| 52 | +#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] |
| 53 | +pub struct CreateTable { |
| 54 | + pub or_replace: bool, |
| 55 | + pub temporary: bool, |
| 56 | + pub external: bool, |
| 57 | + pub global: Option<bool>, |
| 58 | + pub if_not_exists: bool, |
| 59 | + pub transient: bool, |
| 60 | + /// Table name |
| 61 | + #[cfg_attr(feature = "visitor", visit(with = "visit_relation"))] |
| 62 | + pub name: ObjectName, |
| 63 | + /// Optional schema |
| 64 | + pub columns: Vec<ColumnDef>, |
| 65 | + pub constraints: Vec<TableConstraint>, |
| 66 | + pub hive_distribution: HiveDistributionStyle, |
| 67 | + pub hive_formats: Option<HiveFormat>, |
| 68 | + pub table_properties: Vec<SqlOption>, |
| 69 | + pub with_options: Vec<SqlOption>, |
| 70 | + pub file_format: Option<FileFormat>, |
| 71 | + pub location: Option<String>, |
| 72 | + pub query: Option<Box<Query>>, |
| 73 | + pub without_rowid: bool, |
| 74 | + pub like: Option<ObjectName>, |
| 75 | + pub clone: Option<ObjectName>, |
| 76 | + pub engine: Option<String>, |
| 77 | + pub comment: Option<String>, |
| 78 | + pub auto_increment_offset: Option<u32>, |
| 79 | + pub default_charset: Option<String>, |
| 80 | + pub collation: Option<String>, |
| 81 | + pub on_commit: Option<OnCommit>, |
| 82 | + /// ClickHouse "ON CLUSTER" clause: |
| 83 | + /// <https://clickhouse.com/docs/en/sql-reference/distributed-ddl/> |
| 84 | + pub on_cluster: Option<String>, |
| 85 | + /// ClickHouse "ORDER BY " clause. Note that omitted ORDER BY is different |
| 86 | + /// than empty (represented as ()), the latter meaning "no sorting". |
| 87 | + /// <https://clickhouse.com/docs/en/sql-reference/statements/create/table/> |
| 88 | + pub order_by: Option<Vec<Ident>>, |
| 89 | + /// BigQuery: A partition expression for the table. |
| 90 | + /// <https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language#partition_expression> |
| 91 | + pub partition_by: Option<Box<Expr>>, |
| 92 | + /// BigQuery: Table clustering column list. |
| 93 | + /// <https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language#table_option_list> |
| 94 | + pub cluster_by: Option<Vec<Ident>>, |
| 95 | + /// BigQuery: Table options list. |
| 96 | + /// <https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language#table_option_list> |
| 97 | + pub options: Option<Vec<SqlOption>>, |
| 98 | + /// SQLite "STRICT" clause. |
| 99 | + /// if the "STRICT" table-option keyword is added to the end, after the closing ")", |
| 100 | + /// then strict typing rules apply to that table. |
| 101 | + pub strict: bool, |
| 102 | +} |
| 103 | + |
| 104 | +impl Display for CreateTable { |
| 105 | + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| 106 | + // We want to allow the following options |
| 107 | + // Empty column list, allowed by PostgreSQL: |
| 108 | + // `CREATE TABLE t ()` |
| 109 | + // No columns provided for CREATE TABLE AS: |
| 110 | + // `CREATE TABLE t AS SELECT a from t2` |
| 111 | + // Columns provided for CREATE TABLE AS: |
| 112 | + // `CREATE TABLE t (a INT) AS SELECT a from t2` |
| 113 | + write!( |
| 114 | + f, |
| 115 | + "CREATE {or_replace}{external}{global}{temporary}{transient}TABLE {if_not_exists}{name}", |
| 116 | + or_replace = if self.or_replace { "OR REPLACE " } else { "" }, |
| 117 | + external = if self.external { "EXTERNAL " } else { "" }, |
| 118 | + global = self.global |
| 119 | + .map(|global| { |
| 120 | + if global { |
| 121 | + "GLOBAL " |
| 122 | + } else { |
| 123 | + "LOCAL " |
| 124 | + } |
| 125 | + }) |
| 126 | + .unwrap_or(""), |
| 127 | + if_not_exists = if self.if_not_exists { "IF NOT EXISTS " } else { "" }, |
| 128 | + temporary = if self.temporary { "TEMPORARY " } else { "" }, |
| 129 | + transient = if self.transient { "TRANSIENT " } else { "" }, |
| 130 | + name = self.name, |
| 131 | + )?; |
| 132 | + if let Some(on_cluster) = &self.on_cluster { |
| 133 | + write!( |
| 134 | + f, |
| 135 | + " ON CLUSTER {}", |
| 136 | + on_cluster.replace('{', "'{").replace('}', "}'") |
| 137 | + )?; |
| 138 | + } |
| 139 | + if !self.columns.is_empty() || !self.constraints.is_empty() { |
| 140 | + write!(f, " ({}", display_comma_separated(&self.columns))?; |
| 141 | + if !self.columns.is_empty() && !self.constraints.is_empty() { |
| 142 | + write!(f, ", ")?; |
| 143 | + } |
| 144 | + write!(f, "{})", display_comma_separated(&self.constraints))?; |
| 145 | + } else if self.query.is_none() && self.like.is_none() && self.clone.is_none() { |
| 146 | + // PostgreSQL allows `CREATE TABLE t ();`, but requires empty parens |
| 147 | + write!(f, " ()")?; |
| 148 | + } |
| 149 | + // Only for SQLite |
| 150 | + if self.without_rowid { |
| 151 | + write!(f, " WITHOUT ROWID")?; |
| 152 | + } |
| 153 | + |
| 154 | + // Only for Hive |
| 155 | + if let Some(l) = &self.like { |
| 156 | + write!(f, " LIKE {l}")?; |
| 157 | + } |
| 158 | + |
| 159 | + if let Some(c) = &self.clone { |
| 160 | + write!(f, " CLONE {c}")?; |
| 161 | + } |
| 162 | + |
| 163 | + match &self.hive_distribution { |
| 164 | + HiveDistributionStyle::PARTITIONED { columns } => { |
| 165 | + write!(f, " PARTITIONED BY ({})", display_comma_separated(columns))?; |
| 166 | + } |
| 167 | + HiveDistributionStyle::CLUSTERED { |
| 168 | + columns, |
| 169 | + sorted_by, |
| 170 | + num_buckets, |
| 171 | + } => { |
| 172 | + write!(f, " CLUSTERED BY ({})", display_comma_separated(columns))?; |
| 173 | + if !sorted_by.is_empty() { |
| 174 | + write!(f, " SORTED BY ({})", display_comma_separated(sorted_by))?; |
| 175 | + } |
| 176 | + if *num_buckets > 0 { |
| 177 | + write!(f, " INTO {num_buckets} BUCKETS")?; |
| 178 | + } |
| 179 | + } |
| 180 | + HiveDistributionStyle::SKEWED { |
| 181 | + columns, |
| 182 | + on, |
| 183 | + stored_as_directories, |
| 184 | + } => { |
| 185 | + write!( |
| 186 | + f, |
| 187 | + " SKEWED BY ({})) ON ({})", |
| 188 | + display_comma_separated(columns), |
| 189 | + display_comma_separated(on) |
| 190 | + )?; |
| 191 | + if *stored_as_directories { |
| 192 | + write!(f, " STORED AS DIRECTORIES")?; |
| 193 | + } |
| 194 | + } |
| 195 | + _ => (), |
| 196 | + } |
| 197 | + |
| 198 | + if let Some(HiveFormat { |
| 199 | + row_format, |
| 200 | + serde_properties, |
| 201 | + storage, |
| 202 | + location, |
| 203 | + }) = &self.hive_formats |
| 204 | + { |
| 205 | + match row_format { |
| 206 | + Some(HiveRowFormat::SERDE { class }) => write!(f, " ROW FORMAT SERDE '{class}'")?, |
| 207 | + Some(HiveRowFormat::DELIMITED { delimiters }) => { |
| 208 | + write!(f, " ROW FORMAT DELIMITED")?; |
| 209 | + if !delimiters.is_empty() { |
| 210 | + write!(f, " {}", display_separated(delimiters, " "))?; |
| 211 | + } |
| 212 | + } |
| 213 | + None => (), |
| 214 | + } |
| 215 | + match storage { |
| 216 | + Some(HiveIOFormat::IOF { |
| 217 | + input_format, |
| 218 | + output_format, |
| 219 | + }) => write!( |
| 220 | + f, |
| 221 | + " STORED AS INPUTFORMAT {input_format} OUTPUTFORMAT {output_format}" |
| 222 | + )?, |
| 223 | + Some(HiveIOFormat::FileFormat { format }) if !self.external => { |
| 224 | + write!(f, " STORED AS {format}")? |
| 225 | + } |
| 226 | + _ => (), |
| 227 | + } |
| 228 | + if let Some(serde_properties) = serde_properties.as_ref() { |
| 229 | + write!( |
| 230 | + f, |
| 231 | + " WITH SERDEPROPERTIES ({})", |
| 232 | + display_comma_separated(serde_properties) |
| 233 | + )?; |
| 234 | + } |
| 235 | + if !self.external { |
| 236 | + if let Some(loc) = location { |
| 237 | + write!(f, " LOCATION '{loc}'")?; |
| 238 | + } |
| 239 | + } |
| 240 | + } |
| 241 | + if self.external { |
| 242 | + if let Some(file_format) = self.file_format { |
| 243 | + write!(f, " STORED AS {file_format}")?; |
| 244 | + } |
| 245 | + write!(f, " LOCATION '{}'", self.location.as_ref().unwrap())?; |
| 246 | + } |
| 247 | + if !self.table_properties.is_empty() { |
| 248 | + write!( |
| 249 | + f, |
| 250 | + " TBLPROPERTIES ({})", |
| 251 | + display_comma_separated(&self.table_properties) |
| 252 | + )?; |
| 253 | + } |
| 254 | + if !self.with_options.is_empty() { |
| 255 | + write!(f, " WITH ({})", display_comma_separated(&self.with_options))?; |
| 256 | + } |
| 257 | + if let Some(engine) = &self.engine { |
| 258 | + write!(f, " ENGINE={engine}")?; |
| 259 | + } |
| 260 | + if let Some(comment) = &self.comment { |
| 261 | + write!(f, " COMMENT '{comment}'")?; |
| 262 | + } |
| 263 | + if let Some(auto_increment_offset) = self.auto_increment_offset { |
| 264 | + write!(f, " AUTO_INCREMENT {auto_increment_offset}")?; |
| 265 | + } |
| 266 | + if let Some(order_by) = &self.order_by { |
| 267 | + write!(f, " ORDER BY ({})", display_comma_separated(order_by))?; |
| 268 | + } |
| 269 | + if let Some(partition_by) = self.partition_by.as_ref() { |
| 270 | + write!(f, " PARTITION BY {partition_by}")?; |
| 271 | + } |
| 272 | + if let Some(cluster_by) = self.cluster_by.as_ref() { |
| 273 | + write!( |
| 274 | + f, |
| 275 | + " CLUSTER BY {}", |
| 276 | + display_comma_separated(cluster_by.as_slice()) |
| 277 | + )?; |
| 278 | + } |
| 279 | + if let Some(options) = self.options.as_ref() { |
| 280 | + write!( |
| 281 | + f, |
| 282 | + " OPTIONS({})", |
| 283 | + display_comma_separated(options.as_slice()) |
| 284 | + )?; |
| 285 | + } |
| 286 | + if let Some(query) = &self.query { |
| 287 | + write!(f, " AS {query}")?; |
| 288 | + } |
| 289 | + if let Some(default_charset) = &self.default_charset { |
| 290 | + write!(f, " DEFAULT CHARSET={default_charset}")?; |
| 291 | + } |
| 292 | + if let Some(collation) = &self.collation { |
| 293 | + write!(f, " COLLATE={collation}")?; |
| 294 | + } |
| 295 | + |
| 296 | + if self.on_commit.is_some() { |
| 297 | + let on_commit = match self.on_commit { |
| 298 | + Some(OnCommit::DeleteRows) => "ON COMMIT DELETE ROWS", |
| 299 | + Some(OnCommit::PreserveRows) => "ON COMMIT PRESERVE ROWS", |
| 300 | + Some(OnCommit::Drop) => "ON COMMIT DROP", |
| 301 | + None => "", |
| 302 | + }; |
| 303 | + write!(f, " {on_commit}")?; |
| 304 | + } |
| 305 | + if self.strict { |
| 306 | + write!(f, " STRICT")?; |
| 307 | + } |
| 308 | + Ok(()) |
| 309 | + } |
| 310 | +} |
| 311 | + |
26 | 312 | /// INSERT statement.
|
27 | 313 | #[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
|
28 | 314 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
|
0 commit comments