Merge pull request #34 from dukefirehawk/feature/update-orm

Update ORM for Postgresql
This commit is contained in:
Thomas Hii 2021-12-30 10:10:28 +08:00 committed by GitHub
commit 342bdb322e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
48 changed files with 1313 additions and 392 deletions

View file

@ -1,29 +0,0 @@
-----BEGIN ENCRYPTED PRIVATE KEY-----
MIIE5DAcBgoqhkiG9w0BDAEBMA4ECL7L6rj6uEHGAgIIAASCBMLbucyfqAkgCbhP
xNSHYllPMAv/dsIjtnsBwepCXPGkCBCuOAw/2FaCHjN9hBqL5V7fkrKeaemhm2YE
ycPtlHJYPDf3kEkyMjdZ9rIY6kePGfQizs2uJPcXj4YPyQ4HsfVXpOicKfQrouf5
Mze9bGzeMN065q3iP4dYUMwHAyZYteXCsanQNHlqvsWli0W+H8St8fdsXefZhnv1
qVatKWdNdWQ9t5MuljgNU2Vv56sHKEYXI0yLxk2QUMk8KlJfnmt8foYUsnPUXHmc
gIjLKwwVkpdololnEHSNu0cEOUPowjgJru+uMpn7vdNl7TPEQ9jbEgdNg4JwoYzU
0nao8WzjaSp7kzvZz0VFwKnk5AjstGvvuAWckADdq23QElbn/mF7AG1m/TBpYxzF
gTt37UdndS/AcvVznWVVrRP5iTSIawdIwvqI4s7rqsoE0GCcak+RhchgAz2gWKkS
oODUo0JL6pPVbJ3l4ebbaO6c99nDVc8dViPtc1EkStJEJ2O4kI4xgLSCr4Y9ahKn
oAaoSkX7Xxq3aQm+BzqSpLjdGL8atsqR/YVOIHYIl3gThvP0NfZGx1xHyvO5mCdZ
kHxSA7tKWxauZ3eQ2clbnzeRsl4El0WMHy/5K1ovene4v7sunmoXVtghBC8hK6eh
zMO9orex2PNQ/VQC7HCvtytunOVx1lkSBoNo7hR70igg6rW9H7UyoAoBOwMpT1xa
J6V62nqruTKOqFNfur7aHJGpHGtDb5/ickHeYCyPTvmGp67u4wChzKReeg02oECe
d1E5FKAcIa8s9TVOB6Z+HvTRNQZu2PsI6TJnjQRowvY9DAHiWTlJZBBY/pko3hxX
TsIeybpvRdEHpDWv86/iqtw1hv9CUxS/8ZTWUgBo+osShHW79FeDASr9FC4/Zn76
ZDERTgV4YWlW/klVWcG2lFo7jix+OPXAB+ZQavLhlN1xdWBcIz1AUWjAM4hdPylW
HCX4PB9CQIPl2E7F+Y2p6nMcMWSJVBi5UIH7E9LfaBguXSzMmTk2Fw5p1aOQ6wfN
goVAMVwi8ppAVs741PfHdZ295xMmK/1LCxz5DeAdD/tsA/SYfT753GotioDuC7im
EyJ5JyvTr5I6RFFBuqt3NlUb3Hp16wP3B2x9DZiB6jxr0l341/NHgsyeBXkuIy9j
ON2mvpBPCJhS8kgWo3G0UyyKnx64tcgpGuSvZhGwPz843B6AbYyE6pMRfSWRMkMS
YZYa+VNKhR4ixdj07ocFZEWLVjCH7kxkE8JZXKt8jKYmkWd0lS1QVjgaKlO6lRa3
q6SPJkhW6pvqobvcqVNXwi1XuzpZeEbuh0B7OTekFTTxx5g9XeDl56M8SVQ1KEhT
Q1t7H2Nba18WCB7cf+6PN0F0K0Jz1Kq7ZWaqEI/grX1m4RQuvNF5807sB/QKMO/Z
Gz3NXvHg5xTJRd/567lxPGkor0cE7qD1EZfmJ2HrBYXQ91bhgA7LToBuMZo6ZRXH
QfsanjbP4FPLMiGdQigLjj3A35L/f4sQOOVac/sRaFnm7pzcxsMvyVU/YtvGcjYE
xaOOVnamg661Wo0wksXoDjeSz/JIyyKO3Gwp1FSm2wGLjjy/Ehmqcqy8rvHuf07w
AUukhVtTNn4=
-----END ENCRYPTED PRIVATE KEY-----

View file

@ -1,57 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIDKTCCAhGgAwIBAgIJAOWmjTS+OnTEMA0GCSqGSIb3DQEBCwUAMBcxFTATBgNV
BAMMDGludGVybWVkaWF0ZTAeFw0xNTA1MTgwOTAwNDBaFw0yMzA4MDQwOTAwNDBa
MBQxEjAQBgNVBAMMCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
AQoCggEBALlcwQJuzd+xH8QFgfJSn5tRlvhkldSX98cE7NiA602NBbnAVyUrkRXq
Ni75lgt0kwjYfA9z674m8WSVbgpLPintPCla9CYky1TH0keIs8Rz6cGWHryWEHiu
EDuljQynu2b3sAFuHu9nfWurbJwZnFakBKpdQ9m4EyOZCHC/jHYY7HacKSXg1Cki
we2ca0BWDrcqy8kLy0dZ5oC6IZG8O8drAK8f3f44CRYw59D3sOKBrKXaabpvyEcb
N7Wk2HDBVwHpUJo1reVwtbM8dhqQayYSD8oXnGpP3RQNu/e2rzlXRyq/BfcDY1JI
7TbC4t/7/N4EcPSpGsTcSOC9A7FpzvECAwEAAaN7MHkwCQYDVR0TBAIwADAsBglg
hkgBhvhCAQ0EHxYdT3BlblNTTCBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0O
BBYEFCnwiEMMFZh7NhCr+qA8K0w4Q+AOMB8GA1UdIwQYMBaAFB0h1Evsaw2vfrmS
YuoCTmC4EE6ZMA0GCSqGSIb3DQEBCwUAA4IBAQAcFmHMaXRxyoNaeOowQ6iQWoZd
AUbvG7SHr7I6Pi2aqdqofsKWts7Ytm5WsS0M2nN+sW504houu0iCPeJJX8RQw2q4
CCcNOs9IXk+2uMzlpocHpv+yYoUiD5DxgWh7eghQMLyMpf8FX3Gy4VazeuXznHOM
4gE4L417xkDzYOzqVTp0FTyAPUv6G2euhNCD6TMru9REcRhYul+K9kocjA5tt2KG
MH6y28LXbLyq4YJUxSUU9gY/xlnbbZS48KDqEcdYC9zjW9nQ0qS+XQuQuFIcwjJ5
V4kAUYxDu6FoTpyQjgsrmBbZlKNxH7Nj4NDlcdJhp/zeSKHqWa5hSWjjKIxp
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDAjCCAeqgAwIBAgIJAOWmjTS+OnTDMA0GCSqGSIb3DQEBCwUAMBgxFjAUBgNV
BAMMDXJvb3RhdXRob3JpdHkwHhcNMTUwNTE4MDkwMDQwWhcNMjMwODA0MDkwMDQw
WjAXMRUwEwYDVQQDDAxpbnRlcm1lZGlhdGUwggEiMA0GCSqGSIb3DQEBAQUAA4IB
DwAwggEKAoIBAQDSrAO1CoPvUllgLOzDm5nG0skDF7vh1DUgAIDVGz0ecD0JFbQx
EF79pju/6MbtpTW2FYvRp11t/G7rGtX923ybOHY/1MNFQrdIvPlO1VV7IGKjoMwP
DNeb0fIGjHoE9QxaDxR8NX8xQbItpsw+TUtRfc9SLkR+jaYJfVRoM21BOncZbSHE
YKiZlEbpecB/+EtwVpgvl+8mPD5U07Fi4fp/lza3WXInXQPyiTVllIEJCt4PKmlu
MocNaJOW38bysL7i0PzDpVZtOxLHOTaW68yF3FckIHNCaA7k1ABEEEegjFMmIao7
B9w7A0jvr4jZVvNmui5Djjn+oJxwEVVgyf8LAgMBAAGjUDBOMB0GA1UdDgQWBBQd
IdRL7GsNr365kmLqAk5guBBOmTAfBgNVHSMEGDAWgBRk81s9d0ZbiZhh44KckwPb
oTc0XzAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBZQTK0plfdB5PC
cC5icut4EmrByJa1RbU7ayuEE70e7hla6KVmVjVdCBGltI4jBYwfhKbRItHiAJ/8
x+XZKBG8DLPFuDb7lAa1ObhAYF7YThUFPQYaBhfzKcWrdmWDBFpvNv6E0Mm364dZ
e7Yxmbe5S4agkYPoxEzgEYmcUk9jbjdR6eTbs8laG169ljrECXfEU9RiAcqz5iSX
NLSewqB47hn3B9qgKcQn+PsgO2j7M+rfklhNgeGJeWmy7j6clSOuCsIjWHU0RLQ4
0W3SB/rpEAJ7fgQbYUPTIUNALSOWi/o1tDX2mXPRjBoxqAv7I+vYk1lZPmSzkyRh
FKvRDxsW
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDAzCCAeugAwIBAgIJAJ0MomS4Ck+8MA0GCSqGSIb3DQEBCwUAMBgxFjAUBgNV
BAMMDXJvb3RhdXRob3JpdHkwHhcNMTUwNTE4MDkwMDQwWhcNMjMwODA0MDkwMDQw
WjAYMRYwFAYDVQQDDA1yb290YXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOC
AQ8AMIIBCgKCAQEAts1ijtBV92S2cOvpUMOSTp9c6A34nIGr0T5Nhz6XiqRVT+gv
dQgmkdKJQjbvR60y6jzltYFsI2MpGVXY8h/oAL81D/k7PDB2aREgyBfTPAhBHyGw
siR+2xYt5b/Zs99q5RdRqQNzNpLPJriIKvUsRyQWy1UiG2s7pRXQeA8qB0XtJdCj
kFIi+G2bDsaffspGeDOCqt7t+yqvRXfSES0c/l7DIHaiMbbp4//ZNML3RNgAjPz2
hCezZ+wOYajOIyoSPK8IgICrhYFYxvgWxwbLDBEfC5B3jOQsySe10GoRAKZz1gBV
DmgReu81tYJmdgkc9zknnQtIFdA0ex+GvZlfWQIDAQABo1AwTjAdBgNVHQ4EFgQU
ZPNbPXdGW4mYYeOCnJMD26E3NF8wHwYDVR0jBBgwFoAUZPNbPXdGW4mYYeOCnJMD
26E3NF8wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEATzkZ97K777uZ
lQcduNX3ey4IbCiEzFA2zO5Blj+ilfIwNbZXNOgm/lqNvVGDYs6J1apJJe30vL3X
J+t2zsZWzzQzb9uIU37zYemt6m0fHrSrx/iy5lGNqt3HMfqEcOqSCOIK3PCTMz2/
uyGe1iw33PVeWsm1JUybQ9IrU/huJjbgOHU4wab+8SJCM49ipArp68Fr6j4lcEaE
4rfRg1ZsvxiOyUB3qPn6wyL/JB8kOJ+QCBe498376eaem8AEFk0kQRh6hDaWtq/k
t6IIXQLjx+EBDVP/veK0UnVhKRP8YTOoV8ZiG1NcdlJmX/Uk7iAfevP7CkBfSN8W
r6AL284qtw==
-----END CERTIFICATE-----

View file

@ -1,21 +1,29 @@
MIT License
BSD 3-Clause License
Copyright (c) 2017 The Angel Framework
Copyright (c) 2021, dukefirehawk.com
All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View file

@ -1,26 +0,0 @@
Copyright 2017 dart_language_server authors
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors
may be used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View file

@ -1,10 +1,11 @@
# jael
[![Pub](https://img.shields.io/pub/v/jael.svg)](https://pub.dartlang.org/packages/jael)
[![build status](https://travis-ci.org/angel-dart/jael.svg)](https://travis-ci.org/angel-dart/jael)
# JAEL3
![Pub Version (including pre-releases)](https://img.shields.io/pub/v/jael3?include_prereleases)
A simple server-side HTML templating engine for Dart.
Though its syntax is but a superset of HTML, it supports features such as:
* **Custom elements**
* Loops
* Conditionals
@ -13,22 +14,21 @@ Though its syntax is but a superset of HTML, it supports features such as:
* `switch` syntax
* Interpolation of any Dart expression
Jael is a good choice for applications of any scale, especially when the development team is small,
or the time invested in building an SPA would be too much.
Jael is a good choice for applications of any scale, especially when the development team is small, or the time invested in building an SPA would be too much.
## Documentation
Each of the [packages within this repository](#this-repository) contains
some sort of documentation.
Each of the [packages within this repository](#this-repository) contains some sort of documentation.
Documentation for Jael syntax and directives has been
**moved** to the
[Angel framework wiki](https://docs.angel-dart.dev/packages/front-end/jael).
[Angel3 framework wiki](https://angel3-docs.dukefirehawk.com/packages/front-end/jael).
## This Repository
Within this repository are three packages:
* `package:jael` - Contains the Jael parser, AST, and HTML renderer.
* `package:jael_preprocessor` - Handles template inheritance, and facilitates the use of "compile-time" constructs.
* `package:build_jael` - Uses `package:build` to compile Jael templates, therefore allowing speedy incremental builds to HTML files.
* `package:angel_jael` - [Angel](https://angel-dart.github.io) support for Jael. Angel contains other
* `package:jael3` - Contains the Jael parser, AST, and HTML renderer.
* `package:jael3_preprocessor` - Handles template inheritance, and facilitates the use of "compile-time" constructs.
* `package:angel3_jael` - [Angel3](https://angel3-framework.web.app/) support for Jael.
facilities to speed up application development, so something like Jael is right at home.

View file

@ -3,7 +3,6 @@ import 'package:angel3_framework/angel3_framework.dart';
import 'package:angel3_framework/http.dart';
import 'package:angel3_jael/angel3_jael.dart';
import 'package:file/local.dart';
import 'package:jael3/jael3.dart';
import 'package:logging/logging.dart';
main() async {

View file

@ -23,9 +23,9 @@ dependencies:
The core `jael3` package exports classes for parsing Jael templates, an AST library, and a `Renderer` class that generates HTML on-the-fly.
```dart
import 'package:belatuk_code_buffer/code_buffer.dart';
import 'package:belatuk_symbol_table/symbol_table.dart';
import 'package:jael3/jael.dart' as jael;
import 'package:belatuk_code_buffer/belatuk_code_buffer.dart';
import 'package:belatuk_symbol_table/belatuk_symbol_table.dart';
import 'package:jael3/jael3.dart' as jael;
void myFunction() {
const template = '''

View file

@ -1,7 +1,8 @@
name: jael3
version: 4.2.0
version: 4.2.1
description: A simple server-side HTML templating engine for Dart. Comparable to Blade or Liquid.
homepage: https://github.com/dukefirehawk/angel/tree/master/packages/jael/jael
homepage: https://angel3-framework.web.app/
repository: https://github.com/dukefirehawk/angel/tree/master/packages/jael/jael
environment:
sdk: '>=2.12.0 <3.0.0'
dependencies:

View file

@ -1,5 +1,9 @@
# Change Log
## 4.0.3
* Removed debugging messages
## 4.0.2
* Updated linter to `package:lints`

View file

@ -1,8 +1,6 @@
import 'dart:async';
import 'package:angel3_model/angel3_model.dart';
import 'package:angel3_orm/angel3_orm.dart';
import 'package:angel3_orm/src/query.dart';
import 'package:angel3_serialize/angel3_serialize.dart';
import 'package:optional/optional.dart';

View file

@ -342,6 +342,8 @@ abstract class Query<T, Where extends QueryWhere> extends QueryBase<T> {
Future<List<T>> delete(QueryExecutor executor) {
var sql = compile({}, preamble: 'DELETE', withFields: false);
//_log.fine("Delete Query = $sql");
if (_joins.isEmpty) {
return executor
.query(tableName, sql, substitutionValues,
@ -375,6 +377,8 @@ abstract class Query<T, Where extends QueryWhere> extends QueryBase<T> {
var sql = compile({});
sql = 'WITH $tableName as ($insertion RETURNING $returning) ' + sql;
//_log.fine("Insert Query = $sql");
return executor.query(tableName, sql, substitutionValues).then((it) {
// Return SQL execution results
return it.isEmpty ? Optional.empty() : deserialize(it.first);
@ -400,6 +404,8 @@ abstract class Query<T, Where extends QueryWhere> extends QueryBase<T> {
var sql = compile({});
sql = 'WITH $tableName as ($updateSql RETURNING $returning) ' + sql;
//_log.fine("Update Query = $sql");
return executor
.query(tableName, sql, substitutionValues)
.then((it) => deserializeList(it));

View file

@ -60,8 +60,8 @@ abstract class QueryBase<T> {
Future<List<T>> get(QueryExecutor executor) async {
var sql = compile({});
_log.fine('sql = $sql');
_log.fine('substitutionValues = $substitutionValues');
//_log.fine('sql = $sql');
//_log.fine('substitutionValues = $substitutionValues');
return executor.query(tableName, sql, substitutionValues).then((it) {
return deserializeList(it);

View file

@ -1,5 +1,5 @@
name: angel3_orm
version: 4.0.2
version: 4.0.3
description: Runtime support for Angel3 ORM. Includes base classes for queries.
homepage: https://angel3-framework.web.app/
repository: https://github.com/dukefirehawk/angel/tree/master/packages/orm/angel_orm

View file

@ -18,4 +18,9 @@ dev_dependencies:
test: ^1.17.0
angel3_orm_test: ^3.0.0
lints: ^1.0.0
dependency_overrides:
angel3_orm_test:
path: ../angel_orm_test
angel3_orm:
path: ../angel_orm

View file

@ -1,5 +1,9 @@
# Change Log
## 3.2.0
* Added `package:postgres_pool` for connection pooling
## 3.1.0
* Updated linter to `package:lints`

View file

@ -5,6 +5,6 @@
[![Gitter](https://img.shields.io/gitter/room/angel_dart/discussion)](https://gitter.im/angel_dart/discussion)
[![License](https://img.shields.io/github/license/dukefirehawk/angel)](https://github.com/dukefirehawk/angel/tree/master/packages/orm/angel_orm_postgres/LICENSE)
Postgresql support for Angel3 ORM. Supported version: 9, 10, 11 and 12.
Postgresql support for Angel3 ORM. Supported version: 10, 11, 12, 13 and 14
For documentation about the ORM, see [Developer Guide](https://angel3-docs.dukefirehawk.com/guides/orm)

View file

@ -1,12 +1,20 @@
import 'dart:io';
import 'package:angel3_orm_postgres/angel3_orm_postgres.dart';
import 'package:postgres/postgres.dart';
import 'package:postgres_pool/postgres_pool.dart';
void main() async {
var executor = PostgreSqlExecutorPool(Platform.numberOfProcessors, () {
return PostgreSQLConnection('localhost', 5432, 'orm_test',
username: 'test', password: 'test123');
});
var executor = PostgreSqlPoolExecutor(PgPool(
PgEndpoint(
host: 'localhost',
port: 5432,
database: 'orm_test',
username: Platform.environment['POSTGRES_USERNAME'] ?? 'test',
password: Platform.environment['POSTGRES_PASSWORD'] ?? 'test123',
),
settings: PgPoolSettings()
..maxConnectionAge = Duration(hours: 1)
..concurrency = 5,
));
var rows = await executor.query('users', 'SELECT * FROM users', {});
print(rows);

View file

@ -1,171 +1,2 @@
import 'dart:async';
import 'dart:convert';
import 'package:angel3_orm/angel3_orm.dart';
import 'package:logging/logging.dart';
import 'package:pool/pool.dart';
import 'package:postgres/postgres.dart';
/// A [QueryExecutor] that queries a PostgreSQL database.
class PostgreSqlExecutor extends QueryExecutor {
final PostgreSQLExecutionContext _connection;
/// An optional [Logger] to print information to.
late Logger logger;
PostgreSqlExecutor(this._connection, {Logger? logger}) {
if (logger != null) {
this.logger = logger;
} else {
this.logger = Logger('PostgreSqlExecutor');
}
}
/// The underlying connection.
PostgreSQLExecutionContext get connection => _connection;
/// Closes the connection.
Future close() {
if (_connection is PostgreSQLConnection) {
return (_connection as PostgreSQLConnection).close();
} else {
return Future.value();
}
}
@override
Future<List<List>> query(
String tableName, String query, Map<String, dynamic> substitutionValues,
[List<String>? returningFields]) {
if (returningFields != null) {
var fields = returningFields.join(', ');
var returning = 'RETURNING $fields';
query = '$query $returning';
}
logger.fine('Query: $query');
logger.fine('Values: $substitutionValues');
// Convert List into String
var param = <String, dynamic>{};
substitutionValues.forEach((key, value) {
if (value is List) {
param[key] = jsonEncode(value);
} else {
param[key] = value;
}
});
return _connection.query(query, substitutionValues: param);
}
@override
Future<T> transaction<T>(FutureOr<T> Function(QueryExecutor) f) async {
if (_connection is! PostgreSQLConnection) {
return await f(this);
}
var conn = _connection as PostgreSQLConnection;
T? returnValue;
var txResult = await conn.transaction((ctx) async {
try {
logger.fine('Entering transaction');
var tx = PostgreSqlExecutor(ctx, logger: logger);
returnValue = await f(tx);
return returnValue;
} catch (e) {
ctx.cancelTransaction(reason: e.toString());
rethrow;
} finally {
logger.fine('Exiting transaction');
}
});
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.');
}
/// 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, (_) {
logger.fine('Spawning connections...');
var conn = connectionFactory();
return conn
.open()
.then((_) => PostgreSqlExecutor(conn, logger: logger));
})));
}
}
Future<PostgreSqlExecutor> _next() {
return _connMutex.withResource(() async {
await _open();
if (_index >= size) _index = 0;
return _connections[_index++];
});
}
@override
Future<List<List>> query(
String tableName, String query, Map<String, dynamic> substitutionValues,
[List<String>? returningFields]) {
return _pool.withResource(() async {
var executor = await _next();
return executor.query(
tableName, query, substitutionValues, returningFields);
});
}
@override
Future<T> transaction<T>(FutureOr<T> Function(QueryExecutor) f) {
return _pool.withResource(() async {
var executor = await _next();
return executor.transaction(f);
});
}
}
export 'src/orm_postgres.dart';
export 'src/orm_postgres_pool.dart';

View file

@ -0,0 +1,169 @@
import 'dart:async';
import 'dart:convert';
import 'package:angel3_orm/angel3_orm.dart';
import 'package:logging/logging.dart';
import 'package:pool/pool.dart';
import 'package:postgres/postgres.dart';
/// A [QueryExecutor] that queries a PostgreSQL database.
class PostgreSqlExecutor extends QueryExecutor {
final PostgreSQLExecutionContext _connection;
/// An optional [Logger] to print information to.
late Logger logger;
PostgreSqlExecutor(this._connection, {Logger? logger}) {
this.logger = logger ?? Logger('PostgreSqlExecutor');
}
/// The underlying connection.
PostgreSQLExecutionContext get connection => _connection;
/// Closes the connection.
Future close() {
if (_connection is PostgreSQLConnection) {
return (_connection as PostgreSQLConnection).close();
} else {
return Future.value();
}
}
@override
Future<PostgreSQLResult> query(
String tableName, String query, Map<String, dynamic> substitutionValues,
[List<String>? returningFields]) {
if (returningFields != null && returningFields.isNotEmpty) {
var fields = returningFields.join(', ');
var returning = 'RETURNING $fields';
query = '$query $returning';
}
//logger.fine('Query: $query');
//logger.fine('Values: $substitutionValues');
// Convert List into String
var param = <String, dynamic>{};
substitutionValues.forEach((key, value) {
if (value is List) {
param[key] = jsonEncode(value);
} else {
param[key] = value;
}
});
return _connection.query(query, substitutionValues: param);
}
@override
Future<T> transaction<T>(FutureOr<T> Function(QueryExecutor) f) async {
if (_connection is! PostgreSQLConnection) {
return await f(this);
}
var conn = _connection as PostgreSQLConnection;
T? returnValue;
var txResult = await conn.transaction((ctx) async {
try {
logger.fine('Entering transaction');
var tx = PostgreSqlExecutor(ctx, logger: logger);
returnValue = await f(tx);
return returnValue;
} catch (e) {
ctx.cancelTransaction(reason: e.toString());
rethrow;
} finally {
logger.fine('Exiting transaction');
}
});
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.');
}
/// 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,
[List<String>? returningFields]) {
return _pool.withResource(() async {
var executor = await _next();
return executor.query(
tableName, query, substitutionValues, 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

@ -0,0 +1,65 @@
import 'dart:async';
import 'dart:convert';
import 'package:angel3_orm/angel3_orm.dart';
import 'package:logging/logging.dart';
import 'package:postgres_pool/postgres_pool.dart';
import '../angel3_orm_postgres.dart';
/// A [QueryExecutor] that uses `package:postgres_pool` for connetions pooling.
class PostgreSqlPoolExecutor extends QueryExecutor {
final PgPool _pool;
/// An optional [Logger] to print information to.
late Logger logger;
PostgreSqlPoolExecutor(this._pool, {Logger? logger}) {
this.logger = logger ?? Logger('PostgreSqlPoolExecutor');
}
/// The underlying connection pooling.
PgPool get pool => _pool;
/// Closes all the connections in the pool.
Future<dynamic> close() {
return _pool.close();
}
/// Run query.
@override
Future<PostgreSQLResult> query(
String tableName, String query, Map<String, dynamic> substitutionValues,
[List<String> returningFields = const []]) {
if (returningFields.isNotEmpty) {
var fields = returningFields.join(', ');
var returning = 'RETURNING $fields';
query = '$query $returning';
}
//logger.fine('Query: $query');
//logger.fine('Values: $substitutionValues');
// Convert List into String
var param = <String, dynamic>{};
substitutionValues.forEach((key, value) {
if (value is List) {
param[key] = jsonEncode(value);
} else {
param[key] = value;
}
});
return _pool.run<PostgreSQLResult>((pgContext) async {
return await pgContext.query(query, substitutionValues: param);
});
}
/// Run query in a transaction.
@override
Future<T> transaction<T>(FutureOr<T> Function(QueryExecutor) f) async {
return _pool.runTx((pgContext) async {
var exec = PostgreSqlExecutor(pgContext, logger: logger);
return await f(exec);
});
}
}

View file

@ -1,5 +1,5 @@
name: angel3_orm_postgres
version: 3.1.0
version: 3.2.0
description: PostgreSQL support for Angel3 ORM. Includes functionality for querying and transactions.
homepage: https://angel3-framework.web.app/
repository: https://github.com/dukefirehawk/angel/tree/master/packages/orm/angel_orm_postgres
@ -10,8 +10,14 @@ dependencies:
logging: ^1.0.1
pool: ^1.5.0
postgres: ^2.4.1
postgres_pool: ^2.1.3
dev_dependencies:
belatuk_pretty_logging: ^4.0.0
angel3_orm_test: ^3.0.0
test: ^1.17.5
lints: ^1.0.0
#dependency_overrides:
# angel3_orm_test:
# path: ../angel_orm_test
# angel3_orm:
# path: ../angel_orm

View file

@ -9,6 +9,9 @@ void main() {
..level = Level.ALL
..onRecord.listen(prettyLog);
//group('performance',
// () => performanceTests(pg(['performance']), close: closePg));
group('postgresql', () {
group('belongsTo',
() => belongsToTests(pg(['author', 'book']), close: closePg));

View file

@ -3,14 +3,28 @@ import 'dart:io';
import 'package:angel3_orm/angel3_orm.dart';
import 'package:angel3_orm_postgres/angel3_orm_postgres.dart';
import 'package:logging/logging.dart';
import 'package:postgres/postgres.dart';
import 'package:postgres_pool/postgres_pool.dart';
FutureOr<QueryExecutor> Function() pg(Iterable<String> schemas) {
// Use single connection
return () => connectToPostgres(schemas);
// Use connection pooling with 1 connection
//return () => connectToPostgresPool(schemas);
// Use PostgreSqlExecutorPool (Not working)
//return () => connectToPostgresPool1(schemas);
}
Future<void> closePg(QueryExecutor executor) =>
(executor as PostgreSqlExecutor).close();
Future<void> closePg(QueryExecutor executor) async {
if (executor is PostgreSqlExecutor) {
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 {
var conn = PostgreSQLConnection('127.0.0.1', 5432, 'orm_test',
@ -25,3 +39,47 @@ Future<PostgreSqlExecutor> connectToPostgres(Iterable<String> schemas) async {
return PostgreSqlExecutor(conn, logger: Logger.root);
}
Future<PostgreSqlExecutorPool> connectToPostgresPool1(
Iterable<String> schemas) async {
PostgreSQLConnection connectionFactory() {
return PostgreSQLConnection('127.0.0.1', 5432, 'orm_test',
username: Platform.environment['POSTGRES_USERNAME'] ?? 'test',
password: Platform.environment['POSTGRES_PASSWORD'] ?? 'test123');
}
PostgreSQLConnection conn = connectionFactory();
await conn.open();
// Run sql to create the tables
for (var s in schemas) {
await conn.execute(await File('test/migrations/$s.sql').readAsString());
}
return PostgreSqlExecutorPool(5, connectionFactory, logger: Logger.root);
}
Future<PostgreSqlPoolExecutor> connectToPostgresPool(
Iterable<String> schemas) async {
var _pool = PgPool(
PgEndpoint(
host: 'localhost',
port: 5432,
database: 'orm_test',
username: Platform.environment['POSTGRES_USERNAME'] ?? 'test',
password: Platform.environment['POSTGRES_PASSWORD'] ?? 'test123',
),
settings: PgPoolSettings()
..maxConnectionAge = Duration(hours: 1)
..concurrency = 200,
);
// Run sql to create the tables in a transaction
//await _pool.runTx((conn) async {
// for (var s in schemas) {
// await conn.execute(await File('test/migrations/$s.sql').readAsString());
// }
//});
return PostgreSqlPoolExecutor(_pool, logger: Logger.root);
}

View file

@ -0,0 +1,11 @@
CREATE TEMPORARY TABLE "world" (
id serial NOT NULL,
randomNumber integer NOT NULL default 0,
PRIMARY KEY (id)
);
CREATE TEMPORARY TABLE "fortune" (
id serial NOT NULL,
message varchar(2048) NOT NULL,
PRIMARY KEY (id)
);

View file

@ -1,5 +1,10 @@
# Change Log
## 3.0.3
* Added `performance_test` test cases
* Update `edge_case_test` test case to support connection pooling
## 3.0.2
* Updated linter to `package:lints`

View file

@ -7,3 +7,4 @@ export 'src/has_map_test.dart';
export 'src/has_one_test.dart';
export 'src/many_to_many_test.dart';
export 'src/standalone_test.dart';
export 'src/performance_test.dart';

View file

@ -42,12 +42,12 @@ void belongsToTests(FutureOr<QueryExecutor> Function() createExecutor,
expect(books, hasLength(1));
var book = books.first;
print(book.toJson());
//print(book.toJson());
expect(book.id, deathlyHallows!.id);
expect(book.name, deathlyHallows!.name);
var author = book.author!;
print(AuthorSerializer.toMap(author));
//print(AuthorSerializer.toMap(author));
expect(author.id, jkRowling!.id);
expect(author.name, jkRowling!.name);
});
@ -55,17 +55,17 @@ void belongsToTests(FutureOr<QueryExecutor> Function() createExecutor,
test('select one', () async {
var query = BookQuery();
query.where!.id.equals(int.parse(deathlyHallows!.id!));
print(query.compile({}));
//print(query.compile({}));
var bookOpt = await query.getOne(executor);
expect(bookOpt.isPresent, true);
bookOpt.ifPresent((book) {
print(book.toJson());
//print(book.toJson());
expect(book.id, deathlyHallows!.id);
expect(book.name, deathlyHallows!.name);
var author = book.author!;
print(AuthorSerializer.toMap(author));
//print(AuthorSerializer.toMap(author));
expect(author.id, jkRowling!.id);
expect(author.name, jkRowling!.name);
});
@ -75,18 +75,18 @@ void belongsToTests(FutureOr<QueryExecutor> Function() createExecutor,
var query = BookQuery()
..where!.name.equals('Goblet of Fire')
..orWhere((w) => w.authorId.equals(int.parse(jkRowling!.id!)));
print(query.compile({}));
//print(query.compile({}));
var books = await query.get(executor);
expect(books, hasLength(1));
var book = books.first;
print(book.toJson());
//print(book.toJson());
expect(book.id, deathlyHallows!.id);
expect(book.name, deathlyHallows!.name);
var author = book.author!;
print(AuthorSerializer.toMap(author));
//print(AuthorSerializer.toMap(author));
expect(author.id, jkRowling!.id);
expect(author.name, jkRowling!.name);
});
@ -99,18 +99,18 @@ void belongsToTests(FutureOr<QueryExecutor> Function() createExecutor,
query1
..union(query2)
..unionAll(query3);
print(query1.compile({}));
//print(query1.compile({}));
var books = await query1.get(executor);
expect(books, hasLength(1));
var book = books.first;
print(book.toJson());
//print(book.toJson());
expect(book.id, deathlyHallows!.id);
expect(book.name, deathlyHallows!.name);
var author = book.author!;
print(AuthorSerializer.toMap(author));
//print(AuthorSerializer.toMap(author));
expect(author.id, jkRowling!.id);
expect(author.name, jkRowling!.name);
});
@ -129,9 +129,9 @@ void belongsToTests(FutureOr<QueryExecutor> Function() createExecutor,
});
test('delete stream', () async {
printSeparator('Delete stream test');
//printSeparator('Delete stream test');
var query = BookQuery()..where!.name.equals(deathlyHallows!.name!);
print(query.compile({}, preamble: 'DELETE', withFields: false));
//print(query.compile({}, preamble: 'DELETE', withFields: false));
var books = await query.delete(executor);
expect(books, hasLength(1));
@ -149,7 +149,7 @@ void belongsToTests(FutureOr<QueryExecutor> Function() createExecutor,
var bookOpt = await (query.updateOne(executor));
expect(bookOpt.isPresent, true);
bookOpt.ifPresent((book) {
print(book.toJson());
//print(book.toJson());
expect(book.name, cloned.name);
expect(book.author, isNotNull);
expect(book.author!.name, jkRowling!.name);

View file

@ -15,11 +15,11 @@ void edgeCaseTests(FutureOr<QueryExecutor> Function() createExecutor,
tearDown(() => close!(executor));
test('can create object with no id', () async {
var query = UnorthodoxQuery()..values.name = 'Hey';
var query = UnorthodoxQuery()..values.name = 'World';
var modelOpt = await query.insert(executor);
expect(modelOpt.isPresent, true);
modelOpt.ifPresent((model) {
expect(model, Unorthodox(name: 'Hey'));
expect(model, Unorthodox(name: 'World'));
});
});
@ -27,12 +27,14 @@ void edgeCaseTests(FutureOr<QueryExecutor> Function() createExecutor,
Unorthodox? unorthodox;
setUp(() async {
//if (unorthodox == null) {
var query = UnorthodoxQuery()..values.name = 'Hey';
var unorthodoxOpt = await query.insert(executor);
unorthodoxOpt.ifPresent((value) {
unorthodox = value;
});
//}
});
test('belongs to', () async {
@ -40,7 +42,7 @@ void edgeCaseTests(FutureOr<QueryExecutor> Function() createExecutor,
var modelOpt = await query.insert(executor);
expect(modelOpt.isPresent, true);
modelOpt.ifPresent((model) {
print(model.toJson());
//print(model.toJson());
expect(model.id, isNotNull); // Postgres should set this.
expect(model.unorthodox, unorthodox);
});
@ -73,7 +75,7 @@ void edgeCaseTests(FutureOr<QueryExecutor> Function() createExecutor,
var wjOpt = await query.getOne(executor);
expect(wjOpt.isPresent, true);
wjOpt.ifPresent((wj) {
print(wj.toJson());
//print(wj.toJson());
expect(wj.song, girlBlue);
});
});
@ -96,7 +98,7 @@ void edgeCaseTests(FutureOr<QueryExecutor> Function() createExecutor,
var wjObj = await query.getOne(executor);
expect(wjObj.isPresent, true);
wjObj.ifPresent((wj) {
print(wj.toJson());
//print(wj.toJson());
expect(wj.numbas, numbas);
});
});
@ -114,8 +116,8 @@ void edgeCaseTests(FutureOr<QueryExecutor> Function() createExecutor,
var fooOpt = await fooQuery.getOne(executor);
expect(fooOpt.isPresent, true);
fooOpt.ifPresent((foo) {
print(foo.toJson());
print(weirdJoin!.toJson());
//print(foo.toJson());
//print(weirdJoin!.toJson());
expect(foo.weirdJoins![0].id, weirdJoin!.id);
});
});

View file

@ -29,7 +29,7 @@ void hasManyTests(FutureOr<QueryExecutor> Function() createExecutor,
Fruit? apple, banana;
void verify(Tree tree) {
print(tree.fruits!.map(FruitSerializer.toMap).toList());
//print(tree.fruits!.map(FruitSerializer.toMap).toList());
expect(tree.fruits, hasLength(2));
expect(tree.fruits![0].commonName, apple!.commonName);
expect(tree.fruits![1].commonName, banana!.commonName);

View file

@ -22,10 +22,10 @@ void hasMapTests(FutureOr<QueryExecutor> Function() createExecutor,
var modelOpt = await (query.insert(executor));
expect(modelOpt.isPresent, true);
modelOpt.ifPresent((model) {
print(model.toString());
//print(model.toString());
var data = HasMap(value: {'foo': 'bar'}, list: ['1', 2, 3.0]);
print(data.toString());
//print(data.toString());
expect(model, data);
});
@ -40,7 +40,7 @@ void hasMapTests(FutureOr<QueryExecutor> Function() createExecutor,
expect(modelOpt.isPresent, true);
if (modelOpt.isPresent) {
var model = modelOpt.value;
print(model.toJson());
//print(model.toJson());
query = HasMapQuery()..values.copyFrom(model);
var result = await query.updateOne(executor);
expect(result.isPresent, true);
@ -83,7 +83,7 @@ void hasMapTests(FutureOr<QueryExecutor> Function() createExecutor,
query.where?.list.equals(['1', 2, 3.0]);
print(query.substitutionValues);
//print(query.substitutionValues);
var result = await query.get(executor);
expect(result, [initialValue]);

View file

@ -18,12 +18,12 @@ void hasOneTests(FutureOr<QueryExecutor> Function() createExecutor,
tearDown(() => close!(executor));
test('sets to null if no child', () async {
print(LegQuery().compile({}));
//print(LegQuery().compile({}));
var query = LegQuery()..where!.id.equals(int.parse(originalLeg!.id!));
var legOpt = await (query.getOne(executor));
expect(legOpt.isPresent, true);
legOpt.ifPresent((leg) {
print(leg.toJson());
//print(leg.toJson());
expect(leg.name, originalLeg?.name);
expect(leg.id, originalLeg?.id);
expect(leg.foot, isNull);
@ -82,7 +82,7 @@ void hasOneTests(FutureOr<QueryExecutor> Function() createExecutor,
expect(footOpt.isPresent, true);
expect(legOpt.isPresent, true);
legOpt.ifPresent((leg) {
print(leg.toJson());
//print(leg.toJson());
expect(leg.name, 'Right');
expect(leg.foot, isNotNull);
footOpt.ifPresent((foot) {
@ -102,7 +102,7 @@ void hasOneTests(FutureOr<QueryExecutor> Function() createExecutor,
expect(footOpt.isPresent, true);
expect(legOpt.isPresent, true);
legOpt.ifPresent((leg) {
print(leg.toJson());
//print(leg.toJson());
expect(leg.name, originalLeg?.name);
expect(leg.foot, isNotNull);
footOpt.ifPresent((foot) {

View file

@ -21,7 +21,9 @@ void manyToManyTests(FutureOr<QueryExecutor> Function() createExecutor,
//var rows = await executor.query(null, query, {});
var rows = await executor.query('', query, {});
print('\n${rows.length} row(s):');
rows.forEach((r) => print(' * $r'));
for (var r in rows) {
print(' * $r');
}
print('==================================================\n\n');
}

View file

@ -0,0 +1,16 @@
import 'package:angel3_migration/angel3_migration.dart';
//import 'package:angel3_model/angel3_model.dart';
import 'package:angel3_serialize/angel3_serialize.dart';
import 'package:angel3_orm/angel3_orm.dart';
import 'package:optional/optional.dart';
part 'fortune.g.dart';
@serializable
@Orm(tableName: 'fortune')
abstract class _Fortune {
int? id;
@Column(length: 2048)
String? message;
}

View file

@ -0,0 +1,200 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'fortune.dart';
// **************************************************************************
// MigrationGenerator
// **************************************************************************
class FortuneMigration extends Migration {
@override
void up(Schema schema) {
schema.create('fortune', (table) {
table.integer('id');
table.varChar('message', length: 2048);
});
}
@override
void down(Schema schema) {
schema.drop('fortune');
}
}
// **************************************************************************
// OrmGenerator
// **************************************************************************
class FortuneQuery extends Query<Fortune, FortuneQueryWhere> {
FortuneQuery({Query? parent, Set<String>? trampoline})
: super(parent: parent) {
trampoline ??= <String>{};
trampoline.add(tableName);
_where = FortuneQueryWhere(this);
}
@override
final FortuneQueryValues values = FortuneQueryValues();
FortuneQueryWhere? _where;
@override
Map<String, String> get casts {
return {};
}
@override
String get tableName {
return 'fortune';
}
@override
List<String> get fields {
return const ['id', 'message'];
}
@override
FortuneQueryWhere? get where {
return _where;
}
@override
FortuneQueryWhere newWhereClause() {
return FortuneQueryWhere(this);
}
static Fortune? parseRow(List row) {
if (row.every((x) => x == null)) {
return null;
}
var model = Fortune(id: (row[0] as int?), message: (row[1] as String?));
return model;
}
@override
Optional<Fortune> deserialize(List row) {
return Optional.ofNullable(parseRow(row));
}
}
class FortuneQueryWhere extends QueryWhere {
FortuneQueryWhere(FortuneQuery query)
: id = NumericSqlExpressionBuilder<int>(query, 'id'),
message = StringSqlExpressionBuilder(query, 'message');
final NumericSqlExpressionBuilder<int> id;
final StringSqlExpressionBuilder message;
@override
List<SqlExpressionBuilder> get expressionBuilders {
return [id, message];
}
}
class FortuneQueryValues extends MapQueryValues {
@override
Map<String, String> get casts {
return {};
}
int? get id {
return (values['id'] as int?);
}
set id(int? value) => values['id'] = value;
String? get message {
return (values['message'] as String?);
}
set message(String? value) => values['message'] = value;
void copyFrom(Fortune model) {
id = model.id;
message = model.message;
}
}
// **************************************************************************
// JsonModelGenerator
// **************************************************************************
@generatedSerializable
class Fortune extends _Fortune {
Fortune({this.id, this.message});
@override
int? id;
@override
String? message;
Fortune copyWith({int? id, String? message}) {
return Fortune(id: id ?? this.id, message: message ?? this.message);
}
@override
bool operator ==(other) {
return other is _Fortune && other.id == id && other.message == message;
}
@override
int get hashCode {
return hashObjects([id, message]);
}
@override
String toString() {
return 'Fortune(id=$id, message=$message)';
}
Map<String, dynamic> toJson() {
return FortuneSerializer.toMap(this);
}
}
// **************************************************************************
// SerializerGenerator
// **************************************************************************
const FortuneSerializer fortuneSerializer = FortuneSerializer();
class FortuneEncoder extends Converter<Fortune, Map> {
const FortuneEncoder();
@override
Map convert(Fortune model) => FortuneSerializer.toMap(model);
}
class FortuneDecoder extends Converter<Map, Fortune> {
const FortuneDecoder();
@override
Fortune convert(Map map) => FortuneSerializer.fromMap(map);
}
class FortuneSerializer extends Codec<Fortune, Map> {
const FortuneSerializer();
@override
FortuneEncoder get encoder => const FortuneEncoder();
@override
FortuneDecoder get decoder => const FortuneDecoder();
static Fortune fromMap(Map map) {
return Fortune(id: map['id'] as int?, message: map['message'] as String?);
}
static Map<String, dynamic> toMap(_Fortune? model) {
if (model == null) {
return {};
}
return {'id': model.id, 'message': model.message};
}
}
abstract class FortuneFields {
static const List<String> allFields = <String>[id, message];
static const String id = 'id';
static const String message = 'message';
}

View file

@ -0,0 +1,16 @@
import 'package:angel3_migration/angel3_migration.dart';
//import 'package:angel3_model/angel3_model.dart';
import 'package:angel3_serialize/angel3_serialize.dart';
import 'package:angel3_orm/angel3_orm.dart';
import 'package:optional/optional.dart';
part 'world.g.dart';
@serializable
@Orm(tableName: 'world')
abstract class _World {
int? id;
@Column()
int? randomNumber;
}

View file

@ -0,0 +1,203 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'world.dart';
// **************************************************************************
// MigrationGenerator
// **************************************************************************
class WorldMigration extends Migration {
@override
void up(Schema schema) {
schema.create('world', (table) {
table.integer('id');
table.integer('randomNumber');
});
}
@override
void down(Schema schema) {
schema.drop('world');
}
}
// **************************************************************************
// OrmGenerator
// **************************************************************************
class WorldQuery extends Query<World, WorldQueryWhere> {
WorldQuery({Query? parent, Set<String>? trampoline}) : super(parent: parent) {
trampoline ??= <String>{};
trampoline.add(tableName);
_where = WorldQueryWhere(this);
}
@override
final WorldQueryValues values = WorldQueryValues();
WorldQueryWhere? _where;
@override
Map<String, String> get casts {
return {};
}
@override
String get tableName {
return 'world';
}
@override
List<String> get fields {
return const ['id', 'randomNumber'];
}
@override
WorldQueryWhere? get where {
return _where;
}
@override
WorldQueryWhere newWhereClause() {
return WorldQueryWhere(this);
}
static World? parseRow(List row) {
if (row.every((x) => x == null)) {
return null;
}
var model = World(id: (row[0] as int?), randomNumber: (row[1] as int?));
return model;
}
@override
Optional<World> deserialize(List row) {
return Optional.ofNullable(parseRow(row));
}
}
class WorldQueryWhere extends QueryWhere {
WorldQueryWhere(WorldQuery query)
: id = NumericSqlExpressionBuilder<int>(query, 'id'),
randomNumber = NumericSqlExpressionBuilder<int>(query, 'randomNumber');
final NumericSqlExpressionBuilder<int> id;
final NumericSqlExpressionBuilder<int> randomNumber;
@override
List<SqlExpressionBuilder> get expressionBuilders {
return [id, randomNumber];
}
}
class WorldQueryValues extends MapQueryValues {
@override
Map<String, String> get casts {
return {};
}
int? get id {
return (values['id'] as int?);
}
set id(int? value) => values['id'] = value;
int? get randomNumber {
return (values['randomNumber'] as int?);
}
set randomNumber(int? value) => values['randomNumber'] = value;
void copyFrom(World model) {
id = model.id;
randomNumber = model.randomNumber;
}
}
// **************************************************************************
// JsonModelGenerator
// **************************************************************************
@generatedSerializable
class World extends _World {
World({this.id, this.randomNumber});
@override
int? id;
@override
int? randomNumber;
World copyWith({int? id, int? randomNumber}) {
return World(
id: id ?? this.id, randomNumber: randomNumber ?? this.randomNumber);
}
@override
bool operator ==(other) {
return other is _World &&
other.id == id &&
other.randomNumber == randomNumber;
}
@override
int get hashCode {
return hashObjects([id, randomNumber]);
}
@override
String toString() {
return 'World(id=$id, randomNumber=$randomNumber)';
}
Map<String, dynamic> toJson() {
return WorldSerializer.toMap(this);
}
}
// **************************************************************************
// SerializerGenerator
// **************************************************************************
const WorldSerializer worldSerializer = WorldSerializer();
class WorldEncoder extends Converter<World, Map> {
const WorldEncoder();
@override
Map convert(World model) => WorldSerializer.toMap(model);
}
class WorldDecoder extends Converter<Map, World> {
const WorldDecoder();
@override
World convert(Map map) => WorldSerializer.fromMap(map);
}
class WorldSerializer extends Codec<World, Map> {
const WorldSerializer();
@override
WorldEncoder get encoder => const WorldEncoder();
@override
WorldDecoder get decoder => const WorldDecoder();
static World fromMap(Map map) {
return World(
id: map['id'] as int?, randomNumber: map['randomNumber'] as int?);
}
static Map<String, dynamic> toMap(_World? model) {
if (model == null) {
return {};
}
return {'id': model.id, 'randomNumber': model.randomNumber};
}
}
abstract class WorldFields {
static const List<String> allFields = <String>[id, randomNumber];
static const String id = 'id';
static const String randomNumber = 'randomNumber';
}

View file

@ -0,0 +1,91 @@
import 'dart:async';
import 'dart:math';
import 'package:angel3_orm/angel3_orm.dart';
import 'package:test/test.dart';
import 'models/fortune.dart';
import 'models/world.dart';
void performanceTests(FutureOr<QueryExecutor> Function() createExecutor,
{FutureOr<void> Function(QueryExecutor)? close}) {
QueryExecutor? executor;
int _sampleSize = 1000;
int concurrency = 200;
// Generate a random number between 1 and 10000
int _genRandomId() {
var rand = Random();
return rand.nextInt(10000) + 1;
}
setUp(() async {
print("Run setup");
executor = await createExecutor();
/*
for (var i = 0; i < _sampleSize; i++) {
var query = WorldQuery();
query.values.randomNumber = _genRandomId();
var world = (await query.insert(executor!)).value;
}
for (var i = 0; i < _sampleSize; i++) {
var query = FortuneQuery();
query.values.message = "message ${_genRandomId()}";
var fortune = (await query.insert(executor!)).value;
}
*/
});
tearDown(() => close!(executor!));
test('select all concurrency', () async {
var stopwatch = Stopwatch();
stopwatch.start();
// Fire and forget
for (var i = 0; i < concurrency; i++) {
FortuneQuery().get(executor!);
}
print("Loop time elapsed: ${stopwatch.elapsed.inMilliseconds}");
var result = await FortuneQuery().get(executor!);
print("Final Time elapsed: ${stopwatch.elapsed.inMilliseconds}");
stopwatch.stop();
expect(result, isNotNull);
});
test('select one concurrency', () async {
var stopwatch = Stopwatch();
stopwatch.start();
var id = _genRandomId();
var query = WorldQuery()..where?.id.equals(id);
var result = await query.get(executor!);
print("Time elapsed: ${stopwatch.elapsed.inMilliseconds}");
stopwatch.stop();
expect(result, isNotNull);
});
test('update concurrency', () async {
var stopwatch = Stopwatch();
stopwatch.start();
var id = _genRandomId();
var query = WorldQuery()..where?.id.equals(id);
var result = await query.get(executor!);
print("Time elapsed: ${stopwatch.elapsed.inMilliseconds}");
stopwatch.stop();
expect(result, isNotNull);
});
}

View file

@ -14,7 +14,7 @@ void standaloneTests(FutureOr<QueryExecutor> Function() createExecutor,
?..familyFriendly.isTrue
..recalledAt.lessThanOrEqualTo(y2k, includeTime: false);
var whereClause = query.where?.compile(tableName: 'cars');
print('Where clause: $whereClause');
//print('Where clause: $whereClause');
expect(whereClause,
'cars.family_friendly = TRUE AND cars.recalled_at <= \'2000-01-01\'');
});
@ -23,11 +23,11 @@ void standaloneTests(FutureOr<QueryExecutor> Function() createExecutor,
// 'id', 'created_at', 'updated_at', 'make', 'description', 'family_friendly', 'recalled_at'
// var row = [0, 'Mazda', 'CX9', true, y2k, y2k, y2k];
var row = [0, y2k, y2k, 'Mazda', 'CX9', true, y2k];
print(row);
//print(row);
var carOpt = CarQuery().deserialize(row);
expect(carOpt.isPresent, true);
carOpt.ifPresent((car) {
print(car.toJson());
//print(car.toJson());
expect(car.id, '0');
expect(car.make, 'Mazda');
expect(car.description, 'CX9');
@ -42,17 +42,17 @@ void standaloneTests(FutureOr<QueryExecutor> Function() createExecutor,
});
group('queries', () {
late QueryExecutor executor;
QueryExecutor? executor;
setUp(() async {
executor = await createExecutor();
});
tearDown(() => close!(executor));
tearDown(() => close!(executor!));
group('selects', () {
test('select all', () async {
var cars = await CarQuery().get(executor);
var cars = await CarQuery().get(executor!);
expect(cars, []);
});
@ -63,19 +63,21 @@ void standaloneTests(FutureOr<QueryExecutor> Function() createExecutor,
var query = CarQuery();
query.values
..make = 'Ferrari東'
..createdAt = y2k
..updatedAt = y2k
..description = 'Vroom vroom!'
..familyFriendly = false;
ferrari = (await query.insert(executor)).value;
ferrari = (await query.insert(executor!)).value;
});
test('where clause is applied', () async {
var query = CarQuery()..where!.familyFriendly.isTrue;
var cars = await query.get(executor);
var cars = await query.get(executor!);
expect(cars, isEmpty);
var sportsCars = CarQuery()..where!.familyFriendly.isFalse;
cars = await sportsCars.get(executor);
print(cars.map((c) => c.toJson()));
cars = await sportsCars.get(executor!);
//print(cars.map((c) => c.toJson()));
var car = cars.first;
expect(car.make, ferrari!.make);
@ -89,8 +91,8 @@ void standaloneTests(FutureOr<QueryExecutor> Function() createExecutor,
var query2 = CarQuery()..where?.familyFriendly.isTrue;
var query3 = CarQuery()..where?.description.equals('Submarine');
var union = query1.union(query2).unionAll(query3);
print(union.compile({}));
var cars = await union.get(executor);
//print(union.compile({}));
var cars = await union.get(executor!);
expect(cars, hasLength(1));
});
@ -100,22 +102,22 @@ void standaloneTests(FutureOr<QueryExecutor> Function() createExecutor,
..orWhere((where) => where
..familyFriendly.isTrue
..make.equals('Honda'));
print(query.compile({}));
var cars = await query.get(executor);
//print(query.compile({}));
var cars = await query.get(executor!);
expect(cars, hasLength(1));
});
test('limit obeyed', () async {
var query = CarQuery()..limit(0);
print(query.compile({}));
var cars = await query.get(executor);
//print(query.compile({}));
var cars = await query.get(executor!);
expect(cars, isEmpty);
});
test('get one', () async {
var id = int.parse(ferrari!.id!);
var query = CarQuery()..where!.id.equals(id);
var carOpt = await query.getOne(executor);
var carOpt = await query.getOne(executor!);
expect(carOpt.isPresent, true);
carOpt.ifPresent((car) {
expect(car, ferrari);
@ -125,14 +127,14 @@ void standaloneTests(FutureOr<QueryExecutor> Function() createExecutor,
test('delete one', () async {
var id = int.parse(ferrari!.id!);
var query = CarQuery()..where!.id.equals(id);
var carOpt = await (query.deleteOne(executor));
var carOpt = await (query.deleteOne(executor!));
expect(carOpt.isPresent, true);
carOpt.ifPresent((car) {
var car = carOpt.value;
expect(car.toJson(), ferrari!.toJson());
});
var cars = await CarQuery().get(executor);
var cars = await CarQuery().get(executor!);
expect(cars, isEmpty);
});
@ -140,9 +142,9 @@ void standaloneTests(FutureOr<QueryExecutor> Function() createExecutor,
var query = CarQuery()
..where!.make.equals('Ferrari東')
..orWhere((w) => w.familyFriendly.isTrue);
print(query.compile({}, preamble: 'DELETE FROM "cars"'));
//print(query.compile({}, preamble: 'DELETE FROM "cars"'));
var cars = await query.delete(executor);
var cars = await query.delete(executor!);
expect(cars, hasLength(1));
expect(cars.first.toJson(), ferrari!.toJson());
});
@ -151,7 +153,7 @@ void standaloneTests(FutureOr<QueryExecutor> Function() createExecutor,
var query = CarQuery()
..where!.id.equals(int.parse(ferrari!.id!))
..values.make = 'Hyundai';
var cars = await query.update(executor);
var cars = await query.update(executor!);
expect(cars, hasLength(1));
expect(cars.first.make, 'Hyundai');
});
@ -159,11 +161,11 @@ void standaloneTests(FutureOr<QueryExecutor> Function() createExecutor,
test('update car', () async {
var cloned = ferrari!.copyWith(make: 'Angel');
var query = CarQuery()..values.copyFrom(cloned);
var carOpt = await (query.updateOne(executor));
var carOpt = await (query.updateOne(executor!));
expect(carOpt.isPresent, true);
carOpt.ifPresent((car) {
var car = carOpt.value;
print(car.toJson());
//print(car.toJson());
expect(car.toJson(), cloned.toJson());
});
});
@ -181,7 +183,7 @@ void standaloneTests(FutureOr<QueryExecutor> Function() createExecutor,
..recalledAt = recalledAt
..createdAt = now
..updatedAt = now;
var carOpt = await (query.insert(executor));
var carOpt = await (query.insert(executor!));
expect(carOpt.isPresent, true);
carOpt.ifPresent((car) {
var car = carOpt.value;
@ -203,10 +205,10 @@ void standaloneTests(FutureOr<QueryExecutor> Function() createExecutor,
familyFriendly: true,
recalledAt: recalledAt);
var query = CarQuery()..values.copyFrom(beetle);
var carOpt = await (query.insert(executor));
var carOpt = await (query.insert(executor!));
expect(carOpt.isPresent, true);
carOpt.ifPresent((car) {
print(car.toJson());
//print(car.toJson());
expect(car.make, beetle.make);
expect(car.description, beetle.description);
expect(car.familyFriendly, beetle.familyFriendly);

View file

@ -1,5 +1,5 @@
name: angel3_orm_test
version: 3.0.2
version: 3.0.3
description: Common tests for Angel3 ORM. Reference implmentation of the generated ORM files.
homepage: https://angel3-framework.web.app/
repository: https://github.com/dukefirehawk/angel/tree/master/packages/orm/angel_orm_test

View file

@ -0,0 +1,149 @@
CREATE TABLE "authors" (
id serial PRIMARY KEY,
name varchar(255) UNIQUE NOT NULL,
created_at timestamp,
updated_at timestamp
);
CREATE TABLE "books" (
id serial PRIMARY KEY,
author_id int NOT NULL,
partner_author_id int,
name varchar(255),
created_at timestamp,
updated_at timestamp
);
CREATE TABLE "cars" (
id serial PRIMARY KEY,
make varchar(255) NOT NULL,
description TEXT NOT NULL,
family_friendly BOOLEAN NOT NULL,
recalled_at timestamp,
created_at timestamp,
updated_at timestamp
);
CREATE TABLE "numbers" (
id serial PRIMARY KEY,
created_at timestamp,
updated_at timestamp
);
CREATE TABLE "alphabets" (
id serial PRIMARY KEY,
value TEXT,
numbers_id int,
created_at timestamp,
updated_at timestamp
);
CREATE TABLE "feet" (
id serial PRIMARY KEY,
leg_id int NOT NULL,
n_toes int NOT NULL,
created_at timestamp,
updated_at timestamp
);
CREATE TABLE "fruits" (
"id" serial,
"tree_id" int,
"common_name" varchar,
"created_at" timestamp,
"updated_at" timestamp,
PRIMARY KEY(id)
);
CREATE TABLE "has_cars" (
id serial PRIMARY KEY,
type int not null,
created_at timestamp,
updated_at timestamp
);
CREATE TABLE "has_maps" (
id serial PRIMARY KEY,
value jsonb not null,
list jsonb not null,
created_at timestamp,
updated_at timestamp
);
CREATE TABLE "legs" (
id serial PRIMARY KEY,
name varchar(255) NOT NULL,
created_at timestamp,
updated_at timestamp
);
CREATE TABLE "roles" (
"id" serial PRIMARY KEY,
"name" varchar(255),
"created_at" timestamp,
"updated_at" timestamp
);
CREATE TABLE "trees" (
"id" serial,
"rings" smallint UNIQUE,
"created_at" timestamp,
"updated_at" timestamp,
UNIQUE(rings),
PRIMARY KEY(id)
);
CREATE TABLE "unorthodoxes" (
"name" varchar(255),
PRIMARY KEY(name)
);
CREATE TABLE "users" (
"id" serial PRIMARY KEY,
"username" varchar(255),
"password" varchar(255),
"email" varchar(255),
"created_at" timestamp,
"updated_at" timestamp
);
CREATE TABLE "role_users" (
"id" serial PRIMARY KEY,
"user_id" int NOT NULL,
"role_id" int NOT NULL,
"created_at" timestamp,
"updated_at" timestamp
);
CREATE TABLE "foos" (
"bar" varchar(255),
PRIMARY KEY(bar)
);
CREATE TABLE "weird_joins" (
"id" serial,
"join_name" varchar(255) references unorthodoxes(name),
PRIMARY KEY(id)
);
CREATE TABLE "songs" (
"id" serial,
"weird_join_id" int references weird_joins(id),
"title" varchar(255),
created_at TIMESTAMP,
updated_at TIMESTAMP,
PRIMARY KEY(id)
);
CREATE TABLE "numbas" (
"i" int,
"parent" int references weird_joins(id),
created_at TIMESTAMP,
updated_at TIMESTAMP,
PRIMARY KEY(i)
);
CREATE TABLE "foo_pivots" (
"weird_join_id" int references weird_joins(id),
"foo_bar" varchar(255) references foos(bar)
);

0
tool/move_repos → tool/archived/move_repos Executable file → Normal file
View file

0
tool/pull_subproject → tool/archived/pull_subproject Executable file → Normal file
View file

View file

@ -0,0 +1,6 @@
Primary Authors
===============
* __[Thomas Hii](dukefirehawk.apps@gmail.com)__
The current main maintainer of the code base.

29
tool/performance/LICENSE Normal file
View file

@ -0,0 +1,29 @@
BSD 3-Clause License
Copyright (c) 2021, dukefirehawk.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View file

@ -0,0 +1 @@
include: package:lints/recommended.yaml

View file

@ -0,0 +1,11 @@
name: performance_tool
version: 1.0.0
description: Angel3 performance testing tool
publish_to: none
environment:
sdk: '>=2.12.0 <3.0.0'
published_to: none
dependencies:
http: ^0.13.4
dev_dependencies:
lints: ^1.0.0

View file

@ -0,0 +1,104 @@
import 'dart:isolate';
import 'package:http/http.dart' as http;
Future<void> fortunes(var message) async {
var stopwatch = Stopwatch()..start();
var url = Uri.http('localhost:3000', '/fortunes');
var response = await http.get(url);
print('Execution($message) Time: ${stopwatch.elapsed.inMilliseconds}ms');
stopwatch.stop();
if (response.statusCode == 200) {
print('Execution($message): success');
} else {
print('Execution($message): error');
}
}
Future<void> plaintext(var message) async {
var stopwatch = Stopwatch()..start();
var url = Uri.http('localhost:3000', '/plaintext');
var response = await http.get(url);
if (response.statusCode == 200) {
print('Execution($message): success');
} else {
print('Execution($message): error');
}
print('Execution($message) Time: ${stopwatch.elapsed.inMilliseconds}ms');
stopwatch.stop();
}
Future<void> json(var message) async {
var stopwatch = Stopwatch()..start();
var url = Uri.http('localhost:3000', '/json');
var response = await http.get(url);
print('Execution($message) Time: ${stopwatch.elapsed.inMilliseconds}ms');
stopwatch.stop();
if (response.statusCode == 200) {
print('Execution($message): success');
} else {
print('Execution($message): error');
}
}
Future<void> dbUpdate(var message) async {
var stopwatch = Stopwatch()..start();
var url = Uri.http('localhost:3000', '/updates', {'queries': "5"});
var response = await http.get(url);
print('Execution($message) Time: ${stopwatch.elapsed.inMilliseconds}ms');
stopwatch.stop();
if (response.statusCode == 200) {
print('Execution($message): success');
} else {
print('Execution($message): error');
}
}
Future<void> dbSingleQuery(var message) async {
var stopwatch = Stopwatch()..start();
var url = Uri.http('localhost:3000', '/db');
var response = await http.get(url);
print('Execution($message) Time: ${stopwatch.elapsed.inMilliseconds}ms');
stopwatch.stop();
if (response.statusCode == 200) {
print('Execution($message): success');
} else {
print('Execution($message): error');
}
}
Future<void> dbMultipleQuery(var message) async {
var stopwatch = Stopwatch()..start();
var url = Uri.http('localhost:3000', '/query', {'queries': "5"});
var response = await http.get(url);
print('Execution($message) Time: ${stopwatch.elapsed.inMilliseconds}ms');
stopwatch.stop();
if (response.statusCode == 200) {
print('Execution($message): success');
} else {
print('Execution($message): error');
}
}
void main() async {
var concurrency = 100;
for (var i = 0; i < concurrency; i++) {
Isolate.spawn(dbUpdate, 'Instance_$i');
}
await Future.delayed(const Duration(seconds: 10));
//print("Exit");
}

View file

@ -0,0 +1,19 @@
### JSON Test
GET http://localhost:3000/json HTTP/1.1
### Plaintext Test
GET http://localhost:3000/plaintext HTTP/1.1
### Fortunes Test
GET http://localhost:3000/fortunes HTTP/1.1
### Db Test
GET http://localhost:3000/db HTTP/1.1
### Query test
GET http://localhost:3000/query?queries=20 HTTP/1.1
### Update Test
GET http://localhost:3000/updates?queries=20 HTTP/1.1