Skip to content

Commit

Permalink
fix: drop SAP statement after prepare per Hana client docs
Browse files Browse the repository at this point in the history
each statement takes up a pool so if you query frequently you can
exhaust that pool of statement handles.  this drops the statement
after we're done with it so we don't exhaust the pool of statements
  • Loading branch information
imnotjames committed Jul 11, 2021
1 parent 7d614e9 commit 6c09abd
Showing 1 changed file with 74 additions and 55 deletions.
129 changes: 74 additions & 55 deletions src/driver/sap/SapQueryRunner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -182,62 +182,81 @@ export class SapQueryRunner extends BaseQueryRunner implements QueryRunner {
}

const promise = new Promise(async (ok, fail) => {
try {
const databaseConnection = await this.connect();
// we disable autocommit because ROLLBACK does not work in autocommit mode
databaseConnection.setAutoCommit(!this.isTransactionActive);
this.driver.connection.logger.logQuery(query, parameters, this);
const queryStartTime = +new Date();
const isInsertQuery = query.substr(0, 11) === "INSERT INTO";

const statement = databaseConnection.prepare(query);
statement.exec(parameters, (err: any, result: any) => {

// log slow queries if maxQueryExecution time is set
const maxQueryExecutionTime = this.driver.connection.options.maxQueryExecutionTime;
const queryEndTime = +new Date();
const queryExecutionTime = queryEndTime - queryStartTime;
if (maxQueryExecutionTime && queryExecutionTime > maxQueryExecutionTime)
this.driver.connection.logger.logQuerySlow(queryExecutionTime, query, parameters, this);

const resolveChain = () => {
if (promiseIndex !== -1)
this.queryResponsibilityChain.splice(promiseIndex, 1);
if (waitingPromiseIndex !== -1)
this.queryResponsibilityChain.splice(waitingPromiseIndex, 1);
waitingOkay();
};

let promiseIndex = this.queryResponsibilityChain.indexOf(promise);
let waitingPromiseIndex = this.queryResponsibilityChain.indexOf(waitingPromise);
if (err) {
this.driver.connection.logger.logQueryError(err, query, parameters, this);
resolveChain();
return fail(new QueryFailedError(query, parameters, err));

} else {
if (isInsertQuery) {
const lastIdQuery = `SELECT CURRENT_IDENTITY_VALUE() FROM "SYS"."DUMMY"`;
this.driver.connection.logger.logQuery(lastIdQuery, [], this);
databaseConnection.exec(lastIdQuery, (err: any, result: { "CURRENT_IDENTITY_VALUE()": number }[]) => {
if (err) {
this.driver.connection.logger.logQueryError(err, lastIdQuery, [], this);
resolveChain();
fail(new QueryFailedError(lastIdQuery, [], err));
return;
}
ok(result[0]["CURRENT_IDENTITY_VALUE()"]);
resolveChain();
});
} else {
ok(result);
resolveChain();
}
}
});
} catch (err) {
fail(err);
const resolveChain = () => {
let promiseIndex = this.queryResponsibilityChain.indexOf(promise);
let waitingPromiseIndex = this.queryResponsibilityChain.indexOf(waitingPromise);

if (promiseIndex !== -1)
this.queryResponsibilityChain.splice(promiseIndex, 1);
if (waitingPromiseIndex !== -1)
this.queryResponsibilityChain.splice(waitingPromiseIndex, 1);

waitingOkay();
};

let statement: any;

const dropStatement = async () => {
await new Promise<void>((ok, fail) => {
if (!statement?.drop) {
ok();
}

statement.drop(() => ok());
})
}

try {
const databaseConnection = await this.connect();
// we disable autocommit because ROLLBACK does not work in autocommit mode
databaseConnection.setAutoCommit(!this.isTransactionActive);
this.driver.connection.logger.logQuery(query, parameters, this);
const queryStartTime = +new Date();
const isInsertQuery = query.substr(0, 11) === "INSERT INTO";

statement = databaseConnection.prepare(query);
statement.exec(parameters, async (err: any, result: any) => {
// log slow queries if maxQueryExecution time is set
const maxQueryExecutionTime = this.driver.connection.options.maxQueryExecutionTime;
const queryEndTime = +new Date();
const queryExecutionTime = queryEndTime - queryStartTime;
if (maxQueryExecutionTime && queryExecutionTime > maxQueryExecutionTime)
this.driver.connection.logger.logQuerySlow(queryExecutionTime, query, parameters, this);

if (err) {
this.driver.connection.logger.logQueryError(err, query, parameters, this);
await dropStatement();
resolveChain();
fail(new QueryFailedError(query, parameters, err));
return;
} else {
if (isInsertQuery) {
const lastIdQuery = `SELECT CURRENT_IDENTITY_VALUE() FROM "SYS"."DUMMY"`;
this.driver.connection.logger.logQuery(lastIdQuery, [], this);
databaseConnection.exec(lastIdQuery, async (err: any, result: { "CURRENT_IDENTITY_VALUE()": number }[]) => {
if (err) {
this.driver.connection.logger.logQueryError(err, lastIdQuery, [], this);
await dropStatement();
resolveChain();
fail(new QueryFailedError(lastIdQuery, [], err));
return;
}
await dropStatement();
ok(result[0]["CURRENT_IDENTITY_VALUE()"]);
resolveChain();
});
} else {
await dropStatement();
ok(result);
resolveChain();
}
}
});
} catch (err) {
await dropStatement();
resolveChain();
fail(err);
}
});

// with this condition, Promise.all causes unexpected behavior.
Expand Down

0 comments on commit 6c09abd

Please sign in to comment.