Files
osm2pgsql/tests/test-flex-indexes.cpp
Jochen Topf ac6008bdf7 Give flex tables a number and use it for prepared statements
This way prepared statements are different and we can have several of
them in the same connection. We'll use that in the future.
2024-02-04 10:25:30 +01:00

347 lines
10 KiB
C++

/**
* SPDX-License-Identifier: GPL-2.0-or-later
*
* This file is part of osm2pgsql (https://osm2pgsql.org/).
*
* Copyright (C) 2006-2024 by the osm2pgsql developer community.
* For a full list of authors see the git log.
*/
#include <catch.hpp>
#include "flex-lua-index.hpp"
#include "flex-table.hpp"
#include "pgsql-capabilities-int.hpp"
#include <lua.hpp>
class test_framework
{
public:
test_framework()
: m_lua_state(luaL_newstate(), [](lua_State *state) { lua_close(state); })
{
auto &c = database_capabilities_for_testing();
c.settings = {};
c.extensions = {"postgis"};
c.schemas = {"testschema"};
c.tablespaces = {"somewhereelse"};
c.index_methods = {"gist", "btree"};
c.database_version = 110000;
}
lua_State *lua_state() const noexcept { return m_lua_state.get(); }
bool run_lua(char const *code) const
{
return luaL_dostring(lua_state(), code) == 0;
}
private:
std::shared_ptr<lua_State> m_lua_state;
};
TEST_CASE("check index with single column", "[NoDB]")
{
test_framework const tf;
flex_table_t table{"public", "test_table", 0};
table.add_column("geom", "geometry", "");
REQUIRE(table.indexes().empty());
REQUIRE(tf.run_lua("return { method = 'gist', column = 'geom' }"));
flex_lua_setup_index(tf.lua_state(), &table);
REQUIRE(table.indexes().size() == 1);
flex_index_t const &idx = table.indexes().front();
REQUIRE(idx.method() == "gist");
REQUIRE(idx.columns() == R"(("geom"))");
REQUIRE_FALSE(idx.is_unique());
REQUIRE(idx.tablespace().empty());
REQUIRE(idx.expression().empty());
REQUIRE(idx.where_condition().empty());
REQUIRE(idx.include_columns().empty());
}
TEST_CASE("check index with multiple columns", "[NoDB]")
{
test_framework const tf;
flex_table_t table{"public", "test_table", 0};
table.add_column("a", "int", "");
table.add_column("b", "int", "");
REQUIRE(tf.run_lua("return { method = 'btree', column = {'a', 'b'} }"));
flex_lua_setup_index(tf.lua_state(), &table);
REQUIRE(table.indexes().size() == 1);
auto const &idx = table.indexes()[0];
REQUIRE(idx.method() == "btree");
REQUIRE(idx.columns() == R"(("a","b"))");
REQUIRE_FALSE(idx.is_unique());
REQUIRE(idx.tablespace().empty());
REQUIRE(idx.expression().empty());
REQUIRE(idx.where_condition().empty());
REQUIRE(idx.include_columns().empty());
}
TEST_CASE("check unique index", "[NoDB]")
{
test_framework const tf;
flex_table_t table{"public", "test_table", 0};
table.add_column("col", "int", "");
REQUIRE(tf.run_lua(
"return { method = 'btree', column = 'col', unique = true }"));
flex_lua_setup_index(tf.lua_state(), &table);
REQUIRE(table.indexes().size() == 1);
auto const &idx = table.indexes()[0];
REQUIRE(idx.method() == "btree");
REQUIRE(idx.columns() == R"(("col"))");
REQUIRE(idx.is_unique());
REQUIRE(idx.tablespace().empty());
REQUIRE(idx.expression().empty());
REQUIRE(idx.where_condition().empty());
REQUIRE(idx.include_columns().empty());
}
TEST_CASE("check index with tablespace from table", "[NoDB]")
{
test_framework const tf;
flex_table_t table{"public", "test_table", 0};
table.set_index_tablespace("foo");
table.add_column("col", "int", "");
REQUIRE(tf.run_lua("return { method = 'btree', column = 'col' }"));
flex_lua_setup_index(tf.lua_state(), &table);
REQUIRE(table.indexes().size() == 1);
auto const &idx = table.indexes()[0];
REQUIRE(idx.method() == "btree");
REQUIRE(idx.columns() == R"(("col"))");
REQUIRE_FALSE(idx.is_unique());
REQUIRE(idx.tablespace() == "foo");
REQUIRE(idx.expression().empty());
REQUIRE(idx.where_condition().empty());
REQUIRE(idx.include_columns().empty());
}
TEST_CASE("check index with tablespace", "[NoDB]")
{
test_framework const tf;
flex_table_t table{"public", "test_table", 0};
table.add_column("col", "int", "");
REQUIRE(tf.run_lua("return { method = 'btree', column = 'col', tablespace "
"= 'somewhereelse' }"));
flex_lua_setup_index(tf.lua_state(), &table);
REQUIRE(table.indexes().size() == 1);
auto const &idx = table.indexes()[0];
REQUIRE(idx.method() == "btree");
REQUIRE(idx.columns() == R"(("col"))");
REQUIRE_FALSE(idx.is_unique());
REQUIRE(idx.tablespace() == "somewhereelse");
REQUIRE(idx.expression().empty());
REQUIRE(idx.where_condition().empty());
REQUIRE(idx.include_columns().empty());
}
TEST_CASE("check index with expression and where clause", "[NoDB]")
{
test_framework const tf;
flex_table_t table{"public", "test_table", 0};
table.add_column("col", "text", "");
REQUIRE(tf.run_lua("return { method = 'btree', expression = 'lower(col)',"
" where = 'length(col) > 1' }"));
flex_lua_setup_index(tf.lua_state(), &table);
REQUIRE(table.indexes().size() == 1);
auto const &idx = table.indexes()[0];
REQUIRE(idx.method() == "btree");
REQUIRE(idx.columns().empty());
REQUIRE_FALSE(idx.is_unique());
REQUIRE(idx.tablespace().empty());
REQUIRE(idx.expression() == "lower(col)");
REQUIRE(idx.where_condition() == "length(col) > 1");
REQUIRE(idx.include_columns().empty());
}
TEST_CASE("check index with include", "[NoDB]")
{
test_framework const tf;
flex_table_t table{"public", "test_table", 0};
table.add_column("col", "int", "");
table.add_column("extra", "int", "");
REQUIRE(tf.run_lua(
"return { method = 'btree', column = 'col', include = 'extra' }"));
flex_lua_setup_index(tf.lua_state(), &table);
REQUIRE(table.indexes().size() == 1);
auto const &idx = table.indexes()[0];
REQUIRE(idx.method() == "btree");
REQUIRE(idx.columns() == R"(("col"))");
REQUIRE_FALSE(idx.is_unique());
REQUIRE(idx.tablespace().empty());
REQUIRE(idx.expression().empty());
REQUIRE(idx.where_condition().empty());
REQUIRE(idx.include_columns() == R"(("extra"))");
}
TEST_CASE("check index with include as array", "[NoDB]")
{
test_framework const tf;
flex_table_t table{"public", "test_table", 0};
table.add_column("col", "int", "");
table.add_column("extra", "int", "");
REQUIRE(tf.run_lua(
"return { method = 'btree', column = 'col', include = { 'extra' } }"));
flex_lua_setup_index(tf.lua_state(), &table);
REQUIRE(table.indexes().size() == 1);
auto const &idx = table.indexes()[0];
REQUIRE(idx.method() == "btree");
REQUIRE(idx.columns() == R"(("col"))");
REQUIRE_FALSE(idx.is_unique());
REQUIRE(idx.tablespace().empty());
REQUIRE(idx.expression().empty());
REQUIRE(idx.where_condition().empty());
REQUIRE(idx.include_columns() == R"(("extra"))");
}
TEST_CASE("check index with empty include array", "[NoDB]")
{
test_framework const tf;
flex_table_t table{"public", "test_table", 0};
table.add_column("col", "int", "");
table.add_column("extra", "int", "");
REQUIRE(tf.run_lua(
"return { method = 'btree', column = 'col', include = {} }"));
flex_lua_setup_index(tf.lua_state(), &table);
REQUIRE(table.indexes().size() == 1);
auto const &idx = table.indexes()[0];
REQUIRE(idx.method() == "btree");
REQUIRE(idx.columns() == R"(("col"))");
REQUIRE_FALSE(idx.is_unique());
REQUIRE(idx.tablespace().empty());
REQUIRE(idx.expression().empty());
REQUIRE(idx.where_condition().empty());
REQUIRE(idx.include_columns().empty());
}
TEST_CASE("check multiple indexes", "[NoDB]")
{
test_framework const tf;
flex_table_t table{"public", "test_table", 0};
table.add_column("a", "int", "");
table.add_column("b", "int", "");
REQUIRE(tf.run_lua("return { method = 'btree', column = {'a'} }"));
flex_lua_setup_index(tf.lua_state(), &table);
REQUIRE(tf.run_lua("return { method = 'gist', column = 'b' }"));
flex_lua_setup_index(tf.lua_state(), &table);
REQUIRE(table.indexes().size() == 2);
auto const &idx0 = table.indexes()[0];
REQUIRE(idx0.method() == "btree");
REQUIRE(idx0.columns() == R"(("a"))");
auto const &idx1 = table.indexes()[1];
REQUIRE(idx1.method() == "gist");
REQUIRE(idx1.columns() == R"(("b"))");
}
TEST_CASE("check various broken index configs", "[NoDB]")
{
test_framework const tf;
flex_table_t table{"public", "test_table", 0};
table.add_column("col", "text", "");
SECTION("empty index description") { REQUIRE(tf.run_lua("return {}")); }
SECTION("missing method")
{
REQUIRE(tf.run_lua("return { column = 'col' }"));
}
SECTION("non-existent method")
{
REQUIRE(tf.run_lua("return { method = 'abc', column = 'col' }"));
}
SECTION("wrong type for method")
{
REQUIRE(tf.run_lua("return { method = 123, column = 'col' }"));
}
SECTION("non-existent column")
{
REQUIRE(tf.run_lua("return { method = 'btree', column = 'x' }"));
}
SECTION("wrong type for column")
{
REQUIRE(tf.run_lua("return { method = 'btree', column = true }"));
}
SECTION("empty array for column")
{
REQUIRE(tf.run_lua("return { method = 'btree', column = {} }"));
}
SECTION("wrong type for expression")
{
REQUIRE(tf.run_lua("return { method = 'btree', expression = true }"));
}
SECTION("column and expression")
{
REQUIRE(tf.run_lua("return { method = 'btree', column = 'col', "
"expression = 'lower(col)' }"));
}
SECTION("non-existent tablespace")
{
REQUIRE(tf.run_lua(
"return { method = 'btree', column = 'col', tablespace = 'not' }"));
}
SECTION("wrong type for tablespace")
{
REQUIRE(tf.run_lua(
"return { method = 'btree', column = 'col', tablespace = 1.3 }"));
}
SECTION("wrong type for unique")
{
REQUIRE(tf.run_lua(
"return { method = 'btree', column = 'col', unique = 1 }"));
}
SECTION("wrong type for where condition")
{
REQUIRE(tf.run_lua(
"return { method = 'btree', column = 'col', where = {} }"));
}
SECTION("wrong type for include")
{
REQUIRE(tf.run_lua(
"return { btree = 'btree', column = 'col', include = 1.2 }"));
}
SECTION("unknown column for include")
{
REQUIRE(tf.run_lua(
"return { btree = 'btree', column = 'col', include = 'foo' }"));
}
REQUIRE_THROWS(flex_lua_setup_index(tf.lua_state(), &table));
}