Skip to content

Commit 53c9222

Browse files
committedJan 30, 2025
init
1 parent 819b3bd commit 53c9222

16 files changed

+3099
-0
lines changed
 

‎NLGPipeline.ts

+829
Large diffs are not rendered by default.

‎SQLComplexity.ts

+197
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
import { SQLParser } from "./SQLParser";
2+
import {
3+
ConstituentCounts,
4+
SQLConstituentType,
5+
isQueryConstituent
6+
} from "./types"
7+
8+
9+
type QueryComplexityFormula = {
10+
[key in SQLConstituentType]?: IQueryConstituentComplexityFormula
11+
}
12+
13+
interface IQueryConstituentComplexityFormula extends QueryComplexityFormula {
14+
weight: number;
15+
diminishingReturnIntervals: Array<IDiminishingReturnInterval>
16+
}
17+
18+
interface IDiminishingReturnInterval {
19+
lowerBound: number;
20+
upperBound: number;
21+
penaltyFactor: number;
22+
}
23+
24+
const trivialComplexityIntervals = [
25+
{
26+
lowerBound: 0,
27+
upperBound: 1,
28+
penaltyFactor: 1
29+
},
30+
{
31+
lowerBound: 2,
32+
upperBound: Infinity,
33+
penaltyFactor: 0
34+
}
35+
];
36+
37+
const column = {
38+
weight: 1,
39+
diminishingReturnIntervals: trivialComplexityIntervals
40+
};
41+
const aggregate = {
42+
weight: 1.5,
43+
diminishingReturnIntervals: [
44+
{
45+
lowerBound: 0,
46+
upperBound: 1,
47+
penaltyFactor: 1
48+
},
49+
{
50+
lowerBound: 0,
51+
upperBound: Infinity,
52+
penaltyFactor: 0
53+
}
54+
]
55+
};
56+
const selectList = {
57+
column,
58+
aggregate,
59+
};
60+
61+
const innerJoin = {};
62+
const partialOuterJoin = {};
63+
const fullOuterJoin = {};
64+
const crossJoin = {};
65+
const tableReference = {
66+
innerJoin,
67+
partialOuterJoin,
68+
fullOuterJoin,
69+
crossJoin
70+
};
71+
72+
const whereClause = {};
73+
const groupBy = {};
74+
const havingClause = {};
75+
const orderBy = {};
76+
77+
const queryComplexityFormula: QueryComplexityFormula = {
78+
selectList: {
79+
...selectList,
80+
weight: 1,
81+
diminishingReturnIntervals: trivialComplexityIntervals
82+
},
83+
// tableReference,
84+
// whereClause,
85+
// groupBy,
86+
// havingClause,
87+
// orderBy
88+
};
89+
90+
interface IStepFunction {
91+
(n: number): Array<number>;
92+
};
93+
94+
export class SQLComplexity {
95+
constructor (private complexityFormula: QueryComplexityFormula, private sqlParser: SQLParser) {}
96+
97+
public computeQueryComplexity(query: string, constrainComplexity: boolean = false): number {
98+
const queryAST = this.sqlParser.parseSQLToAST([query])[0];
99+
const constituentCounts = this.sqlParser.retrieveConstituentCounts(queryAST);
100+
101+
const queryComplexity = this.calculateConstituentComplexity(this.complexityFormula, constituentCounts as unknown as ConstituentCounts);
102+
const normalizedQueryComplexity = this.normalizeConstituentComplexity(
103+
queryComplexity,
104+
this.complexityFormula,
105+
constrainComplexity ? constituentCounts : {} as ConstituentCounts
106+
)
107+
108+
return normalizedQueryComplexity;
109+
}
110+
111+
private calculateConstituentComplexity(complexityFormula: QueryComplexityFormula, constituentCounts: ConstituentCounts): number {
112+
return Object.entries(complexityFormula).reduce((sum, [queryConstituent, complexityClause]) => {
113+
const { weight, diminishingReturnIntervals, ...nestedConstituents } = complexityClause;
114+
115+
const stepFunction = this.constructStepFunction(diminishingReturnIntervals);
116+
117+
let localComplexityScore = 0;
118+
if (isQueryConstituent(queryConstituent)) {
119+
const n = constituentCounts[queryConstituent];
120+
localComplexityScore = this.calculateLocalComplexityScore(stepFunction(n), weight);
121+
}
122+
123+
return sum + localComplexityScore + this.calculateConstituentComplexity(nestedConstituents, constituentCounts);
124+
}, 0)
125+
}
126+
127+
private normalizeConstituentComplexity(queryComplexityScore: number, complexityFormula: QueryComplexityFormula, constituentCounts: ConstituentCounts) {
128+
const maxComplexityScore = this.calculateMaxComplexityScore(complexityFormula, constituentCounts);
129+
return queryComplexityScore / maxComplexityScore;
130+
}
131+
132+
private calculateMaxComplexityScore(complexityFormula: QueryComplexityFormula, constituentCounts: ConstituentCounts): number {
133+
const maxConstituentCounts = Object.keys(constituentCounts).length ?
134+
this.getMaxConstituentCounts(complexityFormula) :
135+
this.getMaxConstituentCounts(this.pruneConstituents(complexityFormula, constituentCounts));
136+
137+
return this.calculateConstituentComplexity(complexityFormula, maxConstituentCounts);
138+
}
139+
140+
private pruneConstituents(complexityFormula: QueryComplexityFormula, constituentCounts: ConstituentCounts): QueryComplexityFormula {
141+
return Object.keys(complexityFormula).reduce((prunedComplexityFormula, queryConstituent) => {
142+
// requires custom typeguard as Object.-methods don't infer type from generics due to JS-compatibility
143+
if (isQueryConstituent(queryConstituent)) {
144+
const { weight, diminishingReturnIntervals, ...nestedConstituents } = complexityFormula[queryConstituent]
145+
if (constituentCounts[queryConstituent] > 0) {
146+
prunedComplexityFormula[queryConstituent] = {
147+
weight,
148+
diminishingReturnIntervals,
149+
...this.pruneConstituents(nestedConstituents, constituentCounts)
150+
}
151+
}
152+
}
153+
return prunedComplexityFormula;
154+
}, {} as QueryComplexityFormula);
155+
}
156+
157+
private getMaxConstituentCounts(complexityFormula: QueryComplexityFormula): ConstituentCounts {
158+
return Object.entries(complexityFormula).map(([queryConstituent, complexityClause]) => {
159+
const { weight, diminishingReturnIntervals, ...nestedConstituents } = complexityClause;
160+
161+
const stepFunction = this.constructStepFunction(diminishingReturnIntervals);
162+
const maxN = diminishingReturnIntervals[diminishingReturnIntervals.length-1].lowerBound;
163+
const maxLocalComplexityScore = this.calculateLocalComplexityScore(stepFunction(maxN), weight);
164+
165+
return maxLocalComplexityScore;
166+
}) as unknown as ConstituentCounts;
167+
}
168+
169+
private calculateLocalComplexityScore(penaltyFactors: Array<number>, weight: number) {
170+
return penaltyFactors.reduce((sum, penaltyFactor) => {
171+
return sum + (weight * penaltyFactor);
172+
}, 0);
173+
}
174+
175+
private constructStepFunction(intervals: Array<IDiminishingReturnInterval>): IStepFunction {
176+
return (n: number) => {
177+
return range(n).map((i) => {
178+
for (const interval of intervals) {
179+
if (i >= interval.lowerBound && i <= interval.upperBound) {
180+
return interval.penaltyFactor;
181+
}
182+
}
183+
});
184+
}
185+
}
186+
}
187+
188+
const range = (
189+
size: number,
190+
startAt: number = 0,
191+
stepSize: number = 1
192+
): ReadonlyArray<number> => Array.from(
193+
{
194+
length: (size - startAt) / stepSize + 1
195+
},
196+
(_, i) => startAt + (i * stepSize)
197+
);

‎SQLDBReflection.ts

+68
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import { templateString } from "../../../helpers/helperFunctions";
2+
import { PgClient } from "../../../database/postgres/postgresDAO";
3+
4+
const reflectionQueries = {
5+
tables: `SELECT columns.table_name,
6+
columns.column_name,
7+
columns.data_type
8+
FROM information_schema.columns
9+
WHERE table_name in
10+
(SELECT tables.table_name
11+
FROM information_schema.tables
12+
WHERE tables.table_schema = '\${schema}'
13+
AND tables.table_name != 'schema_version'
14+
AND tables.table_type = 'BASE TABLE');`,
15+
foreignKeys: `SELECT m.relname AS source_table,
16+
(SELECT a.attname FROM pg_attribute a
17+
WHERE a.attrelid = m.oid
18+
AND a.attnum = o.conkey[1]
19+
AND a.attisdropped = false)
20+
AS source_column,
21+
f.relname AS target_table,
22+
(SELECT a.attname
23+
FROM pg_attribute a
24+
WHERE a.attrelid = f.oid
25+
AND a.attnum = o.confkey[1]
26+
AND a.attisdropped = false)
27+
AS target_column
28+
FROM pg_constraint o
29+
LEFT JOIN pg_class f ON f.oid = o.confrelid
30+
LEFT JOIN pg_class m ON m.oid = o.conrelid
31+
WHERE o.contype = 'f'
32+
AND o.conrelid IN (SELECT oid FROM pg_class c WHERE c.relkind = 'r')
33+
AND o.connamespace::regnamespace::text = '\${schema}';`,
34+
primaryKeys: `SELECT kcu.table_schema,
35+
kcu.table_name,
36+
tco.constraint_name,
37+
kcu.ordinal_position as position,
38+
kcu.column_name as key_column
39+
FROM information_schema.table_constraints tco
40+
JOIN information_schema.key_column_usage kcu
41+
ON kcu.constraint_name = tco.constraint_name
42+
AND kcu.constraint_schema = tco.constraint_schema
43+
AND kcu.constraint_name = tco.constraint_name
44+
WHERE tco.constraint_type = 'PRIMARY KEY'
45+
AND kcu.table_schema = '\${schema}'
46+
ORDER BY kcu.table_schema, kcu.table_name, position;`,
47+
};
48+
49+
export class SQLDBReflection {
50+
constructor(private schema: Array<string>, private dbClient: PgClient) {}
51+
52+
public async reflectDB() {
53+
return {
54+
foreignKeys: await this.reflectForeignKeys(),
55+
tables: await this.reflectTables(),
56+
primaryKeys: await this.reflectPrimaryKeys(),
57+
};
58+
}
59+
public async reflectForeignKeys() {
60+
return await this.dbClient.queryDB(templateString(reflectionQueries.foreignKeys, { schema: this.schema }));
61+
}
62+
public async reflectTables() {
63+
return await this.dbClient.queryDB(templateString(reflectionQueries.tables, { schema: this.schema }));
64+
}
65+
public async reflectPrimaryKeys() {
66+
return await this.dbClient.queryDB(templateString(reflectionQueries.primaryKeys, { schema: this.schema }));
67+
}
68+
}

0 commit comments

Comments
 (0)
Please sign in to comment.