@vantreeseba/drizzle-graphql 11.0.2 → 12.0.1
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 +45 -1
- package/dist/index.cjs +942 -792
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +942 -792
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -544,11 +544,12 @@ const isJsonColumn = (column) => (column.dataType ?? "").includes("json") && col
|
|
|
544
544
|
* StringArray, …) so arrays with different element types never share
|
|
545
545
|
* one filter input; membership operators instead of scalar comparisons
|
|
546
546
|
* - "Id" → uuid-typed columns (no string pattern operators)
|
|
547
|
-
* - "Boolean" → boolean columns
|
|
547
|
+
* - "Boolean" → boolean columns (equality and membership only — no ordering, no patterns)
|
|
548
548
|
* - "BigInt" → bigint columns (BigInt-scalar-typed operators, no string pattern operators)
|
|
549
549
|
* - "Decimal" → numeric/decimal columns (Decimal-scalar-typed operators, no string pattern operators)
|
|
550
|
-
* - the enum GraphQL type name → enum columns (still unique per enum
|
|
551
|
-
*
|
|
550
|
+
* - the enum GraphQL type name → enum columns (still unique per enum; equality and
|
|
551
|
+
* membership only, like Boolean)
|
|
552
|
+
* - "DateTime" → timestamp and date columns (ordering kept, no string pattern operators)
|
|
552
553
|
* - "Int" → integer/serial columns (no string pattern operators)
|
|
553
554
|
* - "Float" → real/double columns (no string pattern operators)
|
|
554
555
|
* - "String" → all other text/varchar columns
|
|
@@ -576,7 +577,8 @@ const resolveGenericFilterDescriptor = (column, columnGraphQLType) => {
|
|
|
576
577
|
};
|
|
577
578
|
if (columnGraphQLType.type instanceof graphql.GraphQLEnumType) return {
|
|
578
579
|
name: columnGraphQLType.type.name,
|
|
579
|
-
kind: "scalar"
|
|
580
|
+
kind: "scalar",
|
|
581
|
+
isEnum: true
|
|
580
582
|
};
|
|
581
583
|
if (columnGraphQLType.type === GraphQLBigIntString) return {
|
|
582
584
|
name: "BigInt",
|
|
@@ -800,18 +802,32 @@ const arrayFilterFields = (colType, colArr) => {
|
|
|
800
802
|
};
|
|
801
803
|
/**
|
|
802
804
|
* Filters whose fields omit the string pattern operators (like/notLike/ilike/notIlike/
|
|
803
|
-
* startsWith/contains/…
|
|
804
|
-
*
|
|
805
|
-
*
|
|
805
|
+
* startsWith/contains/… and the `insensitive` flag that modifies them): they are nonsensical
|
|
806
|
+
* on opaque uuids, booleans and enum members, invalid SQL on numeric columns, and on a
|
|
807
|
+
* timestamp they match whatever the session happens to render the value as rather than
|
|
808
|
+
* anything in the query. Keyed on the generic filter name (i.e. on column type) — a
|
|
809
|
+
* string-typed column keeps the string operators whatever it is called. Enum filters are
|
|
810
|
+
* named after their enum and are recognised by `isEnum` instead.
|
|
806
811
|
*/
|
|
807
812
|
const FILTERS_WITHOUT_STRING_OPS = /* @__PURE__ */ new Set([
|
|
808
813
|
"Id",
|
|
809
814
|
"Int",
|
|
810
815
|
"Float",
|
|
811
816
|
"BigInt",
|
|
812
|
-
"Decimal"
|
|
817
|
+
"Decimal",
|
|
818
|
+
"Boolean",
|
|
819
|
+
"Date",
|
|
820
|
+
"DateTime"
|
|
813
821
|
]);
|
|
814
822
|
/**
|
|
823
|
+
* Filters that additionally omit the ordering operators (lt/lte/gt/gte). Ordering two
|
|
824
|
+
* booleans is `eq` spelled as a puzzle (`gt: false` is `eq: true`), and ordering enum members
|
|
825
|
+
* compares their declaration order — an artefact of how the column was written rather than a
|
|
826
|
+
* fact about the domain. Timestamps and the numerics keep them: ranges are the point of
|
|
827
|
+
* having them. Enum filters are recognised by `isEnum`, as above.
|
|
828
|
+
*/
|
|
829
|
+
const FILTERS_WITHOUT_ORDERING_OPS = /* @__PURE__ */ new Set(["Boolean"]);
|
|
830
|
+
/**
|
|
815
831
|
* Scalars the built-in detection itself can produce. A scalar override to one of these
|
|
816
832
|
* shares the built-in filter type for that scalar, so its shape must stay exactly what a
|
|
817
833
|
* natural column of the scalar gets — whichever column builds the cached type first.
|
|
@@ -854,14 +870,15 @@ const resolveOverrideFilterDescriptor = (scalar) => {
|
|
|
854
870
|
const generateColumnFilterValues = (column, tableName, columnName, cacheCtx) => {
|
|
855
871
|
const columnGraphQLType = drizzleColumnToGraphQLType(column, columnName, tableName, true, false, true);
|
|
856
872
|
const inputOverride = getColumnScalarOverride(column, true);
|
|
857
|
-
const { name: genericName, kind } = inputOverride ? resolveOverrideFilterDescriptor(inputOverride) : resolveGenericFilterDescriptor(column, columnGraphQLType);
|
|
873
|
+
const { name: genericName, kind, isEnum } = inputOverride ? resolveOverrideFilterDescriptor(inputOverride) : resolveGenericFilterDescriptor(column, columnGraphQLType);
|
|
858
874
|
const cached = cacheCtx.genericFilterCache.get(genericName);
|
|
859
875
|
if (cached) return cached;
|
|
860
876
|
const colType = columnGraphQLType.type;
|
|
861
877
|
const colArr = new graphql.GraphQLList(new graphql.GraphQLNonNull(colType));
|
|
862
878
|
const customOverrideFilter = inputOverride !== void 0 && !BUILTIN_FILTER_SCALARS.has(inputOverride);
|
|
863
879
|
const underlying = (0, drizzle_orm.extractExtendedColumnType)(column);
|
|
864
|
-
const omitStringOps = customOverrideFilter ? underlying.type !== "string" || underlying.constraint === "numeric" : FILTERS_WITHOUT_STRING_OPS.has(genericName);
|
|
880
|
+
const omitStringOps = customOverrideFilter ? underlying.type !== "string" || underlying.constraint === "numeric" : isEnum || FILTERS_WITHOUT_STRING_OPS.has(genericName);
|
|
881
|
+
const omitOrderingOps = !customOverrideFilter && (isEnum || FILTERS_WITHOUT_ORDERING_OPS.has(genericName));
|
|
865
882
|
const baseFields = kind === "json" ? jsonFilterFields(column, colType, cacheCtx.typeName) : kind === "array" ? arrayFilterFields(colType, colArr) : {
|
|
866
883
|
eq: {
|
|
867
884
|
type: colType,
|
|
@@ -871,21 +888,23 @@ const generateColumnFilterValues = (column, tableName, columnName, cacheCtx) =>
|
|
|
871
888
|
type: colType,
|
|
872
889
|
description: "Not equal to"
|
|
873
890
|
},
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
891
|
+
...omitOrderingOps ? {} : {
|
|
892
|
+
lt: {
|
|
893
|
+
type: colType,
|
|
894
|
+
description: "Less than"
|
|
895
|
+
},
|
|
896
|
+
lte: {
|
|
897
|
+
type: colType,
|
|
898
|
+
description: "Less than or equal to"
|
|
899
|
+
},
|
|
900
|
+
gt: {
|
|
901
|
+
type: colType,
|
|
902
|
+
description: "Greater than"
|
|
903
|
+
},
|
|
904
|
+
gte: {
|
|
905
|
+
type: colType,
|
|
906
|
+
description: "Greater than or equal to"
|
|
907
|
+
}
|
|
889
908
|
},
|
|
890
909
|
...omitStringOps ? {} : {
|
|
891
910
|
like: { type: graphql.GraphQLString },
|
|
@@ -2907,6 +2926,154 @@ const listFieldComplexity = (options, policy) => ({ args, childComplexity }) =>
|
|
|
2907
2926
|
*/
|
|
2908
2927
|
const aggregateFieldComplexity = (options) => ({ childComplexity }) => options.aggregateCost + childComplexity;
|
|
2909
2928
|
//#endregion
|
|
2929
|
+
//#region src/util/parse-resolve-info.ts
|
|
2930
|
+
/** Unwraps `[T]!`-style wrappers down to the named type. `getNamedType` without the realm tie. */
|
|
2931
|
+
const namedTypeOf = (type) => {
|
|
2932
|
+
let current = type;
|
|
2933
|
+
while (current?.ofType) current = current.ofType;
|
|
2934
|
+
return current;
|
|
2935
|
+
};
|
|
2936
|
+
/**
|
|
2937
|
+
* Whether a field's named output type has sub-selections. Object types and interfaces expose
|
|
2938
|
+
* `getFields`, unions expose `getTypes`; scalars and enums expose neither. Input object types
|
|
2939
|
+
* also expose `getFields`, but an input type can never be a field's output type, so there is
|
|
2940
|
+
* nothing here for that to collide with.
|
|
2941
|
+
*/
|
|
2942
|
+
const isCompositeLike = (type) => !!type && (typeof type.getFields === "function" || typeof type.getTypes === "function");
|
|
2943
|
+
/**
|
|
2944
|
+
* The fields declared on a type, or `undefined` when it has none to declare. A union is the
|
|
2945
|
+
* case that matters: it has no fields of its own, so a selection directly against one resolves
|
|
2946
|
+
* to nothing and is dropped — only the inline fragments underneath contribute.
|
|
2947
|
+
*/
|
|
2948
|
+
const fieldsOf = (type) => typeof type?.getFields === "function" ? type.getFields() : void 0;
|
|
2949
|
+
/**
|
|
2950
|
+
* The runtime variable values, in the plain-map form the directive check wants. graphql 17
|
|
2951
|
+
* wraps them in `{ sources, coerced }`; graphql 16 hands over the map itself.
|
|
2952
|
+
*/
|
|
2953
|
+
const coercedVariables = (info) => {
|
|
2954
|
+
const values = info.variableValues;
|
|
2955
|
+
if (values && typeof values === "object" && "coerced" in values) return values.coerced ?? {};
|
|
2956
|
+
return values ?? {};
|
|
2957
|
+
};
|
|
2958
|
+
/** The value of a `@skip` / `@include` `if:` argument — a literal or a variable reference. */
|
|
2959
|
+
const directiveIfValue = (info, value) => {
|
|
2960
|
+
if (value.kind === graphql.Kind.VARIABLE) return coercedVariables(info)[value.name.value];
|
|
2961
|
+
if (value.kind === graphql.Kind.BOOLEAN) return value.value;
|
|
2962
|
+
};
|
|
2963
|
+
/**
|
|
2964
|
+
* Whether `@skip` / `@include` on this selection exclude it. Applies to fields, fragment
|
|
2965
|
+
* spreads and inline fragments alike, which is where graphql puts them.
|
|
2966
|
+
*/
|
|
2967
|
+
const isExcluded = (info, node) => {
|
|
2968
|
+
for (const directive of node.directives ?? []) {
|
|
2969
|
+
const directiveName = directive.name.value;
|
|
2970
|
+
if (directiveName !== "skip" && directiveName !== "include") continue;
|
|
2971
|
+
const ifArgument = directive.arguments?.find((argument) => argument.name.value === "if");
|
|
2972
|
+
if (!ifArgument) continue;
|
|
2973
|
+
const value = directiveIfValue(info, ifArgument.value);
|
|
2974
|
+
if (directiveName === "skip" ? !!value : !value) return true;
|
|
2975
|
+
}
|
|
2976
|
+
return false;
|
|
2977
|
+
};
|
|
2978
|
+
/** The type a fragment's `on Type` condition names, looked up in the executing schema. */
|
|
2979
|
+
const typeFromCondition = (info, condition) => info.schema.getType(condition.name.value);
|
|
2980
|
+
/**
|
|
2981
|
+
* Introspection meta-fields are not schema fields and never reach the database. `__typename` is
|
|
2982
|
+
* the one that turns up in practice; `__id` is excluded from the rule because a schema is free
|
|
2983
|
+
* to declare a field by that name.
|
|
2984
|
+
*/
|
|
2985
|
+
const isMetaField = (name) => name.startsWith("__") && name !== "__id";
|
|
2986
|
+
/**
|
|
2987
|
+
* Merges one selection set into `tree`, which is a `fieldsByTypeName` map — the caller owns the
|
|
2988
|
+
* level, this owns the entries under `parentType`'s name.
|
|
2989
|
+
*
|
|
2990
|
+
* `activeFragments` is the set of fragment names on the current spread path. A document that
|
|
2991
|
+
* passed validation cannot contain a fragment cycle, but this parser also runs on trees a
|
|
2992
|
+
* caller assembled by hand, and an unbounded walk would hang rather than fail.
|
|
2993
|
+
*/
|
|
2994
|
+
const collectSelections = (selections, info, tree, parentType, deep, activeFragments) => {
|
|
2995
|
+
const parentTypeName = parentType?.name;
|
|
2996
|
+
if (!parentTypeName) return tree;
|
|
2997
|
+
let fields = tree[parentTypeName];
|
|
2998
|
+
if (!fields) {
|
|
2999
|
+
fields = {};
|
|
3000
|
+
tree[parentTypeName] = fields;
|
|
3001
|
+
}
|
|
3002
|
+
const parentFields = fieldsOf(parentType);
|
|
3003
|
+
for (const selection of selections) {
|
|
3004
|
+
if (isExcluded(info, selection)) continue;
|
|
3005
|
+
if (selection.kind === graphql.Kind.FIELD) {
|
|
3006
|
+
const name = selection.name.value;
|
|
3007
|
+
if (isMetaField(name)) continue;
|
|
3008
|
+
const fieldDef = parentFields?.[name];
|
|
3009
|
+
if (!fieldDef) continue;
|
|
3010
|
+
const fieldType = namedTypeOf(fieldDef.type);
|
|
3011
|
+
if (!fieldType) continue;
|
|
3012
|
+
const composite = isCompositeLike(fieldType);
|
|
3013
|
+
const alias = selection.alias?.value || name;
|
|
3014
|
+
let node = fields[alias];
|
|
3015
|
+
if (!node) {
|
|
3016
|
+
node = {
|
|
3017
|
+
name,
|
|
3018
|
+
alias,
|
|
3019
|
+
args: { ...(0, graphql.getArgumentValues)(fieldDef, selection, info.variableValues) },
|
|
3020
|
+
fieldsByTypeName: composite && fieldType.name ? { [fieldType.name]: {} } : {}
|
|
3021
|
+
};
|
|
3022
|
+
fields[alias] = node;
|
|
3023
|
+
}
|
|
3024
|
+
if (selection.selectionSet && deep && composite) collectSelections(selection.selectionSet.selections, info, node.fieldsByTypeName, fieldType, deep, activeFragments);
|
|
3025
|
+
continue;
|
|
3026
|
+
}
|
|
3027
|
+
if (!deep) continue;
|
|
3028
|
+
if (selection.kind === graphql.Kind.FRAGMENT_SPREAD) {
|
|
3029
|
+
const fragmentName = selection.name.value;
|
|
3030
|
+
const fragment = info.fragments?.[fragmentName];
|
|
3031
|
+
if (!fragment) throw new Error(`Drizzle-GraphQL Error: unknown fragment '${fragmentName}' — it is spread in the query but not defined in the document.`);
|
|
3032
|
+
if (activeFragments.has(fragmentName)) continue;
|
|
3033
|
+
const fragmentType = fragment.typeCondition ? typeFromCondition(info, fragment.typeCondition) : parentType;
|
|
3034
|
+
if (!isCompositeLike(fragmentType)) continue;
|
|
3035
|
+
activeFragments.add(fragmentName);
|
|
3036
|
+
try {
|
|
3037
|
+
collectSelections(fragment.selectionSet.selections, info, tree, fragmentType, deep, activeFragments);
|
|
3038
|
+
} finally {
|
|
3039
|
+
activeFragments.delete(fragmentName);
|
|
3040
|
+
}
|
|
3041
|
+
continue;
|
|
3042
|
+
}
|
|
3043
|
+
if (selection.kind === graphql.Kind.INLINE_FRAGMENT) {
|
|
3044
|
+
const fragmentType = selection.typeCondition ? typeFromCondition(info, selection.typeCondition) : parentType;
|
|
3045
|
+
if (!isCompositeLike(fragmentType)) continue;
|
|
3046
|
+
collectSelections(selection.selectionSet.selections, info, tree, fragmentType, deep, activeFragments);
|
|
3047
|
+
}
|
|
3048
|
+
}
|
|
3049
|
+
return tree;
|
|
3050
|
+
};
|
|
3051
|
+
/** The first key of an object, without materializing the whole key array to discard it. */
|
|
3052
|
+
const firstKey = (object) => {
|
|
3053
|
+
for (const key in object) return key;
|
|
3054
|
+
};
|
|
3055
|
+
/**
|
|
3056
|
+
* Reads the selection under a resolver's own field out of its `info`, as a tree of
|
|
3057
|
+
* {@link ResolveTree} nodes.
|
|
3058
|
+
*
|
|
3059
|
+
* ```ts
|
|
3060
|
+
* const parsed = parseResolveInfo(info, { deep: true })!;
|
|
3061
|
+
* const selected = parsed.fieldsByTypeName['Users']; // keyed by response key
|
|
3062
|
+
* ```
|
|
3063
|
+
*
|
|
3064
|
+
* Returns `undefined` when the resolver's field produced no node at all, which in practice
|
|
3065
|
+
* means `info` carried no field nodes.
|
|
3066
|
+
*/
|
|
3067
|
+
const parseResolveInfo = (info, options = {}) => {
|
|
3068
|
+
const fieldNodes = info.fieldNodes;
|
|
3069
|
+
if (!fieldNodes) throw new Error("Drizzle-GraphQL Error: resolve info carries no fieldNodes, so no selection could be read from it.");
|
|
3070
|
+
const tree = collectSelections(fieldNodes, info, {}, info.parentType, options.deep ?? true, /* @__PURE__ */ new Set());
|
|
3071
|
+
const typeKey = firstKey(tree);
|
|
3072
|
+
const fields = typeKey === void 0 ? void 0 : tree[typeKey];
|
|
3073
|
+
const fieldKey = fields && firstKey(fields);
|
|
3074
|
+
return fieldKey === void 0 ? void 0 : fields?.[fieldKey];
|
|
3075
|
+
};
|
|
3076
|
+
//#endregion
|
|
2910
3077
|
//#region src/util/builders/field-updates.ts
|
|
2911
3078
|
/**
|
|
2912
3079
|
* The scalars arithmetic is offered on. Decimal and BigInt transport as strings but are
|
|
@@ -3239,75 +3406,196 @@ const pruneNonEagerRelations = (relationMap, shouldEagerLoad) => {
|
|
|
3239
3406
|
for (const [tableName, rels] of Object.entries(relationMap)) out[tableName] = Object.fromEntries(Object.entries(rels).filter(([relationName]) => shouldEagerLoad(tableName, relationName)));
|
|
3240
3407
|
return out;
|
|
3241
3408
|
};
|
|
3242
|
-
|
|
3243
|
-
|
|
3244
|
-
fieldNames: /* @__PURE__ */ new Set(),
|
|
3245
|
-
states: /* @__PURE__ */ new WeakMap()
|
|
3246
|
-
} : void 0;
|
|
3247
|
-
const includedByDirectives = (node, variables) => {
|
|
3248
|
-
for (const directive of node.directives ?? []) {
|
|
3249
|
-
const name = directive.name.value;
|
|
3250
|
-
if (name !== "skip" && name !== "include") continue;
|
|
3251
|
-
const ifArg = directive.arguments?.find((arg) => arg.name.value === "if");
|
|
3252
|
-
let value;
|
|
3253
|
-
if (ifArg?.value.kind === "BooleanValue") value = ifArg.value.value;
|
|
3254
|
-
else if (ifArg?.value.kind === "Variable") value = variables?.[ifArg.value.name.value];
|
|
3255
|
-
if (name === "skip" && value === true) return false;
|
|
3256
|
-
if (name === "include" && value !== true) return false;
|
|
3257
|
-
}
|
|
3258
|
-
return true;
|
|
3259
|
-
};
|
|
3409
|
+
//#endregion
|
|
3410
|
+
//#region src/util/builders/common/select-runtime.ts
|
|
3260
3411
|
/**
|
|
3261
|
-
*
|
|
3262
|
-
*
|
|
3263
|
-
*
|
|
3264
|
-
*
|
|
3265
|
-
*
|
|
3266
|
-
* open a transaction it might never be able to close.
|
|
3412
|
+
* Runs the relational-query-builder select shared by every dialect's `generateSelect*`
|
|
3413
|
+
* resolver: selected columns + offset/limit + aliased orderBy/where callbacks + the eager
|
|
3414
|
+
* `with:` relation params, then remaps the result. `single` switches between
|
|
3415
|
+
* findFirst/findMany (and the single path omits `limit`). The PG fallback for tables
|
|
3416
|
+
* without RQB support stays in pg.ts; this covers the common RQB path for all three.
|
|
3267
3417
|
*/
|
|
3268
|
-
const
|
|
3269
|
-
|
|
3270
|
-
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
|
|
3274
|
-
|
|
3275
|
-
|
|
3276
|
-
|
|
3277
|
-
if (
|
|
3278
|
-
} else if (
|
|
3279
|
-
|
|
3280
|
-
|
|
3281
|
-
visitedFragments.add(fragmentName);
|
|
3282
|
-
const fragment = fragments?.[fragmentName];
|
|
3283
|
-
if (!fragment) return false;
|
|
3284
|
-
if (!collectRootMutationKeys(fragment.selectionSet, fragments, variables, known, keys, visitedFragments)) return false;
|
|
3285
|
-
}
|
|
3418
|
+
const runRelationalSelect = async (opts) => {
|
|
3419
|
+
const { queryBase, tables, tableName, table, relationMap, typeName, typeNameMapper, parsedInfo, offset, orderBy, where, single, filterCtx, pkNames, after, scope, deleted } = opts;
|
|
3420
|
+
const distinct = opts.distinct?.length ? opts.distinct : void 0;
|
|
3421
|
+
const cursorSelected = !single && isCursorFieldSelected(parsedInfo.fieldsByTypeName[typeName], table);
|
|
3422
|
+
let cursorEntries;
|
|
3423
|
+
if (!single && (after != null || cursorSelected)) {
|
|
3424
|
+
if (after != null && distinct) throw drizzleError("'after' cannot be combined with 'distinct'.", { code: "DRIZZLE_INVALID_CURSOR" });
|
|
3425
|
+
const cursorObstacle = orderByCursorObstacle(orderBy);
|
|
3426
|
+
if (cursorObstacle) {
|
|
3427
|
+
if (after != null) throw drizzleError(cursorObstacle, { code: "DRIZZLE_INVALID_CURSOR" });
|
|
3428
|
+
} else if (!pkNames?.length) {
|
|
3429
|
+
if (after != null) throw drizzleError(`Table ${tableName} has no primary key, so cursor pagination cannot be used on it.`, { code: "DRIZZLE_INVALID_CURSOR" });
|
|
3430
|
+
} else cursorEntries = cursorOrderingEntries(orderBy, pkNames);
|
|
3286
3431
|
}
|
|
3287
|
-
|
|
3288
|
-
|
|
3289
|
-
|
|
3290
|
-
|
|
3291
|
-
|
|
3292
|
-
|
|
3293
|
-
|
|
3294
|
-
|
|
3295
|
-
|
|
3296
|
-
|
|
3297
|
-
|
|
3298
|
-
|
|
3299
|
-
|
|
3300
|
-
|
|
3301
|
-
|
|
3302
|
-
|
|
3303
|
-
}
|
|
3304
|
-
|
|
3305
|
-
|
|
3306
|
-
|
|
3307
|
-
|
|
3308
|
-
|
|
3309
|
-
|
|
3310
|
-
|
|
3432
|
+
const cursorValues = after != null && cursorEntries ? decodeCursor(after, cursorEntries) : void 0;
|
|
3433
|
+
const needsDefaultOrder = single || offset != null || opts.limit != null;
|
|
3434
|
+
let distinctKeys;
|
|
3435
|
+
if (distinct) {
|
|
3436
|
+
distinctKeys = await selectDistinctKeys({
|
|
3437
|
+
db: opts.db,
|
|
3438
|
+
table,
|
|
3439
|
+
tableName,
|
|
3440
|
+
distinct,
|
|
3441
|
+
pkNames: pkNames ?? [],
|
|
3442
|
+
where: withScope(scope, tableName, table, where ? extractFilters(table, tableName, where, relationFilterCtx(filterCtx, tableName)) : void 0, deleted),
|
|
3443
|
+
orderBy,
|
|
3444
|
+
limit: single ? 1 : opts.limit,
|
|
3445
|
+
offset
|
|
3446
|
+
});
|
|
3447
|
+
if (!distinctKeys.length) return single ? void 0 : [];
|
|
3448
|
+
}
|
|
3449
|
+
const params = {
|
|
3450
|
+
columns: extractSelectedColumnsFromTree(parsedInfo.fieldsByTypeName[typeName], table, {
|
|
3451
|
+
tableName,
|
|
3452
|
+
relationMap,
|
|
3453
|
+
tables,
|
|
3454
|
+
allRelations: filterCtx?.relationMap
|
|
3455
|
+
}),
|
|
3456
|
+
offset: distinctKeys ? void 0 : offset,
|
|
3457
|
+
orderBy: distinctKeys ? (aliasedTable) => [...orderBy ? extractOrderBy(aliasedTable, orderBy, relationFilterCtx(filterCtx, tableName), where) : [], ...primaryKeyOrderExprs(aliasedTable, pkNames)] : cursorEntries ? (aliasedTable) => cursorOrderExprs(aliasedTable, cursorEntries) : orderBy ? (aliasedTable) => extractOrderBy(aliasedTable, orderBy, relationFilterCtx(filterCtx, tableName), where) : needsDefaultOrder && pkNames?.length ? (aliasedTable) => primaryKeyOrderExprs(aliasedTable, pkNames) : void 0,
|
|
3458
|
+
where: distinctKeys ? { RAW: (aliasedTable) => primaryKeyRestriction(aliasedTable, pkNames, distinctKeys) } : where || cursorValues || scope?.has(tableName, deleted) ? { RAW: (aliasedTable) => withScope(scope, tableName, aliasedTable, (0, drizzle_orm.and)(where ? extractFilters(aliasedTable, tableName, where, relationFilterCtx(filterCtx, tableName)) : void 0, cursorValues ? buildCursorCondition(aliasedTable, cursorEntries, cursorValues, opts.nullOrdering ?? "nulls-smallest") : void 0), deleted) } : void 0,
|
|
3459
|
+
with: relationMap[tableName] ? extractRelationsParams(relationMap, tables, tableName, parsedInfo, typeName, {
|
|
3460
|
+
typeNameMapper,
|
|
3461
|
+
filterCtx,
|
|
3462
|
+
limits: opts.limits,
|
|
3463
|
+
scope,
|
|
3464
|
+
defaultOrderBy: opts.defaultOrderBy,
|
|
3465
|
+
resolveName: opts.resolveName
|
|
3466
|
+
}) : void 0
|
|
3467
|
+
};
|
|
3468
|
+
if (single) {
|
|
3469
|
+
const result = await queryBase.findFirst(params);
|
|
3470
|
+
return result ? remapToGraphQLSingleOutput(result, tableName, table, relationMap) : void 0;
|
|
3471
|
+
}
|
|
3472
|
+
if (cursorEntries && cursorSelected) for (const [column] of cursorEntries) params.columns[column] = true;
|
|
3473
|
+
params.limit = distinctKeys ? void 0 : opts.limit;
|
|
3474
|
+
const result = await queryBase.findMany(params);
|
|
3475
|
+
if (cursorEntries && cursorSelected) attachRowCursors(result, cursorEntries);
|
|
3476
|
+
return remapToGraphQLArrayOutput(result, tableName, table, relationMap);
|
|
3477
|
+
};
|
|
3478
|
+
/**
|
|
3479
|
+
* After a mutation, re-fetch the mutated rows through the relational query builder so the
|
|
3480
|
+
* selected relations are eagerly loaded in a single query, then merge those relations onto
|
|
3481
|
+
* the `.returning()` rows — making the per-field BatchLoader fallback unnecessary.
|
|
3482
|
+
*
|
|
3483
|
+
* `withParams` is the pre-computed relation selection (from extractRelationsParams); the
|
|
3484
|
+
* caller only invokes this when relations are actually selected, so it also gates whether
|
|
3485
|
+
* the PK was forced into RETURNING.
|
|
3486
|
+
*
|
|
3487
|
+
* Falls back to the original `.returning()` rows (relations then resolve via the
|
|
3488
|
+
* field-level BatchLoader) when the table has no RQB support, no primary key columns can be
|
|
3489
|
+
* determined, or the re-fetch fails. Supports single- and multi-column primary keys.
|
|
3490
|
+
*/
|
|
3491
|
+
const eagerLoadMutationRelations = async (db, tableName, rows, pkNames, withParams) => {
|
|
3492
|
+
if (!rows.length || !pkNames.length || !withParams || !Object.keys(withParams).length) return rows;
|
|
3493
|
+
const queryBase = db.query?.[tableName];
|
|
3494
|
+
if (!queryBase) return rows;
|
|
3495
|
+
const keyableRows = rows.filter((row) => pkNames.every((n) => row[n] != null));
|
|
3496
|
+
if (!keyableRows.length) return rows;
|
|
3497
|
+
const pkColumns = {};
|
|
3498
|
+
for (const pk of pkNames) pkColumns[pk] = true;
|
|
3499
|
+
const relationNames = Object.keys(withParams);
|
|
3500
|
+
const keyOf = (row) => JSON.stringify(pkNames.map((n) => typeof row[n] === "bigint" ? row[n].toString() : row[n]));
|
|
3501
|
+
let whereRaw;
|
|
3502
|
+
if (pkNames.length === 1) {
|
|
3503
|
+
const pkName = pkNames[0];
|
|
3504
|
+
const ids = keyableRows.map((r) => r[pkName]);
|
|
3505
|
+
whereRaw = (aliased) => (0, drizzle_orm.inArray)(aliased[pkName], ids);
|
|
3506
|
+
} else whereRaw = (aliased) => {
|
|
3507
|
+
const lhs = drizzle_orm.sql.join(pkNames.map((n) => drizzle_orm.sql`${aliased[n]}`), drizzle_orm.sql`, `);
|
|
3508
|
+
const tuples = drizzle_orm.sql.join(keyableRows.map((row) => drizzle_orm.sql`(${drizzle_orm.sql.join(pkNames.map((n) => drizzle_orm.sql`${row[n]}`), drizzle_orm.sql`, `)})`), drizzle_orm.sql`, `);
|
|
3509
|
+
return drizzle_orm.sql`(${lhs}) in (${tuples})`;
|
|
3510
|
+
};
|
|
3511
|
+
let enriched;
|
|
3512
|
+
try {
|
|
3513
|
+
enriched = await queryBase.findMany({
|
|
3514
|
+
columns: pkColumns,
|
|
3515
|
+
where: { RAW: whereRaw },
|
|
3516
|
+
with: withParams
|
|
3517
|
+
});
|
|
3518
|
+
} catch (err) {
|
|
3519
|
+
console.warn(`[drizzle-graphql] eager-loading relations for a "${tableName}" mutation failed; falling back to lazy resolution.`, err);
|
|
3520
|
+
return rows;
|
|
3521
|
+
}
|
|
3522
|
+
const byKey = new Map(enriched.map((e) => [keyOf(e), e]));
|
|
3523
|
+
for (const row of rows) {
|
|
3524
|
+
const match = byKey.get(keyOf(row));
|
|
3525
|
+
if (!match) continue;
|
|
3526
|
+
for (const rel of relationNames) row[rel] = match[rel];
|
|
3527
|
+
}
|
|
3528
|
+
return rows;
|
|
3529
|
+
};
|
|
3530
|
+
const createMutationTxCtx = (options) => options ? {
|
|
3531
|
+
timeoutMs: options.timeoutMs,
|
|
3532
|
+
fieldNames: /* @__PURE__ */ new Set(),
|
|
3533
|
+
states: /* @__PURE__ */ new WeakMap()
|
|
3534
|
+
} : void 0;
|
|
3535
|
+
const includedByDirectives = (node, variables) => {
|
|
3536
|
+
for (const directive of node.directives ?? []) {
|
|
3537
|
+
const name = directive.name.value;
|
|
3538
|
+
if (name !== "skip" && name !== "include") continue;
|
|
3539
|
+
const ifArg = directive.arguments?.find((arg) => arg.name.value === "if");
|
|
3540
|
+
let value;
|
|
3541
|
+
if (ifArg?.value.kind === "BooleanValue") value = ifArg.value.value;
|
|
3542
|
+
else if (ifArg?.value.kind === "Variable") value = variables?.[ifArg.value.name.value];
|
|
3543
|
+
if (name === "skip" && value === true) return false;
|
|
3544
|
+
if (name === "include" && value !== true) return false;
|
|
3545
|
+
}
|
|
3546
|
+
return true;
|
|
3547
|
+
};
|
|
3548
|
+
/**
|
|
3549
|
+
* Collects the response keys of every root mutation field the executor will run, expanding
|
|
3550
|
+
* fragments and honoring `@skip` / `@include`. Distinct response keys (alias ?? name) are
|
|
3551
|
+
* counted, matching graphql-js field collection, so a field selected twice under one key is
|
|
3552
|
+
* still one resolver call. Returns `false` when the document selects a root mutation field
|
|
3553
|
+
* this build did not generate — its completion cannot be tracked, so the caller must not
|
|
3554
|
+
* open a transaction it might never be able to close.
|
|
3555
|
+
*/
|
|
3556
|
+
const collectRootMutationKeys = (selectionSet, fragments, variables, known, keys, visitedFragments) => {
|
|
3557
|
+
for (const selection of selectionSet.selections) {
|
|
3558
|
+
if (!includedByDirectives(selection, variables)) continue;
|
|
3559
|
+
if (selection.kind === "Field") {
|
|
3560
|
+
const name = selection.name.value;
|
|
3561
|
+
if (name === "__typename") continue;
|
|
3562
|
+
if (!known.has(name)) return false;
|
|
3563
|
+
keys.add(selection.alias?.value ?? name);
|
|
3564
|
+
} else if (selection.kind === "InlineFragment") {
|
|
3565
|
+
if (!collectRootMutationKeys(selection.selectionSet, fragments, variables, known, keys, visitedFragments)) return false;
|
|
3566
|
+
} else if (selection.kind === "FragmentSpread") {
|
|
3567
|
+
const fragmentName = selection.name.value;
|
|
3568
|
+
if (visitedFragments.has(fragmentName)) continue;
|
|
3569
|
+
visitedFragments.add(fragmentName);
|
|
3570
|
+
const fragment = fragments?.[fragmentName];
|
|
3571
|
+
if (!fragment) return false;
|
|
3572
|
+
if (!collectRootMutationKeys(fragment.selectionSet, fragments, variables, known, keys, visitedFragments)) return false;
|
|
3573
|
+
}
|
|
3574
|
+
}
|
|
3575
|
+
return true;
|
|
3576
|
+
};
|
|
3577
|
+
const abortedBatchError = () => drizzleError("Drizzle-GraphQL Error: Mutation was not executed because an earlier mutation in the same request failed and the shared transaction was rolled back.", { code: "DRIZZLE_TRANSACTION_ABORTED" });
|
|
3578
|
+
/** Opens the request-wide transaction and wires up latch, inactivity timer and cleanup. */
|
|
3579
|
+
const openSharedTx = (db, context, txCtx, stateKey, expected) => {
|
|
3580
|
+
let release;
|
|
3581
|
+
let rejectLatch;
|
|
3582
|
+
const latch = new Promise((resolve, reject) => {
|
|
3583
|
+
release = resolve;
|
|
3584
|
+
rejectLatch = reject;
|
|
3585
|
+
});
|
|
3586
|
+
let readyResolve;
|
|
3587
|
+
let readyReject;
|
|
3588
|
+
const ready = new Promise((resolve, reject) => {
|
|
3589
|
+
readyResolve = resolve;
|
|
3590
|
+
readyReject = reject;
|
|
3591
|
+
});
|
|
3592
|
+
ready.catch(() => {});
|
|
3593
|
+
const canStash = context !== null && typeof context === "object" && !Object.isFrozen(context);
|
|
3594
|
+
const settled = db.transaction(async (tx) => {
|
|
3595
|
+
if (canStash) context[drizzleExecutorKey] = tx;
|
|
3596
|
+
readyResolve(tx);
|
|
3597
|
+
await latch;
|
|
3598
|
+
});
|
|
3311
3599
|
let timer;
|
|
3312
3600
|
const clearTimer = () => {
|
|
3313
3601
|
if (timer !== void 0) {
|
|
@@ -3417,12 +3705,6 @@ const hardDeleteArg = {
|
|
|
3417
3705
|
defaultValue: false,
|
|
3418
3706
|
description: "Remove the matched rows instead of marking them deleted. Reads at `INCLUDE`, so it reaches rows that are already marked; a `scope` still confines what it can reach."
|
|
3419
3707
|
};
|
|
3420
|
-
/**
|
|
3421
|
-
* Computes the RETURNING columns and relation selection for a mutation resolver: extracts
|
|
3422
|
-
* the selected scalar columns, determines whether any relations were selected, and only
|
|
3423
|
-
* then forces the primary key into the column set (so the post-mutation eager-load can
|
|
3424
|
-
* re-key rows). Returns everything the resolver needs to decide whether to eager-load.
|
|
3425
|
-
*/
|
|
3426
3708
|
const prepareMutationRelationColumns = (params) => {
|
|
3427
3709
|
const { relationMap, tables, tableName, typeNameMapper, table, pkNames, parsedInfo, resolveName } = params;
|
|
3428
3710
|
const typeName = resolveObjectTypeName(tableName, typeNameMapper, resolveName);
|
|
@@ -3446,6 +3728,38 @@ const prepareMutationRelationColumns = (params) => {
|
|
|
3446
3728
|
};
|
|
3447
3729
|
};
|
|
3448
3730
|
/**
|
|
3731
|
+
* The prologue every write resolver runs: parse the resolve info, then work out the columns
|
|
3732
|
+
* to return and the relations to eager-load from it. `parsedInfo` comes back too — the delete
|
|
3733
|
+
* resolver reads its own columns off the tree rather than taking the prepared set.
|
|
3734
|
+
*/
|
|
3735
|
+
const mutationSelection = (info, params) => {
|
|
3736
|
+
const parsedInfo = parseResolveInfo(info, { deep: true });
|
|
3737
|
+
return {
|
|
3738
|
+
parsedInfo,
|
|
3739
|
+
...prepareMutationRelationColumns({
|
|
3740
|
+
...params,
|
|
3741
|
+
parsedInfo
|
|
3742
|
+
})
|
|
3743
|
+
};
|
|
3744
|
+
};
|
|
3745
|
+
/**
|
|
3746
|
+
* The `where` a write runs under: the caller's filters — required when the mutation refuses to
|
|
3747
|
+
* run unbounded — with the row scope ANDed on last, so a supplied filter can only narrow the
|
|
3748
|
+
* scope and never widen it. An out-of-scope row is simply not matched.
|
|
3749
|
+
*
|
|
3750
|
+
* `deleted` states which soft-delete mode the match reads at, for the writes that need one:
|
|
3751
|
+
* `restore` sees only marked rows, a hard delete has to see them too.
|
|
3752
|
+
*/
|
|
3753
|
+
const scopedWhere = (params) => {
|
|
3754
|
+
const { scope, tableName, table, where, relationCtx, required, deleted } = params;
|
|
3755
|
+
return withScope(scope, tableName, table, required ? extractRequiredFilters(table, tableName, where, relationCtx) : where ? extractFilters(table, tableName, where, relationCtx) : void 0, deleted);
|
|
3756
|
+
};
|
|
3757
|
+
/**
|
|
3758
|
+
* The epilogue: the written rows with their selected relations attached, or the rows as they
|
|
3759
|
+
* came back when the selection asked for no relation at all.
|
|
3760
|
+
*/
|
|
3761
|
+
const withEagerRelations = (executor, tableName, rows, pkNames, withParams, hasRelations) => hasRelations ? eagerLoadMutationRelations(executor, tableName, rows, pkNames, withParams) : rows;
|
|
3762
|
+
/**
|
|
3449
3763
|
* The per-entry input of `update<Table>Many`: `{ where, set }`, reusing the table's
|
|
3450
3764
|
* update `set` input and filter input. Shared by all three dialect builders.
|
|
3451
3765
|
*/
|
|
@@ -3545,8 +3859,14 @@ const generateWriteCount = ({ db, tableName, table, kind, setArgs, filterArgs, f
|
|
|
3545
3859
|
try {
|
|
3546
3860
|
return await runMutation(db, context, info, txCtx, async (executor) => {
|
|
3547
3861
|
const { where, set } = args;
|
|
3548
|
-
const
|
|
3549
|
-
|
|
3862
|
+
const filters = scopedWhere({
|
|
3863
|
+
scope: void 0,
|
|
3864
|
+
tableName,
|
|
3865
|
+
table,
|
|
3866
|
+
where,
|
|
3867
|
+
relationCtx: relationFilterCtx(filterCtx, tableName),
|
|
3868
|
+
required: requireWhere
|
|
3869
|
+
});
|
|
3550
3870
|
let query;
|
|
3551
3871
|
if (kind === "delete") query = executor.delete(table);
|
|
3552
3872
|
else {
|
|
@@ -3792,196 +4112,48 @@ const identifyRows = (field, args) => {
|
|
|
3792
4112
|
};
|
|
3793
4113
|
};
|
|
3794
4114
|
//#endregion
|
|
3795
|
-
//#region src/util/
|
|
3796
|
-
/** Unwraps `[T]!`-style wrappers down to the named type. `getNamedType` without the realm tie. */
|
|
3797
|
-
const namedTypeOf = (type) => {
|
|
3798
|
-
let current = type;
|
|
3799
|
-
while (current?.ofType) current = current.ofType;
|
|
3800
|
-
return current;
|
|
3801
|
-
};
|
|
3802
|
-
/**
|
|
3803
|
-
* Whether a field's named output type has sub-selections. Object types and interfaces expose
|
|
3804
|
-
* `getFields`, unions expose `getTypes`; scalars and enums expose neither. Input object types
|
|
3805
|
-
* also expose `getFields`, but an input type can never be a field's output type, so there is
|
|
3806
|
-
* nothing here for that to collide with.
|
|
3807
|
-
*/
|
|
3808
|
-
const isCompositeLike = (type) => !!type && (typeof type.getFields === "function" || typeof type.getTypes === "function");
|
|
3809
|
-
/**
|
|
3810
|
-
* The fields declared on a type, or `undefined` when it has none to declare. A union is the
|
|
3811
|
-
* case that matters: it has no fields of its own, so a selection directly against one resolves
|
|
3812
|
-
* to nothing and is dropped — only the inline fragments underneath contribute.
|
|
3813
|
-
*/
|
|
3814
|
-
const fieldsOf = (type) => typeof type?.getFields === "function" ? type.getFields() : void 0;
|
|
4115
|
+
//#region src/util/builders/common/relation-resolvers.ts
|
|
3815
4116
|
/**
|
|
3816
|
-
*
|
|
3817
|
-
*
|
|
4117
|
+
* Fetches a to-many relation with per-parent limit/offset for ALL parents in a
|
|
4118
|
+
* single query, using a window function (ROW_NUMBER() OVER (PARTITION BY fk ...)).
|
|
4119
|
+
*
|
|
4120
|
+
* This replaces the previous per-parent fallback that issued one query per parent
|
|
4121
|
+
* (true N+1) whenever pagination args were present. Each parent gets its own
|
|
4122
|
+
* limit/offset window while the database is hit exactly once for the whole batch.
|
|
4123
|
+
*
|
|
4124
|
+
* Window functions require PostgreSQL, MySQL >= 8.0, or SQLite >= 3.25.
|
|
4125
|
+
* Returns raw rows (NOT remapped); the caller groups + remaps them.
|
|
3818
4126
|
*/
|
|
3819
|
-
const
|
|
3820
|
-
const
|
|
3821
|
-
|
|
3822
|
-
|
|
3823
|
-
|
|
3824
|
-
|
|
3825
|
-
const
|
|
3826
|
-
|
|
3827
|
-
|
|
4127
|
+
const batchedPaginatedRelationQuery = async (db, targetTable, foreignCol, whereCondition, orderByArg, limit, offset, pkNames, columns, orderCtx, whereArgs) => {
|
|
4128
|
+
const cols = columns ?? (0, drizzle_orm.getColumns)(targetTable);
|
|
4129
|
+
const orderExprs = [...orderByArg ? extractOrderBy(targetTable, orderByArg, orderCtx, whereArgs) : [], ...primaryKeyOrderExprs(targetTable, pkNames)];
|
|
4130
|
+
const orderClause = orderExprs.length ? drizzle_orm.sql` order by ${drizzle_orm.sql.join(orderExprs, drizzle_orm.sql`, `)}` : drizzle_orm.sql``;
|
|
4131
|
+
const RN = "__drizzle_graphql_rn";
|
|
4132
|
+
const rowNumber = drizzle_orm.sql`row_number() over (partition by ${foreignCol}${orderClause})`.as(RN);
|
|
4133
|
+
const sub = db.select({
|
|
4134
|
+
...cols,
|
|
4135
|
+
[RN]: rowNumber
|
|
4136
|
+
}).from(targetTable).where(whereCondition).as("__paginated");
|
|
4137
|
+
const lower = offset ?? 0;
|
|
4138
|
+
const windowConds = [(0, drizzle_orm.gt)(sub[RN], lower)];
|
|
4139
|
+
if (limit != null) windowConds.push((0, drizzle_orm.lte)(sub[RN], lower + limit));
|
|
4140
|
+
const rows = await db.select().from(sub).where((0, drizzle_orm.and)(...windowConds)).orderBy(sub[RN]);
|
|
4141
|
+
for (const row of rows) delete row[RN];
|
|
4142
|
+
return rows;
|
|
3828
4143
|
};
|
|
3829
4144
|
/**
|
|
3830
|
-
*
|
|
3831
|
-
*
|
|
3832
|
-
|
|
3833
|
-
|
|
3834
|
-
|
|
3835
|
-
|
|
3836
|
-
|
|
3837
|
-
|
|
3838
|
-
|
|
3839
|
-
|
|
3840
|
-
|
|
3841
|
-
|
|
3842
|
-
return false;
|
|
3843
|
-
};
|
|
3844
|
-
/** The type a fragment's `on Type` condition names, looked up in the executing schema. */
|
|
3845
|
-
const typeFromCondition = (info, condition) => info.schema.getType(condition.name.value);
|
|
3846
|
-
/**
|
|
3847
|
-
* Introspection meta-fields are not schema fields and never reach the database. `__typename` is
|
|
3848
|
-
* the one that turns up in practice; `__id` is excluded from the rule because a schema is free
|
|
3849
|
-
* to declare a field by that name.
|
|
3850
|
-
*/
|
|
3851
|
-
const isMetaField = (name) => name.startsWith("__") && name !== "__id";
|
|
3852
|
-
/**
|
|
3853
|
-
* Merges one selection set into `tree`, which is a `fieldsByTypeName` map — the caller owns the
|
|
3854
|
-
* level, this owns the entries under `parentType`'s name.
|
|
3855
|
-
*
|
|
3856
|
-
* `activeFragments` is the set of fragment names on the current spread path. A document that
|
|
3857
|
-
* passed validation cannot contain a fragment cycle, but this parser also runs on trees a
|
|
3858
|
-
* caller assembled by hand, and an unbounded walk would hang rather than fail.
|
|
3859
|
-
*/
|
|
3860
|
-
const collectSelections = (selections, info, tree, parentType, deep, activeFragments) => {
|
|
3861
|
-
const parentTypeName = parentType?.name;
|
|
3862
|
-
if (!parentTypeName) return tree;
|
|
3863
|
-
let fields = tree[parentTypeName];
|
|
3864
|
-
if (!fields) {
|
|
3865
|
-
fields = {};
|
|
3866
|
-
tree[parentTypeName] = fields;
|
|
3867
|
-
}
|
|
3868
|
-
const parentFields = fieldsOf(parentType);
|
|
3869
|
-
for (const selection of selections) {
|
|
3870
|
-
if (isExcluded(info, selection)) continue;
|
|
3871
|
-
if (selection.kind === graphql.Kind.FIELD) {
|
|
3872
|
-
const name = selection.name.value;
|
|
3873
|
-
if (isMetaField(name)) continue;
|
|
3874
|
-
const fieldDef = parentFields?.[name];
|
|
3875
|
-
if (!fieldDef) continue;
|
|
3876
|
-
const fieldType = namedTypeOf(fieldDef.type);
|
|
3877
|
-
if (!fieldType) continue;
|
|
3878
|
-
const composite = isCompositeLike(fieldType);
|
|
3879
|
-
const alias = selection.alias?.value || name;
|
|
3880
|
-
let node = fields[alias];
|
|
3881
|
-
if (!node) {
|
|
3882
|
-
node = {
|
|
3883
|
-
name,
|
|
3884
|
-
alias,
|
|
3885
|
-
args: { ...(0, graphql.getArgumentValues)(fieldDef, selection, info.variableValues) },
|
|
3886
|
-
fieldsByTypeName: composite && fieldType.name ? { [fieldType.name]: {} } : {}
|
|
3887
|
-
};
|
|
3888
|
-
fields[alias] = node;
|
|
3889
|
-
}
|
|
3890
|
-
if (selection.selectionSet && deep && composite) collectSelections(selection.selectionSet.selections, info, node.fieldsByTypeName, fieldType, deep, activeFragments);
|
|
3891
|
-
continue;
|
|
3892
|
-
}
|
|
3893
|
-
if (!deep) continue;
|
|
3894
|
-
if (selection.kind === graphql.Kind.FRAGMENT_SPREAD) {
|
|
3895
|
-
const fragmentName = selection.name.value;
|
|
3896
|
-
const fragment = info.fragments?.[fragmentName];
|
|
3897
|
-
if (!fragment) throw new Error(`Drizzle-GraphQL Error: unknown fragment '${fragmentName}' — it is spread in the query but not defined in the document.`);
|
|
3898
|
-
if (activeFragments.has(fragmentName)) continue;
|
|
3899
|
-
const fragmentType = fragment.typeCondition ? typeFromCondition(info, fragment.typeCondition) : parentType;
|
|
3900
|
-
if (!isCompositeLike(fragmentType)) continue;
|
|
3901
|
-
activeFragments.add(fragmentName);
|
|
3902
|
-
try {
|
|
3903
|
-
collectSelections(fragment.selectionSet.selections, info, tree, fragmentType, deep, activeFragments);
|
|
3904
|
-
} finally {
|
|
3905
|
-
activeFragments.delete(fragmentName);
|
|
3906
|
-
}
|
|
3907
|
-
continue;
|
|
3908
|
-
}
|
|
3909
|
-
if (selection.kind === graphql.Kind.INLINE_FRAGMENT) {
|
|
3910
|
-
const fragmentType = selection.typeCondition ? typeFromCondition(info, selection.typeCondition) : parentType;
|
|
3911
|
-
if (!isCompositeLike(fragmentType)) continue;
|
|
3912
|
-
collectSelections(selection.selectionSet.selections, info, tree, fragmentType, deep, activeFragments);
|
|
3913
|
-
}
|
|
3914
|
-
}
|
|
3915
|
-
return tree;
|
|
3916
|
-
};
|
|
3917
|
-
/** The first key of an object, without materializing the whole key array to discard it. */
|
|
3918
|
-
const firstKey = (object) => {
|
|
3919
|
-
for (const key in object) return key;
|
|
3920
|
-
};
|
|
3921
|
-
/**
|
|
3922
|
-
* Reads the selection under a resolver's own field out of its `info`, as a tree of
|
|
3923
|
-
* {@link ResolveTree} nodes.
|
|
3924
|
-
*
|
|
3925
|
-
* ```ts
|
|
3926
|
-
* const parsed = parseResolveInfo(info, { deep: true })!;
|
|
3927
|
-
* const selected = parsed.fieldsByTypeName['Users']; // keyed by response key
|
|
3928
|
-
* ```
|
|
3929
|
-
*
|
|
3930
|
-
* Returns `undefined` when the resolver's field produced no node at all, which in practice
|
|
3931
|
-
* means `info` carried no field nodes.
|
|
3932
|
-
*/
|
|
3933
|
-
const parseResolveInfo = (info, options = {}) => {
|
|
3934
|
-
const fieldNodes = info.fieldNodes;
|
|
3935
|
-
if (!fieldNodes) throw new Error("Drizzle-GraphQL Error: resolve info carries no fieldNodes, so no selection could be read from it.");
|
|
3936
|
-
const tree = collectSelections(fieldNodes, info, {}, info.parentType, options.deep ?? true, /* @__PURE__ */ new Set());
|
|
3937
|
-
const typeKey = firstKey(tree);
|
|
3938
|
-
const fields = typeKey === void 0 ? void 0 : tree[typeKey];
|
|
3939
|
-
const fieldKey = fields && firstKey(fields);
|
|
3940
|
-
return fieldKey === void 0 ? void 0 : fields?.[fieldKey];
|
|
3941
|
-
};
|
|
3942
|
-
//#endregion
|
|
3943
|
-
//#region src/util/builders/common/relation-resolvers.ts
|
|
3944
|
-
/**
|
|
3945
|
-
* Fetches a to-many relation with per-parent limit/offset for ALL parents in a
|
|
3946
|
-
* single query, using a window function (ROW_NUMBER() OVER (PARTITION BY fk ...)).
|
|
3947
|
-
*
|
|
3948
|
-
* This replaces the previous per-parent fallback that issued one query per parent
|
|
3949
|
-
* (true N+1) whenever pagination args were present. Each parent gets its own
|
|
3950
|
-
* limit/offset window while the database is hit exactly once for the whole batch.
|
|
3951
|
-
*
|
|
3952
|
-
* Window functions require PostgreSQL, MySQL >= 8.0, or SQLite >= 3.25.
|
|
3953
|
-
* Returns raw rows (NOT remapped); the caller groups + remaps them.
|
|
3954
|
-
*/
|
|
3955
|
-
const batchedPaginatedRelationQuery = async (db, targetTable, foreignCol, whereCondition, orderByArg, limit, offset, pkNames, columns, orderCtx, whereArgs) => {
|
|
3956
|
-
const cols = columns ?? (0, drizzle_orm.getColumns)(targetTable);
|
|
3957
|
-
const orderExprs = [...orderByArg ? extractOrderBy(targetTable, orderByArg, orderCtx, whereArgs) : [], ...primaryKeyOrderExprs(targetTable, pkNames)];
|
|
3958
|
-
const orderClause = orderExprs.length ? drizzle_orm.sql` order by ${drizzle_orm.sql.join(orderExprs, drizzle_orm.sql`, `)}` : drizzle_orm.sql``;
|
|
3959
|
-
const RN = "__drizzle_graphql_rn";
|
|
3960
|
-
const rowNumber = drizzle_orm.sql`row_number() over (partition by ${foreignCol}${orderClause})`.as(RN);
|
|
3961
|
-
const sub = db.select({
|
|
3962
|
-
...cols,
|
|
3963
|
-
[RN]: rowNumber
|
|
3964
|
-
}).from(targetTable).where(whereCondition).as("__paginated");
|
|
3965
|
-
const lower = offset ?? 0;
|
|
3966
|
-
const windowConds = [(0, drizzle_orm.gt)(sub[RN], lower)];
|
|
3967
|
-
if (limit != null) windowConds.push((0, drizzle_orm.lte)(sub[RN], lower + limit));
|
|
3968
|
-
const rows = await db.select().from(sub).where((0, drizzle_orm.and)(...windowConds)).orderBy(sub[RN]);
|
|
3969
|
-
for (const row of rows) delete row[RN];
|
|
3970
|
-
return rows;
|
|
3971
|
-
};
|
|
3972
|
-
/**
|
|
3973
|
-
* Creates a RelationResolverFactory that generates field-level resolvers for each relation.
|
|
3974
|
-
* Each resolver:
|
|
3975
|
-
* 1. Returns pre-fetched data if the parent resolver already included it (eager path, zero cost).
|
|
3976
|
-
* 2. When limit/offset args are present, falls back to a direct per-item query.
|
|
3977
|
-
* 3. Otherwise batches all sibling resolver calls within the same GraphQL execution tick
|
|
3978
|
-
* into a single IN-clause query, eliminating N+1 database round-trips.
|
|
3979
|
-
*
|
|
3980
|
-
* `nullOrdering` is the dialect's native `ORDER BY` placement for `NULL` — the keyset
|
|
3981
|
-
* predicate behind a relation's `after` argument has to match it, or rows with a `NULL` in an
|
|
3982
|
-
* ordered column would be skipped instead of paged through. It has no sensible default, so
|
|
3983
|
-
* each dialect builder states it: `'nulls-largest'` for PostgreSQL, `'nulls-smallest'` for
|
|
3984
|
-
* MySQL and SQLite.
|
|
4145
|
+
* Creates a RelationResolverFactory that generates field-level resolvers for each relation.
|
|
4146
|
+
* Each resolver:
|
|
4147
|
+
* 1. Returns pre-fetched data if the parent resolver already included it (eager path, zero cost).
|
|
4148
|
+
* 2. When limit/offset args are present, falls back to a direct per-item query.
|
|
4149
|
+
* 3. Otherwise batches all sibling resolver calls within the same GraphQL execution tick
|
|
4150
|
+
* into a single IN-clause query, eliminating N+1 database round-trips.
|
|
4151
|
+
*
|
|
4152
|
+
* `nullOrdering` is the dialect's native `ORDER BY` placement for `NULL` — the keyset
|
|
4153
|
+
* predicate behind a relation's `after` argument has to match it, or rows with a `NULL` in an
|
|
4154
|
+
* ordered column would be skipped instead of paged through. It has no sensible default, so
|
|
4155
|
+
* each dialect builder states it: `'nulls-largest'` for PostgreSQL, `'nulls-smallest'` for
|
|
4156
|
+
* MySQL and SQLite.
|
|
3985
4157
|
*/
|
|
3986
4158
|
const createRelationResolverFactory = (db, tables, nullOrdering, filterCtx, limits, policies) => ({ tableName, relationName, relEntry, isOne }) => {
|
|
3987
4159
|
const parentTable = tables[tableName];
|
|
@@ -4201,127 +4373,6 @@ const defineRootField = (fields, kind, name, config) => {
|
|
|
4201
4373
|
fields[name] = config;
|
|
4202
4374
|
};
|
|
4203
4375
|
//#endregion
|
|
4204
|
-
//#region src/util/builders/common/select-runtime.ts
|
|
4205
|
-
/**
|
|
4206
|
-
* Runs the relational-query-builder select shared by every dialect's `generateSelect*`
|
|
4207
|
-
* resolver: selected columns + offset/limit + aliased orderBy/where callbacks + the eager
|
|
4208
|
-
* `with:` relation params, then remaps the result. `single` switches between
|
|
4209
|
-
* findFirst/findMany (and the single path omits `limit`). The PG fallback for tables
|
|
4210
|
-
* without RQB support stays in pg.ts; this covers the common RQB path for all three.
|
|
4211
|
-
*/
|
|
4212
|
-
const runRelationalSelect = async (opts) => {
|
|
4213
|
-
const { queryBase, tables, tableName, table, relationMap, typeName, typeNameMapper, parsedInfo, offset, orderBy, where, single, filterCtx, pkNames, after, scope, deleted } = opts;
|
|
4214
|
-
const distinct = opts.distinct?.length ? opts.distinct : void 0;
|
|
4215
|
-
const cursorSelected = !single && isCursorFieldSelected(parsedInfo.fieldsByTypeName[typeName], table);
|
|
4216
|
-
let cursorEntries;
|
|
4217
|
-
if (!single && (after != null || cursorSelected)) {
|
|
4218
|
-
if (after != null && distinct) throw drizzleError("'after' cannot be combined with 'distinct'.", { code: "DRIZZLE_INVALID_CURSOR" });
|
|
4219
|
-
const cursorObstacle = orderByCursorObstacle(orderBy);
|
|
4220
|
-
if (cursorObstacle) {
|
|
4221
|
-
if (after != null) throw drizzleError(cursorObstacle, { code: "DRIZZLE_INVALID_CURSOR" });
|
|
4222
|
-
} else if (!pkNames?.length) {
|
|
4223
|
-
if (after != null) throw drizzleError(`Table ${tableName} has no primary key, so cursor pagination cannot be used on it.`, { code: "DRIZZLE_INVALID_CURSOR" });
|
|
4224
|
-
} else cursorEntries = cursorOrderingEntries(orderBy, pkNames);
|
|
4225
|
-
}
|
|
4226
|
-
const cursorValues = after != null && cursorEntries ? decodeCursor(after, cursorEntries) : void 0;
|
|
4227
|
-
const needsDefaultOrder = single || offset != null || opts.limit != null;
|
|
4228
|
-
let distinctKeys;
|
|
4229
|
-
if (distinct) {
|
|
4230
|
-
distinctKeys = await selectDistinctKeys({
|
|
4231
|
-
db: opts.db,
|
|
4232
|
-
table,
|
|
4233
|
-
tableName,
|
|
4234
|
-
distinct,
|
|
4235
|
-
pkNames: pkNames ?? [],
|
|
4236
|
-
where: withScope(scope, tableName, table, where ? extractFilters(table, tableName, where, relationFilterCtx(filterCtx, tableName)) : void 0, deleted),
|
|
4237
|
-
orderBy,
|
|
4238
|
-
limit: single ? 1 : opts.limit,
|
|
4239
|
-
offset
|
|
4240
|
-
});
|
|
4241
|
-
if (!distinctKeys.length) return single ? void 0 : [];
|
|
4242
|
-
}
|
|
4243
|
-
const params = {
|
|
4244
|
-
columns: extractSelectedColumnsFromTree(parsedInfo.fieldsByTypeName[typeName], table, {
|
|
4245
|
-
tableName,
|
|
4246
|
-
relationMap,
|
|
4247
|
-
tables,
|
|
4248
|
-
allRelations: filterCtx?.relationMap
|
|
4249
|
-
}),
|
|
4250
|
-
offset: distinctKeys ? void 0 : offset,
|
|
4251
|
-
orderBy: distinctKeys ? (aliasedTable) => [...orderBy ? extractOrderBy(aliasedTable, orderBy, relationFilterCtx(filterCtx, tableName), where) : [], ...primaryKeyOrderExprs(aliasedTable, pkNames)] : cursorEntries ? (aliasedTable) => cursorOrderExprs(aliasedTable, cursorEntries) : orderBy ? (aliasedTable) => extractOrderBy(aliasedTable, orderBy, relationFilterCtx(filterCtx, tableName), where) : needsDefaultOrder && pkNames?.length ? (aliasedTable) => primaryKeyOrderExprs(aliasedTable, pkNames) : void 0,
|
|
4252
|
-
where: distinctKeys ? { RAW: (aliasedTable) => primaryKeyRestriction(aliasedTable, pkNames, distinctKeys) } : where || cursorValues || scope?.has(tableName, deleted) ? { RAW: (aliasedTable) => withScope(scope, tableName, aliasedTable, (0, drizzle_orm.and)(where ? extractFilters(aliasedTable, tableName, where, relationFilterCtx(filterCtx, tableName)) : void 0, cursorValues ? buildCursorCondition(aliasedTable, cursorEntries, cursorValues, opts.nullOrdering ?? "nulls-smallest") : void 0), deleted) } : void 0,
|
|
4253
|
-
with: relationMap[tableName] ? extractRelationsParams(relationMap, tables, tableName, parsedInfo, typeName, {
|
|
4254
|
-
typeNameMapper,
|
|
4255
|
-
filterCtx,
|
|
4256
|
-
limits: opts.limits,
|
|
4257
|
-
scope,
|
|
4258
|
-
defaultOrderBy: opts.defaultOrderBy,
|
|
4259
|
-
resolveName: opts.resolveName
|
|
4260
|
-
}) : void 0
|
|
4261
|
-
};
|
|
4262
|
-
if (single) {
|
|
4263
|
-
const result = await queryBase.findFirst(params);
|
|
4264
|
-
return result ? remapToGraphQLSingleOutput(result, tableName, table, relationMap) : void 0;
|
|
4265
|
-
}
|
|
4266
|
-
if (cursorEntries && cursorSelected) for (const [column] of cursorEntries) params.columns[column] = true;
|
|
4267
|
-
params.limit = distinctKeys ? void 0 : opts.limit;
|
|
4268
|
-
const result = await queryBase.findMany(params);
|
|
4269
|
-
if (cursorEntries && cursorSelected) attachRowCursors(result, cursorEntries);
|
|
4270
|
-
return remapToGraphQLArrayOutput(result, tableName, table, relationMap);
|
|
4271
|
-
};
|
|
4272
|
-
/**
|
|
4273
|
-
* After a mutation, re-fetch the mutated rows through the relational query builder so the
|
|
4274
|
-
* selected relations are eagerly loaded in a single query, then merge those relations onto
|
|
4275
|
-
* the `.returning()` rows — making the per-field BatchLoader fallback unnecessary.
|
|
4276
|
-
*
|
|
4277
|
-
* `withParams` is the pre-computed relation selection (from extractRelationsParams); the
|
|
4278
|
-
* caller only invokes this when relations are actually selected, so it also gates whether
|
|
4279
|
-
* the PK was forced into RETURNING.
|
|
4280
|
-
*
|
|
4281
|
-
* Falls back to the original `.returning()` rows (relations then resolve via the
|
|
4282
|
-
* field-level BatchLoader) when the table has no RQB support, no primary key columns can be
|
|
4283
|
-
* determined, or the re-fetch fails. Supports single- and multi-column primary keys.
|
|
4284
|
-
*/
|
|
4285
|
-
const eagerLoadMutationRelations = async (db, tableName, rows, pkNames, withParams) => {
|
|
4286
|
-
if (!rows.length || !pkNames.length || !withParams || !Object.keys(withParams).length) return rows;
|
|
4287
|
-
const queryBase = db.query?.[tableName];
|
|
4288
|
-
if (!queryBase) return rows;
|
|
4289
|
-
const keyableRows = rows.filter((row) => pkNames.every((n) => row[n] != null));
|
|
4290
|
-
if (!keyableRows.length) return rows;
|
|
4291
|
-
const pkColumns = {};
|
|
4292
|
-
for (const pk of pkNames) pkColumns[pk] = true;
|
|
4293
|
-
const relationNames = Object.keys(withParams);
|
|
4294
|
-
const keyOf = (row) => JSON.stringify(pkNames.map((n) => typeof row[n] === "bigint" ? row[n].toString() : row[n]));
|
|
4295
|
-
let whereRaw;
|
|
4296
|
-
if (pkNames.length === 1) {
|
|
4297
|
-
const pkName = pkNames[0];
|
|
4298
|
-
const ids = keyableRows.map((r) => r[pkName]);
|
|
4299
|
-
whereRaw = (aliased) => (0, drizzle_orm.inArray)(aliased[pkName], ids);
|
|
4300
|
-
} else whereRaw = (aliased) => {
|
|
4301
|
-
const lhs = drizzle_orm.sql.join(pkNames.map((n) => drizzle_orm.sql`${aliased[n]}`), drizzle_orm.sql`, `);
|
|
4302
|
-
const tuples = drizzle_orm.sql.join(keyableRows.map((row) => drizzle_orm.sql`(${drizzle_orm.sql.join(pkNames.map((n) => drizzle_orm.sql`${row[n]}`), drizzle_orm.sql`, `)})`), drizzle_orm.sql`, `);
|
|
4303
|
-
return drizzle_orm.sql`(${lhs}) in (${tuples})`;
|
|
4304
|
-
};
|
|
4305
|
-
let enriched;
|
|
4306
|
-
try {
|
|
4307
|
-
enriched = await queryBase.findMany({
|
|
4308
|
-
columns: pkColumns,
|
|
4309
|
-
where: { RAW: whereRaw },
|
|
4310
|
-
with: withParams
|
|
4311
|
-
});
|
|
4312
|
-
} catch (err) {
|
|
4313
|
-
console.warn(`[drizzle-graphql] eager-loading relations for a "${tableName}" mutation failed; falling back to lazy resolution.`, err);
|
|
4314
|
-
return rows;
|
|
4315
|
-
}
|
|
4316
|
-
const byKey = new Map(enriched.map((e) => [keyOf(e), e]));
|
|
4317
|
-
for (const row of rows) {
|
|
4318
|
-
const match = byKey.get(keyOf(row));
|
|
4319
|
-
if (!match) continue;
|
|
4320
|
-
for (const rel of relationNames) row[rel] = match[rel];
|
|
4321
|
-
}
|
|
4322
|
-
return rows;
|
|
4323
|
-
};
|
|
4324
|
-
//#endregion
|
|
4325
4376
|
//#region src/util/builders/common/table-inputs.ts
|
|
4326
4377
|
const orderMap = /* @__PURE__ */ new WeakMap();
|
|
4327
4378
|
const generateTableOrderCached = (table, innerOrder) => {
|
|
@@ -6479,6 +6530,72 @@ const createSchemaBuilder = (adapter) => {
|
|
|
6479
6530
|
havingInput
|
|
6480
6531
|
};
|
|
6481
6532
|
};
|
|
6533
|
+
/**
|
|
6534
|
+
* The per-table loop, run once for every table the build covers.
|
|
6535
|
+
*
|
|
6536
|
+
* The dialect's callback receives the values the loop has already worked out — the feature
|
|
6537
|
+
* flags, the extension factory, the field names, the table's generated types and its
|
|
6538
|
+
* soft-delete config — and answers with the mutations it generated. Registering them, and
|
|
6539
|
+
* registering the inputs and outputs those mutations reference, happens here: both sides
|
|
6540
|
+
* did it identically, down to the `description` strings on the count mutations, so a gate
|
|
6541
|
+
* changed on one side and forgotten on the other used to be a silent dialect divergence.
|
|
6542
|
+
*
|
|
6543
|
+
* What stays in the dialect is what genuinely differs — which generators run, what each
|
|
6544
|
+
* mutation returns, and (on MySQL) that a write cannot report the rows it touched.
|
|
6545
|
+
*/
|
|
6546
|
+
const forEachTable = (ctx, perTable) => {
|
|
6547
|
+
const { schema, featureOf, softDeleteOf, typeNameMapper, gqlSchemaTypes, mutations, inputs, outputs } = ctx;
|
|
6548
|
+
const { prefixes, suffixes } = ctx.options;
|
|
6549
|
+
for (const [tableName, tableTypes] of Object.entries(gqlSchemaTypes)) {
|
|
6550
|
+
const tableFeatures = featureOf(tableName);
|
|
6551
|
+
const drizzleMeta = tableFieldExtensions(tableName, primaryKeyPropNames(schema[tableName]));
|
|
6552
|
+
const names = computeResolverFieldNames(tableName, typeNameMapper, prefixes, suffixes);
|
|
6553
|
+
const softDeleteInfo = softDeleteOf?.(tableName);
|
|
6554
|
+
const readTypes = addReadFields(ctx, tableName, names, tableTypes, drizzleMeta);
|
|
6555
|
+
const built = perTable({
|
|
6556
|
+
tableName,
|
|
6557
|
+
table: schema[tableName],
|
|
6558
|
+
tableFeatures,
|
|
6559
|
+
drizzleMeta,
|
|
6560
|
+
names,
|
|
6561
|
+
types: tableTypes,
|
|
6562
|
+
softDeleteInfo
|
|
6563
|
+
});
|
|
6564
|
+
for (const { generated, type, meta, description } of built.mutations) if (generated) defineRootField(mutations, "mutation", generated.name, {
|
|
6565
|
+
type,
|
|
6566
|
+
args: generated.args,
|
|
6567
|
+
resolve: generated.resolver,
|
|
6568
|
+
...meta ? { extensions: { drizzle: drizzleMeta({
|
|
6569
|
+
kind: "mutation",
|
|
6570
|
+
...meta
|
|
6571
|
+
}) } } : {},
|
|
6572
|
+
...description ? { description } : {}
|
|
6573
|
+
});
|
|
6574
|
+
const { insertInput, updateInput, tableFilters, tableOrder } = tableTypes.inputs;
|
|
6575
|
+
[
|
|
6576
|
+
...tableFeatures.insert || built.onConflictInput ? [insertInput] : [],
|
|
6577
|
+
...built.onConflictInput ? [built.onConflictInput] : [],
|
|
6578
|
+
...tableFeatures.update ? [updateInput] : [],
|
|
6579
|
+
...built.updateManyInput ? [built.updateManyInput] : [],
|
|
6580
|
+
tableFilters,
|
|
6581
|
+
tableOrder
|
|
6582
|
+
].forEach((e) => {
|
|
6583
|
+
inputs[e.name] = e;
|
|
6584
|
+
});
|
|
6585
|
+
const { selectSingleOutput } = tableTypes.outputs;
|
|
6586
|
+
outputs[selectSingleOutput.name] = selectSingleOutput;
|
|
6587
|
+
if (adapter.returnsRows) {
|
|
6588
|
+
const { singleTableItemOutput } = tableTypes.outputs;
|
|
6589
|
+
outputs[singleTableItemOutput.name] = singleTableItemOutput;
|
|
6590
|
+
}
|
|
6591
|
+
const { aggregateType, groupByType, havingInput } = readTypes;
|
|
6592
|
+
if (aggregateType) outputs[aggregateType.name] = aggregateType;
|
|
6593
|
+
if (groupByType && havingInput) {
|
|
6594
|
+
outputs[groupByType.name] = groupByType;
|
|
6595
|
+
inputs[havingInput.name] = havingInput;
|
|
6596
|
+
}
|
|
6597
|
+
}
|
|
6598
|
+
};
|
|
6482
6599
|
/** The relation field resolvers and the transaction roster, once every root field exists. */
|
|
6483
6600
|
const finalizeBuild = (ctx) => {
|
|
6484
6601
|
if (ctx.mutationTxCtx) for (const name of Object.keys(ctx.mutations)) ctx.mutationTxCtx.fieldNames.add(name);
|
|
@@ -6509,7 +6626,7 @@ const createSchemaBuilder = (adapter) => {
|
|
|
6509
6626
|
primaryKeyPropNames,
|
|
6510
6627
|
uniqueColumnSets,
|
|
6511
6628
|
prepareBuild,
|
|
6512
|
-
|
|
6629
|
+
forEachTable,
|
|
6513
6630
|
finalizeBuild
|
|
6514
6631
|
};
|
|
6515
6632
|
};
|
|
@@ -6607,7 +6724,14 @@ const generateUpdate = (db, tableName, table, setArgs, filterArgs, fieldName, si
|
|
|
6607
6724
|
if (!Object.keys(input).length) throw drizzleError("Unable to update with no values specified!", { code: "DRIZZLE_NO_VALUES" });
|
|
6608
6725
|
await before();
|
|
6609
6726
|
const relationCtx = relationFilterCtx(filterCtx, tableName);
|
|
6610
|
-
const filters =
|
|
6727
|
+
const filters = scopedWhere({
|
|
6728
|
+
scope,
|
|
6729
|
+
tableName,
|
|
6730
|
+
table,
|
|
6731
|
+
where,
|
|
6732
|
+
relationCtx,
|
|
6733
|
+
required: single || requireWhere
|
|
6734
|
+
});
|
|
6611
6735
|
if (single) await assertSingleMatch(executor, table, filters);
|
|
6612
6736
|
let query = executor.update(table).set(input);
|
|
6613
6737
|
if (filters) query = query.where(filters);
|
|
@@ -6689,7 +6813,15 @@ const generateDelete = (db, tableName, table, filterArgs, fieldName, single, req
|
|
|
6689
6813
|
const hard = canHardDelete && args.hard === true;
|
|
6690
6814
|
const scope = policies?.scope?.(context);
|
|
6691
6815
|
const relationCtx = relationFilterCtx(filterCtx, tableName);
|
|
6692
|
-
const filters =
|
|
6816
|
+
const filters = scopedWhere({
|
|
6817
|
+
scope,
|
|
6818
|
+
tableName,
|
|
6819
|
+
table,
|
|
6820
|
+
where,
|
|
6821
|
+
relationCtx,
|
|
6822
|
+
required: single || requireWhere,
|
|
6823
|
+
deleted: restore ? "ONLY" : hard ? "INCLUDE" : void 0
|
|
6824
|
+
});
|
|
6693
6825
|
if (single) await assertSingleMatch(executor, table, filters);
|
|
6694
6826
|
let query = softDelete && !hard ? executor.update(table).set({ [softDelete.columnName]: restore ? softDelete.writeRestored : softDelete.writeDeleted() }) : executor.delete(table);
|
|
6695
6827
|
if (filters) query = query.where(filters);
|
|
@@ -6703,7 +6835,7 @@ const generateDelete = (db, tableName, table, filterArgs, fieldName, single, req
|
|
|
6703
6835
|
const getTableConfig$2 = memoizeTableConfig(drizzle_orm_mysql_core.getTableConfig);
|
|
6704
6836
|
/** Primary-key property names for a MySQL table, including table-level composite keys. */
|
|
6705
6837
|
const mysqlPrimaryKeyPropNames = (table) => getPrimaryKeyPropNamesFromConfig(table, getTableConfig$2);
|
|
6706
|
-
const { prepareBuild,
|
|
6838
|
+
const { prepareBuild, forEachTable, finalizeBuild } = createSchemaBuilder({
|
|
6707
6839
|
tableClass: drizzle_orm_mysql_core.MySqlTable,
|
|
6708
6840
|
getTableConfig: getTableConfig$2,
|
|
6709
6841
|
primaryKeyPropNames: mysqlPrimaryKeyPropNames,
|
|
@@ -6714,9 +6846,9 @@ const { prepareBuild, addReadFields, finalizeBuild } = createSchemaBuilder({
|
|
|
6714
6846
|
}
|
|
6715
6847
|
});
|
|
6716
6848
|
const generateSchemaData$2 = (db, schema, relations, options) => {
|
|
6717
|
-
const { prefixes
|
|
6849
|
+
const { prefixes } = options;
|
|
6718
6850
|
const ctx = prepareBuild(db, schema, relations, options);
|
|
6719
|
-
const {
|
|
6851
|
+
const { anyTable, filterCtx, policies, cacheCtx, mutationTxCtx, outputs } = ctx;
|
|
6720
6852
|
const mutationReturnType = new graphql.GraphQLObjectType({
|
|
6721
6853
|
name: cacheCtx.typeName({
|
|
6722
6854
|
kind: "shared",
|
|
@@ -6730,15 +6862,9 @@ const generateSchemaData$2 = (db, schema, relations, options) => {
|
|
|
6730
6862
|
"update",
|
|
6731
6863
|
"delete"
|
|
6732
6864
|
].some((feature) => anyTable(feature))) outputs["MutationReturn"] = mutationReturnType;
|
|
6733
|
-
|
|
6734
|
-
const
|
|
6735
|
-
const drizzleMeta = tableFieldExtensions(tableName, mysqlPrimaryKeyPropNames(schema[tableName]));
|
|
6736
|
-
const { insertInput, updateInput, tableFilters, tableOrder } = tableTypes.inputs;
|
|
6737
|
-
const { selectSingleOutput } = tableTypes.outputs;
|
|
6738
|
-
const names = computeResolverFieldNames(tableName, typeNameMapper, prefixes, suffixes);
|
|
6865
|
+
forEachTable(ctx, ({ tableName, tableFeatures, names, types, softDeleteInfo }) => {
|
|
6866
|
+
const { insertInput, updateInput, tableFilters } = types.inputs;
|
|
6739
6867
|
const { typeName, createArrayFieldName, createSingleFieldName, upsertArrayFieldName, upsertSingleFieldName, updateFieldName, updateManyFieldName, updateSingleFieldName, updateCountFieldName, deleteFieldName, deleteSingleFieldName, restoreFieldName, restoreSingleFieldName, deleteCountFieldName } = names;
|
|
6740
|
-
const softDeleteInfo = softDeleteOf?.(tableName);
|
|
6741
|
-
const { aggregateType, groupByType, havingInput } = addReadFields(ctx, tableName, names, tableTypes, drizzleMeta);
|
|
6742
6868
|
const insertArrGenerated = tableFeatures.insert ? generateInsertArray(db, tableName, schema[tableName], insertInput, createArrayFieldName, mutationTxCtx, policies) : void 0;
|
|
6743
6869
|
const insertSingleGenerated = tableFeatures.insert ? generateInsertSingle(db, tableName, schema[tableName], insertInput, createSingleFieldName, mutationTxCtx, policies) : void 0;
|
|
6744
6870
|
const onConflictInput = tableFeatures.upsert ? generateOnConflictInput({
|
|
@@ -6791,103 +6917,85 @@ const generateSchemaData$2 = (db, schema, relations, options) => {
|
|
|
6791
6917
|
txCtx: mutationTxCtx
|
|
6792
6918
|
}) : void 0;
|
|
6793
6919
|
const hardDeleteMeta = softDeleteInfo?.hardDelete ? { hardDelete: true } : {};
|
|
6794
|
-
const
|
|
6795
|
-
|
|
6796
|
-
operation: "insert",
|
|
6797
|
-
single: false,
|
|
6798
|
-
targetArg: "values"
|
|
6799
|
-
}],
|
|
6800
|
-
[insertSingleGenerated, {
|
|
6801
|
-
operation: "insert",
|
|
6802
|
-
single: true,
|
|
6803
|
-
targetArg: "values"
|
|
6804
|
-
}],
|
|
6805
|
-
[upsertArrGenerated, {
|
|
6806
|
-
operation: "upsert",
|
|
6807
|
-
single: false,
|
|
6808
|
-
targetArg: "values"
|
|
6809
|
-
}],
|
|
6810
|
-
[upsertSingleGenerated, {
|
|
6811
|
-
operation: "upsert",
|
|
6812
|
-
single: true,
|
|
6813
|
-
targetArg: "values"
|
|
6814
|
-
}],
|
|
6815
|
-
[updateGenerated, {
|
|
6816
|
-
operation: "update",
|
|
6817
|
-
single: false,
|
|
6818
|
-
targetArg: "where"
|
|
6819
|
-
}],
|
|
6820
|
-
[updateManyGenerated, {
|
|
6821
|
-
operation: "updateMany",
|
|
6822
|
-
single: false,
|
|
6823
|
-
targetArg: "updates"
|
|
6824
|
-
}],
|
|
6825
|
-
[updateSingleGenerated, {
|
|
6826
|
-
operation: "update",
|
|
6827
|
-
single: true,
|
|
6828
|
-
targetArg: "where"
|
|
6829
|
-
}],
|
|
6830
|
-
[deleteGenerated, {
|
|
6831
|
-
operation: "delete",
|
|
6832
|
-
single: false,
|
|
6833
|
-
targetArg: "where",
|
|
6834
|
-
...hardDeleteMeta
|
|
6835
|
-
}],
|
|
6836
|
-
[deleteSingleGenerated, {
|
|
6837
|
-
operation: "delete",
|
|
6838
|
-
single: true,
|
|
6839
|
-
targetArg: "where",
|
|
6840
|
-
...hardDeleteMeta
|
|
6841
|
-
}],
|
|
6842
|
-
[restoreGenerated, {
|
|
6843
|
-
operation: "restore",
|
|
6844
|
-
single: false,
|
|
6845
|
-
targetArg: "where"
|
|
6846
|
-
}],
|
|
6847
|
-
[restoreSingleGenerated, {
|
|
6848
|
-
operation: "restore",
|
|
6849
|
-
single: true,
|
|
6850
|
-
targetArg: "where"
|
|
6851
|
-
}]
|
|
6852
|
-
];
|
|
6853
|
-
for (const [generated, meta] of generatedMutations) if (generated) defineRootField(mutations, "mutation", generated.name, {
|
|
6920
|
+
const withMeta = (generated, meta) => ({
|
|
6921
|
+
generated,
|
|
6854
6922
|
type: mutationReturnType,
|
|
6855
|
-
|
|
6856
|
-
resolve: generated.resolver,
|
|
6857
|
-
extensions: { drizzle: drizzleMeta({
|
|
6858
|
-
kind: "mutation",
|
|
6859
|
-
...meta
|
|
6860
|
-
}) }
|
|
6861
|
-
});
|
|
6862
|
-
if (updateCountGenerated) defineRootField(mutations, "mutation", updateCountGenerated.name, {
|
|
6863
|
-
type: new graphql.GraphQLNonNull(graphql.GraphQLInt),
|
|
6864
|
-
args: updateCountGenerated.args,
|
|
6865
|
-
resolve: updateCountGenerated.resolver,
|
|
6866
|
-
description: "How many rows the update touched. The rows themselves are not read back, which is the point of this mutation."
|
|
6923
|
+
meta
|
|
6867
6924
|
});
|
|
6868
|
-
|
|
6869
|
-
|
|
6870
|
-
|
|
6871
|
-
|
|
6872
|
-
|
|
6873
|
-
|
|
6874
|
-
|
|
6875
|
-
|
|
6876
|
-
|
|
6877
|
-
|
|
6878
|
-
|
|
6879
|
-
|
|
6880
|
-
|
|
6881
|
-
|
|
6882
|
-
|
|
6883
|
-
|
|
6884
|
-
|
|
6885
|
-
|
|
6886
|
-
|
|
6887
|
-
|
|
6888
|
-
|
|
6889
|
-
|
|
6890
|
-
|
|
6925
|
+
return {
|
|
6926
|
+
mutations: [
|
|
6927
|
+
withMeta(insertArrGenerated, {
|
|
6928
|
+
operation: "insert",
|
|
6929
|
+
single: false,
|
|
6930
|
+
targetArg: "values"
|
|
6931
|
+
}),
|
|
6932
|
+
withMeta(insertSingleGenerated, {
|
|
6933
|
+
operation: "insert",
|
|
6934
|
+
single: true,
|
|
6935
|
+
targetArg: "values"
|
|
6936
|
+
}),
|
|
6937
|
+
withMeta(upsertArrGenerated, {
|
|
6938
|
+
operation: "upsert",
|
|
6939
|
+
single: false,
|
|
6940
|
+
targetArg: "values"
|
|
6941
|
+
}),
|
|
6942
|
+
withMeta(upsertSingleGenerated, {
|
|
6943
|
+
operation: "upsert",
|
|
6944
|
+
single: true,
|
|
6945
|
+
targetArg: "values"
|
|
6946
|
+
}),
|
|
6947
|
+
withMeta(updateGenerated, {
|
|
6948
|
+
operation: "update",
|
|
6949
|
+
single: false,
|
|
6950
|
+
targetArg: "where"
|
|
6951
|
+
}),
|
|
6952
|
+
withMeta(updateManyGenerated, {
|
|
6953
|
+
operation: "updateMany",
|
|
6954
|
+
single: false,
|
|
6955
|
+
targetArg: "updates"
|
|
6956
|
+
}),
|
|
6957
|
+
withMeta(updateSingleGenerated, {
|
|
6958
|
+
operation: "update",
|
|
6959
|
+
single: true,
|
|
6960
|
+
targetArg: "where"
|
|
6961
|
+
}),
|
|
6962
|
+
withMeta(deleteGenerated, {
|
|
6963
|
+
operation: "delete",
|
|
6964
|
+
single: false,
|
|
6965
|
+
targetArg: "where",
|
|
6966
|
+
...hardDeleteMeta
|
|
6967
|
+
}),
|
|
6968
|
+
withMeta(deleteSingleGenerated, {
|
|
6969
|
+
operation: "delete",
|
|
6970
|
+
single: true,
|
|
6971
|
+
targetArg: "where",
|
|
6972
|
+
...hardDeleteMeta
|
|
6973
|
+
}),
|
|
6974
|
+
withMeta(restoreGenerated, {
|
|
6975
|
+
operation: "restore",
|
|
6976
|
+
single: false,
|
|
6977
|
+
targetArg: "where"
|
|
6978
|
+
}),
|
|
6979
|
+
withMeta(restoreSingleGenerated, {
|
|
6980
|
+
operation: "restore",
|
|
6981
|
+
single: true,
|
|
6982
|
+
targetArg: "where"
|
|
6983
|
+
}),
|
|
6984
|
+
{
|
|
6985
|
+
generated: updateCountGenerated,
|
|
6986
|
+
type: new graphql.GraphQLNonNull(graphql.GraphQLInt),
|
|
6987
|
+
description: "How many rows the update touched. The rows themselves are not read back, which is the point of this mutation."
|
|
6988
|
+
},
|
|
6989
|
+
{
|
|
6990
|
+
generated: deleteCountGenerated,
|
|
6991
|
+
type: new graphql.GraphQLNonNull(graphql.GraphQLInt),
|
|
6992
|
+
description: "How many rows the delete removed. The rows themselves are not read back, which is the point of this mutation."
|
|
6993
|
+
}
|
|
6994
|
+
],
|
|
6995
|
+
onConflictInput,
|
|
6996
|
+
updateManyInput
|
|
6997
|
+
};
|
|
6998
|
+
});
|
|
6891
6999
|
return finalizeBuild(ctx);
|
|
6892
7000
|
};
|
|
6893
7001
|
//#endregion
|
|
@@ -6897,28 +7005,37 @@ const generateSchemaData$2 = (db, schema, relations, options) => {
|
|
|
6897
7005
|
* result, so the generator call sites read exactly as they did when the functions were local.
|
|
6898
7006
|
*/
|
|
6899
7007
|
const buildWriteResolvers = (primaryKeyPropNames) => {
|
|
6900
|
-
|
|
6901
|
-
|
|
7008
|
+
/**
|
|
7009
|
+
* `insert<Table>` / `insert<Table>Single` — one generator for both shapes, the way
|
|
7010
|
+
* {@link generateUpsert} handles its pair. `single` decides the argument type, whether the
|
|
7011
|
+
* result is remapped as a row or a list, and whether an empty return is an error; the write
|
|
7012
|
+
* itself is the same multi-row statement either way, with the single variant supplying a
|
|
7013
|
+
* one-element list.
|
|
7014
|
+
*/
|
|
7015
|
+
const generateInsert = (build, opts) => {
|
|
7016
|
+
const { db, tables, relationMap, typeNameMapper, txCtx, nested, limits, policies, resolveName } = build;
|
|
7017
|
+
const { tableName, table, baseType, fieldName, typeName, single, conflictDoNothing = false } = opts;
|
|
7018
|
+
const queryArgs = { values: { type: single ? new graphql.GraphQLNonNull(baseType) : new graphql.GraphQLNonNull(new graphql.GraphQLList(new graphql.GraphQLNonNull(baseType))) } };
|
|
6902
7019
|
const pkNames = primaryKeyPropNames(table);
|
|
6903
7020
|
return writeResolver({
|
|
6904
7021
|
db,
|
|
6905
7022
|
tableName,
|
|
6906
7023
|
operation: "insert",
|
|
6907
|
-
single
|
|
7024
|
+
single,
|
|
6908
7025
|
fieldName,
|
|
6909
7026
|
txCtx,
|
|
6910
7027
|
policies,
|
|
6911
7028
|
args: queryArgs,
|
|
6912
7029
|
run: async ({ executor, args, context, info, before, after }) => {
|
|
6913
|
-
|
|
7030
|
+
const supplied = single ? [args.values] : args.values;
|
|
7031
|
+
if (!supplied.length) throw drizzleError("No values were provided!", { code: "DRIZZLE_NO_VALUES" });
|
|
6914
7032
|
await before();
|
|
6915
|
-
const entries = nested?.enabled(tableName) ?
|
|
7033
|
+
const entries = nested?.enabled(tableName) ? supplied.map((values) => nested.split(tableName, values)) : void 0;
|
|
6916
7034
|
const nestedEntries = entries?.some((entry) => nested.hasOps(entry.ops)) ? entries : void 0;
|
|
6917
7035
|
const contextColumns = policies?.contextValues?.(tableName);
|
|
6918
7036
|
const scope = policies?.scope?.(context);
|
|
6919
|
-
const input = nestedEntries ? [] : applyContextValuesAll(remapFromGraphQLArrayInput(entries ? entries.map((entry) => entry.columns) :
|
|
6920
|
-
const
|
|
6921
|
-
const { columns, hasRelations, withParams } = prepareMutationRelationColumns({
|
|
7037
|
+
const input = nestedEntries ? [] : applyContextValuesAll(remapFromGraphQLArrayInput(entries ? entries.map((entry) => entry.columns) : supplied, table), contextColumns, context);
|
|
7038
|
+
const { columns, hasRelations, withParams } = mutationSelection(info, {
|
|
6922
7039
|
relationMap,
|
|
6923
7040
|
tables,
|
|
6924
7041
|
tableName,
|
|
@@ -6927,7 +7044,6 @@ const buildWriteResolvers = (primaryKeyPropNames) => {
|
|
|
6927
7044
|
resolveName,
|
|
6928
7045
|
table,
|
|
6929
7046
|
pkNames,
|
|
6930
|
-
parsedInfo,
|
|
6931
7047
|
limits,
|
|
6932
7048
|
scope,
|
|
6933
7049
|
defaultOrderBy: policies?.defaultOrderBy
|
|
@@ -6948,67 +7064,12 @@ const buildWriteResolvers = (primaryKeyPropNames) => {
|
|
|
6948
7064
|
context
|
|
6949
7065
|
}) : await runInsert(executor, input);
|
|
6950
7066
|
await after(result);
|
|
6951
|
-
|
|
6952
|
-
return remapToGraphQLArrayOutput(enriched, tableName, table, relationMap);
|
|
6953
|
-
}
|
|
6954
|
-
});
|
|
6955
|
-
};
|
|
6956
|
-
const generateInsertSingle = (db, tableName, table, tables, relationMap, baseType, fieldName, typeName, typeNameMapper, conflictDoNothing = false, txCtx, nested, limits, policies, resolveName) => {
|
|
6957
|
-
const queryArgs = { values: { type: new graphql.GraphQLNonNull(baseType) } };
|
|
6958
|
-
const pkNames = primaryKeyPropNames(table);
|
|
6959
|
-
return writeResolver({
|
|
6960
|
-
db,
|
|
6961
|
-
tableName,
|
|
6962
|
-
operation: "insert",
|
|
6963
|
-
single: true,
|
|
6964
|
-
fieldName,
|
|
6965
|
-
txCtx,
|
|
6966
|
-
policies,
|
|
6967
|
-
args: queryArgs,
|
|
6968
|
-
run: async ({ executor, args, context, info, before, after }) => {
|
|
6969
|
-
await before();
|
|
6970
|
-
const entry = nested?.enabled(tableName) ? nested.split(tableName, args.values) : void 0;
|
|
6971
|
-
const nestedEntry = entry && nested.hasOps(entry.ops) ? entry : void 0;
|
|
6972
|
-
const contextColumns = policies?.contextValues?.(tableName);
|
|
6973
|
-
const scope = policies?.scope?.(context);
|
|
6974
|
-
const input = nestedEntry ? {} : applyContextValues(remapFromGraphQLSingleInput(entry ? entry.columns : args.values, table), contextColumns, context);
|
|
6975
|
-
const parsedInfo = parseResolveInfo(info, { deep: true });
|
|
6976
|
-
const { columns, hasRelations, withParams } = prepareMutationRelationColumns({
|
|
6977
|
-
relationMap,
|
|
6978
|
-
tables,
|
|
6979
|
-
tableName,
|
|
6980
|
-
typeName,
|
|
6981
|
-
typeNameMapper,
|
|
6982
|
-
resolveName,
|
|
6983
|
-
table,
|
|
6984
|
-
pkNames,
|
|
6985
|
-
parsedInfo,
|
|
6986
|
-
limits,
|
|
6987
|
-
scope,
|
|
6988
|
-
defaultOrderBy: policies?.defaultOrderBy
|
|
6989
|
-
});
|
|
6990
|
-
const returning = nestedEntry ? nested.withJoinColumns(tableName, nestedEntry.ops, { ...columns }, table) : columns;
|
|
6991
|
-
const runInsert = async (target, values) => {
|
|
6992
|
-
let query = target.insert(table).values(values).returning(returning);
|
|
6993
|
-
if (conflictDoNothing) query = query.onConflictDoNothing();
|
|
6994
|
-
return await query;
|
|
6995
|
-
};
|
|
6996
|
-
const result = nestedEntry ? await writeWithNestedOps({
|
|
6997
|
-
executor,
|
|
6998
|
-
runtime: nested,
|
|
6999
|
-
tableName,
|
|
7000
|
-
entries: [nestedEntry],
|
|
7001
|
-
remapValues: (values) => applyContextValues(remapFromGraphQLSingleInput(values, table), contextColumns, context),
|
|
7002
|
-
write: runInsert,
|
|
7003
|
-
context
|
|
7004
|
-
}) : await runInsert(executor, input);
|
|
7005
|
-
await after(result);
|
|
7006
|
-
if (!result[0]) {
|
|
7067
|
+
if (single && !result[0]) {
|
|
7007
7068
|
if (!conflictDoNothing) throw drizzleError("The insert returned no row.", { code: "DRIZZLE_NO_ROW_RETURNED" });
|
|
7008
7069
|
return;
|
|
7009
7070
|
}
|
|
7010
|
-
const enriched =
|
|
7011
|
-
return remapToGraphQLSingleOutput(enriched[0], tableName, table, relationMap);
|
|
7071
|
+
const enriched = await withEagerRelations(executor, tableName, result, pkNames, withParams, hasRelations);
|
|
7072
|
+
return single ? remapToGraphQLSingleOutput(enriched[0], tableName, table, relationMap) : remapToGraphQLArrayOutput(enriched, tableName, table, relationMap);
|
|
7012
7073
|
}
|
|
7013
7074
|
});
|
|
7014
7075
|
};
|
|
@@ -7018,7 +7079,9 @@ const buildWriteResolvers = (primaryKeyPropNames) => {
|
|
|
7018
7079
|
*
|
|
7019
7080
|
* Shares the insert input: an upsert supplies a whole row, same as a create.
|
|
7020
7081
|
*/
|
|
7021
|
-
const generateUpsert = (
|
|
7082
|
+
const generateUpsert = (build, opts) => {
|
|
7083
|
+
const { db, tables, relationMap, typeNameMapper, filterCtx, txCtx, nested, limits, policies, resolveName } = build;
|
|
7084
|
+
const { tableName, table, baseType, onConflictType, uniqueSets, fieldName, typeName, single } = opts;
|
|
7022
7085
|
const queryArgs = {
|
|
7023
7086
|
values: { type: single ? new graphql.GraphQLNonNull(baseType) : new graphql.GraphQLNonNull(new graphql.GraphQLList(new graphql.GraphQLNonNull(baseType))) },
|
|
7024
7087
|
onConflict: {
|
|
@@ -7045,8 +7108,7 @@ const buildWriteResolvers = (primaryKeyPropNames) => {
|
|
|
7045
7108
|
const contextColumns = policies?.contextValues?.(tableName);
|
|
7046
7109
|
const scope = policies?.scope?.(context);
|
|
7047
7110
|
const input = nestedEntries ? [] : applyContextValuesAll(remapFromGraphQLArrayInput(entries ? entries.map((entry) => entry.columns) : supplied, table), contextColumns, context);
|
|
7048
|
-
const
|
|
7049
|
-
const { columns, hasRelations, withParams } = prepareMutationRelationColumns({
|
|
7111
|
+
const { columns, hasRelations, withParams } = mutationSelection(info, {
|
|
7050
7112
|
relationMap,
|
|
7051
7113
|
tables,
|
|
7052
7114
|
tableName,
|
|
@@ -7055,7 +7117,6 @@ const buildWriteResolvers = (primaryKeyPropNames) => {
|
|
|
7055
7117
|
resolveName,
|
|
7056
7118
|
table,
|
|
7057
7119
|
pkNames,
|
|
7058
|
-
parsedInfo,
|
|
7059
7120
|
limits,
|
|
7060
7121
|
scope,
|
|
7061
7122
|
defaultOrderBy: policies?.defaultOrderBy
|
|
@@ -7092,12 +7153,14 @@ const buildWriteResolvers = (primaryKeyPropNames) => {
|
|
|
7092
7153
|
}) : await runUpsert(executor, input);
|
|
7093
7154
|
await after(result);
|
|
7094
7155
|
if (single && !result[0]) return;
|
|
7095
|
-
const enriched =
|
|
7156
|
+
const enriched = await withEagerRelations(executor, tableName, result, pkNames, withParams, hasRelations);
|
|
7096
7157
|
return single ? remapToGraphQLSingleOutput(enriched[0], tableName, table, relationMap) : remapToGraphQLArrayOutput(enriched, tableName, table, relationMap);
|
|
7097
7158
|
}
|
|
7098
7159
|
});
|
|
7099
7160
|
};
|
|
7100
|
-
const generateUpdate = (
|
|
7161
|
+
const generateUpdate = (build, opts) => {
|
|
7162
|
+
const { db, tables, relationMap, typeNameMapper, filterCtx, txCtx, nested, limits, policies, resolveName } = build;
|
|
7163
|
+
const { tableName, table, setArgs, filterArgs, fieldName, typeName, single, requireWhere } = opts;
|
|
7101
7164
|
const queryArgs = {
|
|
7102
7165
|
set: { type: new graphql.GraphQLNonNull(setArgs) },
|
|
7103
7166
|
where: { type: single || requireWhere ? new graphql.GraphQLNonNull(filterArgs) : filterArgs }
|
|
@@ -7116,8 +7179,7 @@ const buildWriteResolvers = (primaryKeyPropNames) => {
|
|
|
7116
7179
|
const { where, set } = args;
|
|
7117
7180
|
const scope = policies?.scope?.(context);
|
|
7118
7181
|
await before();
|
|
7119
|
-
const
|
|
7120
|
-
const { columns, hasRelations, withParams } = prepareMutationRelationColumns({
|
|
7182
|
+
const { columns, hasRelations, withParams } = mutationSelection(info, {
|
|
7121
7183
|
relationMap,
|
|
7122
7184
|
tables,
|
|
7123
7185
|
tableName,
|
|
@@ -7126,7 +7188,6 @@ const buildWriteResolvers = (primaryKeyPropNames) => {
|
|
|
7126
7188
|
resolveName,
|
|
7127
7189
|
table,
|
|
7128
7190
|
pkNames,
|
|
7129
|
-
parsedInfo,
|
|
7130
7191
|
limits,
|
|
7131
7192
|
scope,
|
|
7132
7193
|
defaultOrderBy: policies?.defaultOrderBy
|
|
@@ -7136,7 +7197,14 @@ const buildWriteResolvers = (primaryKeyPropNames) => {
|
|
|
7136
7197
|
const input = stripContextValues(remapUpdateInput(entry ? entry.columns : set, table, tableName), policies?.contextValues?.(tableName));
|
|
7137
7198
|
if (!Object.keys(input).length && !nestedOps) throw drizzleError("Unable to update with no values specified!", { code: "DRIZZLE_NO_VALUES" });
|
|
7138
7199
|
const relationCtx = relationFilterCtx(filterCtx, tableName);
|
|
7139
|
-
const filters =
|
|
7200
|
+
const filters = scopedWhere({
|
|
7201
|
+
scope,
|
|
7202
|
+
tableName,
|
|
7203
|
+
table,
|
|
7204
|
+
where,
|
|
7205
|
+
relationCtx,
|
|
7206
|
+
required: single || requireWhere
|
|
7207
|
+
});
|
|
7140
7208
|
if (single) await assertSingleMatch(executor, table, filters);
|
|
7141
7209
|
const returning = nestedOps ? nested.withJoinColumns(tableName, nestedOps, { ...columns }, table) : columns;
|
|
7142
7210
|
const runUpdate = async (target, values) => {
|
|
@@ -7158,7 +7226,7 @@ const buildWriteResolvers = (primaryKeyPropNames) => {
|
|
|
7158
7226
|
await after(result);
|
|
7159
7227
|
if (single && result.length > 1) throw drizzleError("'where' matched more than one row!", { code: "DRIZZLE_MULTI_ROW_MATCH" });
|
|
7160
7228
|
if (single && !result[0]) return;
|
|
7161
|
-
const enriched =
|
|
7229
|
+
const enriched = await withEagerRelations(executor, tableName, result, pkNames, withParams, hasRelations);
|
|
7162
7230
|
return single ? remapToGraphQLSingleOutput(enriched[0], tableName, table, relationMap) : remapToGraphQLArrayOutput(enriched, tableName, table, relationMap);
|
|
7163
7231
|
}
|
|
7164
7232
|
});
|
|
@@ -7175,7 +7243,10 @@ const buildWriteResolvers = (primaryKeyPropNames) => {
|
|
|
7175
7243
|
* `DELETE` — emptying the trash, reclaiming a unique key. It reads at `INCLUDE`, since the
|
|
7176
7244
|
* rows it mostly exists to remove are the ones already marked; a `scope` still confines it.
|
|
7177
7245
|
*/
|
|
7178
|
-
const generateDelete = (
|
|
7246
|
+
const generateDelete = (build, opts) => {
|
|
7247
|
+
const { db, filterCtx, txCtx, policies, resolveName } = build;
|
|
7248
|
+
const { tableName, table, filterArgs, fieldName, typeName, single, requireWhere, selectionCtx } = opts;
|
|
7249
|
+
const restore = opts.restore ?? false;
|
|
7179
7250
|
const softDelete = policies?.softDelete?.(tableName);
|
|
7180
7251
|
const operation = restore ? "restore" : "delete";
|
|
7181
7252
|
const canHardDelete = !restore && softDelete?.hardDelete === true;
|
|
@@ -7200,7 +7271,15 @@ const buildWriteResolvers = (primaryKeyPropNames) => {
|
|
|
7200
7271
|
const parsedInfo = parseResolveInfo(info, { deep: true });
|
|
7201
7272
|
const columns = extractSelectedColumnsFromTreeSQLFormat(parsedInfo.fieldsByTypeName[applyObjectTypeName(typeName, tableName, resolveName)], table, selectionCtx);
|
|
7202
7273
|
const relationCtx = relationFilterCtx(filterCtx, tableName);
|
|
7203
|
-
const filters =
|
|
7274
|
+
const filters = scopedWhere({
|
|
7275
|
+
scope,
|
|
7276
|
+
tableName,
|
|
7277
|
+
table,
|
|
7278
|
+
where,
|
|
7279
|
+
relationCtx,
|
|
7280
|
+
required: single || requireWhere,
|
|
7281
|
+
deleted: restore ? "ONLY" : hard ? "INCLUDE" : void 0
|
|
7282
|
+
});
|
|
7204
7283
|
if (single) await assertSingleMatch(executor, table, filters);
|
|
7205
7284
|
let query = softDelete && !hard ? executor.update(table).set({ [softDelete.columnName]: restore ? softDelete.writeRestored : softDelete.writeDeleted() }) : executor.delete(table);
|
|
7206
7285
|
if (filters) query = query.where(filters);
|
|
@@ -7214,8 +7293,7 @@ const buildWriteResolvers = (primaryKeyPropNames) => {
|
|
|
7214
7293
|
});
|
|
7215
7294
|
};
|
|
7216
7295
|
return {
|
|
7217
|
-
|
|
7218
|
-
generateInsertSingle,
|
|
7296
|
+
generateInsert,
|
|
7219
7297
|
generateUpsert,
|
|
7220
7298
|
generateUpdate,
|
|
7221
7299
|
generateDelete
|
|
@@ -7232,23 +7310,46 @@ const buildWriteResolvers = (primaryKeyPropNames) => {
|
|
|
7232
7310
|
const createSchemaDataGenerator = (adapter) => {
|
|
7233
7311
|
const primaryKeyPropNames = adapter.primaryKeyPropNames;
|
|
7234
7312
|
const uniqueColumnSets = (table) => getUniqueColumnSets(table, adapter.getTableConfig);
|
|
7235
|
-
const {
|
|
7236
|
-
const { prepareBuild,
|
|
7313
|
+
const { generateInsert, generateUpsert, generateUpdate, generateDelete } = buildWriteResolvers(primaryKeyPropNames);
|
|
7314
|
+
const { prepareBuild, forEachTable, finalizeBuild } = createSchemaBuilder(adapter);
|
|
7237
7315
|
return (db, schema, relations, options) => {
|
|
7238
|
-
const { prefixes,
|
|
7316
|
+
const { prefixes, conflictDoNothing, typeNameMapper, limits } = options;
|
|
7239
7317
|
const ctx = prepareBuild(db, schema, relations, options);
|
|
7240
|
-
const { tables,
|
|
7241
|
-
|
|
7242
|
-
|
|
7243
|
-
|
|
7244
|
-
|
|
7245
|
-
|
|
7246
|
-
|
|
7318
|
+
const { tables, eagerRelations, filterCtx, policies, namedRelations, cacheCtx, nestedRuntime, mutationTxCtx } = ctx;
|
|
7319
|
+
const writeBuild = {
|
|
7320
|
+
db,
|
|
7321
|
+
tables,
|
|
7322
|
+
relationMap: eagerRelations,
|
|
7323
|
+
typeNameMapper,
|
|
7324
|
+
filterCtx,
|
|
7325
|
+
txCtx: mutationTxCtx,
|
|
7326
|
+
nested: nestedRuntime,
|
|
7327
|
+
limits,
|
|
7328
|
+
policies,
|
|
7329
|
+
resolveName: cacheCtx.typeName
|
|
7330
|
+
};
|
|
7331
|
+
forEachTable(ctx, ({ tableName, tableFeatures, names, types, softDeleteInfo }) => {
|
|
7332
|
+
const { insertInput, updateInput, tableFilters } = types.inputs;
|
|
7333
|
+
const { singleTableItemOutput, arrTableItemOutput } = types.outputs;
|
|
7247
7334
|
const { typeName, createArrayFieldName, createSingleFieldName, upsertArrayFieldName, upsertSingleFieldName, updateFieldName, updateManyFieldName, updateSingleFieldName, updateCountFieldName, deleteFieldName, deleteSingleFieldName, restoreFieldName, restoreSingleFieldName, deleteCountFieldName } = names;
|
|
7248
|
-
const
|
|
7249
|
-
|
|
7250
|
-
|
|
7251
|
-
|
|
7335
|
+
const insertArrGenerated = tableFeatures.insert ? generateInsert(writeBuild, {
|
|
7336
|
+
tableName,
|
|
7337
|
+
table: schema[tableName],
|
|
7338
|
+
baseType: insertInput,
|
|
7339
|
+
fieldName: createArrayFieldName,
|
|
7340
|
+
typeName,
|
|
7341
|
+
single: false,
|
|
7342
|
+
conflictDoNothing
|
|
7343
|
+
}) : void 0;
|
|
7344
|
+
const insertSingleGenerated = tableFeatures.insert ? generateInsert(writeBuild, {
|
|
7345
|
+
tableName,
|
|
7346
|
+
table: schema[tableName],
|
|
7347
|
+
baseType: insertInput,
|
|
7348
|
+
fieldName: createSingleFieldName,
|
|
7349
|
+
typeName,
|
|
7350
|
+
single: true,
|
|
7351
|
+
conflictDoNothing
|
|
7352
|
+
}) : void 0;
|
|
7252
7353
|
const uniqueSets = tableFeatures.upsert ? uniqueColumnSets(schema[tableName]) : [];
|
|
7253
7354
|
const onConflictInput = tableFeatures.upsert ? generateOnConflictInput({
|
|
7254
7355
|
table: schema[tableName],
|
|
@@ -7259,10 +7360,46 @@ const createSchemaDataGenerator = (adapter) => {
|
|
|
7259
7360
|
withTarget: true,
|
|
7260
7361
|
cacheCtx
|
|
7261
7362
|
}) : void 0;
|
|
7262
|
-
const upsertArrGenerated = onConflictInput ? generateUpsert(
|
|
7263
|
-
|
|
7264
|
-
|
|
7265
|
-
|
|
7363
|
+
const upsertArrGenerated = onConflictInput ? generateUpsert(writeBuild, {
|
|
7364
|
+
tableName,
|
|
7365
|
+
table: schema[tableName],
|
|
7366
|
+
baseType: insertInput,
|
|
7367
|
+
onConflictType: onConflictInput,
|
|
7368
|
+
uniqueSets,
|
|
7369
|
+
fieldName: upsertArrayFieldName,
|
|
7370
|
+
typeName,
|
|
7371
|
+
single: false
|
|
7372
|
+
}) : void 0;
|
|
7373
|
+
const upsertSingleGenerated = onConflictInput ? generateUpsert(writeBuild, {
|
|
7374
|
+
tableName,
|
|
7375
|
+
table: schema[tableName],
|
|
7376
|
+
baseType: insertInput,
|
|
7377
|
+
onConflictType: onConflictInput,
|
|
7378
|
+
uniqueSets,
|
|
7379
|
+
fieldName: upsertSingleFieldName,
|
|
7380
|
+
typeName,
|
|
7381
|
+
single: true
|
|
7382
|
+
}) : void 0;
|
|
7383
|
+
const updateGenerated = tableFeatures.update ? generateUpdate(writeBuild, {
|
|
7384
|
+
tableName,
|
|
7385
|
+
table: schema[tableName],
|
|
7386
|
+
setArgs: updateInput,
|
|
7387
|
+
filterArgs: tableFilters,
|
|
7388
|
+
fieldName: updateFieldName,
|
|
7389
|
+
typeName,
|
|
7390
|
+
single: false,
|
|
7391
|
+
requireWhere: tableFeatures.requireWhere
|
|
7392
|
+
}) : void 0;
|
|
7393
|
+
const updateSingleGenerated = tableFeatures.update ? generateUpdate(writeBuild, {
|
|
7394
|
+
tableName,
|
|
7395
|
+
table: schema[tableName],
|
|
7396
|
+
setArgs: updateInput,
|
|
7397
|
+
filterArgs: tableFilters,
|
|
7398
|
+
fieldName: updateSingleFieldName,
|
|
7399
|
+
typeName,
|
|
7400
|
+
single: true,
|
|
7401
|
+
requireWhere: tableFeatures.requireWhere
|
|
7402
|
+
}) : void 0;
|
|
7266
7403
|
const updateManyInput = tableFeatures.update && tableFeatures.updateMany ? generateUpdateManyInput({
|
|
7267
7404
|
tableName,
|
|
7268
7405
|
typeName,
|
|
@@ -7271,27 +7408,73 @@ const createSchemaDataGenerator = (adapter) => {
|
|
|
7271
7408
|
tableFilters,
|
|
7272
7409
|
cacheCtx
|
|
7273
7410
|
}) : void 0;
|
|
7274
|
-
const updateManyGenerated = updateManyInput ? adapter.generateUpdateMany(
|
|
7275
|
-
|
|
7411
|
+
const updateManyGenerated = updateManyInput ? adapter.generateUpdateMany(writeBuild, {
|
|
7412
|
+
tableName,
|
|
7413
|
+
table: schema[tableName],
|
|
7414
|
+
updateManyInput,
|
|
7415
|
+
fieldName: updateManyFieldName,
|
|
7416
|
+
typeName
|
|
7417
|
+
}) : void 0;
|
|
7418
|
+
const deleteGenerated = tableFeatures.delete ? generateDelete(writeBuild, {
|
|
7276
7419
|
tableName,
|
|
7277
|
-
|
|
7278
|
-
|
|
7279
|
-
|
|
7280
|
-
|
|
7420
|
+
table: schema[tableName],
|
|
7421
|
+
filterArgs: tableFilters,
|
|
7422
|
+
fieldName: deleteFieldName,
|
|
7423
|
+
typeName,
|
|
7424
|
+
single: false,
|
|
7425
|
+
requireWhere: tableFeatures.requireWhere,
|
|
7426
|
+
restore: false,
|
|
7427
|
+
selectionCtx: {
|
|
7428
|
+
tableName,
|
|
7429
|
+
relationMap: namedRelations,
|
|
7430
|
+
tables
|
|
7431
|
+
}
|
|
7432
|
+
}) : void 0;
|
|
7433
|
+
const deleteSingleGenerated = tableFeatures.delete ? generateDelete(writeBuild, {
|
|
7281
7434
|
tableName,
|
|
7282
|
-
|
|
7283
|
-
|
|
7284
|
-
|
|
7285
|
-
|
|
7435
|
+
table: schema[tableName],
|
|
7436
|
+
filterArgs: tableFilters,
|
|
7437
|
+
fieldName: deleteSingleFieldName,
|
|
7438
|
+
typeName,
|
|
7439
|
+
single: true,
|
|
7440
|
+
requireWhere: tableFeatures.requireWhere,
|
|
7441
|
+
restore: false,
|
|
7442
|
+
selectionCtx: {
|
|
7443
|
+
tableName,
|
|
7444
|
+
relationMap: namedRelations,
|
|
7445
|
+
tables
|
|
7446
|
+
}
|
|
7447
|
+
}) : void 0;
|
|
7448
|
+
const restoreGenerated = softDeleteInfo ? generateDelete(writeBuild, {
|
|
7286
7449
|
tableName,
|
|
7287
|
-
|
|
7288
|
-
|
|
7289
|
-
|
|
7290
|
-
|
|
7450
|
+
table: schema[tableName],
|
|
7451
|
+
filterArgs: tableFilters,
|
|
7452
|
+
fieldName: restoreFieldName,
|
|
7453
|
+
typeName,
|
|
7454
|
+
single: false,
|
|
7455
|
+
requireWhere: tableFeatures.requireWhere,
|
|
7456
|
+
restore: true,
|
|
7457
|
+
selectionCtx: {
|
|
7458
|
+
tableName,
|
|
7459
|
+
relationMap: namedRelations,
|
|
7460
|
+
tables
|
|
7461
|
+
}
|
|
7462
|
+
}) : void 0;
|
|
7463
|
+
const restoreSingleGenerated = softDeleteInfo ? generateDelete(writeBuild, {
|
|
7291
7464
|
tableName,
|
|
7292
|
-
|
|
7293
|
-
|
|
7294
|
-
|
|
7465
|
+
table: schema[tableName],
|
|
7466
|
+
filterArgs: tableFilters,
|
|
7467
|
+
fieldName: restoreSingleFieldName,
|
|
7468
|
+
typeName,
|
|
7469
|
+
single: true,
|
|
7470
|
+
requireWhere: tableFeatures.requireWhere,
|
|
7471
|
+
restore: true,
|
|
7472
|
+
selectionCtx: {
|
|
7473
|
+
tableName,
|
|
7474
|
+
relationMap: namedRelations,
|
|
7475
|
+
tables
|
|
7476
|
+
}
|
|
7477
|
+
}) : void 0;
|
|
7295
7478
|
const updateCountGenerated = tableFeatures.update && tableFeatures.countMutations ? generateWriteCount({
|
|
7296
7479
|
db,
|
|
7297
7480
|
tableName,
|
|
@@ -7316,160 +7499,126 @@ const createSchemaDataGenerator = (adapter) => {
|
|
|
7316
7499
|
filterCtx,
|
|
7317
7500
|
txCtx: mutationTxCtx
|
|
7318
7501
|
}) : void 0;
|
|
7319
|
-
|
|
7320
|
-
|
|
7321
|
-
|
|
7322
|
-
|
|
7323
|
-
|
|
7324
|
-
|
|
7325
|
-
|
|
7326
|
-
|
|
7327
|
-
|
|
7328
|
-
|
|
7329
|
-
|
|
7330
|
-
|
|
7331
|
-
|
|
7332
|
-
|
|
7333
|
-
|
|
7334
|
-
|
|
7335
|
-
|
|
7336
|
-
|
|
7337
|
-
|
|
7338
|
-
|
|
7339
|
-
|
|
7340
|
-
|
|
7341
|
-
|
|
7342
|
-
|
|
7343
|
-
|
|
7344
|
-
|
|
7345
|
-
|
|
7346
|
-
|
|
7347
|
-
|
|
7348
|
-
|
|
7349
|
-
|
|
7350
|
-
|
|
7351
|
-
|
|
7352
|
-
|
|
7353
|
-
|
|
7354
|
-
|
|
7355
|
-
|
|
7356
|
-
|
|
7357
|
-
|
|
7358
|
-
|
|
7359
|
-
|
|
7360
|
-
|
|
7361
|
-
|
|
7362
|
-
|
|
7363
|
-
|
|
7364
|
-
|
|
7365
|
-
|
|
7366
|
-
|
|
7367
|
-
|
|
7368
|
-
|
|
7369
|
-
|
|
7370
|
-
|
|
7371
|
-
|
|
7372
|
-
|
|
7373
|
-
|
|
7374
|
-
|
|
7375
|
-
|
|
7376
|
-
|
|
7377
|
-
|
|
7378
|
-
|
|
7379
|
-
|
|
7380
|
-
|
|
7381
|
-
|
|
7382
|
-
|
|
7383
|
-
|
|
7384
|
-
|
|
7385
|
-
|
|
7386
|
-
|
|
7387
|
-
|
|
7388
|
-
|
|
7389
|
-
|
|
7390
|
-
|
|
7391
|
-
|
|
7392
|
-
|
|
7393
|
-
|
|
7394
|
-
|
|
7395
|
-
|
|
7396
|
-
|
|
7397
|
-
|
|
7398
|
-
|
|
7399
|
-
|
|
7400
|
-
|
|
7401
|
-
|
|
7402
|
-
|
|
7403
|
-
|
|
7404
|
-
|
|
7405
|
-
|
|
7406
|
-
|
|
7407
|
-
|
|
7408
|
-
|
|
7409
|
-
|
|
7410
|
-
|
|
7411
|
-
|
|
7412
|
-
|
|
7413
|
-
|
|
7414
|
-
|
|
7415
|
-
|
|
7416
|
-
|
|
7417
|
-
|
|
7418
|
-
|
|
7419
|
-
|
|
7420
|
-
|
|
7421
|
-
|
|
7422
|
-
|
|
7423
|
-
|
|
7424
|
-
|
|
7425
|
-
|
|
7426
|
-
|
|
7427
|
-
|
|
7428
|
-
|
|
7429
|
-
|
|
7430
|
-
|
|
7431
|
-
|
|
7432
|
-
|
|
7433
|
-
|
|
7434
|
-
|
|
7435
|
-
|
|
7436
|
-
|
|
7437
|
-
|
|
7438
|
-
|
|
7439
|
-
single: true,
|
|
7440
|
-
targetArg: "where"
|
|
7441
|
-
}) }
|
|
7442
|
-
});
|
|
7443
|
-
if (updateCountGenerated) defineRootField(mutations, "mutation", updateCountGenerated.name, {
|
|
7444
|
-
type: new graphql.GraphQLNonNull(graphql.GraphQLInt),
|
|
7445
|
-
args: updateCountGenerated.args,
|
|
7446
|
-
resolve: updateCountGenerated.resolver,
|
|
7447
|
-
description: "How many rows the update touched. The rows themselves are not read back, which is the point of this mutation."
|
|
7448
|
-
});
|
|
7449
|
-
if (deleteCountGenerated) defineRootField(mutations, "mutation", deleteCountGenerated.name, {
|
|
7450
|
-
type: new graphql.GraphQLNonNull(graphql.GraphQLInt),
|
|
7451
|
-
args: deleteCountGenerated.args,
|
|
7452
|
-
resolve: deleteCountGenerated.resolver,
|
|
7453
|
-
description: "How many rows the delete removed. The rows themselves are not read back, which is the point of this mutation."
|
|
7454
|
-
});
|
|
7455
|
-
[
|
|
7456
|
-
...tableFeatures.insert || onConflictInput ? [insertInput] : [],
|
|
7457
|
-
...onConflictInput ? [onConflictInput] : [],
|
|
7458
|
-
...tableFeatures.update ? [updateInput] : [],
|
|
7459
|
-
...updateManyInput ? [updateManyInput] : [],
|
|
7460
|
-
tableFilters,
|
|
7461
|
-
tableOrder
|
|
7462
|
-
].forEach((e) => {
|
|
7463
|
-
inputs[e.name] = e;
|
|
7464
|
-
});
|
|
7465
|
-
outputs[selectSingleOutput.name] = selectSingleOutput;
|
|
7466
|
-
outputs[singleTableItemOutput.name] = singleTableItemOutput;
|
|
7467
|
-
if (aggregateType) outputs[aggregateType.name] = aggregateType;
|
|
7468
|
-
if (groupByType && havingInput) {
|
|
7469
|
-
outputs[groupByType.name] = groupByType;
|
|
7470
|
-
inputs[havingInput.name] = havingInput;
|
|
7471
|
-
}
|
|
7472
|
-
}
|
|
7502
|
+
const hardDeleteMeta = softDeleteInfo?.hardDelete ? { hardDelete: true } : {};
|
|
7503
|
+
return {
|
|
7504
|
+
mutations: [
|
|
7505
|
+
{
|
|
7506
|
+
generated: insertArrGenerated,
|
|
7507
|
+
type: arrTableItemOutput,
|
|
7508
|
+
meta: {
|
|
7509
|
+
operation: "insert",
|
|
7510
|
+
single: false,
|
|
7511
|
+
targetArg: "values"
|
|
7512
|
+
}
|
|
7513
|
+
},
|
|
7514
|
+
{
|
|
7515
|
+
generated: insertSingleGenerated,
|
|
7516
|
+
type: conflictDoNothing ? singleTableItemOutput : new graphql.GraphQLNonNull(singleTableItemOutput),
|
|
7517
|
+
meta: {
|
|
7518
|
+
operation: "insert",
|
|
7519
|
+
single: true,
|
|
7520
|
+
targetArg: "values"
|
|
7521
|
+
}
|
|
7522
|
+
},
|
|
7523
|
+
{
|
|
7524
|
+
generated: upsertArrGenerated,
|
|
7525
|
+
type: arrTableItemOutput,
|
|
7526
|
+
meta: {
|
|
7527
|
+
operation: "upsert",
|
|
7528
|
+
single: false,
|
|
7529
|
+
targetArg: "values"
|
|
7530
|
+
}
|
|
7531
|
+
},
|
|
7532
|
+
{
|
|
7533
|
+
generated: upsertSingleGenerated,
|
|
7534
|
+
type: singleTableItemOutput,
|
|
7535
|
+
meta: {
|
|
7536
|
+
operation: "upsert",
|
|
7537
|
+
single: true,
|
|
7538
|
+
targetArg: "values"
|
|
7539
|
+
}
|
|
7540
|
+
},
|
|
7541
|
+
{
|
|
7542
|
+
generated: updateGenerated,
|
|
7543
|
+
type: arrTableItemOutput,
|
|
7544
|
+
meta: {
|
|
7545
|
+
operation: "update",
|
|
7546
|
+
single: false,
|
|
7547
|
+
targetArg: "where"
|
|
7548
|
+
}
|
|
7549
|
+
},
|
|
7550
|
+
{
|
|
7551
|
+
generated: updateManyGenerated,
|
|
7552
|
+
type: new graphql.GraphQLNonNull(new graphql.GraphQLList(singleTableItemOutput)),
|
|
7553
|
+
meta: {
|
|
7554
|
+
operation: "updateMany",
|
|
7555
|
+
single: false,
|
|
7556
|
+
targetArg: "updates"
|
|
7557
|
+
},
|
|
7558
|
+
description: "Each entry's updated rows, in entry order. An entry whose `where` matched no rows contributes `null` in its slot; an entry that matched several contributes each of its rows."
|
|
7559
|
+
},
|
|
7560
|
+
{
|
|
7561
|
+
generated: updateSingleGenerated,
|
|
7562
|
+
type: singleTableItemOutput,
|
|
7563
|
+
meta: {
|
|
7564
|
+
operation: "update",
|
|
7565
|
+
single: true,
|
|
7566
|
+
targetArg: "where"
|
|
7567
|
+
}
|
|
7568
|
+
},
|
|
7569
|
+
{
|
|
7570
|
+
generated: deleteGenerated,
|
|
7571
|
+
type: arrTableItemOutput,
|
|
7572
|
+
meta: {
|
|
7573
|
+
operation: "delete",
|
|
7574
|
+
single: false,
|
|
7575
|
+
targetArg: "where",
|
|
7576
|
+
...hardDeleteMeta
|
|
7577
|
+
}
|
|
7578
|
+
},
|
|
7579
|
+
{
|
|
7580
|
+
generated: deleteSingleGenerated,
|
|
7581
|
+
type: singleTableItemOutput,
|
|
7582
|
+
meta: {
|
|
7583
|
+
operation: "delete",
|
|
7584
|
+
single: true,
|
|
7585
|
+
targetArg: "where",
|
|
7586
|
+
...hardDeleteMeta
|
|
7587
|
+
}
|
|
7588
|
+
},
|
|
7589
|
+
{
|
|
7590
|
+
generated: restoreGenerated,
|
|
7591
|
+
type: arrTableItemOutput,
|
|
7592
|
+
meta: {
|
|
7593
|
+
operation: "restore",
|
|
7594
|
+
single: false,
|
|
7595
|
+
targetArg: "where"
|
|
7596
|
+
}
|
|
7597
|
+
},
|
|
7598
|
+
{
|
|
7599
|
+
generated: restoreSingleGenerated,
|
|
7600
|
+
type: singleTableItemOutput,
|
|
7601
|
+
meta: {
|
|
7602
|
+
operation: "restore",
|
|
7603
|
+
single: true,
|
|
7604
|
+
targetArg: "where"
|
|
7605
|
+
}
|
|
7606
|
+
},
|
|
7607
|
+
{
|
|
7608
|
+
generated: updateCountGenerated,
|
|
7609
|
+
type: new graphql.GraphQLNonNull(graphql.GraphQLInt),
|
|
7610
|
+
description: "How many rows the update touched. The rows themselves are not read back, which is the point of this mutation."
|
|
7611
|
+
},
|
|
7612
|
+
{
|
|
7613
|
+
generated: deleteCountGenerated,
|
|
7614
|
+
type: new graphql.GraphQLNonNull(graphql.GraphQLInt),
|
|
7615
|
+
description: "How many rows the delete removed. The rows themselves are not read back, which is the point of this mutation."
|
|
7616
|
+
}
|
|
7617
|
+
],
|
|
7618
|
+
onConflictInput,
|
|
7619
|
+
updateManyInput
|
|
7620
|
+
};
|
|
7621
|
+
});
|
|
7473
7622
|
return finalizeBuild(ctx);
|
|
7474
7623
|
};
|
|
7475
7624
|
};
|
|
@@ -7511,7 +7660,9 @@ const runUpdateManyBatch = ({ executor, entries, table, returning, nested, table
|
|
|
7511
7660
|
* @param runBatch how the entries execute; defaults to {@link runUpdateManyBatch}
|
|
7512
7661
|
*/
|
|
7513
7662
|
const createUpdateManyGenerator = (primaryKeyPropNames, runBatch = runUpdateManyBatch) => {
|
|
7514
|
-
return (
|
|
7663
|
+
return (build, opts) => {
|
|
7664
|
+
const { db, tables, relationMap, typeNameMapper, filterCtx, txCtx, nested, limits, policies, resolveName } = build;
|
|
7665
|
+
const { tableName, table, updateManyInput, fieldName, typeName } = opts;
|
|
7515
7666
|
const queryArgs = { updates: { type: new graphql.GraphQLNonNull(new graphql.GraphQLList(new graphql.GraphQLNonNull(updateManyInput))) } };
|
|
7516
7667
|
const pkNames = primaryKeyPropNames(table);
|
|
7517
7668
|
return writeResolver({
|
|
@@ -7529,8 +7680,7 @@ const createUpdateManyGenerator = (primaryKeyPropNames, runBatch = runUpdateMany
|
|
|
7529
7680
|
await before();
|
|
7530
7681
|
const scope = policies?.scope?.(context);
|
|
7531
7682
|
const contextColumns = policies?.contextValues?.(tableName);
|
|
7532
|
-
const
|
|
7533
|
-
const { columns, hasRelations, withParams } = prepareMutationRelationColumns({
|
|
7683
|
+
const { columns, hasRelations, withParams } = mutationSelection(info, {
|
|
7534
7684
|
relationMap,
|
|
7535
7685
|
tables,
|
|
7536
7686
|
tableName,
|
|
@@ -7539,7 +7689,6 @@ const createUpdateManyGenerator = (primaryKeyPropNames, runBatch = runUpdateMany
|
|
|
7539
7689
|
resolveName,
|
|
7540
7690
|
table,
|
|
7541
7691
|
pkNames,
|
|
7542
|
-
parsedInfo,
|
|
7543
7692
|
limits,
|
|
7544
7693
|
scope,
|
|
7545
7694
|
defaultOrderBy: policies?.defaultOrderBy
|
|
@@ -7556,11 +7705,12 @@ const createUpdateManyGenerator = (primaryKeyPropNames, runBatch = runUpdateMany
|
|
|
7556
7705
|
};
|
|
7557
7706
|
});
|
|
7558
7707
|
const anyNested = entries.some((entry) => entry.ops);
|
|
7708
|
+
const returning = anyNested ? nested.withJoinColumns(tableName, mergedOps(entries.map((entry) => ({ ops: entry.ops ?? {} }))), { ...columns }, table) : columns;
|
|
7559
7709
|
const perEntry = await runBatch({
|
|
7560
7710
|
executor,
|
|
7561
7711
|
entries,
|
|
7562
7712
|
table,
|
|
7563
|
-
returning
|
|
7713
|
+
returning,
|
|
7564
7714
|
anyNested,
|
|
7565
7715
|
nested,
|
|
7566
7716
|
tableName,
|
|
@@ -7568,7 +7718,7 @@ const createUpdateManyGenerator = (primaryKeyPropNames, runBatch = runUpdateMany
|
|
|
7568
7718
|
});
|
|
7569
7719
|
const flatRows = perEntry.flat();
|
|
7570
7720
|
await after(flatRows);
|
|
7571
|
-
const enriched =
|
|
7721
|
+
const enriched = await withEagerRelations(executor, tableName, flatRows, pkNames, withParams, hasRelations);
|
|
7572
7722
|
const output = [];
|
|
7573
7723
|
let offset = 0;
|
|
7574
7724
|
for (const rows of perEntry) {
|