@bitblit/ratchet-rdbms 4.0.115-alpha
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +19 -0
- package/License.txt +13 -0
- package/README.md +41 -0
- package/lib/build/ratchet-rdbms-info.d.ts +5 -0
- package/lib/build/ratchet-rdbms-info.js +14 -0
- package/lib/model/connection-config.d.ts +4 -0
- package/lib/model/connection-config.js +1 -0
- package/lib/model/db-config.d.ts +9 -0
- package/lib/model/db-config.js +1 -0
- package/lib/model/group-by-count-result.d.ts +4 -0
- package/lib/model/group-by-count-result.js +1 -0
- package/lib/model/mysql/mysql-master-status.d.ts +6 -0
- package/lib/model/mysql/mysql-master-status.js +1 -0
- package/lib/model/mysql/mysql-results-wrapper.d.ts +5 -0
- package/lib/model/mysql/mysql-results-wrapper.js +1 -0
- package/lib/model/mysql/mysql-slave-status.d.ts +52 -0
- package/lib/model/mysql/mysql-slave-status.js +1 -0
- package/lib/model/mysql/mysql-style-connection-provider.d.ts +7 -0
- package/lib/model/mysql/mysql-style-connection-provider.js +1 -0
- package/lib/model/mysql/mysql-update-results.d.ts +9 -0
- package/lib/model/mysql/mysql-update-results.js +1 -0
- package/lib/model/paginated-results.d.ts +5 -0
- package/lib/model/paginated-results.js +1 -0
- package/lib/model/pagination-bounds.d.ts +6 -0
- package/lib/model/pagination-bounds.js +1 -0
- package/lib/model/paginator.d.ts +9 -0
- package/lib/model/paginator.js +1 -0
- package/lib/model/query-defaults.d.ts +4 -0
- package/lib/model/query-defaults.js +1 -0
- package/lib/model/query-text-provider.d.ts +4 -0
- package/lib/model/query-text-provider.js +1 -0
- package/lib/model/sort-direction.d.ts +4 -0
- package/lib/model/sort-direction.js +5 -0
- package/lib/model/ssh/ssh-tunnel-config.d.ts +7 -0
- package/lib/model/ssh/ssh-tunnel-config.js +1 -0
- package/lib/model/ssh/ssh-tunnel-container.d.ts +12 -0
- package/lib/model/ssh/ssh-tunnel-container.js +1 -0
- package/lib/model/ssh-config.d.ts +5 -0
- package/lib/model/ssh-config.js +1 -0
- package/lib/model/transaction-isolation-level.d.ts +4 -0
- package/lib/model/transaction-isolation-level.js +5 -0
- package/lib/named-parameter-maria-db-service.d.ts +41 -0
- package/lib/named-parameter-maria-db-service.js +251 -0
- package/lib/non-pooled-mysql-style-connection-provider.d.ts +8 -0
- package/lib/non-pooled-mysql-style-connection-provider.js +16 -0
- package/lib/query-builder/query-builder-result.d.ts +9 -0
- package/lib/query-builder/query-builder-result.js +12 -0
- package/lib/query-builder/query-builder.d.ts +52 -0
- package/lib/query-builder/query-builder.js +352 -0
- package/lib/query-builder/query-builder.spec.d.ts +1 -0
- package/lib/query-builder/query-builder.spec.js +126 -0
- package/lib/query-builder/query-util.d.ts +12 -0
- package/lib/query-builder/query-util.js +69 -0
- package/lib/rds-mysql-style-connection-provider.d.ts +23 -0
- package/lib/rds-mysql-style-connection-provider.js +159 -0
- package/lib/ssh-tunnel-service.d.ts +6 -0
- package/lib/ssh-tunnel-service.js +47 -0
- package/lib/transactional-named-parameter-maria-db-service.d.ts +23 -0
- package/lib/transactional-named-parameter-maria-db-service.js +129 -0
- package/lib/util/relational-database-utils.d.ts +4 -0
- package/lib/util/relational-database-utils.js +29 -0
- package/package.json +68 -0
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Connection, ConnectionOptions } from 'mysql2/promise';
|
|
2
|
+
import { SshTunnelService } from './ssh-tunnel-service.js';
|
|
3
|
+
import { MysqlStyleConnectionProvider } from './model/mysql/mysql-style-connection-provider.js';
|
|
4
|
+
import { ConnectionConfig } from './model/connection-config.js';
|
|
5
|
+
import { QueryDefaults } from './model/query-defaults.js';
|
|
6
|
+
export declare class RdsMysqlStyleConnectionProvider implements MysqlStyleConnectionProvider {
|
|
7
|
+
private inConfigPromise;
|
|
8
|
+
private additionalConfig;
|
|
9
|
+
private ssh?;
|
|
10
|
+
static DEFAULT_CONNECTION_OPTIONS: ConnectionOptions;
|
|
11
|
+
private tunnels;
|
|
12
|
+
private dbPromise;
|
|
13
|
+
private readonly configPromise;
|
|
14
|
+
constructor(inConfigPromise: Promise<ConnectionConfig>, additionalConfig?: ConnectionOptions, ssh?: SshTunnelService);
|
|
15
|
+
get usingSshTunnel(): boolean;
|
|
16
|
+
private addShutdownHandlerToProcess;
|
|
17
|
+
clearConnectionCache(): Promise<boolean>;
|
|
18
|
+
getConnection(name: string): Promise<Connection | undefined>;
|
|
19
|
+
createNonPooledDatabaseConnection(queryDefaults: QueryDefaults, additionalConfig?: ConnectionOptions): Promise<Connection | undefined>;
|
|
20
|
+
private getDbConfig;
|
|
21
|
+
private createDatabaseConnection;
|
|
22
|
+
private prepareConnectionConfig;
|
|
23
|
+
}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import maria from 'mysql2/promise';
|
|
2
|
+
import { Logger } from '@bitblit/ratchet-common/lib/logger/logger.js';
|
|
3
|
+
import getPort from 'get-port';
|
|
4
|
+
import _ from 'lodash';
|
|
5
|
+
import { ErrorRatchet } from '@bitblit/ratchet-common/lib/lang/error-ratchet.js';
|
|
6
|
+
import { RequireRatchet } from '@bitblit/ratchet-common/lib/lang/require-ratchet.js';
|
|
7
|
+
export class RdsMysqlStyleConnectionProvider {
|
|
8
|
+
inConfigPromise;
|
|
9
|
+
additionalConfig;
|
|
10
|
+
ssh;
|
|
11
|
+
static DEFAULT_CONNECTION_OPTIONS = { multipleStatements: true };
|
|
12
|
+
tunnels = new Map();
|
|
13
|
+
dbPromise = new Map();
|
|
14
|
+
configPromise;
|
|
15
|
+
constructor(inConfigPromise, additionalConfig = RdsMysqlStyleConnectionProvider.DEFAULT_CONNECTION_OPTIONS, ssh) {
|
|
16
|
+
this.inConfigPromise = inConfigPromise;
|
|
17
|
+
this.additionalConfig = additionalConfig;
|
|
18
|
+
this.ssh = ssh;
|
|
19
|
+
this.configPromise = this.prepareConnectionConfig(inConfigPromise);
|
|
20
|
+
Logger.info('Added shutdown handler to the process (Only once per instantiation)');
|
|
21
|
+
this.addShutdownHandlerToProcess();
|
|
22
|
+
}
|
|
23
|
+
get usingSshTunnel() {
|
|
24
|
+
return !!this.ssh;
|
|
25
|
+
}
|
|
26
|
+
addShutdownHandlerToProcess() {
|
|
27
|
+
process.on('exit', () => {
|
|
28
|
+
Logger.info('Process is shutting down, closing connections');
|
|
29
|
+
this.clearConnectionCache().catch((err) => {
|
|
30
|
+
Logger.error('Shutdown connection failed : %s', err);
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
async clearConnectionCache() {
|
|
35
|
+
const rval = false;
|
|
36
|
+
Logger.info('Clearing connection cache for RdsMysqlConnectionProvider');
|
|
37
|
+
const oldDbHooks = this.dbPromise;
|
|
38
|
+
const oldSshTunnels = this.tunnels;
|
|
39
|
+
this.dbPromise = new Map();
|
|
40
|
+
this.tunnels = new Map();
|
|
41
|
+
if (oldDbHooks.size > 0) {
|
|
42
|
+
const hookNames = Array.from(oldDbHooks.keys());
|
|
43
|
+
for (const hookName of hookNames) {
|
|
44
|
+
Logger.info('Shutting down connection : %s', hookName);
|
|
45
|
+
const oldDbHook = oldDbHooks.get(hookName);
|
|
46
|
+
try {
|
|
47
|
+
const oldConn = await oldDbHook;
|
|
48
|
+
if (oldConn) {
|
|
49
|
+
await oldConn.destroy();
|
|
50
|
+
Logger.info('Finished destroying old connection');
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
Logger.warn('Could not get old connection, so not destroying it');
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
catch (err) {
|
|
57
|
+
if (ErrorRatchet.asErr(err).message.includes('closed state')) {
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
Logger.error('Something went wrong closing the connection : %s', err);
|
|
61
|
+
throw err;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
if (oldSshTunnels.size > 0) {
|
|
67
|
+
const tunnelNames = Array.from(oldSshTunnels.keys());
|
|
68
|
+
for (const tunnelName of tunnelNames) {
|
|
69
|
+
try {
|
|
70
|
+
Logger.info('Shutting down SSH tunnel: %s', tunnelName);
|
|
71
|
+
const tunnel = oldSshTunnels.get(tunnelName);
|
|
72
|
+
if (tunnel) {
|
|
73
|
+
await this.ssh.shutdown(tunnel);
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
Logger.warn('Could not get old tunnel, so not destroying it');
|
|
77
|
+
}
|
|
78
|
+
Logger.info('SSH Tunnel closed');
|
|
79
|
+
}
|
|
80
|
+
catch (err) {
|
|
81
|
+
Logger.error('Failure closing old tunnel : %s', err);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
Logger.info('Old db and tunnel removed');
|
|
86
|
+
return rval;
|
|
87
|
+
}
|
|
88
|
+
async getConnection(name) {
|
|
89
|
+
if (!this.dbPromise.has(name)) {
|
|
90
|
+
const dbConfig = await this.getDbConfig(name);
|
|
91
|
+
const connection = this.createDatabaseConnection(dbConfig, this.additionalConfig, true);
|
|
92
|
+
this.dbPromise.set(name, connection);
|
|
93
|
+
}
|
|
94
|
+
return this.dbPromise.get(name);
|
|
95
|
+
}
|
|
96
|
+
async createNonPooledDatabaseConnection(queryDefaults, additionalConfig = RdsMysqlStyleConnectionProvider.DEFAULT_CONNECTION_OPTIONS) {
|
|
97
|
+
Logger.info('Creating non-pooled connection for %s', queryDefaults.databaseName);
|
|
98
|
+
const dbConfig = await this.getDbConfig(queryDefaults.databaseName);
|
|
99
|
+
const rval = await this.createDatabaseConnection(dbConfig, additionalConfig, false);
|
|
100
|
+
return rval;
|
|
101
|
+
}
|
|
102
|
+
async getDbConfig(name) {
|
|
103
|
+
Logger.info('RdsMysqlStyleConnectionProvider:getDbConfig:Initiating promise for %s', name);
|
|
104
|
+
const cfgs = await this.configPromise;
|
|
105
|
+
const dbConfig = cfgs.dbList.find((s) => s.label === name.toLowerCase());
|
|
106
|
+
if (!dbConfig) {
|
|
107
|
+
throw new Error(`Cannot find any connection config named ${name}`);
|
|
108
|
+
}
|
|
109
|
+
return dbConfig;
|
|
110
|
+
}
|
|
111
|
+
async createDatabaseConnection(dbCfg, additionalConfig = RdsMysqlStyleConnectionProvider.DEFAULT_CONNECTION_OPTIONS, clearCacheOnConnectionFailure, sshConfigPromise) {
|
|
112
|
+
Logger.debug('In RdsMysqlStyleConnectionProvider:createDatabaseConnection : %s', dbCfg.label);
|
|
113
|
+
const cfgCopy = _.omit(_.clone(dbCfg), 'label', 'tunnelPort');
|
|
114
|
+
if (this.usingSshTunnel && dbCfg.tunnelPort) {
|
|
115
|
+
let tunnel = this.tunnels.get(dbCfg.label);
|
|
116
|
+
if (!tunnel) {
|
|
117
|
+
Logger.debug('Creating SSH tunnel from local port %d to remote host %s and port %d', dbCfg.tunnelPort, dbCfg.host, dbCfg.port);
|
|
118
|
+
const sshConfig = await sshConfigPromise;
|
|
119
|
+
tunnel = await this.ssh.createSSHTunnel(sshConfig, dbCfg.host, dbCfg.port, dbCfg.tunnelPort);
|
|
120
|
+
this.tunnels.set(dbCfg.label, tunnel);
|
|
121
|
+
}
|
|
122
|
+
cfgCopy.port = tunnel.serverOptions.port ?? -1;
|
|
123
|
+
cfgCopy.host = 'localhost';
|
|
124
|
+
}
|
|
125
|
+
Logger.debug('Opening connection for RdsMysqlStyleConnectionProvider');
|
|
126
|
+
let connection;
|
|
127
|
+
try {
|
|
128
|
+
connection = await maria.createConnection({ ...additionalConfig, ...cfgCopy });
|
|
129
|
+
}
|
|
130
|
+
catch (err) {
|
|
131
|
+
Logger.info('Failed trying to create connection : %s : clearing for retry', err);
|
|
132
|
+
if (clearCacheOnConnectionFailure) {
|
|
133
|
+
this.dbPromise = new Map();
|
|
134
|
+
}
|
|
135
|
+
return undefined;
|
|
136
|
+
}
|
|
137
|
+
connection.on('error', (err) => {
|
|
138
|
+
Logger.info('An error was detected on the connection : %s : Clearing', err);
|
|
139
|
+
this.clearConnectionCache()
|
|
140
|
+
.then((cleared) => {
|
|
141
|
+
Logger.info('Connection cleared: %s', cleared);
|
|
142
|
+
})
|
|
143
|
+
.catch((err) => Logger.error('Failed to clear RDS connection cache: %j', err));
|
|
144
|
+
});
|
|
145
|
+
Logger.info('Added error handler to db, there are now %d error handlers and %d shutdown handlers', connection.rawListeners('error').length, process.rawListeners('exit').length);
|
|
146
|
+
return connection;
|
|
147
|
+
}
|
|
148
|
+
async prepareConnectionConfig(inputPromise) {
|
|
149
|
+
RequireRatchet.notNullOrUndefined(inputPromise, 'input');
|
|
150
|
+
const cfg = await inputPromise;
|
|
151
|
+
RequireRatchet.true(cfg.dbList.length > 0, 'input.dbList');
|
|
152
|
+
if (this.usingSshTunnel) {
|
|
153
|
+
for (const dbConfig of cfg.dbList) {
|
|
154
|
+
dbConfig.tunnelPort = await getPort();
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return cfg;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { SshTunnelContainer } from './model/ssh/ssh-tunnel-container.js';
|
|
2
|
+
import { SshTunnelConfig } from './model/ssh/ssh-tunnel-config.js';
|
|
3
|
+
export declare class SshTunnelService {
|
|
4
|
+
shutdown(ssh: SshTunnelContainer): Promise<boolean>;
|
|
5
|
+
createSSHTunnel(sshOptions: SshTunnelConfig, dstHost: string, dstPort: number, localPort: number): Promise<SshTunnelContainer>;
|
|
6
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import * as TunnelSsh from 'tunnel-ssh';
|
|
2
|
+
import { Logger } from '@bitblit/ratchet-common/lib/logger/logger.js';
|
|
3
|
+
export class SshTunnelService {
|
|
4
|
+
async shutdown(ssh) {
|
|
5
|
+
if (!ssh.connection) {
|
|
6
|
+
Logger.info('Not shutting down tunnel - non-tunnel passed');
|
|
7
|
+
return false;
|
|
8
|
+
}
|
|
9
|
+
try {
|
|
10
|
+
Logger.info('Shutting down SSH Tunnel');
|
|
11
|
+
ssh.connection.end();
|
|
12
|
+
ssh.server.close();
|
|
13
|
+
return true;
|
|
14
|
+
}
|
|
15
|
+
catch (err) {
|
|
16
|
+
Logger.error('Error closing ssh tunnel : %s', err);
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
async createSSHTunnel(sshOptions, dstHost, dstPort, localPort) {
|
|
21
|
+
const tunnelOptions = {
|
|
22
|
+
autoClose: true,
|
|
23
|
+
};
|
|
24
|
+
const serverOptions = {
|
|
25
|
+
port: localPort,
|
|
26
|
+
};
|
|
27
|
+
const forwardOptions = {
|
|
28
|
+
srcAddr: 'localhost',
|
|
29
|
+
srcPort: localPort,
|
|
30
|
+
dstAddr: dstHost,
|
|
31
|
+
dstPort: dstPort,
|
|
32
|
+
};
|
|
33
|
+
const [server, connection] = await TunnelSsh.createTunnel(tunnelOptions, serverOptions, sshOptions, forwardOptions);
|
|
34
|
+
server.on('error', (err) => {
|
|
35
|
+
Logger.warn('SSH Server Error : %s', err);
|
|
36
|
+
});
|
|
37
|
+
const rval = {
|
|
38
|
+
tunnelOptions: tunnelOptions,
|
|
39
|
+
serverOptions: serverOptions,
|
|
40
|
+
sshOptions: sshOptions,
|
|
41
|
+
forwardOptions: forwardOptions,
|
|
42
|
+
server: server,
|
|
43
|
+
connection: connection,
|
|
44
|
+
};
|
|
45
|
+
return rval;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { NamedParameterMariaDbService } from './named-parameter-maria-db-service.js';
|
|
2
|
+
import { MysqlStyleConnectionProvider } from './model/mysql/mysql-style-connection-provider.js';
|
|
3
|
+
import { ConnectionOptions } from 'mysql2/promise';
|
|
4
|
+
import { QueryBuilder } from './query-builder/query-builder.js';
|
|
5
|
+
import { MysqlUpdateResults } from './model/mysql/mysql-update-results.js';
|
|
6
|
+
import { QueryDefaults } from './model/query-defaults.js';
|
|
7
|
+
import { QueryTextProvider } from './model/query-text-provider.js';
|
|
8
|
+
export declare class TransactionalNamedParameterMariaDbService extends NamedParameterMariaDbService {
|
|
9
|
+
private myQueryProvider;
|
|
10
|
+
private myConnectionProvider;
|
|
11
|
+
private myQueryDefaults;
|
|
12
|
+
private currentTxFlag?;
|
|
13
|
+
static create(src: NamedParameterMariaDbService, queryDefaults: QueryDefaults, additionalConfig?: ConnectionOptions): Promise<TransactionalNamedParameterMariaDbService>;
|
|
14
|
+
constructor(myQueryProvider: QueryTextProvider, myConnectionProvider: MysqlStyleConnectionProvider, myQueryDefaults: QueryDefaults);
|
|
15
|
+
cleanShutdown(): Promise<void>;
|
|
16
|
+
startTransaction(): Promise<void>;
|
|
17
|
+
commitTransaction(): Promise<void>;
|
|
18
|
+
rollBackTransaction(): Promise<void>;
|
|
19
|
+
buildAndExecuteUpdateOrInsertInTransaction(queryBuilder: QueryBuilder, timeoutMS?: number): Promise<MysqlUpdateResults | null>;
|
|
20
|
+
buildAndExecuteInTransaction<T>(queryBuilder: QueryBuilder, timeoutMS?: number): Promise<T[] | null>;
|
|
21
|
+
static oneStepBuildAndExecuteUpdateOrInsertInTransaction(src: NamedParameterMariaDbService, queryBuilder: QueryBuilder, timeoutMS?: number, additionalConfig?: ConnectionOptions): Promise<MysqlUpdateResults | null>;
|
|
22
|
+
static oneStepBuildAndExecuteInTransaction<T>(src: NamedParameterMariaDbService, queryBuilder: QueryBuilder, timeoutMS?: number, additionalConfig?: ConnectionOptions): Promise<T[] | null>;
|
|
23
|
+
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { NamedParameterMariaDbService } from './named-parameter-maria-db-service.js';
|
|
2
|
+
import { ErrorRatchet } from '@bitblit/ratchet-common/lib/lang/error-ratchet.js';
|
|
3
|
+
import { Logger } from '@bitblit/ratchet-common/lib/logger/logger.js';
|
|
4
|
+
import { StringRatchet } from '@bitblit/ratchet-common/lib/lang/string-ratchet.js';
|
|
5
|
+
export class TransactionalNamedParameterMariaDbService extends NamedParameterMariaDbService {
|
|
6
|
+
myQueryProvider;
|
|
7
|
+
myConnectionProvider;
|
|
8
|
+
myQueryDefaults;
|
|
9
|
+
currentTxFlag;
|
|
10
|
+
static async create(src, queryDefaults, additionalConfig) {
|
|
11
|
+
Logger.info('createTransactionalNamedParameterMariaDbService : %j : %j', queryDefaults, additionalConfig);
|
|
12
|
+
const connProv = await src.createNonPooledMysqlStyleConnectionProvider(queryDefaults, additionalConfig);
|
|
13
|
+
return new TransactionalNamedParameterMariaDbService(src.getQueryProvider(), connProv, queryDefaults);
|
|
14
|
+
}
|
|
15
|
+
constructor(myQueryProvider, myConnectionProvider, myQueryDefaults) {
|
|
16
|
+
super(myQueryProvider, myConnectionProvider, myQueryDefaults);
|
|
17
|
+
this.myQueryProvider = myQueryProvider;
|
|
18
|
+
this.myConnectionProvider = myConnectionProvider;
|
|
19
|
+
this.myQueryDefaults = myQueryDefaults;
|
|
20
|
+
}
|
|
21
|
+
async cleanShutdown() {
|
|
22
|
+
Logger.info('cleanShutdown');
|
|
23
|
+
try {
|
|
24
|
+
const conn = await this.myConnectionProvider.getConnection();
|
|
25
|
+
if (conn) {
|
|
26
|
+
Logger.info('Shutting down connection');
|
|
27
|
+
conn.destroy();
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
catch (err) {
|
|
31
|
+
Logger.info('Failure shutting down single-use connection : %s', err);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
async startTransaction() {
|
|
35
|
+
if (!this.currentTxFlag) {
|
|
36
|
+
this.currentTxFlag = StringRatchet.createRandomHexString(10);
|
|
37
|
+
Logger.info('Starting a transaction : %s', this.currentTxFlag);
|
|
38
|
+
const conn = await this.myConnectionProvider.getConnection();
|
|
39
|
+
await conn?.beginTransaction();
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
ErrorRatchet.throwFormattedErr('Tried to start a new transaction while one is already in progress : %s', this.currentTxFlag);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
async commitTransaction() {
|
|
46
|
+
if (this.currentTxFlag) {
|
|
47
|
+
Logger.info('commit a transaction : %s', this.currentTxFlag);
|
|
48
|
+
const conn = await this.myConnectionProvider.getConnection();
|
|
49
|
+
await conn?.commit();
|
|
50
|
+
this.currentTxFlag = undefined;
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
ErrorRatchet.throwFormattedErr('Cannot commit transaction - none in process');
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
async rollBackTransaction() {
|
|
57
|
+
if (this.currentTxFlag) {
|
|
58
|
+
Logger.info('rollBack a transaction : %s', this.currentTxFlag);
|
|
59
|
+
const conn = await this.myConnectionProvider.getConnection();
|
|
60
|
+
await conn?.rollback();
|
|
61
|
+
this.currentTxFlag = undefined;
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
ErrorRatchet.throwFormattedErr('Cannot rollBack transaction - none in process');
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
async buildAndExecuteUpdateOrInsertInTransaction(queryBuilder, timeoutMS = this.myQueryDefaults.timeoutMS) {
|
|
68
|
+
Logger.info('buildAndExecuteUpdateOrInsertInTransaction');
|
|
69
|
+
await this.startTransaction();
|
|
70
|
+
try {
|
|
71
|
+
const rval = await this.buildAndExecuteUpdateOrInsert(queryBuilder, timeoutMS);
|
|
72
|
+
await this.commitTransaction();
|
|
73
|
+
return rval;
|
|
74
|
+
}
|
|
75
|
+
catch (err) {
|
|
76
|
+
Logger.error('Failed - rolling back transaction : %s', err, err);
|
|
77
|
+
await this.rollBackTransaction();
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
async buildAndExecuteInTransaction(queryBuilder, timeoutMS = this.myQueryDefaults.timeoutMS) {
|
|
82
|
+
Logger.info('buildAndExecuteInTransaction');
|
|
83
|
+
await this.startTransaction();
|
|
84
|
+
try {
|
|
85
|
+
const rval = await this.buildAndExecute(queryBuilder, timeoutMS);
|
|
86
|
+
await this.commitTransaction();
|
|
87
|
+
return rval;
|
|
88
|
+
}
|
|
89
|
+
catch (err) {
|
|
90
|
+
Logger.error('Failed - rolling back transaction : %s', err, err);
|
|
91
|
+
await this.rollBackTransaction();
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
static async oneStepBuildAndExecuteUpdateOrInsertInTransaction(src, queryBuilder, timeoutMS = src.getQueryDefaults().timeoutMS, additionalConfig) {
|
|
96
|
+
let handler;
|
|
97
|
+
let rval = null;
|
|
98
|
+
try {
|
|
99
|
+
handler = await TransactionalNamedParameterMariaDbService.create(src, src.getQueryDefaults(), additionalConfig);
|
|
100
|
+
rval = await handler.buildAndExecuteUpdateOrInsertInTransaction(queryBuilder, timeoutMS);
|
|
101
|
+
}
|
|
102
|
+
catch (err) {
|
|
103
|
+
Logger.error('Failure in oneStepBuildAndExecuteUpdateOrInsertInTransaction : %j : %s', queryBuilder, err, err);
|
|
104
|
+
}
|
|
105
|
+
finally {
|
|
106
|
+
if (handler) {
|
|
107
|
+
await handler.cleanShutdown();
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return rval;
|
|
111
|
+
}
|
|
112
|
+
static async oneStepBuildAndExecuteInTransaction(src, queryBuilder, timeoutMS = src.getQueryDefaults().timeoutMS, additionalConfig) {
|
|
113
|
+
let handler;
|
|
114
|
+
let rval = null;
|
|
115
|
+
try {
|
|
116
|
+
handler = await TransactionalNamedParameterMariaDbService.create(src, src.getQueryDefaults(), additionalConfig);
|
|
117
|
+
rval = await handler.buildAndExecuteInTransaction(queryBuilder, timeoutMS);
|
|
118
|
+
}
|
|
119
|
+
catch (err) {
|
|
120
|
+
Logger.error('Failure in oneStepbuildAndExecuteInTransaction : %j : %s', queryBuilder, err, err);
|
|
121
|
+
}
|
|
122
|
+
finally {
|
|
123
|
+
if (handler) {
|
|
124
|
+
await handler.cleanShutdown();
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return rval;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { NamedParameterMariaDbService } from '../named-parameter-maria-db-service.js';
|
|
2
|
+
export declare class RelationalDatabaseUtils {
|
|
3
|
+
static uploadObjectArrayToTable<Item extends object>(db: NamedParameterMariaDbService, tableName: string, data: Item[], clearTableFirst?: boolean): Promise<number>;
|
|
4
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { RequireRatchet } from '@bitblit/ratchet-common/lib/lang/require-ratchet.js';
|
|
2
|
+
import { Logger } from '@bitblit/ratchet-common/lib/logger/logger.js';
|
|
3
|
+
import { TransactionIsolationLevel } from '../model/transaction-isolation-level.js';
|
|
4
|
+
export class RelationalDatabaseUtils {
|
|
5
|
+
static async uploadObjectArrayToTable(db, tableName, data, clearTableFirst = false) {
|
|
6
|
+
RequireRatchet.notNullOrUndefined(db, 'db');
|
|
7
|
+
RequireRatchet.notNullOrUndefined(tableName, 'tableName');
|
|
8
|
+
RequireRatchet.notNullOrUndefined(data, 'data');
|
|
9
|
+
Logger.info('Writing %d items to %s (clear: %s)', data.length, tableName, clearTableFirst);
|
|
10
|
+
if (clearTableFirst) {
|
|
11
|
+
Logger.info('Clearing table %s', tableName);
|
|
12
|
+
const { results: deleteResult } = await db.executeQueryWithMeta(TransactionIsolationLevel.Default, 'DELETE FROM ' + tableName, {});
|
|
13
|
+
Logger.info('Removed %d rows', deleteResult.affectedRows);
|
|
14
|
+
}
|
|
15
|
+
const columns = new Set(data.flatMap((row) => Object.keys(row)));
|
|
16
|
+
const colArr = Array.from(columns);
|
|
17
|
+
Logger.info('Found columns : %j', colArr);
|
|
18
|
+
const sql = 'INSERT INTO ' + tableName + ' (' + colArr.join(',') + ') VALUES :multiValue';
|
|
19
|
+
const { results: insertResult } = await db.executeQueryWithMeta(TransactionIsolationLevel.Default, sql, {
|
|
20
|
+
multiValue: data,
|
|
21
|
+
});
|
|
22
|
+
const inserted = insertResult.affectedRows;
|
|
23
|
+
Logger.info('Wrote %d rows', inserted);
|
|
24
|
+
if (inserted !== data.length) {
|
|
25
|
+
Logger.warn('Should have written %d but wrote %d', data.length, inserted);
|
|
26
|
+
}
|
|
27
|
+
return inserted;
|
|
28
|
+
}
|
|
29
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@bitblit/ratchet-rdbms",
|
|
3
|
+
"version": "4.0.115-alpha",
|
|
4
|
+
"description": "Ratchet tooling for working with relational databases",
|
|
5
|
+
"sideEffects": false,
|
|
6
|
+
"type": "module",
|
|
7
|
+
"module": "index.js",
|
|
8
|
+
"files": [
|
|
9
|
+
"lib/*",
|
|
10
|
+
"bin/*"
|
|
11
|
+
],
|
|
12
|
+
"contributors": [
|
|
13
|
+
"Christopher Weiss <bitblit@gmail.com>",
|
|
14
|
+
"Zach Herridge"
|
|
15
|
+
],
|
|
16
|
+
"husky": {
|
|
17
|
+
"hooks": {
|
|
18
|
+
"pre-commit": "pretty-quick --staged"
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"prettier": {
|
|
22
|
+
"printWidth": 140,
|
|
23
|
+
"singleQuote": true,
|
|
24
|
+
"arrowParens": "always"
|
|
25
|
+
},
|
|
26
|
+
"config": {},
|
|
27
|
+
"scripts": {
|
|
28
|
+
"watch": "tsc-watch",
|
|
29
|
+
"clean": "shx rm -Rf lib",
|
|
30
|
+
"test": "cross-env NODE_OPTIONS=--experimental-vm-modules jest",
|
|
31
|
+
"docs": "typedoc",
|
|
32
|
+
"lint": "eslint src/**/*.ts",
|
|
33
|
+
"lint-fix": "eslint --fix src/**/*.ts",
|
|
34
|
+
"__generate-barrels": "barrelsby -q --delete -d src -e .*\\.spec\\.ts",
|
|
35
|
+
"__build": "yarn run generate-barrels && tsc",
|
|
36
|
+
"build": "tsc",
|
|
37
|
+
"force-build": "tsc --build --force"
|
|
38
|
+
},
|
|
39
|
+
"repository": {
|
|
40
|
+
"type": "git",
|
|
41
|
+
"url": "https://github.com/bitblit/Ratchet"
|
|
42
|
+
},
|
|
43
|
+
"keywords": [
|
|
44
|
+
"wrench",
|
|
45
|
+
"utility"
|
|
46
|
+
],
|
|
47
|
+
"bugs": {
|
|
48
|
+
"url": "https://github.com/bitblit/Ratchet/issues"
|
|
49
|
+
},
|
|
50
|
+
"homepage": "https://github.com/bitblit/Ratchet#readme",
|
|
51
|
+
"engines": {
|
|
52
|
+
"node": ">=14.18"
|
|
53
|
+
},
|
|
54
|
+
"license": "Apache-2.0",
|
|
55
|
+
"dependencies": {
|
|
56
|
+
"@bitblit/ratchet-common": "4.0.115-alpha",
|
|
57
|
+
"mysql2": "3.3.0",
|
|
58
|
+
"tunnel-ssh": "5.0.5",
|
|
59
|
+
"get-port": "4.0.0"
|
|
60
|
+
},
|
|
61
|
+
"peerDependencies": {
|
|
62
|
+
"mysql2": "3.3.0",
|
|
63
|
+
"tunnel-ssh": "5.0.5",
|
|
64
|
+
"get-port": "4.0.0"
|
|
65
|
+
},
|
|
66
|
+
"resolutions": {},
|
|
67
|
+
"devDependencies": {}
|
|
68
|
+
}
|