1pub use feldera_ir::SourcePosition;
2use serde::{Deserialize, Deserializer, Serialize, Serializer};
3use std::cmp::Ordering;
4use std::collections::BTreeMap;
5use std::fmt::Display;
6use std::hash::{Hash, Hasher};
7use utoipa::ToSchema;
8use utoipa::openapi::{ObjectBuilder, RefOr, Schema, SchemaType};
9
10#[cfg(feature = "testing")]
11use proptest::{collection::vec, prelude::any};
12
13pub fn canonical_identifier(id: &str) -> String {
21 if id.starts_with('"') && id.ends_with('"') && id.len() >= 2 {
22 id[1..id.len() - 1].to_string()
23 } else {
24 id.to_lowercase()
25 }
26}
27
28#[derive(Serialize, Deserialize, ToSchema, Debug, Clone)]
33#[cfg_attr(feature = "testing", derive(proptest_derive::Arbitrary))]
34pub struct SqlIdentifier {
35 #[cfg_attr(feature = "testing", proptest(regex = "relation1|relation2|relation3"))]
36 name: String,
37 pub case_sensitive: bool,
38}
39
40impl SqlIdentifier {
41 pub fn new<S: AsRef<str>>(name: S, case_sensitive: bool) -> Self {
42 Self {
43 name: name.as_ref().to_string(),
44 case_sensitive,
45 }
46 }
47
48 pub fn name(&self) -> String {
58 if self.case_sensitive {
59 self.name.clone()
60 } else {
61 self.name.to_lowercase()
62 }
63 }
64
65 pub fn sql_name(&self) -> String {
76 if self.case_sensitive {
77 format!("\"{}\"", self.name)
78 } else {
79 self.name.clone()
80 }
81 }
82}
83
84impl Hash for SqlIdentifier {
85 fn hash<H: Hasher>(&self, state: &mut H) {
86 self.name().hash(state);
87 }
88}
89
90impl PartialEq for SqlIdentifier {
91 fn eq(&self, other: &Self) -> bool {
92 match (self.case_sensitive, other.case_sensitive) {
93 (true, true) => self.name == other.name,
94 (false, false) => self.name.to_lowercase() == other.name.to_lowercase(),
95 (true, false) => self.name == other.name,
96 (false, true) => self.name == other.name,
97 }
98 }
99}
100
101impl Ord for SqlIdentifier {
102 fn cmp(&self, other: &Self) -> Ordering {
103 self.name().cmp(&other.name())
104 }
105}
106
107impl PartialOrd for SqlIdentifier {
108 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
109 Some(self.cmp(other))
110 }
111}
112
113impl<S: AsRef<str>> PartialEq<S> for SqlIdentifier {
114 fn eq(&self, other: &S) -> bool {
115 self == &SqlIdentifier::from(other.as_ref())
116 }
117}
118
119impl Eq for SqlIdentifier {}
120
121impl<S: AsRef<str>> From<S> for SqlIdentifier {
122 fn from(name: S) -> Self {
123 if name.as_ref().starts_with('"')
124 && name.as_ref().ends_with('"')
125 && name.as_ref().len() >= 2
126 {
127 Self {
128 name: name.as_ref()[1..name.as_ref().len() - 1].to_string(),
129 case_sensitive: true,
130 }
131 } else {
132 Self::new(name, false)
133 }
134 }
135}
136
137impl From<SqlIdentifier> for String {
138 fn from(id: SqlIdentifier) -> String {
139 id.name()
140 }
141}
142
143impl From<&SqlIdentifier> for String {
144 fn from(id: &SqlIdentifier) -> String {
145 id.name()
146 }
147}
148
149impl Display for SqlIdentifier {
150 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
151 write!(f, "{}", self.name())
152 }
153}
154
155#[derive(Default, Serialize, Deserialize, ToSchema, Debug, Eq, PartialEq, Clone)]
159#[cfg_attr(feature = "testing", derive(proptest_derive::Arbitrary))]
160pub struct ProgramSchema {
161 #[cfg_attr(
162 feature = "testing",
163 proptest(strategy = "vec(any::<Relation>(), 0..2)")
164 )]
165 pub inputs: Vec<Relation>,
166 #[cfg_attr(
167 feature = "testing",
168 proptest(strategy = "vec(any::<Relation>(), 0..2)")
169 )]
170 pub outputs: Vec<Relation>,
171}
172
173impl ProgramSchema {
174 pub fn relations_with_lateness(&self) -> Vec<SqlIdentifier> {
175 self.inputs
176 .iter()
177 .chain(self.outputs.iter())
178 .filter(|rel| rel.has_lateness())
179 .map(|rel| rel.name.clone())
180 .collect()
181 }
182}
183
184#[derive(Debug, Deserialize)]
189pub struct ProgramSchemaPropertiesOnly {
190 #[serde(default)]
191 pub inputs: Vec<RelationPropertiesOnly>,
192 #[serde(default)]
193 pub outputs: Vec<RelationPropertiesOnly>,
194}
195
196#[derive(Serialize, Deserialize, ToSchema, Debug, Eq, PartialEq, Clone)]
197#[cfg_attr(feature = "testing", derive(proptest_derive::Arbitrary))]
198pub struct PropertyValue {
199 pub value: String,
200 pub key_position: SourcePosition,
201 pub value_position: SourcePosition,
202}
203
204#[derive(Serialize, Deserialize, ToSchema, Debug, Eq, PartialEq, Clone)]
208#[cfg_attr(feature = "testing", derive(proptest_derive::Arbitrary))]
209pub struct Relation {
210 #[serde(flatten)]
211 pub name: SqlIdentifier,
212 #[cfg_attr(feature = "testing", proptest(value = "Vec::new()"))]
213 pub fields: Vec<Field>,
214 #[serde(default)]
215 pub materialized: bool,
216 #[serde(default)]
217 pub properties: BTreeMap<String, PropertyValue>,
218 pub primary_key: Option<Vec<String>>,
219}
220
221impl Relation {
222 pub fn empty() -> Self {
223 Self {
224 name: SqlIdentifier::from("".to_string()),
225 fields: Vec::new(),
226 materialized: false,
227 properties: BTreeMap::new(),
228 primary_key: None,
229 }
230 }
231
232 pub fn new(
233 name: SqlIdentifier,
234 fields: Vec<Field>,
235 materialized: bool,
236 properties: BTreeMap<String, PropertyValue>,
237 ) -> Self {
238 Self {
239 name,
240 fields,
241 materialized,
242 properties,
243 primary_key: None,
244 }
245 }
246
247 pub fn field(&self, name: &str) -> Option<&Field> {
249 let name = canonical_identifier(name);
250 self.fields.iter().find(|f| f.name == name)
251 }
252
253 pub fn has_lateness(&self) -> bool {
254 self.fields.iter().any(|f| f.lateness.is_some())
255 }
256
257 pub fn get_property(&self, name: &str) -> Option<&str> {
258 self.properties.get(name).map(|p| p.value.as_str())
259 }
260
261 pub fn skip_unused_columns(&self) -> bool {
265 self.get_property("skip_unused_columns") == Some("true")
266 }
267
268 pub fn with_primary_key<'a>(
269 mut self,
270 primary_key: impl IntoIterator<Item = &'a SqlIdentifier>,
271 ) -> Self {
272 self.primary_key = Some(primary_key.into_iter().map(|id| id.name()).collect());
273 self
274 }
275}
276
277#[derive(Debug, Deserialize)]
281pub struct RelationPropertiesOnly {
282 #[serde(flatten)]
283 pub name: SqlIdentifier,
284 #[serde(default)]
285 pub properties: BTreeMap<String, PropertyValue>,
286}
287
288#[derive(Serialize, ToSchema, Debug, Eq, PartialEq, Clone)]
292#[cfg_attr(feature = "testing", derive(proptest_derive::Arbitrary))]
293pub struct Field {
294 #[serde(flatten)]
295 pub name: SqlIdentifier,
296 pub columntype: ColumnType,
297 pub lateness: Option<String>,
298 pub default: Option<String>,
299 pub unused: bool,
300 pub watermark: Option<String>,
301}
302
303impl Field {
304 pub fn new(name: SqlIdentifier, columntype: ColumnType) -> Self {
305 Self {
306 name,
307 columntype,
308 lateness: None,
309 default: None,
310 unused: false,
311 watermark: None,
312 }
313 }
314
315 pub fn with_lateness(mut self, lateness: &str) -> Self {
316 self.lateness = Some(lateness.to_string());
317 self
318 }
319
320 pub fn with_unused(mut self, unused: bool) -> Self {
321 self.unused = unused;
322 self
323 }
324}
325
326impl<'de> Deserialize<'de> for Field {
331 fn deserialize<D>(deserializer: D) -> Result<Field, D::Error>
332 where
333 D: Deserializer<'de>,
334 {
335 const fn default_is_struct() -> Option<SqlType> {
336 Some(SqlType::Struct)
337 }
338
339 #[derive(Debug, Clone, Deserialize)]
340 struct FieldHelper {
341 name: Option<String>,
342 #[serde(default)]
343 case_sensitive: bool,
344 columntype: Option<ColumnType>,
345 #[serde(rename = "type")]
346 #[serde(default = "default_is_struct")]
347 typ: Option<SqlType>,
348 nullable: Option<bool>,
349 precision: Option<i64>,
350 scale: Option<i64>,
351 component: Option<Box<ColumnType>>,
352 fields: Option<serde_json::Value>,
353 key: Option<Box<ColumnType>>,
354 value: Option<Box<ColumnType>>,
355 default: Option<String>,
356 #[serde(default)]
357 unused: bool,
358 lateness: Option<String>,
359 watermark: Option<String>,
360 }
361
362 fn helper_to_field(helper: FieldHelper) -> Field {
363 let columntype = if let Some(ctype) = helper.columntype {
364 ctype
365 } else if let Some(serde_json::Value::Array(fields)) = helper.fields {
366 let fields = fields
367 .into_iter()
368 .map(|field| {
369 let field: FieldHelper = serde_json::from_value(field).unwrap();
370 helper_to_field(field)
371 })
372 .collect::<Vec<Field>>();
373
374 ColumnType {
375 typ: helper.typ.unwrap_or(SqlType::Null),
376 nullable: helper.nullable.unwrap_or(false),
377 precision: helper.precision,
378 scale: helper.scale,
379 component: helper.component,
380 fields: Some(fields),
381 key: None,
382 value: None,
383 }
384 } else if let Some(serde_json::Value::Object(obj)) = helper.fields {
385 serde_json::from_value(serde_json::Value::Object(obj))
386 .expect("Failed to deserialize object")
387 } else {
388 ColumnType {
389 typ: helper.typ.unwrap_or(SqlType::Null),
390 nullable: helper.nullable.unwrap_or(false),
391 precision: helper.precision,
392 scale: helper.scale,
393 component: helper.component,
394 fields: None,
395 key: helper.key,
396 value: helper.value,
397 }
398 };
399
400 Field {
401 name: SqlIdentifier::new(helper.name.unwrap(), helper.case_sensitive),
402 columntype,
403 default: helper.default,
404 unused: helper.unused,
405 lateness: helper.lateness,
406 watermark: helper.watermark,
407 }
408 }
409
410 let helper = FieldHelper::deserialize(deserializer)?;
411 Ok(helper_to_field(helper))
412 }
413}
414
415#[derive(ToSchema, Debug, Eq, PartialEq, Clone, Copy)]
420#[cfg_attr(feature = "testing", derive(proptest_derive::Arbitrary))]
421pub enum IntervalUnit {
422 Day,
424 DayToHour,
426 DayToMinute,
428 DayToSecond,
430 Hour,
432 HourToMinute,
434 HourToSecond,
436 Minute,
438 MinuteToSecond,
440 Month,
442 Second,
444 Year,
446 YearToMonth,
448}
449
450#[derive(Debug, Eq, PartialEq, Clone, Copy)]
461#[cfg_attr(feature = "testing", derive(proptest_derive::Arbitrary))]
462pub enum SqlType {
463 Boolean,
465 TinyInt,
467 SmallInt,
469 Int,
471 BigInt,
473 UTinyInt,
475 USmallInt,
477 UInt,
479 UBigInt,
481 Real,
483 Double,
485 Decimal,
487 Char,
489 Varchar,
491 Binary,
493 Varbinary,
495 Time,
497 Date,
499 Timestamp,
501 TimestampTz,
503 Interval(IntervalUnit),
505 Array,
507 Struct,
509 Map,
511 Null,
513 Uuid,
515 Variant,
517}
518
519const SQL_TYPE_VALUES: &[&str] = &[
527 "BOOLEAN",
528 "TINYINT",
529 "SMALLINT",
530 "INTEGER",
531 "BIGINT",
532 "UTINYINT",
533 "USMALLINT",
534 "UINTEGER",
535 "UBIGINT",
536 "REAL",
537 "DOUBLE",
538 "DECIMAL",
539 "CHAR",
540 "VARCHAR",
541 "BINARY",
542 "VARBINARY",
543 "TIME",
544 "DATE",
545 "TIMESTAMP",
546 "TIMESTAMP_TZ",
547 "INTERVAL_DAY",
548 "INTERVAL_DAY_HOUR",
549 "INTERVAL_DAY_MINUTE",
550 "INTERVAL_DAY_SECOND",
551 "INTERVAL_HOUR",
552 "INTERVAL_HOUR_MINUTE",
553 "INTERVAL_HOUR_SECOND",
554 "INTERVAL_MINUTE",
555 "INTERVAL_MINUTE_SECOND",
556 "INTERVAL_MONTH",
557 "INTERVAL_SECOND",
558 "INTERVAL_YEAR",
559 "INTERVAL_YEAR_MONTH",
560 "ARRAY",
561 "STRUCT",
562 "MAP",
563 "NULL",
564 "UUID",
565 "VARIANT",
566];
567
568impl SqlType {
569 pub fn as_wire_str(&self) -> &'static str {
574 match self {
575 SqlType::Boolean => "BOOLEAN",
576 SqlType::TinyInt => "TINYINT",
577 SqlType::SmallInt => "SMALLINT",
578 SqlType::Int => "INTEGER",
579 SqlType::BigInt => "BIGINT",
580 SqlType::UTinyInt => "UTINYINT",
581 SqlType::USmallInt => "USMALLINT",
582 SqlType::UInt => "UINTEGER",
583 SqlType::UBigInt => "UBIGINT",
584 SqlType::Real => "REAL",
585 SqlType::Double => "DOUBLE",
586 SqlType::Decimal => "DECIMAL",
587 SqlType::Char => "CHAR",
588 SqlType::Varchar => "VARCHAR",
589 SqlType::Binary => "BINARY",
590 SqlType::Varbinary => "VARBINARY",
591 SqlType::Time => "TIME",
592 SqlType::Date => "DATE",
593 SqlType::Timestamp => "TIMESTAMP",
594 SqlType::TimestampTz => "TIMESTAMP_TZ",
595 SqlType::Interval(interval_unit) => match interval_unit {
596 IntervalUnit::Day => "INTERVAL_DAY",
597 IntervalUnit::DayToHour => "INTERVAL_DAY_HOUR",
598 IntervalUnit::DayToMinute => "INTERVAL_DAY_MINUTE",
599 IntervalUnit::DayToSecond => "INTERVAL_DAY_SECOND",
600 IntervalUnit::Hour => "INTERVAL_HOUR",
601 IntervalUnit::HourToMinute => "INTERVAL_HOUR_MINUTE",
602 IntervalUnit::HourToSecond => "INTERVAL_HOUR_SECOND",
603 IntervalUnit::Minute => "INTERVAL_MINUTE",
604 IntervalUnit::MinuteToSecond => "INTERVAL_MINUTE_SECOND",
605 IntervalUnit::Month => "INTERVAL_MONTH",
606 IntervalUnit::Second => "INTERVAL_SECOND",
607 IntervalUnit::Year => "INTERVAL_YEAR",
608 IntervalUnit::YearToMonth => "INTERVAL_YEAR_MONTH",
609 },
610 SqlType::Array => "ARRAY",
611 SqlType::Struct => "STRUCT",
612 SqlType::Map => "MAP",
613 SqlType::Null => "NULL",
614 SqlType::Uuid => "UUID",
615 SqlType::Variant => "VARIANT",
616 }
617 }
618}
619
620impl ToSchema<'_> for SqlType {
621 fn schema() -> (&'static str, RefOr<Schema>) {
622 (
623 "SqlType",
624 RefOr::T(Schema::Object(
625 ObjectBuilder::new()
626 .schema_type(SchemaType::String)
627 .description(Some(
628 "The available SQL column type names. Each value is the platform's wire \
629 encoding of the type (e.g. `BIGINT`, `INTEGER`, `INTERVAL_DAY`), not \
630 valid SQL type syntax.",
631 ))
632 .enum_values(Some(SQL_TYPE_VALUES.iter().copied()))
633 .build(),
634 )),
635 )
636 }
637}
638
639impl Display for SqlType {
640 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
641 f.write_str(&serde_json::to_string(self).unwrap())
642 }
643}
644
645impl<'de> Deserialize<'de> for SqlType {
646 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
647 where
648 D: Deserializer<'de>,
649 {
650 let value: String = Deserialize::deserialize(deserializer)?;
651 match value.to_lowercase().as_str() {
652 "interval_day" => Ok(SqlType::Interval(IntervalUnit::Day)),
653 "interval_day_hour" => Ok(SqlType::Interval(IntervalUnit::DayToHour)),
654 "interval_day_minute" => Ok(SqlType::Interval(IntervalUnit::DayToMinute)),
655 "interval_day_second" => Ok(SqlType::Interval(IntervalUnit::DayToSecond)),
656 "interval_hour" => Ok(SqlType::Interval(IntervalUnit::Hour)),
657 "interval_hour_minute" => Ok(SqlType::Interval(IntervalUnit::HourToMinute)),
658 "interval_hour_second" => Ok(SqlType::Interval(IntervalUnit::HourToSecond)),
659 "interval_minute" => Ok(SqlType::Interval(IntervalUnit::Minute)),
660 "interval_minute_second" => Ok(SqlType::Interval(IntervalUnit::MinuteToSecond)),
661 "interval_month" => Ok(SqlType::Interval(IntervalUnit::Month)),
662 "interval_second" => Ok(SqlType::Interval(IntervalUnit::Second)),
663 "interval_year" => Ok(SqlType::Interval(IntervalUnit::Year)),
664 "interval_year_month" => Ok(SqlType::Interval(IntervalUnit::YearToMonth)),
665 "boolean" => Ok(SqlType::Boolean),
666 "tinyint" => Ok(SqlType::TinyInt),
667 "smallint" => Ok(SqlType::SmallInt),
668 "integer" => Ok(SqlType::Int),
669 "bigint" => Ok(SqlType::BigInt),
670 "utinyint" => Ok(SqlType::UTinyInt),
671 "usmallint" => Ok(SqlType::USmallInt),
672 "uinteger" => Ok(SqlType::UInt),
673 "ubigint" => Ok(SqlType::UBigInt),
674 "real" => Ok(SqlType::Real),
675 "double" => Ok(SqlType::Double),
676 "decimal" => Ok(SqlType::Decimal),
677 "char" => Ok(SqlType::Char),
678 "varchar" => Ok(SqlType::Varchar),
679 "binary" => Ok(SqlType::Binary),
680 "varbinary" => Ok(SqlType::Varbinary),
681 "variant" => Ok(SqlType::Variant),
682 "time" => Ok(SqlType::Time),
683 "date" => Ok(SqlType::Date),
684 "timestamp" => Ok(SqlType::Timestamp),
685 "timestamp_tz" => Ok(SqlType::TimestampTz),
686 "array" => Ok(SqlType::Array),
687 "struct" => Ok(SqlType::Struct),
688 "map" => Ok(SqlType::Map),
689 "null" => Ok(SqlType::Null),
690 "uuid" => Ok(SqlType::Uuid),
691 _ => Err(serde::de::Error::custom(format!(
692 "Unknown SQL type: {}",
693 value
694 ))),
695 }
696 }
697}
698
699impl Serialize for SqlType {
700 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
701 where
702 S: Serializer,
703 {
704 serializer.serialize_str(self.as_wire_str())
705 }
706}
707
708impl SqlType {
709 pub fn is_string(&self) -> bool {
711 matches!(self, Self::Char | Self::Varchar)
712 }
713
714 pub fn is_varchar(&self) -> bool {
715 matches!(self, Self::Varchar)
716 }
717
718 pub fn is_varbinary(&self) -> bool {
719 matches!(self, Self::Varbinary)
720 }
721}
722
723const fn default_is_struct() -> SqlType {
726 SqlType::Struct
727}
728
729#[derive(Serialize, Deserialize, ToSchema, Debug, Eq, PartialEq, Clone)]
733#[cfg_attr(feature = "testing", derive(proptest_derive::Arbitrary))]
734pub struct ColumnType {
735 #[serde(rename = "type")]
737 #[serde(default = "default_is_struct")]
738 pub typ: SqlType,
739 pub nullable: bool,
741 pub precision: Option<i64>,
750 pub scale: Option<i64>,
755 #[cfg_attr(feature = "testing", proptest(value = "None"))]
762 pub component: Option<Box<ColumnType>>,
763 #[cfg_attr(feature = "testing", proptest(value = "Some(Vec::new())"))]
785 pub fields: Option<Vec<Field>>,
786 #[cfg_attr(feature = "testing", proptest(value = "None"))]
788 pub key: Option<Box<ColumnType>>,
789 #[cfg_attr(feature = "testing", proptest(value = "None"))]
791 pub value: Option<Box<ColumnType>>,
792}
793
794impl ColumnType {
795 pub fn boolean(nullable: bool) -> Self {
796 ColumnType {
797 typ: SqlType::Boolean,
798 nullable,
799 precision: None,
800 scale: None,
801 component: None,
802 fields: None,
803 key: None,
804 value: None,
805 }
806 }
807
808 pub fn uuid(nullable: bool) -> Self {
809 ColumnType {
810 typ: SqlType::Uuid,
811 nullable,
812 precision: None,
813 scale: None,
814 component: None,
815 fields: None,
816 key: None,
817 value: None,
818 }
819 }
820
821 pub fn tinyint(nullable: bool) -> Self {
822 ColumnType {
823 typ: SqlType::TinyInt,
824 nullable,
825 precision: None,
826 scale: None,
827 component: None,
828 fields: None,
829 key: None,
830 value: None,
831 }
832 }
833
834 pub fn smallint(nullable: bool) -> Self {
835 ColumnType {
836 typ: SqlType::SmallInt,
837 nullable,
838 precision: None,
839 scale: None,
840 component: None,
841 fields: None,
842 key: None,
843 value: None,
844 }
845 }
846
847 pub fn int(nullable: bool) -> Self {
848 ColumnType {
849 typ: SqlType::Int,
850 nullable,
851 precision: None,
852 scale: None,
853 component: None,
854 fields: None,
855 key: None,
856 value: None,
857 }
858 }
859
860 pub fn bigint(nullable: bool) -> Self {
861 ColumnType {
862 typ: SqlType::BigInt,
863 nullable,
864 precision: None,
865 scale: None,
866 component: None,
867 fields: None,
868 key: None,
869 value: None,
870 }
871 }
872
873 pub fn utinyint(nullable: bool) -> Self {
874 ColumnType {
875 typ: SqlType::UTinyInt,
876 nullable,
877 precision: None,
878 scale: None,
879 component: None,
880 fields: None,
881 key: None,
882 value: None,
883 }
884 }
885
886 pub fn usmallint(nullable: bool) -> Self {
887 ColumnType {
888 typ: SqlType::USmallInt,
889 nullable,
890 precision: None,
891 scale: None,
892 component: None,
893 fields: None,
894 key: None,
895 value: None,
896 }
897 }
898
899 pub fn uint(nullable: bool) -> Self {
900 ColumnType {
901 typ: SqlType::UInt,
902 nullable,
903 precision: None,
904 scale: None,
905 component: None,
906 fields: None,
907 key: None,
908 value: None,
909 }
910 }
911
912 pub fn ubigint(nullable: bool) -> Self {
913 ColumnType {
914 typ: SqlType::UBigInt,
915 nullable,
916 precision: None,
917 scale: None,
918 component: None,
919 fields: None,
920 key: None,
921 value: None,
922 }
923 }
924
925 pub fn double(nullable: bool) -> Self {
926 ColumnType {
927 typ: SqlType::Double,
928 nullable,
929 precision: None,
930 scale: None,
931 component: None,
932 fields: None,
933 key: None,
934 value: None,
935 }
936 }
937
938 pub fn real(nullable: bool) -> Self {
939 ColumnType {
940 typ: SqlType::Real,
941 nullable,
942 precision: None,
943 scale: None,
944 component: None,
945 fields: None,
946 key: None,
947 value: None,
948 }
949 }
950
951 pub fn decimal(precision: i64, scale: i64, nullable: bool) -> Self {
952 ColumnType {
953 typ: SqlType::Decimal,
954 nullable,
955 precision: Some(precision),
956 scale: Some(scale),
957 component: None,
958 fields: None,
959 key: None,
960 value: None,
961 }
962 }
963
964 pub fn varchar(nullable: bool) -> Self {
965 ColumnType {
966 typ: SqlType::Varchar,
967 nullable,
968 precision: None,
969 scale: None,
970 component: None,
971 fields: None,
972 key: None,
973 value: None,
974 }
975 }
976
977 pub fn varbinary(nullable: bool) -> Self {
978 ColumnType {
979 typ: SqlType::Varbinary,
980 nullable,
981 precision: None,
982 scale: None,
983 component: None,
984 fields: None,
985 key: None,
986 value: None,
987 }
988 }
989
990 pub fn fixed(width: i64, nullable: bool) -> Self {
991 ColumnType {
992 typ: SqlType::Binary,
993 nullable,
994 precision: Some(width),
995 scale: None,
996 component: None,
997 fields: None,
998 key: None,
999 value: None,
1000 }
1001 }
1002
1003 pub fn date(nullable: bool) -> Self {
1004 ColumnType {
1005 typ: SqlType::Date,
1006 nullable,
1007 precision: None,
1008 scale: None,
1009 component: None,
1010 fields: None,
1011 key: None,
1012 value: None,
1013 }
1014 }
1015
1016 pub fn time(nullable: bool) -> Self {
1017 ColumnType {
1018 typ: SqlType::Time,
1019 nullable,
1020 precision: None,
1021 scale: None,
1022 component: None,
1023 fields: None,
1024 key: None,
1025 value: None,
1026 }
1027 }
1028
1029 pub fn timestamp(nullable: bool) -> Self {
1030 ColumnType {
1031 typ: SqlType::Timestamp,
1032 nullable,
1033 precision: None,
1034 scale: None,
1035 component: None,
1036 fields: None,
1037 key: None,
1038 value: None,
1039 }
1040 }
1041
1042 pub fn timestamp_tz(nullable: bool) -> Self {
1043 ColumnType {
1044 typ: SqlType::TimestampTz,
1045 nullable,
1046 precision: None,
1047 scale: None,
1048 component: None,
1049 fields: None,
1050 key: None,
1051 value: None,
1052 }
1053 }
1054
1055 pub fn variant(nullable: bool) -> Self {
1056 ColumnType {
1057 typ: SqlType::Variant,
1058 nullable,
1059 precision: None,
1060 scale: None,
1061 component: None,
1062 fields: None,
1063 key: None,
1064 value: None,
1065 }
1066 }
1067
1068 pub fn array(nullable: bool, element: ColumnType) -> Self {
1069 ColumnType {
1070 typ: SqlType::Array,
1071 nullable,
1072 precision: None,
1073 scale: None,
1074 component: Some(Box::new(element)),
1075 fields: None,
1076 key: None,
1077 value: None,
1078 }
1079 }
1080
1081 pub fn structure(nullable: bool, fields: &[Field]) -> Self {
1082 ColumnType {
1083 typ: SqlType::Struct,
1084 nullable,
1085 precision: None,
1086 scale: None,
1087 component: None,
1088 fields: Some(fields.to_vec()),
1089 key: None,
1090 value: None,
1091 }
1092 }
1093
1094 pub fn map(nullable: bool, key: ColumnType, val: ColumnType) -> Self {
1095 ColumnType {
1096 typ: SqlType::Map,
1097 nullable,
1098 precision: None,
1099 scale: None,
1100 component: None,
1101 fields: None,
1102 key: Some(Box::new(key)),
1103 value: Some(Box::new(val)),
1104 }
1105 }
1106
1107 pub fn is_integral_type(&self) -> bool {
1108 matches!(
1109 &self.typ,
1110 SqlType::TinyInt
1111 | SqlType::SmallInt
1112 | SqlType::Int
1113 | SqlType::BigInt
1114 | SqlType::UTinyInt
1115 | SqlType::USmallInt
1116 | SqlType::UInt
1117 | SqlType::UBigInt
1118 )
1119 }
1120
1121 pub fn is_fp_type(&self) -> bool {
1122 matches!(&self.typ, SqlType::Double | SqlType::Real)
1123 }
1124
1125 pub fn is_decimal_type(&self) -> bool {
1126 matches!(&self.typ, SqlType::Decimal)
1127 }
1128
1129 pub fn is_numeric_type(&self) -> bool {
1130 self.is_integral_type() || self.is_fp_type() || self.is_decimal_type()
1131 }
1132}
1133
1134#[cfg(test)]
1135mod tests {
1136 use super::{IntervalUnit, SqlIdentifier};
1137 use crate::program_schema::{ColumnType, Field, SQL_TYPE_VALUES, SqlType};
1138
1139 fn all_sql_types() -> Vec<SqlType> {
1141 let mut types = vec![
1142 SqlType::Boolean,
1143 SqlType::TinyInt,
1144 SqlType::SmallInt,
1145 SqlType::Int,
1146 SqlType::BigInt,
1147 SqlType::UTinyInt,
1148 SqlType::USmallInt,
1149 SqlType::UInt,
1150 SqlType::UBigInt,
1151 SqlType::Real,
1152 SqlType::Double,
1153 SqlType::Decimal,
1154 SqlType::Char,
1155 SqlType::Varchar,
1156 SqlType::Binary,
1157 SqlType::Varbinary,
1158 SqlType::Time,
1159 SqlType::Date,
1160 SqlType::Timestamp,
1161 SqlType::TimestampTz,
1162 ];
1163 types.extend(
1164 [
1165 IntervalUnit::Day,
1166 IntervalUnit::DayToHour,
1167 IntervalUnit::DayToMinute,
1168 IntervalUnit::DayToSecond,
1169 IntervalUnit::Hour,
1170 IntervalUnit::HourToMinute,
1171 IntervalUnit::HourToSecond,
1172 IntervalUnit::Minute,
1173 IntervalUnit::MinuteToSecond,
1174 IntervalUnit::Month,
1175 IntervalUnit::Second,
1176 IntervalUnit::Year,
1177 IntervalUnit::YearToMonth,
1178 ]
1179 .map(SqlType::Interval),
1180 );
1181 types.extend([
1182 SqlType::Array,
1183 SqlType::Struct,
1184 SqlType::Map,
1185 SqlType::Null,
1186 SqlType::Uuid,
1187 SqlType::Variant,
1188 ]);
1189 types
1190 }
1191
1192 #[test]
1197 fn sql_type_schema_matches_serialization() {
1198 let serialized: Vec<String> = all_sql_types()
1199 .iter()
1200 .map(|t| {
1201 serde_json::to_value(t)
1202 .unwrap()
1203 .as_str()
1204 .unwrap()
1205 .to_owned()
1206 })
1207 .collect();
1208
1209 assert_eq!(
1211 serialized,
1212 SQL_TYPE_VALUES
1213 .iter()
1214 .map(|s| s.to_string())
1215 .collect::<Vec<_>>(),
1216 "SQL_TYPE_VALUES (the OpenAPI enum) must match `Serialize` output exactly"
1217 );
1218
1219 for t in all_sql_types() {
1221 assert_eq!(
1222 serde_json::to_value(t).unwrap().as_str().unwrap(),
1223 t.as_wire_str()
1224 );
1225 }
1226 }
1227
1228 #[test]
1231 fn relation_skip_unused_columns_property() {
1232 use super::{PropertyValue, Relation, SourcePosition};
1233 use std::collections::BTreeMap;
1234
1235 let zero = SourcePosition {
1236 start_line_number: 0,
1237 start_column: 0,
1238 end_line_number: 0,
1239 end_column: 0,
1240 };
1241 let relation = |value: Option<&str>| {
1242 let mut properties = BTreeMap::new();
1243 if let Some(value) = value {
1244 properties.insert(
1245 "skip_unused_columns".to_string(),
1246 PropertyValue {
1247 value: value.to_string(),
1248 key_position: zero,
1249 value_position: zero,
1250 },
1251 );
1252 }
1253 Relation::new(
1254 SqlIdentifier::new("t", false),
1255 Vec::new(),
1256 false,
1257 properties,
1258 )
1259 };
1260
1261 assert!(relation(Some("true")).skip_unused_columns());
1262 assert!(!relation(Some("false")).skip_unused_columns());
1263 assert!(!relation(Some("TRUE")).skip_unused_columns());
1264 assert!(!relation(None).skip_unused_columns());
1265 }
1266
1267 #[test]
1268 fn serde_sql_type() {
1269 for (sql_str_base, expected_value) in [
1270 ("Boolean", SqlType::Boolean),
1271 ("Uuid", SqlType::Uuid),
1272 ("TinyInt", SqlType::TinyInt),
1273 ("SmallInt", SqlType::SmallInt),
1274 ("Integer", SqlType::Int),
1275 ("BigInt", SqlType::BigInt),
1276 ("UTinyInt", SqlType::UTinyInt),
1277 ("USmallInt", SqlType::USmallInt),
1278 ("UInteger", SqlType::UInt),
1279 ("UBigInt", SqlType::UBigInt),
1280 ("Real", SqlType::Real),
1281 ("Double", SqlType::Double),
1282 ("Decimal", SqlType::Decimal),
1283 ("Char", SqlType::Char),
1284 ("Varchar", SqlType::Varchar),
1285 ("Binary", SqlType::Binary),
1286 ("Varbinary", SqlType::Varbinary),
1287 ("Time", SqlType::Time),
1288 ("Date", SqlType::Date),
1289 ("Timestamp", SqlType::Timestamp),
1290 ("Timestamp_Tz", SqlType::TimestampTz),
1291 ("Interval_Day", SqlType::Interval(IntervalUnit::Day)),
1292 (
1293 "Interval_Day_Hour",
1294 SqlType::Interval(IntervalUnit::DayToHour),
1295 ),
1296 (
1297 "Interval_Day_Minute",
1298 SqlType::Interval(IntervalUnit::DayToMinute),
1299 ),
1300 (
1301 "Interval_Day_Second",
1302 SqlType::Interval(IntervalUnit::DayToSecond),
1303 ),
1304 ("Interval_Hour", SqlType::Interval(IntervalUnit::Hour)),
1305 (
1306 "Interval_Hour_Minute",
1307 SqlType::Interval(IntervalUnit::HourToMinute),
1308 ),
1309 (
1310 "Interval_Hour_Second",
1311 SqlType::Interval(IntervalUnit::HourToSecond),
1312 ),
1313 ("Interval_Minute", SqlType::Interval(IntervalUnit::Minute)),
1314 (
1315 "Interval_Minute_Second",
1316 SqlType::Interval(IntervalUnit::MinuteToSecond),
1317 ),
1318 ("Interval_Month", SqlType::Interval(IntervalUnit::Month)),
1319 ("Interval_Second", SqlType::Interval(IntervalUnit::Second)),
1320 ("Interval_Year", SqlType::Interval(IntervalUnit::Year)),
1321 (
1322 "Interval_Year_Month",
1323 SqlType::Interval(IntervalUnit::YearToMonth),
1324 ),
1325 ("Array", SqlType::Array),
1326 ("Struct", SqlType::Struct),
1327 ("Map", SqlType::Map),
1328 ("Null", SqlType::Null),
1329 ("Variant", SqlType::Variant),
1330 ] {
1331 for sql_str in [
1332 sql_str_base, &sql_str_base.to_lowercase(), &sql_str_base.to_uppercase(), ] {
1336 let value1: SqlType = serde_json::from_str(&format!("\"{}\"", sql_str))
1337 .unwrap_or_else(|e| {
1338 panic!(
1339 "\"{sql_str}\" should deserialize into its SQL type: {}",
1340 e.to_string()
1341 )
1342 });
1343 assert_eq!(value1, expected_value);
1344 let serialized_str =
1345 serde_json::to_string(&value1).expect("Value should serialize into JSON");
1346 let value2: SqlType = serde_json::from_str(&serialized_str).unwrap_or_else(|_| {
1347 panic!(
1348 "{} should deserialize back into its SQL type",
1349 serialized_str
1350 )
1351 });
1352 assert_eq!(value1, value2);
1353 }
1354 }
1355 }
1356
1357 #[test]
1358 fn deserialize_interval_types() {
1359 use super::IntervalUnit::*;
1360 use super::SqlType::*;
1361
1362 let schema = r#"
1363{
1364 "inputs" : [ {
1365 "name" : "sales",
1366 "case_sensitive" : false,
1367 "fields" : [ {
1368 "name" : "sales_id",
1369 "case_sensitive" : false,
1370 "columntype" : {
1371 "type" : "INTEGER",
1372 "nullable" : true
1373 }
1374 }, {
1375 "name" : "customer_id",
1376 "case_sensitive" : false,
1377 "columntype" : {
1378 "type" : "INTEGER",
1379 "nullable" : true
1380 }
1381 }, {
1382 "name" : "age",
1383 "case_sensitive" : false,
1384 "columntype" : {
1385 "type" : "UINTEGER",
1386 "nullable" : true
1387 }
1388 }, {
1389 "name" : "amount",
1390 "case_sensitive" : false,
1391 "columntype" : {
1392 "type" : "DECIMAL",
1393 "nullable" : true,
1394 "precision" : 10,
1395 "scale" : 2
1396 }
1397 }, {
1398 "name" : "sale_date",
1399 "case_sensitive" : false,
1400 "columntype" : {
1401 "type" : "DATE",
1402 "nullable" : true
1403 }
1404 } ],
1405 "primary_key" : [ "sales_id" ]
1406 } ],
1407 "outputs" : [ {
1408 "name" : "salessummary",
1409 "case_sensitive" : false,
1410 "fields" : [ {
1411 "name" : "customer_id",
1412 "case_sensitive" : false,
1413 "columntype" : {
1414 "type" : "INTEGER",
1415 "nullable" : true
1416 }
1417 }, {
1418 "name" : "total_sales",
1419 "case_sensitive" : false,
1420 "columntype" : {
1421 "type" : "DECIMAL",
1422 "nullable" : true,
1423 "precision" : 38,
1424 "scale" : 2
1425 }
1426 }, {
1427 "name" : "interval_day",
1428 "case_sensitive" : false,
1429 "columntype" : {
1430 "type" : "INTERVAL_DAY",
1431 "nullable" : false,
1432 "precision" : 2,
1433 "scale" : 6
1434 }
1435 }, {
1436 "name" : "interval_day_to_hour",
1437 "case_sensitive" : false,
1438 "columntype" : {
1439 "type" : "INTERVAL_DAY_HOUR",
1440 "nullable" : false,
1441 "precision" : 2,
1442 "scale" : 6
1443 }
1444 }, {
1445 "name" : "interval_day_to_minute",
1446 "case_sensitive" : false,
1447 "columntype" : {
1448 "type" : "INTERVAL_DAY_MINUTE",
1449 "nullable" : false,
1450 "precision" : 2,
1451 "scale" : 6
1452 }
1453 }, {
1454 "name" : "interval_day_to_second",
1455 "case_sensitive" : false,
1456 "columntype" : {
1457 "type" : "INTERVAL_DAY_SECOND",
1458 "nullable" : false,
1459 "precision" : 2,
1460 "scale" : 6
1461 }
1462 }, {
1463 "name" : "interval_hour",
1464 "case_sensitive" : false,
1465 "columntype" : {
1466 "type" : "INTERVAL_HOUR",
1467 "nullable" : false,
1468 "precision" : 2,
1469 "scale" : 6
1470 }
1471 }, {
1472 "name" : "interval_hour_to_minute",
1473 "case_sensitive" : false,
1474 "columntype" : {
1475 "type" : "INTERVAL_HOUR_MINUTE",
1476 "nullable" : false,
1477 "precision" : 2,
1478 "scale" : 6
1479 }
1480 }, {
1481 "name" : "interval_hour_to_second",
1482 "case_sensitive" : false,
1483 "columntype" : {
1484 "type" : "INTERVAL_HOUR_SECOND",
1485 "nullable" : false,
1486 "precision" : 2,
1487 "scale" : 6
1488 }
1489 }, {
1490 "name" : "interval_minute",
1491 "case_sensitive" : false,
1492 "columntype" : {
1493 "type" : "INTERVAL_MINUTE",
1494 "nullable" : false,
1495 "precision" : 2,
1496 "scale" : 6
1497 }
1498 }, {
1499 "name" : "interval_minute_to_second",
1500 "case_sensitive" : false,
1501 "columntype" : {
1502 "type" : "INTERVAL_MINUTE_SECOND",
1503 "nullable" : false,
1504 "precision" : 2,
1505 "scale" : 6
1506 }
1507 }, {
1508 "name" : "interval_month",
1509 "case_sensitive" : false,
1510 "columntype" : {
1511 "type" : "INTERVAL_MONTH",
1512 "nullable" : false
1513 }
1514 }, {
1515 "name" : "interval_second",
1516 "case_sensitive" : false,
1517 "columntype" : {
1518 "type" : "INTERVAL_SECOND",
1519 "nullable" : false,
1520 "precision" : 2,
1521 "scale" : 6
1522 }
1523 }, {
1524 "name" : "interval_year",
1525 "case_sensitive" : false,
1526 "columntype" : {
1527 "type" : "INTERVAL_YEAR",
1528 "nullable" : false
1529 }
1530 }, {
1531 "name" : "interval_year_to_month",
1532 "case_sensitive" : false,
1533 "columntype" : {
1534 "type" : "INTERVAL_YEAR_MONTH",
1535 "nullable" : false
1536 }
1537 } ]
1538 } ]
1539}
1540"#;
1541
1542 let schema: super::ProgramSchema = serde_json::from_str(schema).unwrap();
1543 let types = schema
1544 .outputs
1545 .iter()
1546 .flat_map(|r| r.fields.iter().map(|f| f.columntype.typ));
1547 let expected_types = [
1548 Int,
1549 Decimal,
1550 Interval(Day),
1551 Interval(DayToHour),
1552 Interval(DayToMinute),
1553 Interval(DayToSecond),
1554 Interval(Hour),
1555 Interval(HourToMinute),
1556 Interval(HourToSecond),
1557 Interval(Minute),
1558 Interval(MinuteToSecond),
1559 Interval(Month),
1560 Interval(Second),
1561 Interval(Year),
1562 Interval(YearToMonth),
1563 ];
1564
1565 assert_eq!(types.collect::<Vec<_>>(), &expected_types);
1566 }
1567
1568 #[test]
1569 fn serialize_struct_schemas() {
1570 let schema = r#"{
1571 "inputs" : [ {
1572 "name" : "PERS",
1573 "case_sensitive" : false,
1574 "fields" : [ {
1575 "name" : "P0",
1576 "case_sensitive" : false,
1577 "columntype" : {
1578 "fields" : [ {
1579 "type" : "VARCHAR",
1580 "nullable" : true,
1581 "precision" : 30,
1582 "name" : "FIRSTNAME"
1583 }, {
1584 "type" : "VARCHAR",
1585 "nullable" : true,
1586 "precision" : 30,
1587 "name" : "LASTNAME"
1588 }, {
1589 "type" : "UINTEGER",
1590 "nullable" : true,
1591 "name" : "AGE"
1592 }, {
1593 "fields" : {
1594 "fields" : [ {
1595 "type" : "VARCHAR",
1596 "nullable" : true,
1597 "precision" : 30,
1598 "name" : "STREET"
1599 }, {
1600 "type" : "VARCHAR",
1601 "nullable" : true,
1602 "precision" : 30,
1603 "name" : "CITY"
1604 }, {
1605 "type" : "CHAR",
1606 "nullable" : true,
1607 "precision" : 2,
1608 "name" : "STATE"
1609 }, {
1610 "type" : "VARCHAR",
1611 "nullable" : true,
1612 "precision" : 6,
1613 "name" : "POSTAL_CODE"
1614 } ],
1615 "nullable" : false
1616 },
1617 "nullable" : false,
1618 "name" : "ADDRESS"
1619 } ],
1620 "nullable" : false
1621 }
1622 }]
1623 } ],
1624 "outputs" : [ ]
1625}
1626"#;
1627 let schema: super::ProgramSchema = serde_json::from_str(schema).unwrap();
1628 eprintln!("{:#?}", schema);
1629 let pers = schema.inputs.iter().find(|r| r.name == "PERS").unwrap();
1630 let p0 = pers.fields.iter().find(|f| f.name == "P0").unwrap();
1631 assert_eq!(p0.columntype.typ, SqlType::Struct);
1632 let p0_fields = p0.columntype.fields.as_ref().unwrap();
1633 assert_eq!(p0_fields[0].columntype.typ, SqlType::Varchar);
1634 assert_eq!(p0_fields[1].columntype.typ, SqlType::Varchar);
1635 assert_eq!(p0_fields[2].columntype.typ, SqlType::UInt);
1636 assert_eq!(p0_fields[3].columntype.typ, SqlType::Struct);
1637 assert_eq!(p0_fields[3].name, "ADDRESS");
1638 let address = &p0_fields[3].columntype.fields.as_ref().unwrap();
1639 assert_eq!(address.len(), 4);
1640 assert_eq!(address[0].name, "STREET");
1641 assert_eq!(address[0].columntype.typ, SqlType::Varchar);
1642 assert_eq!(address[1].columntype.typ, SqlType::Varchar);
1643 assert_eq!(address[2].columntype.typ, SqlType::Char);
1644 assert_eq!(address[3].columntype.typ, SqlType::Varchar);
1645 }
1646
1647 #[test]
1648 fn sql_identifier_cmp() {
1649 assert_eq!(SqlIdentifier::from("foo"), SqlIdentifier::from("foo"));
1650 assert_ne!(SqlIdentifier::from("foo"), SqlIdentifier::from("bar"));
1651 assert_eq!(SqlIdentifier::from("bar"), SqlIdentifier::from("BAR"));
1652 assert_eq!(SqlIdentifier::from("foo"), SqlIdentifier::from("\"foo\""));
1653 assert_eq!(SqlIdentifier::from("bar"), SqlIdentifier::from("\"bar\""));
1654 assert_eq!(SqlIdentifier::from("bAr"), SqlIdentifier::from("\"bAr\""));
1655 assert_eq!(
1656 SqlIdentifier::new("bAr", true),
1657 SqlIdentifier::from("\"bAr\"")
1658 );
1659
1660 assert_eq!(SqlIdentifier::from("bAr"), "bar");
1661 assert_eq!(SqlIdentifier::from("bAr"), "bAr");
1662 }
1663
1664 #[test]
1665 fn sql_identifier_ord() {
1666 let mut btree = std::collections::BTreeSet::new();
1667 assert!(btree.insert(SqlIdentifier::from("foo")));
1668 assert!(btree.insert(SqlIdentifier::from("bar")));
1669 assert!(!btree.insert(SqlIdentifier::from("BAR")));
1670 assert!(!btree.insert(SqlIdentifier::from("\"foo\"")));
1671 assert!(!btree.insert(SqlIdentifier::from("\"bar\"")));
1672 }
1673
1674 #[test]
1675 fn sql_identifier_hash() {
1676 let mut hs = std::collections::HashSet::new();
1677 assert!(hs.insert(SqlIdentifier::from("foo")));
1678 assert!(hs.insert(SqlIdentifier::from("bar")));
1679 assert!(!hs.insert(SqlIdentifier::from("BAR")));
1680 assert!(!hs.insert(SqlIdentifier::from("\"foo\"")));
1681 assert!(!hs.insert(SqlIdentifier::from("\"bar\"")));
1682 }
1683
1684 #[test]
1685 fn sql_identifier_name() {
1686 assert_eq!(SqlIdentifier::from("foo").name(), "foo");
1687 assert_eq!(SqlIdentifier::from("bAr").name(), "bar");
1688 assert_eq!(SqlIdentifier::from("\"bAr\"").name(), "bAr");
1689 assert_eq!(SqlIdentifier::from("foo").sql_name(), "foo");
1690 assert_eq!(SqlIdentifier::from("bAr").sql_name(), "bAr");
1691 assert_eq!(SqlIdentifier::from("\"bAr\"").sql_name(), "\"bAr\"");
1692 }
1693
1694 #[test]
1695 fn issue3277() {
1696 let schema = r#"{
1697 "name" : "j",
1698 "case_sensitive" : false,
1699 "columntype" : {
1700 "fields" : [ {
1701 "key" : {
1702 "nullable" : false,
1703 "precision" : -1,
1704 "type" : "VARCHAR"
1705 },
1706 "name" : "s",
1707 "nullable" : true,
1708 "type" : "MAP",
1709 "value" : {
1710 "nullable" : true,
1711 "precision" : -1,
1712 "type" : "VARCHAR"
1713 }
1714 } ],
1715 "nullable" : true
1716 }
1717 }"#;
1718 let field: Field = serde_json::from_str(schema).unwrap();
1719 println!("field: {:#?}", field);
1720 assert_eq!(
1721 field,
1722 Field {
1723 name: SqlIdentifier {
1724 name: "j".to_string(),
1725 case_sensitive: false,
1726 },
1727 columntype: ColumnType {
1728 typ: SqlType::Struct,
1729 nullable: true,
1730 precision: None,
1731 scale: None,
1732 component: None,
1733 fields: Some(vec![Field {
1734 name: SqlIdentifier {
1735 name: "s".to_string(),
1736 case_sensitive: false,
1737 },
1738 columntype: ColumnType {
1739 typ: SqlType::Map,
1740 nullable: true,
1741 precision: None,
1742 scale: None,
1743 component: None,
1744 fields: None,
1745 key: Some(Box::new(ColumnType {
1746 typ: SqlType::Varchar,
1747 nullable: false,
1748 precision: Some(-1),
1749 scale: None,
1750 component: None,
1751 fields: None,
1752 key: None,
1753 value: None,
1754 })),
1755 value: Some(Box::new(ColumnType {
1756 typ: SqlType::Varchar,
1757 nullable: true,
1758 precision: Some(-1),
1759 scale: None,
1760 component: None,
1761 fields: None,
1762 key: None,
1763 value: None,
1764 })),
1765 },
1766 lateness: None,
1767 default: None,
1768 unused: false,
1769 watermark: None,
1770 }]),
1771 key: None,
1772 value: None,
1773 },
1774 lateness: None,
1775 default: None,
1776 unused: false,
1777 watermark: None,
1778 }
1779 );
1780 }
1781}