envio 3.5.1 → 3.6.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/evm.schema.json +7 -0
- package/fuel.schema.json +7 -0
- package/index.d.ts +33 -10
- package/package.json +6 -6
- package/src/ChainState.res +12 -0
- package/src/ChainState.res.mjs +15 -2
- package/src/ChainState.resi +3 -0
- package/src/Config.res +60 -2
- package/src/Config.res.mjs +49 -13
- package/src/EffectState.res +2 -2
- package/src/EffectState.res.mjs +2 -2
- package/src/EntityTables.res +32 -0
- package/src/EntityTables.res.mjs +44 -0
- package/src/Envio.res +7 -7
- package/src/Envio.res.mjs +5 -6
- package/src/Hasura.res +37 -3
- package/src/Hasura.res.mjs +23 -5
- package/src/InMemoryStore.res +48 -8
- package/src/InMemoryStore.res.mjs +37 -7
- package/src/IndexerState.res +30 -24
- package/src/IndexerState.res.mjs +20 -35
- package/src/IndexerState.resi +8 -6
- package/src/Internal.res +29 -11
- package/src/Internal.res.mjs +27 -10
- package/src/LoadLayer.res +39 -8
- package/src/LoadLayer.res.mjs +36 -9
- package/src/LoadLayer.resi +2 -0
- package/src/Persistence.res +12 -1
- package/src/PgStorage.res +183 -37
- package/src/PgStorage.res.mjs +149 -34
- package/src/PruneStaleHistory.res +1 -0
- package/src/PruneStaleHistory.res.mjs +2 -1
- package/src/Sink.res +3 -3
- package/src/Sink.res.mjs +2 -2
- package/src/TestIndexer.res +128 -17
- package/src/TestIndexer.res.mjs +75 -9
- package/src/UserContext.res +19 -4
- package/src/UserContext.res.mjs +15 -9
- package/src/Writing.res +8 -16
- package/src/Writing.res.mjs +10 -10
- package/src/bindings/ClickHouse.res +40 -4
- package/src/bindings/ClickHouse.res.mjs +37 -5
- package/src/db/EntityHistory.res +64 -21
- package/src/db/EntityHistory.res.mjs +43 -22
- package/src/db/InternalTable.res.mjs +31 -31
- package/src/db/Table.res +24 -1
- package/src/db/Table.res.mjs +26 -4
- package/svm.schema.json +7 -0
package/src/PgStorage.res
CHANGED
|
@@ -118,6 +118,36 @@ let makeCreateTableQuery = (
|
|
|
118
118
|
: ""});`
|
|
119
119
|
}
|
|
120
120
|
|
|
121
|
+
// The entity as it's stored: the handler-visible schema plus the chain-id
|
|
122
|
+
// column a per-chain entity's table carries. The value for that column is
|
|
123
|
+
// stamped from the flush group's scope right before serialization, so it never
|
|
124
|
+
// has to be re-derived from a checkpoint.
|
|
125
|
+
let rowSchemaCache = Utils.WeakMap.make()
|
|
126
|
+
let getRowSchema = (entityConfig: Internal.entityConfig): S.t<Internal.entity> =>
|
|
127
|
+
switch rowSchemaCache->Utils.WeakMap.get(entityConfig) {
|
|
128
|
+
| Some(cached) => cached
|
|
129
|
+
| None =>
|
|
130
|
+
let schema = switch entityConfig.table->Table.getChainIdField {
|
|
131
|
+
| None => entityConfig.schema
|
|
132
|
+
| Some(chainIdField) =>
|
|
133
|
+
S.schema(s => {
|
|
134
|
+
let dict = Dict.make()
|
|
135
|
+
switch entityConfig.schema->S.classify {
|
|
136
|
+
| Object({items}) =>
|
|
137
|
+
items->Array.forEach(({location, schema}) => dict->Dict.set(location, s.matches(schema)))
|
|
138
|
+
| _ =>
|
|
139
|
+
JsError.throwWithMessage(
|
|
140
|
+
`Unexpected non-object schema for entity "${entityConfig.name}".`,
|
|
141
|
+
)
|
|
142
|
+
}
|
|
143
|
+
dict->Dict.set(chainIdField.fieldName, s.matches(ChainId.schema->S.toUnknown))
|
|
144
|
+
dict
|
|
145
|
+
})->(Utils.magic: S.t<dict<unknown>> => S.t<Internal.entity>)
|
|
146
|
+
}
|
|
147
|
+
rowSchemaCache->Utils.WeakMap.set(entityConfig, schema)->ignore
|
|
148
|
+
schema
|
|
149
|
+
}
|
|
150
|
+
|
|
121
151
|
let entityHistoryCache = Utils.WeakMap.make()
|
|
122
152
|
let getEntityHistory = (~entityConfig: Internal.entityConfig): EntityHistory.pgEntityHistory<
|
|
123
153
|
'entity,
|
|
@@ -134,6 +164,9 @@ let getEntityHistory = (~entityConfig: Internal.entityConfig): EntityHistory.pgE
|
|
|
134
164
|
switch field.fieldName {
|
|
135
165
|
//id is not nullable and should be part of the pk
|
|
136
166
|
| "id" => {...field, fieldName: id, isPrimaryKey: true}->Table.Field->Some
|
|
167
|
+
// Same for the chain id of a per-chain entity: it completes the row's
|
|
168
|
+
// identity, so it can neither be nulled out nor left out of the pk.
|
|
169
|
+
| _ if field.isChainId => field->Table.Field->Some
|
|
137
170
|
| _ =>
|
|
138
171
|
{
|
|
139
172
|
...field,
|
|
@@ -174,7 +207,7 @@ let getEntityHistory = (~entityConfig: Internal.entityConfig): EntityHistory.pgE
|
|
|
174
207
|
|
|
175
208
|
let setChangeSchema = EntityHistory.makeSetUpdateSchema(
|
|
176
209
|
~idSchema=entityConfig.table->Table.getIdSchema,
|
|
177
|
-
entityConfig
|
|
210
|
+
entityConfig->getRowSchema,
|
|
178
211
|
)
|
|
179
212
|
|
|
180
213
|
{
|
|
@@ -353,12 +386,22 @@ let rec makeFilterCondition = (
|
|
|
353
386
|
}
|
|
354
387
|
}
|
|
355
388
|
|
|
356
|
-
|
|
357
|
-
|
|
389
|
+
// The chain-id predicate a per-chain entity's row-level SQL needs, already
|
|
390
|
+
// including the leading AND. The chain id is bound as $2 — after the id
|
|
391
|
+
// params at $1 — so one prepared statement serves every chain. Empty for
|
|
392
|
+
// cross-chain entities and for internal tables, which have no such column.
|
|
393
|
+
let makeChainIdCondition = (~table: Table.table, ~chainId: option<ChainId.t>) =>
|
|
394
|
+
switch (table->Table.getChainIdField, chainId) {
|
|
395
|
+
| (Some(field), Some(_)) => ` AND "${field->Table.getPgDbFieldName}" = $2`
|
|
396
|
+
| _ => ""
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
let makeDeleteByIdQuery = (~pgSchema, ~tableName, ~chainIdCondition) => {
|
|
400
|
+
`DELETE FROM "${pgSchema}"."${tableName}" WHERE id = $1${chainIdCondition};`
|
|
358
401
|
}
|
|
359
402
|
|
|
360
|
-
let makeDeleteByIdsQuery = (~pgSchema, ~tableName, ~idPgType) => {
|
|
361
|
-
`DELETE FROM "${pgSchema}"."${tableName}" WHERE id = ANY($1::${idPgType}[]);`
|
|
403
|
+
let makeDeleteByIdsQuery = (~pgSchema, ~tableName, ~idPgType, ~chainIdCondition) => {
|
|
404
|
+
`DELETE FROM "${pgSchema}"."${tableName}" WHERE id = ANY($1::${idPgType}[])${chainIdCondition};`
|
|
362
405
|
}
|
|
363
406
|
|
|
364
407
|
let makeLoadAllQuery = (~pgSchema, ~tableName) => {
|
|
@@ -779,17 +822,32 @@ let getConnectedPsqlExec = {
|
|
|
779
822
|
}
|
|
780
823
|
}
|
|
781
824
|
|
|
782
|
-
let deleteByIdsOrThrow = async (
|
|
825
|
+
let deleteByIdsOrThrow = async (
|
|
826
|
+
sql,
|
|
827
|
+
~pgSchema,
|
|
828
|
+
~ids: array<EntityId.t>,
|
|
829
|
+
~table: Table.table,
|
|
830
|
+
~chainId: option<ChainId.t>=None,
|
|
831
|
+
) => {
|
|
832
|
+
let chainIdCondition = makeChainIdCondition(~table, ~chainId)
|
|
833
|
+
let chainIdParams = switch chainId {
|
|
834
|
+
| Some(chainId) if chainIdCondition !== "" => [chainId->(Utils.magic: ChainId.t => unknown)]
|
|
835
|
+
| _ => []
|
|
836
|
+
}
|
|
783
837
|
// A JSON array of the serialized ids. For a single id the query binds it as
|
|
784
838
|
// `$1` directly (the array is the positional-params array); for many it binds
|
|
785
|
-
// the whole array to `$1` behind an `ANY(...)`.
|
|
839
|
+
// the whole array to `$1` behind an `ANY(...)`. The chain id, when the
|
|
840
|
+
// condition needs it, rides along as $2.
|
|
786
841
|
let idsJson = table->Table.encodeIdsToJson(ids)
|
|
787
842
|
switch await (
|
|
788
843
|
switch ids {
|
|
789
844
|
| [_] =>
|
|
790
845
|
sql->Postgres.preparedUnsafe(
|
|
791
|
-
makeDeleteByIdQuery(~pgSchema, ~tableName=table.tableName),
|
|
792
|
-
idsJson
|
|
846
|
+
makeDeleteByIdQuery(~pgSchema, ~tableName=table.tableName, ~chainIdCondition),
|
|
847
|
+
idsJson
|
|
848
|
+
->(Utils.magic: JSON.t => array<unknown>)
|
|
849
|
+
->Array.concat(chainIdParams)
|
|
850
|
+
->Obj.magic,
|
|
793
851
|
)
|
|
794
852
|
| _ =>
|
|
795
853
|
sql->Postgres.preparedUnsafe(
|
|
@@ -797,8 +855,9 @@ let deleteByIdsOrThrow = async (sql, ~pgSchema, ~ids: array<EntityId.t>, ~table:
|
|
|
797
855
|
~pgSchema,
|
|
798
856
|
~tableName=table.tableName,
|
|
799
857
|
~idPgType=table->Table.getIdPgFieldType(~pgSchema),
|
|
858
|
+
~chainIdCondition,
|
|
800
859
|
),
|
|
801
|
-
[idsJson]->Obj.magic,
|
|
860
|
+
[idsJson->(Utils.magic: JSON.t => unknown)]->Array.concat(chainIdParams)->Obj.magic,
|
|
802
861
|
)
|
|
803
862
|
}
|
|
804
863
|
) {
|
|
@@ -813,7 +872,11 @@ let deleteByIdsOrThrow = async (sql, ~pgSchema, ~ids: array<EntityId.t>, ~table:
|
|
|
813
872
|
}
|
|
814
873
|
}
|
|
815
874
|
|
|
816
|
-
let makeInsertDeleteUpdatesQuery = (
|
|
875
|
+
let makeInsertDeleteUpdatesQuery = (
|
|
876
|
+
~entityConfig: Internal.entityConfig,
|
|
877
|
+
~pgSchema,
|
|
878
|
+
~chainId: option<ChainId.t>,
|
|
879
|
+
) => {
|
|
817
880
|
let historyTableName = EntityHistory.historyTableName(
|
|
818
881
|
~entityName=entityConfig.name,
|
|
819
882
|
~entityIndex=entityConfig.index,
|
|
@@ -833,6 +896,13 @@ let makeInsertDeleteUpdatesQuery = (~entityConfig: Internal.entityConfig, ~pgSch
|
|
|
833
896
|
allHistoryFieldNames->Array.map(name => `"${name}"`)->Array.joinUnsafe(", ")
|
|
834
897
|
|
|
835
898
|
// Build the SELECT part: id from unnest, envio_checkpoint_id from unnest, 'DELETE' for action, NULL for all other fields
|
|
899
|
+
// The chain-id column is part of the history primary key, so a DELETE row
|
|
900
|
+
// carries the scope's chain — bound once as $3 — rather than the NULL every
|
|
901
|
+
// other data field gets.
|
|
902
|
+
let chainIdColumn = switch (entityConfig.table->Table.getChainIdField, chainId) {
|
|
903
|
+
| (Some(field), Some(_)) => field->Table.getPgDbFieldName
|
|
904
|
+
| _ => ""
|
|
905
|
+
}
|
|
836
906
|
let selectParts = allHistoryFieldNames->Array.map(fieldName => {
|
|
837
907
|
switch fieldName {
|
|
838
908
|
| field if field == Table.idFieldName => `u.${Table.idFieldName}`
|
|
@@ -840,6 +910,7 @@ let makeInsertDeleteUpdatesQuery = (~entityConfig: Internal.entityConfig, ~pgSch
|
|
|
840
910
|
`u.${EntityHistory.checkpointIdFieldName}`
|
|
841
911
|
| field if field == EntityHistory.changeFieldName =>
|
|
842
912
|
`'${(EntityHistory.RowAction.DELETE :> string)}'`
|
|
913
|
+
| field if chainIdColumn !== "" && field == chainIdColumn => "$3"
|
|
843
914
|
| _ => "NULL"
|
|
844
915
|
}
|
|
845
916
|
})
|
|
@@ -942,10 +1013,38 @@ let rec writeBatch = async (
|
|
|
942
1013
|
}
|
|
943
1014
|
}
|
|
944
1015
|
|
|
945
|
-
let setEntities = updatedEntities->Array.map(({entityConfig, changes}) => {
|
|
1016
|
+
let setEntities = updatedEntities->Array.map(({entityConfig, scope, changes}) => {
|
|
946
1017
|
let entitiesToSet = []
|
|
947
1018
|
let idsToDelete = []
|
|
948
1019
|
|
|
1020
|
+
// Every row in this group belongs to the group's scope, so the chain id
|
|
1021
|
+
// is stamped once here instead of being looked up per row downstream.
|
|
1022
|
+
let scopeChainId = switch scope {
|
|
1023
|
+
| Internal.CrossChain => None
|
|
1024
|
+
| Chain(chainId) => Some(chainId)
|
|
1025
|
+
}
|
|
1026
|
+
let changes = switch (entityConfig.table->Table.getChainIdField, scopeChainId) {
|
|
1027
|
+
| (Some(field), Some(chainId)) =>
|
|
1028
|
+
changes->Array.map(change =>
|
|
1029
|
+
switch change {
|
|
1030
|
+
| Change.Set(set) =>
|
|
1031
|
+
Change.Set({
|
|
1032
|
+
...set,
|
|
1033
|
+
entity: set.entity->Internal.stampChainId(~fieldName=field.fieldName, ~chainId),
|
|
1034
|
+
})
|
|
1035
|
+
| Delete(_) => change
|
|
1036
|
+
}
|
|
1037
|
+
)
|
|
1038
|
+
| _ => changes
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
// Bound as $3 by the history-delete query, after its two unnest arrays.
|
|
1042
|
+
// Empty for cross-chain entities, whose SQL has no such param.
|
|
1043
|
+
let chainIdParams = switch (entityConfig.table->Table.getChainIdField, scopeChainId) {
|
|
1044
|
+
| (Some(_), Some(chainId)) => [chainId->(Utils.magic: ChainId.t => unknown)]
|
|
1045
|
+
| _ => []
|
|
1046
|
+
}
|
|
1047
|
+
|
|
949
1048
|
// The rollback-diff change is written to the entity table only, never the
|
|
950
1049
|
// history table; when present it is an id's oldest change.
|
|
951
1050
|
let diffCheckpointId = rollback->Option.map(r => r.diffCheckpointId)
|
|
@@ -1016,6 +1115,7 @@ let rec writeBatch = async (
|
|
|
1016
1115
|
~pgSchema,
|
|
1017
1116
|
~table=entityConfig.table,
|
|
1018
1117
|
~entityIndex=entityConfig.index,
|
|
1118
|
+
~chainId=scopeChainId,
|
|
1019
1119
|
~ids=backfillHistoryIds->Utils.Set.toArray,
|
|
1020
1120
|
)
|
|
1021
1121
|
}
|
|
@@ -1024,11 +1124,17 @@ let rec writeBatch = async (
|
|
|
1024
1124
|
promises->Array.push(
|
|
1025
1125
|
sql
|
|
1026
1126
|
->Postgres.preparedUnsafe(
|
|
1027
|
-
makeInsertDeleteUpdatesQuery(~entityConfig, ~pgSchema),
|
|
1028
|
-
|
|
1029
|
-
entityConfig.table
|
|
1030
|
-
|
|
1031
|
-
|
|
1127
|
+
makeInsertDeleteUpdatesQuery(~entityConfig, ~pgSchema, ~chainId=scopeChainId),
|
|
1128
|
+
[
|
|
1129
|
+
entityConfig.table
|
|
1130
|
+
->Table.encodeIdsToJson(batchDeleteEntityIds)
|
|
1131
|
+
->(Utils.magic: JSON.t => unknown),
|
|
1132
|
+
batchDeleteCheckpointIds
|
|
1133
|
+
->Utils.BigInt.arrayToStringArray
|
|
1134
|
+
->(Utils.magic: array<string> => unknown),
|
|
1135
|
+
]
|
|
1136
|
+
->Array.concat(chainIdParams)
|
|
1137
|
+
->Obj.magic,
|
|
1032
1138
|
)
|
|
1033
1139
|
->Utils.Promise.ignoreValue,
|
|
1034
1140
|
)
|
|
@@ -1069,7 +1175,7 @@ let rec writeBatch = async (
|
|
|
1069
1175
|
sql->setOrThrow(
|
|
1070
1176
|
~items=entitiesToSet,
|
|
1071
1177
|
~table=entityConfig.table,
|
|
1072
|
-
~itemSchema=entityConfig
|
|
1178
|
+
~itemSchema=entityConfig->getRowSchema,
|
|
1073
1179
|
~pgSchema,
|
|
1074
1180
|
~chainIdMode,
|
|
1075
1181
|
),
|
|
@@ -1077,7 +1183,12 @@ let rec writeBatch = async (
|
|
|
1077
1183
|
}
|
|
1078
1184
|
if idsToDelete->Utils.Array.notEmpty {
|
|
1079
1185
|
promises->Array.push(
|
|
1080
|
-
sql->deleteByIdsOrThrow(
|
|
1186
|
+
sql->deleteByIdsOrThrow(
|
|
1187
|
+
~pgSchema,
|
|
1188
|
+
~ids=idsToDelete,
|
|
1189
|
+
~table=entityConfig.table,
|
|
1190
|
+
~chainId=scopeChainId,
|
|
1191
|
+
),
|
|
1081
1192
|
)
|
|
1082
1193
|
}
|
|
1083
1194
|
|
|
@@ -1244,6 +1355,14 @@ let rec writeBatch = async (
|
|
|
1244
1355
|
|
|
1245
1356
|
// Returns the most recent history row at or before the rollback target for IDs changed after it.
|
|
1246
1357
|
// envio_change is included so ReScript can turn SET rows into restores and DELETE rows into removals.
|
|
1358
|
+
// The columns that identify a history row: the id, plus the chain id for a
|
|
1359
|
+
// per-chain entity.
|
|
1360
|
+
let rollbackKeyColumns = (entityConfig: Internal.entityConfig) =>
|
|
1361
|
+
switch entityConfig.table->Table.getChainIdField {
|
|
1362
|
+
| Some(field) => [Table.idFieldName, field->Table.getPgDbFieldName]
|
|
1363
|
+
| None => [Table.idFieldName]
|
|
1364
|
+
}
|
|
1365
|
+
|
|
1247
1366
|
let makeGetRollbackPreTargetRowsQuery = (~entityConfig: Internal.entityConfig, ~pgSchema) => {
|
|
1248
1367
|
let dataFieldNames = entityConfig.table.fields->Array.filterMap(fieldOrDerived =>
|
|
1249
1368
|
switch fieldOrDerived {
|
|
@@ -1260,16 +1379,25 @@ let makeGetRollbackPreTargetRowsQuery = (~entityConfig: Internal.entityConfig, ~
|
|
|
1260
1379
|
~entityIndex=entityConfig.index,
|
|
1261
1380
|
)
|
|
1262
1381
|
|
|
1263
|
-
|
|
1382
|
+
// A per-chain entity's rows are only comparable within a chain, so the row's
|
|
1383
|
+
// identity here is (id, chain id) rather than the id alone.
|
|
1384
|
+
let keyColumns = rollbackKeyColumns(entityConfig)
|
|
1385
|
+
let keyColumnsCommaSeparated = keyColumns->Array.map(c => `"${c}"`)->Array.joinUnsafe(", ")
|
|
1386
|
+
let keyMatch =
|
|
1387
|
+
keyColumns
|
|
1388
|
+
->Array.map(c => `h."${c}" = "${historyTableName}"."${c}"`)
|
|
1389
|
+
->Array.joinUnsafe(" AND ")
|
|
1390
|
+
|
|
1391
|
+
`SELECT DISTINCT ON (${keyColumnsCommaSeparated}) ${dataFieldsCommaSeparated}, "${EntityHistory.changeFieldName}"
|
|
1264
1392
|
FROM "${pgSchema}"."${historyTableName}"
|
|
1265
1393
|
WHERE "${EntityHistory.checkpointIdFieldName}" <= $1
|
|
1266
1394
|
AND EXISTS (
|
|
1267
1395
|
SELECT 1
|
|
1268
1396
|
FROM "${pgSchema}"."${historyTableName}" h
|
|
1269
|
-
WHERE
|
|
1397
|
+
WHERE ${keyMatch}
|
|
1270
1398
|
AND h."${EntityHistory.checkpointIdFieldName}" > $1
|
|
1271
1399
|
)
|
|
1272
|
-
ORDER BY
|
|
1400
|
+
ORDER BY ${keyColumnsCommaSeparated}, "${EntityHistory.checkpointIdFieldName}" DESC`
|
|
1273
1401
|
}
|
|
1274
1402
|
|
|
1275
1403
|
// Returns entity IDs that were created after the rollback target and have no history before it.
|
|
@@ -1279,13 +1407,19 @@ let makeGetRollbackRemovedIdsQuery = (~entityConfig: Internal.entityConfig, ~pgS
|
|
|
1279
1407
|
~entityName=entityConfig.name,
|
|
1280
1408
|
~entityIndex=entityConfig.index,
|
|
1281
1409
|
)
|
|
1282
|
-
|
|
1410
|
+
let keyColumns = rollbackKeyColumns(entityConfig)
|
|
1411
|
+
let keyMatch =
|
|
1412
|
+
keyColumns
|
|
1413
|
+
->Array.map(c => `h."${c}" = "${historyTableName}"."${c}"`)
|
|
1414
|
+
->Array.joinUnsafe(" AND ")
|
|
1415
|
+
|
|
1416
|
+
`SELECT DISTINCT ${keyColumns->Array.map(c => `"${c}"`)->Array.joinUnsafe(", ")}
|
|
1283
1417
|
FROM "${pgSchema}"."${historyTableName}"
|
|
1284
1418
|
WHERE "${EntityHistory.checkpointIdFieldName}" > $1
|
|
1285
1419
|
AND NOT EXISTS (
|
|
1286
1420
|
SELECT 1
|
|
1287
1421
|
FROM "${pgSchema}"."${historyTableName}" h
|
|
1288
|
-
WHERE
|
|
1422
|
+
WHERE ${keyMatch}
|
|
1289
1423
|
AND h."${EntityHistory.checkpointIdFieldName}" <= $1
|
|
1290
1424
|
)`
|
|
1291
1425
|
}
|
|
@@ -1303,11 +1437,19 @@ let rollbackRowStateSchema: Table.table => S.t<(
|
|
|
1303
1437
|
))
|
|
1304
1438
|
)
|
|
1305
1439
|
|
|
1440
|
+
// The chain a rollback row belongs to, read from the chain-id column both
|
|
1441
|
+
// rollback queries select. None for a cross-chain entity, which has no column.
|
|
1442
|
+
let rollbackChainIdSchema: Table.table => option<S.t<ChainId.t>> = Utils.WeakMap.memoize(table =>
|
|
1443
|
+
table
|
|
1444
|
+
->Table.getChainIdField
|
|
1445
|
+
->Option.map(field => S.object(s => s.field(field->Table.getPgDbFieldName, ChainId.schema)))
|
|
1446
|
+
)
|
|
1447
|
+
|
|
1306
1448
|
// Same reason as above for the id-only rows: both rollback queries must yield
|
|
1307
1449
|
// ids in the entity's own representation, or the two halves of the diff would
|
|
1308
1450
|
// disagree (Postgres hands back a NUMERIC id as a string, not a bigint).
|
|
1309
|
-
let
|
|
1310
|
-
S.
|
|
1451
|
+
let rollbackRemovedIdSchema: Table.table => S.t<EntityId.t> = Utils.WeakMap.memoize(table =>
|
|
1452
|
+
S.object(s => s.field(Table.idFieldName, table->Table.getIdSchema))
|
|
1311
1453
|
)
|
|
1312
1454
|
|
|
1313
1455
|
let make = (
|
|
@@ -1366,12 +1508,6 @@ let make = (
|
|
|
1366
1508
|
"indexes": invalidIndexNames,
|
|
1367
1509
|
})
|
|
1368
1510
|
}
|
|
1369
|
-
Logging.info({
|
|
1370
|
-
"storage": storageName,
|
|
1371
|
-
"msg": `Found ${catalog
|
|
1372
|
-
->IndexCatalog.size
|
|
1373
|
-
->Int.toString} existing indexes in the indexer schema.`,
|
|
1374
|
-
})
|
|
1375
1511
|
}
|
|
1376
1512
|
|
|
1377
1513
|
let isInitialized = async () => {
|
|
@@ -1657,7 +1793,7 @@ SELECT id, chain_id, -1, -1, contract_name FROM unnest($1::text[],$2::${addrChai
|
|
|
1657
1793
|
}),
|
|
1658
1794
|
)
|
|
1659
1795
|
| rows =>
|
|
1660
|
-
try rows->S.parseOrThrow(table->Table.
|
|
1796
|
+
try rows->S.parseOrThrow(table->Table.pgEntityRowsSchema) catch {
|
|
1661
1797
|
| exn =>
|
|
1662
1798
|
throw(
|
|
1663
1799
|
Persistence.StorageError({
|
|
@@ -2101,12 +2237,13 @@ SELECT id, chain_id, -1, -1, contract_name FROM unnest($1::text[],$2::${addrChai
|
|
|
2101
2237
|
let pruneStaleCheckpoints = (~safeCheckpointId) =>
|
|
2102
2238
|
InternalTable.Checkpoints.pruneStaleCheckpoints(sql, ~pgSchema, ~safeCheckpointId)
|
|
2103
2239
|
|
|
2104
|
-
let pruneStaleEntityHistory = (~entityName, ~entityIndex, ~safeCheckpointId) =>
|
|
2240
|
+
let pruneStaleEntityHistory = (~entityName, ~entityIndex, ~chainIdColumn, ~safeCheckpointId) =>
|
|
2105
2241
|
EntityHistory.pruneStaleEntityHistory(
|
|
2106
2242
|
sql,
|
|
2107
2243
|
~pgSchema,
|
|
2108
2244
|
~entityName,
|
|
2109
2245
|
~entityIndex,
|
|
2246
|
+
~chainIdColumn,
|
|
2110
2247
|
~safeCheckpointId,
|
|
2111
2248
|
)
|
|
2112
2249
|
|
|
@@ -2142,17 +2279,26 @@ SELECT id, chain_id, -1, -1, contract_name FROM unnest($1::text[],$2::${addrChai
|
|
|
2142
2279
|
->(Utils.magic: promise<unknown> => promise<array<unknown>>),
|
|
2143
2280
|
))
|
|
2144
2281
|
|
|
2145
|
-
let
|
|
2282
|
+
let chainIdSchema = rollbackChainIdSchema(entityConfig.table)
|
|
2283
|
+
let scopeOf = row =>
|
|
2284
|
+
switch chainIdSchema {
|
|
2285
|
+
| None => Internal.CrossChain
|
|
2286
|
+
| Some(schema) => Internal.Chain(row->S.parseOrThrow(schema))
|
|
2287
|
+
}
|
|
2288
|
+
let removals = removedIdRows->Array.map((row): Persistence.rollbackRemoval => {
|
|
2289
|
+
entityId: row->S.parseOrThrow(rollbackRemovedIdSchema(entityConfig.table)),
|
|
2290
|
+
scope: scopeOf(row),
|
|
2291
|
+
})
|
|
2146
2292
|
let restoredEntitiesResult = []
|
|
2147
2293
|
rollbackRows->Array.forEach(row => {
|
|
2148
2294
|
let (entityId, action) = row->S.parseOrThrow(rollbackRowStateSchema(entityConfig.table))
|
|
2149
2295
|
switch action {
|
|
2150
2296
|
| SET => restoredEntitiesResult->Array.push(row)->ignore
|
|
2151
|
-
| DELETE =>
|
|
2297
|
+
| DELETE => removals->Array.push({entityId, scope: scopeOf(row)})->ignore
|
|
2152
2298
|
}
|
|
2153
2299
|
})
|
|
2154
2300
|
|
|
2155
|
-
(
|
|
2301
|
+
(removals, restoredEntitiesResult)
|
|
2156
2302
|
}
|
|
2157
2303
|
|
|
2158
2304
|
let writeBatchMethod = async (
|