Working on insert.
This commit is contained in:
parent
a3eb1b8a32
commit
f4dbca98fc
8 changed files with 160 additions and 47 deletions
|
@ -1,4 +1,5 @@
|
||||||
export 'src/annotations.dart';
|
export 'src/annotations.dart';
|
||||||
export 'src/migration.dart';
|
export 'src/migration.dart';
|
||||||
|
export 'src/pool.dart';
|
||||||
export 'src/relations.dart';
|
export 'src/relations.dart';
|
||||||
export 'src/query.dart';
|
export 'src/query.dart';
|
|
@ -52,6 +52,10 @@ PostgresBuildContext buildContext(
|
||||||
// Check for column annotation...
|
// Check for column annotation...
|
||||||
var column = findAnnotation<Column>(field, Column);
|
var column = findAnnotation<Column>(field, Column);
|
||||||
|
|
||||||
|
if (column == null && field.name == 'id' && ctx.shimmed['id'] == true) {
|
||||||
|
column = const Column(type: ColumnType.SERIAL);
|
||||||
|
}
|
||||||
|
|
||||||
if (column == null) {
|
if (column == null) {
|
||||||
// Guess what kind of column this is...
|
// Guess what kind of column this is...
|
||||||
switch (field.type.name) {
|
switch (field.type.name) {
|
||||||
|
|
|
@ -19,10 +19,10 @@ import 'postgres_build_context.dart';
|
||||||
|
|
||||||
// TODO: HasOne, HasMany, BelongsTo
|
// TODO: HasOne, HasMany, BelongsTo
|
||||||
class PostgresORMGenerator extends GeneratorForAnnotation<ORM> {
|
class PostgresORMGenerator extends GeneratorForAnnotation<ORM> {
|
||||||
/// If `true` (default), then field names will automatically be (de)serialized as snake_case.
|
/// If "true" (default), then field names will automatically be (de)serialized as snake_case.
|
||||||
final bool autoSnakeCaseNames;
|
final bool autoSnakeCaseNames;
|
||||||
|
|
||||||
/// If `true` (default), then
|
/// If "true" (default), then
|
||||||
final bool autoIdAndDateFields;
|
final bool autoIdAndDateFields;
|
||||||
|
|
||||||
const PostgresORMGenerator(
|
const PostgresORMGenerator(
|
||||||
|
@ -138,6 +138,21 @@ class PostgresORMGenerator extends GeneratorForAnnotation<ORM> {
|
||||||
MethodBuilder buildToSqlMethod(PostgresBuildContext ctx) {
|
MethodBuilder buildToSqlMethod(PostgresBuildContext ctx) {
|
||||||
// TODO: Bake relations into SQL queries
|
// TODO: Bake relations into SQL queries
|
||||||
var meth = new MethodBuilder('toSql', returnType: lib$core.String);
|
var meth = new MethodBuilder('toSql', returnType: lib$core.String);
|
||||||
|
meth.addStatement(varField('buf',
|
||||||
|
value: lib$core.StringBuffer
|
||||||
|
.newInstance([literal('SELECT * FROM "${ctx.tableName}"')])));
|
||||||
|
meth.addStatement(varField('whereClause',
|
||||||
|
value: reference('where').invoke('toWhereClause', [])));
|
||||||
|
var buf = reference('buf');
|
||||||
|
var whereClause = reference('whereClause');
|
||||||
|
|
||||||
|
meth.addStatement(ifThen(whereClause.notEquals(literal(null)), [
|
||||||
|
buf.invoke('write', [literal(' ') + whereClause])
|
||||||
|
]));
|
||||||
|
|
||||||
|
meth.addStatement(buf.invoke('write', [literal(';')]));
|
||||||
|
meth.addStatement(buf.invoke('toString', []).asReturn());
|
||||||
|
|
||||||
return meth;
|
return meth;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,6 +160,8 @@ class PostgresORMGenerator extends GeneratorForAnnotation<ORM> {
|
||||||
var meth = new MethodBuilder('parseRow',
|
var meth = new MethodBuilder('parseRow',
|
||||||
returnType: new TypeBuilder(ctx.modelClassName));
|
returnType: new TypeBuilder(ctx.modelClassName));
|
||||||
meth.addPositional(parameter('row', [lib$core.List]));
|
meth.addPositional(parameter('row', [lib$core.List]));
|
||||||
|
//meth.addStatement(lib$core.print.call(
|
||||||
|
// [literal('ROW MAP: ') + reference('row').invoke('toString', [])]));
|
||||||
var row = reference('row');
|
var row = reference('row');
|
||||||
var DATE_YMD_HMS = reference('DATE_YMD_HMS');
|
var DATE_YMD_HMS = reference('DATE_YMD_HMS');
|
||||||
|
|
||||||
|
@ -158,7 +175,7 @@ class PostgresORMGenerator extends GeneratorForAnnotation<ORM> {
|
||||||
var name = ctx.resolveFieldName(field.name);
|
var name = ctx.resolveFieldName(field.name);
|
||||||
var rowKey = row[literal(i++)];
|
var rowKey = row[literal(i++)];
|
||||||
|
|
||||||
if (field.type.name == 'DateTime') {
|
if (false && field.type.name == 'DateTime') {
|
||||||
// TODO: Handle DATE and not just DATETIME
|
// TODO: Handle DATE and not just DATETIME
|
||||||
data[name] = DATE_YMD_HMS.invoke('parse', [rowKey]);
|
data[name] = DATE_YMD_HMS.invoke('parse', [rowKey]);
|
||||||
} else if (field.name == 'id' && ctx.shimmed.containsKey('id')) {
|
} else if (field.name == 'id' && ctx.shimmed.containsKey('id')) {
|
||||||
|
@ -210,7 +227,7 @@ class PostgresORMGenerator extends GeneratorForAnnotation<ORM> {
|
||||||
meth.addPositional(
|
meth.addPositional(
|
||||||
parameter('connection', [new TypeBuilder('PostgreSQLConnection')]));
|
parameter('connection', [new TypeBuilder('PostgreSQLConnection')]));
|
||||||
meth.addStatement(reference('connection').invoke('query', [
|
meth.addStatement(reference('connection').invoke('query', [
|
||||||
literal('SELECT * FROM `${ctx.tableName}` WHERE `id` = @id;')
|
literal('SELECT * FROM "${ctx.tableName}" WHERE "id" = @id;')
|
||||||
], namedArguments: {
|
], namedArguments: {
|
||||||
'substitutionValues': map({'id': reference('id')})
|
'substitutionValues': map({'id': reference('id')})
|
||||||
}).invoke('then', [
|
}).invoke('then', [
|
||||||
|
@ -255,23 +272,56 @@ class PostgresORMGenerator extends GeneratorForAnnotation<ORM> {
|
||||||
meth.addNamed(p);
|
meth.addNamed(p);
|
||||||
});
|
});
|
||||||
|
|
||||||
var buf = new StringBuffer('INSERT INTO `${ctx.tableName}` (');
|
var buf = new StringBuffer('INSERT INTO "${ctx.tableName}" (');
|
||||||
for (int i = 0; i < ctx.fields.length; i++) {
|
int i = 0;
|
||||||
if (i > 0) buf.write(', ');
|
ctx.fields.forEach((field) {
|
||||||
var key = ctx.resolveFieldName(ctx.fields[i].name);
|
if (field.name == 'id')
|
||||||
buf.write('`$key`');
|
return;
|
||||||
|
else {
|
||||||
|
if (i++ > 0) buf.write(', ');
|
||||||
|
var key = ctx.resolveFieldName(field.name);
|
||||||
|
buf.write('"$key"');
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
buf.write(' VALUES (');
|
buf.write(') VALUES (');
|
||||||
for (int i = 0; i < ctx.fields.length; i++) {
|
i = 0;
|
||||||
if (i > 0) buf.write(', ');
|
ctx.fields.forEach((field) {
|
||||||
buf.write('@${ctx.fields[i].name}');
|
if (field.name == 'id')
|
||||||
|
return;
|
||||||
|
else {
|
||||||
|
if (i++ > 0) buf.write(', ');
|
||||||
|
buf.write('@${field.name}');
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
buf.write(')');
|
||||||
|
|
||||||
|
buf.write(' RETURNING (');
|
||||||
|
i = 0;
|
||||||
|
ctx.fields.forEach((field) {
|
||||||
|
if (i++ > 0) buf.write(', ');
|
||||||
|
var name = ctx.resolveFieldName(field.name);
|
||||||
|
buf.write('"$name"');
|
||||||
|
});
|
||||||
|
|
||||||
buf.write(');');
|
buf.write(');');
|
||||||
|
meth.addStatement(lib$core.print.call([literal(buf.toString())]));
|
||||||
|
|
||||||
|
if (ctx.fields.any((f) => f.name == 'createdAt' || f.name == 'updatedAt')) {
|
||||||
|
meth.addStatement(varField('__ormNow__',
|
||||||
|
value: lib$core.DateTime.newInstance([], constructor: 'now')));
|
||||||
|
}
|
||||||
|
|
||||||
Map<String, ExpressionBuilder> substitutionValues = {};
|
Map<String, ExpressionBuilder> substitutionValues = {};
|
||||||
ctx.fields.forEach((field) {
|
ctx.fields.forEach((field) {
|
||||||
|
if (field.name == 'id')
|
||||||
|
return;
|
||||||
|
else if (field.name == 'createdAt' || field.name == 'updatedAt') {
|
||||||
|
var ref = reference(field.name);
|
||||||
|
substitutionValues[field.name] =
|
||||||
|
ref.notEquals(literal(null)).ternary(ref, reference('__ormNow__'));
|
||||||
|
} else
|
||||||
substitutionValues[field.name] = reference(field.name);
|
substitutionValues[field.name] = reference(field.name);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -285,15 +335,15 @@ class PostgresORMGenerator extends GeneratorForAnnotation<ORM> {
|
||||||
// Create "INSERT INTO segment"
|
// Create "INSERT INTO segment"
|
||||||
var fieldNames = ctx.fields
|
var fieldNames = ctx.fields
|
||||||
.map((f) => ctx.resolveFieldName(f.name))
|
.map((f) => ctx.resolveFieldName(f.name))
|
||||||
.map((k) => '`$k`')
|
.map((k) => '"$k"')
|
||||||
.join(', ');
|
.join(', ');
|
||||||
var insertInto =
|
var insertInto =
|
||||||
literal('INSERT INTO `${ctx.tableName}` ($fieldNames) VALUES (');
|
literal('INSERT INTO "${ctx.tableName}" ($fieldNames) VALUES (');
|
||||||
meth.addStatement(buf.invoke('write', [insertInto]));
|
meth.addStatement(buf.invoke('write', [insertInto]));
|
||||||
|
|
||||||
// Write all fields
|
// Write all fields
|
||||||
int i = 0;
|
int i = 0;
|
||||||
var backtick = literal('`');
|
var backtick = literal('"');
|
||||||
var numType = ctx.typeProvider.numType;
|
var numType = ctx.typeProvider.numType;
|
||||||
var boolType = ctx.typeProvider.boolType;
|
var boolType = ctx.typeProvider.boolType;
|
||||||
ctx.fields.forEach((field) {
|
ctx.fields.forEach((field) {
|
||||||
|
@ -385,7 +435,7 @@ class PostgresORMGenerator extends GeneratorForAnnotation<ORM> {
|
||||||
type: queryBuilderType, value: queryBuilderType.newInstance(args)));
|
type: queryBuilderType, value: queryBuilderType.newInstance(args)));
|
||||||
});
|
});
|
||||||
|
|
||||||
// Create `toWhereClause()`
|
// Create "toWhereClause()"
|
||||||
var toWhereClause =
|
var toWhereClause =
|
||||||
new MethodBuilder('toWhereClause', returnType: lib$core.String);
|
new MethodBuilder('toWhereClause', returnType: lib$core.String);
|
||||||
|
|
||||||
|
@ -401,7 +451,7 @@ class PostgresORMGenerator extends GeneratorForAnnotation<ORM> {
|
||||||
var queryBuilder = reference(field.name);
|
var queryBuilder = reference(field.name);
|
||||||
var toAdd = field.type.name == 'DateTime'
|
var toAdd = field.type.name == 'DateTime'
|
||||||
? queryBuilder.invoke('compile', [])
|
? queryBuilder.invoke('compile', [])
|
||||||
: (literal('`$name` ') + queryBuilder.invoke('compile', []));
|
: (literal('"$name" ') + queryBuilder.invoke('compile', []));
|
||||||
|
|
||||||
toWhereClause.addStatement(ifThen(queryBuilder.property('hasValue'), [
|
toWhereClause.addStatement(ifThen(queryBuilder.property('hasValue'), [
|
||||||
expressions.invoke('add', [toAdd])
|
expressions.invoke('add', [toAdd])
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
|
const List<String> SQL_RESERVED_WORDS = const [
|
||||||
|
'SELECT', 'UPDATE', 'INSERT', 'DELETE', 'FROM', 'ASC', 'DESC', 'VALUES', 'RETURNING', 'ORDER', 'BY',
|
||||||
|
];
|
||||||
|
|
||||||
/// Applies additional attributes to a database column.
|
/// Applies additional attributes to a database column.
|
||||||
class Column {
|
class Column {
|
||||||
/// If `true`, a SQL field will be auto-incremented.
|
|
||||||
final bool autoIncrement;
|
|
||||||
|
|
||||||
/// If `true`, a SQL field will be nullable.
|
/// If `true`, a SQL field will be nullable.
|
||||||
final bool nullable;
|
final bool nullable;
|
||||||
|
|
||||||
|
@ -19,8 +20,7 @@ class Column {
|
||||||
final defaultValue;
|
final defaultValue;
|
||||||
|
|
||||||
const Column(
|
const Column(
|
||||||
{this.autoIncrement: false,
|
{this.nullable: true,
|
||||||
this.nullable: true,
|
|
||||||
this.length,
|
this.length,
|
||||||
this.type,
|
this.type,
|
||||||
this.index: IndexType.NONE,
|
this.index: IndexType.NONE,
|
||||||
|
@ -28,8 +28,10 @@ class Column {
|
||||||
}
|
}
|
||||||
|
|
||||||
class PrimaryKey extends Column {
|
class PrimaryKey extends Column {
|
||||||
const PrimaryKey({bool autoIncrement: true})
|
const PrimaryKey({ColumnType columnType})
|
||||||
: super(autoIncrement: autoIncrement, index: IndexType.PRIMARY_KEY);
|
: super(
|
||||||
|
type: columnType ?? ColumnType.SERIAL,
|
||||||
|
index: IndexType.PRIMARY_KEY);
|
||||||
}
|
}
|
||||||
|
|
||||||
const Column primaryKey = const PrimaryKey();
|
const Column primaryKey = const PrimaryKey();
|
||||||
|
@ -56,6 +58,10 @@ class ColumnType {
|
||||||
final String name;
|
final String name;
|
||||||
const ColumnType._(this.name);
|
const ColumnType._(this.name);
|
||||||
|
|
||||||
|
static const ColumnType SMALL_SERIAL = const ColumnType._('smallserial');
|
||||||
|
static const ColumnType SERIAL = const ColumnType._('serial');
|
||||||
|
static const ColumnType BIG_SERIAL = const ColumnType._('bigserial');
|
||||||
|
|
||||||
// Numbers
|
// Numbers
|
||||||
static const ColumnType BIG_INT = const ColumnType._('bigint');
|
static const ColumnType BIG_INT = const ColumnType._('bigint');
|
||||||
static const ColumnType INT = const ColumnType._('int');
|
static const ColumnType INT = const ColumnType._('int');
|
||||||
|
|
44
lib/src/pool.dart
Normal file
44
lib/src/pool.dart
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
import 'dart:async';
|
||||||
|
import 'package:pool/pool.dart';
|
||||||
|
import 'package:postgres/postgres.dart';
|
||||||
|
|
||||||
|
/// Connects to a PostgreSQL database, whether synchronously or asynchronously.
|
||||||
|
typedef FutureOr<PostgreSQLConnection> PostgreSQLConnector();
|
||||||
|
|
||||||
|
/// Pools connections to a PostgreSQL database.
|
||||||
|
class PostgreSQLConnectionPool {
|
||||||
|
Pool _pool;
|
||||||
|
|
||||||
|
/// The maximum number of concurrent connections to the database.
|
||||||
|
///
|
||||||
|
/// Default: `5`
|
||||||
|
final int concurrency;
|
||||||
|
|
||||||
|
/// An optional timeout for pooled connections to execute.
|
||||||
|
final Duration timeout;
|
||||||
|
|
||||||
|
/// A function that connects this pool to the database, on-demand.
|
||||||
|
final PostgreSQLConnector connector;
|
||||||
|
|
||||||
|
PostgreSQLConnectionPool(this.connector,
|
||||||
|
{this.concurrency: 5, this.timeout}) {
|
||||||
|
_pool = new Pool(concurrency, timeout: timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<PostgreSQLConnection> _connect() async {
|
||||||
|
var connection = await connector() as PostgreSQLConnection;
|
||||||
|
await connection.open();
|
||||||
|
return connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Connects to the database, and then executes the [callback].
|
||||||
|
///
|
||||||
|
/// Returns the result of [callback].
|
||||||
|
Future<T> run<T>(FutureOr<T> callback(PostgreSQLConnection connection)) {
|
||||||
|
return _pool.request().then((resx) {
|
||||||
|
return _connect().then((connection) {
|
||||||
|
return new Future<T>.sync(() => callback(connection)).whenComplete(resx.release);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,6 +9,8 @@ dependencies:
|
||||||
id: ^1.0.0
|
id: ^1.0.0
|
||||||
inflection: ^0.4.1
|
inflection: ^0.4.1
|
||||||
intl: ^0.15.1
|
intl: ^0.15.1
|
||||||
|
pool: ^1.0.0
|
||||||
|
postgres: ">=0.9.5 <1.0.0"
|
||||||
query_builder_sql: ^1.0.0-alpha
|
query_builder_sql: ^1.0.0-alpha
|
||||||
recase: ^1.0.0
|
recase: ^1.0.0
|
||||||
source_gen: ^0.5.8
|
source_gen: ^0.5.8
|
||||||
|
@ -19,8 +21,4 @@ dev_dependencies:
|
||||||
angel_test: ">=1.0.0 <2.0.0"
|
angel_test: ">=1.0.0 <2.0.0"
|
||||||
build_runner: ^0.3.0
|
build_runner: ^0.3.0
|
||||||
http: ">= 0.11.3 < 0.12.0"
|
http: ">= 0.11.3 < 0.12.0"
|
||||||
postgres: ">=0.9.5 <1.0.0"
|
|
||||||
test: ">= 0.12.13 < 0.13.0"
|
test: ">= 0.12.13 < 0.13.0"
|
||||||
dependency_overrides:
|
|
||||||
source_gen:
|
|
||||||
path: ../../Dart/source_gen
|
|
|
@ -40,7 +40,15 @@ class CarQuery {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String toSql() {}
|
String toSql() {
|
||||||
|
var buf = new StringBuffer('SELECT * FROM "cars"');
|
||||||
|
var whereClause = where.toWhereClause();
|
||||||
|
if (whereClause != null) {
|
||||||
|
buf.write(' ' + whereClause);
|
||||||
|
}
|
||||||
|
buf.write(';');
|
||||||
|
return buf.toString();
|
||||||
|
}
|
||||||
|
|
||||||
static Car parseRow(List row) {
|
static Car parseRow(List row) {
|
||||||
return new Car.fromJson({
|
return new Car.fromJson({
|
||||||
|
@ -48,9 +56,9 @@ class CarQuery {
|
||||||
'make': row[1],
|
'make': row[1],
|
||||||
'description': row[2],
|
'description': row[2],
|
||||||
'family_friendly': row[3] == 1,
|
'family_friendly': row[3] == 1,
|
||||||
'recalled_at': DATE_YMD_HMS.parse(row[4]),
|
'recalled_at': row[4],
|
||||||
'created_at': DATE_YMD_HMS.parse(row[5]),
|
'created_at': row[5],
|
||||||
'updated_at': DATE_YMD_HMS.parse(row[6])
|
'updated_at': row[6]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,7 +72,7 @@ class CarQuery {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Car> getOne(int id, PostgreSQLConnection connection) {
|
Future<Car> getOne(int id, PostgreSQLConnection connection) {
|
||||||
return connection.query('SELECT * FROM `cars` WHERE `id` = @id;',
|
return connection.query('SELECT * FROM "cars" WHERE "id" = @id;',
|
||||||
substitutionValues: {'id': id}).then((rows) => parseRow(rows.first));
|
substitutionValues: {'id': id}).then((rows) => parseRow(rows.first));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,16 +88,18 @@ class CarQuery {
|
||||||
DateTime recalledAt,
|
DateTime recalledAt,
|
||||||
DateTime createdAt,
|
DateTime createdAt,
|
||||||
DateTime updatedAt}) async {
|
DateTime updatedAt}) async {
|
||||||
|
print(
|
||||||
|
'INSERT INTO "cars" ("make", "description", "family_friendly", "recalled_at", "created_at", "updated_at") VALUES (@make, @description, @familyFriendly, @recalledAt, @createdAt, @updatedAt) RETURNING ("id", "make", "description", "family_friendly", "recalled_at", "created_at", "updated_at");');
|
||||||
|
var __ormNow__ = new DateTime.now();
|
||||||
var result = await connection.query(
|
var result = await connection.query(
|
||||||
'INSERT INTO `cars` (`id`, `make`, `description`, `family_friendly`, `recalled_at`, `created_at`, `updated_at` VALUES (@id, @make, @description, @familyFriendly, @recalledAt, @createdAt, @updatedAt);',
|
'INSERT INTO "cars" ("make", "description", "family_friendly", "recalled_at", "created_at", "updated_at") VALUES (@make, @description, @familyFriendly, @recalledAt, @createdAt, @updatedAt) RETURNING ("id", "make", "description", "family_friendly", "recalled_at", "created_at", "updated_at");',
|
||||||
substitutionValues: {
|
substitutionValues: {
|
||||||
'id': id,
|
|
||||||
'make': make,
|
'make': make,
|
||||||
'description': description,
|
'description': description,
|
||||||
'familyFriendly': familyFriendly,
|
'familyFriendly': familyFriendly,
|
||||||
'recalledAt': recalledAt,
|
'recalledAt': recalledAt,
|
||||||
'createdAt': createdAt,
|
'createdAt': createdAt != null ? createdAt : __ormNow__,
|
||||||
'updatedAt': updatedAt
|
'updatedAt': updatedAt != null ? updatedAt : __ormNow__
|
||||||
});
|
});
|
||||||
return parseRow(result);
|
return parseRow(result);
|
||||||
}
|
}
|
||||||
|
@ -121,16 +131,16 @@ class CarQueryWhere {
|
||||||
String toWhereClause() {
|
String toWhereClause() {
|
||||||
final List<String> expressions = [];
|
final List<String> expressions = [];
|
||||||
if (id.hasValue) {
|
if (id.hasValue) {
|
||||||
expressions.add('`id` ' + id.compile());
|
expressions.add('"id" ' + id.compile());
|
||||||
}
|
}
|
||||||
if (make.hasValue) {
|
if (make.hasValue) {
|
||||||
expressions.add('`make` ' + make.compile());
|
expressions.add('"make" ' + make.compile());
|
||||||
}
|
}
|
||||||
if (description.hasValue) {
|
if (description.hasValue) {
|
||||||
expressions.add('`description` ' + description.compile());
|
expressions.add('"description" ' + description.compile());
|
||||||
}
|
}
|
||||||
if (familyFriendly.hasValue) {
|
if (familyFriendly.hasValue) {
|
||||||
expressions.add('`family_friendly` ' + familyFriendly.compile());
|
expressions.add('"family_friendly" ' + familyFriendly.compile());
|
||||||
}
|
}
|
||||||
if (recalledAt.hasValue) {
|
if (recalledAt.hasValue) {
|
||||||
expressions.add(recalledAt.compile());
|
expressions.add(recalledAt.compile());
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
CREATE TABLE "cars" (
|
CREATE TABLE "cars" (
|
||||||
"id" varchar,
|
"id" serial,
|
||||||
"make" varchar,
|
"make" varchar,
|
||||||
"description" varchar,
|
"description" varchar,
|
||||||
"family_friendly" bit,
|
"family_friendly" bit,
|
||||||
|
|
Loading…
Reference in a new issue