Skip to content

Commit

Permalink
feat(psql): introduce column type ltree (#604)
Browse files Browse the repository at this point in the history
* feat: introduce `LTree` column type

* feat: the `ltree` type
  • Loading branch information
EstebanBorai authored Oct 4, 2023
1 parent 7f5f1af commit cc8e78c
Show file tree
Hide file tree
Showing 8 changed files with 95 additions and 2 deletions.
1 change: 1 addition & 0 deletions src/backend/mysql/table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ impl TableBuilder for MysqlQueryBuilder {
ColumnType::Cidr => unimplemented!("Cidr is not available in MySQL."),
ColumnType::Inet => unimplemented!("Inet is not available in MySQL."),
ColumnType::MacAddr => unimplemented!("MacAddr is not available in MySQL."),
ColumnType::LTree => unimplemented!("LTree is not available in MySQL."),
}
)
.unwrap();
Expand Down
1 change: 1 addition & 0 deletions src/backend/postgres/table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ impl TableBuilder for PostgresQueryBuilder {
ColumnType::Inet => "inet".into(),
ColumnType::MacAddr => "macaddr".into(),
ColumnType::Year(_) => unimplemented!("Year is not available in Postgres."),
ColumnType::LTree => "ltree".into(),
}
)
.unwrap()
Expand Down
1 change: 1 addition & 0 deletions src/backend/sqlite/table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ impl TableBuilder for SqliteQueryBuilder {
ColumnType::Year(_) => unimplemented!("Year is not available in Sqlite."),
ColumnType::Bit(_) => unimplemented!("Bit is not available in Sqlite."),
ColumnType::VarBit(_) => unimplemented!("VarBit is not available in Sqlite."),
ColumnType::LTree => unimplemented!("LTree is not available in Sqlite."),
}
)
.unwrap()
Expand Down
57 changes: 57 additions & 0 deletions src/extension/postgres/ltree.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
use std::fmt;

/// PostgreSQL `ltree` extension type.
///
/// `ltree` stores a raber path which in this struct is represented as the
/// tuple's first value.
///
/// # PostcreSQL Reference
///
/// The following set of SQL statements can be used to create a table with
/// a `ltree` column. Here the `ltree` column is called `path`.
///
/// The `path` column is then populated to generate the tree.
///
/// ```ignore
/// CREATE TABLE test (path ltree);
/// INSERT INTO test VALUES ('Top');
/// INSERT INTO test VALUES ('Top.Science');
/// INSERT INTO test VALUES ('Top.Science.Astronomy');
/// INSERT INTO test VALUES ('Top.Science.Astronomy.Astrophysics');
/// INSERT INTO test VALUES ('Top.Science.Astronomy.Cosmology');
/// INSERT INTO test VALUES ('Top.Hobbies');
/// INSERT INTO test VALUES ('Top.Hobbies.Amateurs_Astronomy');
/// INSERT INTO test VALUES ('Top.Collections');
/// INSERT INTO test VALUES ('Top.Collections.Pictures');
/// INSERT INTO test VALUES ('Top.Collections.Pictures.Astronomy');
/// INSERT INTO test VALUES ('Top.Collections.Pictures.Astronomy.Stars');
/// INSERT INTO test VALUES ('Top.Collections.Pictures.Astronomy.Galaxies');
/// INSERT INTO test VALUES ('Top.Collections.Pictures.Astronomy.Astronauts');
/// CREATE INDEX path_gist_idx ON test USING GIST (path);
/// CREATE INDEX path_idx ON test USING BTREE (path);
/// ```
///
/// The set of queries above will generate the following tree:
///
/// ```ignore
/// Top
/// / | \
/// Science Hobbies Collections
/// / | \
/// Astronomy Amateurs_Astronomy Pictures
/// / \ |
/// Astrophysics Cosmology Astronomy
/// / | \
/// Galaxies Stars Astronauts
/// ```
/// [Source][1]
///
/// [1]: https://www.postgresql.org/docs/current/ltree.html
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct PgLTree;

impl fmt::Display for PgLTree {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "ltree")
}
}
2 changes: 2 additions & 0 deletions src/extension/postgres/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ pub use expr::*;
pub use extension::*;
pub use func::*;
pub use interval::*;
pub use ltree::*;
pub use types::*;

use crate::types::BinOper;
Expand All @@ -10,6 +11,7 @@ pub(crate) mod expr;
pub(crate) mod extension;
pub(crate) mod func;
pub(crate) mod interval;
pub(crate) mod ltree;
pub(crate) mod types;

/// Binary operator
Expand Down
8 changes: 8 additions & 0 deletions src/table/column.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ pub enum ColumnType {
Cidr,
Inet,
MacAddr,
LTree,
}

impl PartialEq for ColumnType {
Expand Down Expand Up @@ -558,6 +559,13 @@ impl ColumnDef {
self
}

/// Set column type as `ltree`
/// This is only supported on Postgres.
pub fn ltree(&mut self) -> &mut Self {
self.types = Some(ColumnType::LTree);
self
}

/// Set constraints as SimpleExpr
///
/// ```
Expand Down
3 changes: 1 addition & 2 deletions tests/mysql/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -995,8 +995,7 @@ fn select_58() {
.and_where(Expr::col(Char::Character).like(LikeExpr::new("A").escape('\\')))
.build(MysqlQueryBuilder),
(
r#"SELECT `character` FROM `character` WHERE `character` LIKE ? ESCAPE '\\'"#
.to_owned(),
r"SELECT `character` FROM `character` WHERE `character` LIKE ? ESCAPE '\\'".to_owned(),
Values(vec!["A".into()])
)
);
Expand Down
24 changes: 24 additions & 0 deletions tests/postgres/table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -597,3 +597,27 @@ fn alter_with_check_constraint() {
r#"ALTER TABLE "glyph" ADD COLUMN "aspect" integer NOT NULL DEFAULT 101 CHECK ("aspect" > 100)"#,
);
}

#[test]
fn create_16() {
assert_eq!(
Table::create()
.table(Glyph::Table)
.col(
ColumnDef::new(Glyph::Id)
.integer()
.not_null()
.auto_increment()
.primary_key()
)
.col(ColumnDef::new(Glyph::Tokens).ltree())
.to_string(PostgresQueryBuilder),
[
r#"CREATE TABLE "glyph" ("#,
r#""id" serial NOT NULL PRIMARY KEY,"#,
r#""tokens" ltree"#,
r#")"#,
]
.join(" ")
);
}

0 comments on commit cc8e78c

Please sign in to comment.