lists
This commit is contained in:
parent
599104f353
commit
50c4b394c7
24 changed files with 377 additions and 229 deletions
|
@ -1,3 +1,6 @@
|
|||
# 2.0.0-dev.18
|
||||
* Add `ListSqlExpressionBuilder` (still in development).
|
||||
|
||||
# 2.0.0-dev.17
|
||||
* Add `EnumSqlExpressionBuilder`.
|
||||
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import 'dart:convert';
|
||||
|
||||
import 'package:charcode/ascii.dart';
|
||||
import 'package:intl/intl.dart' show DateFormat;
|
||||
import 'package:string_scanner/string_scanner.dart';
|
||||
|
@ -47,14 +49,6 @@ abstract class SqlExpressionBuilder<T> {
|
|||
bool get hasValue;
|
||||
|
||||
String compile();
|
||||
|
||||
void isBetween(T lower, T upper);
|
||||
|
||||
void isNotBetween(T lower, T upper);
|
||||
|
||||
void isIn(Iterable<T> values);
|
||||
|
||||
void isNotIn(Iterable<T> values);
|
||||
}
|
||||
|
||||
class NumericSqlExpressionBuilder<T extends num>
|
||||
|
@ -116,25 +110,21 @@ class NumericSqlExpressionBuilder<T extends num>
|
|||
_change('!=', value);
|
||||
}
|
||||
|
||||
@override
|
||||
void isBetween(T lower, T upper) {
|
||||
_raw = 'BETWEEN $lower AND $upper';
|
||||
_hasValue = true;
|
||||
}
|
||||
|
||||
@override
|
||||
void isNotBetween(T lower, T upper) {
|
||||
_raw = 'NOT BETWEEN $lower AND $upper';
|
||||
_hasValue = true;
|
||||
}
|
||||
|
||||
@override
|
||||
void isIn(Iterable<T> values) {
|
||||
_raw = 'IN (' + values.join(', ') + ')';
|
||||
_hasValue = true;
|
||||
}
|
||||
|
||||
@override
|
||||
void isNotIn(Iterable<T> values) {
|
||||
_raw = 'NOT IN (' + values.join(', ') + ')';
|
||||
_hasValue = true;
|
||||
|
@ -170,7 +160,7 @@ class EnumSqlExpressionBuilder<T> extends SqlExpressionBuilder<T> {
|
|||
if (_value == null) return null;
|
||||
return '$_op $_value';
|
||||
}
|
||||
|
||||
|
||||
void isNull() {
|
||||
_hasValue = true;
|
||||
_raw = 'IS NOT NULL';
|
||||
|
@ -189,19 +179,15 @@ class EnumSqlExpressionBuilder<T> extends SqlExpressionBuilder<T> {
|
|||
_change('!=', value);
|
||||
}
|
||||
|
||||
@override
|
||||
void isBetween(T lower, T upper) => throw _unsupported();
|
||||
|
||||
@override
|
||||
void isNotBetween(T lower, T upper) => throw _unsupported();
|
||||
|
||||
@override
|
||||
void isIn(Iterable<T> values) {
|
||||
_raw = 'IN (' + values.map(_getValue).join(', ') + ')';
|
||||
_hasValue = true;
|
||||
}
|
||||
|
||||
@override
|
||||
void isNotIn(Iterable<T> values) {
|
||||
_raw = 'NOT IN (' + values.map(_getValue).join(', ') + ')';
|
||||
_hasValue = true;
|
||||
|
@ -262,7 +248,6 @@ class StringSqlExpressionBuilder extends SqlExpressionBuilder<String> {
|
|||
_hasValue = true;
|
||||
}
|
||||
|
||||
@override
|
||||
void isBetween(String lower, String upper) {
|
||||
query.substitutionValues[lowerName] = lower;
|
||||
query.substitutionValues[upperName] = upper;
|
||||
|
@ -270,7 +255,6 @@ class StringSqlExpressionBuilder extends SqlExpressionBuilder<String> {
|
|||
_hasValue = true;
|
||||
}
|
||||
|
||||
@override
|
||||
void isNotBetween(String lower, String upper) {
|
||||
query.substitutionValues[lowerName] = lower;
|
||||
query.substitutionValues[upperName] = upper;
|
||||
|
@ -288,13 +272,11 @@ class StringSqlExpressionBuilder extends SqlExpressionBuilder<String> {
|
|||
')';
|
||||
}
|
||||
|
||||
@override
|
||||
void isIn(Iterable<String> values) {
|
||||
_raw = _in(values);
|
||||
_hasValue = true;
|
||||
}
|
||||
|
||||
@override
|
||||
void isNotIn(Iterable<String> values) {
|
||||
_raw = 'NOT ' + _in(values);
|
||||
_hasValue = true;
|
||||
|
@ -338,26 +320,6 @@ class BooleanSqlExpressionBuilder extends SqlExpressionBuilder<bool> {
|
|||
void notEquals(bool value) {
|
||||
_change('!=', value);
|
||||
}
|
||||
|
||||
@override
|
||||
void isBetween(bool lower, bool upper) => throw new UnsupportedError(
|
||||
'Booleans do not support BETWEEN expressions.');
|
||||
|
||||
@override
|
||||
void isNotBetween(bool lower, bool upper) => isBetween(lower, upper);
|
||||
|
||||
@override
|
||||
void isIn(Iterable<bool> values) {
|
||||
_raw = 'IN (' + values.map((b) => b ? 'TRUE' : 'FALSE').join(', ') + ')';
|
||||
_hasValue = true;
|
||||
}
|
||||
|
||||
@override
|
||||
void isNotIn(Iterable<bool> values) {
|
||||
_raw =
|
||||
'NOT IN (' + values.map((b) => b ? 'TRUE' : 'FALSE').join(', ') + ')';
|
||||
_hasValue = true;
|
||||
}
|
||||
}
|
||||
|
||||
class DateTimeSqlExpressionBuilder extends SqlExpressionBuilder<DateTime> {
|
||||
|
@ -425,27 +387,23 @@ class DateTimeSqlExpressionBuilder extends SqlExpressionBuilder<DateTime> {
|
|||
_change('>=', value, includeTime != false);
|
||||
}
|
||||
|
||||
@override
|
||||
void isIn(Iterable<DateTime> values) {
|
||||
_raw = '$columnName IN (' +
|
||||
values.map(dateYmdHms.format).map((s) => '$s').join(', ') +
|
||||
')';
|
||||
}
|
||||
|
||||
@override
|
||||
void isNotIn(Iterable<DateTime> values) {
|
||||
_raw = '$columnName NOT IN (' +
|
||||
values.map(dateYmdHms.format).map((s) => '$s').join(', ') +
|
||||
')';
|
||||
}
|
||||
|
||||
@override
|
||||
void isBetween(DateTime lower, DateTime upper) {
|
||||
var l = dateYmdHms.format(lower), u = dateYmdHms.format(upper);
|
||||
_raw = "$columnName BETWEEN '$l' and '$u'";
|
||||
}
|
||||
|
||||
@override
|
||||
void isNotBetween(DateTime lower, DateTime upper) {
|
||||
var l = dateYmdHms.format(lower), u = dateYmdHms.format(upper);
|
||||
_raw = "$columnName NOT BETWEEN '$l' and '$u'";
|
||||
|
@ -471,60 +429,89 @@ class DateTimeSqlExpressionBuilder extends SqlExpressionBuilder<DateTime> {
|
|||
}
|
||||
}
|
||||
|
||||
class MapSqlExpressionBuilder extends SqlExpressionBuilder {
|
||||
abstract class JsonSqlExpressionBuilder<T, K> extends SqlExpressionBuilder<T> {
|
||||
final List<JsonSqlExpressionBuilderProperty> _properties = [];
|
||||
bool _hasValue = false;
|
||||
Map _value;
|
||||
T _value;
|
||||
String _op;
|
||||
String _raw;
|
||||
|
||||
MapSqlExpressionBuilder(Query query, String columnName)
|
||||
JsonSqlExpressionBuilder(Query query, String columnName)
|
||||
: super(query, columnName);
|
||||
|
||||
MapSqlExpressionBuilderProperty operator [](String name) {
|
||||
return MapSqlExpressionBuilderProperty(this, name);
|
||||
JsonSqlExpressionBuilderProperty operator [](K name) {
|
||||
var p = _property(name);
|
||||
_properties.add(p);
|
||||
return p;
|
||||
}
|
||||
|
||||
bool get hasRaw => _raw != null;
|
||||
JsonSqlExpressionBuilderProperty _property(K name);
|
||||
|
||||
bool get hasRaw => _raw != null || _properties.any((p) => p.hasValue);
|
||||
|
||||
@override
|
||||
bool get hasValue => _hasValue;
|
||||
bool get hasValue => _hasValue || _properties.any((p) => p.hasValue);
|
||||
|
||||
UnsupportedError _unsupported() =>
|
||||
UnsupportedError('JSON/JSONB does not support this operation.');
|
||||
_encodeValue(T v) => v;
|
||||
|
||||
void _append(SqlExpressionBuilder b) {
|
||||
var c = b.compile();
|
||||
if (c != null) {
|
||||
_hasValue = true;
|
||||
_raw ??= '';
|
||||
|
||||
if (b is! DateTimeSqlExpressionBuilder) {
|
||||
_raw += '${b.columnName} ';
|
||||
}
|
||||
|
||||
_raw += c;
|
||||
}
|
||||
}
|
||||
|
||||
bool _change(String op, Map value) {
|
||||
bool _change(String op, T value) {
|
||||
_raw = null;
|
||||
_op = op;
|
||||
_value = value;
|
||||
query.substitutionValues[substitution] = _value;
|
||||
query.substitutionValues[substitution] = _encodeValue(_value);
|
||||
return _hasValue = true;
|
||||
}
|
||||
|
||||
@override
|
||||
String compile() {
|
||||
var s = _compile();
|
||||
if (!_properties.any((p) => p.hasValue)) return s;
|
||||
s ??= '';
|
||||
|
||||
for (var p in _properties) {
|
||||
if (p.hasValue) {
|
||||
var c = p.compile();
|
||||
|
||||
if (c != null) {
|
||||
_hasValue = true;
|
||||
s ??= '';
|
||||
|
||||
if (p.typed is! DateTimeSqlExpressionBuilder) {
|
||||
s += '${p.typed.columnName} ';
|
||||
}
|
||||
|
||||
s += c;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
String _compile() {
|
||||
if (_raw != null) return _raw;
|
||||
if (_value == null) return null;
|
||||
return "::jsonb $_op @$substitution::jsonb";
|
||||
}
|
||||
|
||||
void contains(Map value) {
|
||||
void contains(T value) {
|
||||
_change('@>', value);
|
||||
}
|
||||
|
||||
void equals(T value) {
|
||||
_change('=', value);
|
||||
}
|
||||
}
|
||||
|
||||
class MapSqlExpressionBuilder extends JsonSqlExpressionBuilder<Map, String> {
|
||||
MapSqlExpressionBuilder(Query query, String columnName)
|
||||
: super(query, columnName);
|
||||
|
||||
@override
|
||||
JsonSqlExpressionBuilderProperty _property(String name) {
|
||||
return JsonSqlExpressionBuilderProperty(this, name, false);
|
||||
}
|
||||
|
||||
void containsKey(String key) {
|
||||
this[key].isNotNull();
|
||||
}
|
||||
|
@ -532,89 +519,96 @@ class MapSqlExpressionBuilder extends SqlExpressionBuilder {
|
|||
void containsPair(key, value) {
|
||||
contains({key: value});
|
||||
}
|
||||
|
||||
void equals(Map value) {
|
||||
_change('=', value);
|
||||
}
|
||||
|
||||
@override
|
||||
void isBetween(lower, upper) => throw _unsupported();
|
||||
|
||||
@override
|
||||
void isIn(Iterable values) => throw _unsupported();
|
||||
|
||||
@override
|
||||
void isNotBetween(lower, upper) => throw _unsupported();
|
||||
|
||||
@override
|
||||
void isNotIn(Iterable values) => throw _unsupported();
|
||||
}
|
||||
|
||||
class MapSqlExpressionBuilderProperty {
|
||||
final MapSqlExpressionBuilder builder;
|
||||
final String name;
|
||||
class ListSqlExpressionBuilder extends JsonSqlExpressionBuilder<List, int> {
|
||||
ListSqlExpressionBuilder(Query query, String columnName)
|
||||
: super(query, columnName);
|
||||
|
||||
MapSqlExpressionBuilderProperty(this.builder, this.name);
|
||||
@override
|
||||
_encodeValue(List v) => json.encode(v);
|
||||
|
||||
@override
|
||||
JsonSqlExpressionBuilderProperty _property(int name) {
|
||||
return JsonSqlExpressionBuilderProperty(this, name.toString(), true);
|
||||
}
|
||||
}
|
||||
|
||||
class JsonSqlExpressionBuilderProperty {
|
||||
final JsonSqlExpressionBuilder builder;
|
||||
final String name;
|
||||
final bool isInt;
|
||||
SqlExpressionBuilder _typed;
|
||||
|
||||
JsonSqlExpressionBuilderProperty(this.builder, this.name, this.isInt);
|
||||
|
||||
SqlExpressionBuilder get typed => _typed;
|
||||
|
||||
bool get hasValue => _typed?.hasValue == true;
|
||||
|
||||
String compile() => _typed?.compile();
|
||||
|
||||
T _set<T extends SqlExpressionBuilder>(T Function() value) {
|
||||
if (_typed is T) {
|
||||
return _typed as T;
|
||||
} else if (_typed != null) {
|
||||
throw StateError(
|
||||
'$nameString is already typed as $_typed, and cannot be changed.');
|
||||
} else {
|
||||
_typed = value().._isProperty = true;
|
||||
return _typed as T;
|
||||
}
|
||||
}
|
||||
|
||||
String get nameString {
|
||||
if (isInt) {
|
||||
return '(${builder.columnName}->>$name)::jsonb';
|
||||
} else {
|
||||
return "(${builder.columnName}->>'$name')::jsonb";
|
||||
}
|
||||
}
|
||||
|
||||
void isNotNull() {
|
||||
builder
|
||||
.._hasValue = true
|
||||
.._raw ??= ''
|
||||
.._raw += "${builder.columnName}->>'$name' IS NOT NULL";
|
||||
.._raw += "$nameString IS NOT NULL";
|
||||
}
|
||||
|
||||
void isNull() {
|
||||
builder
|
||||
.._hasValue = true
|
||||
.._raw ??= ''
|
||||
.._raw += "${builder.columnName}->>'$name' IS NULL";
|
||||
.._raw += "$nameString IS NULL";
|
||||
}
|
||||
|
||||
void asString(void Function(StringSqlExpressionBuilder) f) {
|
||||
var b = StringSqlExpressionBuilder(
|
||||
builder.query, "${builder.columnName}->>'$name'")
|
||||
.._isProperty = true;
|
||||
f(b);
|
||||
builder._append(b);
|
||||
StringSqlExpressionBuilder get asString {
|
||||
return _set(() => StringSqlExpressionBuilder(builder.query, nameString));
|
||||
}
|
||||
|
||||
void asBool(void Function(BooleanSqlExpressionBuilder) f) {
|
||||
var b = BooleanSqlExpressionBuilder(
|
||||
builder.query, "${builder.columnName}->>'$name'")
|
||||
.._isProperty = true;
|
||||
f(b);
|
||||
builder._append(b);
|
||||
BooleanSqlExpressionBuilder get asBool {
|
||||
return _set(() => BooleanSqlExpressionBuilder(builder.query, nameString));
|
||||
}
|
||||
|
||||
void asDateTime(void Function(DateTimeSqlExpressionBuilder) f) {
|
||||
var b = DateTimeSqlExpressionBuilder(
|
||||
builder.query, "${builder.columnName}->>'$name'")
|
||||
.._isProperty = true;
|
||||
f(b);
|
||||
builder._append(b);
|
||||
DateTimeSqlExpressionBuilder get asDateTime {
|
||||
return _set(() => DateTimeSqlExpressionBuilder(builder.query, nameString));
|
||||
}
|
||||
|
||||
void asDouble(void Function(NumericSqlExpressionBuilder<double>) f) {
|
||||
var b = NumericSqlExpressionBuilder<double>(
|
||||
builder.query, "${builder.columnName}->>'$name'")
|
||||
.._isProperty = true;
|
||||
f(b);
|
||||
builder._append(b);
|
||||
NumericSqlExpressionBuilder<double> get asDouble {
|
||||
return _set(
|
||||
() => NumericSqlExpressionBuilder<double>(builder.query, nameString));
|
||||
}
|
||||
|
||||
void asInt(void Function(NumericSqlExpressionBuilder<int>) f) {
|
||||
var b = NumericSqlExpressionBuilder<int>(
|
||||
builder.query, "${builder.columnName}->>'$name'")
|
||||
.._isProperty = true;
|
||||
f(b);
|
||||
builder._append(b);
|
||||
NumericSqlExpressionBuilder<int> get asInt {
|
||||
return _set(
|
||||
() => NumericSqlExpressionBuilder<int>(builder.query, nameString));
|
||||
}
|
||||
|
||||
void asMap(void Function(MapSqlExpressionBuilder) f) {
|
||||
var b = MapSqlExpressionBuilder(
|
||||
builder.query, "${builder.columnName}->>'$name'")
|
||||
.._isProperty = true;
|
||||
f(b);
|
||||
builder._append(b);
|
||||
MapSqlExpressionBuilder get asMap {
|
||||
return _set(() => MapSqlExpressionBuilder(builder.query, nameString));
|
||||
}
|
||||
|
||||
ListSqlExpressionBuilder get asList {
|
||||
return _set(() => ListSqlExpressionBuilder(builder.query, nameString));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -341,8 +341,19 @@ abstract class Query<T, Where extends QueryWhere> extends QueryBase<T> {
|
|||
}
|
||||
|
||||
abstract class QueryValues {
|
||||
Map<String, String> get casts => {};
|
||||
|
||||
Map<String, dynamic> toMap();
|
||||
|
||||
String applyCast(String name, String sub) {
|
||||
if (casts.containsKey(name)) {
|
||||
var type = casts[name];
|
||||
return 'CAST ($sub as $type)';
|
||||
} else {
|
||||
return sub;
|
||||
}
|
||||
}
|
||||
|
||||
String compileInsert(Query query, String tableName) {
|
||||
var data = toMap();
|
||||
if (data.isEmpty) return null;
|
||||
|
@ -355,8 +366,9 @@ abstract class QueryValues {
|
|||
if (i++ > 0) b.write(', ');
|
||||
|
||||
var name = query.reserveName(entry.key);
|
||||
var s = applyCast(entry.key, '@$name');
|
||||
query.substitutionValues[name] = entry.value;
|
||||
b.write('@$name');
|
||||
b.write(s);
|
||||
}
|
||||
|
||||
b.write(')');
|
||||
|
@ -376,8 +388,9 @@ abstract class QueryValues {
|
|||
b.write('=');
|
||||
|
||||
var name = query.reserveName(entry.key);
|
||||
var s = applyCast(entry.key, '@$name');
|
||||
query.substitutionValues[name] = entry.value;
|
||||
b.write('@$name');
|
||||
b.write(s);
|
||||
}
|
||||
return b.toString();
|
||||
}
|
||||
|
@ -421,7 +434,7 @@ abstract class QueryWhere {
|
|||
if (builder.hasValue) {
|
||||
if (i++ > 0) b.write(' AND ');
|
||||
if (builder is DateTimeSqlExpressionBuilder ||
|
||||
(builder is MapSqlExpressionBuilder && builder.hasRaw)) {
|
||||
(builder is JsonSqlExpressionBuilder && builder.hasRaw)) {
|
||||
if (tableName != null) b.write('$tableName.');
|
||||
b.write(builder.compile());
|
||||
} else {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
name: angel_orm
|
||||
version: 2.0.0-dev.17
|
||||
version: 2.0.0-dev.18
|
||||
description: Runtime support for Angel's ORM. Includes base classes for queries.
|
||||
author: Tobe O <thosakwe@gmail.com>
|
||||
homepage: https://github.com/angel-dart/orm
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
# 2.0.0-dev.4
|
||||
* List generation support.
|
||||
|
||||
# 2.0.0-dev.3
|
||||
* Add JSON/JSONB support for Maps.
|
||||
|
||||
|
|
|
@ -217,6 +217,8 @@ ColumnType inferColumnType(DartType type) {
|
|||
return ColumnType.timeStamp;
|
||||
if (const TypeChecker.fromRuntime(Map).isAssignableFromType(type))
|
||||
return ColumnType.jsonb;
|
||||
if (const TypeChecker.fromRuntime(List).isAssignableFromType(type))
|
||||
return ColumnType.jsonb;
|
||||
if (type is InterfaceType && type.element.isEnum) return ColumnType.int;
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -515,6 +515,9 @@ class OrmGenerator extends GeneratorForAnnotation<Orm> {
|
|||
} else if (const TypeChecker.fromRuntime(Map)
|
||||
.isAssignableFromType(type)) {
|
||||
builderType = refer('MapSqlExpressionBuilder');
|
||||
} else if (const TypeChecker.fromRuntime(List)
|
||||
.isAssignableFromType(type)) {
|
||||
builderType = refer('ListSqlExpressionBuilder');
|
||||
} else if (ctx.relations.containsKey(field.name)) {
|
||||
var relation = ctx.relations[field.name];
|
||||
if (relation.type != RelationshipType.belongsTo)
|
||||
|
@ -565,6 +568,30 @@ class OrmGenerator extends GeneratorForAnnotation<Orm> {
|
|||
..name = '${rc.pascalCase}QueryValues'
|
||||
..extend = refer('MapQueryValues');
|
||||
|
||||
// Override casts so that we can cast Lists
|
||||
clazz.methods.add(Method((b) {
|
||||
b
|
||||
..name = 'casts'
|
||||
..annotations.add(refer('override'))
|
||||
..type = MethodType.getter
|
||||
..body = Block((b) {
|
||||
var args = <String, Expression>{};
|
||||
|
||||
for (var field in ctx.effectiveFields) {
|
||||
var fType = field.type;
|
||||
var name = ctx.buildContext.resolveFieldName(field.name);
|
||||
var type = ctx.columns[field.name]?.type?.name;
|
||||
if (type == null) continue;
|
||||
if (const TypeChecker.fromRuntime(List)
|
||||
.isAssignableFromType(fType)) {
|
||||
args[name] = literalString(type);
|
||||
}
|
||||
}
|
||||
|
||||
b.addExpression(literalMap(args).returned);
|
||||
});
|
||||
}));
|
||||
|
||||
// Each field generates a getter for setter
|
||||
for (var field in ctx.effectiveFields) {
|
||||
var fType = field.type;
|
||||
|
@ -580,6 +607,11 @@ class OrmGenerator extends GeneratorForAnnotation<Orm> {
|
|||
var asInt = value.asA(refer('int'));
|
||||
var t = convertTypeReference(fType);
|
||||
value = t.property('values').index(asInt);
|
||||
} else if (const TypeChecker.fromRuntime(List)
|
||||
.isAssignableFromType(fType)) {
|
||||
value = refer('json')
|
||||
.property('decode')
|
||||
.call([value.asA(refer('String'))]).asA(refer('List'));
|
||||
} else {
|
||||
value = value.asA(type);
|
||||
}
|
||||
|
@ -596,6 +628,9 @@ class OrmGenerator extends GeneratorForAnnotation<Orm> {
|
|||
|
||||
if (fType is InterfaceType && fType.element.isEnum) {
|
||||
value = value.property('index');
|
||||
} else if (const TypeChecker.fromRuntime(List)
|
||||
.isAssignableFromType(fType)) {
|
||||
value = refer('json').property('encode').call([value]);
|
||||
}
|
||||
|
||||
b
|
||||
|
@ -618,18 +653,13 @@ class OrmGenerator extends GeneratorForAnnotation<Orm> {
|
|||
..name = 'model'
|
||||
..type = ctx.buildContext.modelClassType))
|
||||
..body = new Block((b) {
|
||||
var args = <String, Expression>{};
|
||||
|
||||
for (var field in ctx.effectiveFields) {
|
||||
if (isSpecialId(ctx, field) || field is RelationFieldImpl)
|
||||
continue;
|
||||
args[ctx.buildContext.resolveFieldName(field.name)] =
|
||||
refer('model').property(field.name);
|
||||
b.addExpression(refer(field.name)
|
||||
.assign(refer('model').property(field.name)));
|
||||
}
|
||||
|
||||
b.addExpression(
|
||||
refer('values').property('addAll').call([literalMap(args)]));
|
||||
|
||||
for (var field in ctx.effectiveFields) {
|
||||
if (field is RelationFieldImpl) {
|
||||
var original = field.originalFieldName;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
name: angel_orm_generator
|
||||
version: 2.0.0-dev.3
|
||||
version: 2.0.0-dev.4
|
||||
description: Code generators for Angel's ORM. Generates query builder classes.
|
||||
author: Tobe O <thosakwe@gmail.com>
|
||||
homepage: https://github.com/angel-dart/orm
|
||||
|
|
|
@ -35,6 +35,7 @@ class PostgresExecutor extends QueryExecutor {
|
|||
if (!Platform.environment.containsKey('STFU')) {
|
||||
print('Running: $query');
|
||||
if (substitutionValues.isNotEmpty) print('Values: $substitutionValues');
|
||||
print(substitutionValues.map((k, v) => MapEntry(k, v.runtimeType)));
|
||||
}
|
||||
return connection.query(query, substitutionValues: substitutionValues);
|
||||
}
|
||||
|
|
|
@ -10,14 +10,20 @@ main() {
|
|||
});
|
||||
|
||||
test('insert', () async {
|
||||
var query = HasMapQuery()..values.value = {'foo': 'bar'};
|
||||
var query = HasMapQuery();
|
||||
query.values
|
||||
..value = {'foo': 'bar'}
|
||||
..list = ['1', 2, 3.0];
|
||||
var model = await query.insert(executor);
|
||||
print(model.toJson());
|
||||
expect(model, HasMap(value: {'foo': 'bar'}));
|
||||
expect(model, HasMap(value: {'foo': 'bar'}, list: ['1', 2, 3.0]));
|
||||
});
|
||||
|
||||
test('insert', () async {
|
||||
var query = HasMapQuery()..values.value = {'foo': 'bar'};
|
||||
test('update', () async {
|
||||
var query = HasMapQuery();
|
||||
query.values
|
||||
..value = {'foo': 'bar'}
|
||||
..list = ['1', 2, 3.0];
|
||||
var model = await query.insert(executor);
|
||||
print(model.toJson());
|
||||
|
||||
|
@ -29,7 +35,10 @@ main() {
|
|||
HasMap initialValue;
|
||||
|
||||
setUp(() async {
|
||||
var query = HasMapQuery()..values.value = {'foo': 'bar'};
|
||||
var query = HasMapQuery();
|
||||
query.values
|
||||
..value = {'foo': 'bar'}
|
||||
..list = ['1', 2, 3.0];
|
||||
initialValue = await query.insert(executor);
|
||||
});
|
||||
|
||||
|
@ -42,15 +51,35 @@ main() {
|
|||
var query = HasMapQuery();
|
||||
query.where.value.equals({'foo': 'bar'});
|
||||
expect(await query.get(executor), [initialValue]);
|
||||
|
||||
query = HasMapQuery();
|
||||
query.where.value.equals({'foo': 'baz'});
|
||||
expect(await query.get(executor), isEmpty);
|
||||
});
|
||||
|
||||
test('property equals', () async {
|
||||
test('list equals', () async {
|
||||
var query = HasMapQuery();
|
||||
query.where.value['foo'].asString((b) => b.equals('bar'));
|
||||
query.where.list.equals(['1', 2, 3.0]);
|
||||
expect(await query.get(executor), [initialValue]);
|
||||
|
||||
query = HasMapQuery();
|
||||
query.where.value['foo'].asString((b) => b.equals('baz'));
|
||||
query.where.list.equals(['10', 20, 30.0]);
|
||||
expect(await query.get(executor), isEmpty);
|
||||
});
|
||||
|
||||
test('property equals', () async {
|
||||
var query = HasMapQuery()..where.value['foo'].asString.equals('bar');
|
||||
expect(await query.get(executor), [initialValue]);
|
||||
|
||||
query = HasMapQuery()..where.value['foo'].asString.equals('baz');
|
||||
expect(await query.get(executor), []);
|
||||
});
|
||||
|
||||
test('index equals', () async {
|
||||
var query = HasMapQuery()..where.list[0].asString.equals('1');
|
||||
expect(await query.get(executor), [initialValue]);
|
||||
|
||||
query = HasMapQuery()..where.list[1].asInt.equals(3);
|
||||
expect(await query.get(executor), []);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
CREATE TEMPORARY TABLE "has_maps" (
|
||||
id serial PRIMARY KEY,
|
||||
value jsonb not null,
|
||||
list jsonb not null,
|
||||
created_at timestamp,
|
||||
updated_at timestamp
|
||||
);
|
|
@ -95,6 +95,11 @@ class AuthorQueryWhere extends QueryWhere {
|
|||
}
|
||||
|
||||
class AuthorQueryValues extends MapQueryValues {
|
||||
@override
|
||||
get casts {
|
||||
return {};
|
||||
}
|
||||
|
||||
int get id {
|
||||
return (values['id'] as int);
|
||||
}
|
||||
|
@ -116,11 +121,9 @@ class AuthorQueryValues extends MapQueryValues {
|
|||
|
||||
set updatedAt(DateTime value) => values['updated_at'] = value;
|
||||
void copyFrom(Author model) {
|
||||
values.addAll({
|
||||
'name': model.name,
|
||||
'created_at': model.createdAt,
|
||||
'updated_at': model.updatedAt
|
||||
});
|
||||
name = model.name;
|
||||
createdAt = model.createdAt;
|
||||
updatedAt = model.updatedAt;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -123,6 +123,11 @@ class BookQueryWhere extends QueryWhere {
|
|||
}
|
||||
|
||||
class BookQueryValues extends MapQueryValues {
|
||||
@override
|
||||
get casts {
|
||||
return {};
|
||||
}
|
||||
|
||||
int get id {
|
||||
return (values['id'] as int);
|
||||
}
|
||||
|
@ -154,11 +159,9 @@ class BookQueryValues extends MapQueryValues {
|
|||
|
||||
set updatedAt(DateTime value) => values['updated_at'] = value;
|
||||
void copyFrom(Book model) {
|
||||
values.addAll({
|
||||
'name': model.name,
|
||||
'created_at': model.createdAt,
|
||||
'updated_at': model.updatedAt
|
||||
});
|
||||
name = model.name;
|
||||
createdAt = model.createdAt;
|
||||
updatedAt = model.updatedAt;
|
||||
if (model.author != null) {
|
||||
values['author_id'] = int.parse(model.author.id);
|
||||
}
|
||||
|
|
|
@ -127,6 +127,11 @@ class CarQueryWhere extends QueryWhere {
|
|||
}
|
||||
|
||||
class CarQueryValues extends MapQueryValues {
|
||||
@override
|
||||
get casts {
|
||||
return {};
|
||||
}
|
||||
|
||||
int get id {
|
||||
return (values['id'] as int);
|
||||
}
|
||||
|
@ -163,14 +168,12 @@ class CarQueryValues extends MapQueryValues {
|
|||
|
||||
set updatedAt(DateTime value) => values['updated_at'] = value;
|
||||
void copyFrom(Car model) {
|
||||
values.addAll({
|
||||
'make': model.make,
|
||||
'description': model.description,
|
||||
'family_friendly': model.familyFriendly,
|
||||
'recalled_at': model.recalledAt,
|
||||
'created_at': model.createdAt,
|
||||
'updated_at': model.updatedAt
|
||||
});
|
||||
make = model.make;
|
||||
description = model.description;
|
||||
familyFriendly = model.familyFriendly;
|
||||
recalledAt = model.recalledAt;
|
||||
createdAt = model.createdAt;
|
||||
updatedAt = model.updatedAt;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -90,6 +90,11 @@ class CustomerQueryWhere extends QueryWhere {
|
|||
}
|
||||
|
||||
class CustomerQueryValues extends MapQueryValues {
|
||||
@override
|
||||
get casts {
|
||||
return {};
|
||||
}
|
||||
|
||||
int get id {
|
||||
return (values['id'] as int);
|
||||
}
|
||||
|
@ -106,8 +111,8 @@ class CustomerQueryValues extends MapQueryValues {
|
|||
|
||||
set updatedAt(DateTime value) => values['updated_at'] = value;
|
||||
void copyFrom(Customer model) {
|
||||
values
|
||||
.addAll({'created_at': model.createdAt, 'updated_at': model.updatedAt});
|
||||
createdAt = model.createdAt;
|
||||
updatedAt = model.updatedAt;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -100,6 +100,11 @@ class FootQueryWhere extends QueryWhere {
|
|||
}
|
||||
|
||||
class FootQueryValues extends MapQueryValues {
|
||||
@override
|
||||
get casts {
|
||||
return {};
|
||||
}
|
||||
|
||||
int get id {
|
||||
return (values['id'] as int);
|
||||
}
|
||||
|
@ -126,12 +131,10 @@ class FootQueryValues extends MapQueryValues {
|
|||
|
||||
set updatedAt(DateTime value) => values['updated_at'] = value;
|
||||
void copyFrom(Foot model) {
|
||||
values.addAll({
|
||||
'leg_id': model.legId,
|
||||
'n_toes': model.nToes,
|
||||
'created_at': model.createdAt,
|
||||
'updated_at': model.updatedAt
|
||||
});
|
||||
legId = model.legId;
|
||||
nToes = model.nToes;
|
||||
createdAt = model.createdAt;
|
||||
updatedAt = model.updatedAt;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -100,6 +100,11 @@ class FruitQueryWhere extends QueryWhere {
|
|||
}
|
||||
|
||||
class FruitQueryValues extends MapQueryValues {
|
||||
@override
|
||||
get casts {
|
||||
return {};
|
||||
}
|
||||
|
||||
int get id {
|
||||
return (values['id'] as int);
|
||||
}
|
||||
|
@ -126,12 +131,10 @@ class FruitQueryValues extends MapQueryValues {
|
|||
|
||||
set updatedAt(DateTime value) => values['updated_at'] = value;
|
||||
void copyFrom(Fruit model) {
|
||||
values.addAll({
|
||||
'tree_id': model.treeId,
|
||||
'common_name': model.commonName,
|
||||
'created_at': model.createdAt,
|
||||
'updated_at': model.updatedAt
|
||||
});
|
||||
treeId = model.treeId;
|
||||
commonName = model.commonName;
|
||||
createdAt = model.createdAt;
|
||||
updatedAt = model.updatedAt;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -96,6 +96,11 @@ class HasCarQueryWhere extends QueryWhere {
|
|||
}
|
||||
|
||||
class HasCarQueryValues extends MapQueryValues {
|
||||
@override
|
||||
get casts {
|
||||
return {};
|
||||
}
|
||||
|
||||
int get id {
|
||||
return (values['id'] as int);
|
||||
}
|
||||
|
@ -117,11 +122,9 @@ class HasCarQueryValues extends MapQueryValues {
|
|||
|
||||
set updatedAt(DateTime value) => values['updated_at'] = value;
|
||||
void copyFrom(HasCar model) {
|
||||
values.addAll({
|
||||
'type': model.type,
|
||||
'created_at': model.createdAt,
|
||||
'updated_at': model.updatedAt
|
||||
});
|
||||
type = model.type;
|
||||
createdAt = model.createdAt;
|
||||
updatedAt = model.updatedAt;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -10,4 +10,6 @@ part 'has_map.g.dart';
|
|||
@serializable
|
||||
abstract class _HasMap {
|
||||
Map get value;
|
||||
|
||||
List get list;
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ class HasMapMigration extends Migration {
|
|||
up(Schema schema) {
|
||||
schema.create('has_maps', (table) {
|
||||
table.declare('value', new ColumnType('jsonb'));
|
||||
table.declare('list', new ColumnType('jsonb'));
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -41,7 +42,7 @@ class HasMapQuery extends Query<HasMap, HasMapQueryWhere> {
|
|||
|
||||
@override
|
||||
get fields {
|
||||
return const ['value'];
|
||||
return const ['value', 'list'];
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -56,7 +57,9 @@ class HasMapQuery extends Query<HasMap, HasMapQueryWhere> {
|
|||
|
||||
static HasMap parseRow(List row) {
|
||||
if (row.every((x) => x == null)) return null;
|
||||
var model = new HasMap(value: (row[0] as Map<dynamic, dynamic>));
|
||||
var model = new HasMap(
|
||||
value: (row[0] as Map<dynamic, dynamic>),
|
||||
list: (row[1] as List<dynamic>));
|
||||
return model;
|
||||
}
|
||||
|
||||
|
@ -68,24 +71,38 @@ class HasMapQuery extends Query<HasMap, HasMapQueryWhere> {
|
|||
|
||||
class HasMapQueryWhere extends QueryWhere {
|
||||
HasMapQueryWhere(HasMapQuery query)
|
||||
: value = new MapSqlExpressionBuilder(query, 'value');
|
||||
: value = new MapSqlExpressionBuilder(query, 'value'),
|
||||
list = new ListSqlExpressionBuilder(query, 'list');
|
||||
|
||||
final MapSqlExpressionBuilder value;
|
||||
|
||||
final ListSqlExpressionBuilder list;
|
||||
|
||||
@override
|
||||
get expressionBuilders {
|
||||
return [value];
|
||||
return [value, list];
|
||||
}
|
||||
}
|
||||
|
||||
class HasMapQueryValues extends MapQueryValues {
|
||||
@override
|
||||
get casts {
|
||||
return {'list': 'jsonb'};
|
||||
}
|
||||
|
||||
Map<dynamic, dynamic> get value {
|
||||
return (values['value'] as Map<dynamic, dynamic>);
|
||||
}
|
||||
|
||||
set value(Map<dynamic, dynamic> value) => values['value'] = value;
|
||||
List<dynamic> get list {
|
||||
return (json.decode((values['list'] as String)) as List);
|
||||
}
|
||||
|
||||
set list(List<dynamic> value) => values['list'] = json.encode(value);
|
||||
void copyFrom(HasMap model) {
|
||||
values.addAll({'value': model.value});
|
||||
value = model.value;
|
||||
list = model.list;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -95,25 +112,30 @@ class HasMapQueryValues extends MapQueryValues {
|
|||
|
||||
@generatedSerializable
|
||||
class HasMap implements _HasMap {
|
||||
const HasMap({Map<dynamic, dynamic> this.value});
|
||||
const HasMap({Map<dynamic, dynamic> this.value, List<dynamic> this.list});
|
||||
|
||||
@override
|
||||
final Map<dynamic, dynamic> value;
|
||||
|
||||
HasMap copyWith({Map<dynamic, dynamic> value}) {
|
||||
return new HasMap(value: value ?? this.value);
|
||||
@override
|
||||
final List<dynamic> list;
|
||||
|
||||
HasMap copyWith({Map<dynamic, dynamic> value, List<dynamic> list}) {
|
||||
return new HasMap(value: value ?? this.value, list: list ?? this.list);
|
||||
}
|
||||
|
||||
bool operator ==(other) {
|
||||
return other is _HasMap &&
|
||||
const MapEquality<dynamic, dynamic>(
|
||||
keys: const DefaultEquality(), values: const DefaultEquality())
|
||||
.equals(other.value, value);
|
||||
.equals(other.value, value) &&
|
||||
const ListEquality<dynamic>(const DefaultEquality())
|
||||
.equals(other.list, list);
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
return hashObjects([value]);
|
||||
return hashObjects([value, list]);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
|
@ -130,6 +152,9 @@ abstract class HasMapSerializer {
|
|||
return new HasMap(
|
||||
value: map['value'] is Map
|
||||
? (map['value'] as Map).cast<dynamic, dynamic>()
|
||||
: null,
|
||||
list: map['list'] is Iterable
|
||||
? (map['list'] as Iterable).cast<dynamic>().toList()
|
||||
: null);
|
||||
}
|
||||
|
||||
|
@ -137,12 +162,14 @@ abstract class HasMapSerializer {
|
|||
if (model == null) {
|
||||
return null;
|
||||
}
|
||||
return {'value': model.value};
|
||||
return {'value': model.value, 'list': model.list};
|
||||
}
|
||||
}
|
||||
|
||||
abstract class HasMapFields {
|
||||
static const List<String> allFields = const <String>[value];
|
||||
static const List<String> allFields = const <String>[value, list];
|
||||
|
||||
static const String value = 'value';
|
||||
|
||||
static const String list = 'list';
|
||||
}
|
||||
|
|
|
@ -104,6 +104,11 @@ class LegQueryWhere extends QueryWhere {
|
|||
}
|
||||
|
||||
class LegQueryValues extends MapQueryValues {
|
||||
@override
|
||||
get casts {
|
||||
return {};
|
||||
}
|
||||
|
||||
int get id {
|
||||
return (values['id'] as int);
|
||||
}
|
||||
|
@ -125,11 +130,9 @@ class LegQueryValues extends MapQueryValues {
|
|||
|
||||
set updatedAt(DateTime value) => values['updated_at'] = value;
|
||||
void copyFrom(Leg model) {
|
||||
values.addAll({
|
||||
'name': model.name,
|
||||
'created_at': model.createdAt,
|
||||
'updated_at': model.updatedAt
|
||||
});
|
||||
name = model.name;
|
||||
createdAt = model.createdAt;
|
||||
updatedAt = model.updatedAt;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -131,6 +131,11 @@ class OrderQueryWhere extends QueryWhere {
|
|||
}
|
||||
|
||||
class OrderQueryValues extends MapQueryValues {
|
||||
@override
|
||||
get casts {
|
||||
return {};
|
||||
}
|
||||
|
||||
int get id {
|
||||
return (values['id'] as int);
|
||||
}
|
||||
|
@ -167,13 +172,11 @@ class OrderQueryValues extends MapQueryValues {
|
|||
|
||||
set updatedAt(DateTime value) => values['updated_at'] = value;
|
||||
void copyFrom(Order model) {
|
||||
values.addAll({
|
||||
'employee_id': model.employeeId,
|
||||
'order_date': model.orderDate,
|
||||
'shipper_id': model.shipperId,
|
||||
'created_at': model.createdAt,
|
||||
'updated_at': model.updatedAt
|
||||
});
|
||||
employeeId = model.employeeId;
|
||||
orderDate = model.orderDate;
|
||||
shipperId = model.shipperId;
|
||||
createdAt = model.createdAt;
|
||||
updatedAt = model.updatedAt;
|
||||
if (model.customer != null) {
|
||||
values['customer_id'] = int.parse(model.customer.id);
|
||||
}
|
||||
|
|
|
@ -165,6 +165,11 @@ class TreeQueryWhere extends QueryWhere {
|
|||
}
|
||||
|
||||
class TreeQueryValues extends MapQueryValues {
|
||||
@override
|
||||
get casts {
|
||||
return {};
|
||||
}
|
||||
|
||||
int get id {
|
||||
return (values['id'] as int);
|
||||
}
|
||||
|
@ -186,11 +191,9 @@ class TreeQueryValues extends MapQueryValues {
|
|||
|
||||
set updatedAt(DateTime value) => values['updated_at'] = value;
|
||||
void copyFrom(Tree model) {
|
||||
values.addAll({
|
||||
'rings': model.rings,
|
||||
'created_at': model.createdAt,
|
||||
'updated_at': model.updatedAt
|
||||
});
|
||||
rings = model.rings;
|
||||
createdAt = model.createdAt;
|
||||
updatedAt = model.updatedAt;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -147,6 +147,11 @@ class UserQueryWhere extends QueryWhere {
|
|||
}
|
||||
|
||||
class UserQueryValues extends MapQueryValues {
|
||||
@override
|
||||
get casts {
|
||||
return {};
|
||||
}
|
||||
|
||||
int get id {
|
||||
return (values['id'] as int);
|
||||
}
|
||||
|
@ -178,13 +183,11 @@ class UserQueryValues extends MapQueryValues {
|
|||
|
||||
set updatedAt(DateTime value) => values['updated_at'] = value;
|
||||
void copyFrom(User model) {
|
||||
values.addAll({
|
||||
'username': model.username,
|
||||
'password': model.password,
|
||||
'email': model.email,
|
||||
'created_at': model.createdAt,
|
||||
'updated_at': model.updatedAt
|
||||
});
|
||||
username = model.username;
|
||||
password = model.password;
|
||||
email = model.email;
|
||||
createdAt = model.createdAt;
|
||||
updatedAt = model.updatedAt;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -273,6 +276,11 @@ class RoleUserQueryWhere extends QueryWhere {
|
|||
}
|
||||
|
||||
class RoleUserQueryValues extends MapQueryValues {
|
||||
@override
|
||||
get casts {
|
||||
return {};
|
||||
}
|
||||
|
||||
int get id {
|
||||
return (values['id'] as int);
|
||||
}
|
||||
|
@ -299,8 +307,8 @@ class RoleUserQueryValues extends MapQueryValues {
|
|||
|
||||
set updatedAt(DateTime value) => values['updated_at'] = value;
|
||||
void copyFrom(RoleUser model) {
|
||||
values
|
||||
.addAll({'created_at': model.createdAt, 'updated_at': model.updatedAt});
|
||||
createdAt = model.createdAt;
|
||||
updatedAt = model.updatedAt;
|
||||
if (model.role != null) {
|
||||
values['role_id'] = int.parse(model.role.id);
|
||||
}
|
||||
|
@ -378,6 +386,11 @@ class RoleQueryWhere extends QueryWhere {
|
|||
}
|
||||
|
||||
class RoleQueryValues extends MapQueryValues {
|
||||
@override
|
||||
get casts {
|
||||
return {};
|
||||
}
|
||||
|
||||
int get id {
|
||||
return (values['id'] as int);
|
||||
}
|
||||
|
@ -399,11 +412,9 @@ class RoleQueryValues extends MapQueryValues {
|
|||
|
||||
set updatedAt(DateTime value) => values['updated_at'] = value;
|
||||
void copyFrom(Role model) {
|
||||
values.addAll({
|
||||
'name': model.name,
|
||||
'created_at': model.createdAt,
|
||||
'updated_at': model.updatedAt
|
||||
});
|
||||
name = model.name;
|
||||
createdAt = model.createdAt;
|
||||
updatedAt = model.updatedAt;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue