@wxn0brp/db-string-query 0.0.8 → 0.0.10
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/.github/workflows/build.yml +1 -1
- package/CHANGELOG.md +22 -0
- package/bun.lock +9 -6
- package/dist/sql/handle/collection.d.ts +4 -0
- package/dist/sql/handle/collection.js +26 -0
- package/dist/sql/handle/delete.d.ts +1 -0
- package/dist/sql/handle/delete.js +11 -0
- package/dist/sql/handle/insert.d.ts +1 -0
- package/dist/sql/handle/insert.js +52 -0
- package/dist/sql/handle/select.d.ts +7 -0
- package/dist/sql/handle/select.js +55 -0
- package/dist/sql/handle/update.d.ts +1 -0
- package/dist/sql/handle/update.js +12 -0
- package/dist/sql/index.d.ts +2 -2
- package/dist/sql/index.js +10 -4
- package/dist/sql/utils/join.util.d.ts +10 -0
- package/dist/sql/utils/join.util.js +52 -0
- package/dist/sql/where.js +4 -1
- package/dist/types.d.ts +5 -1
- package/package.json +7 -2
- package/src/sql/handle/collection.ts +28 -0
- package/src/sql/handle/delete.ts +12 -0
- package/src/sql/handle/insert.ts +50 -0
- package/src/sql/handle/select.ts +68 -0
- package/src/sql/handle/update.ts +13 -0
- package/src/sql/index.ts +12 -13
- package/src/sql/utils/join.util.ts +69 -0
- package/src/sql/where.ts +3 -0
- package/src/types.ts +9 -1
- package/{src/test → test}/js.test.ts +1 -1
- package/{src/test → test}/sql/delete.test.ts +2 -2
- package/test/sql/index.test.ts +17 -0
- package/{src/test → test}/sql/insert.test.ts +2 -2
- package/{src/test → test}/sql/select.test.ts +29 -8
- package/{src/test → test}/sql/update.test.ts +2 -2
- package/test/sql/utils/join.test.ts +42 -0
- package/test/tsconfig.json +19 -0
- package/tsconfig.json +7 -3
- package/typedocs-generated/404.html +76 -0
- package/typedocs-generated/assets/hierarchy.js +1 -1
- package/typedocs-generated/assets/highlight.css +1 -1
- package/typedocs-generated/assets/navigation.js +1 -1
- package/typedocs-generated/assets/search.js +1 -1
- package/typedocs-generated/assets/style.css +3 -3
- package/typedocs-generated/classes/sql_index.default.html +2 -2
- package/typedocs-generated/classes/sql_utils_join.util.JoinToRelationsEngine.html +3 -0
- package/typedocs-generated/functions/sql_handle_collection.handleCreate.html +1 -0
- package/typedocs-generated/functions/sql_handle_collection.handleDrop.html +1 -0
- package/typedocs-generated/functions/sql_handle_collection.handleExists.html +1 -0
- package/typedocs-generated/functions/sql_handle_collection.handleGet.html +1 -0
- package/typedocs-generated/functions/sql_handle_delete.handleDelete.html +1 -0
- package/typedocs-generated/functions/sql_handle_insert.handleInsert.html +1 -0
- package/typedocs-generated/functions/sql_handle_select.handleSelect.html +1 -0
- package/typedocs-generated/functions/sql_handle_select.parseJoinClauses.html +1 -0
- package/typedocs-generated/functions/sql_handle_select.parseSelectClause.html +1 -0
- package/typedocs-generated/functions/sql_handle_update.handleUpdate.html +1 -0
- package/typedocs-generated/hierarchy.html +1 -1
- package/typedocs-generated/interfaces/types.Opts.html +3 -0
- package/typedocs-generated/interfaces/types.ValtheraParser.html +2 -2
- package/typedocs-generated/modules/index.html +1 -1
- package/typedocs-generated/modules/sql_handle_collection.html +1 -0
- package/typedocs-generated/modules/sql_handle_delete.html +1 -0
- package/typedocs-generated/modules/sql_handle_insert.html +1 -0
- package/typedocs-generated/modules/sql_handle_select.html +1 -0
- package/typedocs-generated/modules/sql_handle_update.html +1 -0
- package/typedocs-generated/modules/sql_utils_join.util.html +1 -0
- package/typedocs-generated/modules/types.html +1 -1
- package/typedocs-generated/modules.html +1 -1
- package/typedocs-generated/types/sql_utils_join.util.JoinClause.html +1 -0
- package/dist/sql/handle.d.ts +0 -12
- package/dist/sql/handle.js +0 -119
- package/dist/test/js.test.d.ts +0 -1
- package/dist/test/js.test.js +0 -85
- package/dist/test/sql/delete.test.d.ts +0 -1
- package/dist/test/sql/delete.test.js +0 -79
- package/dist/test/sql/insert.test.d.ts +0 -1
- package/dist/test/sql/insert.test.js +0 -79
- package/dist/test/sql/select.test.d.ts +0 -1
- package/dist/test/sql/select.test.js +0 -59
- package/dist/test/sql/update.test.d.ts +0 -1
- package/dist/test/sql/update.test.js +0 -71
- package/src/sql/handle.ts +0 -129
- package/typedocs-generated/functions/sql_handle.handleCreate.html +0 -1
- package/typedocs-generated/functions/sql_handle.handleDelete.html +0 -1
- package/typedocs-generated/functions/sql_handle.handleDrop.html +0 -1
- package/typedocs-generated/functions/sql_handle.handleExists.html +0 -1
- package/typedocs-generated/functions/sql_handle.handleGet.html +0 -1
- package/typedocs-generated/functions/sql_handle.handleInsert.html +0 -1
- package/typedocs-generated/functions/sql_handle.handleSelect.html +0 -1
- package/typedocs-generated/functions/sql_handle.handleUpdate.html +0 -1
- package/typedocs-generated/functions/sql_handle.parseSelectClause.html +0 -1
- package/typedocs-generated/modules/sql_handle.html +0 -1
- package/typedocs-generated/modules/test_js.test.html +0 -1
- package/typedocs-generated/modules/test_sql_delete.test.html +0 -1
- package/typedocs-generated/modules/test_sql_insert.test.html +0 -1
- package/typedocs-generated/modules/test_sql_select.test.html +0 -1
- package/typedocs-generated/modules/test_sql_update.test.html +0 -1
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { parseReturn } from "#sql/utils";
|
|
2
|
+
import { parseWhere } from "#sql/where";
|
|
3
|
+
import { Opts } from "#types.js";
|
|
4
|
+
import { JoinToRelationsEngine } from "../utils/join.util.js";
|
|
5
|
+
|
|
6
|
+
export function handleSelect(
|
|
7
|
+
query: string,
|
|
8
|
+
opts?: Opts,
|
|
9
|
+
) {
|
|
10
|
+
let whereClauseStr: string | undefined;
|
|
11
|
+
let mainQueryPart = query;
|
|
12
|
+
|
|
13
|
+
const whereIndex = query.toUpperCase().lastIndexOf(" WHERE ");
|
|
14
|
+
if (whereIndex !== -1) {
|
|
15
|
+
whereClauseStr = query.substring(whereIndex + 7).trim();
|
|
16
|
+
mainQueryPart = query.substring(0, whereIndex);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const match = mainQueryPart.match(/SELECT\s+(.+?)\s+FROM\s+([\w\/]+)((?:\s+JOIN\s+.+)*)?/i);
|
|
20
|
+
if (!match) throw new Error("Invalid SELECT syntax");
|
|
21
|
+
|
|
22
|
+
const columnsPart = match[1].trim();
|
|
23
|
+
const collection = match[2];
|
|
24
|
+
const joinPart = match[3] || "";
|
|
25
|
+
const whereClause = whereClauseStr ? parseWhere(whereClauseStr) : {};
|
|
26
|
+
|
|
27
|
+
const findOpts = parseSelectClause(columnsPart);
|
|
28
|
+
|
|
29
|
+
if (joinPart && opts?.defaultDbKey) {
|
|
30
|
+
const joinClauses = parseJoinClauses(joinPart);
|
|
31
|
+
const relationsEngine = new JoinToRelationsEngine(opts.defaultDbKey, opts.tableDbMap);
|
|
32
|
+
const relations = relationsEngine.buildRelations(joinClauses, collection);
|
|
33
|
+
const path = [opts.defaultDbKey, collection];
|
|
34
|
+
return parseReturn("relation-find", [path, whereClause, relations, findOpts]);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return parseReturn("find", [collection, whereClause, {}, findOpts]);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function parseJoinClauses(joinPart: string): Record<string, string> {
|
|
41
|
+
const joinClauses: Record<string, string> = {};
|
|
42
|
+
const joinRegex = /\s+JOIN\s+([\w\/]+)(?:\s+AS\s+)?([\w\/]+)?\s+ON\s+([^\s]+)\s*=\s*([^\s]+)/gi;
|
|
43
|
+
let match;
|
|
44
|
+
while ((match = joinRegex.exec(joinPart)) !== null) {
|
|
45
|
+
const table = match[1];
|
|
46
|
+
const alias = match[2] || table;
|
|
47
|
+
const condition = `${match[3]} = ${match[4]}`;
|
|
48
|
+
joinClauses[alias] = condition;
|
|
49
|
+
}
|
|
50
|
+
return joinClauses;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function parseSelectClause(selectClause: string): { select?: string[]; exclude?: string[] } {
|
|
54
|
+
selectClause = selectClause.trim();
|
|
55
|
+
|
|
56
|
+
if (selectClause === "*") return {};
|
|
57
|
+
|
|
58
|
+
const excludeMatch = selectClause.match(/\*\s+EXCLUDE\s+(.+)/i);
|
|
59
|
+
if (excludeMatch) {
|
|
60
|
+
return {
|
|
61
|
+
exclude: excludeMatch[1].split(/\s*,\s*/),
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return {
|
|
66
|
+
select: selectClause.split(/\s*,\s*/),
|
|
67
|
+
};
|
|
68
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { parseReturn, parseSet } from "#sql/utils";
|
|
2
|
+
import { parseWhere } from "#sql/where";
|
|
3
|
+
|
|
4
|
+
export function handleUpdate(query: string) {
|
|
5
|
+
const match = query.match(/UPDATE\s+([\w\/]+)\s+SET\s+(.+)\s+WHERE\s+(.+)/i);
|
|
6
|
+
if (!match) throw new Error("Invalid UPDATE syntax");
|
|
7
|
+
|
|
8
|
+
const collection = match[1];
|
|
9
|
+
const setClause = parseSet(match[2]);
|
|
10
|
+
const whereClause = parseWhere(match[3]);
|
|
11
|
+
|
|
12
|
+
return parseReturn("update", [collection, whereClause, setClause]);
|
|
13
|
+
}
|
package/src/sql/index.ts
CHANGED
|
@@ -1,23 +1,22 @@
|
|
|
1
|
-
import { ValtheraParser } from "../types";
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
handleGet,
|
|
8
|
-
handleInsert,
|
|
9
|
-
handleSelect,
|
|
10
|
-
handleUpdate
|
|
11
|
-
} from "./handle";
|
|
1
|
+
import { Opts, ValtheraParser } from "../types";
|
|
2
|
+
import { handleCreate, handleDrop, handleExists, handleGet } from "./handle/collection";
|
|
3
|
+
import { handleDelete } from "./handle/delete";
|
|
4
|
+
import { handleInsert } from "./handle/insert";
|
|
5
|
+
import { handleSelect } from "./handle/select";
|
|
6
|
+
import { handleUpdate } from "./handle/update";
|
|
12
7
|
|
|
13
8
|
class SQLParser implements ValtheraParser {
|
|
14
|
-
parse(
|
|
9
|
+
parse(
|
|
10
|
+
query: string,
|
|
11
|
+
opts?: Opts
|
|
12
|
+
) {
|
|
15
13
|
query = query.replace(/\s+/g, " ").trim();
|
|
14
|
+
if (query.endsWith(";")) query = query.slice(0, -1);
|
|
16
15
|
const tokens = query.split(/\s+/);
|
|
17
16
|
const method = tokens[0].toUpperCase();
|
|
18
17
|
|
|
19
18
|
if (method === "SELECT") {
|
|
20
|
-
return handleSelect(query);
|
|
19
|
+
return handleSelect(query, opts);
|
|
21
20
|
} else if (method === "INSERT") {
|
|
22
21
|
return handleInsert(query);
|
|
23
22
|
} else if (method === "UPDATE") {
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { RelationTypes } from "@wxn0brp/db-core";
|
|
2
|
+
export type JoinClause = Record<string, string>;
|
|
3
|
+
|
|
4
|
+
export class JoinToRelationsEngine {
|
|
5
|
+
private knownForeignKeys: Set<string> = new Set();
|
|
6
|
+
|
|
7
|
+
constructor(
|
|
8
|
+
private defaultDbKey: string,
|
|
9
|
+
private tableDbMap?: Record<string, string>
|
|
10
|
+
) { }
|
|
11
|
+
|
|
12
|
+
private identifyPkFk(left: string, right: string, mainTable: string, knownFks: Set<string>): { pk: string, fk: string } {
|
|
13
|
+
const [leftTable, leftField] = left.split(".");
|
|
14
|
+
const [rightTable, rightField] = right.split(".");
|
|
15
|
+
|
|
16
|
+
if (leftTable === mainTable)
|
|
17
|
+
return { pk: left, fk: right };
|
|
18
|
+
else if (rightTable === mainTable)
|
|
19
|
+
return { pk: right, fk: left };
|
|
20
|
+
|
|
21
|
+
if (knownFks.has(left))
|
|
22
|
+
return { pk: right, fk: left };
|
|
23
|
+
else if (knownFks.has(right))
|
|
24
|
+
return { pk: left, fk: right };
|
|
25
|
+
|
|
26
|
+
// If either of the above conditions are met, use a heuristic: set pk to the field named "id" or "_id"
|
|
27
|
+
if (leftField === "id" || leftField === "_id") {
|
|
28
|
+
return { pk: left, fk: right };
|
|
29
|
+
} else if (rightField === "id" || rightField === "_id") {
|
|
30
|
+
return { pk: right, fk: left };
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// If still unsure, throw an error
|
|
34
|
+
throw new Error(`Cannot determine pk/fk from condition: "${left} = ${right}"`);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
public buildRelations(
|
|
38
|
+
joinClauses: JoinClause,
|
|
39
|
+
mainTable: string
|
|
40
|
+
): RelationTypes.Relation {
|
|
41
|
+
const relations: RelationTypes.Relation = {};
|
|
42
|
+
const knownFks = new Set(this.knownForeignKeys);
|
|
43
|
+
|
|
44
|
+
for (const [alias, condition] of Object.entries(joinClauses)) {
|
|
45
|
+
const [left, right] = condition.split(/\s*=\s*/);
|
|
46
|
+
|
|
47
|
+
const { pk, fk } = this.identifyPkFk(left, right, mainTable, knownFks);
|
|
48
|
+
|
|
49
|
+
const [pkTable, pkField] = pk.split(".");
|
|
50
|
+
const [fkTable, fkField] = fk.split(".");
|
|
51
|
+
|
|
52
|
+
knownFks.add(fk);
|
|
53
|
+
|
|
54
|
+
const dbKey = this.tableDbMap?.[fkTable] || this.defaultDbKey;
|
|
55
|
+
|
|
56
|
+
relations[alias] = {
|
|
57
|
+
type: "1n",
|
|
58
|
+
path: [dbKey, fkTable],
|
|
59
|
+
pk: pkField,
|
|
60
|
+
fk: fkField,
|
|
61
|
+
as: alias,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
this.knownForeignKeys = knownFks;
|
|
66
|
+
|
|
67
|
+
return relations;
|
|
68
|
+
}
|
|
69
|
+
}
|
package/src/sql/where.ts
CHANGED
package/src/types.ts
CHANGED
|
@@ -3,6 +3,14 @@ export interface ValtheraQuery {
|
|
|
3
3
|
args: any[];
|
|
4
4
|
}
|
|
5
5
|
|
|
6
|
+
export interface Opts {
|
|
7
|
+
defaultDbKey: string;
|
|
8
|
+
tableDbMap?: Record<string, string>;
|
|
9
|
+
}
|
|
10
|
+
|
|
6
11
|
export interface ValtheraParser {
|
|
7
|
-
parse(
|
|
12
|
+
parse(
|
|
13
|
+
query: string,
|
|
14
|
+
opts?: Opts,
|
|
15
|
+
): ValtheraQuery;
|
|
8
16
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
import SQLParser from "#sql";
|
|
1
2
|
import { describe, expect, test } from "bun:test";
|
|
2
|
-
import { ValtheraDbParsers } from "../..";
|
|
3
3
|
|
|
4
|
-
const sqlParser = new
|
|
4
|
+
const sqlParser = new SQLParser();
|
|
5
5
|
|
|
6
6
|
describe("SQL Parser - DELETE", () => {
|
|
7
7
|
test("should parse a simple DELETE query", () => {
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import SQLParser from "#sql";
|
|
2
|
+
import { describe, expect, test } from "bun:test";
|
|
3
|
+
|
|
4
|
+
const sqlParser = new SQLParser();
|
|
5
|
+
|
|
6
|
+
describe("SQL Parser - SELECT", () => {
|
|
7
|
+
test("should parse with ;", () => {
|
|
8
|
+
const query = "SELECT * FROM users WHERE id = 1;";
|
|
9
|
+
const parsedQuery = sqlParser.parse(query);
|
|
10
|
+
|
|
11
|
+
expect(parsedQuery).toBeDefined();
|
|
12
|
+
expect(parsedQuery.method).toBe("find");
|
|
13
|
+
expect(parsedQuery.args).toHaveLength(4);
|
|
14
|
+
expect(parsedQuery.args[0]).toBe("users"); // collection name
|
|
15
|
+
expect(parsedQuery.args[1]).toEqual({ id: 1 }); // where clause
|
|
16
|
+
});
|
|
17
|
+
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
import SQLParser from "#sql";
|
|
1
2
|
import { describe, expect, test } from "bun:test";
|
|
2
|
-
import { ValtheraDbParsers } from "../..";
|
|
3
3
|
|
|
4
|
-
const sqlParser = new
|
|
4
|
+
const sqlParser = new SQLParser();
|
|
5
5
|
|
|
6
6
|
describe("SQL Parser - INSERT", () => {
|
|
7
7
|
test("should parse a simple INSERT query", () => {
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
+
import SQLParser from "#sql";
|
|
1
2
|
import { describe, expect, test } from "bun:test";
|
|
2
|
-
import { ValtheraDbParsers } from "../..";
|
|
3
3
|
|
|
4
|
-
const sqlParser = new
|
|
4
|
+
const sqlParser = new SQLParser();
|
|
5
5
|
|
|
6
6
|
describe("SQL Parser - SELECT", () => {
|
|
7
|
-
test("should parse a simple SELECT query", () => {
|
|
7
|
+
test("1. should parse a simple SELECT query", () => {
|
|
8
8
|
const query = "SELECT * FROM users WHERE id = 1";
|
|
9
9
|
const parsedQuery = sqlParser.parse(query);
|
|
10
10
|
|
|
@@ -15,7 +15,7 @@ describe("SQL Parser - SELECT", () => {
|
|
|
15
15
|
expect(parsedQuery.args[1]).toEqual({ id: 1 }); // where clause
|
|
16
16
|
});
|
|
17
17
|
|
|
18
|
-
test("should parse a SELECT query with specific columns", () => {
|
|
18
|
+
test("2. should parse a SELECT query with specific columns", () => {
|
|
19
19
|
const query = "SELECT name, email FROM users WHERE active = 1";
|
|
20
20
|
const parsedQuery = sqlParser.parse(query);
|
|
21
21
|
|
|
@@ -27,7 +27,7 @@ describe("SQL Parser - SELECT", () => {
|
|
|
27
27
|
expect(parsedQuery.args[3]).toEqual({ select: ["name", "email"] }); // select options
|
|
28
28
|
});
|
|
29
29
|
|
|
30
|
-
test("should parse a SELECT query without WHERE clause", () => {
|
|
30
|
+
test("3. should parse a SELECT query without WHERE clause", () => {
|
|
31
31
|
const query = "SELECT * FROM users";
|
|
32
32
|
const parsedQuery = sqlParser.parse(query);
|
|
33
33
|
|
|
@@ -38,7 +38,7 @@ describe("SQL Parser - SELECT", () => {
|
|
|
38
38
|
expect(parsedQuery.args[1]).toEqual({}); // empty where clause
|
|
39
39
|
});
|
|
40
40
|
|
|
41
|
-
test("should parse a SELECT query with EXCLUDE clause", () => {
|
|
41
|
+
test("4. should parse a SELECT query with EXCLUDE clause", () => {
|
|
42
42
|
const query = "SELECT * EXCLUDE password, createdAt FROM users WHERE active = 1";
|
|
43
43
|
const parsedQuery = sqlParser.parse(query);
|
|
44
44
|
|
|
@@ -50,7 +50,7 @@ describe("SQL Parser - SELECT", () => {
|
|
|
50
50
|
expect(parsedQuery.args[3]).toEqual({ exclude: ["password", "createdAt"] }); // exclude options
|
|
51
51
|
});
|
|
52
52
|
|
|
53
|
-
test("should parse a SELECT query with complex WHERE conditions", () => {
|
|
53
|
+
test("5. should parse a SELECT query with complex WHERE conditions", () => {
|
|
54
54
|
const query = "SELECT * FROM users WHERE age > 18 AND status = 'active'";
|
|
55
55
|
const parsedQuery = sqlParser.parse(query);
|
|
56
56
|
|
|
@@ -61,7 +61,28 @@ describe("SQL Parser - SELECT", () => {
|
|
|
61
61
|
expect(parsedQuery.args[1]).toEqual({ $gt: { age: 18 }, status: "active" }); // where clause
|
|
62
62
|
});
|
|
63
63
|
|
|
64
|
-
test("should
|
|
64
|
+
test("6. should parse a SELECT query with a JOIN clause", () => {
|
|
65
|
+
const query = "SELECT posts.*, users.name FROM posts JOIN users ON posts.userId = users.id WHERE posts.id = 1";
|
|
66
|
+
const parsedQuery = sqlParser.parse(query, { defaultDbKey: "db" });
|
|
67
|
+
|
|
68
|
+
expect(parsedQuery).toBeDefined();
|
|
69
|
+
expect(parsedQuery.method).toBe("relation-find");
|
|
70
|
+
expect(parsedQuery.args).toHaveLength(4);
|
|
71
|
+
expect(parsedQuery.args[0]).toEqual(["db", "posts"]); // path
|
|
72
|
+
expect(parsedQuery.args[1]).toEqual({ id: 1 }); // where clause
|
|
73
|
+
expect(parsedQuery.args[2]).toEqual({
|
|
74
|
+
users: {
|
|
75
|
+
type: '1n',
|
|
76
|
+
path: ['db', 'users'],
|
|
77
|
+
pk: 'userId',
|
|
78
|
+
fk: 'id',
|
|
79
|
+
as: 'users'
|
|
80
|
+
}
|
|
81
|
+
}); // relations
|
|
82
|
+
expect(parsedQuery.args[3]).toEqual({ select: ["posts.*", "users.name"] }); // select options
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
test("7. should throw error for invalid SELECT syntax", () => {
|
|
65
86
|
const query = "SELECT FROM users";
|
|
66
87
|
|
|
67
88
|
expect(() => {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
import SQLParser from "#sql";
|
|
1
2
|
import { describe, expect, test } from "bun:test";
|
|
2
|
-
import { ValtheraDbParsers } from "../..";
|
|
3
3
|
|
|
4
|
-
const sqlParser = new
|
|
4
|
+
const sqlParser = new SQLParser();
|
|
5
5
|
|
|
6
6
|
describe("SQL Parser - UPDATE", () => {
|
|
7
7
|
test("should parse a simple UPDATE query", () => {
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { JoinClause, JoinToRelationsEngine } from "#sql/utils/join.util";
|
|
2
|
+
import { describe, expect, test } from "bun:test";
|
|
3
|
+
|
|
4
|
+
describe("SQL Utils - Join", () => {
|
|
5
|
+
test("works", () => {
|
|
6
|
+
const joinClauses: JoinClause = {
|
|
7
|
+
comments: "posts.id = comments.post_id",
|
|
8
|
+
users: "comments.user_id = users.id",
|
|
9
|
+
tags: "posts.id = tags.post_id"
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const engine = new JoinToRelationsEngine(
|
|
13
|
+
"defaultDb",
|
|
14
|
+
{ comments: "blogDb", users: "userDb" }
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
const relations = engine.buildRelations(joinClauses, "posts");
|
|
18
|
+
|
|
19
|
+
expect(relations).toBeDefined();
|
|
20
|
+
expect(relations.comments).toEqual({
|
|
21
|
+
type: "1n",
|
|
22
|
+
path: ["blogDb", "comments"],
|
|
23
|
+
pk: "id",
|
|
24
|
+
fk: "post_id",
|
|
25
|
+
as: "comments",
|
|
26
|
+
})
|
|
27
|
+
expect(relations.users).toEqual({
|
|
28
|
+
type: "1n",
|
|
29
|
+
path: ["blogDb", "comments"],
|
|
30
|
+
pk: "id",
|
|
31
|
+
fk: "user_id",
|
|
32
|
+
as: "users",
|
|
33
|
+
})
|
|
34
|
+
expect(relations.tags).toEqual({
|
|
35
|
+
type: "1n",
|
|
36
|
+
path: ["defaultDb", "tags"],
|
|
37
|
+
pk: "id",
|
|
38
|
+
fk: "post_id",
|
|
39
|
+
as: "tags",
|
|
40
|
+
})
|
|
41
|
+
});
|
|
42
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"module": "ES2022",
|
|
4
|
+
"target": "ES2022",
|
|
5
|
+
"moduleResolution": "bundler",
|
|
6
|
+
"paths": {
|
|
7
|
+
"#*": [
|
|
8
|
+
"../src/*"
|
|
9
|
+
]
|
|
10
|
+
},
|
|
11
|
+
"esModuleInterop": true,
|
|
12
|
+
"skipLibCheck": true,
|
|
13
|
+
"outDir": "./dist",
|
|
14
|
+
"declaration": true
|
|
15
|
+
},
|
|
16
|
+
"include": [
|
|
17
|
+
"."
|
|
18
|
+
]
|
|
19
|
+
}
|
package/tsconfig.json
CHANGED
|
@@ -3,7 +3,11 @@
|
|
|
3
3
|
"module": "ES2022",
|
|
4
4
|
"target": "ES2022",
|
|
5
5
|
"moduleResolution": "bundler",
|
|
6
|
-
"paths": {
|
|
6
|
+
"paths": {
|
|
7
|
+
"#*": [
|
|
8
|
+
"./src/*"
|
|
9
|
+
]
|
|
10
|
+
},
|
|
7
11
|
"esModuleInterop": true,
|
|
8
12
|
"skipLibCheck": true,
|
|
9
13
|
"outDir": "./dist",
|
|
@@ -11,7 +15,7 @@
|
|
|
11
15
|
"declaration": true
|
|
12
16
|
},
|
|
13
17
|
"include": [
|
|
14
|
-
|
|
18
|
+
"./src"
|
|
15
19
|
],
|
|
16
20
|
"exclude": [
|
|
17
21
|
"node_modules"
|
|
@@ -20,4 +24,4 @@
|
|
|
20
24
|
"resolveFullPaths": true,
|
|
21
25
|
"verbose": false
|
|
22
26
|
}
|
|
23
|
-
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
|
|
4
|
+
<head>
|
|
5
|
+
<meta charset="UTF-8">
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
7
|
+
<title>404 - Page Not Found</title>
|
|
8
|
+
<style>
|
|
9
|
+
body {
|
|
10
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
|
|
11
|
+
display: flex;
|
|
12
|
+
justify-content: center;
|
|
13
|
+
align-items: center;
|
|
14
|
+
min-height: 100vh;
|
|
15
|
+
margin: 0;
|
|
16
|
+
background-color: #121212;
|
|
17
|
+
color: #e0e0e0;
|
|
18
|
+
text-align: center;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.container {
|
|
22
|
+
padding: 20px;
|
|
23
|
+
border-radius: 8px;
|
|
24
|
+
background-color: #1e1e1e;
|
|
25
|
+
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
h1 {
|
|
29
|
+
font-size: 3em;
|
|
30
|
+
color: #cf6679;
|
|
31
|
+
margin-bottom: 10px;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
p {
|
|
35
|
+
font-size: 1.2em;
|
|
36
|
+
margin-bottom: 20px;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
a {
|
|
40
|
+
color: #bb86fc;
|
|
41
|
+
text-decoration: none;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
a:hover {
|
|
45
|
+
text-decoration: underline;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
#go-back {
|
|
49
|
+
background-color: #03dac6;
|
|
50
|
+
color: #121212;
|
|
51
|
+
border: none;
|
|
52
|
+
padding: 10px 20px;
|
|
53
|
+
border-radius: 4px;
|
|
54
|
+
cursor: pointer;
|
|
55
|
+
font-size: 1em;
|
|
56
|
+
margin-top: 10px;
|
|
57
|
+
}
|
|
58
|
+
</style>
|
|
59
|
+
</head>
|
|
60
|
+
|
|
61
|
+
<body>
|
|
62
|
+
<div class="container">
|
|
63
|
+
<h1>404</h1>
|
|
64
|
+
<p>Oops! The page you're looking for doesn't exist.</p>
|
|
65
|
+
<p>You might have mistyped the address or the page may have moved.</p>
|
|
66
|
+
<p><a id="home-link" href="/">Go to Homepage</a></p>
|
|
67
|
+
<button onclick="history.back()" id="go-back">Go Back</button>
|
|
68
|
+
</div>
|
|
69
|
+
|
|
70
|
+
<script type="module">
|
|
71
|
+
const segments = window.location.pathname.split("/").filter(Boolean);
|
|
72
|
+
document.querySelector("#home-link").href = "/" + (segments.length > 1 ? segments[0] : "");
|
|
73
|
+
</script>
|
|
74
|
+
</body>
|
|
75
|
+
|
|
76
|
+
</html>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
window.hierarchyData = "eJyVjssKwjAQRf/
|
|
1
|
+
window.hierarchyData = "eJyVjssKwjAQRf/lrmPLCIrJV7hyI0VCM6XRmGomgiL9d6mPogtBN7OYmXvuuSJ1XRaYNelKIXETuM6+iwJzBelhRrtnGKxsyC0nu7RJOEFh56ODmc7mCqcUYOBj5tTYmqXMlwNL8Rkp2rwPUKiDFYFBFjcZGJMxNxxbH1ziCLPWWhFR1Svodw/HjT2FPArQdPESuJNZyq0Uz6+vnY9Fr0BE/8HlGDY+Oj7/2tH3Nx6zdwM="
|
|
@@ -1 +1 @@
|
|
|
1
|
-
window.navigationData = "
|
|
1
|
+
window.navigationData = "eJylll9P2zAUxb+L9xoxGgobfaUIbS/bgLEHVFVufLuYGSfYDhQhvvsUu03sxHEc8Rqf8/O5vv6T+zekYKfQAqEElVjlaIEeC1IxkJ+PcvXIUIL+UU7QIk1QllNGBHC0uG9sd5ipHARebn5iIUHIlvOMBcUbTeqpXPZJ+p54iNfAsKIFH2M2uh51ZXEpJ7DrV6k/f7TUPuzT815NNmWjNvj57Hx+cjyPLTnIFpbepdulP3hwDzKqaAJbXDHV+jOGpTT+/ZjLmaVfnbnlUz3mQ+eYEwYDg1nBGGR1af3o8omtjXndyqKqMa4LAVhBy91WXCMGyZbLnebM7qKRLUVRTkXXnlHw5Y5KJaeijWsUfgVqKvkKVA9rN54AA3uVPd0zkgmdW3aY3qQHquUIBqVcglDBoEYyIei3DtMb9EC1HMGgEuoOBIMayYSgNx2mN+iBajkCO0rfd98Lyi8YriSMbdk9vOsam8DEMOIJM9i24GJXJcEj29dIJiz27w7Tm/ZAtRyeoBEvW82Mf90GL/oWE3XfV4oyz3NTU/RQVBjdqmtQleBDq2VglnB8wwxuc4t147nVrIe0oPyoVgcKXDeiqFLrLX9bNO/+Jf9LOfi70MV7rf3uJM5c3ROjXssA3ndQ0uPzL7PTtLMJX3IQA8dFD8X3/Y9LcltlWK1u5Gjo6vqp9OeoRD9K+82lXIHY4qxB1MMdzumZ58/O/DEGQK4wBvmrAvEaQdS6PnD1vvoPAUgwPQ=="
|
|
@@ -1 +1 @@
|
|
|
1
|
-
window.searchData = "
|
|
1
|
+
window.searchData = "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";
|