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

overhaul our Postgres Range support #1050

Merged
merged 8 commits into from
Feb 22, 2023
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
3 changes: 3 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,9 @@ jobs:
- name: Run operators example tests
run: cargo test --package operators --features "pg$PG_VER" --no-default-features

- name: Run range example tests
run: cargo test --package range --features "pg$PG_VER" --no-default-features

- name: Run schemas example tests
run: cargo test --package schemas --features "pg$PG_VER" --no-default-features

Expand Down
8 changes: 8 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ members = [
"pgx-examples/numeric",
"pgx-examples/pgtrybuilder",
"pgx-examples/operators",
"pgx-examples/range",
"pgx-examples/schemas",
"pgx-examples/shmem",
"pgx-examples/spi",
Expand Down
72 changes: 39 additions & 33 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -163,39 +163,45 @@ cargo pgx init

### Mapping of Postgres types to Rust

Postgres Type | Rust Type (as `Option<T>`)
--------------|-----------
`bytea` | `Vec<u8>` or `&[u8]` (zero-copy)
`text` | `String` or `&str` (zero-copy)
`varchar` | `String` or `&str` (zero-copy) or `char`
`"char"` | `i8`
`smallint` | `i16`
`integer` | `i32`
`bigint` | `i64`
`oid` | `u32`
`real` | `f32`
`double precision` | `f64`
`bool` | `bool`
`json` | `pgx::Json(serde_json::Value)`
`jsonb` | `pgx::JsonB(serde_json::Value)`
`date` | `pgx::Date`
`time` | `pgx::Time`
`timestamp` | `pgx::Timestamp`
`time with time zone` | `pgx::TimeWithTimeZone`
`timestamp with time zone` | `pgx::TimestampWithTimeZone`
`anyarray` | `pgx::AnyArray`
`anyelement` | `pgx::AnyElement`
`box` | `pgx::pg_sys::BOX`
`point` | `pgx::pgx_sys::Point`
`tid` | `pgx::pg_sys::ItemPointerData`
`cstring` | `&core::ffi::CStr`
`inet` | `pgx::Inet(String)` -- TODO: needs better support
`numeric` | `pgx::Numeric<P, S> or pgx::AnyNumeric`
`void` | `()`
`ARRAY[]::<type>` | `Vec<Option<T>>` or `pgx::Array<T>` (zero-copy)
`NULL` | `Option::None`
`internal` | `pgx::PgBox<T>` where `T` is any Rust/Postgres struct
`uuid` | `pgx::Uuid([u8; 16])`
| Postgres Type | Rust Type (as `Option<T>`) |
|----------------------------|-------------------------------------------------------|
| `bytea` | `Vec<u8>` or `&[u8]` (zero-copy) |
| `text` | `String` or `&str` (zero-copy) |
| `varchar` | `String` or `&str` (zero-copy) or `char` |
| `"char"` | `i8` |
| `smallint` | `i16` |
| `integer` | `i32` |
| `bigint` | `i64` |
| `oid` | `u32` |
| `real` | `f32` |
| `double precision` | `f64` |
| `bool` | `bool` |
| `json` | `pgx::Json(serde_json::Value)` |
| `jsonb` | `pgx::JsonB(serde_json::Value)` |
eeeebbbbrrrr marked this conversation as resolved.
Show resolved Hide resolved
| `date` | `pgx::Date` |
| `time` | `pgx::Time` |
| `timestamp` | `pgx::Timestamp` |
| `time with time zone` | `pgx::TimeWithTimeZone` |
| `timestamp with time zone` | `pgx::TimestampWithTimeZone` |
| `anyarray` | `pgx::AnyArray` |
| `anyelement` | `pgx::AnyElement` |
| `box` | `pgx::pg_sys::BOX` |
| `point` | `pgx::pgx_sys::Point` |
| `tid` | `pgx::pg_sys::ItemPointerData` |
| `cstring` | `&core::ffi::CStr` |
| `inet` | `pgx::Inet(String)` -- TODO: needs better support |
eeeebbbbrrrr marked this conversation as resolved.
Show resolved Hide resolved
| `numeric` | `pgx::Numeric<P, S> or pgx::AnyNumeric` |
| `void` | `()` |
| `ARRAY[]::<type>` | `Vec<Option<T>>` or `pgx::Array<T>` (zero-copy) |
| `int4range` | `pgx::Range<i32>` |
| `int8range` | `pgx::Range<i64>` |
| `numrange` | `pgx::Range<Numeric<P, S>>` or `pgx::Range<AnyRange>` |
| `daterange` | `pgx::Range<pgx::Date>` |
| `tsrange` | `pgx::Range<pgx::Timestamp>` |
| `tstzrange` | `pgx::Range<pgx::TimestampWithTimeZone>` |
| `NULL` | `Option::None` |
| `internal` | `pgx::PgBox<T>` where `T` is any Rust/Postgres struct |
| `uuid` | `pgx::Uuid([u8; 16])` |
eeeebbbbrrrr marked this conversation as resolved.
Show resolved Hide resolved

There are also `IntoDatum` and `FromDatum` traits for implementing additional type conversions,
along with `#[derive(PostgresType)]` and `#[derive(PostgresEnum)]` for automatic conversion of
Expand Down
7 changes: 7 additions & 0 deletions pgx-examples/range/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.DS_Store
.idea/
/target
*.iml
**/*.rs.bk
Cargo.lock
sql/arrays-1.0.sql
32 changes: 32 additions & 0 deletions pgx-examples/range/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
[package]
name = "range"
version = "0.1.0"
edition = "2021"

[lib]
crate-type = ["cdylib"]

[features]
default = ["pg13"]
pg11 = ["pgx/pg11", "pgx-tests/pg11" ]
pg12 = ["pgx/pg12", "pgx-tests/pg12" ]
pg13 = ["pgx/pg13", "pgx-tests/pg13" ]
pg14 = ["pgx/pg14", "pgx-tests/pg14" ]
pg15 = ["pgx/pg15", "pgx-tests/pg15" ]
pg_test = []

[dependencies]
pgx = { path = "../../pgx", default-features = false }

[dev-dependencies]
pgx-tests = { path = "../../pgx-tests" }

# uncomment these if compiling outside of 'pgx'
# [profile.dev]
# panic = "unwind"

# [profile.release]
# panic = "unwind"
# opt-level = 3
# lto = "fat"
# codegen-units = 1
4 changes: 4 additions & 0 deletions pgx-examples/range/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Examples for using pgx' `Range<T>` support.

pgx supports the Postgres `int4range`, `int8range`, `numrange`, `daterange`, `tsrange`, and `tstzrange` types, safely
mapped to `pgx::Range<T>` where `T` is any of `i32`, `i64`, `Numeric<P, S>`, `AnyNumeric`, `Date`, `Timestamp`, and `TimestampWithTimeZone`.
6 changes: 6 additions & 0 deletions pgx-examples/range/range.control
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
comment = 'range: Created by pgx'
default_version = '0.1.0'
module_pathname = '$libdir/range'
relocatable = false
superuser = false
schema = range
57 changes: 57 additions & 0 deletions pgx-examples/range/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
Portions Copyright 2019-2021 ZomboDB, LLC.
Portions Copyright 2021-2022 Technology Concepts & Design, Inc. <[email protected]>

All rights reserved.

Use of this source code is governed by the MIT license that can be found in the LICENSE file.
*/

use pgx::prelude::*;

pgx::pg_module_magic!();

#[pg_extern]
fn range(s: i32, e: i32) -> pgx::Range<i32> {
(s..e).into()
}

#[pg_extern]
fn range_from(s: i32) -> pgx::Range<i32> {
(s..).into()
}

#[pg_extern]
fn range_full() -> pgx::Range<i32> {
(..).into()
}

#[pg_extern]
fn range_inclusive(s: i32, e: i32) -> pgx::Range<i32> {
(s..=e).into()
}

#[pg_extern]
fn range_to(e: i32) -> pgx::Range<i32> {
(..e).into()
}

#[pg_extern]
fn range_to_inclusive(e: i32) -> pgx::Range<i32> {
(..=e).into()
}

#[pg_extern]
fn empty() -> pgx::Range<i32> {
pgx::Range::empty()
}

#[pg_extern]
fn infinite() -> pgx::Range<i32> {
pgx::Range::infinite()
}

#[pg_extern]
fn assert_range(r: pgx::Range<i32>, s: i32, e: i32) -> bool {
r == (s..e).into()
}
36 changes: 5 additions & 31 deletions pgx-tests/src/tests/range_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ Use of this source code is governed by the MIT license that can be found in the
*/

use pgx::prelude::*;
use pgx::RangeBound;

#[pg_extern]
fn accept_range_i32(range: Range<i32>) -> Range<i32> {
Expand Down Expand Up @@ -43,43 +44,16 @@ fn range_round_trip_values<T>(range: Range<T>) -> Range<T>
where
T: FromDatum + IntoDatum + RangeSubType,
{
let range_data: RangeData<T> = range.into();
if range_data.is_empty {
RangeData::<T>::empty_range_data().into()
} else {
let range_data = RangeData::<T>::from_range_values(
range_data.lower_val(),
range_data.upper_val(),
range_data.lower.inclusive,
range_data.upper.inclusive,
);
range_data.into()
}
range
}

fn range_round_trip_bounds<T>(range: Range<T>) -> Range<T>
where
T: FromDatum + IntoDatum + RangeSubType,
{
let range_data: RangeData<T> = range.into();
if range_data.is_empty {
RangeData::<T>::empty_range_data().into()
} else {
let mut lower_bound = pg_sys::RangeBound::default();
lower_bound.lower = true;
lower_bound.inclusive = range_data.lower.inclusive;
lower_bound.infinite = range_data.lower.infinite;
lower_bound.val = range_data.lower.val.clone();

let mut upper_bound = pg_sys::RangeBound::default();
upper_bound.lower = false;
upper_bound.inclusive = range_data.upper.inclusive;
upper_bound.infinite = range_data.upper.infinite;
upper_bound.val = range_data.upper.val.clone();

let range_data = RangeData::<T>::from_range_bounds(lower_bound, upper_bound);

range_data.into()
match range.as_ref() {
None => Range::empty(),
Some((l, u)) => Range::new(l, u),
}
}

Expand Down
Loading