@vantreeseba/drizzle-graphql 5.2.0 → 5.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 +47 -0
- package/dist/README.md +47 -0
- package/dist/index.cjs +493 -11
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +39 -1
- package/dist/index.d.ts +39 -1
- package/dist/index.js +494 -12
- package/dist/index.js.map +1 -1
- package/dist/package.json +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1020,6 +1020,13 @@ var generateTableSelectTypeFieldsCached = (table, tableName) => {
|
|
|
1020
1020
|
drizzleColumnToGraphQLType(columnDescription, columnName, tableName)
|
|
1021
1021
|
])
|
|
1022
1022
|
);
|
|
1023
|
+
if (!remapped[CURSOR_FIELD_NAME]) {
|
|
1024
|
+
remapped[CURSOR_FIELD_NAME] = {
|
|
1025
|
+
type: GraphQLString2,
|
|
1026
|
+
description: "Opaque cursor of this row's position in the query's ordering. Pass it as `after` to resume from here. Only set on rows returned by a list query.",
|
|
1027
|
+
resolve: rowCursorResolver
|
|
1028
|
+
};
|
|
1029
|
+
}
|
|
1023
1030
|
fieldMap.set(table, remapped);
|
|
1024
1031
|
return remapped;
|
|
1025
1032
|
};
|
|
@@ -1847,6 +1854,117 @@ var primaryKeyOrderExprs = (table, pkNames) => {
|
|
|
1847
1854
|
const cols = getColumns(table);
|
|
1848
1855
|
return pkNames.map((n) => cols[n]).filter(Boolean).map((col) => asc(col));
|
|
1849
1856
|
};
|
|
1857
|
+
var CURSOR_FIELD_NAME = "cursor";
|
|
1858
|
+
var ROW_CURSOR_PROP = "__drizzle_graphql_cursor";
|
|
1859
|
+
var orderByHasRelationEntry = (orderBy) => !!orderBy && Object.values(orderBy).some((config) => config && typeof config === "object" && config.direction === void 0);
|
|
1860
|
+
var cursorOrderingEntries = (orderBy, pkNames) => {
|
|
1861
|
+
const entries = orderBy ? orderByEntries(orderBy) : [];
|
|
1862
|
+
const seen = new Set(entries.map(([column]) => column));
|
|
1863
|
+
for (const pk of pkNames) {
|
|
1864
|
+
if (!seen.has(pk)) {
|
|
1865
|
+
entries.push([pk, "asc"]);
|
|
1866
|
+
}
|
|
1867
|
+
}
|
|
1868
|
+
return entries;
|
|
1869
|
+
};
|
|
1870
|
+
var cursorOrderExprs = (table, entries) => {
|
|
1871
|
+
const cols = getColumns(table);
|
|
1872
|
+
return entries.flatMap(
|
|
1873
|
+
([column, direction, nulls]) => orderExpressions(cols[column], direction, nulls ?? void 0, cols[column])
|
|
1874
|
+
);
|
|
1875
|
+
};
|
|
1876
|
+
var encodeCursorValue = (value) => {
|
|
1877
|
+
if (value instanceof Date) {
|
|
1878
|
+
return { $type: "date", value: value.toISOString() };
|
|
1879
|
+
}
|
|
1880
|
+
if (typeof value === "bigint") {
|
|
1881
|
+
return { $type: "bigint", value: value.toString() };
|
|
1882
|
+
}
|
|
1883
|
+
return value;
|
|
1884
|
+
};
|
|
1885
|
+
var decodeCursorValue = (value) => {
|
|
1886
|
+
if (value && typeof value === "object" && typeof value.$type === "string") {
|
|
1887
|
+
if (value.$type === "date") {
|
|
1888
|
+
return new Date(value.value);
|
|
1889
|
+
}
|
|
1890
|
+
if (value.$type === "bigint") {
|
|
1891
|
+
return BigInt(value.value);
|
|
1892
|
+
}
|
|
1893
|
+
}
|
|
1894
|
+
return value;
|
|
1895
|
+
};
|
|
1896
|
+
var encodeCursor = (entries, row) => {
|
|
1897
|
+
const payload = {
|
|
1898
|
+
o: entries,
|
|
1899
|
+
v: entries.map(([column]) => encodeCursorValue(row[column] ?? null))
|
|
1900
|
+
};
|
|
1901
|
+
return Buffer.from(JSON.stringify(payload), "utf8").toString("base64url");
|
|
1902
|
+
};
|
|
1903
|
+
var decodeCursor = (after, entries) => {
|
|
1904
|
+
let payload;
|
|
1905
|
+
try {
|
|
1906
|
+
payload = JSON.parse(Buffer.from(after, "base64url").toString("utf8"));
|
|
1907
|
+
} catch (_e) {
|
|
1908
|
+
throw new GraphQLError3("Invalid cursor: unable to decode it. Pass a cursor returned by a previous page.");
|
|
1909
|
+
}
|
|
1910
|
+
if (!payload || typeof payload !== "object" || !Array.isArray(payload.o) || !Array.isArray(payload.v) || payload.o.length !== payload.v.length) {
|
|
1911
|
+
throw new GraphQLError3("Invalid cursor: malformed payload. Pass a cursor returned by a previous page.");
|
|
1912
|
+
}
|
|
1913
|
+
const matchesOrdering = payload.o.length === entries.length && payload.o.every(
|
|
1914
|
+
(entry, i) => Array.isArray(entry) && entry[0] === entries[i][0] && entry[1] === entries[i][1] && // The nulls override changes where NULLs sort, so it is part of the ordering identity.
|
|
1915
|
+
// JSON round-trips undefined as null — compare the normalized forms.
|
|
1916
|
+
(entry[2] ?? null) === (entries[i][2] ?? null)
|
|
1917
|
+
);
|
|
1918
|
+
if (!matchesOrdering) {
|
|
1919
|
+
throw new GraphQLError3(
|
|
1920
|
+
"Invalid cursor: it was issued for a different ordering. Pass the same orderBy the cursor's page used."
|
|
1921
|
+
);
|
|
1922
|
+
}
|
|
1923
|
+
return payload.v.map(decodeCursorValue);
|
|
1924
|
+
};
|
|
1925
|
+
var buildCursorCondition = (table, entries, values, nullOrdering) => {
|
|
1926
|
+
const cols = getColumns(table);
|
|
1927
|
+
const disjuncts = [];
|
|
1928
|
+
const equalities = [];
|
|
1929
|
+
entries.forEach(([column, direction, nulls], i) => {
|
|
1930
|
+
const col = cols[column];
|
|
1931
|
+
if (!col) {
|
|
1932
|
+
throw new GraphQLError3(`Invalid cursor: unknown column '${column}'.`);
|
|
1933
|
+
}
|
|
1934
|
+
const value = values[i];
|
|
1935
|
+
const nullsSortLast = nulls != null ? nulls === "last" : direction === "asc" === (nullOrdering === "nulls-largest");
|
|
1936
|
+
let strictlyAfter;
|
|
1937
|
+
if (value === null || value === void 0) {
|
|
1938
|
+
strictlyAfter = nullsSortLast ? void 0 : isNotNull(col);
|
|
1939
|
+
} else {
|
|
1940
|
+
const comparison = direction === "asc" ? gt(col, value) : lt(col, value);
|
|
1941
|
+
strictlyAfter = nullsSortLast ? or(comparison, isNull(col)) : comparison;
|
|
1942
|
+
}
|
|
1943
|
+
if (strictlyAfter) {
|
|
1944
|
+
disjuncts.push(equalities.length ? and(...equalities, strictlyAfter) : strictlyAfter);
|
|
1945
|
+
}
|
|
1946
|
+
equalities.push(value === null || value === void 0 ? isNull(col) : eq(col, value));
|
|
1947
|
+
});
|
|
1948
|
+
if (!disjuncts.length) {
|
|
1949
|
+
return sql`1 = 0`;
|
|
1950
|
+
}
|
|
1951
|
+
return disjuncts.length > 1 ? or(...disjuncts) : disjuncts[0];
|
|
1952
|
+
};
|
|
1953
|
+
var isCursorFieldSelected = (tree, table) => {
|
|
1954
|
+
if (!tree) {
|
|
1955
|
+
return false;
|
|
1956
|
+
}
|
|
1957
|
+
if (getColumns(table)[CURSOR_FIELD_NAME]) {
|
|
1958
|
+
return false;
|
|
1959
|
+
}
|
|
1960
|
+
return Object.values(tree).some((field) => field.name === CURSOR_FIELD_NAME);
|
|
1961
|
+
};
|
|
1962
|
+
var attachRowCursors = (rows, entries) => {
|
|
1963
|
+
for (const row of rows) {
|
|
1964
|
+
row[ROW_CURSOR_PROP] = encodeCursor(entries, row);
|
|
1965
|
+
}
|
|
1966
|
+
};
|
|
1967
|
+
var rowCursorResolver = (source) => source?.[ROW_CURSOR_PROP] ?? null;
|
|
1850
1968
|
var getUniqueColumnSets = (table, getTableConfig4) => {
|
|
1851
1969
|
const cols = getColumns(table);
|
|
1852
1970
|
const propNameByColumnName = new Map(Object.entries(cols).map(([propName, col]) => [col.name, propName]));
|
|
@@ -2133,6 +2251,7 @@ var computeResolverFieldNames = (tableName, typeNameMapper, prefixes, suffixes)
|
|
|
2133
2251
|
const upsertArrayFieldName = `${upsertPrefix}${mapped ? capitalize(mapped.plural) : capitalize(tableName)}`;
|
|
2134
2252
|
const upsertSingleFieldName = mapped ? `${upsertPrefix}${capitalize(mapped.singular)}` : `${upsertPrefix}${capitalize(tableName)}${suffixes.single}`;
|
|
2135
2253
|
const updateFieldName = `${prefixes.update}${mapped ? capitalize(mapped.singular) : capitalize(tableName)}`;
|
|
2254
|
+
const updateManyFieldName = `${prefixes.update}${mapped ? capitalize(mapped.plural) : capitalize(tableName)}Many`;
|
|
2136
2255
|
const deleteFieldName = `${prefixes.delete}${mapped ? capitalize(mapped.singular) : capitalize(tableName)}`;
|
|
2137
2256
|
const writeSingleSuffix = suffixes.single === "" ? "Single" : suffixes.single;
|
|
2138
2257
|
const updateSingleFieldName = `${updateFieldName}${writeSingleSuffix}`;
|
|
@@ -2148,11 +2267,28 @@ var computeResolverFieldNames = (tableName, typeNameMapper, prefixes, suffixes)
|
|
|
2148
2267
|
upsertArrayFieldName,
|
|
2149
2268
|
upsertSingleFieldName,
|
|
2150
2269
|
updateFieldName,
|
|
2270
|
+
updateManyFieldName,
|
|
2151
2271
|
updateSingleFieldName,
|
|
2152
2272
|
deleteFieldName,
|
|
2153
2273
|
deleteSingleFieldName
|
|
2154
2274
|
};
|
|
2155
2275
|
};
|
|
2276
|
+
var generateUpdateManyInput = (params) => {
|
|
2277
|
+
const { typeName, updatePrefix, updateInput, tableFilters } = params;
|
|
2278
|
+
return new GraphQLInputObjectType2({
|
|
2279
|
+
name: `${capitalize(updatePrefix)}${typeName}ManyInput`,
|
|
2280
|
+
description: `One entry of a batch update of ${typeName}: the rows \`where\` matches get this entry's \`set\` applied.`,
|
|
2281
|
+
fields: {
|
|
2282
|
+
where: {
|
|
2283
|
+
type: tableFilters,
|
|
2284
|
+
description: "Rows this entry updates. An omitted filter updates every row."
|
|
2285
|
+
},
|
|
2286
|
+
set: {
|
|
2287
|
+
type: new GraphQLNonNull2(updateInput)
|
|
2288
|
+
}
|
|
2289
|
+
}
|
|
2290
|
+
});
|
|
2291
|
+
};
|
|
2156
2292
|
var extractRequiredFilters = (table, tableName, where, fieldName, relationCtx) => {
|
|
2157
2293
|
const filters = where ? extractFilters(table, tableName, where, relationCtx) : void 0;
|
|
2158
2294
|
if (!filters) {
|
|
@@ -2171,6 +2307,10 @@ var selectArrayArgs = (orderArgs, filterArgs, distinctEnum) => ({
|
|
|
2171
2307
|
limit: { type: GraphQLInt2 },
|
|
2172
2308
|
orderBy: { type: orderArgs },
|
|
2173
2309
|
where: { type: filterArgs },
|
|
2310
|
+
after: {
|
|
2311
|
+
type: GraphQLString2,
|
|
2312
|
+
description: "Keyset pagination: only return rows strictly after this cursor (a row's `cursor` field from a previous page, under the same orderBy)."
|
|
2313
|
+
},
|
|
2174
2314
|
...distinctEnum ? { distinct: { type: new GraphQLList2(new GraphQLNonNull2(distinctEnum)) } } : {}
|
|
2175
2315
|
});
|
|
2176
2316
|
var selectSingleArgs = (orderArgs, filterArgs) => ({
|
|
@@ -2193,9 +2333,31 @@ var runRelationalSelect = async (opts) => {
|
|
|
2193
2333
|
where,
|
|
2194
2334
|
single,
|
|
2195
2335
|
filterCtx,
|
|
2196
|
-
pkNames
|
|
2336
|
+
pkNames,
|
|
2337
|
+
after
|
|
2197
2338
|
} = opts;
|
|
2198
2339
|
const distinct = opts.distinct?.length ? opts.distinct : void 0;
|
|
2340
|
+
const cursorSelected = !single && isCursorFieldSelected(parsedInfo.fieldsByTypeName[typeName], table);
|
|
2341
|
+
let cursorEntries;
|
|
2342
|
+
if (!single && (after != null || cursorSelected)) {
|
|
2343
|
+
if (after != null && distinct) {
|
|
2344
|
+
throw new GraphQLError3("'after' cannot be combined with 'distinct'.");
|
|
2345
|
+
}
|
|
2346
|
+
if (orderByHasRelationEntry(orderBy)) {
|
|
2347
|
+
if (after != null) {
|
|
2348
|
+
throw new GraphQLError3(
|
|
2349
|
+
"'after' cannot be combined with an orderBy that orders through a relation \u2014 a related row's value cannot be encoded into a cursor."
|
|
2350
|
+
);
|
|
2351
|
+
}
|
|
2352
|
+
} else if (!pkNames?.length) {
|
|
2353
|
+
if (after != null) {
|
|
2354
|
+
throw new GraphQLError3(`Table ${tableName} has no primary key, so cursor pagination cannot be used on it.`);
|
|
2355
|
+
}
|
|
2356
|
+
} else {
|
|
2357
|
+
cursorEntries = cursorOrderingEntries(orderBy, pkNames);
|
|
2358
|
+
}
|
|
2359
|
+
}
|
|
2360
|
+
const cursorValues = after != null && cursorEntries ? decodeCursor(after, cursorEntries) : void 0;
|
|
2199
2361
|
const needsDefaultOrder = single || offset != null || opts.limit != null;
|
|
2200
2362
|
let distinctKeys;
|
|
2201
2363
|
if (distinct) {
|
|
@@ -2226,9 +2388,21 @@ var runRelationalSelect = async (opts) => {
|
|
|
2226
2388
|
orderBy: distinctKeys ? (aliasedTable2) => [
|
|
2227
2389
|
...orderBy ? extractOrderBy(aliasedTable2, orderBy, relationFilterCtx(filterCtx, tableName)) : [],
|
|
2228
2390
|
...primaryKeyOrderExprs(aliasedTable2, pkNames)
|
|
2229
|
-
] :
|
|
2230
|
-
|
|
2231
|
-
|
|
2391
|
+
] : cursorEntries ? (
|
|
2392
|
+
// Cursor pagination needs a total order: the request's orderBy plus the PK tiebreak,
|
|
2393
|
+
// exactly the ordering the cursor encodes and the keyset predicate compares against.
|
|
2394
|
+
(aliasedTable2) => cursorOrderExprs(aliasedTable2, cursorEntries)
|
|
2395
|
+
) : orderBy ? (aliasedTable2) => extractOrderBy(aliasedTable2, orderBy, relationFilterCtx(filterCtx, tableName)) : needsDefaultOrder && pkNames?.length ? (aliasedTable2) => primaryKeyOrderExprs(aliasedTable2, pkNames) : void 0,
|
|
2396
|
+
where: distinctKeys ? { RAW: (aliasedTable2) => primaryKeyRestriction(aliasedTable2, pkNames, distinctKeys) } : where || cursorValues ? {
|
|
2397
|
+
RAW: (aliasedTable2) => and(
|
|
2398
|
+
where ? extractFilters(aliasedTable2, tableName, where, relationFilterCtx(filterCtx, tableName)) : void 0,
|
|
2399
|
+
cursorValues ? buildCursorCondition(
|
|
2400
|
+
aliasedTable2,
|
|
2401
|
+
cursorEntries,
|
|
2402
|
+
cursorValues,
|
|
2403
|
+
opts.nullOrdering ?? "nulls-smallest"
|
|
2404
|
+
) : void 0
|
|
2405
|
+
)
|
|
2232
2406
|
} : void 0,
|
|
2233
2407
|
with: relationMap[tableName] ? extractRelationsParams(relationMap, tables, tableName, parsedInfo, typeName, typeNameMapper, filterCtx) : void 0
|
|
2234
2408
|
};
|
|
@@ -2236,8 +2410,16 @@ var runRelationalSelect = async (opts) => {
|
|
|
2236
2410
|
const result2 = await queryBase.findFirst(params);
|
|
2237
2411
|
return result2 ? remapToGraphQLSingleOutput(result2, tableName, table, relationMap) : void 0;
|
|
2238
2412
|
}
|
|
2413
|
+
if (cursorEntries && cursorSelected) {
|
|
2414
|
+
for (const [column] of cursorEntries) {
|
|
2415
|
+
params.columns[column] = true;
|
|
2416
|
+
}
|
|
2417
|
+
}
|
|
2239
2418
|
params.limit = distinctKeys ? void 0 : opts.limit;
|
|
2240
2419
|
const result = await queryBase.findMany(params);
|
|
2420
|
+
if (cursorEntries && cursorSelected) {
|
|
2421
|
+
attachRowCursors(result, cursorEntries);
|
|
2422
|
+
}
|
|
2241
2423
|
return remapToGraphQLArrayOutput(result, tableName, table, relationMap);
|
|
2242
2424
|
};
|
|
2243
2425
|
var eagerLoadMutationRelations = async (db, tableName, rows, pkNames, withParams) => {
|
|
@@ -2816,7 +2998,9 @@ var generateSelectArray = (db, tableName, tables, relationMap, orderArgs, filter
|
|
|
2816
2998
|
single: false,
|
|
2817
2999
|
filterCtx,
|
|
2818
3000
|
pkNames,
|
|
2819
|
-
db: executor
|
|
3001
|
+
db: executor,
|
|
3002
|
+
// MySQL sorts NULLs as the smallest values (first in ASC).
|
|
3003
|
+
nullOrdering: "nulls-smallest"
|
|
2820
3004
|
});
|
|
2821
3005
|
} catch (e) {
|
|
2822
3006
|
throw toGraphQLError(e);
|
|
@@ -2985,6 +3169,48 @@ var generateUpdate = (db, tableName, table, setArgs, filterArgs, fieldName, sing
|
|
|
2985
3169
|
args: queryArgs
|
|
2986
3170
|
};
|
|
2987
3171
|
};
|
|
3172
|
+
var generateUpdateMany = (db, tableName, table, updateManyInput, fieldName, filterCtx) => {
|
|
3173
|
+
const queryArgs = {
|
|
3174
|
+
updates: {
|
|
3175
|
+
type: new GraphQLNonNull4(new GraphQLList4(new GraphQLNonNull4(updateManyInput)))
|
|
3176
|
+
}
|
|
3177
|
+
};
|
|
3178
|
+
return {
|
|
3179
|
+
name: fieldName,
|
|
3180
|
+
resolver: async (_source, args, context, _info) => {
|
|
3181
|
+
try {
|
|
3182
|
+
const { updates } = args;
|
|
3183
|
+
if (!updates.length) {
|
|
3184
|
+
throw new GraphQLError5("No updates were provided!");
|
|
3185
|
+
}
|
|
3186
|
+
const entries = updates.map(({ where, set }) => {
|
|
3187
|
+
const input = remapFromGraphQLSingleInput(set, table);
|
|
3188
|
+
if (!Object.keys(input).length) {
|
|
3189
|
+
throw new GraphQLError5("Unable to update with no values specified!");
|
|
3190
|
+
}
|
|
3191
|
+
return {
|
|
3192
|
+
set: input,
|
|
3193
|
+
filters: where ? extractFilters(table, tableName, where, relationFilterCtx(filterCtx, tableName)) : void 0
|
|
3194
|
+
};
|
|
3195
|
+
});
|
|
3196
|
+
const executor = resolveExecutor(db, context);
|
|
3197
|
+
await executor.transaction(async (tx) => {
|
|
3198
|
+
for (const entry of entries) {
|
|
3199
|
+
let query = tx.update(table).set(entry.set);
|
|
3200
|
+
if (entry.filters) {
|
|
3201
|
+
query = query.where(entry.filters);
|
|
3202
|
+
}
|
|
3203
|
+
await query;
|
|
3204
|
+
}
|
|
3205
|
+
});
|
|
3206
|
+
return { isSuccess: true };
|
|
3207
|
+
} catch (e) {
|
|
3208
|
+
throw toGraphQLError(e);
|
|
3209
|
+
}
|
|
3210
|
+
},
|
|
3211
|
+
args: queryArgs
|
|
3212
|
+
};
|
|
3213
|
+
};
|
|
2988
3214
|
var generateDelete = (db, tableName, table, filterArgs, fieldName, single, requireWhere, filterCtx) => {
|
|
2989
3215
|
const queryArgs = {
|
|
2990
3216
|
where: {
|
|
@@ -3092,6 +3318,7 @@ var generateSchemaData = (db, schema, relations, options) => {
|
|
|
3092
3318
|
upsertArrayFieldName,
|
|
3093
3319
|
upsertSingleFieldName,
|
|
3094
3320
|
updateFieldName,
|
|
3321
|
+
updateManyFieldName,
|
|
3095
3322
|
updateSingleFieldName,
|
|
3096
3323
|
deleteFieldName,
|
|
3097
3324
|
deleteSingleFieldName
|
|
@@ -3154,6 +3381,15 @@ var generateSchemaData = (db, schema, relations, options) => {
|
|
|
3154
3381
|
features.requireWhere,
|
|
3155
3382
|
filterCtx
|
|
3156
3383
|
) : void 0;
|
|
3384
|
+
const updateManyInput = features.update && features.updateMany ? generateUpdateManyInput({ typeName, updatePrefix: prefixes.update, updateInput, tableFilters }) : void 0;
|
|
3385
|
+
const updateManyGenerated = updateManyInput ? generateUpdateMany(
|
|
3386
|
+
db,
|
|
3387
|
+
tableName,
|
|
3388
|
+
schema[tableName],
|
|
3389
|
+
updateManyInput,
|
|
3390
|
+
updateManyFieldName,
|
|
3391
|
+
filterCtx
|
|
3392
|
+
) : void 0;
|
|
3157
3393
|
const deleteGenerated = features.delete ? generateDelete(
|
|
3158
3394
|
db,
|
|
3159
3395
|
tableName,
|
|
@@ -3231,6 +3467,7 @@ var generateSchemaData = (db, schema, relations, options) => {
|
|
|
3231
3467
|
upsertArrGenerated,
|
|
3232
3468
|
upsertSingleGenerated,
|
|
3233
3469
|
updateGenerated,
|
|
3470
|
+
updateManyGenerated,
|
|
3234
3471
|
updateSingleGenerated,
|
|
3235
3472
|
deleteGenerated,
|
|
3236
3473
|
deleteSingleGenerated
|
|
@@ -3248,6 +3485,7 @@ var generateSchemaData = (db, schema, relations, options) => {
|
|
|
3248
3485
|
...features.insert || onConflictInput ? [insertInput] : [],
|
|
3249
3486
|
...onConflictInput ? [onConflictInput] : [],
|
|
3250
3487
|
...features.update ? [updateInput] : [],
|
|
3488
|
+
...updateManyInput ? [updateManyInput] : [],
|
|
3251
3489
|
tableFilters,
|
|
3252
3490
|
tableOrder
|
|
3253
3491
|
];
|
|
@@ -3281,7 +3519,7 @@ var generateSchemaData = (db, schema, relations, options) => {
|
|
|
3281
3519
|
};
|
|
3282
3520
|
|
|
3283
3521
|
// src/util/builders/pg.ts
|
|
3284
|
-
import { is as is5, One as One4 } from "drizzle-orm";
|
|
3522
|
+
import { and as and3, getColumns as getColumns3, is as is5, One as One4 } from "drizzle-orm";
|
|
3285
3523
|
import { getTableConfig as getTableConfig2, PgTable } from "drizzle-orm/pg-core";
|
|
3286
3524
|
import {
|
|
3287
3525
|
GraphQLError as GraphQLError6,
|
|
@@ -3318,16 +3556,48 @@ var generateSelectArray2 = (db, tableName, tables, relationMap, orderArgs, filte
|
|
|
3318
3556
|
single: false,
|
|
3319
3557
|
filterCtx,
|
|
3320
3558
|
pkNames,
|
|
3321
|
-
db: executor
|
|
3559
|
+
db: executor,
|
|
3560
|
+
// PostgreSQL sorts NULLs as the largest values (last in ASC).
|
|
3561
|
+
nullOrdering: "nulls-largest"
|
|
3322
3562
|
});
|
|
3323
3563
|
}
|
|
3324
|
-
const { offset, limit, orderBy, where, distinct } = args;
|
|
3564
|
+
const { offset, limit, orderBy, where, distinct, after } = args;
|
|
3325
3565
|
const selectedColumnsSql = extractSelectedColumnsFromTreeSQLFormat(
|
|
3326
3566
|
parsedInfo.fieldsByTypeName[typeName],
|
|
3327
3567
|
table,
|
|
3328
3568
|
{ tableName, relationMap, tables }
|
|
3329
3569
|
);
|
|
3330
|
-
const
|
|
3570
|
+
const cursorSelected = isCursorFieldSelected(parsedInfo.fieldsByTypeName[typeName], table);
|
|
3571
|
+
let cursorEntries;
|
|
3572
|
+
if (after != null || cursorSelected) {
|
|
3573
|
+
if (after != null && distinct?.length) {
|
|
3574
|
+
throw new GraphQLError6("'after' cannot be combined with 'distinct'.");
|
|
3575
|
+
}
|
|
3576
|
+
if (orderByHasRelationEntry(orderBy)) {
|
|
3577
|
+
if (after != null) {
|
|
3578
|
+
throw new GraphQLError6(
|
|
3579
|
+
"'after' cannot be combined with an orderBy that orders through a relation \u2014 a related row's value cannot be encoded into a cursor."
|
|
3580
|
+
);
|
|
3581
|
+
}
|
|
3582
|
+
} else if (!pkNames.length) {
|
|
3583
|
+
if (after != null) {
|
|
3584
|
+
throw new GraphQLError6(
|
|
3585
|
+
`Table ${tableName} has no primary key, so cursor pagination cannot be used on it.`
|
|
3586
|
+
);
|
|
3587
|
+
}
|
|
3588
|
+
} else {
|
|
3589
|
+
cursorEntries = cursorOrderingEntries(orderBy, pkNames);
|
|
3590
|
+
if (cursorSelected) {
|
|
3591
|
+
const allColumns = getColumns3(table);
|
|
3592
|
+
for (const [column] of cursorEntries) {
|
|
3593
|
+
selectedColumnsSql[column] ??= allColumns[column];
|
|
3594
|
+
}
|
|
3595
|
+
}
|
|
3596
|
+
}
|
|
3597
|
+
}
|
|
3598
|
+
const cursorCondition = after != null && cursorEntries ? buildCursorCondition(table, cursorEntries, decodeCursor(after, cursorEntries), "nulls-largest") : void 0;
|
|
3599
|
+
const baseWhereSql = where ? extractFilters(table, tableName, where, relationFilterCtx(filterCtx, tableName)) : void 0;
|
|
3600
|
+
const whereSql = cursorCondition ? and3(baseWhereSql, cursorCondition) : baseWhereSql;
|
|
3331
3601
|
let distinctKeys;
|
|
3332
3602
|
if (distinct?.length) {
|
|
3333
3603
|
distinctKeys = await selectDistinctKeys({
|
|
@@ -3351,7 +3621,9 @@ var generateSelectArray2 = (db, tableName, tables, relationMap, orderArgs, filte
|
|
|
3351
3621
|
} else if (whereSql) {
|
|
3352
3622
|
q = q.where(whereSql);
|
|
3353
3623
|
}
|
|
3354
|
-
if (
|
|
3624
|
+
if (cursorEntries && !distinctKeys) {
|
|
3625
|
+
q = q.orderBy(...cursorOrderExprs(table, cursorEntries));
|
|
3626
|
+
} else if (orderBy) {
|
|
3355
3627
|
q = q.orderBy(
|
|
3356
3628
|
...extractOrderBy(table, orderBy, relationFilterCtx(filterCtx, tableName)),
|
|
3357
3629
|
...distinctKeys ? primaryKeyOrderExprs(table, pkNames) : []
|
|
@@ -3367,7 +3639,11 @@ var generateSelectArray2 = (db, tableName, tables, relationMap, orderArgs, filte
|
|
|
3367
3639
|
q = q.limit(limit);
|
|
3368
3640
|
}
|
|
3369
3641
|
}
|
|
3370
|
-
|
|
3642
|
+
const rows = await q;
|
|
3643
|
+
if (cursorEntries && cursorSelected) {
|
|
3644
|
+
attachRowCursors(rows, cursorEntries);
|
|
3645
|
+
}
|
|
3646
|
+
return remapToGraphQLArrayOutput(rows, tableName, table, relationMap);
|
|
3371
3647
|
} catch (e) {
|
|
3372
3648
|
throw toGraphQLError(e);
|
|
3373
3649
|
}
|
|
@@ -3633,6 +3909,78 @@ var generateUpdate2 = (db, tableName, table, tables, relationMap, setArgs, filte
|
|
|
3633
3909
|
args: queryArgs
|
|
3634
3910
|
};
|
|
3635
3911
|
};
|
|
3912
|
+
var generateUpdateMany2 = (db, tableName, table, tables, relationMap, updateManyInput, fieldName, typeName, typeNameMapper, filterCtx) => {
|
|
3913
|
+
const queryArgs = {
|
|
3914
|
+
updates: {
|
|
3915
|
+
type: new GraphQLNonNull5(new GraphQLList5(new GraphQLNonNull5(updateManyInput)))
|
|
3916
|
+
}
|
|
3917
|
+
};
|
|
3918
|
+
const pkNames = pgPrimaryKeyPropNames(table);
|
|
3919
|
+
return {
|
|
3920
|
+
name: fieldName,
|
|
3921
|
+
resolver: async (_source, args, context, info) => {
|
|
3922
|
+
try {
|
|
3923
|
+
const { updates } = args;
|
|
3924
|
+
if (!updates.length) {
|
|
3925
|
+
throw new GraphQLError6("No updates were provided!");
|
|
3926
|
+
}
|
|
3927
|
+
const parsedInfo = parseResolveInfo3(info, {
|
|
3928
|
+
deep: true
|
|
3929
|
+
});
|
|
3930
|
+
const { columns, hasRelations, withParams } = prepareMutationRelationColumns({
|
|
3931
|
+
relationMap,
|
|
3932
|
+
tables,
|
|
3933
|
+
tableName,
|
|
3934
|
+
typeName,
|
|
3935
|
+
typeNameMapper,
|
|
3936
|
+
table,
|
|
3937
|
+
pkNames,
|
|
3938
|
+
parsedInfo
|
|
3939
|
+
});
|
|
3940
|
+
const entries = updates.map(({ where, set }) => {
|
|
3941
|
+
const input = remapFromGraphQLSingleInput(set, table);
|
|
3942
|
+
if (!Object.keys(input).length) {
|
|
3943
|
+
throw new GraphQLError6("Unable to update with no values specified!");
|
|
3944
|
+
}
|
|
3945
|
+
return {
|
|
3946
|
+
set: input,
|
|
3947
|
+
filters: where ? extractFilters(table, tableName, where, relationFilterCtx(filterCtx, tableName)) : void 0
|
|
3948
|
+
};
|
|
3949
|
+
});
|
|
3950
|
+
const executor = resolveExecutor(db, context);
|
|
3951
|
+
const perEntry = await executor.transaction(async (tx) => {
|
|
3952
|
+
const results = [];
|
|
3953
|
+
for (const entry of entries) {
|
|
3954
|
+
let query = tx.update(table).set(entry.set);
|
|
3955
|
+
if (entry.filters) {
|
|
3956
|
+
query = query.where(entry.filters);
|
|
3957
|
+
}
|
|
3958
|
+
results.push(await query.returning(columns));
|
|
3959
|
+
}
|
|
3960
|
+
return results;
|
|
3961
|
+
});
|
|
3962
|
+
const flatRows = perEntry.flat();
|
|
3963
|
+
const enriched = hasRelations ? await eagerLoadMutationRelations(executor, tableName, flatRows, pkNames, withParams) : flatRows;
|
|
3964
|
+
const output = [];
|
|
3965
|
+
let offset = 0;
|
|
3966
|
+
for (const rows of perEntry) {
|
|
3967
|
+
if (!rows.length) {
|
|
3968
|
+
output.push(null);
|
|
3969
|
+
continue;
|
|
3970
|
+
}
|
|
3971
|
+
for (let i = 0; i < rows.length; i++) {
|
|
3972
|
+
output.push(remapToGraphQLSingleOutput(enriched[offset + i], tableName, table, relationMap));
|
|
3973
|
+
}
|
|
3974
|
+
offset += rows.length;
|
|
3975
|
+
}
|
|
3976
|
+
return output;
|
|
3977
|
+
} catch (e) {
|
|
3978
|
+
throw toGraphQLError(e);
|
|
3979
|
+
}
|
|
3980
|
+
},
|
|
3981
|
+
args: queryArgs
|
|
3982
|
+
};
|
|
3983
|
+
};
|
|
3636
3984
|
var generateDelete2 = (db, tableName, table, filterArgs, fieldName, typeName, single, requireWhere, filterCtx, selectionCtx) => {
|
|
3637
3985
|
const queryArgs = {
|
|
3638
3986
|
where: {
|
|
@@ -3751,6 +4099,7 @@ function generateSchemaData2(db, schema, relations, options) {
|
|
|
3751
4099
|
upsertArrayFieldName,
|
|
3752
4100
|
upsertSingleFieldName,
|
|
3753
4101
|
updateFieldName,
|
|
4102
|
+
updateManyFieldName,
|
|
3754
4103
|
updateSingleFieldName,
|
|
3755
4104
|
deleteFieldName,
|
|
3756
4105
|
deleteSingleFieldName
|
|
@@ -3872,6 +4221,19 @@ function generateSchemaData2(db, schema, relations, options) {
|
|
|
3872
4221
|
typeNameMapper,
|
|
3873
4222
|
filterCtx
|
|
3874
4223
|
) : void 0;
|
|
4224
|
+
const updateManyInput = features.update && features.updateMany ? generateUpdateManyInput({ typeName, updatePrefix: prefixes.update, updateInput, tableFilters }) : void 0;
|
|
4225
|
+
const updateManyGenerated = updateManyInput ? generateUpdateMany2(
|
|
4226
|
+
db,
|
|
4227
|
+
tableName,
|
|
4228
|
+
schema[tableName],
|
|
4229
|
+
tables,
|
|
4230
|
+
eagerRelations,
|
|
4231
|
+
updateManyInput,
|
|
4232
|
+
updateManyFieldName,
|
|
4233
|
+
typeName,
|
|
4234
|
+
typeNameMapper,
|
|
4235
|
+
filterCtx
|
|
4236
|
+
) : void 0;
|
|
3875
4237
|
const deleteGenerated = features.delete ? generateDelete2(
|
|
3876
4238
|
db,
|
|
3877
4239
|
tableName,
|
|
@@ -3982,6 +4344,14 @@ function generateSchemaData2(db, schema, relations, options) {
|
|
|
3982
4344
|
resolve: updateGenerated.resolver
|
|
3983
4345
|
};
|
|
3984
4346
|
}
|
|
4347
|
+
if (updateManyGenerated) {
|
|
4348
|
+
mutations[updateManyGenerated.name] = {
|
|
4349
|
+
// Nullable items: a no-match entry yields `null` in its slot.
|
|
4350
|
+
type: new GraphQLNonNull5(new GraphQLList5(singleTableItemOutput)),
|
|
4351
|
+
args: updateManyGenerated.args,
|
|
4352
|
+
resolve: updateManyGenerated.resolver
|
|
4353
|
+
};
|
|
4354
|
+
}
|
|
3985
4355
|
if (updateSingleGenerated) {
|
|
3986
4356
|
mutations[updateSingleGenerated.name] = {
|
|
3987
4357
|
type: singleTableItemOutput,
|
|
@@ -4008,6 +4378,7 @@ function generateSchemaData2(db, schema, relations, options) {
|
|
|
4008
4378
|
...features.insert || onConflictInput ? [insertInput] : [],
|
|
4009
4379
|
...onConflictInput ? [onConflictInput] : [],
|
|
4010
4380
|
...features.update ? [updateInput] : [],
|
|
4381
|
+
...updateManyInput ? [updateManyInput] : [],
|
|
4011
4382
|
tableFilters,
|
|
4012
4383
|
tableOrder
|
|
4013
4384
|
];
|
|
@@ -4083,7 +4454,9 @@ var generateSelectArray3 = (db, tableName, tables, relationMap, orderArgs, filte
|
|
|
4083
4454
|
single: false,
|
|
4084
4455
|
filterCtx,
|
|
4085
4456
|
pkNames,
|
|
4086
|
-
db: executor
|
|
4457
|
+
db: executor,
|
|
4458
|
+
// SQLite sorts NULLs as the smallest values (first in ASC).
|
|
4459
|
+
nullOrdering: "nulls-smallest"
|
|
4087
4460
|
});
|
|
4088
4461
|
} catch (e) {
|
|
4089
4462
|
throw toGraphQLError(e);
|
|
@@ -4332,6 +4705,91 @@ var generateUpdate3 = (db, tableName, table, tables, relationMap, setArgs, filte
|
|
|
4332
4705
|
args: queryArgs
|
|
4333
4706
|
};
|
|
4334
4707
|
};
|
|
4708
|
+
var generateUpdateMany3 = (db, tableName, table, tables, relationMap, updateManyInput, fieldName, typeName, typeNameMapper, filterCtx) => {
|
|
4709
|
+
const queryArgs = {
|
|
4710
|
+
updates: {
|
|
4711
|
+
type: new GraphQLNonNull6(new GraphQLList6(new GraphQLNonNull6(updateManyInput)))
|
|
4712
|
+
}
|
|
4713
|
+
};
|
|
4714
|
+
const pkNames = sqlitePrimaryKeyPropNames(table);
|
|
4715
|
+
return {
|
|
4716
|
+
name: fieldName,
|
|
4717
|
+
resolver: async (_source, args, context, info) => {
|
|
4718
|
+
try {
|
|
4719
|
+
const { updates } = args;
|
|
4720
|
+
if (!updates.length) {
|
|
4721
|
+
throw new GraphQLError7("No updates were provided!");
|
|
4722
|
+
}
|
|
4723
|
+
const parsedInfo = parseResolveInfo4(info, {
|
|
4724
|
+
deep: true
|
|
4725
|
+
});
|
|
4726
|
+
const { columns, hasRelations, withParams } = prepareMutationRelationColumns({
|
|
4727
|
+
relationMap,
|
|
4728
|
+
tables,
|
|
4729
|
+
tableName,
|
|
4730
|
+
typeName,
|
|
4731
|
+
typeNameMapper,
|
|
4732
|
+
table,
|
|
4733
|
+
pkNames,
|
|
4734
|
+
parsedInfo
|
|
4735
|
+
});
|
|
4736
|
+
const entries = updates.map(({ where, set }) => {
|
|
4737
|
+
const input = remapFromGraphQLSingleInput(set, table);
|
|
4738
|
+
if (!Object.keys(input).length) {
|
|
4739
|
+
throw new GraphQLError7("Unable to update with no values specified!");
|
|
4740
|
+
}
|
|
4741
|
+
return {
|
|
4742
|
+
set: input,
|
|
4743
|
+
filters: where ? extractFilters(table, tableName, where, relationFilterCtx(filterCtx, tableName)) : void 0
|
|
4744
|
+
};
|
|
4745
|
+
});
|
|
4746
|
+
const runEntry = (tx, entry) => {
|
|
4747
|
+
let query = tx.update(table).set(entry.set);
|
|
4748
|
+
if (entry.filters) {
|
|
4749
|
+
query = query.where(entry.filters);
|
|
4750
|
+
}
|
|
4751
|
+
return query.returning(columns).all();
|
|
4752
|
+
};
|
|
4753
|
+
const executor = resolveExecutor(db, context);
|
|
4754
|
+
const perEntry = await executor.transaction((tx) => {
|
|
4755
|
+
const first = runEntry(tx, entries[0]);
|
|
4756
|
+
if (typeof first?.then === "function") {
|
|
4757
|
+
return (async () => {
|
|
4758
|
+
const results2 = [await first];
|
|
4759
|
+
for (let i = 1; i < entries.length; i++) {
|
|
4760
|
+
results2.push(await runEntry(tx, entries[i]));
|
|
4761
|
+
}
|
|
4762
|
+
return results2;
|
|
4763
|
+
})();
|
|
4764
|
+
}
|
|
4765
|
+
const results = [first];
|
|
4766
|
+
for (let i = 1; i < entries.length; i++) {
|
|
4767
|
+
results.push(runEntry(tx, entries[i]));
|
|
4768
|
+
}
|
|
4769
|
+
return results;
|
|
4770
|
+
});
|
|
4771
|
+
const flatRows = perEntry.flat();
|
|
4772
|
+
const enriched = hasRelations ? await eagerLoadMutationRelations(executor, tableName, flatRows, pkNames, withParams) : flatRows;
|
|
4773
|
+
const output = [];
|
|
4774
|
+
let offset = 0;
|
|
4775
|
+
for (const rows of perEntry) {
|
|
4776
|
+
if (!rows.length) {
|
|
4777
|
+
output.push(null);
|
|
4778
|
+
continue;
|
|
4779
|
+
}
|
|
4780
|
+
for (let i = 0; i < rows.length; i++) {
|
|
4781
|
+
output.push(remapToGraphQLSingleOutput(enriched[offset + i], tableName, table, relationMap));
|
|
4782
|
+
}
|
|
4783
|
+
offset += rows.length;
|
|
4784
|
+
}
|
|
4785
|
+
return output;
|
|
4786
|
+
} catch (e) {
|
|
4787
|
+
throw toGraphQLError(e);
|
|
4788
|
+
}
|
|
4789
|
+
},
|
|
4790
|
+
args: queryArgs
|
|
4791
|
+
};
|
|
4792
|
+
};
|
|
4335
4793
|
var generateDelete3 = (db, tableName, table, filterArgs, fieldName, typeName, single, requireWhere, filterCtx, selectionCtx) => {
|
|
4336
4794
|
const queryArgs = {
|
|
4337
4795
|
where: {
|
|
@@ -4451,6 +4909,7 @@ var generateSchemaData3 = (db, schema, relations, options) => {
|
|
|
4451
4909
|
upsertArrayFieldName,
|
|
4452
4910
|
upsertSingleFieldName,
|
|
4453
4911
|
updateFieldName,
|
|
4912
|
+
updateManyFieldName,
|
|
4454
4913
|
updateSingleFieldName,
|
|
4455
4914
|
deleteFieldName,
|
|
4456
4915
|
deleteSingleFieldName
|
|
@@ -4572,6 +5031,19 @@ var generateSchemaData3 = (db, schema, relations, options) => {
|
|
|
4572
5031
|
typeNameMapper,
|
|
4573
5032
|
filterCtx
|
|
4574
5033
|
) : void 0;
|
|
5034
|
+
const updateManyInput = features.update && features.updateMany ? generateUpdateManyInput({ typeName, updatePrefix: prefixes.update, updateInput, tableFilters }) : void 0;
|
|
5035
|
+
const updateManyGenerated = updateManyInput ? generateUpdateMany3(
|
|
5036
|
+
db,
|
|
5037
|
+
tableName,
|
|
5038
|
+
schema[tableName],
|
|
5039
|
+
tables,
|
|
5040
|
+
eagerRelations,
|
|
5041
|
+
updateManyInput,
|
|
5042
|
+
updateManyFieldName,
|
|
5043
|
+
typeName,
|
|
5044
|
+
typeNameMapper,
|
|
5045
|
+
filterCtx
|
|
5046
|
+
) : void 0;
|
|
4575
5047
|
const deleteGenerated = features.delete ? generateDelete3(
|
|
4576
5048
|
db,
|
|
4577
5049
|
tableName,
|
|
@@ -4682,6 +5154,14 @@ var generateSchemaData3 = (db, schema, relations, options) => {
|
|
|
4682
5154
|
resolve: updateGenerated.resolver
|
|
4683
5155
|
};
|
|
4684
5156
|
}
|
|
5157
|
+
if (updateManyGenerated) {
|
|
5158
|
+
mutations[updateManyGenerated.name] = {
|
|
5159
|
+
// Nullable items: a no-match entry yields `null` in its slot.
|
|
5160
|
+
type: new GraphQLNonNull6(new GraphQLList6(singleTableItemOutput)),
|
|
5161
|
+
args: updateManyGenerated.args,
|
|
5162
|
+
resolve: updateManyGenerated.resolver
|
|
5163
|
+
};
|
|
5164
|
+
}
|
|
4685
5165
|
if (updateSingleGenerated) {
|
|
4686
5166
|
mutations[updateSingleGenerated.name] = {
|
|
4687
5167
|
type: singleTableItemOutput,
|
|
@@ -4708,6 +5188,7 @@ var generateSchemaData3 = (db, schema, relations, options) => {
|
|
|
4708
5188
|
...features.insert || onConflictInput ? [insertInput] : [],
|
|
4709
5189
|
...onConflictInput ? [onConflictInput] : [],
|
|
4710
5190
|
...features.update ? [updateInput] : [],
|
|
5191
|
+
...updateManyInput ? [updateManyInput] : [],
|
|
4711
5192
|
tableFilters,
|
|
4712
5193
|
tableOrder
|
|
4713
5194
|
];
|
|
@@ -4770,6 +5251,7 @@ var buildSchema = (db, config) => {
|
|
|
4770
5251
|
distinct: config?.features?.distinct ?? true,
|
|
4771
5252
|
insert: config?.features?.insert ?? true,
|
|
4772
5253
|
update: config?.features?.update ?? true,
|
|
5254
|
+
updateMany: config?.features?.updateMany ?? true,
|
|
4773
5255
|
delete: config?.features?.delete ?? true,
|
|
4774
5256
|
upsert: config?.features?.upsert ?? false,
|
|
4775
5257
|
requireWhere: config?.features?.requireWhere ?? false
|