Skip to content

Commit c91c5a4

Browse files
authored
Replace BTreeSet with IndexSet in type to order more intuitively (#477)
1 parent 1b22fb6 commit c91c5a4

File tree

10 files changed

+71
-46
lines changed

10 files changed

+71
-46
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1111
- *BREAKING:* partiql-ast: changed modeling of `GroupByExpr` `strategy` field to be an `Option`
1212
- *BREAKING:* partiql-ast: changed modeling of `PathStep` to split `PathExpr` to `PathIndex` (e.g., `[2]`) and `PathProject` (e.g., `.a`)
1313
- *BREAKING:* partiql-ast: changed modeling of `PathStep` to rename `PathWildcard` to `PathForEach` (for `[*]`)
14+
- *BREAKING:* partiql-types: changed type ordering to match specification order
1415

1516
### Added
1617
- partiql-ast: Pretty-printing of AST via `ToPretty` trait

extension/partiql-extension-ddl/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ thiserror = "1.0"
2828
miette = { version = "7.2", features = ["fancy"] }
2929
time = { version = "0.3", features = ["formatting", "parsing", "serde"] }
3030

31+
indexmap = "2.2"
32+
3133
[dev-dependencies]
3234
criterion = "0.4"
3335

extension/partiql-extension-ddl/src/ddl.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -224,8 +224,8 @@ impl PartiqlDdlEncoder for PartiqlBasicDdlEncoder {
224224
#[cfg(test)]
225225
mod tests {
226226
use super::*;
227+
use indexmap::IndexSet;
227228
use partiql_types::{array, bag, f64, int8, r#struct, str, struct_fields, StructConstraint};
228-
use std::collections::BTreeSet;
229229

230230
#[test]
231231
fn ddl_test() {
@@ -240,7 +240,7 @@ mod tests {
240240
("b", array![str![]]),
241241
("c", f64!()),
242242
];
243-
let details = r#struct![BTreeSet::from([nested_attrs])];
243+
let details = r#struct![IndexSet::from([nested_attrs])];
244244

245245
let fields = struct_fields![
246246
("employee_id", int8![]),
@@ -249,17 +249,17 @@ mod tests {
249249
("details", details),
250250
("dependents", array![str![]])
251251
];
252-
let ty = bag![r#struct![BTreeSet::from([
252+
let ty = bag![r#struct![IndexSet::from([
253253
fields,
254254
StructConstraint::Open(false)
255255
])]];
256256

257-
let expected_compact = r#""dependents" ARRAY<VARCHAR>,"details" STRUCT<"a": UNION<TINYINT,DECIMAL(5, 4)>,"b": ARRAY<VARCHAR>,"c": DOUBLE>,"employee_id" TINYINT,"full_name" VARCHAR,"salary" DECIMAL(8, 2)"#;
258-
let expected_pretty = r#""dependents" ARRAY<VARCHAR>,
259-
"details" STRUCT<"a": UNION<TINYINT,DECIMAL(5, 4)>,"b": ARRAY<VARCHAR>,"c": DOUBLE>,
260-
"employee_id" TINYINT,
257+
let expected_compact = r#""employee_id" TINYINT,"full_name" VARCHAR,"salary" DECIMAL(8, 2),"details" STRUCT<"a": UNION<DECIMAL(5, 4),TINYINT>,"b": ARRAY<VARCHAR>,"c": DOUBLE>,"dependents" ARRAY<VARCHAR>"#;
258+
let expected_pretty = r#""employee_id" TINYINT,
261259
"full_name" VARCHAR,
262-
"salary" DECIMAL(8, 2)"#;
260+
"salary" DECIMAL(8, 2),
261+
"details" STRUCT<"a": UNION<DECIMAL(5, 4),TINYINT>,"b": ARRAY<VARCHAR>,"c": DOUBLE>,
262+
"dependents" ARRAY<VARCHAR>"#;
263263

264264
let ddl_compact = PartiqlBasicDdlEncoder::new(DdlFormat::Compact);
265265
assert_eq!(ddl_compact.ddl(&ty).expect("write shape"), expected_compact);

extension/partiql-extension-ddl/tests/ddl-tests.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,27 @@
1+
use indexmap::IndexSet;
12
use partiql_extension_ddl::ddl::{DdlFormat, PartiqlBasicDdlEncoder, PartiqlDdlEncoder};
23
use partiql_types::{bag, int, r#struct, str, struct_fields, StructConstraint, StructField};
34
use partiql_types::{BagType, PartiqlShape, Static, StructType};
4-
use std::collections::BTreeSet;
55

66
#[test]
77
fn basic_ddl_test() {
88
let details_fields = struct_fields![("age", int!())];
9-
let details = r#struct![BTreeSet::from([details_fields])];
9+
let details = r#struct![IndexSet::from([details_fields])];
1010
let fields = [
1111
StructField::new("id", int!()),
1212
StructField::new("name", str!()),
1313
StructField::new("address", PartiqlShape::new_non_nullable(Static::String)),
1414
StructField::new_optional("details", details.clone()),
1515
]
1616
.into();
17-
let shape = bag![r#struct![BTreeSet::from([
17+
let shape = bag![r#struct![IndexSet::from([
1818
StructConstraint::Fields(fields),
1919
StructConstraint::Open(false)
2020
])]];
2121

2222
let ddl_compact = PartiqlBasicDdlEncoder::new(DdlFormat::Compact);
2323
let actual = ddl_compact.ddl(&shape).expect("ddl_output");
24-
let expected = r#""address" VARCHAR NOT NULL,"id" INT,"name" VARCHAR,"details" OPTIONAL STRUCT<"age": INT>"#;
24+
let expected = r#""id" INT,"name" VARCHAR,"address" VARCHAR NOT NULL,"details" OPTIONAL STRUCT<"age": INT>"#;
2525

2626
println!("Actual: {actual}");
2727
println!("Expected: {expected}");

partiql-ast-passes/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ partiql-types = { path = "../partiql-types", version = "0.8.*" }
2626

2727
assert_matches = "1.5.*"
2828
fnv = "1"
29-
indexmap = "1.9"
29+
indexmap = "2.2"
3030
thiserror = "1.0"
3131

3232
[dev-dependencies]

partiql-ast/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ path = "src/lib.rs"
2020
bench = false
2121

2222
[dependencies]
23-
indexmap = { version = "1.9", default-features = false }
23+
indexmap = "2.2"
2424
rust_decimal = { version = "1.25.0", default-features = false, features = ["std"] }
2525
serde = { version = "1.*", features = ["derive"], optional = true }
2626
pretty = "0.12"

partiql-logical-planner/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ ion-rs = "0.18"
3434
ordered-float = "3.*"
3535
itertools = "0.10.*"
3636
unicase = "2.6"
37-
indexmap = "1.9"
37+
indexmap = "2.2"
3838
petgraph = "0.6.*"
3939
num = "0.4"
4040
fnv = "1"

partiql-logical-planner/src/typer.rs

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::typer::LookupOrder::{GlobalLocal, LocalGlobal};
2-
use indexmap::IndexMap;
2+
use indexmap::{IndexMap, IndexSet};
33
use partiql_ast::ast::{CaseSensitivity, SymbolPrimitive};
44
use partiql_catalog::Catalog;
55
use partiql_logical::{BindingsOp, LogicalPlan, OpId, PathComponent, ValueExpr, VarRefType};
@@ -11,7 +11,7 @@ use partiql_value::{BindingsName, Value};
1111
use petgraph::algo::toposort;
1212
use petgraph::graph::NodeIndex;
1313
use petgraph::prelude::StableGraph;
14-
use std::collections::{BTreeSet, HashMap};
14+
use std::collections::HashMap;
1515
use thiserror::Error;
1616

1717
#[macro_export]
@@ -218,7 +218,7 @@ impl<'c> PlanTyper<'c> {
218218
StructField::new(k.as_str(), self.get_singleton_type_from_env())
219219
});
220220

221-
let ty = PartiqlShape::new_struct(StructType::new(BTreeSet::from([
221+
let ty = PartiqlShape::new_struct(StructType::new(IndexSet::from([
222222
StructConstraint::Fields(fields.collect()),
223223
])));
224224

@@ -686,7 +686,7 @@ mod tests {
686686

687687
// Open Schema with `Strict` typing mode and `age` in nested attribute.
688688
let details_fields = struct_fields![("age", int!())];
689-
let details = r#struct![BTreeSet::from([details_fields])];
689+
let details = r#struct![IndexSet::from([details_fields])];
690690

691691
assert_query_typing(
692692
TypingMode::Strict,
@@ -731,7 +731,7 @@ mod tests {
731731
fn simple_sfw_with_alias() {
732732
// Open Schema with `Strict` typing mode and `age` in nested attribute.
733733
let details_fields = struct_fields![("age", int!())];
734-
let details = r#struct![BTreeSet::from([details_fields])];
734+
let details = r#struct![IndexSet::from([details_fields])];
735735

736736
// TODO Revise this behavior once the following discussion is conclusive and spec. is
737737
// in place: https://github.com/partiql/partiql-spec/discussions/65
@@ -775,7 +775,7 @@ mod tests {
775775
#[test]
776776
fn simple_sfw_err() {
777777
// Closed Schema with `Strict` typing mode and `age` non-existent projection.
778-
let err1 = r#"No Typing Information for SymbolPrimitive { value: "age", case: CaseInsensitive } in closed Schema Static(StaticType { ty: Struct(StructType { constraints: {Open(false), Fields({StructField { optional: false, name: "id", ty: Static(StaticType { ty: Int, nullable: true }) }, StructField { optional: false, name: "name", ty: Static(StaticType { ty: String, nullable: true }) }})} }), nullable: true })"#;
778+
let err1 = r#"No Typing Information for SymbolPrimitive { value: "age", case: CaseInsensitive } in closed Schema Static(StaticType { ty: Struct(StructType { constraints: {Fields({StructField { optional: false, name: "id", ty: Static(StaticType { ty: Int, nullable: true }) }, StructField { optional: false, name: "name", ty: Static(StaticType { ty: String, nullable: true }) }}), Open(false)} }), nullable: true })"#;
779779

780780
assert_err(
781781
assert_query_typing(
@@ -792,7 +792,7 @@ mod tests {
792792
vec![],
793793
),
794794
vec![TypingError::TypeCheck(err1.to_string())],
795-
Some(bag![r#struct![BTreeSet::from([StructConstraint::Fields(
795+
Some(bag![r#struct![IndexSet::from([StructConstraint::Fields(
796796
[
797797
StructField::new("id", int!()),
798798
StructField::new("name", str!()),
@@ -804,12 +804,12 @@ mod tests {
804804

805805
// Closed Schema with `Strict` typing mode and `bar` non-existent projection from closed nested `details`.
806806
let details_fields = struct_fields![("age", int!())];
807-
let details = r#struct![BTreeSet::from([
807+
let details = r#struct![IndexSet::from([
808808
details_fields,
809809
StructConstraint::Open(false)
810810
])];
811811

812-
let err1 = r#"No Typing Information for SymbolPrimitive { value: "details", case: CaseInsensitive } in closed Schema Static(StaticType { ty: Struct(StructType { constraints: {Open(false), Fields({StructField { optional: false, name: "age", ty: Static(StaticType { ty: Int, nullable: true }) }})} }), nullable: true })"#;
812+
let err1 = r#"No Typing Information for SymbolPrimitive { value: "details", case: CaseInsensitive } in closed Schema Static(StaticType { ty: Struct(StructType { constraints: {Fields({StructField { optional: false, name: "age", ty: Static(StaticType { ty: Int, nullable: true }) }}), Open(false)} }), nullable: true })"#;
813813
let err2 = r"Illegal Derive Type Undefined";
814814

815815
assert_err(
@@ -831,7 +831,7 @@ mod tests {
831831
TypingError::TypeCheck(err1.to_string()),
832832
TypingError::IllegalState(err2.to_string()),
833833
],
834-
Some(bag![r#struct![BTreeSet::from([StructConstraint::Fields(
834+
Some(bag![r#struct![IndexSet::from([StructConstraint::Fields(
835835
[
836836
StructField::new("id", int!()),
837837
StructField::new("name", str!()),
@@ -863,8 +863,8 @@ mod tests {
863863
};
864864
}
865865

866-
fn create_customer_schema(is_open: bool, fields: BTreeSet<StructField>) -> PartiqlShape {
867-
bag![r#struct![BTreeSet::from([
866+
fn create_customer_schema(is_open: bool, fields: IndexSet<StructField>) -> PartiqlShape {
867+
bag![r#struct![IndexSet::from([
868868
StructConstraint::Fields(fields),
869869
StructConstraint::Open(is_open)
870870
])]]
@@ -876,7 +876,7 @@ mod tests {
876876
schema: PartiqlShape,
877877
expected_fields: Vec<StructField>,
878878
) -> Result<(), TypeErr> {
879-
let expected_fields: BTreeSet<_> = expected_fields.into_iter().collect();
879+
let expected_fields: IndexSet<_> = expected_fields.into_iter().collect();
880880
let actual = type_query(mode, query, TypeEnvEntry::new("customers", &[], schema))?
881881
.expect_static()?;
882882

@@ -887,7 +887,7 @@ mod tests {
887887

888888
let f: Vec<_> = expected_fields
889889
.iter()
890-
.filter(|f| !fields.contains(f))
890+
.filter(|f| !fields.contains(*f))
891891
.collect();
892892
assert!(f.is_empty());
893893
assert_eq!(expected_fields.len(), fields.len());

partiql-types/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,5 +29,9 @@ unicase = "2.6"
2929
miette = { version ="7.2.*", features = ["fancy"] }
3030
thiserror = "1.*"
3131

32+
indexmap = "2.2"
33+
34+
derivative = "2.2"
35+
3236
[dev-dependencies]
3337
criterion = "0.4"

partiql-types/src/lib.rs

Lines changed: 35 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
#![deny(rust_2018_idioms)]
22
#![deny(clippy::all)]
33

4+
use derivative::Derivative;
5+
use indexmap::IndexSet;
46
use itertools::Itertools;
57
use miette::Diagnostic;
6-
use std::collections::BTreeSet;
78
use std::fmt::{Debug, Display, Formatter};
8-
use std::hash::Hash;
9+
use std::hash::{Hash, Hasher};
910
use thiserror::Error;
1011

1112
#[derive(Debug, Clone, Eq, PartialEq, Hash, Error, Diagnostic)]
@@ -23,6 +24,16 @@ pub trait Type {}
2324

2425
impl Type for StaticType {}
2526

27+
fn indexset_hash<H, T>(set: &IndexSet<T>, state: &mut H)
28+
where
29+
H: Hasher,
30+
T: Hash,
31+
{
32+
for val in set {
33+
val.hash(state)
34+
}
35+
}
36+
2637
#[macro_export]
2738
macro_rules! dynamic {
2839
() => {
@@ -140,7 +151,7 @@ macro_rules! undefined {
140151
}
141152

142153
/// Represents a PartiQL Shape
143-
#[derive(Debug, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
154+
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
144155
// With this implementation `Dynamic` and `AnyOf` cannot have `nullability`; this does not mean their
145156
// `null` value at runtime cannot belong to their domain.
146157
// TODO adopt the correct model Pending PartiQL Types semantics finalization: https://github.com/partiql/partiql-lang/issues/18
@@ -151,13 +162,13 @@ pub enum PartiqlShape {
151162
Undefined,
152163
}
153164

154-
#[derive(Debug, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
165+
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
155166
pub struct StaticType {
156167
ty: Static,
157168
nullable: bool,
158169
}
159170

160-
#[derive(Debug, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
171+
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
161172
pub enum Static {
162173
// Scalar Types
163174
Int,
@@ -506,15 +517,17 @@ impl Display for PartiqlShape {
506517
}
507518
}
508519

509-
#[derive(Hash, Eq, PartialEq, Debug, Clone, Ord, PartialOrd)]
520+
#[derive(Derivative, Eq, Debug, Clone)]
521+
#[derivative(PartialEq, Hash)]
510522
#[allow(dead_code)]
511523
pub struct AnyOf {
512-
types: BTreeSet<PartiqlShape>,
524+
#[derivative(Hash(hash_with = "indexset_hash"))]
525+
types: IndexSet<PartiqlShape>,
513526
}
514527

515528
impl AnyOf {
516529
#[must_use]
517-
pub const fn new(types: BTreeSet<PartiqlShape>) -> Self {
530+
pub const fn new(types: IndexSet<PartiqlShape>) -> Self {
518531
AnyOf { types }
519532
}
520533

@@ -531,15 +544,17 @@ impl FromIterator<PartiqlShape> for AnyOf {
531544
}
532545
}
533546

534-
#[derive(Debug, Clone, Hash, PartialEq, Eq, Ord, PartialOrd)]
547+
#[derive(Derivative, Eq, Debug, Clone)]
548+
#[derivative(PartialEq, Hash)]
535549
#[allow(dead_code)]
536550
pub struct StructType {
537-
constraints: BTreeSet<StructConstraint>,
551+
#[derivative(Hash(hash_with = "indexset_hash"))]
552+
constraints: IndexSet<StructConstraint>,
538553
}
539554

540555
impl StructType {
541556
#[must_use]
542-
pub fn new(constraints: BTreeSet<StructConstraint>) -> Self {
557+
pub fn new(constraints: IndexSet<StructConstraint>) -> Self {
543558
StructType { constraints }
544559
}
545560

@@ -551,7 +566,7 @@ impl StructType {
551566
}
552567

553568
#[must_use]
554-
pub fn fields(&self) -> BTreeSet<StructField> {
569+
pub fn fields(&self) -> IndexSet<StructField> {
555570
self.constraints
556571
.iter()
557572
.flat_map(|c| {
@@ -575,17 +590,18 @@ impl StructType {
575590
}
576591
}
577592

578-
#[derive(Debug, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
593+
#[derive(Derivative, Eq, Debug, Clone)]
594+
#[derivative(PartialEq, Hash)]
579595
#[allow(dead_code)]
580596
#[non_exhaustive]
581597
pub enum StructConstraint {
582598
Open(bool),
583599
Ordered(bool),
584600
DuplicateAttrs(bool),
585-
Fields(BTreeSet<StructField>),
601+
Fields(#[derivative(Hash(hash_with = "indexset_hash"))] IndexSet<StructField>),
586602
}
587603

588-
#[derive(Debug, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
604+
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
589605
#[allow(dead_code)]
590606
pub struct StructField {
591607
optional: bool,
@@ -648,7 +664,8 @@ impl From<(&str, PartiqlShape, bool)> for StructField {
648664
}
649665
}
650666

651-
#[derive(Debug, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
667+
#[derive(Derivative, Eq, Debug, Clone)]
668+
#[derivative(PartialEq, Hash)]
652669
#[allow(dead_code)]
653670
pub struct BagType {
654671
element_type: Box<PartiqlShape>,
@@ -671,7 +688,8 @@ impl BagType {
671688
}
672689
}
673690

674-
#[derive(Debug, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
691+
#[derive(Derivative, Eq, Debug, Clone)]
692+
#[derivative(PartialEq, Hash)]
675693
#[allow(dead_code)]
676694
pub struct ArrayType {
677695
element_type: Box<PartiqlShape>,

0 commit comments

Comments
 (0)