#include #include #include "ResultSourceMapping.h" namespace { constexpr Oid tableA = 1001; constexpr Oid tableB = 1002; ResultColumnSource src(Oid table, int attnum) { ResultColumnSource s; s.tableOid = table; s.attnum = attnum; return s; } ResultColumnSource expression() { return ResultColumnSource{}; } TablePrimaryKey pk(Oid table, std::vector attnums) { TablePrimaryKey key; key.tableOid = table; key.attnums = std::move(attnums); return key; } } TEST(ResultSourceMappingTest, single_table_with_pk_all_columns_editable) { ResultSourceMapping m({src(tableA, 1), src(tableA, 2), src(tableA, 3)}, {pk(tableA, {1})}); EXPECT_EQ(m.columnCount(), 3); EXPECT_TRUE(m.isEditable(0)); EXPECT_TRUE(m.isEditable(1)); EXPECT_TRUE(m.isEditable(2)); EXPECT_EQ(m.tableOid(1), tableA); EXPECT_EQ(m.attnum(2), 3); EXPECT_THAT(m.primaryKeyColumns(tableA), ::testing::ElementsAre(0)); } TEST(ResultSourceMappingTest, composite_pk_maps_key_columns_in_key_order) { ResultSourceMapping m({src(tableA, 3), src(tableA, 1), src(tableA, 2)}, {pk(tableA, {1, 2})}); EXPECT_TRUE(m.isEditable(0)); EXPECT_THAT(m.primaryKeyColumns(tableA), ::testing::ElementsAre(1, 2)); } TEST(ResultSourceMappingTest, join_with_both_pks_both_tables_editable) { ResultSourceMapping m({src(tableA, 1), src(tableA, 2), src(tableB, 1), src(tableB, 2)}, {pk(tableA, {1}), pk(tableB, {1})}); for (int col = 0; col < m.columnCount(); ++col) { EXPECT_TRUE(m.isEditable(col)) << "column " << col; } EXPECT_THAT(m.primaryKeyColumns(tableA), ::testing::ElementsAre(0)); EXPECT_THAT(m.primaryKeyColumns(tableB), ::testing::ElementsAre(2)); } TEST(ResultSourceMappingTest, join_where_only_one_table_has_pk_in_result) { ResultSourceMapping m({src(tableA, 1), src(tableA, 2), src(tableB, 2)}, {pk(tableA, {1}), pk(tableB, {1})}); EXPECT_TRUE(m.isEditable(0)); EXPECT_TRUE(m.isEditable(1)); EXPECT_FALSE(m.isEditable(2)); EXPECT_TRUE(m.primaryKeyColumns(tableB).empty()); } TEST(ResultSourceMappingTest, pk_missing_from_result_is_read_only) { ResultSourceMapping m({src(tableA, 2), src(tableA, 3)}, {pk(tableA, {1})}); EXPECT_FALSE(m.isEditable(0)); EXPECT_FALSE(m.isEditable(1)); EXPECT_TRUE(m.primaryKeyColumns(tableA).empty()); } TEST(ResultSourceMappingTest, composite_pk_partially_present_is_read_only) { ResultSourceMapping m({src(tableA, 1), src(tableA, 3)}, {pk(tableA, {1, 2})}); EXPECT_FALSE(m.isEditable(0)); EXPECT_FALSE(m.isEditable(1)); } TEST(ResultSourceMappingTest, table_without_pk_is_read_only) { ResultSourceMapping m({src(tableA, 1), src(tableA, 2)}, {}); EXPECT_FALSE(m.isEditable(0)); EXPECT_FALSE(m.isEditable(1)); } TEST(ResultSourceMappingTest, expression_column_is_read_only) { ResultSourceMapping m({src(tableA, 1), expression(), src(tableA, 2)}, {pk(tableA, {1})}); EXPECT_TRUE(m.isEditable(0)); EXPECT_FALSE(m.isEditable(1)); EXPECT_EQ(m.tableOid(1), InvalidOid); EXPECT_EQ(m.attnum(1), 0); EXPECT_TRUE(m.isEditable(2)); } TEST(ResultSourceMappingTest, system_column_is_read_only) { ResultSourceMapping m({src(tableA, 1), src(tableA, -2)}, {pk(tableA, {1})}); EXPECT_TRUE(m.isEditable(0)); EXPECT_FALSE(m.isEditable(1)); } TEST(ResultSourceMappingTest, duplicate_pk_column_still_editable) { ResultSourceMapping m({src(tableA, 1), src(tableA, 1), src(tableA, 2)}, {pk(tableA, {1})}); EXPECT_TRUE(m.isEditable(0)); EXPECT_TRUE(m.isEditable(1)); EXPECT_TRUE(m.isEditable(2)); EXPECT_THAT(m.primaryKeyColumns(tableA), ::testing::ElementsAre(0)); } TEST(ResultSourceMappingTest, same_non_pk_column_twice_makes_table_read_only) { ResultSourceMapping m({src(tableA, 1), src(tableA, 2), src(tableA, 2)}, {pk(tableA, {1})}); EXPECT_FALSE(m.isEditable(0)); EXPECT_FALSE(m.isEditable(1)); EXPECT_FALSE(m.isEditable(2)); EXPECT_TRUE(m.primaryKeyColumns(tableA).empty()); } TEST(ResultSourceMappingTest, self_join_with_aliased_non_pk_columns_is_read_only) { ResultSourceMapping m({src(tableA, 1), src(tableA, 1), src(tableA, 2), src(tableA, 2)}, {pk(tableA, {1})}); EXPECT_FALSE(m.isEditable(0)); EXPECT_FALSE(m.isEditable(2)); } TEST(ResultSourceMappingTest, out_of_range_column_throws) { ResultSourceMapping m({src(tableA, 1)}, {pk(tableA, {1})}); EXPECT_THROW(m.isEditable(1), std::range_error); EXPECT_THROW(m.isEditable(-1), std::range_error); EXPECT_THROW(m.tableOid(1), std::range_error); EXPECT_THROW(m.attnum(-1), std::range_error); } TEST(ResultSourceMappingTest, empty_result_has_no_columns) { ResultSourceMapping m({}, {}); EXPECT_EQ(m.columnCount(), 0); EXPECT_TRUE(m.primaryKeyColumns(tableA).empty()); }