ores.cpp.repository.repository_impl

Table of Contents

CRUD + temporal queries over sqlgen; custom-method paste points. repository profile. Database access layer using Qt SQL parameterised queries, tracking `valid_from`/`valid_to`.

See the Template variable reference for the complete list of available variables and their semantics.

Template

The full template source. Edit here and re-tangle with compass build --direct tangle_codegen_templates to regenerate library/templates/cpp_domain_type_repository.cpp.mustache.

{{! GENERATED FILE — tangled from projects/ores.codegen/library/templates/cpp_repository.org. Edit the org source. }}
{{{cpp_license}}}
{{#domain_entity}}
#include "ores.{{component_core}}/repository/{{entity_singular}}_repository.hpp"

#include <sqlgen/postgres.hpp>
#include "ores.database/repository/helpers.hpp"
#include "ores.database/repository/bitemporal_operations.hpp"
#include "ores.{{component_include}}/domain/{{entity_singular}}_json_io.hpp" // IWYU pragma: keep.
#include "ores.{{component_core}}/repository/{{entity_singular}}_entity.hpp"
#include "ores.{{component_core}}/repository/{{entity_singular}}_mapper.hpp"
{{#domain_entity.sql.has_list_by_as_of}}
#include "ores.platform/time/datetime.hpp"
{{/domain_entity.sql.has_list_by_as_of}}
{{#has_parent_id}}
#include <boost/lexical_cast.hpp>
#include <boost/uuid/uuid_io.hpp>
{{/has_parent_id}}
{{#primary_key.is_compound}}
#include <stdexcept>
{{/primary_key.is_compound}}
{{#has_batch_read}}
{{#primary_key.is_compound}}
#include <set>
#include <tuple>
{{/primary_key.is_compound}}
{{/has_batch_read}}
<<paste:6141050C-0ED4-4680-B387-7DDDA3A69806>>

namespace ores::{{component}}::repository {

using namespace sqlgen;
using namespace sqlgen::literals;
using namespace ores::logging;
using namespace ores::database::repository;

std::string {{entity_singular}}_repository::sql() {
    return generate_create_table_sql<{{entity_singular}}_entity>(lg());
}

void {{entity_singular}}_repository::write(context ctx, const domain::{{entity_singular}}& v) {
    BOOST_LOG_SEV(lg(), debug) << "Writing {{entity_singular_words}}. " << {{{primary_key.value_log_fields}}};
    execute_write_query(ctx, {{entity_singular}}_mapper::map(v),
        lg(), "Writing {{entity_singular_words}} to database.");
}

void {{entity_singular}}_repository::write(
    context ctx, const std::vector<domain::{{entity_singular}}>& v) {
    BOOST_LOG_SEV(lg(), debug) << "Writing {{entity_plural_words}}. Count: " << v.size();
    execute_write_query(ctx, {{entity_singular}}_mapper::map(v),
        lg(), "Writing {{entity_plural_words}} to database.");
}

std::vector<domain::{{entity_singular}}>
{{entity_singular}}_repository::read_latest(context ctx) {
    static const auto max(make_timestamp(MAX_TIMESTAMP, lg()));
{{#read_tenant_filtered}}
    const auto tid = ctx.tenant_id().to_string();
{{#has_workspace_id}}
    const auto& chain = ctx.workspace_resolution();
    if (!chain.empty()) {
        const auto query = sqlgen::read<std::vector<{{entity_singular}}_entity>> |
            where("tenant_id"_c == tid && "workspace_id"_c.in(chain) && "valid_to"_c == max.value()) |
            order_by({{{primary_key.order_by_cols}}});
        return execute_read_query<{{entity_singular}}_entity, domain::{{entity_singular}}>(
            ctx, query,
            [](const auto& entities) { return {{entity_singular}}_mapper::map(entities); },
            lg(), "Reading latest {{entity_plural_words}} (workspace resolution chain).");
    }
    const auto wid = ctx.workspace_id();
    const auto query = sqlgen::read<std::vector<{{entity_singular}}_entity>> |
        where("tenant_id"_c == tid && "workspace_id"_c == wid && "valid_to"_c == max.value()) |
        order_by({{{primary_key.order_by_cols}}});
{{/has_workspace_id}}
{{^has_workspace_id}}
    const auto query = sqlgen::read<std::vector<{{entity_singular}}_entity>> |
        where("tenant_id"_c == tid && "valid_to"_c == max.value()) |
        order_by({{{primary_key.order_by_cols}}});
{{/has_workspace_id}}
{{/read_tenant_filtered}}
{{^read_tenant_filtered}}
{{#has_workspace_id}}
    const auto& chain = ctx.workspace_resolution();
    if (!chain.empty()) {
        const auto query = sqlgen::read<std::vector<{{entity_singular}}_entity>> |
            where("workspace_id"_c.in(chain) && "valid_to"_c == max.value()) |
            order_by({{{primary_key.order_by_cols}}});
        return execute_read_query<{{entity_singular}}_entity, domain::{{entity_singular}}>(
            ctx, query,
            [](const auto& entities) { return {{entity_singular}}_mapper::map(entities); },
            lg(), "Reading latest {{entity_plural_words}} (workspace resolution chain).");
    }
    const auto wid = ctx.workspace_id();
    const auto query = sqlgen::read<std::vector<{{entity_singular}}_entity>> |
        where("workspace_id"_c == wid && "valid_to"_c == max.value()) |
        order_by({{{primary_key.order_by_cols}}});
{{/has_workspace_id}}
{{^has_workspace_id}}
    const auto query = sqlgen::read<std::vector<{{entity_singular}}_entity>> |
        where("valid_to"_c == max.value()) |
        order_by({{{primary_key.order_by_cols}}});
{{/has_workspace_id}}
{{/read_tenant_filtered}}

    return execute_read_query<{{entity_singular}}_entity, domain::{{entity_singular}}>(
        ctx, query,
        [](const auto& entities) { return {{entity_singular}}_mapper::map(entities); },
        lg(), "Reading latest {{entity_plural_words}}");
}

std::vector<domain::{{entity_singular}}>
{{entity_singular}}_repository::read_latest(context ctx, {{{primary_key.params}}}) {
    BOOST_LOG_SEV(lg(), debug) << "Reading latest {{entity_singular_words}}. " << {{{primary_key.log_fields}}};
    static const auto max(make_timestamp(MAX_TIMESTAMP, lg()));
{{#read_tenant_filtered}}
    const auto tid = ctx.tenant_id().to_string();
{{#has_workspace_id}}
    const auto wid = ctx.workspace_id();
    const auto query = sqlgen::read<std::vector<{{entity_singular}}_entity>> |
        where("tenant_id"_c == tid && "workspace_id"_c == wid && {{{primary_key.where_and}}} && "valid_to"_c == max.value());
{{/has_workspace_id}}
{{^has_workspace_id}}
    const auto query = sqlgen::read<std::vector<{{entity_singular}}_entity>> |
        where("tenant_id"_c == tid && {{{primary_key.where_and}}} && "valid_to"_c == max.value());
{{/has_workspace_id}}
{{/read_tenant_filtered}}
{{^read_tenant_filtered}}
{{#has_workspace_id}}
    const auto wid = ctx.workspace_id();
    const auto query = sqlgen::read<std::vector<{{entity_singular}}_entity>> |
        where("workspace_id"_c == wid && {{{primary_key.where_and}}} && "valid_to"_c == max.value());
{{/has_workspace_id}}
{{^has_workspace_id}}
    const auto query = sqlgen::read<std::vector<{{entity_singular}}_entity>> |
        where({{{primary_key.where_and}}} && "valid_to"_c == max.value());
{{/has_workspace_id}}
{{/read_tenant_filtered}}

    return execute_read_query<{{entity_singular}}_entity, domain::{{entity_singular}}>(
        ctx, query,
        [](const auto& entities) { return {{entity_singular}}_mapper::map(entities); },
        lg(), "Reading latest {{entity_singular_words}} by {{primary_key.column}}.");
}

{{#service_find_by_code}}
std::vector<domain::{{entity_singular}}>
{{entity_singular}}_repository::read_latest_by_code(context ctx,
{{#parent_column}}
    const std::string& {{parent_column}},
{{/parent_column}}
    const std::string& {{column}}) {
{{#parent_column}}
    BOOST_LOG_SEV(lg(), debug) << "Reading latest {{entity_singular_words}} by {{parent_column}}/{{column}}: "
                               << {{parent_column}} << "/" << {{column}};
{{/parent_column}}
{{^parent_column}}
    BOOST_LOG_SEV(lg(), debug) << "Reading latest {{entity_singular_words}} by {{column}}: " << {{column}};
{{/parent_column}}
    static const auto max(make_timestamp(MAX_TIMESTAMP, lg()));
{{#read_tenant_filtered}}
    const auto tid = ctx.tenant_id().to_string();
{{#has_workspace_id}}
    const auto wid = ctx.workspace_id();
    const auto query = sqlgen::read<std::vector<{{entity_singular}}_entity>> |
        where("tenant_id"_c == tid && "workspace_id"_c == wid && {{#parent_column}}"{{parent_column}}"_c == {{parent_column}} && {{/parent_column}}"{{column}}"_c == {{column}} && "valid_to"_c == max.value());
{{/has_workspace_id}}
{{^has_workspace_id}}
    const auto query = sqlgen::read<std::vector<{{entity_singular}}_entity>> |
        where("tenant_id"_c == tid && {{#parent_column}}"{{parent_column}}"_c == {{parent_column}} && {{/parent_column}}"{{column}}"_c == {{column}} && "valid_to"_c == max.value());
{{/has_workspace_id}}
{{/read_tenant_filtered}}
{{^read_tenant_filtered}}
{{#has_workspace_id}}
    const auto wid = ctx.workspace_id();
    const auto query = sqlgen::read<std::vector<{{entity_singular}}_entity>> |
        where("workspace_id"_c == wid && {{#parent_column}}"{{parent_column}}"_c == {{parent_column}} && {{/parent_column}}"{{column}}"_c == {{column}} && "valid_to"_c == max.value());
{{/has_workspace_id}}
{{^has_workspace_id}}
    const auto query = sqlgen::read<std::vector<{{entity_singular}}_entity>> |
        where({{#parent_column}}"{{parent_column}}"_c == {{parent_column}} && {{/parent_column}}"{{column}}"_c == {{column}} && "valid_to"_c == max.value());
{{/has_workspace_id}}
{{/read_tenant_filtered}}

    return execute_read_query<{{entity_singular}}_entity, domain::{{entity_singular}}>(
        ctx, query,
        [](const auto& entities) { return {{entity_singular}}_mapper::map(entities); },
        lg(), "Reading latest {{entity_singular_words}} by {{column}}.");
}
{{/service_find_by_code}}
{{#has_as_of_lookup}}
std::vector<domain::{{entity_singular}}>
{{entity_singular}}_repository::read_at_timepoint(context ctx, const std::string& as_of) {
    BOOST_LOG_SEV(lg(), debug) << "Reading {{entity_plural_words}} at timepoint: " << as_of;
    const auto ts = make_timestamp(as_of, lg());
{{#read_tenant_filtered}}
    const auto tid = ctx.tenant_id().to_string();
{{#has_workspace_id}}
    const auto wid = ctx.workspace_id();
    const auto query = sqlgen::read<std::vector<{{entity_singular}}_entity>> |
        where("tenant_id"_c == tid && "workspace_id"_c == wid &&
              "valid_from"_c <= ts.value() && "valid_to"_c > ts.value()) |
        order_by("{{primary_key.column}}"_c);
{{/has_workspace_id}}
{{^has_workspace_id}}
    const auto query = sqlgen::read<std::vector<{{entity_singular}}_entity>> |
        where("tenant_id"_c == tid &&
              "valid_from"_c <= ts.value() && "valid_to"_c > ts.value()) |
        order_by("{{primary_key.column}}"_c);
{{/has_workspace_id}}
{{/read_tenant_filtered}}
{{^read_tenant_filtered}}
{{#has_workspace_id}}
    const auto wid = ctx.workspace_id();
    const auto query = sqlgen::read<std::vector<{{entity_singular}}_entity>> |
        where("workspace_id"_c == wid &&
              "valid_from"_c <= ts.value() && "valid_to"_c > ts.value()) |
        order_by("{{primary_key.column}}"_c);
{{/has_workspace_id}}
{{^has_workspace_id}}
    const auto query = sqlgen::read<std::vector<{{entity_singular}}_entity>> |
        where("valid_from"_c <= ts.value() && "valid_to"_c > ts.value()) |
        order_by("{{primary_key.column}}"_c);
{{/has_workspace_id}}
{{/read_tenant_filtered}}

    return execute_read_query<{{entity_singular}}_entity, domain::{{entity_singular}}>(
        ctx, query,
        [](const auto& entities) { return {{entity_singular}}_mapper::map(entities); },
        lg(), "Reading {{entity_plural_words}} at timepoint.");
}

std::vector<domain::{{entity_singular}}>
{{entity_singular}}_repository::read_at_timepoint(
    context ctx, const std::string& as_of, const std::string& {{primary_key.column}}) {
    BOOST_LOG_SEV(lg(), debug) << "Reading {{entity_singular_words}} at timepoint. {{primary_key.column}}: " << {{primary_key.column}};
    const auto ts = make_timestamp(as_of, lg());
{{#read_tenant_filtered}}
    const auto tid = ctx.tenant_id().to_string();
{{#has_workspace_id}}
    const auto wid = ctx.workspace_id();
    const auto query = sqlgen::read<std::vector<{{entity_singular}}_entity>> |
        where("tenant_id"_c == tid && "workspace_id"_c == wid && "{{primary_key.column}}"_c == {{primary_key.column}} &&
              "valid_from"_c <= ts.value() && "valid_to"_c > ts.value());
{{/has_workspace_id}}
{{^has_workspace_id}}
    const auto query = sqlgen::read<std::vector<{{entity_singular}}_entity>> |
        where("tenant_id"_c == tid && "{{primary_key.column}}"_c == {{primary_key.column}} &&
              "valid_from"_c <= ts.value() && "valid_to"_c > ts.value());
{{/has_workspace_id}}
{{/read_tenant_filtered}}
{{^read_tenant_filtered}}
{{#has_workspace_id}}
    const auto wid = ctx.workspace_id();
    const auto query = sqlgen::read<std::vector<{{entity_singular}}_entity>> |
        where("workspace_id"_c == wid && "{{primary_key.column}}"_c == {{primary_key.column}} &&
              "valid_from"_c <= ts.value() && "valid_to"_c > ts.value());
{{/has_workspace_id}}
{{^has_workspace_id}}
    const auto query = sqlgen::read<std::vector<{{entity_singular}}_entity>> |
        where("{{primary_key.column}}"_c == {{primary_key.column}} &&
              "valid_from"_c <= ts.value() && "valid_to"_c > ts.value());
{{/has_workspace_id}}
{{/read_tenant_filtered}}

    return execute_read_query<{{entity_singular}}_entity, domain::{{entity_singular}}>(
        ctx, query,
        [](const auto& entities) { return {{entity_singular}}_mapper::map(entities); },
        lg(), "Reading {{entity_singular_words}} at timepoint by {{primary_key.column}}.");
}
{{/has_as_of_lookup}}

std::vector<domain::{{entity_singular}}>
{{entity_singular}}_repository::read_all(context ctx, {{{primary_key.params}}}) {
    BOOST_LOG_SEV(lg(), debug) << "Reading all {{entity_singular_words}} versions. " << {{{primary_key.log_fields}}};
{{#read_tenant_filtered}}
    const auto tid = ctx.tenant_id().to_string();
{{#has_workspace_id}}
    const auto wid = ctx.workspace_id();
    const auto query = sqlgen::read<std::vector<{{entity_singular}}_entity>> |
        where("tenant_id"_c == tid && "workspace_id"_c == wid && {{{primary_key.where_and}}}) |
{{#has_audit_columns}}
        order_by("version"_c.desc(), "valid_from"_c.desc());
{{/has_audit_columns}}
{{^has_audit_columns}}
        order_by("valid_from"_c.desc());
{{/has_audit_columns}}
{{/has_workspace_id}}
{{^has_workspace_id}}
    const auto query = sqlgen::read<std::vector<{{entity_singular}}_entity>> |
        where("tenant_id"_c == tid && {{{primary_key.where_and}}}) |
{{#has_audit_columns}}
        order_by("version"_c.desc(), "valid_from"_c.desc());
{{/has_audit_columns}}
{{^has_audit_columns}}
        order_by("valid_from"_c.desc());
{{/has_audit_columns}}
{{/has_workspace_id}}
{{/read_tenant_filtered}}
{{^read_tenant_filtered}}
{{#has_workspace_id}}
    const auto wid = ctx.workspace_id();
    const auto query = sqlgen::read<std::vector<{{entity_singular}}_entity>> |
        where("workspace_id"_c == wid && {{{primary_key.where_and}}}) |
{{#has_audit_columns}}
        order_by("version"_c.desc(), "valid_from"_c.desc());
{{/has_audit_columns}}
{{^has_audit_columns}}
        order_by("valid_from"_c.desc());
{{/has_audit_columns}}
{{/has_workspace_id}}
{{^has_workspace_id}}
    const auto query = sqlgen::read<std::vector<{{entity_singular}}_entity>> |
        where({{{primary_key.where_and}}}) |
{{#has_audit_columns}}
        order_by("version"_c.desc(), "valid_from"_c.desc());
{{/has_audit_columns}}
{{^has_audit_columns}}
        order_by("valid_from"_c.desc());
{{/has_audit_columns}}
{{/has_workspace_id}}
{{/read_tenant_filtered}}

    return execute_read_query<{{entity_singular}}_entity, domain::{{entity_singular}}>(
        ctx, query,
        [](const auto& entities) { return {{entity_singular}}_mapper::map(entities); },
        lg(), "Reading all {{entity_singular_words}} versions by {{primary_key.column}}.");
}

{{#has_audit_columns}}
std::optional<domain::{{entity_singular}}>
{{entity_singular}}_repository::read_at_version(context ctx, {{{primary_key.params}}},
                                                std::uint32_t version) {
    BOOST_LOG_SEV(lg(), debug) << "Reading {{entity_singular_words}} at version. " << {{{primary_key.log_fields}}}
                               << " version: " << version;
{{#read_tenant_filtered}}
    const auto tid = ctx.tenant_id().to_string();
{{#has_workspace_id}}
    const auto wid = ctx.workspace_id();
    const auto query = sqlgen::read<std::vector<{{entity_singular}}_entity>> |
        where("tenant_id"_c == tid && "workspace_id"_c == wid && {{{primary_key.where_and}}} && "version"_c == version) |
        sqlgen::limit(1);
{{/has_workspace_id}}
{{^has_workspace_id}}
    const auto query = sqlgen::read<std::vector<{{entity_singular}}_entity>> |
        where("tenant_id"_c == tid && {{{primary_key.where_and}}} && "version"_c == version) |
        sqlgen::limit(1);
{{/has_workspace_id}}
{{/read_tenant_filtered}}
{{^read_tenant_filtered}}
{{#has_workspace_id}}
    const auto wid = ctx.workspace_id();
    const auto query = sqlgen::read<std::vector<{{entity_singular}}_entity>> |
        where("workspace_id"_c == wid && {{{primary_key.where_and}}} && "version"_c == version) |
        sqlgen::limit(1);
{{/has_workspace_id}}
{{^has_workspace_id}}
    const auto query = sqlgen::read<std::vector<{{entity_singular}}_entity>> |
        where({{{primary_key.where_and}}} && "version"_c == version) |
        sqlgen::limit(1);
{{/has_workspace_id}}
{{/read_tenant_filtered}}

    const auto entities = execute_read_query<{{entity_singular}}_entity, domain::{{entity_singular}}>(
        ctx, query,
        [](const auto& entities) { return {{entity_singular}}_mapper::map(entities); },
        lg(), "Reading {{entity_singular_words}} at version.");

    if (entities.empty())
        return std::nullopt;
    return entities.front();
}
{{/has_audit_columns}}

{{#foreign_keys}}
{{#list_by}}
std::vector<domain::{{entity_singular}}>
{{entity_singular}}_repository::read_latest_by_{{column}}(context ctx, const std::string& {{column}},
                                                          std::uint32_t offset, std::uint32_t limit) {
    BOOST_LOG_SEV(lg(), debug) << "Reading latest {{entity_plural_words}}. {{column}}: " << {{column}}
                               << " offset: " << offset << " limit: " << limit;
    static const auto max(make_timestamp(MAX_TIMESTAMP, lg()));
{{#read_tenant_filtered}}
    const auto tid = ctx.tenant_id().to_string();
{{#has_workspace_id}}
    const auto wid = ctx.workspace_id();
    const auto query = sqlgen::read<std::vector<{{entity_singular}}_entity>> |
        where("tenant_id"_c == tid && "workspace_id"_c == wid && "{{column}}"_c == {{column}} && "valid_to"_c == max.value()) |
        order_by("{{list_by_order_column}}"_c{{#list_by_order_desc}}.desc(){{/list_by_order_desc}}) | sqlgen::offset(offset) | sqlgen::limit(limit);
{{/has_workspace_id}}
{{^has_workspace_id}}
    const auto query = sqlgen::read<std::vector<{{entity_singular}}_entity>> |
        where("tenant_id"_c == tid && "{{column}}"_c == {{column}} && "valid_to"_c == max.value()) |
        order_by("{{list_by_order_column}}"_c{{#list_by_order_desc}}.desc(){{/list_by_order_desc}}) | sqlgen::offset(offset) | sqlgen::limit(limit);
{{/has_workspace_id}}
{{/read_tenant_filtered}}
{{^read_tenant_filtered}}
{{#has_workspace_id}}
    const auto wid = ctx.workspace_id();
    const auto query = sqlgen::read<std::vector<{{entity_singular}}_entity>> |
        where("workspace_id"_c == wid && "{{column}}"_c == {{column}} && "valid_to"_c == max.value()) |
        order_by("{{list_by_order_column}}"_c{{#list_by_order_desc}}.desc(){{/list_by_order_desc}}) | sqlgen::offset(offset) | sqlgen::limit(limit);
{{/has_workspace_id}}
{{^has_workspace_id}}
    const auto query = sqlgen::read<std::vector<{{entity_singular}}_entity>> |
        where("{{column}}"_c == {{column}} && "valid_to"_c == max.value()) |
        order_by("{{list_by_order_column}}"_c{{#list_by_order_desc}}.desc(){{/list_by_order_desc}}) | sqlgen::offset(offset) | sqlgen::limit(limit);
{{/has_workspace_id}}
{{/read_tenant_filtered}}

    return execute_read_query<{{entity_singular}}_entity, domain::{{entity_singular}}>(
        ctx, query,
        [](const auto& entities) { return {{entity_singular}}_mapper::map(entities); },
        lg(), "Reading latest {{entity_plural_words}} by {{column}}.");
}

std::uint32_t {{entity_singular}}_repository::get_total_{{entity_singular_short}}_count_by_{{column}}(
    context ctx, const std::string& {{column}}) {
    BOOST_LOG_SEV(lg(), debug) << "Retrieving total active {{entity_plural_words}} count. {{column}}: " << {{column}};
    static const auto max(make_timestamp(MAX_TIMESTAMP, lg()));

    struct count_result { long long count; };

{{#read_tenant_filtered}}
    const auto tid = ctx.tenant_id().to_string();
{{#has_workspace_id}}
    const auto wid = ctx.workspace_id();
    const auto query = sqlgen::select_from<{{entity_singular}}_entity>(sqlgen::count().as<"count">()) |
                       where("tenant_id"_c == tid && "workspace_id"_c == wid && "{{column}}"_c == {{column}} && "valid_to"_c == max.value()) |
                       sqlgen::to<count_result>;
{{/has_workspace_id}}
{{^has_workspace_id}}
    const auto query = sqlgen::select_from<{{entity_singular}}_entity>(sqlgen::count().as<"count">()) |
                       where("tenant_id"_c == tid && "{{column}}"_c == {{column}} && "valid_to"_c == max.value()) |
                       sqlgen::to<count_result>;
{{/has_workspace_id}}
{{/read_tenant_filtered}}
{{^read_tenant_filtered}}
{{#has_workspace_id}}
    const auto wid = ctx.workspace_id();
    const auto query = sqlgen::select_from<{{entity_singular}}_entity>(sqlgen::count().as<"count">()) |
                       where("workspace_id"_c == wid && "{{column}}"_c == {{column}} && "valid_to"_c == max.value()) |
                       sqlgen::to<count_result>;
{{/has_workspace_id}}
{{^has_workspace_id}}
    const auto query = sqlgen::select_from<{{entity_singular}}_entity>(sqlgen::count().as<"count">()) |
                       where("{{column}}"_c == {{column}} && "valid_to"_c == max.value()) |
                       sqlgen::to<count_result>;
{{/has_workspace_id}}
{{/read_tenant_filtered}}

    const auto r = sqlgen::session(ctx.connection_pool()).and_then(query);
    ensure_success(r, lg());

    const auto count = static_cast<std::uint32_t>(r->count);
    BOOST_LOG_SEV(lg(), debug) << "Total active {{entity_plural_words}} count by {{column}}: " << count;
    return count;
}
{{/list_by}}

{{#list_by_as_of}}
std::vector<domain::{{entity_singular}}>
{{entity_singular}}_repository::read_by_{{column}}_as_of(
    context ctx, const std::string& {{column}},
    std::chrono::system_clock::time_point valid_from_bound,
    std::chrono::system_clock::time_point valid_to_bound) {
    BOOST_LOG_SEV(lg(), debug) << "Reading {{entity_plural_words}} as of window. {{column}}: " << {{column}};

    const auto vf(make_timestamp(ores::platform::time::datetime::to_db_string(valid_from_bound), lg()));
    const auto vt(make_timestamp(ores::platform::time::datetime::to_db_string(valid_to_bound), lg()));
{{#read_tenant_filtered}}
    const auto tid = ctx.tenant_id().to_string();
    const auto query = sqlgen::read<std::vector<{{entity_singular}}_entity>> |
        where("tenant_id"_c == tid && "{{column}}"_c == {{column}} &&
              "valid_from"_c < vt.value() && "valid_to"_c > vf.value()) |
        order_by({{{primary_key.order_by_cols}}});
{{/read_tenant_filtered}}
{{^read_tenant_filtered}}
    const auto query = sqlgen::read<std::vector<{{entity_singular}}_entity>> |
        where("{{column}}"_c == {{column}} &&
              "valid_from"_c < vt.value() && "valid_to"_c > vf.value()) |
        order_by({{{primary_key.order_by_cols}}});
{{/read_tenant_filtered}}

    return execute_read_query<{{entity_singular}}_entity, domain::{{entity_singular}}>(
        ctx, query,
        [](const auto& entities) { return {{entity_singular}}_mapper::map(entities); },
        lg(), "Reading {{entity_plural_words}} as of window by {{column}}.");
}
{{/list_by_as_of}}
{{/foreign_keys}}
void {{entity_singular}}_repository::remove(context ctx, {{{primary_key.params}}}) {
    BOOST_LOG_SEV(lg(), debug) << "Removing {{entity_singular_words}}. " << {{{primary_key.log_fields}}};
    static const auto max(make_timestamp(MAX_TIMESTAMP, lg()));
{{#has_tenant_id}}
    const auto tid = ctx.tenant_id().to_string();
{{#has_workspace_id}}
    const auto wid = ctx.workspace_id();
    const auto query = sqlgen::delete_from<{{entity_singular}}_entity> |
        where("tenant_id"_c == tid && "workspace_id"_c == wid && {{{primary_key.where_and}}} && "valid_to"_c == max.value());
{{/has_workspace_id}}
{{^has_workspace_id}}
    const auto query = sqlgen::delete_from<{{entity_singular}}_entity> |
        where("tenant_id"_c == tid && {{{primary_key.where_and}}} && "valid_to"_c == max.value());
{{/has_workspace_id}}
{{/has_tenant_id}}
{{^has_tenant_id}}
{{#has_workspace_id}}
    const auto wid = ctx.workspace_id();
    const auto query = sqlgen::delete_from<{{entity_singular}}_entity> |
        where("workspace_id"_c == wid && {{{primary_key.where_and}}} && "valid_to"_c == max.value());
{{/has_workspace_id}}
{{^has_workspace_id}}
    const auto query = sqlgen::delete_from<{{entity_singular}}_entity> |
        where({{{primary_key.where_and}}} && "valid_to"_c == max.value());
{{/has_workspace_id}}
{{/has_tenant_id}}

    execute_delete_query(ctx, query, lg(), "Removing {{entity_singular_words}} from database.");
}

std::vector<domain::{{entity_singular}}>
{{entity_singular}}_repository::read_latest(context ctx, std::uint32_t offset, std::uint32_t limit) {
    BOOST_LOG_SEV(lg(), debug) << "Reading latest {{entity_plural_words}} with offset: " << offset
                               << " and limit: " << limit;
    static const auto max(make_timestamp(MAX_TIMESTAMP, lg()));
{{#read_tenant_filtered}}
    const auto tid = ctx.tenant_id().to_string();
{{#has_workspace_id}}
    const auto wid = ctx.workspace_id();
    const auto query = sqlgen::read<std::vector<{{entity_singular}}_entity>> |
                       where("tenant_id"_c == tid && "workspace_id"_c == wid && "valid_to"_c == max.value()) |
                       order_by({{{primary_key.order_by_cols}}}) |
                       sqlgen::offset(offset) | sqlgen::limit(limit);
{{/has_workspace_id}}
{{^has_workspace_id}}
    const auto query = sqlgen::read<std::vector<{{entity_singular}}_entity>> |
                       where("tenant_id"_c == tid && "valid_to"_c == max.value()) |
                       order_by({{{primary_key.order_by_cols}}}) |
                       sqlgen::offset(offset) | sqlgen::limit(limit);
{{/has_workspace_id}}
{{/read_tenant_filtered}}
{{^read_tenant_filtered}}
{{#has_workspace_id}}
    const auto wid = ctx.workspace_id();
    const auto query = sqlgen::read<std::vector<{{entity_singular}}_entity>> |
                       where("workspace_id"_c == wid && "valid_to"_c == max.value()) |
                       order_by({{{primary_key.order_by_cols}}}) |
                       sqlgen::offset(offset) | sqlgen::limit(limit);
{{/has_workspace_id}}
{{^has_workspace_id}}
    const auto query = sqlgen::read<std::vector<{{entity_singular}}_entity>> |
                       where("valid_to"_c == max.value()) |
                       order_by({{{primary_key.order_by_cols}}}) |
                       sqlgen::offset(offset) | sqlgen::limit(limit);
{{/has_workspace_id}}
{{/read_tenant_filtered}}

    return execute_read_query<{{entity_singular}}_entity, domain::{{entity_singular}}>(
        ctx, query,
        [](const auto& entities) { return {{entity_singular}}_mapper::map(entities); },
        lg(), "Reading latest {{entity_plural_words}} with pagination.");
}

std::uint32_t {{entity_singular}}_repository::get_total_{{entity_singular_short}}_count(context ctx) {
    BOOST_LOG_SEV(lg(), debug) << "Retrieving total active {{entity_singular_words}} count";
    static const auto max(make_timestamp(MAX_TIMESTAMP, lg()));

    struct count_result { long long count; };

{{#read_tenant_filtered}}
    const auto tid = ctx.tenant_id().to_string();
{{#has_workspace_id}}
    const auto wid = ctx.workspace_id();
    const auto query = sqlgen::select_from<{{entity_singular}}_entity>(sqlgen::count().as<"count">()) |
                       where("tenant_id"_c == tid && "workspace_id"_c == wid && "valid_to"_c == max.value()) |
                       sqlgen::to<count_result>;
{{/has_workspace_id}}
{{^has_workspace_id}}
    const auto query = sqlgen::select_from<{{entity_singular}}_entity>(sqlgen::count().as<"count">()) |
                       where("tenant_id"_c == tid && "valid_to"_c == max.value()) |
                       sqlgen::to<count_result>;
{{/has_workspace_id}}
{{/read_tenant_filtered}}
{{^read_tenant_filtered}}
{{#has_workspace_id}}
    const auto wid = ctx.workspace_id();
    const auto query = sqlgen::select_from<{{entity_singular}}_entity>(sqlgen::count().as<"count">()) |
                       where("workspace_id"_c == wid && "valid_to"_c == max.value()) |
                       sqlgen::to<count_result>;
{{/has_workspace_id}}
{{^has_workspace_id}}
    const auto query = sqlgen::select_from<{{entity_singular}}_entity>(sqlgen::count().as<"count">()) |
                       where("valid_to"_c == max.value()) |
                       sqlgen::to<count_result>;
{{/has_workspace_id}}
{{/read_tenant_filtered}}

    const auto r = sqlgen::session(ctx.connection_pool()).and_then(query);
    ensure_success(r, lg());

    const auto count = static_cast<std::uint32_t>(r->count);
    BOOST_LOG_SEV(lg(), debug) << "Total active {{entity_singular_words}} count: " << count;
    return count;
}

{{#has_batch_read}}
std::vector<domain::{{entity_singular}}>
{{entity_singular}}_repository::read_latest(
    context ctx, {{{primary_key.batch_params}}}) {
    if ({{{primary_key.batch_empty_check}}}) return {};
    static const auto max(make_timestamp(MAX_TIMESTAMP, lg()));
{{#read_tenant_filtered}}
    const auto tid = ctx.tenant_id().to_string();
{{#has_workspace_id}}
    const auto wid = ctx.workspace_id();
    const auto query = sqlgen::read<std::vector<{{entity_singular}}_entity>> |
        where("tenant_id"_c == tid && "workspace_id"_c == wid
              && {{{primary_key.batch_in_and}}}
              && "valid_to"_c == max.value());
{{/has_workspace_id}}
{{^has_workspace_id}}
    const auto query = sqlgen::read<std::vector<{{entity_singular}}_entity>> |
        where("tenant_id"_c == tid && {{{primary_key.batch_in_and}}}
              && "valid_to"_c == max.value());
{{/has_workspace_id}}
{{/read_tenant_filtered}}
{{^read_tenant_filtered}}
{{#has_workspace_id}}
    const auto wid = ctx.workspace_id();
    const auto query = sqlgen::read<std::vector<{{entity_singular}}_entity>> |
        where("workspace_id"_c == wid && {{{primary_key.batch_in_and}}}
              && "valid_to"_c == max.value());
{{/has_workspace_id}}
{{^has_workspace_id}}
    const auto query = sqlgen::read<std::vector<{{entity_singular}}_entity>> |
        where({{{primary_key.batch_in_and}}} && "valid_to"_c == max.value());
{{/has_workspace_id}}
{{/read_tenant_filtered}}
    auto result = execute_read_query<{{entity_singular}}_entity, domain::{{entity_singular}}>(
        ctx, query,
        [](const auto& entities) { return {{entity_singular}}_mapper::map(entities); },
        lg(), "Reading latest {{entity_plural_words}} by ids.");
{{#primary_key.is_compound}}
    // Compound key: the query above is a per-column .in() cross-product
    // over-fetch (sqlgen has no tuple/composite IN), so filter down to the
    // exact requested key-tuples here.
    if ({{{primary_key.batch_size_mismatch_check}}})
        throw std::invalid_argument(
            "{{entity_singular}}_repository::read_latest: key column vectors must be the same length");
    std::set<std::tuple<{{{primary_key.batch_tuple_type}}}>> requested;
    for (std::size_t i = 0; i < {{primary_key.batch_requested_size_check}}; ++i)
        {{{primary_key.batch_requested_insert}}}
    std::vector<domain::{{entity_singular}}> filtered;
    filtered.reserve(result.size());
    for (auto& item : result) {
        if (requested.contains(std::make_tuple({{{primary_key.batch_tuple_from_item}}})))
            filtered.push_back(std::move(item));
    }
    return filtered;
{{/primary_key.is_compound}}
{{^primary_key.is_compound}}
    return result;
{{/primary_key.is_compound}}
}

{{/has_batch_read}}
void {{entity_singular}}_repository::remove(
    context ctx, {{{primary_key.batch_params}}}) {
{{#primary_key.is_compound}}
    // Compound key: a per-column .in() DELETE would be a cross-product
    // over-delete (rows outside the requested tuples), and a DELETE can't
    // be filtered after the fact like a read -- remove one tuple at a time.
    if ({{{primary_key.batch_size_mismatch_check}}})
        throw std::invalid_argument(
            "{{entity_singular}}_repository::remove: key column vectors must be the same length");
    for (std::size_t i = 0; i < {{primary_key.batch_requested_size_check}}; ++i)
        remove(ctx, {{{primary_key.batch_loop_args}}});
{{/primary_key.is_compound}}
{{^primary_key.is_compound}}
    static const auto max(make_timestamp(MAX_TIMESTAMP, lg()));
{{#has_tenant_id}}
    const auto tid = ctx.tenant_id().to_string();
{{#has_workspace_id}}
    const auto wid = ctx.workspace_id();
    const auto query = sqlgen::delete_from<{{entity_singular}}_entity> |
        where("tenant_id"_c == tid && "workspace_id"_c == wid && {{{primary_key.batch_in_and}}}
              && "valid_to"_c == max.value());
{{/has_workspace_id}}
{{^has_workspace_id}}
    const auto query = sqlgen::delete_from<{{entity_singular}}_entity> |
        where("tenant_id"_c == tid && {{{primary_key.batch_in_and}}}
              && "valid_to"_c == max.value());
{{/has_workspace_id}}
{{/has_tenant_id}}
{{^has_tenant_id}}
{{#has_workspace_id}}
    const auto wid = ctx.workspace_id();
    const auto query = sqlgen::delete_from<{{entity_singular}}_entity> |
        where("workspace_id"_c == wid && {{{primary_key.batch_in_and}}} && "valid_to"_c == max.value());
{{/has_workspace_id}}
{{^has_workspace_id}}
    const auto query = sqlgen::delete_from<{{entity_singular}}_entity> |
        where({{{primary_key.batch_in_and}}} && "valid_to"_c == max.value());
{{/has_workspace_id}}
{{/has_tenant_id}}
    execute_delete_query(ctx, query, lg(), "Batch removing {{entity_plural_words}}.");
{{/primary_key.is_compound}}
}

{{#has_parent_id}}
std::vector<ores::utility::domain::hierarchy_flat_row>
{{entity_singular}}_repository::get_hierarchy(context ctx,
                                       const boost::uuids::uuid& root_id,
                                       bool from_root) {
    BOOST_LOG_SEV(lg(), debug) << "Reading {{entity_singular}} hierarchy. Root: " << root_id
                               << " from_root: " << from_root;

    const auto tenant_str = boost::uuids::to_string(ctx.tenant_id().to_uuid());
    const auto root_str = boost::uuids::to_string(root_id);
    const std::string sql = "SELECT * FROM {{sql_name_base}}_hierarchy_fn('" +
                            tenant_str + "'::uuid, '" + root_str + "'::uuid, " +
                            (from_root ? "true" : "false") + ")";

    const auto rows =
        execute_raw_multi_column_query(ctx, sql, lg(), "Reading {{entity_singular}} hierarchy");

    std::vector<ores::utility::domain::hierarchy_flat_row> result;
    result.reserve(rows.size());
    for (const auto& row : rows) {
        if (row.size() >= 3 && row[0]) {
            ores::utility::domain::hierarchy_flat_row r;
            r.id = boost::lexical_cast<boost::uuids::uuid>(*row[0]);
            if (row[1])
                r.parent_id = boost::lexical_cast<boost::uuids::uuid>(*row[1]);
            if (row[2])
                r.name = *row[2];
            result.push_back(std::move(r));
        }
    }

    BOOST_LOG_SEV(lg(), debug) << "Read " << result.size() << " {{entity_singular}} hierarchy rows.";
    return result;
}

{{/has_parent_id}}

<<paste:F2EB1914-5E94-42CA-9C77-46BDB364BF9E>>

}
{{/domain_entity}}
{{#junction}}
#include "ores.{{component_core}}/repository/{{name_singular}}_repository.hpp"

#include <sqlgen/postgres.hpp>
{{#has_uuid_left_or_right}}
#include <boost/uuid/uuid_io.hpp>
{{/has_uuid_left_or_right}}
#include "ores.database/repository/helpers.hpp"
#include "ores.database/repository/bitemporal_operations.hpp"
#include "ores.{{component_include}}/domain/{{name_singular}}_json_io.hpp" // IWYU pragma: keep.
#include "ores.{{component_core}}/repository/{{name_singular}}_entity.hpp"
#include "ores.{{component_core}}/repository/{{name_singular}}_mapper.hpp"

namespace ores::{{component}}::repository {

using namespace sqlgen;
using namespace sqlgen::literals;
using namespace ores::logging;
using namespace ores::database::repository;

std::string {{name_singular}}_repository::sql() {
    return generate_create_table_sql<{{name_singular}}_entity>(lg());
}

{{name_singular}}_repository::{{name_singular}}_repository(context ctx)
    : ctx_(std::move(ctx)) {}

{{^read_only}}
void {{name_singular}}_repository::write(
    const domain::{{name_singular}}& {{name_singular_short}}) {
    BOOST_LOG_SEV(lg(), debug) << "Writing {{name_singular_words}} to database: "
                               << {{name_singular_short}}.{{left.column}} << "/" << {{name_singular_short}}.{{right.column}};
    execute_write_query(ctx_, {{name_singular}}_mapper::map({{name_singular_short}}),
        lg(), "writing {{name_singular_words}} to database");
}

void {{name_singular}}_repository::write(
    const std::vector<domain::{{name_singular}}>& {{name_short}}) {
    BOOST_LOG_SEV(lg(), debug) << "Writing {{name_words}} to database. Count: "
                               << {{name_short}}.size();
    execute_write_query(ctx_, {{name_singular}}_mapper::map({{name_short}}),
        lg(), "writing {{name_words}} to database");
}

{{/read_only}}
std::vector<domain::{{name_singular}}>
{{name_singular}}_repository::read_latest() {
    static const auto max(make_timestamp(MAX_TIMESTAMP, lg()));
{{#has_tenant_id}}
    const auto tid = ctx_.tenant_id().to_string();
    const auto query = sqlgen::read<std::vector<{{name_singular}}_entity>> |
        where("tenant_id"_c == tid && "valid_to"_c == max.value()) |
        order_by("{{left.column}}"_c, "{{order_column}}"_c);
{{/has_tenant_id}}
{{^has_tenant_id}}
    const auto query = sqlgen::read<std::vector<{{name_singular}}_entity>> |
        where("valid_to"_c == max.value()) |
        order_by("{{left.column}}"_c, "{{order_column}}"_c);
{{/has_tenant_id}}

    return execute_read_query<{{name_singular}}_entity, domain::{{name_singular}}>(
        ctx_, query,
        [](const auto& entities) { return {{name_singular}}_mapper::map(entities); },
        lg(), "Reading latest {{name_words}}");
}

std::vector<domain::{{name_singular}}>
{{name_singular}}_repository::read_latest_by_{{left.column_short}}(
{{#left.is_uuid}}
    const boost::uuids::uuid& {{left.column}}) {
{{/left.is_uuid}}
{{^left.is_uuid}}
    const std::string& {{left.column}}) {
{{/left.is_uuid}}
    BOOST_LOG_SEV(lg(), debug) << "Reading latest {{name_words}}. {{left.column_title}}: "
                               << {{left.column}};

    static const auto max(make_timestamp(MAX_TIMESTAMP, lg()));
{{#left.is_uuid}}
    const auto {{left.column}}_str = boost::uuids::to_string({{left.column}});
{{#has_tenant_id}}
    const auto tid = ctx_.tenant_id().to_string();
    const auto query = sqlgen::read<std::vector<{{name_singular}}_entity>> |
        where("tenant_id"_c == tid && "{{left.column}}"_c == {{left.column}}_str && "valid_to"_c == max.value()) |
{{/has_tenant_id}}
{{^has_tenant_id}}
    const auto query = sqlgen::read<std::vector<{{name_singular}}_entity>> |
        where("{{left.column}}"_c == {{left.column}}_str && "valid_to"_c == max.value()) |
{{/has_tenant_id}}
{{/left.is_uuid}}
{{^left.is_uuid}}
{{#has_tenant_id}}
    const auto tid = ctx_.tenant_id().to_string();
    const auto query = sqlgen::read<std::vector<{{name_singular}}_entity>> |
        where("tenant_id"_c == tid && "{{left.column}}"_c == {{left.column}} && "valid_to"_c == max.value()) |
{{/has_tenant_id}}
{{^has_tenant_id}}
    const auto query = sqlgen::read<std::vector<{{name_singular}}_entity>> |
        where("{{left.column}}"_c == {{left.column}} && "valid_to"_c == max.value()) |
{{/has_tenant_id}}
{{/left.is_uuid}}
        order_by("{{order_column}}"_c);

    return execute_read_query<{{name_singular}}_entity, domain::{{name_singular}}>(
        ctx_, query,
        [](const auto& entities) { return {{name_singular}}_mapper::map(entities); },
        lg(), "Reading latest {{name_words}} by {{left.column_short}}.");
}

std::vector<domain::{{name_singular}}>
{{name_singular}}_repository::read_latest_by_{{right.column_short}}(
{{#right.is_uuid}}
    const boost::uuids::uuid& {{right.column}}) {
{{/right.is_uuid}}
{{^right.is_uuid}}
    const std::string& {{right.column}}) {
{{/right.is_uuid}}
    BOOST_LOG_SEV(lg(), debug) << "Reading latest {{name_words}}. {{right.column_title}}: "
                               << {{right.column}};

    static const auto max(make_timestamp(MAX_TIMESTAMP, lg()));
{{#right.is_uuid}}
    const auto {{right.column}}_str = boost::uuids::to_string({{right.column}});
{{#has_tenant_id}}
    const auto tid = ctx_.tenant_id().to_string();
    const auto query = sqlgen::read<std::vector<{{name_singular}}_entity>> |
        where("tenant_id"_c == tid && "{{right.column}}"_c == {{right.column}}_str && "valid_to"_c == max.value()) |
{{/has_tenant_id}}
{{^has_tenant_id}}
    const auto query = sqlgen::read<std::vector<{{name_singular}}_entity>> |
        where("{{right.column}}"_c == {{right.column}}_str && "valid_to"_c == max.value()) |
{{/has_tenant_id}}
{{/right.is_uuid}}
{{^right.is_uuid}}
{{#has_tenant_id}}
    const auto tid = ctx_.tenant_id().to_string();
    const auto query = sqlgen::read<std::vector<{{name_singular}}_entity>> |
        where("tenant_id"_c == tid && "{{right.column}}"_c == {{right.column}} && "valid_to"_c == max.value()) |
{{/has_tenant_id}}
{{^has_tenant_id}}
    const auto query = sqlgen::read<std::vector<{{name_singular}}_entity>> |
        where("{{right.column}}"_c == {{right.column}} && "valid_to"_c == max.value()) |
{{/has_tenant_id}}
{{/right.is_uuid}}
        order_by("{{left.column}}"_c);

    return execute_read_query<{{name_singular}}_entity, domain::{{name_singular}}>(
        ctx_, query,
        [](const auto& entities) { return {{name_singular}}_mapper::map(entities); },
        lg(), "Reading latest {{name_words}} by {{right.column_short}}.");
}

{{#left.list_by}}
std::vector<domain::{{name_singular}}>
{{name_singular}}_repository::read_latest_by_{{left.column_short}}(
{{#left.is_uuid}}
    const boost::uuids::uuid& {{left.column}}, std::uint32_t offset, std::uint32_t limit) {
    const auto {{left.column}}_str = boost::uuids::to_string({{left.column}});
{{/left.is_uuid}}
{{^left.is_uuid}}
    const std::string& {{left.column}}, std::uint32_t offset, std::uint32_t limit) {
{{/left.is_uuid}}
    BOOST_LOG_SEV(lg(), debug) << "Reading latest {{name_words}}. {{left.column_title}}: "
                               << {{left.column}} << " offset: " << offset << " limit: " << limit;

    static const auto max(make_timestamp(MAX_TIMESTAMP, lg()));
{{#has_tenant_id}}
    const auto tid = ctx_.tenant_id().to_string();
    const auto query = sqlgen::read<std::vector<{{name_singular}}_entity>> |
{{#left.is_uuid}}
        where("tenant_id"_c == tid && "{{left.column}}"_c == {{left.column}}_str && "valid_to"_c == max.value()) |
{{/left.is_uuid}}
{{^left.is_uuid}}
        where("tenant_id"_c == tid && "{{left.column}}"_c == {{left.column}} && "valid_to"_c == max.value()) |
{{/left.is_uuid}}
        order_by("{{order_column}}"_c) | sqlgen::offset(offset) | sqlgen::limit(limit);
{{/has_tenant_id}}
{{^has_tenant_id}}
    const auto query = sqlgen::read<std::vector<{{name_singular}}_entity>> |
{{#left.is_uuid}}
        where("{{left.column}}"_c == {{left.column}}_str && "valid_to"_c == max.value()) |
{{/left.is_uuid}}
{{^left.is_uuid}}
        where("{{left.column}}"_c == {{left.column}} && "valid_to"_c == max.value()) |
{{/left.is_uuid}}
        order_by("{{order_column}}"_c) | sqlgen::offset(offset) | sqlgen::limit(limit);
{{/has_tenant_id}}

    return execute_read_query<{{name_singular}}_entity, domain::{{name_singular}}>(
        ctx_, query,
        [](const auto& entities) { return {{name_singular}}_mapper::map(entities); },
        lg(), "Reading latest {{name_words}} by {{left.column_short}} (paginated).");
}

std::uint32_t {{name_singular}}_repository::get_total_{{name_singular_short}}_count_by_{{left.column_short}}(
{{#left.is_uuid}}
    const boost::uuids::uuid& {{left.column}}) {
    const auto {{left.column}}_str = boost::uuids::to_string({{left.column}});
{{/left.is_uuid}}
{{^left.is_uuid}}
    const std::string& {{left.column}}) {
{{/left.is_uuid}}
    BOOST_LOG_SEV(lg(), debug) << "Retrieving total active {{name_words}} count. {{left.column_title}}: " << {{left.column}};
    static const auto max(make_timestamp(MAX_TIMESTAMP, lg()));

    struct count_result { long long count; };

{{#has_tenant_id}}
    const auto tid = ctx_.tenant_id().to_string();
    const auto query = sqlgen::select_from<{{name_singular}}_entity>(sqlgen::count().as<"count">()) |
{{#left.is_uuid}}
                       where("tenant_id"_c == tid && "{{left.column}}"_c == {{left.column}}_str && "valid_to"_c == max.value()) |
{{/left.is_uuid}}
{{^left.is_uuid}}
                       where("tenant_id"_c == tid && "{{left.column}}"_c == {{left.column}} && "valid_to"_c == max.value()) |
{{/left.is_uuid}}
                       sqlgen::to<count_result>;
{{/has_tenant_id}}
{{^has_tenant_id}}
    const auto query = sqlgen::select_from<{{name_singular}}_entity>(sqlgen::count().as<"count">()) |
{{#left.is_uuid}}
                       where("{{left.column}}"_c == {{left.column}}_str && "valid_to"_c == max.value()) |
{{/left.is_uuid}}
{{^left.is_uuid}}
                       where("{{left.column}}"_c == {{left.column}} && "valid_to"_c == max.value()) |
{{/left.is_uuid}}
                       sqlgen::to<count_result>;
{{/has_tenant_id}}

    const auto r = sqlgen::session(ctx_.connection_pool()).and_then(query);
    ensure_success(r, lg());

    const auto count = static_cast<std::uint32_t>(r->count);
    BOOST_LOG_SEV(lg(), debug) << "Total active {{name_words}} count by {{left.column_short}}: " << count;
    return count;
}

{{/left.list_by}}
{{#right.list_by}}
std::vector<domain::{{name_singular}}>
{{name_singular}}_repository::read_latest_by_{{right.column_short}}(
{{#right.is_uuid}}
    const boost::uuids::uuid& {{right.column}}, std::uint32_t offset, std::uint32_t limit) {
    const auto {{right.column}}_str = boost::uuids::to_string({{right.column}});
{{/right.is_uuid}}
{{^right.is_uuid}}
    const std::string& {{right.column}}, std::uint32_t offset, std::uint32_t limit) {
{{/right.is_uuid}}
    BOOST_LOG_SEV(lg(), debug) << "Reading latest {{name_words}}. {{right.column_title}}: "
                               << {{right.column}} << " offset: " << offset << " limit: " << limit;

    static const auto max(make_timestamp(MAX_TIMESTAMP, lg()));
{{#has_tenant_id}}
    const auto tid = ctx_.tenant_id().to_string();
    const auto query = sqlgen::read<std::vector<{{name_singular}}_entity>> |
{{#right.is_uuid}}
        where("tenant_id"_c == tid && "{{right.column}}"_c == {{right.column}}_str && "valid_to"_c == max.value()) |
{{/right.is_uuid}}
{{^right.is_uuid}}
        where("tenant_id"_c == tid && "{{right.column}}"_c == {{right.column}} && "valid_to"_c == max.value()) |
{{/right.is_uuid}}
        order_by("{{left.column}}"_c) | sqlgen::offset(offset) | sqlgen::limit(limit);
{{/has_tenant_id}}
{{^has_tenant_id}}
    const auto query = sqlgen::read<std::vector<{{name_singular}}_entity>> |
{{#right.is_uuid}}
        where("{{right.column}}"_c == {{right.column}}_str && "valid_to"_c == max.value()) |
{{/right.is_uuid}}
{{^right.is_uuid}}
        where("{{right.column}}"_c == {{right.column}} && "valid_to"_c == max.value()) |
{{/right.is_uuid}}
        order_by("{{left.column}}"_c) | sqlgen::offset(offset) | sqlgen::limit(limit);
{{/has_tenant_id}}

    return execute_read_query<{{name_singular}}_entity, domain::{{name_singular}}>(
        ctx_, query,
        [](const auto& entities) { return {{name_singular}}_mapper::map(entities); },
        lg(), "Reading latest {{name_words}} by {{right.column_short}} (paginated).");
}

std::uint32_t {{name_singular}}_repository::get_total_{{name_singular_short}}_count_by_{{right.column_short}}(
{{#right.is_uuid}}
    const boost::uuids::uuid& {{right.column}}) {
    const auto {{right.column}}_str = boost::uuids::to_string({{right.column}});
{{/right.is_uuid}}
{{^right.is_uuid}}
    const std::string& {{right.column}}) {
{{/right.is_uuid}}
    BOOST_LOG_SEV(lg(), debug) << "Retrieving total active {{name_words}} count. {{right.column_title}}: " << {{right.column}};
    static const auto max(make_timestamp(MAX_TIMESTAMP, lg()));

    struct count_result { long long count; };

{{#has_tenant_id}}
    const auto tid = ctx_.tenant_id().to_string();
    const auto query = sqlgen::select_from<{{name_singular}}_entity>(sqlgen::count().as<"count">()) |
{{#right.is_uuid}}
                       where("tenant_id"_c == tid && "{{right.column}}"_c == {{right.column}}_str && "valid_to"_c == max.value()) |
{{/right.is_uuid}}
{{^right.is_uuid}}
                       where("tenant_id"_c == tid && "{{right.column}}"_c == {{right.column}} && "valid_to"_c == max.value()) |
{{/right.is_uuid}}
                       sqlgen::to<count_result>;
{{/has_tenant_id}}
{{^has_tenant_id}}
    const auto query = sqlgen::select_from<{{name_singular}}_entity>(sqlgen::count().as<"count">()) |
{{#right.is_uuid}}
                       where("{{right.column}}"_c == {{right.column}}_str && "valid_to"_c == max.value()) |
{{/right.is_uuid}}
{{^right.is_uuid}}
                       where("{{right.column}}"_c == {{right.column}} && "valid_to"_c == max.value()) |
{{/right.is_uuid}}
                       sqlgen::to<count_result>;
{{/has_tenant_id}}

    const auto r = sqlgen::session(ctx_.connection_pool()).and_then(query);
    ensure_success(r, lg());

    const auto count = static_cast<std::uint32_t>(r->count);
    BOOST_LOG_SEV(lg(), debug) << "Total active {{name_words}} count by {{right.column_short}}: " << count;
    return count;
}

{{/right.list_by}}
{{^read_only}}
void {{name_singular}}_repository::remove(
{{#left.is_uuid}}
    const boost::uuids::uuid& {{left.column}},
{{/left.is_uuid}}
{{^left.is_uuid}}
    const std::string& {{left.column}},
{{/left.is_uuid}}
{{#right.is_uuid}}
    const boost::uuids::uuid& {{right.column}}) {
{{/right.is_uuid}}
{{^right.is_uuid}}
    const std::string& {{right.column}}) {
{{/right.is_uuid}}
    BOOST_LOG_SEV(lg(), debug) << "Removing {{name_singular_words}} from database: "
                               << {{left.column}} << "/" << {{right.column}};

{{#left.is_uuid}}
    const auto {{left.column}}_str = boost::uuids::to_string({{left.column}});
{{/left.is_uuid}}
{{#right.is_uuid}}
    const auto {{right.column}}_str = boost::uuids::to_string({{right.column}});
{{/right.is_uuid}}
{{#has_tenant_id}}
    const auto tid = ctx_.tenant_id().to_string();
{{/has_tenant_id}}
    const auto query = sqlgen::delete_from<{{name_singular}}_entity> |
{{#has_tenant_id}}
        where("tenant_id"_c == tid &&
{{#left.is_uuid}}
{{#right.is_uuid}}
              "{{left.column}}"_c == {{left.column}}_str &&
              "{{right.column}}"_c == {{right.column}}_str);
{{/right.is_uuid}}
{{^right.is_uuid}}
              "{{left.column}}"_c == {{left.column}}_str &&
              "{{right.column}}"_c == {{right.column}});
{{/right.is_uuid}}
{{/left.is_uuid}}
{{^left.is_uuid}}
{{#right.is_uuid}}
              "{{left.column}}"_c == {{left.column}} &&
              "{{right.column}}"_c == {{right.column}}_str);
{{/right.is_uuid}}
{{^right.is_uuid}}
              "{{left.column}}"_c == {{left.column}} &&
              "{{right.column}}"_c == {{right.column}});
{{/right.is_uuid}}
{{/left.is_uuid}}
{{/has_tenant_id}}
{{^has_tenant_id}}
{{#left.is_uuid}}
{{#right.is_uuid}}
        where("{{left.column}}"_c == {{left.column}}_str &&
              "{{right.column}}"_c == {{right.column}}_str);
{{/right.is_uuid}}
{{^right.is_uuid}}
        where("{{left.column}}"_c == {{left.column}}_str &&
              "{{right.column}}"_c == {{right.column}});
{{/right.is_uuid}}
{{/left.is_uuid}}
{{^left.is_uuid}}
{{#right.is_uuid}}
        where("{{left.column}}"_c == {{left.column}} &&
              "{{right.column}}"_c == {{right.column}}_str);
{{/right.is_uuid}}
{{^right.is_uuid}}
        where("{{left.column}}"_c == {{left.column}} &&
              "{{right.column}}"_c == {{right.column}});
{{/right.is_uuid}}
{{/left.is_uuid}}
{{/has_tenant_id}}

    execute_delete_query(ctx_, query, lg(),
        "removing {{name_singular_words}} from database");
}

void {{name_singular}}_repository::remove_by_{{left.column_short}}(
{{#left.is_uuid}}
    const boost::uuids::uuid& {{left.column}}) {
{{/left.is_uuid}}
{{^left.is_uuid}}
    const std::string& {{left.column}}) {
{{/left.is_uuid}}
    BOOST_LOG_SEV(lg(), debug) << "Removing all {{name_words}} from database: "
                               << {{left.column}};

{{#left.is_uuid}}
    const auto {{left.column}}_str = boost::uuids::to_string({{left.column}});
{{#has_tenant_id}}
    const auto tid = ctx_.tenant_id().to_string();
    const auto query = sqlgen::delete_from<{{name_singular}}_entity> |
        where("tenant_id"_c == tid && "{{left.column}}"_c == {{left.column}}_str);
{{/has_tenant_id}}
{{^has_tenant_id}}
    const auto query = sqlgen::delete_from<{{name_singular}}_entity> |
        where("{{left.column}}"_c == {{left.column}}_str);
{{/has_tenant_id}}
{{/left.is_uuid}}
{{^left.is_uuid}}
{{#has_tenant_id}}
    const auto tid = ctx_.tenant_id().to_string();
    const auto query = sqlgen::delete_from<{{name_singular}}_entity> |
        where("tenant_id"_c == tid && "{{left.column}}"_c == {{left.column}});
{{/has_tenant_id}}
{{^has_tenant_id}}
    const auto query = sqlgen::delete_from<{{name_singular}}_entity> |
        where("{{left.column}}"_c == {{left.column}});
{{/has_tenant_id}}
{{/left.is_uuid}}

    execute_delete_query(ctx_, query, lg(),
        "removing all {{name_words}} from database");
}

{{/read_only}}
}
{{/junction}}

See also

Emacs 29.3 (Org mode 9.6.15)