Skip to content

Commit a160421

Browse files
authored
feat: Allow setting column name explicitly on models (serverpod#4088)
1 parent d1f5f2e commit a160421

62 files changed

Lines changed: 31314 additions & 3471 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

packages/serverpod/lib/src/database/adapters/postgres/database_connection.dart

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -207,8 +207,11 @@ class DatabaseConnection {
207207
.map((columnName) => '"$columnName" = data."$columnName"')
208208
.join(', ');
209209

210+
const tableAlias = 't';
211+
var returning = buildReturningClause(table, tableAlias: tableAlias);
212+
210213
var query =
211-
'UPDATE "${table.tableName}" AS t SET $setColumns FROM (VALUES $values) AS data($columnNames) WHERE data.id = t.id RETURNING *';
214+
'UPDATE "${table.tableName}" AS $tableAlias SET $setColumns FROM (VALUES $values) AS data($columnNames) WHERE data.id = $tableAlias.id RETURNING $returning';
212215

213216
return (await _mappedResultsQuery(
214217
session,
@@ -284,7 +287,9 @@ class DatabaseConnection {
284287
);
285288
}
286289

287-
return _poolManager.serializationManager.deserialize<T>(result.first);
290+
return _poolManager.serializationManager.deserialize<T>(
291+
result.first,
292+
);
288293
}
289294

290295
/// Updates all rows matching the WHERE expression with the specified column values.
@@ -369,7 +374,7 @@ class DatabaseConnection {
369374
);
370375

371376
return result
372-
.map((row) => _poolManager.serializationManager.deserialize<T>(row))
377+
.map(_poolManager.serializationManager.deserialize<T>)
373378
.toList();
374379
}
375380

@@ -673,7 +678,7 @@ class DatabaseConnection {
673678
include: include,
674679
),
675680
)
676-
.map((row) => _poolManager.serializationManager.deserialize<T>(row))
681+
.map(_poolManager.serializationManager.deserialize<T>)
677682
.toList();
678683
}
679684

@@ -855,7 +860,7 @@ class DatabaseConnection {
855860
Iterable<Column> column,
856861
) {
857862
return rows
858-
.map((row) => row.toJson() as Map<String, dynamic>)
863+
.map((row) => row.toJsonForDatabase() as Map<String, dynamic>)
859864
.map((row) {
860865
var values = column
861866
.map((column) {

packages/serverpod/lib/src/database/adapters/postgres/postgres_result_parser.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,12 +89,12 @@ Map<String, dynamic> _createColumnMapFromQueryAliasColumns(
8989
var columnMap = <String, dynamic>{};
9090
for (var column in columns) {
9191
var queryKey = truncateIdentifier(
92-
column.queryAlias,
92+
column.fieldQueryAlias,
9393
DatabaseConstants.pgsqlMaxNameLimitation,
9494
);
9595
var columnData = rawTableRow[queryKey];
9696
if (columnData != null) {
97-
columnMap[column.columnName] = columnData;
97+
columnMap[column.fieldName] = columnData;
9898
}
9999
}
100100

packages/serverpod/lib/src/database/concepts/columns.dart

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,19 @@ abstract class Column<T> {
1717
/// Name of the [Column].
1818
String get columnName => _columnName;
1919

20+
final String? _fieldName;
21+
22+
/// Name of the field in the model
23+
String get fieldName => _fieldName ?? _columnName;
24+
2025
/// Table that column belongs to.
2126
final Table table;
2227

2328
/// Query alias for the [Column].
24-
String get queryAlias => '${table.queryPrefix}.$_columnName';
29+
String get queryAlias => '${table.queryPrefix}.$columnName';
30+
31+
/// Field name alias for the [Column] to be used in queries.
32+
String get fieldQueryAlias => '${table.queryPrefix}.$fieldName';
2533

2634
/// flag to tell if this [Column] has any [default] value
2735
final bool hasDefault;
@@ -31,7 +39,9 @@ abstract class Column<T> {
3139
this._columnName,
3240
this.table, {
3341
this.hasDefault = false,
34-
}) : type = T;
42+
String? fieldName,
43+
}) : _fieldName = fieldName ?? _columnName,
44+
type = T;
3545

3646
@override
3747
String toString() {
@@ -46,6 +56,7 @@ class ColumnByteData extends Column<ByteData> {
4656
super.columnName,
4757
super.table, {
4858
super.hasDefault,
59+
super.fieldName,
4960
});
5061
}
5162

@@ -57,6 +68,7 @@ class ColumnSerializable<T> extends Column<T> {
5768
super.columnName,
5869
super.table, {
5970
super.hasDefault,
71+
super.fieldName,
6072
});
6173
}
6274

@@ -65,6 +77,7 @@ abstract class _ValueOperatorColumn<T> extends Column<T> {
6577
super.columnName,
6678
super.table, {
6779
super.hasDefault,
80+
super.fieldName,
6881
});
6982

7083
/// Applies encoding to value before it is sent to the database.
@@ -79,6 +92,7 @@ abstract class _ColumnComparableEquals<T> extends _ValueOperatorColumn<T>
7992
super.columnName,
8093
super.table, {
8194
super.hasDefault,
95+
super.fieldName,
8296
});
8397
}
8498

@@ -92,6 +106,7 @@ abstract class ColumnComparable<T> extends _ColumnComparableEquals<T>
92106
super.columnName,
93107
super.table, {
94108
super.hasDefault,
109+
super.fieldName,
95110
});
96111
}
97112

@@ -104,6 +119,7 @@ class ColumnEnum<E extends Enum> extends _ColumnComparableEquals<E> {
104119
super.table,
105120
this._serialized, {
106121
super.hasDefault,
122+
super.fieldName,
107123
});
108124

109125
/// Creates a new [Column], this is typically done in generated code only.
@@ -112,6 +128,7 @@ class ColumnEnum<E extends Enum> extends _ColumnComparableEquals<E> {
112128
Table table,
113129
EnumSerialization serialized, {
114130
bool hasDefault,
131+
String fieldName,
115132
}) = ColumnEnumExtended<E>;
116133

117134
@override
@@ -133,6 +150,7 @@ class ColumnEnumExtended<E extends Enum> extends ColumnEnum<E> {
133150
super.table,
134151
super.serialized, {
135152
super.hasDefault,
153+
super.fieldName,
136154
}) : super._();
137155

138156
/// Data type for serialization of the enum.
@@ -150,6 +168,7 @@ class ColumnString extends ColumnComparable<String> {
150168
super.table, {
151169
this.varcharLength,
152170
super.hasDefault,
171+
super.fieldName,
153172
});
154173

155174
/// Creates an [Expression] checking if the value in the column is LIKE the
@@ -191,6 +210,7 @@ class ColumnBool extends _ColumnComparableEquals<bool> {
191210
super.columnName,
192211
super.table, {
193212
super.hasDefault,
213+
super.fieldName,
194214
});
195215

196216
@override
@@ -206,6 +226,7 @@ class ColumnDateTime extends ColumnComparable<DateTime>
206226
super.columnName,
207227
super.table, {
208228
super.hasDefault,
229+
super.fieldName,
209230
});
210231

211232
@override
@@ -220,6 +241,7 @@ class ColumnDuration extends ColumnComparable<Duration>
220241
super.columnName,
221242
super.table, {
222243
super.hasDefault,
244+
super.fieldName,
223245
});
224246

225247
@override
@@ -233,6 +255,7 @@ class ColumnUuid extends ColumnComparable<UuidValue> {
233255
super.columnName,
234256
super.table, {
235257
super.hasDefault,
258+
super.fieldName,
236259
});
237260

238261
@override
@@ -247,6 +270,7 @@ class ColumnUri extends _ValueOperatorColumn<Uri>
247270
super.columnName,
248271
super.table, {
249272
super.hasDefault,
273+
super.fieldName,
250274
});
251275

252276
@override
@@ -261,6 +285,7 @@ class ColumnBigInt extends _ValueOperatorColumn<BigInt>
261285
super.columnName,
262286
super.table, {
263287
super.hasDefault,
288+
super.fieldName,
264289
});
265290

266291
@override
@@ -275,6 +300,7 @@ class ColumnInt extends ColumnComparable<int>
275300
super.columnName,
276301
super.table, {
277302
super.hasDefault,
303+
super.fieldName,
278304
});
279305

280306
@override
@@ -289,6 +315,7 @@ class ColumnDouble extends ColumnComparable<double>
289315
super.columnName,
290316
super.table, {
291317
super.hasDefault,
318+
super.fieldName,
292319
});
293320

294321
@override
@@ -311,7 +338,7 @@ class ColumnCount extends _ValueOperatorColumn<int>
311338

312339
/// Creates a new [Column], this is typically done in generated code only.
313340
ColumnCount(this.innerWhere, Column column)
314-
: super(column.columnName, column.table);
341+
: super(column.columnName, column.table, fieldName: column.fieldName);
315342

316343
@override
317344
Expression _encodeValueForQuery(int value) => Expression(value);
@@ -331,6 +358,7 @@ class ColumnVector extends _ValueOperatorColumn<Vector>
331358
super.table, {
332359
required this.dimension,
333360
super.hasDefault,
361+
super.fieldName,
334362
});
335363

336364
@override
@@ -351,6 +379,7 @@ class ColumnHalfVector extends _ValueOperatorColumn<HalfVector>
351379
super.table, {
352380
required this.dimension,
353381
super.hasDefault,
382+
super.fieldName,
354383
});
355384

356385
@override
@@ -371,6 +400,7 @@ class ColumnSparseVector extends _ValueOperatorColumn<SparseVector>
371400
super.table, {
372401
required this.dimension,
373402
super.hasDefault,
403+
super.fieldName,
374404
});
375405

376406
@override
@@ -390,6 +420,7 @@ class ColumnBit extends _ValueOperatorColumn<Bit>
390420
super.table, {
391421
required this.dimension,
392422
super.hasDefault,
423+
super.fieldName,
393424
});
394425

395426
@override
@@ -424,7 +455,11 @@ class ColumnVectorDistance<T> extends ColumnDouble {
424455

425456
/// Creates a new [Column], this is typically done in generated code only.
426457
ColumnVectorDistance(this._expression)
427-
: super(_expression.column.columnName, _expression.column.table);
458+
: super(
459+
_expression.column.columnName,
460+
_expression.column.table,
461+
fieldName: _expression.column.fieldName,
462+
);
428463

429464
@override
430465
String toString() => _expression.toString();

packages/serverpod/lib/src/database/concepts/table.dart

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,25 @@ abstract interface class TableRow<T_ID> implements SerializableModel {
1616
Table<T_ID> get table;
1717
}
1818

19+
/// Extension on a [TableRow] to ensure database serialization matches the
20+
/// expected naming of the column in the database
21+
extension TableRowDatabaseJsonExtension on TableRow {
22+
/// Returns the json representation to be sent to the database for storage
23+
dynamic toJsonForDatabase() {
24+
final json = toJson();
25+
if (json is! Map<String, dynamic>) return json;
26+
27+
final dbJson = <String, dynamic>{};
28+
for (final column in table.columns) {
29+
// Eliminate non persistent fields
30+
if (!json.containsKey(column.fieldName)) continue;
31+
32+
dbJson[column.columnName] = json[column.fieldName];
33+
}
34+
return dbJson;
35+
}
36+
}
37+
1938
/// Represents a database table.
2039
class Table<T_ID> {
2140
/// Name of the table as used in the database.
@@ -39,6 +58,10 @@ class Table<T_ID> {
3958
/// Table relation for [Column]s of the table.
4059
final TableRelation? tableRelation;
4160

61+
/// Cached flag indicating whether any columns have explicit column names
62+
/// that differ from their field names. Computed lazily on first access.
63+
late final bool hasColumnMapping = _computeHasColumnMapping();
64+
4265
/// Creates a new [Table]. Typically, this is done only by generated code.
4366
Table({
4467
required this.tableName,
@@ -71,6 +94,16 @@ class Table<T_ID> {
7194
}
7295
}
7396

97+
/// Checks if any columns have explicit column names that differ from field names.
98+
bool _computeHasColumnMapping() {
99+
for (final column in columns) {
100+
if (column.columnName != column.fieldName) {
101+
return true;
102+
}
103+
}
104+
return false;
105+
}
106+
74107
/// Returns [TableColumnRelation] for the given [relationField]. If no relation
75108
/// exists, returns null. The return must be dynamic to allow for relations
76109
/// with different id types.

packages/serverpod/lib/src/database/concepts/table_relation.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ class TableRelation {
9595

9696
/// The foreign field name joined on.
9797
String get foreignFieldName {
98-
return _tableRelationEntries.last.foreignField.columnName;
98+
return _tableRelationEntries.last.foreignField.fieldName;
9999
}
100100

101101
/// The foreign field name with including the table escaped.

packages/serverpod/lib/src/database/database_pool_manager.dart

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
import 'package:meta/meta.dart';
22
import 'package:postgres/postgres.dart' as pg;
3-
import 'package:serverpod_serialization/serverpod_serialization.dart';
4-
import 'package:serverpod_shared/serverpod_shared.dart';
53
import 'package:serverpod/src/database/concepts/runtime_parameters.dart';
64
import 'package:serverpod/src/serialization/serialization_manager.dart';
5+
import 'package:serverpod_shared/serverpod_shared.dart';
76

87
import 'adapters/postgres/pgvector_encoder.dart';
98
import 'adapters/postgres/value_encoder.dart';
@@ -14,10 +13,10 @@ class DatabasePoolManager {
1413
/// Database configuration.
1514
final DatabaseConfig config;
1615

17-
late SerializationManager _serializationManager;
16+
late SerializationManagerServer _serializationManager;
1817

1918
/// Access to the serialization manager.
20-
SerializationManager get serializationManager => _serializationManager;
19+
SerializationManagerServer get serializationManager => _serializationManager;
2120

2221
pg.Pool? _pgPool;
2322

0 commit comments

Comments
 (0)