1use crate::{Expr, LogicalPlan, SortExpr, Volatility};
19use std::cmp::Ordering;
20use std::collections::HashMap;
21use std::sync::Arc;
22use std::{
23 fmt::{self, Display},
24 hash::{Hash, Hasher},
25};
26
27#[cfg(not(feature = "sql"))]
28use crate::expr::Ident;
29use crate::expr::Sort;
30use arrow::datatypes::DataType;
31use datafusion_common::tree_node::{Transformed, TreeNodeContainer, TreeNodeRecursion};
32use datafusion_common::{
33 Constraints, DFSchemaRef, Result, SchemaReference, TableReference,
34};
35#[cfg(feature = "sql")]
36use sqlparser::ast::Ident;
37
38#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Hash)]
40pub enum DdlStatement {
41 CreateExternalTable(CreateExternalTable),
43 CreateMemoryTable(CreateMemoryTable),
45 CreateView(CreateView),
47 CreateCatalogSchema(CreateCatalogSchema),
49 CreateCatalog(CreateCatalog),
51 CreateIndex(CreateIndex),
53 DropTable(DropTable),
55 DropView(DropView),
57 DropCatalogSchema(DropCatalogSchema),
59 CreateFunction(CreateFunction),
61 DropFunction(DropFunction),
63}
64
65impl DdlStatement {
66 pub fn schema(&self) -> &DFSchemaRef {
68 match self {
69 DdlStatement::CreateExternalTable(CreateExternalTable { schema, .. }) => {
70 schema
71 }
72 DdlStatement::CreateMemoryTable(CreateMemoryTable { input, .. })
73 | DdlStatement::CreateView(CreateView { input, .. }) => input.schema(),
74 DdlStatement::CreateCatalogSchema(CreateCatalogSchema { schema, .. }) => {
75 schema
76 }
77 DdlStatement::CreateCatalog(CreateCatalog { schema, .. }) => schema,
78 DdlStatement::CreateIndex(CreateIndex { schema, .. }) => schema,
79 DdlStatement::DropTable(DropTable { schema, .. }) => schema,
80 DdlStatement::DropView(DropView { schema, .. }) => schema,
81 DdlStatement::DropCatalogSchema(DropCatalogSchema { schema, .. }) => schema,
82 DdlStatement::CreateFunction(CreateFunction { schema, .. }) => schema,
83 DdlStatement::DropFunction(DropFunction { schema, .. }) => schema,
84 }
85 }
86
87 pub fn name(&self) -> &str {
90 match self {
91 DdlStatement::CreateExternalTable(_) => "CreateExternalTable",
92 DdlStatement::CreateMemoryTable(_) => "CreateMemoryTable",
93 DdlStatement::CreateView(_) => "CreateView",
94 DdlStatement::CreateCatalogSchema(_) => "CreateCatalogSchema",
95 DdlStatement::CreateCatalog(_) => "CreateCatalog",
96 DdlStatement::CreateIndex(_) => "CreateIndex",
97 DdlStatement::DropTable(_) => "DropTable",
98 DdlStatement::DropView(_) => "DropView",
99 DdlStatement::DropCatalogSchema(_) => "DropCatalogSchema",
100 DdlStatement::CreateFunction(_) => "CreateFunction",
101 DdlStatement::DropFunction(_) => "DropFunction",
102 }
103 }
104
105 pub fn inputs(&self) -> Vec<&LogicalPlan> {
107 match self {
108 DdlStatement::CreateExternalTable(_) => vec![],
109 DdlStatement::CreateCatalogSchema(_) => vec![],
110 DdlStatement::CreateCatalog(_) => vec![],
111 DdlStatement::CreateMemoryTable(CreateMemoryTable { input, .. }) => {
112 vec![input]
113 }
114 DdlStatement::CreateView(CreateView { input, .. }) => vec![input],
115 DdlStatement::CreateIndex(_) => vec![],
116 DdlStatement::DropTable(_) => vec![],
117 DdlStatement::DropView(_) => vec![],
118 DdlStatement::DropCatalogSchema(_) => vec![],
119 DdlStatement::CreateFunction(_) => vec![],
120 DdlStatement::DropFunction(_) => vec![],
121 }
122 }
123
124 pub fn display(&self) -> impl Display + '_ {
130 struct Wrapper<'a>(&'a DdlStatement);
131 impl Display for Wrapper<'_> {
132 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
133 match self.0 {
134 DdlStatement::CreateExternalTable(CreateExternalTable {
135 ref name,
136 constraints,
137 ..
138 }) => {
139 if constraints.is_empty() {
140 write!(f, "CreateExternalTable: {name:?}")
141 } else {
142 write!(f, "CreateExternalTable: {name:?} {constraints}")
143 }
144 }
145 DdlStatement::CreateMemoryTable(CreateMemoryTable {
146 name,
147 constraints,
148 ..
149 }) => {
150 if constraints.is_empty() {
151 write!(f, "CreateMemoryTable: {name:?}")
152 } else {
153 write!(f, "CreateMemoryTable: {name:?} {constraints}")
154 }
155 }
156 DdlStatement::CreateView(CreateView { name, .. }) => {
157 write!(f, "CreateView: {name:?}")
158 }
159 DdlStatement::CreateCatalogSchema(CreateCatalogSchema {
160 schema_name,
161 ..
162 }) => {
163 write!(f, "CreateCatalogSchema: {schema_name:?}")
164 }
165 DdlStatement::CreateCatalog(CreateCatalog {
166 catalog_name, ..
167 }) => {
168 write!(f, "CreateCatalog: {catalog_name:?}")
169 }
170 DdlStatement::CreateIndex(CreateIndex { name, .. }) => {
171 write!(f, "CreateIndex: {name:?}")
172 }
173 DdlStatement::DropTable(DropTable {
174 name, if_exists, ..
175 }) => {
176 write!(f, "DropTable: {name:?} if not exist:={if_exists}")
177 }
178 DdlStatement::DropView(DropView {
179 name, if_exists, ..
180 }) => {
181 write!(f, "DropView: {name:?} if not exist:={if_exists}")
182 }
183 DdlStatement::DropCatalogSchema(DropCatalogSchema {
184 name,
185 if_exists,
186 cascade,
187 ..
188 }) => {
189 write!(f, "DropCatalogSchema: {name:?} if not exist:={if_exists} cascade:={cascade}")
190 }
191 DdlStatement::CreateFunction(CreateFunction { name, .. }) => {
192 write!(f, "CreateFunction: name {name:?}")
193 }
194 DdlStatement::DropFunction(DropFunction { name, .. }) => {
195 write!(f, "DropFunction: name {name:?}")
196 }
197 }
198 }
199 }
200 Wrapper(self)
201 }
202}
203
204#[derive(Debug, Clone, PartialEq, Eq)]
206pub struct CreateExternalTable {
207 pub schema: DFSchemaRef,
209 pub name: TableReference,
211 pub location: String,
213 pub file_type: String,
215 pub table_partition_cols: Vec<String>,
217 pub if_not_exists: bool,
219 pub or_replace: bool,
221 pub temporary: bool,
223 pub definition: Option<String>,
225 pub order_exprs: Vec<Vec<Sort>>,
227 pub unbounded: bool,
229 pub options: HashMap<String, String>,
231 pub constraints: Constraints,
233 pub column_defaults: HashMap<String, Expr>,
235}
236
237impl Hash for CreateExternalTable {
239 fn hash<H: Hasher>(&self, state: &mut H) {
240 self.schema.hash(state);
241 self.name.hash(state);
242 self.location.hash(state);
243 self.file_type.hash(state);
244 self.table_partition_cols.hash(state);
245 self.if_not_exists.hash(state);
246 self.definition.hash(state);
247 self.order_exprs.hash(state);
248 self.unbounded.hash(state);
249 self.options.len().hash(state); }
251}
252
253impl PartialOrd for CreateExternalTable {
256 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
257 #[derive(PartialEq, PartialOrd)]
258 struct ComparableCreateExternalTable<'a> {
259 pub name: &'a TableReference,
261 pub location: &'a String,
263 pub file_type: &'a String,
265 pub table_partition_cols: &'a Vec<String>,
267 pub if_not_exists: &'a bool,
269 pub definition: &'a Option<String>,
271 pub order_exprs: &'a Vec<Vec<Sort>>,
273 pub unbounded: &'a bool,
275 pub constraints: &'a Constraints,
277 }
278 let comparable_self = ComparableCreateExternalTable {
279 name: &self.name,
280 location: &self.location,
281 file_type: &self.file_type,
282 table_partition_cols: &self.table_partition_cols,
283 if_not_exists: &self.if_not_exists,
284 definition: &self.definition,
285 order_exprs: &self.order_exprs,
286 unbounded: &self.unbounded,
287 constraints: &self.constraints,
288 };
289 let comparable_other = ComparableCreateExternalTable {
290 name: &other.name,
291 location: &other.location,
292 file_type: &other.file_type,
293 table_partition_cols: &other.table_partition_cols,
294 if_not_exists: &other.if_not_exists,
295 definition: &other.definition,
296 order_exprs: &other.order_exprs,
297 unbounded: &other.unbounded,
298 constraints: &other.constraints,
299 };
300 comparable_self
301 .partial_cmp(&comparable_other)
302 .filter(|cmp| *cmp != Ordering::Equal || self == other)
304 }
305}
306
307#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Hash)]
309pub struct CreateMemoryTable {
310 pub name: TableReference,
312 pub constraints: Constraints,
314 pub input: Arc<LogicalPlan>,
316 pub if_not_exists: bool,
318 pub or_replace: bool,
320 pub column_defaults: Vec<(String, Expr)>,
322 pub temporary: bool,
324}
325
326#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Hash)]
328pub struct CreateView {
329 pub name: TableReference,
331 pub input: Arc<LogicalPlan>,
333 pub or_replace: bool,
335 pub definition: Option<String>,
337 pub temporary: bool,
339}
340
341#[derive(Debug, Clone, PartialEq, Eq, Hash)]
343pub struct CreateCatalog {
344 pub catalog_name: String,
346 pub if_not_exists: bool,
348 pub schema: DFSchemaRef,
350}
351
352impl PartialOrd for CreateCatalog {
354 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
355 match self.catalog_name.partial_cmp(&other.catalog_name) {
356 Some(Ordering::Equal) => self.if_not_exists.partial_cmp(&other.if_not_exists),
357 cmp => cmp,
358 }
359 .filter(|cmp| *cmp != Ordering::Equal || self == other)
361 }
362}
363
364#[derive(Debug, Clone, PartialEq, Eq, Hash)]
366pub struct CreateCatalogSchema {
367 pub schema_name: String,
369 pub if_not_exists: bool,
371 pub schema: DFSchemaRef,
373}
374
375impl PartialOrd for CreateCatalogSchema {
377 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
378 match self.schema_name.partial_cmp(&other.schema_name) {
379 Some(Ordering::Equal) => self.if_not_exists.partial_cmp(&other.if_not_exists),
380 cmp => cmp,
381 }
382 .filter(|cmp| *cmp != Ordering::Equal || self == other)
384 }
385}
386
387#[derive(Debug, Clone, PartialEq, Eq, Hash)]
389pub struct DropTable {
390 pub name: TableReference,
392 pub if_exists: bool,
394 pub schema: DFSchemaRef,
396}
397
398impl PartialOrd for DropTable {
400 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
401 match self.name.partial_cmp(&other.name) {
402 Some(Ordering::Equal) => self.if_exists.partial_cmp(&other.if_exists),
403 cmp => cmp,
404 }
405 .filter(|cmp| *cmp != Ordering::Equal || self == other)
407 }
408}
409
410#[derive(Debug, Clone, PartialEq, Eq, Hash)]
412pub struct DropView {
413 pub name: TableReference,
415 pub if_exists: bool,
417 pub schema: DFSchemaRef,
419}
420
421impl PartialOrd for DropView {
423 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
424 match self.name.partial_cmp(&other.name) {
425 Some(Ordering::Equal) => self.if_exists.partial_cmp(&other.if_exists),
426 cmp => cmp,
427 }
428 .filter(|cmp| *cmp != Ordering::Equal || self == other)
430 }
431}
432
433#[derive(Debug, Clone, PartialEq, Eq, Hash)]
435pub struct DropCatalogSchema {
436 pub name: SchemaReference,
438 pub if_exists: bool,
440 pub cascade: bool,
442 pub schema: DFSchemaRef,
444}
445
446impl PartialOrd for DropCatalogSchema {
448 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
449 match self.name.partial_cmp(&other.name) {
450 Some(Ordering::Equal) => match self.if_exists.partial_cmp(&other.if_exists) {
451 Some(Ordering::Equal) => self.cascade.partial_cmp(&other.cascade),
452 cmp => cmp,
453 },
454 cmp => cmp,
455 }
456 .filter(|cmp| *cmp != Ordering::Equal || self == other)
458 }
459}
460
461#[derive(Clone, PartialEq, Eq, Hash, Debug)]
474pub struct CreateFunction {
475 pub or_replace: bool,
476 pub temporary: bool,
477 pub name: String,
478 pub args: Option<Vec<OperateFunctionArg>>,
479 pub return_type: Option<DataType>,
480 pub params: CreateFunctionBody,
481 pub schema: DFSchemaRef,
483}
484
485impl PartialOrd for CreateFunction {
487 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
488 #[derive(PartialEq, PartialOrd)]
489 struct ComparableCreateFunction<'a> {
490 pub or_replace: &'a bool,
491 pub temporary: &'a bool,
492 pub name: &'a String,
493 pub args: &'a Option<Vec<OperateFunctionArg>>,
494 pub return_type: &'a Option<DataType>,
495 pub params: &'a CreateFunctionBody,
496 }
497 let comparable_self = ComparableCreateFunction {
498 or_replace: &self.or_replace,
499 temporary: &self.temporary,
500 name: &self.name,
501 args: &self.args,
502 return_type: &self.return_type,
503 params: &self.params,
504 };
505 let comparable_other = ComparableCreateFunction {
506 or_replace: &other.or_replace,
507 temporary: &other.temporary,
508 name: &other.name,
509 args: &other.args,
510 return_type: &other.return_type,
511 params: &other.params,
512 };
513 comparable_self
514 .partial_cmp(&comparable_other)
515 .filter(|cmp| *cmp != Ordering::Equal || self == other)
517 }
518}
519
520#[derive(Clone, PartialEq, Eq, PartialOrd, Hash, Debug)]
524pub struct OperateFunctionArg {
525 pub name: Option<Ident>,
528 pub data_type: DataType,
529 pub default_expr: Option<Expr>,
530}
531
532impl<'a> TreeNodeContainer<'a, Expr> for OperateFunctionArg {
533 fn apply_elements<F: FnMut(&'a Expr) -> Result<TreeNodeRecursion>>(
534 &'a self,
535 f: F,
536 ) -> Result<TreeNodeRecursion> {
537 self.default_expr.apply_elements(f)
538 }
539
540 fn map_elements<F: FnMut(Expr) -> Result<Transformed<Expr>>>(
541 self,
542 f: F,
543 ) -> Result<Transformed<Self>> {
544 self.default_expr.map_elements(f)?.map_data(|default_expr| {
545 Ok(Self {
546 default_expr,
547 ..self
548 })
549 })
550 }
551}
552
553#[derive(Clone, PartialEq, Eq, PartialOrd, Hash, Debug)]
557pub struct CreateFunctionBody {
558 pub language: Option<Ident>,
560 pub behavior: Option<Volatility>,
562 pub function_body: Option<Expr>,
564}
565
566impl<'a> TreeNodeContainer<'a, Expr> for CreateFunctionBody {
567 fn apply_elements<F: FnMut(&'a Expr) -> Result<TreeNodeRecursion>>(
568 &'a self,
569 f: F,
570 ) -> Result<TreeNodeRecursion> {
571 self.function_body.apply_elements(f)
572 }
573
574 fn map_elements<F: FnMut(Expr) -> Result<Transformed<Expr>>>(
575 self,
576 f: F,
577 ) -> Result<Transformed<Self>> {
578 self.function_body
579 .map_elements(f)?
580 .map_data(|function_body| {
581 Ok(Self {
582 function_body,
583 ..self
584 })
585 })
586 }
587}
588
589#[derive(Clone, PartialEq, Eq, Hash, Debug)]
590pub struct DropFunction {
591 pub name: String,
592 pub if_exists: bool,
593 pub schema: DFSchemaRef,
594}
595
596impl PartialOrd for DropFunction {
597 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
598 match self.name.partial_cmp(&other.name) {
599 Some(Ordering::Equal) => self.if_exists.partial_cmp(&other.if_exists),
600 cmp => cmp,
601 }
602 .filter(|cmp| *cmp != Ordering::Equal || self == other)
604 }
605}
606
607#[derive(Clone, PartialEq, Eq, Hash, Debug)]
608pub struct CreateIndex {
609 pub name: Option<String>,
610 pub table: TableReference,
611 pub using: Option<String>,
612 pub columns: Vec<SortExpr>,
613 pub unique: bool,
614 pub if_not_exists: bool,
615 pub schema: DFSchemaRef,
616}
617
618impl PartialOrd for CreateIndex {
620 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
621 #[derive(PartialEq, PartialOrd)]
622 struct ComparableCreateIndex<'a> {
623 pub name: &'a Option<String>,
624 pub table: &'a TableReference,
625 pub using: &'a Option<String>,
626 pub columns: &'a Vec<SortExpr>,
627 pub unique: &'a bool,
628 pub if_not_exists: &'a bool,
629 }
630 let comparable_self = ComparableCreateIndex {
631 name: &self.name,
632 table: &self.table,
633 using: &self.using,
634 columns: &self.columns,
635 unique: &self.unique,
636 if_not_exists: &self.if_not_exists,
637 };
638 let comparable_other = ComparableCreateIndex {
639 name: &other.name,
640 table: &other.table,
641 using: &other.using,
642 columns: &other.columns,
643 unique: &other.unique,
644 if_not_exists: &other.if_not_exists,
645 };
646 comparable_self
647 .partial_cmp(&comparable_other)
648 .filter(|cmp| *cmp != Ordering::Equal || self == other)
650 }
651}
652
653#[cfg(test)]
654mod test {
655 use crate::{CreateCatalog, DdlStatement, DropView};
656 use datafusion_common::{DFSchema, DFSchemaRef, TableReference};
657 use std::cmp::Ordering;
658
659 #[test]
660 fn test_partial_ord() {
661 let catalog = DdlStatement::CreateCatalog(CreateCatalog {
662 catalog_name: "name".to_string(),
663 if_not_exists: false,
664 schema: DFSchemaRef::new(DFSchema::empty()),
665 });
666 let catalog_2 = DdlStatement::CreateCatalog(CreateCatalog {
667 catalog_name: "name".to_string(),
668 if_not_exists: true,
669 schema: DFSchemaRef::new(DFSchema::empty()),
670 });
671
672 assert_eq!(catalog.partial_cmp(&catalog_2), Some(Ordering::Less));
673
674 let drop_view = DdlStatement::DropView(DropView {
675 name: TableReference::from("table"),
676 if_exists: false,
677 schema: DFSchemaRef::new(DFSchema::empty()),
678 });
679
680 assert_eq!(drop_view.partial_cmp(&catalog), Some(Ordering::Greater));
681 }
682}