@rvoh/dream 2.27.0 → 2.28.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/dist/cjs/src/Dream.js +63 -1
- package/dist/cjs/src/db/errors.js +4 -0
- package/dist/cjs/src/decorators/field/Encrypted.js +12 -0
- package/dist/cjs/src/decorators/field/sortable/Sortable.js +43 -118
- package/dist/cjs/src/decorators/field/sortable/helpers/acquireSortableScopeLocks.js +38 -0
- package/dist/cjs/src/decorators/field/sortable/helpers/acquireStabilizedSortableBatchLocks.js +183 -0
- package/dist/cjs/src/decorators/field/sortable/helpers/acquireStabilizedSortableScopeLocks.js +40 -0
- package/dist/cjs/src/decorators/field/sortable/helpers/canonicalScopeValue.js +194 -0
- package/dist/cjs/src/decorators/field/sortable/helpers/clearCachedSortableValues.js +18 -0
- package/dist/cjs/src/decorators/field/sortable/helpers/decrementScopedRecordsGreaterThanPosition.js +9 -9
- package/dist/cjs/src/decorators/field/sortable/helpers/filterQueryToInstanceScope.js +20 -0
- package/dist/cjs/src/decorators/field/sortable/helpers/filterQueryToScopeValues.js +32 -0
- package/dist/cjs/src/decorators/field/sortable/helpers/heldSortableScopeLockKeys.js +34 -0
- package/dist/cjs/src/decorators/field/sortable/helpers/performSortableDestroyWork.js +21 -0
- package/dist/cjs/src/decorators/field/sortable/helpers/performSortablePositionWork.js +36 -0
- package/dist/cjs/src/decorators/field/sortable/helpers/prepareSortableFieldsForDestroy.js +56 -0
- package/dist/cjs/src/decorators/field/sortable/helpers/prepareSortableFieldsForSave.js +84 -0
- package/dist/cjs/src/decorators/field/sortable/helpers/resortAllRecords.js +186 -29
- package/dist/cjs/src/decorators/field/sortable/helpers/setPosition.js +90 -46
- package/dist/cjs/src/decorators/field/sortable/helpers/sortableRowCache.js +73 -0
- package/dist/cjs/src/decorators/field/sortable/helpers/sortableScopeColumns.js +42 -0
- package/dist/cjs/src/decorators/field/sortable/helpers/sortableScopeLockKey.js +99 -0
- package/dist/cjs/src/decorators/field/sortable/helpers/sortableScopeLockKeys.js +72 -0
- package/dist/cjs/src/decorators/field/sortable/helpers/sortableSnapshot.js +101 -0
- package/dist/cjs/src/decorators/field/sortable/helpers/stabilizeSortableScopeLocks.js +86 -0
- package/dist/cjs/src/decorators/field/sortable/helpers/validPosition.js +21 -4
- package/dist/cjs/src/decorators/field/sortable/hooks/afterSortableDestroy.js +24 -7
- package/dist/cjs/src/decorators/field/sortable/hooks/afterSortableUpdate.js +10 -0
- package/dist/cjs/src/dream/Query.js +73 -11
- package/dist/cjs/src/dream/QueryDriver/Base.js +75 -2
- package/dist/cjs/src/dream/QueryDriver/Kysely.js +11 -4
- package/dist/cjs/src/dream/QueryDriver/Postgres.js +74 -0
- package/dist/cjs/src/dream/internal/destroyDream.js +60 -6
- package/dist/cjs/src/dream/internal/saveDream.js +109 -7
- package/dist/cjs/src/dream/internal/similarity/SimilarityBuilder.js +19 -4
- package/dist/cjs/src/dream/internal/softDeleteDream.js +13 -1
- package/dist/cjs/src/dream/internal/undestroyDream.js +56 -8
- package/dist/cjs/src/dream-app/index.js +21 -0
- package/dist/cjs/src/encrypt/assertNoEncryptedColumnWrites.js +62 -0
- package/dist/cjs/src/errors/SortableBatchRequiresTooManyScopeLocks.js +57 -0
- package/dist/cjs/src/errors/SortableRequiresAdvisoryTransactionLocks.js +26 -0
- package/dist/cjs/src/errors/SortableScopeDidNotStabilize.js +39 -0
- package/dist/cjs/src/errors/SortableScopeLockWaitTimedOut.js +50 -0
- package/dist/cjs/src/errors/encrypt/CannotSetEncryptedColumnInQueryUpdate.js +31 -0
- package/dist/cjs/src/package-exports/errors.js +3 -0
- package/dist/esm/src/Dream.js +63 -1
- package/dist/esm/src/db/errors.js +4 -0
- package/dist/esm/src/decorators/field/Encrypted.js +12 -0
- package/dist/esm/src/decorators/field/sortable/Sortable.js +43 -118
- package/dist/esm/src/decorators/field/sortable/helpers/acquireSortableScopeLocks.js +38 -0
- package/dist/esm/src/decorators/field/sortable/helpers/acquireStabilizedSortableBatchLocks.js +183 -0
- package/dist/esm/src/decorators/field/sortable/helpers/acquireStabilizedSortableScopeLocks.js +40 -0
- package/dist/esm/src/decorators/field/sortable/helpers/canonicalScopeValue.js +194 -0
- package/dist/esm/src/decorators/field/sortable/helpers/clearCachedSortableValues.js +18 -0
- package/dist/esm/src/decorators/field/sortable/helpers/decrementScopedRecordsGreaterThanPosition.js +9 -9
- package/dist/esm/src/decorators/field/sortable/helpers/filterQueryToInstanceScope.js +20 -0
- package/dist/esm/src/decorators/field/sortable/helpers/filterQueryToScopeValues.js +32 -0
- package/dist/esm/src/decorators/field/sortable/helpers/heldSortableScopeLockKeys.js +34 -0
- package/dist/esm/src/decorators/field/sortable/helpers/performSortableDestroyWork.js +21 -0
- package/dist/esm/src/decorators/field/sortable/helpers/performSortablePositionWork.js +36 -0
- package/dist/esm/src/decorators/field/sortable/helpers/prepareSortableFieldsForDestroy.js +56 -0
- package/dist/esm/src/decorators/field/sortable/helpers/prepareSortableFieldsForSave.js +84 -0
- package/dist/esm/src/decorators/field/sortable/helpers/resortAllRecords.js +186 -29
- package/dist/esm/src/decorators/field/sortable/helpers/setPosition.js +90 -46
- package/dist/esm/src/decorators/field/sortable/helpers/sortableRowCache.js +73 -0
- package/dist/esm/src/decorators/field/sortable/helpers/sortableScopeColumns.js +42 -0
- package/dist/esm/src/decorators/field/sortable/helpers/sortableScopeLockKey.js +99 -0
- package/dist/esm/src/decorators/field/sortable/helpers/sortableScopeLockKeys.js +72 -0
- package/dist/esm/src/decorators/field/sortable/helpers/sortableSnapshot.js +101 -0
- package/dist/esm/src/decorators/field/sortable/helpers/stabilizeSortableScopeLocks.js +86 -0
- package/dist/esm/src/decorators/field/sortable/helpers/validPosition.js +21 -4
- package/dist/esm/src/decorators/field/sortable/hooks/afterSortableDestroy.js +24 -7
- package/dist/esm/src/decorators/field/sortable/hooks/afterSortableUpdate.js +10 -0
- package/dist/esm/src/dream/Query.js +73 -11
- package/dist/esm/src/dream/QueryDriver/Base.js +75 -2
- package/dist/esm/src/dream/QueryDriver/Kysely.js +11 -4
- package/dist/esm/src/dream/QueryDriver/Postgres.js +74 -0
- package/dist/esm/src/dream/internal/destroyDream.js +60 -6
- package/dist/esm/src/dream/internal/saveDream.js +109 -7
- package/dist/esm/src/dream/internal/similarity/SimilarityBuilder.js +19 -4
- package/dist/esm/src/dream/internal/softDeleteDream.js +13 -1
- package/dist/esm/src/dream/internal/undestroyDream.js +56 -8
- package/dist/esm/src/dream-app/index.js +21 -0
- package/dist/esm/src/encrypt/assertNoEncryptedColumnWrites.js +62 -0
- package/dist/esm/src/errors/SortableBatchRequiresTooManyScopeLocks.js +57 -0
- package/dist/esm/src/errors/SortableRequiresAdvisoryTransactionLocks.js +26 -0
- package/dist/esm/src/errors/SortableScopeDidNotStabilize.js +39 -0
- package/dist/esm/src/errors/SortableScopeLockWaitTimedOut.js +50 -0
- package/dist/esm/src/errors/encrypt/CannotSetEncryptedColumnInQueryUpdate.js +31 -0
- package/dist/esm/src/package-exports/errors.js +3 -0
- package/dist/types/src/Dream.d.ts +56 -0
- package/dist/types/src/db/errors.d.ts +2 -0
- package/dist/types/src/decorators/field/sortable/Sortable.d.ts +33 -0
- package/dist/types/src/decorators/field/sortable/helpers/acquireSortableScopeLocks.d.ts +25 -0
- package/dist/types/src/decorators/field/sortable/helpers/acquireStabilizedSortableBatchLocks.d.ts +91 -0
- package/dist/types/src/decorators/field/sortable/helpers/acquireStabilizedSortableScopeLocks.d.ts +31 -0
- package/dist/types/src/decorators/field/sortable/helpers/canonicalScopeValue.d.ts +85 -0
- package/dist/types/src/decorators/field/sortable/helpers/clearCachedSortableValues.d.ts +13 -0
- package/dist/types/src/decorators/field/sortable/helpers/decrementScopedRecordsGreaterThanPosition.d.ts +8 -1
- package/dist/types/src/decorators/field/sortable/helpers/filterQueryToInstanceScope.d.ts +24 -0
- package/dist/types/src/decorators/field/sortable/helpers/filterQueryToScopeValues.d.ts +22 -0
- package/dist/types/src/decorators/field/sortable/helpers/heldSortableScopeLockKeys.d.ts +27 -0
- package/dist/types/src/decorators/field/sortable/helpers/performSortableDestroyWork.d.ts +14 -0
- package/dist/types/src/decorators/field/sortable/helpers/performSortablePositionWork.d.ts +15 -0
- package/dist/types/src/decorators/field/sortable/helpers/prepareSortableFieldsForDestroy.d.ts +41 -0
- package/dist/types/src/decorators/field/sortable/helpers/prepareSortableFieldsForSave.d.ts +35 -0
- package/dist/types/src/decorators/field/sortable/helpers/resortAllRecords.d.ts +32 -0
- package/dist/types/src/decorators/field/sortable/helpers/setPosition.d.ts +27 -11
- package/dist/types/src/decorators/field/sortable/helpers/sortableRowCache.d.ts +48 -0
- package/dist/types/src/decorators/field/sortable/helpers/sortableScopeColumns.d.ts +30 -0
- package/dist/types/src/decorators/field/sortable/helpers/sortableScopeLockKey.d.ts +44 -0
- package/dist/types/src/decorators/field/sortable/helpers/sortableScopeLockKeys.d.ts +51 -0
- package/dist/types/src/decorators/field/sortable/helpers/sortableSnapshot.d.ts +92 -0
- package/dist/types/src/decorators/field/sortable/helpers/stabilizeSortableScopeLocks.d.ts +59 -0
- package/dist/types/src/decorators/field/sortable/helpers/validPosition.d.ts +14 -2
- package/dist/types/src/decorators/field/sortable/hooks/afterSortableCreate.d.ts +1 -1
- package/dist/types/src/decorators/field/sortable/hooks/afterSortableDestroy.d.ts +3 -1
- package/dist/types/src/decorators/field/sortable/hooks/afterSortableUpdate.d.ts +1 -1
- package/dist/types/src/dream/Query.d.ts +47 -9
- package/dist/types/src/dream/QueryDriver/Base.d.ts +61 -1
- package/dist/types/src/dream/QueryDriver/Kysely.d.ts +6 -2
- package/dist/types/src/dream/QueryDriver/Postgres.d.ts +40 -0
- package/dist/types/src/dream/internal/softDeleteDream.d.ts +7 -1
- package/dist/types/src/dream-app/index.d.ts +18 -2
- package/dist/types/src/encrypt/assertNoEncryptedColumnWrites.d.ts +27 -0
- package/dist/types/src/errors/SortableBatchRequiresTooManyScopeLocks.d.ts +37 -0
- package/dist/types/src/errors/SortableRequiresAdvisoryTransactionLocks.d.ts +13 -0
- package/dist/types/src/errors/SortableScopeDidNotStabilize.d.ts +23 -0
- package/dist/types/src/errors/SortableScopeLockWaitTimedOut.d.ts +34 -0
- package/dist/types/src/errors/encrypt/CannotSetEncryptedColumnInQueryUpdate.d.ts +8 -0
- package/dist/types/src/package-exports/errors.d.ts +3 -0
- package/docs/assets/hierarchy.js +1 -1
- package/docs/assets/navigation.js +1 -1
- package/docs/assets/search.js +1 -1
- package/docs/classes/db.DreamMigrationHelpers.html +11 -11
- package/docs/classes/db.KyselyQueryDriver.html +88 -41
- package/docs/classes/db.PostgresQueryDriver.html +82 -42
- package/docs/classes/db.QueryDriverBase.html +86 -39
- 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.SortableBatchRequiresTooManyScopeLocks.html +61 -0
- package/docs/classes/errors.SortableScopeDidNotStabilize.html +48 -0
- package/docs/classes/errors.SortableScopeLockWaitTimedOut.html +58 -0
- 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 +151 -124
- package/docs/classes/index.DreamApp.html +11 -10
- package/docs/classes/index.DreamTransaction.html +2 -2
- package/docs/classes/index.Env.html +2 -2
- package/docs/classes/index.Query.html +124 -98
- 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 +2 -2
- 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/modules/errors.html +1 -1
- 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 +1 -1
- 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
- package/dist/cjs/src/decorators/field/sortable/helpers/applySortableScopeToQuery.js +0 -13
- package/dist/cjs/src/decorators/field/sortable/helpers/sortableQueryExcludingDream.js +0 -7
- package/dist/cjs/src/decorators/field/sortable/hooks/beforeSortableSave.js +0 -41
- package/dist/esm/src/decorators/field/sortable/helpers/applySortableScopeToQuery.js +0 -13
- package/dist/esm/src/decorators/field/sortable/helpers/sortableQueryExcludingDream.js +0 -7
- package/dist/esm/src/decorators/field/sortable/hooks/beforeSortableSave.js +0 -41
- package/dist/types/src/decorators/field/sortable/helpers/applySortableScopeToQuery.d.ts +0 -12
- package/dist/types/src/decorators/field/sortable/helpers/sortableQueryExcludingDream.d.ts +0 -12
- package/dist/types/src/decorators/field/sortable/hooks/beforeSortableSave.d.ts +0 -12
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import Dream from '../../../../Dream.js';
|
|
2
|
+
import DreamTransaction from '../../../../dream/DreamTransaction.js';
|
|
3
|
+
import { SortableFieldConfig } from '../Sortable.js';
|
|
4
|
+
/**
|
|
5
|
+
* @internal
|
|
6
|
+
*
|
|
7
|
+
* What a sortable position-mutating path knows about the row as it physically
|
|
8
|
+
* exists, rather than as the in-memory instance remembers it.
|
|
9
|
+
*
|
|
10
|
+
* `position` is the row's real position; `scopeValues` are the row's real sort
|
|
11
|
+
* scope column values, keyed by column. An instance loaded before another
|
|
12
|
+
* writer moved the row carries neither.
|
|
13
|
+
*/
|
|
14
|
+
export interface SortableSnapshot {
|
|
15
|
+
position: number | null;
|
|
16
|
+
scopeValues: Map<string, unknown>;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* @internal
|
|
20
|
+
*
|
|
21
|
+
* Snapshots keyed by position field — a model may declare several sortable
|
|
22
|
+
* fields, each with its own scope and its own position space.
|
|
23
|
+
*/
|
|
24
|
+
export type SortableSnapshots = Map<string, SortableSnapshot>;
|
|
25
|
+
/**
|
|
26
|
+
* @internal
|
|
27
|
+
*
|
|
28
|
+
* The outcome of a snapshot read: the snapshots themselves, plus whether the
|
|
29
|
+
* row was physically there to read.
|
|
30
|
+
*
|
|
31
|
+
* The two are deliberately separate. An empty `snapshots` map means only "no
|
|
32
|
+
* snapshot is available", which the model having no sortable fields produces
|
|
33
|
+
* just as readily as a row another writer has already deleted — and a caller
|
|
34
|
+
* that reads absence out of the empty map compacts a scope this operation never
|
|
35
|
+
* vacated.
|
|
36
|
+
*/
|
|
37
|
+
export interface SortableSnapshotRead {
|
|
38
|
+
/**
|
|
39
|
+
* False only when a read positively found no row. A read that never had to
|
|
40
|
+
* run — a model with no sortable fields — reports true, since it observed
|
|
41
|
+
* nothing.
|
|
42
|
+
*/
|
|
43
|
+
rowExists: boolean;
|
|
44
|
+
snapshots: SortableSnapshots;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* @internal
|
|
48
|
+
*
|
|
49
|
+
* Reads the record's real position and sort scope column values straight from
|
|
50
|
+
* the row, inside the given transaction.
|
|
51
|
+
*
|
|
52
|
+
* The read deliberately bypasses Dream's default scopes and goes to the table
|
|
53
|
+
* by primary key: soft-deleted rows have to be readable here (undestroy reads
|
|
54
|
+
* one), and an STI child's row is the same physical row whatever scope the
|
|
55
|
+
* class carries.
|
|
56
|
+
*
|
|
57
|
+
* When the row is not there the snapshots are empty and `rowExists` is false;
|
|
58
|
+
* a caller that goes on to compute a shift falls back to the instance, which is
|
|
59
|
+
* the pre-existing behavior.
|
|
60
|
+
*
|
|
61
|
+
* @param dream - the record to snapshot
|
|
62
|
+
* @param txn - the transaction the read runs in; the read must be protected by
|
|
63
|
+
* whatever scope lock the caller holds in that transaction
|
|
64
|
+
* @param configs - the sortable fields whose position and scope columns to read
|
|
65
|
+
*/
|
|
66
|
+
export declare function readSortableSnapshots(dream: Dream, txn: DreamTransaction<any>, configs: SortableFieldConfig[]): Promise<SortableSnapshotRead>;
|
|
67
|
+
/**
|
|
68
|
+
* @internal
|
|
69
|
+
*
|
|
70
|
+
* Stashes snapshots on the instance so the phase that computes the shift can
|
|
71
|
+
* reach the read the earlier phase made. The read and the shift are separate
|
|
72
|
+
* calls on every path — `prepareSortableFieldsForDestroy` to
|
|
73
|
+
* `performSortableDestroyWork`, the `saveDream` lock acquisition to
|
|
74
|
+
* `performSortablePositionWork` — and the instance is what crosses between
|
|
75
|
+
* them.
|
|
76
|
+
*/
|
|
77
|
+
export declare function cacheSortableSnapshots(dream: Dream, snapshots: SortableSnapshots): void;
|
|
78
|
+
/**
|
|
79
|
+
* @internal
|
|
80
|
+
*/
|
|
81
|
+
export declare function sortableSnapshotFor(dream: Dream, positionField: string): SortableSnapshot | undefined;
|
|
82
|
+
/**
|
|
83
|
+
* @internal
|
|
84
|
+
*/
|
|
85
|
+
export declare function clearSortableSnapshot(dream: Dream, positionField: string): void;
|
|
86
|
+
/**
|
|
87
|
+
* @internal
|
|
88
|
+
*
|
|
89
|
+
* The value to filter a sort scope column by: the row's real value when a
|
|
90
|
+
* snapshot is available, and the caller's own fallback when it is not.
|
|
91
|
+
*/
|
|
92
|
+
export declare function snapshotScopeValue(snapshot: SortableSnapshot | undefined, column: string, fallback: () => unknown): unknown;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import Dream from '../../../../Dream.js';
|
|
2
|
+
import DreamTransaction from '../../../../dream/DreamTransaction.js';
|
|
3
|
+
/**
|
|
4
|
+
* How many acquisition passes a position-mutating operation will make while
|
|
5
|
+
* following a record that has been moved to another sort scope, before giving
|
|
6
|
+
* up. One pass is the ordinary case (lock what the caller says, read, agree); a
|
|
7
|
+
* second covers a competitor that moved the row between the two; a third exists
|
|
8
|
+
* so that a single unlucky retry is not fatal. Beyond that the row is being
|
|
9
|
+
* moved faster than it can be read, and raising is better than compacting a
|
|
10
|
+
* scope it no longer occupies.
|
|
11
|
+
*
|
|
12
|
+
* The bound counts acquisitions, and every one of them counts: the convergence
|
|
13
|
+
* check runs after the last acquisition and the read taken under it, so an
|
|
14
|
+
* operation that settles on its final pass settles rather than raising.
|
|
15
|
+
*/
|
|
16
|
+
export declare const MAX_SCOPE_STABILIZATION_ROUNDS = 3;
|
|
17
|
+
/**
|
|
18
|
+
* @internal
|
|
19
|
+
*
|
|
20
|
+
* The stabilization protocol every sortable lock acquisition follows, in one
|
|
21
|
+
* place: acquire the keys the current knowledge implies, read again under those
|
|
22
|
+
* locks, and take whatever additional key the read reveals — until a pass
|
|
23
|
+
* reveals nothing new, or the bound above is exhausted.
|
|
24
|
+
*
|
|
25
|
+
* The bootstrapping problem it solves: a sort scope's lock key is derived from
|
|
26
|
+
* the record's *physical* scope columns, and those cannot be read safely until
|
|
27
|
+
* that scope is locked. So the first pass locks what the caller can know without
|
|
28
|
+
* reading — an instance's own values, or an unprotected candidate read — and
|
|
29
|
+
* every later pass locks what the read taken under those locks reveals.
|
|
30
|
+
*
|
|
31
|
+
* Nothing here is ever unwound: the locks are transaction-scoped advisory locks,
|
|
32
|
+
* which are additive and are released only when the transaction ends. A pass
|
|
33
|
+
* that discovers a new key simply also takes it, holding at worst one lock too
|
|
34
|
+
* many for the rest of the transaction — which costs contention and never
|
|
35
|
+
* correctness. That is what lets this run inside a caller-owned transaction
|
|
36
|
+
* unchanged; no path here restarts or rolls back a transaction it did not open.
|
|
37
|
+
*
|
|
38
|
+
* Keys the transaction already holds — taken by an earlier phase of the same
|
|
39
|
+
* operation, or by the preflight of the locked batch this record is being
|
|
40
|
+
* walked in — are not taken again; they are held to the end of the transaction
|
|
41
|
+
* regardless. Convergence is judged on that same set, so a pass whose read
|
|
42
|
+
* reveals only keys already held is a pass that agrees.
|
|
43
|
+
*
|
|
44
|
+
* Keys are taken in sorted order within a pass, so that two operations touching
|
|
45
|
+
* the same set of scopes cannot deadlock against each other. Across passes the
|
|
46
|
+
* order is discovery-driven and therefore unsortable in principle, which is the
|
|
47
|
+
* named fallback: two operations moving records in opposite directions between
|
|
48
|
+
* the same two scopes can raise Postgres's `40P01` deadlock error, a visible
|
|
49
|
+
* retryable failure.
|
|
50
|
+
*
|
|
51
|
+
* @param dream - a record of the model being locked; supplies the connection
|
|
52
|
+
* and, when this fails, the class name in the error
|
|
53
|
+
* @param txn - the transaction the locks are scoped to
|
|
54
|
+
* @param keysForRound - reads whatever this pass can see — the caller's own
|
|
55
|
+
* snapshot or candidate read — and returns every lock key it implies. Called
|
|
56
|
+
* once per pass with the number of acquisitions made so far, so a caller whose
|
|
57
|
+
* first pass needs no read can skip it on round 0
|
|
58
|
+
*/
|
|
59
|
+
export default function stabilizeSortableScopeLocks(dream: Dream, txn: DreamTransaction<any>, keysForRound: (round: number) => Promise<Iterable<bigint>>): Promise<void>;
|
|
@@ -1,2 +1,14 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import { SortablePositionWrite } from './setPosition.js';
|
|
2
|
+
/**
|
|
3
|
+
* @internal
|
|
4
|
+
*
|
|
5
|
+
* The position the record will be written at: the caller's, clamped to the
|
|
6
|
+
* scope the record now occupies.
|
|
7
|
+
*
|
|
8
|
+
* The scope is read off the instance rather than off the snapshot because the
|
|
9
|
+
* scope in question is the one being *entered*, which is the instance's — the
|
|
10
|
+
* snapshot holds the scope the row was in before the driver write. On every
|
|
11
|
+
* path into here the driver write has already landed the instance's scope
|
|
12
|
+
* values on the row.
|
|
13
|
+
*/
|
|
14
|
+
export default function validPosition({ changingScope, dream, position, positionField, positionUnderSentinel, query, scope, wasNewRecord, previousPosition, txn, }: SortablePositionWrite): Promise<number>;
|
|
@@ -5,6 +5,6 @@ export default function afterSortableCreate({ positionField, dream, query, txn,
|
|
|
5
5
|
positionField: string;
|
|
6
6
|
dream: Dream;
|
|
7
7
|
query: Query<Dream>;
|
|
8
|
-
txn
|
|
8
|
+
txn: DreamTransaction<any>;
|
|
9
9
|
scope: string | string[] | undefined;
|
|
10
10
|
}): Promise<void>;
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import Dream from '../../../../Dream.js';
|
|
2
|
+
import DreamTransaction from '../../../../dream/DreamTransaction.js';
|
|
2
3
|
import Query from '../../../../dream/Query.js';
|
|
3
|
-
export default function afterSortableDestroy({ positionField, dream, query, scope, }: {
|
|
4
|
+
export default function afterSortableDestroy({ positionField, dream, query, scope, txn, }: {
|
|
4
5
|
positionField: string;
|
|
5
6
|
dream: Dream;
|
|
6
7
|
query: Query<Dream>;
|
|
7
8
|
scope: string | string[] | undefined;
|
|
9
|
+
txn: DreamTransaction<any>;
|
|
8
10
|
}): Promise<void>;
|
|
@@ -5,6 +5,6 @@ export default function afterUpdateSortable({ positionField, dream, query, txn,
|
|
|
5
5
|
positionField: string;
|
|
6
6
|
dream: Dream;
|
|
7
7
|
query: Query<Dream>;
|
|
8
|
-
txn
|
|
8
|
+
txn: DreamTransaction<any>;
|
|
9
9
|
scope: string | string[] | undefined;
|
|
10
10
|
}): Promise<void>;
|
|
@@ -1328,9 +1328,11 @@ export default class Query<DreamInstance extends Dream, QueryTypeOpts extends Re
|
|
|
1328
1328
|
* read is a plain `FOR UPDATE`: if another transaction already holds a lock
|
|
1329
1329
|
* on one of the batch's rows, this call blocks until that transaction ends,
|
|
1330
1330
|
* holding its own transaction and its pooled connection open the whole
|
|
1331
|
-
* time. Dream sets no `lock_timeout` or `statement_timeout
|
|
1332
|
-
*
|
|
1333
|
-
*
|
|
1331
|
+
* time. Dream sets no `lock_timeout` or `statement_timeout` on a row-lock
|
|
1332
|
+
* wait — the one it does set, around a sortable model's scope-lock
|
|
1333
|
+
* acquisition, is restored before the rows are claimed — so a long-running
|
|
1334
|
+
* writer on the other side stalls the run indefinitely and, at scale, ties
|
|
1335
|
+
* up connections. If that is a risk in your application, set a
|
|
1334
1336
|
* `lock_timeout` on the connection so a stuck batch fails instead of
|
|
1335
1337
|
* hanging.
|
|
1336
1338
|
* - **A `limit` or `offset` on the Query is incompatible with `destroy` on
|
|
@@ -1408,6 +1410,36 @@ export default class Query<DreamInstance extends Dream, QueryTypeOpts extends Re
|
|
|
1408
1410
|
* When the Query already carries a transaction, that transaction is used for
|
|
1409
1411
|
* every batch rather than opening one per batch, so the caller's transaction
|
|
1410
1412
|
* boundary wins and the locks are held until the caller commits.
|
|
1413
|
+
*
|
|
1414
|
+
* On a sortable model, the advisory scope keys this batch needs are acquired
|
|
1415
|
+
* before the claim (see `acquireStabilizedSortableBatchLocks`), which removes
|
|
1416
|
+
* the row-lock/advisory-lock inversion for the rows the batch itself claims.
|
|
1417
|
+
* Two things stay outside that guarantee and are fallbacks rather than
|
|
1418
|
+
* guarantees, with Postgres's `40P01` as the residual failure in both:
|
|
1419
|
+
*
|
|
1420
|
+
* - the ordering is **per batch**. A multi-batch run inside a caller-owned
|
|
1421
|
+
* transaction keeps batch N-1's row locks while batch N preflights. The
|
|
1422
|
+
* same shape accumulates *advisory* locks: they are released only when the
|
|
1423
|
+
* transaction ends, so every batch's scope keys are still held when the
|
|
1424
|
+
* next one preflights. That accumulation is bounded — the preflight counts
|
|
1425
|
+
* the keys the transaction already holds along with the ones the batch
|
|
1426
|
+
* would add, and refuses with `SortableBatchRequiresTooManyScopeLocks`
|
|
1427
|
+
* rather than letting a long run over a high-cardinality sort scope exhaust
|
|
1428
|
+
* `max_locks_per_transaction` for the whole cluster — so a run long enough
|
|
1429
|
+
* raises instead of completing. Letting Dream open a transaction per batch
|
|
1430
|
+
* releases the keys as it goes and gives each batch the whole budget.
|
|
1431
|
+
* - the preflight covers **the claimed rows' own scopes only**. A
|
|
1432
|
+
* `dependent: 'destroy'` cascade under `destroy({ lock: true })` destroys
|
|
1433
|
+
* child records of other tables, and a sortable child takes its own table's
|
|
1434
|
+
* advisory key inside `perBatch` — after this transaction is already
|
|
1435
|
+
* holding the root rows' row locks. Walking
|
|
1436
|
+
* `dependentDestroyAssociationNames()` to fold those keys into the sorted
|
|
1437
|
+
* pass is deliberately not done: the cascade's shape is only knowable per
|
|
1438
|
+
* record, and the deadlock it leaves is detected and retryable. The keys
|
|
1439
|
+
* the preflight *did* take are held across the whole batch, cascade
|
|
1440
|
+
* included — a per-record destroy holds its scope lock across one delete
|
|
1441
|
+
* and one compaction, while here the hold spans batch size × cascade depth.
|
|
1442
|
+
* A smaller `batchSize` shortens it (the default is 10).
|
|
1411
1443
|
*/
|
|
1412
1444
|
private lockedBatches;
|
|
1413
1445
|
/**
|
|
@@ -1489,7 +1521,7 @@ export default class Query<DreamInstance extends Dream, QueryTypeOpts extends Re
|
|
|
1489
1521
|
* use {@link Query.destroy} instead.
|
|
1490
1522
|
*
|
|
1491
1523
|
* ```ts
|
|
1492
|
-
* await User.where({ email: ops.ilike('%burpcollaborator%').delete()
|
|
1524
|
+
* await User.where({ email: ops.ilike('%burpcollaborator%') }).delete()
|
|
1493
1525
|
* // 12
|
|
1494
1526
|
* ```
|
|
1495
1527
|
*
|
|
@@ -1651,14 +1683,19 @@ export default class Query<DreamInstance extends Dream, QueryTypeOpts extends Re
|
|
|
1651
1683
|
* read is a plain `FOR UPDATE`: if another transaction already holds a
|
|
1652
1684
|
* lock on one of the batch's rows, this call blocks until that transaction
|
|
1653
1685
|
* ends, holding its own transaction and its pooled connection open the
|
|
1654
|
-
* whole time. Dream sets no `lock_timeout` or `statement_timeout
|
|
1655
|
-
*
|
|
1656
|
-
*
|
|
1657
|
-
*
|
|
1686
|
+
* whole time. Dream sets no `lock_timeout` or `statement_timeout` on a
|
|
1687
|
+
* row-lock wait — the one it does set, around a sortable model's scope-lock
|
|
1688
|
+
* acquisition, is restored before the rows are claimed — so a long-running
|
|
1689
|
+
* writer on the other side stalls the run indefinitely and, at scale, ties
|
|
1690
|
+
* up connections. If that is a risk in your application, set a
|
|
1691
|
+
* `lock_timeout` on the connection so a stuck batch fails instead of
|
|
1658
1692
|
* hanging.
|
|
1659
1693
|
* - **`skipHooks` composes with `lock`, but stays on the per-instance
|
|
1660
1694
|
* path.** With an attributes object and no `lock`, `skipHooks: true`
|
|
1661
|
-
* issues a single `UPDATE ... WHERE` statement that writes raw columns
|
|
1695
|
+
* issues a single `UPDATE ... WHERE` statement that writes raw columns —
|
|
1696
|
+
* with one refusal: it will not write a non-null value into an
|
|
1697
|
+
* `@Encrypted` backing column, since nothing on that path encrypts it, and
|
|
1698
|
+
* throws instead; `null`, which clears the column, is permitted.
|
|
1662
1699
|
* With `lock: true`, `skipHooks: true` skips the model hooks on each
|
|
1663
1700
|
* claimed record but still writes through the instance, so custom setters
|
|
1664
1701
|
* — including the setters encrypted attributes encrypt through — run as
|
|
@@ -1681,6 +1718,7 @@ export default class Query<DreamInstance extends Dream, QueryTypeOpts extends Re
|
|
|
1681
1718
|
* @throws InvalidBatchSize if `batchSize` is not a positive integer
|
|
1682
1719
|
* @throws MissingRequiredLockOptionForUpdateCallback if a callback is passed without an options object carrying a boolean `lock`
|
|
1683
1720
|
* @throws BatchingIncompatibleWithLimitOrOffset if the query carries a `limit` or `offset`
|
|
1721
|
+
* @throws CannotSetEncryptedColumnInQueryUpdate if the single-statement `skipHooks` form's attributes name an `@Encrypted` backing column with anything other than `null`
|
|
1684
1722
|
*/
|
|
1685
1723
|
update(cb: (record: DreamInstance) => UpdateableProperties<DreamInstance> | undefined | Promise<UpdateableProperties<DreamInstance> | undefined>, options: {
|
|
1686
1724
|
lock: boolean;
|
|
@@ -145,6 +145,62 @@ export default class QueryDriverBase<DreamInstance extends Dream> {
|
|
|
145
145
|
* worker's lifetime.
|
|
146
146
|
*/
|
|
147
147
|
static openTestDatabaseLockSession(connectionName: string): Promise<TestDatabaseLockSession>;
|
|
148
|
+
/**
|
|
149
|
+
* @internal
|
|
150
|
+
*
|
|
151
|
+
* Whether this driver implements the transaction-scoped advisory lock seam
|
|
152
|
+
* ({@link acquireAdvisoryTransactionLocks}). Adapters that can hold a
|
|
153
|
+
* key-addressed lock for the life of a transaction override this to `true`.
|
|
154
|
+
* It defaults to `false` so that a driver without the primitive fails loud
|
|
155
|
+
* for features that depend on it (today, the `Sortable` decorator) rather
|
|
156
|
+
* than silently racing on position values.
|
|
157
|
+
*/
|
|
158
|
+
static supportsAdvisoryTransactionLocks: boolean;
|
|
159
|
+
/**
|
|
160
|
+
* @internal
|
|
161
|
+
*
|
|
162
|
+
* Acquire exclusive, transaction-scoped locks on arbitrary keys, waiting
|
|
163
|
+
* until any other transaction holding one of them finishes. The locks are
|
|
164
|
+
* released when the enclosing transaction commits or rolls back — never by an
|
|
165
|
+
* explicit unlock — so callers only ever have to acquire (Postgres:
|
|
166
|
+
* `pg_advisory_xact_lock(bigint)`).
|
|
167
|
+
*
|
|
168
|
+
* Unlike {@link applyRowLock}, this does not depend on rows existing: it is
|
|
169
|
+
* the only way to serialize writers that are about to insert into the same
|
|
170
|
+
* logical group. The `Sortable` decorator uses it to make each sort scope's
|
|
171
|
+
* position computation exclusive, keyed on the physical database, table,
|
|
172
|
+
* position field and scope values.
|
|
173
|
+
*
|
|
174
|
+
* The seam takes the whole key set rather than one key at a time so that a
|
|
175
|
+
* driver able to express it can take them in a single round trip — the
|
|
176
|
+
* batched, row-locking query APIs can need one key per candidate record per
|
|
177
|
+
* sortable field. **Keys must be acquired in the order given**: callers sort
|
|
178
|
+
* them, and that shared order is what keeps two operations over the same
|
|
179
|
+
* scopes from deadlocking. A driver that has no bulk form implements this by
|
|
180
|
+
* looping over the keys in order.
|
|
181
|
+
*
|
|
182
|
+
* **The wait must be bounded.** A caller that finds a key held waits for the
|
|
183
|
+
* holder's transaction, and an implementation that waits forever turns an
|
|
184
|
+
* ordinary mistake — a sortable operation issued without the enclosing
|
|
185
|
+
* transaction, so that it waits on a key that transaction itself holds — into
|
|
186
|
+
* a permanently wedged connection that no deadlock detector can see, because
|
|
187
|
+
* only one side is waiting. So an implementation applies the
|
|
188
|
+
* `sortableScopeLockTimeout` DreamApp option (milliseconds) to the
|
|
189
|
+
* acquisition and raises `SortableScopeLockWaitTimedOut` when it elapses. A
|
|
190
|
+
* configured `0` means no bound, and is applied just as literally: the
|
|
191
|
+
* acquisition waits without bound even where the connection carries a
|
|
192
|
+
* wait bound of its own. The bound belongs to
|
|
193
|
+
* the acquisition alone: whatever lock-wait behavior the connection was
|
|
194
|
+
* configured with has to be in force again by the time this returns, so that
|
|
195
|
+
* the row locks {@link applyRowLock} takes are unaffected.
|
|
196
|
+
*
|
|
197
|
+
* Only ever called when {@link supportsAdvisoryTransactionLocks} is `true`;
|
|
198
|
+
* the base default throws.
|
|
199
|
+
*
|
|
200
|
+
* @param txn - the transaction the locks are scoped to
|
|
201
|
+
* @param keys - the 64-bit signed lock keys, in acquisition order
|
|
202
|
+
*/
|
|
203
|
+
static acquireAdvisoryTransactionLocks(txn: DreamTransaction<Dream>, keys: bigint[]): Promise<void>;
|
|
148
204
|
static getColumnData(connectionName: string, tableName: string, allTableAssociationData: {
|
|
149
205
|
[key: string]: SchemaBuilderAssociationData;
|
|
150
206
|
}): Promise<{
|
|
@@ -391,8 +447,12 @@ export default class QueryDriverBase<DreamInstance extends Dream> {
|
|
|
391
447
|
* @param dream - the dream instance you wish to destroy
|
|
392
448
|
* @param txn - a transaction to encapsulate, consistently provided by underlying dream mechanisms
|
|
393
449
|
* @param reallyDestroy - whether or not to reallyDestroy. If false, soft delete will be attempted when relevant
|
|
450
|
+
* @returns the number of rows this call removed (or soft deleted). Zero means
|
|
451
|
+
* the row was already gone, or that a `preventDeletion` veto stopped the
|
|
452
|
+
* write — callers distinguish the two by the veto flag, and use the count to
|
|
453
|
+
* decide whether there is anything for the after-destroy hooks to act on.
|
|
394
454
|
*/
|
|
395
|
-
static destroyDream(dream: Dream, txn: DreamTransaction<Dream>, reallyDestroy: boolean): Promise<
|
|
455
|
+
static destroyDream(dream: Dream, txn: DreamTransaction<Dream>, reallyDestroy: boolean): Promise<number>;
|
|
396
456
|
static get syncDialect(): string;
|
|
397
457
|
/**
|
|
398
458
|
* Returns the sql that would be executed by this Query
|
|
@@ -60,13 +60,17 @@ export default class KyselyQueryDriver<DreamInstance extends Dream> extends Quer
|
|
|
60
60
|
static primaryKeyType(): LegacyCompatiblePrimaryKeyType;
|
|
61
61
|
/**
|
|
62
62
|
* destroys a dream, possibly implementing soft delete if reallyDestroy is false
|
|
63
|
-
* and the record being deleted implements soft delete.
|
|
63
|
+
* and the record being deleted implements soft delete. Does nothing at all when a
|
|
64
|
+
* beforeDestroy hook called `preventDeletion`.
|
|
64
65
|
*
|
|
65
66
|
* @param dream - the dream instance you wish to destroy
|
|
66
67
|
* @param txn - a transaction to encapsulate, consistently provided by underlying dream mechanisms
|
|
67
68
|
* @param reallyDestroy - whether or not to reallyDestroy. If false, soft delete will be attempted when relevant
|
|
69
|
+
* @returns the number of rows removed — zero when a `preventDeletion` veto
|
|
70
|
+
* stopped the write, and zero when the row was already gone, which on the
|
|
71
|
+
* soft delete path includes a row that is already soft deleted
|
|
68
72
|
*/
|
|
69
|
-
static destroyDream(dream: Dream, txn: DreamTransaction<Dream>, reallyDestroy: boolean): Promise<
|
|
73
|
+
static destroyDream(dream: Dream, txn: DreamTransaction<Dream>, reallyDestroy: boolean): Promise<number>;
|
|
70
74
|
/**
|
|
71
75
|
* Converts the given dream class into a Kysely query, enabling
|
|
72
76
|
* you to build custom queries using the Kysely API
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type SelectQueryBuilder } from 'kysely';
|
|
2
2
|
import Dream from '../../Dream.js';
|
|
3
3
|
import { SchemaBuilderAssociationData, SchemaBuilderColumnData } from '../../helpers/cli/ASTBuilder.js';
|
|
4
|
+
import DreamTransaction from '../DreamTransaction.js';
|
|
4
5
|
import KyselyQueryDriver from './Kysely.js';
|
|
5
6
|
import type { TestDatabaseLockSession } from './Base.js';
|
|
6
7
|
export default class PostgresQueryDriver<DreamInstance extends Dream> extends KyselyQueryDriver<DreamInstance> {
|
|
@@ -54,6 +55,45 @@ export default class PostgresQueryDriver<DreamInstance extends Dream> extends Ky
|
|
|
54
55
|
* `Query#destroy`.
|
|
55
56
|
*/
|
|
56
57
|
applyRowLock(kyselyQuery: SelectQueryBuilder<any, any, any>, tableAlias: string): SelectQueryBuilder<any, any, any>;
|
|
58
|
+
static supportsAdvisoryTransactionLocks: boolean;
|
|
59
|
+
/**
|
|
60
|
+
* @internal
|
|
61
|
+
*
|
|
62
|
+
* Postgres implementation of the transaction-scoped advisory lock seam.
|
|
63
|
+
*
|
|
64
|
+
* Uses the single-`bigint` form `pg_advisory_xact_lock(bigint)`, whose lock
|
|
65
|
+
* space is distinct from the two-`int4` form the test-database pool claims
|
|
66
|
+
* with (see {@link openTestDatabaseLockSession}), so an application lock can
|
|
67
|
+
* never collide with a pool claim.
|
|
68
|
+
*
|
|
69
|
+
* Every key is taken in one statement, over `unnest` of the key array: a
|
|
70
|
+
* function scan yields its rows in array order, so the caller's sorted
|
|
71
|
+
* acquisition order — the thing that keeps two operations over the same
|
|
72
|
+
* scopes from deadlocking — is preserved, while a batch that needs one key
|
|
73
|
+
* per candidate record costs one round trip rather than N.
|
|
74
|
+
*
|
|
75
|
+
* The locks are held until the enclosing transaction ends, and are waiting
|
|
76
|
+
* (no `NOWAIT`): a second transaction asking for the same key blocks at that
|
|
77
|
+
* key's row until the holder commits or rolls back. Because they are
|
|
78
|
+
* transaction-scoped there is nothing to release, and re-acquiring a key
|
|
79
|
+
* within one transaction is a no-op rather than an error.
|
|
80
|
+
*
|
|
81
|
+
* The wait is bounded by `lock_timeout`, taken from
|
|
82
|
+
* {@link DreamApp.sortableScopeLockTimeout} and applied to the acquisition
|
|
83
|
+
* statement alone: the prior value is read in the same statement that sets
|
|
84
|
+
* the bound and restored in the next one, so the transaction's row-lock waits
|
|
85
|
+
* keep whatever the application configured. Postgres cancels the statement
|
|
86
|
+
* with `55P03` when the bound elapses, and that is translated here into
|
|
87
|
+
* {@link SortableScopeLockWaitTimedOut} so no Postgres error code escapes the
|
|
88
|
+
* driver.
|
|
89
|
+
*
|
|
90
|
+
* A timeout of `0` means "no bound", which is what `lock_timeout` itself
|
|
91
|
+
* means by it, and is sent as the bound rather than skipped: an application
|
|
92
|
+
* whose connection carries a `lock_timeout` of its own would otherwise have
|
|
93
|
+
* its acquisition cancelled by that bound, which is neither the unbounded
|
|
94
|
+
* wait `0` asks for nor a cancellation this driver could translate.
|
|
95
|
+
*/
|
|
96
|
+
static acquireAdvisoryTransactionLocks(txn: DreamTransaction<Dream>, keys: bigint[]): Promise<void>;
|
|
57
97
|
static supportsParallelTestDatabases: boolean;
|
|
58
98
|
/**
|
|
59
99
|
* @internal
|
|
@@ -1,3 +1,9 @@
|
|
|
1
1
|
import Dream from '../../Dream.js';
|
|
2
2
|
import DreamTransaction from '../DreamTransaction.js';
|
|
3
|
-
|
|
3
|
+
/**
|
|
4
|
+
* @internal
|
|
5
|
+
*
|
|
6
|
+
* @returns the number of rows soft deleted — zero when the row is already gone,
|
|
7
|
+
* and zero when it is already soft deleted
|
|
8
|
+
*/
|
|
9
|
+
export default function softDeleteDream(dream: Dream, txn: DreamTransaction<any>): Promise<number>;
|
|
@@ -120,6 +120,22 @@ export default class DreamApp {
|
|
|
120
120
|
get paginationPageSize(): number;
|
|
121
121
|
private _paginationMaxPageSize;
|
|
122
122
|
get paginationMaxPageSize(): number;
|
|
123
|
+
/**
|
|
124
|
+
* How long, in milliseconds, a position-mutating operation on a
|
|
125
|
+
* `@deco.Sortable` model will wait for another transaction to release the
|
|
126
|
+
* sort scope it needs before giving up with
|
|
127
|
+
* `SortableScopeLockWaitTimedOut`. Applied to the lock acquisition statement
|
|
128
|
+
* alone — the transaction's other lock waits keep whatever `lock_timeout`
|
|
129
|
+
* the application has configured.
|
|
130
|
+
*
|
|
131
|
+
* `0` waits without bound, which is what Postgres's `lock_timeout` means by
|
|
132
|
+
* it, and holds even on a connection configured with a `lock_timeout` of its
|
|
133
|
+
* own: the acquisition disables the bound for the duration of the statement.
|
|
134
|
+
*
|
|
135
|
+
* Set it with `dreamApp.set('sortableScopeLockTimeout', milliseconds)`.
|
|
136
|
+
*/
|
|
137
|
+
private _sortableScopeLockTimeout;
|
|
138
|
+
get sortableScopeLockTimeout(): number;
|
|
123
139
|
private _primaryKeyType;
|
|
124
140
|
get primaryKeyType(): LegacyCompatiblePrimaryKeyType;
|
|
125
141
|
private _projectRoot;
|
|
@@ -221,7 +237,7 @@ export default class DreamApp {
|
|
|
221
237
|
private requireTestDatabaseClaimSupport;
|
|
222
238
|
load<RT extends 'models' | 'serializers'>(resourceType: RT, resourcePath: string, importCb: (path: string) => Promise<any>): Promise<void>;
|
|
223
239
|
plugin(cb: (app: DreamApp) => void | Promise<void>): void;
|
|
224
|
-
set<ApplyOpt extends DreamAppSetOption>(applyOption: ApplyOpt, options: ApplyOpt extends 'bypassDeprecationChecks' ? boolean : ApplyOpt extends 'db' ? DreamDbCredentialOptions | string : ApplyOpt extends 'encryption' ? DreamAppEncryptionOptions : ApplyOpt extends 'primaryKeyType' ? LegacyCompatiblePrimaryKeyType : ApplyOpt extends 'importExtension' ? GeneratorImportStyle : ApplyOpt extends 'logger' ? DreamLogger : ApplyOpt extends 'projectRoot' ? string : ApplyOpt extends 'inflections' ? () => void | Promise<void> : ApplyOpt extends 'packageManager' ? DreamAppAllowedPackageManagersEnum : ApplyOpt extends 'paths' ? DreamDirectoryPaths : ApplyOpt extends 'parallelTests' ? number : ApplyOpt extends 'unicodeNormalization' ? UnicodeNormalizationForm : ApplyOpt extends 'paginationPageSize' ? number : ApplyOpt extends 'paginationMaxPageSize' ? number : never, secondaryOptions?: ApplyOpt extends 'db' ? DreamDbCredentialOptions : never): void;
|
|
240
|
+
set<ApplyOpt extends DreamAppSetOption>(applyOption: ApplyOpt, options: ApplyOpt extends 'bypassDeprecationChecks' ? boolean : ApplyOpt extends 'db' ? DreamDbCredentialOptions | string : ApplyOpt extends 'encryption' ? DreamAppEncryptionOptions : ApplyOpt extends 'primaryKeyType' ? LegacyCompatiblePrimaryKeyType : ApplyOpt extends 'importExtension' ? GeneratorImportStyle : ApplyOpt extends 'logger' ? DreamLogger : ApplyOpt extends 'projectRoot' ? string : ApplyOpt extends 'inflections' ? () => void | Promise<void> : ApplyOpt extends 'packageManager' ? DreamAppAllowedPackageManagersEnum : ApplyOpt extends 'paths' ? DreamDirectoryPaths : ApplyOpt extends 'parallelTests' ? number : ApplyOpt extends 'unicodeNormalization' ? UnicodeNormalizationForm : ApplyOpt extends 'paginationPageSize' ? number : ApplyOpt extends 'paginationMaxPageSize' ? number : ApplyOpt extends 'sortableScopeLockTimeout' ? number : never, secondaryOptions?: ApplyOpt extends 'db' ? DreamDbCredentialOptions : never): void;
|
|
225
241
|
on<T extends DreamHookEventType>(hookEventType: T, cb: T extends 'db:log' ? (event: KyselyLogEvent) => void : T extends 'repl:start' ? (context: Context) => void | Promise<void> : never): void;
|
|
226
242
|
}
|
|
227
243
|
export type DreamHookEventType = 'db:log' | 'repl:start';
|
|
@@ -234,7 +250,7 @@ export interface DreamAppOpts {
|
|
|
234
250
|
serializerCasing?: DreamSerializerCasing;
|
|
235
251
|
parallelTests: number | undefined;
|
|
236
252
|
}
|
|
237
|
-
export type DreamAppSetOption = 'bypassDeprecationChecks' | 'db' | 'encryption' | 'inflections' | 'importExtension' | 'logger' | 'paths' | 'primaryKeyType' | 'projectRoot' | 'serializerCasing' | 'parallelTests' | 'unicodeNormalization' | 'paginationPageSize' | 'paginationMaxPageSize' | 'packageManager';
|
|
253
|
+
export type DreamAppSetOption = 'bypassDeprecationChecks' | 'db' | 'encryption' | 'inflections' | 'importExtension' | 'logger' | 'paths' | 'primaryKeyType' | 'projectRoot' | 'serializerCasing' | 'parallelTests' | 'unicodeNormalization' | 'paginationPageSize' | 'paginationMaxPageSize' | 'sortableScopeLockTimeout' | 'packageManager';
|
|
238
254
|
export interface DreamDirectoryPaths {
|
|
239
255
|
models?: string;
|
|
240
256
|
serializers?: string;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import Dream from '../Dream.js';
|
|
2
|
+
/**
|
|
3
|
+
* @internal
|
|
4
|
+
*
|
|
5
|
+
* Throws when `attributes` names an @Encrypted backing column with anything other
|
|
6
|
+
* than null. Guards the write paths that never instantiate a model, and so never
|
|
7
|
+
* reach the throwing setter the Encrypted decorator installs for that column.
|
|
8
|
+
*
|
|
9
|
+
* `encryptedAttributes` is class-scoped, but a query rooted at an STI base reaches
|
|
10
|
+
* every one of its children's rows on the shared table, so `dreamClass` and every STI
|
|
11
|
+
* class below it are searched for backing columns.
|
|
12
|
+
*
|
|
13
|
+
* The search deliberately stops there rather than covering the whole hierarchy. A query
|
|
14
|
+
* rooted at an STI child matches only that child's rows, and a backing column only a
|
|
15
|
+
* sibling declares holds nothing meaningful for them: writing plaintext into it corrupts
|
|
16
|
+
* no ciphertext, because there is none to corrupt. Converting such a row to the sibling
|
|
17
|
+
* type is what has to clear that column, and it has to clear it whatever this guard did.
|
|
18
|
+
* Widening the walk to the siblings therefore buys no protection, and this guard runs on
|
|
19
|
+
* every single-statement update, so keep it narrow.
|
|
20
|
+
*
|
|
21
|
+
* Keys are matched in their snakeified form, since Kysely's CamelCasePlugin
|
|
22
|
+
* resolves both the camelCase and the snake_case spelling of a key to the same
|
|
23
|
+
* physical column.
|
|
24
|
+
*
|
|
25
|
+
* null is permitted: it clears the column, and there is no plaintext to mangle.
|
|
26
|
+
*/
|
|
27
|
+
export default function assertNoEncryptedColumnWrites(dreamClass: typeof Dream, attributes: Record<string, unknown>): void;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Raised when a batch of a locked `Query#destroy` or `Query#update` on a
|
|
3
|
+
* sortable model would carry its transaction past the number of advisory
|
|
4
|
+
* sort-scope locks Dream is willing to hold at once.
|
|
5
|
+
*
|
|
6
|
+
* The batched, row-locking query APIs take every advisory scope key the batch
|
|
7
|
+
* needs before they claim a single row — one key per sort scope the batch's
|
|
8
|
+
* candidate records occupy, per sortable field the model declares. Advisory
|
|
9
|
+
* locks live in Postgres's cluster-wide shared lock table, whose size is fixed
|
|
10
|
+
* at startup (`max_locks_per_transaction` × `max_connections`), and they are
|
|
11
|
+
* held until the transaction ends. A batch over a table whose sort scope has
|
|
12
|
+
* very high cardinality can therefore consume a meaningful fraction of a
|
|
13
|
+
* resource every other connection in the cluster shares, and exhausting it
|
|
14
|
+
* fails unrelated transactions rather than only this one.
|
|
15
|
+
*
|
|
16
|
+
* So the preflight counts first and refuses, before claiming any row. The count
|
|
17
|
+
* is the whole transaction's: the keys it already holds plus the keys the
|
|
18
|
+
* refused batch would add, since advisory locks are released only when the
|
|
19
|
+
* transaction ends. Inside a transaction Dream opens for the batch that is the
|
|
20
|
+
* batch's own key set and nothing has been locked when this is raised; inside a
|
|
21
|
+
* transaction you own, it also counts every key the run's earlier batches took,
|
|
22
|
+
* and those stay held.
|
|
23
|
+
*
|
|
24
|
+
* The fix is almost always a smaller `batchSize`: the keys one batch needs are
|
|
25
|
+
* bounded by how many distinct sort scopes its records span, so halving the
|
|
26
|
+
* batch size halves the worst case. If the run itself is what accumulates them
|
|
27
|
+
* — a long multi-batch run inside a transaction you opened — either let Dream
|
|
28
|
+
* open a transaction per batch (do not pass a transaction of your own) or
|
|
29
|
+
* commit between runs, which is what releases the locks as it goes.
|
|
30
|
+
*/
|
|
31
|
+
export default class SortableBatchRequiresTooManyScopeLocks extends Error {
|
|
32
|
+
dreamClassName: string;
|
|
33
|
+
keyCount: number;
|
|
34
|
+
maxKeyCount: number;
|
|
35
|
+
constructor(dreamClassName: string, keyCount: number, maxKeyCount: number);
|
|
36
|
+
get message(): string;
|
|
37
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Raised when a model declaring sortable fields is used on a query driver that
|
|
3
|
+
* cannot take transaction-scoped advisory locks.
|
|
4
|
+
*
|
|
5
|
+
* Sortable computes position values under a lock on the sort scope, without
|
|
6
|
+
* which concurrent writers collide on the same position. A driver that cannot
|
|
7
|
+
* provide that lock fails loudly here rather than racing silently.
|
|
8
|
+
*/
|
|
9
|
+
export default class SortableRequiresAdvisoryTransactionLocks extends Error {
|
|
10
|
+
private queryDriverClassName;
|
|
11
|
+
constructor(queryDriverClassName: string);
|
|
12
|
+
get message(): string;
|
|
13
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Raised when a position-mutating operation cannot settle on the sort scope its
|
|
3
|
+
* record actually occupies.
|
|
4
|
+
*
|
|
5
|
+
* A save, destroy, undestroy or locked batch operation locks the sort scopes it
|
|
6
|
+
* believes it is touching, then reads the rows to find out which scopes they
|
|
7
|
+
* physically occupy. When another writer has moved a row in between, the
|
|
8
|
+
* operation takes the additional scope's lock and reads again. A row that keeps
|
|
9
|
+
* moving exhausts that bound, and the operation raises rather than compacting a
|
|
10
|
+
* scope the row has already left.
|
|
11
|
+
*
|
|
12
|
+
* This is a retryable condition: retrying the operation is the intended
|
|
13
|
+
* response. One caveat for saves the caller did not wrap in a transaction: the
|
|
14
|
+
* model's before-hooks run before the transaction this error aborts, so any
|
|
15
|
+
* effects those hooks had outside the record's own row — writes to other
|
|
16
|
+
* tables, enqueued work — are not rolled back, and run again on retry.
|
|
17
|
+
*/
|
|
18
|
+
export default class SortableScopeDidNotStabilize extends Error {
|
|
19
|
+
dreamClassName: string;
|
|
20
|
+
rounds: number;
|
|
21
|
+
constructor(dreamClassName: string, rounds: number);
|
|
22
|
+
get message(): string;
|
|
23
|
+
}
|