Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add pre exec hook #6070

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions lib/execution/internal/ensure-connection-callback.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,24 @@ function ensureConnectionCallback(runner) {
if (Array.isArray(sql)) {
return runner.queryArray(sql);
}

if (typeof runner.client.config.preExecHook === 'function') {
return runner.client.config.preExecHook(sql).then((hookRes) => {
hookRes = hookRes || {};

if (!hookRes.hasOwnProperty('continue')) {
hookRes.continue = true;
}

if (hookRes.continue) {
return runner.query(sql);
} else {
hookRes.data = hookRes.data || [];
return Promise.resolve(hookRes.data);
}
});
}

return runner.query(sql);
}

Expand Down
95 changes: 95 additions & 0 deletions test/integration2/query/select/hooks.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
const { expect } = require('chai');
const {
Db,
getAllDbs,
getKnexForDb,
} = require('../../util/knex-instance-provider');

describe('Selects', () => {
describe('select query', () => {
getAllDbs()
// only sqlite is currently supported
.filter((db) => db === Db.SQLite)
.forEach((db) => {
describe(db, () => {
let knex;

after(() => {
return knex.destroy();
});

afterEach(async () => {
await knex.schema.dropTable('fts_products');
});

/* this is a good example for when you might have a pre hook
that checks some other cache first and returns that data on HIT
If HIT one would set continue to false to indicate not to EXEC the SQL,
but rather short circuit with cache data
*/
it('does not query DB and returns data from preExecHook', async () => {
const settings = {
preExecHook(queryObj) {
return Promise.resolve({
continue: false,
data: ['short circuit pre exec hook'],
});
},
};

knex = getKnexForDb(db, settings);
await knex.schema.raw(
'CREATE VIRTUAL TABLE fts_products USING fts5(name);'
);

const matchingRows = await knex
.select('*')
.from('fts_products')
.where('name', 'match', 'red shirt');

expect(matchingRows).to.eql(['short circuit pre exec hook']);
});

/* example preExecHook where SQL is executed as continue is true
In the above cache example in this case it would represent a MISS,
where you would fall back to EXEC SQL and then you could store response
in local cache.

Or you could use continue as true in other example for some generic logging
and such.
*/
it('defines preExecHook but returns data from db', async () => {
const settings = {
preExecHook(queryObj) {
return Promise.resolve({ continue: true });
},
};

knex = getKnexForDb(db, settings);
await knex.schema.raw(
'CREATE VIRTUAL TABLE fts_products USING fts5(name);'
);

await knex('fts_products').insert([
{ name: 'Red flannel shirt' },
{ name: 'Blue flannel shirt' },
{ name: 'Red polo shirt' },
{ name: 'Blue polo shirt' },
{ name: 'Red hooded jacket' },
{ name: 'Blue hooded jacket' },
]);

const matchingRows = await knex
.select('*')
.from('fts_products')
.where('name', 'match', 'red shirt');

expect(matchingRows).to.eql([
{ name: 'Red flannel shirt' },
{ name: 'Red polo shirt' },
]);
});
});
});
});
});
8 changes: 8 additions & 0 deletions types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2726,6 +2726,13 @@ declare namespace Knex {
nullable: boolean;
}

type PreExecHookPromiseResponse = {
continue: boolean;
data: [];
};

type PreExecHook = Promise<PreExecHookPromiseResponse>;

interface Config<SV extends {} = any> {
debug?: boolean;
client?: string | typeof Client;
Expand All @@ -2735,6 +2742,7 @@ declare namespace Knex {
connection?: string | StaticConnectionConfig | ConnectionConfigProvider;
pool?: PoolConfig;
migrations?: MigratorConfig;
preExecHook?: () => PreExecHook;
postProcessResponse?: (result: any, queryContext: any) => any;
wrapIdentifier?: (
value: string,
Expand Down