Skip to content

Commit

Permalink
fix: SAP HANA driver fixes (#5343)
Browse files Browse the repository at this point in the history
* fix: fk on update should not use attributes of on delete

* int should be normalized to integer

* for default values only put single quotation mark around text types, transform sap specific boolean value

* correctly read and create full text indeces

* There is no "default null" in HANA (default null in HANA means that there is no default value).

* There is no "default null" in HANA (default null in HANA means that there is no default value). To avoid unnecessary alters when synchronizing undefined and null is equal for hana default value.

* fix hana port

* set travis tests

* Revert "set travis tests"

This reverts commit e91010b.

* add sap hana driver to circle ci

* testing CI PR build

* fix yml format errors

* circli re enable auth

* test circleCI

* wait for hana to start

* wait for hana to start

* revert hana circleci

* change to hdb pool

* add more pool parameters

* fix lint errors

* update docs

Co-authored-by: Umed Khudoiberdiev <pleerock.me@gmail.com>
  • Loading branch information
pleerock committed Jan 28, 2020
2 parents 6c6bde7 + df8d3f3 commit 41fc60f
Show file tree
Hide file tree
Showing 6 changed files with 160 additions and 76 deletions.
3 changes: 2 additions & 1 deletion README-zh_CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,8 @@ await timber.remove();

```
npm config set @sap:registry https://npm.sap.com
npm i @sap/hdbext
npm i @sap/hana-client
npm i hdb-pool
```

##### TypeScript 配置
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,8 @@ await timber.remove();

```
npm config set @sap:registry https://npm.sap.com
npm i @sap/hdbext
npm i @sap/hana-client
npm i hdb-pool
```

* for **MongoDB** (experimental)
Expand Down
25 changes: 25 additions & 0 deletions src/driver/sap/SapConnectionCredentialsOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,29 @@ export interface SapConnectionCredentialsOptions {
*/
readonly database?: string;

/**
* Encrypt database connection
*/
readonly encrypt?: boolean;

/**
* Validate database certificate
*/
readonly validateCertificate?: boolean;

/**
* Key for encrypted connection
*/
readonly key?: boolean;

/**
* Cert for encrypted connection
*/
readonly cert?: boolean;

/**
* Ca for encrypted connection
*/
readonly ca?: boolean;

}
40 changes: 40 additions & 0 deletions src/driver/sap/SapConnectionOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,44 @@ export interface SapConnectionOptions extends BaseConnectionOptions, SapConnecti
*/
readonly schema?: string;

/**
* Pool options.
*/
readonly pool?: {

/**
* Max number of connections.
*/
readonly max?: number;

/**
* Minimum number of connections.
*/
readonly min?: number;

/**
* Maximum number of waiting requests allowed. (default=0, no limit).
*/
readonly maxWaitingRequests?: number;
/**
* Max milliseconds a request will wait for a resource before timing out. (default=5000)
*/
readonly requestTimeout?: number;
/**
* How often to run resource timeout checks. (default=0, disabled)
*/
readonly checkInterval?: number;
/**
* Idle timeout
*/
readonly idleTimeout?: number;

/**
* Function handling errors thrown by drivers pool.
* Defaults to logging error with `warn` level.
*/
readonly poolErrorHandler?: (err: any) => any;

};

}
112 changes: 59 additions & 53 deletions src/driver/sap/SapDriver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export class SapDriver implements Driver {
connection: Connection;

/**
* Hana client instance.
* Hana Pool instance.
*/
client: any;

Expand Down Expand Up @@ -208,6 +208,43 @@ export class SapDriver implements Driver {
*/
async connect(): Promise<void> {
// this.master = await this.createConnection(this.options);


// HANA connection info
const dbParams = {
hostName: this.options.host,
port: this.options.port,
userName: this.options.username,
password: this.options.password,
...this.options.extra
};

if (this.options.database) dbParams.databaseName = this.options.database;
if (this.options.encrypt) dbParams.encrypt = this.options.encrypt;
if (this.options.encrypt) dbParams.validateCertificate = this.options.validateCertificate;
if (this.options.encrypt) dbParams.key = this.options.key;
if (this.options.encrypt) dbParams.cert = this.options.cert;
if (this.options.encrypt) dbParams.ca = this.options.ca;

// pool options
const options: any = {
min: this.options.pool && this.options.pool.min ? this.options.pool && this.options.pool.min : 1,
max: this.options.pool && this.options.pool.max ? this.options.pool && this.options.pool.max : 1,
};

if (this.options.pool && this.options.pool.checkInterval) options.checkInterval = this.options.pool.checkInterval;
if (this.options.pool && this.options.pool.checkInterval) options.maxWaitingRequests = this.options.pool.maxWaitingRequests;
if (this.options.pool && this.options.pool.checkInterval) options.requestTimeout = this.options.pool.requestTimeout;
if (this.options.pool && this.options.pool.checkInterval) options.idleTimeout = this.options.pool.idleTimeout;

const { logger } = this.connection;

const poolErrorHandler = options.poolErrorHandler || ((error: any) => logger.log("warn", `Postgres pool raised an error. ${error}`));
this.client.eventEmitter.on("poolError", poolErrorHandler);

// create the pool
this.master = this.client.createPool(dbParams, options);

this.database = this.options.database;
}

Expand All @@ -222,22 +259,11 @@ export class SapDriver implements Driver {
* Closes connection with the database.
*/
async disconnect(): Promise<void> {
await this.closeConnection();
const promise = this.master.clear();
this.master = undefined;
return promise;
}


/**
* Closes connection pool.
*/
protected async closeConnection(): Promise<void> {
return new Promise<void>((ok, fail) => {
if (!this.master) return ok();
this.master.disconnect((err: any) => err ? fail(err) : ok());
});
}


/**
* Creates a schema builder used to build and sync a schema.
*/
Expand Down Expand Up @@ -392,7 +418,7 @@ export class SapDriver implements Driver {
* Creates a database type from a given column metadata.
*/
normalizeType(column: { type?: ColumnType, length?: number | string, precision?: number|null, scale?: number }): string {
if (column.type === Number || column.type === "integer") {
if (column.type === Number || column.type === "int") {
return "integer";

} else if (column.type === String) {
Expand Down Expand Up @@ -430,6 +456,9 @@ export class SapDriver implements Driver {
if (typeof defaultValue === "number") {
return "" + defaultValue;

} else if (typeof defaultValue === "boolean") {
return defaultValue === true ? "true" : "false";

} else if (typeof defaultValue === "function") {
return defaultValue();

Expand Down Expand Up @@ -487,7 +516,7 @@ export class SapDriver implements Driver {
type += `(${column.precision},${column.scale})`;

} else if (column.precision !== null && column.precision !== undefined) {
type += `(${column.precision})`;
type += `(${column.precision})`;
}

if (column.isArray)
Expand All @@ -502,7 +531,7 @@ export class SapDriver implements Driver {
* If replication is not setup then returns default connection's database connection.
*/
obtainMasterConnection(): Promise<any> {
return this.createConnection();
return this.master.getConnection();
}

/**
Expand All @@ -511,8 +540,7 @@ export class SapDriver implements Driver {
* If replication is not setup then returns master (default) connection's database connection.
*/
obtainSlaveConnection(): Promise<any> {
// return Promise.resolve(this.master);
return this.createConnection();
return this.obtainMasterConnection();
}

/**
Expand Down Expand Up @@ -559,13 +587,16 @@ export class SapDriver implements Driver {
// console.log((columnMetadata.generationStrategy !== "uuid" && tableColumn.isGenerated !== columnMetadata.isGenerated));
// console.log("==========================================");

const normalizeDefault = this.normalizeDefault(columnMetadata);
const hanaNullComapatibleDefault = normalizeDefault == null ? undefined : normalizeDefault;

return tableColumn.name !== columnMetadata.databaseName
|| tableColumn.type !== this.normalizeType(columnMetadata)
|| tableColumn.length !== columnMetadata.length
|| tableColumn.precision !== columnMetadata.precision
|| tableColumn.scale !== columnMetadata.scale
// || tableColumn.comment !== columnMetadata.comment || // todo
|| (!tableColumn.isGenerated && this.normalizeDefault(columnMetadata) !== tableColumn.default) // we included check for generated here, because generated columns already can have default values
|| (!tableColumn.isGenerated && (hanaNullComapatibleDefault !== tableColumn.default)) // we included check for generated here, because generated columns already can have default values
|| tableColumn.isPrimary !== columnMetadata.isPrimary
|| tableColumn.isNullable !== columnMetadata.isNullable
|| tableColumn.isUnique !== this.normalizeIsUnique(columnMetadata)
Expand Down Expand Up @@ -603,43 +634,18 @@ export class SapDriver implements Driver {
*/
protected loadDependencies(): void {
try {
this.client = PlatformTools.load("@sap/hdbext");
this.client = PlatformTools.load("hdb-pool");

} catch (e) { // todo: better error for browser env
throw new DriverPackageNotInstalledError("SAP Hana", "hdb");
throw new DriverPackageNotInstalledError("SAP Hana", "hdb-pool");
}
}

/**
* Creates a new connection pool for a given database credentials.
*/
protected createConnection(): Promise<any> {

// pooling is enabled either when its set explicitly to true,
// either when its not defined at all (e.g. enabled by default)
return new Promise<any>((ok, fail) => {
try {
// const master = ();
this.client.createConnection({
host: this.options.host,
port: this.options.port,
uid: this.options.username,
pwd: this.options.password,
databaseName: this.options.database,
pooling: true,
...this.options.extra
}, (err: any, master: any) => {
if (err) {
fail(err);
return;
}
ok(master);
});

} catch (err) {
fail(err);
}
});
try {
PlatformTools.load("@sap/hana-client");

} catch (e) { // todo: better error for browser env
throw new DriverPackageNotInstalledError("SAP Hana", "@sap/hana-client");
}
}

}

0 comments on commit 41fc60f

Please sign in to comment.