@rvoh/dream 2.40.2 → 2.41.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 -2
- package/dist/cjs/src/dream/internal/findOrCreateBy.js +2 -3
- package/dist/cjs/src/dream/internal/findOrCreateLookupStatement.js +19 -0
- package/dist/cjs/src/dream/internal/updateOrCreateBy.js +2 -3
- package/dist/cjs/src/helpers/db/normalizeDataForDb.js +16 -2
- package/dist/cjs/src/ops/index.js +7 -3
- package/dist/esm/src/Dream.js +3 -2
- package/dist/esm/src/dream/internal/findOrCreateBy.js +2 -3
- package/dist/esm/src/dream/internal/findOrCreateLookupStatement.js +19 -0
- package/dist/esm/src/dream/internal/updateOrCreateBy.js +2 -3
- package/dist/esm/src/helpers/db/normalizeDataForDb.js +16 -2
- package/dist/esm/src/ops/index.js +7 -3
- package/dist/types/src/dream/internal/findOrCreateLookupStatement.d.ts +14 -0
- package/dist/types/src/ops/index.d.ts +7 -3
- package/dist/types/src/types/associations/shared.d.ts +7 -5
- package/dist/types/src/types/types/associations/shared.ts +27 -5
- package/docs/classes/db.DreamMigrationHelpers.html +11 -11
- package/docs/classes/db.KyselyQueryDriver.html +49 -49
- package/docs/classes/db.PostgresQueryDriver.html +49 -49
- package/docs/classes/db.QueryDriverBase.html +49 -49
- package/docs/classes/errors.CannotSaveMissingDream.html +2 -2
- 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.SortableScopeDidNotStabilize.html +3 -3
- package/docs/classes/errors.SortableScopeLockWaitTimedOut.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 +126 -126
- package/docs/classes/index.DreamApp.html +13 -13
- package/docs/classes/index.DreamTransaction.html +3 -3
- package/docs/classes/index.Env.html +5 -5
- package/docs/classes/index.Query.html +65 -65
- package/docs/classes/system.CliFileWriter.html +4 -4
- package/docs/classes/system.DreamBin.html +3 -3
- 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 +4 -4
- 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/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.DecryptCallbacks.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 +6 -6
- package/docs/interfaces/types.DurationObject.html +2 -2
- package/docs/interfaces/types.EncryptOptions.html +2 -2
- package/docs/interfaces/types.EncryptionLegacyKeyUsedEvent.html +4 -4
- package/docs/interfaces/types.InternalAnyTypedSerializerRendersMany.html +2 -2
- package/docs/interfaces/types.InternalAnyTypedSerializerRendersOne.html +2 -2
- package/docs/interfaces/types.IntrospectedColumn.html +7 -7
- package/docs/interfaces/types.IntrospectedDatabase.html +2 -2
- package/docs/interfaces/types.IntrospectedEnum.html +2 -2
- package/docs/interfaces/types.IntrospectedTable.html +5 -5
- 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.OpenapiSchemaCombinatorFields.html +1 -1
- package/docs/types/openapi.OpenapiSchemaCommonFields.html +1 -1
- package/docs/types/openapi.OpenapiSchemaExpressionAllOf.html +1 -1
- package/docs/types/openapi.OpenapiSchemaExpressionAnyOf.html +1 -1
- package/docs/types/openapi.OpenapiSchemaExpressionOneOf.html +1 -1
- 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.OpenapiSchemaObjectBase.html +1 -1
- package/docs/types/openapi.OpenapiSchemaObjectBaseShorthand.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 +1 -1
- package/docs/types/openapi.OpenapiSchemaShorthandExpressionAnyOf.html +1 -1
- package/docs/types/openapi.OpenapiSchemaShorthandExpressionOneOf.html +1 -1
- 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.IntrospectedValueType.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 +8 -4
- 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/Dream.js
CHANGED
|
@@ -20,6 +20,7 @@ import destroyDream from './dream/internal/destroyDream.js';
|
|
|
20
20
|
import { destroyOptions, reallyDestroyOptions, undestroyOptions } from './dream/internal/destroyOptions.js';
|
|
21
21
|
import ensureSTITypeFieldIsSet from './dream/internal/ensureSTITypeFieldIsSet.js';
|
|
22
22
|
import findOrCreateBy from './dream/internal/findOrCreateBy.js';
|
|
23
|
+
import findOrCreateLookupStatement from './dream/internal/findOrCreateLookupStatement.js';
|
|
23
24
|
import mergeSerializerAssociationEdges from './dream/internal/mergeSerializerAssociationEdges.js';
|
|
24
25
|
import printSerializerHierarchyLevel from './dream/internal/printSerializerHierarchyLevel.js';
|
|
25
26
|
import reload from './dream/internal/reload.js';
|
|
@@ -1288,7 +1289,7 @@ export default class Dream {
|
|
|
1288
1289
|
}
|
|
1289
1290
|
catch (err) {
|
|
1290
1291
|
if (pgErrorType(err) === UNIQUE_VIOLATION) {
|
|
1291
|
-
const dreamModel = await this.findBy(this
|
|
1292
|
+
const dreamModel = await this.findBy(findOrCreateLookupStatement(this, attributes));
|
|
1292
1293
|
if (!dreamModel)
|
|
1293
1294
|
throw new CreateOrFindByFailedToCreateAndFind(this);
|
|
1294
1295
|
return dreamModel;
|
|
@@ -1342,7 +1343,7 @@ export default class Dream {
|
|
|
1342
1343
|
}
|
|
1343
1344
|
catch (err) {
|
|
1344
1345
|
if (pgErrorType(err) === UNIQUE_VIOLATION) {
|
|
1345
|
-
const existingRecord = await this.findBy(this
|
|
1346
|
+
const existingRecord = await this.findBy(findOrCreateLookupStatement(this, attributes));
|
|
1346
1347
|
if (!existingRecord)
|
|
1347
1348
|
throw new CreateOrUpdateByFailedToCreateAndUpdate(this);
|
|
1348
1349
|
const { with: attrs } = extraOpts;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
+
import findOrCreateLookupStatement from './findOrCreateLookupStatement.js';
|
|
1
2
|
export default async function findOrCreateBy(dreamClass, txn = null, attributes, extraOpts = {}) {
|
|
2
|
-
const existingRecord = await dreamClass
|
|
3
|
-
.txn(txn)
|
|
4
|
-
.findBy(dreamClass['extractAttributesFromUpdateableProperties'](attributes));
|
|
3
|
+
const existingRecord = await dreamClass.txn(txn).findBy(findOrCreateLookupStatement(dreamClass, attributes));
|
|
5
4
|
if (existingRecord)
|
|
6
5
|
return existingRecord;
|
|
7
6
|
const { createWith, skipHooks } = extraOpts;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import isDatabaseArrayColumn from '../../helpers/db/types/isDatabaseArrayColumn.js';
|
|
2
|
+
import ops from '../../ops/index.js';
|
|
3
|
+
/**
|
|
4
|
+
* Builds the `findBy` statement that findOrCreateBy, createOrFindBy,
|
|
5
|
+
* updateOrCreateBy and createOrUpdateBy use to look up an existing record.
|
|
6
|
+
* Their attributes are both the lookup and the values to persist, so an array
|
|
7
|
+
* given for an array column is the whole array to store: the lookup compares
|
|
8
|
+
* it with `ops.equal`, matching only a record whose array equals it
|
|
9
|
+
* element-for-element, in order, instead of the `IN` a plain list means in a
|
|
10
|
+
* where clause. PostgreSQL defines no equality operator for `json`, so on a
|
|
11
|
+
* `json[]` column this lookup raises at the database, as `ops.equal` does.
|
|
12
|
+
*/
|
|
13
|
+
export default function findOrCreateLookupStatement(dreamClass, attributes) {
|
|
14
|
+
const lookup = dreamClass['extractAttributesFromUpdateableProperties'](attributes);
|
|
15
|
+
return Object.fromEntries(Object.entries(lookup).map(([column, value]) => [
|
|
16
|
+
column,
|
|
17
|
+
Array.isArray(value) && isDatabaseArrayColumn(dreamClass, column) ? ops.equal(value) : value,
|
|
18
|
+
]));
|
|
19
|
+
}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
+
import findOrCreateLookupStatement from './findOrCreateLookupStatement.js';
|
|
1
2
|
import saveDream from './saveDream.js';
|
|
2
3
|
export default async function updateOrCreateBy(dreamClass, txn = null, attributes, extraOpts = {}) {
|
|
3
|
-
const existingRecord = await dreamClass
|
|
4
|
-
.txn(txn)
|
|
5
|
-
.findBy(dreamClass['extractAttributesFromUpdateableProperties'](attributes));
|
|
4
|
+
const existingRecord = await dreamClass.txn(txn).findBy(findOrCreateLookupStatement(dreamClass, attributes));
|
|
6
5
|
const { with: attrs, skipHooks } = extraOpts;
|
|
7
6
|
if (existingRecord) {
|
|
8
7
|
existingRecord.assignAttributes(attrs ?? {});
|
|
@@ -38,11 +38,18 @@ function normalizedString(_val) {
|
|
|
38
38
|
const normalizedValueArray = (alreadyAnArray ? _val : [_val]).map(val => typeof val === 'string' ? normalizeUnicode(val) : val);
|
|
39
39
|
return alreadyAnArray ? normalizedValueArray : normalizedValueArray[0];
|
|
40
40
|
}
|
|
41
|
+
/**
|
|
42
|
+
* A value for a date or timestamp column (or an array of them) may be a single
|
|
43
|
+
* value, an array, or, as the `IN` operand against an array column, a list of
|
|
44
|
+
* whole arrays; every nested array is converted element by element.
|
|
45
|
+
*/
|
|
41
46
|
function dateTimeOrDateToSql(dateClass, _val) {
|
|
42
47
|
const alreadyAnArray = Array.isArray(_val);
|
|
43
|
-
const normalizedValueArray = (alreadyAnArray ? _val : [_val]).map(val => {
|
|
48
|
+
const normalizedValueArray = (alreadyAnArray ? _val : [_val]).map((val) => {
|
|
44
49
|
if (val === null || val === undefined)
|
|
45
50
|
return val;
|
|
51
|
+
if (Array.isArray(val))
|
|
52
|
+
return dateTimeOrDateToSql(dateClass, val);
|
|
46
53
|
if (val instanceof DateTime || val instanceof CalendarDate)
|
|
47
54
|
return val.toSQL();
|
|
48
55
|
try {
|
|
@@ -59,11 +66,18 @@ function dateTimeOrDateToSql(dateClass, _val) {
|
|
|
59
66
|
});
|
|
60
67
|
return alreadyAnArray ? normalizedValueArray : normalizedValueArray[0];
|
|
61
68
|
}
|
|
69
|
+
/**
|
|
70
|
+
* A value for a time column (or an array of them) may be a single value, an
|
|
71
|
+
* array, or, as the `IN` operand against an array column, a list of whole
|
|
72
|
+
* arrays; every nested array is converted element by element.
|
|
73
|
+
*/
|
|
62
74
|
function clockTimeOrClockTimeTzToSql(clockTimeClass, _val) {
|
|
63
75
|
const alreadyAnArray = Array.isArray(_val);
|
|
64
|
-
const normalizedValueArray = (alreadyAnArray ? _val : [_val]).map(val => {
|
|
76
|
+
const normalizedValueArray = (alreadyAnArray ? _val : [_val]).map((val) => {
|
|
65
77
|
if (val === null || val === undefined)
|
|
66
78
|
return val;
|
|
79
|
+
if (Array.isArray(val))
|
|
80
|
+
return clockTimeOrClockTimeTzToSql(clockTimeClass, val);
|
|
67
81
|
if (val instanceof BaseClockTime)
|
|
68
82
|
return val.toSQL();
|
|
69
83
|
try {
|
|
@@ -119,9 +119,13 @@ const ops = {
|
|
|
119
119
|
*
|
|
120
120
|
* **Array columns:** passing an array means literal array equality — the
|
|
121
121
|
* whole array is bound as a single parameter, so the column must match
|
|
122
|
-
* element-for-element, in order.
|
|
123
|
-
*
|
|
124
|
-
*
|
|
122
|
+
* element-for-element, in order. The plain value form of a where clause
|
|
123
|
+
* targets regular (scalar) columns, so comparing an array column against an
|
|
124
|
+
* array value goes through `ops`: a flat list against an array column
|
|
125
|
+
* (`Post.where({ tags: ['a', 'b'] })`) is a compile error. A list of whole
|
|
126
|
+
* arrays (`Post.where({ tags: [['a', 'b'], ['c']] })`) means `IN`, matching
|
|
127
|
+
* rows whose column equals any one of them, and `ops.any` tests for
|
|
128
|
+
* containment of a single element.
|
|
125
129
|
* Element types are constrained by the column, so an enum array column
|
|
126
130
|
* rejects a string that is not a member of its enum.
|
|
127
131
|
*
|
package/dist/esm/src/Dream.js
CHANGED
|
@@ -20,6 +20,7 @@ import destroyDream from './dream/internal/destroyDream.js';
|
|
|
20
20
|
import { destroyOptions, reallyDestroyOptions, undestroyOptions } from './dream/internal/destroyOptions.js';
|
|
21
21
|
import ensureSTITypeFieldIsSet from './dream/internal/ensureSTITypeFieldIsSet.js';
|
|
22
22
|
import findOrCreateBy from './dream/internal/findOrCreateBy.js';
|
|
23
|
+
import findOrCreateLookupStatement from './dream/internal/findOrCreateLookupStatement.js';
|
|
23
24
|
import mergeSerializerAssociationEdges from './dream/internal/mergeSerializerAssociationEdges.js';
|
|
24
25
|
import printSerializerHierarchyLevel from './dream/internal/printSerializerHierarchyLevel.js';
|
|
25
26
|
import reload from './dream/internal/reload.js';
|
|
@@ -1288,7 +1289,7 @@ export default class Dream {
|
|
|
1288
1289
|
}
|
|
1289
1290
|
catch (err) {
|
|
1290
1291
|
if (pgErrorType(err) === UNIQUE_VIOLATION) {
|
|
1291
|
-
const dreamModel = await this.findBy(this
|
|
1292
|
+
const dreamModel = await this.findBy(findOrCreateLookupStatement(this, attributes));
|
|
1292
1293
|
if (!dreamModel)
|
|
1293
1294
|
throw new CreateOrFindByFailedToCreateAndFind(this);
|
|
1294
1295
|
return dreamModel;
|
|
@@ -1342,7 +1343,7 @@ export default class Dream {
|
|
|
1342
1343
|
}
|
|
1343
1344
|
catch (err) {
|
|
1344
1345
|
if (pgErrorType(err) === UNIQUE_VIOLATION) {
|
|
1345
|
-
const existingRecord = await this.findBy(this
|
|
1346
|
+
const existingRecord = await this.findBy(findOrCreateLookupStatement(this, attributes));
|
|
1346
1347
|
if (!existingRecord)
|
|
1347
1348
|
throw new CreateOrUpdateByFailedToCreateAndUpdate(this);
|
|
1348
1349
|
const { with: attrs } = extraOpts;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
+
import findOrCreateLookupStatement from './findOrCreateLookupStatement.js';
|
|
1
2
|
export default async function findOrCreateBy(dreamClass, txn = null, attributes, extraOpts = {}) {
|
|
2
|
-
const existingRecord = await dreamClass
|
|
3
|
-
.txn(txn)
|
|
4
|
-
.findBy(dreamClass['extractAttributesFromUpdateableProperties'](attributes));
|
|
3
|
+
const existingRecord = await dreamClass.txn(txn).findBy(findOrCreateLookupStatement(dreamClass, attributes));
|
|
5
4
|
if (existingRecord)
|
|
6
5
|
return existingRecord;
|
|
7
6
|
const { createWith, skipHooks } = extraOpts;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import isDatabaseArrayColumn from '../../helpers/db/types/isDatabaseArrayColumn.js';
|
|
2
|
+
import ops from '../../ops/index.js';
|
|
3
|
+
/**
|
|
4
|
+
* Builds the `findBy` statement that findOrCreateBy, createOrFindBy,
|
|
5
|
+
* updateOrCreateBy and createOrUpdateBy use to look up an existing record.
|
|
6
|
+
* Their attributes are both the lookup and the values to persist, so an array
|
|
7
|
+
* given for an array column is the whole array to store: the lookup compares
|
|
8
|
+
* it with `ops.equal`, matching only a record whose array equals it
|
|
9
|
+
* element-for-element, in order, instead of the `IN` a plain list means in a
|
|
10
|
+
* where clause. PostgreSQL defines no equality operator for `json`, so on a
|
|
11
|
+
* `json[]` column this lookup raises at the database, as `ops.equal` does.
|
|
12
|
+
*/
|
|
13
|
+
export default function findOrCreateLookupStatement(dreamClass, attributes) {
|
|
14
|
+
const lookup = dreamClass['extractAttributesFromUpdateableProperties'](attributes);
|
|
15
|
+
return Object.fromEntries(Object.entries(lookup).map(([column, value]) => [
|
|
16
|
+
column,
|
|
17
|
+
Array.isArray(value) && isDatabaseArrayColumn(dreamClass, column) ? ops.equal(value) : value,
|
|
18
|
+
]));
|
|
19
|
+
}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
+
import findOrCreateLookupStatement from './findOrCreateLookupStatement.js';
|
|
1
2
|
import saveDream from './saveDream.js';
|
|
2
3
|
export default async function updateOrCreateBy(dreamClass, txn = null, attributes, extraOpts = {}) {
|
|
3
|
-
const existingRecord = await dreamClass
|
|
4
|
-
.txn(txn)
|
|
5
|
-
.findBy(dreamClass['extractAttributesFromUpdateableProperties'](attributes));
|
|
4
|
+
const existingRecord = await dreamClass.txn(txn).findBy(findOrCreateLookupStatement(dreamClass, attributes));
|
|
6
5
|
const { with: attrs, skipHooks } = extraOpts;
|
|
7
6
|
if (existingRecord) {
|
|
8
7
|
existingRecord.assignAttributes(attrs ?? {});
|
|
@@ -38,11 +38,18 @@ function normalizedString(_val) {
|
|
|
38
38
|
const normalizedValueArray = (alreadyAnArray ? _val : [_val]).map(val => typeof val === 'string' ? normalizeUnicode(val) : val);
|
|
39
39
|
return alreadyAnArray ? normalizedValueArray : normalizedValueArray[0];
|
|
40
40
|
}
|
|
41
|
+
/**
|
|
42
|
+
* A value for a date or timestamp column (or an array of them) may be a single
|
|
43
|
+
* value, an array, or, as the `IN` operand against an array column, a list of
|
|
44
|
+
* whole arrays; every nested array is converted element by element.
|
|
45
|
+
*/
|
|
41
46
|
function dateTimeOrDateToSql(dateClass, _val) {
|
|
42
47
|
const alreadyAnArray = Array.isArray(_val);
|
|
43
|
-
const normalizedValueArray = (alreadyAnArray ? _val : [_val]).map(val => {
|
|
48
|
+
const normalizedValueArray = (alreadyAnArray ? _val : [_val]).map((val) => {
|
|
44
49
|
if (val === null || val === undefined)
|
|
45
50
|
return val;
|
|
51
|
+
if (Array.isArray(val))
|
|
52
|
+
return dateTimeOrDateToSql(dateClass, val);
|
|
46
53
|
if (val instanceof DateTime || val instanceof CalendarDate)
|
|
47
54
|
return val.toSQL();
|
|
48
55
|
try {
|
|
@@ -59,11 +66,18 @@ function dateTimeOrDateToSql(dateClass, _val) {
|
|
|
59
66
|
});
|
|
60
67
|
return alreadyAnArray ? normalizedValueArray : normalizedValueArray[0];
|
|
61
68
|
}
|
|
69
|
+
/**
|
|
70
|
+
* A value for a time column (or an array of them) may be a single value, an
|
|
71
|
+
* array, or, as the `IN` operand against an array column, a list of whole
|
|
72
|
+
* arrays; every nested array is converted element by element.
|
|
73
|
+
*/
|
|
62
74
|
function clockTimeOrClockTimeTzToSql(clockTimeClass, _val) {
|
|
63
75
|
const alreadyAnArray = Array.isArray(_val);
|
|
64
|
-
const normalizedValueArray = (alreadyAnArray ? _val : [_val]).map(val => {
|
|
76
|
+
const normalizedValueArray = (alreadyAnArray ? _val : [_val]).map((val) => {
|
|
65
77
|
if (val === null || val === undefined)
|
|
66
78
|
return val;
|
|
79
|
+
if (Array.isArray(val))
|
|
80
|
+
return clockTimeOrClockTimeTzToSql(clockTimeClass, val);
|
|
67
81
|
if (val instanceof BaseClockTime)
|
|
68
82
|
return val.toSQL();
|
|
69
83
|
try {
|
|
@@ -119,9 +119,13 @@ const ops = {
|
|
|
119
119
|
*
|
|
120
120
|
* **Array columns:** passing an array means literal array equality — the
|
|
121
121
|
* whole array is bound as a single parameter, so the column must match
|
|
122
|
-
* element-for-element, in order.
|
|
123
|
-
*
|
|
124
|
-
*
|
|
122
|
+
* element-for-element, in order. The plain value form of a where clause
|
|
123
|
+
* targets regular (scalar) columns, so comparing an array column against an
|
|
124
|
+
* array value goes through `ops`: a flat list against an array column
|
|
125
|
+
* (`Post.where({ tags: ['a', 'b'] })`) is a compile error. A list of whole
|
|
126
|
+
* arrays (`Post.where({ tags: [['a', 'b'], ['c']] })`) means `IN`, matching
|
|
127
|
+
* rows whose column equals any one of them, and `ops.any` tests for
|
|
128
|
+
* containment of a single element.
|
|
125
129
|
* Element types are constrained by the column, so an enum array column
|
|
126
130
|
* rejects a string that is not a member of its enum.
|
|
127
131
|
*
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import Dream from '../../Dream.js';
|
|
2
|
+
import { WhereStatement } from '../../types/associations/shared.js';
|
|
3
|
+
import { UpdateablePropertiesForClass } from '../../types/dream.js';
|
|
4
|
+
/**
|
|
5
|
+
* Builds the `findBy` statement that findOrCreateBy, createOrFindBy,
|
|
6
|
+
* updateOrCreateBy and createOrUpdateBy use to look up an existing record.
|
|
7
|
+
* Their attributes are both the lookup and the values to persist, so an array
|
|
8
|
+
* given for an array column is the whole array to store: the lookup compares
|
|
9
|
+
* it with `ops.equal`, matching only a record whose array equals it
|
|
10
|
+
* element-for-element, in order, instead of the `IN` a plain list means in a
|
|
11
|
+
* where clause. PostgreSQL defines no equality operator for `json`, so on a
|
|
12
|
+
* `json[]` column this lookup raises at the database, as `ops.equal` does.
|
|
13
|
+
*/
|
|
14
|
+
export default function findOrCreateLookupStatement<T extends typeof Dream>(dreamClass: T, attributes: UpdateablePropertiesForClass<T>): WhereStatement<InstanceType<T>>;
|
|
@@ -114,9 +114,13 @@ declare const ops: {
|
|
|
114
114
|
*
|
|
115
115
|
* **Array columns:** passing an array means literal array equality — the
|
|
116
116
|
* whole array is bound as a single parameter, so the column must match
|
|
117
|
-
* element-for-element, in order.
|
|
118
|
-
*
|
|
119
|
-
*
|
|
117
|
+
* element-for-element, in order. The plain value form of a where clause
|
|
118
|
+
* targets regular (scalar) columns, so comparing an array column against an
|
|
119
|
+
* array value goes through `ops`: a flat list against an array column
|
|
120
|
+
* (`Post.where({ tags: ['a', 'b'] })`) is a compile error. A list of whole
|
|
121
|
+
* arrays (`Post.where({ tags: [['a', 'b'], ['c']] })`) means `IN`, matching
|
|
122
|
+
* rows whose column equals any one of them, and `ops.any` tests for
|
|
123
|
+
* containment of a single element.
|
|
120
124
|
* Element types are constrained by the column, so an enum array column
|
|
121
125
|
* rejects a string that is not a member of its enum.
|
|
122
126
|
*
|
|
@@ -25,23 +25,25 @@ 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>, 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 ?
|
|
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 ? 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>}`;
|
|
32
32
|
export type ColumnNamesAccountingForJoinedAssociations<JoinedAssociations extends Readonly<JoinedAssociation[]>, DB, RootTableName extends keyof DB, RootTableAlias extends string> = JoinedAssociations['length'] extends 0 ? TableColumnNames<DB, RootTableName> : TableColumnNames<DB, RootTableName> | JoinedAssociationColumnNames<JoinedAssociations, DB, AssociationNameToDotReferencedColumns<DB, RootTableName & keyof DB, RootTableAlias>>;
|
|
33
33
|
type JoinedAssociationColumnNames<JoinedAssociations extends Readonly<JoinedAssociation[]>, DB, AllColumnNames, Depth extends number = 0, CurrentJoinedAssociation = Readonly<JoinedAssociations[0]>, NextTableName = CurrentJoinedAssociation extends Readonly<JoinedAssociation> ? CurrentJoinedAssociation['table'] : never, NextAssociationName = CurrentJoinedAssociation extends Readonly<JoinedAssociation> ? CurrentJoinedAssociation['alias'] : never> = JoinedAssociations['length'] extends 0 ? AllColumnNames : Depth extends MAX_JOINED_TABLES_DEPTH ? AllColumnNames : JoinedAssociationColumnNames<ReadonlyTail<JoinedAssociations>, DB, AllColumnNames | AssociationNameToDotReferencedColumns<DB, NextTableName & keyof DB, NextAssociationName>, Inc<Depth>>;
|
|
34
|
-
type Whereable<R> = {
|
|
35
|
-
[K in keyof Selectable<R>]?: Selectable<R>[K] | Selectable<R>[K][];
|
|
34
|
+
type Whereable<DB, Schema, TableName extends keyof DB, R = DB[TableName]> = {
|
|
35
|
+
[K in keyof Selectable<R>]?: TableColumnIsArray<Schema, TableName, K> extends true ? Extract<Selectable<R>[K], null> | Selectable<R>[K][] : Selectable<R>[K] | Selectable<R>[K][];
|
|
36
36
|
};
|
|
37
37
|
type WhereableAssociatedModelParam<I extends Dream> = {
|
|
38
38
|
[K in keyof AssociatedModelParam<I>]: AssociatedModelParam<I>[K] | NonNullable<AssociatedModelParam<I>[K]>[];
|
|
39
39
|
};
|
|
40
40
|
export type WhereStatement<I extends Dream> = InternalWhereStatement<I, I['DB'], I['schema'], I['table']>;
|
|
41
|
-
export type InternalWhereStatement<I extends Dream, DB, Schema, TableName extends AssociationTableNames<DB, Schema> & keyof DB> = Partial<MergeUnionOfRecordTypes<Whereable<DB
|
|
41
|
+
export type InternalWhereStatement<I extends Dream, DB, Schema, TableName extends AssociationTableNames<DB, Schema> & keyof DB> = Partial<MergeUnionOfRecordTypes<Whereable<DB, Schema, TableName> | DreamSelectable<DB, Schema, TableName> | WhereableAssociatedModelParam<I>>>;
|
|
42
42
|
export type OnStatementForAssociation<I extends Dream, DB, Schema, TableName extends AssociationTableNames<DB, Schema> & keyof DB, RequiredOnClauseKeysForThisAssociation, OnStatement extends InternalWhereStatement<I, DB, Schema, TableName> = InternalWhereStatement<I, DB, Schema, TableName>> = RequiredOnClauseKeysForThisAssociation extends null ? InternalWhereStatement<I, DB, Schema, TableName> : RequiredOnClauseKeysForThisAssociation extends string[] ? Required<Pick<OnStatement, RequiredOnClauseKeysForThisAssociation[number] & keyof OnStatement>> & Partial<Omit<OnStatement, RequiredOnClauseKeysForThisAssociation[number] & keyof OnStatement>> : never;
|
|
43
43
|
export type OnStatementForSpecificColumns<I extends Dream, DB, Schema, TableName extends AssociationTableNames<DB, Schema> & keyof DB, Columns extends string[], OnStatement extends InternalWhereStatement<I, DB, Schema, TableName> = InternalWhereStatement<I, DB, Schema, TableName>> = Pick<OnStatement, Columns[number] & keyof OnStatement>;
|
|
44
|
-
type OnStatementForAssociationDefinition<DB, Schema, TableName extends AssociationTableNames<DB, Schema> & keyof DB> = Partial<MergeUnionOfRecordTypes<Partial<
|
|
44
|
+
type OnStatementForAssociationDefinition<DB, Schema, TableName extends AssociationTableNames<DB, Schema> & keyof DB> = Partial<MergeUnionOfRecordTypes<Partial<{
|
|
45
|
+
[K in keyof Selectable<DB[TableName]>]: TableColumnIsArray<Schema, TableName, K> extends true ? Extract<Selectable<DB[TableName]>[K], null> | readonly [] : Selectable<DB[TableName]>[K];
|
|
46
|
+
}> | Partial<{
|
|
45
47
|
[ColumnName in keyof DB[TableName]]: NonKyselySupportedSupplementalWhereClauseValues<DB, Schema, TableName, ColumnName> | typeof DreamConst.passthrough | typeof DreamConst.required;
|
|
46
48
|
}>>>;
|
|
47
49
|
export type SelfOnStatement<BaseInstance extends Dream, DB, Schema, TableName extends AssociationTableNames<DB, Schema> & keyof DB> = Partial<Record<keyof DB[TableName], DreamColumnNames<BaseInstance>>>;
|
|
@@ -134,7 +134,6 @@ type NonKyselySupportedSupplementalWhereClauseValues<
|
|
|
134
134
|
// a database error.
|
|
135
135
|
PartialTypes = IsArrayColumn extends true
|
|
136
136
|
?
|
|
137
|
-
| (EnumTypeArray extends string[] ? EnumTypeArray : never)
|
|
138
137
|
| OpsStatement<'=' | '!=' | '<>', ArrayOpsValType, any>
|
|
139
138
|
| OpsStatement<KyselyComparisonOperatorExpression, PermanentOpsValTypes | RawBuilder<unknown>, any>
|
|
140
139
|
: EnumTypeArray extends null
|
|
@@ -248,8 +247,22 @@ type JoinedAssociationColumnNames<
|
|
|
248
247
|
Inc<Depth>
|
|
249
248
|
>
|
|
250
249
|
|
|
251
|
-
|
|
252
|
-
|
|
250
|
+
// The plain value form of a where clause targets regular (scalar) columns: a
|
|
251
|
+
// value means `=`, a list of values means `IN`, and `null` means `IS NULL`.
|
|
252
|
+
// Comparing an array column against an array value goes through `ops`, e.g.
|
|
253
|
+
// `Post.where({ tags: ops.equal(['a', 'b']) })`. An array column's plain value
|
|
254
|
+
// is therefore limited to `null` and a list whose entries are each a whole
|
|
255
|
+
// array or `null` (an `IN` of whole arrays, where `[]` matches nothing). A flat
|
|
256
|
+
// list of elements against an array column, e.g.
|
|
257
|
+
// `Post.where({ tags: ['a', 'b'] })`, is a compile error, since it is rendered
|
|
258
|
+
// as the value list `IN ('a', 'b')`, which PostgreSQL rejects for an array
|
|
259
|
+
// column unless every element is itself an array literal such as `'{a}'`.
|
|
260
|
+
// Array-ness comes from the schema (`TableColumnIsArray`), not from the
|
|
261
|
+
// Kysely column type, which also admits arrays for jsonb columns.
|
|
262
|
+
type Whereable<DB, Schema, TableName extends keyof DB, R = DB[TableName]> = {
|
|
263
|
+
[K in keyof Selectable<R>]?: TableColumnIsArray<Schema, TableName, K> extends true
|
|
264
|
+
? Extract<Selectable<R>[K], null> | Selectable<R>[K][]
|
|
265
|
+
: Selectable<R>[K] | Selectable<R>[K][]
|
|
253
266
|
}
|
|
254
267
|
|
|
255
268
|
// For filtering by BelongsTo model instance(s) in a where/and statement, e.g.
|
|
@@ -270,7 +283,9 @@ export type InternalWhereStatement<
|
|
|
270
283
|
TableName extends AssociationTableNames<DB, Schema> & keyof DB,
|
|
271
284
|
> = Partial<
|
|
272
285
|
MergeUnionOfRecordTypes<
|
|
273
|
-
|
|
286
|
+
| Whereable<DB, Schema, TableName>
|
|
287
|
+
| DreamSelectable<DB, Schema, TableName>
|
|
288
|
+
| WhereableAssociatedModelParam<I>
|
|
274
289
|
>
|
|
275
290
|
>
|
|
276
291
|
|
|
@@ -314,7 +329,14 @@ type OnStatementForAssociationDefinition<
|
|
|
314
329
|
TableName extends AssociationTableNames<DB, Schema> & keyof DB,
|
|
315
330
|
> = Partial<
|
|
316
331
|
MergeUnionOfRecordTypes<
|
|
317
|
-
|
|
332
|
+
// As in `Whereable`, the plain value form targets regular (scalar) columns.
|
|
333
|
+
// An array column accepts only `null` and `[]` as a plain value here;
|
|
334
|
+
// every other comparison against it goes through `ops`.
|
|
335
|
+
| Partial<{
|
|
336
|
+
[K in keyof Selectable<DB[TableName]>]: TableColumnIsArray<Schema, TableName, K> extends true
|
|
337
|
+
? Extract<Selectable<DB[TableName]>[K], null> | readonly []
|
|
338
|
+
: Selectable<DB[TableName]>[K]
|
|
339
|
+
}>
|
|
318
340
|
| Partial<{
|
|
319
341
|
[ColumnName in keyof DB[TableName]]:
|
|
320
342
|
| NonKyselySupportedSupplementalWhereClauseValues<DB, Schema, TableName, ColumnName>
|