build: update CircleCI config & repair failing tests (#10590)

* updated CircleCI config

* fixed failing test

* fixed failing test

* fixed failing test

* fixed failing test

* fixed CockroachDB enum sync;
removed redundant await;
fixed failing tests;

* fixing failing tests

* fixing failing tests

* added check for empty commands

* fixed failing tests

* fixed failing tests

* fixed failing test
This commit is contained in:
Dmitry Zotov 2024-01-03 13:32:17 +05:00 committed by GitHub
parent b5ec0889f7
commit 15bc8876f8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
35 changed files with 546 additions and 479 deletions

View File

@ -53,26 +53,6 @@ commands:
npm install
npm install oracledb
fi
- run:
# This is pretty terrible but OracleDB requires you to grab the binaries OOB
# from the normal installation, place them in the LD Path
# also - not super well documented - grab `libaio` as well
# Because this is technically the same image as the runner we'll snag
# the libaio1 and place them in the same instantclient directory.
name: Download Required OracleDB Binaries
command: |
if [ ! -d node_modules/oracledb/instantclient_19_8 ]; then
curl -sf -o node_modules/oracledb/instantclient.zip $BLOB_URL
unzip -qqo node_modules/oracledb/instantclient.zip -d node_modules/oracledb/
rm node_modules/oracledb/instantclient.zip
sudo apt-get update -qq && sudo apt-get -qq -y install libaio1
(cp /lib/*/libaio.so.* node_modules/oracledb/instantclient_19_8/ ||
cp /usr/lib/*/libaio.so.* node_modules/oracledb/instantclient_19_8/)
fi
environment:
BLOB_URL: https://download.oracle.com/otn_software/linux/instantclient/19800/instantclient-basiclite-linux.x64-19.8.0.0.0dbru.zip
DEBIAN_FRONTEND: noninteractive
- save_cache:
name: Save node_modules cache
key: node_modules-{{ checksum "package-lock.json" }}
@ -159,11 +139,13 @@ jobs:
)
echo "Running '$COMMANDS'"
docker run \
--network typeorm_default \
--tty \
ubuntu:trusty \
timeout 60 sh -c "until ($COMMANDS); do echo \"Waiting for Services to be Available ...\"; sleep 5; done"
if [ ! -z "$COMMANDS" ]; then
docker run \
--network typeorm_default \
--tty \
ubuntu:trusty \
timeout 60 sh -c "until ($COMMANDS); do echo \"Waiting for Services to be Available ...\"; sleep 5; done"
fi
- run:
name: "Wait for OracleDB to be Available"
command: |
@ -189,7 +171,6 @@ jobs:
command: |
docker run \
--env npm_config_yes='true' \
--env LD_LIBRARY_PATH='/typeorm/node_modules/oracledb/instantclient_19_8/:$LD_LIBRARY_PATH' \
--volumes-from typeorm-code \
--network typeorm_default \
--tty \
@ -213,21 +194,11 @@ workflows:
- lint
- build
- test:
name: test (mysql mariadb postgres mongodb sqlite better-sqlite3 sqljs) - Node v<< matrix.node-version >>
name: test (postgres) - Node v16
requires:
- lint
- build
databases: "mysql mariadb postgres mongodb sqlite better-sqlite3 sqljs"
matrix:
parameters:
node-version:
- "16.20.0"
- test:
name: test (mssql) - Node v16
requires:
- lint
- build
databases: "mssql"
databases: "postgres"
node-version: "16.20.0"
- test:
name: test (cockroachdb) - Node v16
@ -236,6 +207,20 @@ workflows:
- build
databases: "cockroachdb"
node-version: "16.20.0"
- test:
name: test (mysql mariadb) - Node v16
requires:
- lint
- build
databases: "mysql mariadb"
node-version: "16.20.0"
- test:
name: test (mssql) - Node v16
requires:
- lint
- build
databases: "mssql"
node-version: "16.20.0"
- test:
name: test (oracle) - Node v16
requires:
@ -244,9 +229,16 @@ workflows:
databases: "oracle"
node-version: "16.20.0"
- test:
name: test (postgres 12) - Node v16
name: test (sqlite better-sqlite3 sqljs) - Node v16
requires:
- lint
- build
databases: "postgres-12"
databases: "sqlite better-sqlite3 sqljs"
node-version: "16.20.0"
- test:
name: test (mongodb) - Node v16
requires:
- lint
- build
databases: "mongodb"
node-version: "16.20.0"

View File

@ -39,19 +39,6 @@ services:
POSTGRES_PASSWORD: "test"
POSTGRES_DB: "test"
postgres-12:
# mdillon/postgis is postgres + PostGIS (only). if you need additional
# extensions, it's probably time to create a purpose-built image with all
# necessary extensions. sorry, and thanks for adding support for them!
image: "postgis/postgis:12-2.5"
container_name: "typeorm-postgres-12"
ports:
- "5532:5432"
environment:
POSTGRES_USER: "test"
POSTGRES_PASSWORD: "test"
POSTGRES_DB: "test"
# mssql
mssql:
image: "mcr.microsoft.com/mssql/server:2022-latest"

View File

@ -22,7 +22,7 @@
"logging": false
},
{
"skip": false,
"skip": true,
"name": "sqlite",
"type": "sqlite",
"database": "./temp/sqlitedb-1.db",
@ -30,7 +30,7 @@
"relationLoadStrategy": "join"
},
{
"skip": false,
"skip": true,
"name": "sqlite-2",
"type": "sqlite",
"database": "./temp/sqlitedb-2.db",
@ -57,17 +57,6 @@
},
{
"skip": true,
"name": "postgres-12",
"type": "postgres",
"host": "typeorm-postgres-12",
"port": 5432,
"username": "test",
"password": "test",
"database": "test",
"logging": false
},
{
"skip": false,
"name": "sqljs",
"type": "sqljs",
"logging": false

320
package-lock.json generated
View File

@ -256,23 +256,22 @@
}
},
"node_modules/@azure/core-rest-pipeline": {
"version": "1.12.1",
"resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.12.1.tgz",
"integrity": "sha512-SsyWQ+T5MFQRX+M8H/66AlaI6HyCbQStGfFngx2fuiW+vKI2DkhtOvbYodPyf9fOe/ARLWWc3ohX54lQ5Kmaog==",
"version": "1.13.0",
"resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.13.0.tgz",
"integrity": "sha512-a62aP/wppgmnfIkJLfcB4ssPBcH94WzrzPVJ3tlJt050zX4lfmtnvy95D3igDo3f31StO+9BgPrzvkj4aOxnoA==",
"dev": true,
"dependencies": {
"@azure/abort-controller": "^1.0.0",
"@azure/abort-controller": "^1.1.0",
"@azure/core-auth": "^1.4.0",
"@azure/core-tracing": "^1.0.1",
"@azure/core-util": "^1.3.0",
"@azure/logger": "^1.0.0",
"form-data": "^4.0.0",
"http-proxy-agent": "^5.0.0",
"https-proxy-agent": "^5.0.0",
"tslib": "^2.2.0"
},
"engines": {
"node": ">=14.0.0"
"node": ">=18.0.0"
}
},
"node_modules/@azure/core-rest-pipeline/node_modules/@tootallnate/once": {
@ -311,52 +310,41 @@
}
},
"node_modules/@azure/core-util": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.4.0.tgz",
"integrity": "sha512-eGAyJpm3skVQoLiRqm/xPa+SXi/NPDdSHMxbRAz2lSprd+Zs+qrpQGQQ2VQ3Nttu+nSZR4XoYQC71LbEI7jsig==",
"version": "1.6.1",
"resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.6.1.tgz",
"integrity": "sha512-h5taHeySlsV9qxuK64KZxy4iln1BtMYlNt5jbuEFN3UFSAd1EwKg/Gjl5a6tZ/W8t6li3xPnutOx7zbDyXnPmQ==",
"dev": true,
"dependencies": {
"@azure/abort-controller": "^1.0.0",
"tslib": "^2.2.0"
},
"engines": {
"node": ">=14.0.0"
"node": ">=16.0.0"
}
},
"node_modules/@azure/identity": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@azure/identity/-/identity-2.1.0.tgz",
"integrity": "sha512-BPDz1sK7Ul9t0l9YKLEa8PHqWU4iCfhGJ+ELJl6c8CP3TpJt2urNCbm0ZHsthmxRsYoMPbz2Dvzj30zXZVmAFw==",
"version": "3.4.1",
"resolved": "https://registry.npmjs.org/@azure/identity/-/identity-3.4.1.tgz",
"integrity": "sha512-oQ/r5MBdfZTMIUcY5Ch8G7Vv9aIXDkEYyU4Dfqjim4MQN+LY2uiQ57P1JDopMLeHCsZxM4yy8lEdne3tM9Xhzg==",
"dev": true,
"dependencies": {
"@azure/abort-controller": "^1.0.0",
"@azure/core-auth": "^1.3.0",
"@azure/core-auth": "^1.5.0",
"@azure/core-client": "^1.4.0",
"@azure/core-rest-pipeline": "^1.1.0",
"@azure/core-tracing": "^1.0.0",
"@azure/core-util": "^1.0.0",
"@azure/core-util": "^1.6.1",
"@azure/logger": "^1.0.0",
"@azure/msal-browser": "^2.26.0",
"@azure/msal-common": "^7.0.0",
"@azure/msal-node": "^1.10.0",
"@azure/msal-browser": "^3.5.0",
"@azure/msal-node": "^2.5.1",
"events": "^3.0.0",
"jws": "^4.0.0",
"open": "^8.0.0",
"stoppable": "^1.1.0",
"tslib": "^2.2.0",
"uuid": "^8.3.0"
"tslib": "^2.2.0"
},
"engines": {
"node": ">=12.0.0"
}
},
"node_modules/@azure/identity/node_modules/uuid": {
"version": "8.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
"dev": true,
"bin": {
"uuid": "dist/bin/uuid"
"node": ">=14.0.0"
}
},
"node_modules/@azure/keyvault-keys": {
@ -394,56 +382,38 @@
}
},
"node_modules/@azure/msal-browser": {
"version": "2.38.2",
"resolved": "https://registry.npmjs.org/@azure/msal-browser/-/msal-browser-2.38.2.tgz",
"integrity": "sha512-71BeIn2we6LIgMplwCSaMq5zAwmalyJR3jFcVOZxNVfQ1saBRwOD+P77nLs5vrRCedVKTq8RMFhIOdpMLNno0A==",
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/@azure/msal-browser/-/msal-browser-3.6.0.tgz",
"integrity": "sha512-FrFBJXRJMyWXjAjg4cUNZwEKktzfzD/YD9+S1kj2ors67hKoveam4aL0bZuCZU/jTiHTn0xDQGQh2ksCMXTXtA==",
"dev": true,
"dependencies": {
"@azure/msal-common": "13.3.0"
"@azure/msal-common": "14.5.0"
},
"engines": {
"node": ">=0.8.0"
}
},
"node_modules/@azure/msal-browser/node_modules/@azure/msal-common": {
"version": "13.3.0",
"resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-13.3.0.tgz",
"integrity": "sha512-/VFWTicjcJbrGp3yQP7A24xU95NiDMe23vxIU1U6qdRPFsprMDNUohMudclnd+WSHE4/McqkZs/nUU3sAKkVjg==",
"dev": true,
"engines": {
"node": ">=0.8.0"
}
},
"node_modules/@azure/msal-common": {
"version": "7.6.0",
"resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-7.6.0.tgz",
"integrity": "sha512-XqfbglUTVLdkHQ8F9UQJtKseRr3sSnr9ysboxtoswvaMVaEfvyLtMoHv9XdKUfOc0qKGzNgRFd9yRjIWVepl6Q==",
"version": "14.5.0",
"resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-14.5.0.tgz",
"integrity": "sha512-Gx5rZbiZV/HiZ2nEKfjfAF/qDdZ4/QWxMvMo2jhIFVz528dVKtaZyFAOtsX2Ak8+TQvRsGCaEfuwJFuXB6tu1A==",
"dev": true,
"engines": {
"node": ">=0.8.0"
}
},
"node_modules/@azure/msal-node": {
"version": "1.18.3",
"resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-1.18.3.tgz",
"integrity": "sha512-lI1OsxNbS/gxRD4548Wyj22Dk8kS7eGMwD9GlBZvQmFV8FJUXoXySL1BiNzDsHUE96/DS/DHmA+F73p1Dkcktg==",
"version": "2.6.0",
"resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-2.6.0.tgz",
"integrity": "sha512-RWAWCYYrSldIYC47oWtofIun41e6SB9TBYgGYsezq6ednagwo9ZRFyRsvl1NabmdTkdDDXRAABIdveeN2Gtd8w==",
"dev": true,
"dependencies": {
"@azure/msal-common": "13.3.0",
"@azure/msal-common": "14.5.0",
"jsonwebtoken": "^9.0.0",
"uuid": "^8.3.0"
},
"engines": {
"node": "10 || 12 || 14 || 16 || 18"
}
},
"node_modules/@azure/msal-node/node_modules/@azure/msal-common": {
"version": "13.3.0",
"resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-13.3.0.tgz",
"integrity": "sha512-/VFWTicjcJbrGp3yQP7A24xU95NiDMe23vxIU1U6qdRPFsprMDNUohMudclnd+WSHE4/McqkZs/nUU3sAKkVjg==",
"dev": true,
"engines": {
"node": ">=0.8.0"
"node": "16|| 18 || 20"
}
},
"node_modules/@azure/msal-node/node_modules/uuid": {
@ -809,9 +779,9 @@
}
},
"node_modules/@js-joda/core": {
"version": "5.5.3",
"resolved": "https://registry.npmjs.org/@js-joda/core/-/core-5.5.3.tgz",
"integrity": "sha512-7dqNYwG8gCt4hfg5PKgM7xLEcgSBcx/UgC92OMnhMmvAnq11QzDFPrxUkNR/u5kn17WWLZ8beZ4A3Qrz4pZcmQ==",
"version": "5.6.1",
"resolved": "https://registry.npmjs.org/@js-joda/core/-/core-5.6.1.tgz",
"integrity": "sha512-Xla/d7ZMMR6+zRd6lTio0wRZECfcfFJP7GGe9A9L4tDOlD5CX4YcZ4YZle9w58bBYzssojVapI84RraKWDQZRg==",
"dev": true
},
"node_modules/@mapbox/node-pre-gyp": {
@ -2565,12 +2535,6 @@
"node": ">= 0.10"
}
},
"node_modules/asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
"dev": true
},
"node_modules/atob": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz",
@ -2767,9 +2731,9 @@
}
},
"node_modules/bl": {
"version": "6.0.7",
"resolved": "https://registry.npmjs.org/bl/-/bl-6.0.7.tgz",
"integrity": "sha512-9FNh0IvlWSU5C9BCDhw0IovmhuqevzBX1AME7BdFHNDMfOju4NmwRWoBrfz5Srs+JNBhxfjrPLxZSnDotgSs9A==",
"version": "6.0.9",
"resolved": "https://registry.npmjs.org/bl/-/bl-6.0.9.tgz",
"integrity": "sha512-Vh+M9HMfeTST9rkkQ1utRnOeABNcBO3i0dJMFkenCv7JIp76XWx8uQOGpaXyXVyenrLDZsdAHXbf0Cz18Eb0fw==",
"dev": true,
"dependencies": {
"buffer": "^6.0.3",
@ -2778,9 +2742,9 @@
}
},
"node_modules/bl/node_modules/readable-stream": {
"version": "4.4.2",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.4.2.tgz",
"integrity": "sha512-Lk/fICSyIhodxy1IDK2HazkeGjSmezAWX2egdtJnYhtzKEsBPJowlI6F6LPb5tqIQILrMbx22S5o3GuJavPusA==",
"version": "4.5.2",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz",
"integrity": "sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==",
"dev": true,
"dependencies": {
"abort-controller": "^3.0.0",
@ -3027,13 +2991,14 @@
}
},
"node_modules/call-bind": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
"integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz",
"integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==",
"dev": true,
"dependencies": {
"function-bind": "^1.1.1",
"get-intrinsic": "^1.0.2"
"function-bind": "^1.1.2",
"get-intrinsic": "^1.2.1",
"set-function-length": "^1.1.1"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
@ -3394,22 +3359,10 @@
"color-support": "bin.js"
}
},
"node_modules/combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"dev": true,
"dependencies": {
"delayed-stream": "~1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/commander": {
"version": "11.0.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-11.0.0.tgz",
"integrity": "sha512-9HMlXtt/BNoYr8ooyjjNRdIilOTkVJXB+GhxMTtOKwk0R4j4lS4NpjuqmRxroBfnfTSHQIHQB7wryHhXarNjmQ==",
"version": "11.1.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz",
"integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==",
"dev": true,
"engines": {
"node": ">=16"
@ -3810,9 +3763,9 @@
}
},
"node_modules/dayjs": {
"version": "1.11.9",
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.9.tgz",
"integrity": "sha512-QvzAURSbQ0pKdIye2txOzNaHmxtUBXerpY0FJsFXUMKbIZeFm5ht1LS/jFsrncjnmtv8HsG0W2g6c0zUjZWmpA=="
"version": "1.11.10",
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.10.tgz",
"integrity": "sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ=="
},
"node_modules/debug": {
"version": "4.3.4",
@ -3957,9 +3910,9 @@
}
},
"node_modules/define-data-property": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.0.tgz",
"integrity": "sha512-UzGwzcjyv3OtAvolTj1GoyNYzfFR+iqbGjcnBEENZVCpM4/Ng1yhGNvS3lR/xDS74Tb2wGG9WzNSNIOS9UVb2g==",
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz",
"integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==",
"dev": true,
"dependencies": {
"get-intrinsic": "^1.2.1",
@ -4087,15 +4040,6 @@
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
"dev": true,
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/delegates": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
@ -4286,26 +4230,26 @@
}
},
"node_modules/es-abstract": {
"version": "1.22.2",
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.2.tgz",
"integrity": "sha512-YoxfFcDmhjOgWPWsV13+2RNjq1F6UQnfs+8TftwNqtzlmFzEXvlUwdrNrYeaizfjQzRMxkZ6ElWMOJIFKdVqwA==",
"version": "1.22.3",
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.3.tgz",
"integrity": "sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA==",
"dev": true,
"dependencies": {
"array-buffer-byte-length": "^1.0.0",
"arraybuffer.prototype.slice": "^1.0.2",
"available-typed-arrays": "^1.0.5",
"call-bind": "^1.0.2",
"call-bind": "^1.0.5",
"es-set-tostringtag": "^2.0.1",
"es-to-primitive": "^1.2.1",
"function.prototype.name": "^1.1.6",
"get-intrinsic": "^1.2.1",
"get-intrinsic": "^1.2.2",
"get-symbol-description": "^1.0.0",
"globalthis": "^1.0.3",
"gopd": "^1.0.1",
"has": "^1.0.3",
"has-property-descriptors": "^1.0.0",
"has-proto": "^1.0.1",
"has-symbols": "^1.0.3",
"hasown": "^2.0.0",
"internal-slot": "^1.0.5",
"is-array-buffer": "^3.0.2",
"is-callable": "^1.2.7",
@ -4315,7 +4259,7 @@
"is-string": "^1.0.7",
"is-typed-array": "^1.1.12",
"is-weakref": "^1.0.2",
"object-inspect": "^1.12.3",
"object-inspect": "^1.13.1",
"object-keys": "^1.1.1",
"object.assign": "^4.1.4",
"regexp.prototype.flags": "^1.5.1",
@ -4329,7 +4273,7 @@
"typed-array-byte-offset": "^1.0.0",
"typed-array-length": "^1.0.4",
"unbox-primitive": "^1.0.2",
"which-typed-array": "^1.1.11"
"which-typed-array": "^1.1.13"
},
"engines": {
"node": ">= 0.4"
@ -4361,14 +4305,14 @@
}
},
"node_modules/es-set-tostringtag": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz",
"integrity": "sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==",
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.2.tgz",
"integrity": "sha512-BuDyupZt65P9D2D2vA/zqcI3G5xRsklm5N3xCwuiy+/vKy8i0ifdsQP1sLgO4tZDSCaQUSnmC48khknGMV3D2Q==",
"dev": true,
"dependencies": {
"get-intrinsic": "^1.1.3",
"has": "^1.0.3",
"has-tostringtag": "^1.0.0"
"get-intrinsic": "^1.2.2",
"has-tostringtag": "^1.0.0",
"hasown": "^2.0.0"
},
"engines": {
"node": ">= 0.4"
@ -5404,20 +5348,6 @@
"node": ">=0.10.0"
}
},
"node_modules/form-data": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
"dev": true,
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"mime-types": "^2.1.12"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/fragment-cache": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz",
@ -5496,10 +5426,13 @@
}
},
"node_modules/function-bind": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
"dev": true
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
"dev": true,
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/function.prototype.name": {
"version": "1.1.6",
@ -5584,15 +5517,15 @@
}
},
"node_modules/get-intrinsic": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz",
"integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==",
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz",
"integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==",
"dev": true,
"dependencies": {
"function-bind": "^1.1.1",
"has": "^1.0.3",
"function-bind": "^1.1.2",
"has-proto": "^1.0.1",
"has-symbols": "^1.0.3"
"has-symbols": "^1.0.3",
"hasown": "^2.0.0"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
@ -7328,15 +7261,16 @@
"node": ">=0.10.0"
}
},
"node_modules/hdb-pool": {
"version": "0.1.6",
"resolved": "https://registry.npmjs.org/hdb-pool/-/hdb-pool-0.1.6.tgz",
"integrity": "sha512-8VZOLn1EHamm1NmTFQj2iqjVcfonYIsD7F5DU2bz2N+gF+knp6/MbAVeRXkJtya717IBkPeA5iv0/1iPuYo4ZA==",
"deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.",
"optional": true,
"peer": true,
"node_modules/hasown": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz",
"integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==",
"dev": true,
"dependencies": {
"function-bind": "^1.1.2"
},
"engines": {
"node": ">= 8"
"node": ">= 0.4"
}
},
"node_modules/he": {
@ -7551,13 +7485,13 @@
"dev": true
},
"node_modules/internal-slot": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz",
"integrity": "sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==",
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.6.tgz",
"integrity": "sha512-Xj6dv+PsbtwyPpEflsejS+oIZxmMlV44zAhG479uYu89MsjcYOhCFnNyKrkJrihbsiasQyY0afoCl/9BLR65bg==",
"dev": true,
"dependencies": {
"get-intrinsic": "^1.2.0",
"has": "^1.0.3",
"get-intrinsic": "^1.2.2",
"hasown": "^2.0.0",
"side-channel": "^1.0.4"
},
"engines": {
@ -9375,27 +9309,6 @@
"node": ">=8.0"
}
},
"node_modules/mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
"dev": true,
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mime-types": {
"version": "2.1.35",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"dev": true,
"dependencies": {
"mime-db": "1.52.0"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mimic-fn": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
@ -10735,9 +10648,9 @@
}
},
"node_modules/object-inspect": {
"version": "1.12.3",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz",
"integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==",
"version": "1.13.1",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz",
"integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==",
"dev": true,
"funding": {
"url": "https://github.com/sponsors/ljharb"
@ -11549,9 +11462,9 @@
}
},
"node_modules/punycode": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz",
"integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==",
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
"integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
"dev": true,
"engines": {
"node": ">=6"
@ -12534,6 +12447,21 @@
"integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==",
"dev": true
},
"node_modules/set-function-length": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz",
"integrity": "sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==",
"dev": true,
"dependencies": {
"define-data-property": "^1.1.1",
"get-intrinsic": "^1.2.1",
"gopd": "^1.0.1",
"has-property-descriptors": "^1.0.0"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/set-function-name": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.1.tgz",
@ -13476,12 +13404,12 @@
}
},
"node_modules/tedious": {
"version": "16.4.0",
"resolved": "https://registry.npmjs.org/tedious/-/tedious-16.4.0.tgz",
"integrity": "sha512-WUWtO18n43GnKI367lVEtmbBxAaTIpTONuZ87sTEMMUcQ9gy5D9H6TCHBKNz/6yYIKnCfjE9wgAc2dR4qiDiaA==",
"version": "16.6.1",
"resolved": "https://registry.npmjs.org/tedious/-/tedious-16.6.1.tgz",
"integrity": "sha512-KKSDB1OPrPk0WbMPug9YqRbPl44zMjdL2hFyzLEidr2IkItzpV0ZbzW8VA47QIS2oyWhCU7ifIEQY12n23IRDA==",
"dev": true,
"dependencies": {
"@azure/identity": "^2.0.4",
"@azure/identity": "^3.4.1",
"@azure/keyvault-keys": "^4.4.0",
"@js-joda/core": "^5.5.3",
"bl": "^6.0.3",
@ -14489,13 +14417,13 @@
"dev": true
},
"node_modules/which-typed-array": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.11.tgz",
"integrity": "sha512-qe9UWWpkeG5yzZ0tNYxDmd7vo58HDBc39mZ0xWWpolAGADdFOzkfamWLDxkOWcvHQKVmdTyQdLD4NOfjLWTKew==",
"version": "1.1.13",
"resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.13.tgz",
"integrity": "sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow==",
"dev": true,
"dependencies": {
"available-typed-arrays": "^1.0.5",
"call-bind": "^1.0.2",
"call-bind": "^1.0.4",
"for-each": "^0.3.3",
"gopd": "^1.0.1",
"has-tostringtag": "^1.0.0"

View File

@ -102,9 +102,7 @@ dataSource.initialize().then(
.where("post.id=:id", { id: post.id })
.getOne()
})
.then((loadedPost) => {
console.log(loadedPost)
console.log("Finally bakhrom's post:")
.then(() => {
post.author = author2
return postRepository.save(post)
})

View File

@ -700,12 +700,15 @@ export class CockroachDriver implements Driver {
normalizeDefault(columnMetadata: ColumnMetadata): string | undefined {
const defaultValue = columnMetadata.default
if (defaultValue === undefined || defaultValue === null) {
return undefined
}
if (
(columnMetadata.type === "enum" ||
columnMetadata.type === "simple-enum") &&
defaultValue !== undefined
) {
if (defaultValue === null) return "NULL"
if (columnMetadata.isArray) {
const enumName = this.buildEnumName(columnMetadata)
let arrayValue = defaultValue
@ -754,10 +757,6 @@ export class CockroachDriver implements Driver {
return `'${JSON.stringify(defaultValue)}'`
}
if (defaultValue === undefined || defaultValue === null) {
return undefined
}
return `${defaultValue}`
}
@ -884,10 +883,19 @@ export class CockroachDriver implements Driver {
)
if (!tableColumn) return false // we don't need new columns, we only need exist and changed
// console.log("table:", columnMetadata.entityMetadata.tableName);
// console.log("name:", tableColumn.name, columnMetadata.databaseName);
// console.log("type:", tableColumn.type, this.normalizeType(columnMetadata));
// console.log("length:", tableColumn.length, columnMetadata.length);
// console.log("table:", columnMetadata.entityMetadata.tableName)
// console.log("name:", {
// tableColumn: tableColumn.name,
// columnMetadata: columnMetadata.databaseName,
// })
// console.log("type:", {
// tableColumn: tableColumn.type,
// columnMetadata: this.normalizeType(columnMetadata),
// })
// console.log("length:", {
// tableColumn: tableColumn.length,
// columnMetadata: columnMetadata.length,
// })
// console.log("width:", tableColumn.width, columnMetadata.width);
// console.log("precision:", tableColumn.precision, columnMetadata.precision);
// console.log("scale:", tableColumn.scale, columnMetadata.scale);
@ -897,7 +905,10 @@ export class CockroachDriver implements Driver {
// console.log("isPrimary:", tableColumn.isPrimary, columnMetadata.isPrimary);
// console.log("isNullable:", tableColumn.isNullable, columnMetadata.isNullable);
// console.log("isUnique:", tableColumn.isUnique, this.normalizeIsUnique(columnMetadata));
// console.log("isGenerated:", tableColumn.isGenerated, columnMetadata.isGenerated);
// console.log("asExpression:", {
// tableColumn: (tableColumn.asExpression || "").trim(),
// columnMetadata: (columnMetadata.asExpression || "").trim(),
// })
// console.log("==========================================");
return (

View File

@ -3366,7 +3366,8 @@ export class CockroachQueryRunner
}
if (
dbColumn["is_generated"] === "YES" &&
(dbColumn["is_generated"] === "YES" ||
dbColumn["is_generated"] === "ALWAYS") &&
dbColumn["generation_expression"]
) {
tableColumn.generatedType =
@ -3375,7 +3376,7 @@ export class CockroachQueryRunner
: "VIRTUAL"
// We cannot relay on information_schema.columns.generation_expression, because it is formatted different.
const asExpressionQuery =
await this.selectTypeormMetadataSql({
this.selectTypeormMetadataSql({
schema: dbTable["table_schema"],
table: dbTable["table_name"],
type: MetadataTableType.GENERATED_COLUMN,

View File

@ -2682,7 +2682,7 @@ export class MysqlQueryRunner extends BaseQueryRunner implements QueryRunner {
// We cannot relay on information_schema.columns.generation_expression, because it is formatted different.
const asExpressionQuery =
await this.selectTypeormMetadataSql({
this.selectTypeormMetadataSql({
schema: dbTable["TABLE_SCHEMA"],
table: dbTable["TABLE_NAME"],
type: MetadataTableType.GENERATED_COLUMN,

View File

@ -2606,7 +2606,7 @@ export class OracleQueryRunner extends BaseQueryRunner implements QueryRunner {
tableColumn.generatedType = "VIRTUAL"
const asExpressionQuery =
await this.selectTypeormMetadataSql({
this.selectTypeormMetadataSql({
table: dbTable["TABLE_NAME"],
type: MetadataTableType.GENERATED_COLUMN,
name: tableColumn.name,

View File

@ -1005,7 +1005,7 @@ export class PostgresDriver implements Driver {
normalizeDefault(columnMetadata: ColumnMetadata): string | undefined {
const defaultValue = columnMetadata.default
if (defaultValue === null) {
if (defaultValue === null || defaultValue === undefined) {
return undefined
}
@ -1039,10 +1039,6 @@ export class PostgresDriver implements Driver {
return `'${JSON.stringify(defaultValue)}'`
}
if (defaultValue === undefined) {
return undefined
}
return `${defaultValue}`
}

View File

@ -3782,7 +3782,7 @@ export class PostgresQueryRunner
tableColumn.generatedType = "STORED"
// We cannot relay on information_schema.columns.generation_expression, because it is formatted different.
const asExpressionQuery =
await this.selectTypeormMetadataSql({
this.selectTypeormMetadataSql({
database: currentDatabase,
schema: dbTable["table_schema"],
table: dbTable["table_name"],

View File

@ -1766,7 +1766,7 @@ export class SpannerQueryRunner extends BaseQueryRunner implements QueryRunner {
// We cannot relay on information_schema.columns.generation_expression, because it is formatted different.
const asExpressionQuery =
await this.selectTypeormMetadataSql({
this.selectTypeormMetadataSql({
table: dbTable["TABLE_NAME"],
type: MetadataTableType.GENERATED_COLUMN,
name: tableColumn.name,

View File

@ -1438,7 +1438,7 @@ export abstract class AbstractSqliteQueryRunner
dbColumn["hidden"] === 2 ? "VIRTUAL" : "STORED"
const asExpressionQuery =
await this.selectTypeormMetadataSql({
this.selectTypeormMetadataSql({
table: table.name,
type: MetadataTableType.GENERATED_COLUMN,
name: tableColumn.name,

View File

@ -171,7 +171,6 @@ export class SqliteQueryRunner extends AbstractSqliteQueryRunner {
fail(err)
} finally {
await broadcasterResult.wait()
console.log("Finally", query, broadcasterResult.count)
}
})
}

View File

@ -3342,7 +3342,7 @@ export class SqlServerQueryRunner
: "VIRTUAL"
// We cannot relay on information_schema.columns.generation_expression, because it is formatted different.
const asExpressionQuery =
await this.selectTypeormMetadataSql({
this.selectTypeormMetadataSql({
database: dbTable["TABLE_CATALOG"],
schema: dbTable["TABLE_SCHEMA"],
table: dbTable["TABLE_NAME"],

View File

@ -53,10 +53,10 @@ export class EntityPersistExecutor {
// save data in the query runner - this is useful functionality to share data from outside of the world
// with third classes - like subscribers and listener methods
let oldQueryRunnerData = queryRunner.data
if (this.options && this.options.data) {
queryRunner.data = this.options.data
}
let oldQueryRunnerData = queryRunner.data
try {
// collect all operate subjects

View File

@ -174,7 +174,7 @@ export class SubjectExecutor {
// update all special columns in persisted entities, like inserted id or remove ids from the removed entities
// console.time(".updateSpecialColumnsInPersistedEntities");
await this.updateSpecialColumnsInPersistedEntities()
this.updateSpecialColumnsInPersistedEntities()
// console.timeEnd(".updateSpecialColumnsInPersistedEntities");
// finally broadcast "after" events after we finish insert / update / remove operations

View File

@ -49,6 +49,8 @@ describe("Connection replication", () => {
})
)[0]
if (!connection) return
const post = new Post()
post.title = "TypeORM Intro"
@ -62,27 +64,43 @@ describe("Connection replication", () => {
afterEach(() => closeTestingConnections([connection]))
it("connection.isConnected should be true", () =>
connection.isInitialized.should.be.true)
it("connection.isConnected should be true", () => {
if (!connection || connection.driver.options.type !== "postgres") {
return
}
connection.isInitialized.should.be.true
})
it("query runners should go to the master by default", async () => {
if (!connection || connection.driver.options.type !== "postgres") {
return
}
const queryRunner = connection.createQueryRunner()
expect(queryRunner.getReplicationMode()).to.equal("master")
await expectCurrentApplicationName(queryRunner, "master")
await queryRunner.release()
})
it("query runners can have their replication mode overridden", async () => {
if (!connection || connection.driver.options.type !== "postgres") {
return
}
let queryRunner = connection.createQueryRunner("master")
queryRunner.getReplicationMode().should.equal("master")
await expectCurrentApplicationName(queryRunner, "master")
await queryRunner.release()
queryRunner = connection.createQueryRunner("slave")
queryRunner.getReplicationMode().should.equal("slave")
await expectCurrentApplicationName(queryRunner, "slave")
await queryRunner.release()
})
it("read queries should go to the slaves by default", async () => {
if (!connection || connection.driver.options.type !== "postgres") {
return
}
const result = await connection.manager
.createQueryBuilder(Post, "post")
.select("id")
@ -95,6 +113,9 @@ describe("Connection replication", () => {
})
it("write queries should go to the master", async () => {
if (!connection || connection.driver.options.type !== "postgres") {
return
}
const result = await connection.manager
.createQueryBuilder(Post, "post")
.insert()
@ -137,6 +158,8 @@ describe("Connection replication", () => {
})
)[0]
if (!connection) return
const post = new Post()
post.title = "TypeORM Intro"
@ -151,23 +174,35 @@ describe("Connection replication", () => {
afterEach(() => closeTestingConnections([connection]))
it("query runners should go to the master by default", async () => {
if (!connection || connection.driver.options.type !== "postgres") {
return
}
const queryRunner = connection.createQueryRunner()
expect(queryRunner.getReplicationMode()).to.equal("master")
await expectCurrentApplicationName(queryRunner, "master")
await queryRunner.release()
})
it("query runners can have their replication mode overridden", async () => {
if (!connection || connection.driver.options.type !== "postgres") {
return
}
let queryRunner = connection.createQueryRunner("master")
queryRunner.getReplicationMode().should.equal("master")
await expectCurrentApplicationName(queryRunner, "master")
await queryRunner.release()
queryRunner = connection.createQueryRunner("slave")
queryRunner.getReplicationMode().should.equal("slave")
await expectCurrentApplicationName(queryRunner, "slave")
await queryRunner.release()
})
it("read queries should go to the master by default", async () => {
if (!connection || connection.driver.options.type !== "postgres") {
return
}
const result = await connection.manager
.createQueryBuilder(Post, "post")
.select("id")

View File

@ -81,7 +81,7 @@ export class EnumEntity {
type: "enum",
enum: StringEnum,
})
enumWithoutdefault: StringEnum
enumWithoutDefault: StringEnum
@Column({
type: "enum",

View File

@ -32,7 +32,7 @@ describe("database schema > enums", () => {
const enumEntity = new EnumEntity()
enumEntity.id = 1
enumEntity.enumWithoutdefault = StringEnum.EDITOR
enumEntity.enumWithoutDefault = StringEnum.EDITOR
await enumEntityRepository.save(enumEntity)
const loadedEnumEntity = await enumEntityRepository.findOneBy({
@ -67,7 +67,7 @@ describe("database schema > enums", () => {
enumEntity.heterogeneousEnum = HeterogeneousEnum.YES
enumEntity.arrayDefinedStringEnum = "editor"
enumEntity.arrayDefinedNumericEnum = 13
enumEntity.enumWithoutdefault = StringEnum.ADMIN
enumEntity.enumWithoutDefault = StringEnum.ADMIN
await enumEntityRepository.save(enumEntity)
const loadedEnumEntity = await enumEntityRepository.findOneBy({
@ -95,6 +95,8 @@ describe("database schema > enums", () => {
.createSchemaBuilder()
.log()
console.log(sqlInMemory.upQueries)
sqlInMemory.upQueries.length.should.be.equal(0)
sqlInMemory.downQueries.length.should.be.equal(0)
}),

View File

@ -16,7 +16,6 @@ describe("entity subscriber > query data", () => {
subscribers: [MockSubscriber],
dropSchema: true,
schemaCreate: true,
enabledDrivers: ["sqlite"],
})),
)
beforeEach(() => {

View File

@ -19,6 +19,7 @@ describe("entity subscriber > transaction flow", () => {
let afterTransactionCommit = sinon.spy()
let beforeTransactionRollback = sinon.spy()
let afterTransactionRollback = sinon.spy()
let afterInsertQueryRunnerData: any = undefined
@EventSubscriber()
class PostSubscriber implements EntitySubscriberInterface {
@ -31,6 +32,7 @@ describe("entity subscriber > transaction flow", () => {
}
afterInsert() {
afterInsertQueryRunnerData = arguments[0].queryRunner.data
if (afterInsert) afterInsert(arguments)
}
@ -68,8 +70,9 @@ describe("entity subscriber > transaction flow", () => {
if (
connection.driver.options.type === "mssql" ||
connection.driver.options.type === "spanner"
)
) {
continue
}
beforeTransactionStart.resetHistory()
afterTransactionStart.resetHistory()
@ -82,7 +85,7 @@ describe("entity subscriber > transaction flow", () => {
isolationLevel = "READ COMMITTED"
}
const queryRunner = await connection.createQueryRunner()
const queryRunner = connection.createQueryRunner()
if (
connection.driver.options.type === "aurora-postgres" ||
@ -154,13 +157,14 @@ describe("entity subscriber > transaction flow", () => {
if (
connection.driver.options.type === "mssql" ||
connection.driver.options.type === "spanner"
)
) {
continue
}
beforeTransactionCommit.resetHistory()
afterTransactionCommit.resetHistory()
const queryRunner = await connection.createQueryRunner()
const queryRunner = connection.createQueryRunner()
await queryRunner.startTransaction()
if (
@ -222,13 +226,14 @@ describe("entity subscriber > transaction flow", () => {
if (
connection.driver.options.type === "mssql" ||
connection.driver.options.type === "spanner"
)
) {
continue
}
beforeTransactionRollback.resetHistory()
afterTransactionRollback.resetHistory()
const queryRunner = await connection.createQueryRunner()
const queryRunner = connection.createQueryRunner()
await queryRunner.startTransaction()
if (
@ -293,33 +298,21 @@ describe("entity subscriber > transaction flow", () => {
const data = { hello: ["world"] }
for (let connection of connections) {
if (
connection.driver.options.type === "mssql" ||
connection.driver.options.type === "spanner"
)
return
beforeTransactionCommit.resetHistory()
afterTransactionCommit.resetHistory()
afterInsert.resetHistory()
const queryRunner = await connection.createQueryRunner()
afterInsertQueryRunnerData = undefined
const queryRunner = connection.createQueryRunner()
await queryRunner.startTransaction()
await connection.manager.save(example, { data })
await queryRunner.manager.save(example, { data })
await queryRunner.commitTransaction()
expect(afterInsert.getCall(0).args[0][0].queryRunner.data).to.eql(
data,
)
expect(
beforeTransactionCommit.getCall(0).args[0][0].queryRunner.data,
).to.eql(data)
expect(
afterTransactionCommit.getCall(0).args[0][0].queryRunner.data,
).to.eql(data)
expect(afterInsertQueryRunnerData).to.eql(data)
afterInsertQueryRunnerData = undefined
await queryRunner.release()
}
})

View File

@ -1,5 +1,5 @@
import { expect } from "chai"
import { Connection } from "../../../../src"
import { DataSource } from "../../../../src"
import {
closeTestingConnections,
createTestingConnections,
@ -8,43 +8,60 @@ import {
import { filterByCteCapabilities } from "./helpers"
describe("query builder > cte > recursive", () => {
let connections: Connection[]
let dataSources: DataSource[]
before(
async () =>
(connections = await createTestingConnections({
(dataSources = await createTestingConnections({
entities: [__dirname + "/entity/*{.js,.ts}"],
schemaCreate: true,
dropSchema: true,
})),
)
beforeEach(() => reloadTestingDatabases(connections))
after(() => closeTestingConnections(connections))
beforeEach(() => reloadTestingDatabases(dataSources))
after(() => closeTestingConnections(dataSources))
it("should work with simple recursive query", () =>
Promise.all(
connections
dataSources
.filter(filterByCteCapabilities("enabled"))
.map(async (connection) => {
.map(async (dataSource) => {
// CTE cannot reference itself in Spanner
if (connection.options.type === "spanner") return
if (dataSource.options.type === "spanner") return
const qb = await connection
.createQueryBuilder()
.select([])
.from("cte", "cte")
.addCommonTableExpression(
`
SELECT 1
UNION ALL
SELECT cte.foo + 1
FROM cte
WHERE cte.foo < 10
`,
"cte",
{ recursive: true, columnNames: ["foo"] },
)
.addSelect("cte.foo", "foo")
.getRawMany<{ foo: number }>()
let qb: { foo: number }[]
if (dataSource.options.type === "oracle") {
qb = await dataSource
.createQueryBuilder()
.select([])
.from("cte", "cte")
.addCommonTableExpression(
`SELECT 1 FROM "DUAL"` +
` UNION ALL` +
` SELECT "cte"."foo" + 1` +
` FROM "cte"` +
` WHERE "cte"."foo" < 10`,
"cte",
{ recursive: true, columnNames: ["foo"] },
)
.addSelect(`"cte"."foo"`, "foo")
.getRawMany<{ foo: number }>()
} else {
qb = await dataSource
.createQueryBuilder()
.select([])
.from("cte", "cte")
.addCommonTableExpression(
`SELECT 1` +
` UNION ALL` +
` SELECT cte.foo + 1` +
` FROM cte` +
` WHERE cte.foo < 10`,
"cte",
{ recursive: true, columnNames: ["foo"] },
)
.addSelect("cte.foo", "foo")
.getRawMany<{ foo: number }>()
}
expect(qb).to.have.length(10)
}),

View File

@ -5,53 +5,62 @@ import {
closeTestingConnections,
reloadTestingDatabases,
} from "../../../utils/test-utils"
import { Connection } from "../../../../src/connection/Connection"
import { Foo } from "./entity/foo"
import { filterByCteCapabilities } from "./helpers"
import { DataSource } from "../../../../src/index.js"
describe("query builder > cte > simple", () => {
let connections: Connection[]
let dataSources: DataSource[]
before(
async () =>
(connections = await createTestingConnections({
(dataSources = await createTestingConnections({
entities: [__dirname + "/entity/*{.js,.ts}"],
schemaCreate: true,
dropSchema: true,
})),
)
beforeEach(() => reloadTestingDatabases(connections))
after(() => closeTestingConnections(connections))
beforeEach(() => reloadTestingDatabases(dataSources))
after(() => closeTestingConnections(dataSources))
it("show allow select from CTE", () =>
Promise.all(
connections
dataSources
.filter(filterByCteCapabilities("enabled"))
.map(async (connection) => {
await connection
.map(async (dataSource) => {
await dataSource
.getRepository(Foo)
.insert(
[1, 2, 3].map((i) => ({ id: i, bar: String(i) })),
)
const cteQuery = connection
let cteSelection =
dataSource.driver.options.type === "oracle"
? `"foo"."bar"`
: `foo.bar`
const cteQuery = dataSource
.createQueryBuilder()
.select()
.addSelect(`foo.bar`, "bar")
.addSelect(cteSelection, "bar")
.from(Foo, "foo")
.where(`foo.bar = :value`, { value: "2" })
.where(`${cteSelection} = :value`, { value: "2" })
// Spanner does not support column names in CTE
const cteOptions =
connection.driver.options.type === "spanner"
dataSource.driver.options.type === "spanner"
? undefined
: {
columnNames: ["raz"],
}
const cteSelection =
connection.driver.options.type === "spanner"
cteSelection =
dataSource.driver.options.type === "spanner"
? "qaz.bar"
: dataSource.driver.options.type === "oracle"
? `"qaz"."raz"`
: "qaz.raz"
const qb = await connection
const qb = dataSource
.createQueryBuilder()
.addCommonTableExpression(cteQuery, "qaz", cteOptions)
.from("qaz", "qaz")
@ -64,37 +73,59 @@ describe("query builder > cte > simple", () => {
it("should allow join with CTE", () =>
Promise.all(
connections
dataSources
.filter(filterByCteCapabilities("enabled"))
.map(async (connection) => {
await connection
.map(async (dataSource) => {
await dataSource
.getRepository(Foo)
.insert(
[1, 2, 3].map((i) => ({ id: i, bar: String(i) })),
)
const cteQuery = connection
let cteSelection =
dataSource.driver.options.type === "oracle"
? `"foo"."bar"`
: `foo.bar`
const cteQuery = dataSource
.createQueryBuilder()
.select()
.addSelect("bar", "bar")
.addSelect(
dataSource.driver.options.type === "oracle"
? `"bar"`
: "bar",
"bar",
)
.from(Foo, "foo")
.where(`foo.bar = '2'`)
.where(`${cteSelection} = '2'`)
// Spanner does not support column names in CTE
const cteOptions =
connection.driver.options.type === "spanner"
dataSource.driver.options.type === "spanner"
? undefined
: {
columnNames: ["raz"],
}
const cteSelection =
connection.driver.options.type === "spanner"
cteSelection =
dataSource.driver.options.type === "spanner"
? "qaz.bar"
: dataSource.driver.options.type === "oracle"
? `"qaz"."raz"`
: "qaz.raz"
const results = await connection
const results = await dataSource
.createQueryBuilder(Foo, "foo")
.addCommonTableExpression(cteQuery, "qaz", cteOptions)
.innerJoin("qaz", "qaz", `${cteSelection} = foo.bar`)
.innerJoin(
"qaz",
"qaz",
`${cteSelection} = ${
dataSource.driver.options.type === "oracle"
? `"foo"."bar"`
: `foo.bar`
}`,
)
.getMany()
expect(results).to.have.length(1)
@ -107,7 +138,7 @@ describe("query builder > cte > simple", () => {
it("should allow to use INSERT with RETURNING clause in CTE", () =>
Promise.all(
connections
dataSources
.filter(filterByCteCapabilities("writable"))
.map(async (connection) => {
const bar = Math.random().toString()
@ -138,14 +169,14 @@ describe("query builder > cte > simple", () => {
it("should allow string for CTE", () =>
Promise.all(
connections
dataSources
.filter(filterByCteCapabilities("enabled"))
.map(async (connection) => {
.map(async (dataSource) => {
// Spanner does not support column names in CTE
let results: { row: any }[] = []
if (connection.driver.options.type === "spanner") {
results = await connection
let results: { row: any }[]
if (dataSource.driver.options.type === "spanner") {
results = await dataSource
.createQueryBuilder()
.select()
.addCommonTableExpression(
@ -159,8 +190,24 @@ describe("query builder > cte > simple", () => {
.from("cte", "cte")
.addSelect("foo", "row")
.getRawMany<{ row: any }>()
} else if (dataSource.driver.options.type === "oracle") {
results = await dataSource
.createQueryBuilder()
.select()
.addCommonTableExpression(
`
SELECT 1 FROM DUAL
UNION
SELECT 2 FROM DUAL
`,
"cte",
{ columnNames: ["foo"] },
)
.from("cte", "cte")
.addSelect(`"foo"`, "row")
.getRawMany<{ row: any }>()
} else {
results = await connection
results = await dataSource
.createQueryBuilder()
.select()
.addCommonTableExpression(

View File

@ -3,86 +3,126 @@ import {
closeTestingConnections,
createTestingConnections,
} from "../../../utils/test-utils"
import { Repository } from "../../../../src/repository/Repository"
import { DataSource } from "../../../../src/data-source/DataSource"
import { Post } from "./entity/Post"
import { LessThan } from "../../../../src"
import { LessThan, DataSource } from "../../../../src"
import { expect } from "chai"
describe("repository > aggregate methods", () => {
let connections: DataSource[]
let repository: Repository<Post>
before(async () => {
connections = await createTestingConnections({
entities: [Post],
schemaCreate: true,
dropSchema: true,
})
repository = connections[0].getRepository(Post)
for (let i = 0; i < 100; i++) {
const post = new Post()
post.id = i
post.counter = i + 1
await repository.save(post)
}
await Promise.all(
connections.map(async (connection) => {
for (let i = 0; i < 100; i++) {
const post = new Post()
post.id = i
post.counter = i + 1
await connection.getRepository(Post).save(post)
}
}),
)
})
after(() => closeTestingConnections(connections))
describe("sum", () => {
it("should return the aggregate sum", async () => {
const sum = await repository.sum("counter")
expect(sum).to.equal(5050)
})
it("should return the aggregate sum", () =>
Promise.all(
connections.map(async (connection) => {
const sum = await connection
.getRepository(Post)
.sum("counter")
expect(sum).to.equal(5050)
}),
))
it("should return null when 0 rows match the query", async () => {
const sum = await repository.sum("counter", { id: LessThan(0) })
expect(sum).to.be.null
})
it("should return null when 0 rows match the query", () =>
Promise.all(
connections.map(async (connection) => {
const sum = await connection
.getRepository(Post)
.sum("counter", { id: LessThan(0) })
expect(sum).to.be.null
}),
))
})
describe("average", () => {
it("should return the aggregate average", async () => {
const average = await repository.average("counter")
// Some RDBMSs (e.g. SQL Server) will return an int when averaging an int column, so either
// answer is acceptable.
expect([50, 50.5]).to.include(average)
})
it("should return the aggregate average", () =>
Promise.all(
connections.map(async (connection) => {
const average = await connection
.getRepository(Post)
.average("counter")
// Some RDBMSs (e.g. SQL Server) will return an int when averaging an int column, so either
// answer is acceptable.
expect([50, 50.5]).to.include(average)
}),
))
it("should return null when 0 rows match the query", async () => {
const average = await repository.average("counter", {
id: LessThan(0),
})
expect(average).to.be.null
})
it("should return null when 0 rows match the query", () =>
Promise.all(
connections.map(async (connection) => {
const average = await connection
.getRepository(Post)
.average("counter", {
id: LessThan(0),
})
expect(average).to.be.null
}),
))
})
describe("minimum", () => {
it("should return the aggregate minimum", async () => {
const minimum = await repository.minimum("counter")
expect(minimum).to.equal(1)
})
it("should return the aggregate minimum", () =>
Promise.all(
connections.map(async (connection) => {
const minimum = await connection
.getRepository(Post)
.minimum("counter")
expect(minimum).to.equal(1)
}),
))
it("should return null when 0 rows match the query", async () => {
const minimum = await repository.minimum("counter", {
id: LessThan(0),
})
expect(minimum).to.be.null
})
it("should return null when 0 rows match the query", () =>
Promise.all(
connections.map(async (connection) => {
const minimum = await connection
.getRepository(Post)
.minimum("counter", {
id: LessThan(0),
})
expect(minimum).to.be.null
}),
))
})
describe("maximum", () => {
it("should return the aggregate maximum", async () => {
const maximum = await repository.maximum("counter")
expect(maximum).to.equal(100)
})
it("should return the aggregate maximum", () =>
Promise.all(
connections.map(async (connection) => {
const maximum = await connection
.getRepository(Post)
.maximum("counter")
expect(maximum).to.equal(100)
}),
))
it("should return null when 0 rows match the query", async () => {
const maximum = await repository.maximum("counter", {
id: LessThan(0),
})
expect(maximum).to.be.null
})
it("should return null when 0 rows match the query", () =>
Promise.all(
connections.map(async (connection) => {
const maximum = await connection
.getRepository(Post)
.maximum("counter", {
id: LessThan(0),
})
expect(maximum).to.be.null
}),
))
})
})

View File

@ -14,6 +14,7 @@ import { Photo } from "./entity/Photo"
import sinon from "sinon"
import { FileLogger } from "../../../../src"
import { promisify } from "util"
import fs from "fs"
import { readFile, unlink } from "fs"
describe("repository > find options", () => {
@ -71,7 +72,7 @@ describe("repository > find options", () => {
user.name = "Alex Messer"
await connection.manager.save(user)
const queryRunner = await connection.createQueryRunner()
const queryRunner = connection.createQueryRunner()
const startTransactionFn = sinon.spy(
queryRunner,
@ -255,7 +256,9 @@ describe("repository > find options > comment", () => {
beforeEach(() => reloadTestingDatabases(connections))
after(async () => {
await closeTestingConnections(connections)
await promisify(unlink)(logPath)
if (fs.existsSync(logPath)) {
await promisify(unlink)(logPath)
}
})
it("repository should insert comment", () =>

View File

@ -20,13 +20,13 @@ export class AssetEntity {
@PrimaryGeneratedColumn("uuid")
id!: string
@Column({ type: "varchar", length: 255 })
@Column({ length: 255 })
name!: string
@Column({ type: "uuid" })
configuration_id!: string
@Column({ type: "numeric" })
@Column()
status!: AssetStatus
@CreateDateColumn()

View File

@ -21,10 +21,10 @@ export class ConfigurationEntity {
@PrimaryGeneratedColumn("uuid")
id!: string
@Column({ type: "varchar", length: 255 })
@Column({ length: 255 })
name!: string
@Column({ type: "numeric" })
@Column()
status!: ConfigurationStatus
@Column({ type: "uuid", nullable: false })
@ -34,7 +34,7 @@ export class ConfigurationEntity {
@JoinColumn({ name: "location_id" })
location!: LocationEntity
@Column({ type: "boolean", default: true })
@Column({ default: true })
active!: boolean
@OneToMany(() => AssetEntity, (asset) => asset.configuration, {

View File

@ -13,10 +13,10 @@ export class LocationEntity {
@PrimaryGeneratedColumn("uuid")
id!: string
@Column({ type: "varchar", length: 255 })
@Column({ length: 255 })
name!: string
@Column({ type: "boolean", default: true })
@Column({ default: true })
active!: boolean
@CreateDateColumn()

View File

@ -4,13 +4,7 @@ import {
closeTestingConnections,
reloadTestingDatabases,
} from "../../utils/test-utils"
import {
AbstractLogger,
DataSource,
LogLevel,
LogMessage,
QueryRunner,
} from "../../../src"
import { AbstractLogger, DataSource, LogLevel, LogMessage } from "../../../src"
import sinon from "sinon"
import { expect } from "chai"
@ -22,7 +16,6 @@ describe("github issues > #10322 logMigration of AbstractLogger has wrong loggin
protected writeLog(
level: LogLevel,
logMessage: LogMessage | LogMessage[],
queryRunner?: QueryRunner,
) {
const messages = this.prepareLogMessages(logMessage, {
highlightSql: false,
@ -56,7 +49,7 @@ describe("github issues > #10322 logMigration of AbstractLogger has wrong loggin
try {
await dataSource.runMigrations()
} catch (e) {
expect(fakeLog.calledOnce).to.be.true
expect(fakeLog.called).to.be.true
}
}),
))

View File

@ -4,7 +4,7 @@ import {
closeTestingConnections,
reloadTestingDatabases,
} from "../../utils/test-utils"
import { DataSource } from "../../../src/data-source/DataSource"
import { DataSource } from "../../../src"
import { assert } from "chai"
import { Dog } from "./entity/family"

View File

@ -14,9 +14,6 @@ describe("github issues > #3913 Cannot set embedded entity to null", () => {
async () =>
(connections = await createTestingConnections({
entities: [__dirname + "/entity/*{.js,.ts}"],
cache: {
alwaysEnabled: true,
},
})),
)
beforeEach(() => reloadTestingDatabases(connections))
@ -25,39 +22,51 @@ describe("github issues > #3913 Cannot set embedded entity to null", () => {
it("should set all embedded columns to null when entity is set to null", () =>
Promise.all(
connections.map(async (connection) => {
const qb = connection.createQueryBuilder(Test, "s")
const t = new Test()
t.embedded = { a: "a", b: "b", nested: { c: "c" } }
const { id } = await connection.manager.save(t)
const t1 = await qb.getOne()
await connection.manager.save(t)
const t1 = await connection
.createQueryBuilder(Test, "s")
.getOne()
expect(t1!.embedded).to.deep.equal({
a: "a",
b: "b",
nested: { c: "c" },
})
t!.embedded = null
t!.id = id
t.embedded = null
await connection.manager.save(t)
const t2 = await connection
.createQueryBuilder(Test, "s")
.getOne()
expect(t2!.embedded).to.equal(null)
await qb
await connection
.createQueryBuilder(Test, "s")
.update()
.set({ embedded: { a: "a", b: null } })
.execute()
const t3 = await qb.getOne()
const t3 = await connection
.createQueryBuilder(Test, "s")
.getOne()
expect(t3!.embedded).to.deep.equal({
a: "a",
b: null,
nested: null,
})
await qb.update().set({ embedded: null }).execute()
const t4 = await qb.getOne()
await connection
.createQueryBuilder(Test, "s")
.update()
.set({ embedded: null })
.execute()
const t4 = await connection
.createQueryBuilder(Test, "s")
.getOne()
expect(t4!.embedded).to.equal(null)
}),
))

View File

@ -5,7 +5,7 @@ import {
createTestingConnections,
} from "../../utils/test-utils"
import { StrictlyInitializedEntity } from "./entity/StrictlyInitializedEntity"
import { DataSource } from "../../../src/data-source/DataSource"
import { DataSource } from "../../../src"
describe("github issues > #8444 entitySkipConstructor not working", () => {
describe("without entitySkipConstructor", () => {
@ -23,9 +23,17 @@ describe("github issues > #8444 entitySkipConstructor not working", () => {
})
}
await expect(
bootstrapWithoutEntitySkipConstructor(),
).to.be.rejectedWith("someColumn cannot be undefined")
try {
const dataSources =
await bootstrapWithoutEntitySkipConstructor()
// if we have zero data sources - it means we are testing in mongodb-only mode - we are fine here
// if we have any data sources - it means test didn't go as we expected
if (dataSources.length > 0) {
expect(true).to.be.false
}
} catch (err) {
expect(err.message).to.contain("someColumn cannot be undefined")
}
})
})

View File

@ -10,7 +10,7 @@ import { InternalUser } from "./entity/InternalUser"
import { InternalRole } from "./entity/InternalRole"
import { User } from "./entity/User"
import { Role } from "./entity/Role"
import { BaseEntity, TypeORMError } from "../../../src"
import { BaseEntity } from "../../../src"
import { ClientRole } from "./entity/ClientRole"
import { afterEach } from "mocha"
@ -92,14 +92,28 @@ describe("github issues > #8522 Single table inheritance returns the same discri
describe("Related tables", () => {
it("Should throw error when related tables have the same discriminator", async () => {
await createTestingConnections({
entities: [BaseEntity, ClientRole, InternalRole, Role, User],
schemaCreate: true,
dropSchema: true,
}).should.be.rejectedWith(
TypeORMError,
`Entities ClientRole and InternalRole have the same discriminator values. Make sure they are different while using the @ChildEntity decorator.`,
)
try {
const dataSources = await createTestingConnections({
entities: [
BaseEntity,
ClientRole,
InternalRole,
Role,
User,
],
schemaCreate: true,
dropSchema: true,
})
// if we have zero data sources - it means we are testing in mongodb-only mode - we are fine here
// if we have any data sources - it means test didn't go as we expected
if (dataSources.length > 0) {
expect(true).to.be.false
}
} catch (err) {
expect(err.message).to.contain(
"Entities ClientRole and InternalRole have the same discriminator values. Make sure they are different while using the @ChildEntity decorator.",
)
}
})
})
})

View File

@ -229,7 +229,7 @@ describe("github issues > #9323 Add new VirtualColumn decorator feature", () =>
}),
))
it("should be able to save and find sub-select data in the databse (with query builder)", () =>
it("should be able to save and find sub-select data in the database (with query builder)", () =>
Promise.all(
connections.map(async (connection) => {
const companyName = "My Company 2"
@ -272,9 +272,15 @@ describe("github issues > #9323 Add new VirtualColumn decorator feature", () =>
const companyQueryData = await connection
.createQueryBuilder(Company, "company")
.leftJoinAndSelect("company.employees", "employee")
.leftJoinAndSelect("employee.timesheets", "timesheet")
.leftJoinAndSelect("timesheet.activities", "activity")
.select([
"company.name",
"company.totalEmployeesCount",
"employee.name",
"timesheet.id",
"timesheet.totalActvityHours",
])
.leftJoin("company.employees", "employee")
.leftJoin("employee.timesheets", "timesheet")
.where("company.name = :name", { name: companyName })
// we won't be supporting where & order bys with VirtualColumns (you will have to make your subquery a function that gets added to the query builder)
//.andWhere("company.totalEmployeesCount > 2")