UNPKG

envio

Version:

A latency and sync speed optimized, developer friendly blockchain data indexer.

327 lines (308 loc) • 14.5 kB
// Generated by ReScript, PLEASE EDIT WITH CARE 'use strict'; var $$Array = require("rescript/lib/js/array.js"); var Table = require("./db/Table.res.js"); var Utils = require("./Utils.res.js"); var Schema = require("./db/Schema.res.js"); var Belt_Array = require("rescript/lib/js/belt_Array.js"); var Caml_option = require("rescript/lib/js/caml_option.js"); var Persistence = require("./Persistence.res.js"); var S$RescriptSchema = require("rescript-schema/src/S.res.js"); var Caml_js_exceptions = require("rescript/lib/js/caml_js_exceptions.js"); function makeCreateIndexSql(tableName, indexFields, pgSchema) { var indexName = tableName + "_" + indexFields.join("_"); var index = Belt_Array.map(indexFields, (function (idx) { return "\"" + idx + "\""; })).join(", "); return "CREATE INDEX IF NOT EXISTS \"" + indexName + "\" ON \"" + pgSchema + "\".\"" + tableName + "\"(" + index + ");"; } function makeCreateTableIndicesSql(table, pgSchema) { var tableName = table.tableName; var createIndex = function (indexField) { return makeCreateIndexSql(tableName, [indexField], pgSchema); }; var createCompositeIndex = function (indexFields) { return makeCreateIndexSql(tableName, indexFields, pgSchema); }; var singleIndices = Table.getSingleIndices(table); var compositeIndices = Table.getCompositeIndices(table); return Belt_Array.map(singleIndices, createIndex).join("\n") + Belt_Array.map(compositeIndices, createCompositeIndex).join("\n"); } function makeCreateTableSql(table, pgSchema) { var fieldsMapped = Belt_Array.map(Table.getFields(table), (function (field) { var defaultValue = field.defaultValue; var fieldType = field.fieldType; var fieldName = Table.getDbFieldName(field); var tmp; tmp = fieldType === "TIMESTAMP" || fieldType === "TIMESTAMP WITH TIME ZONE" || fieldType === "JSONB" || fieldType === "SERIAL" || fieldType === "TEXT" || fieldType === "DOUBLE PRECISION" || fieldType === "NUMERIC" || fieldType === "BOOLEAN" || fieldType === "INTEGER" || fieldType === "TIMESTAMP WITH TIME ZONE NULL" || fieldType.startsWith("NUMERIC(") ? fieldType : "\"" + pgSchema + "\"." + fieldType; return "\"" + fieldName + "\" " + tmp + ( field.isArray ? "[]" : "" ) + ( defaultValue !== undefined ? " DEFAULT " + defaultValue : ( field.isNullable ? "" : " NOT NULL" ) ); })).join(", "); var primaryKeyFieldNames = Table.getPrimaryKeyFieldNames(table); var primaryKey = Belt_Array.map(primaryKeyFieldNames, (function (field) { return "\"" + field + "\""; })).join(", "); return "CREATE TABLE IF NOT EXISTS \"" + pgSchema + "\".\"" + table.tableName + "\"(" + fieldsMapped + ( primaryKeyFieldNames.length !== 0 ? ", PRIMARY KEY(" + primaryKey + ")" : "" ) + ");"; } function makeInitializeTransaction(pgSchema, pgUser, generalTablesOpt, entitiesOpt, enumsOpt, cleanRunOpt) { var generalTables = generalTablesOpt !== undefined ? generalTablesOpt : []; var entities = entitiesOpt !== undefined ? entitiesOpt : []; var enums = enumsOpt !== undefined ? enumsOpt : []; var cleanRun = cleanRunOpt !== undefined ? cleanRunOpt : false; var allTables = $$Array.copy(generalTables); var allEntityTables = []; entities.forEach(function (entity) { allEntityTables.push(entity.table); allTables.push(entity.table); allTables.push(entity.entityHistory.table); }); var derivedSchema = Schema.make(allEntityTables); var query = { contents: ( cleanRun ? "DROP SCHEMA IF EXISTS \"" + pgSchema + "\" CASCADE;\nCREATE SCHEMA \"" + pgSchema + "\";" : "CREATE SCHEMA IF NOT EXISTS \"" + pgSchema + "\";" ) + ("GRANT ALL ON SCHEMA \"" + pgSchema + "\" TO \"" + pgUser + "\";\nGRANT ALL ON SCHEMA \"" + pgSchema + "\" TO public;") }; enums.forEach(function (enumConfig) { var enumCreateQuery = "CREATE TYPE \"" + pgSchema + "\"." + enumConfig.name + " AS ENUM(" + enumConfig.variants.map(function (v) { return "'" + v + "'"; }).join(", ") + ");"; query.contents = query.contents + "\n" + ( cleanRun ? enumCreateQuery : "IF NOT EXISTS (\n SELECT 1 FROM pg_type \n WHERE typname = '" + enumConfig.name.toLowerCase() + "' \n AND typnamespace = (SELECT oid FROM pg_namespace WHERE nspname = '" + pgSchema + "')\n) THEN \n " + enumCreateQuery + "\nEND IF;" ); }); allTables.forEach(function (table) { query.contents = query.contents + "\n" + makeCreateTableSql(table, pgSchema); }); allTables.forEach(function (table) { var indices = makeCreateTableIndicesSql(table, pgSchema); if (indices !== "") { query.contents = query.contents + "\n" + indices; return ; } }); var functionsQuery = { contents: "" }; entities.forEach(function (entity) { functionsQuery.contents = functionsQuery.contents + "\n" + entity.entityHistory.createInsertFnQuery; Table.getDerivedFromFields(entity.table).forEach(function (derivedFromField) { var indexField = Utils.unwrapResultExn(Schema.getDerivedFromFieldName(derivedSchema, derivedFromField)); query.contents = query.contents + "\n" + makeCreateIndexSql(derivedFromField.derivedFromEntity, [indexField], pgSchema); }); }); return [cleanRun || Utils.$$Array.isEmpty(enums) ? query.contents : "DO $$ BEGIN " + query.contents + " END $$;"].concat(functionsQuery.contents !== "" ? [functionsQuery.contents] : []); } function makeLoadByIdSql(pgSchema, tableName) { return "SELECT * FROM \"" + pgSchema + "\".\"" + tableName + "\" WHERE id = $1 LIMIT 1;"; } function makeLoadByIdsSql(pgSchema, tableName) { return "SELECT * FROM \"" + pgSchema + "\".\"" + tableName + "\" WHERE id = ANY($1::text[]);"; } function makeInsertUnnestSetSql(pgSchema, table, itemSchema, isRawEvents) { var match = Table.toSqlParams(table, itemSchema); var quotedNonPrimaryFieldNames = match.quotedNonPrimaryFieldNames; var primaryKeyFieldNames = Table.getPrimaryKeyFieldNames(table); return "INSERT INTO \"" + pgSchema + "\".\"" + table.tableName + "\" (" + match.quotedFieldNames.join(", ") + ")\nSELECT * FROM unnest(" + match.arrayFieldTypes.map(function (arrayFieldType, idx) { return "$" + (idx + 1 | 0).toString() + "::" + arrayFieldType; }).join(",") + ")" + ( isRawEvents || primaryKeyFieldNames.length === 0 ? "" : "ON CONFLICT(" + primaryKeyFieldNames.map(function (fieldName) { return "\"" + fieldName + "\""; }).join(",") + ") DO " + ( Utils.$$Array.isEmpty(quotedNonPrimaryFieldNames) ? "NOTHING" : "UPDATE SET " + quotedNonPrimaryFieldNames.map(function (fieldName) { return fieldName + " = EXCLUDED." + fieldName; }).join(",") ) ) + ";"; } function makeInsertValuesSetSql(pgSchema, table, itemSchema, itemsCount) { var match = Table.toSqlParams(table, itemSchema); var quotedNonPrimaryFieldNames = match.quotedNonPrimaryFieldNames; var quotedFieldNames = match.quotedFieldNames; var primaryKeyFieldNames = Table.getPrimaryKeyFieldNames(table); var fieldsCount = quotedFieldNames.length; var placeholders = ""; for(var idx = 1; idx <= itemsCount; ++idx){ if (idx > 1) { placeholders = placeholders + ","; } placeholders = placeholders + "("; for(var fieldIdx = 0; fieldIdx < fieldsCount; ++fieldIdx){ if (fieldIdx > 0) { placeholders = placeholders + ","; } placeholders = placeholders + ("$" + (Math.imul(fieldIdx, itemsCount) + idx | 0).toString()); } placeholders = placeholders + ")"; } return "INSERT INTO \"" + pgSchema + "\".\"" + table.tableName + "\" (" + quotedFieldNames.join(", ") + ")\nVALUES" + placeholders + ( primaryKeyFieldNames.length !== 0 ? "ON CONFLICT(" + primaryKeyFieldNames.map(function (fieldName) { return "\"" + fieldName + "\""; }).join(",") + ") DO " + ( Utils.$$Array.isEmpty(quotedNonPrimaryFieldNames) ? "NOTHING" : "UPDATE SET " + quotedNonPrimaryFieldNames.map(function (fieldName) { return fieldName + " = EXCLUDED." + fieldName; }).join(",") ) : "" ) + ";"; } var rawEventsTableName = "raw_events"; function makeTableBatchSetQuery(pgSchema, table, itemSchema) { var match = Table.toSqlParams(table, itemSchema); var isRawEvents = table.tableName === rawEventsTableName; if (isRawEvents || !match.hasArrayField) { return { sql: makeInsertUnnestSetSql(pgSchema, table, itemSchema, isRawEvents), convertOrThrow: S$RescriptSchema.compile(S$RescriptSchema.unnest(match.dbSchema), "Output", "Input", "Sync", false), isInsertValues: false }; } else { return { sql: makeInsertValuesSetSql(pgSchema, table, itemSchema, 500), convertOrThrow: S$RescriptSchema.compile(S$RescriptSchema.preprocess(S$RescriptSchema.unnest(itemSchema), (function (param) { return { s: (function (prim) { return prim.flat(1); }) }; })), "Output", "Input", "Sync", false), isInsertValues: true }; } } function chunkArray(arr, chunkSize) { var chunks = []; var i = 0; while(i < arr.length) { var chunk = arr.slice(i, i + chunkSize | 0); chunks.push(chunk); i = i + chunkSize | 0; }; return chunks; } var setQueryCache = new WeakMap(); async function setOrThrow(sql, items, table, itemSchema, pgSchema) { if (items.length === 0) { return ; } var cached = setQueryCache.get(table); var query; if (cached !== undefined) { query = Caml_option.valFromOption(cached); } else { var newQuery = makeTableBatchSetQuery(pgSchema, table, itemSchema); setQueryCache.set(table, newQuery); query = newQuery; } var sqlQuery = query.sql; try { if (!query.isInsertValues) { return await sql.unsafe(sqlQuery, query.convertOrThrow(items), {prepare: true}); } var chunks = chunkArray(items, 500); var responses = []; chunks.forEach(function (chunk) { var chunkSize = chunk.length; var isFullChunk = chunkSize === 500; var response = sql.unsafe(isFullChunk ? sqlQuery : makeInsertValuesSetSql(pgSchema, table, itemSchema, chunkSize), query.convertOrThrow(chunk), {prepare: true}); responses.push(response); }); await Promise.all(responses); return ; } catch (raw_exn){ var exn = Caml_js_exceptions.internalToOCamlException(raw_exn); if (exn.RE_EXN_ID === S$RescriptSchema.Raised) { throw { RE_EXN_ID: Persistence.StorageError, message: "Failed to convert items for table \"" + table.tableName + "\"", reason: exn, Error: new Error() }; } throw { RE_EXN_ID: Persistence.StorageError, message: "Failed to insert items into table \"" + table.tableName + "\"", reason: exn, Error: new Error() }; } } function make(sql, pgSchema, pgUser) { var isInitialized = async function () { var schemas = await sql.unsafe("SELECT schema_name FROM information_schema.schemata WHERE schema_name = '" + pgSchema + "';"); return Utils.$$Array.notEmpty(schemas); }; var initialize = async function (entitiesOpt, generalTablesOpt, enumsOpt, cleanRunOpt) { var entities = entitiesOpt !== undefined ? entitiesOpt : []; var generalTables = generalTablesOpt !== undefined ? generalTablesOpt : []; var enums = enumsOpt !== undefined ? enumsOpt : []; var cleanRun = cleanRunOpt !== undefined ? cleanRunOpt : false; var queries = makeInitializeTransaction(pgSchema, pgUser, generalTables, entities, enums, cleanRun); await sql.begin(function (sql) { return queries.map(function (query) { return sql.unsafe(query); }); }); }; var loadByIdsOrThrow = async function (ids, table, rowsSchema) { var rows; try { rows = await ( ids.length !== 1 ? sql.unsafe(makeLoadByIdsSql(pgSchema, table.tableName), [ids], {prepare: true}) : sql.unsafe(makeLoadByIdSql(pgSchema, table.tableName), ids, {prepare: true}) ); } catch (raw_exn){ var exn = Caml_js_exceptions.internalToOCamlException(raw_exn); throw { RE_EXN_ID: Persistence.StorageError, message: "Failed loading \"" + table.tableName + "\" from storage by ids", reason: exn, Error: new Error() }; } try { return S$RescriptSchema.parseOrThrow(rows, rowsSchema); } catch (raw_exn$1){ var exn$1 = Caml_js_exceptions.internalToOCamlException(raw_exn$1); throw { RE_EXN_ID: Persistence.StorageError, message: "Failed to parse \"" + table.tableName + "\" loaded from storage by ids", reason: exn$1, Error: new Error() }; } }; var setOrThrow$1 = function (items, table, itemSchema) { return setOrThrow(sql, items, table, itemSchema, pgSchema); }; return { isInitialized: isInitialized, initialize: initialize, loadByIdsOrThrow: loadByIdsOrThrow, setOrThrow: setOrThrow$1 }; } var maxItemsPerQuery = 500; exports.makeCreateIndexSql = makeCreateIndexSql; exports.makeCreateTableIndicesSql = makeCreateTableIndicesSql; exports.makeCreateTableSql = makeCreateTableSql; exports.makeInitializeTransaction = makeInitializeTransaction; exports.makeLoadByIdSql = makeLoadByIdSql; exports.makeLoadByIdsSql = makeLoadByIdsSql; exports.makeInsertUnnestSetSql = makeInsertUnnestSetSql; exports.makeInsertValuesSetSql = makeInsertValuesSetSql; exports.rawEventsTableName = rawEventsTableName; exports.maxItemsPerQuery = maxItemsPerQuery; exports.makeTableBatchSetQuery = makeTableBatchSetQuery; exports.chunkArray = chunkArray; exports.setQueryCache = setQueryCache; exports.setOrThrow = setOrThrow; exports.make = make; /* setQueryCache Not a pure module */