"use strict";
Object.defineProperty(exports, "__esModule", {
    value: true
});
function _export(target, all) {
    for(var name in all)Object.defineProperty(target, name, {
        enumerable: true,
        get: Object.getOwnPropertyDescriptor(all, name).get
    });
}
_export(exports, {
    get createSnapshotTable () {
        return createSnapshotTable;
    },
    get dropAllSnapshotTables () {
        return dropAllSnapshotTables;
    },
    get dropMarkedForSyncPatientsTable () {
        return dropMarkedForSyncPatientsTable;
    },
    get dropSnapshotTable () {
        return dropSnapshotTable;
    },
    get getMarkedForSyncPatientsTableName () {
        return getMarkedForSyncPatientsTableName;
    },
    get getSnapshotTableName () {
        return getSnapshotTableName;
    },
    get insertSnapshotRecords () {
        return insertSnapshotRecords;
    },
    get updateSnapshotRecords () {
        return updateSnapshotRecords;
    },
    get vacuumAnalyzeSnapshotTable () {
        return vacuumAnalyzeSnapshotTable;
    }
});
const _case = require("case");
const SCHEMA = 'sync_snapshots';
const assertIfSessionIdIsSafe = (sessionId)=>{
    const safeIdRegex = /^[A-Za-z0-9-]+$/;
    if (!safeIdRegex.test(sessionId)) {
        throw new Error(`${sessionId} does not match the expected format of a session id - be careful of SQL injection!`);
    }
};
const getSnapshotTableName = (sessionId)=>{
    assertIfSessionIdIsSafe(sessionId);
    return `"${SCHEMA}"."${sessionId}"`;
};
const getMarkedForSyncPatientsTableName = (sessionId, isFullSync)=>{
    assertIfSessionIdIsSafe(sessionId);
    return `"${SCHEMA}"."${sessionId}_${isFullSync ? 'full_sync' : 'regular_sync'}_marked_for_sync_patients"`;
};
const createSnapshotTable = async (sequelize, sessionId)=>{
    const tableName = getSnapshotTableName(sessionId);
    const indexNamePrefix = tableName.replaceAll('.', '_').replaceAll('"', '').replaceAll('-', '');
    await sequelize.query(`
    CREATE TABLE ${tableName} (
      id BIGSERIAL PRIMARY KEY,
      direction text NOT NULL,
      record_type text NOT NULL,
      record_id text NOT NULL,
      is_deleted boolean NOT NULL,
      data json NOT NULL,
      saved_at_sync_tick bigint, -- saved_at_sync_tick is used to check whether record has been updated between incoming and outgoing phase of a single session
      updated_at_by_field_sum bigint, -- updated_at_by_field_sum is used to check whether record has had changes to field during merge and save component of push phase
      sync_lookup_id bigint,
      changelog_records json,
      requires_repull boolean DEFAULT false
    ) WITH (
      autovacuum_enabled = off
    );
    CREATE INDEX ${indexNamePrefix}_dir_id_idx ON ${tableName}(direction, id) INCLUDE (record_type);
    CREATE INDEX ${indexNamePrefix}_dir_rec_id_idx ON ${tableName}(direction, record_type, id);
  `);
};
const dropSnapshotTable = async (sequelize, sessionId)=>{
    const tableName = getSnapshotTableName(sessionId);
    await sequelize.query(`
    DROP TABLE IF EXISTS ${tableName};
  `);
};
const dropMarkedForSyncPatientsTable = async (sequelize, sessionId)=>{
    const fullSyncMarkedForSyncPatientsTableName = getMarkedForSyncPatientsTableName(sessionId, true);
    const regularSyncMarkedForSyncPatientsTableName = getMarkedForSyncPatientsTableName(sessionId, false);
    await sequelize.query(`
    DROP TABLE IF EXISTS ${fullSyncMarkedForSyncPatientsTableName};
    DROP TABLE IF EXISTS ${regularSyncMarkedForSyncPatientsTableName};
  `);
};
const dropAllSnapshotTables = async (sequelize)=>{
    const queryInterface = sequelize.getQueryInterface();
    await queryInterface.dropSchema(SCHEMA);
    await queryInterface.createSchema(SCHEMA, {});
};
const snakeKey = (obj)=>Object.fromEntries(Object.entries(obj).map(([key, value])=>[
            (0, _case.snake)(key),
            value
        ]));
const insertSnapshotRecords = async (sequelize, sessionId, records)=>{
    const queryInterface = sequelize.getQueryInterface();
    const sanitizedRecords = records.map((r)=>snakeKey(r)).map((r)=>({
            ...r,
            data: JSON.stringify(r.data),
            changelog_records: JSON.stringify(r.changelog_records)
        }));
    await queryInterface.bulkInsert({
        tableName: sessionId,
        schema: SCHEMA
    }, sanitizedRecords);
};
const updateSnapshotRecords = async (sequelize, sessionId, values, where)=>{
    const queryInterface = sequelize.getQueryInterface();
    await queryInterface.bulkUpdate({
        tableName: sessionId,
        schema: SCHEMA
    }, snakeKey(values), snakeKey(where));
};
const vacuumAnalyzeSnapshotTable = async (sequelize, sessionId)=>{
    const tableName = getSnapshotTableName(sessionId);
    // VACUUM cannot run inside an open transaction; caller must ensure autocommit context
    await sequelize.query(`VACUUM (ANALYZE) ${tableName};`);
};

//# sourceMappingURL=manageSnapshotTable.js.map