postgresdk 0.11.0 → 0.12.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 +7 -1
- package/dist/cli.js +88 -12
- package/dist/core/operations.d.ts +2 -0
- package/dist/index.js +88 -12
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -160,7 +160,7 @@ const authors = await sdk.authors.list({
|
|
|
160
160
|
### Filtering & Pagination
|
|
161
161
|
|
|
162
162
|
```typescript
|
|
163
|
-
// Simple equality filtering
|
|
163
|
+
// Simple equality filtering with single-column sorting
|
|
164
164
|
const users = await sdk.users.list({
|
|
165
165
|
where: { status: "active" },
|
|
166
166
|
orderBy: "created_at",
|
|
@@ -169,6 +169,12 @@ const users = await sdk.users.list({
|
|
|
169
169
|
offset: 40
|
|
170
170
|
});
|
|
171
171
|
|
|
172
|
+
// Multi-column sorting
|
|
173
|
+
const sorted = await sdk.users.list({
|
|
174
|
+
orderBy: ["status", "created_at"],
|
|
175
|
+
order: ["asc", "desc"] // or use single direction: order: "asc"
|
|
176
|
+
});
|
|
177
|
+
|
|
172
178
|
// Advanced WHERE operators
|
|
173
179
|
const filtered = await sdk.users.list({
|
|
174
180
|
where: {
|
package/dist/cli.js
CHANGED
|
@@ -793,9 +793,9 @@ const items = await sdk.${tableName}.list();
|
|
|
793
793
|
const filtered = await sdk.${tableName}.list({
|
|
794
794
|
limit: 20,
|
|
795
795
|
offset: 0,
|
|
796
|
-
${table.columns[0]?.name || "field"}
|
|
797
|
-
|
|
798
|
-
|
|
796
|
+
where: { ${table.columns[0]?.name || "field"}: { $like: '%search%' } },
|
|
797
|
+
orderBy: '${table.columns[0]?.name || "created_at"}',
|
|
798
|
+
order: 'desc'
|
|
799
799
|
});`,
|
|
800
800
|
correspondsTo: `GET ${basePath}`
|
|
801
801
|
});
|
|
@@ -1058,8 +1058,8 @@ function generateQueryParams(table) {
|
|
|
1058
1058
|
const params = {
|
|
1059
1059
|
limit: "number - Max records to return (default: 50)",
|
|
1060
1060
|
offset: "number - Records to skip",
|
|
1061
|
-
|
|
1062
|
-
|
|
1061
|
+
orderBy: "string | string[] - Field(s) to sort by",
|
|
1062
|
+
order: "'asc' | 'desc' | ('asc' | 'desc')[] - Sort direction(s)"
|
|
1063
1063
|
};
|
|
1064
1064
|
let filterCount = 0;
|
|
1065
1065
|
for (const col of table.columns) {
|
|
@@ -1351,6 +1351,64 @@ function generateUnifiedContractMarkdown(contract) {
|
|
|
1351
1351
|
lines.push("");
|
|
1352
1352
|
lines.push("**Note:** The WHERE clause types are fully type-safe. TypeScript will only allow operators that are valid for each field type.");
|
|
1353
1353
|
lines.push("");
|
|
1354
|
+
lines.push("## Sorting");
|
|
1355
|
+
lines.push("");
|
|
1356
|
+
lines.push("Sort query results using the `orderBy` and `order` parameters. Supports both single and multi-column sorting.");
|
|
1357
|
+
lines.push("");
|
|
1358
|
+
lines.push("### Single Column Sorting");
|
|
1359
|
+
lines.push("");
|
|
1360
|
+
lines.push("```typescript");
|
|
1361
|
+
lines.push("// Sort by one column ascending");
|
|
1362
|
+
lines.push("const users = await sdk.users.list({");
|
|
1363
|
+
lines.push(" orderBy: 'created_at',");
|
|
1364
|
+
lines.push(" order: 'asc'");
|
|
1365
|
+
lines.push("});");
|
|
1366
|
+
lines.push("");
|
|
1367
|
+
lines.push("// Sort descending");
|
|
1368
|
+
lines.push("const latest = await sdk.users.list({");
|
|
1369
|
+
lines.push(" orderBy: 'created_at',");
|
|
1370
|
+
lines.push(" order: 'desc'");
|
|
1371
|
+
lines.push("});");
|
|
1372
|
+
lines.push("");
|
|
1373
|
+
lines.push("// Order defaults to 'asc' if not specified");
|
|
1374
|
+
lines.push("const sorted = await sdk.users.list({");
|
|
1375
|
+
lines.push(" orderBy: 'name'");
|
|
1376
|
+
lines.push("});");
|
|
1377
|
+
lines.push("```");
|
|
1378
|
+
lines.push("");
|
|
1379
|
+
lines.push("### Multi-Column Sorting");
|
|
1380
|
+
lines.push("");
|
|
1381
|
+
lines.push("```typescript");
|
|
1382
|
+
lines.push("// Sort by multiple columns (all same direction)");
|
|
1383
|
+
lines.push("const users = await sdk.users.list({");
|
|
1384
|
+
lines.push(" orderBy: ['status', 'created_at'],");
|
|
1385
|
+
lines.push(" order: 'desc'");
|
|
1386
|
+
lines.push("});");
|
|
1387
|
+
lines.push("");
|
|
1388
|
+
lines.push("// Different direction per column");
|
|
1389
|
+
lines.push("const sorted = await sdk.users.list({");
|
|
1390
|
+
lines.push(" orderBy: ['status', 'created_at'],");
|
|
1391
|
+
lines.push(" order: ['asc', 'desc'] // status ASC, created_at DESC");
|
|
1392
|
+
lines.push("});");
|
|
1393
|
+
lines.push("```");
|
|
1394
|
+
lines.push("");
|
|
1395
|
+
lines.push("### Combining Sorting with Filters");
|
|
1396
|
+
lines.push("");
|
|
1397
|
+
lines.push("```typescript");
|
|
1398
|
+
lines.push("const results = await sdk.users.list({");
|
|
1399
|
+
lines.push(" where: {");
|
|
1400
|
+
lines.push(" status: 'active',");
|
|
1401
|
+
lines.push(" age: { $gte: 18 }");
|
|
1402
|
+
lines.push(" },");
|
|
1403
|
+
lines.push(" orderBy: 'created_at',");
|
|
1404
|
+
lines.push(" order: 'desc',");
|
|
1405
|
+
lines.push(" limit: 50,");
|
|
1406
|
+
lines.push(" offset: 0");
|
|
1407
|
+
lines.push("});");
|
|
1408
|
+
lines.push("```");
|
|
1409
|
+
lines.push("");
|
|
1410
|
+
lines.push("**Note:** Column names are validated by Zod schemas. Only valid table columns are accepted, preventing SQL injection.");
|
|
1411
|
+
lines.push("");
|
|
1354
1412
|
lines.push("## Resources");
|
|
1355
1413
|
lines.push("");
|
|
1356
1414
|
for (const resource of contract.resources) {
|
|
@@ -2625,6 +2683,7 @@ function emitHonoRoutes(table, _graph, opts) {
|
|
|
2625
2683
|
const hasAuth = opts.authStrategy && opts.authStrategy !== "none";
|
|
2626
2684
|
const ext = opts.useJsExtensions ? ".js" : "";
|
|
2627
2685
|
const authImport = hasAuth ? `import { authMiddleware } from "../auth${ext}";` : "";
|
|
2686
|
+
const columnNames = table.columns.map((c) => `"${c.name}"`).join(", ");
|
|
2628
2687
|
return `/**
|
|
2629
2688
|
* AUTO-GENERATED FILE - DO NOT EDIT
|
|
2630
2689
|
*
|
|
@@ -2641,12 +2700,15 @@ import { loadIncludes } from "../include-loader${ext}";
|
|
|
2641
2700
|
import * as coreOps from "../core/operations${ext}";
|
|
2642
2701
|
${authImport}
|
|
2643
2702
|
|
|
2703
|
+
const columnEnum = z.enum([${columnNames}]);
|
|
2704
|
+
|
|
2644
2705
|
const listSchema = z.object({
|
|
2645
2706
|
where: z.any().optional(),
|
|
2646
2707
|
include: z.any().optional(),
|
|
2647
2708
|
limit: z.number().int().positive().max(100).optional(),
|
|
2648
2709
|
offset: z.number().int().min(0).optional(),
|
|
2649
|
-
orderBy: z.
|
|
2710
|
+
orderBy: z.union([columnEnum, z.array(columnEnum)]).optional(),
|
|
2711
|
+
order: z.union([z.enum(["asc", "desc"]), z.array(z.enum(["asc", "desc"]))]).optional()
|
|
2650
2712
|
});
|
|
2651
2713
|
|
|
2652
2714
|
export function register${Type}Routes(app: Hono, deps: { pg: { query: (text: string, params?: any[]) => Promise<{ rows: any[] }> }, onRequest?: (c: Context, pg: { query: (text: string, params?: any[]) => Promise<{ rows: any[] }> }) => Promise<void> }) {
|
|
@@ -2835,7 +2897,7 @@ function emitClient(table, graph, opts, model) {
|
|
|
2835
2897
|
let includeMethodsCode = "";
|
|
2836
2898
|
for (const method of includeMethods) {
|
|
2837
2899
|
const isGetByPk = method.name.startsWith("getByPk");
|
|
2838
|
-
const baseParams = isGetByPk ? "" : `params?: Omit<{ limit?: number; offset?: number; where?: Where<Select${Type}>; orderBy?: string; order?: "asc" | "desc"; }, "include">`;
|
|
2900
|
+
const baseParams = isGetByPk ? "" : `params?: Omit<{ limit?: number; offset?: number; where?: Where<Select${Type}>; orderBy?: string | string[]; order?: "asc" | "desc" | ("asc" | "desc")[]; }, "include">`;
|
|
2839
2901
|
if (isGetByPk) {
|
|
2840
2902
|
const pkWhere = hasCompositePk ? `{ ${safePk.map((col) => `${col}: pk.${col}`).join(", ")} }` : `{ ${safePk[0] || "id"}: pk }`;
|
|
2841
2903
|
const baseReturnType = method.returnType.replace(" | null", "");
|
|
@@ -2891,8 +2953,8 @@ export class ${Type}Client extends BaseClient {
|
|
|
2891
2953
|
limit?: number;
|
|
2892
2954
|
offset?: number;
|
|
2893
2955
|
where?: Where<Select${Type}>;
|
|
2894
|
-
orderBy?: string;
|
|
2895
|
-
order?: "asc" | "desc";
|
|
2956
|
+
orderBy?: string | string[];
|
|
2957
|
+
order?: "asc" | "desc" | ("asc" | "desc")[];
|
|
2896
2958
|
}): Promise<Select${Type}[]> {
|
|
2897
2959
|
return this.post<Select${Type}[]>(\`\${this.resource}/list\`, params ?? {});
|
|
2898
2960
|
}
|
|
@@ -4226,10 +4288,10 @@ function buildWhereClause(
|
|
|
4226
4288
|
*/
|
|
4227
4289
|
export async function listRecords(
|
|
4228
4290
|
ctx: OperationContext,
|
|
4229
|
-
params: { where?: any; limit?: number; offset?: number; include?: any }
|
|
4291
|
+
params: { where?: any; limit?: number; offset?: number; include?: any; orderBy?: string | string[]; order?: "asc" | "desc" | ("asc" | "desc")[] }
|
|
4230
4292
|
): Promise<{ data?: any; error?: string; issues?: any; needsIncludes?: boolean; includeSpec?: any; status: number }> {
|
|
4231
4293
|
try {
|
|
4232
|
-
const { where: whereClause, limit = 50, offset = 0, include } = params;
|
|
4294
|
+
const { where: whereClause, limit = 50, offset = 0, include, orderBy, order } = params;
|
|
4233
4295
|
|
|
4234
4296
|
// Build WHERE clause
|
|
4235
4297
|
let paramIndex = 1;
|
|
@@ -4253,12 +4315,26 @@ export async function listRecords(
|
|
|
4253
4315
|
|
|
4254
4316
|
const whereSQL = whereParts.length > 0 ? \`WHERE \${whereParts.join(" AND ")}\` : "";
|
|
4255
4317
|
|
|
4318
|
+
// Build ORDER BY clause
|
|
4319
|
+
let orderBySQL = "";
|
|
4320
|
+
if (orderBy) {
|
|
4321
|
+
const columns = Array.isArray(orderBy) ? orderBy : [orderBy];
|
|
4322
|
+
const directions = Array.isArray(order) ? order : (order ? Array(columns.length).fill(order) : Array(columns.length).fill("asc"));
|
|
4323
|
+
|
|
4324
|
+
const orderParts = columns.map((col, i) => {
|
|
4325
|
+
const dir = (directions[i] || "asc").toUpperCase();
|
|
4326
|
+
return \`"\${col}" \${dir}\`;
|
|
4327
|
+
});
|
|
4328
|
+
|
|
4329
|
+
orderBySQL = \`ORDER BY \${orderParts.join(", ")}\`;
|
|
4330
|
+
}
|
|
4331
|
+
|
|
4256
4332
|
// Add limit and offset params
|
|
4257
4333
|
const limitParam = \`$\${paramIndex}\`;
|
|
4258
4334
|
const offsetParam = \`$\${paramIndex + 1}\`;
|
|
4259
4335
|
const allParams = [...whereParams, limit, offset];
|
|
4260
4336
|
|
|
4261
|
-
const text = \`SELECT * FROM "\${ctx.table}" \${whereSQL} LIMIT \${limitParam} OFFSET \${offsetParam}\`;
|
|
4337
|
+
const text = \`SELECT * FROM "\${ctx.table}" \${whereSQL} \${orderBySQL} LIMIT \${limitParam} OFFSET \${offsetParam}\`;
|
|
4262
4338
|
log.debug(\`LIST \${ctx.table} SQL:\`, text, "params:", allParams);
|
|
4263
4339
|
|
|
4264
4340
|
const { rows } = await ctx.pg.query(text, allParams);
|
package/dist/index.js
CHANGED
|
@@ -792,9 +792,9 @@ const items = await sdk.${tableName}.list();
|
|
|
792
792
|
const filtered = await sdk.${tableName}.list({
|
|
793
793
|
limit: 20,
|
|
794
794
|
offset: 0,
|
|
795
|
-
${table.columns[0]?.name || "field"}
|
|
796
|
-
|
|
797
|
-
|
|
795
|
+
where: { ${table.columns[0]?.name || "field"}: { $like: '%search%' } },
|
|
796
|
+
orderBy: '${table.columns[0]?.name || "created_at"}',
|
|
797
|
+
order: 'desc'
|
|
798
798
|
});`,
|
|
799
799
|
correspondsTo: `GET ${basePath}`
|
|
800
800
|
});
|
|
@@ -1057,8 +1057,8 @@ function generateQueryParams(table) {
|
|
|
1057
1057
|
const params = {
|
|
1058
1058
|
limit: "number - Max records to return (default: 50)",
|
|
1059
1059
|
offset: "number - Records to skip",
|
|
1060
|
-
|
|
1061
|
-
|
|
1060
|
+
orderBy: "string | string[] - Field(s) to sort by",
|
|
1061
|
+
order: "'asc' | 'desc' | ('asc' | 'desc')[] - Sort direction(s)"
|
|
1062
1062
|
};
|
|
1063
1063
|
let filterCount = 0;
|
|
1064
1064
|
for (const col of table.columns) {
|
|
@@ -1350,6 +1350,64 @@ function generateUnifiedContractMarkdown(contract) {
|
|
|
1350
1350
|
lines.push("");
|
|
1351
1351
|
lines.push("**Note:** The WHERE clause types are fully type-safe. TypeScript will only allow operators that are valid for each field type.");
|
|
1352
1352
|
lines.push("");
|
|
1353
|
+
lines.push("## Sorting");
|
|
1354
|
+
lines.push("");
|
|
1355
|
+
lines.push("Sort query results using the `orderBy` and `order` parameters. Supports both single and multi-column sorting.");
|
|
1356
|
+
lines.push("");
|
|
1357
|
+
lines.push("### Single Column Sorting");
|
|
1358
|
+
lines.push("");
|
|
1359
|
+
lines.push("```typescript");
|
|
1360
|
+
lines.push("// Sort by one column ascending");
|
|
1361
|
+
lines.push("const users = await sdk.users.list({");
|
|
1362
|
+
lines.push(" orderBy: 'created_at',");
|
|
1363
|
+
lines.push(" order: 'asc'");
|
|
1364
|
+
lines.push("});");
|
|
1365
|
+
lines.push("");
|
|
1366
|
+
lines.push("// Sort descending");
|
|
1367
|
+
lines.push("const latest = await sdk.users.list({");
|
|
1368
|
+
lines.push(" orderBy: 'created_at',");
|
|
1369
|
+
lines.push(" order: 'desc'");
|
|
1370
|
+
lines.push("});");
|
|
1371
|
+
lines.push("");
|
|
1372
|
+
lines.push("// Order defaults to 'asc' if not specified");
|
|
1373
|
+
lines.push("const sorted = await sdk.users.list({");
|
|
1374
|
+
lines.push(" orderBy: 'name'");
|
|
1375
|
+
lines.push("});");
|
|
1376
|
+
lines.push("```");
|
|
1377
|
+
lines.push("");
|
|
1378
|
+
lines.push("### Multi-Column Sorting");
|
|
1379
|
+
lines.push("");
|
|
1380
|
+
lines.push("```typescript");
|
|
1381
|
+
lines.push("// Sort by multiple columns (all same direction)");
|
|
1382
|
+
lines.push("const users = await sdk.users.list({");
|
|
1383
|
+
lines.push(" orderBy: ['status', 'created_at'],");
|
|
1384
|
+
lines.push(" order: 'desc'");
|
|
1385
|
+
lines.push("});");
|
|
1386
|
+
lines.push("");
|
|
1387
|
+
lines.push("// Different direction per column");
|
|
1388
|
+
lines.push("const sorted = await sdk.users.list({");
|
|
1389
|
+
lines.push(" orderBy: ['status', 'created_at'],");
|
|
1390
|
+
lines.push(" order: ['asc', 'desc'] // status ASC, created_at DESC");
|
|
1391
|
+
lines.push("});");
|
|
1392
|
+
lines.push("```");
|
|
1393
|
+
lines.push("");
|
|
1394
|
+
lines.push("### Combining Sorting with Filters");
|
|
1395
|
+
lines.push("");
|
|
1396
|
+
lines.push("```typescript");
|
|
1397
|
+
lines.push("const results = await sdk.users.list({");
|
|
1398
|
+
lines.push(" where: {");
|
|
1399
|
+
lines.push(" status: 'active',");
|
|
1400
|
+
lines.push(" age: { $gte: 18 }");
|
|
1401
|
+
lines.push(" },");
|
|
1402
|
+
lines.push(" orderBy: 'created_at',");
|
|
1403
|
+
lines.push(" order: 'desc',");
|
|
1404
|
+
lines.push(" limit: 50,");
|
|
1405
|
+
lines.push(" offset: 0");
|
|
1406
|
+
lines.push("});");
|
|
1407
|
+
lines.push("```");
|
|
1408
|
+
lines.push("");
|
|
1409
|
+
lines.push("**Note:** Column names are validated by Zod schemas. Only valid table columns are accepted, preventing SQL injection.");
|
|
1410
|
+
lines.push("");
|
|
1353
1411
|
lines.push("## Resources");
|
|
1354
1412
|
lines.push("");
|
|
1355
1413
|
for (const resource of contract.resources) {
|
|
@@ -1865,6 +1923,7 @@ function emitHonoRoutes(table, _graph, opts) {
|
|
|
1865
1923
|
const hasAuth = opts.authStrategy && opts.authStrategy !== "none";
|
|
1866
1924
|
const ext = opts.useJsExtensions ? ".js" : "";
|
|
1867
1925
|
const authImport = hasAuth ? `import { authMiddleware } from "../auth${ext}";` : "";
|
|
1926
|
+
const columnNames = table.columns.map((c) => `"${c.name}"`).join(", ");
|
|
1868
1927
|
return `/**
|
|
1869
1928
|
* AUTO-GENERATED FILE - DO NOT EDIT
|
|
1870
1929
|
*
|
|
@@ -1881,12 +1940,15 @@ import { loadIncludes } from "../include-loader${ext}";
|
|
|
1881
1940
|
import * as coreOps from "../core/operations${ext}";
|
|
1882
1941
|
${authImport}
|
|
1883
1942
|
|
|
1943
|
+
const columnEnum = z.enum([${columnNames}]);
|
|
1944
|
+
|
|
1884
1945
|
const listSchema = z.object({
|
|
1885
1946
|
where: z.any().optional(),
|
|
1886
1947
|
include: z.any().optional(),
|
|
1887
1948
|
limit: z.number().int().positive().max(100).optional(),
|
|
1888
1949
|
offset: z.number().int().min(0).optional(),
|
|
1889
|
-
orderBy: z.
|
|
1950
|
+
orderBy: z.union([columnEnum, z.array(columnEnum)]).optional(),
|
|
1951
|
+
order: z.union([z.enum(["asc", "desc"]), z.array(z.enum(["asc", "desc"]))]).optional()
|
|
1890
1952
|
});
|
|
1891
1953
|
|
|
1892
1954
|
export function register${Type}Routes(app: Hono, deps: { pg: { query: (text: string, params?: any[]) => Promise<{ rows: any[] }> }, onRequest?: (c: Context, pg: { query: (text: string, params?: any[]) => Promise<{ rows: any[] }> }) => Promise<void> }) {
|
|
@@ -2075,7 +2137,7 @@ function emitClient(table, graph, opts, model) {
|
|
|
2075
2137
|
let includeMethodsCode = "";
|
|
2076
2138
|
for (const method of includeMethods) {
|
|
2077
2139
|
const isGetByPk = method.name.startsWith("getByPk");
|
|
2078
|
-
const baseParams = isGetByPk ? "" : `params?: Omit<{ limit?: number; offset?: number; where?: Where<Select${Type}>; orderBy?: string; order?: "asc" | "desc"; }, "include">`;
|
|
2140
|
+
const baseParams = isGetByPk ? "" : `params?: Omit<{ limit?: number; offset?: number; where?: Where<Select${Type}>; orderBy?: string | string[]; order?: "asc" | "desc" | ("asc" | "desc")[]; }, "include">`;
|
|
2079
2141
|
if (isGetByPk) {
|
|
2080
2142
|
const pkWhere = hasCompositePk ? `{ ${safePk.map((col) => `${col}: pk.${col}`).join(", ")} }` : `{ ${safePk[0] || "id"}: pk }`;
|
|
2081
2143
|
const baseReturnType = method.returnType.replace(" | null", "");
|
|
@@ -2131,8 +2193,8 @@ export class ${Type}Client extends BaseClient {
|
|
|
2131
2193
|
limit?: number;
|
|
2132
2194
|
offset?: number;
|
|
2133
2195
|
where?: Where<Select${Type}>;
|
|
2134
|
-
orderBy?: string;
|
|
2135
|
-
order?: "asc" | "desc";
|
|
2196
|
+
orderBy?: string | string[];
|
|
2197
|
+
order?: "asc" | "desc" | ("asc" | "desc")[];
|
|
2136
2198
|
}): Promise<Select${Type}[]> {
|
|
2137
2199
|
return this.post<Select${Type}[]>(\`\${this.resource}/list\`, params ?? {});
|
|
2138
2200
|
}
|
|
@@ -3466,10 +3528,10 @@ function buildWhereClause(
|
|
|
3466
3528
|
*/
|
|
3467
3529
|
export async function listRecords(
|
|
3468
3530
|
ctx: OperationContext,
|
|
3469
|
-
params: { where?: any; limit?: number; offset?: number; include?: any }
|
|
3531
|
+
params: { where?: any; limit?: number; offset?: number; include?: any; orderBy?: string | string[]; order?: "asc" | "desc" | ("asc" | "desc")[] }
|
|
3470
3532
|
): Promise<{ data?: any; error?: string; issues?: any; needsIncludes?: boolean; includeSpec?: any; status: number }> {
|
|
3471
3533
|
try {
|
|
3472
|
-
const { where: whereClause, limit = 50, offset = 0, include } = params;
|
|
3534
|
+
const { where: whereClause, limit = 50, offset = 0, include, orderBy, order } = params;
|
|
3473
3535
|
|
|
3474
3536
|
// Build WHERE clause
|
|
3475
3537
|
let paramIndex = 1;
|
|
@@ -3493,12 +3555,26 @@ export async function listRecords(
|
|
|
3493
3555
|
|
|
3494
3556
|
const whereSQL = whereParts.length > 0 ? \`WHERE \${whereParts.join(" AND ")}\` : "";
|
|
3495
3557
|
|
|
3558
|
+
// Build ORDER BY clause
|
|
3559
|
+
let orderBySQL = "";
|
|
3560
|
+
if (orderBy) {
|
|
3561
|
+
const columns = Array.isArray(orderBy) ? orderBy : [orderBy];
|
|
3562
|
+
const directions = Array.isArray(order) ? order : (order ? Array(columns.length).fill(order) : Array(columns.length).fill("asc"));
|
|
3563
|
+
|
|
3564
|
+
const orderParts = columns.map((col, i) => {
|
|
3565
|
+
const dir = (directions[i] || "asc").toUpperCase();
|
|
3566
|
+
return \`"\${col}" \${dir}\`;
|
|
3567
|
+
});
|
|
3568
|
+
|
|
3569
|
+
orderBySQL = \`ORDER BY \${orderParts.join(", ")}\`;
|
|
3570
|
+
}
|
|
3571
|
+
|
|
3496
3572
|
// Add limit and offset params
|
|
3497
3573
|
const limitParam = \`$\${paramIndex}\`;
|
|
3498
3574
|
const offsetParam = \`$\${paramIndex + 1}\`;
|
|
3499
3575
|
const allParams = [...whereParams, limit, offset];
|
|
3500
3576
|
|
|
3501
|
-
const text = \`SELECT * FROM "\${ctx.table}" \${whereSQL} LIMIT \${limitParam} OFFSET \${offsetParam}\`;
|
|
3577
|
+
const text = \`SELECT * FROM "\${ctx.table}" \${whereSQL} \${orderBySQL} LIMIT \${limitParam} OFFSET \${offsetParam}\`;
|
|
3502
3578
|
log.debug(\`LIST \${ctx.table} SQL:\`, text, "params:", allParams);
|
|
3503
3579
|
|
|
3504
3580
|
const { rows } = await ctx.pg.query(text, allParams);
|