Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support partial index #478

Merged
merged 8 commits into from
Sep 28, 2024
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/diesel.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ jobs:
steps:
- uses: actions/checkout@v3
- uses: dtolnay/rust-toolchain@stable
- run: cargo update --manifest-path sea-query-diesel/Cargo.toml --workspace -p bigdecimal:0.4.2 --precise 0.3.1
- run: cargo update --manifest-path sea-query-diesel/Cargo.toml --workspace -p bigdecimal:0.4.5 --precise 0.3.1
- run: cargo build --manifest-path sea-query-diesel/Cargo.toml --workspace --features postgres,sqlite,mysql --features=with-chrono,with-json,with-rust_decimal,with-bigdecimal,with-uuid,with-time,with-ipnetwork,with-mac_address,postgres-array
- run: cargo build --manifest-path sea-query-diesel/Cargo.toml --workspace --features postgres,sqlite,mysql --features=with-chrono
- run: cargo build --manifest-path sea-query-diesel/Cargo.toml --workspace --features postgres,sqlite,mysql --features=with-json
Expand Down Expand Up @@ -157,6 +157,6 @@ jobs:
steps:
- uses: actions/checkout@v3
- uses: dtolnay/rust-toolchain@stable
- run: cargo update --manifest-path examples/${{ matrix.example }}/Cargo.toml -p bigdecimal:0.4.2 --precise 0.3.1
- run: cargo update --manifest-path examples/${{ matrix.example }}/Cargo.toml -p bigdecimal:0.4.5 --precise 0.3.1
- run: cargo build --manifest-path examples/${{ matrix.example }}/Cargo.toml
- run: cargo run --manifest-path examples/${{ matrix.example }}/Cargo.toml
2 changes: 1 addition & 1 deletion examples/diesel_postgres/Readme.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# SeaQuery Diesel Postgres example

> WARN: If you enable `with-bigdecimal`, you HAVE to update the version used by default by `diesel`
> otherwise it will fail to build. Use `cargo update -p bigdecimal:0.4.2 --precise 0.3.1`.
> otherwise it will fail to build. Use `cargo update -p bigdecimal:0.4.5 --precise 0.3.1`.

Running:

Expand Down
12 changes: 11 additions & 1 deletion examples/diesel_sqlite/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ use diesel::deserialize::{self, FromSql};
use diesel::sql_types::BigInt;
use diesel::sql_types::{Blob, Text};
use diesel::{Connection, QueryableByName, RunQueryDsl, SqliteConnection};
use sea_query::{Alias, ColumnDef, Expr, Func, Iden, Order, Query, SqliteQueryBuilder, Table};
use sea_query::{
Alias, ColumnDef, ConditionalStatement, Expr, Func, Iden, Index, Order, Query,
SqliteQueryBuilder, Table,
};
use sea_query_diesel::DieselBinder;
use serde_json::json;
use time::macros::{date, time};
Expand Down Expand Up @@ -40,6 +43,13 @@ fn main() {
.col(ColumnDef::new(Character::Meta).json().not_null())
.col(ColumnDef::new(Character::Created).date_time())
.build(SqliteQueryBuilder),
Index::create()
.name("partial_index_small_font")
.if_not_exists()
.table(Character::Table)
.col(Character::FontSize)
.and_where(Expr::col(Character::FontSize).lt(11).not())
.build(SqliteQueryBuilder),
]
.join("; ");

Expand Down
4 changes: 2 additions & 2 deletions examples/postgres/Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ cargo run

Example output:
```
DROP TABLE IF EXISTS "document"; CREATE TABLE IF NOT EXISTS "document" ( "id" serial NOT NULL PRIMARY KEY, "json_field" jsonb, "timestamp" timestamp )
Create table document: ()
DROP TABLE IF EXISTS "document"; CREATE TABLE IF NOT EXISTS "document" ( "id" serial NOT NULL PRIMARY KEY, "uuid" uuid, "json_field" jsonb, "timestamp" timestamp, "timestamp_with_time_zone" timestamp with time zone, "decimal" decimal, "array" integer[] ); CREATE INDEX "partial_index_small_decimal" ON "document" ("decimal") WHERE NOT "decimal" < 11
Create table document: Ok(())

Insert into document: Ok(1)

Expand Down
11 changes: 10 additions & 1 deletion examples/postgres/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ use chrono::{DateTime, FixedOffset, NaiveDate, NaiveDateTime};

use postgres::{Client, NoTls, Row};
use rust_decimal::Decimal;
use sea_query::{ColumnDef, ColumnType, Iden, Order, PostgresQueryBuilder, Query, Table};
use sea_query::{
ColumnDef, ColumnType, ConditionalStatement, Expr, Iden, Index, Order, PostgresQueryBuilder,
Query, Table,
};
use sea_query_postgres::PostgresBinder;
use time::{
macros::{date, offset, time},
Expand Down Expand Up @@ -37,6 +40,12 @@ fn main() {
.col(ColumnDef::new(Document::Decimal).decimal())
.col(ColumnDef::new(Document::Array).array(ColumnType::Integer))
.build(PostgresQueryBuilder),
Index::create()
.name("partial_index_small_decimal")
.table(Document::Table)
.col(Document::Decimal)
.and_where(Expr::col(Document::Decimal).lt(11).not())
.build(PostgresQueryBuilder),
]
.join("; ");

Expand Down
12 changes: 10 additions & 2 deletions examples/rusqlite/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use chrono::{NaiveDate, NaiveDateTime};
use rusqlite::{Connection, Result, Row};
use sea_query::{ColumnDef, Expr, Func, Iden, Order, Query, SqliteQueryBuilder, Table};

use sea_query::{
ColumnDef, ConditionalStatement, Expr, Func, Iden, Index, Order, Query, SqliteQueryBuilder,
Table,
};
use sea_query_rusqlite::RusqliteBinder;
use serde_json::{json, Value as Json};
use time::{
Expand Down Expand Up @@ -37,6 +39,12 @@ fn main() -> Result<()> {
.col(ColumnDef::new(Character::Meta).json())
.col(ColumnDef::new(Character::Created).date_time())
.build(SqliteQueryBuilder),
Index::create()
.name("partial_index_small_font")
.table(Character::Table)
.col(Character::FontSize)
.and_where(Expr::col(Character::FontSize).lt(11).not())
.build(SqliteQueryBuilder),
]
.join("; ");

Expand Down
2 changes: 2 additions & 0 deletions examples/sqlx_postgres/Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ Example output:
```
Create table character: Ok(PgQueryResult { rows_affected: 0 })

Create partial index: Ok(PgQueryResult { rows_affected: 0 })

Insert into character: Ok(PgQueryResult { rows_affected: 1 })

Select one from character:
Expand Down
15 changes: 14 additions & 1 deletion examples/sqlx_postgres/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ use bigdecimal::{BigDecimal, FromPrimitive};
use chrono::{NaiveDate, NaiveDateTime};
use rust_decimal::Decimal;
use sea_query::{
ColumnDef, Expr, Func, Iden, OnConflict, Order, PostgresQueryBuilder, Query, Table,
ColumnDef, ConditionalStatement, Expr, Func, Iden, Index, OnConflict, Order,
PostgresQueryBuilder, Query, Table,
};
use sea_query_binder::SqlxBinder;
use sqlx::{PgPool, Row};
Expand Down Expand Up @@ -50,6 +51,18 @@ async fn main() {
let result = sqlx::query(&sql).execute(&mut *pool).await;
println!("Create table character: {result:?}\n");

// Partial Index
let partial_index = Index::create()
.if_not_exists()
.name("partial_index_small_font")
.table(Character::Table)
.col(Character::FontSize)
.and_where(Expr::col(Character::FontSize).lt(11).not())
.build(PostgresQueryBuilder);

let index = sqlx::query(&partial_index).execute(&mut *pool).await;
println!("Create partial index: {index:?}\n");

// Create

let (sql, values) = Query::insert()
Expand Down
2 changes: 2 additions & 0 deletions examples/sqlx_sqlite/Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ Example output:
```
Create table character: Ok(SqliteQueryResult { changes: 0, last_insert_rowid: 0 })

Create partial index: Ok(SqliteQueryResult { changes: 0, last_insert_rowid: 0 })

Insert into character: last_insert_id = 1

Select one from character:
Expand Down
16 changes: 15 additions & 1 deletion examples/sqlx_sqlite/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
use chrono::{NaiveDate, NaiveDateTime};
use sea_query::{ColumnDef, Expr, Func, Iden, OnConflict, Order, Query, SqliteQueryBuilder, Table};
use sea_query::{
ColumnDef, ConditionalStatement, Expr, Func, Iden, Index, OnConflict, Order, Query,
SqliteQueryBuilder, Table,
};
use sea_query_binder::SqlxBinder;
use serde_json::{json, Value as Json};
use sqlx::{Row, SqlitePool};
Expand Down Expand Up @@ -34,6 +37,17 @@ async fn main() {
let result = sqlx::query(&sql).execute(&pool).await;
println!("Create table character: {result:?}\n");

// Partial Index
let partial_index = Index::create()
.name("partial_index_small_font")
.table(Character::Table)
.col(Character::FontSize)
.and_where(Expr::col(Character::FontSize).lt(11).not())
.build(SqliteQueryBuilder);

let index = sqlx::query(&partial_index).execute(&pool).await;
println!("Create partial index: {index:?}\n");

// Create
let (sql, values) = Query::insert()
.into_table(Character::Table)
Expand Down
6 changes: 6 additions & 0 deletions src/backend/index_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ pub trait IndexBuilder: QuotedBuilder + TableRefBuilder {
self.prepare_index_prefix(create, sql);

self.prepare_index_columns(&create.index.columns, sql);

self.prepare_filter(&create.r#where, sql);
}

/// Translate [`IndexCreateStatement`] into SQL statement.
Expand Down Expand Up @@ -74,4 +76,8 @@ pub trait IndexBuilder: QuotedBuilder + TableRefBuilder {
});
write!(sql, ")").unwrap();
}

#[doc(hidden)]
// Write WHERE clause for partial index. This function is not available in MySQL.
fn prepare_filter(&self, _condition: &ConditionHolder, _sql: &mut dyn SqlWriter) {}
}
5 changes: 5 additions & 0 deletions src/backend/postgres/index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ impl IndexBuilder for PostgresQueryBuilder {
if create.nulls_not_distinct {
write!(sql, " NULLS NOT DISTINCT").unwrap();
}
self.prepare_filter(&create.r#where, sql);
}

fn prepare_table_ref_index_stmt(&self, table_ref: &TableRef, sql: &mut dyn SqlWriter) {
Expand Down Expand Up @@ -128,4 +129,8 @@ impl IndexBuilder for PostgresQueryBuilder {
write!(sql, "UNIQUE ").unwrap();
}
}

fn prepare_filter(&self, condition: &ConditionHolder, sql: &mut dyn SqlWriter) {
self.prepare_condition(condition, "WHERE", sql);
}
}
5 changes: 5 additions & 0 deletions src/backend/sqlite/index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ impl IndexBuilder for SqliteQueryBuilder {

write!(sql, " ").unwrap();
self.prepare_index_columns(&create.index.columns, sql);
self.prepare_filter(&create.r#where, sql);
}

fn prepare_table_ref_index_stmt(&self, table_ref: &TableRef, sql: &mut dyn SqlWriter) {
Expand Down Expand Up @@ -69,4 +70,8 @@ impl IndexBuilder for SqliteQueryBuilder {
}

fn write_column_index_prefix(&self, _col_prefix: &Option<u32>, _sql: &mut dyn SqlWriter) {}

fn prepare_filter(&self, condition: &ConditionHolder, sql: &mut dyn SqlWriter) {
self.prepare_condition(condition, "WHERE", sql);
}
}
50 changes: 49 additions & 1 deletion src/index/create.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use inherent::inherent;

use crate::{backend::SchemaBuilder, types::*, SchemaStatementBuilder};
use crate::{ConditionHolder, ConditionalStatement, IntoCondition};

use super::common::*;

Expand Down Expand Up @@ -148,6 +149,27 @@ use super::common::*;
/// r#"CREATE INDEX "idx-glyph-aspect" ON "glyph" ("aspect" ASC)"#
/// );
/// ```
///
/// Partial Index with prefix and order
/// ```
/// use sea_query::{tests_cfg::*, *};
///
/// let index = Index::create()
/// .name("idx-glyph-aspect")
/// .table(Glyph::Table)
/// .col((Glyph::Aspect, 64, IndexOrder::Asc))
/// .and_where(Expr::col((Glyph::Table, Glyph::Aspect)).is_in(vec![3, 4]))
/// .to_owned();
///
/// assert_eq!(
/// index.to_string(PostgresQueryBuilder),
/// r#"CREATE INDEX "idx-glyph-aspect" ON "glyph" ("aspect" (64) ASC) WHERE "glyph"."aspect" IN (3, 4)"#
/// );
/// assert_eq!(
/// index.to_string(SqliteQueryBuilder),
/// r#"CREATE INDEX "idx-glyph-aspect" ON "glyph" ("aspect" ASC) WHERE "glyph"."aspect" IN (3, 4)"#
/// );
/// ```
#[derive(Default, Debug, Clone)]
pub struct IndexCreateStatement {
pub(crate) table: Option<TableRef>,
Expand All @@ -157,6 +179,7 @@ pub struct IndexCreateStatement {
pub(crate) nulls_not_distinct: bool,
pub(crate) index_type: Option<IndexType>,
pub(crate) if_not_exists: bool,
pub(crate) r#where: ConditionHolder,
}

/// Specification of a table index
Expand All @@ -171,7 +194,16 @@ pub enum IndexType {
impl IndexCreateStatement {
/// Construct a new [`IndexCreateStatement`]
pub fn new() -> Self {
Self::default()
Self {
table: None,
index: Default::default(),
primary: false,
unique: false,
nulls_not_distinct: false,
index_type: None,
if_not_exists: false,
r#where: ConditionHolder::new(),
}
}

/// Create index if index not exists
Expand Down Expand Up @@ -263,6 +295,7 @@ impl IndexCreateStatement {
nulls_not_distinct: self.nulls_not_distinct,
index_type: self.index_type.take(),
if_not_exists: self.if_not_exists,
r#where: self.r#where.clone(),
}
}
}
Expand All @@ -283,3 +316,18 @@ impl SchemaStatementBuilder for IndexCreateStatement {

pub fn to_string<T: SchemaBuilder>(&self, schema_builder: T) -> String;
}

impl ConditionalStatement for IndexCreateStatement {
fn and_or_where(&mut self, condition: LogicalChainOper) -> &mut Self {
self.r#where.add_and_or(condition);
self
}

fn cond_where<C>(&mut self, condition: C) -> &mut Self
where
C: IntoCondition,
{
self.r#where.add_condition(condition.into_condition());
self
}
}
15 changes: 15 additions & 0 deletions tests/postgres/index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,21 @@ fn create_6() {
);
}

#[test]
fn create_7() {
assert_eq!(
Index::create()
.unique()
.nulls_not_distinct()
.name("partial-index-glyph-image-not-null")
.table(Glyph::Table)
.col(Glyph::Image)
.and_where(Expr::col(Glyph::Image).is_not_null())
.to_string(PostgresQueryBuilder),
r#"CREATE UNIQUE INDEX "partial-index-glyph-image-not-null" ON "glyph" ("image") NULLS NOT DISTINCT WHERE "image" IS NOT NULL"#
);
}

#[test]
fn drop_1() {
assert_eq!(
Expand Down
15 changes: 15 additions & 0 deletions tests/sqlite/index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,21 @@ fn create_3() {
);
}

#[test]
fn create_4() {
assert_eq!(
Index::create()
.if_not_exists()
.unique()
.name("partial-index-glyph-image-not-null")
.table(Glyph::Table)
.col(Glyph::Image)
.and_where(Expr::col(Glyph::Image).is_not_null())
.to_string(SqliteQueryBuilder),
r#"CREATE UNIQUE INDEX IF NOT EXISTS "partial-index-glyph-image-not-null" ON "glyph" ("image") WHERE "image" IS NOT NULL"#
);
}

#[test]
fn drop_1() {
assert_eq!(
Expand Down
Loading