@rvoh/dream 2.21.1 → 2.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/src/Dream.js +3 -0
- package/dist/cjs/src/dream/DreamClassTransactionBuilder.js +3 -0
- package/dist/cjs/src/dream/Query.js +203 -4
- package/dist/cjs/src/dream/QueryDriver/Base.js +26 -0
- package/dist/cjs/src/dream/QueryDriver/Kysely.js +46 -4
- package/dist/cjs/src/dream/QueryDriver/Postgres.js +21 -0
- package/dist/cjs/src/dream/internal/destroyAssociatedRecords.js +76 -4
- package/dist/cjs/src/dream/internal/destroyDream.js +6 -7
- package/dist/cjs/src/errors/InvalidBatchSize.js +19 -0
- package/dist/cjs/src/errors/RowLockIncompatibleWithDistinct.js +14 -0
- package/dist/cjs/src/errors/RowLockIncompatibleWithJoinLoad.js +14 -0
- package/dist/cjs/src/errors/ops/ArrayEqualityRequiresArrayColumn.js +23 -0
- package/dist/cjs/src/ops/index.js +19 -0
- package/dist/esm/src/Dream.js +3 -0
- package/dist/esm/src/dream/DreamClassTransactionBuilder.js +3 -0
- package/dist/esm/src/dream/Query.js +203 -4
- package/dist/esm/src/dream/QueryDriver/Base.js +26 -0
- package/dist/esm/src/dream/QueryDriver/Kysely.js +46 -4
- package/dist/esm/src/dream/QueryDriver/Postgres.js +21 -0
- package/dist/esm/src/dream/internal/destroyAssociatedRecords.js +76 -4
- package/dist/esm/src/dream/internal/destroyDream.js +6 -7
- package/dist/esm/src/errors/InvalidBatchSize.js +19 -0
- package/dist/esm/src/errors/RowLockIncompatibleWithDistinct.js +14 -0
- package/dist/esm/src/errors/RowLockIncompatibleWithJoinLoad.js +14 -0
- package/dist/esm/src/errors/ops/ArrayEqualityRequiresArrayColumn.js +23 -0
- package/dist/esm/src/ops/index.js +19 -0
- package/dist/types/src/Dream.d.ts +3 -0
- package/dist/types/src/dream/DreamClassTransactionBuilder.d.ts +3 -0
- package/dist/types/src/dream/Query.d.ts +140 -3
- package/dist/types/src/dream/QueryDriver/Base.d.ts +21 -0
- package/dist/types/src/dream/QueryDriver/Kysely.d.ts +1 -0
- package/dist/types/src/dream/QueryDriver/Postgres.d.ts +20 -0
- package/dist/types/src/dream/internal/destroyAssociatedRecords.d.ts +22 -3
- package/dist/types/src/errors/InvalidBatchSize.d.ts +5 -0
- package/dist/types/src/errors/RowLockIncompatibleWithDistinct.d.ts +3 -0
- package/dist/types/src/errors/RowLockIncompatibleWithJoinLoad.d.ts +3 -0
- package/dist/types/src/errors/ops/ArrayEqualityRequiresArrayColumn.d.ts +8 -0
- package/dist/types/src/ops/index.d.ts +19 -0
- package/dist/types/src/types/associations/shared.d.ts +2 -2
- package/dist/types/src/types/dream.d.ts +8 -0
- package/dist/types/src/types/types/associations/shared.ts +84 -55
- package/dist/types/src/types/types/dream.ts +30 -0
- package/docs/assets/hierarchy.js +1 -1
- package/docs/assets/search.js +1 -1
- package/docs/classes/db.DreamMigrationHelpers.html +11 -11
- package/docs/classes/db.KyselyQueryDriver.html +55 -41
- package/docs/classes/db.PostgresQueryDriver.html +55 -42
- package/docs/classes/db.QueryDriverBase.html +54 -40
- package/docs/classes/errors.CheckConstraintViolation.html +3 -3
- package/docs/classes/errors.ColumnOverflow.html +3 -3
- package/docs/classes/errors.CreateOrFindByFailedToCreateAndFind.html +3 -3
- package/docs/classes/errors.DataIncompatibleWithDatabaseField.html +3 -3
- package/docs/classes/errors.DataTypeColumnTypeMismatch.html +3 -3
- package/docs/classes/errors.DecryptionError.html +2 -2
- package/docs/classes/errors.DecryptionParseError.html +2 -2
- package/docs/classes/errors.DecryptionRotationError.html +3 -3
- package/docs/classes/errors.GlobalNameNotSet.html +3 -3
- package/docs/classes/errors.InvalidCalendarDate.html +2 -2
- package/docs/classes/errors.InvalidClockTime.html +2 -2
- package/docs/classes/errors.InvalidClockTimeTz.html +2 -2
- package/docs/classes/errors.InvalidDateTime.html +2 -2
- package/docs/classes/errors.MissingSerializersDefinition.html +3 -3
- package/docs/classes/errors.NonLoadedAssociation.html +3 -3
- package/docs/classes/errors.NotNullViolation.html +3 -3
- package/docs/classes/errors.RecordNotFound.html +3 -3
- package/docs/classes/errors.ValidationError.html +3 -3
- package/docs/classes/index.CalendarDate.html +33 -33
- package/docs/classes/index.ClockTime.html +32 -32
- package/docs/classes/index.ClockTimeTz.html +35 -35
- package/docs/classes/index.DateTime.html +86 -86
- package/docs/classes/index.Decorators.html +19 -19
- package/docs/classes/index.Dream.html +127 -125
- package/docs/classes/index.DreamApp.html +10 -10
- package/docs/classes/index.DreamTransaction.html +2 -2
- package/docs/classes/index.Env.html +2 -2
- package/docs/classes/index.Query.html +161 -71
- package/docs/classes/system.CliFileWriter.html +4 -4
- package/docs/classes/system.DreamBin.html +2 -2
- package/docs/classes/system.DreamCLI.html +7 -7
- package/docs/classes/system.DreamImporter.html +2 -2
- package/docs/classes/system.DreamLogos.html +2 -2
- package/docs/classes/system.DreamSerializerBuilder.html +11 -11
- package/docs/classes/system.ObjectSerializerBuilder.html +8 -8
- package/docs/classes/system.PathHelpers.html +3 -3
- package/docs/classes/utils.Encrypt.html +3 -3
- package/docs/classes/utils.Range.html +2 -2
- package/docs/functions/db.closeAllDbConnections.html +1 -1
- package/docs/functions/db.dreamDbConnections.html +1 -1
- package/docs/functions/db.untypedDb.html +1 -1
- package/docs/functions/db.validateColumn.html +1 -1
- package/docs/functions/db.validateTable.html +1 -1
- package/docs/functions/errors.pgErrorType.html +1 -1
- package/docs/functions/index.DreamSerializer.html +1 -1
- package/docs/functions/index.ObjectSerializer.html +1 -1
- package/docs/functions/index.ReplicaSafe.html +1 -1
- package/docs/functions/index.STI.html +1 -1
- package/docs/functions/index.SoftDelete.html +1 -1
- package/docs/functions/utils.camelize.html +1 -1
- package/docs/functions/utils.capitalize.html +1 -1
- package/docs/functions/utils.cloneDeepSafe.html +1 -1
- package/docs/functions/utils.compact.html +1 -1
- package/docs/functions/utils.groupBy.html +1 -1
- package/docs/functions/utils.hyphenize.html +1 -1
- package/docs/functions/utils.intersection.html +1 -1
- package/docs/functions/utils.isEmpty.html +1 -1
- package/docs/functions/utils.normalizeUnicode.html +1 -1
- package/docs/functions/utils.pascalize.html +1 -1
- package/docs/functions/utils.percent.html +1 -1
- package/docs/functions/utils.range.html +1 -1
- package/docs/functions/utils.round.html +1 -1
- package/docs/functions/utils.sanitizeString.html +1 -1
- package/docs/functions/utils.snakeify.html +1 -1
- package/docs/functions/utils.sort.html +1 -1
- package/docs/functions/utils.sortBy.html +1 -1
- package/docs/functions/utils.sortObjectByKey.html +1 -1
- package/docs/functions/utils.sortObjectByValue.html +1 -1
- package/docs/functions/utils.uncapitalize.html +1 -1
- package/docs/functions/utils.uniq.html +1 -1
- package/docs/hierarchy.html +1 -1
- package/docs/interfaces/openapi.OpenapiDescription.html +2 -2
- package/docs/interfaces/openapi.OpenapiSchemaProperties.html +1 -1
- package/docs/interfaces/openapi.OpenapiSchemaPropertiesShorthand.html +1 -1
- package/docs/interfaces/openapi.OpenapiTypeFieldObject.html +1 -1
- package/docs/interfaces/types.BelongsToStatement.html +2 -2
- package/docs/interfaces/types.DecoratorContext.html +2 -2
- package/docs/interfaces/types.DreamAppInitOptions.html +2 -2
- package/docs/interfaces/types.DreamAppOpts.html +2 -2
- package/docs/interfaces/types.DreamDbConfig.html +5 -5
- package/docs/interfaces/types.DurationObject.html +2 -2
- package/docs/interfaces/types.EncryptOptions.html +2 -2
- package/docs/interfaces/types.InternalAnyTypedSerializerRendersMany.html +2 -2
- package/docs/interfaces/types.InternalAnyTypedSerializerRendersOne.html +2 -2
- package/docs/interfaces/types.SerializerRendererOpts.html +2 -2
- package/docs/types/openapi.CommonOpenapiSchemaObjectFields.html +1 -1
- package/docs/types/openapi.OpenapiAllTypes.html +1 -1
- package/docs/types/openapi.OpenapiFormats.html +1 -1
- package/docs/types/openapi.OpenapiNumberFormats.html +1 -1
- package/docs/types/openapi.OpenapiPrimitiveBaseTypes.html +1 -1
- package/docs/types/openapi.OpenapiPrimitiveTypes.html +1 -1
- package/docs/types/openapi.OpenapiSchemaArray.html +1 -1
- package/docs/types/openapi.OpenapiSchemaArrayShorthand.html +1 -1
- package/docs/types/openapi.OpenapiSchemaBase.html +1 -1
- package/docs/types/openapi.OpenapiSchemaBody.html +1 -1
- package/docs/types/openapi.OpenapiSchemaBodyShorthand.html +1 -1
- package/docs/types/openapi.OpenapiSchemaCommonFields.html +1 -1
- package/docs/types/openapi.OpenapiSchemaExpressionAllOf.html +2 -2
- package/docs/types/openapi.OpenapiSchemaExpressionAnyOf.html +2 -2
- package/docs/types/openapi.OpenapiSchemaExpressionOneOf.html +2 -2
- package/docs/types/openapi.OpenapiSchemaExpressionRef.html +2 -2
- package/docs/types/openapi.OpenapiSchemaExpressionRefSchemaShorthand.html +2 -2
- package/docs/types/openapi.OpenapiSchemaInteger.html +1 -1
- package/docs/types/openapi.OpenapiSchemaNull.html +2 -2
- package/docs/types/openapi.OpenapiSchemaNumber.html +1 -1
- package/docs/types/openapi.OpenapiSchemaObject.html +1 -1
- package/docs/types/openapi.OpenapiSchemaObjectAllOf.html +1 -1
- package/docs/types/openapi.OpenapiSchemaObjectAllOfShorthand.html +1 -1
- package/docs/types/openapi.OpenapiSchemaObjectAnyOf.html +1 -1
- package/docs/types/openapi.OpenapiSchemaObjectAnyOfShorthand.html +1 -1
- package/docs/types/openapi.OpenapiSchemaObjectBase.html +1 -1
- package/docs/types/openapi.OpenapiSchemaObjectBaseShorthand.html +1 -1
- package/docs/types/openapi.OpenapiSchemaObjectOneOf.html +1 -1
- package/docs/types/openapi.OpenapiSchemaObjectOneOfShorthand.html +1 -1
- package/docs/types/openapi.OpenapiSchemaObjectShorthand.html +1 -1
- package/docs/types/openapi.OpenapiSchemaPrimitiveGeneric.html +1 -1
- package/docs/types/openapi.OpenapiSchemaShorthandExpressionAllOf.html +2 -2
- package/docs/types/openapi.OpenapiSchemaShorthandExpressionAnyOf.html +2 -2
- package/docs/types/openapi.OpenapiSchemaShorthandExpressionOneOf.html +2 -2
- package/docs/types/openapi.OpenapiSchemaShorthandExpressionSerializableRef.html +2 -2
- package/docs/types/openapi.OpenapiSchemaShorthandExpressionSerializerRef.html +2 -2
- package/docs/types/openapi.OpenapiSchemaShorthandPrimitiveGeneric.html +1 -1
- package/docs/types/openapi.OpenapiSchemaString.html +1 -1
- package/docs/types/openapi.OpenapiShorthandAllTypes.html +1 -1
- package/docs/types/openapi.OpenapiShorthandPrimitiveBaseTypes.html +1 -1
- package/docs/types/openapi.OpenapiShorthandPrimitiveTypes.html +1 -1
- package/docs/types/openapi.OpenapiTypeField.html +1 -1
- package/docs/types/system.DreamAppAllowedPackageManagersEnum.html +1 -1
- package/docs/types/types.CalendarDateDurationUnit.html +1 -1
- package/docs/types/types.CalendarDateObject.html +1 -1
- package/docs/types/types.Camelized.html +1 -1
- package/docs/types/types.ClockTimeObject.html +1 -1
- package/docs/types/types.DbConnectionType.html +1 -1
- package/docs/types/types.DbTypes.html +1 -1
- package/docs/types/types.DreamAssociationMetadata.html +1 -1
- package/docs/types/types.DreamAttributes.html +1 -1
- package/docs/types/types.DreamClassAssociationAndStatement.html +1 -1
- package/docs/types/types.DreamClassColumn.html +1 -1
- package/docs/types/types.DreamColumn.html +1 -1
- package/docs/types/types.DreamColumnNames.html +1 -1
- package/docs/types/types.DreamLogLevel.html +1 -1
- package/docs/types/types.DreamLogger.html +2 -2
- package/docs/types/types.DreamModelSerializerType.html +1 -1
- package/docs/types/types.DreamOrViewModelClassSerializerKey.html +1 -1
- package/docs/types/types.DreamOrViewModelSerializerKey.html +1 -1
- package/docs/types/types.DreamParamSafeAttributes.html +1 -1
- package/docs/types/types.DreamParamSafeColumnNames.html +1 -1
- package/docs/types/types.DreamSerializable.html +1 -1
- package/docs/types/types.DreamSerializableArray.html +1 -1
- package/docs/types/types.DreamSerializerKey.html +1 -1
- package/docs/types/types.DreamSerializers.html +1 -1
- package/docs/types/types.DreamVirtualColumns.html +1 -1
- package/docs/types/types.DurationUnit.html +1 -1
- package/docs/types/types.EncryptAlgorithm.html +1 -1
- package/docs/types/types.HasManyStatement.html +1 -1
- package/docs/types/types.HasOneStatement.html +1 -1
- package/docs/types/types.Hyphenized.html +1 -1
- package/docs/types/types.Pascalized.html +1 -1
- package/docs/types/types.PrimaryKeyType.html +1 -1
- package/docs/types/types.RoundingPrecision.html +1 -1
- package/docs/types/types.SerializerCasing.html +1 -1
- package/docs/types/types.SimpleObjectSerializerType.html +1 -1
- package/docs/types/types.Snakeified.html +1 -1
- package/docs/types/types.StrictInterface.html +1 -1
- package/docs/types/types.UpdateableAssociationProperties.html +1 -1
- package/docs/types/types.UpdateableProperties.html +1 -1
- package/docs/types/types.ValidationType.html +1 -1
- package/docs/types/types.ViewModel.html +2 -2
- package/docs/types/types.ViewModelClass.html +1 -1
- package/docs/types/types.WeekdayName.html +1 -1
- package/docs/types/types.WhereStatementForDream.html +1 -1
- package/docs/types/types.WhereStatementForDreamClass.html +1 -1
- package/docs/variables/index.DreamConst.html +1 -1
- package/docs/variables/index.ops.html +38 -23
- package/docs/variables/openapi.openapiPrimitiveTypes.html +1 -1
- package/docs/variables/openapi.openapiShorthandPrimitiveTypes.html +1 -1
- package/docs/variables/system.DreamAppAllowedPackageManagersEnumValues.html +1 -1
- package/docs/variables/system.primaryKeyTypes.html +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export default class RowLockIncompatibleWithJoinLoad extends Error {
|
|
2
|
+
get message() {
|
|
3
|
+
// A join load builds its own select statement (one row per joined
|
|
4
|
+
// association row, later collapsed in memory), which cannot carry the
|
|
5
|
+
// Query's row lock. Rather than silently performing an unlocked read and
|
|
6
|
+
// dropping the compare-and-set guarantee the caller asked for, refuse.
|
|
7
|
+
return `
|
|
8
|
+
An exclusive row lock was requested on a query that also uses leftJoinPreload.
|
|
9
|
+
|
|
10
|
+
A join-loaded query cannot be locked. Use \`preload\` instead of
|
|
11
|
+
\`leftJoinPreload\`, or drop the lock.
|
|
12
|
+
`;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export default class ArrayEqualityRequiresArrayColumn extends Error {
|
|
2
|
+
dreamClass;
|
|
3
|
+
column;
|
|
4
|
+
operator;
|
|
5
|
+
constructor(dreamClass, column, operator) {
|
|
6
|
+
super();
|
|
7
|
+
this.dreamClass = dreamClass;
|
|
8
|
+
this.column = column;
|
|
9
|
+
this.operator = operator;
|
|
10
|
+
}
|
|
11
|
+
get message() {
|
|
12
|
+
return `
|
|
13
|
+
Attempting to compare an array for equality against a column that is not an
|
|
14
|
+
array in the database:
|
|
15
|
+
|
|
16
|
+
${this.dreamClass.sanitizedName}#${this.column} (operator: ${this.operator})
|
|
17
|
+
|
|
18
|
+
Whole-array equality (\`=\`, \`!=\`, \`<>\`) is only meaningful for array columns.
|
|
19
|
+
If you meant "is this column one of these values", use \`ops.in([...])\`
|
|
20
|
+
(or a bare array) instead.
|
|
21
|
+
`;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -117,11 +117,27 @@ const ops = {
|
|
|
117
117
|
/**
|
|
118
118
|
* Creates an `OpsStatement` using the `=` operator for strict equality.
|
|
119
119
|
*
|
|
120
|
+
* **Array columns:** passing an array means literal array equality — the
|
|
121
|
+
* whole array is bound as a single parameter, so the column must match
|
|
122
|
+
* element-for-element, in order. This is the opt-in counterpart to a bare
|
|
123
|
+
* array in a where clause (`Post.where({ tags: ['a', 'b'] })`), which means
|
|
124
|
+
* `IN`, and to `ops.any`, which tests for containment of a single element.
|
|
125
|
+
* Element types are constrained by the column, so an enum array column
|
|
126
|
+
* rejects a string that is not a member of its enum.
|
|
127
|
+
*
|
|
128
|
+
* Note that PostgreSQL defines no equality operator for `json` (as opposed
|
|
129
|
+
* to `jsonb`), so `ops.equal` against a `json[]` column raises at the
|
|
130
|
+
* database.
|
|
131
|
+
*
|
|
120
132
|
* @param equal - The value the column must equal.
|
|
121
133
|
* @returns An `OpsStatement` using the `=` operator.
|
|
122
134
|
*
|
|
123
135
|
* @example
|
|
124
136
|
* User.where({ role: ops.equal('admin') })
|
|
137
|
+
*
|
|
138
|
+
* @example
|
|
139
|
+
* // matches only rows whose tags column is exactly ['a', 'b'], in that order
|
|
140
|
+
* Post.where({ tags: ops.equal(['a', 'b']) })
|
|
125
141
|
*/
|
|
126
142
|
equal: (equal) => new OpsStatement('=', equal),
|
|
127
143
|
/**
|
|
@@ -260,6 +276,9 @@ const ops = {
|
|
|
260
276
|
/**
|
|
261
277
|
* Creates an `OpsStatement` using the `!=` operator for inequality.
|
|
262
278
|
*
|
|
279
|
+
* **Array columns:** as with `ops.equal`, passing an array compares against
|
|
280
|
+
* the whole array rather than against its elements.
|
|
281
|
+
*
|
|
263
282
|
* @param equal - The value the column must not equal.
|
|
264
283
|
* @returns An `OpsStatement` using the `!=` operator.
|
|
265
284
|
*
|
|
@@ -1011,6 +1011,9 @@ export default class Dream {
|
|
|
1011
1011
|
* // User{email: 'goodbye@world'}
|
|
1012
1012
|
* ```
|
|
1013
1013
|
*
|
|
1014
|
+
* Records are always visited in ascending primary key order; any `order`
|
|
1015
|
+
* applied to the query is discarded. See {@link Query.findEach} for why.
|
|
1016
|
+
*
|
|
1014
1017
|
* @param cb - The callback to call for each found record
|
|
1015
1018
|
* @param opts - Optional parameters for batch processing
|
|
1016
1019
|
* @param opts.batchSize - the batch size you wish to collect records in. If not provided, it will default to 1000
|
|
@@ -384,6 +384,9 @@ export default class DreamClassTransactionBuilder<DreamClass extends typeof Drea
|
|
|
384
384
|
* // User{email: 'goodbye@world'}
|
|
385
385
|
* ```
|
|
386
386
|
*
|
|
387
|
+
* Records are always visited in ascending primary key order; any `order`
|
|
388
|
+
* applied to the query is discarded. See {@link Query.findEach} for why.
|
|
389
|
+
*
|
|
387
390
|
* @param cb - The callback to call for each found record
|
|
388
391
|
* @param options.batchSize - The batch size you wish to collect records in. If not provided, it will default to 1000
|
|
389
392
|
* @returns void
|
|
@@ -16,6 +16,7 @@ export default class Query<DreamInstance extends Dream, QueryTypeOpts extends Re
|
|
|
16
16
|
*/
|
|
17
17
|
static readonly BATCH_SIZES: {
|
|
18
18
|
FIND_EACH: number;
|
|
19
|
+
LOCKED_DESTROY: number;
|
|
19
20
|
PLUCK_EACH: number;
|
|
20
21
|
PLUCK_EACH_THROUGH: number;
|
|
21
22
|
};
|
|
@@ -282,13 +283,30 @@ export default class Query<DreamInstance extends Dream, QueryTypeOpts extends Re
|
|
|
282
283
|
* records matching the Query have been fetched.
|
|
283
284
|
*
|
|
284
285
|
* ```ts
|
|
285
|
-
* await User.
|
|
286
|
+
* await User.where({ deletedAt: null }).findEach(user => {
|
|
286
287
|
* DreamApp.log(user)
|
|
287
288
|
* })
|
|
288
289
|
* // User{id: 1}
|
|
289
290
|
* // User{id: 2}
|
|
290
291
|
* ```
|
|
291
292
|
*
|
|
293
|
+
* IMPORTANT: `findEach` always iterates in ascending primary key order.
|
|
294
|
+
* Any `order` applied to the Query is discarded; passing one has no effect
|
|
295
|
+
* on the order records are visited in.
|
|
296
|
+
*
|
|
297
|
+
* This is not a limitation that can be lifted. `findEach` guarantees that
|
|
298
|
+
* every matching record is visited exactly once, and it delivers that by
|
|
299
|
+
* paginating on a keyset: each batch selects the next `batchSize` records
|
|
300
|
+
* with a primary key greater than the last record of the previous batch.
|
|
301
|
+
* That guarantee only holds over a stable, unique key. Ordering by a
|
|
302
|
+
* mutable, non-unique column would mean a record whose sort value changed
|
|
303
|
+
* partway through the iteration could be skipped entirely or visited twice.
|
|
304
|
+
* ({@link Query.cursorPaginate} can honor an arbitrary order precisely
|
|
305
|
+
* because a paginated UI tolerates that anomaly; a full traversal cannot.)
|
|
306
|
+
*
|
|
307
|
+
* If you need records processed in a particular order, load them with a
|
|
308
|
+
* regular ordered query instead of `findEach`.
|
|
309
|
+
*
|
|
292
310
|
* @param cb - The callback to call for each found record
|
|
293
311
|
* @param __namedParameters - Options for batch processing
|
|
294
312
|
* @param __namedParameters.batchSize - The batch size you wish to collect records in. If not provided, it will default to 1000
|
|
@@ -1209,17 +1227,131 @@ export default class Query<DreamInstance extends Dream, QueryTypeOpts extends Re
|
|
|
1209
1227
|
* // 12
|
|
1210
1228
|
* ```
|
|
1211
1229
|
*
|
|
1230
|
+
* ## Guarded (compare-and-set) destroy
|
|
1231
|
+
*
|
|
1232
|
+
* By default, `destroy` reads the matching records and then destroys each of
|
|
1233
|
+
* them, so a concurrent writer can change a record in between — the record is
|
|
1234
|
+
* destroyed even though it no longer matches the Query that selected it.
|
|
1235
|
+
* Passing `lock: true` closes that window, making `destroy` a compare-and-set
|
|
1236
|
+
* the way `update(..., { skipHooks: true })` already is:
|
|
1237
|
+
*
|
|
1238
|
+
* ```ts
|
|
1239
|
+
* const destroyed = await Order.where({ status: 'pending' }).destroy({ lock: true })
|
|
1240
|
+
* // 0 — someone else moved every one of them out of 'pending' first
|
|
1241
|
+
* ```
|
|
1242
|
+
*
|
|
1243
|
+
* With `lock: true`, records are processed in batches, and each batch runs in
|
|
1244
|
+
* its own transaction which first re-selects that batch's records with an
|
|
1245
|
+
* exclusive row lock and then destroys them before committing. Because the
|
|
1246
|
+
* database re-checks the Query's conditions after acquiring each row lock,
|
|
1247
|
+
* a record a concurrent transaction has already moved out of the Query drops
|
|
1248
|
+
* out of the locked read and is never destroyed. The returned count is the
|
|
1249
|
+
* number of records actually claimed, so a caller can detect that it lost a
|
|
1250
|
+
* race by comparing the count against what it expected.
|
|
1251
|
+
*
|
|
1252
|
+
* Things to know before relying on it:
|
|
1253
|
+
*
|
|
1254
|
+
* - **The guarantee is per batch, not set-wide.** A run spanning more than one
|
|
1255
|
+
* batch can win a race in batch 1 and lose one in batch 2; nothing holds the
|
|
1256
|
+
* whole matched set still for the duration of the run. Sharing one
|
|
1257
|
+
* transaction across every batch — by threading a transaction onto the Query
|
|
1258
|
+
* itself, `Order.query().txn(txn).where({ ... }).destroy({ lock: true })` —
|
|
1259
|
+
* buys two things: the destroys become all-or-nothing (a rollback undoes the
|
|
1260
|
+
* whole run), and the row locks taken by every batch are held until you
|
|
1261
|
+
* commit. It does **not** extend the compare-and-set to the part of the set
|
|
1262
|
+
* the run has not reached yet: under READ COMMITTED each statement takes a
|
|
1263
|
+
* fresh snapshot, so a record moved out of the Query between batch 1 and
|
|
1264
|
+
* batch 2 still drops out of batch 2's locked read. A genuinely set-wide
|
|
1265
|
+
* guarantee needs REPEATABLE READ / SERIALIZABLE, or a single locking
|
|
1266
|
+
* statement over the whole set.
|
|
1267
|
+
* - **Only a transaction carried by the Query is shared.** Merely wrapping the
|
|
1268
|
+
* call in `Model.transaction(...)` does not thread that transaction onto the
|
|
1269
|
+
* Query — `Dream.transaction` establishes no ambient transaction, it only
|
|
1270
|
+
* hands a `txn` to your callback — so each batch would open its own
|
|
1271
|
+
* transaction on a different pooled connection. Worse, if the surrounding
|
|
1272
|
+
* transaction has already written to any of the matched rows, each batch's
|
|
1273
|
+
* locked read blocks on locks the surrounding transaction holds while that
|
|
1274
|
+
* transaction awaits the destroy: a cycle across two connections, which
|
|
1275
|
+
* Postgres's deadlock detector cannot see, so it hangs rather than aborting.
|
|
1276
|
+
* Always pass the transaction with `.txn(txn)`.
|
|
1277
|
+
* - **Behavior above READ COMMITTED differs.** The drop-out-of-the-result
|
|
1278
|
+
* behavior described above is READ COMMITTED (Postgres's default). Under
|
|
1279
|
+
* REPEATABLE READ or SERIALIZABLE, the same race raises a serialization
|
|
1280
|
+
* failure instead of silently returning a lower count. Both are safe; only
|
|
1281
|
+
* one of them is quiet.
|
|
1282
|
+
* - **`batchSize` defaults to 10 here**, not `findEach`'s 1000, and the
|
|
1283
|
+
* default is deliberately small. Every row in a batch stays locked for the
|
|
1284
|
+
* whole batch, including the time spent running each record's hooks and
|
|
1285
|
+
* `dependent: 'destroy'` cascade, so an oversized batch blocks unrelated
|
|
1286
|
+
* writers touching those rows and surfaces as timeouts somewhere else in the
|
|
1287
|
+
* application, which is very hard to trace back to this destroy. Too small a
|
|
1288
|
+
* batch, by contrast, only slows down this call, which you can see and fix.
|
|
1289
|
+
* Lower it further for models with deep `dependent: 'destroy'` cascades or
|
|
1290
|
+
* expensive destroy hooks.
|
|
1291
|
+
* - **A batch waits, without bound, for whoever holds the row.** The locked
|
|
1292
|
+
* read is a plain `FOR UPDATE`: if another transaction already holds a lock
|
|
1293
|
+
* on one of the batch's rows, this call blocks until that transaction ends,
|
|
1294
|
+
* holding its own transaction and its pooled connection open the whole
|
|
1295
|
+
* time. Dream sets no `lock_timeout` or `statement_timeout`, so a
|
|
1296
|
+
* long-running writer on the other side stalls the run indefinitely and, at
|
|
1297
|
+
* scale, ties up connections. If that is a risk in your application, set a
|
|
1298
|
+
* `lock_timeout` on the connection so a stuck batch fails instead of
|
|
1299
|
+
* hanging.
|
|
1300
|
+
* - **If you are destroying a large set and do not need compare-and-set, do
|
|
1301
|
+
* not pass `lock`.** Plain `destroy()` (hooks and cascade, no locking) or
|
|
1302
|
+
* {@link Query.delete | delete} (a single statement, no hooks or cascade)
|
|
1303
|
+
* are the right tools for bulk removal.
|
|
1304
|
+
*
|
|
1212
1305
|
* @param __namedParameters - Options for destroying the instance
|
|
1213
1306
|
* @param __namedParameters.skipHooks - If true, skips applying model hooks during the destroy operation. Defaults to false
|
|
1214
1307
|
* @param __namedParameters.cascade - If false, skips destroying associations marked `dependent: 'destroy'`. Defaults to true
|
|
1308
|
+
* @param __namedParameters.lock - If true, each batch is re-selected with an exclusive row lock inside its own transaction before being destroyed, making the destroy a compare-and-set. Defaults to false
|
|
1309
|
+
* @param __namedParameters.batchSize - The number of records to process per batch. Must be a positive integer. Defaults to 10 when `lock` is true, and to 1000 otherwise
|
|
1215
1310
|
* @param __namedParameters.bypassAllDefaultScopes - If true, bypasses all default scopes when cascade destroying. Defaults to false
|
|
1216
1311
|
* @param __namedParameters.defaultScopesToBypass - An array of default scope names to bypass when cascade destroying. Defaults to an empty array
|
|
1217
1312
|
* @returns The number of records that were removed
|
|
1313
|
+
* @throws InvalidBatchSize if `batchSize` is not a positive integer
|
|
1218
1314
|
*/
|
|
1219
|
-
destroy({ skipHooks, cascade, }?: {
|
|
1315
|
+
destroy({ skipHooks, cascade, lock, batchSize, }?: {
|
|
1220
1316
|
skipHooks?: boolean | undefined;
|
|
1221
1317
|
cascade?: boolean | undefined;
|
|
1318
|
+
lock?: boolean | undefined;
|
|
1319
|
+
batchSize?: number | undefined;
|
|
1222
1320
|
}): Promise<number>;
|
|
1321
|
+
/**
|
|
1322
|
+
* @internal
|
|
1323
|
+
*
|
|
1324
|
+
* The compare-and-set implementation behind `destroy({ lock: true })`.
|
|
1325
|
+
*
|
|
1326
|
+
* Each batch is handled in two steps inside one transaction:
|
|
1327
|
+
*
|
|
1328
|
+
* 1. an unlocked pluck of the next `batchSize` primary keys, in ascending
|
|
1329
|
+
* primary key order, which establishes the batch's window (its keyset
|
|
1330
|
+
* cursor) and tells us how many records were *attempted*. Only the keys are
|
|
1331
|
+
* read: hydrating and preloading these records would duplicate the work
|
|
1332
|
+
* step 2 does over the same rows; then
|
|
1333
|
+
* 2. a locked re-read restricted to exactly those primary keys, which
|
|
1334
|
+
* re-applies every condition on the Query while acquiring an exclusive row
|
|
1335
|
+
* lock on each row it returns. Records a concurrent transaction has moved
|
|
1336
|
+
* out of the Query since step 1 do not come back, and are the records this
|
|
1337
|
+
* run lost the race for.
|
|
1338
|
+
*
|
|
1339
|
+
* The count comes from step 2's result set, which is why it reports records
|
|
1340
|
+
* actually claimed rather than records attempted. (One thing it does not
|
|
1341
|
+
* account for, because it never has: a `beforeDestroy` hook that sets
|
|
1342
|
+
* `_preventDeletion` leaves the record in place but still counts.)
|
|
1343
|
+
*
|
|
1344
|
+
* The two-step shape is what keeps iteration correct. If the locked read both
|
|
1345
|
+
* claimed rows and advanced the cursor, a batch whose records all lost the
|
|
1346
|
+
* race would come back empty and end the run early, silently skipping every
|
|
1347
|
+
* record after it. Advancing the cursor from step 1 instead means losing a
|
|
1348
|
+
* race skips only the records that were lost.
|
|
1349
|
+
*
|
|
1350
|
+
* When the Query already carries a transaction, that transaction is used for
|
|
1351
|
+
* every batch rather than opening one per batch, so the caller's transaction
|
|
1352
|
+
* boundary wins and the locks are held until the caller commits.
|
|
1353
|
+
*/
|
|
1354
|
+
private lockedDestroy;
|
|
1223
1355
|
/**
|
|
1224
1356
|
* Destroys all records matching the Query,
|
|
1225
1357
|
* ignoring the SoftDelete decorator and
|
|
@@ -1241,13 +1373,18 @@ export default class Query<DreamInstance extends Dream, QueryTypeOpts extends Re
|
|
|
1241
1373
|
* @param __namedParameters - Options for destroying the instance
|
|
1242
1374
|
* @param __namedParameters.skipHooks - If true, skips applying model hooks during the destroy operation. Defaults to false
|
|
1243
1375
|
* @param __namedParameters.cascade - If false, skips destroying associations marked `dependent: 'destroy'`. Defaults to true
|
|
1376
|
+
* @param __namedParameters.lock - If true, each batch is re-selected with an exclusive row lock inside its own transaction before being destroyed, making the destroy a compare-and-set. See {@link Query.destroy} for the full semantics. Defaults to false
|
|
1377
|
+
* @param __namedParameters.batchSize - The number of records to process per batch. Must be a positive integer. Defaults to 10 when `lock` is true, and to 1000 otherwise
|
|
1244
1378
|
* @param __namedParameters.bypassAllDefaultScopes - If true, bypasses all default scopes when cascade destroying. Defaults to false
|
|
1245
1379
|
* @param __namedParameters.defaultScopesToBypass - An array of default scope names to bypass when cascade destroying. Defaults to an empty array
|
|
1246
1380
|
* @returns The number of records that were removed
|
|
1381
|
+
* @throws InvalidBatchSize if `batchSize` is not a positive integer
|
|
1247
1382
|
*/
|
|
1248
|
-
reallyDestroy({ skipHooks, cascade, }?: {
|
|
1383
|
+
reallyDestroy({ skipHooks, cascade, lock, batchSize, }?: {
|
|
1249
1384
|
skipHooks?: boolean;
|
|
1250
1385
|
cascade?: boolean;
|
|
1386
|
+
lock?: boolean;
|
|
1387
|
+
batchSize?: number;
|
|
1251
1388
|
}): Promise<number>;
|
|
1252
1389
|
/**
|
|
1253
1390
|
* Undestroys a SoftDelete model, unsetting
|
|
@@ -190,7 +190,28 @@ export default class QueryDriverBase<DreamInstance extends Dream> {
|
|
|
190
190
|
*/
|
|
191
191
|
takeAll(options?: {
|
|
192
192
|
columns?: DreamColumnNames<DreamInstance>[];
|
|
193
|
+
lock?: boolean;
|
|
193
194
|
}): Promise<DreamInstance[]>;
|
|
195
|
+
/**
|
|
196
|
+
* @internal
|
|
197
|
+
*
|
|
198
|
+
* Adapter seam for exclusive row locking. Given the select statement about to
|
|
199
|
+
* be executed, return a statement that additionally acquires an exclusive
|
|
200
|
+
* lock on each row it returns, held until the enclosing transaction commits
|
|
201
|
+
* or rolls back (Postgres: `SELECT ... FOR UPDATE OF <table>`).
|
|
202
|
+
*
|
|
203
|
+
* This is only ever reached when a caller opts into a locked read (today,
|
|
204
|
+
* `Query#destroy({ lock: true })`), and only from inside a transaction. The
|
|
205
|
+
* base implementation throws so that an adapter which has not implemented row
|
|
206
|
+
* locking fails loud rather than silently performing an unlocked read and
|
|
207
|
+
* quietly dropping the compare-and-set guarantee the caller asked for.
|
|
208
|
+
*
|
|
209
|
+
* @param kyselyQuery - the select statement to lock
|
|
210
|
+
* @param tableAlias - the alias of the Query's base table; only rows of that
|
|
211
|
+
* table are locked, so a joined query does not lock unrelated rows and does
|
|
212
|
+
* not fail on the nullable side of an outer join
|
|
213
|
+
*/
|
|
214
|
+
applyRowLock(kyselyQuery: SelectQueryBuilder<any, any, any>, tableAlias: string): SelectQueryBuilder<any, any, any>;
|
|
194
215
|
/**
|
|
195
216
|
* Retrieves the max value of the specified column
|
|
196
217
|
* for this Query
|
|
@@ -142,6 +142,7 @@ export default class KyselyQueryDriver<DreamInstance extends Dream> extends Quer
|
|
|
142
142
|
*/
|
|
143
143
|
takeAll(options?: {
|
|
144
144
|
columns?: DreamColumnNames<DreamInstance>[];
|
|
145
|
+
lock?: boolean;
|
|
145
146
|
}): Promise<DreamInstance[]>;
|
|
146
147
|
/**
|
|
147
148
|
* Retrieves the max value of the specified column
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type SelectQueryBuilder } from 'kysely';
|
|
1
2
|
import Dream from '../../Dream.js';
|
|
2
3
|
import { SchemaBuilderAssociationData, SchemaBuilderColumnData } from '../../helpers/cli/ASTBuilder.js';
|
|
3
4
|
import KyselyQueryDriver from './Kysely.js';
|
|
@@ -34,6 +35,25 @@ export default class PostgresQueryDriver<DreamInstance extends Dream> extends Ky
|
|
|
34
35
|
[key: string]: SchemaBuilderColumnData;
|
|
35
36
|
}>;
|
|
36
37
|
static duplicateDatabase(connectionName: string): Promise<void>;
|
|
38
|
+
/**
|
|
39
|
+
* @internal
|
|
40
|
+
*
|
|
41
|
+
* Postgres implementation of the row-locking seam: appends
|
|
42
|
+
* `FOR UPDATE OF "<tableAlias>"` to the select, so every row the statement
|
|
43
|
+
* returns is exclusively locked until the enclosing transaction ends.
|
|
44
|
+
*
|
|
45
|
+
* Scoping the lock to the Query's own base table (rather than a bare
|
|
46
|
+
* `FOR UPDATE`) means a query that joins other tables neither locks rows the
|
|
47
|
+
* caller never asked about nor raises Postgres's "FOR UPDATE cannot be
|
|
48
|
+
* applied to the nullable side of an outer join".
|
|
49
|
+
*
|
|
50
|
+
* This is a waiting lock: neither `NOWAIT` nor `SKIP LOCKED` is applied, so a
|
|
51
|
+
* statement that collides with another transaction's lock blocks until that
|
|
52
|
+
* transaction ends. Callers that cannot afford an unbounded wait should set a
|
|
53
|
+
* `lock_timeout` on the connection; see the guarded-destroy notes on
|
|
54
|
+
* `Query#destroy`.
|
|
55
|
+
*/
|
|
56
|
+
applyRowLock(kyselyQuery: SelectQueryBuilder<any, any, any>, tableAlias: string): SelectQueryBuilder<any, any, any>;
|
|
37
57
|
static supportsParallelTestDatabases: boolean;
|
|
38
58
|
/**
|
|
39
59
|
* @internal
|
|
@@ -7,8 +7,27 @@ import { ReallyDestroyOptions } from './destroyDream.js';
|
|
|
7
7
|
* Destroys all HasOne/HasMany associations on this
|
|
8
8
|
* dream that are marked as `dependent: 'destroy'`.
|
|
9
9
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
10
|
+
* The whole dependent-destroy tree is loaded ONCE, at the root of the destroy
|
|
11
|
+
* operation, by `loadDependentDestroyTree`. Descendants reached by recursing
|
|
12
|
+
* through this function consume those already-hydrated associations rather
|
|
13
|
+
* than re-querying their own subtrees.
|
|
14
|
+
*
|
|
15
|
+
* A load still happens here when an association is not hydrated. That covers
|
|
16
|
+
* two cases:
|
|
17
|
+
*
|
|
18
|
+
* 1. the root of the operation, whose associations have not been loaded yet
|
|
19
|
+
* 2. a node past `RECURSIVE_DESTROY_PRELOAD_MAX_REPEATS` levels of
|
|
20
|
+
* self-reference, which the root's preload could not reach
|
|
21
|
+
*
|
|
22
|
+
* The second case is why the check is per-association-hydration rather than a
|
|
23
|
+
* "descendants are already loaded" flag: such a flag would truncate cascades
|
|
24
|
+
* deeper than the preload horizon.
|
|
25
|
+
*
|
|
26
|
+
* NOTE: reusing a hydrated subtree is only sound because the same `options`
|
|
27
|
+
* are forwarded unchanged all the way down the cascade (see the recursive
|
|
28
|
+
* `destroy`/`reallyDestroy` calls below), so every node's associations were
|
|
29
|
+
* loaded under the scopes this node would have used. Nothing in the types
|
|
30
|
+
* enforces that; if `options` ever start varying by depth, this reuse has to
|
|
31
|
+
* be revisited.
|
|
13
32
|
*/
|
|
14
33
|
export default function destroyAssociatedRecords<I extends Dream>(dream: I, txn: DreamTransaction<I>, options: ReallyDestroyOptions<I>): Promise<void>;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import Dream from '../../Dream.js';
|
|
2
|
+
export default class ArrayEqualityRequiresArrayColumn extends Error {
|
|
3
|
+
dreamClass: typeof Dream;
|
|
4
|
+
column: string;
|
|
5
|
+
operator: string;
|
|
6
|
+
constructor(dreamClass: typeof Dream, column: string, operator: string);
|
|
7
|
+
get message(): string;
|
|
8
|
+
}
|
|
@@ -112,11 +112,27 @@ declare const ops: {
|
|
|
112
112
|
/**
|
|
113
113
|
* Creates an `OpsStatement` using the `=` operator for strict equality.
|
|
114
114
|
*
|
|
115
|
+
* **Array columns:** passing an array means literal array equality — the
|
|
116
|
+
* whole array is bound as a single parameter, so the column must match
|
|
117
|
+
* element-for-element, in order. This is the opt-in counterpart to a bare
|
|
118
|
+
* array in a where clause (`Post.where({ tags: ['a', 'b'] })`), which means
|
|
119
|
+
* `IN`, and to `ops.any`, which tests for containment of a single element.
|
|
120
|
+
* Element types are constrained by the column, so an enum array column
|
|
121
|
+
* rejects a string that is not a member of its enum.
|
|
122
|
+
*
|
|
123
|
+
* Note that PostgreSQL defines no equality operator for `json` (as opposed
|
|
124
|
+
* to `jsonb`), so `ops.equal` against a `json[]` column raises at the
|
|
125
|
+
* database.
|
|
126
|
+
*
|
|
115
127
|
* @param equal - The value the column must equal.
|
|
116
128
|
* @returns An `OpsStatement` using the `=` operator.
|
|
117
129
|
*
|
|
118
130
|
* @example
|
|
119
131
|
* User.where({ role: ops.equal('admin') })
|
|
132
|
+
*
|
|
133
|
+
* @example
|
|
134
|
+
* // matches only rows whose tags column is exactly ['a', 'b'], in that order
|
|
135
|
+
* Post.where({ tags: ops.equal(['a', 'b']) })
|
|
120
136
|
*/
|
|
121
137
|
equal: <const T>(equal: T) => OpsStatement<"=", T, undefined>;
|
|
122
138
|
/**
|
|
@@ -269,6 +285,9 @@ declare const ops: {
|
|
|
269
285
|
/**
|
|
270
286
|
* Creates an `OpsStatement` using the `!=` operator for inequality.
|
|
271
287
|
*
|
|
288
|
+
* **Array columns:** as with `ops.equal`, passing an array compares against
|
|
289
|
+
* the whole array rather than against its elements.
|
|
290
|
+
*
|
|
272
291
|
* @param equal - The value the column must not equal.
|
|
273
292
|
* @returns An `OpsStatement` using the `!=` operator.
|
|
274
293
|
*
|
|
@@ -8,7 +8,7 @@ import { ClockTime, ClockTimeTz } from '../../package-exports/index.js';
|
|
|
8
8
|
import CalendarDate from '../../utils/datetime/CalendarDate.js';
|
|
9
9
|
import { DateTime } from '../../utils/datetime/DateTime.js';
|
|
10
10
|
import { AssociationTableNames } from '../db.js';
|
|
11
|
-
import { DefaultScopeName, DefaultScopeNameForTable, DreamBelongsToAssociationMetadata, DreamColumnNames, GlobalModelNameTableMap, ModelColumnType, OrderDir, TableColumnEnumTypeArray, TableColumnNames, TableColumnType, TableNameForGlobalModelName, TrigramOperator } from '../dream.js';
|
|
11
|
+
import { DefaultScopeName, DefaultScopeNameForTable, DreamBelongsToAssociationMetadata, DreamColumnNames, GlobalModelNameTableMap, ModelColumnType, OrderDir, TableColumnEnumTypeArray, TableColumnIsArray, TableColumnNames, TableColumnType, TableNameForGlobalModelName, TrigramOperator } from '../dream.js';
|
|
12
12
|
import { Inc, MergeUnionOfRecordTypes, ReadonlyTail, UnionToIntersection } from '../utils.js';
|
|
13
13
|
import { JoinedAssociation } from '../variadic.js';
|
|
14
14
|
import { BelongsToStatement } from './belongsTo.js';
|
|
@@ -25,7 +25,7 @@ export type PassthroughOnClause<PassthroughColumns extends string[]> = Partial<R
|
|
|
25
25
|
type DreamSelectable<DB, Schema, TableName extends AssociationTableNames<DB, Schema> & keyof DB> = Partial<{
|
|
26
26
|
[ColumnName in keyof DB[TableName]]: NonKyselySupportedSupplementalWhereClauseValues<DB, Schema, TableName, ColumnName>;
|
|
27
27
|
}>;
|
|
28
|
-
type NonKyselySupportedSupplementalWhereClauseValues<DB, Schema, TableName, Column, ModelPropertyType = ModelColumnType<Schema, TableName, Column>, ColumnType = TableColumnType<Schema, TableName, Column>, EnumTypeArray extends string[] | null = TableColumnEnumTypeArray<Schema, TableName, Column>, PermanentOpsValTypes = null | readonly [], OpsValType = EnumTypeArray extends null ? ColumnType extends 'bigint' ? TypesAllowedForBigintAgainstTheDb | PermanentOpsValTypes : ModelPropertyType extends DateTime | CalendarDate ? DateTime | CalendarDate | null : ModelPropertyType extends ClockTime ? ClockTime | null : ModelPropertyType extends ClockTimeTz ? ClockTimeTz | null : ModelPropertyType extends number | string ? ModelPropertyType | PermanentOpsValTypes : never : EnumTypeArray extends string[] ? EnumTypeArray[number] | PermanentOpsValTypes : never, ResolvedOpsValType = OpsValType | RawBuilder<unknown>, PartialTypes = EnumTypeArray extends null ? ModelPropertyType extends DateTime ? Range<DateTime | CalendarDate> | Range<null, DateTime | CalendarDate> | (() => Range<DateTime | CalendarDate>) | (() => Range<null, DateTime | CalendarDate>) | OpsStatement<KyselyComparisonOperatorExpression, ResolvedOpsValType, any> : ModelPropertyType extends ClockTime ? Range<ClockTime> | Range<null, ClockTime> | (() => Range<ClockTime>) | (() => Range<null, ClockTime>) | OpsStatement<KyselyComparisonOperatorExpression, ResolvedOpsValType, any> : ModelPropertyType extends ClockTimeTz ? Range<ClockTimeTz> | Range<null, ClockTimeTz> | (() => Range<ClockTimeTz>) | (() => Range<null, ClockTimeTz>) | OpsStatement<KyselyComparisonOperatorExpression, ResolvedOpsValType, any> : ModelPropertyType extends CalendarDate ? Range<DateTime | CalendarDate> | Range<null, DateTime | CalendarDate> | (() => Range<DateTime | CalendarDate>) | (() => Range<null, DateTime | CalendarDate>) | OpsStatement<KyselyComparisonOperatorExpression, ResolvedOpsValType> : ColumnType extends 'bigint' ? Range<TypesAllowedForBigintAgainstTheDb> | Range<null, TypesAllowedForBigintAgainstTheDb> | OpsStatement<KyselyComparisonOperatorExpression, ResolvedOpsValType, any> : ModelPropertyType extends number ? Range<ModelPropertyType> | Range<null, ModelPropertyType> | OpsStatement<KyselyComparisonOperatorExpression, ResolvedOpsValType, any> : ModelPropertyType extends string ? Range<string> | Range<null, string> | OpsStatement<KyselyComparisonOperatorExpression, string, any> | OpsStatement<TrigramOperator, ResolvedOpsValType, ExtraSimilarityArgs> : never : EnumTypeArray extends string[] ? EnumTypeArray | OpsStatement<KyselyComparisonOperatorExpression, ResolvedOpsValType, any> : never> = PartialTypes extends never ? OpsStatement<KyselyComparisonOperatorExpression, ResolvedOpsValType, any> | CurriedOpsStatement<any, any, any, ResolvedOpsValType> | SelectQueryBuilder<DB, keyof DB, any> : PartialTypes | CurriedOpsStatement<any, any, any, ResolvedOpsValType> | SelectQueryBuilder<DB, keyof DB, any>;
|
|
28
|
+
type NonKyselySupportedSupplementalWhereClauseValues<DB, Schema, TableName, Column, ModelPropertyType = ModelColumnType<Schema, TableName, Column>, ColumnType = TableColumnType<Schema, TableName, Column>, EnumTypeArray extends string[] | null = TableColumnEnumTypeArray<Schema, TableName, Column>, IsArrayColumn = TableColumnIsArray<Schema, TableName, Column>, PermanentOpsValTypes = null | readonly [], ArrayElementType = ModelPropertyType extends readonly (infer ElementType)[] ? ElementType : never, ArrayOpsValType = readonly ArrayElementType[] | PermanentOpsValTypes | RawBuilder<unknown>, OpsValType = IsArrayColumn extends true ? ArrayElementType | PermanentOpsValTypes : EnumTypeArray extends null ? ColumnType extends 'bigint' ? TypesAllowedForBigintAgainstTheDb | PermanentOpsValTypes : ModelPropertyType extends DateTime | CalendarDate ? DateTime | CalendarDate | null : ModelPropertyType extends ClockTime ? ClockTime | null : ModelPropertyType extends ClockTimeTz ? ClockTimeTz | null : ModelPropertyType extends number | string ? ModelPropertyType | PermanentOpsValTypes : never : EnumTypeArray extends string[] ? EnumTypeArray[number] | PermanentOpsValTypes : never, ResolvedOpsValType = OpsValType | RawBuilder<unknown>, PartialTypes = IsArrayColumn extends true ? (EnumTypeArray extends string[] ? EnumTypeArray : never) | OpsStatement<'=' | '!=' | '<>', ArrayOpsValType, any> | OpsStatement<KyselyComparisonOperatorExpression, PermanentOpsValTypes | RawBuilder<unknown>, any> : EnumTypeArray extends null ? ModelPropertyType extends DateTime ? Range<DateTime | CalendarDate> | Range<null, DateTime | CalendarDate> | (() => Range<DateTime | CalendarDate>) | (() => Range<null, DateTime | CalendarDate>) | OpsStatement<KyselyComparisonOperatorExpression, ResolvedOpsValType, any> : ModelPropertyType extends ClockTime ? Range<ClockTime> | Range<null, ClockTime> | (() => Range<ClockTime>) | (() => Range<null, ClockTime>) | OpsStatement<KyselyComparisonOperatorExpression, ResolvedOpsValType, any> : ModelPropertyType extends ClockTimeTz ? Range<ClockTimeTz> | Range<null, ClockTimeTz> | (() => Range<ClockTimeTz>) | (() => Range<null, ClockTimeTz>) | OpsStatement<KyselyComparisonOperatorExpression, ResolvedOpsValType, any> : ModelPropertyType extends CalendarDate ? Range<DateTime | CalendarDate> | Range<null, DateTime | CalendarDate> | (() => Range<DateTime | CalendarDate>) | (() => Range<null, DateTime | CalendarDate>) | OpsStatement<KyselyComparisonOperatorExpression, ResolvedOpsValType> : ColumnType extends 'bigint' ? Range<TypesAllowedForBigintAgainstTheDb> | Range<null, TypesAllowedForBigintAgainstTheDb> | OpsStatement<KyselyComparisonOperatorExpression, ResolvedOpsValType, any> : ModelPropertyType extends number ? Range<ModelPropertyType> | Range<null, ModelPropertyType> | OpsStatement<KyselyComparisonOperatorExpression, ResolvedOpsValType, any> : ModelPropertyType extends string ? Range<string> | Range<null, string> | OpsStatement<KyselyComparisonOperatorExpression, string, any> | OpsStatement<TrigramOperator, ResolvedOpsValType, ExtraSimilarityArgs> : never : EnumTypeArray extends string[] ? EnumTypeArray | OpsStatement<KyselyComparisonOperatorExpression, ResolvedOpsValType, any> : never> = PartialTypes extends never ? OpsStatement<KyselyComparisonOperatorExpression, ResolvedOpsValType, any> | CurriedOpsStatement<any, any, any, ResolvedOpsValType> | SelectQueryBuilder<DB, keyof DB, any> : PartialTypes | CurriedOpsStatement<any, any, any, ResolvedOpsValType> | SelectQueryBuilder<DB, keyof DB, any>;
|
|
29
29
|
export type WhereStatementForDreamClass<DreamClass extends typeof Dream> = WhereStatement<InstanceType<DreamClass>>;
|
|
30
30
|
export type WhereStatementForDream<DreamInstance extends Dream> = WhereStatement<DreamInstance>;
|
|
31
31
|
type AssociationNameToDotReferencedColumns<DB, TableName extends keyof DB, AssociationName> = `${AssociationName & string}.${TableColumnNames<DB, TableName>}`;
|
|
@@ -103,6 +103,14 @@ export type DreamTableSchema<DreamInstance extends Dream> = Updateable<DreamInst
|
|
|
103
103
|
export type ModelColumnType<Schema, TableName, Column, TableSchema extends Schema[TableName & keyof Schema] = Schema[TableName & keyof Schema], TableColumns extends TableSchema['columns' & keyof TableSchema] = TableSchema['columns' & keyof TableSchema], TableColumnMetadata extends TableColumns[Column & keyof TableColumns] = TableColumns[Column & keyof TableColumns], ColumnType extends TableColumnMetadata['coercedType' & keyof TableColumnMetadata] = TableColumnMetadata['coercedType' & keyof TableColumnMetadata]> = ColumnType;
|
|
104
104
|
export type TableColumnType<Schema, TableName, Column, TableSchema extends Schema[TableName & keyof Schema] = Schema[TableName & keyof Schema], TableColumns extends TableSchema['columns' & keyof TableSchema] = TableSchema['columns' & keyof TableSchema], TableColumnMetadata extends TableColumns[Column & keyof TableColumns] = TableColumns[Column & keyof TableColumns], ColumnType extends TableColumnMetadata['dbType' & keyof TableColumnMetadata] = TableColumnMetadata['dbType' & keyof TableColumnMetadata]> = ColumnType;
|
|
105
105
|
export type TableColumnEnumTypeArray<Schema, TableName, Column, TableSchema extends Schema[TableName & keyof Schema] = Schema[TableName & keyof Schema], TableColumns extends TableSchema['columns' & keyof TableSchema] = TableSchema['columns' & keyof TableSchema], TableColumnMetadata extends TableColumns[Column & keyof TableColumns] = TableColumns[Column & keyof TableColumns], EnumTypeCandidate = TableColumnMetadata['enumArrayType' & keyof TableColumnMetadata], EnumTypeArray extends string[] | null = EnumTypeCandidate extends null ? null : EnumTypeCandidate extends undefined ? null : EnumTypeCandidate extends string[] ? EnumTypeCandidate : never> = EnumTypeArray;
|
|
106
|
+
/**
|
|
107
|
+
* Resolves to `true` when the schema marks the column as a database array
|
|
108
|
+
* column (e.g. `text[]`, `integer[]`, `balloon_colors_enum[]`), otherwise
|
|
109
|
+
* `false`. Enum array columns carry both `enumArrayType` and `isArray: true`,
|
|
110
|
+
* so any branch keyed off array-ness must be checked before branching on
|
|
111
|
+
* `TableColumnEnumTypeArray`, which is also set for scalar enum columns.
|
|
112
|
+
*/
|
|
113
|
+
export type TableColumnIsArray<Schema, TableName, Column, TableSchema extends Schema[TableName & keyof Schema] = Schema[TableName & keyof Schema], TableColumns extends TableSchema['columns' & keyof TableSchema] = TableSchema['columns' & keyof TableSchema], TableColumnMetadata extends TableColumns[Column & keyof TableColumns] = TableColumns[Column & keyof TableColumns], IsArrayCandidate = TableColumnMetadata['isArray' & keyof TableColumnMetadata]> = [IsArrayCandidate] extends [never] ? false : IsArrayCandidate extends true ? true : false;
|
|
106
114
|
type AssociationMetadataForTable<Schema, TableName extends keyof Schema> = Schema[TableName]['associations' & keyof Schema[TableName]];
|
|
107
115
|
export type AssociationNamesForTable<Schema, TableName extends keyof Schema> = keyof AssociationMetadataForTable<Schema, TableName> | AliasedSchemaAssociation<Schema, TableName>;
|
|
108
116
|
type MetadataForAssociation<Schema, TableName extends keyof Schema, AssociationName, AssociationMetadata = AssociationMetadataForTable<Schema, TableName>> = AssociationMetadata[AssociationName & keyof AssociationMetadata];
|