Skip to content

Commit e584297

Browse files
feat: Aurora Data API - Postgres Support (#5651)
* Data API Postgres WIP * Refactored the code to be more supportable
1 parent 2ab88c2 commit e584297

File tree

8 files changed

+298
-4
lines changed

8 files changed

+298
-4
lines changed

src/connection/ConnectionOptions.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {NativescriptConnectionOptions} from "../driver/nativescript/Nativescript
1212
import {ExpoConnectionOptions} from "../driver/expo/ExpoConnectionOptions";
1313
import {AuroraDataApiConnectionOptions} from "../driver/aurora-data-api/AuroraDataApiConnectionOptions";
1414
import {SapConnectionOptions} from "../driver/sap/SapConnectionOptions";
15+
import {AuroraDataApiPostgresConnectionOptions} from "../driver/aurora-data-api-pg/AuroraDataApiPostgresConnectionOptions";
1516

1617

1718
/**
@@ -33,4 +34,5 @@ export type ConnectionOptions =
3334
SqljsConnectionOptions|
3435
MongoConnectionOptions|
3536
AuroraDataApiConnectionOptions|
37+
AuroraDataApiPostgresConnectionOptions|
3638
ExpoConnectionOptions;

src/driver/DriverFactory.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {AuroraDataApiDriver} from "./aurora-data-api/AuroraDataApiDriver";
1515
import {Driver} from "./Driver";
1616
import {Connection} from "../connection/Connection";
1717
import {SapDriver} from "./sap/SapDriver";
18+
import {AuroraDataApiPostgresDriver} from "./postgres/PostgresDriver";
1819

1920
/**
2021
* Helps to create drivers.
@@ -57,6 +58,8 @@ export class DriverFactory {
5758
return new ExpoDriver(connection);
5859
case "aurora-data-api":
5960
return new AuroraDataApiDriver(connection);
61+
case "aurora-data-api-pg":
62+
return new AuroraDataApiPostgresDriver(connection);
6063
default:
6164
throw new MissingDriverError(type);
6265
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import {BaseConnectionOptions} from "../../connection/BaseConnectionOptions";
2+
3+
/**
4+
* Postgres-specific connection options.
5+
*/
6+
export interface AuroraDataApiPostgresConnectionOptions extends BaseConnectionOptions {
7+
8+
/**
9+
* Database type.
10+
*/
11+
readonly type: "aurora-data-api-pg";
12+
13+
readonly region: string;
14+
15+
readonly secretArn: string;
16+
17+
readonly resourceArn: string;
18+
19+
readonly database: string;
20+
21+
/**
22+
* The Postgres extension to use to generate UUID columns. Defaults to uuid-ossp.
23+
* If pgcrypto is selected, TypeORM will use the gen_random_uuid() function from this extension.
24+
* If uuid-ossp is selected, TypeORM will use the uuid_generate_v4() function from this extension.
25+
*/
26+
readonly uuidExtension?: "pgcrypto" | "uuid-ossp";
27+
28+
29+
/*
30+
* Function handling errors thrown by drivers pool.
31+
* Defaults to logging error with `warn` level.
32+
*/
33+
readonly poolErrorHandler?: (err: any) => any;
34+
}
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
import {QueryRunnerAlreadyReleasedError} from "../../error/QueryRunnerAlreadyReleasedError";
2+
import {TransactionAlreadyStartedError} from "../../error/TransactionAlreadyStartedError";
3+
import {TransactionNotStartedError} from "../../error/TransactionNotStartedError";
4+
import {QueryRunner} from "../../query-runner/QueryRunner";
5+
import {IsolationLevel} from "../types/IsolationLevel";
6+
import {AuroraDataApiPostgresDriver} from "../postgres/PostgresDriver";
7+
import {PostgresQueryRunner} from "../postgres/PostgresQueryRunner";
8+
9+
class PostgresQueryRunnerWrapper extends PostgresQueryRunner {
10+
driver: any;
11+
12+
constructor(driver: any, mode: "master"|"slave") {
13+
super(driver, mode);
14+
}
15+
}
16+
17+
/**
18+
* Runs queries on a single postgres database connection.
19+
*/
20+
export class AuroraDataApiPostgresQueryRunner extends PostgresQueryRunnerWrapper implements QueryRunner {
21+
22+
// -------------------------------------------------------------------------
23+
// Public Implemented Properties
24+
// -------------------------------------------------------------------------
25+
26+
/**
27+
* Database driver used by connection.
28+
*/
29+
driver: AuroraDataApiPostgresDriver;
30+
31+
// -------------------------------------------------------------------------
32+
// Protected Properties
33+
// -------------------------------------------------------------------------
34+
35+
/**
36+
* Promise used to obtain a database connection for a first time.
37+
*/
38+
protected databaseConnectionPromise: Promise<any>;
39+
40+
/**
41+
* Special callback provided by a driver used to release a created connection.
42+
*/
43+
protected releaseCallback: Function;
44+
45+
// -------------------------------------------------------------------------
46+
// Constructor
47+
// -------------------------------------------------------------------------
48+
49+
constructor(driver: AuroraDataApiPostgresDriver, mode: "master"|"slave" = "master") {
50+
super(driver, mode);
51+
}
52+
53+
// -------------------------------------------------------------------------
54+
// Public Methods
55+
// -------------------------------------------------------------------------
56+
57+
/**
58+
* Creates/uses database connection from the connection pool to perform further operations.
59+
* Returns obtained database connection.
60+
*/
61+
connect(): Promise<any> {
62+
if (this.databaseConnection)
63+
return Promise.resolve(this.databaseConnection);
64+
65+
if (this.databaseConnectionPromise)
66+
return this.databaseConnectionPromise;
67+
68+
if (this.mode === "slave" && this.driver.isReplicated) {
69+
this.databaseConnectionPromise = this.driver.obtainSlaveConnection().then(([ connection, release]: any[]) => {
70+
this.driver.connectedQueryRunners.push(this);
71+
this.databaseConnection = connection;
72+
this.releaseCallback = release;
73+
return this.databaseConnection;
74+
});
75+
76+
} else { // master
77+
this.databaseConnectionPromise = this.driver.obtainMasterConnection().then(([connection, release]: any[]) => {
78+
this.driver.connectedQueryRunners.push(this);
79+
this.databaseConnection = connection;
80+
this.releaseCallback = release;
81+
return this.databaseConnection;
82+
});
83+
}
84+
85+
return this.databaseConnectionPromise;
86+
}
87+
88+
/**
89+
* Starts transaction on the current connection.
90+
*/
91+
async startTransaction(isolationLevel?: IsolationLevel): Promise<void> {
92+
if (this.isTransactionActive)
93+
throw new TransactionAlreadyStartedError();
94+
95+
this.isTransactionActive = true;
96+
await this.driver.client.startTransaction();
97+
}
98+
99+
/**
100+
* Commits transaction.
101+
* Error will be thrown if transaction was not started.
102+
*/
103+
async commitTransaction(): Promise<void> {
104+
if (!this.isTransactionActive)
105+
throw new TransactionNotStartedError();
106+
107+
await this.driver.client.commitTransaction();
108+
this.isTransactionActive = false;
109+
}
110+
111+
/**
112+
* Rollbacks transaction.
113+
* Error will be thrown if transaction was not started.
114+
*/
115+
async rollbackTransaction(): Promise<void> {
116+
if (!this.isTransactionActive)
117+
throw new TransactionNotStartedError();
118+
119+
await this.driver.client.rollbackTransaction();
120+
this.isTransactionActive = false;
121+
}
122+
123+
/**
124+
* Executes a given SQL query.
125+
*/
126+
async query(query: string, parameters?: any[]): Promise<any> {
127+
if (this.isReleased)
128+
throw new QueryRunnerAlreadyReleasedError();
129+
130+
const result = await this.driver.client.query(query, parameters);
131+
132+
if (result.records) {
133+
return result.records;
134+
}
135+
136+
return result;
137+
}
138+
}

src/driver/postgres/PostgresDriver.ts

Lines changed: 117 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ import {PostgresConnectionCredentialsOptions} from "./PostgresConnectionCredenti
1919
import {EntityMetadata} from "../../metadata/EntityMetadata";
2020
import {OrmUtils} from "../../util/OrmUtils";
2121
import {ApplyValueTransformers} from "../../util/ApplyValueTransformers";
22+
import {AuroraDataApiPostgresConnectionOptions} from "../aurora-data-api-pg/AuroraDataApiPostgresConnectionOptions";
23+
import {AuroraDataApiPostgresQueryRunner} from "../aurora-data-api-pg/AuroraDataApiPostgresQueryRunner";
2224

2325
/**
2426
* Organizes communication with PostgreSQL DBMS.
@@ -248,7 +250,11 @@ export class PostgresDriver implements Driver {
248250
// Constructor
249251
// -------------------------------------------------------------------------
250252

251-
constructor(connection: Connection) {
253+
constructor(connection?: Connection) {
254+
if (!connection) {
255+
return;
256+
}
257+
252258
this.connection = connection;
253259
this.options = connection.options as PostgresConnectionOptions;
254260
this.isReplicated = this.options.replication ? true : false;
@@ -972,3 +978,113 @@ export class PostgresDriver implements Driver {
972978
}
973979

974980
}
981+
982+
abstract class PostgresWrapper extends PostgresDriver {
983+
options: any;
984+
985+
abstract createQueryRunner(mode: "master"|"slave"): any;
986+
}
987+
988+
/**
989+
* Organizes communication with PostgreSQL DBMS.
990+
*/
991+
export class AuroraDataApiPostgresDriver extends PostgresWrapper {
992+
993+
// -------------------------------------------------------------------------
994+
// Public Properties
995+
// -------------------------------------------------------------------------
996+
997+
/**
998+
* Connection used by driver.
999+
*/
1000+
connection: Connection;
1001+
1002+
/**
1003+
* Aurora Data API underlying library.
1004+
*/
1005+
DataApiDriver: any;
1006+
1007+
client: any;
1008+
1009+
// -------------------------------------------------------------------------
1010+
// Public Implemented Properties
1011+
// -------------------------------------------------------------------------
1012+
1013+
/**
1014+
* Connection options.
1015+
*/
1016+
options: AuroraDataApiPostgresConnectionOptions;
1017+
1018+
/**
1019+
* Master database used to perform all write queries.
1020+
*/
1021+
database?: string;
1022+
1023+
// -------------------------------------------------------------------------
1024+
// Constructor
1025+
// -------------------------------------------------------------------------
1026+
1027+
constructor(connection: Connection) {
1028+
super();
1029+
this.connection = connection;
1030+
this.options = connection.options as AuroraDataApiPostgresConnectionOptions;
1031+
this.isReplicated = false;
1032+
1033+
// load data-api package
1034+
this.loadDependencies();
1035+
1036+
this.client = new this.DataApiDriver(
1037+
this.options.region,
1038+
this.options.secretArn,
1039+
this.options.resourceArn,
1040+
this.options.database,
1041+
(query: string, parameters?: any[]) => this.connection.logger.logQuery(query, parameters),
1042+
);
1043+
}
1044+
1045+
// -------------------------------------------------------------------------
1046+
// Public Implemented Methods
1047+
// -------------------------------------------------------------------------
1048+
1049+
/**
1050+
* Performs connection to the database.
1051+
* Based on pooling options, it can either create connection immediately,
1052+
* either create a pool and create connection when needed.
1053+
*/
1054+
async connect(): Promise<void> {
1055+
}
1056+
1057+
/**
1058+
* Closes connection with database.
1059+
*/
1060+
async disconnect(): Promise<void> {
1061+
}
1062+
1063+
/**
1064+
* Creates a query runner used to execute database queries.
1065+
*/
1066+
createQueryRunner(mode: "master"|"slave" = "master") {
1067+
return new AuroraDataApiPostgresQueryRunner(this, mode);
1068+
}
1069+
1070+
// -------------------------------------------------------------------------
1071+
// Protected Methods
1072+
// -------------------------------------------------------------------------
1073+
1074+
/**
1075+
* If driver dependency is not given explicitly, then try to load it via "require".
1076+
*/
1077+
protected loadDependencies(): void {
1078+
const { pg } = PlatformTools.load("typeorm-aurora-data-api-driver");
1079+
1080+
this.DataApiDriver = pg;
1081+
}
1082+
1083+
/**
1084+
* Executes given query.
1085+
*/
1086+
protected executeQuery(connection: any, query: string) {
1087+
return this.client.query(query);
1088+
}
1089+
1090+
}

src/driver/types/DatabaseType.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,5 @@ export type DatabaseType =
1616
"mssql"|
1717
"mongodb"|
1818
"aurora-data-api"|
19+
"aurora-data-api-pg"|
1920
"expo";

src/error/MissingDriverError.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ export class MissingDriverError extends Error {
77
constructor(driverType: string) {
88
super();
99
Object.setPrototypeOf(this, MissingDriverError.prototype);
10-
this.message = `Wrong driver: "${driverType}" given. Supported drivers are: "cordova", "expo", "mariadb", "mongodb", "mssql", "mysql", "oracle", "postgres", "sqlite", "sqljs", "react-native".`;
10+
this.message = `Wrong driver: "${driverType}" given. Supported drivers are: "cordova", "expo", "mariadb", "mongodb", "mssql", "mysql", "oracle", "postgres", "sqlite", "sqljs", "react-native", "aurora-data-api", "aurora-data-api-pg".`;
1111
}
1212

13-
}
13+
}

test/functional/query-builder/order-by/query-builder-order-by.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,4 +135,4 @@ describe("query builder > order-by", () => {
135135
expect(loadedPost2!.num2).to.be.equal(2);
136136
})));
137137

138-
});
138+
});

0 commit comments

Comments
 (0)