@graphoria/server 0.2.3 → 0.4.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/README.md +29 -20
- package/dist/src/ai/mcp/index.d.ts +9 -2
- package/dist/src/ai/mcp/index.d.ts.map +1 -1
- package/dist/src/ai/tools/core.d.ts +6 -1
- package/dist/src/ai/tools/core.d.ts.map +1 -1
- package/dist/src/ai/tools/core.test.d.ts +2 -0
- package/dist/src/ai/tools/core.test.d.ts.map +1 -0
- package/dist/src/analyzeQuery/analyzers/selectionAnalyzer.d.ts.map +1 -1
- package/dist/src/analyzeQuery/costLimit.d.ts +30 -0
- package/dist/src/analyzeQuery/costLimit.d.ts.map +1 -0
- package/dist/src/analyzeQuery/costLimit.test.d.ts +2 -0
- package/dist/src/analyzeQuery/costLimit.test.d.ts.map +1 -0
- package/dist/src/analyzeQuery/depthLimit.d.ts.map +1 -1
- package/dist/src/analyzeQuery/fragments.d.ts +6 -0
- package/dist/src/analyzeQuery/fragments.d.ts.map +1 -0
- package/dist/src/authentication/capabilities.d.ts +18 -0
- package/dist/src/authentication/capabilities.d.ts.map +1 -0
- package/dist/src/authentication/capabilities.test.d.ts +2 -0
- package/dist/src/authentication/capabilities.test.d.ts.map +1 -0
- package/dist/src/authentication/jwt.d.ts.map +1 -1
- package/dist/src/authentication/paseto.d.ts.map +1 -1
- package/dist/src/authentication/secrets.d.ts +9 -0
- package/dist/src/authentication/secrets.d.ts.map +1 -0
- package/dist/src/authentication/secrets.test.d.ts +2 -0
- package/dist/src/authentication/secrets.test.d.ts.map +1 -0
- package/dist/src/config/types/auth.d.ts +8 -0
- package/dist/src/config/types/auth.d.ts.map +1 -1
- package/dist/src/config/types/db.d.ts +2 -0
- package/dist/src/config/types/db.d.ts.map +1 -1
- package/dist/src/config/types/operation.d.ts +4 -0
- package/dist/src/config/types/operation.d.ts.map +1 -1
- package/dist/src/config/types/operation.test.d.ts +2 -0
- package/dist/src/config/types/operation.test.d.ts.map +1 -0
- package/dist/src/configuration/crossReferences.d.ts +27 -0
- package/dist/src/configuration/crossReferences.d.ts.map +1 -0
- package/dist/src/configuration/crossReferences.test.d.ts +2 -0
- package/dist/src/configuration/crossReferences.test.d.ts.map +1 -0
- package/dist/src/configuration/getSchemas/index.d.ts +70 -6
- package/dist/src/configuration/getSchemas/index.d.ts.map +1 -1
- package/dist/src/configuration/getSchemas/mergeEntities/index.d.ts +3 -1
- package/dist/src/configuration/getSchemas/mergeEntities/index.d.ts.map +1 -1
- package/dist/src/configuration/gql/gqlAuthOperations.d.ts.map +1 -1
- package/dist/src/configuration/gql/handleGraphQLRequestFactory.d.ts +32 -2
- package/dist/src/configuration/gql/handleGraphQLRequestFactory.d.ts.map +1 -1
- package/dist/src/configuration/gql/handleGraphQLSubscriptionFactory.d.ts.map +1 -1
- package/dist/src/configuration/index.d.ts +41 -3
- package/dist/src/configuration/index.d.ts.map +1 -1
- package/dist/src/configuration/rest/generateOpenAPI.d.ts.map +1 -1
- package/dist/src/configuration/rest/generateOpenAPI.test.d.ts +2 -0
- package/dist/src/configuration/rest/generateOpenAPI.test.d.ts.map +1 -0
- package/dist/src/configuration/rest/handleRESTRequestFactory.d.ts.map +1 -1
- package/dist/src/configuration/rest/index.d.ts.map +1 -1
- package/dist/src/console/api.d.ts +7 -5
- package/dist/src/console/api.d.ts.map +1 -1
- package/dist/src/console/session.d.ts +29 -0
- package/dist/src/console/session.d.ts.map +1 -0
- package/dist/src/console/session.test.d.ts +2 -0
- package/dist/src/console/session.test.d.ts.map +1 -0
- package/dist/src/databases/common.d.ts +12 -4
- package/dist/src/databases/common.d.ts.map +1 -1
- package/dist/src/databases/core/executor.d.ts +7 -3
- package/dist/src/databases/core/executor.d.ts.map +1 -1
- package/dist/src/databases/core/function-mapping.d.ts +5 -4
- package/dist/src/databases/core/function-mapping.d.ts.map +1 -1
- package/dist/src/databases/core/procedure-arguments.d.ts +17 -0
- package/dist/src/databases/core/procedure-arguments.d.ts.map +1 -0
- package/dist/src/databases/core/procedure-arguments.test.d.ts +2 -0
- package/dist/src/databases/core/procedure-arguments.test.d.ts.map +1 -0
- package/dist/src/databases/core/query-builder.d.ts +7 -1
- package/dist/src/databases/core/query-builder.d.ts.map +1 -1
- package/dist/src/databases/engines/mssql/connection.d.ts +30 -7
- package/dist/src/databases/engines/mssql/connection.d.ts.map +1 -1
- package/dist/src/databases/engines/mssql/connection.test.d.ts +2 -0
- package/dist/src/databases/engines/mssql/connection.test.d.ts.map +1 -0
- package/dist/src/databases/engines/mssql/format.d.ts +2 -1
- package/dist/src/databases/engines/mssql/format.d.ts.map +1 -1
- package/dist/src/databases/engines/mssql/query/index.d.ts +3 -2
- package/dist/src/databases/engines/mssql/query/index.d.ts.map +1 -1
- package/dist/src/databases/engines/mysql/connection.d.ts +17 -1
- package/dist/src/databases/engines/mysql/connection.d.ts.map +1 -1
- package/dist/src/databases/engines/mysql/connection.test.d.ts +2 -0
- package/dist/src/databases/engines/mysql/connection.test.d.ts.map +1 -0
- package/dist/src/databases/engines/mysql/format.d.ts +2 -1
- package/dist/src/databases/engines/mysql/format.d.ts.map +1 -1
- package/dist/src/databases/engines/mysql/query/index.d.ts +3 -2
- package/dist/src/databases/engines/mysql/query/index.d.ts.map +1 -1
- package/dist/src/databases/engines/postgresql/connection.d.ts +26 -7
- package/dist/src/databases/engines/postgresql/connection.d.ts.map +1 -1
- package/dist/src/databases/engines/postgresql/connection.test.d.ts +2 -0
- package/dist/src/databases/engines/postgresql/connection.test.d.ts.map +1 -0
- package/dist/src/databases/engines/postgresql/format.d.ts +2 -1
- package/dist/src/databases/engines/postgresql/format.d.ts.map +1 -1
- package/dist/src/databases/engines/postgresql/query/index.d.ts +3 -2
- package/dist/src/databases/engines/postgresql/query/index.d.ts.map +1 -1
- package/dist/src/databases/high-level-operations.d.ts +2 -0
- package/dist/src/databases/high-level-operations.d.ts.map +1 -1
- package/dist/src/databases/transformers/data-transformers.d.ts +2 -0
- package/dist/src/databases/transformers/data-transformers.d.ts.map +1 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/logging/audit.d.ts +27 -0
- package/dist/src/logging/audit.d.ts.map +1 -0
- package/dist/src/logging/audit.test.d.ts +2 -0
- package/dist/src/logging/audit.test.d.ts.map +1 -0
- package/dist/src/singletons/env.d.ts +27 -4
- package/dist/src/singletons/env.d.ts.map +1 -1
- package/dist/src/singletons/queryTimeout.d.ts +3 -0
- package/dist/src/singletons/queryTimeout.d.ts.map +1 -0
- package/dist/src/subscriptions/strategies/database.d.ts.map +1 -1
- package/dist/src/subscriptions/strategies/database.test.d.ts +2 -0
- package/dist/src/subscriptions/strategies/database.test.d.ts.map +1 -0
- package/dist/src/subscriptions/types.d.ts +4 -1
- package/dist/src/subscriptions/types.d.ts.map +1 -1
- package/dist/src/subscriptions/utils/polling.d.ts.map +1 -1
- package/dist/src/types/env.d.ts +97 -12
- package/dist/src/types/env.d.ts.map +1 -1
- package/dist/src/types/zod/auth.d.ts +12 -0
- package/dist/src/types/zod/auth.d.ts.map +1 -1
- package/dist/src/types/zod/configuration.d.ts +28 -0
- package/dist/src/types/zod/configuration.d.ts.map +1 -1
- package/dist/src/utils/rateLimit.d.ts +65 -0
- package/dist/src/utils/rateLimit.d.ts.map +1 -0
- package/dist/src/utils/rateLimit.test.d.ts +2 -0
- package/dist/src/utils/rateLimit.test.d.ts.map +1 -0
- package/dist/src/utils/responses.d.ts +6 -0
- package/dist/src/utils/responses.d.ts.map +1 -1
- package/dist/src/utils/sessionVariables.d.ts +2 -0
- package/dist/src/utils/sessionVariables.d.ts.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +5 -5
- package/playgrounds/console/index.html +7 -7
- package/playgrounds/graphiql/index.html +548 -685
- package/src/ai/mcp/index.ts +19 -14
- package/src/ai/tools/core.ts +30 -6
- package/src/analyzeQuery/analyzers/selectionAnalyzer.ts +2 -1
- package/src/analyzeQuery/costLimit.ts +236 -0
- package/src/analyzeQuery/depthLimit.ts +4 -23
- package/src/analyzeQuery/fragments.ts +18 -0
- package/src/authentication/capabilities.ts +54 -0
- package/src/authentication/index.ts +3 -3
- package/src/authentication/jwt.ts +30 -25
- package/src/authentication/paseto.ts +36 -26
- package/src/authentication/secrets.ts +30 -0
- package/src/config/types/auth.ts +7 -0
- package/src/config/types/db.ts +7 -0
- package/src/config/types/operation.ts +4 -0
- package/src/configuration/crossReferences.ts +340 -0
- package/src/configuration/getSchemas/mergeEntities/index.ts +0 -26
- package/src/configuration/getSchemas/type-definition-generator/index.ts +1 -1
- package/src/configuration/gql/buildExecute.ts +1 -1
- package/src/configuration/gql/gqlAuthOperations.ts +26 -0
- package/src/configuration/gql/handleGraphQLRequestFactory.ts +97 -15
- package/src/configuration/gql/handleGraphQLSubscriptionFactory.ts +31 -2
- package/src/configuration/index.ts +24 -6
- package/src/configuration/rest/generateOpenAPI.ts +61 -11
- package/src/configuration/rest/handleRESTRequestFactory.ts +32 -4
- package/src/configuration/rest/index.ts +11 -2
- package/src/console/api.ts +102 -16
- package/src/console/session.ts +129 -0
- package/src/databases/common.ts +55 -10
- package/src/databases/core/executor.ts +13 -4
- package/src/databases/core/function-mapping.ts +5 -1
- package/src/databases/core/procedure-arguments.ts +25 -0
- package/src/databases/core/query-builder.ts +7 -0
- package/src/databases/engines/mssql/connection.ts +66 -17
- package/src/databases/engines/mssql/format.ts +3 -1
- package/src/databases/engines/mssql/query/index.ts +12 -2
- package/src/databases/engines/mysql/connection.ts +40 -20
- package/src/databases/engines/mysql/format.ts +3 -1
- package/src/databases/engines/mysql/query/index.ts +25 -4
- package/src/databases/engines/postgresql/connection.ts +86 -22
- package/src/databases/engines/postgresql/format.ts +3 -1
- package/src/databases/engines/postgresql/query/index.ts +12 -3
- package/src/databases/high-level-operations.ts +13 -2
- package/src/index.ts +127 -19
- package/src/logging/audit.ts +86 -0
- package/src/singletons/queryTimeout.ts +20 -0
- package/src/subscriptions/strategies/database.ts +19 -3
- package/src/subscriptions/types.ts +4 -1
- package/src/subscriptions/utils/polling.ts +6 -3
- package/src/types/env.ts +47 -9
- package/src/utils/rateLimit.ts +209 -0
- package/src/utils/responses.ts +16 -0
- package/src/utils/sessionVariables.ts +2 -0
|
@@ -6,32 +6,78 @@ import type { VariableDefinition } from "../../../analyzeQuery/types";
|
|
|
6
6
|
import type { Database } from "../../../types/configuration";
|
|
7
7
|
import type { ProcedureResolver } from "../../../types/db";
|
|
8
8
|
|
|
9
|
+
import { MSSQLConnectionOptionsZod } from "../../../config/types/db";
|
|
9
10
|
import { databasesConnections } from "../../../singletons/databases";
|
|
11
|
+
import { getQueryTimeoutMs } from "../../../singletons/queryTimeout";
|
|
10
12
|
import { logger } from "../../../logging";
|
|
11
13
|
|
|
12
|
-
|
|
14
|
+
// Every pool bound comes from the schema, which is the only place they are
|
|
15
|
+
// declared and documented. Repeating them here as `?? n` fallbacks let the
|
|
16
|
+
// documented default and the applied one drift apart, differently per engine.
|
|
17
|
+
//
|
|
18
|
+
// Defaults are overlaid rather than re-parsed: `connectionOptions` is an
|
|
19
|
+
// undiscriminated union of the two engine shapes, and an object carrying
|
|
20
|
+
// nothing MSSQL-specific — `{}` included — validates against the Bun SQL shape
|
|
21
|
+
// even on an MSSQL database. A strict re-parse of one of those would reject the
|
|
22
|
+
// Bun SQL keys and take the server down at boot.
|
|
23
|
+
export const poolOptions = (db: Database, timeoutMs: number = getQueryTimeoutMs()) => {
|
|
13
24
|
const ci = db.connection;
|
|
14
|
-
const
|
|
15
|
-
|
|
25
|
+
const raw = db.connectionOptions as MSSQLConnectionOptions | undefined;
|
|
26
|
+
|
|
27
|
+
const defaults = MSSQLConnectionOptionsZod.parse({});
|
|
28
|
+
const opts = { ...defaults, ...raw, pool: { ...defaults.pool, ...raw?.pool } };
|
|
29
|
+
|
|
30
|
+
return {
|
|
16
31
|
server: ci.host,
|
|
17
32
|
port: ci.port,
|
|
18
33
|
user: ci.user,
|
|
19
34
|
password: ci.password,
|
|
20
35
|
database: ci.database,
|
|
21
|
-
connectionTimeout: opts
|
|
22
|
-
|
|
36
|
+
connectionTimeout: opts.connectionTimeout * 1000,
|
|
37
|
+
// Only an omitted key falls through to the resolved timeout: reading it off
|
|
38
|
+
// `opts` cannot tell an operator who set 30 from Zod defaulting to 30, and
|
|
39
|
+
// silently overriding the first would break the slow report it was raised
|
|
40
|
+
// for. Seconds there, milliseconds here.
|
|
41
|
+
requestTimeout: raw?.requestTimeout !== undefined ? raw.requestTimeout * 1000 : timeoutMs,
|
|
42
|
+
// These three keep their code fallbacks rather than the schema's. Two of
|
|
43
|
+
// them default the opposite way there, so taking the schema value would
|
|
44
|
+
// turn certificate validation on and trusted connections off for every
|
|
45
|
+
// deployment that omits `connectionOptions` — a change to how a connection
|
|
46
|
+
// is secured, not to how the pool is sized.
|
|
23
47
|
options: {
|
|
24
|
-
encrypt:
|
|
25
|
-
trustServerCertificate:
|
|
26
|
-
trustedConnection:
|
|
48
|
+
encrypt: raw?.encrypt ?? false,
|
|
49
|
+
trustServerCertificate: raw?.trustServerCertificate ?? true,
|
|
50
|
+
trustedConnection: raw?.trustedConnection ?? true,
|
|
27
51
|
},
|
|
28
52
|
pool: {
|
|
29
|
-
max: opts
|
|
30
|
-
min: opts
|
|
31
|
-
idleTimeoutMillis:
|
|
53
|
+
max: opts.pool.max,
|
|
54
|
+
min: opts.pool.min,
|
|
55
|
+
idleTimeoutMillis: opts.pool.idleTimeout * 1000,
|
|
56
|
+
acquireTimeoutMillis: opts.pool.acquireTimeout * 1000,
|
|
32
57
|
},
|
|
33
|
-
parseJSON: opts
|
|
34
|
-
}
|
|
58
|
+
parseJSON: opts.parseJSON,
|
|
59
|
+
};
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
/** The bound the pool already applies, so the executor can tell an override from the default. */
|
|
63
|
+
export const poolRequestTimeoutMs = (db: Database) => poolOptions(db).requestTimeout;
|
|
64
|
+
|
|
65
|
+
// `ConnectionPool.request` takes per-request overrides from mssql 12.7.0, which
|
|
66
|
+
// is what the workspace pins, but `@types/mssql@12.3.0` still declares it with
|
|
67
|
+
// no parameter. Narrowed to the one key used rather than cast wholesale, so the
|
|
68
|
+
// day the types catch up this stops compiling and can be deleted.
|
|
69
|
+
//
|
|
70
|
+
// Not interchangeable with assigning `request.timeout`: that property does not
|
|
71
|
+
// exist, and the pool default is used instead.
|
|
72
|
+
type PoolWithRequestOverrides = {
|
|
73
|
+
request(overrides: { requestTimeout: number }): ReturnType<ConnectionPool["request"]>;
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
const requestWithTimeout = (pool: ConnectionPool, timeoutMs: number) =>
|
|
77
|
+
(pool as unknown as PoolWithRequestOverrides).request({ requestTimeout: timeoutMs });
|
|
78
|
+
|
|
79
|
+
export const getPool = async (db: Database) => {
|
|
80
|
+
const pool = new ConnectionPool(poolOptions(db));
|
|
35
81
|
|
|
36
82
|
await pool.connect(); // Connect to the database
|
|
37
83
|
|
|
@@ -52,6 +98,7 @@ export const executeQueryFactory =
|
|
|
52
98
|
db: Database,
|
|
53
99
|
variablesDefinition: VariableDefinition[] = [],
|
|
54
100
|
params: Record<string, unknown> = {},
|
|
101
|
+
timeoutMs?: number,
|
|
55
102
|
) => {
|
|
56
103
|
const pool = singleQuery ? await getPool(db) : await getPoolSingleton(db);
|
|
57
104
|
|
|
@@ -59,7 +106,10 @@ export const executeQueryFactory =
|
|
|
59
106
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
60
107
|
}
|
|
61
108
|
|
|
62
|
-
const request =
|
|
109
|
+
const request =
|
|
110
|
+
timeoutMs !== undefined && timeoutMs !== poolRequestTimeoutMs(db)
|
|
111
|
+
? requestWithTimeout(pool!, timeoutMs)
|
|
112
|
+
: pool!.request();
|
|
63
113
|
|
|
64
114
|
variablesDefinition.forEach((v, i) => {
|
|
65
115
|
const varName = (i + 1).toString();
|
|
@@ -93,12 +143,14 @@ export const executeQueryJSONFactory =
|
|
|
93
143
|
db: Database,
|
|
94
144
|
variablesDefinition: VariableDefinition[] = [],
|
|
95
145
|
params: Record<string, unknown> = {},
|
|
146
|
+
timeoutMs?: number,
|
|
96
147
|
): Promise<T> => {
|
|
97
148
|
const result = await (singleQuery ? executeQuerySingle : executeQuery)(
|
|
98
149
|
query,
|
|
99
150
|
db,
|
|
100
151
|
variablesDefinition,
|
|
101
152
|
params,
|
|
153
|
+
timeoutMs,
|
|
102
154
|
);
|
|
103
155
|
|
|
104
156
|
return result[0] as T;
|
|
@@ -109,7 +161,6 @@ export const executeQueryJSONSingle = executeQueryJSONFactory(true);
|
|
|
109
161
|
|
|
110
162
|
export const callStoredProcedure = async (
|
|
111
163
|
sp: ProcedureResolver,
|
|
112
|
-
variablesDefinition: VariableDefinition[],
|
|
113
164
|
variables: Record<string, unknown> = {},
|
|
114
165
|
) => {
|
|
115
166
|
try {
|
|
@@ -118,8 +169,6 @@ export const callStoredProcedure = async (
|
|
|
118
169
|
const request = pool!.request();
|
|
119
170
|
|
|
120
171
|
for (const [key, value] of Object.entries(variables)) {
|
|
121
|
-
if (!value) continue;
|
|
122
|
-
|
|
123
172
|
const paramFound = sp.parameters.find((p) => p.name === `@${key}`);
|
|
124
173
|
|
|
125
174
|
if (paramFound?.dataType === "varchar") {
|
|
@@ -2,6 +2,7 @@ import { format as sqlFormatter } from "sql-formatter";
|
|
|
2
2
|
|
|
3
3
|
import type { AnalysisResult } from "../../../analyzeQuery/types";
|
|
4
4
|
import type { MergedEntities } from "../../../configuration/getSchemas/mergeEntities";
|
|
5
|
+
import type { PageLimits } from "../../common";
|
|
5
6
|
|
|
6
7
|
import { generateSQL } from "../../core/query-builder";
|
|
7
8
|
|
|
@@ -16,8 +17,9 @@ export const genSql = (
|
|
|
16
17
|
query: AnalysisResult,
|
|
17
18
|
variables: Record<string, unknown> = {},
|
|
18
19
|
hash = false,
|
|
20
|
+
pageLimits: PageLimits | null = null,
|
|
19
21
|
) => {
|
|
20
|
-
const [[, sql]] = generateSQL(entitites, query, variables, hash);
|
|
22
|
+
const [[, sql]] = generateSQL(entitites, query, variables, hash, pageLimits);
|
|
21
23
|
|
|
22
24
|
return format(sql);
|
|
23
25
|
};
|
|
@@ -4,7 +4,7 @@ import type {
|
|
|
4
4
|
VariableDefinition,
|
|
5
5
|
} from "../../../../analyzeQuery/types";
|
|
6
6
|
import type { MergedEntities } from "../../../../configuration/getSchemas/mergeEntities";
|
|
7
|
-
import type { GroupByInfo } from "../../../common";
|
|
7
|
+
import type { GroupByInfo, PageLimits } from "../../../common";
|
|
8
8
|
|
|
9
9
|
import {
|
|
10
10
|
buildOrderByClauseMSSQL,
|
|
@@ -164,6 +164,7 @@ export const generateSQL = (
|
|
|
164
164
|
operation: OperationAnalysis,
|
|
165
165
|
variables: Record<string, unknown> = {},
|
|
166
166
|
forHashMethod: boolean = false,
|
|
167
|
+
pageLimits: PageLimits | null = null,
|
|
167
168
|
): string => {
|
|
168
169
|
if (forHashMethod) {
|
|
169
170
|
return `SELECT HASHBYTES('MD5', (
|
|
@@ -176,6 +177,7 @@ export const generateSQL = (
|
|
|
176
177
|
null,
|
|
177
178
|
1,
|
|
178
179
|
{},
|
|
180
|
+
pageLimits,
|
|
179
181
|
)})
|
|
180
182
|
) AS ResultHash`;
|
|
181
183
|
}
|
|
@@ -238,6 +240,7 @@ export const generateSQL = (
|
|
|
238
240
|
null,
|
|
239
241
|
index + 1,
|
|
240
242
|
{},
|
|
243
|
+
pageLimits,
|
|
241
244
|
);
|
|
242
245
|
|
|
243
246
|
fieldQueries.push(`(${fieldSQL}) as ${wrapIdentifierMSSQL(field.alias || field.name)}`);
|
|
@@ -261,6 +264,7 @@ export const buildSQLForField = (
|
|
|
261
264
|
parentTableAlias: string | null,
|
|
262
265
|
level: number,
|
|
263
266
|
aliasMap: { [alias: string]: string },
|
|
267
|
+
pageLimits: PageLimits | null,
|
|
264
268
|
): string => {
|
|
265
269
|
const tableAlias = generateTableAlias(level);
|
|
266
270
|
|
|
@@ -325,6 +329,7 @@ export const buildSQLForField = (
|
|
|
325
329
|
tableAlias,
|
|
326
330
|
level,
|
|
327
331
|
aliasMap,
|
|
332
|
+
pageLimits,
|
|
328
333
|
),
|
|
329
334
|
([name, selector]) => `${selector} AS ${wrapIdentifierMSSQL(name)}`,
|
|
330
335
|
);
|
|
@@ -332,7 +337,12 @@ export const buildSQLForField = (
|
|
|
332
337
|
const fromClause = `FROM ${dottedQuotedName} ${tableAlias}`;
|
|
333
338
|
|
|
334
339
|
const orderByClause = buildOrderByClauseMSSQL(entities, field, tableAlias);
|
|
335
|
-
const paginationClause = buildPaginationClauseMSSQL(
|
|
340
|
+
const paginationClause = buildPaginationClauseMSSQL(
|
|
341
|
+
field,
|
|
342
|
+
variablesDefinition,
|
|
343
|
+
variables,
|
|
344
|
+
pageLimits,
|
|
345
|
+
);
|
|
336
346
|
|
|
337
347
|
const isArray = field.isArray;
|
|
338
348
|
const forJson =
|
|
@@ -5,27 +5,45 @@ import type { VariableDefinition } from "../../../analyzeQuery/types";
|
|
|
5
5
|
import type { Database } from "../../../types/configuration";
|
|
6
6
|
import type { ProcedureResolver } from "../../../types/db";
|
|
7
7
|
|
|
8
|
+
import { BunSQLConnectionOptionsZod } from "../../../config/types/db";
|
|
8
9
|
import { databasesConnections } from "../../../singletons/databases";
|
|
10
|
+
import { orderProcedureArguments } from "../../core/procedure-arguments";
|
|
9
11
|
import { toMySQLPlaceholders } from "./placeholders";
|
|
10
12
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
13
|
+
// Every pool bound comes from the schema, which is the only place they are
|
|
14
|
+
// declared and documented. Repeating them here as `?? n` fallbacks let the
|
|
15
|
+
// documented default and the applied one drift apart, differently per engine.
|
|
16
|
+
//
|
|
17
|
+
// Defaults are overlaid rather than re-parsed: `connectionOptions` is an
|
|
18
|
+
// undiscriminated union of the two engine shapes, so an object can arrive
|
|
19
|
+
// already validated against the other one, and a strict re-parse would reject
|
|
20
|
+
// it at boot.
|
|
21
|
+
export const poolOptions = (db: Database) => {
|
|
22
|
+
const opts = {
|
|
23
|
+
...BunSQLConnectionOptionsZod.parse({}),
|
|
24
|
+
...(db.connectionOptions as BunSQLConnectionOptions | undefined),
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
return {
|
|
14
28
|
host: db.connection.host,
|
|
15
29
|
port: db.connection.port,
|
|
16
30
|
username: db.connection.user,
|
|
17
31
|
password: db.connection.password,
|
|
18
32
|
database: db.connection.database,
|
|
19
|
-
adapter: "mysql",
|
|
20
|
-
max: opts
|
|
21
|
-
idleTimeout: opts
|
|
22
|
-
connectionTimeout: opts
|
|
23
|
-
maxLifetime: opts
|
|
24
|
-
tls: opts
|
|
25
|
-
allowPublicKeyRetrieval: opts
|
|
26
|
-
prepare: opts
|
|
27
|
-
bigint: opts
|
|
28
|
-
}
|
|
33
|
+
adapter: "mysql" as const,
|
|
34
|
+
max: opts.max,
|
|
35
|
+
idleTimeout: opts.idleTimeout,
|
|
36
|
+
connectionTimeout: opts.connectionTimeout,
|
|
37
|
+
maxLifetime: opts.maxLifetime,
|
|
38
|
+
tls: opts.tls,
|
|
39
|
+
allowPublicKeyRetrieval: opts.allowPublicKeyRetrieval,
|
|
40
|
+
prepare: opts.prepare,
|
|
41
|
+
bigint: opts.bigint,
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export const getPool = async (db: Database) => {
|
|
46
|
+
const pool = new SQL(poolOptions(db));
|
|
29
47
|
|
|
30
48
|
await pool.connect(); // Connect to the database
|
|
31
49
|
|
|
@@ -80,22 +98,24 @@ export const executeQueryJSONFactory =
|
|
|
80
98
|
export const executeQueryJSON = executeQueryJSONFactory();
|
|
81
99
|
export const executeQueryJSONSingle = executeQueryJSONFactory(true);
|
|
82
100
|
|
|
101
|
+
// The generated schema types every stored procedure mutation as `Boolean!`, so
|
|
102
|
+
// the call reports whether it ran, not what it selected. Returning the rows here
|
|
103
|
+
// would put an array behind a field the schema promises is a boolean.
|
|
83
104
|
export const callStoredProcedure = async (
|
|
84
105
|
sp: ProcedureResolver,
|
|
85
|
-
variablesDefinition: VariableDefinition[],
|
|
86
106
|
variables: Record<string, unknown>,
|
|
87
107
|
) => {
|
|
88
108
|
try {
|
|
89
|
-
const
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
109
|
+
const args = orderProcedureArguments(sp, variables);
|
|
110
|
+
|
|
111
|
+
await executeQuery(
|
|
112
|
+
`CALL ${sp.dottedQuotedName}(${args.map((_, i) => `$${i + 1}`).join(", ")});`,
|
|
93
113
|
sp.db!,
|
|
94
|
-
|
|
114
|
+
args,
|
|
95
115
|
variables,
|
|
96
116
|
);
|
|
97
117
|
|
|
98
|
-
return
|
|
118
|
+
return true;
|
|
99
119
|
} catch {
|
|
100
120
|
return false;
|
|
101
121
|
}
|
|
@@ -2,6 +2,7 @@ import { format as sqlFormatter } from "sql-formatter";
|
|
|
2
2
|
|
|
3
3
|
import type { AnalysisResult } from "../../../analyzeQuery/types";
|
|
4
4
|
import type { MergedEntities } from "../../../configuration/getSchemas/mergeEntities";
|
|
5
|
+
import type { PageLimits } from "../../common";
|
|
5
6
|
|
|
6
7
|
import { generateSQL } from "../../core/query-builder";
|
|
7
8
|
|
|
@@ -16,8 +17,9 @@ export const genSql = (
|
|
|
16
17
|
query: AnalysisResult,
|
|
17
18
|
variables: Record<string, unknown> = {},
|
|
18
19
|
hash = false,
|
|
20
|
+
pageLimits: PageLimits | null = null,
|
|
19
21
|
) => {
|
|
20
|
-
const [[, sql]] = generateSQL(entitites, query, variables, hash);
|
|
22
|
+
const [[, sql]] = generateSQL(entitites, query, variables, hash, pageLimits);
|
|
21
23
|
|
|
22
24
|
return format(sql);
|
|
23
25
|
};
|
|
@@ -4,7 +4,7 @@ import type {
|
|
|
4
4
|
VariableDefinition,
|
|
5
5
|
} from "../../../../analyzeQuery/types";
|
|
6
6
|
import type { MergedEntities } from "../../../../configuration/getSchemas/mergeEntities";
|
|
7
|
-
import type { GroupByInfo } from "../../../common";
|
|
7
|
+
import type { GroupByInfo, PageLimits } from "../../../common";
|
|
8
8
|
|
|
9
9
|
import {
|
|
10
10
|
buildOrderByClauseMySQL,
|
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
} from "../../../common";
|
|
22
22
|
|
|
23
23
|
import { applyDirectives } from "../../../directives";
|
|
24
|
+
import { getQueryTimeoutMs } from "../../../../singletons/queryTimeout";
|
|
24
25
|
|
|
25
26
|
// Generate CTE for aggregations
|
|
26
27
|
const buildAggregationCTE = (
|
|
@@ -157,9 +158,21 @@ export const generateSQL = (
|
|
|
157
158
|
operation: OperationAnalysis,
|
|
158
159
|
variables: Record<string, unknown> = {},
|
|
159
160
|
forHashMethod: boolean = false,
|
|
161
|
+
pageLimits: PageLimits | null = null,
|
|
162
|
+
timeoutMs?: number,
|
|
160
163
|
): string => {
|
|
164
|
+
// Bun's MySQL adapter ignores the `connection` bag and exposes no
|
|
165
|
+
// per-connection init hook, so there is no pool-level route for
|
|
166
|
+
// max_execution_time: the bound has to ride in the SQL text. MySQL honours the
|
|
167
|
+
// hint only on the outermost SELECT — one placed inside a CTE is silently
|
|
168
|
+
// discarded — which is why it is emitted here, at each return, rather than
|
|
169
|
+
// spliced into the finished string by the executor.
|
|
170
|
+
const effectiveTimeoutMs = timeoutMs ?? getQueryTimeoutMs();
|
|
171
|
+
const timeoutHint =
|
|
172
|
+
effectiveTimeoutMs > 0 ? ` /*+ MAX_EXECUTION_TIME(${effectiveTimeoutMs}) */` : "";
|
|
173
|
+
|
|
161
174
|
if (forHashMethod) {
|
|
162
|
-
return `SELECT MD5((${buildSQLForField(entities, operation.variables ?? [], variables, operation.fields[0], null, null, 1, {})})) AS ResultHash`;
|
|
175
|
+
return `SELECT${timeoutHint} MD5((${buildSQLForField(entities, operation.variables ?? [], variables, operation.fields[0], null, null, 1, {}, pageLimits)})) AS ResultHash`;
|
|
163
176
|
}
|
|
164
177
|
|
|
165
178
|
const variablesWithDefault = {
|
|
@@ -220,6 +233,7 @@ export const generateSQL = (
|
|
|
220
233
|
null,
|
|
221
234
|
index + 1,
|
|
222
235
|
{},
|
|
236
|
+
pageLimits,
|
|
223
237
|
);
|
|
224
238
|
|
|
225
239
|
fieldQueries.push(`'${field.alias || field.name}', ${fieldSQL}`);
|
|
@@ -228,7 +242,7 @@ export const generateSQL = (
|
|
|
228
242
|
const cteClause = ctes.length > 0 ? `WITH\n${ctes.join(",\n")}\n` : "";
|
|
229
243
|
|
|
230
244
|
return `
|
|
231
|
-
${cteClause}SELECT JSON_OBJECT(
|
|
245
|
+
${cteClause}SELECT${timeoutHint} JSON_OBJECT(
|
|
232
246
|
${fieldQueries.join(",\n")}
|
|
233
247
|
) as json_result`;
|
|
234
248
|
};
|
|
@@ -242,6 +256,7 @@ export const buildSQLForField = (
|
|
|
242
256
|
parentTableAlias: string | null,
|
|
243
257
|
level: number,
|
|
244
258
|
aliasMap: { [alias: string]: string },
|
|
259
|
+
pageLimits: PageLimits | null,
|
|
245
260
|
): string => {
|
|
246
261
|
const tableAlias = generateTableAlias(level);
|
|
247
262
|
|
|
@@ -306,6 +321,7 @@ export const buildSQLForField = (
|
|
|
306
321
|
tableAlias,
|
|
307
322
|
level,
|
|
308
323
|
aliasMap,
|
|
324
|
+
pageLimits,
|
|
309
325
|
),
|
|
310
326
|
([name, selector]) => `'${name}', ${selector}`,
|
|
311
327
|
);
|
|
@@ -313,7 +329,12 @@ export const buildSQLForField = (
|
|
|
313
329
|
const fromClause = `FROM ${dottedQuotedName} ${tableAlias}`;
|
|
314
330
|
|
|
315
331
|
const orderByClause = buildOrderByClauseMySQL(entities, field, tableAlias);
|
|
316
|
-
const paginationClause = buildPaginationClauseMySQL(
|
|
332
|
+
const paginationClause = buildPaginationClauseMySQL(
|
|
333
|
+
field,
|
|
334
|
+
variablesDefinition,
|
|
335
|
+
variables,
|
|
336
|
+
pageLimits,
|
|
337
|
+
);
|
|
317
338
|
|
|
318
339
|
const isArraySelection = !!field.isArray && !withoutArrayWrapper;
|
|
319
340
|
|
|
@@ -5,24 +5,52 @@ import type { VariableDefinition } from "../../../analyzeQuery/types";
|
|
|
5
5
|
import type { Database } from "../../../types/configuration";
|
|
6
6
|
import type { ProcedureResolver } from "../../../types/db";
|
|
7
7
|
|
|
8
|
+
import { BunSQLConnectionOptionsZod } from "../../../config/types/db";
|
|
8
9
|
import { databasesConnections } from "../../../singletons/databases";
|
|
10
|
+
import { orderProcedureArguments } from "../../core/procedure-arguments";
|
|
11
|
+
import { getQueryTimeoutMs } from "../../../singletons/queryTimeout";
|
|
12
|
+
|
|
13
|
+
// Every pool bound comes from the schema, which is the only place they are
|
|
14
|
+
// declared and documented. Repeating them here as `?? n` fallbacks let the
|
|
15
|
+
// documented default and the applied one drift apart, differently per engine.
|
|
16
|
+
//
|
|
17
|
+
// Defaults are overlaid rather than re-parsed: `connectionOptions` is an
|
|
18
|
+
// undiscriminated union of the two engine shapes, so an object can arrive
|
|
19
|
+
// already validated against the other one, and a strict re-parse would reject
|
|
20
|
+
// it at boot.
|
|
21
|
+
export const poolOptions = (db: Database, timeoutMs: number = getQueryTimeoutMs()) => {
|
|
22
|
+
const opts = {
|
|
23
|
+
...BunSQLConnectionOptionsZod.parse({}),
|
|
24
|
+
...(db.connectionOptions as BunSQLConnectionOptions | undefined),
|
|
25
|
+
};
|
|
9
26
|
|
|
10
|
-
|
|
11
|
-
const opts = db.connectionOptions as BunSQLConnectionOptions | undefined;
|
|
12
|
-
const pool = new SQL({
|
|
27
|
+
return {
|
|
13
28
|
host: db.connection.host,
|
|
14
29
|
port: db.connection.port,
|
|
15
30
|
user: db.connection.user,
|
|
16
31
|
password: db.connection.password,
|
|
17
32
|
database: db.connection.database,
|
|
18
|
-
max: opts
|
|
19
|
-
idleTimeout: opts
|
|
20
|
-
connectionTimeout: opts
|
|
21
|
-
maxLifetime: opts
|
|
22
|
-
tls: opts
|
|
23
|
-
prepare: opts
|
|
24
|
-
bigint: opts
|
|
25
|
-
|
|
33
|
+
max: opts.max,
|
|
34
|
+
idleTimeout: opts.idleTimeout,
|
|
35
|
+
connectionTimeout: opts.connectionTimeout,
|
|
36
|
+
maxLifetime: opts.maxLifetime,
|
|
37
|
+
tls: opts.tls,
|
|
38
|
+
prepare: opts.prepare,
|
|
39
|
+
bigint: opts.bigint,
|
|
40
|
+
// Applied once per pooled connection rather than per query, so it bounds
|
|
41
|
+
// everything that runs on the connection — auth and introspection included
|
|
42
|
+
// — at no per-query cost. Omitted at 0 rather than sent as "0", which
|
|
43
|
+
// Postgres reads as unlimited but still costs a round trip to set.
|
|
44
|
+
...(timeoutMs > 0 ? { connection: { statement_timeout: String(timeoutMs) } } : {}),
|
|
45
|
+
};
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
/** The bound the pool already applies, so the executor can tell an override from the default. */
|
|
49
|
+
export const poolStatementTimeoutMs = (db: Database) =>
|
|
50
|
+
Number(poolOptions(db).connection?.statement_timeout ?? 0);
|
|
51
|
+
|
|
52
|
+
export const getPool = async (db: Database) => {
|
|
53
|
+
const pool = new SQL(poolOptions(db));
|
|
26
54
|
|
|
27
55
|
await pool.connect(); // Connect to the database
|
|
28
56
|
|
|
@@ -31,6 +59,35 @@ export const getPool = async (db: Database) => {
|
|
|
31
59
|
|
|
32
60
|
export const getPoolSingleton = async (db: Database) => databasesConnections[db.name] as SQL;
|
|
33
61
|
|
|
62
|
+
// A timeout other than the pool's own has to be set on a connection, which
|
|
63
|
+
// means pinning one for the duration. Only an override pays for that.
|
|
64
|
+
const executeReserved = async <T>(
|
|
65
|
+
pool: SQL,
|
|
66
|
+
timeoutMs: number,
|
|
67
|
+
query: string,
|
|
68
|
+
params: unknown[],
|
|
69
|
+
) => {
|
|
70
|
+
// SET takes no bind parameters, so the value is interpolated. It is an
|
|
71
|
+
// integer from the config schema; re-checked here so a widened type upstream
|
|
72
|
+
// cannot turn this line into an injection point.
|
|
73
|
+
if (!Number.isInteger(timeoutMs) || timeoutMs < 0) {
|
|
74
|
+
throw new Error(`Invalid statement timeout: ${timeoutMs}`);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const reserved = await pool.reserve();
|
|
78
|
+
|
|
79
|
+
try {
|
|
80
|
+
await reserved.unsafe(`SET statement_timeout = ${timeoutMs}`);
|
|
81
|
+
|
|
82
|
+
return await reserved.unsafe<T>(query, params);
|
|
83
|
+
} finally {
|
|
84
|
+
// The connection goes back to the pool carrying the pool's default again,
|
|
85
|
+
// not this query's override.
|
|
86
|
+
await reserved.unsafe("RESET statement_timeout");
|
|
87
|
+
await reserved.release();
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
|
|
34
91
|
export const executeQueryFactory =
|
|
35
92
|
(singleQuery = false) =>
|
|
36
93
|
async <T>(
|
|
@@ -38,13 +95,16 @@ export const executeQueryFactory =
|
|
|
38
95
|
db: Database,
|
|
39
96
|
variablesDefinition: VariableDefinition[],
|
|
40
97
|
values: Record<string, unknown> = {},
|
|
98
|
+
timeoutMs?: number,
|
|
41
99
|
) => {
|
|
42
100
|
const pool = singleQuery ? await getPool(db) : await getPoolSingleton(db);
|
|
101
|
+
const params = variablesDefinition.map((v) => values[v.name]);
|
|
43
102
|
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
103
|
+
const override = timeoutMs !== undefined && timeoutMs !== poolStatementTimeoutMs(db);
|
|
104
|
+
|
|
105
|
+
const result = override
|
|
106
|
+
? await executeReserved<T>(pool, timeoutMs, query, params)
|
|
107
|
+
: await pool.unsafe<T>(query, params);
|
|
48
108
|
|
|
49
109
|
if (singleQuery) {
|
|
50
110
|
await pool.close();
|
|
@@ -63,12 +123,14 @@ export const executeQueryJSONFactory =
|
|
|
63
123
|
db: Database,
|
|
64
124
|
variablesDefinition: VariableDefinition[] = [],
|
|
65
125
|
values: Record<string, unknown> = {},
|
|
126
|
+
timeoutMs?: number,
|
|
66
127
|
) => {
|
|
67
128
|
const result = await (singleQuery ? executeQuerySingle : executeQuery)<[{ json_result: T }]>(
|
|
68
129
|
query,
|
|
69
130
|
db,
|
|
70
131
|
variablesDefinition,
|
|
71
132
|
values,
|
|
133
|
+
timeoutMs,
|
|
72
134
|
);
|
|
73
135
|
|
|
74
136
|
return result[0].json_result as T;
|
|
@@ -77,22 +139,24 @@ export const executeQueryJSONFactory =
|
|
|
77
139
|
export const executeQueryJSON = executeQueryJSONFactory();
|
|
78
140
|
export const executeQueryJSONSingle = executeQueryJSONFactory(true);
|
|
79
141
|
|
|
142
|
+
// The generated schema types every stored procedure mutation as `Boolean!`, so
|
|
143
|
+
// the call reports whether it ran, not what it selected. Returning the rows here
|
|
144
|
+
// would put an array behind a field the schema promises is a boolean.
|
|
80
145
|
export const callStoredProcedure = async (
|
|
81
146
|
sp: ProcedureResolver,
|
|
82
|
-
variablesDefinition: VariableDefinition[],
|
|
83
147
|
variables: Record<string, unknown>,
|
|
84
148
|
) => {
|
|
85
149
|
try {
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
150
|
+
const args = orderProcedureArguments(sp, variables);
|
|
151
|
+
|
|
152
|
+
await executeQuery(
|
|
153
|
+
`SELECT * FROM ${sp.dottedQuotedName}(${args.map((_, i) => `$${i + 1}`).join(", ")});`,
|
|
90
154
|
sp.db!,
|
|
91
|
-
|
|
155
|
+
args,
|
|
92
156
|
variables,
|
|
93
157
|
);
|
|
94
158
|
|
|
95
|
-
return
|
|
159
|
+
return true;
|
|
96
160
|
} catch {
|
|
97
161
|
return false;
|
|
98
162
|
}
|
|
@@ -2,6 +2,7 @@ import { format as sqlFormatter } from "sql-formatter";
|
|
|
2
2
|
|
|
3
3
|
import type { AnalysisResult } from "../../../analyzeQuery/types";
|
|
4
4
|
import type { MergedEntities } from "../../../configuration/getSchemas/mergeEntities";
|
|
5
|
+
import type { PageLimits } from "../../common";
|
|
5
6
|
|
|
6
7
|
import { generateSQL } from "../../core/query-builder";
|
|
7
8
|
|
|
@@ -16,8 +17,9 @@ export const genSql = (
|
|
|
16
17
|
query: AnalysisResult,
|
|
17
18
|
variables: Record<string, unknown> = {},
|
|
18
19
|
hash = false,
|
|
20
|
+
pageLimits: PageLimits | null = null,
|
|
19
21
|
) => {
|
|
20
|
-
const [[, sql]] = generateSQL(entitites, query, variables, hash);
|
|
22
|
+
const [[, sql]] = generateSQL(entitites, query, variables, hash, pageLimits);
|
|
21
23
|
|
|
22
24
|
return format(sql);
|
|
23
25
|
};
|
|
@@ -4,7 +4,7 @@ import type {
|
|
|
4
4
|
VariableDefinition,
|
|
5
5
|
} from "../../../../analyzeQuery/types";
|
|
6
6
|
import type { MergedEntities } from "../../../../configuration/getSchemas/mergeEntities";
|
|
7
|
-
import type { GroupByInfo } from "../../../common";
|
|
7
|
+
import type { GroupByInfo, PageLimits } from "../../../common";
|
|
8
8
|
|
|
9
9
|
import {
|
|
10
10
|
buildOrderByClausePG,
|
|
@@ -154,9 +154,10 @@ export const generateSQL = (
|
|
|
154
154
|
operation: OperationAnalysis,
|
|
155
155
|
variables: Record<string, unknown> = {},
|
|
156
156
|
forHashMethod: boolean = false,
|
|
157
|
+
pageLimits: PageLimits | null = null,
|
|
157
158
|
): string => {
|
|
158
159
|
if (forHashMethod) {
|
|
159
|
-
return `SELECT MD5((${buildSQLForField(entities, operation.variables ?? [], variables, operation.fields[0], null, null, 1, {})})::text) AS "ResultHash"`;
|
|
160
|
+
return `SELECT MD5((${buildSQLForField(entities, operation.variables ?? [], variables, operation.fields[0], null, null, 1, {}, pageLimits)})::text) AS "ResultHash"`;
|
|
160
161
|
}
|
|
161
162
|
|
|
162
163
|
const variablesWithDefault = {
|
|
@@ -217,6 +218,7 @@ export const generateSQL = (
|
|
|
217
218
|
null,
|
|
218
219
|
index + 1,
|
|
219
220
|
{},
|
|
221
|
+
pageLimits,
|
|
220
222
|
);
|
|
221
223
|
|
|
222
224
|
fieldQueries.push(`'${field.alias || field.name}', ${fieldSQL}`);
|
|
@@ -239,6 +241,7 @@ export const buildSQLForField = (
|
|
|
239
241
|
parentTableAlias: string | null,
|
|
240
242
|
level: number,
|
|
241
243
|
aliasMap: { [alias: string]: string },
|
|
244
|
+
pageLimits: PageLimits | null,
|
|
242
245
|
): string => {
|
|
243
246
|
const tableAlias = generateTableAlias(level);
|
|
244
247
|
|
|
@@ -303,6 +306,7 @@ export const buildSQLForField = (
|
|
|
303
306
|
tableAlias,
|
|
304
307
|
level,
|
|
305
308
|
aliasMap,
|
|
309
|
+
pageLimits,
|
|
306
310
|
),
|
|
307
311
|
([name, selector]) => `'${name}', ${selector}`,
|
|
308
312
|
);
|
|
@@ -310,7 +314,12 @@ export const buildSQLForField = (
|
|
|
310
314
|
const fromClause = `FROM ${dottedQuotedName} ${tableAlias}`;
|
|
311
315
|
|
|
312
316
|
const orderByClause = buildOrderByClausePG(entities, field, tableAlias);
|
|
313
|
-
const paginationClause = buildPaginationClausePG(
|
|
317
|
+
const paginationClause = buildPaginationClausePG(
|
|
318
|
+
field,
|
|
319
|
+
variablesDefinition,
|
|
320
|
+
variables,
|
|
321
|
+
pageLimits,
|
|
322
|
+
);
|
|
314
323
|
|
|
315
324
|
const isArraySelection = !!field.isArray && !withoutArrayWrapper;
|
|
316
325
|
|