Merge pull request #34 from dukefirehawk/feature/update-orm
Update ORM for Postgresql
This commit is contained in:
commit
342bdb322e
48 changed files with 1313 additions and 392 deletions
|
@ -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-----
|
|
@ -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-----
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
|
@ -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.
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 = '''
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
# Change Log
|
||||
|
||||
## 4.0.3
|
||||
|
||||
* Removed debugging messages
|
||||
|
||||
## 4.0.2
|
||||
|
||||
* Updated linter to `package:lints`
|
||||
|
|
|
@ -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';
|
||||
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
# Change Log
|
||||
|
||||
## 3.2.0
|
||||
|
||||
* Added `package:postgres_pool` for connection pooling
|
||||
|
||||
## 3.1.0
|
||||
|
||||
* Updated linter to `package:lints`
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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';
|
||||
|
|
169
packages/orm/angel_orm_postgres/lib/src/orm_postgres.dart
Normal file
169
packages/orm/angel_orm_postgres/lib/src/orm_postgres.dart
Normal 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);
|
||||
});
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
);
|
|
@ -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`
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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]);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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');
|
||||
}
|
||||
|
||||
|
|
16
packages/orm/angel_orm_test/lib/src/models/fortune.dart
Normal file
16
packages/orm/angel_orm_test/lib/src/models/fortune.dart
Normal 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;
|
||||
}
|
200
packages/orm/angel_orm_test/lib/src/models/fortune.g.dart
Normal file
200
packages/orm/angel_orm_test/lib/src/models/fortune.g.dart
Normal 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';
|
||||
}
|
16
packages/orm/angel_orm_test/lib/src/models/world.dart
Normal file
16
packages/orm/angel_orm_test/lib/src/models/world.dart
Normal 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;
|
||||
}
|
203
packages/orm/angel_orm_test/lib/src/models/world.g.dart
Normal file
203
packages/orm/angel_orm_test/lib/src/models/world.g.dart
Normal 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';
|
||||
}
|
91
packages/orm/angel_orm_test/lib/src/performance_test.dart
Normal file
91
packages/orm/angel_orm_test/lib/src/performance_test.dart
Normal 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);
|
||||
});
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
149
packages/orm/angel_orm_test/scripts/create_tables.sql
Normal file
149
packages/orm/angel_orm_test/scripts/create_tables.sql
Normal 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
0
tool/move_repos → tool/archived/move_repos
Executable file → Normal file
0
tool/pull_subproject → tool/archived/pull_subproject
Executable file → Normal file
0
tool/pull_subproject → tool/archived/pull_subproject
Executable file → Normal file
6
tool/performance/AUTHORS.md
Normal file
6
tool/performance/AUTHORS.md
Normal 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
29
tool/performance/LICENSE
Normal 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.
|
1
tool/performance/analysis_options.yaml
Normal file
1
tool/performance/analysis_options.yaml
Normal file
|
@ -0,0 +1 @@
|
|||
include: package:lints/recommended.yaml
|
11
tool/performance/pubspec.yaml
Normal file
11
tool/performance/pubspec.yaml
Normal 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
|
104
tool/performance/techempower/main.dart
Normal file
104
tool/performance/techempower/main.dart
Normal 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");
|
||||
}
|
19
tool/performance/tests/techempower.http
Normal file
19
tool/performance/tests/techempower.http
Normal 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
|
||||
|
||||
|
Loading…
Reference in a new issue