envio 2.32.1 → 3.0.0-alpha-main-clickhouse-sink

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.
Files changed (45) hide show
  1. package/index.d.ts +1 -0
  2. package/package.json +6 -5
  3. package/src/Batch.res +4 -4
  4. package/src/Change.res +9 -0
  5. package/src/Change.res.js +2 -0
  6. package/src/Config.res +5 -5
  7. package/src/Config.res.js +3 -1
  8. package/src/Envio.gen.ts +3 -3
  9. package/src/Envio.res +14 -3
  10. package/src/EventRegister.res +3 -11
  11. package/src/EventRegister.res.js +4 -8
  12. package/src/EventRegister.resi +1 -1
  13. package/src/InMemoryStore.gen.ts +6 -0
  14. package/src/InMemoryStore.res +149 -0
  15. package/src/InMemoryStore.res.js +161 -0
  16. package/src/InMemoryTable.res +50 -35
  17. package/src/InMemoryTable.res.js +52 -84
  18. package/src/Internal.gen.ts +0 -2
  19. package/src/Internal.res +20 -38
  20. package/src/Internal.res.js +2 -16
  21. package/src/LoadManager.res +23 -16
  22. package/src/LoadManager.res.js +17 -15
  23. package/src/Persistence.res +190 -38
  24. package/src/Persistence.res.js +92 -39
  25. package/src/PgStorage.res +700 -14
  26. package/src/PgStorage.res.js +431 -19
  27. package/src/Platform.res +141 -0
  28. package/src/Platform.res.js +170 -0
  29. package/src/Prometheus.res +41 -0
  30. package/src/Prometheus.res.js +45 -0
  31. package/src/SafeCheckpointTracking.res +5 -4
  32. package/src/Sink.res +47 -0
  33. package/src/Sink.res.js +36 -0
  34. package/src/Utils.res +2 -0
  35. package/src/Utils.res.js +3 -0
  36. package/src/bindings/ClickHouse.res +387 -0
  37. package/src/bindings/ClickHouse.res.js +274 -0
  38. package/src/bindings/Postgres.res +15 -0
  39. package/src/bindings/Promise.res +3 -0
  40. package/src/db/EntityHistory.res +33 -156
  41. package/src/db/EntityHistory.res.js +40 -115
  42. package/src/db/InternalTable.res +56 -55
  43. package/src/db/InternalTable.res.js +49 -52
  44. package/src/db/Table.res +86 -22
  45. package/src/db/Table.res.js +77 -10
@@ -6,6 +6,8 @@ var Path = require("path");
6
6
  var $$Array = require("rescript/lib/js/array.js");
7
7
  var Table = require("./db/Table.res.js");
8
8
  var Utils = require("./Utils.res.js");
9
+ var Config = require("./Config.res.js");
10
+ var Hrtime = require("./bindings/Hrtime.res.js");
9
11
  var Js_exn = require("rescript/lib/js/js_exn.js");
10
12
  var Schema = require("./db/Schema.res.js");
11
13
  var Js_dict = require("rescript/lib/js/js_dict.js");
@@ -13,6 +15,7 @@ var Logging = require("./Logging.res.js");
13
15
  var $$Promise = require("./bindings/Promise.res.js");
14
16
  var Internal = require("./Internal.res.js");
15
17
  var Belt_Array = require("rescript/lib/js/belt_Array.js");
18
+ var Prometheus = require("./Prometheus.res.js");
16
19
  var Caml_option = require("rescript/lib/js/caml_option.js");
17
20
  var Persistence = require("./Persistence.res.js");
18
21
  var EntityHistory = require("./db/EntityHistory.res.js");
@@ -48,20 +51,11 @@ function makeCreateTableIndicesQuery(table, pgSchema) {
48
51
  function makeCreateTableQuery(table, pgSchema, isNumericArrayAsText) {
49
52
  var fieldsMapped = Belt_Array.map(Table.getFields(table), (function (field) {
50
53
  var defaultValue = field.defaultValue;
51
- var isArray = field.isArray;
52
- var fieldType = field.fieldType;
54
+ var isNullable = field.isNullable;
53
55
  var fieldName = Table.getDbFieldName(field);
54
- var tmp;
55
- tmp = fieldType === "TIMESTAMP" || fieldType === "TIMESTAMP WITH TIME ZONE" || fieldType === "JSONB" || fieldType === "SERIAL" || fieldType === "TEXT" || fieldType === "DOUBLE PRECISION" || fieldType === "NUMERIC" || fieldType === "BOOLEAN" || fieldType === "BIGINT" || fieldType === "INTEGER" || fieldType === "TIMESTAMP WITH TIME ZONE NULL" ? (
56
- fieldType === "NUMERIC" && isArray && isNumericArrayAsText ? "TEXT" : fieldType
57
- ) : (
58
- fieldType.startsWith("NUMERIC(") ? fieldType : "\"" + pgSchema + "\"." + fieldType
59
- );
60
- return "\"" + fieldName + "\" " + tmp + (
61
- isArray ? "[]" : ""
62
- ) + (
56
+ return "\"" + fieldName + "\" " + Table.getPgFieldType(field.fieldType, pgSchema, field.isArray, isNumericArrayAsText, isNullable) + (
63
57
  defaultValue !== undefined ? " DEFAULT " + defaultValue : (
64
- field.isNullable ? "" : " NOT NULL"
58
+ isNullable ? "" : " NOT NULL"
65
59
  )
66
60
  );
67
61
  })).join(", ");
@@ -74,6 +68,68 @@ function makeCreateTableQuery(table, pgSchema, isNumericArrayAsText) {
74
68
  ) + ");";
75
69
  }
76
70
 
71
+ function getEntityHistory(entityConfig) {
72
+ var cache = entityConfig.pgEntityHistoryCache;
73
+ if (cache !== undefined) {
74
+ return cache;
75
+ }
76
+ var dataFields = Belt_Array.keepMap(entityConfig.table.fields, (function (field) {
77
+ if (field.TAG !== "Field") {
78
+ return ;
79
+ }
80
+ var field$1 = field._0;
81
+ var match = field$1.fieldName;
82
+ if (match === "id") {
83
+ return {
84
+ TAG: "Field",
85
+ _0: {
86
+ fieldName: "id",
87
+ fieldType: field$1.fieldType,
88
+ fieldSchema: field$1.fieldSchema,
89
+ isArray: field$1.isArray,
90
+ isNullable: field$1.isNullable,
91
+ isPrimaryKey: true,
92
+ isIndex: field$1.isIndex,
93
+ linkedEntity: field$1.linkedEntity,
94
+ defaultValue: field$1.defaultValue
95
+ }
96
+ };
97
+ } else {
98
+ return {
99
+ TAG: "Field",
100
+ _0: {
101
+ fieldName: field$1.fieldName,
102
+ fieldType: field$1.fieldType,
103
+ fieldSchema: field$1.fieldSchema,
104
+ isArray: field$1.isArray,
105
+ isNullable: true,
106
+ isPrimaryKey: field$1.isPrimaryKey,
107
+ isIndex: false,
108
+ linkedEntity: field$1.linkedEntity,
109
+ defaultValue: field$1.defaultValue
110
+ }
111
+ };
112
+ }
113
+ }));
114
+ var actionField = Table.mkField(EntityHistory.changeFieldName, EntityHistory.changeFieldType, S$RescriptSchema.never, undefined, undefined, undefined, undefined, undefined, undefined);
115
+ var checkpointIdField = Table.mkField(EntityHistory.checkpointIdFieldName, EntityHistory.checkpointIdFieldType, EntityHistory.unsafeCheckpointIdSchema, undefined, undefined, undefined, true, undefined, undefined);
116
+ var entityTableName = entityConfig.table.tableName;
117
+ var historyTableName = EntityHistory.historyTableName(entityTableName, entityConfig.index);
118
+ var table = Table.mkTable(historyTableName, undefined, Belt_Array.concat(dataFields, [
119
+ checkpointIdField,
120
+ actionField
121
+ ]));
122
+ var setChangeSchema = EntityHistory.makeSetUpdateSchema(entityConfig.schema);
123
+ var cache_setChangeSchemaRows = S$RescriptSchema.array(setChangeSchema);
124
+ var cache$1 = {
125
+ table: table,
126
+ setChangeSchema: setChangeSchema,
127
+ setChangeSchemaRows: cache_setChangeSchemaRows
128
+ };
129
+ entityConfig.pgEntityHistoryCache = cache$1;
130
+ return cache$1;
131
+ }
132
+
77
133
  function makeInitializeTransaction(pgSchema, pgUser, isHasuraEnabled, chainConfigsOpt, entitiesOpt, enumsOpt, isEmptyPgSchemaOpt) {
78
134
  var chainConfigs = chainConfigsOpt !== undefined ? chainConfigsOpt : [];
79
135
  var entities = entitiesOpt !== undefined ? entitiesOpt : [];
@@ -87,10 +143,10 @@ function makeInitializeTransaction(pgSchema, pgUser, isHasuraEnabled, chainConfi
87
143
  ];
88
144
  var allTables = $$Array.copy(generalTables);
89
145
  var allEntityTables = [];
90
- entities.forEach(function (entity) {
91
- allEntityTables.push(entity.table);
92
- allTables.push(entity.table);
93
- allTables.push(entity.entityHistory.table);
146
+ entities.forEach(function (entityConfig) {
147
+ allEntityTables.push(entityConfig.table);
148
+ allTables.push(entityConfig.table);
149
+ allTables.push(getEntityHistory(entityConfig).table);
94
150
  });
95
151
  var derivedSchema = Schema.make(allEntityTables);
96
152
  var query = {
@@ -145,6 +201,14 @@ function makeLoadByIdsQuery(pgSchema, tableName) {
145
201
  return "SELECT * FROM \"" + pgSchema + "\".\"" + tableName + "\" WHERE id = ANY($1::text[]);";
146
202
  }
147
203
 
204
+ function makeDeleteByIdQuery(pgSchema, tableName) {
205
+ return "DELETE FROM \"" + pgSchema + "\".\"" + tableName + "\" WHERE id = $1;";
206
+ }
207
+
208
+ function makeDeleteByIdsQuery(pgSchema, tableName) {
209
+ return "DELETE FROM \"" + pgSchema + "\".\"" + tableName + "\" WHERE id = ANY($1::text[]);";
210
+ }
211
+
148
212
  function makeLoadAllQuery(pgSchema, tableName) {
149
213
  return "SELECT * FROM \"" + pgSchema + "\".\"" + tableName + "\";";
150
214
  }
@@ -360,7 +424,267 @@ async function getConnectedPsqlExec(pgUser, pgHost, pgDatabase, pgPort) {
360
424
  return result;
361
425
  }
362
426
 
363
- function make(sql, pgHost, pgSchema, pgPort, pgUser, pgDatabase, pgPassword, isHasuraEnabled, onInitialize, onNewTables) {
427
+ async function deleteByIdsOrThrow(sql, pgSchema, ids, table) {
428
+ try {
429
+ await (
430
+ ids.length !== 1 ? sql.unsafe(makeDeleteByIdsQuery(pgSchema, table.tableName), [ids], {prepare: true}) : sql.unsafe(makeDeleteByIdQuery(pgSchema, table.tableName), ids, {prepare: true})
431
+ );
432
+ return ;
433
+ }
434
+ catch (raw_exn){
435
+ var exn = Caml_js_exceptions.internalToOCamlException(raw_exn);
436
+ throw {
437
+ RE_EXN_ID: Persistence.StorageError,
438
+ message: "Failed deleting \"" + table.tableName + "\" from storage by ids",
439
+ reason: exn,
440
+ Error: new Error()
441
+ };
442
+ }
443
+ }
444
+
445
+ function makeInsertDeleteUpdatesQuery(entityConfig, pgSchema) {
446
+ var historyTableName = EntityHistory.historyTableName(entityConfig.name, entityConfig.index);
447
+ var allHistoryFieldNames = Belt_Array.keepMap(entityConfig.table.fields, (function (fieldOrDerived) {
448
+ if (fieldOrDerived.TAG === "Field") {
449
+ return Table.getDbFieldName(fieldOrDerived._0);
450
+ }
451
+
452
+ }));
453
+ allHistoryFieldNames.push(EntityHistory.checkpointIdFieldName);
454
+ allHistoryFieldNames.push(EntityHistory.changeFieldName);
455
+ var allHistoryFieldNamesStr = Belt_Array.map(allHistoryFieldNames, (function (name) {
456
+ return "\"" + name + "\"";
457
+ })).join(", ");
458
+ var selectParts = Belt_Array.map(allHistoryFieldNames, (function (fieldName) {
459
+ if (fieldName === Table.idFieldName) {
460
+ return "u." + Table.idFieldName;
461
+ } else if (fieldName === EntityHistory.checkpointIdFieldName) {
462
+ return "u." + EntityHistory.checkpointIdFieldName;
463
+ } else if (fieldName === EntityHistory.changeFieldName) {
464
+ return "'" + "DELETE" + "'";
465
+ } else {
466
+ return "NULL";
467
+ }
468
+ }));
469
+ var selectPartsStr = selectParts.join(", ");
470
+ var checkpointIdPgType = Table.getPgFieldType(EntityHistory.checkpointIdFieldType, pgSchema, false, false, false);
471
+ return "INSERT INTO \"" + pgSchema + "\".\"" + historyTableName + "\" (" + allHistoryFieldNamesStr + ")\nSELECT " + selectPartsStr + "\nFROM UNNEST($1::text[], $2::" + checkpointIdPgType + "[]) AS u(" + Table.idFieldName + ", " + EntityHistory.checkpointIdFieldName + ")";
472
+ }
473
+
474
+ function executeSet(sql, items, dbFunction) {
475
+ if (items.length !== 0) {
476
+ return dbFunction(sql, items);
477
+ } else {
478
+ return Promise.resolve();
479
+ }
480
+ }
481
+
482
+ async function writeBatch(sql, batch, rawEvents, pgSchema, rollbackTargetCheckpointId, isInReorgThreshold, config, allEntities, setEffectCacheOrThrow, updatedEffectsCache, updatedEntities, sinkPromise, escapeTables) {
483
+ try {
484
+ var shouldSaveHistory = Config.shouldSaveHistory(config, isInReorgThreshold);
485
+ var specificError = {
486
+ contents: undefined
487
+ };
488
+ var setRawEvents = function (__x) {
489
+ return executeSet(__x, rawEvents, (function (sql, items) {
490
+ return setOrThrow(sql, items, InternalTable.RawEvents.table, InternalTable.RawEvents.schema, pgSchema);
491
+ }));
492
+ };
493
+ var setEntities = Belt_Array.map(updatedEntities, (function (param) {
494
+ var updates = param.updates;
495
+ var entityConfig = param.entityConfig;
496
+ var entitiesToSet = [];
497
+ var idsToDelete = [];
498
+ updates.forEach(function (row) {
499
+ var match = row.latestChange;
500
+ if (match.type === "SET") {
501
+ entitiesToSet.push(match.entity);
502
+ return ;
503
+ }
504
+ idsToDelete.push(match.entityId);
505
+ });
506
+ var shouldRemoveInvalidUtf8 = escapeTables !== undefined && Caml_option.valFromOption(escapeTables).has(entityConfig.table) ? true : false;
507
+ return async function (sql) {
508
+ try {
509
+ var promises = [];
510
+ if (shouldSaveHistory) {
511
+ var backfillHistoryIds = new Set();
512
+ var batchSetUpdates = [];
513
+ var batchDeleteCheckpointIds = [];
514
+ var batchDeleteEntityIds = [];
515
+ updates.forEach(function (update) {
516
+ var containsRollbackDiffChange = update.containsRollbackDiffChange;
517
+ update.history.forEach(function (change) {
518
+ if (!containsRollbackDiffChange) {
519
+ backfillHistoryIds.add(change.entityId);
520
+ }
521
+ if (change.type === "SET") {
522
+ batchSetUpdates.push(change);
523
+ return ;
524
+ }
525
+ batchDeleteEntityIds.push(change.entityId);
526
+ batchDeleteCheckpointIds.push(change.checkpointId);
527
+ });
528
+ });
529
+ if (backfillHistoryIds.size !== 0) {
530
+ await EntityHistory.backfillHistory(sql, pgSchema, entityConfig.name, entityConfig.index, Array.from(backfillHistoryIds));
531
+ }
532
+ if (Utils.$$Array.notEmpty(batchDeleteCheckpointIds)) {
533
+ promises.push(sql.unsafe(makeInsertDeleteUpdatesQuery(entityConfig, pgSchema), [
534
+ batchDeleteEntityIds,
535
+ batchDeleteCheckpointIds
536
+ ], {prepare: true}));
537
+ }
538
+ if (Utils.$$Array.notEmpty(batchSetUpdates)) {
539
+ if (shouldRemoveInvalidUtf8) {
540
+ var entities = batchSetUpdates.map(function (batchSetUpdate) {
541
+ if (batchSetUpdate.type === "SET") {
542
+ return batchSetUpdate.entity;
543
+ } else {
544
+ return Js_exn.raiseError("Expected Set action");
545
+ }
546
+ });
547
+ removeInvalidUtf8InPlace(entities);
548
+ }
549
+ var entityHistory = getEntityHistory(entityConfig);
550
+ promises.push(setOrThrow(sql, batchSetUpdates, entityHistory.table, entityHistory.setChangeSchema, pgSchema));
551
+ }
552
+
553
+ }
554
+ if (Utils.$$Array.notEmpty(entitiesToSet)) {
555
+ if (shouldRemoveInvalidUtf8) {
556
+ removeInvalidUtf8InPlace(entitiesToSet);
557
+ }
558
+ promises.push(setOrThrow(sql, entitiesToSet, entityConfig.table, entityConfig.schema, pgSchema));
559
+ }
560
+ if (Utils.$$Array.notEmpty(idsToDelete)) {
561
+ promises.push(deleteByIdsOrThrow(sql, pgSchema, idsToDelete, entityConfig.table));
562
+ }
563
+ await Promise.all(promises);
564
+ return ;
565
+ }
566
+ catch (raw_exn){
567
+ var exn = Caml_js_exceptions.internalToOCamlException(raw_exn);
568
+ var normalizedExn = Caml_js_exceptions.internalToOCamlException(exn.RE_EXN_ID === "JsError" || exn.RE_EXN_ID !== Persistence.StorageError ? exn : exn.reason);
569
+ if (normalizedExn.RE_EXN_ID === "JsError") {
570
+ var val;
571
+ try {
572
+ val = S$RescriptSchema.parseOrThrow(normalizedExn._1, pgErrorMessageSchema);
573
+ }
574
+ catch (exn$1){
575
+ return ;
576
+ }
577
+ switch (val) {
578
+ case "current transaction is aborted, commands ignored until end of transaction block" :
579
+ return ;
580
+ case "invalid byte sequence for encoding \"UTF8\": 0x00" :
581
+ specificError.contents = {
582
+ RE_EXN_ID: PgEncodingError,
583
+ table: entityConfig.table
584
+ };
585
+ return ;
586
+ default:
587
+ specificError.contents = Utils.prettifyExn(exn);
588
+ return ;
589
+ }
590
+ } else {
591
+ if (normalizedExn.RE_EXN_ID !== S$RescriptSchema.Raised) {
592
+ return ;
593
+ }
594
+ throw normalizedExn;
595
+ }
596
+ }
597
+ };
598
+ }));
599
+ var rollbackTables = rollbackTargetCheckpointId !== undefined ? (function (sql) {
600
+ var promises = allEntities.map(function (entityConfig) {
601
+ return EntityHistory.rollback(sql, pgSchema, entityConfig.name, entityConfig.index, rollbackTargetCheckpointId);
602
+ });
603
+ promises.push(InternalTable.Checkpoints.rollback(sql, pgSchema, rollbackTargetCheckpointId));
604
+ return Promise.all(promises);
605
+ }) : undefined;
606
+ try {
607
+ await Promise.all([
608
+ sql.begin(async function (sql) {
609
+ if (rollbackTables !== undefined) {
610
+ await rollbackTables(sql);
611
+ }
612
+ var setOperations = Belt_Array.concat([
613
+ (function (sql) {
614
+ return InternalTable.Chains.setProgressedChains(sql, pgSchema, Utils.Dict.mapValuesToArray(batch.progressedChainsById, (function (chainAfterBatch) {
615
+ return {
616
+ chainId: chainAfterBatch.fetchState.chainId,
617
+ progressBlockNumber: chainAfterBatch.progressBlockNumber,
618
+ totalEventsProcessed: chainAfterBatch.totalEventsProcessed
619
+ };
620
+ })));
621
+ }),
622
+ setRawEvents
623
+ ], setEntities);
624
+ if (shouldSaveHistory) {
625
+ setOperations.push(function (sql) {
626
+ return InternalTable.Checkpoints.insert(sql, pgSchema, batch.checkpointIds, batch.checkpointChainIds, batch.checkpointBlockNumbers, batch.checkpointBlockHashes, batch.checkpointEventsProcessed);
627
+ });
628
+ }
629
+ await Promise.all(Belt_Array.map(setOperations, (function (dbFunc) {
630
+ return dbFunc(sql);
631
+ })));
632
+ if (sinkPromise === undefined) {
633
+ return ;
634
+ }
635
+ var exn = await Caml_option.valFromOption(sinkPromise);
636
+ if (exn === undefined) {
637
+ return ;
638
+ }
639
+ throw exn;
640
+ }),
641
+ Promise.all(Belt_Array.map(updatedEffectsCache, (function (param) {
642
+ return setEffectCacheOrThrow(param.effect, param.items, param.shouldInitialize);
643
+ })))
644
+ ]);
645
+ var specificError$1 = specificError.contents;
646
+ if (specificError$1 === undefined) {
647
+ return ;
648
+ }
649
+ throw specificError$1;
650
+ }
651
+ catch (raw_exn){
652
+ var exn = Caml_js_exceptions.internalToOCamlException(raw_exn);
653
+ var specificError$2 = specificError.contents;
654
+ throw specificError$2 !== undefined ? specificError$2 : exn;
655
+ }
656
+ }
657
+ catch (raw_exn$1){
658
+ var exn$1 = Caml_js_exceptions.internalToOCamlException(raw_exn$1);
659
+ if (exn$1.RE_EXN_ID === PgEncodingError) {
660
+ var escapeTables$1 = escapeTables !== undefined ? Caml_option.valFromOption(escapeTables) : new Set();
661
+ escapeTables$1.add(exn$1.table);
662
+ return await writeBatch(sql, batch, rawEvents, pgSchema, rollbackTargetCheckpointId, isInReorgThreshold, config, allEntities, setEffectCacheOrThrow, updatedEffectsCache, updatedEntities, sinkPromise, Caml_option.some(escapeTables$1));
663
+ }
664
+ throw exn$1;
665
+ }
666
+ }
667
+
668
+ function makeGetRollbackRestoredEntitiesQuery(entityConfig, pgSchema) {
669
+ var dataFieldNames = Belt_Array.keepMap(entityConfig.table.fields, (function (fieldOrDerived) {
670
+ if (fieldOrDerived.TAG === "Field") {
671
+ return Table.getDbFieldName(fieldOrDerived._0);
672
+ }
673
+
674
+ }));
675
+ var dataFieldsCommaSeparated = Belt_Array.map(dataFieldNames, (function (name) {
676
+ return "\"" + name + "\"";
677
+ })).join(", ");
678
+ var historyTableName = EntityHistory.historyTableName(entityConfig.name, entityConfig.index);
679
+ return "SELECT DISTINCT ON (" + Table.idFieldName + ") " + dataFieldsCommaSeparated + "\nFROM \"" + pgSchema + "\".\"" + historyTableName + "\"\nWHERE \"" + EntityHistory.checkpointIdFieldName + "\" <= $1\n AND EXISTS (\n SELECT 1\n FROM \"" + pgSchema + "\".\"" + historyTableName + "\" h\n WHERE h." + Table.idFieldName + " = \"" + historyTableName + "\"." + Table.idFieldName + "\n AND h.\"" + EntityHistory.checkpointIdFieldName + "\" > $1\n )\nORDER BY " + Table.idFieldName + ", \"" + EntityHistory.checkpointIdFieldName + "\" DESC";
680
+ }
681
+
682
+ function makeGetRollbackRemovedIdsQuery(entityConfig, pgSchema) {
683
+ var historyTableName = EntityHistory.historyTableName(entityConfig.name, entityConfig.index);
684
+ return "SELECT DISTINCT " + Table.idFieldName + "\nFROM \"" + pgSchema + "\".\"" + historyTableName + "\"\nWHERE \"" + EntityHistory.checkpointIdFieldName + "\" > $1\nAND NOT EXISTS (\n SELECT 1\n FROM \"" + pgSchema + "\".\"" + historyTableName + "\" h\n WHERE h." + Table.idFieldName + " = \"" + historyTableName + "\"." + Table.idFieldName + "\n AND h.\"" + EntityHistory.checkpointIdFieldName + "\" <= $1\n)";
685
+ }
686
+
687
+ function make(sql, pgHost, pgSchema, pgPort, pgUser, pgDatabase, pgPassword, isHasuraEnabled, sink, onInitialize, onNewTables) {
364
688
  var psqlExecOptions_env = Js_dict.fromArray([
365
689
  [
366
690
  "PGPASSWORD",
@@ -467,6 +791,9 @@ function make(sql, pgHost, pgSchema, pgPort, pgUser, pgDatabase, pgPassword, isH
467
791
  })) {
468
792
  Js_exn.raiseError("Cannot run Envio migrations on PostgreSQL schema \"" + pgSchema + "\" because it contains non-Envio tables. Running migrations would delete all data in this schema.\n\nTo resolve this:\n1. If you want to use this schema, first backup any important data, then drop it with: \"pnpm envio local db-migrate down\"\n2. Or specify a different schema name by setting the \"ENVIO_PG_PUBLIC_SCHEMA\" environment variable\n3. Or manually drop the schema in your database if you're certain the data is not needed.");
469
793
  }
794
+ if (sink !== undefined) {
795
+ await sink.initialize(chainConfigs, entities, enums);
796
+ }
470
797
  var queries = makeInitializeTransaction(pgSchema, pgUser, isHasuraEnabled, chainConfigs, entities, enums, Utils.$$Array.isEmpty(schemaTableNames));
471
798
  await sql.begin(function (sql) {
472
799
  return Promise.all(queries.map(function (query) {
@@ -649,14 +976,81 @@ function make(sql, pgHost, pgSchema, pgPort, pgUser, pgDatabase, pgPassword, isH
649
976
  sql.unsafe(InternalTable.Checkpoints.makeCommitedCheckpointIdQuery(pgSchema)),
650
977
  sql.unsafe(InternalTable.Checkpoints.makeGetReorgCheckpointsQuery(pgSchema))
651
978
  ]);
979
+ var checkpointId = match[2][0].id;
980
+ if (sink !== undefined) {
981
+ await sink.resume(checkpointId);
982
+ }
652
983
  return {
653
984
  cleanRun: false,
654
985
  cache: match[0],
655
986
  chains: match[1],
656
- checkpointId: match[2][0].id,
987
+ checkpointId: checkpointId,
657
988
  reorgCheckpoints: match[3]
658
989
  };
659
990
  };
991
+ var executeUnsafe = function (query) {
992
+ return sql.unsafe(query);
993
+ };
994
+ var hasEntityHistoryRows = async function () {
995
+ var historyTables = await sql.unsafe("SELECT table_name FROM information_schema.tables \n WHERE table_schema = '" + pgSchema + "' \n AND table_name LIKE 'envio_history_%';");
996
+ if (Utils.$$Array.isEmpty(historyTables)) {
997
+ return false;
998
+ }
999
+ var checks = await Promise.all(Belt_Array.map(historyTables, (async function (table) {
1000
+ try {
1001
+ var query = "SELECT EXISTS(SELECT 1 FROM \"" + pgSchema + "\".\"" + table.table_name + "\" LIMIT 1);";
1002
+ var result = await sql.unsafe(query);
1003
+ if (result.length !== 1) {
1004
+ return false;
1005
+ } else {
1006
+ return result[0].exists;
1007
+ }
1008
+ }
1009
+ catch (exn){
1010
+ return false;
1011
+ }
1012
+ })));
1013
+ return Belt_Array.some(checks, (function (v) {
1014
+ return v;
1015
+ }));
1016
+ };
1017
+ var setChainMeta = function (chainsData) {
1018
+ return InternalTable.Chains.setMeta(sql, pgSchema, chainsData).then(function (param) {
1019
+ return undefined;
1020
+ });
1021
+ };
1022
+ var pruneStaleCheckpoints = function (safeCheckpointId) {
1023
+ return InternalTable.Checkpoints.pruneStaleCheckpoints(sql, pgSchema, safeCheckpointId);
1024
+ };
1025
+ var pruneStaleEntityHistory = function (entityName, entityIndex, safeCheckpointId) {
1026
+ return EntityHistory.pruneStaleEntityHistory(sql, entityName, entityIndex, pgSchema, safeCheckpointId);
1027
+ };
1028
+ var getRollbackTargetCheckpoint = function (reorgChainId, lastKnownValidBlockNumber) {
1029
+ return InternalTable.Checkpoints.getRollbackTargetCheckpoint(sql, pgSchema, reorgChainId, lastKnownValidBlockNumber);
1030
+ };
1031
+ var getRollbackProgressDiff = function (rollbackTargetCheckpointId) {
1032
+ return InternalTable.Checkpoints.getRollbackProgressDiff(sql, pgSchema, rollbackTargetCheckpointId);
1033
+ };
1034
+ var getRollbackData = async function (entityConfig, rollbackTargetCheckpointId) {
1035
+ return await Promise.all([
1036
+ sql.unsafe(makeGetRollbackRemovedIdsQuery(entityConfig, pgSchema), [rollbackTargetCheckpointId], {prepare: true}),
1037
+ sql.unsafe(makeGetRollbackRestoredEntitiesQuery(entityConfig, pgSchema), [rollbackTargetCheckpointId], {prepare: true})
1038
+ ]);
1039
+ };
1040
+ var writeBatchMethod = async function (batch, rawEvents, rollbackTargetCheckpointId, isInReorgThreshold, config, allEntities, updatedEffectsCache, updatedEntities) {
1041
+ var sinkPromise;
1042
+ if (sink !== undefined) {
1043
+ var timerRef = Hrtime.makeTimer();
1044
+ sinkPromise = Caml_option.some(sink.writeBatch(batch, updatedEntities).then(function () {
1045
+ Prometheus.SinkWrite.increment(sink.name, Hrtime.intFromMillis(Hrtime.toMillis(Hrtime.timeSince(timerRef))));
1046
+ }).catch(function (exn) {
1047
+ return exn;
1048
+ }));
1049
+ } else {
1050
+ sinkPromise = undefined;
1051
+ }
1052
+ return await writeBatch(sql, batch, rawEvents, pgSchema, rollbackTargetCheckpointId, isInReorgThreshold, config, allEntities, setEffectCacheOrThrow, updatedEffectsCache, updatedEntities, sinkPromise, undefined);
1053
+ };
660
1054
  return {
661
1055
  isInitialized: isInitialized,
662
1056
  initialize: initialize,
@@ -665,7 +1059,16 @@ function make(sql, pgHost, pgSchema, pgPort, pgUser, pgDatabase, pgPassword, isH
665
1059
  loadByFieldOrThrow: loadByFieldOrThrow,
666
1060
  setOrThrow: setOrThrow$1,
667
1061
  setEffectCacheOrThrow: setEffectCacheOrThrow,
668
- dumpEffectCache: dumpEffectCache
1062
+ dumpEffectCache: dumpEffectCache,
1063
+ executeUnsafe: executeUnsafe,
1064
+ hasEntityHistoryRows: hasEntityHistoryRows,
1065
+ setChainMeta: setChainMeta,
1066
+ pruneStaleCheckpoints: pruneStaleCheckpoints,
1067
+ pruneStaleEntityHistory: pruneStaleEntityHistory,
1068
+ getRollbackTargetCheckpoint: getRollbackTargetCheckpoint,
1069
+ getRollbackProgressDiff: getRollbackProgressDiff,
1070
+ getRollbackData: getRollbackData,
1071
+ writeBatch: writeBatchMethod
669
1072
  };
670
1073
  }
671
1074
 
@@ -675,10 +1078,13 @@ exports.getCacheRowCountFnName = getCacheRowCountFnName;
675
1078
  exports.makeCreateIndexQuery = makeCreateIndexQuery;
676
1079
  exports.makeCreateTableIndicesQuery = makeCreateTableIndicesQuery;
677
1080
  exports.makeCreateTableQuery = makeCreateTableQuery;
1081
+ exports.getEntityHistory = getEntityHistory;
678
1082
  exports.makeInitializeTransaction = makeInitializeTransaction;
679
1083
  exports.makeLoadByIdQuery = makeLoadByIdQuery;
680
1084
  exports.makeLoadByFieldQuery = makeLoadByFieldQuery;
681
1085
  exports.makeLoadByIdsQuery = makeLoadByIdsQuery;
1086
+ exports.makeDeleteByIdQuery = makeDeleteByIdQuery;
1087
+ exports.makeDeleteByIdsQuery = makeDeleteByIdsQuery;
682
1088
  exports.makeLoadAllQuery = makeLoadAllQuery;
683
1089
  exports.makeInsertUnnestSetQuery = makeInsertUnnestSetQuery;
684
1090
  exports.makeInsertValuesSetQuery = makeInsertValuesSetQuery;
@@ -694,5 +1100,11 @@ exports.makeSchemaTableNamesQuery = makeSchemaTableNamesQuery;
694
1100
  exports.cacheTablePrefixLength = cacheTablePrefixLength;
695
1101
  exports.makeSchemaCacheTableInfoQuery = makeSchemaCacheTableInfoQuery;
696
1102
  exports.getConnectedPsqlExec = getConnectedPsqlExec;
1103
+ exports.deleteByIdsOrThrow = deleteByIdsOrThrow;
1104
+ exports.makeInsertDeleteUpdatesQuery = makeInsertDeleteUpdatesQuery;
1105
+ exports.executeSet = executeSet;
1106
+ exports.writeBatch = writeBatch;
1107
+ exports.makeGetRollbackRestoredEntitiesQuery = makeGetRollbackRestoredEntitiesQuery;
1108
+ exports.makeGetRollbackRemovedIdsQuery = makeGetRollbackRemovedIdsQuery;
697
1109
  exports.make = make;
698
1110
  /* pgErrorMessageSchema Not a pure module */
@@ -0,0 +1,141 @@
1
+ type name = | @as("evm") Evm | @as("fuel") Fuel
2
+
3
+ type t = {
4
+ name: name,
5
+ blockFields: array<string>,
6
+ transactionFields: array<string>,
7
+ blockNumberName: string,
8
+ blockTimestampName: string,
9
+ blockHashName: string,
10
+ getNumber: Internal.eventBlock => int,
11
+ getTimestamp: Internal.eventBlock => int,
12
+ getId: Internal.eventBlock => string,
13
+ cleanUpRawEventFieldsInPlace: Js.Json.t => unit,
14
+ }
15
+
16
+ module Evm = {
17
+ @get external getNumber: Internal.eventBlock => int = "number"
18
+ @get external getTimestamp: Internal.eventBlock => int = "timestamp"
19
+ @get external getId: Internal.eventBlock => string = "hash"
20
+
21
+ let cleanUpRawEventFieldsInPlace: Js.Json.t => unit = %raw(`fields => {
22
+ delete fields.hash
23
+ delete fields.number
24
+ delete fields.timestamp
25
+ }`)
26
+ }
27
+
28
+ let evm: t = {
29
+ name: Evm,
30
+ blockFields: [
31
+ "number",
32
+ "timestamp",
33
+ "hash",
34
+ "parentHash",
35
+ "nonce",
36
+ "sha3Uncles",
37
+ "logsBloom",
38
+ "transactionsRoot",
39
+ "stateRoot",
40
+ "receiptsRoot",
41
+ "miner",
42
+ "difficulty",
43
+ "totalDifficulty",
44
+ "extraData",
45
+ "size",
46
+ "gasLimit",
47
+ "gasUsed",
48
+ "uncles",
49
+ "baseFeePerGas",
50
+ "blobGasUsed",
51
+ "excessBlobGas",
52
+ "parentBeaconBlockRoot",
53
+ "withdrawalsRoot",
54
+ "l1BlockNumber",
55
+ "sendCount",
56
+ "sendRoot",
57
+ "mixHash",
58
+ ],
59
+ transactionFields: [
60
+ "transactionIndex",
61
+ "hash",
62
+ "from",
63
+ "to",
64
+ "gas",
65
+ "gasPrice",
66
+ "maxPriorityFeePerGas",
67
+ "maxFeePerGas",
68
+ "cumulativeGasUsed",
69
+ "effectiveGasPrice",
70
+ "gasUsed",
71
+ "input",
72
+ "nonce",
73
+ "value",
74
+ "v",
75
+ "r",
76
+ "s",
77
+ "contractAddress",
78
+ "logsBloom",
79
+ "root",
80
+ "status",
81
+ "yParity",
82
+ "chainId",
83
+ "maxFeePerBlobGas",
84
+ "blobVersionedHashes",
85
+ "kind",
86
+ "l1Fee",
87
+ "l1GasPrice",
88
+ "l1GasUsed",
89
+ "l1FeeScalar",
90
+ "gasUsedForL1",
91
+ "accessList",
92
+ "authorizationList",
93
+ ],
94
+ blockNumberName: "number",
95
+ blockTimestampName: "timestamp",
96
+ blockHashName: "hash",
97
+ getNumber: Evm.getNumber,
98
+ getTimestamp: Evm.getTimestamp,
99
+ getId: Evm.getId,
100
+ cleanUpRawEventFieldsInPlace: Evm.cleanUpRawEventFieldsInPlace,
101
+ }
102
+
103
+ module Fuel = {
104
+ @get external getNumber: Internal.eventBlock => int = "height"
105
+ @get external getTimestamp: Internal.eventBlock => int = "time"
106
+ @get external getId: Internal.eventBlock => string = "id"
107
+
108
+ let cleanUpRawEventFieldsInPlace: Js.Json.t => unit = %raw(`fields => {
109
+ delete fields.id
110
+ delete fields.height
111
+ delete fields.time
112
+ }`)
113
+ }
114
+
115
+ let fuel: t = {
116
+ name: Fuel,
117
+ blockFields: ["id", "height", "time"],
118
+ transactionFields: ["id"],
119
+ blockNumberName: "height",
120
+ blockTimestampName: "time",
121
+ blockHashName: "id",
122
+ getNumber: Fuel.getNumber,
123
+ getTimestamp: Fuel.getTimestamp,
124
+ getId: Fuel.getId,
125
+ cleanUpRawEventFieldsInPlace: Fuel.cleanUpRawEventFieldsInPlace,
126
+ }
127
+
128
+ let fromName = (name: name): t => {
129
+ switch name {
130
+ | Evm => evm
131
+ | Fuel => fuel
132
+ }
133
+ }
134
+
135
+ // Create a block event object for block handlers based on platform
136
+ let makeBlockEvent = (~blockNumber: int, ~chainId: int, platform: t): Internal.blockEvent => {
137
+ let blockEvent = Js.Dict.empty()
138
+ blockEvent->Js.Dict.set("chainId", chainId->Utils.magic)
139
+ blockEvent->Js.Dict.set(platform.blockNumberName, blockNumber->Utils.magic)
140
+ blockEvent->Utils.magic
141
+ }