Alias all fields in child queries

This commit is contained in:
Tobe O 2019-10-12 14:36:24 -04:00
parent 2a8a186bca
commit 8222230c8a
5 changed files with 43 additions and 13 deletions

View file

@ -1,3 +1,7 @@
# 2.1.0-beta.1
* Calls to `leftJoin`, etc. alias all fields in a child query, to prevent
`ambiguous column a0.id` errors.
# 2.1.0-beta # 2.1.0-beta
* Split the formerly 600+ line `src/query.dart` up into * Split the formerly 600+ line `src/query.dart` up into
separate files. separate files.

View file

@ -6,6 +6,7 @@ class JoinBuilder {
final JoinType type; final JoinType type;
final Query from; final Query from;
final String key, value, op, alias; final String key, value, op, alias;
final bool aliasAllFields;
/// A callback to produces the expression to join against, i.e. /// A callback to produces the expression to join against, i.e.
/// a table name, or the result of compiling a query. /// a table name, or the result of compiling a query.
@ -13,34 +14,37 @@ class JoinBuilder {
final List<String> additionalFields; final List<String> additionalFields;
JoinBuilder(this.type, this.from, this.to, this.key, this.value, JoinBuilder(this.type, this.from, this.to, this.key, this.value,
{this.op = '=', this.alias, this.additionalFields = const []}) { {this.op = '=',
this.alias,
this.additionalFields = const [],
this.aliasAllFields = false}) {
assert(to != null, assert(to != null,
'computation of this join threw an error, and returned null.'); 'computation of this join threw an error, and returned null.');
} }
String get fieldName { String get fieldName {
var right = '$to.$value'; var v = value;
if (alias != null) right = '$alias.$value'; if (aliasAllFields) {
v = '${alias}_$v';
}
var right = '${from.tableName}.$v';
if (alias != null) right = '$alias.$v';
return right; return right;
} }
String nameFor(String name) { String nameFor(String name) {
var right = '$to.$name'; if (aliasAllFields) name = '${alias}_$name';
var right = '${from.tableName}.$name';
if (alias != null) right = '$alias.$name'; if (alias != null) right = '$alias.$name';
return right; return right;
} }
String compile(Set<String> trampoline) { String compile(Set<String> trampoline) {
var compiledTo = to(); var compiledTo = to();
if (compiledTo == null) {
print(
'NULLLLL $to; from $from; key: $key, value: $value, addl: $additionalFields');
}
if (compiledTo == null) return null; if (compiledTo == null) return null;
var b = StringBuffer(); var b = StringBuffer();
var left = '${from.tableName}.$key'; var left = '${from.tableName}.$key';
var right = fieldName; var right = fieldName;
switch (type) { switch (type) {
case JoinType.inner: case JoinType.inner:
b.write(' INNER JOIN'); b.write(' INNER JOIN');

View file

@ -151,10 +151,17 @@ abstract class Query<T, Where extends QueryWhere> extends QueryBase<T> {
var to = _compileJoin(tableName, trampoline); var to = _compileJoin(tableName, trampoline);
if (to != null) { if (to != null) {
var alias = _joinAlias(trampoline);
if (tableName is Query) {
for (var field in tableName.fields) {
tableName.aliases[field] = '${alias}_$field';
}
}
_joins.add(JoinBuilder(type, this, to, localKey, foreignKey, _joins.add(JoinBuilder(type, this, to, localKey, foreignKey,
op: op, op: op,
alias: _joinAlias(trampoline), alias: alias,
additionalFields: additionalFields)); additionalFields: additionalFields,
aliasAllFields: tableName is Query));
} }
} }
@ -228,6 +235,13 @@ abstract class Query<T, Where extends QueryWhere> extends QueryBase<T> {
var ss = includeTableName ? '$tableName.$s' : s; var ss = includeTableName ? '$tableName.$s' : s;
var cast = casts[s]; var cast = casts[s];
if (cast != null) ss = 'CAST ($ss AS $cast)'; if (cast != null) ss = 'CAST ($ss AS $cast)';
if (aliases.containsKey(s)) {
if (cast != null) {
ss = '($ss) AS ${aliases[s]}';
} else {
ss = '$ss AS ${aliases[s]}';
}
}
return ss; return ss;
})); }));
_joins.forEach((j) { _joins.forEach((j) {

View file

@ -7,6 +7,9 @@ abstract class QueryBase<T> {
/// Casts to perform when querying the database. /// Casts to perform when querying the database.
Map<String, String> get casts => {}; Map<String, String> get casts => {};
/// `AS` aliases to inject into the query, if any.
Map<String, String> aliases = {};
/// Values to insert into a prepared statement. /// Values to insert into a prepared statement.
final Map<String, dynamic> substitutionValues = {}; final Map<String, dynamic> substitutionValues = {};
@ -21,7 +24,12 @@ abstract class QueryBase<T> {
/// A String of all [fields], joined by a comma (`,`). /// A String of all [fields], joined by a comma (`,`).
String get fieldSet => fields.map((k) { String get fieldSet => fields.map((k) {
var cast = casts[k]; var cast = casts[k];
if (!aliases.containsKey(k)) {
return cast == null ? k : 'CAST ($k AS $cast)'; return cast == null ? k : 'CAST ($k AS $cast)';
} else {
var inner = cast == null ? k : '(CAST ($k AS $cast))';
return '$inner AS ${aliases[k]}';
}
}).join(', '); }).join(', ');
String compile(Set<String> trampoline, String compile(Set<String> trampoline,

View file

@ -1,5 +1,5 @@
name: angel_orm name: angel_orm
version: 2.1.0-beta version: 2.1.0-beta.1
description: Runtime support for Angel's ORM. Includes base classes for queries. description: Runtime support for Angel's ORM. Includes base classes for queries.
author: Tobe O <thosakwe@gmail.com> author: Tobe O <thosakwe@gmail.com>
homepage: https://github.com/angel-dart/orm homepage: https://github.com/angel-dart/orm