Updated to postgres 3

This commit is contained in:
thomashii 2024-01-06 18:33:09 +08:00
parent 1580cc98e6
commit ea528abc3d
7 changed files with 63 additions and 162 deletions

View file

@ -12,7 +12,7 @@
"js": [] "js": []
}, },
"editor.codeActionsOnSave": { "editor.codeActionsOnSave": {
"source.fixAll.markdownlint": true "source.fixAll.markdownlint": "explicit"
}, },
"cmake.configureOnOpen": false "cmake.configureOnOpen": false
} }

BIN
logo_icon_512.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

View file

@ -1,11 +1,9 @@
# Change Log # Change Log
## 8.1.1
* Updated repository link
## 8.1.0 ## 8.1.0
* Updated repository links
* Updated `postgres` to 3.0.0
* Updated `lints` to 3.0.0 * Updated `lints` to 3.0.0
* Fixed linter warnings * Fixed linter warnings

View file

@ -1,6 +1,5 @@
import 'dart:io'; import 'dart:io';
import 'package:angel3_orm_postgres/angel3_orm_postgres.dart'; import 'package:angel3_orm_postgres/angel3_orm_postgres.dart';
import 'package:postgres_pool/postgres_pool.dart';
void main() async { void main() async {
var executor = PostgreSqlPoolExecutor(PgPool( var executor = PostgreSqlPoolExecutor(PgPool(

View file

@ -2,40 +2,46 @@ import 'dart:async';
import 'dart:convert'; import 'dart:convert';
import 'package:angel3_orm/angel3_orm.dart'; import 'package:angel3_orm/angel3_orm.dart';
import 'package:logging/logging.dart'; import 'package:logging/logging.dart';
import 'package:pool/pool.dart';
import 'package:postgres/postgres.dart'; import 'package:postgres/postgres.dart';
/// A [QueryExecutor] that queries a PostgreSQL database. /// A [QueryExecutor] that queries a PostgreSQL database.
class PostgreSqlExecutor extends QueryExecutor { class PostgreSqlExecutor extends QueryExecutor {
PostgreSQLExecutionContext _connection; final Dialect _dialect = const PostgreSQLDialect();
ConnectionSettings? _settings;
Endpoint? _endpoint;
Session _session;
/// An optional [Logger] to print information to. A default logger will be used /// An optional [Logger] to print information to. A default logger will be used
/// if not set /// if not set
late Logger logger; late Logger logger;
PostgreSqlExecutor(this._connection, {Logger? logger}) {
this.logger = logger ?? Logger('PostgreSqlExecutor');
}
final Dialect _dialect = const PostgreSQLDialect();
@override @override
Dialect get dialect => _dialect; Dialect get dialect => _dialect;
/// The underlying connection. /// The underlying database session.
PostgreSQLExecutionContext get connection => _connection; Session get session => _session;
PostgreSqlExecutor(this._session,
{Endpoint? endpoint, ConnectionSettings? settings, Logger? logger}) {
this.logger = logger ?? Logger('PostgreSqlExecutor');
_settings = settings;
_endpoint = endpoint;
}
/// Closes the connection. /// Closes the connection.
Future close() { Future<void> close() async {
if (_connection is PostgreSQLConnection) { if (_session is Connection) {
return (_connection as PostgreSQLConnection).close(); await (_session as Connection).close();
} else {
return Future.value();
} }
return Future.value();
} }
@override @override
Future<PostgreSQLResult> query( Future<Result> query(
String tableName, String query, Map<String, dynamic> substitutionValues, String tableName, String query, Map<String, dynamic> substitutionValues,
{String returningQuery = '', List<String> returningFields = const []}) { {String returningQuery = '', List<String> returningFields = const []}) {
if (returningFields.isNotEmpty) { if (returningFields.isNotEmpty) {
@ -44,8 +50,8 @@ class PostgreSqlExecutor extends QueryExecutor {
query = '$query $returning'; query = '$query $returning';
} }
//logger.fine('Query: $query'); logger.fine('Query: $query');
//logger.fine('Values: $substitutionValues'); logger.fine('Values: $substitutionValues');
// Convert List into String // Convert List into String
var param = <String, dynamic>{}; var param = <String, dynamic>{};
@ -57,28 +63,24 @@ class PostgreSqlExecutor extends QueryExecutor {
} }
}); });
return _connection return _session
.query(query, substitutionValues: param) .execute(Sql.named(query), parameters: param)
.catchError((err) async { .catchError((err) async {
logger.warning(err); logger.warning(err);
if (err is PostgreSQLException) { if (err is PgException) {
// This is a hack to detect broken db connection // This is a hack to detect broken db connection
bool brokenConnection = bool brokenConnection = err.message.contains("connection is not open");
err.message?.contains("connection is not open") ?? false;
if (brokenConnection) { if (brokenConnection) {
if (_connection is PostgreSQLConnection) { // Open a new db session
// Open a new db connection if (_session is Connection) {
var currentConnection = _connection as PostgreSQLConnection; (_session as Connection).close();
currentConnection.close();
logger.warning( logger.warning(
"A broken database connection is detected. Creating a new database connection."); "A broken database connection is detected. Creating a new database connection.");
var conn = _createNewConnection(currentConnection); _session = await _createNewSession();
await conn.open();
_connection = conn;
// Retry the query with the new db connection // Retry the query with the new db connection
return _connection.query(query, substitutionValues: param); return _session.execute(Sql.named(query), parameters: param);
} }
} }
} }
@ -86,129 +88,36 @@ class PostgreSqlExecutor extends QueryExecutor {
}); });
} }
// Create a new database connection from an existing connection // Create a new database connection
PostgreSQLConnection _createNewConnection(PostgreSQLConnection conn) { Future<Session> _createNewSession() async {
return PostgreSQLConnection(conn.host, conn.port, conn.databaseName, if (_endpoint != null) {
username: conn.username, return await Connection.open(_endpoint!, settings: _settings);
password: conn.password, }
useSSL: conn.useSSL,
timeZone: conn.timeZone, throw PgException("Unable to create new connection");
timeoutInSeconds: conn.timeoutInSeconds);
} }
@override @override
Future<T> transaction<T>(FutureOr<T> Function(QueryExecutor) f) async { Future<T> transaction<T>(FutureOr<T> Function(QueryExecutor) f) async {
if (_connection is! PostgreSQLConnection) { //if (_connection is! PostgreSQLConnection) {
return await f(this); // return await f(this);
} //}
var conn = _connection as PostgreSQLConnection; var conn = _session as Connection;
T? returnValue;
var txResult = await conn.transaction((ctx) async { return await conn.runTx((ctx) async {
try { try {
//logger.fine('Entering transaction'); //logger.fine('Entering transaction');
var tx = PostgreSqlExecutor(ctx, logger: logger); var tx = PostgreSqlExecutor(ctx, logger: logger);
returnValue = await f(tx); return await f(tx);
return returnValue;
} catch (e) { } catch (e) {
ctx.cancelTransaction(reason: e.toString()); ctx.rollback();
//ctx.cancelTransaction(reason: e.toString());
logger.warning("The transation has failed due to ", e);
rethrow; rethrow;
} }
}); }).onError((error, stackTrace) {
throw StateError('The transaction was cancelled.');
if (txResult is PostgreSQLRollback) {
//if (txResult.reason == null) {
// throw StateError('The transaction was cancelled.');
//} else {
throw StateError(
'The transaction was cancelled with reason "${txResult.reason}".');
//}
} else {
return returnValue!;
}
}
}
/// A [QueryExecutor] that manages a pool of PostgreSQL connections.
class PostgreSqlExecutorPool extends QueryExecutor {
/// The maximum amount of concurrent connections.
final int size;
/// Creates a new [PostgreSQLConnection], on demand.
///
/// The created connection should **not** be open.
final PostgreSQLConnection Function() connectionFactory;
/// An optional [Logger] to print information to.
late Logger logger;
final List<PostgreSqlExecutor> _connections = [];
int _index = 0;
final Pool _pool, _connMutex = Pool(1);
PostgreSqlExecutorPool(this.size, this.connectionFactory, {Logger? logger})
: _pool = Pool(size) {
if (logger != null) {
this.logger = logger;
} else {
this.logger = Logger('PostgreSqlExecutorPool');
}
assert(size > 0, 'Connection pool cannot be empty.');
}
final Dialect _dialect = const PostgreSQLDialect();
@override
Dialect get dialect => _dialect;
/// Closes all connections.
Future close() async {
await _pool.close();
await _connMutex.close();
return Future.wait(_connections.map((c) => c.close()));
}
Future _open() async {
if (_connections.isEmpty) {
_connections.addAll(await Future.wait(List.generate(size, (_) async {
//logger.fine('Spawning connections...');
var conn = connectionFactory();
await conn.open();
//return conn
// .open()
// .then((_) => PostgreSqlExecutor(conn, logger: logger));
return PostgreSqlExecutor(conn, logger: logger);
})));
}
}
Future<PostgreSqlExecutor> _next() {
return _connMutex.withResource(() async {
await _open();
if (_index >= size) _index = 0;
return _connections[_index++];
});
}
@override
Future<PostgreSQLResult> query(
String tableName, String query, Map<String, dynamic> substitutionValues,
{String returningQuery = '', List<String> returningFields = const []}) {
return _pool.withResource(() async {
var executor = await _next();
return executor.query(tableName, query, substitutionValues,
returningFields: returningFields);
});
}
@override
Future<T> transaction<T>(FutureOr<T> Function(QueryExecutor) f) {
return _pool.withResource(() async {
var executor = await _next();
return executor.transaction(f);
}); });
} }
} }

View file

@ -1,17 +1,15 @@
name: angel3_orm_postgres name: angel3_orm_postgres
version: 8.1.1 version: 8.1.0
description: PostgreSQL support for Angel3 ORM. Includes functionality for querying and transactions. description: PostgreSQL support for Angel3 ORM. Includes functionality for querying and transactions.
homepage: https://angel3-framework.web.app/ homepage: https://angel3-framework.web.app/
repository: https://github.com/dart-backend/angel/tree/master/packages/orm/angel_orm_postgres repository: https://github.com/dart-backend/angel/tree/master/packages/orm/angel_orm_postgres
publish_to: none
environment: environment:
sdk: '>=3.0.0 <4.0.0' sdk: '>=3.0.0 <4.0.0'
dependencies: dependencies:
angel3_orm: ^8.0.0 angel3_orm: ^8.0.0
logging: ^1.2.0 logging: ^1.2.0
pool: ^1.5.0 pool: ^1.5.0
postgres: ^2.6.1 postgres: ^3.0.0
postgres_pool: ^2.1.6
dev_dependencies: dev_dependencies:
belatuk_pretty_logging: ^6.1.0 belatuk_pretty_logging: ^6.1.0
angel3_orm_generator: ^8.0.0 angel3_orm_generator: ^8.0.0

View file

@ -3,7 +3,7 @@ import 'dart:io';
import 'package:angel3_orm/angel3_orm.dart'; import 'package:angel3_orm/angel3_orm.dart';
import 'package:angel3_orm_postgres/angel3_orm_postgres.dart'; import 'package:angel3_orm_postgres/angel3_orm_postgres.dart';
import 'package:logging/logging.dart'; import 'package:logging/logging.dart';
import 'package:postgres_pool/postgres_pool.dart'; import 'package:postgres/postgres.dart';
FutureOr<QueryExecutor> Function() pg(Iterable<String> schemas) { FutureOr<QueryExecutor> Function() pg(Iterable<String> schemas) {
// Use single connection // Use single connection
@ -19,19 +19,16 @@ FutureOr<QueryExecutor> Function() pg(Iterable<String> schemas) {
Future<void> closePg(QueryExecutor executor) async { Future<void> closePg(QueryExecutor executor) async {
if (executor is PostgreSqlExecutor) { if (executor is PostgreSqlExecutor) {
await executor.close(); await executor.close();
//} else if (executor is PostgreSqlExecutorPool) {
// await executor.close();
} else if (executor is PostgreSqlPoolExecutor) {
await executor.close();
} }
} }
Future<PostgreSqlExecutor> connectToPostgres(Iterable<String> schemas) async { Future<PostgreSqlExecutor> connectToPostgres(Iterable<String> schemas) async {
var conn = PostgreSQLConnection( var conn = await Connection.open(Endpoint(
'localhost', 5432, Platform.environment['POSTGRES_DB'] ?? 'orm_test', host: 'localhost',
port: 5432,
database: Platform.environment['POSTGRES_DB'] ?? 'orm_test',
username: Platform.environment['POSTGRES_USERNAME'] ?? 'test', username: Platform.environment['POSTGRES_USERNAME'] ?? 'test',
password: Platform.environment['POSTGRES_PASSWORD'] ?? 'test123'); password: Platform.environment['POSTGRES_PASSWORD'] ?? 'test123'));
await conn.open();
// Run sql to create the tables // Run sql to create the tables
for (var s in schemas) { for (var s in schemas) {