Skip to content

Commit 3fc51eb

Browse files
committed
Make parameter more dynamic
1 parent 3daf561 commit 3fc51eb

File tree

10 files changed

+526
-243
lines changed

10 files changed

+526
-243
lines changed

README.md

Lines changed: 49 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -12,26 +12,56 @@ A highly versatile, fast and secured OData Version 4.01 SQL Connector which prov
1212
```Javascript
1313
import { odataSql, DbTypes } from '@slackbyte/odata-sql-connect';
1414

15+
// Inpur str for Oracle, Postgres, MySql, MsSql
16+
import { orderByStr, stringFuncs, topSkipObj } from '../tests/allTestInOutput';
17+
1518
const odataSqlPostgres = odataSql({ dbType: DbTypes.PostgreSql });
16-
//example request: GET /api/customers?$filter=City in ('Mumbai', 'London') and (Age sub 5) gt 18 and not endswith(CompanyName,'Futterkiste')
17-
app.get("/api/customers", async (req: Request, res: Response) => {
18-
const filterRes = odataSqlPostgres.createFilter(req.query.$filter);
19-
if(filterRes.error) {
20-
res.status(422).json({ message: filterRes.error });
21-
return;
22-
}
23-
const query = `SELECT * FROM customers WHERE ${filter.where}`;
24-
const queryParams = Object.fromEntries(filterRes.parameters!);
25-
const queryRes = await runSelectQuery(query, queryParams); //DB layer function
26-
if(queryRes.error) {
27-
res.status(422).json({ message: queryRes.error });
28-
return;
29-
}
30-
res.json({
31-
'@odata.context': req.protocol + '://' + req.get('host') + '/api/$metadata#customers',
32-
value: queryRes.data
33-
});
34-
});
19+
const { error: filterErr, where, parameters } = odataSqlPostgres.createFilter(stringFuncs);
20+
if (filterErr) {
21+
console.error(filterErr.message);
22+
} else {
23+
console.log(`Oracle/Postgres Example => Where clause: ${where}`);
24+
console.log(`Oracle/Postgres Example => Parameters/Bind Variables: ${Object.fromEntries(parameters)}`);
25+
}
26+
27+
const { error: orderByErr, orderBy } = odataSqlPostgres.createOrderBy(orderByStr);
28+
if (orderByErr) {
29+
console.error(orderByErr.message);
30+
} else {
31+
console.log(`Oracle/Postgres Example => orderBy clause: ${orderBy}`);
32+
}
33+
34+
const { error: topSkipErr, top, skip } = odataSqlPostgres.createTopSkip(topSkipObj);
35+
if (topSkipErr) {
36+
console.error(topSkipErr.message);
37+
} else {
38+
console.log(`Oracle/Postgres Example => Top: ${top}, Skip: ${skip}`);
39+
}
40+
41+
// FIlter with named parameter prefix
42+
43+
const odataSqlMsSqlPrefix = odataSql({ dbType: DbTypes.MsSql, namedParamPrefix: 'value' });
44+
const { error: filterPrefixErr, where: preWhere, parameters: preParameters } = odataSqlMsSqlPrefix.createFilter(stringFuncs);
45+
if (filterPrefixErr) {
46+
console.error(filterPrefixErr.message);
47+
} else {
48+
console.log(`MsSql Example => Where clause: ${preWhere}`);
49+
console.log(`MsSql Example => Parameters/Bind Variables: ${Object.fromEntries(preParameters)}`);
50+
}
51+
52+
// With Raw parameters
53+
54+
const odataSqlPostgresRaw = odataSql({ dbType: DbTypes.PostgreSql, namedParamPrefix: 'var', useRawParameters: true });
55+
const { error: filterRawErr, where: rawWhere, parameters: rawParameters } = odataSqlPostgresRaw.createFilter(stringFuncs);
56+
if (filterRawErr) {
57+
console.error(filterRawErr.message);
58+
} else {
59+
console.log(`Postgres Example => Where clause: ${rawWhere}`);
60+
const valArr = [...rawParameters.values()];
61+
console.log(`Postgres Example => Parameters/Bind Variables Values Array: ${valArr}`);
62+
console.log(`Postgres Example => Parameters/Bind Variables: ${Object.fromEntries(rawParameters)}`);
63+
}
64+
3565
```
3666

3767
## How to build

examples/filter.ts

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// Example for Oracle and Postgres
2+
import { DbTypes, odataSql } from '../src/index';
3+
import { orderByStr, stringFuncs, topSkipObj } from '../tests/allTestInOutput';
4+
5+
const odataSqlPostgres = odataSql({ dbType: DbTypes.PostgreSql });
6+
const { error: filterErr, where, parameters } = odataSqlPostgres.createFilter(stringFuncs);
7+
if (filterErr) {
8+
console.error(filterErr.message);
9+
} else {
10+
console.log(`Oracle/Postgres Example => Where clause: ${where}`);
11+
/**
12+
* ( CompanyName NOT LIKE :v0 AND CompanyName LIKE :v1 ) AND Price LIKE :v2 AND CompanyName LIKE :v3 OR strpos(CompanyName, :v4) - 1 = :v5 AND length(CompanyName) = :v6 OR SUBSTRING(CompanyName,1,2) = :v7 AND lower(CompanyName) = :v8 OR upper(CompanyName) = :v9 AND trim(CompanyName) = :v10
13+
*/
14+
console.log(`Oracle/Postgres Example => Parameters/Bind Variables: ${Object.fromEntries(parameters)}`);
15+
/**
16+
* {"v0":"'%Futterkiste'","v1":"'%freds%'","v2":"%30%","v3":"40%","v4":"'soft'","v5":1,"v6":19,"v7":"'lf'","v8":"'alfreds futterkiste'","v9":"'ALFREDS FUTTERKISTE'","v10":"'Alfreds Futterkiste'"}
17+
*/
18+
}
19+
20+
const { error: orderByErr, orderBy } = odataSqlPostgres.createOrderBy(orderByStr);
21+
if (orderByErr) {
22+
console.error(orderByErr.message);
23+
} else {
24+
console.log(`Oracle/Postgres Example => orderBy clause: ${orderBy}`);
25+
// ReleaseDate asc, Rating desc
26+
}
27+
28+
const { error: topSkipErr, top, skip } = odataSqlPostgres.createTopSkip(topSkipObj);
29+
if (topSkipErr) {
30+
console.error(topSkipErr.message);
31+
} else {
32+
console.log(`Oracle/Postgres Example => Top: ${top}, Skip: ${skip}`);
33+
// Top: LIMIT 20, Skip: OFFSET 100
34+
}
35+
36+
// FIlter with named parameter prefix
37+
38+
const odataSqlMsSqlPrefix = odataSql({ dbType: DbTypes.MsSql, namedParamPrefix: 'value' });
39+
const { error: filterPrefixErr, where: preWhere, parameters: preParameters } = odataSqlMsSqlPrefix.createFilter(stringFuncs);
40+
if (filterPrefixErr) {
41+
console.error(filterPrefixErr.message);
42+
} else {
43+
console.log(`MsSql Example => Where clause: ${preWhere}`);
44+
/**
45+
( CompanyName NOT LIKE @value0 AND CompanyName LIKE @value1 ) AND Price LIKE @value2 AND CompanyName LIKE @value3 OR CHARINDEX(@value4, CompanyName) - 1 = @value5 AND LEN(CompanyName) = @value6 OR SUBSTRING(CompanyName,1,2) = @value7 AND lower(CompanyName) = @value8 OR upper(CompanyName) = @value9 AND trim(CompanyName) = @value10
46+
*/
47+
console.log(`MsSql Example => Parameters/Bind Variables: ${Object.fromEntries(preParameters)}`);
48+
/**
49+
{"value0":"'%Futterkiste'","value1":"'%freds%'","value2":"%30%","value3":"40%","value4":"'soft'","value5":1,"value6":19,"value7":"'lf'","value8":"'alfreds futterkiste'","value9":"'ALFREDS FUTTERKISTE'","value10":"'Alfreds Futterkiste'"}
50+
*/
51+
}
52+
53+
const odataSqlPostgresRaw = odataSql({ dbType: DbTypes.MsSql, namedParamPrefix: 'var', useRawParameters: true });
54+
const { error: filterRawErr, where: rawWhere, parameters: rawParameters } = odataSqlPostgresRaw.createFilter(stringFuncs);
55+
if (filterRawErr) {
56+
console.error(filterRawErr.message);
57+
} else {
58+
console.log(`Postgres Example => Where clause: ${rawWhere}`);
59+
/**
60+
( CompanyName NOT LIKE ? AND CompanyName LIKE ? ) AND Price LIKE ? AND CompanyName LIKE ? OR CHARINDEX(?, CompanyName) - 1 = ? AND LEN(CompanyName) = ? OR SUBSTRING(CompanyName,1,2) = ? AND lower(CompanyName) = ? OR upper(CompanyName) = ? AND trim(CompanyName) = ?
61+
*/
62+
const valArr = [...rawParameters.values()];
63+
console.log(`Postgres Example => Parameters/Bind Variables Values Array: ${valArr}`);
64+
// ['%Futterkiste','%freds%',%30%,40%,'soft',1,19,'lf','alfreds futterkiste','ALFREDS FUTTERKISTE','Alfreds Futterkiste']
65+
console.log(`Postgres Example => Parameters/Bind Variables: ${Object.fromEntries(rawParameters)}`);
66+
/**
67+
{"var0":"'%Futterkiste'","var1":"'%freds%'","var2":"%30%","var3":"40%","var4":"'soft'","var5":1,"var6":19,"var7":"'lf'","var8":"'alfreds futterkiste'","var9":"'ALFREDS FUTTERKISTE'","var10":"'Alfreds Futterkiste'"}
68+
*/
69+
}

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@
1313
"lint": "eslint --ext .ts,.js .",
1414
"lint:fix": "eslint --ext .ts,.js . --fix",
1515
"clean": "rimraf ./dist ",
16-
"build": "tsc -p tsconfig.build.json",
17-
"dev": "npm run clean && npm run build && node ./dist/index.js"
16+
"build": "npm run clean && tsc -p tsconfig.build.json",
17+
"dev": "npm run build && node ./dist/index.js"
1818
},
1919
"repository": {
2020
"type": "git",

src/index.ts

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { IOdataSkipToken, IOdataTopToken, IParsedFilterRes, IParsedOrderByRes, IParsedTopRes, parseFilter, parseOrderby, parseSkip, parseTop } from '@slackbyte/odata-query-parser';
22
import { odataSqlConnector } from './lib/connector';
3-
import { orderByStr, topObj, topSkipObj } from './tests/allTestInOutput';
43

54
export enum DbTypes {
65
MsSql,
@@ -11,7 +10,26 @@ export enum DbTypes {
1110
}
1211

1312
export interface IOptions {
13+
/*
14+
Default: Postgres
15+
MsSql,
16+
MySql,
17+
PostgreSql,
18+
Oracle,
19+
BigQuery,
20+
*/
1421
dbType?: DbTypes;
22+
/**
23+
* Default: false
24+
* Set this to true if you need '?' in the where clause as a raw parameter.
25+
* Set this to false if you need named parameter in the where clause, for eg: :v1, :v2 etc
26+
*/
27+
useRawParameters?: boolean;
28+
/**
29+
* Default: 'v'
30+
* Set this to set the named parameter prefix, for example: val1, value1 etc
31+
*/
32+
namedParamPrefix?: string;
1533
}
1634

1735
export interface IConnectorRes {
@@ -21,7 +39,7 @@ export interface IConnectorRes {
2139
orderBy?: string;
2240
skip?: string;
2341
top?: string;
24-
parameters?: Map<string, string | number | boolean>;
42+
parameters: Map<string, string | number | boolean>;
2543
}
2644

2745
export interface ICreateTopSkip {
@@ -30,15 +48,17 @@ export interface ICreateTopSkip {
3048
}
3149

3250
export const dbParamVals = {
33-
oracle: ':v',
34-
msSql: '@v',
51+
oracle: ':',
52+
msSql: '@',
3553
mySql: '?',
36-
postgresql: ':v',
54+
postgresql: ':',
3755
};
3856

3957
export const odataSql = (options?: IOptions) => {
4058
const connector = odataSqlConnector(options);
41-
const odataSqlRes: IConnectorRes = {};
59+
const odataSqlRes: IConnectorRes = {
60+
parameters: new Map(),
61+
};
4262
return {
4363
createFilter: (source: string): IConnectorRes => {
4464
const tokens: IParsedFilterRes = parseFilter(source);
@@ -89,5 +109,3 @@ export const odataSql = (options?: IOptions) => {
89109
},
90110
};
91111
};
92-
93-
console.log(odataSql({ dbType: DbTypes.MsSql }).createTopSkip(topObj));

src/lib/connector.ts

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,15 @@ class ODataSqlConnector {
4747
private connectorRes: IConnectorRes;
4848
private bindCtr: number;
4949
private nextExecptedExp: string[];
50+
private useRawParameters: boolean;
51+
private namedParamPrefix: string;
5052
constructor(options?: IOptions) {
51-
this.dbType = DbTypes.PostgreSql;
52-
if (typeof options?.dbType !== 'undefined') {
53-
this.dbType = options.dbType;
54-
}
53+
this.dbType = typeof options?.dbType !== 'undefined' ? options.dbType : DbTypes.PostgreSql;
54+
// if useRawParameters is not defined => is Mysql ? true: false
55+
// if useRawParameters is defined, use its value
56+
this.useRawParameters = typeof options?.useRawParameters !== 'undefined' ? options.useRawParameters : this.dbType === DbTypes.MySql ? true : false;
57+
this.namedParamPrefix = typeof options?.namedParamPrefix !== 'undefined' ? options.namedParamPrefix : 'v';
58+
5559
this.connectorRes = {
5660
where: '',
5761
parameters: new Map(),
@@ -239,18 +243,28 @@ class ODataSqlConnector {
239243
}
240244

241245
private appendToparameters(value: string): string {
246+
//This should return either ? or named parameter like :v1 or @val2
242247
const bindVarName = this.getBindVarName();
248+
let paramName = '?';
243249
this.connectorRes.parameters?.set(bindVarName, convertDataType(value));
244-
return this.dbType === DbTypes.MySql ? dbParamVals.mySql : bindVarName;
250+
if (this.useRawParameters) {
251+
return paramName;
252+
}
253+
if (this.dbType === DbTypes.PostgreSql) {
254+
paramName = `${dbParamVals.postgresql}${bindVarName}`;
255+
} else if (this.dbType === DbTypes.MySql) {
256+
paramName = `${dbParamVals.mySql}${bindVarName}`;
257+
} else if (this.dbType === DbTypes.MsSql) {
258+
paramName = `${dbParamVals.msSql}${bindVarName}`;
259+
} else if (this.dbType === DbTypes.Oracle) {
260+
paramName = `${dbParamVals.oracle}${bindVarName}`;
261+
}
262+
return paramName;
245263
}
246264

247265
private getBindVarName(): string {
248-
let bindVarName = `${dbParamVals.postgresql}${this.bindCtr++}`;
249-
if (this.dbType === DbTypes.MySql) {
250-
bindVarName = `q${this.bindCtr}`;
251-
} else if (this.dbType === DbTypes.MsSql) {
252-
bindVarName = `${dbParamVals.msSql}${this.bindCtr}`;
253-
}
266+
const bindVarName = `${this.namedParamPrefix}${this.bindCtr}`;
267+
this.bindCtr++;
254268
return bindVarName;
255269
}
256270

0 commit comments

Comments
 (0)