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
27use crate::expr::Sort;
28#[cfg(not(feature = "sql"))]
29use crate::sql::Ident;
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(Box<CreateExternalTable>),
45 CreateMemoryTable(CreateMemoryTable),
47 CreateView(CreateView),
49 CreateCatalogSchema(CreateCatalogSchema),
51 CreateCatalog(CreateCatalog),
53 CreateIndex(CreateIndex),
55 DropTable(DropTable),
57 DropView(DropView),
59 DropCatalogSchema(DropCatalogSchema),
61 CreateFunction(Box<CreateFunction>),
64 DropFunction(DropFunction),
66}
67
68impl DdlStatement {
69 pub fn schema(&self) -> &DFSchemaRef {
71 match self {
72 DdlStatement::CreateExternalTable(ce) => &ce.schema,
73 DdlStatement::CreateMemoryTable(CreateMemoryTable { input, .. })
74 | DdlStatement::CreateView(CreateView { input, .. }) => input.schema(),
75 DdlStatement::CreateCatalogSchema(CreateCatalogSchema { schema, .. }) => {
76 schema
77 }
78 DdlStatement::CreateCatalog(CreateCatalog { schema, .. }) => schema,
79 DdlStatement::CreateIndex(CreateIndex { schema, .. }) => schema,
80 DdlStatement::DropTable(DropTable { schema, .. }) => schema,
81 DdlStatement::DropView(DropView { schema, .. }) => schema,
82 DdlStatement::DropCatalogSchema(DropCatalogSchema { schema, .. }) => schema,
83 DdlStatement::CreateFunction(cf) => &cf.schema,
84 DdlStatement::DropFunction(DropFunction { schema, .. }) => schema,
85 }
86 }
87
88 pub fn name(&self) -> &str {
91 match self {
92 DdlStatement::CreateExternalTable(_) => "CreateExternalTable",
93 DdlStatement::CreateMemoryTable(_) => "CreateMemoryTable",
94 DdlStatement::CreateView(_) => "CreateView",
95 DdlStatement::CreateCatalogSchema(_) => "CreateCatalogSchema",
96 DdlStatement::CreateCatalog(_) => "CreateCatalog",
97 DdlStatement::CreateIndex(_) => "CreateIndex",
98 DdlStatement::DropTable(_) => "DropTable",
99 DdlStatement::DropView(_) => "DropView",
100 DdlStatement::DropCatalogSchema(_) => "DropCatalogSchema",
101 DdlStatement::CreateFunction(_) => "CreateFunction",
102 DdlStatement::DropFunction(_) => "DropFunction",
103 }
104 }
105
106 pub fn inputs(&self) -> Vec<&LogicalPlan> {
108 match self {
109 DdlStatement::CreateExternalTable(_) => vec![],
110 DdlStatement::CreateCatalogSchema(_) => vec![],
111 DdlStatement::CreateCatalog(_) => vec![],
112 DdlStatement::CreateMemoryTable(CreateMemoryTable { input, .. }) => {
113 vec![input]
114 }
115 DdlStatement::CreateView(CreateView { input, .. }) => vec![input],
116 DdlStatement::CreateIndex(_) => vec![],
117 DdlStatement::DropTable(_) => vec![],
118 DdlStatement::DropView(_) => vec![],
119 DdlStatement::DropCatalogSchema(_) => vec![],
120 DdlStatement::CreateFunction(_) => vec![],
121 DdlStatement::DropFunction(_) => vec![],
122 }
123 }
124
125 pub fn display(&self) -> impl Display + '_ {
131 struct Wrapper<'a>(&'a DdlStatement);
132 impl Display for Wrapper<'_> {
133 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
134 match self.0 {
135 DdlStatement::CreateExternalTable(ce) => {
136 let name = &ce.name;
137 let constraints = &ce.constraints;
138 if constraints.is_empty() {
139 write!(f, "CreateExternalTable: {name:?}")
140 } else {
141 write!(f, "CreateExternalTable: {name:?} {constraints}")
142 }
143 }
144 DdlStatement::CreateMemoryTable(CreateMemoryTable {
145 name,
146 constraints,
147 ..
148 }) => {
149 if constraints.is_empty() {
150 write!(f, "CreateMemoryTable: {name:?}")
151 } else {
152 write!(f, "CreateMemoryTable: {name:?} {constraints}")
153 }
154 }
155 DdlStatement::CreateView(CreateView { name, .. }) => {
156 write!(f, "CreateView: {name:?}")
157 }
158 DdlStatement::CreateCatalogSchema(CreateCatalogSchema {
159 schema_name,
160 ..
161 }) => {
162 write!(f, "CreateCatalogSchema: {schema_name:?}")
163 }
164 DdlStatement::CreateCatalog(CreateCatalog {
165 catalog_name, ..
166 }) => {
167 write!(f, "CreateCatalog: {catalog_name:?}")
168 }
169 DdlStatement::CreateIndex(CreateIndex { name, .. }) => {
170 write!(f, "CreateIndex: {name:?}")
171 }
172 DdlStatement::DropTable(DropTable {
173 name, if_exists, ..
174 }) => {
175 write!(f, "DropTable: {name:?} if not exist:={if_exists}")
176 }
177 DdlStatement::DropView(DropView {
178 name, if_exists, ..
179 }) => {
180 write!(f, "DropView: {name:?} if not exist:={if_exists}")
181 }
182 DdlStatement::DropCatalogSchema(DropCatalogSchema {
183 name,
184 if_exists,
185 cascade,
186 ..
187 }) => {
188 write!(
189 f,
190 "DropCatalogSchema: {name:?} if not exist:={if_exists} cascade:={cascade}"
191 )
192 }
193 DdlStatement::CreateFunction(cf) => {
194 let name = &cf.name;
195 write!(f, "CreateFunction: name {name:?}")
196 }
197 DdlStatement::DropFunction(DropFunction { name, .. }) => {
198 write!(f, "DropFunction: name {name:?}")
199 }
200 }
201 }
202 }
203 Wrapper(self)
204 }
205}
206
207#[derive(Debug, Clone, PartialEq, Eq)]
209pub struct CreateExternalTable {
210 pub schema: DFSchemaRef,
212 pub name: TableReference,
214 pub locations: Vec<String>,
220 pub file_type: String,
222 pub table_partition_cols: Vec<String>,
224 pub if_not_exists: bool,
226 pub or_replace: bool,
228 pub temporary: bool,
230 pub definition: Option<String>,
232 pub order_exprs: Vec<Vec<Sort>>,
234 pub unbounded: bool,
236 pub options: HashMap<String, String>,
238 pub constraints: Constraints,
240 pub column_defaults: HashMap<String, Expr>,
242}
243
244impl CreateExternalTable {
245 pub fn builder(
266 name: impl Into<TableReference>,
267 location: impl Into<String>,
268 file_type: impl Into<String>,
269 schema: DFSchemaRef,
270 ) -> CreateExternalTableBuilder {
271 CreateExternalTableBuilder {
272 name: name.into(),
273 locations: vec![location.into()],
274 file_type: file_type.into(),
275 schema,
276 table_partition_cols: vec![],
277 if_not_exists: false,
278 or_replace: false,
279 temporary: false,
280 definition: None,
281 order_exprs: vec![],
282 unbounded: false,
283 options: HashMap::new(),
284 constraints: Default::default(),
285 column_defaults: HashMap::new(),
286 }
287 }
288}
289
290#[derive(Debug, Clone)]
294pub struct CreateExternalTableBuilder {
295 name: TableReference,
296 locations: Vec<String>,
297 file_type: String,
298 schema: DFSchemaRef,
299 table_partition_cols: Vec<String>,
300 if_not_exists: bool,
301 or_replace: bool,
302 temporary: bool,
303 definition: Option<String>,
304 order_exprs: Vec<Vec<Sort>>,
305 unbounded: bool,
306 options: HashMap<String, String>,
307 constraints: Constraints,
308 column_defaults: HashMap<String, Expr>,
309}
310
311impl CreateExternalTableBuilder {
312 pub fn with_partition_cols(mut self, cols: Vec<String>) -> Self {
314 self.table_partition_cols = cols;
315 self
316 }
317
318 pub fn with_locations(mut self, locations: Vec<String>) -> Self {
324 self.locations = locations;
325 self
326 }
327
328 pub fn with_if_not_exists(mut self, if_not_exists: bool) -> Self {
330 self.if_not_exists = if_not_exists;
331 self
332 }
333
334 pub fn with_or_replace(mut self, or_replace: bool) -> Self {
336 self.or_replace = or_replace;
337 self
338 }
339
340 pub fn with_temporary(mut self, temporary: bool) -> Self {
342 self.temporary = temporary;
343 self
344 }
345
346 pub fn with_definition(mut self, definition: Option<String>) -> Self {
348 self.definition = definition;
349 self
350 }
351
352 pub fn with_order_exprs(mut self, order_exprs: Vec<Vec<Sort>>) -> Self {
354 self.order_exprs = order_exprs;
355 self
356 }
357
358 pub fn with_unbounded(mut self, unbounded: bool) -> Self {
360 self.unbounded = unbounded;
361 self
362 }
363
364 pub fn with_options(mut self, options: HashMap<String, String>) -> Self {
366 self.options = options;
367 self
368 }
369
370 pub fn with_constraints(mut self, constraints: Constraints) -> Self {
372 self.constraints = constraints;
373 self
374 }
375
376 pub fn with_column_defaults(
378 mut self,
379 column_defaults: HashMap<String, Expr>,
380 ) -> Self {
381 self.column_defaults = column_defaults;
382 self
383 }
384
385 pub fn build(self) -> CreateExternalTable {
387 CreateExternalTable {
388 schema: self.schema,
389 name: self.name,
390 locations: self.locations,
391 file_type: self.file_type,
392 table_partition_cols: self.table_partition_cols,
393 if_not_exists: self.if_not_exists,
394 or_replace: self.or_replace,
395 temporary: self.temporary,
396 definition: self.definition,
397 order_exprs: self.order_exprs,
398 unbounded: self.unbounded,
399 options: self.options,
400 constraints: self.constraints,
401 column_defaults: self.column_defaults,
402 }
403 }
404}
405
406impl Hash for CreateExternalTable {
408 fn hash<H: Hasher>(&self, state: &mut H) {
409 self.schema.hash(state);
410 self.name.hash(state);
411 self.locations.hash(state);
412 self.file_type.hash(state);
413 self.table_partition_cols.hash(state);
414 self.if_not_exists.hash(state);
415 self.definition.hash(state);
416 self.order_exprs.hash(state);
417 self.unbounded.hash(state);
418 self.options.len().hash(state); }
420}
421
422impl PartialOrd for CreateExternalTable {
425 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
426 #[derive(PartialEq, PartialOrd)]
427 struct ComparableCreateExternalTable<'a> {
428 pub name: &'a TableReference,
430 pub locations: &'a Vec<String>,
432 pub file_type: &'a String,
434 pub table_partition_cols: &'a Vec<String>,
436 pub if_not_exists: &'a bool,
438 pub definition: &'a Option<String>,
440 pub order_exprs: &'a Vec<Vec<Sort>>,
442 pub unbounded: &'a bool,
444 pub constraints: &'a Constraints,
446 }
447 let comparable_self = ComparableCreateExternalTable {
448 name: &self.name,
449 locations: &self.locations,
450 file_type: &self.file_type,
451 table_partition_cols: &self.table_partition_cols,
452 if_not_exists: &self.if_not_exists,
453 definition: &self.definition,
454 order_exprs: &self.order_exprs,
455 unbounded: &self.unbounded,
456 constraints: &self.constraints,
457 };
458 let comparable_other = ComparableCreateExternalTable {
459 name: &other.name,
460 locations: &other.locations,
461 file_type: &other.file_type,
462 table_partition_cols: &other.table_partition_cols,
463 if_not_exists: &other.if_not_exists,
464 definition: &other.definition,
465 order_exprs: &other.order_exprs,
466 unbounded: &other.unbounded,
467 constraints: &other.constraints,
468 };
469 comparable_self
470 .partial_cmp(&comparable_other)
471 .filter(|cmp| *cmp != Ordering::Equal || self == other)
473 }
474}
475
476#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Hash)]
478pub struct CreateMemoryTable {
479 pub name: TableReference,
481 pub constraints: Constraints,
483 pub input: Arc<LogicalPlan>,
485 pub if_not_exists: bool,
487 pub or_replace: bool,
489 pub column_defaults: Vec<(String, Expr)>,
491 pub temporary: bool,
493}
494
495#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Hash)]
497pub struct CreateView {
498 pub name: TableReference,
500 pub input: Arc<LogicalPlan>,
502 pub or_replace: bool,
504 pub definition: Option<String>,
506 pub temporary: bool,
508}
509
510#[derive(Debug, Clone, PartialEq, Eq, Hash)]
512pub struct CreateCatalog {
513 pub catalog_name: String,
515 pub if_not_exists: bool,
517 pub schema: DFSchemaRef,
519}
520
521impl PartialOrd for CreateCatalog {
523 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
524 match self.catalog_name.partial_cmp(&other.catalog_name) {
525 Some(Ordering::Equal) => self.if_not_exists.partial_cmp(&other.if_not_exists),
526 cmp => cmp,
527 }
528 .filter(|cmp| *cmp != Ordering::Equal || self == other)
530 }
531}
532
533#[derive(Debug, Clone, PartialEq, Eq, Hash)]
535pub struct CreateCatalogSchema {
536 pub schema_name: String,
538 pub if_not_exists: bool,
540 pub schema: DFSchemaRef,
542}
543
544impl PartialOrd for CreateCatalogSchema {
546 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
547 match self.schema_name.partial_cmp(&other.schema_name) {
548 Some(Ordering::Equal) => self.if_not_exists.partial_cmp(&other.if_not_exists),
549 cmp => cmp,
550 }
551 .filter(|cmp| *cmp != Ordering::Equal || self == other)
553 }
554}
555
556#[derive(Debug, Clone, PartialEq, Eq, Hash)]
558pub struct DropTable {
559 pub name: TableReference,
561 pub if_exists: bool,
563 pub schema: DFSchemaRef,
565}
566
567impl PartialOrd for DropTable {
569 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
570 match self.name.partial_cmp(&other.name) {
571 Some(Ordering::Equal) => self.if_exists.partial_cmp(&other.if_exists),
572 cmp => cmp,
573 }
574 .filter(|cmp| *cmp != Ordering::Equal || self == other)
576 }
577}
578
579#[derive(Debug, Clone, PartialEq, Eq, Hash)]
581pub struct DropView {
582 pub name: TableReference,
584 pub if_exists: bool,
586 pub schema: DFSchemaRef,
588}
589
590impl PartialOrd for DropView {
592 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
593 match self.name.partial_cmp(&other.name) {
594 Some(Ordering::Equal) => self.if_exists.partial_cmp(&other.if_exists),
595 cmp => cmp,
596 }
597 .filter(|cmp| *cmp != Ordering::Equal || self == other)
599 }
600}
601
602#[derive(Debug, Clone, PartialEq, Eq, Hash)]
604pub struct DropCatalogSchema {
605 pub name: SchemaReference,
607 pub if_exists: bool,
609 pub cascade: bool,
611 pub schema: DFSchemaRef,
613}
614
615impl PartialOrd for DropCatalogSchema {
617 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
618 match self.name.partial_cmp(&other.name) {
619 Some(Ordering::Equal) => match self.if_exists.partial_cmp(&other.if_exists) {
620 Some(Ordering::Equal) => self.cascade.partial_cmp(&other.cascade),
621 cmp => cmp,
622 },
623 cmp => cmp,
624 }
625 .filter(|cmp| *cmp != Ordering::Equal || self == other)
627 }
628}
629
630#[derive(Clone, PartialEq, Eq, Hash, Debug)]
643pub struct CreateFunction {
644 pub or_replace: bool,
645 pub temporary: bool,
646 pub name: String,
647 pub args: Option<Vec<OperateFunctionArg>>,
648 pub return_type: Option<DataType>,
649 pub params: CreateFunctionBody,
650 pub schema: DFSchemaRef,
652}
653
654impl PartialOrd for CreateFunction {
656 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
657 #[derive(PartialEq, PartialOrd)]
658 struct ComparableCreateFunction<'a> {
659 pub or_replace: &'a bool,
660 pub temporary: &'a bool,
661 pub name: &'a String,
662 pub args: &'a Option<Vec<OperateFunctionArg>>,
663 pub return_type: &'a Option<DataType>,
664 pub params: &'a CreateFunctionBody,
665 }
666 let comparable_self = ComparableCreateFunction {
667 or_replace: &self.or_replace,
668 temporary: &self.temporary,
669 name: &self.name,
670 args: &self.args,
671 return_type: &self.return_type,
672 params: &self.params,
673 };
674 let comparable_other = ComparableCreateFunction {
675 or_replace: &other.or_replace,
676 temporary: &other.temporary,
677 name: &other.name,
678 args: &other.args,
679 return_type: &other.return_type,
680 params: &other.params,
681 };
682 comparable_self
683 .partial_cmp(&comparable_other)
684 .filter(|cmp| *cmp != Ordering::Equal || self == other)
686 }
687}
688
689#[derive(Clone, PartialEq, Eq, PartialOrd, Hash, Debug)]
693pub struct OperateFunctionArg {
694 pub name: Option<Ident>,
697 pub data_type: DataType,
698 pub default_expr: Option<Expr>,
699}
700
701impl<'a> TreeNodeContainer<'a, Expr> for OperateFunctionArg {
702 fn apply_elements<F: FnMut(&'a Expr) -> Result<TreeNodeRecursion>>(
703 &'a self,
704 f: F,
705 ) -> Result<TreeNodeRecursion> {
706 self.default_expr.apply_elements(f)
707 }
708
709 fn map_elements<F: FnMut(Expr) -> Result<Transformed<Expr>>>(
710 self,
711 f: F,
712 ) -> Result<Transformed<Self>> {
713 self.default_expr.map_elements(f)?.map_data(|default_expr| {
714 Ok(Self {
715 default_expr,
716 ..self
717 })
718 })
719 }
720}
721
722#[derive(Clone, PartialEq, Eq, PartialOrd, Hash, Debug)]
726pub struct CreateFunctionBody {
727 pub language: Option<Ident>,
729 pub behavior: Option<Volatility>,
731 pub function_body: Option<Expr>,
733}
734
735impl<'a> TreeNodeContainer<'a, Expr> for CreateFunctionBody {
736 fn apply_elements<F: FnMut(&'a Expr) -> Result<TreeNodeRecursion>>(
737 &'a self,
738 f: F,
739 ) -> Result<TreeNodeRecursion> {
740 self.function_body.apply_elements(f)
741 }
742
743 fn map_elements<F: FnMut(Expr) -> Result<Transformed<Expr>>>(
744 self,
745 f: F,
746 ) -> Result<Transformed<Self>> {
747 self.function_body
748 .map_elements(f)?
749 .map_data(|function_body| {
750 Ok(Self {
751 function_body,
752 ..self
753 })
754 })
755 }
756}
757
758#[derive(Clone, PartialEq, Eq, Hash, Debug)]
759pub struct DropFunction {
760 pub name: String,
761 pub if_exists: bool,
762 pub schema: DFSchemaRef,
763}
764
765impl PartialOrd for DropFunction {
766 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
767 match self.name.partial_cmp(&other.name) {
768 Some(Ordering::Equal) => self.if_exists.partial_cmp(&other.if_exists),
769 cmp => cmp,
770 }
771 .filter(|cmp| *cmp != Ordering::Equal || self == other)
773 }
774}
775
776#[derive(Clone, PartialEq, Eq, Hash, Debug)]
777pub struct CreateIndex {
778 pub name: Option<String>,
779 pub table: TableReference,
780 pub using: Option<String>,
781 pub columns: Vec<SortExpr>,
782 pub unique: bool,
783 pub if_not_exists: bool,
784 pub schema: DFSchemaRef,
785}
786
787impl PartialOrd for CreateIndex {
789 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
790 #[derive(PartialEq, PartialOrd)]
791 struct ComparableCreateIndex<'a> {
792 pub name: &'a Option<String>,
793 pub table: &'a TableReference,
794 pub using: &'a Option<String>,
795 pub columns: &'a Vec<SortExpr>,
796 pub unique: &'a bool,
797 pub if_not_exists: &'a bool,
798 }
799 let comparable_self = ComparableCreateIndex {
800 name: &self.name,
801 table: &self.table,
802 using: &self.using,
803 columns: &self.columns,
804 unique: &self.unique,
805 if_not_exists: &self.if_not_exists,
806 };
807 let comparable_other = ComparableCreateIndex {
808 name: &other.name,
809 table: &other.table,
810 using: &other.using,
811 columns: &other.columns,
812 unique: &other.unique,
813 if_not_exists: &other.if_not_exists,
814 };
815 comparable_self
816 .partial_cmp(&comparable_other)
817 .filter(|cmp| *cmp != Ordering::Equal || self == other)
819 }
820}
821
822#[cfg(test)]
823mod test {
824 use crate::{CreateCatalog, DdlStatement, DropView};
825 use datafusion_common::{DFSchema, DFSchemaRef, TableReference};
826 use std::cmp::Ordering;
827
828 #[test]
829 fn test_partial_ord() {
830 let catalog = DdlStatement::CreateCatalog(CreateCatalog {
831 catalog_name: "name".to_string(),
832 if_not_exists: false,
833 schema: DFSchemaRef::new(DFSchema::empty()),
834 });
835 let catalog_2 = DdlStatement::CreateCatalog(CreateCatalog {
836 catalog_name: "name".to_string(),
837 if_not_exists: true,
838 schema: DFSchemaRef::new(DFSchema::empty()),
839 });
840
841 assert_eq!(catalog.partial_cmp(&catalog_2), Some(Ordering::Less));
842
843 let drop_view = DdlStatement::DropView(DropView {
844 name: TableReference::from("table"),
845 if_exists: false,
846 schema: DFSchemaRef::new(DFSchema::empty()),
847 });
848
849 assert_eq!(drop_view.partial_cmp(&catalog), Some(Ordering::Greater));
850 }
851}