"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.__ΩNewEntityFields = exports.__ΩEntityOptionals = exports.__ΩOptionalKeys = exports.__ΩHasDefault = exports.__ΩEntityClassFields = exports.__ΩEntityFields = exports.__ΩEntityFieldKeys = exports.__ΩDataTypes = exports.BaseDatabase = void 0;
exports.createEntity = createEntity;
exports.createEntities = createEntities;
exports.createQueuedEntity = createQueuedEntity;
exports.createQueuedEntities = createQueuedEntities;
exports.persistEntity = persistEntity;
exports.persistEntities = persistEntities;
exports.createPersistedEntity = createPersistedEntity;
exports.createPersistedEntities = createPersistedEntities;
exports.getPKFieldForEntity = getPKFieldForEntity;
exports.getPKFieldForEntityInstance = getPKFieldForEntityInstance;
exports.getEntityOrUndefined = getEntityOrUndefined;
exports.getEntityOr404 = getEntityOr404;
exports.getEntity = getEntity;
exports.entityExists = entityExists;
exports.getEntityFields = getEntityFields;
exports.logSql = logSql;
const __ΩInstanceType = ['T', 'args', 'new', 'InstanceType', 'l>e"!R"RPde#!P"@2"h"!/#qk#\'QRb!Pde"!p)w$y'];
const __ΩPick = ['T', 'K', 'Pick', 'l+e#!e"!fRb!b"Pde""N#!w#y'];
const __ΩOmit = ['T', 'K', () => __ΩPick, () => __ΩExclude, 'Omit', 'b!b"e!!e!!ge!"o$#o##w%y'];
const __ΩExclude = ['T', 'U', 'Exclude', 'l6!Re$!RPe#!e$"qk#%QRb!b"Pde"!p)w#y'];
function __assignType(fn, args) {
    fn.__type = args;
    return fn;
}
/*@ts-ignore*/
var { __ΩClassType, __ΩAbstractClassType, __ΩTypeAnnotation } = require('@deepkit/core');
const http_1 = require("@deepkit/http");
/*@ts-ignore*/
var { __ΩActiveRecordClassType, __ΩFilterQuery } = require('@deepkit/orm');
const orm_1 = require("@deepkit/orm");
const sql_1 = require("@deepkit/sql");
/*@ts-ignore*/
var { __ΩReceiveType, __ΩAutoIncrement } = require('@deepkit/type');
const type_1 = require("@deepkit/type");
const lodash_1 = require("lodash");
/*@ts-ignore*/
var { __ΩMutexKey } = require('../helpers');
const helpers_1 = require("../helpers");
/*@ts-ignore*/
var { __ΩObjectKeysMatching } = require('../types');
const dialect_1 = require("./dialect");
const PreCommitHooksSymbol = Symbol('PreCommitHooks');
const PostCommitHooksSymbol = Symbol('PostCommitHooks');
orm_1.DatabaseSession.prototype.addPreCommitHook = __assignType(function (hook) {
    if (!this[PreCommitHooksSymbol])
        this[PreCommitHooksSymbol] = [];
    this[PreCommitHooksSymbol].push(hook);
}, ['', 'hook', 'PP$`/!2""/!']);
orm_1.DatabaseSession.prototype.addPostCommitHook = __assignType(function (hook) {
    if (!this[PostCommitHooksSymbol])
        this[PostCommitHooksSymbol] = [];
    this[PostCommitHooksSymbol].push(hook);
}, ['', 'hook', 'PP$`/!2""/!']);
orm_1.DatabaseSession.prototype.acquireSessionLock = __assignType(async function (key) {
    const flattenedKey = (0, helpers_1.flattenMutexKey)(key);
    const adapter = this.adapter;
    const dialect = (0, dialect_1.getDialect)(adapter);
    if (dialect === 'postgres') {
        // use a transaction-scoped advisory lock — automatically released on commit/rollback
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        const lockQuery = adapter.rawFactory(this).create((0, sql_1.sql) `SELECT pg_advisory_xact_lock(hashtext(${flattenedKey})::bigint)`);
        await lockQuery.find();
    }
    else {
        // the idea here is to create a new row outside the transaction so that a primary key index lock isn't obtained
        // then we can use an in-transaction row level update so that a row-level lock is acquired
        // this will be automatically released when the transaction is committed or rolled back
        // insert the lock row outside the transaction on a separate connection
        const insertConn = await adapter.connectionPool.getConnection();
        try {
            await insertConn.run(`INSERT IGNORE INTO _locks (\`key\`) VALUES (?)`, [flattenedKey]);
        }
        finally {
            insertConn.release();
        }
        // update in-transaction to acquire row-level lock
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        const updateQuery = adapter.rawFactory(this).create((0, sql_1.sql) `UPDATE _locks SET lastTouched=NOW() WHERE \`key\` = ${flattenedKey}`);
        await updateQuery.execute();
    }
}, [() => __ΩMutexKey, () => __ΩMutexKey, 'key', '', 'PPn!n"FJ2#"/$']);
const __ΩQueryClassType = ['T', () => __ΩReceiveType, () => __ΩClassType, () => __ΩAbstractClassType, () => type_1.ReflectionClass, 'QueryClassType', 'b!Pe"!o""e"!o#"e"!o$"Pe#!7%Jw&y'];
class BaseDatabase extends orm_1.Database {
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    constructor(adapter, schemas) {
        super(adapter, schemas);
        // we don't like the default "clone" behavior of the query builder
        // todo: ideally we could keep .clone but only have it actually perform the clone if it's called from outside the lib
        const originalQuery = this.query;
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        this.query = __assignType((type, txn) => {
            const result = txn ? txn.query(type) : originalQuery.apply(this, [type]);
            result.clone = () => result;
            return result;
        }, [() => __ΩQueryClassType, 'type', () => orm_1.DatabaseAdapter, () => orm_1.DatabaseSession, 'txn', '', 'P"o!"2"PP7#7$2%8"/&']);
    }
    async transaction(callback) {
        let session_;
        const result = await super.transaction(__assignType(async (session) => {
            session_ = session;
            session.withIdentityMap = false;
            const result = await callback(session);
            if (session[PreCommitHooksSymbol]) {
                await session.flush();
                for (const hook of session[PreCommitHooksSymbol]) {
                    await hook();
                }
            }
            return result;
        }, ['session', '', 'P"2!"/"']));
        if (session_?.[PostCommitHooksSymbol]) {
            for (const hook of session_[PostCommitHooksSymbol]) {
                await hook();
            }
        }
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        return result; // todo: types
    }
    async session(worker) {
        return super.session(__assignType(async (session) => {
            session.withIdentityMap = false;
            return worker(session);
        }, ['session', '', 'P"2!"/"']));
    }
    async withTransaction(txn, callback) {
        if (txn) {
            return await callback(txn);
        }
        else {
            return await this.transaction(__assignType(async (txn) => callback(txn), ['txn', '', 'P"2!"/"']));
        }
    }
    async withSession(session, callback) {
        if (session) {
            return await callback(session);
        }
        else {
            return await this.session(__assignType(async (session) => callback(session), ['session', '', 'P"2!"/"']));
        }
    }
    async rawExecute(sqlIn, 
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    existingSession) {
        return this.withSession(existingSession, __assignType(async (session) => {
            if (!(session.adapter instanceof sql_1.SQLDatabaseAdapter)) {
                throw new Error('Cannot perform raw query on non-SQL database');
            }
            // raw queries executed against a session likely expect the data to already be written,
            // so let's flush everything we have already queued up
            if (existingSession) {
                await session.flush();
            }
            const adapter = session.adapter;
            const sqlQuery = typeof sqlIn === 'string' ? new sql_1.SqlQuery([sqlIn]) : sqlIn;
            // eslint-disable-next-line @typescript-eslint/no-explicit-any
            const rawQuery = adapter.rawFactory(session).create(sqlQuery);
            // eslint-disable-next-line @typescript-eslint/no-explicit-any
            return rawQuery.execute();
        }, ['session', '', 'P"2!"/"']));
    }
    async rawExecuteUnsafe(sqlIn, 
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    bindings, 
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    existingSession) {
        return this.rawExecute(this.createSqlQuery(sqlIn, bindings), existingSession);
    }
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    async rawQuery(sqlIn, existingSession) {
        return this.withSession(existingSession, __assignType(async (session) => {
            if (!(session.adapter instanceof sql_1.SQLDatabaseAdapter)) {
                throw new Error('Cannot perform raw query on non-SQL database');
            }
            // raw queries executed against a session likely expect the data to already be written,
            // so let's flush everything we have already queued up
            if (existingSession) {
                await session.flush();
            }
            const adapter = session.adapter;
            const sqlQuery = typeof sqlIn === 'string' ? new sql_1.SqlQuery([sqlIn]) : sqlIn;
            // eslint-disable-next-line @typescript-eslint/no-explicit-any
            const rawQuery = adapter.rawFactory(session).create(sqlQuery);
            return await rawQuery.find();
        }, ['session', '', 'P"2!"/"']));
    }
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    async rawFind(sqlIn, existingSession, type = this.rawFind.Ω?.[0]) {
        this.rawFind.Ω = undefined;
        const rows = await this.rawQuery(sqlIn, existingSession);
        return type
            ? rows.map(__assignType(row => (type_1.deserialize.Ω = [[type, 'n!']], (0, type_1.deserialize)(row, undefined, this.adapter.platform.serializer, undefined, type)), ['row', '', 'P"2!"/"']))
            : rows;
    }
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    async rawFindUnsafe(sqlIn, bindings, existingSession, type = this.rawFindUnsafe.Ω?.[0]) {
        this.rawFindUnsafe.Ω = undefined;
        return (this.rawFind.Ω = [[type, 'n!']], this.rawFind(this.createSqlQuery(sqlIn, bindings), existingSession, type));
    }
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    async rawFindOne(sqlIn, existingSession, type = this.rawFindOne.Ω?.[0]) {
        this.rawFindOne.Ω = undefined;
        const rows = await (this.rawFind.Ω = [[type, 'n!']], this.rawFind(sqlIn, existingSession, type));
        return rows[0];
    }
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    async rawFindOneUnsafe(sqlIn, bindings, existingSession, type = this.rawFindOneUnsafe.Ω?.[0]) {
        this.rawFindOneUnsafe.Ω = undefined;
        return (this.rawFindOne.Ω = [[type, 'n!']], this.rawFindOne(this.createSqlQuery(sqlIn, bindings), existingSession, type));
    }
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    createSqlQuery(sqlIn, bindings) {
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        const sqlPieces = sqlIn.split('?');
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        sqlPieces.raw = sqlIn;
        return (0, sql_1.sql)(sqlPieces, ...bindings);
    }
}
exports.BaseDatabase = BaseDatabase;
BaseDatabase.__type = [() => orm_1.DatabaseAdapter, 'A', () => orm_1.Database, 'adapter', 'Type', () => __ΩClassType, () => type_1.ReflectionClass, 'schemas', 'constructor', () => __ΩQueryClassType, 'type', () => orm_1.DatabaseAdapter, () => orm_1.DatabaseSession, 'txn', () => sql_1.SQLDatabaseQuery, '', 'query', () => orm_1.DatabaseSession, 'session', 'callback', 'transaction', () => orm_1.DatabaseSession, 'worker', () => orm_1.DatabaseSession, () => orm_1.DatabaseSession, 'withTransaction', () => orm_1.DatabaseSession, () => orm_1.DatabaseSession, 'withSession', () => sql_1.SqlQuery, 'sqlIn', () => orm_1.DatabaseSession, 'existingSession', 'affectedRows', 'insertId', 'warningStatus', 'rawExecute', 'bindings', () => orm_1.DatabaseSession, 'rawExecuteUnsafe', () => sql_1.SqlQuery, () => orm_1.DatabaseSession, 'rawQuery', () => sql_1.SqlQuery, () => orm_1.DatabaseSession, () => __ΩReceiveType, 'rawFind', () => orm_1.DatabaseSession, () => __ΩReceiveType, 'rawFindUnsafe', () => sql_1.SqlQuery, () => orm_1.DatabaseSession, () => __ΩReceiveType, 'rawFindOne', () => orm_1.DatabaseSession, () => __ΩReceiveType, 'rawFindOneUnsafe', () => sql_1.SqlQuery, 'createSqlQuery', 'BaseDatabase', 'P7!c"Pe"!7#Pe"!2$P"w%n&P"7\'JF2(8"0)P"o*"2+8PP7,7-2.8P"7//031PPPe$!7223"`/024"`05PPPe$!7623"`/027"`03PPPe$!78-J2.PPe$!7923"`/024"`0:PPPe$!7;-J23PPe$!7<23"`/024"`0=PPP7>&J2?P"7@2A8P\'4B\'4C\'4DM`0EP&2?"F2FP"7G2A8P\'4B\'4C\'4DM`0HPPP7I&J2?P"7J2A8"0KPPP7L&J2?P"7M2A8"oN"2+8"F`0OP&2?"F2FP"7P2A8"oQ"2+8"F`0RPPP7S&J2?P"7T2A8"oU"2+8P"-J`0VP&2?"F2FP"7W2A8"oX"2+8P"-J`0YP&2?"F2FP7Z0[5e!!6"w\\'];
const __ΩFieldsMatching = ['T', 'V', () => Function, 'FieldsMatching', 'lM!Re&!RPe&!e%!fPu#qk#%QR!RPe$!e#!fe$"qk):QRb!b"Pde"!gN<!e!!gfw$y'];
const __ΩDataTypes = ['DataTypes', 'P&\')T%,Jw!y'];
exports.__ΩDataTypes = __ΩDataTypes;
const __ΩEntityFieldKeys = ['T', () => __ΩFieldsMatching, () => __ΩDataTypes, 'EntityFieldKeys', 'b!e!!n#o"#w$y'];
exports.__ΩEntityFieldKeys = __ΩEntityFieldKeys;
const __ΩEntityFields = ['T', () => __ΩPick, () => __ΩEntityFieldKeys, 'EntityFields', 'b!e!!e!!o#"o"#w$y'];
exports.__ΩEntityFields = __ΩEntityFields;
const __ΩEntityClassFields = ['T', () => __ΩPick, () => __ΩInstanceType, () => __ΩEntityFieldKeys, () => __ΩInstanceType, 'EntityClassFields', 'b!e!!o#"e!!o%"o$"o"#w&y'];
exports.__ΩEntityClassFields = __ΩEntityClassFields;
const __ΩHasDefault = [() => __ΩTypeAnnotation, "dksf:hasDefault", 'HasDefault', '."o!"w#y'];
exports.__ΩHasDefault = __ΩHasDefault;
const __ΩOptionalKeys = ['T', () => __ΩObjectKeysMatching, () => __ΩHasDefault, () => __ΩObjectKeysMatching, () => __ΩObjectKeysMatching, () => __ΩAutoIncrement, 'OptionalKeys', 'b!Pe"!n#o"#e"!,o$#e"!n&o%#Jw\'y'];
exports.__ΩOptionalKeys = __ΩOptionalKeys;
const __ΩEntityOptionals = ['T', () => __ΩPick, () => __ΩOptionalKeys, () => __ΩOmit, () => __ΩOptionalKeys, 'EntityOptionals', 'l3e$!e"!fRe$!e"!fRb!PPde#!e#!o#"o"#gN#"Pde#!e#!o%"o$#gN+!Kw&y'];
exports.__ΩEntityOptionals = __ΩEntityOptionals;
const __ΩNewEntityFields = ['T', () => __ΩEntityOptionals, () => __ΩEntityFields, 'NewEntityFields', 'b!e!!o#"o""w$y'];
exports.__ΩNewEntityFields = __ΩNewEntityFields;
function createEntity(Entity, data) {
    const entity = new Entity();
    Object.assign(entity, data);
    // fill in the nulls and auto-increments
    const type = type_1.ReflectionClass.from(Entity);
    for (const property of type.getProperties()) {
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        if (data[property.name] === undefined) {
            if (property.isAutoIncrement()) {
                // eslint-disable-next-line @typescript-eslint/no-explicit-any
                entity[property.name] = 0;
            }
            else if (property.isNullable()) {
                // eslint-disable-next-line @typescript-eslint/no-explicit-any
                entity[property.name] = null;
            }
        }
    }
    return entity;
}
createEntity.__type = [() => __ΩClassType, 'Entity', () => __ΩNewEntityFields, 'data', 'createEntity', 'P"o!"2""o#"2$"/%'];
function createEntities(Entity, data) {
    return data.map(__assignType(d => createEntity(Entity, d), ['d', '', 'P"2!"/"']));
}
createEntities.__type = [() => __ΩClassType, 'Entity', () => __ΩNewEntityFields, 'data', 'createEntities', 'P"o!"2""o#"F2$"F/%'];
function createQueuedEntity(Entity, data, 
// eslint-disable-next-line @typescript-eslint/no-explicit-any
session) {
    const entity = createEntity(Entity, data);
    session.add(entity);
    return entity;
}
createQueuedEntity.__type = [() => __ΩClassType, 'Entity', () => __ΩNewEntityFields, 'data', () => orm_1.DatabaseSession, 'session', 'createQueuedEntity', 'P"o!"2""o#"2$P"7%2&"/\''];
function createQueuedEntities(Entity, data, 
// eslint-disable-next-line @typescript-eslint/no-explicit-any
session) {
    const entities = createEntities(Entity, data);
    session.add(...entities);
    return entities;
}
createQueuedEntities.__type = [() => __ΩClassType, 'Entity', () => __ΩNewEntityFields, 'data', () => orm_1.DatabaseSession, 'session', 'createQueuedEntities', 'P"o!"2""o#"F2$P"7%2&"F/\''];
// eslint-disable-next-line @typescript-eslint/no-explicit-any
async function persistEntity(entity, session) {
    return persistEntities([entity], session);
}
persistEntity.__type = ['entity', () => orm_1.DatabaseSession, 'session', 'persistEntity', 'P"2!P"7"2#8"/$'];
// eslint-disable-next-line @typescript-eslint/no-explicit-any
async function persistEntities(entities, session) {
    if (session) {
        session.add(...entities);
        await session.flush();
    }
    else {
        for (const entity of entities) {
            await entity.save();
        }
    }
}
persistEntities.__type = ['entities', () => orm_1.DatabaseSession, 'session', 'persistEntities', 'P"F2!P"7"2#8"/$'];
async function createPersistedEntity(Entity, data, 
// eslint-disable-next-line @typescript-eslint/no-explicit-any
session) {
    const entity = createEntity(Entity, data);
    await persistEntity(entity, session);
    return entity;
}
createPersistedEntity.__type = [() => __ΩClassType, 'Entity', () => __ΩNewEntityFields, 'data', () => orm_1.DatabaseSession, 'session', 'createPersistedEntity', 'P"o!"2""o#"2$P"7%2&8"`/\''];
async function createPersistedEntities(Entity, data, 
// eslint-disable-next-line @typescript-eslint/no-explicit-any
session) {
    const entities = createEntities(Entity, data);
    await persistEntities(entities, session);
    return entities;
}
createPersistedEntities.__type = [() => __ΩClassType, 'Entity', () => __ΩNewEntityFields, 'data', () => orm_1.DatabaseSession, 'session', 'createPersistedEntities', 'P"o!"2""o#"F2$P"7%2&8"F`/\''];
const PKFieldCache = (WeakMap.Ω = [[() => __ΩActiveRecordClassType, 'n!'], ['&']], new WeakMap());
function getPKFieldForEntity(EntityClass) {
    if (PKFieldCache.has(EntityClass))
        return PKFieldCache.get(EntityClass);
    const pkField = type_1.ReflectionClass.from(EntityClass).getPrimary().name;
    PKFieldCache.set(EntityClass, pkField);
    return pkField;
}
getPKFieldForEntity.__type = [() => __ΩActiveRecordClassType, 'EntityClass', 'getPKFieldForEntity', 'Pn!2""/#'];
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function getPKFieldForEntityInstance(entity) {
    if (!(entity instanceof orm_1.ActiveRecord)) {
        if (typeof entity === 'object' && 'id' in entity)
            return 'id';
        throw new Error('Entity does not extend ActiveRecord class and does not have "id" field');
    }
    return getPKFieldForEntity(entity.constructor);
}
getPKFieldForEntityInstance.__type = ['entity', 'getPKFieldForEntityInstance', 'P"2!"/"'];
const __ΩEntityFilter = ['T', () => __ΩFilterQuery, () => __ΩInstanceType, 'EntityFilter', 'b!Pe"!o#"o""&\'Jw$y'];
function getEntityFilter(EntityClass, filter) {
    if (typeof filter === 'object')
        return filter;
    const pkField = getPKFieldForEntity(EntityClass);
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    return { [pkField]: filter };
}
getEntityFilter.__type = ['EntityClass', () => __ΩEntityFilter, 'filter', () => __ΩFilterQuery, () => __ΩInstanceType, 'getEntityFilter', 'P"2!"o""2#"o%"o$"/&'];
async function getEntityOrUndefined(EntityClass, filter) {
    const query = EntityClass.query();
    const resolvedFilter = getEntityFilter(EntityClass, filter);
    return query.filter(resolvedFilter).findOneOrUndefined();
}
getEntityOrUndefined.__type = ['EntityClass', () => __ΩEntityFilter, 'filter', () => __ΩInstanceType, 'getEntityOrUndefined', 'P"2!"o""2#P"o$"-J`/%'];
async function getEntityOr404(EntityClass, filter) {
    const entity = await getEntityOrUndefined(EntityClass, filter);
    if (!entity)
        throw new http_1.HttpNotFoundError();
    return entity;
}
getEntityOr404.__type = ['EntityClass', () => __ΩEntityFilter, 'filter', () => __ΩInstanceType, 'getEntityOr404', 'P"2!"o""2#"o$"`/%'];
async function getEntity(EntityClass, filter) {
    const entity = await getEntityOrUndefined(EntityClass, filter);
    if (!entity)
        throw new orm_1.ItemNotFound();
    return entity;
}
getEntity.__type = ['EntityClass', () => __ΩEntityFilter, 'filter', () => __ΩInstanceType, 'getEntity', 'P"2!"o""2#"o$"`/%'];
async function entityExists(EntityClass, filter) {
    const query = EntityClass.query();
    const resolvedFilter = getEntityFilter(EntityClass, filter);
    return query.filter(resolvedFilter).has();
}
entityExists.__type = ['EntityClass', () => __ΩEntityFilter, 'filter', 'entityExists', 'P"2!"o""2#)`/$'];
function getEntityFields(entity) {
    return (0, lodash_1.pick)(entity, Object.keys(entity));
}
getEntityFields.__type = ['entity', () => __ΩEntityFields, 'getEntityFields', 'P"2!"o""/#'];
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function logSql(sql, bindings = []) {
    const logBindings = [...bindings];
    console.log(sql.replace(/\?/g, () => JSON.stringify(logBindings.shift())));
}
logSql.__type = ['sql', 'bindings', () => [], 'logSql', 'P&2!"F2">#"/$'];
//# sourceMappingURL=common.js.map