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

feat(psl, query-engine, schema-engine): add cuid(2) support, fix uuid(7) #5047

Merged
merged 35 commits into from
Nov 22, 2024
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
454112f
feat(psl): add cuid(2) support, fix uuid(7) re-introspection
jkomyno Nov 15, 2024
8b6ac6e
chore(psl): fix clippy
jkomyno Nov 15, 2024
195d25d
chore(psl): fix clippy
jkomyno Nov 15, 2024
04af8ad
fix(dmmf): [breaking] serialise uuid and cuid with proper args
jkomyno Nov 15, 2024
e902e3a
test(dmmf): expand functions.json test suite
jkomyno Nov 15, 2024
df254e9
chore(query-structure): interpret "cuid()" as "cuid(1)"
jkomyno Nov 15, 2024
547c979
chore: remove leftover
jkomyno Nov 15, 2024
8e91dfb
Merge branch 'main' into feat/cuid2-support
jkomyno Nov 19, 2024
a154696
fix(query-engine): fix "create_uuid_v7_and_retrieve_it_should_work" s…
jkomyno Nov 19, 2024
56a6ca6
test(query-engine): add create_cuid_v2_and_retrieve_it_should_work test
jkomyno Nov 19, 2024
16e3d1d
feat(query-engine): port cuid to workspace, replace Prisma's wasm32-o…
jkomyno Nov 19, 2024
71c4aeb
feat(query-engine): replace cuid1 with cuid2 for internal transaction…
jkomyno Nov 19, 2024
9d191ed
Merge branch 'feat/cuid2-support' of github.com:prisma/prisma-engines…
jkomyno Nov 19, 2024
148551c
chore: cargo fmt
jkomyno Nov 19, 2024
c1159eb
chore: clippy
jkomyno Nov 19, 2024
9d04afb
fix(psl): keep cuid() default version as 1
jkomyno Nov 20, 2024
da162ec
test(query-engine): add "create_cuid_v1_and_retrieve_it_should_work" …
jkomyno Nov 20, 2024
0af3191
chore: address review comments
jkomyno Nov 21, 2024
7e6a66e
test(psl): add validation tests for cuid and uuid
jkomyno Nov 21, 2024
5102928
fix(dmmf): serialise uuid, cuid like nanoid; parse nanoid like uuid, …
jkomyno Nov 21, 2024
e0e5158
chore(psl): revert bad test update to "server_side_functions"
jkomyno Nov 21, 2024
4410268
chore(dmmf): remove unused tuple, simplify "default_value" for genera…
jkomyno Nov 21, 2024
e232e8a
fix(psl): typo in test
jkomyno Nov 21, 2024
fe03f7e
fix(psl): typo in test
jkomyno Nov 21, 2024
f9f2ef1
chore(psl): use itertools
jkomyno Nov 21, 2024
270e457
fix(qe): fix broken shebang in query-engine-wasm/build.sh
aqrln Nov 21, 2024
9b43712
Merge branch 'main' into feat/cuid2-support
jkomyno Nov 21, 2024
d9386d2
Merge branch 'main' into feat/cuid2-support
jkomyno Nov 21, 2024
1e91366
chore(driver-adapters):: bump wrangler version to 3.88.0
jkomyno Nov 21, 2024
f767af2
chore(driver-adapters): bump wasm32-unknown-unknown rust-toolchain to…
jkomyno Nov 21, 2024
c538098
Merge branch 'feat/cuid2-support' of github.com:prisma/prisma-engines…
jkomyno Nov 21, 2024
520fb09
fix(query-engine-wasm): replace "cuid" with "prisma/cuid-rust?branch=…
jkomyno Nov 21, 2024
5e70c06
Merge branch 'feat/cuid2-support' of github.com:prisma/prisma-engines…
jkomyno Nov 21, 2024
c57a8e2
chore: revert upgrade to wrangler (from 3.50.0 to 3.88.0) for suspect…
jkomyno Nov 22, 2024
bd42901
chore: remove leftover
jkomyno Nov 22, 2024
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
56 changes: 45 additions & 11 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 @@ -54,6 +54,7 @@ chrono = { version = "0.4.38", features = ["serde"] }
derive_more = "0.99.17"
user-facing-errors = { path = "./libs/user-facing-errors" }
uuid = { version = "1", features = ["serde", "v4", "v7", "js"] }
cuid = { version = "1.3.3" }
indoc = "2.0.1"
indexmap = { version = "2.2.2", features = ["serde"] }
itertools = "0.12"
Expand Down
2 changes: 1 addition & 1 deletion libs/telemetry/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ tracing-futures = "0.2"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
tracing-opentelemetry = "0.17.4"
uuid.workspace = true
cuid = { git = "https://github.com/prisma/cuid-rust", branch = "wasm32-support" }
cuid.workspace = true
crosstarget-utils = { path = "../crosstarget-utils" }
lru = "0.7.7"
enumflags2.workspace = true
Expand Down
41 changes: 31 additions & 10 deletions psl/parser-database/src/attributes/default.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use crate::{
ast::{self, WithName},
coerce,
context::Context,
generators::{CUID_SUPPORTED_VERSIONS, UUID_SUPPORTED_VERSIONS},
types::{DefaultAttribute, ScalarFieldType, ScalarType},
DatamodelError, ScalarFieldId, StringId,
};
Expand Down Expand Up @@ -197,10 +198,10 @@ fn validate_model_builtin_scalar_type_default(
validate_empty_function_args(funcname, &funcargs.arguments, accept, ctx)
}
(ScalarType::String, ast::Expression::Function(funcname, funcargs, _)) if funcname == FN_CUID => {
validate_empty_function_args(funcname, &funcargs.arguments, accept, ctx)
validate_uid_int_args(funcname, &funcargs.arguments, &CUID_SUPPORTED_VERSIONS, accept, ctx)
}
(ScalarType::String, ast::Expression::Function(funcname, funcargs, _)) if funcname == FN_UUID => {
validate_uuid_args(&funcargs.arguments, accept, ctx)
validate_uid_int_args(funcname, &funcargs.arguments, &UUID_SUPPORTED_VERSIONS, accept, ctx)
}
(ScalarType::String, ast::Expression::Function(funcname, funcargs, _)) if funcname == FN_NANOID => {
validate_nanoid_args(&funcargs.arguments, accept, ctx)
Expand Down Expand Up @@ -244,10 +245,10 @@ fn validate_composite_builtin_scalar_type_default(
match (scalar_type, value) {
// Functions
(ScalarType::String, ast::Expression::Function(funcname, funcargs, _)) if funcname == FN_CUID => {
validate_empty_function_args(funcname, &funcargs.arguments, accept, ctx)
validate_uid_int_args(funcname, &funcargs.arguments, &CUID_SUPPORTED_VERSIONS, accept, ctx)
}
(ScalarType::String, ast::Expression::Function(funcname, funcargs, _)) if funcname == FN_UUID => {
validate_uuid_args(&funcargs.arguments, accept, ctx)
validate_uid_int_args(funcname, &funcargs.arguments, &UUID_SUPPORTED_VERSIONS, accept, ctx)
}
(ScalarType::DateTime, ast::Expression::Function(funcname, funcargs, _)) if funcname == FN_NOW => {
validate_empty_function_args(FN_NOW, &funcargs.arguments, accept, ctx)
Expand Down Expand Up @@ -381,18 +382,38 @@ fn validate_dbgenerated_args(args: &[ast::Argument], accept: AcceptFn<'_>, ctx:
}
}

fn validate_uuid_args(args: &[ast::Argument], accept: AcceptFn<'_>, ctx: &mut Context<'_>) {
let mut bail = || ctx.push_attribute_validation_error("`uuid()` takes a single Int argument.");
fn format_valid_values<const N: usize>(valid_values: &[u8; N]) -> String {
match valid_values.len() {
0 => String::new(),
1 => valid_values[0].to_string(),
2 => format!("{} or {}", valid_values[0], valid_values[1]),
_ => {
let (last, rest) = valid_values.split_last().unwrap();
let rest_str = rest.iter().map(|v| v.to_string()).collect::<Vec<_>>().join(", ");
aqrln marked this conversation as resolved.
Show resolved Hide resolved
format!("{}, or {}", rest_str, last)
aqrln marked this conversation as resolved.
Show resolved Hide resolved
}
}
}

fn validate_uid_int_args<const N: usize>(
fn_name: &str,
args: &[ast::Argument],
valid_values: &[u8; N],
accept: AcceptFn<'_>,
ctx: &mut Context<'_>,
) {
let mut bail = || ctx.push_attribute_validation_error(&format!("`{fn_name}()` takes a single Int argument."));

if args.len() > 1 {
bail()
}

match args.first().map(|arg| &arg.value) {
Some(ast::Expression::NumericValue(val, _)) if ![4u8, 7u8].contains(&val.parse::<u8>().unwrap()) => {
ctx.push_attribute_validation_error(
"`uuid()` takes either no argument, or a single integer argument which is either 4 or 7.",
);
Some(ast::Expression::NumericValue(val, _)) if !valid_values.contains(&val.parse::<u8>().unwrap()) => {
let valid_values_str = format_valid_values(valid_values);
ctx.push_attribute_validation_error(&format!(
"`{fn_name}()` takes either no argument, or a single integer argument which is either {valid_values_str}.",
));
}
None | Some(ast::Expression::NumericValue(_, _)) => accept(ctx),
_ => bail(),
Expand Down
13 changes: 13 additions & 0 deletions psl/parser-database/src/generators.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//! Convenient access to a ID generator constants, used by Prisma in psl, Query Engine and Schema Engine.

/// Version of the `uuid()` ID generator supported by Prisma.
pub const UUID_SUPPORTED_VERSIONS: [u8; 2] = [4, 7];

/// Version of the `cuid()` ID generator supported by Prisma.
pub const CUID_SUPPORTED_VERSIONS: [u8; 2] = [1, 2];

/// Default version of the `uuid()` ID generator.
pub const DEFAULT_UUID_VERSION: u8 = 4;

/// Default version of the `cuid()` ID generator.
pub const DEFAULT_CUID_VERSION: u8 = 2;
aqrln marked this conversation as resolved.
Show resolved Hide resolved
1 change: 1 addition & 0 deletions psl/parser-database/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ mod attributes;
mod coerce_expression;
mod context;
mod files;
pub mod generators;
mod ids;
mod interner;
mod names;
Expand Down
2 changes: 1 addition & 1 deletion psl/psl-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ pub use crate::{
reformat::{reformat, reformat_multiple, reformat_validated_schema_into_single},
};
pub use diagnostics;
pub use parser_database::{self, is_reserved_type_name};
pub use parser_database::{self, generators, is_reserved_type_name};
pub use schema_ast;
pub use set_config_dir::set_config_dir;

Expand Down
1 change: 1 addition & 0 deletions psl/psl/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ pub use psl_core::{
builtin_connectors::{can_have_capability, can_support_relation_load_strategy, has_capability},
datamodel_connector,
diagnostics::{self, Diagnostics},
generators,
is_reserved_type_name,
mcf::config_to_mcf_json_value as get_config,
mcf::{generators_to_json, render_sources_to_json}, // for tests
Expand Down
43 changes: 41 additions & 2 deletions psl/psl/tests/attributes/id_positive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,45 @@ fn should_allow_string_ids_with_cuid() {
model.assert_id_on_fields(&["id"]);
}

#[test]
fn should_allow_string_ids_with_cuid_version_specified() {
let dml = indoc! {r#"
model ModelA {
id String @id @default(cuid(1))
}

model ModelB {
id String @id @default(cuid(2))
}
"#};

let schema = psl::parse_schema(dml).unwrap();

{
let model = schema.assert_has_model("ModelA");

model
.assert_has_scalar_field("id")
.assert_scalar_type(ScalarType::String)
.assert_default_value()
.assert_cuid_version(1);

model.assert_id_on_fields(&["id"]);
}

{
let model = schema.assert_has_model("ModelB");

model
.assert_has_scalar_field("id")
.assert_scalar_type(ScalarType::String)
.assert_default_value()
.assert_cuid_version(2);

model.assert_id_on_fields(&["id"]);
}
}

#[test]
fn should_allow_string_ids_with_uuid() {
let dml = indoc! {r#"
Expand Down Expand Up @@ -91,7 +130,7 @@ fn should_allow_string_ids_with_uuid_version_specified() {
.assert_has_scalar_field("id")
.assert_scalar_type(ScalarType::String)
.assert_default_value()
.assert_uuid();
.assert_uuid_version(4);

model.assert_id_on_fields(&["id"]);
}
Expand All @@ -103,7 +142,7 @@ fn should_allow_string_ids_with_uuid_version_specified() {
.assert_has_scalar_field("id")
.assert_scalar_type(ScalarType::String)
.assert_default_value()
.assert_uuid();
.assert_uuid_version(7);

model.assert_id_on_fields(&["id"]);
}
Expand Down
52 changes: 49 additions & 3 deletions psl/psl/tests/common/asserts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,9 @@ pub(crate) trait DefaultValueAssert {
fn assert_bytes(&self, val: &[u8]) -> &Self;
fn assert_now(&self) -> &Self;
fn assert_cuid(&self) -> &Self;
fn assert_cuid_version(&self, version: u8) -> &Self;
fn assert_uuid(&self) -> &Self;
fn assert_uuid_version(&self, version: u8) -> &Self;
fn assert_dbgenerated(&self, val: &str) -> &Self;
fn assert_mapped_name(&self, val: &str) -> &Self;
}
Expand Down Expand Up @@ -433,12 +435,24 @@ impl<'a> DefaultValueAssert for walkers::DefaultValueWalker<'a> {
self
}

#[track_caller]
fn assert_cuid_version(&self, version: u8) -> &Self {
self.value().assert_cuid_version(version);
self
}

#[track_caller]
fn assert_uuid(&self) -> &Self {
self.value().assert_uuid();
self
}

#[track_caller]
fn assert_uuid_version(&self, version: u8) -> &Self {
self.value().assert_uuid_version(version);
self
}

#[track_caller]
fn assert_dbgenerated(&self, val: &str) -> &Self {
self.value().assert_dbgenerated(val);
Expand Down Expand Up @@ -622,9 +636,24 @@ impl DefaultValueAssert for ast::Expression {

#[track_caller]
fn assert_cuid(&self) -> &Self {
assert!(
matches!(self, ast::Expression::Function(name, args, _) if name == "cuid" && args.arguments.is_empty())
);
assert!(matches!(self, ast::Expression::Function(name, _, _) if name == "cuid"));

self
}

#[track_caller]
fn assert_cuid_version(&self, version: u8) -> &Self {
self.assert_cuid();

if let ast::Expression::Function(_, args, _) = self {
if let ast::Expression::NumericValue(actual, _) = &args.arguments[0].value {
assert_eq!(actual, &format!("{version}"));
jkomyno marked this conversation as resolved.
Show resolved Hide resolved
} else {
panic!("Expected a numeric value for the version.");
}
} else {
unreachable!();
aqrln marked this conversation as resolved.
Show resolved Hide resolved
}

self
}
Expand All @@ -636,6 +665,23 @@ impl DefaultValueAssert for ast::Expression {
self
}

#[track_caller]
fn assert_uuid_version(&self, version: u8) -> &Self {
self.assert_uuid();

if let ast::Expression::Function(_, args, _) = self {
if let ast::Expression::NumericValue(actual, _) = &args.arguments[0].value {
assert_eq!(actual, &format!("{version}"));
jkomyno marked this conversation as resolved.
Show resolved Hide resolved
} else {
panic!("Expected a numeric value for the version.");
}
} else {
unreachable!();
jkomyno marked this conversation as resolved.
Show resolved Hide resolved
}

self
}

#[track_caller]
fn assert_dbgenerated(&self, val: &str) -> &Self {
match self {
Expand Down
Loading
Loading