Skip to content

Commit

Permalink
fix: truncate query aliases generated by the table relation class use…
Browse files Browse the repository at this point in the history
…d for joins and sub queries.
  • Loading branch information
SandPod committed Jan 12, 2024
1 parent 07d9ca2 commit 26e6622
Show file tree
Hide file tree
Showing 2 changed files with 79 additions and 7 deletions.
39 changes: 32 additions & 7 deletions packages/serverpod/lib/src/database/table_relation.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,42 @@ import 'package:collection/collection.dart';
import 'package:meta/meta.dart';
import 'package:serverpod/src/database/columns.dart';
import 'package:serverpod/src/database/expressions.dart';
import 'package:serverpod_shared/serverpod_shared.dart';

typedef _TruncateFunction = String Function(String identifier);

/// Records the relation between two tables.
/// All query aliases generated by [TableRelation] are truncated by the
/// [truncateFunction].
/// This is typically only used internally by the serverpod framework.
@internal
class TableRelation {
/// Records the relationship between multiple tables.
/// Order is important, as it determines the order of joins in the query.
final List<TableRelationEntry> _tableRelationEntries;

// Default initialized to truncate to Postgres max name length.
_TruncateFunction _truncateFunction = (String identifier) =>
truncateIdentifier(identifier, DatabaseConstants.pgsqlMaxNameLimitation);

/// Creates a new [TableRelation].
/// Throws [ArgumentError] if [tableRelationEntries] is empty.
TableRelation(this._tableRelationEntries) {
///
/// [truncateFunction] is used to truncate query aliases generated by
/// [TableRelation].
/// If [truncateFunction] is not provided, the query aliases are truncated
/// to Postgres max name length.
TableRelation(
this._tableRelationEntries, {
_TruncateFunction? truncateFunction,
}) {
if (_tableRelationEntries.isEmpty) {
throw ArgumentError('TableRelation must have at least one entry.');
}

if (truncateFunction != null) {
_truncateFunction = truncateFunction;
}
}

/// Builds all table relations required to join the tables.
Expand Down Expand Up @@ -51,7 +72,7 @@ class TableRelation {

/// The field name query alias including table.
String get fieldQueryAlias {
return _tableRelationEntries.last.field.queryAlias;
return _truncateFunction(_tableRelationEntries.last.field.queryAlias);
}

/// Field column that is joined on.
Expand All @@ -61,12 +82,13 @@ class TableRelation {

/// The query alias for field name to be joined on including all joins.
String get fieldQueryAliasWithJoins {
return '${_fromRelationQueryAlias()}.${_tableRelationEntries.last.field.columnName}';
return _truncateFunction(
'${_fromRelationQueryAlias()}.${_tableRelationEntries.last.field.columnName}');
}

/// The field name to be joined on including all joins.
String get fieldNameWithJoins {
return '"${_fromRelationQueryAlias()}"."${_tableRelationEntries.last.field.columnName}"';
return '"${_truncateFunction(_fromRelationQueryAlias())}"."${_tableRelationEntries.last.field.columnName}"';
}

/// The foreign field name joined on.
Expand All @@ -81,12 +103,14 @@ class TableRelation {

/// The foreign field name to be joined on including all joins.
String get foreignFieldNameWithJoins {
return '"${_buildRelationQueryAlias()}"."${_tableRelationEntries.last.foreignField.columnName}"';
return '"${_truncateFunction(_buildRelationQueryAlias())}"."${_tableRelationEntries.last.foreignField.columnName}"';
}

/// The field name query alias including table.
String get foreignFieldQueryAlias {
return _tableRelationEntries.last.foreignField.queryAlias;
return _truncateFunction(
_tableRelationEntries.last.foreignField.queryAlias,
);
}

/// Create a new [TableRelation] with only one entry for the last table
Expand All @@ -101,7 +125,8 @@ class TableRelation {
}

/// Retrieves the name of the table with the query prefix applied.
String get relationQueryAlias => _buildRelationQueryAlias();
String get relationQueryAlias =>
_truncateFunction(_buildRelationQueryAlias());

/// Builds the relation query alias including [TableRelationEntries]
/// up until [end] index.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import 'package:serverpod/database.dart';
import 'package:serverpod/src/database/database_query.dart';
import 'package:serverpod/test_util/table_relation_builder.dart';
import 'package:test/test.dart';

void main() {
var citizenTable = Table(tableName: 'citizen');
var companyTable = Table(tableName: 'company');
var relationTable = TableRelationBuilder(companyTable).withRelationsFrom([
BuilderRelation(
citizenTable,
'thisFieldIsExactly61CharactersLongAndIsThereforeValidAsNameFo',
),
]).build();

group('Given SelectQueryBuilder', () {
group('when filtering causes a long join name.', () {
var query = SelectQueryBuilder(table: citizenTable)
.withWhere(relationTable.id.equals(1))
.build();

test('then join query name is truncated.', () {
expect(
query,
contains(
'LEFT JOIN "company" AS "citizen_thisFieldIsExactly61CharactersLongAndIsThereforeVale9b4"'),
);
});

test('then join query uses the truncated join name.', () {
expect(
query,
contains(
'"citizen"."id" = "citizen_thisFieldIsExactly61CharactersLongAndIsThereforeVale9b4"."id"'),
);
});

test('then uses truncated name in where statements in for join.', () {
expect(
query,
contains(
'"citizen_thisFieldIsExactly61CharactersLongAndIsThereforeVale9b4"."id" = 1'),
);
});
});
});
}

0 comments on commit 26e6622

Please sign in to comment.