@checkstack/healthcheck-backend 0.17.0 → 0.17.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/CHANGELOG.md +16 -0
- package/drizzle/0011_fluffy_sphinx.sql +16 -0
- package/drizzle/meta/0011_snapshot.json +441 -0
- package/drizzle/meta/_journal.json +7 -0
- package/package.json +1 -1
- package/src/schema.ts +6 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# @checkstack/healthcheck-backend
|
|
2
2
|
|
|
3
|
+
## 0.17.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- c4e7560: Fix data integrity, cache invalidation, and mobile UI issues
|
|
8
|
+
|
|
9
|
+
- **Centralized mutation cache invalidation**: Every mutation now automatically invalidates its plugin's query cache on success via the shared `createProcedureHook` in `orpc-query.tsx`. This ensures all views stay in sync without requiring individual components to remember manual `invalidateQueries` calls.
|
|
10
|
+
- **Fixed oRPC query key matching**: Query keys use nested arrays (`[["pluginId"]]`) to correctly match oRPC's `[pathArray, options]` key structure. Fixed the broken flat-string pattern in `SystemBadgeDataProvider`.
|
|
11
|
+
- **Fixed hourly aggregation duplication**: Added `NULLS NOT DISTINCT` to the `health_check_aggregates` unique constraint so local runs (`source_id = NULL`) correctly conflict-match instead of creating duplicate hourly buckets. Includes a migration to clean up existing duplicates.
|
|
12
|
+
- **Fixed modal scrolling on mobile**: Added `max-height` + `overflow-y-auto` to `ConfirmationModal`, and refactored `Dialog` from translate-centering to flex-centering with `dvh` units for reliable mobile scroll containment.
|
|
13
|
+
- @checkstack/catalog-common@1.5.1
|
|
14
|
+
- @checkstack/incident-common@0.4.8
|
|
15
|
+
- @checkstack/maintenance-common@0.4.10
|
|
16
|
+
- @checkstack/satellite-backend@0.2.15
|
|
17
|
+
- @checkstack/catalog-backend@0.6.1
|
|
18
|
+
|
|
3
19
|
## 0.17.0
|
|
4
20
|
|
|
5
21
|
### Minor Changes
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
DROP INDEX "health_check_aggregates_bucket_unique";--> statement-breakpoint
|
|
2
|
+
|
|
3
|
+
-- Clean up duplicate local (source_id IS NULL) hourly buckets that were
|
|
4
|
+
-- created due to the missing NULLS NOT DISTINCT clause. Keep the row with
|
|
5
|
+
-- the highest id (most recent insert) for each bucket group.
|
|
6
|
+
DELETE FROM "health_check_aggregates" a
|
|
7
|
+
USING "health_check_aggregates" b
|
|
8
|
+
WHERE a.source_id IS NULL
|
|
9
|
+
AND b.source_id IS NULL
|
|
10
|
+
AND a.configuration_id = b.configuration_id
|
|
11
|
+
AND a.system_id = b.system_id
|
|
12
|
+
AND a.bucket_start = b.bucket_start
|
|
13
|
+
AND a.bucket_size = b.bucket_size
|
|
14
|
+
AND a.id < b.id;--> statement-breakpoint
|
|
15
|
+
|
|
16
|
+
ALTER TABLE "health_check_aggregates" ADD CONSTRAINT "health_check_aggregates_bucket_unique" UNIQUE NULLS NOT DISTINCT("configuration_id","system_id","bucket_start","bucket_size","source_id");
|
|
@@ -0,0 +1,441 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "446eaec3-7894-4425-b7db-00c7761ca83f",
|
|
3
|
+
"prevId": "743f0b98-66a5-462e-8c35-4b2eb3093010",
|
|
4
|
+
"version": "7",
|
|
5
|
+
"dialect": "postgresql",
|
|
6
|
+
"tables": {
|
|
7
|
+
"public.health_check_aggregates": {
|
|
8
|
+
"name": "health_check_aggregates",
|
|
9
|
+
"schema": "",
|
|
10
|
+
"columns": {
|
|
11
|
+
"id": {
|
|
12
|
+
"name": "id",
|
|
13
|
+
"type": "uuid",
|
|
14
|
+
"primaryKey": true,
|
|
15
|
+
"notNull": true,
|
|
16
|
+
"default": "gen_random_uuid()"
|
|
17
|
+
},
|
|
18
|
+
"configuration_id": {
|
|
19
|
+
"name": "configuration_id",
|
|
20
|
+
"type": "uuid",
|
|
21
|
+
"primaryKey": false,
|
|
22
|
+
"notNull": true
|
|
23
|
+
},
|
|
24
|
+
"system_id": {
|
|
25
|
+
"name": "system_id",
|
|
26
|
+
"type": "text",
|
|
27
|
+
"primaryKey": false,
|
|
28
|
+
"notNull": true
|
|
29
|
+
},
|
|
30
|
+
"bucket_start": {
|
|
31
|
+
"name": "bucket_start",
|
|
32
|
+
"type": "timestamp",
|
|
33
|
+
"primaryKey": false,
|
|
34
|
+
"notNull": true
|
|
35
|
+
},
|
|
36
|
+
"bucket_size": {
|
|
37
|
+
"name": "bucket_size",
|
|
38
|
+
"type": "bucket_size",
|
|
39
|
+
"typeSchema": "public",
|
|
40
|
+
"primaryKey": false,
|
|
41
|
+
"notNull": true
|
|
42
|
+
},
|
|
43
|
+
"run_count": {
|
|
44
|
+
"name": "run_count",
|
|
45
|
+
"type": "integer",
|
|
46
|
+
"primaryKey": false,
|
|
47
|
+
"notNull": true
|
|
48
|
+
},
|
|
49
|
+
"healthy_count": {
|
|
50
|
+
"name": "healthy_count",
|
|
51
|
+
"type": "integer",
|
|
52
|
+
"primaryKey": false,
|
|
53
|
+
"notNull": true
|
|
54
|
+
},
|
|
55
|
+
"degraded_count": {
|
|
56
|
+
"name": "degraded_count",
|
|
57
|
+
"type": "integer",
|
|
58
|
+
"primaryKey": false,
|
|
59
|
+
"notNull": true
|
|
60
|
+
},
|
|
61
|
+
"unhealthy_count": {
|
|
62
|
+
"name": "unhealthy_count",
|
|
63
|
+
"type": "integer",
|
|
64
|
+
"primaryKey": false,
|
|
65
|
+
"notNull": true
|
|
66
|
+
},
|
|
67
|
+
"latency_sum_ms": {
|
|
68
|
+
"name": "latency_sum_ms",
|
|
69
|
+
"type": "integer",
|
|
70
|
+
"primaryKey": false,
|
|
71
|
+
"notNull": false
|
|
72
|
+
},
|
|
73
|
+
"avg_latency_ms": {
|
|
74
|
+
"name": "avg_latency_ms",
|
|
75
|
+
"type": "integer",
|
|
76
|
+
"primaryKey": false,
|
|
77
|
+
"notNull": false
|
|
78
|
+
},
|
|
79
|
+
"min_latency_ms": {
|
|
80
|
+
"name": "min_latency_ms",
|
|
81
|
+
"type": "integer",
|
|
82
|
+
"primaryKey": false,
|
|
83
|
+
"notNull": false
|
|
84
|
+
},
|
|
85
|
+
"max_latency_ms": {
|
|
86
|
+
"name": "max_latency_ms",
|
|
87
|
+
"type": "integer",
|
|
88
|
+
"primaryKey": false,
|
|
89
|
+
"notNull": false
|
|
90
|
+
},
|
|
91
|
+
"p95_latency_ms": {
|
|
92
|
+
"name": "p95_latency_ms",
|
|
93
|
+
"type": "integer",
|
|
94
|
+
"primaryKey": false,
|
|
95
|
+
"notNull": false
|
|
96
|
+
},
|
|
97
|
+
"aggregated_result": {
|
|
98
|
+
"name": "aggregated_result",
|
|
99
|
+
"type": "jsonb",
|
|
100
|
+
"primaryKey": false,
|
|
101
|
+
"notNull": false
|
|
102
|
+
},
|
|
103
|
+
"tdigest_state": {
|
|
104
|
+
"name": "tdigest_state",
|
|
105
|
+
"type": "jsonb",
|
|
106
|
+
"primaryKey": false,
|
|
107
|
+
"notNull": false
|
|
108
|
+
},
|
|
109
|
+
"source_id": {
|
|
110
|
+
"name": "source_id",
|
|
111
|
+
"type": "text",
|
|
112
|
+
"primaryKey": false,
|
|
113
|
+
"notNull": false
|
|
114
|
+
},
|
|
115
|
+
"source_label": {
|
|
116
|
+
"name": "source_label",
|
|
117
|
+
"type": "text",
|
|
118
|
+
"primaryKey": false,
|
|
119
|
+
"notNull": false
|
|
120
|
+
}
|
|
121
|
+
},
|
|
122
|
+
"indexes": {},
|
|
123
|
+
"foreignKeys": {
|
|
124
|
+
"health_check_aggregates_configuration_id_health_check_configurations_id_fk": {
|
|
125
|
+
"name": "health_check_aggregates_configuration_id_health_check_configurations_id_fk",
|
|
126
|
+
"tableFrom": "health_check_aggregates",
|
|
127
|
+
"tableTo": "health_check_configurations",
|
|
128
|
+
"columnsFrom": [
|
|
129
|
+
"configuration_id"
|
|
130
|
+
],
|
|
131
|
+
"columnsTo": [
|
|
132
|
+
"id"
|
|
133
|
+
],
|
|
134
|
+
"onDelete": "cascade",
|
|
135
|
+
"onUpdate": "no action"
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
"compositePrimaryKeys": {},
|
|
139
|
+
"uniqueConstraints": {
|
|
140
|
+
"health_check_aggregates_bucket_unique": {
|
|
141
|
+
"name": "health_check_aggregates_bucket_unique",
|
|
142
|
+
"nullsNotDistinct": true,
|
|
143
|
+
"columns": [
|
|
144
|
+
"configuration_id",
|
|
145
|
+
"system_id",
|
|
146
|
+
"bucket_start",
|
|
147
|
+
"bucket_size",
|
|
148
|
+
"source_id"
|
|
149
|
+
]
|
|
150
|
+
}
|
|
151
|
+
},
|
|
152
|
+
"policies": {},
|
|
153
|
+
"checkConstraints": {},
|
|
154
|
+
"isRLSEnabled": false
|
|
155
|
+
},
|
|
156
|
+
"public.health_check_configurations": {
|
|
157
|
+
"name": "health_check_configurations",
|
|
158
|
+
"schema": "",
|
|
159
|
+
"columns": {
|
|
160
|
+
"id": {
|
|
161
|
+
"name": "id",
|
|
162
|
+
"type": "uuid",
|
|
163
|
+
"primaryKey": true,
|
|
164
|
+
"notNull": true,
|
|
165
|
+
"default": "gen_random_uuid()"
|
|
166
|
+
},
|
|
167
|
+
"name": {
|
|
168
|
+
"name": "name",
|
|
169
|
+
"type": "text",
|
|
170
|
+
"primaryKey": false,
|
|
171
|
+
"notNull": true
|
|
172
|
+
},
|
|
173
|
+
"strategy_id": {
|
|
174
|
+
"name": "strategy_id",
|
|
175
|
+
"type": "text",
|
|
176
|
+
"primaryKey": false,
|
|
177
|
+
"notNull": true
|
|
178
|
+
},
|
|
179
|
+
"config": {
|
|
180
|
+
"name": "config",
|
|
181
|
+
"type": "jsonb",
|
|
182
|
+
"primaryKey": false,
|
|
183
|
+
"notNull": true
|
|
184
|
+
},
|
|
185
|
+
"collectors": {
|
|
186
|
+
"name": "collectors",
|
|
187
|
+
"type": "jsonb",
|
|
188
|
+
"primaryKey": false,
|
|
189
|
+
"notNull": false
|
|
190
|
+
},
|
|
191
|
+
"interval_seconds": {
|
|
192
|
+
"name": "interval_seconds",
|
|
193
|
+
"type": "integer",
|
|
194
|
+
"primaryKey": false,
|
|
195
|
+
"notNull": true
|
|
196
|
+
},
|
|
197
|
+
"is_template": {
|
|
198
|
+
"name": "is_template",
|
|
199
|
+
"type": "boolean",
|
|
200
|
+
"primaryKey": false,
|
|
201
|
+
"notNull": false,
|
|
202
|
+
"default": false
|
|
203
|
+
},
|
|
204
|
+
"paused": {
|
|
205
|
+
"name": "paused",
|
|
206
|
+
"type": "boolean",
|
|
207
|
+
"primaryKey": false,
|
|
208
|
+
"notNull": true,
|
|
209
|
+
"default": false
|
|
210
|
+
},
|
|
211
|
+
"created_at": {
|
|
212
|
+
"name": "created_at",
|
|
213
|
+
"type": "timestamp",
|
|
214
|
+
"primaryKey": false,
|
|
215
|
+
"notNull": true,
|
|
216
|
+
"default": "now()"
|
|
217
|
+
},
|
|
218
|
+
"updated_at": {
|
|
219
|
+
"name": "updated_at",
|
|
220
|
+
"type": "timestamp",
|
|
221
|
+
"primaryKey": false,
|
|
222
|
+
"notNull": true,
|
|
223
|
+
"default": "now()"
|
|
224
|
+
}
|
|
225
|
+
},
|
|
226
|
+
"indexes": {},
|
|
227
|
+
"foreignKeys": {},
|
|
228
|
+
"compositePrimaryKeys": {},
|
|
229
|
+
"uniqueConstraints": {},
|
|
230
|
+
"policies": {},
|
|
231
|
+
"checkConstraints": {},
|
|
232
|
+
"isRLSEnabled": false
|
|
233
|
+
},
|
|
234
|
+
"public.health_check_runs": {
|
|
235
|
+
"name": "health_check_runs",
|
|
236
|
+
"schema": "",
|
|
237
|
+
"columns": {
|
|
238
|
+
"id": {
|
|
239
|
+
"name": "id",
|
|
240
|
+
"type": "uuid",
|
|
241
|
+
"primaryKey": true,
|
|
242
|
+
"notNull": true,
|
|
243
|
+
"default": "gen_random_uuid()"
|
|
244
|
+
},
|
|
245
|
+
"configuration_id": {
|
|
246
|
+
"name": "configuration_id",
|
|
247
|
+
"type": "uuid",
|
|
248
|
+
"primaryKey": false,
|
|
249
|
+
"notNull": true
|
|
250
|
+
},
|
|
251
|
+
"system_id": {
|
|
252
|
+
"name": "system_id",
|
|
253
|
+
"type": "text",
|
|
254
|
+
"primaryKey": false,
|
|
255
|
+
"notNull": true
|
|
256
|
+
},
|
|
257
|
+
"status": {
|
|
258
|
+
"name": "status",
|
|
259
|
+
"type": "health_check_status",
|
|
260
|
+
"typeSchema": "public",
|
|
261
|
+
"primaryKey": false,
|
|
262
|
+
"notNull": true
|
|
263
|
+
},
|
|
264
|
+
"latency_ms": {
|
|
265
|
+
"name": "latency_ms",
|
|
266
|
+
"type": "integer",
|
|
267
|
+
"primaryKey": false,
|
|
268
|
+
"notNull": false
|
|
269
|
+
},
|
|
270
|
+
"result": {
|
|
271
|
+
"name": "result",
|
|
272
|
+
"type": "jsonb",
|
|
273
|
+
"primaryKey": false,
|
|
274
|
+
"notNull": false
|
|
275
|
+
},
|
|
276
|
+
"source_id": {
|
|
277
|
+
"name": "source_id",
|
|
278
|
+
"type": "text",
|
|
279
|
+
"primaryKey": false,
|
|
280
|
+
"notNull": false
|
|
281
|
+
},
|
|
282
|
+
"source_label": {
|
|
283
|
+
"name": "source_label",
|
|
284
|
+
"type": "text",
|
|
285
|
+
"primaryKey": false,
|
|
286
|
+
"notNull": false
|
|
287
|
+
},
|
|
288
|
+
"timestamp": {
|
|
289
|
+
"name": "timestamp",
|
|
290
|
+
"type": "timestamp",
|
|
291
|
+
"primaryKey": false,
|
|
292
|
+
"notNull": true,
|
|
293
|
+
"default": "now()"
|
|
294
|
+
}
|
|
295
|
+
},
|
|
296
|
+
"indexes": {},
|
|
297
|
+
"foreignKeys": {
|
|
298
|
+
"health_check_runs_configuration_id_health_check_configurations_id_fk": {
|
|
299
|
+
"name": "health_check_runs_configuration_id_health_check_configurations_id_fk",
|
|
300
|
+
"tableFrom": "health_check_runs",
|
|
301
|
+
"tableTo": "health_check_configurations",
|
|
302
|
+
"columnsFrom": [
|
|
303
|
+
"configuration_id"
|
|
304
|
+
],
|
|
305
|
+
"columnsTo": [
|
|
306
|
+
"id"
|
|
307
|
+
],
|
|
308
|
+
"onDelete": "cascade",
|
|
309
|
+
"onUpdate": "no action"
|
|
310
|
+
}
|
|
311
|
+
},
|
|
312
|
+
"compositePrimaryKeys": {},
|
|
313
|
+
"uniqueConstraints": {},
|
|
314
|
+
"policies": {},
|
|
315
|
+
"checkConstraints": {},
|
|
316
|
+
"isRLSEnabled": false
|
|
317
|
+
},
|
|
318
|
+
"public.system_health_checks": {
|
|
319
|
+
"name": "system_health_checks",
|
|
320
|
+
"schema": "",
|
|
321
|
+
"columns": {
|
|
322
|
+
"system_id": {
|
|
323
|
+
"name": "system_id",
|
|
324
|
+
"type": "text",
|
|
325
|
+
"primaryKey": false,
|
|
326
|
+
"notNull": true
|
|
327
|
+
},
|
|
328
|
+
"configuration_id": {
|
|
329
|
+
"name": "configuration_id",
|
|
330
|
+
"type": "uuid",
|
|
331
|
+
"primaryKey": false,
|
|
332
|
+
"notNull": true
|
|
333
|
+
},
|
|
334
|
+
"enabled": {
|
|
335
|
+
"name": "enabled",
|
|
336
|
+
"type": "boolean",
|
|
337
|
+
"primaryKey": false,
|
|
338
|
+
"notNull": true,
|
|
339
|
+
"default": true
|
|
340
|
+
},
|
|
341
|
+
"state_thresholds": {
|
|
342
|
+
"name": "state_thresholds",
|
|
343
|
+
"type": "jsonb",
|
|
344
|
+
"primaryKey": false,
|
|
345
|
+
"notNull": false
|
|
346
|
+
},
|
|
347
|
+
"retention_config": {
|
|
348
|
+
"name": "retention_config",
|
|
349
|
+
"type": "jsonb",
|
|
350
|
+
"primaryKey": false,
|
|
351
|
+
"notNull": false
|
|
352
|
+
},
|
|
353
|
+
"satellite_ids": {
|
|
354
|
+
"name": "satellite_ids",
|
|
355
|
+
"type": "jsonb",
|
|
356
|
+
"primaryKey": false,
|
|
357
|
+
"notNull": false
|
|
358
|
+
},
|
|
359
|
+
"include_local": {
|
|
360
|
+
"name": "include_local",
|
|
361
|
+
"type": "boolean",
|
|
362
|
+
"primaryKey": false,
|
|
363
|
+
"notNull": true,
|
|
364
|
+
"default": true
|
|
365
|
+
},
|
|
366
|
+
"created_at": {
|
|
367
|
+
"name": "created_at",
|
|
368
|
+
"type": "timestamp",
|
|
369
|
+
"primaryKey": false,
|
|
370
|
+
"notNull": true,
|
|
371
|
+
"default": "now()"
|
|
372
|
+
},
|
|
373
|
+
"updated_at": {
|
|
374
|
+
"name": "updated_at",
|
|
375
|
+
"type": "timestamp",
|
|
376
|
+
"primaryKey": false,
|
|
377
|
+
"notNull": true,
|
|
378
|
+
"default": "now()"
|
|
379
|
+
}
|
|
380
|
+
},
|
|
381
|
+
"indexes": {},
|
|
382
|
+
"foreignKeys": {
|
|
383
|
+
"system_health_checks_configuration_id_health_check_configurations_id_fk": {
|
|
384
|
+
"name": "system_health_checks_configuration_id_health_check_configurations_id_fk",
|
|
385
|
+
"tableFrom": "system_health_checks",
|
|
386
|
+
"tableTo": "health_check_configurations",
|
|
387
|
+
"columnsFrom": [
|
|
388
|
+
"configuration_id"
|
|
389
|
+
],
|
|
390
|
+
"columnsTo": [
|
|
391
|
+
"id"
|
|
392
|
+
],
|
|
393
|
+
"onDelete": "cascade",
|
|
394
|
+
"onUpdate": "no action"
|
|
395
|
+
}
|
|
396
|
+
},
|
|
397
|
+
"compositePrimaryKeys": {
|
|
398
|
+
"system_health_checks_system_id_configuration_id_pk": {
|
|
399
|
+
"name": "system_health_checks_system_id_configuration_id_pk",
|
|
400
|
+
"columns": [
|
|
401
|
+
"system_id",
|
|
402
|
+
"configuration_id"
|
|
403
|
+
]
|
|
404
|
+
}
|
|
405
|
+
},
|
|
406
|
+
"uniqueConstraints": {},
|
|
407
|
+
"policies": {},
|
|
408
|
+
"checkConstraints": {},
|
|
409
|
+
"isRLSEnabled": false
|
|
410
|
+
}
|
|
411
|
+
},
|
|
412
|
+
"enums": {
|
|
413
|
+
"public.bucket_size": {
|
|
414
|
+
"name": "bucket_size",
|
|
415
|
+
"schema": "public",
|
|
416
|
+
"values": [
|
|
417
|
+
"hourly",
|
|
418
|
+
"daily"
|
|
419
|
+
]
|
|
420
|
+
},
|
|
421
|
+
"public.health_check_status": {
|
|
422
|
+
"name": "health_check_status",
|
|
423
|
+
"schema": "public",
|
|
424
|
+
"values": [
|
|
425
|
+
"healthy",
|
|
426
|
+
"unhealthy",
|
|
427
|
+
"degraded"
|
|
428
|
+
]
|
|
429
|
+
}
|
|
430
|
+
},
|
|
431
|
+
"schemas": {},
|
|
432
|
+
"sequences": {},
|
|
433
|
+
"roles": {},
|
|
434
|
+
"policies": {},
|
|
435
|
+
"views": {},
|
|
436
|
+
"_meta": {
|
|
437
|
+
"columns": {},
|
|
438
|
+
"schemas": {},
|
|
439
|
+
"tables": {}
|
|
440
|
+
}
|
|
441
|
+
}
|
package/package.json
CHANGED
package/src/schema.ts
CHANGED
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
uuid,
|
|
9
9
|
timestamp,
|
|
10
10
|
primaryKey,
|
|
11
|
-
|
|
11
|
+
unique,
|
|
12
12
|
} from "drizzle-orm/pg-core";
|
|
13
13
|
import type {
|
|
14
14
|
StateThresholds,
|
|
@@ -182,13 +182,15 @@ export const healthCheckAggregates = pgTable(
|
|
|
182
182
|
sourceLabel: text("source_label"),
|
|
183
183
|
},
|
|
184
184
|
(t) => ({
|
|
185
|
-
// Unique constraint includes sourceId for per-region aggregation
|
|
186
|
-
|
|
185
|
+
// Unique constraint includes sourceId for per-region aggregation.
|
|
186
|
+
// NULLS NOT DISTINCT ensures local runs (sourceId=NULL) correctly
|
|
187
|
+
// conflict-match instead of creating duplicate rows per hour.
|
|
188
|
+
bucketUnique: unique("health_check_aggregates_bucket_unique").on(
|
|
187
189
|
t.configurationId,
|
|
188
190
|
t.systemId,
|
|
189
191
|
t.bucketStart,
|
|
190
192
|
t.bucketSize,
|
|
191
193
|
t.sourceId,
|
|
192
|
-
),
|
|
194
|
+
).nullsNotDistinct(),
|
|
193
195
|
}),
|
|
194
196
|
);
|