42 std::size_t counter_id;
44 uint64_t stride_in_bits;
46 std::vector<stride_info_t> stride_info_stack;
49 std::size_t
id = -1UL;
50 std::size_t offset_id = -1UL;
56 operator bool() {
return id != -1UL; }
57 bool adjustable_offset() {
return offset_id != -1UL; }
60 std::unordered_map<std::size_t, std::size_t> leaf2id;
61 std::unordered_map<std::size_t, std::size_t> leaf2mask;
64 const bool needs_null_bitmap = [&]() {
74 auto find_null_bitmap = [&](
const DataLayout &layout, std::size_t row_id) ->
void {
75 auto find_null_bitmap_impl = [&](
const DataLayout::INode &node, uintptr_t offset, std::size_t row_id,
76 auto &find_null_bitmap_ref) ->
void
78 for (
auto &child : node) {
79 if (
auto child_leaf = cast<const DataLayout::Leaf>(child.ptr.get())) {
80 if (child_leaf->index() == null_bitmap_idx) {
81 M_insist(not null_bitmap_info,
"there must be at most one null bitmap in the linearization");
82 const uint64_t additional_offset_in_bits = child.offset_in_bits + row_id * child.stride_in_bits;
84 null_bitmap_info.id = SM.
add(
reinterpret_cast<void*
>(offset + additional_offset_in_bits / 8));
85 null_bitmap_info.bit_offset = (additional_offset_in_bits) % 8;
86 null_bitmap_info.bit_stride = child.stride_in_bits;
87 null_bitmap_info.num_tuples = node.num_tuples();
88 null_bitmap_info.row_id = row_id;
91 auto child_inode = as<const DataLayout::INode>(child.ptr.get());
92 const std::size_t lin_id = row_id / child_inode->num_tuples();
93 const std::size_t inner_row_id = row_id % child_inode->num_tuples();
94 const uint64_t additional_offset = child.offset_in_bits / 8 + lin_id * child.stride_in_bits / 8;
95 find_null_bitmap_ref(*child_inode, offset + additional_offset, inner_row_id, find_null_bitmap_ref);
99 find_null_bitmap_impl(
static_cast<const DataLayout::INode&
>(layout), uintptr_t(address), row_id,
100 find_null_bitmap_impl);
102 if (needs_null_bitmap)
103 find_null_bitmap(layout, row_id);
104 if (null_bitmap_info
and null_bitmap_info.bit_stride) {
105 null_bitmap_info.offset_id = SM.
add(null_bitmap_info.bit_offset);
109 auto compile_accesses = [&](
const DataLayout &layout, std::size_t row_id) ->
void {
110 auto compile_accesses_impl = [&](
const DataLayout::INode &node, uintptr_t offset, std::size_t row_id,
111 auto &compile_accesses_ref) ->
void
113 for (
auto &child : node) {
114 if (
auto child_leaf = cast<const DataLayout::Leaf>(child.ptr.get())) {
115 if (child_leaf->index() != null_bitmap_idx) {
116 const bool attr_can_be_null = null_bitmap_info
and layout_schema[child_leaf->index()].nullable();
122 const uint64_t additional_offset_in_bits = child.offset_in_bits + row_id * child.stride_in_bits;
123 const std::size_t byte_offset = additional_offset_in_bits / 8;
124 const std::size_t bit_offset = additional_offset_in_bits % 8;
125 M_insist(not bit_offset or child_leaf->type()->is_boolean() or child_leaf->type()->is_bitmap(),
126 "only booleans and bitmaps may not be byte aligned");
128 const std::size_t byte_stride = child.stride_in_bits / 8;
129 const std::size_t bit_stride = child.stride_in_bits % 8;
130 M_insist(not bit_stride or child_leaf->type()->is_boolean() or child_leaf->type()->is_bitmap(),
131 "only booleans and bitmaps may not be byte aligned");
132 M_insist(bit_stride == 0 or byte_stride == 0,
133 "the stride must be a whole multiple of a byte or less than a byte");
136 if (attr_can_be_null) {
137 if (not null_bitmap_info.bit_stride) {
139 const std::size_t bit_offset = null_bitmap_info.bit_offset + child_leaf->index();
140 if (bit_offset < 8) {
141 SM.emit_Ld_Ctx(null_bitmap_info.id);
142 if constexpr (IsStore) {
143 SM.emit_Ld_Tup(tuple_id, idx);
145 SM.emit_St_b(bit_offset);
147 SM.emit_Ld_b(0x1UL << bit_offset);
152 SM.emit_Ld_Ctx(null_bitmap_info.id);
154 if constexpr (IsStore) {
155 SM.emit_Ld_Tup(tuple_id, idx);
157 SM.emit_St_b(bit_offset % 8);
159 SM.emit_Ld_b(0x1UL << (bit_offset % 8));
164 M_insist(null_bitmap_info.adjustable_offset());
167 std::size_t address_id, mask_id;
168 if constexpr (IsStore) {
169 address_id = SM.
add(
reinterpret_cast<void*
>(0));
170 mask_id = SM.
add(uint64_t(0));
174 SM.emit_Ld_Ctx(null_bitmap_info.offset_id);
178 SM.emit_Ld_Ctx(null_bitmap_info.id);
180 if constexpr (IsStore)
181 SM.emit_Upd_Ctx(address_id);
185 if constexpr (IsStore) {
187 SM.emit_Ld_Tup(tuple_id, idx);
195 SM.emit_Ld_Ctx(null_bitmap_info.offset_id);
203 if constexpr (IsStore) {
204 SM.emit_Upd_Ctx(mask_id);
207 SM.emit_Ld_Ctx(address_id);
212 SM.emit_Ld_Ctx(mask_id);
214 SM.emit_Ld_Ctx(address_id);
228 if constexpr (not IsStore)
233 const std::size_t offset_id = SM.
add_and_emit_load(
reinterpret_cast<void*
>(offset + byte_offset));
234 leaf2id[child_leaf->index()] = offset_id;
237 M_insist(child_leaf->type()->is_boolean(),
"only booleans are supported yet");
239 if constexpr (IsStore) {
241 SM.emit_Ld_Tup(tuple_id, idx);
248 const std::size_t mask_id = SM.
add(uint64_t(0x1UL << bit_offset));
249 leaf2mask[child_leaf->index()] = mask_id;
251 if constexpr (IsStore) {
253 SM.emit_Ld_Ctx(offset_id);
255 SM.emit_Ld_Ctx(mask_id);
259 SM.emit_Ld_Ctx(offset_id);
261 SM.emit_Ld_Ctx(mask_id);
271 SM.emit_Ld_Ctx(mask_id);
275 if (attr_can_be_null)
284 SM.emit_Ld_Ctx(mask_id);
286 SM.emit_Upd_Ctx(mask_id);
293 SM.emit_Ld_Ctx(mask_id);
295 SM.emit_Upd_Ctx(mask_id);
300 SM.emit_Ld_Ctx(offset_id);
302 SM.emit_Upd_Ctx(offset_id);
305 if constexpr (IsStore) {
307 SM.emit_Ld_Tup(tuple_id, idx);
310 if (child_leaf->type()->is_boolean())
311 SM.emit_St_b(bit_offset);
313 SM.
emit_St(child_leaf->type());
316 if (child_leaf->type()->is_boolean())
317 SM.emit_Ld_b(0x1UL << bit_offset);
319 SM.
emit_Ld(child_leaf->type());
321 if (attr_can_be_null)
334 SM.emit_Ld_Ctx(offset_id);
336 SM.emit_Upd_Ctx(offset_id);
344 auto child_inode = as<const DataLayout::INode>(child.ptr.get());
345 const std::size_t lin_id = row_id / child_inode->num_tuples();
346 const std::size_t inner_row_id = row_id % child_inode->num_tuples();
347 const uint64_t additional_offset = child.offset_in_bits / 8 + lin_id * child.stride_in_bits / 8;
348 compile_accesses_ref(*child_inode, offset + additional_offset, inner_row_id, compile_accesses_ref);
352 compile_accesses_impl(
static_cast<const DataLayout::INode&
>(layout), uintptr_t(address), row_id,
353 compile_accesses_impl);
355 compile_accesses(layout, row_id);
358 if (null_bitmap_info
and null_bitmap_info.bit_stride) {
359 M_insist(null_bitmap_info.adjustable_offset());
360 M_insist(null_bitmap_info.num_tuples > 1);
363 SM.emit_Ld_Ctx(null_bitmap_info.offset_id);
366 SM.emit_Upd_Ctx(null_bitmap_info.offset_id);
372 SM.emit_Upd_Ctx(counter_id);
375 SM.emit_Dup(); SM.emit_Dup();
378 SM.emit_Ld_Ctx(null_bitmap_info.offset_id);
381 SM.emit_Ld_Ctx(null_bitmap_info.id);
383 SM.emit_Upd_Ctx(null_bitmap_info.id);
388 SM.emit_Ld_Ctx(null_bitmap_info.offset_id);
390 SM.emit_Upd_Ctx(null_bitmap_info.offset_id);
395 SM.emit_Ld_Ctx(counter_id);
397 SM.emit_Upd_Ctx(counter_id);
402 auto compile_strides = [&](
const DataLayout &layout, std::size_t row_id) ->
void {
403 auto compile_strides_impl = [&](
const DataLayout::INode &node, std::size_t row_id,
404 auto &compile_strides_ref) ->
void {
405 for (
auto &child : node) {
406 if (
auto child_leaf = cast<const DataLayout::Leaf>(child.ptr.get())) {
407 std::size_t offset_id;
408 std::size_t mask_id = -1UL;
409 if (null_bitmap_info
and child_leaf->index() == null_bitmap_idx) {
410 offset_id = null_bitmap_info.id;
411 mask_id = null_bitmap_info.offset_id;
412 }
else if (
auto it = leaf2id.find(child_leaf->index()); it != leaf2id.end()) {
413 offset_id = it->second;
414 if (
auto it = leaf2mask.find(child_leaf->index()); it != leaf2mask.end())
415 mask_id = it->second;
421 std::size_t prev_num_tuples = 1;
422 std::size_t prev_stride_in_bits = child.stride_in_bits;
423 for (
auto it = stride_info_stack.rbegin(), end = stride_info_stack.rend(); it != end; ++it) {
427 const std::size_t stride_remaining_in_bits =
428 info.stride_in_bits - (info.num_tuples / prev_num_tuples) * prev_stride_in_bits;
431 if (stride_remaining_in_bits) {
432 std::size_t byte_stride = stride_remaining_in_bits / 8;
433 const std::size_t bit_stride = stride_remaining_in_bits % 8;
436 M_insist(child_leaf->index() == null_bitmap_idx or child_leaf->type()->is_boolean(),
437 "only the null bitmap or booleans may cause not byte aligned stride jumps, "
438 "bitmaps are not supported yet");
439 M_insist(child_leaf->index() != null_bitmap_idx or null_bitmap_info.adjustable_offset(),
440 "only null bitmaps with adjustable offset may cause not byte aligned stride jumps");
444 if (child_leaf->index() == null_bitmap_idx) {
446 if (info.num_tuples != 1) {
448 SM.emit_Ld_Ctx(info.counter_id);
455 SM.emit_Ld_Ctx(mask_id);
457 SM.emit_Upd_Ctx(mask_id);
461 if (info.num_tuples != 1) {
463 SM.emit_Ld_Ctx(info.counter_id);
467 SM.emit_Ld_Ctx(mask_id);
472 SM.emit_Upd_Ctx(mask_id);
481 if (info.num_tuples != 1) {
483 SM.emit_Ld_Ctx(info.counter_id);
493 SM.emit_Ld_Ctx(offset_id);
495 SM.emit_Upd_Ctx(offset_id);
500 prev_num_tuples = info.num_tuples;
501 prev_stride_in_bits = info.stride_in_bits;
504 auto child_inode = as<const DataLayout::INode>(child.ptr.get());
507 const std::size_t inner_row_id = row_id % child_inode->num_tuples();
510 SM.emit_Upd_Ctx(counter_id);
514 stride_info_stack.push_back(stride_info_t{
515 .counter_id = counter_id,
516 .num_tuples = child_inode->num_tuples(),
517 .stride_in_bits = child.stride_in_bits
519 compile_strides_ref(*child_inode, inner_row_id, compile_strides_ref);
520 stride_info_stack.pop_back();
523 if (child_inode->num_tuples() != 1) {
524 SM.emit_Ld_Ctx(counter_id);
528 SM.emit_Ld_Ctx(counter_id);
533 SM.emit_Upd_Ctx(counter_id);
537 compile_strides_impl(
static_cast<const DataLayout::INode&
>(layout), row_id, compile_strides_impl);
539 compile_strides(layout, row_id);
564 uint32_t num_rows = 0;
567 : printer(op.schema())
569 auto &S = op.schema();
570 auto ostream_index = printer.
add(&op.out);
571 for (std::size_t i = 0; i != S.num_entries(); ++i) {
573 printer.emit_Putc(ostream_index,
',');
574 printer.emit_Ld_Tup(0, i);
582 uint32_t num_rows = 0;
588 std::optional<StackMachine> projections;
592 : pipeline(
op.schema())
597 projections.emplace(pipeline_schema);
598 std::size_t out_idx = 0;
599 for (
auto &p :
op.projections()) {
600 projections->emit(p.first.get(), 1);
601 projections->emit_St_Tup(0, out_idx++, p.first.get().type());
609 std::vector<StackMachine> load_attrs;
613 void emit_load_attrs(
const Schema &in_schema) {
614 auto &SM = load_attrs.emplace_back();
615 for (std::size_t schema_idx = 0; schema_idx != in_schema.
num_entries(); ++schema_idx) {
616 auto &e = in_schema[schema_idx];
619 SM.emit_Ld_Tup(1, schema_idx);
620 SM.emit_St_Tup(0, std::distance(pipeline.
schema().
begin(), it), e.type);
626struct NestedLoopsJoinData : JoinData
628 using buffer_type = std::vector<Tuple>;
631 std::vector<Schema> buffer_schemas;
632 buffer_type *buffers;
633 std::size_t active_child;
638 , buffers(new buffer_type[
op.children().size() - 1])
642 ~NestedLoopsJoinData() {
delete[] buffers; }
645struct SimpleHashJoinData : JoinData
648 bool is_probe_phase =
false;
649 std::vector<std::pair<const ast::Expr*, const ast::Expr*>> exprs;
661 auto &schema_lhs =
op.child(0)->schema();
663 auto &schema_rhs =
op.child(1)->schema();
668 auto &pred =
op.predicate();
669 for (
auto &clause : pred) {
670 M_insist(clause.size() == 1,
"invalid predicate for simple hash join");
671 auto &literal = clause[0];
672 M_insist(not literal.negative(),
"invalid predicate for simple hash join");
673 auto &
expr = literal.expr();
674 auto binary = as<const ast::BinaryExpr>(&expr);
676 auto first =
binary->lhs.get();
677 auto second =
binary->rhs.get();
678 M_insist(
is_comparable(first->type(), second->type()),
"the two sides of a comparison should be comparable");
679 M_insist(first->type() == second->type(),
"operand types must be equal");
682 key_schema.
add(C.
pool(
"key"), first->type());
685 auto required_by_first = first->get_required();
687 auto required_by_second = second->get_required();
689 if ((required_by_first & schema_lhs).num_entries() != 0) {
691 M_insist((required_by_second & schema_rhs).num_entries() != 0,
"second must belong to RHS");
693 exprs.emplace_back(first, second);
696 M_insist((required_by_first & schema_rhs).num_entries() != 0,
"first must belong to RHS");
697 M_insist((required_by_second & schema_lhs).num_entries() != 0,
"second must belong to LHS");
699 exprs.emplace_back(second, first);
704 key =
Tuple(key_schema);
707 void load_build_key(
const Schema &pipeline_schema) {
708 for (std::size_t i = 0; i != exprs.size(); ++i) {
710 build_key.
emit(*expr, pipeline_schema, 1);
715 void load_probe_key(
const Schema &pipeline_schema) {
716 for (std::size_t i = 0; i != exprs.size(); ++i) {
718 probe_key.
emit(*expr, pipeline_schema, 1);
726 std::size_t num_tuples = 0;
733 std::vector<StackMachine> compute_aggregate_arguments;
734 std::vector<Tuple>
args;
737 : pipeline(
op.schema())
738 , compute_key(
op.child(0)->schema())
740 std::ostringstream oss;
744 std::size_t key_idx = 0;
745 for (
auto [grp, alias] :
op.group_by()) {
746 compute_key.
emit(grp.get(), 1);
747 compute_key.
emit_St_Tup(0, key_idx++, grp.get().type());
753 for (
auto agg :
op.aggregates()) {
754 auto &fe = as<const ast::FnApplicationExpr>(agg.get());
755 std::size_t arg_idx = 0;
757 std::vector<const Type*> arg_types;
758 for (
auto &arg : fe.args) {
760 sm.emit_Cast(agg.get().type(), arg->type());
761 sm.emit_St_Tup(0, arg_idx++, arg->type());
762 arg_types.push_back(arg->type());
765 compute_aggregate_arguments.emplace_back(std::move(sm));
774 std::vector<StackMachine> compute_aggregate_arguments;
775 std::vector<Tuple>
args;
778 : pipeline(
op.schema())
780 std::vector<const Type*> types;
781 for (
auto &e :
op.schema())
782 types.push_back(e.type);
784 aggregates =
Tuple(std::move(types));
785 aggregates.
set(
op.schema().num_entries(), 0L);
787 for (
auto agg :
op.aggregates()) {
788 auto &fe = as<const ast::FnApplicationExpr>(agg.get());
789 std::size_t arg_idx = 0;
791 std::vector<const Type*> arg_types;
792 for (
auto &arg : fe.args) {
794 sm.emit_Cast(agg.get().type(), arg->type());
795 sm.emit_St_Tup(0, arg_idx++, agg.get().type());
796 arg_types.push_back(agg.get().type());
799 compute_aggregate_arguments.emplace_back(std::move(sm));
804struct HashBasedGroupingData : GroupingData
809 std::size_t key_size;
811 hasher(std::size_t key_size) : key_size(key_size) { }
813 uint64_t operator()(
const Tuple &tup)
const {
815 uint64_t
hash = 0xcbf29ce484222325;
816 for (std::size_t i = 0; i != key_size; ++i) {
818 hash *= 1099511628211;
827 std::size_t key_size;
829 equals(std::size_t key_size) : key_size(key_size) { }
831 uint64_t operator()(
const Tuple &first,
const Tuple &second)
const {
832 for (std::size_t i = 0; i != key_size; ++i) {
835 if (first.
get(i) != second.
get(i))
return false;
843 std::unordered_map<Tuple, unsigned, hasher, equals> groups;
847 , groups(1024, hasher(
op.group_by().size()), equals(
op.group_by().size()))
854 std::vector<Tuple> buffer;
856 SortingData(
Schema buffer_schema) : pipeline(
std::
move(buffer_schema)) { }
865 : filter(pipeline_schema)
868 filter.
emit(
op.filter(), 1);
869 filter.emit_St_Tup_b(0, 0);
875 std::vector<StackMachine> predicates;
881 auto clause =
op.filter()[0];
885 StackMachine &SM = predicates.emplace_back(pipeline_schema);
887 SM.emit_St_Tup_b(0, 0);
901 auto &store =
op.store();
902 auto &table = store.table();
903 const auto num_rows = store.num_rows();
911 for (
auto end = num_rows - remainder; i != end; i +=
block_.
capacity()) {
918 op.parent()->accept(*
this);
924 for (std::size_t j = 0; i !=
op.store().num_rows(); ++i, ++j) {
929 op.parent()->accept(*
this);
936 op.callback()(
op.schema(), t);
941 auto data = as<PrintData>(
op.data());
958 op.data(
new FilterData(op, this->
schema()));
960 auto data = as<FilterData>(
op.data());
964 if (data->res.is_null(0) or not data->res[0].as_b())
block_.
erase(it);
967 op.parent()->accept(*
this);
973 op.data(
new DisjunctiveFilterData(op, this->
schema()));
975 auto data = as<DisjunctiveFilterData>(
op.data());
977 data->res.set(0,
false);
980 for (
auto &pred : data->predicates) {
982 if (not data->res.is_null(0)
and data->res[0].as_b())
989 op.parent()->accept(*
this);
994 if (is<SimpleHashJoinData>(
op.data())) {
996 auto data = as<SimpleHashJoinData>(
op.data());
997 Tuple *
args[2] = { &data->key,
nullptr };
998 if (data->is_probe_phase) {
999 if (data->load_attrs.size() != 2) {
1000 data->load_probe_key(this->
schema());
1001 data->emit_load_attrs(this->
schema());
1003 auto &pipeline = data->pipeline;
1007 data->probe_key(
args);
1009 data->ht.for_all(*
args[0], [&](std::pair<const Tuple, Tuple> &v) {
1011 pipeline.push(*op.parent());
1016 Tuple *load_args[2] = { &pipeline.block_[i], &v.second };
1017 data->load_attrs[0](load_args);
1020 Tuple *load_args[2] = { &pipeline.
block_[i], &t };
1021 data->load_attrs[1](load_args);
1030 pipeline.
push(*
op.parent());
1033 if (data->load_attrs.size() != 1) {
1034 data->load_build_key(this->
schema());
1035 data->emit_load_attrs(this->
schema());
1040 data->build_key(
args);
1046 auto data = as<NestedLoopsJoinData>(
op.data());
1047 auto size =
op.children().size();
1048 std::vector<Tuple*> predicate_args(size + 1,
nullptr);
1049 predicate_args[0] = &data->res;
1051 if (data->active_child == size - 1) {
1054 std::vector<std::size_t> positions(size - 1, std::size_t(-1L));
1055 std::size_t child_id = 0;
1056 auto &pipeline = data->pipeline;
1059 if (data->buffer_schemas.size() != size) {
1060 M_insist(data->buffer_schemas.size() == size - 1);
1061 M_insist(data->load_attrs.size() == size - 1);
1062 data->emit_load_attrs(this->schema());
1063 data->buffer_schemas.emplace_back(this->schema());
1064 if (
op.predicate().size()) {
1065 std::vector<std::size_t> tuple_ids(size);
1066 std::iota(tuple_ids.begin(), tuple_ids.end(), 1);
1067 data->predicate.emit(
op.predicate(), data->buffer_schemas, tuple_ids);
1068 data->predicate.emit_St_Tup_b(0, 0);
1072 M_insist(data->buffer_schemas.size() == size);
1073 M_insist(data->load_attrs.size() == size);
1076 if (child_id == size - 1) {
1081 if (
op.predicate().size()) {
1082 for (std::size_t cid = 0; cid != child_id; ++cid)
1083 predicate_args[cid + 1] = &data->buffers[cid][positions[cid]];
1087 for (
auto output_it = pipeline.
block_.
begin(); output_it != pipeline.
block_.
end(); ++output_it) {
1088 auto &rhs = block_[output_it.index()];
1089 if (
op.predicate().size()) {
1090 predicate_args[size] = &rhs;
1091 data->predicate(predicate_args.data());
1092 if (data->res.is_null(0) or not data->res[0].as_b()) {
1098 for (std::size_t i = 0; i != child_id; ++i) {
1099 auto &buffer = data->buffers[i];
1100 Tuple *load_args[2] = { &*output_it, &buffer[positions[i]] };
1101 data->load_attrs[i](load_args);
1105 Tuple *load_args[2] = { &*output_it, &rhs };
1106 data->load_attrs[child_id](load_args);
1111 pipeline.
push(*
op.parent());
1114 ++positions[child_id];
1115 auto &buffer = data->buffers[child_id];
1116 if (positions[child_id] == buffer.size()) {
1119 positions[child_id] = std::size_t(-1L);
1122 M_insist(positions[child_id] < buffer.size(),
"position out of bounds");
1129 const auto &
tuple_schema =
op.child(data->active_child)->schema();
1130 if (data->buffer_schemas.size() <= data->active_child) {
1131 data->buffer_schemas.emplace_back(this->schema());
1132 data->emit_load_attrs(this->schema());
1133 M_insist(data->buffer_schemas.size() == data->load_attrs.size());
1135 for (
auto &t : block_)
1136 data->buffers[data->active_child].emplace_back(t.clone(
tuple_schema));
1143 auto data = as<ProjectionData>(
op.data());
1144 auto &pipeline = data->pipeline;
1145 if (not data->projections)
1146 data->emit_projections(this->
schema(), op);
1152 auto &out = pipeline.
block_[it.index()];
1154 (*data->projections)(
args);
1157 pipeline.
push(*
op.parent());
1162 auto data = as<LimitData>(
op.data());
1165 if (data->num_tuples <
op.offset() or data->num_tuples >=
op.offset() +
op.limit())
1171 op.parent()->accept(*
this);
1173 if (data->num_tuples >=
op.offset() +
op.limit())
1179 auto perform_aggregation = [&](
decltype(HashBasedGroupingData::groups)::value_type &entry,
Tuple &tuple,
1182 const std::size_t key_size =
op.group_by().size();
1184 Tuple &group =
const_cast<Tuple&
>(entry.first);
1185 const unsigned nth_tuple = ++entry.second;
1188 for (std::size_t i = 0, end =
op.aggregates().size(); i != end; ++i) {
1189 auto &aggregate_arguments = data.args[i];
1190 Tuple *
args[] = { &aggregate_arguments, &tuple };
1191 data.compute_aggregate_arguments[i](
args);
1194 auto &val = group[key_size + i];
1196 auto &fe = as<const ast::FnApplicationExpr>(
op.aggregates()[i].get());
1197 auto ty = fe.type();
1198 auto &fn = fe.get_function();
1207 case Function::FN_COUNT:
1209 group.
set(key_size + i, 0);
1210 if (fe.args.size() == 0) {
1213 val.as_i() += not aggregate_arguments.is_null(0);
1217 case Function::FN_SUM: {
1218 auto n = as<const Numeric>(ty);
1220 if (
n->is_floating_point())
1221 group.
set(key_size + i, 0.);
1223 group.
set(key_size + i, 0);
1225 if (aggregate_arguments.is_null(0))
continue;
1226 if (
n->is_floating_point())
1227 val.as_d() += aggregate_arguments[0].as_d();
1229 val.as_i() += aggregate_arguments[0].as_i();
1233 case Function::FN_AVG: {
1235 if (ty->is_floating_point())
1236 group.
set(key_size + i, 0.);
1238 group.
set(key_size + i, 0);
1240 if (aggregate_arguments.is_null(0))
continue;
1243 val.as_d() += (aggregate_arguments[0].as_d() - val.as_d()) / nth_tuple;
1247 case Function::FN_MIN: {
1249 if (aggregate_arguments.is_null(0))
continue;
1251 group.
set(key_size + i, aggregate_arguments[0]);
1255 auto n = as<const Numeric>(ty);
1257 val.as_f() =
min(val.as_f(), aggregate_arguments[0].as_f());
1258 else if (
n->is_double())
1259 val.as_d() =
min(val.as_d(), aggregate_arguments[0].as_d());
1261 val.as_i() =
min(val.as_i(), aggregate_arguments[0].as_i());
1265 case Function::FN_MAX: {
1267 if (aggregate_arguments.is_null(0))
continue;
1269 group.
set(key_size + i, aggregate_arguments[0]);
1273 auto n = as<const Numeric>(ty);
1275 val.as_f() =
max(val.as_f(), aggregate_arguments[0].as_f());
1276 else if (
n->is_double())
1277 val.as_d() =
max(val.as_d(), aggregate_arguments[0].as_d());
1279 val.as_i() =
max(val.as_i(), aggregate_arguments[0].as_i());
1287 auto data = as<HashBasedGroupingData>(
op.data());
1288 auto &groups = data->groups;
1291 for (
auto &tuple :
block_) {
1293 data->compute_key(
args);
1294 auto it = groups.find(key);
1295 if (it == groups.end()) {
1298 it = groups.emplace_hint(it, std::move(key), 0);
1301 perform_aggregation(*it, tuple, *data);
1307 auto data = as<AggregationData>(
op.data());
1308 auto &nth_tuple = data->aggregates[
op.schema().num_entries()].as_i();
1310 for (
auto &tuple :
block_) {
1312 for (std::size_t i = 0, end =
op.aggregates().size(); i != end; ++i) {
1313 auto &aggregate_arguments = data->args[i];
1314 Tuple *
args[] = { &aggregate_arguments, &tuple };
1315 data->compute_aggregate_arguments[i](
args);
1317 auto &fe = as<const ast::FnApplicationExpr>(
op.aggregates()[i].get());
1318 auto ty = fe.type();
1319 auto &fn = fe.get_function();
1321 bool agg_is_null = data->aggregates.is_null(i);
1322 auto &val = data->aggregates[i];
1331 case Function::FN_COUNT:
1332 if (fe.args.size() == 0) {
1335 val.as_i() += not aggregate_arguments.is_null(0);
1339 case Function::FN_SUM: {
1340 auto n = as<const Numeric>(ty);
1341 if (aggregate_arguments.is_null(0))
continue;
1342 if (
n->is_floating_point())
1343 val.as_d() += aggregate_arguments[0].as_d();
1345 val.as_i() += aggregate_arguments[0].as_i();
1349 case Function::FN_AVG: {
1350 if (aggregate_arguments.is_null(0))
continue;
1353 val.as_d() += (aggregate_arguments[0].as_d() - val.as_d()) / nth_tuple;
1357 case Function::FN_MIN: {
1359 if (aggregate_arguments.is_null(0))
continue;
1361 data->aggregates.set(i, aggregate_arguments[0]);
1365 auto n = as<const Numeric>(ty);
1367 val.as_f() =
min(val.as_f(), aggregate_arguments[0].as_f());
1368 else if (
n->is_double())
1369 val.as_d() =
min(val.as_d(), aggregate_arguments[0].as_d());
1371 val.as_i() =
min(val.as_i(), aggregate_arguments[0].as_i());
1375 case Function::FN_MAX: {
1377 if (aggregate_arguments.is_null(0))
continue;
1379 data->aggregates.set(i, aggregate_arguments[0]);
1383 auto n = as<const Numeric>(ty);
1385 val.as_f() =
max(val.as_f(), aggregate_arguments[0].as_f());
1386 else if (
n->is_double())
1387 val.as_d() =
max(val.as_d(), aggregate_arguments[0].as_d());
1389 val.as_i() =
max(val.as_i(), aggregate_arguments[0].as_i());
1400 op.data(
new SortingData(this->
schema()));
1403 auto data = as<SortingData>(
op.data());
1405 data->buffer.emplace_back(t.clone(this->schema()));
1414 op.child(0)->accept(*
this);
1419 op.data(
new PrintData(op));
1420 op.child(0)->accept(*
this);
1422 op.out << as<PrintData>(
op.data())->num_rows <<
" rows\n";
1427 op.data(
new NoOpData());
1428 op.child(0)->accept(*
this);
1429 op.out << as<NoOpData>(
op.data())->num_rows <<
" rows\n";
1440 op.child(0)->accept(*
this);
1445 op.child(0)->accept(*
this);
1450 if (
op.predicate().is_equi()) {
1452 auto data =
new SimpleHashJoinData(op);
1455 data->ht.resize(
op.info().estimated_cardinality);
1456 op.child(0)->accept(*
this);
1457 if (data->ht.size() == 0)
1459 data->is_probe_phase =
true;
1460 op.child(1)->accept(*
this);
1463 auto data =
new NestedLoopsJoinData(op);
1465 for (std::size_t i = 0, end =
op.children().size(); i != end; ++i) {
1466 data->active_child = i;
1467 auto c =
op.child(i);
1469 if (i !=
op.children().size() - 1
and data->buffers[i].empty())
1477 bool has_child =
op.children().size();
1478 auto data =
new ProjectionData(op);
1483 op.child(0)->accept(*
this);
1494 op.data(
new LimitData());
1495 op.child(0)->accept(*
this);
1503 auto &parent = *
op.parent();
1504 auto data =
new HashBasedGroupingData(op);
1507 op.child(0)->accept(*
this);
1509 const auto num_groups = data->groups.size();
1510 const auto remainder = num_groups % data->pipeline.block_.capacity();
1511 auto it = data->groups.begin();
1512 for (std::size_t i = 0; i != num_groups - remainder; i += data->pipeline.block_.capacity()) {
1513 data->pipeline.block_.clear();
1514 data->pipeline.block_.fill();
1515 for (std::size_t j = 0; j != data->pipeline.block_.capacity(); ++j) {
1516 auto node = data->groups.extract(it++);
1517 swap(data->pipeline.block_[j], node.key());
1519 data->pipeline.push(parent);
1521 data->pipeline.block_.clear();
1522 data->pipeline.block_.mask((1UL << remainder) - 1UL);
1523 for (std::size_t i = 0; i != remainder; ++i) {
1524 auto node = data->groups.extract(it++);
1525 swap(data->pipeline.block_[i], node.key());
1527 data->pipeline.push(parent);
1532 op.data(
new AggregationData(op));
1533 auto data = as<AggregationData>(
op.data());
1536 for (std::size_t i = 0, end =
op.aggregates().size(); i != end; ++i) {
1537 auto &fe = as<const ast::FnApplicationExpr>(
op.aggregates()[i].get());
1538 auto ty = fe.type();
1539 auto &fn = fe.get_function();
1548 case Function::FN_COUNT:
1549 data->aggregates.set(i, 0);
1552 case Function::FN_SUM: {
1553 auto n = as<const Numeric>(ty);
1554 if (
n->is_floating_point())
1555 data->aggregates.set(i, 0.);
1557 data->aggregates.set(i, 0L);
1561 case Function::FN_AVG: {
1562 if (ty->is_floating_point())
1563 data->aggregates.set(i, 0.);
1565 data->aggregates.set(i, 0L);
1569 case Function::FN_MIN:
1570 case Function::FN_MAX: {
1571 data->aggregates.null(i);
1576 op.child(0)->accept(*
this);
1579 data->pipeline.block_.clear();
1580 data->pipeline.block_.mask(1UL);
1581 swap(data->pipeline.block_[0], data->aggregates);
1582 data->pipeline.push(*
op.parent());
1587 op.child(0)->accept(*
this);
1589 auto data = as<SortingData>(
op.data());
1593 const auto &orderings =
op.order_by();
1596 for (
auto o : orderings) {
1597 comparator.emit(o.first.get(), 1);
1598 comparator.emit(o.first.get(), 2);
1601 auto ty = o.first.get().type();
1603 [&comparator](
const Boolean&) { comparator.emit_Cmp_b(); },
1607 case Numeric::N_Int:
1608 case Numeric::N_Decimal:
1609 comparator.emit_Cmp_i();
1612 case Numeric::N_Float:
1614 comparator.emit_Cmp_f();
1616 comparator.emit_Cmp_d();
1620 [&comparator](
const Date&) { comparator.emit_Cmp_i(); },
1621 [&comparator](
const DateTime&) { comparator.emit_Cmp_i(); },
1622 [](
auto&&) {
M_insist(
"invalid type"); }
1626 comparator.emit_Minus_i();
1627 comparator.emit_St_Tup_i(0, 0);
1628 comparator.emit_Stop_NZ();
1632 std::sort(data->buffer.begin(), data->buffer.end(), [&](
Tuple &first,
Tuple &second) {
1633 Tuple *args[] = { &res, &first, &second };
1636 return res[0].as_i() < 0;
1639 auto &parent = *
op.parent();
1640 const auto num_tuples = data->buffer.size();
1641 const auto remainder = num_tuples % data->pipeline.block_.capacity();
1642 auto it = data->buffer.begin();
1643 for (std::size_t i = 0; i != num_tuples - remainder; i += data->pipeline.block_.capacity()) {
1644 data->pipeline.block_.clear();
1645 data->pipeline.block_.fill();
1646 for (std::size_t j = 0; j != data->pipeline.block_.capacity(); ++j)
1647 data->pipeline.block_[j] = std::move(*it++);
1648 data->pipeline.push(parent);
1650 data->pipeline.block_.clear();
1651 data->pipeline.block_.mask((1UL << remainder) - 1UL);
1652 for (std::size_t i = 0; i != remainder; ++i)
1653 data->pipeline.block_[i] = std::move(*it++);
1654 data->pipeline.push(parent);
1658static
void register_interpreter()
static StackMachine compile_data_layout(const Schema &tuple_schema, void *address, const DataLayout &layout, const Schema &layout_schema, std::size_t row_id, std::size_t tuple_id)
Compile a StackMachine to load or store a tuple of Schema tuple_schema using a given memory address a...
__attribute__((constructor(202))) static void register_interpreter()
#define M_unreachable(MSG)
const Schema const Schema & tuple_schema
const Schema & layout_schema
::wasm::Expression * expr()
Moves the underlying Binaryen ::wasm::Expression out of this.
Bool< L > is_null(SQL_t &variant)
auto max(PrimitiveExpr< U, L > other) -> PrimitiveExpr< common_type_t< T, U >, L > std
Computes the maximum of this and other.
for(std::size_t idx=1;idx< num_vectors;++idx) res.emplace((vectors_[idx].bitmask()<< uint32_t(idx *vector_type return * res
PrimitiveExpr< ResultType, ResultL > binary(::wasm::BinaryOp op, PrimitiveExpr< OperandType, OperandL > other)
Helper function to implement binary operations.
std::pair<::wasm::Expression *, std::list< std::shared_ptr< Bit > > > move()
Moves the underlying Binaryen ::wasm::Expression and the referenced bits out of this.
PrimitiveExpr< uint64_t, L > hash() and(L
PrimitiveExpr clone() const
Creates and returns a deep copy of this.
and arithmetically_combinable< T, U, L > auto L auto L auto min(PrimitiveExpr< U, L > other) -> PrimitiveExpr< common_type_t< T, U >, L >
bool M_EXPORT is_comparable(const Type *first, const Type *second)
Returns true iff both types have the same PrimitiveType, i.e.
void swap(PlanTableBase< Actual > &first, PlanTableBase< Actual > &second)
auto visit(Callable &&callable, Base &obj, m::tag< Callable > &&=m::tag< Callable >())
Generic implementation to visit a class hierarchy, with similar syntax as std::visit.
bool empty() const
Returns true iff the block has no alive tuples, i.e. size() == 0.
void erase(std::size_t index)
Erase the tuple at the given index from this Block.
void clear()
Renders all tuples dead and removes their attributes.
uint64_t mask() const
Returns the bit mask that identifies which tuples of this Block are alive.
static constexpr std::size_t capacity()
Return the capacity of this Block.
std::size_t size() const
Return the number of alive tuples in this Block.
void fill()
Make all tuples in this Block alive.
The catalog contains all Databases and keeps track of all meta information of the database system.
ThreadSafePooledString pool(const char *str) const
Creates an internalized copy of the string str by adding it to the internal StringPool.
static Catalog & Get()
Return a reference to the single Catalog instance.
void register_backend(ThreadSafePooledString name, const char *description=nullptr)
Registers a new Backend with the given name.
The type of character strings, both fixed length and varying length.
Producer * child(std::size_t i) const
Returns the i-th child of this Consumer.
Evaluates SQL operator trees on the database.
static StackMachine compile_store(const Schema &tuple_schema, void *address, const storage::DataLayout &layout, const Schema &layout_schema, std::size_t row_id=0, std::size_t tuple_id=0)
Compile a StackMachine to store a tuple of Schema tuple_schema using a given memory address and a giv...
static StackMachine compile_load(const Schema &tuple_schema, void *address, const storage::DataLayout &layout, const Schema &layout_schema, std::size_t row_id=0, std::size_t tuple_id=0)
Compile a StackMachine to load a tuple of Schema tuple_schema using a given memory address and a give...
Drops the produced results and outputs only the number of result tuples produced.
The numeric type represents integer and floating-point types of different precision and scale.
This interface allows for attaching arbitrary data to Operator instances.
Schema & schema()
Returns the Schema of this Operator.
static Options & Get()
Return a reference to the single Options instance.
Implements push-based evaluation of a pipeline in the plan.
const Schema & schema() const
void push(const Operator &pipeline_start)
Prints the produced Tuples to a std::ostream instance.
A Schema represents a sequence of identifiers, optionally with a prefix, and their associated types.
std::size_t num_entries() const
Returns the number of entries in this Schema.
iterator find(const Identifier &id)
Returns an iterator to the entry with the given Identifier id, or end() if no such entry exists.
void add(entry_type e)
Adds the entry e to this Schema.
A stack machine that evaluates an expression.
void emit_Print(std::size_t ostream_index, const Type *ty)
Emit a Print_X instruction based on Type ty, e.g. Print_i for integral Types.
std::size_t add_and_emit_load(Value val)
Adds the Value val to the context and emits a load instruction to load this value to the top of the s...
void emit_Ld(const Type *ty)
Emit a Ld_X instruction based on Type ty, e.g. Ld_i32 for 4 byte integral types.
void emit_St(const Type *ty)
Emit a St_X instruction based on Type ty, e.g. St_i32 for 4 byte integral types.
std::size_t add(Value val)
Appends the Value val to the context and returns its assigned index.
void emit_St_Tup(std::size_t tuple_id, std::size_t index, const Type *ty)
Emit a St_Tup_X instruction based on Type ty, e.g. St_Tup_i for integral Types.
void emit(const ast::Expr &expr, std::size_t tuple_id=0)
Emit operations evaluating the Expr expr.
Value & get(std::size_t idx)
Returns a reference to the Value at index idx.
bool is_null(std::size_t idx) const
Returns true iff the Value at index idx is NULL.
void set(std::size_t idx, Value val)
Assigns the Value val to this Tuple at index idx and clears the respective NULL bit.
static Pooled< Boolean > Get_Boolean(category_t category)
Returns a Boolean type of the given category.
static Pooled< Numeric > Get_Integer(category_t category, unsigned num_bytes)
Returns a Numeric type for integrals of given category and num_bytes bytes.
A CNF represents a conjunction of cnf::Clauses.
A cnf::Clause represents a disjunction of Predicates.
A Predicate contains a Expr of Boolean type in either positive or negative form.
An internal node of the recursive data layout model.
Models how data is laid out in a linear address space.