Skip to content

Commit

Permalink
fix: handle native types for joined queries
Browse files Browse the repository at this point in the history
  • Loading branch information
Weakky committed Dec 8, 2023
1 parent 1964a5c commit c667de4
Show file tree
Hide file tree
Showing 12 changed files with 526 additions and 12 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

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

2 changes: 2 additions & 0 deletions psl/builtin-connectors/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,5 @@ indoc.workspace = true
lsp-types = "0.91.1"
once_cell = "1.3"
regex = "1"
chrono = { version = "0.4.6", default_features = false }
bigdecimal = "0.3"
45 changes: 45 additions & 0 deletions psl/builtin-connectors/src/cockroach_datamodel_connector.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
mod native_types;
mod validations;

use bigdecimal::{BigDecimal, ParseBigDecimalError};
pub use native_types::CockroachType;

use chrono::*;
use enumflags2::BitFlags;
use lsp_types::{CompletionItem, CompletionItemKind, CompletionList};
use psl_core::{
Expand Down Expand Up @@ -307,6 +309,49 @@ impl Connector for CockroachDatamodelConnector {
fn flavour(&self) -> Flavour {
Flavour::Cockroach
}

fn coerce_json_datetime(
&self,
str: &str,
nt: Option<NativeTypeInstance>,
) -> chrono::ParseResult<chrono::DateTime<FixedOffset>> {
let native_type: Option<&CockroachType> = nt.as_ref().map(|nt| nt.downcast_ref());

match native_type {
Some(ct) => match ct {
CockroachType::Timestamptz(_) => crate::utils::parse_timestamptz(str),
CockroachType::Timestamp(_) => crate::utils::parse_timestamp(str),
CockroachType::Date => crate::utils::parse_date(str),
CockroachType::Time(_) => crate::utils::parse_time(str),
CockroachType::Timetz(_) => {
// We currently don't support time with timezone.
// We strip the timezone information and parse it as a time.
// This is inline with what Quaint does already.
let time_without_tz = str.split("+").next().unwrap();

crate::utils::parse_time(time_without_tz)
}
_ => unreachable!(),
},
None => crate::utils::parse_timestamptz(str),
}
}

fn coerce_json_decimal(
&self,
str: &str,
nt: Option<NativeTypeInstance>,
) -> Result<BigDecimal, ParseBigDecimalError> {
let native_type: Option<&CockroachType> = nt.as_ref().map(|nt| nt.downcast_ref());

match native_type {
Some(pt) => match pt {
CockroachType::Decimal(_) => crate::utils::parse_decimal(str),
_ => unreachable!(),
},
None => crate::utils::parse_decimal(str),
}
}
}

/// An `@default(sequence())` function.
Expand Down
1 change: 1 addition & 0 deletions psl/builtin-connectors/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ mod mysql_datamodel_connector;
mod native_type_definition;
mod postgres_datamodel_connector;
mod sqlite_datamodel_connector;
mod utils;

use psl_core::{datamodel_connector::Connector, ConnectorRegistry};

Expand Down
43 changes: 43 additions & 0 deletions psl/builtin-connectors/src/postgres_datamodel_connector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ mod validations;

pub use native_types::PostgresType;

use bigdecimal::{BigDecimal, ParseBigDecimalError};
use chrono::*;
use enumflags2::BitFlags;
use lsp_types::{CompletionItem, CompletionItemKind, CompletionList, InsertTextFormat};
use psl_core::{
Expand Down Expand Up @@ -567,6 +569,47 @@ impl Connector for PostgresDatamodelConnector {
fn flavour(&self) -> Flavour {
Flavour::Postgres
}

fn coerce_json_datetime(
&self,
str: &str,
nt: Option<NativeTypeInstance>,
) -> chrono::ParseResult<chrono::DateTime<FixedOffset>> {
let native_type: Option<&PostgresType> = nt.as_ref().map(|nt| nt.downcast_ref());

match native_type {
Some(pt) => match pt {
Timestamptz(_) => crate::utils::parse_timestamptz(str),
Timestamp(_) => crate::utils::parse_timestamp(str),
Date => crate::utils::parse_date(str),
Time(_) => crate::utils::parse_time(str),
Timetz(_) => {
let time_without_tz = str.split('+').next().unwrap();

crate::utils::parse_time(time_without_tz)
}
_ => unreachable!(),
},
None => crate::utils::parse_timestamptz(str),
}
}

fn coerce_json_decimal(
&self,
str: &str,
nt: Option<NativeTypeInstance>,
) -> Result<BigDecimal, ParseBigDecimalError> {
let native_type: Option<&PostgresType> = nt.as_ref().map(|nt| nt.downcast_ref());

match native_type {
Some(pt) => match pt {
Decimal(_) => crate::utils::parse_decimal(str),
Money => crate::utils::parse_money(str),
_ => unreachable!(),
},
None => crate::utils::parse_decimal(str),
}
}
}

fn allowed_index_operator_classes(algo: IndexAlgorithm, field: walkers::ScalarFieldWalker<'_>) -> Vec<OperatorClass> {
Expand Down
39 changes: 39 additions & 0 deletions psl/builtin-connectors/src/utils.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
use bigdecimal::{BigDecimal, ParseBigDecimalError};
use chrono::*;
use std::str::FromStr;

pub(crate) fn parse_date(str: &str) -> Result<DateTime<FixedOffset>, chrono::ParseError> {
chrono::NaiveDate::parse_from_str(str, "%Y-%m-%d")
.map(|date| DateTime::<Utc>::from_utc(date.and_hms_opt(0, 0, 0).unwrap(), Utc))
.map(DateTime::<FixedOffset>::from)
}

pub(crate) fn parse_timestamptz(str: &str) -> Result<DateTime<FixedOffset>, chrono::ParseError> {
DateTime::parse_from_rfc3339(str)
}

pub(crate) fn parse_timestamp(str: &str) -> Result<DateTime<FixedOffset>, chrono::ParseError> {
NaiveDateTime::parse_from_str(str, "%Y-%m-%dT%H:%M:%S%.f")
.map(|dt| DateTime::from_utc(dt, Utc))
.or_else(|_| DateTime::parse_from_rfc3339(str).map(DateTime::<Utc>::from))
.map(DateTime::<FixedOffset>::from)
}

pub(crate) fn parse_time(str: &str) -> Result<DateTime<FixedOffset>, chrono::ParseError> {
chrono::NaiveTime::parse_from_str(str, "%H:%M:%S%.f")
.map(|time| {
let base_date = chrono::NaiveDate::from_ymd_opt(1970, 1, 1).unwrap();

DateTime::<Utc>::from_utc(base_date.and_time(time), Utc)
})
.map(DateTime::<FixedOffset>::from)
}

pub(crate) fn parse_money(str: &str) -> Result<BigDecimal, ParseBigDecimalError> {
// We strip out the currency sign from the string.
BigDecimal::from_str(&str[1..]).map(|bd| bd.normalized())
}

pub(crate) fn parse_decimal(str: &str) -> Result<BigDecimal, ParseBigDecimalError> {
BigDecimal::from_str(str).map(|bd| bd.normalized())
}
18 changes: 18 additions & 0 deletions psl/psl-core/src/datamodel_connector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ pub use self::{
};

use crate::{configuration::DatasourceConnectorData, Configuration, Datasource, PreviewFeature};
use bigdecimal::{BigDecimal, ParseBigDecimalError};
use chrono::{DateTime, FixedOffset};
use diagnostics::{DatamodelError, Diagnostics, NativeTypeErrorFactory, Span};
use enumflags2::BitFlags;
use lsp_types::CompletionList;
Expand Down Expand Up @@ -359,6 +361,22 @@ pub trait Connector: Send + Sync {
) -> DatasourceConnectorData {
Default::default()
}

fn coerce_json_datetime(
&self,
_str: &str,
_nt: Option<NativeTypeInstance>,
) -> chrono::ParseResult<DateTime<FixedOffset>> {
unreachable!("This method is only implemented on connectors with lateral join support.")
}

fn coerce_json_decimal(
&self,
_str: &str,
_nt: Option<NativeTypeInstance>,
) -> Result<BigDecimal, ParseBigDecimalError> {
unreachable!("This method is only implemented on connectors with lateral join support.")
}
}

#[derive(Copy, Clone, Debug, PartialEq)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@ mod enum_type;
mod float;
mod int;
mod json;
mod native;
mod string;
mod through_relation;
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
mod postgres;
Loading

0 comments on commit c667de4

Please sign in to comment.