diff --git a/Cargo.lock b/Cargo.lock index e99ce2fd828..0fa05ba1a25 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -167,6 +167,17 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" +[[package]] +name = "bigdecimal" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1374191e2dd25f9ae02e3aa95041ed5d747fc77b3c102b49fe2dd9a8117a6244" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", +] + [[package]] name = "bitflags" version = "1.3.2" @@ -246,6 +257,7 @@ dependencies = [ "ammonia", "anyhow", "base64", + "bigdecimal", "cargo-registry-s3", "chrono", "claim", @@ -678,10 +690,14 @@ version = "1.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bba51ca66f57261fd17cadf8b73e4775cc307d0521d855de3f5de91a8f074e0e" dependencies = [ + "bigdecimal", "bitflags", "byteorder", "chrono", "diesel_derives", + "num-bigint", + "num-integer", + "num-traits", "pq-sys", "r2d2", "serde_json", @@ -1564,6 +1580,17 @@ dependencies = [ "winapi", ] +[[package]] +name = "num-bigint" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + [[package]] name = "num-integer" version = "0.1.44" diff --git a/Cargo.toml b/Cargo.toml index e04545e72d3..df1cd799553 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -90,8 +90,10 @@ tracing-subscriber = "0.2" url = "2.1" [dev-dependencies] +bigdecimal = { version = ">= 0.0.10, < 0.2.0" } claim = "0.5" conduit-test = "0.9.0-alpha.4" +diesel = { version = "1.4.0", features = ["numeric"] } hyper-tls = "0.5" lazy_static = "1.0" tokio = "1.5.0" diff --git a/migrations/2021-09-03-132838_fix_to_semver_no_prerelease/down.sql b/migrations/2021-09-03-132838_fix_to_semver_no_prerelease/down.sql new file mode 100644 index 00000000000..2a60bec3a90 --- /dev/null +++ b/migrations/2021-09-03-132838_fix_to_semver_no_prerelease/down.sql @@ -0,0 +1,12 @@ +create or replace function to_semver_no_prerelease(text) returns semver_triple + immutable + language sql +as +$$ +SELECT ( + split_part($1, '.', 1)::numeric, + split_part($1, '.', 2)::numeric, + split_part(split_part($1, '+', 1), '.', 3)::numeric + )::semver_triple +WHERE strpos($1, '-') = 0 +$$; diff --git a/migrations/2021-09-03-132838_fix_to_semver_no_prerelease/up.sql b/migrations/2021-09-03-132838_fix_to_semver_no_prerelease/up.sql new file mode 100644 index 00000000000..87ed5b8cc1d --- /dev/null +++ b/migrations/2021-09-03-132838_fix_to_semver_no_prerelease/up.sql @@ -0,0 +1,19 @@ +create or replace function to_semver_no_prerelease(text) returns semver_triple + immutable + language sql +as +$$ +SELECT ( + -- 2) then, we extract the major, minor and patch numbers + -- (dropping the prerelease part to avoid number conversion errors) + split_part(version_without_metadata, '.', 1)::numeric, + split_part(version_without_metadata, '.', 2)::numeric, + split_part(split_part(version_without_metadata, '-', 1), '.', 3)::numeric + )::semver_triple +FROM ( + -- 1) first, we split off the release metadata, if it exists + SELECT split_part($1, '+', 1) as version_without_metadata + ) as vwm +-- 3) finally, we only return the result if there is no prerelease part +WHERE strpos(version_without_metadata, '-') = 0 +$$; diff --git a/src/tests/all.rs b/src/tests/all.rs index f10412b94aa..5bfa101f211 100644 --- a/src/tests/all.rs +++ b/src/tests/all.rs @@ -46,6 +46,7 @@ mod record; mod schema_details; mod server; mod server_binary; +mod sql; mod team; mod token; mod unhealthy_database; diff --git a/src/tests/sql/custom_functions.rs b/src/tests/sql/custom_functions.rs new file mode 100644 index 00000000000..6c844fe7c7c --- /dev/null +++ b/src/tests/sql/custom_functions.rs @@ -0,0 +1,39 @@ +use bigdecimal::BigDecimal; +use diesel::prelude::*; +use diesel::sql_types::Text; +use diesel::{select, sql_function}; + +fn pg_connection() -> PgConnection { + let database_url = + dotenv::var("TEST_DATABASE_URL").expect("TEST_DATABASE_URL must be set to run tests"); + let conn = PgConnection::establish(&database_url).unwrap(); + conn.begin_test_transaction().unwrap(); + conn +} + +sql_function!(fn to_semver_no_prerelease(x: Text) -> Nullable>); + +#[test] +fn to_semver_no_prerelease_works() { + let conn = pg_connection(); + + #[track_caller] + fn test(conn: &PgConnection, text: &str, expected: Option<(i32, i32, i32)>) { + let query = select(to_semver_no_prerelease(text)); + let result = query + .get_result::>(conn) + .unwrap(); + + let expected = expected.map(|it| (it.0.into(), it.1.into(), it.2.into())); + assert_eq!(result, expected); + } + + test(&conn, "0.0.0", Some((0, 0, 0))); + test(&conn, "1.2.4", Some((1, 2, 4))); + test(&conn, "1.2.4+metadata", Some((1, 2, 4))); + test(&conn, "1.2.4-beta.3", None); + + // see https://github.com/rust-lang/crates.io/issues/3882 + test(&conn, "0.4.45+curl-7.78.0", Some((0, 4, 45))); + test(&conn, "0.1.4-preview+4.3.2", None); +} diff --git a/src/tests/sql/mod.rs b/src/tests/sql/mod.rs new file mode 100644 index 00000000000..14154705f85 --- /dev/null +++ b/src/tests/sql/mod.rs @@ -0,0 +1 @@ +mod custom_functions;