Working on insert.

This commit is contained in:
thosakwe 2017-06-30 18:39:17 -04:00
parent a3eb1b8a32
commit f4dbca98fc
8 changed files with 160 additions and 47 deletions

View file

@ -1,4 +1,5 @@
export 'src/annotations.dart';
export 'src/migration.dart';
export 'src/pool.dart';
export 'src/relations.dart';
export 'src/query.dart';

View file

@ -52,6 +52,10 @@ PostgresBuildContext buildContext(
// Check for column annotation...
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) {
// Guess what kind of column this is...
switch (field.type.name) {

View file

@ -19,10 +19,10 @@ import 'postgres_build_context.dart';
// TODO: HasOne, HasMany, BelongsTo
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;
/// If `true` (default), then
/// If "true" (default), then
final bool autoIdAndDateFields;
const PostgresORMGenerator(
@ -138,6 +138,21 @@ class PostgresORMGenerator extends GeneratorForAnnotation<ORM> {
MethodBuilder buildToSqlMethod(PostgresBuildContext ctx) {
// TODO: Bake relations into SQL queries
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;
}
@ -145,6 +160,8 @@ class PostgresORMGenerator extends GeneratorForAnnotation<ORM> {
var meth = new MethodBuilder('parseRow',
returnType: new TypeBuilder(ctx.modelClassName));
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 DATE_YMD_HMS = reference('DATE_YMD_HMS');
@ -158,7 +175,7 @@ class PostgresORMGenerator extends GeneratorForAnnotation<ORM> {
var name = ctx.resolveFieldName(field.name);
var rowKey = row[literal(i++)];
if (field.type.name == 'DateTime') {
if (false && field.type.name == 'DateTime') {
// TODO: Handle DATE and not just DATETIME
data[name] = DATE_YMD_HMS.invoke('parse', [rowKey]);
} else if (field.name == 'id' && ctx.shimmed.containsKey('id')) {
@ -210,7 +227,7 @@ class PostgresORMGenerator extends GeneratorForAnnotation<ORM> {
meth.addPositional(
parameter('connection', [new TypeBuilder('PostgreSQLConnection')]));
meth.addStatement(reference('connection').invoke('query', [
literal('SELECT * FROM `${ctx.tableName}` WHERE `id` = @id;')
literal('SELECT * FROM "${ctx.tableName}" WHERE "id" = @id;')
], namedArguments: {
'substitutionValues': map({'id': reference('id')})
}).invoke('then', [
@ -255,24 +272,57 @@ class PostgresORMGenerator extends GeneratorForAnnotation<ORM> {
meth.addNamed(p);
});
var buf = new StringBuffer('INSERT INTO `${ctx.tableName}` (');
for (int i = 0; i < ctx.fields.length; i++) {
if (i > 0) buf.write(', ');
var key = ctx.resolveFieldName(ctx.fields[i].name);
buf.write('`$key`');
}
var buf = new StringBuffer('INSERT INTO "${ctx.tableName}" (');
int i = 0;
ctx.fields.forEach((field) {
if (field.name == 'id')
return;
else {
if (i++ > 0) buf.write(', ');
var key = ctx.resolveFieldName(field.name);
buf.write('"$key"');
}
});
buf.write(' VALUES (');
for (int i = 0; i < ctx.fields.length; i++) {
if (i > 0) buf.write(', ');
buf.write('@${ctx.fields[i].name}');
}
buf.write(') VALUES (');
i = 0;
ctx.fields.forEach((field) {
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(');');
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 = {};
ctx.fields.forEach((field) {
substitutionValues[field.name] = reference(field.name);
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);
});
/*
@ -285,15 +335,15 @@ class PostgresORMGenerator extends GeneratorForAnnotation<ORM> {
// Create "INSERT INTO segment"
var fieldNames = ctx.fields
.map((f) => ctx.resolveFieldName(f.name))
.map((k) => '`$k`')
.map((k) => '"$k"')
.join(', ');
var insertInto =
literal('INSERT INTO `${ctx.tableName}` ($fieldNames) VALUES (');
literal('INSERT INTO "${ctx.tableName}" ($fieldNames) VALUES (');
meth.addStatement(buf.invoke('write', [insertInto]));
// Write all fields
int i = 0;
var backtick = literal('`');
var backtick = literal('"');
var numType = ctx.typeProvider.numType;
var boolType = ctx.typeProvider.boolType;
ctx.fields.forEach((field) {
@ -385,7 +435,7 @@ class PostgresORMGenerator extends GeneratorForAnnotation<ORM> {
type: queryBuilderType, value: queryBuilderType.newInstance(args)));
});
// Create `toWhereClause()`
// Create "toWhereClause()"
var toWhereClause =
new MethodBuilder('toWhereClause', returnType: lib$core.String);
@ -401,7 +451,7 @@ class PostgresORMGenerator extends GeneratorForAnnotation<ORM> {
var queryBuilder = reference(field.name);
var toAdd = field.type.name == 'DateTime'
? queryBuilder.invoke('compile', [])
: (literal('`$name` ') + queryBuilder.invoke('compile', []));
: (literal('"$name" ') + queryBuilder.invoke('compile', []));
toWhereClause.addStatement(ifThen(queryBuilder.property('hasValue'), [
expressions.invoke('add', [toAdd])

View file

@ -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.
class Column {
/// If `true`, a SQL field will be auto-incremented.
final bool autoIncrement;
/// If `true`, a SQL field will be nullable.
final bool nullable;
@ -19,8 +20,7 @@ class Column {
final defaultValue;
const Column(
{this.autoIncrement: false,
this.nullable: true,
{this.nullable: true,
this.length,
this.type,
this.index: IndexType.NONE,
@ -28,8 +28,10 @@ class Column {
}
class PrimaryKey extends Column {
const PrimaryKey({bool autoIncrement: true})
: super(autoIncrement: autoIncrement, index: IndexType.PRIMARY_KEY);
const PrimaryKey({ColumnType columnType})
: super(
type: columnType ?? ColumnType.SERIAL,
index: IndexType.PRIMARY_KEY);
}
const Column primaryKey = const PrimaryKey();
@ -56,6 +58,10 @@ class ColumnType {
final String 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
static const ColumnType BIG_INT = const ColumnType._('bigint');
static const ColumnType INT = const ColumnType._('int');

44
lib/src/pool.dart Normal file
View 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);
});
});
}
}

View file

@ -9,6 +9,8 @@ dependencies:
id: ^1.0.0
inflection: ^0.4.1
intl: ^0.15.1
pool: ^1.0.0
postgres: ">=0.9.5 <1.0.0"
query_builder_sql: ^1.0.0-alpha
recase: ^1.0.0
source_gen: ^0.5.8
@ -19,8 +21,4 @@ dev_dependencies:
angel_test: ">=1.0.0 <2.0.0"
build_runner: ^0.3.0
http: ">= 0.11.3 < 0.12.0"
postgres: ">=0.9.5 <1.0.0"
test: ">= 0.12.13 < 0.13.0"
dependency_overrides:
source_gen:
path: ../../Dart/source_gen

View file

@ -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) {
return new Car.fromJson({
@ -48,9 +56,9 @@ class CarQuery {
'make': row[1],
'description': row[2],
'family_friendly': row[3] == 1,
'recalled_at': DATE_YMD_HMS.parse(row[4]),
'created_at': DATE_YMD_HMS.parse(row[5]),
'updated_at': DATE_YMD_HMS.parse(row[6])
'recalled_at': row[4],
'created_at': row[5],
'updated_at': row[6]
});
}
@ -64,7 +72,7 @@ class CarQuery {
}
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));
}
@ -80,16 +88,18 @@ class CarQuery {
DateTime recalledAt,
DateTime createdAt,
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(
'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: {
'id': id,
'make': make,
'description': description,
'familyFriendly': familyFriendly,
'recalledAt': recalledAt,
'createdAt': createdAt,
'updatedAt': updatedAt
'createdAt': createdAt != null ? createdAt : __ormNow__,
'updatedAt': updatedAt != null ? updatedAt : __ormNow__
});
return parseRow(result);
}
@ -121,16 +131,16 @@ class CarQueryWhere {
String toWhereClause() {
final List<String> expressions = [];
if (id.hasValue) {
expressions.add('`id` ' + id.compile());
expressions.add('"id" ' + id.compile());
}
if (make.hasValue) {
expressions.add('`make` ' + make.compile());
expressions.add('"make" ' + make.compile());
}
if (description.hasValue) {
expressions.add('`description` ' + description.compile());
expressions.add('"description" ' + description.compile());
}
if (familyFriendly.hasValue) {
expressions.add('`family_friendly` ' + familyFriendly.compile());
expressions.add('"family_friendly" ' + familyFriendly.compile());
}
if (recalledAt.hasValue) {
expressions.add(recalledAt.compile());

View file

@ -1,5 +1,5 @@
CREATE TABLE "cars" (
"id" varchar,
"id" serial,
"make" varchar,
"description" varchar,
"family_friendly" bit,