Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 117 additions & 0 deletions src/include/generated/index_search_ops.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
// GENERATED by tools/codegen_duck_udfs.py from the MEOS-API catalog — DO NOT EDIT.
// The index search of every operator name, `column <name> query` and `query <name> column`,
// from the per-signature indexSearch of the catalog.
#pragma once

#include "meos_wrapper_simple.hpp"

namespace duckdb {

//! A name an in-memory index answers and the MEOS search it asks for. A predicate is read as
//! `column <name> query`; with the query on the left it asks for `commuted`, and an operator
//! whose MobilityDB declaration has no COMMUTATOR (the overlapping orderings) has none, so a
//! predicate written that way is answered by a scan.
struct IndexOperatorEntry {
const char *name;
//! The search of `column <name> query`
IndexSearchOp direct;
//! True when `query <name> column` has a search
bool has_commuted;
//! The search of `query <name> column`, meaningful only when has_commuted
IndexSearchOp commuted;
};

static constexpr IndexOperatorEntry INDEX_OPERATORS[] = {
{"#&>", INDEX_OVERAFTER, false, INDEX_OVERAFTER},
{"#>>", INDEX_AFTER, true, INDEX_BEFORE},
{"&&", INDEX_OVERLAPS, true, INDEX_OVERLAPS},
{"&<", INDEX_OVERLEFT, false, INDEX_OVERLEFT},
{"&<#", INDEX_OVERBEFORE, false, INDEX_OVERBEFORE},
{"&</", INDEX_OVERFRONT, false, INDEX_OVERFRONT},
{"&<|", INDEX_OVERBELOW, false, INDEX_OVERBELOW},
{"&>", INDEX_OVERRIGHT, false, INDEX_OVERRIGHT},
{"-|-", INDEX_ADJACENT, true, INDEX_ADJACENT},
{"/&>", INDEX_OVERBACK, false, INDEX_OVERBACK},
{"/>>", INDEX_BACK, true, INDEX_FRONT},
{"<<", INDEX_LEFT, true, INDEX_RIGHT},
{"<<#", INDEX_BEFORE, true, INDEX_AFTER},
{"<</", INDEX_FRONT, true, INDEX_BACK},
{"<<|", INDEX_BELOW, true, INDEX_ABOVE},
{"<@", INDEX_CONTAINED_BY, true, INDEX_CONTAINS},
{">>", INDEX_RIGHT, true, INDEX_LEFT},
{"@>", INDEX_CONTAINS, true, INDEX_CONTAINED_BY},
{"adjacent", INDEX_ADJACENT, true, INDEX_ADJACENT},
{"contained", INDEX_CONTAINED_BY, true, INDEX_CONTAINS},
{"contains", INDEX_CONTAINS, true, INDEX_CONTAINED_BY},
{"overlaps", INDEX_OVERLAPS, true, INDEX_OVERLAPS},
{"same", INDEX_SAME, true, INDEX_SAME},
{"setAfter", INDEX_AFTER, true, INDEX_BEFORE},
{"setBefore", INDEX_BEFORE, true, INDEX_AFTER},
{"setLeft", INDEX_LEFT, true, INDEX_RIGHT},
{"setOverafter", INDEX_OVERAFTER, false, INDEX_OVERAFTER},
{"setOverbefore", INDEX_OVERBEFORE, false, INDEX_OVERBEFORE},
{"setOverleft", INDEX_OVERLEFT, false, INDEX_OVERLEFT},
{"setOverright", INDEX_OVERRIGHT, false, INDEX_OVERRIGHT},
{"setRight", INDEX_RIGHT, true, INDEX_LEFT},
{"spanAfter", INDEX_AFTER, true, INDEX_BEFORE},
{"spanBefore", INDEX_BEFORE, true, INDEX_AFTER},
{"spanLeft", INDEX_LEFT, true, INDEX_RIGHT},
{"spanOverafter", INDEX_OVERAFTER, false, INDEX_OVERAFTER},
{"spanOverbefore", INDEX_OVERBEFORE, false, INDEX_OVERBEFORE},
{"spanOverleft", INDEX_OVERLEFT, false, INDEX_OVERLEFT},
{"spanOverright", INDEX_OVERRIGHT, false, INDEX_OVERRIGHT},
{"spanRight", INDEX_RIGHT, true, INDEX_LEFT},
{"spansetAfter", INDEX_AFTER, true, INDEX_BEFORE},
{"spansetBefore", INDEX_BEFORE, true, INDEX_AFTER},
{"spansetLeft", INDEX_LEFT, true, INDEX_RIGHT},
{"spansetOverafter", INDEX_OVERAFTER, false, INDEX_OVERAFTER},
{"spansetOverbefore", INDEX_OVERBEFORE, false, INDEX_OVERBEFORE},
{"spansetOverleft", INDEX_OVERLEFT, false, INDEX_OVERLEFT},
{"spansetOverright", INDEX_OVERRIGHT, false, INDEX_OVERRIGHT},
{"spansetRight", INDEX_RIGHT, true, INDEX_LEFT},
{"stboxAbove", INDEX_ABOVE, true, INDEX_BELOW},
{"stboxAfter", INDEX_AFTER, true, INDEX_BEFORE},
{"stboxBack", INDEX_BACK, true, INDEX_FRONT},
{"stboxBefore", INDEX_BEFORE, true, INDEX_AFTER},
{"stboxBelow", INDEX_BELOW, true, INDEX_ABOVE},
{"stboxFront", INDEX_FRONT, true, INDEX_BACK},
{"stboxLeft", INDEX_LEFT, true, INDEX_RIGHT},
{"stboxOverabove", INDEX_OVERABOVE, false, INDEX_OVERABOVE},
{"stboxOverafter", INDEX_OVERAFTER, false, INDEX_OVERAFTER},
{"stboxOverback", INDEX_OVERBACK, false, INDEX_OVERBACK},
{"stboxOverbefore", INDEX_OVERBEFORE, false, INDEX_OVERBEFORE},
{"stboxOverbelow", INDEX_OVERBELOW, false, INDEX_OVERBELOW},
{"stboxOverfront", INDEX_OVERFRONT, false, INDEX_OVERFRONT},
{"stboxOverleft", INDEX_OVERLEFT, false, INDEX_OVERLEFT},
{"stboxOverright", INDEX_OVERRIGHT, false, INDEX_OVERRIGHT},
{"stboxRight", INDEX_RIGHT, true, INDEX_LEFT},
{"tboxAfter", INDEX_AFTER, true, INDEX_BEFORE},
{"tboxBefore", INDEX_BEFORE, true, INDEX_AFTER},
{"tboxLeft", INDEX_LEFT, true, INDEX_RIGHT},
{"tboxOverafter", INDEX_OVERAFTER, false, INDEX_OVERAFTER},
{"tboxOverbefore", INDEX_OVERBEFORE, false, INDEX_OVERBEFORE},
{"tboxOverleft", INDEX_OVERLEFT, false, INDEX_OVERLEFT},
{"tboxOverright", INDEX_OVERRIGHT, false, INDEX_OVERRIGHT},
{"tboxRight", INDEX_RIGHT, true, INDEX_LEFT},
{"tpcboxAbove", INDEX_ABOVE, true, INDEX_BELOW},
{"tpcboxAfter", INDEX_AFTER, true, INDEX_BEFORE},
{"tpcboxBack", INDEX_BACK, true, INDEX_FRONT},
{"tpcboxBefore", INDEX_BEFORE, true, INDEX_AFTER},
{"tpcboxBelow", INDEX_BELOW, true, INDEX_ABOVE},
{"tpcboxFront", INDEX_FRONT, true, INDEX_BACK},
{"tpcboxLeft", INDEX_LEFT, true, INDEX_RIGHT},
{"tpcboxOverabove", INDEX_OVERABOVE, false, INDEX_OVERABOVE},
{"tpcboxOverafter", INDEX_OVERAFTER, false, INDEX_OVERAFTER},
{"tpcboxOverback", INDEX_OVERBACK, false, INDEX_OVERBACK},
{"tpcboxOverbefore", INDEX_OVERBEFORE, false, INDEX_OVERBEFORE},
{"tpcboxOverbelow", INDEX_OVERBELOW, false, INDEX_OVERBELOW},
{"tpcboxOverfront", INDEX_OVERFRONT, false, INDEX_OVERFRONT},
{"tpcboxOverleft", INDEX_OVERLEFT, false, INDEX_OVERLEFT},
{"tpcboxOverright", INDEX_OVERRIGHT, false, INDEX_OVERRIGHT},
{"tpcboxRight", INDEX_RIGHT, true, INDEX_LEFT},
{"|&>", INDEX_OVERABOVE, false, INDEX_OVERABOVE},
{"|>>", INDEX_ABOVE, true, INDEX_BELOW},
{"~=", INDEX_SAME, true, INDEX_SAME},
};

} // namespace duckdb
181 changes: 53 additions & 128 deletions src/include/index/index_search_ops.hpp
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
#pragma once

#include <cstring>

#include "duckdb.hpp"
#include "meos_wrapper_simple.hpp"
#include "generated/index_search_ops.hpp"

namespace duckdb {

Expand All @@ -19,86 +18,41 @@ enum IndexOperatorAxis {
INDEX_AXIS_TIME,
};

//! A bounding-box operator an in-memory index answers, and the MEOS operation it asks the index
//! for. A predicate is read as `column <name> query`, so an operator whose two sides play
//! different roles asks for a different operation once the query sits on the left: `a @> b` with
//! the indexed column on the left looks for stored boxes that CONTAIN the query, and the same
//! operator with the column on the right looks for stored boxes CONTAINED BY it. Both directions
//! are named here so that a caller reads the operation off the operand order rather than
//! assuming one.
struct IndexOperatorEntry {
const char *name;
//! The operation of `column <name> query`
IndexSearchOp direct;
//! The operation of `query <name> column`
IndexSearchOp commuted;
//! The axis the operator compares along, which decides the box types answering it
IndexOperatorAxis axis;
};

//! Every operator an index answers. An operator absent from this table is answered by a scan.
//!
//! The ordering operators are the negated duals of one another under an exchange of operands --
//! `a << b` holds exactly when `b >> a` -- so the commuted column of each is its opposite. The set
//! mirrors the operators the PostgreSQL operator classes of the same box types index, so a
//! predicate that reaches an index here reaches one there.
//!
//! Equality and adjacency are their own opposites -- a box equals, or meets, another exactly when
//! that other equals, or meets, it -- so each names one operation in both columns, and both compare
//! whole extents, so every box type answers them.
//!
//! A bounding-box predicate is registered under two spellings, a symbol and a word, and both are
//! named here so that a query reaches the index however it is written. The temporal orderings are
//! the exception: they carry no symbol, `<<#` and its siblings not being operators DuckDB lexes,
//! so the word is the only spelling to route.
//!
//! A word is also reached through its class-prefixed spelling: the functions behind the position
//! operators carry the name of the class they compare, `stboxLeft`, `tboxBefore`, `spanOverleft`,
//! and such a spelling reaches the entry of the word it prefixes (see IndexOperatorWord).
static constexpr IndexOperatorEntry INDEX_OPERATORS[] = {
{"&&", INDEX_OVERLAPS, INDEX_OVERLAPS, INDEX_AXIS_EXTENT},
{"overlaps", INDEX_OVERLAPS, INDEX_OVERLAPS, INDEX_AXIS_EXTENT},
{"@>", INDEX_CONTAINS, INDEX_CONTAINED_BY, INDEX_AXIS_EXTENT},
{"contains", INDEX_CONTAINS, INDEX_CONTAINED_BY, INDEX_AXIS_EXTENT},
{"<@", INDEX_CONTAINED_BY, INDEX_CONTAINS, INDEX_AXIS_EXTENT},
{"contained", INDEX_CONTAINED_BY, INDEX_CONTAINS, INDEX_AXIS_EXTENT},
{"~=", INDEX_SAME, INDEX_SAME, INDEX_AXIS_EXTENT},
{"same", INDEX_SAME, INDEX_SAME, INDEX_AXIS_EXTENT},
{"-|-", INDEX_ADJACENT, INDEX_ADJACENT, INDEX_AXIS_EXTENT},
{"adjacent", INDEX_ADJACENT, INDEX_ADJACENT, INDEX_AXIS_EXTENT},

{"<<", INDEX_LEFT, INDEX_RIGHT, INDEX_AXIS_HORIZONTAL},
{"left", INDEX_LEFT, INDEX_RIGHT, INDEX_AXIS_HORIZONTAL},
{"&<", INDEX_OVERLEFT, INDEX_OVERRIGHT, INDEX_AXIS_HORIZONTAL},
{"overleft", INDEX_OVERLEFT, INDEX_OVERRIGHT, INDEX_AXIS_HORIZONTAL},
{">>", INDEX_RIGHT, INDEX_LEFT, INDEX_AXIS_HORIZONTAL},
{"right", INDEX_RIGHT, INDEX_LEFT, INDEX_AXIS_HORIZONTAL},
{"&>", INDEX_OVERRIGHT, INDEX_OVERLEFT, INDEX_AXIS_HORIZONTAL},
{"overright", INDEX_OVERRIGHT, INDEX_OVERLEFT, INDEX_AXIS_HORIZONTAL},

{"<<|", INDEX_BELOW, INDEX_ABOVE, INDEX_AXIS_VERTICAL},
{"below", INDEX_BELOW, INDEX_ABOVE, INDEX_AXIS_VERTICAL},
{"&<|", INDEX_OVERBELOW, INDEX_OVERABOVE, INDEX_AXIS_VERTICAL},
{"overbelow", INDEX_OVERBELOW, INDEX_OVERABOVE, INDEX_AXIS_VERTICAL},
{"|>>", INDEX_ABOVE, INDEX_BELOW, INDEX_AXIS_VERTICAL},
{"above", INDEX_ABOVE, INDEX_BELOW, INDEX_AXIS_VERTICAL},
{"|&>", INDEX_OVERABOVE, INDEX_OVERBELOW, INDEX_AXIS_VERTICAL},
{"overabove", INDEX_OVERABOVE, INDEX_OVERBELOW, INDEX_AXIS_VERTICAL},

{"<</", INDEX_FRONT, INDEX_BACK, INDEX_AXIS_DEPTH},
{"front", INDEX_FRONT, INDEX_BACK, INDEX_AXIS_DEPTH},
{"&</", INDEX_OVERFRONT, INDEX_OVERBACK, INDEX_AXIS_DEPTH},
{"overfront", INDEX_OVERFRONT, INDEX_OVERBACK, INDEX_AXIS_DEPTH},
{"/>>", INDEX_BACK, INDEX_FRONT, INDEX_AXIS_DEPTH},
{"back", INDEX_BACK, INDEX_FRONT, INDEX_AXIS_DEPTH},
{"/&>", INDEX_OVERBACK, INDEX_OVERFRONT, INDEX_AXIS_DEPTH},
{"overback", INDEX_OVERBACK, INDEX_OVERFRONT, INDEX_AXIS_DEPTH},

{"before", INDEX_BEFORE, INDEX_AFTER, INDEX_AXIS_TIME},
{"overbefore", INDEX_OVERBEFORE, INDEX_OVERAFTER, INDEX_AXIS_TIME},
{"after", INDEX_AFTER, INDEX_BEFORE, INDEX_AXIS_TIME},
{"overafter", INDEX_OVERAFTER, INDEX_OVERBEFORE, INDEX_AXIS_TIME},
};
//! The axis the MEOS search `op` compares along. The overlap, containment, equality and adjacency
//! searches compare whole extents; each ordering search, strict or overlapping, compares along
//! the one axis it names.
inline IndexOperatorAxis IndexSearchAxis(IndexSearchOp op) {
switch (op) {
case INDEX_OVERLAPS:
case INDEX_CONTAINS:
case INDEX_CONTAINED_BY:
case INDEX_SAME:
case INDEX_ADJACENT:
return INDEX_AXIS_EXTENT;
case INDEX_LEFT:
case INDEX_OVERLEFT:
case INDEX_RIGHT:
case INDEX_OVERRIGHT:
return INDEX_AXIS_HORIZONTAL;
case INDEX_BELOW:
case INDEX_OVERBELOW:
case INDEX_ABOVE:
case INDEX_OVERABOVE:
return INDEX_AXIS_VERTICAL;
case INDEX_FRONT:
case INDEX_OVERFRONT:
case INDEX_BACK:
case INDEX_OVERBACK:
return INDEX_AXIS_DEPTH;
case INDEX_BEFORE:
case INDEX_OVERBEFORE:
case INDEX_AFTER:
case INDEX_OVERAFTER:
return INDEX_AXIS_TIME;
default:
throw InternalException("IndexSearchAxis: unknown IndexSearchOp %d", (int) op);
}
}

//! Return true if a box of `bbox_type` carries `axis`, and so answers the operators ordering
//! along it. A spatiotemporal box carries every axis; a temporal box carries a value extent and a
Expand All @@ -125,63 +79,34 @@ inline bool IndexBboxHasAxis(MeosType bbox_type, IndexOperatorAxis axis) {
}
}

//! The class prefixes a word of INDEX_OPERATORS takes in its class-prefixed spelling, longest first
//! so that `spansetLeft` is read as `spanset` + `Left` and never as `span` + `setLeft`.
static constexpr const char *INDEX_OPERATOR_CLASS_PREFIXES[] = {
"spanset", "tpcbox", "stbox", "tbox", "span", "set",
};

//! Return the word of INDEX_OPERATORS that `name` spells. A class-prefixed spelling, one of
//! INDEX_OPERATOR_CLASS_PREFIXES followed by an uppercase letter, spells the remainder with its
//! first letter lowercased: `stboxOverleft` spells `overleft`. Every other name, a symbol or an
//! unprefixed word, spells itself.
inline string IndexOperatorWord(const string &name) {
for (auto prefix : INDEX_OPERATOR_CLASS_PREFIXES) {
const size_t length = strlen(prefix);
if (name.size() > length && name.compare(0, length, prefix) == 0 && name[length] >= 'A' &&
name[length] <= 'Z') {
string word = name.substr(length);
word[0] = static_cast<char>(word[0] - 'A' + 'a');
return word;
}
}
return name;
}

//! Return the operation `name` asks of an index over `bbox_type`, false when such an index answers
//! no such operator. `name` is a symbol, a word or the word's class-prefixed spelling.
//! `query_on_left` names the operand order: true when the query is the left argument.
//! Return the search `name` asks of an index over `bbox_type`, false when such an index answers no
//! such operator. `name` is an operator symbol or the SQL name of a function backing it, as the
//! generated INDEX_OPERATORS states them. `query_on_left` names the operand order: true when the
//! query is the left argument, which asks for the commuted search, and an operator with none is
//! answered by a scan.
inline bool IndexSearchOpFromName(const string &name, MeosType bbox_type, bool query_on_left,
IndexSearchOp &result) {
const string word = IndexOperatorWord(name);
for (auto &entry : INDEX_OPERATORS) {
if (word == entry.name && IndexBboxHasAxis(bbox_type, entry.axis)) {
result = query_on_left ? entry.commuted : entry.direct;
return true;
if (name != entry.name || !IndexBboxHasAxis(bbox_type, IndexSearchAxis(entry.direct))) {
continue;
}
if (query_on_left && !entry.has_commuted) {
return false;
}
result = query_on_left ? entry.commuted : entry.direct;
return true;
}
return false;
}

//! The operator names an index over `bbox_type` answers, each word under its bare and its
//! class-prefixed spellings. Overlap and containment compare whole extents, so every box type
//! answers those; an ordering operator is answered only where the box carries the axis it orders
//! along.
//! The operator names an index over `bbox_type` answers in at least one operand order: overlap and
//! containment compare whole extents, so every box type answers those, and an ordering operator is
//! answered only where the box carries the axis it orders along.
inline unordered_set<string> IndexOperatorNames(MeosType bbox_type) {
unordered_set<string> names;
for (auto &entry : INDEX_OPERATORS) {
if (!IndexBboxHasAxis(bbox_type, entry.axis)) {
continue;
}
const string name = entry.name;
names.insert(name);
if (name[0] < 'a' || name[0] > 'z') {
continue;
}
string capitalized = name;
capitalized[0] = static_cast<char>(capitalized[0] - 'a' + 'A');
for (auto prefix : INDEX_OPERATOR_CLASS_PREFIXES) {
names.insert(prefix + capitalized);
if (IndexBboxHasAxis(bbox_type, IndexSearchAxis(entry.direct))) {
names.insert(entry.name);
}
}
return names;
Expand Down
9 changes: 9 additions & 0 deletions src/index/rtree_optimize_scan.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

#include "index/rtree_module.hpp"
#include "index/rtree_index_scan.hpp"
#include "index/index_search_ops.hpp"
#include "time_util.hpp"


Expand Down Expand Up @@ -95,6 +96,14 @@ class TRTreeIndexScanOptimizer : public OptimizerExtension {
} else {
return false;
}
// The index answers an operator only in an operand order it has a search for: an
// overlapping ordering with the query on the left (`query &< column`) has none, so
// the predicate stays a filter and a scan answers it.
IndexSearchOp search_op;
if (!IndexSearchOpFromName(function_name, rtree_index.GetBboxType(), query_on_left,
search_op)) {
return false;
}

Expression *const_expr = nullptr;

Expand Down
9 changes: 9 additions & 0 deletions src/index/sptree_optimize_scan.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

#include "index/sptree_module.hpp"
#include "index/sptree_index_scan.hpp"
#include "index/index_search_ops.hpp"
#include "time_util.hpp"


Expand Down Expand Up @@ -95,6 +96,14 @@ class TSPTreeIndexScanOptimizer : public OptimizerExtension {
} else {
return false;
}
// The index answers an operator only in an operand order it has a search for: an
// overlapping ordering with the query on the left (`query &< column`) has none, so
// the predicate stays a filter and a scan answers it.
IndexSearchOp search_op;
if (!IndexSearchOpFromName(function_name, sptree_index.GetBboxType(), query_on_left,
search_op)) {
return false;
}

Expression *const_expr = nullptr;

Expand Down
Loading
Loading