Skip to content

Commit ab635e6

Browse files
authored
1 parent ab17e34 commit ab635e6

6 files changed

Lines changed: 85 additions & 15 deletions

File tree

libs/driver-adapters/executor/src/query-compiler.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,8 @@ export async function initQueryCompiler(
2121
params: QueryCompilerParams,
2222
connector: Env['CONNECTOR'],
2323
): Promise<QueryCompiler> {
24-
const { getQueryCompilerForConnector } = await import(
25-
'./query-compiler-wasm.js'
26-
)
24+
const { getQueryCompilerForConnector } =
25+
await import('./query-compiler-wasm.js')
2726
const WasmQueryCompiler = (await getQueryCompilerForConnector(
2827
connector,
2928
)) as QueryCompiler

libs/driver-adapters/pnpm-workspace.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ packages:
1212
- '../../../prisma/packages/client-runtime-utils'
1313
- '../../../prisma/packages/debug'
1414
- '../../../prisma/packages/driver-adapter-utils'
15+
- '../../../prisma/packages/sqlcommenter'
16+
- '../../../prisma/packages/json-protocol'
1517
- './executor'
1618

1719
onlyBuiltDependencies:

query-compiler/query-builders/query-builder/src/lib.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ pub trait QueryBuilder {
2828
linkage: RelationLinkage,
2929
query_arguments: QueryArguments,
3030
selected_fields: &FieldSelection,
31-
) -> Result<DbQuery, Box<dyn std::error::Error + Send + Sync>>;
31+
) -> Result<GetRelatedRecordsQuery, Box<dyn std::error::Error + Send + Sync>>;
3232

3333
fn build_aggregate(
3434
&self,
@@ -141,6 +141,15 @@ pub struct CreateRecordDefaultsQuery {
141141
pub field_placeholders: Vec<(ScalarField, Placeholder)>,
142142
}
143143

144+
/// A query that retrieves related records through an M2M relation.
145+
#[cfg(feature = "relation_joins")]
146+
pub struct GetRelatedRecordsQuery {
147+
/// The query that retrieves the related records.
148+
pub query: DbQuery,
149+
/// The alias used for the linking field in the query.
150+
pub linking_field_alias: String,
151+
}
152+
144153
#[derive(Debug)]
145154
pub struct ConditionalLink {
146155
field: ScalarField,

query-compiler/query-builders/sql-query-builder/src/lib.rs

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ pub mod update;
1616
pub mod value;
1717
pub mod write;
1818

19-
use std::{collections::HashMap, iter, marker::PhantomData};
19+
use std::{collections::HashMap, iter, marker::PhantomData, sync::atomic::AtomicUsize};
2020

2121
use itertools::{Either, Itertools};
2222
use model_extensions::ScalarFieldExt;
@@ -28,6 +28,8 @@ use quaint::{
2828
},
2929
visitor::Visitor,
3030
};
31+
#[cfg(feature = "relation_joins")]
32+
use query_builder::GetRelatedRecordsQuery;
3133
use query_builder::{Chunkable, CreateRecord, CreateRecordDefaultsQuery, DbQuery, QueryBuilder};
3234
use query_structure::{
3335
AggregationSelection, DatasourceFieldName, FieldSelection, Filter, Model, ModelProjection, QueryArguments,
@@ -43,20 +45,24 @@ pub use sql_trace::SqlTraceComment;
4345
use value::GeneratorCall;
4446

4547
const PARAMETER_LIMIT: usize = 2000;
48+
// The smallest identifier length limit among our primary databases (PostgreSQL, MySQL, SQLite).
49+
const IDENTIFIER_LENGTH_LIMIT: usize = 63;
4650

4751
// The number of parameters that are used for take and limit in a query.
4852
const TAKE_AND_LIMIT_PARAM_COUNT: usize = 2;
4953

5054
pub struct SqlQueryBuilder<'a, Visitor> {
5155
context: Context<'a>,
5256
phantom: PhantomData<fn(Visitor)>,
57+
linking_field_alias_counter: AtomicUsize,
5358
}
5459

5560
impl<'a, V> SqlQueryBuilder<'a, V> {
5661
pub fn new(context: Context<'a>) -> Self {
5762
Self {
5863
context,
5964
phantom: PhantomData,
65+
linking_field_alias_counter: AtomicUsize::new(0),
6066
}
6167
}
6268

@@ -89,6 +95,18 @@ impl<'a, V> SqlQueryBuilder<'a, V> {
8995
chunkable,
9096
})
9197
}
98+
99+
fn generate_linking_field_alias(&self, linkage: &query_builder::RelationLinkage) -> String {
100+
let full_name = linkage.to_string();
101+
// use full name if it fits within the limit
102+
if full_name.len() <= IDENTIFIER_LENGTH_LIMIT {
103+
return full_name;
104+
}
105+
let index = self
106+
.linking_field_alias_counter
107+
.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
108+
format!("@link${index}")
109+
}
92110
}
93111

94112
impl<'a, V: Visitor<'a>> QueryBuilder for SqlQueryBuilder<'a, V> {
@@ -153,7 +171,7 @@ impl<'a, V: Visitor<'a>> QueryBuilder for SqlQueryBuilder<'a, V> {
153171
linkage: query_builder::RelationLinkage,
154172
query_arguments: QueryArguments,
155173
selected_fields: &FieldSelection,
156-
) -> Result<DbQuery, Box<dyn std::error::Error + Send + Sync>> {
174+
) -> Result<GetRelatedRecordsQuery, Box<dyn std::error::Error + Send + Sync>> {
157175
use std::slice;
158176

159177
use crate::read::SelectDefinition;
@@ -163,7 +181,7 @@ impl<'a, V: Visitor<'a>> QueryBuilder for SqlQueryBuilder<'a, V> {
163181
use select::JoinConditionExt;
164182

165183
let chunkable = Chunkable::from(&query_arguments);
166-
let link_alias = linkage.to_string();
184+
let linking_field_alias = self.generate_linking_field_alias(&linkage);
167185
let (rf, conditions_per_field) = linkage.into_parent_field_and_conditions();
168186

169187
let m2m_alias = self.context.next_table_alias();
@@ -197,7 +215,7 @@ impl<'a, V: Visitor<'a>> QueryBuilder for SqlQueryBuilder<'a, V> {
197215
.map(|col| col.table(rf.related_model().as_table(&self.context)))
198216
// Add an m2m column with an alias to make it possible to join it outside of this
199217
// function.
200-
.chain([m2m_col.alias(link_alias)]);
218+
.chain([m2m_col.alias(linking_field_alias.clone())]);
201219

202220
let join_condition = rf.m2m_join_conditions(Some(m2m_alias), None, &self.context);
203221

@@ -213,7 +231,11 @@ impl<'a, V: Visitor<'a>> QueryBuilder for SqlQueryBuilder<'a, V> {
213231
.into_iter()
214232
.fold(select, |acc, val| acc.value(val));
215233

216-
self.convert_query(select, chunkable)
234+
let query = self.convert_query(select, chunkable)?;
235+
Ok(GetRelatedRecordsQuery {
236+
query,
237+
linking_field_alias,
238+
})
217239
}
218240

219241
fn build_aggregate(

query-compiler/query-compiler/src/translate/query/read.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -282,16 +282,14 @@ fn build_read_m2m_query(
282282
selected_fields: &FieldSelection,
283283
builder: &dyn QueryBuilder,
284284
) -> TranslateResult<(Expression, JoinMetadata)> {
285-
let link_name = linkage.to_string();
286-
287-
let query = builder
285+
let result = builder
288286
.build_get_related_records(linkage, args, selected_fields)
289287
.map_err(TranslateError::QueryBuildFailure)?;
290288

291289
Ok((
292-
Expression::Query(query),
290+
Expression::Query(result.query),
293291
JoinMetadata {
294-
fields: vec![link_name],
292+
fields: vec![result.linking_field_alias],
295293
is_relation_unique: false,
296294
},
297295
))

query-engine/connector-test-kit-rs/query-engine-tests/tests/queries/simple/m2m.rs

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,7 @@ mod m2m {
227227
insta::assert_snapshot!(
228228
run_query!(&runner, r#"{
229229
findUniqueItem(relationLoadStrategy: join, where: { id: 1 })
230-
{ id categories { id } }
230+
{ id categories { id } }
231231
}"#),
232232
@r###"{"data":{"findUniqueItem":null}}"###
233233
);
@@ -243,6 +243,46 @@ mod m2m {
243243
Ok(())
244244
}
245245

246+
fn schema_28304() -> String {
247+
let schema = indoc! {
248+
r#"model AReallyLongModelName {
249+
id Int @id @default(autoincrement())
250+
251+
verys AVeryVeryLongModelName[]
252+
}
253+
254+
model AVeryVeryLongModelName {
255+
id Int @id @default(autoincrement())
256+
257+
reallys AReallyLongModelName[]
258+
}"#
259+
};
260+
261+
schema.to_owned()
262+
}
263+
264+
// ! (https://github.com/prisma/prisma/issues/28304) - Many-to-many alias character limit
265+
#[connector_test(schema(schema_28304), only(Postgres))]
266+
async fn repro_28304(runner: Runner) -> TestResult<()> {
267+
run_query!(
268+
&runner,
269+
r#"mutation { createOneAReallyLongModelName(data: { verys: { create: {} } }) { id } }"#
270+
);
271+
272+
insta::assert_snapshot!(
273+
run_query!(&runner, r#"{
274+
findFirstAReallyLongModelName(relationLoadStrategy: query) {
275+
id
276+
verys {
277+
id
278+
}
279+
}
280+
}"#),
281+
@r###"{"data":{"findFirstAReallyLongModelName":{"id":1,"verys":[{"id":1}]}}}"###
282+
);
283+
Ok(())
284+
}
285+
246286
async fn test_data(runner: &Runner) -> TestResult<()> {
247287
runner
248288
.query(

0 commit comments

Comments
 (0)