agent-sql 0.2.2 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +10 -2
- package/dist/index.mjs +210 -68
- package/package.json +2 -2
package/dist/index.d.mts
CHANGED
|
@@ -15,10 +15,10 @@ type WhereGuard = GuardCol & {
|
|
|
15
15
|
};
|
|
16
16
|
type OneOrTwoDots<S extends string> = S extends `${infer A}.${infer B}.${infer C}` ? A extends `${string}.${string}` ? never : B extends `${string}.${string}` ? never : C extends `${string}.${string}` ? never : S : S extends `${infer A}.${infer B}` ? A extends `${string}.${string}` ? never : B extends `${string}.${string}` ? never : S : never;
|
|
17
17
|
type SchemaGuardKeys<T> = { [Table in keyof T & string]: `${Table}.${keyof T[Table] & string}` }[keyof T & string];
|
|
18
|
-
declare function applyGuards(ast: SelectStatement, guards: WhereGuard[], limit
|
|
18
|
+
declare function applyGuards(ast: SelectStatement, guards: WhereGuard[], limit: number): Result<SelectStatement>;
|
|
19
19
|
//#endregion
|
|
20
20
|
//#region src/output.d.ts
|
|
21
|
-
declare function outputSql(ast: SelectStatement): string;
|
|
21
|
+
declare function outputSql(ast: SelectStatement, pretty?: boolean): string;
|
|
22
22
|
//#endregion
|
|
23
23
|
//#region src/parse.d.ts
|
|
24
24
|
declare function parseSql(expr: string): Result<SelectStatement>;
|
|
@@ -26,23 +26,31 @@ declare function parseSql(expr: string): Result<SelectStatement>;
|
|
|
26
26
|
//#region src/index.d.ts
|
|
27
27
|
declare function agentSql<S extends string>(sql: string, column: S & OneOrTwoDots<S>, value: GuardVal, {
|
|
28
28
|
schema,
|
|
29
|
+
autoJoin,
|
|
29
30
|
limit,
|
|
31
|
+
pretty,
|
|
30
32
|
db,
|
|
31
33
|
allowExtraFunctions
|
|
32
34
|
}?: {
|
|
33
35
|
schema?: Schema;
|
|
36
|
+
autoJoin?: boolean;
|
|
34
37
|
limit?: number;
|
|
38
|
+
pretty?: boolean;
|
|
35
39
|
db?: DbType;
|
|
36
40
|
allowExtraFunctions?: string[];
|
|
37
41
|
}): string;
|
|
38
42
|
declare function createAgentSql<T extends Schema, S extends SchemaGuardKeys<T>>(schema: T, guards: Record<S, GuardVal>, opts: {
|
|
43
|
+
autoJoin?: boolean;
|
|
39
44
|
limit?: number;
|
|
45
|
+
pretty?: boolean;
|
|
40
46
|
throws: false;
|
|
41
47
|
db?: DbType;
|
|
42
48
|
allowExtraFunctions?: string[];
|
|
43
49
|
}): (expr: string) => Result<string>;
|
|
44
50
|
declare function createAgentSql<T extends Schema, S extends SchemaGuardKeys<T>>(schema: T, guards: Record<S, GuardVal>, opts?: {
|
|
51
|
+
autoJoin?: boolean;
|
|
45
52
|
limit?: number;
|
|
53
|
+
pretty?: boolean;
|
|
46
54
|
throws?: true;
|
|
47
55
|
db?: DbType;
|
|
48
56
|
allowExtraFunctions?: string[];
|
package/dist/index.mjs
CHANGED
|
@@ -36,6 +36,7 @@ function returnOrThrow(result, throws) {
|
|
|
36
36
|
}
|
|
37
37
|
//#endregion
|
|
38
38
|
//#region src/functions.ts
|
|
39
|
+
const DEFAULT_DB = "postgres";
|
|
39
40
|
const COMMON_FUNCTIONS = [
|
|
40
41
|
"count",
|
|
41
42
|
"sum",
|
|
@@ -773,28 +774,184 @@ function checkWhereExpr(expr, allowed) {
|
|
|
773
774
|
}
|
|
774
775
|
}
|
|
775
776
|
//#endregion
|
|
777
|
+
//#region src/joins.ts
|
|
778
|
+
function defineSchema(schema) {
|
|
779
|
+
return schema;
|
|
780
|
+
}
|
|
781
|
+
function checkJoins(ast, schema) {
|
|
782
|
+
if (schema === void 0) {
|
|
783
|
+
if (ast.joins.length > 0) return Err(new SanitiseError("No joins allowed when using simple API without schema."));
|
|
784
|
+
return Ok(ast);
|
|
785
|
+
}
|
|
786
|
+
if (!(ast.from.table.name in schema)) return Err(new SanitiseError(`Table ${ast.from.table.name} is not allowed`));
|
|
787
|
+
for (const join of ast.joins) {
|
|
788
|
+
const joinSettings = schema[join.table.name];
|
|
789
|
+
if (joinSettings === void 0) return Err(new SanitiseError(`Table ${join.table.name} is not allowed`));
|
|
790
|
+
if (join.condition === null || join.condition.type === "join_using" || join.condition.expr.type !== "where_comparison" || join.condition.expr.operator !== "=" || join.condition.expr.left.type !== "where_value" || join.condition.expr.left.kind !== "column_ref" || join.condition.expr.right.type !== "where_value" || join.condition.expr.right.kind !== "column_ref") return Err(new SanitiseError("Only JOIN ON column_ref = column_ref supported"));
|
|
791
|
+
const { joining, foreign } = getJoinTableRef(join.table.name, join.condition.expr.left.ref, join.condition.expr.right.ref);
|
|
792
|
+
const joinTableCol = joinSettings[joining.name];
|
|
793
|
+
if (joinTableCol === void 0) return Err(new SanitiseError(`Tried to join using ${join.table.name}.${joining.name}`));
|
|
794
|
+
if (joinTableCol === null) {
|
|
795
|
+
const foreignTableSettings = schema[foreign.table];
|
|
796
|
+
if (foreignTableSettings === void 0) return Err(new SanitiseError(`Table ${foreign.name} is not allowed`));
|
|
797
|
+
const foreignCol = foreignTableSettings[foreign.name];
|
|
798
|
+
if (foreignCol === void 0 || foreignCol === null) return Err(new SanitiseError(`Tried to join using ${foreign.table}.${foreign.name}`));
|
|
799
|
+
if (joining.table !== foreignCol.ft || joining.name !== foreignCol.fc) return Err(new SanitiseError(`Tried to join using ${joining.table}.${joining.name}`));
|
|
800
|
+
} else if (foreign.table !== joinTableCol.ft || foreign.name !== joinTableCol.fc) return Err(new SanitiseError(`Tried to join using ${foreign.table}.${foreign.name}`));
|
|
801
|
+
}
|
|
802
|
+
return Ok(ast);
|
|
803
|
+
}
|
|
804
|
+
function getJoinTableRef(joinTableName, left, right) {
|
|
805
|
+
if (left.table === joinTableName) return {
|
|
806
|
+
joining: left,
|
|
807
|
+
foreign: right
|
|
808
|
+
};
|
|
809
|
+
return {
|
|
810
|
+
joining: right,
|
|
811
|
+
foreign: left
|
|
812
|
+
};
|
|
813
|
+
}
|
|
814
|
+
//#endregion
|
|
815
|
+
//#region src/graph.ts
|
|
816
|
+
function insertNeededGuardJoins(ast, schema, guards, autoJoin) {
|
|
817
|
+
if (schema === void 0) return Ok(ast);
|
|
818
|
+
if (!autoJoin) return Ok(ast);
|
|
819
|
+
return resolveGraphForJoins(ast, schema, guards);
|
|
820
|
+
}
|
|
821
|
+
function resolveGraphForJoins(ast, schema, guards) {
|
|
822
|
+
const haveTables = /* @__PURE__ */ new Set();
|
|
823
|
+
haveTables.add(ast.from.table.name);
|
|
824
|
+
for (const join of ast.joins) haveTables.add(join.table.name);
|
|
825
|
+
const adj = buildAdjacency(schema);
|
|
826
|
+
const newJoins = [];
|
|
827
|
+
for (const guard of guards) {
|
|
828
|
+
if (haveTables.has(guard.table)) continue;
|
|
829
|
+
const path = bfsPath(adj, haveTables, guard.table);
|
|
830
|
+
if (path === null) return Err(new SanitiseError(`No join path from query tables to guard table '${guard.table}'`));
|
|
831
|
+
let current = [...haveTables].find((t) => path[0] && (path[0].tableA === t || path[0].tableB === t));
|
|
832
|
+
for (const edge of path) {
|
|
833
|
+
const neighbor = edge.tableA === current ? edge.tableB : edge.tableA;
|
|
834
|
+
if (!haveTables.has(neighbor)) {
|
|
835
|
+
newJoins.push(edgeToJoin(edge, neighbor));
|
|
836
|
+
haveTables.add(neighbor);
|
|
837
|
+
}
|
|
838
|
+
current = neighbor;
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
if (newJoins.length === 0) return Ok(ast);
|
|
842
|
+
return Ok({
|
|
843
|
+
...ast,
|
|
844
|
+
joins: [...ast.joins, ...newJoins]
|
|
845
|
+
});
|
|
846
|
+
}
|
|
847
|
+
function buildAdjacency(schema) {
|
|
848
|
+
const adj = /* @__PURE__ */ new Map();
|
|
849
|
+
const tables = schema;
|
|
850
|
+
for (const [tableName, cols] of Object.entries(tables)) {
|
|
851
|
+
if (!adj.has(tableName)) adj.set(tableName, []);
|
|
852
|
+
for (const [colName, def] of Object.entries(cols)) if (def && typeof def === "object" && "ft" in def && "fc" in def) {
|
|
853
|
+
const edge = {
|
|
854
|
+
tableA: tableName,
|
|
855
|
+
colA: colName,
|
|
856
|
+
tableB: def.ft,
|
|
857
|
+
colB: def.fc
|
|
858
|
+
};
|
|
859
|
+
adj.get(tableName).push(edge);
|
|
860
|
+
if (!adj.has(edge.tableB)) adj.set(edge.tableB, []);
|
|
861
|
+
adj.get(edge.tableB).push(edge);
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
return adj;
|
|
865
|
+
}
|
|
866
|
+
function bfsPath(adj, startTables, target) {
|
|
867
|
+
if (startTables.has(target)) return [];
|
|
868
|
+
const visited = new Set(startTables);
|
|
869
|
+
const queue = [];
|
|
870
|
+
for (const t of startTables) queue.push([t, []]);
|
|
871
|
+
while (queue.length > 0) {
|
|
872
|
+
const [current, path] = queue.shift();
|
|
873
|
+
for (const edge of adj.get(current) ?? []) {
|
|
874
|
+
const neighbor = edge.tableA === current ? edge.tableB : edge.tableA;
|
|
875
|
+
if (visited.has(neighbor)) continue;
|
|
876
|
+
visited.add(neighbor);
|
|
877
|
+
const newPath = [...path, edge];
|
|
878
|
+
if (neighbor === target) return newPath;
|
|
879
|
+
queue.push([neighbor, newPath]);
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
return null;
|
|
883
|
+
}
|
|
884
|
+
function edgeToJoin(edge, fromTable) {
|
|
885
|
+
const [localTable, localCol, foreignTable, foreignCol] = edge.tableA === fromTable ? [
|
|
886
|
+
edge.tableA,
|
|
887
|
+
edge.colA,
|
|
888
|
+
edge.tableB,
|
|
889
|
+
edge.colB
|
|
890
|
+
] : [
|
|
891
|
+
edge.tableB,
|
|
892
|
+
edge.colB,
|
|
893
|
+
edge.tableA,
|
|
894
|
+
edge.colA
|
|
895
|
+
];
|
|
896
|
+
return {
|
|
897
|
+
type: "join",
|
|
898
|
+
joinType: "inner",
|
|
899
|
+
table: {
|
|
900
|
+
type: "table_ref",
|
|
901
|
+
name: localTable
|
|
902
|
+
},
|
|
903
|
+
condition: {
|
|
904
|
+
type: "join_on",
|
|
905
|
+
expr: {
|
|
906
|
+
type: "where_comparison",
|
|
907
|
+
operator: "=",
|
|
908
|
+
left: {
|
|
909
|
+
type: "where_value",
|
|
910
|
+
kind: "column_ref",
|
|
911
|
+
ref: {
|
|
912
|
+
type: "column_ref",
|
|
913
|
+
table: localTable,
|
|
914
|
+
name: localCol
|
|
915
|
+
}
|
|
916
|
+
},
|
|
917
|
+
right: {
|
|
918
|
+
type: "where_value",
|
|
919
|
+
kind: "column_ref",
|
|
920
|
+
ref: {
|
|
921
|
+
type: "column_ref",
|
|
922
|
+
table: foreignTable,
|
|
923
|
+
name: foreignCol
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
};
|
|
929
|
+
}
|
|
930
|
+
//#endregion
|
|
776
931
|
//#region src/utils.ts
|
|
777
932
|
function unreachable(x) {
|
|
778
933
|
throw new Error(`Unhandled variant: ${JSON.stringify(x)}`);
|
|
779
934
|
}
|
|
780
935
|
//#endregion
|
|
781
936
|
//#region src/output.ts
|
|
782
|
-
function outputSql(ast) {
|
|
783
|
-
|
|
937
|
+
function outputSql(ast, pretty = false) {
|
|
938
|
+
const res = r(ast, pretty);
|
|
939
|
+
if (pretty) return res;
|
|
940
|
+
return normaliseWhitespace(res);
|
|
784
941
|
}
|
|
785
942
|
function normaliseWhitespace(s) {
|
|
786
943
|
return s.replace(/\s+/g, " ").trim();
|
|
787
944
|
}
|
|
788
|
-
function r(node) {
|
|
945
|
+
function r(node, pretty = false) {
|
|
789
946
|
if (node === null || node === void 0) return "";
|
|
790
947
|
if (typeof node === "string") return node;
|
|
791
948
|
if (typeof node === "number") return String(node);
|
|
792
949
|
switch (node.type) {
|
|
793
|
-
case "select": return handleSelect(node);
|
|
950
|
+
case "select": return handleSelect(node, pretty);
|
|
794
951
|
case "select_from": return handleSelectFrom(node);
|
|
795
|
-
case "join": return handleJoin(node);
|
|
952
|
+
case "join": return handleJoin(node, pretty);
|
|
796
953
|
case "join_on":
|
|
797
|
-
case "join_using": return handleJoinCondition(node);
|
|
954
|
+
case "join_using": return handleJoinCondition(node, pretty);
|
|
798
955
|
case "table_ref": return handleTableRef(node);
|
|
799
956
|
case "limit": return handleLimit(node);
|
|
800
957
|
case "offset": return handleOffset(node);
|
|
@@ -822,7 +979,7 @@ function r(node) {
|
|
|
822
979
|
case "case_expr": return handleCaseExpr(node);
|
|
823
980
|
case "cast_expr": return handleCastExpr(node);
|
|
824
981
|
case "where_value": return handleWhereValue(node);
|
|
825
|
-
case "column": return handleColumn(node);
|
|
982
|
+
case "column": return handleColumn(node, pretty);
|
|
826
983
|
case "column_expr": return handleColumnExpr(node);
|
|
827
984
|
case "column_ref": return handleColumnRef(node);
|
|
828
985
|
case "alias": return handleAlias(node);
|
|
@@ -830,23 +987,37 @@ function r(node) {
|
|
|
830
987
|
default: return unreachable(node);
|
|
831
988
|
}
|
|
832
989
|
}
|
|
833
|
-
function
|
|
834
|
-
return t.map((n) => r(n).
|
|
990
|
+
function rMap(t, pretty = false, sep = ", ") {
|
|
991
|
+
return t.map((n) => r(n, pretty).trimEnd()).join(sep);
|
|
835
992
|
}
|
|
836
|
-
function handleSelect(node) {
|
|
837
|
-
const
|
|
838
|
-
|
|
993
|
+
function handleSelect(node, pretty) {
|
|
994
|
+
const distinctStr = node.distinct ? `${r(node.distinct)} ` : "";
|
|
995
|
+
const colSep = pretty ? ",\n" : ", ";
|
|
996
|
+
const joinSep = pretty ? "\n" : " ";
|
|
997
|
+
const joiner = pretty ? "\n" : " ";
|
|
998
|
+
return [
|
|
999
|
+
"SELECT",
|
|
1000
|
+
`${distinctStr}${rMap(node.columns, pretty, colSep)}`,
|
|
1001
|
+
r(node.from),
|
|
1002
|
+
rMap(node.joins, pretty, joinSep),
|
|
1003
|
+
r(node.where),
|
|
1004
|
+
r(node.groupBy),
|
|
1005
|
+
r(node.having),
|
|
1006
|
+
r(node.orderBy),
|
|
1007
|
+
r(node.limit),
|
|
1008
|
+
r(node.offset)
|
|
1009
|
+
].join(joiner);
|
|
839
1010
|
}
|
|
840
1011
|
function handleDistinct(_node) {
|
|
841
1012
|
return "DISTINCT";
|
|
842
1013
|
}
|
|
843
1014
|
function handleDistinctOn(node) {
|
|
844
|
-
return `DISTINCT ON (${
|
|
1015
|
+
return `DISTINCT ON (${rMap(node.columns)})`;
|
|
845
1016
|
}
|
|
846
1017
|
function handleSelectFrom(node) {
|
|
847
1018
|
return `FROM ${r(node.table)}`;
|
|
848
1019
|
}
|
|
849
|
-
function handleJoin(node) {
|
|
1020
|
+
function handleJoin(node, pretty) {
|
|
850
1021
|
const typeStr = {
|
|
851
1022
|
inner: "INNER JOIN",
|
|
852
1023
|
inner_outer: "INNER OUTER JOIN",
|
|
@@ -859,12 +1030,13 @@ function handleJoin(node) {
|
|
|
859
1030
|
cross: "CROSS JOIN",
|
|
860
1031
|
natural: "NATURAL JOIN"
|
|
861
1032
|
};
|
|
862
|
-
const cond = node.condition ?
|
|
1033
|
+
const cond = node.condition ? `${handleJoinCondition(node.condition, pretty)}` : "";
|
|
863
1034
|
return `${typeStr[node.joinType]} ${r(node.table)}${cond}`;
|
|
864
1035
|
}
|
|
865
|
-
function handleJoinCondition(node) {
|
|
866
|
-
|
|
867
|
-
return
|
|
1036
|
+
function handleJoinCondition(node, pretty) {
|
|
1037
|
+
const prefix = pretty ? "\n " : " ";
|
|
1038
|
+
if (node.type === "join_on") return `${prefix}ON ${r(node.expr)}`;
|
|
1039
|
+
return `${prefix}USING (${node.columns.join(", ")})`;
|
|
868
1040
|
}
|
|
869
1041
|
function handleLimit(node) {
|
|
870
1042
|
return `LIMIT ${node.value}`;
|
|
@@ -873,13 +1045,13 @@ function handleOffset(node) {
|
|
|
873
1045
|
return `OFFSET ${node.value}`;
|
|
874
1046
|
}
|
|
875
1047
|
function handleGroupBy(node) {
|
|
876
|
-
return `GROUP BY ${
|
|
1048
|
+
return `GROUP BY ${rMap(node.items)}`;
|
|
877
1049
|
}
|
|
878
1050
|
function handleHaving(node) {
|
|
879
1051
|
return `HAVING ${r(node.expr)}`;
|
|
880
1052
|
}
|
|
881
1053
|
function handleOrderBy(node) {
|
|
882
|
-
return `ORDER BY ${
|
|
1054
|
+
return `ORDER BY ${rMap(node.items)}`;
|
|
883
1055
|
}
|
|
884
1056
|
function handleOrderByItem(node) {
|
|
885
1057
|
const dir = node.direction ? ` ${node.direction.toUpperCase()}` : "";
|
|
@@ -914,7 +1086,7 @@ function handleWhereBetween(node) {
|
|
|
914
1086
|
return `${r(node.expr)}${node.not ? " NOT" : ""} BETWEEN ${r(node.low)} AND ${r(node.high)}`;
|
|
915
1087
|
}
|
|
916
1088
|
function handleWhereIn(node) {
|
|
917
|
-
return `${r(node.expr)}${node.not ? " NOT" : ""} IN (${
|
|
1089
|
+
return `${r(node.expr)}${node.not ? " NOT" : ""} IN (${rMap(node.list)})`;
|
|
918
1090
|
}
|
|
919
1091
|
function handleCaseExpr(node) {
|
|
920
1092
|
return `CASE${node.subject ? ` ${r(node.subject)}` : ""} ${node.whens.map((w) => `WHEN ${r(w.condition)} THEN ${r(w.result)}`).join(" ")}${node.else ? ` ELSE ${r(node.else)}` : ""} END`;
|
|
@@ -959,8 +1131,8 @@ function handleWhereValue(node) {
|
|
|
959
1131
|
default: return unreachable(node);
|
|
960
1132
|
}
|
|
961
1133
|
}
|
|
962
|
-
function handleColumn(node) {
|
|
963
|
-
return `${r(node.expr)} ${r(node.alias)}`;
|
|
1134
|
+
function handleColumn(node, pretty) {
|
|
1135
|
+
return `${pretty ? " " : ""}${r(node.expr)} ${r(node.alias)}`;
|
|
964
1136
|
}
|
|
965
1137
|
function handleAlias(node) {
|
|
966
1138
|
return `AS ${node.name}`;
|
|
@@ -972,7 +1144,7 @@ function handleColumnRef(node) {
|
|
|
972
1144
|
function handleFuncCall(node) {
|
|
973
1145
|
const { name, args } = node;
|
|
974
1146
|
if (args.kind === "wildcard") return `${name}(*)`;
|
|
975
|
-
return `${name}(${args.distinct ? "DISTINCT " : ""}${
|
|
1147
|
+
return `${name}(${args.distinct ? "DISTINCT " : ""}${rMap(args.args)})`;
|
|
976
1148
|
}
|
|
977
1149
|
function handleColumnExpr(node) {
|
|
978
1150
|
switch (node.kind) {
|
|
@@ -985,7 +1157,7 @@ function handleColumnExpr(node) {
|
|
|
985
1157
|
//#endregion
|
|
986
1158
|
//#region src/guard.ts
|
|
987
1159
|
const DEFAULT_LIMIT = 1e4;
|
|
988
|
-
function applyGuards(ast, guards, limit
|
|
1160
|
+
function applyGuards(ast, guards, limit) {
|
|
989
1161
|
const ast2 = addWhereGuard(ast, guards);
|
|
990
1162
|
if (!ast2.ok) return ast2;
|
|
991
1163
|
return addLimitGuard(ast2.data, limit);
|
|
@@ -1090,44 +1262,6 @@ function tableEquals(a, b) {
|
|
|
1090
1262
|
return a.schema == b.schema && a.name == b.name;
|
|
1091
1263
|
}
|
|
1092
1264
|
//#endregion
|
|
1093
|
-
//#region src/joins.ts
|
|
1094
|
-
function defineSchema(schema) {
|
|
1095
|
-
return schema;
|
|
1096
|
-
}
|
|
1097
|
-
function checkJoins(ast, schema) {
|
|
1098
|
-
if (schema === void 0) {
|
|
1099
|
-
if (ast.joins.length > 0) return Err(new SanitiseError("No joins allowed when using simple API without schema."));
|
|
1100
|
-
return Ok(ast);
|
|
1101
|
-
}
|
|
1102
|
-
if (!(ast.from.table.name in schema)) return Err(new SanitiseError(`Table ${ast.from.table.name} is not allowed`));
|
|
1103
|
-
for (const join of ast.joins) {
|
|
1104
|
-
const joinSettings = schema[join.table.name];
|
|
1105
|
-
if (joinSettings === void 0) return Err(new SanitiseError(`Table ${join.table.name} is not allowed`));
|
|
1106
|
-
if (join.condition === null || join.condition.type === "join_using" || join.condition.expr.type !== "where_comparison" || join.condition.expr.operator !== "=" || join.condition.expr.left.type !== "where_value" || join.condition.expr.left.kind !== "column_ref" || join.condition.expr.right.type !== "where_value" || join.condition.expr.right.kind !== "column_ref") return Err(new SanitiseError("Only JOIN ON column_ref = column_ref supported"));
|
|
1107
|
-
const { joining, foreign } = getJoinTableRef(join.table.name, join.condition.expr.left.ref, join.condition.expr.right.ref);
|
|
1108
|
-
const joinTableCol = joinSettings[joining.name];
|
|
1109
|
-
if (joinTableCol === void 0) return Err(new SanitiseError(`Tried to join using ${join.table.name}.${joining.name}`));
|
|
1110
|
-
if (joinTableCol === null) {
|
|
1111
|
-
const foreignTableSettings = schema[foreign.table];
|
|
1112
|
-
if (foreignTableSettings === void 0) return Err(new SanitiseError(`Table ${foreign.name} is not allowed`));
|
|
1113
|
-
const foreignCol = foreignTableSettings[foreign.name];
|
|
1114
|
-
if (foreignCol === void 0 || foreignCol === null) return Err(new SanitiseError(`Tried to join using ${foreign.table}.${foreign.name}`));
|
|
1115
|
-
if (joining.table !== foreignCol.ft || joining.name !== foreignCol.fc) return Err(new SanitiseError(`Tried to join using ${joining.table}.${joining.name}`));
|
|
1116
|
-
} else if (foreign.table !== joinTableCol.ft || foreign.name !== joinTableCol.fc) return Err(new SanitiseError(`Tried to join using ${foreign.table}.${foreign.name}`));
|
|
1117
|
-
}
|
|
1118
|
-
return Ok(ast);
|
|
1119
|
-
}
|
|
1120
|
-
function getJoinTableRef(joinTableName, left, right) {
|
|
1121
|
-
if (left.table === joinTableName) return {
|
|
1122
|
-
joining: left,
|
|
1123
|
-
foreign: right
|
|
1124
|
-
};
|
|
1125
|
-
return {
|
|
1126
|
-
joining: right,
|
|
1127
|
-
foreign: left
|
|
1128
|
-
};
|
|
1129
|
-
}
|
|
1130
|
-
//#endregion
|
|
1131
1265
|
//#region src/sql.ohm-bundle.js
|
|
1132
1266
|
const result = makeRecipe([
|
|
1133
1267
|
"grammar",
|
|
@@ -7186,34 +7320,40 @@ function parseSql(expr) {
|
|
|
7186
7320
|
}
|
|
7187
7321
|
//#endregion
|
|
7188
7322
|
//#region src/index.ts
|
|
7189
|
-
function agentSql(sql, column, value, { schema, limit, db =
|
|
7323
|
+
function agentSql(sql, column, value, { schema, autoJoin = true, limit = DEFAULT_LIMIT, pretty = false, db = DEFAULT_DB, allowExtraFunctions = [] } = {}) {
|
|
7190
7324
|
return privateAgentSql(sql, {
|
|
7191
7325
|
guards: { [column]: value },
|
|
7192
7326
|
schema,
|
|
7327
|
+
autoJoin,
|
|
7193
7328
|
limit,
|
|
7329
|
+
pretty,
|
|
7194
7330
|
db,
|
|
7195
7331
|
allowExtraFunctions,
|
|
7196
7332
|
throws: true
|
|
7197
7333
|
});
|
|
7198
7334
|
}
|
|
7199
|
-
function createAgentSql(schema, guards, { limit,
|
|
7335
|
+
function createAgentSql(schema, guards, { autoJoin = true, limit = DEFAULT_LIMIT, pretty = false, db = DEFAULT_DB, allowExtraFunctions = [], throws = true } = {}) {
|
|
7200
7336
|
return (expr) => throws ? privateAgentSql(expr, {
|
|
7201
7337
|
guards,
|
|
7202
7338
|
schema,
|
|
7339
|
+
autoJoin,
|
|
7203
7340
|
limit,
|
|
7341
|
+
pretty,
|
|
7204
7342
|
db,
|
|
7205
7343
|
allowExtraFunctions,
|
|
7206
7344
|
throws
|
|
7207
7345
|
}) : privateAgentSql(expr, {
|
|
7208
7346
|
guards,
|
|
7209
7347
|
schema,
|
|
7348
|
+
autoJoin,
|
|
7210
7349
|
limit,
|
|
7350
|
+
pretty,
|
|
7211
7351
|
db,
|
|
7212
7352
|
allowExtraFunctions,
|
|
7213
7353
|
throws
|
|
7214
7354
|
});
|
|
7215
7355
|
}
|
|
7216
|
-
function privateAgentSql(sql, { guards: guardsRaw, schema, limit, db, allowExtraFunctions, throws }) {
|
|
7356
|
+
function privateAgentSql(sql, { guards: guardsRaw, schema, autoJoin, limit, pretty, db, allowExtraFunctions, throws }) {
|
|
7217
7357
|
const guards = resolveGuards(guardsRaw);
|
|
7218
7358
|
if (!guards.ok) throw guards.error;
|
|
7219
7359
|
const ast = parseSql(sql);
|
|
@@ -7222,9 +7362,11 @@ function privateAgentSql(sql, { guards: guardsRaw, schema, limit, db, allowExtra
|
|
|
7222
7362
|
if (!ast2.ok) return returnOrThrow(ast2, throws);
|
|
7223
7363
|
const ast3 = checkFunctions(ast2.data, db, allowExtraFunctions);
|
|
7224
7364
|
if (!ast3.ok) return returnOrThrow(ast3, throws);
|
|
7225
|
-
const
|
|
7365
|
+
const ast4 = insertNeededGuardJoins(ast3.data, schema, guards.data, autoJoin);
|
|
7366
|
+
if (!ast4.ok) return returnOrThrow(ast4, throws);
|
|
7367
|
+
const san = applyGuards(ast4.data, guards.data, limit);
|
|
7226
7368
|
if (!san.ok) return returnOrThrow(san, throws);
|
|
7227
|
-
const res = outputSql(san.data);
|
|
7369
|
+
const res = outputSql(san.data, pretty);
|
|
7228
7370
|
if (throws) return res;
|
|
7229
7371
|
return Ok(res);
|
|
7230
7372
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-sql",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "A starter for creating a TypeScript package.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agent",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"test": "vp test",
|
|
37
37
|
"check": "vp check",
|
|
38
38
|
"prepublishOnly": "vp run build",
|
|
39
|
-
"
|
|
39
|
+
"ohm": "ohm generateBundles --withTypes --esm 'src/sql.ohm'",
|
|
40
40
|
"script": "vp exec tsx"
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|