@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/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
- ] : orderBy ? (aliasedTable2) => extractOrderBy(aliasedTable2, orderBy, relationFilterCtx(filterCtx, tableName)) : needsDefaultOrder && pkNames?.length ? (aliasedTable2) => primaryKeyOrderExprs(aliasedTable2, pkNames) : void 0,
2230
- where: distinctKeys ? { RAW: (aliasedTable2) => primaryKeyRestriction(aliasedTable2, pkNames, distinctKeys) } : where ? {
2231
- RAW: (aliasedTable2) => extractFilters(aliasedTable2, tableName, where, relationFilterCtx(filterCtx, tableName))
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 whereSql = where ? extractFilters(table, tableName, where, relationFilterCtx(filterCtx, tableName)) : void 0;
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 (orderBy) {
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
- return remapToGraphQLArrayOutput(await q, tableName, table, relationMap);
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