Skip to content

Commit

Permalink
fix: pass formatOptions to Data API Client, fix extensions (#6404)
Browse files Browse the repository at this point in the history
* fix(aurora): pass formatOptions to Data API Client, fix UUID type support and all other extensions

* fix(aurora): refactored the code to avoid duplication (#1)

* fix(aurora): refactored the code to avoid duplication
  • Loading branch information
ArsenyYankovsky committed Jul 21, 2020
1 parent c6ae601 commit 9abab82
Show file tree
Hide file tree
Showing 5 changed files with 101 additions and 58 deletions.
3 changes: 1 addition & 2 deletions src/driver/DriverFactory.ts
Expand Up @@ -9,13 +9,12 @@ import {ReactNativeDriver} from "./react-native/ReactNativeDriver";
import {NativescriptDriver} from "./nativescript/NativescriptDriver";
import {SqljsDriver} from "./sqljs/SqljsDriver";
import {MysqlDriver} from "./mysql/MysqlDriver";
import {PostgresDriver} from "./postgres/PostgresDriver";
import {PostgresDriver, AuroraDataApiPostgresDriver} from "./postgres/PostgresDriver";
import {ExpoDriver} from "./expo/ExpoDriver";
import {AuroraDataApiDriver} from "./aurora-data-api/AuroraDataApiDriver";
import {Driver} from "./Driver";
import {Connection} from "../connection/Connection";
import {SapDriver} from "./sap/SapDriver";
import {AuroraDataApiPostgresDriver} from "./postgres/PostgresDriver";
import {BetterSqlite3Driver} from "./better-sqlite3/BetterSqlite3Driver";

/**
Expand Down
Expand Up @@ -33,4 +33,6 @@ export interface AuroraDataApiPostgresConnectionOptions extends BaseConnectionOp
readonly poolErrorHandler?: (err: any) => any;

readonly serviceConfigOptions?: { [key: string]: any };

readonly formatOptions?: { [key: string]: any };
}
2 changes: 2 additions & 0 deletions src/driver/aurora-data-api/AuroraDataApiConnectionOptions.ts
Expand Up @@ -23,6 +23,8 @@ export interface AuroraDataApiConnectionOptions extends BaseConnectionOptions, A

readonly serviceConfigOptions?: { [key: string]: any }; // pass optional AWS.ConfigurationOptions here

readonly formatOptions?: { [key: string]: any };

/**
* Use spatial functions like GeomFromText and AsText which are removed in MySQL 8.
* (Default: true)
Expand Down
3 changes: 2 additions & 1 deletion src/driver/aurora-data-api/AuroraDataApiDriver.ts
Expand Up @@ -306,7 +306,8 @@ export class AuroraDataApiDriver implements Driver {
this.options.resourceArn,
this.options.database,
(query: string, parameters?: any[]) => this.connection.logger.logQuery(query, parameters),
this.options.serviceConfigOptions
this.options.serviceConfigOptions,
this.options.formatOptions,
);

// validate options to make sure everything is set
Expand Down
149 changes: 94 additions & 55 deletions src/driver/postgres/PostgresDriver.ts
Expand Up @@ -301,6 +301,76 @@ export class PostgresDriver implements Driver {
* Makes any action after connection (e.g. create extensions in Postgres driver).
*/
async afterConnect(): Promise<void> {
const extensionsMetadata = await this.checkMetadataForExtensions();

if (extensionsMetadata.hasExtensions) {
await Promise.all([this.master, ...this.slaves].map(pool => {
return new Promise((ok, fail) => {
pool.connect(async (err: any, connection: any, release: Function) => {
await this.enableExtensions(extensionsMetadata, connection);
if (err) return fail(err);
release();
ok();
});
});
}));
}

return Promise.resolve();
}

protected async enableExtensions(extensionsMetadata: any, connection: any) {
const { logger } = this.connection;

const {
hasUuidColumns,
hasCitextColumns,
hasHstoreColumns,
hasCubeColumns,
hasGeometryColumns,
hasExclusionConstraints,
} = extensionsMetadata;

if (hasUuidColumns)
try {
await this.executeQuery(connection, `CREATE EXTENSION IF NOT EXISTS "${this.options.uuidExtension || "uuid-ossp"}"`);
} catch (_) {
logger.log("warn", `At least one of the entities has uuid column, but the '${this.options.uuidExtension || "uuid-ossp"}' extension cannot be installed automatically. Please install it manually using superuser rights, or select another uuid extension.`);
}
if (hasCitextColumns)
try {
await this.executeQuery(connection, `CREATE EXTENSION IF NOT EXISTS "citext"`);
} catch (_) {
logger.log("warn", "At least one of the entities has citext column, but the 'citext' extension cannot be installed automatically. Please install it manually using superuser rights");
}
if (hasHstoreColumns)
try {
await this.executeQuery(connection, `CREATE EXTENSION IF NOT EXISTS "hstore"`);
} catch (_) {
logger.log("warn", "At least one of the entities has hstore column, but the 'hstore' extension cannot be installed automatically. Please install it manually using superuser rights");
}
if (hasGeometryColumns)
try {
await this.executeQuery(connection, `CREATE EXTENSION IF NOT EXISTS "postgis"`);
} catch (_) {
logger.log("warn", "At least one of the entities has a geometry column, but the 'postgis' extension cannot be installed automatically. Please install it manually using superuser rights");
}
if (hasCubeColumns)
try {
await this.executeQuery(connection, `CREATE EXTENSION IF NOT EXISTS "cube"`);
} catch (_) {
logger.log("warn", "At least one of the entities has a cube column, but the 'cube' extension cannot be installed automatically. Please install it manually using superuser rights");
}
if (hasExclusionConstraints)
try {
// The btree_gist extension provides operator support in PostgreSQL exclusion constraints
await this.executeQuery(connection, `CREATE EXTENSION IF NOT EXISTS "btree_gist"`);
} catch (_) {
logger.log("warn", "At least one of the entities has an exclusion constraint, but the 'btree_gist' extension cannot be installed automatically. Please install it manually using superuser rights");
}
}

protected async checkMetadataForExtensions() {
const hasUuidColumns = this.connection.entityMetadatas.some(metadata => {
return metadata.generatedColumns.filter(column => column.generationStrategy === "uuid").length > 0;
});
Expand All @@ -319,57 +389,16 @@ export class PostgresDriver implements Driver {
const hasExclusionConstraints = this.connection.entityMetadatas.some(metadata => {
return metadata.exclusions.length > 0;
});
if (hasUuidColumns || hasCitextColumns || hasHstoreColumns || hasGeometryColumns || hasCubeColumns || hasExclusionConstraints) {
await Promise.all([this.master, ...this.slaves].map(pool => {
return new Promise((ok, fail) => {
pool.connect(async (err: any, connection: any, release: Function) => {
const { logger } = this.connection;
if (err) return fail(err);
if (hasUuidColumns)
try {
await this.executeQuery(connection, `CREATE EXTENSION IF NOT EXISTS "${this.options.uuidExtension || "uuid-ossp"}"`);
} catch (_) {
logger.log("warn", `At least one of the entities has uuid column, but the '${this.options.uuidExtension || "uuid-ossp"}' extension cannot be installed automatically. Please install it manually using superuser rights, or select another uuid extension.`);
}
if (hasCitextColumns)
try {
await this.executeQuery(connection, `CREATE EXTENSION IF NOT EXISTS "citext"`);
} catch (_) {
logger.log("warn", "At least one of the entities has citext column, but the 'citext' extension cannot be installed automatically. Please install it manually using superuser rights");
}
if (hasHstoreColumns)
try {
await this.executeQuery(connection, `CREATE EXTENSION IF NOT EXISTS "hstore"`);
} catch (_) {
logger.log("warn", "At least one of the entities has hstore column, but the 'hstore' extension cannot be installed automatically. Please install it manually using superuser rights");
}
if (hasGeometryColumns)
try {
await this.executeQuery(connection, `CREATE EXTENSION IF NOT EXISTS "postgis"`);
} catch (_) {
logger.log("warn", "At least one of the entities has a geometry column, but the 'postgis' extension cannot be installed automatically. Please install it manually using superuser rights");
}
if (hasCubeColumns)
try {
await this.executeQuery(connection, `CREATE EXTENSION IF NOT EXISTS "cube"`);
} catch (_) {
logger.log("warn", "At least one of the entities has a cube column, but the 'cube' extension cannot be installed automatically. Please install it manually using superuser rights");
}
if (hasExclusionConstraints)
try {
// The btree_gist extension provides operator support in PostgreSQL exclusion constraints
await this.executeQuery(connection, `CREATE EXTENSION IF NOT EXISTS "btree_gist"`);
} catch (_) {
logger.log("warn", "At least one of the entities has an exclusion constraint, but the 'btree_gist' extension cannot be installed automatically. Please install it manually using superuser rights");
}
release();
ok();
});
});
}));
}

return Promise.resolve();
return {
hasUuidColumns,
hasCitextColumns,
hasHstoreColumns,
hasCubeColumns,
hasGeometryColumns,
hasExclusionConstraints,
hasExtensions: hasUuidColumns || hasCitextColumns || hasHstoreColumns || hasGeometryColumns || hasCubeColumns || hasExclusionConstraints,
};
}

/**
Expand All @@ -395,7 +424,7 @@ export class PostgresDriver implements Driver {
/**
* Creates a query runner used to execute database queries.
*/
createQueryRunner(mode: "master"|"slave" = "master") {
createQueryRunner(mode: "master"|"slave" = "master"): QueryRunner {
return new PostgresQueryRunner(this, mode);
}

Expand Down Expand Up @@ -987,9 +1016,6 @@ abstract class PostgresWrapper extends PostgresDriver {
abstract createQueryRunner(mode: "master"|"slave"): any;
}

/**
* Organizes communication with PostgreSQL DBMS.
*/
export class AuroraDataApiPostgresDriver extends PostgresWrapper {

// -------------------------------------------------------------------------
Expand Down Expand Up @@ -1041,7 +1067,8 @@ export class AuroraDataApiPostgresDriver extends PostgresWrapper {
this.options.resourceArn,
this.options.database,
(query: string, parameters?: any[]) => this.connection.logger.logQuery(query, parameters),
this.options.serviceConfigOptions
this.options.serviceConfigOptions,
this.options.formatOptions,
);
}

Expand Down Expand Up @@ -1090,4 +1117,16 @@ export class AuroraDataApiPostgresDriver extends PostgresWrapper {
return this.client.query(query);
}

/**
* Makes any action after connection (e.g. create extensions in Postgres driver).
*/
async afterConnect(): Promise<void> {
const extensionsMetadata = await this.checkMetadataForExtensions();

if (extensionsMetadata.hasExtensions) {
await this.enableExtensions(extensionsMetadata, this.connection);
}

return Promise.resolve();
}
}

0 comments on commit 9abab82

Please sign in to comment.