[proof-of-concept] compile time query language
От: Evgeny.Panasyuk Россия  
Дата: 11.07.16 18:15
Оценка: 165 (13) +1 -1 :)

CTQL — Compile Time Query Language

В рамках одной из КСВ тем сделал proof-of-concept встроенного языка запросов времени компиляции, а точнее одной конкретной формы выражения (примеров EDSL и так предостаточно).
Все этапы, включая генерацию текста SQL запроса, вынесены в compile-time.

Пример использования

#include "ctql.hpp"

#include <iostream>

DEFINE_TABLE
(
    foo,
    (int, id)
    (int, code)
    (double, value)
    (int, number)
);

int main()
{
    using namespace CTQL;

    for(auto &&x : to_vector(
                       from(foo)
                       .where(foo.id > 42_i)
                       .select(foo.value, foo.id, foo.number)
                   ))
    {
        using namespace std;
        cout << "ROW: " << x.id << " " << x.value << " " << x.number << endl;
        // cout << x.code; // - compile error, because .code is not in projection
    }
}

Вывод

База-Данных-заглушка для контроля выводит информацию о производимых операциях. В строчке execute виден итоговый текст запроса:
execute SELECT foo.value, foo.id, foo.number FROM foo WHERE foo.id>42 
read_count 
get<double> 0 
get<int> 1 
get<int> 2 
move_to_next 
get<double> 0 
get<int> 1 
get<int> 2 
move_to_next 
get<double> 0 
get<int> 1 
get<int> 2 
move_to_next 
ROW: 100 1 200
ROW: 110 1.01 210
ROW: 120 1.02 220

Live Demo

Примеры автокомплита





Сравнение с ручным вариантом

В ручном варианте (функция handwritten ниже) всё жёстко закодировано оптимальным образом: готовый текст запроса, готовый результирующий тип строки результата, запрос колонок результата посредством целочисленных индексов:
#include "ctql.hpp"

#include <iostream>

DEFINE_TABLE
(
    foo,
    (int, id)
    (int, code)
    (double, value)
    (int, number)
);

NOINLINE auto handwritten()
{
    using namespace CTQL;

    struct Row
    {
        double value;
        int id;
        int number;
    } row;
    vector_noinline<Row> xs;

    dummy_db db;

    db.execute("SELECT foo.value, foo.id, foo.number FROM foo WHERE foo.id>42");

    auto count = db.read_count();
    xs.reserve(count);

    for(unsigned i=0; i!=count; ++i, db.move_to_next())
    {
        row.value = db.get<double>(0);
        row.id = db.get<int>(1);
        row.number = db.get<int>(2);

        xs.push_back(row);
    }

    return xs;
}

template<typename T>
void print(T &&xs)
{
    using namespace std;

    for(auto &x : xs)
        cout << "ROW: " << x.id << " " << x.value << " " << x.number << endl;
}

int main()
{
    using namespace CTQL;

    print( to_vector( from(foo).where(foo.id > 42_i).select(foo.value, foo.id, foo.number) ) );
    print_line();
    print( handwritten() );
}
  Вывод
execute SELECT foo.value, foo.id, foo.number FROM foo WHERE foo.id>42 
read_count 
get<double> 0 
get<int> 1 
get<int> 2 
move_to_next 
get<double> 0 
get<int> 1 
get<int> 2 
move_to_next 
get<double> 0 
get<int> 1 
get<int> 2 
move_to_next 
ROW: 100 1 200
ROW: 110 1.01 210
ROW: 120 1.02 220

execute SELECT foo.value, foo.id, foo.number FROM foo WHERE foo.id>42 
read_count 
get<double> 0 
get<int> 1 
get<int> 2 
move_to_next 
get<double> 0 
get<int> 1 
get<int> 2 
move_to_next 
get<double> 0 
get<int> 1 
get<int> 2 
move_to_next 
ROW: 100 1 200
ROW: 110 1.01 210
ROW: 120 1.02 220

Benchmark

Никакой benchmark не нужен, так как результирующий ассемблерный код в обоих вариантах идентичен, с точностью до идентификаторов — то есть истинный zero overhead
Собственно ради демонстрации нулевых расходов от EDSL запросов и затевался данный proof-of-concept.

Скрипт сравнения ассемблерного кода вариантов

g++ ctql.cpp -std=c++14 -O3 -Wall -pedantic -DNDEBUG -S -masm=intel &&
cat ctql.s | c++filt > ctql.filtered.s &&

sed -n '/.*auto CTQL::to_vector<.*):$/,/seh_endproc/p' ctql.filtered.s > to_vector.s &&
sed -n '/handwritten():$/,/seh_endproc/p' ctql.filtered.s > handwritten.s &&

diff -u handwritten.s to_vector.s > result.diff &&
cat result.diff
  ASM diff
--- handwritten.s   2016-07-11 21:12:30.407918000 +0300
+++ to_vector.s 2016-07-11 21:12:30.357917900 +0300
@@ -1,5 +1,5 @@
-handwritten():
-.LFB2021:
+auto CTQL::to_vector<CTQL::make_table<foo_aux::name_string, foo_aux::id_column, foo_aux::code_column, foo_aux::value_column, foo_aux::number_column>, CTQL::string_t<(char)102, (char)111, (char)111, (char)46, (char)105, (char)100, (char)62, (char)52, (char)50> const, CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> >(CTQL::query<CTQL::make_table<foo_aux::name_string, foo_aux::id_column, foo_aux::code_column, foo_aux::value_column, foo_aux::number_column>, CTQL::string_t<(char)102, (char)111, (char)111, (char)46, (char)105, (char)100, (char)62, (char)52, (char)50> const, CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> >):
+.LFB2067:
    push    r12
    .seh_pushreg    r12
    push    rbp
@@ -15,23 +15,23 @@
    .seh_endprologue
    lea rbx, 32[rsp]
    mov rdi, rcx
-   call    CTQL::vector_noinline<handwritten()::Row>::vector_noinline()
-   lea rdx, .LC9[rip]
+   call    CTQL::vector_noinline<CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > >::vector_noinline()
+   lea rdx, CTQL::string_value<(char)83, (char)69, (char)76, (char)69, (char)67, (char)84, (char)32, (char)102, (char)111, (char)111, (char)46, (char)118, (char)97, (char)108, (char)117, (char)101, (char)44, (char)32, (char)102, (char)111, (char)111, (char)46, (char)105, (char)100, (char)44, (char)32, (char)102, (char)111, (char)111, (char)46, (char)110, (char)117, (char)109, (char)98, (char)101, (char)114, (char)32, (char)70, (char)82, (char)79, (char)77, (char)32, (char)102, (char)111, (char)111, (char)32, (char)87, (char)72, (char)69, (char)82, (char)69, (char)32, (char)102, (char)111, (char)111, (char)46, (char)105, (char)100, (char)62, (char)52, (char)50>::value[rip]
    mov rcx, rbx
    mov DWORD PTR 32[rsp], 0
-.LEHB0:
+.LEHB2:
    call    CTQL::dummy_db::execute(char const*)
    call    CTQL::dummy_db::read_count() const [clone .isra.32]
    mov edx, eax
    mov rcx, rdi
    mov rbp, rdx
-   call    CTQL::vector_noinline<handwritten()::Row>::reserve(unsigned long) [clone .constprop.34]
+   call    CTQL::vector_noinline<CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > >::reserve(unsigned long)
    test    ebp, ebp
-   je  .L100
+   je  .L169
    lea r12, 48[rsp]
    xor esi, esi
    .p2align 4,,10
-.L103:
+.L172:
    mov rcx, rbx
    call    double CTQL::dummy_db::get<double>(unsigned int) const [clone .constprop.40]
    mov rcx, rbx
@@ -43,14 +43,14 @@
    mov rdx, r12
    mov rcx, rdi
    mov DWORD PTR 60[rsp], eax
-   call    CTQL::vector_noinline<handwritten()::Row>::push_back(handwritten()::Row const&) [clone .constprop.36]
+   call    CTQL::vector_noinline<CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > >::push_back(CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > const&)
    mov rcx, rbx
    add esi, 1
    call    CTQL::dummy_db::move_to_next()
-.LEHE0:
+.LEHE2:
    cmp ebp, esi
-   jne .L103
-.L100:
+   jne .L172
+.L169:
    mov rax, rdi
    add rsp, 64
    pop rbx
@@ -59,32 +59,32 @@
    pop rbp
    pop r12
    ret
-.L106:
+.L175:
    mov rbx, rax
    mov rcx, rdi
-   call    CTQL::vector_noinline<handwritten()::Row>::~vector_noinline()
+   call    CTQL::vector_noinline<CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > >::~vector_noinline()
    mov rcx, rbx
-.LEHB1:
+.LEHB3:
    call    _Unwind_Resume
    nop
-.LEHE1:
-   .def    __gxx_personality_seh0; .scl    2;  .type   32; .endef
+.LEHE3:
    .seh_handler    __gxx_personality_seh0, @unwind, @except
    .seh_handlerdata
-.LLSDA2021:
+.LLSDA2067:
    .byte   0xff
    .byte   0xff
    .byte   0x1
-   .uleb128 .LLSDACSE2021-.LLSDACSB2021
-.LLSDACSB2021:
-   .uleb128 .LEHB0-.LFB2021
-   .uleb128 .LEHE0-.LEHB0
-   .uleb128 .L106-.LFB2021
+   .uleb128 .LLSDACSE2067-.LLSDACSB2067
+.LLSDACSB2067:
+   .uleb128 .LEHB2-.LFB2067
+   .uleb128 .LEHE2-.LEHB2
+   .uleb128 .L175-.LFB2067
    .uleb128 0
-   .uleb128 .LEHB1-.LFB2021
-   .uleb128 .LEHE1-.LEHB1
+   .uleb128 .LEHB3-.LFB2067
+   .uleb128 .LEHE3-.LEHB3
    .uleb128 0
    .uleb128 0
-.LLSDACSE2021:
-   .text
+.LLSDACSE2067:
+   .section    .text$_ZN4CTQL9to_vectorINS_10make_tableIN7foo_aux11name_stringEJNS2_9id_columnENS2_11code_columnENS2_12value_columnENS2_13number_columnEEEEKNS_8string_tIJLc102ELc111ELc111ELc46ELc105ELc100ELc62ELc52ELc50EEEEJNS_12table_columnIS2_dNS6_11field_valueEEENSC_IS2_iNS4_8field_idEEENSC_IS2_iNS7_12field_numberEEEEEEDaNS_5queryIT_T0_JDpT1_EEE,"x"
+   .linkonce discard
    .seh_endproc

Live Demo


Файлы

  ctql.hpp
// Copyright Evgeny Panasyuk 2016.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)

// e-mail: E?????[dot]P???????[at]gmail.???

// C++ Compile-Time DB Query EDSL Proof-of-Concept

#ifndef CTQL_HPP
#define CTQL_HPP

/******************************************************************************/
#include <boost/preprocessor/punctuation/comma_if.hpp>
#include <boost/preprocessor/seq/for_each_i.hpp>
#include <boost/preprocessor/seq/pop_front.hpp>
#include <boost/preprocessor/seq/for_each.hpp>
#include <boost/preprocessor/seq/elem.hpp>
#include <boost/preprocessor/tuple/elem.hpp>
#include <boost/preprocessor/stringize.hpp>
#include <boost/preprocessor/cat.hpp>

#include <initializer_list>
#include <type_traits>
#include <iostream>
#include <iterator>
#include <cassert>
#include <utility>
#include <vector>
#include <tuple>

namespace CTQL
{

/******************************************************************************/
#ifdef __GNUC__
    #define NOINLINE __attribute__((noinline))
#else
    #define NOINLINE
#endif

/******************************************************************************/
template<typename ...Ts>
void print_line(const Ts&... xs)
{
    using namespace std;
    (void)initializer_list<int>({(cout << xs << " ", 0)...});
    cout << endl;
}

/******************************************************************************/
class dummy_db
{
    unsigned current = 0;
public:
    NOINLINE void execute(const char *sql)
    {
        print_line("execute", sql);
        current = 0u;
    }
    NOINLINE unsigned read_count() const
    {
        using namespace std;
        print_line("read_count");
        return 3;
    }
    NOINLINE void move_to_next()
    {
        print_line("move_to_next");
        ++current;
    }
    template<typename T>
    NOINLINE T get(unsigned column) const;
};

template<>
NOINLINE int dummy_db::get<int>(unsigned column) const
{
    assert(column != 0 && column < 4);

    print_line("get<int>", column);
    return current*10 + column*100;
}

template<>
NOINLINE double dummy_db::get<double>(unsigned column) const
{
    assert(column == 0);

    print_line("get<double>", column);
    return 1.0 + current*0.01;
}

/******************************************************************************/
template<typename T>
struct vector_noinline
{
    std::vector<T> xs;

    NOINLINE vector_noinline() = default;
    NOINLINE ~vector_noinline() = default;
    NOINLINE vector_noinline(vector_noinline &&) = default;
    NOINLINE vector_noinline(const vector_noinline &) = default;

    NOINLINE void reserve(std::size_t n) { xs.reserve(n); }
    NOINLINE void push_back(const T &x) { xs.push_back(x); }
    auto begin() { return xs.begin(); }
    auto end() { return xs.end(); }
};

/******************************************************************************/
template<typename I, typename T>
constexpr I find(I first, T x)
{
    while(*first != x)
        ++first;
    return first;
}

constexpr std::size_t c_str_length(const char *x)
{
    return find(x, '\0') - x;
}

/******************************************************************************/
// compile-time string

template<char... cs>
struct string_value
{
    static constexpr const char value[sizeof...(cs)+1] = {cs..., '\0'};
};
template<char... cs>
constexpr const char string_value<cs...>::value[sizeof...(cs)+1];

template<char ...cs>
struct string_t
{
    using value_type = string_value<cs...>;
};

template<char ...cs>
constexpr string_t<cs...> string{};

template<typename String, std::size_t ...Is>
constexpr auto make_string_aux(String x, std::index_sequence<Is...>)
{
    return string<String::value()[Is]...>;
}

template<typename String>
constexpr auto make_string(String x)
{
    constexpr auto length = c_str_length(String::value());
    return make_string_aux(x, std::make_index_sequence<length>{});
}

/******************************************************************************/
constexpr auto concat()
{
    return string<>;
}

template<char ...cs>
constexpr auto concat(string_t<cs...> first)
{
    return first;
}

template<char ...cs1, char ...cs2, typename ...Rest>
constexpr auto concat
(
    string_t<cs1...> first,
    string_t<cs2...> second,
    Rest... rest
)
{
    return concat(string<cs1..., cs2...>, rest...);
}

/******************************************************************************/
template<typename T, typename U>
constexpr auto is_same(T, U)
{
    return std::false_type{};
}

template<typename T>
constexpr auto is_same(T, T)
{
    return std::true_type{};
}

template<typename TrueCase, typename FalseCase>
constexpr auto conditional(std::true_type, TrueCase true_case, FalseCase)
{
    return true_case;
}

template<typename TrueCase, typename FalseCase>
constexpr auto conditional(std::false_type, TrueCase, FalseCase false_case)
{
    return false_case;
}

using empty = string_t<>;

template<typename S, typename Rest>
constexpr auto skip_if_empty(S s, Rest rest)
{

    return conditional(is_same(s, empty{}), empty{}, rest);
}

/******************************************************************************/
template<char... digits>
constexpr auto operator"" _i() noexcept
{
    // static_assert
    return string<digits...>;
}

/******************************************************************************/
#define CTQL_STRUCT_WRAP_STRING(name, str)                              \
    struct name { static constexpr auto value() { return str; } }       \
/**/

#define CTQL_LAMBDA_WRAP_STRING(x)         \
    ([]                                    \
    {                                      \
        CTQL_STRUCT_WRAP_STRING(, x) str;  \
        return str;                        \
    })                                     \
/**/

template<typename Lambda>
constexpr auto make_string_from_lambda(Lambda x)
{
    return make_string(decltype(x()){});
}

#define CTQL_STRING(x)                                           \
    (CTQL::make_string_from_lambda(CTQL_LAMBDA_WRAP_STRING(x)))  \
/**/

/******************************************************************************/
constexpr auto space = string<' '>;
constexpr auto comma = string<','>;
constexpr auto dot = string<'.'>;

/******************************************************************************/
template<typename Field>
constexpr auto name_string(Field)
{
    return make_string(typename Field::name_string{});
}

/******************************************************************************/
#define FIELD(name)                                                     \
    struct BOOST_PP_CAT(field_, name)                                   \
    {                                                                   \
        CTQL_STRUCT_WRAP_STRING(name_string, BOOST_PP_STRINGIZE(name)); \
        template<typename T>                                            \
        struct field                                                    \
        {                                                               \
            T name;                                                     \
        };                                                              \
        template<typename T>                                            \
        static auto &access(T &x)                                       \
        {                                                               \
            return x.name;                                              \
        }                                                               \
    }                                                                   \
/**/

/******************************************************************************/
template<typename Column>
constexpr auto full_column_name(Column column)
{
    return concat
    (
        name_string(typename Column::table{}),
        dot,
        name_string(typename Column::field{})
    );    
}

template<typename Table, typename T, typename Field>
struct table_column
{
    using table = Table;
    using column_type = T;
    using field = Field;

    template<char ...cs>
    constexpr auto operator>(string_t<cs...> x) const
    {
        return concat(full_column_name(table_column{}), string<'>'>, x);
    }
};

template<typename Name, typename ...Columns>
struct make_table
    : Columns::type::field::template field<typename Columns::type>...
{
    using name_string = Name;
};

template<typename ...Columns>
struct make_row
    : Columns::field::template field<typename Columns::column_type>...
{};

/******************************************************************************/
// map db result onto row

template<typename Ts, typename Row, typename Db, std::size_t ...Is>
void map_result_aux(Row &row, Db &db, std::index_sequence<Is...>)
{
    (void)std::initializer_list<int>(
    {
        (
            (void)
            (
                std::tuple_element_t<Is, Ts>::field::access(row) =
                    db.template get
                    <
                        typename std::tuple_element_t<Is, Ts>::column_type
                    >(Is)
            ),
            0
        )...
    });
}

template<typename ...Ts, typename Row, typename Db>
void map_result(Row &row, Db &db)
{
    map_result_aux<std::tuple<Ts...>>
        (row, db, std::make_index_sequence<sizeof...(Ts)>());
}

/******************************************************************************/
// query EDSL

template<typename Table, typename Condition, typename ...Columns>
struct query
{
    using source = Table;
    using condition = Condition;
    using projection = std::tuple<Columns...>;

    template<typename ...RightColumns>
    auto select(RightColumns...) const
    {
        return query<Table, Condition, Columns..., RightColumns...>{};
    }

    template<typename RightCondition>
    auto where(RightCondition right_condition) const
    {
        constexpr auto new_condition = concat
        (
            skip_if_empty
            (
                Condition{},
                concat(Condition{}, CTQL_STRING(" AND "))
            ),
            right_condition
        );

        return query<Table, decltype(new_condition), Columns...>{};
    }
};

template<typename Table>
auto from(Table)
{
    return query<Table, empty>{};
}

/******************************************************************************/
// SQL generation

template<typename Source>
constexpr auto source_string(Source source)
{
    return name_string(source);
}

template<typename FirstColumn, typename ...Rest>
constexpr auto concat_columns(FirstColumn first_column, Rest... rest)
{
    return concat
    (
        full_column_name(first_column),
        concat(comma, space, full_column_name(rest))...
    );
}

template<typename Source, typename Condition, typename ...Columns>
constexpr auto make_sql
(
    Source source, Condition condition,
    Columns... columns
)
{
    return concat
    (
        CTQL_STRING("SELECT "), concat_columns(columns...),
        CTQL_STRING(" FROM "), source_string(source),
        skip_if_empty(condition, concat(CTQL_STRING(" WHERE "), condition))
    );
}

/******************************************************************************/
// Inner Query
template<typename Source, typename ...Ts>
constexpr auto source_string(query<Source, Ts...>)
{
    return concat(string<'('>, make_sql(Source{}, Ts{}...), string<')'>);
}

#ifdef CTQL_TRANSFORM_FROM_SUBQUERY
// transform
template
<
    typename SubSource, typename SubCondition, typename ...SubColumns,
    typename Condition, typename ...Columns
>
constexpr auto make_sql
(
    query<SubSource, SubCondition, SubColumns...>,
    Condition condition,
    Columns... columns
)
{
    return make_sql
    (
        SubSource{},
        concat(condition, CTQL_STRING(" AND "), SubCondition{}),
        columns...
    );
}
#endif

/******************************************************************************/
template<typename Table, typename Condition, typename ...Columns>
NOINLINE auto to_vector(query<Table, Condition, Columns...>)
{
    using sql_text = decltype(make_sql(Table{}, Condition{}, Columns{}...));

    using Row = make_row<Columns...>;
    Row row;

    vector_noinline<Row> xs;

    dummy_db db;
    db.execute(sql_text::value_type::value);
    auto count = db.read_count();
    xs.reserve(count);

    for(unsigned i=0; i!=count; ++i, db.move_to_next())
    {
        map_result<Columns...>(row, db);
        xs.push_back(row);
    }

    return xs;
}

/******************************************************************************/
// table definition macro
// refer http://www.boost.org/doc/libs/1_56_0/boost/fusion/adapted/struct/define_struct.hpp
// rerquired for sequence of tuples syntax ((a,b)(c,d)...)

#define PAIR_TYPE_0(X, Y) ((X, Y)) PAIR_TYPE_1
#define PAIR_TYPE_1(X, Y) ((X, Y)) PAIR_TYPE_0
#define PAIR_TYPE_0_END
#define PAIR_TYPE_1_END

#define PAIR_FIRST(elem) BOOST_PP_TUPLE_ELEM(2, 0, elem)
#define PAIR_SECOND(elem) BOOST_PP_TUPLE_ELEM(2, 1, elem)


#define DECLARE_COLUMN(r, data, i, elem)            \
    struct BOOST_PP_CAT(PAIR_SECOND(elem), _column) \
    {                                               \
        FIELD(PAIR_SECOND(elem));                   \
        using type = CTQL::table_column             \
        <                                           \
            table_type,                             \
            PAIR_FIRST(elem),                       \
            BOOST_PP_CAT(field_, PAIR_SECOND(elem)) \
        >;                                          \
    };                                              \
/**/

#define COLUMN_NAME(r, data, i, elem) , BOOST_PP_CAT(PAIR_SECOND(elem), _column)

#define FOR_EACH_DECL(action, seq) \
    BOOST_PP_SEQ_FOR_EACH_I(action, _, BOOST_PP_SEQ_POP_FRONT(BOOST_PP_CAT(PAIR_TYPE_0(0,0)seq,_END))) \
/**/

#define DEFINE_TABLE(name, seq)                                                     \
    struct name ## _aux                                                             \
    {                                                                               \
        using table_type = name ## _aux;                                            \
        FOR_EACH_DECL(DECLARE_COLUMN, seq)                                          \
        CTQL_STRUCT_WRAP_STRING(name_string, #name);                                \
        using type = CTQL::make_table<name_string FOR_EACH_DECL(COLUMN_NAME, seq)>; \
    };                                                                              \
    name ## _aux :: type name                                                       \
/**/


} // namespace end

#endif
  ctql.cpp
#include "ctql.hpp"

#include <iostream>

DEFINE_TABLE
(
    foo,
    (int, id)
    (int, code)
    (double, value)
    (int, number)
);

NOINLINE auto handwritten()
{
    using namespace CTQL;

    struct Row
    {
        double value;
        int id;
        int number;
    } row;
    vector_noinline<Row> xs;

    dummy_db db;

    db.execute("SELECT foo.value, foo.id, foo.number FROM foo WHERE foo.id>42");

    auto count = db.read_count();
    xs.reserve(count);

    for(unsigned i=0; i!=count; ++i, db.move_to_next())
    {
        row.value = db.get<double>(0);
        row.id = db.get<int>(1);
        row.number = db.get<int>(2);

        xs.push_back(row);
    }

    return xs;
}

template<typename T>
void print(T &&xs)
{
    using namespace std;

    for(auto &x : xs)
        cout << "ROW: " << x.id << " " << x.value << " " << x.number << endl;
}

int main()
{
    using namespace CTQL;

    print( to_vector( from(foo).where(foo.id > 42_i).select(foo.value, foo.id, foo.number) ) );
    print_line();
    print( handwritten() );
}
  to_vector.s
auto CTQL::to_vector<CTQL::make_table<foo_aux::name_string, foo_aux::id_column, foo_aux::code_column, foo_aux::value_column, foo_aux::number_column>, CTQL::string_t<(char)102, (char)111, (char)111, (char)46, (char)105, (char)100, (char)62, (char)52, (char)50> const, CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> >(CTQL::query<CTQL::make_table<foo_aux::name_string, foo_aux::id_column, foo_aux::code_column, foo_aux::value_column, foo_aux::number_column>, CTQL::string_t<(char)102, (char)111, (char)111, (char)46, (char)105, (char)100, (char)62, (char)52, (char)50> const, CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> >):
.LFB2067:
    push    r12
    .seh_pushreg    r12
    push    rbp
    .seh_pushreg    rbp
    push    rdi
    .seh_pushreg    rdi
    push    rsi
    .seh_pushreg    rsi
    push    rbx
    .seh_pushreg    rbx
    sub rsp, 64
    .seh_stackalloc 64
    .seh_endprologue
    lea rbx, 32[rsp]
    mov rdi, rcx
    call    CTQL::vector_noinline<CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > >::vector_noinline()
    lea rdx, CTQL::string_value<(char)83, (char)69, (char)76, (char)69, (char)67, (char)84, (char)32, (char)102, (char)111, (char)111, (char)46, (char)118, (char)97, (char)108, (char)117, (char)101, (char)44, (char)32, (char)102, (char)111, (char)111, (char)46, (char)105, (char)100, (char)44, (char)32, (char)102, (char)111, (char)111, (char)46, (char)110, (char)117, (char)109, (char)98, (char)101, (char)114, (char)32, (char)70, (char)82, (char)79, (char)77, (char)32, (char)102, (char)111, (char)111, (char)32, (char)87, (char)72, (char)69, (char)82, (char)69, (char)32, (char)102, (char)111, (char)111, (char)46, (char)105, (char)100, (char)62, (char)52, (char)50>::value[rip]
    mov rcx, rbx
    mov DWORD PTR 32[rsp], 0
.LEHB2:
    call    CTQL::dummy_db::execute(char const*)
    call    CTQL::dummy_db::read_count() const [clone .isra.32]
    mov edx, eax
    mov rcx, rdi
    mov rbp, rdx
    call    CTQL::vector_noinline<CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > >::reserve(unsigned long)
    test    ebp, ebp
    je  .L169
    lea r12, 48[rsp]
    xor esi, esi
    .p2align 4,,10
.L172:
    mov rcx, rbx
    call    double CTQL::dummy_db::get<double>(unsigned int) const [clone .constprop.40]
    mov rcx, rbx
    movsd   QWORD PTR 48[rsp], xmm0
    call    int CTQL::dummy_db::get<int>(unsigned int) const [clone .constprop.41]
    mov rcx, rbx
    mov DWORD PTR 56[rsp], eax
    call    int CTQL::dummy_db::get<int>(unsigned int) const [clone .constprop.42]
    mov rdx, r12
    mov rcx, rdi
    mov DWORD PTR 60[rsp], eax
    call    CTQL::vector_noinline<CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > >::push_back(CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > const&)
    mov rcx, rbx
    add esi, 1
    call    CTQL::dummy_db::move_to_next()
.LEHE2:
    cmp ebp, esi
    jne .L172
.L169:
    mov rax, rdi
    add rsp, 64
    pop rbx
    pop rsi
    pop rdi
    pop rbp
    pop r12
    ret
.L175:
    mov rbx, rax
    mov rcx, rdi
    call    CTQL::vector_noinline<CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > >::~vector_noinline()
    mov rcx, rbx
.LEHB3:
    call    _Unwind_Resume
    nop
.LEHE3:
    .seh_handler    __gxx_personality_seh0, @unwind, @except
    .seh_handlerdata
.LLSDA2067:
    .byte   0xff
    .byte   0xff
    .byte   0x1
    .uleb128 .LLSDACSE2067-.LLSDACSB2067
.LLSDACSB2067:
    .uleb128 .LEHB2-.LFB2067
    .uleb128 .LEHE2-.LEHB2
    .uleb128 .L175-.LFB2067
    .uleb128 0
    .uleb128 .LEHB3-.LFB2067
    .uleb128 .LEHE3-.LEHB3
    .uleb128 0
    .uleb128 0
.LLSDACSE2067:
    .section    .text$_ZN4CTQL9to_vectorINS_10make_tableIN7foo_aux11name_stringEJNS2_9id_columnENS2_11code_columnENS2_12value_columnENS2_13number_columnEEEEKNS_8string_tIJLc102ELc111ELc111ELc46ELc105ELc100ELc62ELc52ELc50EEEEJNS_12table_columnIS2_dNS6_11field_valueEEENSC_IS2_iNS4_8field_idEEENSC_IS2_iNS7_12field_numberEEEEEEDaNS_5queryIT_T0_JDpT1_EEE,"x"
    .linkonce discard
    .seh_endproc
  handwritten.s
handwritten():
.LFB2021:
    push    r12
    .seh_pushreg    r12
    push    rbp
    .seh_pushreg    rbp
    push    rdi
    .seh_pushreg    rdi
    push    rsi
    .seh_pushreg    rsi
    push    rbx
    .seh_pushreg    rbx
    sub rsp, 64
    .seh_stackalloc 64
    .seh_endprologue
    lea rbx, 32[rsp]
    mov rdi, rcx
    call    CTQL::vector_noinline<handwritten()::Row>::vector_noinline()
    lea rdx, .LC9[rip]
    mov rcx, rbx
    mov DWORD PTR 32[rsp], 0
.LEHB0:
    call    CTQL::dummy_db::execute(char const*)
    call    CTQL::dummy_db::read_count() const [clone .isra.32]
    mov edx, eax
    mov rcx, rdi
    mov rbp, rdx
    call    CTQL::vector_noinline<handwritten()::Row>::reserve(unsigned long) [clone .constprop.34]
    test    ebp, ebp
    je  .L100
    lea r12, 48[rsp]
    xor esi, esi
    .p2align 4,,10
.L103:
    mov rcx, rbx
    call    double CTQL::dummy_db::get<double>(unsigned int) const [clone .constprop.40]
    mov rcx, rbx
    movsd   QWORD PTR 48[rsp], xmm0
    call    int CTQL::dummy_db::get<int>(unsigned int) const [clone .constprop.41]
    mov rcx, rbx
    mov DWORD PTR 56[rsp], eax
    call    int CTQL::dummy_db::get<int>(unsigned int) const [clone .constprop.42]
    mov rdx, r12
    mov rcx, rdi
    mov DWORD PTR 60[rsp], eax
    call    CTQL::vector_noinline<handwritten()::Row>::push_back(handwritten()::Row const&) [clone .constprop.36]
    mov rcx, rbx
    add esi, 1
    call    CTQL::dummy_db::move_to_next()
.LEHE0:
    cmp ebp, esi
    jne .L103
.L100:
    mov rax, rdi
    add rsp, 64
    pop rbx
    pop rsi
    pop rdi
    pop rbp
    pop r12
    ret
.L106:
    mov rbx, rax
    mov rcx, rdi
    call    CTQL::vector_noinline<handwritten()::Row>::~vector_noinline()
    mov rcx, rbx
.LEHB1:
    call    _Unwind_Resume
    nop
.LEHE1:
    .def    __gxx_personality_seh0; .scl    2;  .type   32; .endef
    .seh_handler    __gxx_personality_seh0, @unwind, @except
    .seh_handlerdata
.LLSDA2021:
    .byte   0xff
    .byte   0xff
    .byte   0x1
    .uleb128 .LLSDACSE2021-.LLSDACSB2021
.LLSDACSB2021:
    .uleb128 .LEHB0-.LFB2021
    .uleb128 .LEHE0-.LEHB0
    .uleb128 .L106-.LFB2021
    .uleb128 0
    .uleb128 .LEHB1-.LFB2021
    .uleb128 .LEHE1-.LEHB1
    .uleb128 0
    .uleb128 0
.LLSDACSE2021:
    .text
    .seh_endproc
  ctql.filtered.s
    .file   "ctql.cpp"
    .intel_syntax noprefix
    .section    .text$_ZNKSt5ctypeIcE8do_widenEc,"x"
    .linkonce discard
    .align 2
    .p2align 4,,15
    .globl  std::ctype<char>::do_widen(char) const
    .def    std::ctype<char>::do_widen(char) const; .scl    2;  .type   32; .endef
    .seh_proc   std::ctype<char>::do_widen(char) const
std::ctype<char>::do_widen(char) const:
.LFB1178:
    .seh_endprologue
    mov eax, edx
    ret
    .seh_endproc
    .text
    .align 2
    .p2align 4,,15
    .def    CTQL::vector_noinline<handwritten()::Row>::vector_noinline();   .scl    3;  .type   32; .endef
    .seh_proc   CTQL::vector_noinline<handwritten()::Row>::vector_noinline()
CTQL::vector_noinline<handwritten()::Row>::vector_noinline():
.LFB2023:
    .seh_endprologue
    mov QWORD PTR [rcx], 0
    mov QWORD PTR 8[rcx], 0
    mov QWORD PTR 16[rcx], 0
    ret
    .seh_endproc
    .def    CTQL::vector_noinline<handwritten()::Row>::vector_noinline();   .scl    3;  .type   32; .endef
    .set    CTQL::vector_noinline<handwritten()::Row>::vector_noinline(),CTQL::vector_noinline<handwritten()::Row>::vector_noinline()
    .align 2
    .p2align 4,,15
    .def    CTQL::vector_noinline<handwritten()::Row>::~vector_noinline();  .scl    3;  .type   32; .endef
    .seh_proc   CTQL::vector_noinline<handwritten()::Row>::~vector_noinline()
CTQL::vector_noinline<handwritten()::Row>::~vector_noinline():
.LFB2026:
    .seh_endprologue
    mov rcx, QWORD PTR [rcx]
    test    rcx, rcx
    je  .L3
    jmp operator delete(void*)
.L3:
    ret
    .seh_endproc
    .def    CTQL::vector_noinline<handwritten()::Row>::~vector_noinline();  .scl    3;  .type   32; .endef
    .set    CTQL::vector_noinline<handwritten()::Row>::~vector_noinline(),CTQL::vector_noinline<handwritten()::Row>::~vector_noinline()
    .align 2
    .p2align 4,,15
    .def    CTQL::vector_noinline<handwritten()::Row>::push_back(handwritten()::Row const&) [clone .constprop.36];  .scl    3;  .type   32; .endef
    .seh_proc   CTQL::vector_noinline<handwritten()::Row>::push_back(handwritten()::Row const&) [clone .constprop.36]
CTQL::vector_noinline<handwritten()::Row>::push_back(handwritten()::Row const&) [clone .constprop.36]:
.LFB2708:
    push    r12
    .seh_pushreg    r12
    push    rbp
    .seh_pushreg    rbp
    push    rdi
    .seh_pushreg    rdi
    push    rsi
    .seh_pushreg    rsi
    push    rbx
    .seh_pushreg    rbx
    sub rsp, 48
    .seh_stackalloc 48
    .seh_endprologue
    mov r8, QWORD PTR 8[rcx]
    cmp r8, QWORD PTR 16[rcx]
    mov rbx, rcx
    je  .L7
    test    r8, r8
    je  .L8
    mov rax, QWORD PTR [rdx]
    mov rdx, QWORD PTR 8[rdx]
    mov QWORD PTR [r8], rax
    mov QWORD PTR 8[r8], rdx
.L8:
    add r8, 16
    mov QWORD PTR 8[rbx], r8
.L6:
    add rsp, 48
    pop rbx
    pop rsi
    pop rdi
    pop rbp
    pop r12
    ret
    .p2align 4,,10
.L7:
    mov rax, r8
    sub rax, QWORD PTR [rcx]
    sar rax, 4
    test    rax, rax
    je  .L17
    lea rcx, [rax+rax]
    cmp rax, rcx
    jbe .L30
.L18:
    mov rbp, -16
.L10:
    mov rcx, rbp
    mov QWORD PTR 40[rsp], rdx
    call    operator new(unsigned long)
    mov r8, QWORD PTR 8[rbx]
    mov rdx, QWORD PTR 40[rsp]
    mov rsi, rax
.L16:
    mov r12, QWORD PTR [rbx]
    mov rdi, rsi
    sub r8, r12
    mov rax, r8
    sar rax, 4
    add rdi, r8
    je  .L12
    mov r9, QWORD PTR [rdx]
    mov r10, QWORD PTR 8[rdx]
    mov QWORD PTR [rdi], r9
    mov QWORD PTR 8[rdi], r10
.L12:
    test    rax, rax
    jne .L31
    add rdi, 16
    test    r12, r12
    je  .L15
.L14:
    mov rcx, r12
    call    operator delete(void*)
.L15:
    mov QWORD PTR [rbx], rsi
    add rsi, rbp
    mov QWORD PTR 8[rbx], rdi
    mov QWORD PTR 16[rbx], rsi
    jmp .L6
    .p2align 4,,10
.L17:
    mov ebp, 16
    jmp .L10
    .p2align 4,,10
.L31:
    mov rdx, r12
    mov rcx, rsi
    add rdi, 16
    call    memmove
    jmp .L14
.L30:
    movabs  r9, 1152921504606846975
    cmp rcx, r9
    ja  .L18
    xor ebp, ebp
    xor esi, esi
    test    rcx, rcx
    je  .L16
    sal rax, 5
    mov rbp, rax
    jmp .L10
    .seh_endproc
    .section .rdata,"dr"
.LC0:
    .ascii "vector::reserve\0"
    .text
    .align 2
    .p2align 4,,15
    .def    CTQL::vector_noinline<handwritten()::Row>::reserve(unsigned long) [clone .constprop.34];    .scl    3;  .type   32; .endef
    .seh_proc   CTQL::vector_noinline<handwritten()::Row>::reserve(unsigned long) [clone .constprop.34]
CTQL::vector_noinline<handwritten()::Row>::reserve(unsigned long) [clone .constprop.34]:
.LFB2710:
    push    r13
    .seh_pushreg    r13
    push    r12
    .seh_pushreg    r12
    push    rbp
    .seh_pushreg    rbp
    push    rdi
    .seh_pushreg    rdi
    push    rsi
    .seh_pushreg    rsi
    push    rbx
    .seh_pushreg    rbx
    sub rsp, 40
    .seh_stackalloc 40
    .seh_endprologue
    movabs  rax, 1152921504606846975
    cmp rdx, rax
    mov rsi, rcx
    ja  .L43
    mov rbp, QWORD PTR [rcx]
    mov rax, QWORD PTR 16[rcx]
    sub rax, rbp
    sar rax, 4
    cmp rdx, rax
    ja  .L44
    add rsp, 40
    pop rbx
    pop rsi
    pop rdi
    pop rbp
    pop r12
    pop r13
    ret
    .p2align 4,,10
.L44:
    mov rdi, QWORD PTR 8[rcx]
    sub rdi, rbp
    test    rdx, rdx
    je  .L39
    mov rbx, rdx
    sal rbx, 4
    mov rcx, rbx
    call    operator new(unsigned long)
    mov r13, QWORD PTR [rsi]
    mov r12, rax
.L35:
    mov rax, rdi
    sar rax, 4
    test    rax, rax
    je  .L37
    mov r8, rdi
    mov rdx, rbp
    mov rcx, r12
    call    memmove
.L37:
    test    r13, r13
    je  .L38
    mov rcx, r13
    call    operator delete(void*)
.L38:
    lea rdx, [r12+rbx]
    add rdi, r12
    mov QWORD PTR [rsi], r12
    mov QWORD PTR 8[rsi], rdi
    mov QWORD PTR 16[rsi], rdx
    add rsp, 40
    pop rbx
    pop rsi
    pop rdi
    pop rbp
    pop r12
    pop r13
    ret
    .p2align 4,,10
.L39:
    mov r13, rbp
    xor ebx, ebx
    xor r12d, r12d
    jmp .L35
.L43:
    lea rcx, .LC0[rip]
    call    std::__throw_length_error(char const*)
    nop
    .seh_endproc
    .section .rdata,"dr"
.LC1:
    .ascii "read_count\0"
.LC2:
    .ascii " \0"
    .text
    .align 2
    .p2align 4,,15
    .def    CTQL::dummy_db::read_count() const [clone .isra.32];    .scl    3;  .type   32; .endef
    .seh_proc   CTQL::dummy_db::read_count() const [clone .isra.32]
CTQL::dummy_db::read_count() const [clone .isra.32]:
.LFB2696:
    push    rbx
    .seh_pushreg    rbx
    sub rsp, 32
    .seh_stackalloc 32
    .seh_endprologue
    mov rcx, QWORD PTR .refptr._ZSt4cout[rip]
    lea rdx, .LC1[rip]
    mov r8d, 10
    call    std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)
    mov rcx, QWORD PTR .refptr._ZSt4cout[rip]
    lea rdx, .LC2[rip]
    mov r8d, 1
    call    std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)
    mov rax, QWORD PTR .refptr._ZSt4cout[rip]
    mov rdx, QWORD PTR [rax]
    mov rdx, QWORD PTR -24[rdx]
    mov rbx, QWORD PTR 240[rdx+rax]
    test    rbx, rbx
    je  .L50
    cmp BYTE PTR 56[rbx], 0
    je  .L47
    movsx   edx, BYTE PTR 67[rbx]
.L48:
    mov rcx, QWORD PTR .refptr._ZSt4cout[rip]
    call    std::basic_ostream<char, std::char_traits<char> >::put(char)
    mov rcx, rax
    call    std::basic_ostream<char, std::char_traits<char> >::flush()
    mov eax, 3
    add rsp, 32
    pop rbx
    ret
    .p2align 4,,10
.L47:
    mov rcx, rbx
    call    std::ctype<char>::_M_widen_init() const
    mov rax, QWORD PTR [rbx]
    lea rcx, std::ctype<char>::do_widen(char) const[rip]
    mov edx, 10
    mov rax, QWORD PTR 48[rax]
    cmp rax, rcx
    je  .L48
    mov rcx, rbx
    call    rax
    movsx   edx, al
    jmp .L48
.L50:
    call    std::__throw_bad_cast()
    nop
    .seh_endproc
    .section .rdata,"dr"
.LC3:
    .ascii "get<double>\0"
    .text
    .align 2
    .p2align 4,,15
    .def    double CTQL::dummy_db::get<double>(unsigned int) const [clone .constprop.40];   .scl    3;  .type   32; .endef
    .seh_proc   double CTQL::dummy_db::get<double>(unsigned int) const [clone .constprop.40]
double CTQL::dummy_db::get<double>(unsigned int) const [clone .constprop.40]:
.LFB2704:
    push    rsi
    .seh_pushreg    rsi
    push    rbx
    .seh_pushreg    rbx
    sub rsp, 40
    .seh_stackalloc 40
    .seh_endprologue
    lea rdx, .LC3[rip]
    mov r8d, 11
    mov rsi, rcx
    mov rcx, QWORD PTR .refptr._ZSt4cout[rip]
    call    std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)
    mov rcx, QWORD PTR .refptr._ZSt4cout[rip]
    lea rdx, .LC2[rip]
    mov r8d, 1
    call    std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)
    mov rcx, QWORD PTR .refptr._ZSt4cout[rip]
    xor edx, edx
    call    std::basic_ostream<char, std::char_traits<char> >& std::basic_ostream<char, std::char_traits<char> >::_M_insert<unsigned long>(unsigned long)
    lea rdx, .LC2[rip]
    mov r8d, 1
    mov rcx, rax
    call    std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)
    mov rax, QWORD PTR .refptr._ZSt4cout[rip]
    mov rdx, QWORD PTR [rax]
    mov rdx, QWORD PTR -24[rdx]
    mov rbx, QWORD PTR 240[rdx+rax]
    test    rbx, rbx
    je  .L58
    cmp BYTE PTR 56[rbx], 0
    je  .L53
    movsx   edx, BYTE PTR 67[rbx]
.L54:
    mov rcx, QWORD PTR .refptr._ZSt4cout[rip]
    call    std::basic_ostream<char, std::char_traits<char> >::put(char)
    mov rcx, rax
    call    std::basic_ostream<char, std::char_traits<char> >::flush()
    mov eax, DWORD PTR [rsi]
    pxor    xmm0, xmm0
    cvtsi2sdq   xmm0, rax
    mulsd   xmm0, QWORD PTR .LC4[rip]
    addsd   xmm0, QWORD PTR .LC5[rip]
    add rsp, 40
    pop rbx
    pop rsi
    ret
    .p2align 4,,10
.L53:
    mov rcx, rbx
    call    std::ctype<char>::_M_widen_init() const
    mov rax, QWORD PTR [rbx]
    lea rcx, std::ctype<char>::do_widen(char) const[rip]
    mov edx, 10
    mov rax, QWORD PTR 48[rax]
    cmp rax, rcx
    je  .L54
    mov rcx, rbx
    call    rax
    movsx   edx, al
    jmp .L54
.L58:
    call    std::__throw_bad_cast()
    nop
    .seh_endproc
    .section .rdata,"dr"
.LC6:
    .ascii "get<int>\0"
    .text
    .align 2
    .p2align 4,,15
    .def    int CTQL::dummy_db::get<int>(unsigned int) const [clone .constprop.42]; .scl    3;  .type   32; .endef
    .seh_proc   int CTQL::dummy_db::get<int>(unsigned int) const [clone .constprop.42]
int CTQL::dummy_db::get<int>(unsigned int) const [clone .constprop.42]:
.LFB2702:
    push    rsi
    .seh_pushreg    rsi
    push    rbx
    .seh_pushreg    rbx
    sub rsp, 40
    .seh_stackalloc 40
    .seh_endprologue
    lea rdx, .LC6[rip]
    mov r8d, 8
    mov rsi, rcx
    mov rcx, QWORD PTR .refptr._ZSt4cout[rip]
    call    std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)
    mov rcx, QWORD PTR .refptr._ZSt4cout[rip]
    lea rdx, .LC2[rip]
    mov r8d, 1
    call    std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)
    mov rcx, QWORD PTR .refptr._ZSt4cout[rip]
    mov edx, 2
    call    std::basic_ostream<char, std::char_traits<char> >& std::basic_ostream<char, std::char_traits<char> >::_M_insert<unsigned long>(unsigned long)
    lea rdx, .LC2[rip]
    mov r8d, 1
    mov rcx, rax
    call    std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)
    mov rax, QWORD PTR .refptr._ZSt4cout[rip]
    mov rdx, QWORD PTR [rax]
    mov rdx, QWORD PTR -24[rdx]
    mov rbx, QWORD PTR 240[rdx+rax]
    test    rbx, rbx
    je  .L64
    cmp BYTE PTR 56[rbx], 0
    je  .L61
    movsx   edx, BYTE PTR 67[rbx]
.L62:
    mov rcx, QWORD PTR .refptr._ZSt4cout[rip]
    call    std::basic_ostream<char, std::char_traits<char> >::put(char)
    mov rcx, rax
    call    std::basic_ostream<char, std::char_traits<char> >::flush()
    mov eax, DWORD PTR [rsi]
    lea eax, [rax+rax*4]
    lea eax, 200[rax+rax]
    add rsp, 40
    pop rbx
    pop rsi
    ret
    .p2align 4,,10
.L61:
    mov rcx, rbx
    call    std::ctype<char>::_M_widen_init() const
    mov rax, QWORD PTR [rbx]
    lea rcx, std::ctype<char>::do_widen(char) const[rip]
    mov edx, 10
    mov rax, QWORD PTR 48[rax]
    cmp rax, rcx
    je  .L62
    mov rcx, rbx
    call    rax
    movsx   edx, al
    jmp .L62
.L64:
    call    std::__throw_bad_cast()
    nop
    .seh_endproc
    .align 2
    .p2align 4,,15
    .def    int CTQL::dummy_db::get<int>(unsigned int) const [clone .constprop.41]; .scl    3;  .type   32; .endef
    .seh_proc   int CTQL::dummy_db::get<int>(unsigned int) const [clone .constprop.41]
int CTQL::dummy_db::get<int>(unsigned int) const [clone .constprop.41]:
.LFB2703:
    push    rsi
    .seh_pushreg    rsi
    push    rbx
    .seh_pushreg    rbx
    sub rsp, 40
    .seh_stackalloc 40
    .seh_endprologue
    lea rdx, .LC6[rip]
    mov r8d, 8
    mov rsi, rcx
    mov rcx, QWORD PTR .refptr._ZSt4cout[rip]
    call    std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)
    mov rcx, QWORD PTR .refptr._ZSt4cout[rip]
    lea rdx, .LC2[rip]
    mov r8d, 1
    call    std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)
    mov rcx, QWORD PTR .refptr._ZSt4cout[rip]
    mov edx, 1
    call    std::basic_ostream<char, std::char_traits<char> >& std::basic_ostream<char, std::char_traits<char> >::_M_insert<unsigned long>(unsigned long)
    lea rdx, .LC2[rip]
    mov r8d, 1
    mov rcx, rax
    call    std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)
    mov rax, QWORD PTR .refptr._ZSt4cout[rip]
    mov rdx, QWORD PTR [rax]
    mov rdx, QWORD PTR -24[rdx]
    mov rbx, QWORD PTR 240[rdx+rax]
    test    rbx, rbx
    je  .L70
    cmp BYTE PTR 56[rbx], 0
    je  .L67
    movsx   edx, BYTE PTR 67[rbx]
.L68:
    mov rcx, QWORD PTR .refptr._ZSt4cout[rip]
    call    std::basic_ostream<char, std::char_traits<char> >::put(char)
    mov rcx, rax
    call    std::basic_ostream<char, std::char_traits<char> >::flush()
    mov eax, DWORD PTR [rsi]
    lea eax, [rax+rax*4]
    lea eax, 100[rax+rax]
    add rsp, 40
    pop rbx
    pop rsi
    ret
    .p2align 4,,10
.L67:
    mov rcx, rbx
    call    std::ctype<char>::_M_widen_init() const
    mov rax, QWORD PTR [rbx]
    lea rcx, std::ctype<char>::do_widen(char) const[rip]
    mov edx, 10
    mov rax, QWORD PTR 48[rax]
    cmp rax, rcx
    je  .L68
    mov rcx, rbx
    call    rax
    movsx   edx, al
    jmp .L68
.L70:
    call    std::__throw_bad_cast()
    nop
    .seh_endproc
    .section .rdata,"dr"
.LC7:
    .ascii "execute\0"
    .section    .text$_ZN4CTQL8dummy_db7executeEPKc,"x"
    .linkonce discard
    .align 2
    .p2align 4,,15
    .globl  CTQL::dummy_db::execute(char const*)
    .def    CTQL::dummy_db::execute(char const*);   .scl    2;  .type   32; .endef
    .seh_proc   CTQL::dummy_db::execute(char const*)
CTQL::dummy_db::execute(char const*):
.LFB1967:
    push    rsi
    .seh_pushreg    rsi
    push    rbx
    .seh_pushreg    rbx
    sub rsp, 40
    .seh_stackalloc 40
    .seh_endprologue
    mov r8d, 7
    mov rsi, rcx
    mov rcx, QWORD PTR .refptr._ZSt4cout[rip]
    mov rbx, rdx
    lea rdx, .LC7[rip]
    call    std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)
    mov rcx, QWORD PTR .refptr._ZSt4cout[rip]
    lea rdx, .LC2[rip]
    mov r8d, 1
    call    std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)
    test    rbx, rbx
    je  .L78
    mov rcx, rbx
    call    strlen
    mov rcx, QWORD PTR .refptr._ZSt4cout[rip]
    mov rdx, rbx
    mov r8, rax
    call    std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)
    mov rbx, QWORD PTR .refptr._ZSt4cout[rip]
    mov rcx, rbx
.L73:
    lea rdx, .LC2[rip]
    mov r8d, 1
    call    std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)
    mov rax, QWORD PTR [rbx]
    mov rax, QWORD PTR -24[rax]
    mov rbx, QWORD PTR 240[rbx+rax]
    test    rbx, rbx
    je  .L79
    cmp BYTE PTR 56[rbx], 0
    je  .L75
    movsx   edx, BYTE PTR 67[rbx]
.L76:
    mov rcx, QWORD PTR .refptr._ZSt4cout[rip]
    call    std::basic_ostream<char, std::char_traits<char> >::put(char)
    mov rcx, rax
    call    std::basic_ostream<char, std::char_traits<char> >::flush()
    mov DWORD PTR [rsi], 0
    add rsp, 40
    pop rbx
    pop rsi
    ret
    .p2align 4,,10
.L75:
    mov rcx, rbx
    call    std::ctype<char>::_M_widen_init() const
    mov rax, QWORD PTR [rbx]
    lea rcx, std::ctype<char>::do_widen(char) const[rip]
    mov edx, 10
    mov rax, QWORD PTR 48[rax]
    cmp rax, rcx
    je  .L76
    mov rcx, rbx
    call    rax
    movsx   edx, al
    jmp .L76
    .p2align 4,,10
.L78:
    mov rbx, QWORD PTR .refptr._ZSt4cout[rip]
    mov rax, QWORD PTR [rbx]
    mov rcx, rbx
    add rcx, QWORD PTR -24[rax]
    mov edx, DWORD PTR 32[rcx]
    or  edx, 1
    call    std::basic_ios<char, std::char_traits<char> >::clear(std::_Ios_Iostate)
    mov rcx, QWORD PTR .refptr._ZSt4cout[rip]
    jmp .L73
.L79:
    call    std::__throw_bad_cast()
    nop
    .seh_endproc
    .section .rdata,"dr"
.LC8:
    .ascii "move_to_next\0"
    .section    .text$_ZN4CTQL8dummy_db12move_to_nextEv,"x"
    .linkonce discard
    .align 2
    .p2align 4,,15
    .globl  CTQL::dummy_db::move_to_next()
    .def    CTQL::dummy_db::move_to_next(); .scl    2;  .type   32; .endef
    .seh_proc   CTQL::dummy_db::move_to_next()
CTQL::dummy_db::move_to_next():
.LFB1969:
    push    rsi
    .seh_pushreg    rsi
    push    rbx
    .seh_pushreg    rbx
    sub rsp, 40
    .seh_stackalloc 40
    .seh_endprologue
    lea rdx, .LC8[rip]
    mov r8d, 12
    mov rsi, rcx
    mov rcx, QWORD PTR .refptr._ZSt4cout[rip]
    call    std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)
    mov rcx, QWORD PTR .refptr._ZSt4cout[rip]
    lea rdx, .LC2[rip]
    mov r8d, 1
    call    std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)
    mov rax, QWORD PTR .refptr._ZSt4cout[rip]
    mov rdx, QWORD PTR [rax]
    mov rdx, QWORD PTR -24[rdx]
    mov rbx, QWORD PTR 240[rdx+rax]
    test    rbx, rbx
    je  .L85
    cmp BYTE PTR 56[rbx], 0
    je  .L82
    movsx   edx, BYTE PTR 67[rbx]
.L83:
    mov rcx, QWORD PTR .refptr._ZSt4cout[rip]
    call    std::basic_ostream<char, std::char_traits<char> >::put(char)
    mov rcx, rax
    call    std::basic_ostream<char, std::char_traits<char> >::flush()
    add DWORD PTR [rsi], 1
    add rsp, 40
    pop rbx
    pop rsi
    ret
    .p2align 4,,10
.L82:
    mov rcx, rbx
    call    std::ctype<char>::_M_widen_init() const
    mov rax, QWORD PTR [rbx]
    lea rcx, std::ctype<char>::do_widen(char) const[rip]
    mov edx, 10
    mov rax, QWORD PTR 48[rax]
    cmp rax, rcx
    je  .L83
    mov rcx, rbx
    call    rax
    movsx   edx, al
    jmp .L83
.L85:
    call    std::__throw_bad_cast()
    nop
    .seh_endproc
    .text
    .align 2
    .p2align 4,,15
    .globl  int CTQL::dummy_db::get<int>(unsigned int) const
    .def    int CTQL::dummy_db::get<int>(unsigned int) const;   .scl    2;  .type   32; .endef
    .seh_proc   int CTQL::dummy_db::get<int>(unsigned int) const
int CTQL::dummy_db::get<int>(unsigned int) const:
.LFB1970:
    push    rdi
    .seh_pushreg    rdi
    push    rsi
    .seh_pushreg    rsi
    push    rbx
    .seh_pushreg    rbx
    sub rsp, 32
    .seh_stackalloc 32
    .seh_endprologue
    mov r8d, 8
    mov rdi, rcx
    mov rcx, QWORD PTR .refptr._ZSt4cout[rip]
    mov ebx, edx
    lea rdx, .LC6[rip]
    call    std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)
    mov rcx, QWORD PTR .refptr._ZSt4cout[rip]
    lea rdx, .LC2[rip]
    mov r8d, 1
    call    std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)
    mov rcx, QWORD PTR .refptr._ZSt4cout[rip]
    mov edx, ebx
    call    std::basic_ostream<char, std::char_traits<char> >& std::basic_ostream<char, std::char_traits<char> >::_M_insert<unsigned long>(unsigned long)
    lea rdx, .LC2[rip]
    mov r8d, 1
    mov rcx, rax
    call    std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)
    mov rax, QWORD PTR .refptr._ZSt4cout[rip]
    mov rdx, QWORD PTR [rax]
    mov rdx, QWORD PTR -24[rdx]
    mov rsi, QWORD PTR 240[rdx+rax]
    test    rsi, rsi
    je  .L91
    cmp BYTE PTR 56[rsi], 0
    je  .L88
    movsx   edx, BYTE PTR 67[rsi]
.L89:
    mov rcx, QWORD PTR .refptr._ZSt4cout[rip]
    imul    ebx, ebx, 100
    call    std::basic_ostream<char, std::char_traits<char> >::put(char)
    mov rcx, rax
    call    std::basic_ostream<char, std::char_traits<char> >::flush()
    mov eax, DWORD PTR [rdi]
    lea eax, [rax+rax*4]
    lea eax, [rbx+rax*2]
    add rsp, 32
    pop rbx
    pop rsi
    pop rdi
    ret
    .p2align 4,,10
.L88:
    mov rcx, rsi
    call    std::ctype<char>::_M_widen_init() const
    mov rax, QWORD PTR [rsi]
    lea rcx, std::ctype<char>::do_widen(char) const[rip]
    mov edx, 10
    mov rax, QWORD PTR 48[rax]
    cmp rax, rcx
    je  .L89
    mov rcx, rsi
    call    rax
    movsx   edx, al
    jmp .L89
.L91:
    call    std::__throw_bad_cast()
    nop
    .seh_endproc
    .align 2
    .p2align 4,,15
    .globl  double CTQL::dummy_db::get<double>(unsigned int) const
    .def    double CTQL::dummy_db::get<double>(unsigned int) const; .scl    2;  .type   32; .endef
    .seh_proc   double CTQL::dummy_db::get<double>(unsigned int) const
double CTQL::dummy_db::get<double>(unsigned int) const:
.LFB1971:
    push    rsi
    .seh_pushreg    rsi
    push    rbx
    .seh_pushreg    rbx
    sub rsp, 40
    .seh_stackalloc 40
    .seh_endprologue
    mov r8d, 11
    mov rsi, rcx
    mov rcx, QWORD PTR .refptr._ZSt4cout[rip]
    mov ebx, edx
    lea rdx, .LC3[rip]
    call    std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)
    mov rcx, QWORD PTR .refptr._ZSt4cout[rip]
    lea rdx, .LC2[rip]
    mov r8d, 1
    call    std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)
    mov rcx, QWORD PTR .refptr._ZSt4cout[rip]
    mov edx, ebx
    call    std::basic_ostream<char, std::char_traits<char> >& std::basic_ostream<char, std::char_traits<char> >::_M_insert<unsigned long>(unsigned long)
    lea rdx, .LC2[rip]
    mov r8d, 1
    mov rcx, rax
    call    std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)
    mov rax, QWORD PTR .refptr._ZSt4cout[rip]
    mov rdx, QWORD PTR [rax]
    mov rdx, QWORD PTR -24[rdx]
    mov rbx, QWORD PTR 240[rdx+rax]
    test    rbx, rbx
    je  .L99
    cmp BYTE PTR 56[rbx], 0
    je  .L94
    movsx   edx, BYTE PTR 67[rbx]
.L95:
    mov rcx, QWORD PTR .refptr._ZSt4cout[rip]
    call    std::basic_ostream<char, std::char_traits<char> >::put(char)
    mov rcx, rax
    call    std::basic_ostream<char, std::char_traits<char> >::flush()
    mov eax, DWORD PTR [rsi]
    pxor    xmm0, xmm0
    cvtsi2sdq   xmm0, rax
    mulsd   xmm0, QWORD PTR .LC4[rip]
    addsd   xmm0, QWORD PTR .LC5[rip]
    add rsp, 40
    pop rbx
    pop rsi
    ret
    .p2align 4,,10
.L94:
    mov rcx, rbx
    call    std::ctype<char>::_M_widen_init() const
    mov rax, QWORD PTR [rbx]
    lea rcx, std::ctype<char>::do_widen(char) const[rip]
    mov edx, 10
    mov rax, QWORD PTR 48[rax]
    cmp rax, rcx
    je  .L95
    mov rcx, rbx
    call    rax
    movsx   edx, al
    jmp .L95
.L99:
    call    std::__throw_bad_cast()
    nop
    .seh_endproc
    .section .rdata,"dr"
    .align 8
.LC9:
    .ascii "SELECT foo.value, foo.id, foo.number FROM foo WHERE foo.id>42\0"
    .text
    .p2align 4,,15
    .globl  handwritten()
    .def    handwritten();  .scl    2;  .type   32; .endef
    .seh_proc   handwritten()
handwritten():
.LFB2021:
    push    r12
    .seh_pushreg    r12
    push    rbp
    .seh_pushreg    rbp
    push    rdi
    .seh_pushreg    rdi
    push    rsi
    .seh_pushreg    rsi
    push    rbx
    .seh_pushreg    rbx
    sub rsp, 64
    .seh_stackalloc 64
    .seh_endprologue
    lea rbx, 32[rsp]
    mov rdi, rcx
    call    CTQL::vector_noinline<handwritten()::Row>::vector_noinline()
    lea rdx, .LC9[rip]
    mov rcx, rbx
    mov DWORD PTR 32[rsp], 0
.LEHB0:
    call    CTQL::dummy_db::execute(char const*)
    call    CTQL::dummy_db::read_count() const [clone .isra.32]
    mov edx, eax
    mov rcx, rdi
    mov rbp, rdx
    call    CTQL::vector_noinline<handwritten()::Row>::reserve(unsigned long) [clone .constprop.34]
    test    ebp, ebp
    je  .L100
    lea r12, 48[rsp]
    xor esi, esi
    .p2align 4,,10
.L103:
    mov rcx, rbx
    call    double CTQL::dummy_db::get<double>(unsigned int) const [clone .constprop.40]
    mov rcx, rbx
    movsd   QWORD PTR 48[rsp], xmm0
    call    int CTQL::dummy_db::get<int>(unsigned int) const [clone .constprop.41]
    mov rcx, rbx
    mov DWORD PTR 56[rsp], eax
    call    int CTQL::dummy_db::get<int>(unsigned int) const [clone .constprop.42]
    mov rdx, r12
    mov rcx, rdi
    mov DWORD PTR 60[rsp], eax
    call    CTQL::vector_noinline<handwritten()::Row>::push_back(handwritten()::Row const&) [clone .constprop.36]
    mov rcx, rbx
    add esi, 1
    call    CTQL::dummy_db::move_to_next()
.LEHE0:
    cmp ebp, esi
    jne .L103
.L100:
    mov rax, rdi
    add rsp, 64
    pop rbx
    pop rsi
    pop rdi
    pop rbp
    pop r12
    ret
.L106:
    mov rbx, rax
    mov rcx, rdi
    call    CTQL::vector_noinline<handwritten()::Row>::~vector_noinline()
    mov rcx, rbx
.LEHB1:
    call    _Unwind_Resume
    nop
.LEHE1:
    .def    __gxx_personality_seh0; .scl    2;  .type   32; .endef
    .seh_handler    __gxx_personality_seh0, @unwind, @except
    .seh_handlerdata
.LLSDA2021:
    .byte   0xff
    .byte   0xff
    .byte   0x1
    .uleb128 .LLSDACSE2021-.LLSDACSB2021
.LLSDACSB2021:
    .uleb128 .LEHB0-.LFB2021
    .uleb128 .LEHE0-.LEHB0
    .uleb128 .L106-.LFB2021
    .uleb128 0
    .uleb128 .LEHB1-.LFB2021
    .uleb128 .LEHE1-.LEHB1
    .uleb128 0
    .uleb128 0
.LLSDACSE2021:
    .text
    .seh_endproc
    .section    .text$_ZN4CTQL15vector_noinlineINS_8make_rowIJNS_12table_columnI7foo_auxdNS3_12value_column11field_valueEEENS2_IS3_iNS3_9id_column8field_idEEENS2_IS3_iNS3_13number_column12field_numberEEEEEEEC1Ev,"x"
    .linkonce discard
    .align 2
    .p2align 4,,15
    .globl  CTQL::vector_noinline<CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > >::vector_noinline()
    .def    CTQL::vector_noinline<CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > >::vector_noinline();    .scl    2;  .type   32; .endef
    .seh_proc   CTQL::vector_noinline<CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > >::vector_noinline()
CTQL::vector_noinline<CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > >::vector_noinline():
.LFB2128:
    .seh_endprologue
    mov QWORD PTR [rcx], 0
    mov QWORD PTR 8[rcx], 0
    mov QWORD PTR 16[rcx], 0
    ret
    .seh_endproc
    .weak   CTQL::vector_noinline<CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > >::vector_noinline()
    .def    CTQL::vector_noinline<CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > >::vector_noinline();    .scl    2;  .type   32; .endef
    .set    CTQL::vector_noinline<CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > >::vector_noinline(),CTQL::vector_noinline<CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > >::vector_noinline()
    .section    .text$_ZN4CTQL15vector_noinlineINS_8make_rowIJNS_12table_columnI7foo_auxdNS3_12value_column11field_valueEEENS2_IS3_iNS3_9id_column8field_idEEENS2_IS3_iNS3_13number_column12field_numberEEEEEEED1Ev,"x"
    .linkonce discard
    .align 2
    .p2align 4,,15
    .globl  CTQL::vector_noinline<CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > >::~vector_noinline()
    .def    CTQL::vector_noinline<CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > >::~vector_noinline();   .scl    2;  .type   32; .endef
    .seh_proc   CTQL::vector_noinline<CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > >::~vector_noinline()
CTQL::vector_noinline<CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > >::~vector_noinline():
.LFB2131:
    .seh_endprologue
    mov rcx, QWORD PTR [rcx]
    test    rcx, rcx
    je  .L113
    jmp operator delete(void*)
    .p2align 4,,10
.L113:
    rep ret
    .seh_endproc
    .weak   CTQL::vector_noinline<CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > >::~vector_noinline()
    .def    CTQL::vector_noinline<CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > >::~vector_noinline();   .scl    2;  .type   32; .endef
    .set    CTQL::vector_noinline<CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > >::~vector_noinline(),CTQL::vector_noinline<CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > >::~vector_noinline()
    .section    .text$_ZN4CTQL15vector_noinlineINS_8make_rowIJNS_12table_columnI7foo_auxdNS3_12value_column11field_valueEEENS2_IS3_iNS3_9id_column8field_idEEENS2_IS3_iNS3_13number_column12field_numberEEEEEEE7reserveEm,"x"
    .linkonce discard
    .align 2
    .p2align 4,,15
    .globl  CTQL::vector_noinline<CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > >::reserve(unsigned long)
    .def    CTQL::vector_noinline<CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > >::reserve(unsigned long);   .scl    2;  .type   32; .endef
    .seh_proc   CTQL::vector_noinline<CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > >::reserve(unsigned long)
CTQL::vector_noinline<CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > >::reserve(unsigned long):
.LFB2273:
    push    r13
    .seh_pushreg    r13
    push    r12
    .seh_pushreg    r12
    push    rbp
    .seh_pushreg    rbp
    push    rdi
    .seh_pushreg    rdi
    push    rsi
    .seh_pushreg    rsi
    push    rbx
    .seh_pushreg    rbx
    sub rsp, 40
    .seh_stackalloc 40
    .seh_endprologue
    movabs  rax, 1152921504606846975
    cmp rdx, rax
    mov rsi, rcx
    ja  .L126
    mov rbp, QWORD PTR [rcx]
    mov rax, QWORD PTR 16[rcx]
    sub rax, rbp
    sar rax, 4
    cmp rdx, rax
    ja  .L127
    add rsp, 40
    pop rbx
    pop rsi
    pop rdi
    pop rbp
    pop r12
    pop r13
    ret
    .p2align 4,,10
.L127:
    mov rdi, QWORD PTR 8[rcx]
    sub rdi, rbp
    test    rdx, rdx
    je  .L122
    mov rbx, rdx
    sal rbx, 4
    mov rcx, rbx
    call    operator new(unsigned long)
    mov r13, QWORD PTR [rsi]
    mov r12, rax
.L118:
    mov rax, rdi
    sar rax, 4
    test    rax, rax
    je  .L120
    mov r8, rdi
    mov rdx, rbp
    mov rcx, r12
    call    memmove
.L120:
    test    r13, r13
    je  .L121
    mov rcx, r13
    call    operator delete(void*)
.L121:
    lea rdx, [r12+rbx]
    add rdi, r12
    mov QWORD PTR [rsi], r12
    mov QWORD PTR 8[rsi], rdi
    mov QWORD PTR 16[rsi], rdx
    add rsp, 40
    pop rbx
    pop rsi
    pop rdi
    pop rbp
    pop r12
    pop r13
    ret
    .p2align 4,,10
.L122:
    mov r13, rbp
    xor ebx, ebx
    xor r12d, r12d
    jmp .L118
.L126:
    lea rcx, .LC0[rip]
    call    std::__throw_length_error(char const*)
    nop
    .seh_endproc
    .weak   CTQL::vector_noinline<CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > >::reserve(unsigned long)
    .def    CTQL::vector_noinline<CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > >::reserve(unsigned long);   .scl    2;  .type   32; .endef
    .set    CTQL::vector_noinline<CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > >::reserve(unsigned long),CTQL::vector_noinline<CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > >::reserve(unsigned long)
    .section .rdata,"dr"
.LC10:
    .ascii "ROW: \0"
    .section    .text$_Z5printIN4CTQL15vector_noinlineINS0_8make_rowIJNS0_12table_columnI7foo_auxdNS4_12value_column11field_valueEEENS3_IS4_iNS4_9id_column8field_idEEENS3_IS4_iNS4_13number_column12field_numberEEEEEEEEEvOT_,"x"
    .linkonce discard
    .p2align 4,,15
    .globl  void print<CTQL::vector_noinline<CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > > >(CTQL::vector_noinline<CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > >&&)
    .def    void print<CTQL::vector_noinline<CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > > >(CTQL::vector_noinline<CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > >&&);  .scl    2;  .type   32; .endef
    .seh_proc   void print<CTQL::vector_noinline<CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > > >(CTQL::vector_noinline<CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > >&&)
void print<CTQL::vector_noinline<CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > > >(CTQL::vector_noinline<CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > >&&):
.LFB2280:
    push    r14
    .seh_pushreg    r14
    push    r13
    .seh_pushreg    r13
    push    r12
    .seh_pushreg    r12
    push    rbp
    .seh_pushreg    rbp
    push    rdi
    .seh_pushreg    rdi
    push    rsi
    .seh_pushreg    rsi
    push    rbx
    .seh_pushreg    rbx
    sub rsp, 48
    .seh_stackalloc 48
    movaps  XMMWORD PTR 32[rsp], xmm6
    .seh_savexmm    xmm6, 32
    .seh_endprologue
    mov rbx, QWORD PTR [rcx]
    mov rbp, QWORD PTR 8[rcx]
    cmp rbx, rbp
    je  .L128
    mov r12, QWORD PTR .refptr._ZSt4cout[rip]
    lea r14, std::ctype<char>::do_widen(char) const[rip]
    mov r13, r12
    jmp .L136
    .p2align 4,,10
.L139:
    movsx   edx, BYTE PTR 67[rsi]
.L132:
    mov rcx, rdi
    add rbx, 16
    call    std::basic_ostream<char, std::char_traits<char> >::put(char)
    mov rcx, rax
    call    std::basic_ostream<char, std::char_traits<char> >::flush()
    cmp rbp, rbx
    je  .L128
.L136:
    mov esi, DWORD PTR 8[rbx]
    lea rdx, .LC10[rip]
    mov r8d, 5
    mov rcx, r12
    mov edi, DWORD PTR 12[rbx]
    movsd   xmm6, QWORD PTR [rbx]
    call    std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)
    mov edx, esi
    mov rcx, r13
    call    std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
    lea rdx, .LC2[rip]
    mov r8d, 1
    mov rcx, rax
    mov rsi, rax
    call    std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)
    movapd  xmm1, xmm6
    mov rcx, rsi
    call    std::basic_ostream<char, std::char_traits<char> >& std::basic_ostream<char, std::char_traits<char> >::_M_insert<double>(double)
    lea rdx, .LC2[rip]
    mov rsi, rax
    mov r8d, 1
    mov rcx, rax
    call    std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)
    mov edx, edi
    mov rcx, rsi
    call    std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
    mov rdi, rax
    mov rax, QWORD PTR [rax]
    mov rax, QWORD PTR -24[rax]
    mov rsi, QWORD PTR 240[rdi+rax]
    test    rsi, rsi
    je  .L138
    cmp BYTE PTR 56[rsi], 0
    jne .L139
    mov rcx, rsi
    call    std::ctype<char>::_M_widen_init() const
    mov rax, QWORD PTR [rsi]
    mov edx, 10
    mov rax, QWORD PTR 48[rax]
    cmp rax, r14
    je  .L132
    mov rcx, rsi
    call    rax
    movsx   edx, al
    jmp .L132
    .p2align 4,,10
.L128:
    movaps  xmm6, XMMWORD PTR 32[rsp]
    add rsp, 48
    pop rbx
    pop rsi
    pop rdi
    pop rbp
    pop r12
    pop r13
    pop r14
    ret
.L138:
    call    std::__throw_bad_cast()
    nop
    .seh_endproc
    .weak   void print<CTQL::vector_noinline<CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > > >(CTQL::vector_noinline<CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > >&&)
    .def    void print<CTQL::vector_noinline<CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > > >(CTQL::vector_noinline<CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > >&&);  .scl    2;  .type   32; .endef
    .set    void print<CTQL::vector_noinline<CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > > >(CTQL::vector_noinline<CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > >&&),void print<CTQL::vector_noinline<CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > > >(CTQL::vector_noinline<CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > >&&)
    .section    .text$_ZNSt6vectorIN4CTQL8make_rowIJNS0_12table_columnI7foo_auxdNS3_12value_column11field_valueEEENS2_IS3_iNS3_9id_column8field_idEEENS2_IS3_iNS3_13number_column12field_numberEEEEEESaISD_EE19_M_emplace_back_auxIJRKSD_EEEvDpOT_,"x"
    .linkonce discard
    .align 2
    .p2align 4,,15
    .globl  void std::vector<CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> >, std::allocator<CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > > >::_M_emplace_back_aux<CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > const&>(CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > const&)
    .def    void std::vector<CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> >, std::allocator<CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > > >::_M_emplace_back_aux<CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > const&>(CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > const&);  .scl    2;  .type   32; .endef
    .seh_proc   void std::vector<CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> >, std::allocator<CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > > >::_M_emplace_back_aux<CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > const&>(CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > const&)
void std::vector<CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> >, std::allocator<CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > > >::_M_emplace_back_aux<CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > const&>(CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > const&):
.LFB2493:
    push    r13
    .seh_pushreg    r13
    push    r12
    .seh_pushreg    r12
    push    rbp
    .seh_pushreg    rbp
    push    rdi
    .seh_pushreg    rdi
    push    rsi
    .seh_pushreg    rsi
    push    rbx
    .seh_pushreg    rbx
    sub rsp, 40
    .seh_stackalloc 40
    .seh_endprologue
    mov r8, QWORD PTR 8[rcx]
    mov rbx, rcx
    mov r13, rdx
    mov rax, r8
    sub rax, QWORD PTR [rcx]
    sar rax, 4
    test    rax, rax
    je  .L148
    lea rdx, [rax+rax]
    cmp rax, rdx
    jbe .L159
.L149:
    mov rbp, -16
    jmp .L141
    .p2align 4,,10
.L148:
    mov ebp, 16
.L141:
    mov rcx, rbp
    call    operator new(unsigned long)
    mov r8, QWORD PTR 8[rbx]
    mov rsi, rax
.L147:
    mov r12, QWORD PTR [rbx]
    mov rdi, rsi
    sub r8, r12
    mov rax, r8
    sar rax, 4
    add rdi, r8
    je  .L143
    mov r9, QWORD PTR 0[r13]
    mov r10, QWORD PTR 8[r13]
    mov QWORD PTR [rdi], r9
    mov QWORD PTR 8[rdi], r10
.L143:
    test    rax, rax
    jne .L160
    add rdi, 16
    test    r12, r12
    je  .L146
.L145:
    mov rcx, r12
    call    operator delete(void*)
.L146:
    mov QWORD PTR [rbx], rsi
    add rsi, rbp
    mov QWORD PTR 8[rbx], rdi
    mov QWORD PTR 16[rbx], rsi
    add rsp, 40
    pop rbx
    pop rsi
    pop rdi
    pop rbp
    pop r12
    pop r13
    ret
    .p2align 4,,10
.L160:
    mov rdx, r12
    mov rcx, rsi
    add rdi, 16
    call    memmove
    jmp .L145
.L159:
    movabs  rcx, 1152921504606846975
    cmp rdx, rcx
    ja  .L149
    xor ebp, ebp
    xor esi, esi
    test    rdx, rdx
    je  .L147
    sal rax, 5
    mov rbp, rax
    jmp .L141
    .seh_endproc
    .weak   void std::vector<CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> >, std::allocator<CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > > >::_M_emplace_back_aux<CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > const&>(CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > const&)
    .def    void std::vector<CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> >, std::allocator<CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > > >::_M_emplace_back_aux<CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > const&>(CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > const&);  .scl    2;  .type   32; .endef
    .set    void std::vector<CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> >, std::allocator<CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > > >::_M_emplace_back_aux<CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > const&>(CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > const&),void std::vector<CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> >, std::allocator<CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > > >::_M_emplace_back_aux<CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > const&>(CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > const&)
    .section    .text$_ZN4CTQL15vector_noinlineINS_8make_rowIJNS_12table_columnI7foo_auxdNS3_12value_column11field_valueEEENS2_IS3_iNS3_9id_column8field_idEEENS2_IS3_iNS3_13number_column12field_numberEEEEEEE9push_backERKSD_,"x"
    .linkonce discard
    .align 2
    .p2align 4,,15
    .globl  CTQL::vector_noinline<CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > >::push_back(CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > const&)
    .def    CTQL::vector_noinline<CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > >::push_back(CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > const&);   .scl    2;  .type   32; .endef
    .seh_proc   CTQL::vector_noinline<CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > >::push_back(CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > const&)
CTQL::vector_noinline<CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > >::push_back(CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > const&):
.LFB2275:
    .seh_endprologue
    mov rax, QWORD PTR 8[rcx]
    cmp rax, QWORD PTR 16[rcx]
    je  .L162
    test    rax, rax
    je  .L163
    mov r9, QWORD PTR [rdx]
    mov r10, QWORD PTR 8[rdx]
    mov QWORD PTR [rax], r9
    mov QWORD PTR 8[rax], r10
.L163:
    add rax, 16
    mov QWORD PTR 8[rcx], rax
    ret
    .p2align 4,,10
.L162:
    jmp void std::vector<CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> >, std::allocator<CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > > >::_M_emplace_back_aux<CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > const&>(CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > const&)
    .seh_endproc
    .weak   CTQL::vector_noinline<CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > >::push_back(CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > const&)
    .def    CTQL::vector_noinline<CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > >::push_back(CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > const&);   .scl    2;  .type   32; .endef
    .set    CTQL::vector_noinline<CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > >::push_back(CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > const&),CTQL::vector_noinline<CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > >::push_back(CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > const&)
    .section    .text$_ZN4CTQL9to_vectorINS_10make_tableIN7foo_aux11name_stringEJNS2_9id_columnENS2_11code_columnENS2_12value_columnENS2_13number_columnEEEEKNS_8string_tIJLc102ELc111ELc111ELc46ELc105ELc100ELc62ELc52ELc50EEEEJNS_12table_columnIS2_dNS6_11field_valueEEENSC_IS2_iNS4_8field_idEEENSC_IS2_iNS7_12field_numberEEEEEEDaNS_5queryIT_T0_JDpT1_EEE,"x"
    .linkonce discard
    .p2align 4,,15
    .globl  auto CTQL::to_vector<CTQL::make_table<foo_aux::name_string, foo_aux::id_column, foo_aux::code_column, foo_aux::value_column, foo_aux::number_column>, CTQL::string_t<(char)102, (char)111, (char)111, (char)46, (char)105, (char)100, (char)62, (char)52, (char)50> const, CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> >(CTQL::query<CTQL::make_table<foo_aux::name_string, foo_aux::id_column, foo_aux::code_column, foo_aux::value_column, foo_aux::number_column>, CTQL::string_t<(char)102, (char)111, (char)111, (char)46, (char)105, (char)100, (char)62, (char)52, (char)50> const, CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> >)
    .def    auto CTQL::to_vector<CTQL::make_table<foo_aux::name_string, foo_aux::id_column, foo_aux::code_column, foo_aux::value_column, foo_aux::number_column>, CTQL::string_t<(char)102, (char)111, (char)111, (char)46, (char)105, (char)100, (char)62, (char)52, (char)50> const, CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> >(CTQL::query<CTQL::make_table<foo_aux::name_string, foo_aux::id_column, foo_aux::code_column, foo_aux::value_column, foo_aux::number_column>, CTQL::string_t<(char)102, (char)111, (char)111, (char)46, (char)105, (char)100, (char)62, (char)52, (char)50> const, CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> >);  .scl    2;  .type   32; .endef
    .seh_proc   auto CTQL::to_vector<CTQL::make_table<foo_aux::name_string, foo_aux::id_column, foo_aux::code_column, foo_aux::value_column, foo_aux::number_column>, CTQL::string_t<(char)102, (char)111, (char)111, (char)46, (char)105, (char)100, (char)62, (char)52, (char)50> const, CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> >(CTQL::query<CTQL::make_table<foo_aux::name_string, foo_aux::id_column, foo_aux::code_column, foo_aux::value_column, foo_aux::number_column>, CTQL::string_t<(char)102, (char)111, (char)111, (char)46, (char)105, (char)100, (char)62, (char)52, (char)50> const, CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> >)
auto CTQL::to_vector<CTQL::make_table<foo_aux::name_string, foo_aux::id_column, foo_aux::code_column, foo_aux::value_column, foo_aux::number_column>, CTQL::string_t<(char)102, (char)111, (char)111, (char)46, (char)105, (char)100, (char)62, (char)52, (char)50> const, CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> >(CTQL::query<CTQL::make_table<foo_aux::name_string, foo_aux::id_column, foo_aux::code_column, foo_aux::value_column, foo_aux::number_column>, CTQL::string_t<(char)102, (char)111, (char)111, (char)46, (char)105, (char)100, (char)62, (char)52, (char)50> const, CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> >):
.LFB2067:
    push    r12
    .seh_pushreg    r12
    push    rbp
    .seh_pushreg    rbp
    push    rdi
    .seh_pushreg    rdi
    push    rsi
    .seh_pushreg    rsi
    push    rbx
    .seh_pushreg    rbx
    sub rsp, 64
    .seh_stackalloc 64
    .seh_endprologue
    lea rbx, 32[rsp]
    mov rdi, rcx
    call    CTQL::vector_noinline<CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > >::vector_noinline()
    lea rdx, CTQL::string_value<(char)83, (char)69, (char)76, (char)69, (char)67, (char)84, (char)32, (char)102, (char)111, (char)111, (char)46, (char)118, (char)97, (char)108, (char)117, (char)101, (char)44, (char)32, (char)102, (char)111, (char)111, (char)46, (char)105, (char)100, (char)44, (char)32, (char)102, (char)111, (char)111, (char)46, (char)110, (char)117, (char)109, (char)98, (char)101, (char)114, (char)32, (char)70, (char)82, (char)79, (char)77, (char)32, (char)102, (char)111, (char)111, (char)32, (char)87, (char)72, (char)69, (char)82, (char)69, (char)32, (char)102, (char)111, (char)111, (char)46, (char)105, (char)100, (char)62, (char)52, (char)50>::value[rip]
    mov rcx, rbx
    mov DWORD PTR 32[rsp], 0
.LEHB2:
    call    CTQL::dummy_db::execute(char const*)
    call    CTQL::dummy_db::read_count() const [clone .isra.32]
    mov edx, eax
    mov rcx, rdi
    mov rbp, rdx
    call    CTQL::vector_noinline<CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > >::reserve(unsigned long)
    test    ebp, ebp
    je  .L169
    lea r12, 48[rsp]
    xor esi, esi
    .p2align 4,,10
.L172:
    mov rcx, rbx
    call    double CTQL::dummy_db::get<double>(unsigned int) const [clone .constprop.40]
    mov rcx, rbx
    movsd   QWORD PTR 48[rsp], xmm0
    call    int CTQL::dummy_db::get<int>(unsigned int) const [clone .constprop.41]
    mov rcx, rbx
    mov DWORD PTR 56[rsp], eax
    call    int CTQL::dummy_db::get<int>(unsigned int) const [clone .constprop.42]
    mov rdx, r12
    mov rcx, rdi
    mov DWORD PTR 60[rsp], eax
    call    CTQL::vector_noinline<CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > >::push_back(CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > const&)
    mov rcx, rbx
    add esi, 1
    call    CTQL::dummy_db::move_to_next()
.LEHE2:
    cmp ebp, esi
    jne .L172
.L169:
    mov rax, rdi
    add rsp, 64
    pop rbx
    pop rsi
    pop rdi
    pop rbp
    pop r12
    ret
.L175:
    mov rbx, rax
    mov rcx, rdi
    call    CTQL::vector_noinline<CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > >::~vector_noinline()
    mov rcx, rbx
.LEHB3:
    call    _Unwind_Resume
    nop
.LEHE3:
    .seh_handler    __gxx_personality_seh0, @unwind, @except
    .seh_handlerdata
.LLSDA2067:
    .byte   0xff
    .byte   0xff
    .byte   0x1
    .uleb128 .LLSDACSE2067-.LLSDACSB2067
.LLSDACSB2067:
    .uleb128 .LEHB2-.LFB2067
    .uleb128 .LEHE2-.LEHB2
    .uleb128 .L175-.LFB2067
    .uleb128 0
    .uleb128 .LEHB3-.LFB2067
    .uleb128 .LEHE3-.LEHB3
    .uleb128 0
    .uleb128 0
.LLSDACSE2067:
    .section    .text$_ZN4CTQL9to_vectorINS_10make_tableIN7foo_aux11name_stringEJNS2_9id_columnENS2_11code_columnENS2_12value_columnENS2_13number_columnEEEEKNS_8string_tIJLc102ELc111ELc111ELc46ELc105ELc100ELc62ELc52ELc50EEEEJNS_12table_columnIS2_dNS6_11field_valueEEENSC_IS2_iNS4_8field_idEEENSC_IS2_iNS7_12field_numberEEEEEEDaNS_5queryIT_T0_JDpT1_EEE,"x"
    .linkonce discard
    .seh_endproc
    .weak   auto CTQL::to_vector<CTQL::make_table<foo_aux::name_string, foo_aux::id_column, foo_aux::code_column, foo_aux::value_column, foo_aux::number_column>, CTQL::string_t<(char)102, (char)111, (char)111, (char)46, (char)105, (char)100, (char)62, (char)52, (char)50> const, CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> >(CTQL::query<CTQL::make_table<foo_aux::name_string, foo_aux::id_column, foo_aux::code_column, foo_aux::value_column, foo_aux::number_column>, CTQL::string_t<(char)102, (char)111, (char)111, (char)46, (char)105, (char)100, (char)62, (char)52, (char)50> const<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > >)
    .def    auto CTQL::to_vector<CTQL::make_table<foo_aux::name_string, foo_aux::id_column, foo_aux::code_column, foo_aux::value_column, foo_aux::number_column>, CTQL::string_t<(char)102, (char)111, (char)111, (char)46, (char)105, (char)100, (char)62, (char)52, (char)50> const, CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> >(CTQL::query<CTQL::make_table<foo_aux::name_string, foo_aux::id_column, foo_aux::code_column, foo_aux::value_column, foo_aux::number_column>, CTQL::string_t<(char)102, (char)111, (char)111, (char)46, (char)105, (char)100, (char)62, (char)52, (char)50> const<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > >); .scl    2;  .type   32; .endef
    .set    auto CTQL::to_vector<CTQL::make_table<foo_aux::name_string, foo_aux::id_column, foo_aux::code_column, foo_aux::value_column, foo_aux::number_column>, CTQL::string_t<(char)102, (char)111, (char)111, (char)46, (char)105, (char)100, (char)62, (char)52, (char)50> const, CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> >(CTQL::query<CTQL::make_table<foo_aux::name_string, foo_aux::id_column, foo_aux::code_column, foo_aux::value_column, foo_aux::number_column>, CTQL::string_t<(char)102, (char)111, (char)111, (char)46, (char)105, (char)100, (char)62, (char)52, (char)50> const<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > >),auto CTQL::to_vector<CTQL::make_table<foo_aux::name_string, foo_aux::id_column, foo_aux::code_column, foo_aux::value_column, foo_aux::number_column>, CTQL::string_t<(char)102, (char)111, (char)111, (char)46, (char)105, (char)100, (char)62, (char)52, (char)50> const, CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> >(CTQL::query<CTQL::make_table<foo_aux::name_string, foo_aux::id_column, foo_aux::code_column, foo_aux::value_column, foo_aux::number_column>, CTQL::string_t<(char)102, (char)111, (char)111, (char)46, (char)105, (char)100, (char)62, (char)52, (char)50> const, CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> >)
    .def    __main; .scl    2;  .type   32; .endef
    .section    .text.startup,"x"
    .p2align 4,,15
    .globl  main
    .def    main;   .scl    2;  .type   32; .endef
    .seh_proc   main
main:
.LFB2035:
    push    r15
    .seh_pushreg    r15
    push    r14
    .seh_pushreg    r14
    push    r13
    .seh_pushreg    r13
    push    r12
    .seh_pushreg    r12
    push    rbp
    .seh_pushreg    rbp
    push    rdi
    .seh_pushreg    rdi
    push    rsi
    .seh_pushreg    rsi
    push    rbx
    .seh_pushreg    rbx
    sub rsp, 88
    .seh_stackalloc 88
    movaps  XMMWORD PTR 64[rsp], xmm6
    .seh_savexmm    xmm6, 64
    .seh_endprologue
    lea r14, 32[rsp]
    call    __main
    mov rcx, r14
    xor edx, edx
.LEHB4:
    call    auto CTQL::to_vector<CTQL::make_table<foo_aux::name_string, foo_aux::id_column, foo_aux::code_column, foo_aux::value_column, foo_aux::number_column>, CTQL::string_t<(char)102, (char)111, (char)111, (char)46, (char)105, (char)100, (char)62, (char)52, (char)50> const, CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> >(CTQL::query<CTQL::make_table<foo_aux::name_string, foo_aux::id_column, foo_aux::code_column, foo_aux::value_column, foo_aux::number_column>, CTQL::string_t<(char)102, (char)111, (char)111, (char)46, (char)105, (char)100, (char)62, (char)52, (char)50> const, CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> >)
.LEHE4:
    mov rcx, r14
.LEHB5:
    call    void print<CTQL::vector_noinline<CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > > >(CTQL::vector_noinline<CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > >&&)
.LEHE5:
    mov rcx, r14
    call    CTQL::vector_noinline<CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > >::~vector_noinline()
    mov rcx, QWORD PTR .refptr._ZSt4cout[rip]
.LEHB6:
    call    std::basic_ostream<char, std::char_traits<char> >& std::endl<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&)
    mov rcx, r14
    call    handwritten()
.LEHE6:
    mov rbx, QWORD PTR 32[rsp]
    mov rbp, QWORD PTR 40[rsp]
    cmp rbx, rbp
    je  .L187
    mov r12, QWORD PTR .refptr._ZSt4cout[rip]
    lea r15, std::ctype<char>::do_widen(char) const[rip]
    mov r13, r12
    jmp .L193
    .p2align 4,,10
.L197:
    movsx   edx, BYTE PTR 67[rsi]
.L185:
    mov rcx, rdi
.LEHB7:
    call    std::basic_ostream<char, std::char_traits<char> >::put(char)
    mov rcx, rax
    call    std::basic_ostream<char, std::char_traits<char> >::flush()
    add rbx, 16
    cmp rbp, rbx
    je  .L187
.L193:
    lea rdx, .LC10[rip]
    mov r8d, 5
    mov rcx, r12
    mov edi, DWORD PTR 12[rbx]
    movsd   xmm6, QWORD PTR [rbx]
    mov esi, DWORD PTR 8[rbx]
    call    std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)
    mov edx, esi
    mov rcx, r13
    call    std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
    lea rdx, .LC2[rip]
    mov r8d, 1
    mov rcx, rax
    mov rsi, rax
    call    std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)
    movapd  xmm1, xmm6
    mov rcx, rsi
    call    std::basic_ostream<char, std::char_traits<char> >& std::basic_ostream<char, std::char_traits<char> >::_M_insert<double>(double)
    lea rdx, .LC2[rip]
    mov r8d, 1
    mov rcx, rax
    mov rsi, rax
    call    std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)
    mov edx, edi
    mov rcx, rsi
    call    std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
    mov rdi, rax
    mov rax, QWORD PTR [rax]
    mov rax, QWORD PTR -24[rax]
    mov rsi, QWORD PTR 240[rdi+rax]
    test    rsi, rsi
    je  .L196
    cmp BYTE PTR 56[rsi], 0
    jne .L197
    mov rcx, rsi
    call    std::ctype<char>::_M_widen_init() const
    mov rax, QWORD PTR [rsi]
    mov edx, 10
    mov rax, QWORD PTR 48[rax]
    cmp rax, r15
    je  .L185
    mov rcx, rsi
    call    rax
    movsx   edx, al
    jmp .L185
    .p2align 4,,10
.L187:
    mov rcx, r14
    call    CTQL::vector_noinline<handwritten()::Row>::~vector_noinline()
    nop
    movaps  xmm6, XMMWORD PTR 64[rsp]
    xor eax, eax
    add rsp, 88
    pop rbx
    pop rsi
    pop rdi
    pop rbp
    pop r12
    pop r13
    pop r14
    pop r15
    ret
.L196:
    call    std::__throw_bad_cast()
.LEHE7:
.L192:
    mov rbx, rax
    mov rcx, r14
    call    CTQL::vector_noinline<handwritten()::Row>::~vector_noinline()
    mov rcx, rbx
.LEHB8:
    call    _Unwind_Resume
.L191:
    mov rbx, rax
    mov rcx, r14
    call    CTQL::vector_noinline<CTQL::make_row<CTQL::table_column<foo_aux, double, foo_aux::value_column::field_value>, CTQL::table_column<foo_aux, int, foo_aux::id_column::field_id>, CTQL::table_column<foo_aux, int, foo_aux::number_column::field_number> > >::~vector_noinline()
    mov rcx, rbx
    call    _Unwind_Resume
    nop
.LEHE8:
    .seh_handler    __gxx_personality_seh0, @unwind, @except
    .seh_handlerdata
.LLSDA2035:
    .byte   0xff
    .byte   0xff
    .byte   0x1
    .uleb128 .LLSDACSE2035-.LLSDACSB2035
.LLSDACSB2035:
    .uleb128 .LEHB4-.LFB2035
    .uleb128 .LEHE4-.LEHB4
    .uleb128 0
    .uleb128 0
    .uleb128 .LEHB5-.LFB2035
    .uleb128 .LEHE5-.LEHB5
    .uleb128 .L191-.LFB2035
    .uleb128 0
    .uleb128 .LEHB6-.LFB2035
    .uleb128 .LEHE6-.LEHB6
    .uleb128 0
    .uleb128 0
    .uleb128 .LEHB7-.LFB2035
    .uleb128 .LEHE7-.LEHB7
    .uleb128 .L192-.LFB2035
    .uleb128 0
    .uleb128 .LEHB8-.LFB2035
    .uleb128 .LEHE8-.LEHB8
    .uleb128 0
    .uleb128 0
.LLSDACSE2035:
    .section    .text.startup,"x"
    .seh_endproc
    .p2align 4,,15
    .def    _GLOBAL__sub_I__ZNK4CTQL8dummy_db3getIiEET_j;   .scl    3;  .type   32; .endef
    .seh_proc   _GLOBAL__sub_I__ZNK4CTQL8dummy_db3getIiEET_j
_GLOBAL__sub_I__ZNK4CTQL8dummy_db3getIiEET_j:
.LFB2663:
    sub rsp, 40
    .seh_stackalloc 40
    .seh_endprologue
    lea rcx, std::__ioinit[rip]
    call    std::ios_base::Init::Init()
    mov r8, QWORD PTR .refptr.__dso_handle[rip]
    lea rdx, std::__ioinit[rip]
    lea rcx, std::ios_base::Init::~Init()[rip]
    add rsp, 40
    jmp __cxa_atexit
    .seh_endproc
    .section    .ctors,"w"
    .align 8
    .quad   _GLOBAL__sub_I__ZNK4CTQL8dummy_db3getIiEET_j
    .globl  CTQL::string_value<(char)83, (char)69, (char)76, (char)69, (char)67, (char)84, (char)32, (char)102, (char)111, (char)111, (char)46, (char)118, (char)97, (char)108, (char)117, (char)101, (char)44, (char)32, (char)102, (char)111, (char)111, (char)46, (char)105, (char)100, (char)44, (char)32, (char)102, (char)111, (char)111, (char)46, (char)110, (char)117, (char)109, (char)98, (char)101, (char)114, (char)32, (char)70, (char)82, (char)79, (char)77, (char)32, (char)102, (char)111, (char)111, (char)32, (char)87, (char)72, (char)69, (char)82, (char)69, (char)32, (char)102, (char)111, (char)111, (char)46, (char)105, (char)100, (char)62, (char)52, (char)50>::value
    .section    .rdata$_ZN4CTQL12string_valueIJLc83ELc69ELc76ELc69ELc67ELc84ELc32ELc102ELc111ELc111ELc46ELc118ELc97ELc108ELc117ELc101ELc44ELc32ELc102ELc111ELc111ELc46ELc105ELc100ELc44ELc32ELc102ELc111ELc111ELc46ELc110ELc117ELc109ELc98ELc101ELc114ELc32ELc70ELc82ELc79ELc77ELc32ELc102ELc111ELc111ELc32ELc87ELc72ELc69ELc82ELc69ELc32ELc102ELc111ELc111ELc46ELc105ELc100ELc62ELc52ELc50EEE5valueE,"dr"
    .linkonce same_size
    .align 32
CTQL::string_value<(char)83, (char)69, (char)76, (char)69, (char)67, (char)84, (char)32, (char)102, (char)111, (char)111, (char)46, (char)118, (char)97, (char)108, (char)117, (char)101, (char)44, (char)32, (char)102, (char)111, (char)111, (char)46, (char)105, (char)100, (char)44, (char)32, (char)102, (char)111, (char)111, (char)46, (char)110, (char)117, (char)109, (char)98, (char)101, (char)114, (char)32, (char)70, (char)82, (char)79, (char)77, (char)32, (char)102, (char)111, (char)111, (char)32, (char)87, (char)72, (char)69, (char)82, (char)69, (char)32, (char)102, (char)111, (char)111, (char)46, (char)105, (char)100, (char)62, (char)52, (char)50>::value:
    .byte   83
    .byte   69
    .byte   76
    .byte   69
    .byte   67
    .byte   84
    .byte   32
    .byte   102
    .byte   111
    .byte   111
    .byte   46
    .byte   118
    .byte   97
    .byte   108
    .byte   117
    .byte   101
    .byte   44
    .byte   32
    .byte   102
    .byte   111
    .byte   111
    .byte   46
    .byte   105
    .byte   100
    .byte   44
    .byte   32
    .byte   102
    .byte   111
    .byte   111
    .byte   46
    .byte   110
    .byte   117
    .byte   109
    .byte   98
    .byte   101
    .byte   114
    .byte   32
    .byte   70
    .byte   82
    .byte   79
    .byte   77
    .byte   32
    .byte   102
    .byte   111
    .byte   111
    .byte   32
    .byte   87
    .byte   72
    .byte   69
    .byte   82
    .byte   69
    .byte   32
    .byte   102
    .byte   111
    .byte   111
    .byte   46
    .byte   105
    .byte   100
    .byte   62
    .byte   52
    .byte   50
    .byte   0
    .globl  CTQL::string_value<(char)83, (char)69, (char)76, (char)69, (char)67, (char)84, (char)32, (char)102, (char)111, (char)111, (char)46, (char)118, (char)97, (char)108, (char)117, (char)101, (char)44, (char)32, (char)102, (char)111, (char)111, (char)46, (char)105, (char)100, (char)44, (char)32, (char)102, (char)111, (char)111, (char)46, (char)110, (char)117, (char)109, (char)98, (char)101, (char)114, (char)32, (char)70, (char)82, (char)79, (char)77, (char)32, (char)102, (char)111, (char)111, (char)32, (char)87, (char)72, (char)69, (char)82, (char)69, (char)32, (char)102, (char)111, (char)111, (char)46, (char)105, (char)100, (char)62, (char)52, (char)50>::value
    .set    CTQL::string_value<(char)83, (char)69, (char)76, (char)69, (char)67, (char)84, (char)32, (char)102, (char)111, (char)111, (char)46, (char)118, (char)97, (char)108, (char)117, (char)101, (char)44, (char)32, (char)102, (char)111, (char)111, (char)46, (char)105, (char)100, (char)44, (char)32, (char)102, (char)111, (char)111, (char)46, (char)110, (char)117, (char)109, (char)98, (char)101, (char)114, (char)32, (char)70, (char)82, (char)79, (char)77, (char)32, (char)102, (char)111, (char)111, (char)32, (char)87, (char)72, (char)69, (char)82, (char)69, (char)32, (char)102, (char)111, (char)111, (char)46, (char)105, (char)100, (char)62, (char)52, (char)50>::value,CTQL::string_value<(char)83, (char)69, (char)76, (char)69, (char)67, (char)84, (char)32, (char)102, (char)111, (char)111, (char)46, (char)118, (char)97, (char)108, (char)117, (char)101, (char)44, (char)32, (char)102, (char)111, (char)111, (char)46, (char)105, (char)100, (char)44, (char)32, (char)102, (char)111, (char)111, (char)46, (char)110, (char)117, (char)109, (char)98, (char)101, (char)114, (char)32, (char)70, (char)82, (char)79, (char)77, (char)32, (char)102, (char)111, (char)111, (char)32, (char)87, (char)72, (char)69, (char)82, (char)69, (char)32, (char)102, (char)111, (char)111, (char)46, (char)105, (char)100, (char)62, (char)52, (char)50>::value
    .globl  foo
    .bss
foo:
    .space 4
.lcomm std::__ioinit,1,1
    .section .rdata,"dr"
    .align 8
.LC4:
    .long   1202590843
    .long   1065646817
    .align 8
.LC5:
    .long   0
    .long   1072693248
    .ident  "GCC: (GNU) 5.3.0"
    .def    __real__ZdlPv;  .scl    2;  .type   32; .endef
    .def    operator delete(void*); .scl    2;  .type   32; .endef
    .def    __real__Znwm;   .scl    2;  .type   32; .endef
    .def    operator new(unsigned long);    .scl    2;  .type   32; .endef
    .def    memmove;    .scl    2;  .type   32; .endef
    .def    std::__throw_length_error(char const*); .scl    2;  .type   32; .endef
    .def    std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long); .scl    2;  .type   32; .endef
    .def    std::basic_ostream<char, std::char_traits<char> >::put(char);   .scl    2;  .type   32; .endef
    .def    std::basic_ostream<char, std::char_traits<char> >::flush(); .scl    2;  .type   32; .endef
    .def    std::ctype<char>::_M_widen_init() const;    .scl    2;  .type   32; .endef
    .def    std::__throw_bad_cast();    .scl    2;  .type   32; .endef
    .def    std::basic_ostream<char, std::char_traits<char> >& std::basic_ostream<char, std::char_traits<char> >::_M_insert<unsigned long>(unsigned long);  .scl    2;  .type   32; .endef
    .def    strlen; .scl    2;  .type   32; .endef
    .def    std::basic_ios<char, std::char_traits<char> >::clear(std::_Ios_Iostate);    .scl    2;  .type   32; .endef
    .def    _Unwind_Resume; .scl    2;  .type   32; .endef
    .def    std::basic_ostream<char, std::char_traits<char> >::operator<<(int); .scl    2;  .type   32; .endef
    .def    std::basic_ostream<char, std::char_traits<char> >& std::basic_ostream<char, std::char_traits<char> >::_M_insert<double>(double);    .scl    2;  .type   32; .endef
    .def    std::basic_ostream<char, std::char_traits<char> >& std::endl<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&);    .scl    2;  .type   32; .endef
    .def    std::ios_base::Init::Init();    .scl    2;  .type   32; .endef
    .def    std::ios_base::Init::~Init();   .scl    2;  .type   32; .endef
    .def    __cxa_atexit;   .scl    2;  .type   32; .endef
    .section    .rdata$.refptr._ZNSt8ios_base4InitD1Ev, "dr"
    .globl  .refptr._ZNSt8ios_base4InitD1Ev
    .linkonce   discard
.refptr._ZNSt8ios_base4InitD1Ev:
    .quad   std::ios_base::Init::~Init()
    .section    .rdata$.refptr.__dso_handle, "dr"
    .globl  .refptr.__dso_handle
    .linkonce   discard
.refptr.__dso_handle:
    .quad   __dso_handle
    .section    .rdata$.refptr._ZSt4cout, "dr"
    .globl  .refptr._ZSt4cout
    .linkonce   discard
.refptr._ZSt4cout:
    .quad   std::cout

Hana-style

Метапрограммирование в примерах выше — в стиле Hana.
Саму Boost.Hana не использовал так как её пока нет на Coliru, а хотелось сделать полноценное Live Demo. С ней конечно же кода было бы меньше, так как многие необходимые компоненты типа конкатенации уже готовы.

Compile-time Pattern Matching

В C++ перегрузка функций, так же как и специализация шаблонов, позволяет делать Pattern Matching по структуре дерева выражения запроса. Что помогает выражать оптимизации в коде.
Для примера возьмём запрос:
auto q = from
(
    from
    (
        from(foo).where(foo.id > 40_i).select(foo.id)
    ).where(foo.id > 41_i).select(foo.id)
).where(foo.id > 42_i).select(foo.id);

Изначально генерируется следующий код (условия тестовые, пока нет псевдонимов, но не суть):
SELECT foo.id FROM
(
    SELECT foo.id FROM
    (
        SELECT foo.id FROM foo WHERE foo.id>40
    ) WHERE foo.id>41
) WHERE foo.id>42

Этот код генерирует функция make_sql:
template<typename Source, typename Condition, typename ...Columns>
constexpr auto make_sql
(
    Source source,
    Condition condition,
    Columns... columns
)
{
    return concat
    (
        CTQL_STRING("SELECT "), concat_columns(columns...),
        CTQL_STRING(" FROM "), source_string(source),
        skip_if_empty(condition, concat(CTQL_STRING(" WHERE "), condition))
    );
}

Добавляем перегрузку для запросов с подзапросом во FROM, что по сути является PM времени компиляции:
template
<
    typename SubSource, typename SubCondition, typename ...SubColumns,
    typename Condition, typename ...Columns
>
constexpr auto make_sql
(
    query<SubSource, SubCondition, SubColumns...>,
    Condition condition,
    Columns... columns
)
{
    return make_sql
    (
        SubSource{},
        concat(condition, CTQL_STRING(" AND "), SubCondition{}),
        columns...
    );
}

Эта перегрузка разбирает внутренний запрос, делает внутренний FROM внешним, и склеивает условия, после чего уходит в рекурсию.
При наличии такой перегрузки генерируемый запрос приобретает вид:
SELECT foo.id FROM foo WHERE foo.id>42 AND foo.id>41 AND foo.id>40

Паттерн естественно может быть и более "многоэтажным", для более глубокого матчинга по дереву выражения.
Отредактировано 26.10.2020 11:56 Evgeny.Panasyuk . Предыдущая версия .
Re: [proof-of-concept] compile time query language
От: niXman Ниоткуда https://github.com/niXman
Дата: 11.07.16 19:09
Оценка:
так вроде уже есть: https://github.com/rbock/sqlpp11
текст запросов строится в компайл-тайм.

я писал препроцессорный генератор табличек. сейчас работаю над шаблонным.
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Отредактировано 11.07.2016 19:10 niXman . Предыдущая версия .
Re[2]: [proof-of-concept] compile time query language
От: Evgeny.Panasyuk Россия  
Дата: 11.07.16 19:15
Оценка:
Здравствуйте, niXman, Вы писали:

X>так вроде уже есть: https://github.com/rbock/sqlpp11


Я видел, это в том числе обсуждалось в исходной КСВ теме.

X>текст запросов строится в компайл-тайм.


ЕМНИП — нет, и даже есть кое-какой overhead, если ничего не путаю.
В любом случае, сравнения выхлопа ASM с ручной версией я там не видел.

X>я писал препроцессорный генератор табличек. сейчас работаю над шаблонным.


Я не знаю насколько нужен подобный генератор внутри языка. Ведь часто нужно обратиться к уже готовой базе, а для этого нужна внешняя кодогенерация с опросом схемы.
Отредактировано 11.07.2016 19:16 Evgeny.Panasyuk . Предыдущая версия .
Re[2]: [proof-of-concept] compile time query language
От: Evgeny.Panasyuk Россия  
Дата: 11.07.16 19:25
Оценка: +1
Здравствуйте, niXman, Вы писали:

X>текст запросов строится в компайл-тайм.


Вот же:
static Context& _(const T&, Context& context)
{
    context << "SELECT ";
    return context;
}

Это runtime.
Re[3]: [proof-of-concept] compile time query language
От: niXman Ниоткуда https://github.com/niXman
Дата: 11.07.16 19:28
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Я видел, это в том числе обсуждалось в исходной КСВ теме.

я не знаю, что такое КСВ, и не знал, что там это обсуждалось.

EP>ЕМНИП — нет, и даже есть кое-какой overhead, если ничего не путаю.

EP>В любом случае, сравнения выхлопа ASM с ручной версией я там не видел.
ну хз...возможно ошибаюсь...

EP>Я не знаю насколько нужен подобный генератор внутри языка. Ведь часто нужно обратиться к уже готовой базе, а для этого нужна внешняя кодогенерация с опросом схемы.

часто, но не всегда.
мне, к примеру, не интересно создавать БД в каком-то редакторе БД, когда я могу из программы это сделать.
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Re[3]: [proof-of-concept] compile time query language
От: niXman Ниоткуда https://github.com/niXman
Дата: 11.07.16 19:30
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Вот же:

EP>
EP>static Context& _(const T&, Context& context)
EP>{
EP>    context << "SELECT ";
EP>    return context;
EP>}
EP>

EP>Это runtime.

ага, точно. т.е. в компайл-тайме производятся всякие проверки, но сам текст запроса в ран-тайме. ясно.
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Отредактировано 11.07.2016 19:31 niXman . Предыдущая версия .
Re[4]: [proof-of-concept] compile time query language
От: Evgeny.Panasyuk Россия  
Дата: 11.07.16 19:35
Оценка:
Здравствуйте, niXman, Вы писали:

EP>>Я видел, это в том числе обсуждалось в исходной КСВ теме.

X>я не знаю, что такое КСВ, и не знал, что там это обсуждалось.

Под-раздел "Компьютерные священные войны". Сама тема
Автор: alex_public
Дата: 17.03.16
. Год назад кстати была подобная тема
Автор: gandjustas
Дата: 15.04.15
.

X>мне, к примеру, не интересно создавать БД в каком-то редакторе БД, когда я могу из программы это сделать.


Ок, а как ты собрался без макросов это делать? Интересует вот эта часть.
Re[4]: [proof-of-concept] compile time query language
От: Evgeny.Panasyuk Россия  
Дата: 11.07.16 19:37
Оценка: +1
Здравствуйте, niXman, Вы писали:

X>ага, точно. т.е. в компайл-тайме производятся всякие проверки, но сам текст запроса в ран-тайме. ясно.


Именно так. Мне же было интересно сделать пример на тему нулевых накладных расходов.
Отредактировано 11.07.2016 19:37 Evgeny.Panasyuk . Предыдущая версия .
Re[5]: [proof-of-concept] compile time query language
От: niXman Ниоткуда https://github.com/niXman
Дата: 11.07.16 19:39
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Ок, а как ты собрался без макросов это делать? Интересует вот эта часть.


или я не понял вопрос, или понял...

без макросов? это ты о том, над чем я сейчас работаю? над генератором тебличек без макросов?
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Re[6]: [proof-of-concept] compile time query language
От: Evgeny.Panasyuk Россия  
Дата: 11.07.16 19:43
Оценка:
Здравствуйте, niXman, Вы писали:

X>без макросов? это ты о том, над чем я сейчас работаю? над генератором тебличек без макросов?


Да, генератор табличек без макросов. Дело в том что для удобного синтаксиса проекций и т.п. желательно иметь код подобный следующему для каждого поля:
struct field_number
{
    struct name_string
    {
        static constexpr auto value() { return "number"; }
    };
    template <typename T>
    struct field
    {
        T number;
    };
    template <typename T>
    static auto &access(T &x)
    {
        return x.number;
    }
};

* name_string — для текста запроса
* field — для синтезирования типа результата с нужными полями
* access — для доступа к нужному полю
Re: [proof-of-concept] compile time query language
От: VTT http://vtt.to
Дата: 11.07.16 19:45
Оценка:
Как я понимаю, основной профит в автоматической генерации строчки SQL запроса?

Или в избавлении от
    auto count = db.read_count();
    xs.reserve(count);

    for(unsigned i=0; i!=count; ++i, db.move_to_next())
    {
        row.value = db.get<double>(0);
        row.id = db.get<int>(1);
        row.number = db.get<int>(2);

        xs.push_back(row);
    }

?

Глядя на название, я сначала подумал, что это compile-time база данных, генерирующая код для выполнения SQL запросов.
Такой вариант наверное был бы поинтереснее.
Говорить дальше не было нужды. Как и все космонавты, капитан Нортон не испытывал особого доверия к явлениям, внешне слишком заманчивым.
Re[7]: [proof-of-concept] compile time query language
От: niXman Ниоткуда https://github.com/niXman
Дата: 11.07.16 19:49
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Да, генератор табличек без макросов.


UDL и вариадик шаблон для строки.
сейчас это выглядит так:
using table = table_t<
   field_t<"id"_S, int>
  ,field_t<"name"_S, std::string>
>;
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Re[2]: [proof-of-concept] compile time query language
От: niXman Ниоткуда https://github.com/niXman
Дата: 11.07.16 19:52
Оценка:
Здравствуйте, VTT, Вы писали:

VTT>Глядя на название, я сначала подумал, что это compile-time база данных, генерирующая код для выполнения SQL запросов.


а данные она "получает" тоже в компайл-тайм?
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Re[2]: [proof-of-concept] compile time query language
От: Evgeny.Panasyuk Россия  
Дата: 11.07.16 19:55
Оценка: 2 (1)
Здравствуйте, VTT, Вы писали:

VTT>Как я понимаю, основной профит в автоматической генерации строчки SQL запроса?

VTT>Или в избавлении от
VTT>
VTT>

VTT>?

И в том, и в другом. То есть было:
auto handwritten()
{
    using namespace CTQL;

    struct Row
    {
        double value;
        int id;
        int number;
    } row;
    vector_noinline<Row> xs;
    
    dummy_db db;

    db.execute("SELECT foo.value, foo.id, foo.number FROM foo WHERE foo.id>42");

    auto count = db.read_count();
    xs.reserve(count);

    for(unsigned i=0; i!=count; ++i, db.move_to_next())
    {
        row.value = db.get<double>(0);
        row.id = db.get<int>(1);
        row.number = db.get<int>(2);

        xs.push_back(row);
    }
    
    return xs;
}
Стало:
to_vector( from(foo).where(foo.id > 42_i).select(foo.value, foo.id, foo.number) )
При этом ассемблерный код не изменился.

Плюс возможности по декомпозиции:
* итоговый запрос может собираться в разных частях программы
* повторяемые куски разных запросов могут быть вынесены и многократно переиспользованны.

Плюс оптимизация запросов (последний пример исходного сообщения). Плюс поддержка особенностей разных СУБД, и целевых оптимизаций под них.

Плюс, конечно же, типизация.
Отредактировано 11.07.2016 20:21 Evgeny.Panasyuk . Предыдущая версия .
Re[8]: [proof-of-concept] compile time query language
От: Evgeny.Panasyuk Россия  
Дата: 11.07.16 19:57
Оценка:
Здравствуйте, niXman, Вы писали:

EP>>Да, генератор табличек без макросов.

X>UDL и вариадик шаблон для строки.
X>сейчас это выглядит так:
X>
X>using table = table_t<
X>   field_t<"id"_S, int>
X>  ,field_t<"name"_S, std::string>
>>;
X>


Основной вопрос скорее не про строки, а про имена полей в программе. Или ты предлагаешь к полям в проекции по строковым именам обращаться?
Re[9]: [proof-of-concept] compile time query language
От: niXman Ниоткуда https://github.com/niXman
Дата: 11.07.16 19:59
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Или ты предлагаешь к полям в проекции по строковым именам обращаться?

да. ничего лучше(без кодогенерации) я придумать не смог %)

т.е. предполагалось, что юзер будет сначала определять колонки, а потом использовать определения многократно.
типа:
using id = field_t<"id"_S, int>;
using name = field_t<"name"_S, std::string>;
using age = field_t<"age"_S, int>;

using table = table_t<"table_name"_S, id, name, age>;

...

from(foo).where(id > 42).select(name, age);
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Отредактировано 11.07.2016 20:06 niXman . Предыдущая версия . Еще …
Отредактировано 11.07.2016 20:03 niXman . Предыдущая версия .
Re[3]: [proof-of-concept] compile time query language
От: VTT http://vtt.to
Дата: 11.07.16 20:08
Оценка:
Здравствуйте, niXman, Вы писали:

X>Здравствуйте, VTT, Вы писали:


VTT>>Глядя на название, я сначала подумал, что это compile-time база данных, генерирующая код для выполнения SQL запросов.


X>а данные она "получает" тоже в компайл-тайм?


Нет конечно, в компайл-тайм только генерируется код метода, собирающего данные.
т.е. в рантайм нет никаких SQL строчек, а просто вызывается метод, выполняющий заданный в компайл-тайм запрос со всеми оптимизациями.
Я, видите ли, некоторое время назад на фрилансе делал один проектик, где приходилось извлекать / проверять много разных выборок из данных с учетом кучи правил.
Там это сделал, задавая операции при выборке и правила валидации в декларативном виде, как последовательностей из соотв. энумераторов.
На выходе получался шитый код с проверенными на совместимость правилами и запросами.
Были мысли расширить это до чего-то более универсального.
С новыми стандартами сейчас можно было бы прямо в compile-time парсить строчки запросов и прочее.
Еще я про это вспомнил из-за соседнего топика
Автор: kochetkov.vladimir
Дата: 08.07.16
.
По идее, компоновка SQL-запросов во время компиляции позволила бы устранить целый класс атак типа SQL injection за счет отсутствия интерпретатора SQL в рантайм.
Говорить дальше не было нужды. Как и все космонавты, капитан Нортон не испытывал особого доверия к явлениям, внешне слишком заманчивым.
Отредактировано 11.07.2016 20:16 VTT . Предыдущая версия . Еще …
Отредактировано 11.07.2016 20:15 VTT . Предыдущая версия .
Re[10]: [proof-of-concept] compile time query language
От: Evgeny.Panasyuk Россия  
Дата: 11.07.16 20:09
Оценка:
Здравствуйте, niXman, Вы писали:

EP>>Или ты предлагаешь к полям в проекции по строковым именам обращаться?

X>да. ничего лучше(без кодогенерации) я придумать не смог %)

Это поможет решить следующая фича:
template<char...>
struct reification;

Где компилятор автоматически бы создавал специализации следующего вида при обращении к ним:
template<>
struct reification<'n', 'u', 'm', 'b', 'e', 'r'>
{
    template <typename T>
    struct field
    {
        T number;
    };
    template <typename T>
    static auto &access(T &x)
    {
        return x.number;
    }
    // ... number function, number type, etc, etc
};

Думаю стоит подумать над proposal'ом.

Сейчас кстати это можно сэмулировать следующим образом. Даём пользователю
template<char...>
struct reification;

Когда он обращается к специализации reification<'n', 'u', 'm', 'b', 'e', 'r'>, получаем ошибку компиляции вида
main.cpp:7:47: error: implicit instantiation of undefined template 'reification<'n', 'u', 'm', 'b', 'e', 'r'>'

    reification<'n', 'u', 'm', 'b', 'e', 'r'> x;

Парсим ошибки компиляции простым скриптом и генерируем специализации в специальный .hpp файл. При повторной компиляции ошибок уже не будет.
Re[10]: [proof-of-concept] compile time query language
От: Evgeny.Panasyuk Россия  
Дата: 11.07.16 20:11
Оценка:
Здравствуйте, niXman, Вы писали:

X>т.е. предполагалось, что юзер будет сначала определять колонки, а потом использовать определения многократно.

X>типа:
X>
X>using id = field_t<"id"_S, int>;
X>using name = field_t<"name"_S, std::string>;
X>using age = field_t<"age"_S, int>;

X>using table = table_t<"table_name"_S, id, name, age>;

X>...

X>from(foo).where(id > 42).select(name, age);
X>


Этого недостаточно для того чтобы к результирующей строке можно было обратиться через точку, то есть вот так:
for(auto &&row : db(from(foo).where(id > 42).select(name, age)))
    cout << row.name;

не получится.
Re[11]: [proof-of-concept] compile time query language
От: niXman Ниоткуда https://github.com/niXman
Дата: 11.07.16 20:16
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Этого недостаточно для того чтобы к результирующей строке можно было обратиться через точку, то есть вот так:

EP>
EP>for(auto &&row : db(from(foo).where(id > 42).select(name, age)))
EP>    cout << row.name;
EP>

EP>не получится.

та понятно =)
ща я тот код откопаю и гляну, как оно там планировалось...
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Re[4]: [proof-of-concept] compile time query language
От: Evgeny.Panasyuk Россия  
Дата: 11.07.16 20:19
Оценка:
Здравствуйте, VTT, Вы писали:

VTT>Нет конечно, в компайл-тайм только генерируется код метода, собирающего данные.

VTT>т.е. в рантайм нет никаких SQL строчек, а просто вызывается метод, выполняющий заданный в компайл-тайм запрос со всеми оптимизациями.

Да, подобная мысль была. Например, грубо говоря убрать этап текстового SQL из связки приложение + SQLite, плюс генерировать код запроса во время компиляции.
Но, это имеет смысл только для сценариев типа встраиваемых СУБД а-ля SQLite.
Отредактировано 11.07.2016 20:19 Evgeny.Panasyuk . Предыдущая версия .
Re[12]: [proof-of-concept] compile time query language
От: niXman Ниоткуда https://github.com/niXman
Дата: 11.07.16 20:28
Оценка:
Здравствуйте, niXman, Вы писали:

X>ща я тот код откопаю и гляну, как оно там планировалось...

ну понятно.
каждый тип на основе field_t хранит в себе хеш-сумму от имени колонки.
select() возвращает vector моих оберток поверх std::tuple, каждая из которых возвращает элемент тьюпла соответствующий хешь-сумме колонки.
for(auto &&row : db(from(foo).where(id > 42).select(name, age)))
    cout << row.get<name>();
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Re[11]: [proof-of-concept] compile time query language
От: Erop Россия  
Дата: 11.07.16 21:07
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>
EP>    struct field
EP>    {
EP>        T number;
EP>    };
EP>

А смысл? Как предлагается это использовать?

EP>
EP>    template <typename T>
EP>    static auto &access(T &x)
EP>    {
EP>        return x.number;
EP>    }
EP>    // ... number function, number type, etc, etc
EP>};
EP>

Думаю, что лучше тогда уж сделать что-то вроде:
template<typename T> static auto member_ptr() { return &T::number; }
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[13]: [proof-of-concept] compile time query language
От: Evgeny.Panasyuk Россия  
Дата: 11.07.16 21:10
Оценка:
Здравствуйте, niXman, Вы писали:

X>>ща я тот код откопаю и гляну, как оно там планировалось...

X>ну понятно.
X>каждый тип на основе field_t хранит в себе хеш-сумму от имени колонки.
X>select() возвращает vector моих оберток поверх std::tuple, каждая из которых возвращает элемент тьюпла соответствующий хешь-сумме колонки.

Непонятно зачем hash, когда можно индексировать непосредственно по полному строковому имени.

X>
X>for(auto &&row : db(from(foo).where(id > 42).select(name, age)))
X>    cout << row.get<name>();
X>


Уже не так удобно, и например авто-дополнение выдающее список полей не будет работать. С макросами удобней жеж, правда не должно быть аллергии на них

Плюс синтаксис будет всё же немного другой — в select name это значение, а в get name это тип, но не суть, это легко обходится.
Re[12]: [proof-of-concept] compile time query language
От: Evgeny.Panasyuk Россия  
Дата: 11.07.16 21:34
Оценка:
Здравствуйте, Erop, Вы писали:

EP>>
EP>>    struct field
EP>>    {
EP>>        T number;
EP>>    };
EP>>

E>А смысл? Как предлагается это использовать?

В запросах к БД в SELECT указывается список полей которые должна содержать строка результирующего набора. Для удобства использования в коде нужно создавать структуру с аналогичным набором полей.
Смотри функцию handwritten из первого сообщения, в ней есть структура:
    struct Row
    {
        double value;
        int id;
        int number;
    } row;
    vector_noinline<Row> xs;

Которая соответствует полям в SELECT: SELECT foo.value, foo.id, foo.number FROM foo WHERE foo.id>42

Трюк выше нужен для автогенерации такой структуры. Собственно он и используется в исходном сообщении:
    for(auto &&x : to_vector(
                       from(foo)
                       .where(foo.id > 42_i)
                       .select(foo.value, foo.id, foo.number)
                   ))
    {
        using namespace std;
        cout << "ROW: " << x.id << " " << x.value << " " << x.number << endl;
        // cout << x.code; // - compile error, because .code is not in projection
    }


Вот упрощённый пример этого трюка
template<typename ...Columns>
struct row : Columns::field...
{};

template<typename ...Columns>
auto select(Columns...)
{
    return row<Columns...>{};
}


E>Думаю, что лучше тогда уж сделать что-то вроде:

E>
template<typename T> static auto member_ptr() { return &T::number; }


Не суть, вариантов много.
Re[12]: [proof-of-concept] compile time query language
От: Evgeny.Panasyuk Россия  
Дата: 11.07.16 21:41
Оценка:
Здравствуйте, Erop, Вы писали:

E>А смысл? Как предлагается это использовать?


Или другой пример на тему reification<char...>:
Начиная с C++11 можно обрабатывать строки в compile-time.
Можно создать строковый DSL, который на основе текстового кода создаёт конкретные структуры у которых имена полей сгенерированны из этих строк.
Совсем абстрактный пример:
auto x = MAKE_STRUCT(" struct Test { int foo, double bar };  ");
x.foo = 1;
x.bar = .5;


Причём подобным образом можно генерировать не только поля, но и например методы и т.п.
Отредактировано 11.07.2016 21:42 Evgeny.Panasyuk . Предыдущая версия .
Re[13]: [proof-of-concept] compile time query language
От: niXman Ниоткуда https://github.com/niXman
Дата: 11.07.16 21:56
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Можно создать строковый DSL, который на основе текстового кода создаёт конкретные структуры у которых имена полей сгенерированны из этих строк.

почему-ьто мне кажется это невозможным... можно пример?
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Re[14]: [proof-of-concept] compile time query language
От: Evgeny.Panasyuk Россия  
Дата: 11.07.16 22:11
Оценка:
Здравствуйте, niXman, Вы писали:

EP>>Можно создать строковый DSL, который на основе текстового кода создаёт конкретные структуры у которых имена полей сгенерированны из этих строк.

X>почему-ьто мне кажется это невозможным... можно пример?

Для этого нужна фича std::reification которую я описал выше.

Ты же можешь обработать строку " struct Test { int foo, double bar }; " во время компиляции и вытащить от туда имена "foo" и "bar"? (пример
Автор: Evgeny.Panasyuk
Дата: 12.10.14
как это сделать)
Для этого даже есть готовый генератор парсеров — Boost.Metaparse.

Вытащив имена, ты можешь их скормить в std::reification<'f', 'o', 'o'>, где компилятор автоматом сгенерирует специализацию (в этом и есть мой proposal):
template<>
struct reification<'f', 'o', 'o'>
{
    template <typename T>
    struct field
    {
        T foo;
    };
};

А далее, уже из этих специализаций можно слепить итоговую структуру через наследование, как в sqlpp11.
Отредактировано 11.07.2016 22:12 Evgeny.Panasyuk . Предыдущая версия .
Re[15]: [proof-of-concept] compile time query language
От: niXman Ниоткуда https://github.com/niXman
Дата: 11.07.16 22:20
Оценка: :)
я не понял одного: как имея строку, в компайл-тайме сгенерить переменную с именем, содержащимся в строке?
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Re[16]: [proof-of-concept] compile time query language
От: Evgeny.Panasyuk Россия  
Дата: 11.07.16 22:27
Оценка:
Здравствуйте, niXman, Вы писали:

X>я не понял одного: как имея строку, в компайл-тайме сгенерить переменную с именем, содержащимся в строке?


Через std::reification. Это несуществующая пока фича. Ты пишешь std::reification<'f', 'o', 'o'>, а компилятор тебе даёт:
template<>
struct reification<'f', 'o', 'o'>
{
    template <typename T>
    struct field
    {
        T foo;
    };
    // ...
};
Это в том числе позволит из твоего определения:
using table = table_t<
   field_t<"id"_S, int>
  ,field_t<"name"_S, std::string>
>;
Получить синтаксис аналогичный sqlpp11 (то есть "через точку" result_row.name), без всяких макросов.

Реализовать в компиляторе должно быть достаточно просто.
Сейчас это можно сэмулировать разбирая сообщения об ошибках внешним скриптом.
Отредактировано 11.07.2016 22:28 Evgeny.Panasyuk . Предыдущая версия .
Re[16]: [proof-of-concept] compile time query language
От: niXman Ниоткуда https://github.com/niXman
Дата: 11.07.16 22:28
Оценка:
Здравствуйте, niXman, Вы писали:

X>я не понял одного: как имея строку, в компайл-тайме сгенерить переменную с именем, содержащимся в строке?


перечитал про reification.
и все равно не понял, как ты себе представляешь генерацию структуры в компайл-тайм, чтоб имена мемберов были те, что в строке... %)

т.е. ты действительно думаешь что такое реально сделать?:
auto x = MAKE_STRUCT("{int a; double b;}");
x.a = 3;
x.b = 3.14;
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Re[17]: [proof-of-concept] compile time query language
От: niXman Ниоткуда https://github.com/niXman
Дата: 11.07.16 22:29
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Это несуществующая пока фича.

ааа, где-то я этот момент упустил =)))
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Re[17]: [proof-of-concept] compile time query language
От: Evgeny.Panasyuk Россия  
Дата: 11.07.16 22:32
Оценка: +1
Здравствуйте, niXman, Вы писали:

X>и все равно не понял, как ты себе представляешь генерацию структуры в компайл-тайм, чтоб имена мемберов были те, что в строке... %)


Это будет делать компилятор, ему не трудно. Сейчас, в самом языке std::reification не реализуем.

X>т.е. ты действительно думаешь что такое реально сделать?:

X>
X>auto x = MAKE_STRUCT("{int a; double b;}");
X>x.a = 3;
X>x.b = 3.14;
X>


Сейчас нет. Это я размышляю о предложении в ISO такой фичи (std::reification), которая позволит это реализовать.
Re[13]: [proof-of-concept] compile time query language
От: Erop Россия  
Дата: 11.07.16 23:19
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Не суть, вариантов много.


Что бы описать структуру с несколькими полями, предлагается выводиться из всех таких структур? Или какая идея?
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[13]: [proof-of-concept] compile time query language
От: Erop Россия  
Дата: 11.07.16 23:20
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Причём подобным образом можно генерировать не только поля, но и например методы и т.п.

Методам ещё теля нужны...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[14]: [proof-of-concept] compile time query language
От: Evgeny.Panasyuk Россия  
Дата: 12.07.16 04:56
Оценка:
Здравствуйте, Erop, Вы писали:

EP>>Не суть, вариантов много.

E>Что бы описать структуру с несколькими полями, предлагается выводиться из всех таких структур? Или какая идея?

Наследование от всех таких структур
template<typename ...Columns>
struct row : Columns::field...
{};
Re[14]: [proof-of-concept] compile time query language
От: Evgeny.Panasyuk Россия  
Дата: 12.07.16 05:06
Оценка:
Здравствуйте, Erop, Вы писали:

EP>>Причём подобным образом можно генерировать не только поля, но и например методы и т.п.

E>Методам ещё теля нужны...

Тела комбинируются из готовых блоков. Например так как в Boost.Phoenix.
Re[18]: [proof-of-concept] compile time query language
От: flаt  
Дата: 12.07.16 05:11
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Сейчас нет. Это я размышляю о предложении в ISO такой фичи (std::reification), которая позволит это реализовать.


https://dlang.org/mixin.html мощнее, почему не аналог его попросить у isocpp? Всё равно они сейчас включают всё подряд в стандарт
Re[19]: [proof-of-concept] compile time query language
От: Evgeny.Panasyuk Россия  
Дата: 12.07.16 05:15
Оценка:
Здравствуйте, flаt, Вы писали:

EP>>Сейчас нет. Это я размышляю о предложении в ISO такой фичи (std::reification), которая позволит это реализовать.

F>https://dlang.org/mixin.html мощнее, почему не аналог его попросить у isocpp? Всё равно они сейчас включают всё подряд в стандарт

Подобное вряд ли включат (хотя я только за), это намного сложнее — std::reification генерирует простую специализацию, а не произвольный текстовый код (который ещё и возможно в свою очередь сгенерирует другой текстовый код).
Re[15]: [proof-of-concept] compile time query language
От: Erop Россия  
Дата: 12.07.16 06:45
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

E>>Что бы описать структуру с несколькими полями, предлагается выводиться из всех таких структур? Или какая идея?


EP>Наследование от всех таких структур

EP>
EP>template<typename ...Columns>
EP>struct row : Columns::field...
EP>{};
EP>


Думаешь это удобно?

На самом деле, в С++ есть какой-то системный косяк уже давно с метапрограммированием.
Если уж развивать язык в эту сторону, то я бы лучше концепцию constexpr развивал. Всё равно у нас для реализации constexpr компилятор должен уметь исполнять CT с++ код без побочных эффектов, так что недалеко до того, что бы сделать так, что бы компилятор отдавал в макрос текст или АСТ или что-то вроде того, и получал наружу модифицированное тоже самое...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[16]: [proof-of-concept] compile time query language
От: Evgeny.Panasyuk Россия  
Дата: 12.07.16 08:43
Оценка:
Здравствуйте, Erop, Вы писали:

E>>>Что бы описать структуру с несколькими полями, предлагается выводиться из всех таких структур? Или какая идея?

EP>>Наследование от всех таких структур
EP>>
EP>>template<typename ...Columns>
EP>>struct row : Columns::field...
EP>>{};
EP>>

E>Думаешь это удобно?

С какой стороны? Пользователя или реализации?

Со стороны пользователя вполне удобно, используется например в sqlpp11:
for (const auto& row : db(select(foo.name, foo.hasFun).from(foo).where(foo.id > 17 and foo.name.like("%bar%"))))
{
    if (row.name.is_null())
        std::cerr << "name is null, will convert to empty string" << std::endl;
    std::string name = row .name;   // string-like fields are implicitly convertible to string
    bool hasFun = row.hasFun;          // bool fields are implicitly convertible to bool
}
Ты можешь придумать более удобный синтаксис?

А со стороны реализации тоже проблем нет — есть же variadic templates

E>На самом деле, в С++ есть какой-то системный косяк уже давно с метапрограммированием.


Обсуждаемый контекст относится скорее к reflection и reification. Хотя да, с метопрограммированием связан.

E>Если уж развивать язык в эту сторону, то я бы лучше концепцию constexpr развивал. Всё равно у нас для реализации constexpr компилятор должен уметь исполнять CT с++ код без побочных эффектов, так что недалеко до того, что бы сделать так, что бы компилятор отдавал в макрос текст или АСТ или что-то вроде того, и получал наружу модифицированное тоже самое...


Да, было бы хорошо, вот только этого даже на горизонте не видно.
И к тому же думаю метапрограммирование на уровне AST не всегда удобно, то есть это не замена шаблонному/constexpr метапрограммированию, а скорее дополнение.
Re[17]: [proof-of-concept] compile time query language
От: Artеm Австралия жж
Дата: 12.07.16 08:56
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Изначально генерируется следующий код (условия тестовые, пока нет псевдонимов, но не суть):
EP>
EP>SELECT foo.id FROM
EP>(
EP> SELECT foo.id FROM
EP> (
EP> SELECT foo.id FROM foo WHERE foo.id>40
EP> ) WHERE foo.id>41
EP>) WHERE foo.id>42


Какой же это zero overhead? SQL выражение из строки парсится в промежуточное представление и исполняется интерпретатором. Лучше было бы сделать наоборот: SQL выражение препроцессится, из него генерится бинарное представление компилятором (исполняемый код reduce), и линкуется к программе.
Re[18]: [proof-of-concept] compile time query language
От: Evgeny.Panasyuk Россия  
Дата: 12.07.16 09:00
Оценка:
Здравствуйте, Artеm, Вы писали:

A>Какой же это zero overhead?


Zero overhead по работе с СУБД, которая есть как данность.

A>SQL выражение из строки парсится в промежуточное представление и исполняется интерпретатором.


Внутри уже готовых СУБД

A>Лучше было бы сделать наоборот: SQL выражение препроцессится, из него генерится бинарное представление компилятором (исполняемый код reduce), и линкуется к программе.


Ты мне предлагаешь СУБД написать?
Re: [proof-of-concept] compile time query language
От: niXman Ниоткуда https://github.com/niXman
Дата: 12.07.16 11:58
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

у меня офтопный вопрос: а зачем тут vector_noinline? в чем его надобность, и почему?

спасибо.
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Re: [proof-of-concept] compile time query language
От: Voivoid Россия  
Дата: 12.07.16 12:04
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>В рамках одной из КСВ тем сделал proof-of-concept встроенного языка запросов времени компиляции

Дык, есть ж ODB, http://www.codesynthesis.com/products/odb/
Re[17]: [proof-of-concept] compile time query language
От: Erop Россия  
Дата: 12.07.16 12:08
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>С какой стороны? Пользователя или реализации?

Со всех.

EP>
EP>for (const auto& row : db(select(foo.name, foo.hasFun).from(foo).where(foo.id > 17 and foo.name.like("%bar%"))))
EP>
Ты можешь придумать более удобный синтаксис?

Конечно могу.
можно сделать foo. факультативным...
Так удобнее же:
EP>for (const auto& row : db(select(name, hasFun).from(foo).where(id > 17 and name.like


А ещё ведь join бывает. Тогда как? Везде надо помнить какое поле откуда взялось или какой-то врапер для join'а описывать?
Ну и это ты придумал, сильно не сразу, кстати, такой хитрый DSL, который на все эти фокусы с наследованиями ложится. А если бы была возможность как-то описывать прямо генерённые в CT структуры, то DSL генерить было бы проще.
Вот, например, хочу я написать CT компилятор regexp'ов, который при мэтчинге записывает в какие-то переменные смытчившиеся с ними диапазоны. И что мне делать?

EP>Обсуждаемый контекст относится скорее к reflection и reification. Хотя да, с метопрограммированием связан.


E>>Если уж развивать язык в эту сторону, то я бы лучше концепцию constexpr развивал. Всё равно у нас для реализации constexpr компилятор должен уметь исполнять CT с++ код без побочных эффектов, так что недалеко до того, что бы сделать так, что бы компилятор отдавал в макрос текст или АСТ или что-то вроде того, и получал наружу модифицированное тоже самое...


EP>Да, было бы хорошо, вот только этого даже на горизонте не видно.

EP>И к тому же думаю метапрограммирование на уровне AST не всегда удобно, то есть это не замена шаблонному/constexpr метапрограммированию, а скорее дополнение.

Ну, это, скорее то, что надо, в отличии от нынешних Тьюринг-полных CT-решений на шаблонах...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[2]: [proof-of-concept] compile time query language
От: Evgeny.Panasyuk Россия  
Дата: 12.07.16 14:09
Оценка: +1
Здравствуйте, niXman, Вы писали:

X>у меня офтопный вопрос: а зачем тут vector_noinline? в чем его надобность, и почему?


Во-первых убрать лишний заинлайненный код из сравниваемых ASM функций.
Во-вторых чтобы было видно название вызываемых функций в ASM коде (у dummy_db тоже методы NOINLINE).
И в-третьих zero overhead не гарантирует одинаковый ASM код, компилятор вполне имеет право переставлять инструкции местами и т.п. — было бы не так красноречиво. Меньший объём кода функции позволяет снизить влияние таких сторонних факторов.
Re[2]: [proof-of-concept] compile time query language
От: Evgeny.Panasyuk Россия  
Дата: 12.07.16 14:12
Оценка:
Здравствуйте, Voivoid, Вы писали:

EP>>В рамках одной из КСВ тем сделал proof-of-concept встроенного языка запросов времени компиляции

V>Дык, есть ж ODB, http://www.codesynthesis.com/products/odb/

Я видел, и даже неоднократно на неё ссылался ранее.
Мой пример не является законченным решением, а лишь демонстрацией нулевых расходов, с другой стороны не думаю что в ODB нулевые расходы.
Re[18]: [proof-of-concept] compile time query language
От: Evgeny.Panasyuk Россия  
Дата: 12.07.16 15:20
Оценка:
Здравствуйте, Erop, Вы писали:

EP>>
EP>>for (const auto& row : db(select(foo.name, foo.hasFun).from(foo).where(foo.id > 17 and foo.name.like("%bar%"))))
EP>>
Ты можешь придумать более удобный синтаксис?

E>Конечно могу.
E>можно сделать foo. факультативным...
E>Так удобнее же:
EP>for (const auto& row : db(select(name, hasFun).from(foo).where(id > 17 and name.like


Это мелочь, и вполне реализуемо в рамках описанного выше подхода с наследованием.

E>А ещё ведь join бывает. Тогда как? Везде надо помнить какое поле откуда взялось или какой-то врапер для join'а описывать?


Описывать придётся в любом случае, так как например будут поля с дублированными именами.
Join уже реализован в sqlpp11.

E>Ну и это ты придумал, сильно не сразу, кстати, такой хитрый DSL, который на все эти фокусы с наследованиями ложится.


Это не я придумал. Я увидел этот приём в sqlpp11, или может каком другом месте, но в sqlpp11 видел точно.

E>А если бы была возможность как-то описывать прямо генерённые в CT структуры, то DSL генерить было бы проще.


Так и тут достаточно просто. С тем что этот трюк нужно сначала придумать — да, согласен, но когда он известен само использование уже достаточно простое.

E>Вот, например, хочу я написать CT компилятор regexp'ов, который при мэтчинге записывает в какие-то переменные смытчившиеся с ними диапазоны. И что мне делать?


Например? В смысле в поля результирующей структуры? Или внешние переменные?

EP>>И к тому же думаю метапрограммирование на уровне AST не всегда удобно, то есть это не замена шаблонному/constexpr метапрограммированию, а скорее дополнение.

E>Ну, это, скорее то, что надо, в отличии от нынешних Тьюринг-полных CT-решений на шаблонах...

Манипулирование AST — да, мощнее, но это низкий уровень, и далеко не самый удобный.
Вот например, текст SQL запроса в моём примере генерируется (во время компиляции) следующей функцией:
template<typename Source, typename Condition, typename ...Columns>
constexpr auto make_sql
(
    Source source,
    Condition condition,
    Columns... columns
)
{
    return concat
    (
        CTQL_STRING("SELECT "), concat_columns(columns...),
        CTQL_STRING(" FROM "), source_string(source),
        skip_if_empty(condition, concat(CTQL_STRING(" WHERE "), condition))
    );
}

В более новых стандартах, можно будет писать вот так:
constexpr auto make_sql(auto source, auto condition, auto... columns)
{
    return concat
    (
        "SELECT "_S, concat_columns(columns...),
        " FROM "_S, source_string(source),
        skip_if_empty(condition, concat(" WHERE "_S, condition))
    );
}

IMHO, это практически самый удобный и лаконичный вариант. А вот как будет тоже самое выглядеть через манипуляцию AST?
Re[19]: [proof-of-concept] compile time query language
От: Erop Россия  
Дата: 12.07.16 15:44
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:


EP>Это мелочь, и вполне реализуемо в рамках описанного выше подхода с наследованием.

Ты спросил, как улучшить этот DSL, а не чего нельзя сделать на С++...
Просто проектируя DSL ты думал не о выразительности, а о том, чтобы это можно было в макрошаблонную магию затолкать...

EP>Описывать придётся в любом случае, так как например будут поля с дублированными именами.

EP>Join уже реализован в sqlpp11.
Ну вот в случае 2-х namespaces, как-то удаётся обойтись без описания join...

EP>Это не я придумал.

Ну не ты, какая разница?

E>>А если бы была возможность как-то описывать прямо генерённые в CT структуры, то DSL генерить было бы проще.


EP>Так и тут достаточно просто. С тем что этот трюк нужно сначала придумать — да, согласен, но когда он известен само использование уже достаточно простое.

Оно сильно ограничивает. Например неудобно писать методы от нескольких полей...

E>>Вот, например, хочу я написать CT компилятор regexp'ов, который при мэтчинге записывает в какие-то переменные смытчившиеся с ними диапазоны. И что мне делать?


EP>Например? В смысле в поля результирующей структуры? Или внешние переменные?


В смысле пишу что-то вроде
auto regExp = RegExp( (@dataPrefixes){data:{day:\d?\d}\\{month:(\d?\d)|(@monthsOfYear)}\\{year:(\d\d)?\d\d)}(@dataSuffixes) );
auto regExpRes = regExp << line;
if( regExpRes ) {
    dayta = MyData( regExpRes.day, regExpRes.month, regExpRes.year );
    dataType = regExpRes.dataPrefixes.Type;
}
И тут dataSuffixes и monthsOfYear такие же парсеры, определённые где-то выше, а может и другие, а regExpRes.dataPrefixes — результат применения dataPrefixes.


EP>Манипулирование AST — да, мощнее, но это низкий уровень, и далеко не самый удобный.

EP>Вот например, текст SQL запроса в моём примере генерируется (во время компиляции) следующей функцией:


EP>IMHO, это практически самый удобный и лаконичный вариант. А вот как будет тоже самое выглядеть через манипуляцию AST?


Будет просто цикл по токенам, возможно с каким-то фильтром по типу, или по атрибутам структуры...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re: [proof-of-concept] compile time query language
От: chaotic-kotik  
Дата: 12.07.16 16:10
Оценка:
Круто конечно, но зачем? Вот например библиотека soci уже давно существует и позволяет встраивать sql в с++ код, но ей никто не пльзуется, ибо это никому особо не нужно. Проще вынести весь код работающий с БД в отдельный DAL слой и работать с ним.
Re[2]: [proof-of-concept] compile time query language
От: Evgeny.Panasyuk Россия  
Дата: 12.07.16 16:19
Оценка:
Здравствуйте, chaotic-kotik, Вы писали:

CK>Круто конечно, но зачем?


Это лишь пример в ответ на заявления о невозможности в другой теме.

CK>Вот например библиотека soci уже давно существует и позволяет встраивать sql в с++ код,


В SOCI нет типизации запроса, труднее проводить декомпозицию, структуры под проекции нужно писать руками, и думаю там не zero overhead.

CK>но ей никто не пльзуется, ибо это никому особо не нужно.


Я думаю это от отсутствия массового спроса на работу с СУБД из C++. Мне вот тоже такие задачи не попадаются совсем.

CK>Проще вынести весь код работающий с БД в отдельный DAL слой и работать с ним.


DAL как реализовывать? На каком языке/библиотеке?
Где делать требуемые проекции, фильтрации? Всё в DAL расписывать?
Re[2]: [proof-of-concept] compile time query language
От: niXman Ниоткуда https://github.com/niXman
Дата: 12.07.16 16:32
Оценка: 10 (1)
Здравствуйте, chaotic-kotik, Вы писали:

CK>Вот например библиотека soci уже давно существует и позволяет встраивать sql в с++ код, но ей никто не пльзуется, ибо это никому особо не нужно.

я пользуюсь, и давольно давно, пока sqlpp11 не нашел.
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Re[3]: [proof-of-concept] compile time query language
От: Evgeny.Panasyuk Россия  
Дата: 12.07.16 16:46
Оценка:
Здравствуйте, niXman, Вы писали:

CK>>Вот например библиотека soci уже давно существует и позволяет встраивать sql в с++ код, но ей никто не пльзуется, ибо это никому особо не нужно.

X>я пользуюсь, и давольно давно, пока sqlpp11 не нашел.

А какого типа у тебя задачи? Хотя бы из какой области? С какими СУБД работаешь?
Re[4]: [proof-of-concept] compile time query language
От: niXman Ниоткуда https://github.com/niXman
Дата: 12.07.16 16:57
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>А какого типа у тебя задачи? Хотя бы из какой области?

та что-то взять из БД, положить, етц... ничего критичного к производительности нет.

EP>С какими СУБД работаешь?

sqlite, mysql, postgresql.
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Re[5]: [proof-of-concept] compile time query language
От: Evgeny.Panasyuk Россия  
Дата: 12.07.16 17:00
Оценка:
Здравствуйте, niXman, Вы писали:

EP>>А какого типа у тебя задачи? Хотя бы из какой области?

X>та что-то взять из БД, положить, етц... ничего критичного к производительности нет.

А области какие? Сервисы или например пользовательские приложения?
Re[6]: [proof-of-concept] compile time query language
От: niXman Ниоткуда https://github.com/niXman
Дата: 12.07.16 17:08
Оценка: 16 (2)
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>А области какие? Сервисы или например пользовательские приложения?

все же больше сервисов... есть игровой проект, весьма популярный, там постгрес в качестве хранилища истории действий. там порядка 2к записей в секунду. и это отдельный сервер.
остальные совсем не нагруженные.
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Re[20]: [proof-of-concept] compile time query language
От: Evgeny.Panasyuk Россия  
Дата: 12.07.16 22:19
Оценка:
Здравствуйте, Erop, Вы писали:

EP>>Это мелочь, и вполне реализуемо в рамках описанного выше подхода с наследованием.

E>Ты спросил, как улучшить этот DSL, а не чего нельзя сделать на С++...
E>Просто проектируя DSL ты думал не о выразительности, а о том, чтобы это можно было в макрошаблонную магию затолкать...

Я думал о том чтобы сделать пример при малых усилиях.
Если же говорить о выразительности, то интересно сделать автоматический join основанный на связях между таблицами в БД — в большинстве случаев именно это и происходит. То есть выражение вида select(foo.name, bar.value) автоматически породит from с необходимыми join'ами, выведенными на основе графа связей. И вот в таком варианте имена таблиц в select'е более чем оправданны.

EP>>Описывать придётся в любом случае, так как например будут поля с дублированными именами.

EP>>Join уже реализован в sqlpp11.
E>Ну вот в случае 2-х namespaces, как-то удаётся обойтись без описания join...

Каких namespaces?

E>Например неудобно писать методы от нескольких полей...


Что имеется в виду?

E>>>Вот, например, хочу я написать CT компилятор regexp'ов, который при мэтчинге записывает в какие-то переменные смытчившиеся с ними диапазоны. И что мне делать?

EP>>Например? В смысле в поля результирующей структуры? Или внешние переменные?
E>В смысле пишу что-то вроде
auto regExp = RegExp( (@dataPrefixes){data:{day:\d?\d}\\{month:(\d?\d)|(@monthsOfYear)}\\{year:(\d\d)?\d\d)}(@dataSuffixes) );
E>auto regExpRes = regExp << line;
E>if( regExpRes ) {
E>    dayta = MyData( regExpRes.day, regExpRes.month, regExpRes.year );
E>    dataType = regExpRes.dataPrefixes.Type;
E>}
И тут dataSuffixes и monthsOfYear такие же парсеры, определённые где-то выше, а может и другие, а regExpRes.dataPrefixes — результат применения dataPrefixes.


В рамках текущего стандарта придётся писать вот так:
auto regExp = REGEXP(R"((@dataPrefixes){data:{day:\d?\d}\\{month:(\d?\d)|(@monthsOfYear)}\\{year:(\d\d)?\d\d)}(@dataSuffixes))", dataPrefixes, monthsOfYear, dataSuffixes);
Тогда твой код regExpRes.dataPrefixes.Type заработает. По аналогии с этим примером
Автор: Evgeny.Panasyuk
Дата: 12.10.14
.

Чтобы не было выделенного, и без макросов, понадобятся две фичи:
1) описанный выше std::reification
2) захват контекста — примерно так как делает лямбда

EP>>Манипулирование AST — да, мощнее, но это низкий уровень, и далеко не самый удобный.

EP>>Вот например, текст SQL запроса в моём примере генерируется (во время компиляции) следующей функцией:
EP>>IMHO, это практически самый удобный и лаконичный вариант. А вот как будет тоже самое выглядеть через манипуляцию AST?
E>Будет просто цикл по токенам, возможно с каким-то фильтром по типу, или по атрибутам структуры...

Это будет как минимум не проще, и при этом на более низком уровне.
Re[3]: [proof-of-concept] compile time query language
От: chaotic-kotik  
Дата: 13.07.16 09:29
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Это лишь пример в ответ на заявления о невозможности в другой теме.


Тогда вопросов больше нет, действительно неплохая демонстрация возможностей языка


EP>Я думаю это от отсутствия массового спроса на работу с СУБД из C++. Мне вот тоже такие задачи не попадаются совсем.


Скорее tradeoffs при работе с БД немного другие и производительность достигается другими методами.

CK>>Проще вынести весь код работающий с БД в отдельный DAL слой и работать с ним.


EP>DAL как реализовывать? На каком языке/библиотеке?


Я как правило предпочитаю нативную клиентскую библиотеку использовать ну и обычные текстовые запросы + prepare для некоторых.

EP>Где делать требуемые проекции, фильтрации? Всё в DAL расписывать?


Да, а что такого? Вынести в отдельный модуль, чтобы все приложение работало через какой-нибудь абстрактный интерфейс и не знало вообще, с чем оно работает, с БД, или еще каким-нибудь источником данных. Тут можно свапнуть БД на другую (например в тестах использовать sqlite вместо постгреса или вообще какой-нибудь nosql). Я еще в одном проекте видел такое — каждый row в результатах запроса конвертировался в boost.property_tree, который передавался кругом. Все ф-ии работающие с данными принимали property_tree, все довольно универсально и гибко было, скажем коду было не важно, пришел property_tree из базы или из POST запроса. Накладные расходы конечно большие, но это сильно лучше чем размазывать работу с БД по всему приложению и при каждом изменении схемы переписывать код и пересобирать.

И что ты понимаешь под фильтрациями? Where statement в SQL или обработку результатов запроса?
Отредактировано 13.07.2016 9:34 chaotic-kotik . Предыдущая версия .
Re[7]: [proof-of-concept] compile time query language
От: chaotic-kotik  
Дата: 13.07.16 09:35
Оценка:
Здравствуйте, niXman, Вы писали:

X>все же больше сервисов... есть игровой проект, весьма популярный, там постгрес в качестве хранилища истории действий. там порядка 2к записей в секунду. и это отдельный сервер.

X>остальные совсем не нагруженные.

2к это тоже совсем не нагруженный
Re[21]: [proof-of-concept] compile time query language
От: Erop Россия  
Дата: 13.07.16 10:10
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

E>>Ну вот в случае 2-х namespaces, как-то удаётся обойтись без описания join...

EP>Каких namespaces?
С++

EP>Это будет как минимум не проще, и при этом на более низком уровне.

Проще это будет при использовании библиотек метапрограммирования, буста там и прочей обвязки. Если написать аналогичных библиотек про AST, то будет проще...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[4]: [proof-of-concept] compile time query language
От: Evgeny.Panasyuk Россия  
Дата: 13.07.16 11:11
Оценка:
Здравствуйте, chaotic-kotik, Вы писали:

EP>>DAL как реализовывать? На каком языке/библиотеке?

CK>Я как правило предпочитаю нативную клиентскую библиотеку использовать ну и обычные текстовые запросы + prepare для некоторых.

Для большого количества сложных запросов это неудобно — нет типизации запросов и нормальных средств декомпозиции.
Если же запросов немного — то думаю не критично.

EP>>Где делать требуемые проекции, фильтрации? Всё в DAL расписывать?

CK>Да, а что такого? Вынести в отдельный модуль, чтобы все приложение работало через какой-нибудь абстрактный интерфейс и не знало вообще, с чем оно работает, с БД, или еще каким-нибудь источником данных.
CK>...
CK>И что ты понимаешь под фильтрациями? Where statement в SQL или обработку результатов запроса?

Where clause. В разных частях программы могут потребоваться разные фильтры, разный набор колонок в select.
В итоге вместо from(foo).where(foo.id > 42_i).select(foo.value, foo.id, foo.number) у тебя в DAL будут функции вида foo_where_id_gt_42_select_value_id_number на каждый чих.

CK>Тут можно свапнуть БД на другую (например в тестах использовать sqlite вместо постгреса или вообще какой-нибудь nosql).


Возможность swap'а БД может быть внутри библиотеки запросов. То есть пишешь запрос на типизированном EDSL, а выхлоп будет отличаться в зависимости от БД.

CK>и при каждом изменении схемы переписывать код


Во-первых тебе и в случае с DAL придётся переписывать код.
Во-вторых как раз за счёт средств декомпозиции запросов на типизированом EDSL переписывать нужно меньше, так как легко вынести повторно используемые части запроса в отдельные функции.
Re[22]: [proof-of-concept] compile time query language
От: Evgeny.Panasyuk Россия  
Дата: 13.07.16 11:23
Оценка:
Здравствуйте, Erop, Вы писали:

E>>>Ну вот в случае 2-х namespaces, как-то удаётся обойтись без описания join...

EP>>Каких namespaces?
E>С++

О чём речь?
Re[23]: [proof-of-concept] compile time query language
От: Erop Россия  
Дата: 13.07.16 11:47
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

E>>>>Ну вот в случае 2-х namespaces, как-то удаётся обойтись без описания join...

EP>>>Каких namespaces?
E>>С++

EP>О чём речь?

namecpace foo {
    int name;
    int bum;
    int bom;
}// namecpace foo

namecpace bar {
    int name;
    int bah;
    int buh;
}// namecpace bar

// где-то дальше
using namecpace foo;
using namecpace bar;

int res = bum + buh +
// Но
    foo.name + bar.name;
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[5]: [proof-of-concept] compile time query language
От: chaotic-kotik  
Дата: 13.07.16 12:01
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Для большого количества сложных запросов это неудобно — нет типизации запросов и нормальных средств декомпозиции.

EP>Если же запросов немного — то думаю не критично.

Что есть "нормальные средства декомпозиции"? Как это должно выглядеть в коде?

EP>Where clause. В разных частях программы могут потребоваться разные фильтры, разный набор колонок в select.

EP>В итоге вместо from(foo).where(foo.id > 42_i).select(foo.value, foo.id, foo.number) у тебя в DAL будут функции вида foo_where_id_gt_42_select_value_id_number на каждый чих.

Ясно. Думаю что код на таком уровне не должен работать (он не должен знать что у тебя есть табличка foo и поле id, не важно как, в виде EDSL или части имени метода). Это чревато ошибками и (что намного хуже) проблемами с производительностью. Так как where clauses у тебя разбросаны по всему коду и чтобы понять, какие индексы нужно создавать, придется собирать статистику с работающего приложения, профилировать и тд. Ну а потом кто-нибудь добавить еще один фильтр и придется все начинать сначала.

Когда же у тебя есть DAL, ты будешь работать через него и все эти where можно просто проскроллить и глазами посмотреть в одном месте. И выглядит это обычно не так как ты описал а как-то так:

auto publisher = DAL.get_publisher(id);  // executes `SELECT * FROM "Publishers" WHERE id = 42`
...
Filter flt;
flt.include_if(Placement.Fields.RECT, Filter.GT, BoundingRectangle(100, 200));
auto placements_list = publisher.get_placements(filter);  // executes `SELECT * FROM "Placements" WHERE id = 42 and (width > 200 and height > 100)`


EP>Во-первых тебе и в случае с DAL придётся переписывать код.

EP>Во-вторых как раз за счёт средств декомпозиции запросов на типизированом EDSL переписывать нужно меньше, так как легко вынести повторно используемые части запроса в отдельные функции.

DAL можно вынести в отдельный модуль, второе предложение — не понял как это, честно говоря, тебе что так что так нужен слой, который бы абстрагировал работу с БД, чтобы код приложения работал с объектами в памяти а не генерировал (пусть и через хитрый EDSL) запросы к БД, так что я вижу только увеличение сложности и уровня абстракции
Отредактировано 13.07.2016 12:02 chaotic-kotik . Предыдущая версия .
Re[6]: [proof-of-concept] compile time query language
От: Evgeny.Panasyuk Россия  
Дата: 13.07.16 19:46
Оценка:
Здравствуйте, chaotic-kotik, Вы писали:

CK>Так как where clauses у тебя разбросаны по всему коду и чтобы понять, какие индексы нужно создавать, придется собирать статистику с работающего приложения, профилировать и тд. Ну а потом кто-нибудь добавить еще один фильтр и придется все начинать сначала.


SQL код генерируется во время компиляции — легко получить список всех запросов. Либо из asm/бинарного кода, либо просто убрать определение значения строки — тогда компилятор выведет список всех запросов в ошибках компиляции/линковки.

CK>Когда же у тебя есть DAL, ты будешь работать через него и все эти where можно просто проскроллить и глазами посмотреть в одном месте. И выглядит это обычно не так как ты описал а как-то так:

CK>
CK>auto publisher = DAL.get_publisher(id);  // executes `SELECT * FROM "Publishers" WHERE id = 42`
CK>


Так в том и дело, "SELECT *" выдаст все колонки, что часто неэффективно, и в разных местах требуются разные проекции. В случае с DAL придётся либо плодить функции на каждый чих, либо вводить аналогичный EDSL.

CK>
CK>Filter flt;
CK>flt.include_if(Placement.Fields.RECT, Filter.GT, BoundingRectangle(100, 200));
CK>auto placements_list = publisher.get_placements(filter);  // executes `SELECT * FROM "Placements" WHERE id = 42 and (width > 200 and height > 100)`
CK>


А здесь ты точно также создаёшь фильтр на стороне пользователя, только в другой форме, но суть та же.

EP>>Во-первых тебе и в случае с DAL придётся переписывать код.

CK>DAL можно вынести в отдельный модуль,

Вот только его абстракция будет постоянно протекать — придётся на каждую проекцию делать функции, точно также реализовывать внешние фильтры, при этом не особо ясно что получаем из бенефитов.

EP>>Во-вторых как раз за счёт средств декомпозиции запросов на типизированом EDSL переписывать нужно меньше, так как легко вынести повторно используемые части запроса в отдельные функции.

EP>>второе предложение — не понял как это, честно говоря,

Можно часто используемый запрос в отдельную функцию:
auto recent_customers()
{
    return from(customers) ...;
}
...
{
    auto q = from(recent_customers).join(...).where(...).select(...);
    // ...
}

Или например кусок запроса, типа фильтра:
auto common_filter(auto q)
{
    return q.where(...);
}

При этом всё остаётся типизировано, никакой ручной склейки строк и т.п., ошибки ловятся на этапе компиляции.
Более того, полный запрос может оптимизироваться под конкретную БД — как например в конце первого сообщения темы.

EP>тебе что так что так нужен слой, который бы абстрагировал работу с БД, чтобы код приложения работал с объектами в памяти а не генерировал (пусть и через хитрый EDSL) запросы к БД, так что я вижу только увеличение сложности и уровня абстракции


EDSL запросов по сути не особо отличается от того же Boost.Range например — практически такое же декларативное описание "запроса" к контейнерам. При этом вызовы Boost.Range ни в какой DAL не прячут.
Re[7]: [proof-of-concept] compile time query language
От: chaotic-kotik  
Дата: 14.07.16 07:51
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>SQL код генерируется во время компиляции — легко получить список всех запросов. Либо из asm/бинарного кода, либо просто убрать определение значения строки — тогда компилятор выведет список всех запросов в ошибках компиляции/линковки.


А что если у меня условия формируются динамически? Допустим есть сервис, к сервису происходит обращение вида GET http://path?field1&lt;100&amp;field2&gt;200 а задача сервиса — понять как сформировать запрос и преобразовать его в какой-нибудь JSON например. Ну или limit/offset clauses, тоже всегда динамически выставляются. В общем, запрос в compile time это очень примитивный сценарий использования. В таких случаях список запросов уже совсем не просто получить, придется использовать средства профилирования БД. Ну и весь type safety куда-то внезапно улетучивается и становится окончательно непонятно, с чего это вдруг мы используем какой-то EDSL.

EP>Так в том и дело, "SELECT *" выдаст все колонки, что часто неэффективно, и в разных местах требуются разные проекции. В случае с DAL придётся либо плодить функции на каждый чих, либо вводить аналогичный EDSL.


Ну это я для простоты через * написал, само собой так делать не нужно никогда.

CK>>
CK>>Filter flt;
CK>>flt.include_if(Placement.Fields.RECT, Filter.GT, BoundingRectangle(100, 200));
CK>>auto placements_list = publisher.get_placements(filter);  // executes `SELECT * FROM "Placements" WHERE id = 42 and (width > 200 and height > 100)`
CK>>


EP>А здесь ты точно также создаёшь фильтр на стороне пользователя, только в другой форме, но суть та же.


Ну по сути да. Можно это в expression template запихать, но тогда теряется динамика. Еще из такого можно легко prepared statement сгенирировать, а в статике нельзя (ну либо твой EDSL начнет работать полностью в динамике).

EP>Вот только его абстракция будет постоянно протекать — придётся на каждую проекцию делать функции, точно также реализовывать внешние фильтры, при этом не особо ясно что получаем из бенефитов.


Ну так если сделать так, как я выше описал то не придется. Я вдел реализации, которые позволяют запросы генерировать из AST, который можно например сериализовать в промежуточный формат или например получить AST из строчки URL или POST-данных HTTP запроса а потом сделать из этого запрос. Так же этот AST можно было анализировать перед исполнением на предмет ошибок и SQL injection. При этом AST был (само собой) абстрактным и не содержал названий полей и таблиц, вместо этого он оперировал сущностями предметной области.

EP>EDSL запросов по сути не особо отличается от того же Boost.Range например — практически такое же декларативное описание "запроса" к контейнерам. При этом вызовы Boost.Range ни в какой DAL не прячут.


Ну так boost.range оперирует объектами в коде программы, SQL запрос оперирует данными в таблицах и по хорошему код не должен ничего знать про имена таблиц/полей, layout, join-s и тд. Для этого и нужен слой DAL. Иначе миграции схемы БД превратятся в сложные рефакторинги а это не есть хорошо. И ты ошибочно предполагаешь что построение запроса и конвертация результатов запроса — это то что нужно оптимизировать. Построение запроса это слезы, этот запрос потом отправляется на другую машину, где он парсится, оптимизируется и исполняется. За время оптимизации запроса можно его миллион раз сгенерировать повторно. Zero overhead преобразование результатов тоже мало полезного дает — некоторые БД отдают результаты в виде текста, некоторые предоставляют апи для доступа к данным результатов запросов вида `int getFieldAsInt(int field_index, int default_value)` и что ты вызовешь этот метод в своем EDSL, что класс в DAL вызовет его, разницы никакой.
Отредактировано 14.07.2016 7:53 chaotic-kotik . Предыдущая версия .
Re[8]: [proof-of-concept] compile time query language
От: Evgeny.Panasyuk Россия  
Дата: 14.07.16 09:57
Оценка:
Здравствуйте, chaotic-kotik, Вы писали:

CK>Ну и весь type safety куда-то внезапно улетучивается и становится окончательно непонятно, с чего это вдруг мы используем какой-то EDSL.


При динамических запросах type-safety никуда не исчезает. Исчезает возможность сгенерировать код запроса во время компиляции, и то только для случаев где пространство динамического выбора большое.

EP>>Так в том и дело, "SELECT *" выдаст все колонки, что часто неэффективно, и в разных местах требуются разные проекции. В случае с DAL придётся либо плодить функции на каждый чих, либо вводить аналогичный EDSL.

CK>Ну это я для простоты через * написал, само собой так делать не нужно никогда.

Так а как ты предлагаешь делать? На каждый вариант проекции делать отдельную функцию в DAL? Постоянно модифицировать API DAL при необходимости нового поля?

EP>>А здесь ты точно также создаёшь фильтр на стороне пользователя, только в другой форме, но суть та же.

CK>Ну по сути да.

Так а в чём профит?

EP>>EDSL запросов по сути не особо отличается от того же Boost.Range например — практически такое же декларативное описание "запроса" к контейнерам. При этом вызовы Boost.Range ни в какой DAL не прячут.

CK>Ну так boost.range оперирует объектами в коде программы, SQL запрос оперирует данными в таблицах и по хорошему код не должен ничего знать про имена таблиц/полей, layout, join-s и тд.

Почему не должен знать? Зачем вводить новые слои на ровном месте? То есть что это даст по факту? И реально ли это необходимо?

CK>Для этого и нужен слой DAL. Иначе миграции схемы БД превратятся в сложные рефакторинги а это не есть хорошо.


За счёт типизации как раз проводить рефакторинги намного проще. Ошибки отлавливаются на этапе компиляции. В случае с текстовыми запросами точно также придётся проводить рефакторинги, только уже без помощи типизации.

CK>И ты ошибочно предполагаешь что построение запроса и конвертация результатов запроса — это то что нужно оптимизировать.


Во-первых, я это не предполагаю. Я лишь сказал что это возможно, в ответ на что получил сомнения в реализуемости. Собственно весь пример это лишь proof-of-concept.
Во-вторых, если у нас в любом случае есть compile-time EDSL запросов (а-ля sqlpp11) и можно достаточно просто (используя новые версии C++, Hana-style и т.п.) оптимизировать в том числе и эти моменты — то почему бы и нет.

CK>Zero overhead преобразование результатов тоже мало полезного дает


Это по сути несколько строчек кода, смотри map_result — так почему бы и нет?

CK>- некоторые БД отдают результаты в виде текста, некоторые предоставляют апи для доступа к данным результатов запросов вида `int getFieldAsInt(int field_index, int default_value)` и что ты вызовешь этот метод в своем EDSL, что класс в DAL вызовет его, разницы никакой.


Вот только в DAL ты будешь путаться в индексах, а EDSL это разрулит автоматом.
Более того, в той КСВ теме даже были заявления, что мол руками никто код с индексами и не пишет, вместо этого используют более медленные варианты (этим оправдывали сравнение EDSL варианта на индексах, против ручного варианта без индексов).
Re[9]: [proof-of-concept] compile time query language
От: chaotic-kotik  
Дата: 14.07.16 11:45
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Так а как ты предлагаешь делать? На каждый вариант проекции делать отдельную функцию в DAL? Постоянно модифицировать API DAL при необходимости нового поля?


Отдельную ф-ю на каждый вариант проектции делать не нужно, ее нужно делать на каждый поддерживаемый тип данных, ф-я при этом должна принимать фильтр, который строится в runtime динамически. Поинт в том, чтобы приложение работало с объектами класса Publisher и Placement а не с таблицами, проекциями и джоинами.

EP>Так а в чём профит?

EP>Почему не должен знать? Зачем вводить новые слои на ровном месте? То есть что это даст по факту? И реально ли это необходимо?

Простой пример, приложение работающее с каталогом — есть две таблицы, которые обозначают модель мобильного телефона и конкретное устройство, скажем iPhone 6s и 64х гигабайтная версия iPhone 6s. Пользователю показывают в каталоге продуктов iPhone 6s, когда он по нему кликает — показывают список устройств с разным объемом памяти и размером экрана. Чтобы показать все параметры нужно сделать join двух таблиц. Мы попрофилировали и поняли, что джоин съедает дофига, нужно денормализовать эти две таблицы сделав одну. Теперь мы можем одним select-ом выбрать iPhone 6s 64GB устройство или iPhone 6s модель телефона и там будут все нужные данные.

Теперь я пойду в код и поменяю один файл с кодом в своем dal, а ты будешь искать в коде все функции, использующие эти таблицы. Мне не нужно будет менять прикладную логику вообще, я смогу сделать так, что код будет работать и со старой схемой и с новой не напрягаясь (dal сможет определить при подключении с какой схемой ему предстоит работать) и прикладной код вообще не изменится. Когда я мигрирую схему в проде, мне даже не придется перезапускать сервисы, так как после рестарта БД сервис заново подключится к ней и начнет использовать код, умеющий работать с новой схемой. Собственно для всего этого и нужен динамизм и поэтому запросы, размазанные по коду (пусть и проверяющиеся в compile time компилятором С++) не работают.

EP>За счёт типизации как раз проводить рефакторинги намного проще. Ошибки отлавливаются на этапе компиляции. В случае с текстовыми запросами точно также придётся проводить рефакторинги, только уже без помощи типизации.


Ты предлагаешь парадоксальную вещь — рефакторить схему БД в коде. Изменили имя поля — рефакторить? Суть БД в том, что приложению класть на layout данных в БД, для этого есть SQL. Маппинг твоей объектной модели на поля таблиц БД должен быть не кодом а конфигурацией. Изменил схему — поменяй конфиг. Можно конечно этот конфиг хранить в коде приложения и генерировать запросы в compile-time, но вообще лучше подкачивать его из БД.

EP>Во-вторых, если у нас в любом случае есть compile-time EDSL запросов (а-ля sqlpp11) и можно достаточно просто (используя новые версии C++, Hana-style и т.п.) оптимизировать в том числе и эти моменты — то почему бы и нет.


Потому что это очень ограниченный способ.

EP>Вот только в DAL ты будешь путаться в индексах, а EDSL это разрулит автоматом.

EP>Более того, в той КСВ теме даже были заявления, что мол руками никто код с индексами и не пишет, вместо этого используют более медленные варианты (этим оправдывали сравнение EDSL варианта на индексах, против ручного варианта без индексов).

Тоже мне проблема, индексы
Отредактировано 14.07.2016 11:50 chaotic-kotik . Предыдущая версия .
Re[10]: [proof-of-concept] compile time query language
От: Evgeny.Panasyuk Россия  
Дата: 14.07.16 20:12
Оценка:
Здравствуйте, chaotic-kotik, Вы писали:

EP>>Так а как ты предлагаешь делать? На каждый вариант проекции делать отдельную функцию в DAL? Постоянно модифицировать API DAL при необходимости нового поля?

CK>Отдельную ф-ю на каждый вариант проектции делать не нужно, ее нужно делать на каждый поддерживаемый тип данных,

Все поля (даже одного типа данных) не всегда нужны. И поэтому либо функция на каждую проекцию, либо тормоза.

EP>>Так а в чём профит?

EP>>Почему не должен знать? Зачем вводить новые слои на ровном месте? То есть что это даст по факту? И реально ли это необходимо?
CK>Простой пример, приложение работающее с каталогом — есть две таблицы, которые обозначают модель мобильного телефона и конкретное устройство, скажем iPhone 6s и 64х гигабайтная версия iPhone 6s. Пользователю показывают в каталоге продуктов iPhone 6s, когда он по нему кликает — показывают список устройств с разным объемом памяти и размером экрана. Чтобы показать все параметры нужно сделать join двух таблиц. Мы попрофилировали и поняли, что джоин съедает дофига, нужно денормализовать эти две таблицы сделав одну.

А зачем вручную денормализовать когда есть materialized view?

CK>Теперь мы можем одним select-ом выбрать iPhone 6s 64GB устройство или iPhone 6s модель телефона и там будут все нужные данные.

CK>Теперь я пойду в код и поменяю один файл с кодом в своем dal, а ты будешь искать в коде все функции, использующие эти таблицы.

Не буду, это пример на декомпозицию. Я уже выше показывал:

EP>Можно часто используемый запрос в отдельную функцию:
EP>

EP>auto recent_customers()
EP>{
EP>    return from(customers) ...;
EP>}
EP>...
EP>{
EP>    auto q = from(recent_customers).join(...).where(...).select(...);
EP>    // ...
EP>}
EP>

Вот только эту функцию (recent_customers) и придётся менять.
А в случае DAL и простых строковых запросов у тебя внутри DAL нужно будет менять много мест, так как нет нормальных средств декомпозиции.

EP>>За счёт типизации как раз проводить рефакторинги намного проще. Ошибки отлавливаются на этапе компиляции. В случае с текстовыми запросами точно также придётся проводить рефакторинги, только уже без помощи типизации.

CK>Ты предлагаешь парадоксальную вещь — рефакторить схему БД в коде. Изменили имя поля — рефакторить?

Нет, я говорю что легче рефакторить код, имея типизацию. Обновил схему в коде из БД — все ошибки вылезли во время компиляции.

CK>Суть БД в том, что приложению класть на layout данных в БД, для этого есть SQL.


БД абстрагирует от физического layout и прочего, но не от схемы. При изменении схемы, код придётся менять и в моём и в твоём варианте. Причём в твоём ещё может придётся менять код промежуточного слоя.
Отредактировано 14.07.2016 20:40 Evgeny.Panasyuk . Предыдущая версия .
Re[11]: [proof-of-concept] compile time query language
От: chaotic-kotik  
Дата: 14.07.16 21:05
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Все поля (даже одного типа данных) не всегда нужны. И поэтому либо функция на каждую проекцию, либо тормоза.


Их можно как-инбудь передать в функцию. Ты же тоже их по сути явно передаешь через свой `select`.

EP>А зачем вручную денормализовать когда есть materialized view?


Это просто пример. Materialized views кстати далеко не всегда можно использовать. Например БД может их тупо не уметь делать (постгрес например).

EP>Не буду, это пример на декомпозицию. Я уже выше показывал:

EP>

EP>>Можно часто используемый запрос в отдельную функцию:
EP>>

EP>>auto recent_customers()
EP>>{
EP>>    return from(customers) ...;
EP>>}
EP>>...
EP>>{
EP>>    auto q = from(recent_customers).join(...).where(...).select(...);
EP>>    // ...
EP>>}
EP>>

EP>Вот только эту функцию (recent_customers) и придётся менять.
EP>А в случае DAL и простых строковых запросов у тебя внутри DAL нужно будет менять много мест, так как нет нормальных средств декомпозиции.

Если они не в compile-time это не значит что они плохие. Кстати, как ты планируешь гарантировать существование нужных таблиц и полей в БД? Тот случай когда в коде все ок и все компилируется, но схема изменилась. КМК из constexpr к БД никак не подключиться

EP>Нет, я говорю что легче рефакторить код, имея типизацию. Обновил схему в коде из БД — все ошибки вылезли во время компиляции.


Ну можно ведь не рефактроирть. Не обязательно приложению заранее знать какие там у объекта поля в БД есть. Если это какой-нибудь веб сервис — очень часто его можно написать так, чтобы он ничего такого не знал заранее и динамически загружал схему (и даже изменения на лету подхватывал). Мне, в моей практике, обычно что-то подобное попадается. А вот в таком стиле как ты хочешь реализовать, тоже, но обычно это более простые приложения.
Re[12]: [proof-of-concept] compile time query language
От: Evgeny.Panasyuk Россия  
Дата: 14.07.16 21:18
Оценка:
Здравствуйте, chaotic-kotik, Вы писали:

EP>>Все поля (даже одного типа данных) не всегда нужны. И поэтому либо функция на каждую проекцию, либо тормоза.

CK>Их можно как-инбудь передать в функцию. Ты же тоже их по сути явно передаешь через свой `select`.

В итоге у тебя будет и EDSL для передачи фильтров, и EDSL для передачи списка полей, плюс приложение будет знать о конкретных полях. Так в чём в итоге профит, если вся абстракция уже протекла?

EP>>[/q]

EP>>Вот только эту функцию (recent_customers) и придётся менять.
EP>>А в случае DAL и простых строковых запросов у тебя внутри DAL нужно будет менять много мест, так как нет нормальных средств декомпозиции.
CK>Если они не в compile-time это не значит что они плохие.

Дело не compile/run-time, а в отсутствии типизации.

CK>Кстати, как ты планируешь гарантировать существование нужных таблиц и полей в БД? Тот случай когда в коде все ок и все компилируется, но схема изменилась.


Например проверять версию схемы в runtime.
С DAL у тебя точно такие же проблемы. Точнее даже хуже — например обновил схему, типа обновил код, но ошибки типа неверных имён полей в тексте запросов и т.п. могут спокойно дожить до runtime'а.

EP>>Нет, я говорю что легче рефакторить код, имея типизацию. Обновил схему в коде из БД — все ошибки вылезли во время компиляции.

CK>Ну можно ведь не рефактроирть.

Так ведь DAL всё равно придётся рефакторить.

CK>Не обязательно приложению заранее знать какие там у объекта поля в БД есть.


Выше, для проекций, ты именно предлагаешь вынести знание о полях в приложение.
Re[13]: [proof-of-concept] compile time query language
От: chaotic-kotik  
Дата: 14.07.16 21:37
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>В итоге у тебя будет и EDSL для передачи фильтров, и EDSL для передачи списка полей, плюс приложение будет знать о конкретных полях. Так в чём в итоге профит, если вся абстракция уже протекла?


Nope.

EP>Дело не compile/run-time, а в отсутствии типизации.


Которая тут не нужна.

EP>Например проверять версию схемы в runtime.

EP>С DAL у тебя точно такие же проблемы. Точнее даже хуже — например обновил схему, типа обновил код, но ошибки типа неверных имён полей в тексте запросов и т.п. могут спокойно дожить до runtime'а.

Пытаюсь тут на простую схему намекать. Представь что у тебя есть таблица с документами (куча полей), есть таблица с метаданными, в метаданных описано в какой таблице хранятся документы, в каком поле этой таблицы хранится то или иное поле документа (у поля есть свое имя, которое в общем случае не равно имени поля таблицы). Так же там описываются типы полей и прочие метаданные (нужно ли отображать поле документа в UI или передавать его в результатах запроса, когда сервис его запрашивает, можно ли по нему фильтровать и тд). Наш DAL в этом случае заранее знает формат таблицы с метаданными и все.

Далее, нам нужно завести новое поле — добавляем новую запись в таблицу метаданных, запускаем скрипт — скрипт делает миграцию (делает ALTER TABLE нужной таблице).

При старте сервис читает метаданные и после этого может работать с новой схемой. В этой архитектуре ничего рефактрить не нужно никогда вообще. Поля новые добавлять или удалять при этом можно. Ес-но это упрощенный пример, в реальной жизни все несколько сложнее, может быть множество сущностный, они могут тоже добавляться и удаляться, могут поддерживаться всякие словари и даже индексы могут автоматически строиться при миграциях. И тут все динамически потому что до старта приложения проверять нечего.
Re[14]: [proof-of-concept] compile time query language
От: Evgeny.Panasyuk Россия  
Дата: 14.07.16 23:25
Оценка: +1
Здравствуйте, chaotic-kotik, Вы писали:

EP>>Дело не compile/run-time, а в отсутствии типизации.

CK>Которая тут не нужна.

Почему? Вручную выписывать индексы, структуры, кое-как склеивать текст запроса, и к тому же ручную контролировать корректность всего этого. Ради чего?

EP>>Например проверять версию схемы в runtime.

EP>>С DAL у тебя точно такие же проблемы. Точнее даже хуже — например обновил схему, типа обновил код, но ошибки типа неверных имён полей в тексте запросов и т.п. могут спокойно дожить до runtime'а.
CK>Пытаюсь тут на простую схему намекать. Представь что у тебя есть таблица с документами (куча полей), есть таблица с метаданными, в метаданных описано в какой таблице хранятся документы,

Это лишь один частный случай, в котором нужна динамика. EDSL запросов может работать в том числе и с динамикой, там где это необходимо — например как в sqlpp11.

CK>Далее, нам нужно завести новое поле — добавляем новую запись в таблицу метаданных, запускаем скрипт — скрипт делает миграцию (делает ALTER TABLE нужной таблице).

CK>При старте сервис читает метаданные и после этого может работать с новой схемой. В этой архитектуре ничего рефактрить не нужно никогда вообще.

Неверно. Не надо рефакторить только определённом наборе вариантов расширяемость которых заранее заложена в изначальную задачу. Причём в случае EDSL в этих же случаях точно также не нужно рефакторить.
Если же произошло любое изменение вне этой расширяемости (например изменилась структура метаданных) — то нужно точно также рефакторить. При этом, опять таки, на основе текстовых запросов это всё делается труднее.

То есть ты говоришь мол есть документ с произвольным набором полей описанным метаданными, и на основе того что можно в него добавлять поля без рефакторинга, ты делаешь неверное обобщение "Ну можно ведь не рефактроирть"
Re[15]: [proof-of-concept] compile time query language
От: chaotic-kotik  
Дата: 15.07.16 08:40
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Почему? Вручную выписывать индексы, структуры, кое-как склеивать текст запроса, и к тому же ручную контролировать корректность всего этого. Ради чего?


Ради того чтобы не думать, а правильно ли эта языковая конструкция сгенерировала запрос. Или для того, чтобы любому было понятно что происходит в коде, без необходимости изучать еще одну библиотеку. Мало ли причин можно придумать. Причин за это я вижу не очень много. Корректность у тебя все равно контролируется в рантайме (т.к. самая частая проблема это не неправильно составленный запрос а изменение схемы, не отраженное в коде, а такое у тебя будет все равно отлавливаться во время исполнения).

CK>>Пытаюсь тут на простую схему намекать. Представь что у тебя есть таблица с документами (куча полей), есть таблица с метаданными, в метаданных описано в какой таблице хранятся документы,

EP>Это лишь один частный случай, в котором нужна динамика. EDSL запросов может работать в том числе и с динамикой, там где это необходимо — например как в sqlpp11.

Этот один частный случай я за последние лет наверное пять вижу вообще везде. Entity framework например похожим образом работает, или Hibernate. Реально с SQL-ем напрямую работают разве что всякие простые приложения, во всех сложных приложениях, с которыми мне приходилось работать, реализуется некий слой абстракции вокруг БД.

CK>>Далее, нам нужно завести новое поле — добавляем новую запись в таблицу метаданных, запускаем скрипт — скрипт делает миграцию (делает ALTER TABLE нужной таблице).

CK>>При старте сервис читает метаданные и после этого может работать с новой схемой. В этой архитектуре ничего рефактрить не нужно никогда вообще.

EP>Неверно. Не надо рефакторить только определённом наборе вариантов расширяемость которых заранее заложена в изначальную задачу. Причём в случае EDSL в этих же случаях точно также не нужно рефакторить.


Это почему не нужно будет? Тебе нужно будет новое поле добавить в структуру и модифицировать запросы, в зависимости от того, какая проекция там нужна.

Вот например есть табличка каталога оборудования и я добавил туда свойство `last_edit_time` которое содержит время последнего изменения. Теперь при выгрузке данных в xml мне нужно это поле читать, в UI контентщика мне нужно его показывать и фильтровать по нему, но в UI пользователя — не нужно показывать. Следовательно, два из трех запросов мне нужно изменить. Мало того, у этого поля должно быть имя в приложении (и в пользовательском и в сервисе), т.е. добавляя поле ты изменяешь сразу несколько артефактов. В сервисе появляется возможность обратиться к URL `/get_catalog_item?id=XXX&last_edit_time<7d-ago`, в UI контентщика появится возможность по этому полю искать и тд.

С общепринятым подходом ты просто добавишь запись в таблицу, в которой у тебя свойства каталога оборудования лежат, вот и все (может это будет SDL какой-нибудь, не обязательно табличка). В этой записи будет информация о том что поле доступно в таких то запросах, по нему можно фильтровать, его не нужно показывать пользователю но нужно показывать контентщику и тд. После миграции твой веб сервис будет принимать свойство last_edit_time (которое возможно будет по другому называться, имя поля в БД будет скрыто) и будет по нему фильтровать, в UI контентщика появится возможность выбрать это поле и отсортировать по нему или отфильтровать. Все это без необходимости изменять код вообще хоть где-нибудь. Конечно, если свойство добавляется в UI пользователя — возможно придется изменить верстку (или гуй) под это дело, но и то не всегда. Я видел несколько крупных веб проектов и одну систему документооборота предприятия, все они были построены таким образом.

Вот собственно это и есть современное состояние дел. Поэтому я и написал о том что вся эта статика — не нужна. Очень поверхностно смотрел на sqlpp11, но мне показалось что это не шаг вперед, а скорее шаг в сторону. Писать запросы в коде более удобно, но это очень низкий уровень. Ну да, можно отловить баги в SQL запросах, но я могу SQL написать и проверить заранее в терминале и потом скопировать его себе в код один в один. Никогда проблема опечаток в SQL у меня остро не стояла, к счастью, поэтому проверка SQL запросов (на самом базовом уровне) во время компиляции — кажется мне чем-то странным.

EP>Если же произошло любое изменение вне этой расширяемости (например изменилась структура метаданных) — то нужно точно также рефакторить. При этом, опять таки, на основе текстовых запросов это всё делается труднее.


Структура метаданных это, как правило, либо готовый фреймверк, либо если это что-то самописное, то меняется все довольно просто, в одном месте.

EP>То есть ты говоришь мол есть документ с произвольным набором полей описанным метаданными, и на основе того что можно в него добавлять поля без рефакторинга, ты делаешь неверное обобщение "Ну можно ведь не рефактроирть"


Я описал упрощенную схему. В реальной жизни это скорее — есть модель данных, которая описана метаданными и в которую заложена расширяемость, поэтому ее можно легко расширять и это не требует изменений кода зачастую (если завязок на конкретные поля в коде сервиса/UI нет, что справедливо для большинства полей). Там конечно же бывает не одна табличка а куча таблиц, связей и тд.
Re: [proof-of-concept] compile time query language
От: chaotic-kotik  
Дата: 15.07.16 08:55
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>

CTQL — Compile Time Query Language

EP>В рамках одной из КСВ тем сделал proof-of-concept встроенного языка запросов времени компиляции, а точнее одной конкретной формы выражения (примеров EDSL и так предостаточно).
EP>Все этапы, включая генерацию текста SQL запроса, вынесены в compile-time.

Кстати, было бы интересно, на мой взгляд, использовать этот подход для генерации protobuf кода. Очень часто через protobuf общаются только С++ приложения и вместо использования IDL и отдельного этапа сборки для генерации обвязки, можно было бы использовать метапрограммирование.
Re[2]: [proof-of-concept] compile time query language
От: Evgeny.Panasyuk Россия  
Дата: 15.07.16 09:37
Оценка:
Здравствуйте, chaotic-kotik, Вы писали:

CK>Кстати, было бы интересно, на мой взгляд, использовать этот подход для генерации protobuf кода. Очень часто через protobuf общаются только С++ приложения и вместо использования IDL и отдельного этапа сборки для генерации обвязки, можно было бы использовать метапрограммирование.


Рекурсивная сериализация структур данных? Это делается на Boost.Fusion (а сейчас и на Boost.Hana) по сути в одну короткую функцию
Автор: Evgeny.Panasyuk
Дата: 06.10.14
.
Отредактировано 15.07.2016 9:38 Evgeny.Panasyuk . Предыдущая версия .
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.