@vantreeseba/drizzle-graphql 11.0.0 → 11.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -3792,6 +3792,154 @@ const identifyRows = (field, args) => {
3792
3792
  };
3793
3793
  };
3794
3794
  //#endregion
3795
+ //#region src/util/parse-resolve-info.ts
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;
3815
+ /**
3816
+ * The runtime variable values, in the plain-map form the directive check wants. graphql 17
3817
+ * wraps them in `{ sources, coerced }`; graphql 16 hands over the map itself.
3818
+ */
3819
+ const coercedVariables = (info) => {
3820
+ const values = info.variableValues;
3821
+ if (values && typeof values === "object" && "coerced" in values) return values.coerced ?? {};
3822
+ return values ?? {};
3823
+ };
3824
+ /** The value of a `@skip` / `@include` `if:` argument — a literal or a variable reference. */
3825
+ const directiveIfValue = (info, value) => {
3826
+ if (value.kind === graphql.Kind.VARIABLE) return coercedVariables(info)[value.name.value];
3827
+ if (value.kind === graphql.Kind.BOOLEAN) return value.value;
3828
+ };
3829
+ /**
3830
+ * Whether `@skip` / `@include` on this selection exclude it. Applies to fields, fragment
3831
+ * spreads and inline fragments alike, which is where graphql puts them.
3832
+ */
3833
+ const isExcluded = (info, node) => {
3834
+ for (const directive of node.directives ?? []) {
3835
+ const directiveName = directive.name.value;
3836
+ if (directiveName !== "skip" && directiveName !== "include") continue;
3837
+ const ifArgument = directive.arguments?.find((argument) => argument.name.value === "if");
3838
+ if (!ifArgument) continue;
3839
+ const value = directiveIfValue(info, ifArgument.value);
3840
+ if (directiveName === "skip" ? !!value : !value) return true;
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
3795
3943
  //#region src/util/builders/common/relation-resolvers.ts
3796
3944
  /**
3797
3945
  * Fetches a to-many relation with per-parent limit/offset for ALL parents in a
@@ -3804,8 +3952,8 @@ const identifyRows = (field, args) => {
3804
3952
  * Window functions require PostgreSQL, MySQL >= 8.0, or SQLite >= 3.25.
3805
3953
  * Returns raw rows (NOT remapped); the caller groups + remaps them.
3806
3954
  */
3807
- const batchedPaginatedRelationQuery = async (db, targetTable, foreignCol, whereCondition, orderByArg, limit, offset, pkNames, orderCtx, whereArgs) => {
3808
- const cols = (0, drizzle_orm.getColumns)(targetTable);
3955
+ const batchedPaginatedRelationQuery = async (db, targetTable, foreignCol, whereCondition, orderByArg, limit, offset, pkNames, columns, orderCtx, whereArgs) => {
3956
+ const cols = columns ?? (0, drizzle_orm.getColumns)(targetTable);
3809
3957
  const orderExprs = [...orderByArg ? extractOrderBy(targetTable, orderByArg, orderCtx, whereArgs) : [], ...primaryKeyOrderExprs(targetTable, pkNames)];
3810
3958
  const orderClause = orderExprs.length ? drizzle_orm.sql` order by ${drizzle_orm.sql.join(orderExprs, drizzle_orm.sql`, `)}` : drizzle_orm.sql``;
3811
3959
  const RN = "__drizzle_graphql_rn";
@@ -3851,12 +3999,39 @@ const createRelationResolverFactory = (db, tables, nullOrdering, filterCtx, limi
3851
3999
  operation: "relation",
3852
4000
  relation: relationName
3853
4001
  };
4002
+ /**
4003
+ * The columns one batch reads: the ones the selection names, plus the ones the batch needs
4004
+ * whatever the client asked for — the key it groups the rows by, and the ordering columns a
4005
+ * row cursor is encoded from. Nested relation fields contribute their own join column
4006
+ * through the selection context, the same way the root select path narrows.
4007
+ *
4008
+ * `undefined` means "every column", which is what a resolver with no readable resolve info
4009
+ * has to fall back to.
4010
+ */
4011
+ const selectedColumns = (info, cursorEntries) => {
4012
+ const parsed = info ? parseResolveInfo(info) : void 0;
4013
+ if (!parsed) return;
4014
+ const tree = Object.assign({}, ...Object.values(parsed.fieldsByTypeName));
4015
+ const columns = extractSelectedColumnsFromTreeSQLFormat(tree, targetTable, {
4016
+ tableName: targetTableName,
4017
+ relationMap: filterCtx?.relationMap ?? {},
4018
+ tables,
4019
+ allRelations: filterCtx?.relationMap
4020
+ });
4021
+ const targetColumns = (0, drizzle_orm.getColumns)(targetTable);
4022
+ columns[foreignColPropName] = foreignCol;
4023
+ for (const [columnName] of cursorEntries ?? []) {
4024
+ const column = targetColumns[columnName];
4025
+ if (column) columns[columnName] = column;
4026
+ }
4027
+ return columns;
4028
+ };
3854
4029
  const resolve = async (parent, args, context, info) => {
3855
4030
  if (parent[relationName] !== void 0) return parent[relationName];
3856
4031
  const localValue = parent[localColPropName];
3857
4032
  if (localValue == null) return isOne ? null : [];
3858
4033
  const { where: whereArg, limit: requestedLimit, offset, after, deleted } = args ?? {};
3859
- const { orderByArg, limit, distinct, cursorEntries, cursorValues, loaderKey } = getOrCreateRequestValue(context, info?.fieldNodes?.[0], `relation:${tableName}::${relationName}`, () => {
4034
+ const { orderByArg, limit, distinct, cursorEntries, cursorValues, columns, loaderKey } = getOrCreateRequestValue(context, info?.fieldNodes?.[0], `relation:${tableName}::${relationName}`, () => {
3860
4035
  const orderByArg = isOne ? args?.orderBy : withDefaultOrderBy(args ?? {}, targetTableName, policies?.defaultOrderBy).orderBy;
3861
4036
  const limit = applyLimitPolicy(requestedLimit, limitPolicy, errorCtx);
3862
4037
  const distinct = (args ?? {}).distinct?.length ? (args ?? {}).distinct : void 0;
@@ -3871,6 +4046,7 @@ const createRelationResolverFactory = (db, tables, nullOrdering, filterCtx, limi
3871
4046
  } else cursorEntries = cursorOrderingEntries(orderByArg, targetPkNames);
3872
4047
  }
3873
4048
  const cursorValues = after != null && cursorEntries ? decodeCursor(after, cursorEntries) : void 0;
4049
+ const columns = selectedColumns(info, cursorEntries);
3874
4050
  const argsKey = JSON.stringify({
3875
4051
  where: whereArg ?? null,
3876
4052
  orderBy: orderByArg ?? null,
@@ -3879,7 +4055,8 @@ const createRelationResolverFactory = (db, tables, nullOrdering, filterCtx, limi
3879
4055
  deleted: deleted ?? defaultDeleted ?? null,
3880
4056
  after: after ?? null,
3881
4057
  distinct: distinct ?? null,
3882
- cursor: cursorEntries ? 1 : 0
4058
+ cursor: cursorEntries ? 1 : 0,
4059
+ columns: columns ? Object.keys(columns).sort() : null
3883
4060
  });
3884
4061
  return {
3885
4062
  orderByArg,
@@ -3887,6 +4064,7 @@ const createRelationResolverFactory = (db, tables, nullOrdering, filterCtx, limi
3887
4064
  distinct,
3888
4065
  cursorEntries,
3889
4066
  cursorValues,
4067
+ columns,
3890
4068
  loaderKey: `${tableName}::${relationName}::${argsKey}`
3891
4069
  };
3892
4070
  });
@@ -3910,9 +4088,9 @@ const createRelationResolverFactory = (db, tables, nullOrdering, filterCtx, limi
3910
4088
  whereCondition = (0, drizzle_orm.and)(whereCondition, primaryKeyRestriction(targetTable, targetPkNames, keys));
3911
4089
  }
3912
4090
  let rows;
3913
- if (limit != null || offset != null) rows = await batchedPaginatedRelationQuery(executor, targetTable, foreignCol, whereCondition, orderByArg, limit ?? null, offset ?? null, targetPkNames, relationFilterCtx(filterCtx, targetTableName), whereArg);
4091
+ if (limit != null || offset != null) rows = await batchedPaginatedRelationQuery(executor, targetTable, foreignCol, whereCondition, orderByArg, limit ?? null, offset ?? null, targetPkNames, columns, relationFilterCtx(filterCtx, targetTableName), whereArg);
3914
4092
  else {
3915
- let q = executor.select().from(targetTable).where(whereCondition);
4093
+ let q = (columns ? executor.select(columns) : executor.select()).from(targetTable).where(whereCondition);
3916
4094
  const orderExprs = cursorEntries ? cursorOrderExprs(targetTable, cursorEntries) : orderByArg ? extractOrderBy(targetTable, orderByArg, relationFilterCtx(filterCtx, targetTableName), whereArg) : [];
3917
4095
  if (orderExprs.length) q = q.orderBy(...orderExprs);
3918
4096
  rows = await q;
@@ -4927,154 +5105,6 @@ const resolveBuildConfig = (config, schema) => {
4927
5105
  };
4928
5106
  };
4929
5107
  //#endregion
4930
- //#region src/util/parse-resolve-info.ts
4931
- /** Unwraps `[T]!`-style wrappers down to the named type. `getNamedType` without the realm tie. */
4932
- const namedTypeOf = (type) => {
4933
- let current = type;
4934
- while (current?.ofType) current = current.ofType;
4935
- return current;
4936
- };
4937
- /**
4938
- * Whether a field's named output type has sub-selections. Object types and interfaces expose
4939
- * `getFields`, unions expose `getTypes`; scalars and enums expose neither. Input object types
4940
- * also expose `getFields`, but an input type can never be a field's output type, so there is
4941
- * nothing here for that to collide with.
4942
- */
4943
- const isCompositeLike = (type) => !!type && (typeof type.getFields === "function" || typeof type.getTypes === "function");
4944
- /**
4945
- * The fields declared on a type, or `undefined` when it has none to declare. A union is the
4946
- * case that matters: it has no fields of its own, so a selection directly against one resolves
4947
- * to nothing and is dropped — only the inline fragments underneath contribute.
4948
- */
4949
- const fieldsOf = (type) => typeof type?.getFields === "function" ? type.getFields() : void 0;
4950
- /**
4951
- * The runtime variable values, in the plain-map form the directive check wants. graphql 17
4952
- * wraps them in `{ sources, coerced }`; graphql 16 hands over the map itself.
4953
- */
4954
- const coercedVariables = (info) => {
4955
- const values = info.variableValues;
4956
- if (values && typeof values === "object" && "coerced" in values) return values.coerced ?? {};
4957
- return values ?? {};
4958
- };
4959
- /** The value of a `@skip` / `@include` `if:` argument — a literal or a variable reference. */
4960
- const directiveIfValue = (info, value) => {
4961
- if (value.kind === graphql.Kind.VARIABLE) return coercedVariables(info)[value.name.value];
4962
- if (value.kind === graphql.Kind.BOOLEAN) return value.value;
4963
- };
4964
- /**
4965
- * Whether `@skip` / `@include` on this selection exclude it. Applies to fields, fragment
4966
- * spreads and inline fragments alike, which is where graphql puts them.
4967
- */
4968
- const isExcluded = (info, node) => {
4969
- for (const directive of node.directives ?? []) {
4970
- const directiveName = directive.name.value;
4971
- if (directiveName !== "skip" && directiveName !== "include") continue;
4972
- const ifArgument = directive.arguments?.find((argument) => argument.name.value === "if");
4973
- if (!ifArgument) continue;
4974
- const value = directiveIfValue(info, ifArgument.value);
4975
- if (directiveName === "skip" ? !!value : !value) return true;
4976
- }
4977
- return false;
4978
- };
4979
- /** The type a fragment's `on Type` condition names, looked up in the executing schema. */
4980
- const typeFromCondition = (info, condition) => info.schema.getType(condition.name.value);
4981
- /**
4982
- * Introspection meta-fields are not schema fields and never reach the database. `__typename` is
4983
- * the one that turns up in practice; `__id` is excluded from the rule because a schema is free
4984
- * to declare a field by that name.
4985
- */
4986
- const isMetaField = (name) => name.startsWith("__") && name !== "__id";
4987
- /**
4988
- * Merges one selection set into `tree`, which is a `fieldsByTypeName` map — the caller owns the
4989
- * level, this owns the entries under `parentType`'s name.
4990
- *
4991
- * `activeFragments` is the set of fragment names on the current spread path. A document that
4992
- * passed validation cannot contain a fragment cycle, but this parser also runs on trees a
4993
- * caller assembled by hand, and an unbounded walk would hang rather than fail.
4994
- */
4995
- const collectSelections = (selections, info, tree, parentType, deep, activeFragments) => {
4996
- const parentTypeName = parentType?.name;
4997
- if (!parentTypeName) return tree;
4998
- let fields = tree[parentTypeName];
4999
- if (!fields) {
5000
- fields = {};
5001
- tree[parentTypeName] = fields;
5002
- }
5003
- const parentFields = fieldsOf(parentType);
5004
- for (const selection of selections) {
5005
- if (isExcluded(info, selection)) continue;
5006
- if (selection.kind === graphql.Kind.FIELD) {
5007
- const name = selection.name.value;
5008
- if (isMetaField(name)) continue;
5009
- const fieldDef = parentFields?.[name];
5010
- if (!fieldDef) continue;
5011
- const fieldType = namedTypeOf(fieldDef.type);
5012
- if (!fieldType) continue;
5013
- const composite = isCompositeLike(fieldType);
5014
- const alias = selection.alias?.value || name;
5015
- let node = fields[alias];
5016
- if (!node) {
5017
- node = {
5018
- name,
5019
- alias,
5020
- args: { ...(0, graphql.getArgumentValues)(fieldDef, selection, info.variableValues) },
5021
- fieldsByTypeName: composite && fieldType.name ? { [fieldType.name]: {} } : {}
5022
- };
5023
- fields[alias] = node;
5024
- }
5025
- if (selection.selectionSet && deep && composite) collectSelections(selection.selectionSet.selections, info, node.fieldsByTypeName, fieldType, deep, activeFragments);
5026
- continue;
5027
- }
5028
- if (!deep) continue;
5029
- if (selection.kind === graphql.Kind.FRAGMENT_SPREAD) {
5030
- const fragmentName = selection.name.value;
5031
- const fragment = info.fragments?.[fragmentName];
5032
- if (!fragment) throw new Error(`Drizzle-GraphQL Error: unknown fragment '${fragmentName}' — it is spread in the query but not defined in the document.`);
5033
- if (activeFragments.has(fragmentName)) continue;
5034
- const fragmentType = fragment.typeCondition ? typeFromCondition(info, fragment.typeCondition) : parentType;
5035
- if (!isCompositeLike(fragmentType)) continue;
5036
- activeFragments.add(fragmentName);
5037
- try {
5038
- collectSelections(fragment.selectionSet.selections, info, tree, fragmentType, deep, activeFragments);
5039
- } finally {
5040
- activeFragments.delete(fragmentName);
5041
- }
5042
- continue;
5043
- }
5044
- if (selection.kind === graphql.Kind.INLINE_FRAGMENT) {
5045
- const fragmentType = selection.typeCondition ? typeFromCondition(info, selection.typeCondition) : parentType;
5046
- if (!isCompositeLike(fragmentType)) continue;
5047
- collectSelections(selection.selectionSet.selections, info, tree, fragmentType, deep, activeFragments);
5048
- }
5049
- }
5050
- return tree;
5051
- };
5052
- /** The first key of an object, without materializing the whole key array to discard it. */
5053
- const firstKey = (object) => {
5054
- for (const key in object) return key;
5055
- };
5056
- /**
5057
- * Reads the selection under a resolver's own field out of its `info`, as a tree of
5058
- * {@link ResolveTree} nodes.
5059
- *
5060
- * ```ts
5061
- * const parsed = parseResolveInfo(info, { deep: true })!;
5062
- * const selected = parsed.fieldsByTypeName['Users']; // keyed by response key
5063
- * ```
5064
- *
5065
- * Returns `undefined` when the resolver's field produced no node at all, which in practice
5066
- * means `info` carried no field nodes.
5067
- */
5068
- const parseResolveInfo = (info, options = {}) => {
5069
- const fieldNodes = info.fieldNodes;
5070
- if (!fieldNodes) throw new Error("Drizzle-GraphQL Error: resolve info carries no fieldNodes, so no selection could be read from it.");
5071
- const tree = collectSelections(fieldNodes, info, {}, info.parentType, options.deep ?? true, /* @__PURE__ */ new Set());
5072
- const typeKey = firstKey(tree);
5073
- const fields = typeKey === void 0 ? void 0 : tree[typeKey];
5074
- const fieldKey = fields && firstKey(fields);
5075
- return fieldKey === void 0 ? void 0 : fields?.[fieldKey];
5076
- };
5077
- //#endregion
5078
5108
  //#region src/util/builders/aggregates.ts
5079
5109
  /** Operations that aggregate over a set of column values. `count` is handled separately (whole rows). */
5080
5110
  const AGGREGATE_OPS = [
@@ -5364,17 +5394,22 @@ const createRelationAggregateFactory = (db, tables, cacheCtx, typeNameMapper, fi
5364
5394
  };
5365
5395
  const resolve = async (parent, args, context, info) => {
5366
5396
  try {
5367
- const request = parseAggregateRequest(info, target);
5397
+ const { request, loaderKey } = getOrCreateRequestValue(context, info?.fieldNodes?.[0], `aggregate:${tableName}::${relationName}`, () => {
5398
+ const request = parseAggregateRequest(info, target);
5399
+ return {
5400
+ request,
5401
+ loaderKey: `${tableName}::${relationName}::aggregate::${JSON.stringify({
5402
+ where: args?.where ?? null,
5403
+ deleted: args?.deleted ?? defaultDeleted ?? null,
5404
+ selection: Object.keys(request.selection).sort()
5405
+ })}`
5406
+ };
5407
+ });
5368
5408
  if (!Object.keys(request.selection).length) return {};
5369
5409
  const localValue = parent[localColPropName];
5370
5410
  if (localValue == null) return assembleAggregateRow({}, request, target);
5371
5411
  const whereArg = args?.where;
5372
5412
  const deleted = args?.deleted;
5373
- const loaderKey = `${tableName}::${relationName}::aggregate::${JSON.stringify({
5374
- where: whereArg ?? null,
5375
- deleted: deleted ?? defaultDeleted ?? null,
5376
- selection: Object.keys(request.selection).sort()
5377
- })}`;
5378
5413
  const loader = getOrCreateLoader(context, loaderKey, async (parentIds) => {
5379
5414
  const executor = resolveExecutor(db, context);
5380
5415
  const uniqueIds = [...new Set(parentIds)];
@@ -5954,7 +5989,7 @@ const createNestedWriteRuntime = (params) => {
5954
5989
  };
5955
5990
  /** The values the junction stores for the target rows one operand selects. */
5956
5991
  const selectedTargetKeys = async (executor, plan, filter, operation, scope) => {
5957
- return (await executor.select().from(plan.targetTable).where(conditionOf(plan, filter, operation, scope))).map((row) => row[plan.foreignColPropName]).filter((value) => value !== void 0 && value !== null);
5992
+ return (await executor.select({ [plan.foreignColPropName]: plan.foreignCol }).from(plan.targetTable).where(conditionOf(plan, filter, operation, scope))).map((row) => row[plan.foreignColPropName]).filter((value) => value !== void 0 && value !== null);
5958
5993
  };
5959
5994
  /**
5960
5995
  * The link half of a many-to-many write: junction rows are inserted and deleted, and both
@@ -5969,7 +6004,7 @@ const createNestedWriteRuntime = (params) => {
5969
6004
  const through = plan.through;
5970
6005
  const unlinkAll = () => executor.delete(through.table).where((0, drizzle_orm.eq)(through.sourceCol, key));
5971
6006
  const link = async (operands, operation) => {
5972
- const existing = new Set((await executor.select().from(through.table).where((0, drizzle_orm.eq)(through.sourceCol, key))).map((row) => row[through.targetColPropName]));
6007
+ const existing = new Set((await executor.select({ [through.targetColPropName]: through.targetCol }).from(through.table).where((0, drizzle_orm.eq)(through.sourceCol, key))).map((row) => row[through.targetColPropName]));
5973
6008
  const rows = [];
5974
6009
  for (const filter of operands) for (const targetKey of await selectedTargetKeys(executor, plan, filter, operation, scope)) if (!existing.has(targetKey)) {
5975
6010
  existing.add(targetKey);
@@ -6030,11 +6065,11 @@ const createNestedWriteRuntime = (params) => {
6030
6065
  assertSingleOperation(plan, op);
6031
6066
  if (op["create"] !== void 0) {
6032
6067
  const values = applyContextValues(remapFromGraphQLSingleInput({ ...op["create"] }, plan.targetTable), contextValues?.(plan.targetTableName), context);
6033
- const created = (await executor.insert(plan.targetTable).values(values).returning())[0];
6068
+ const created = (await executor.insert(plan.targetTable).values(values).returning({ [plan.foreignColPropName]: plan.foreignCol }))[0];
6034
6069
  if (!created) throw drizzleError(`Drizzle-GraphQL Error: 'create' on '${relationName}' inserted no row, so there is nothing to attach.`, { code: "DRIZZLE_NESTED_WRITE_INVALID" });
6035
6070
  patch[plan.localColPropName] = created[plan.foreignColPropName];
6036
6071
  } else if (op["connect"] !== void 0) {
6037
- const rows = await executor.select().from(plan.targetTable).where(conditionOf(plan, op["connect"], "connect", scope));
6072
+ const rows = await executor.select({ [plan.foreignColPropName]: plan.foreignCol }).from(plan.targetTable).where(conditionOf(plan, op["connect"], "connect", scope));
6038
6073
  if (rows.length !== 1) throw drizzleError(`Drizzle-GraphQL Error: 'connect' on '${relationName}' matched ${rows.length} rows — it attaches a single row, so its filter must match exactly one.`, { code: "DRIZZLE_NESTED_WRITE_INVALID" });
6039
6074
  patch[plan.localColPropName] = rows[0][plan.foreignColPropName];
6040
6075
  } else if (op["disconnect"] === true) patch[plan.localColPropName] = null;