blob: 268199b3b8fc27e62b0141457b29ee29b782f5d3 [file]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#include "iceberg/location_provider.h"
#include <gtest/gtest.h>
#include "iceberg/location_provider.h"
#include "iceberg/partition_spec.h"
#include "iceberg/row/partition_values.h"
#include "iceberg/table_properties.h"
#include "iceberg/test/matchers.h"
#include "iceberg/transform.h"
namespace iceberg {
namespace {
// Helper function to split a string by delimiter
std::vector<std::string> SplitString(const std::string& str, char delimiter) {
std::vector<std::string> result;
std::stringstream ss(str);
std::string item;
while (std::getline(ss, item, delimiter)) {
result.push_back(item);
}
return result;
}
} // namespace
class LocationProviderTest : public ::testing::Test {
protected:
void SetUp() override { table_location_ = "/test/table/location"; }
std::string table_location_;
TableProperties properties_;
};
TEST_F(LocationProviderTest, DefaultLocationProvider) {
properties_ = {}; // Empty properties to use defaults
ICEBERG_UNWRAP_OR_FAIL(auto provider,
LocationProvider::Make(table_location_, properties_));
auto location = provider->NewDataLocation("my_file");
EXPECT_EQ(std::format("{}/data/my_file", table_location_), location);
}
TEST_F(LocationProviderTest, DefaultLocationProviderWithCustomDataLocation) {
std::ignore =
properties_.Set(TableProperties::kWriteDataLocation, std::string("new_location"));
ICEBERG_UNWRAP_OR_FAIL(auto provider,
LocationProvider::Make(table_location_, properties_));
auto location = provider->NewDataLocation("my_file");
EXPECT_EQ("new_location/my_file", location);
}
TEST_F(LocationProviderTest, ObjectStorageLocationProvider) {
std::ignore = properties_.Set(TableProperties::kObjectStoreEnabled, true);
ICEBERG_UNWRAP_OR_FAIL(auto provider,
LocationProvider::Make(table_location_, properties_));
auto location = provider->NewDataLocation("test.parquet");
std::string relative_location = location;
if (relative_location.starts_with(table_location_)) {
relative_location = relative_location.substr(table_location_.size());
}
std::vector<std::string> parts = SplitString(relative_location, '/');
ASSERT_EQ(7, parts.size());
EXPECT_EQ("", parts[0]);
EXPECT_EQ("data", parts[1]);
for (int i = 2; i <= 5; i++) {
EXPECT_FALSE(parts[i].empty());
}
EXPECT_EQ("test.parquet", parts[6]);
}
TEST_F(LocationProviderTest, ObjectStorageWithPartition) {
std::ignore = properties_.Set(TableProperties::kObjectStoreEnabled, true);
ICEBERG_UNWRAP_OR_FAIL(auto provider,
LocationProvider::Make(table_location_, properties_));
ICEBERG_UNWRAP_OR_FAIL(
auto mock_spec,
PartitionSpec::Make(PartitionSpec::kInitialSpecId,
{PartitionField(1, 1, "data#1", Transform::Identity())},
PartitionSpec::kInvalidPartitionFieldId + 1));
PartitionValues mock_partition_data({Literal::String("val#1")});
ICEBERG_UNWRAP_OR_FAIL(
auto location,
provider->NewDataLocation(*mock_spec, mock_partition_data, "test.parquet"));
std::vector<std::string> parts = SplitString(location, '/');
ASSERT_GT(parts.size(), 2);
EXPECT_EQ("data%231=val%231", parts[parts.size() - 2]);
}
TEST_F(LocationProviderTest, ObjectStorageExcludePartitionInPath) {
std::ignore = properties_.Set(TableProperties::kObjectStoreEnabled, true)
.Set(TableProperties::kWriteObjectStorePartitionedPaths, false);
ICEBERG_UNWRAP_OR_FAIL(auto provider,
LocationProvider::Make(table_location_, properties_));
auto location = provider->NewDataLocation("test.parquet");
EXPECT_THAT(location, testing::HasSubstr(table_location_));
EXPECT_THAT(location, testing::HasSubstr("/data/"));
EXPECT_THAT(location, testing::HasSubstr("-test.parquet"));
}
TEST_F(LocationProviderTest, HashInjection) {
std::ignore = properties_.Set(TableProperties::kObjectStoreEnabled, true);
ICEBERG_UNWRAP_OR_FAIL(auto provider,
LocationProvider::Make(table_location_, properties_));
auto location_a = provider->NewDataLocation("a");
EXPECT_THAT(location_a, testing::EndsWith("/data/0101/0110/1001/10110010/a"));
auto location_b = provider->NewDataLocation("b");
EXPECT_THAT(location_b, testing::EndsWith("/data/1110/0111/1110/00000011/b"));
auto location_c = provider->NewDataLocation("c");
EXPECT_THAT(location_c, testing::EndsWith("/data/0010/1101/0110/01011111/c"));
auto location_d = provider->NewDataLocation("d");
EXPECT_THAT(location_d, testing::EndsWith("/data/1001/0001/0100/01110011/d"));
auto location_file = provider->NewDataLocation("test.parquet");
EXPECT_THAT(location_file,
testing::EndsWith("/data/0110/1010/0011/11101000/test.parquet"));
}
} // namespace iceberg