LCOV - code coverage report
Current view: top level - backend/engine/duckdb - duckdb_executor_struct_cast_test.cc (source / functions) Coverage Total Hit
Test: _coverage_report.dat Lines: 97.8 % 90 88
Test Date: 2026-07-02 21:01:18 Functions: 100.0 % 8 8

            Line data    Source code
       1              : // Positional STRUCT cast regression tests for the DuckDB executor.
       2              : // Kept in a separate file so `duckdb_executor_test.cc` stays under the
       3              : // cpp-lint file-length cap.
       4              : 
       5              : #include <cstdlib>
       6              : #include <filesystem>
       7              : #include <memory>
       8              : #include <random>
       9              : #include <string>
      10              : #include <system_error>
      11              : #include <utility>
      12              : 
      13              : #include "absl/status/status.h"
      14              : #include "absl/status/statusor.h"
      15              : #include "absl/strings/str_cat.h"
      16              : #include "backend/catalog/googlesql_catalog.h"
      17              : #include "backend/engine/duckdb/duckdb_executor.h"
      18              : #include "backend/engine/engine.h"
      19              : #include "backend/storage/duckdb/duckdb_storage.h"
      20              : #include "googlesql/public/analyzer.h"
      21              : #include "googlesql/public/analyzer_options.h"
      22              : #include "googlesql/public/analyzer_output.h"
      23              : #include "googlesql/public/language_options.h"
      24              : #include "googlesql/public/options.pb.h"
      25              : #include "googlesql/public/types/type_factory.h"
      26              : #include "googlesql/resolved_ast/resolved_ast.h"
      27              : #include "gtest/gtest.h"
      28              : 
      29              : namespace bigquery_emulator {
      30              : namespace backend {
      31              : namespace engine {
      32              : namespace duckdb {
      33              : namespace {
      34              : 
      35              : namespace fs = std::filesystem;
      36              : 
      37            2 : ::googlesql::LanguageOptions MakeLanguageOptions() {
      38            2 :   ::googlesql::LanguageOptions language;
      39            2 :   language.EnableMaximumLanguageFeatures();
      40            2 :   language.set_product_mode(::googlesql::PRODUCT_EXTERNAL);
      41            2 :   language.set_name_resolution_mode(::googlesql::NAME_RESOLUTION_DEFAULT);
      42            2 :   return language;
      43            2 : }
      44              : 
      45            1 : ::googlesql::AnalyzerOptions MakeAnalyzerOptions(bool all_statements) {
      46            1 :   ::googlesql::AnalyzerOptions options(MakeLanguageOptions());
      47            1 :   options.set_error_message_mode(::googlesql::ERROR_MESSAGE_ONE_LINE);
      48            1 :   options.set_attach_error_location_payload(true);
      49            1 :   options.CreateDefaultArenasIfNotSet();
      50            1 :   if (all_statements) {
      51            0 :     options.mutable_language()->SetSupportsAllStatementKinds();
      52            0 :   }
      53            1 :   return options;
      54            1 : }
      55              : 
      56              : class DuckDbExecutorStructCastTest : public ::testing::Test {
      57              :  protected:
      58            1 :   void SetUp() override {
      59            1 :     const char* tmpdir_env = std::getenv("TMPDIR");
      60            1 :     const std::string tmpdir = tmpdir_env != nullptr ? tmpdir_env : "/tmp";
      61            1 :     std::random_device rd;
      62            1 :     std::seed_seq seed{rd(), rd()};
      63            1 :     std::mt19937_64 rng(seed);
      64            1 :     data_dir_ = fs::path(tmpdir) /
      65            1 :                 absl::StrCat("bqemu-duckdb-struct-cast-test-", rng());
      66            1 :     std::error_code ec;
      67            1 :     fs::remove_all(data_dir_, ec);
      68            1 :     auto opened = storage::duckdb::DuckDBStorage::Open(data_dir_.string());
      69            2 :     ASSERT_TRUE(opened.ok()) << opened.status();
      70            1 :     storage_ = std::move(opened).value();
      71            1 :     executor_ = std::make_unique<DuckDbExecutor>(storage_.get());
      72            1 :   }
      73              : 
      74            1 :   void TearDown() override {
      75            1 :     executor_.reset();
      76            1 :     storage_.reset();
      77            1 :     std::error_code ec;
      78            1 :     fs::remove_all(data_dir_, ec);
      79            1 :   }
      80              : 
      81            1 :   QueryRequest MakeRequest(absl::string_view sql) {
      82            1 :     QueryRequest req;
      83            1 :     req.project_id = "proj-test";
      84            1 :     req.sql = std::string(sql);
      85            1 :     return req;
      86            1 :   }
      87              : 
      88              :   struct CatalogBundle {
      89              :     std::unique_ptr<::googlesql::TypeFactory> type_factory{};
      90              :     std::unique_ptr<catalog::GoogleSqlCatalog> catalog{};
      91              :   };
      92            1 :   CatalogBundle MakeCatalog() {
      93            1 :     auto type_factory = std::make_unique<::googlesql::TypeFactory>();
      94            1 :     auto catalog = std::make_unique<catalog::GoogleSqlCatalog>(
      95            1 :         "proj-test", storage_.get(), type_factory.get(), MakeLanguageOptions());
      96            1 :     return {std::move(type_factory), std::move(catalog)};
      97            1 :   }
      98              : 
      99              :   absl::StatusOr<std::unique_ptr<const ::googlesql::AnalyzerOutput>> Analyze(
     100              :       absl::string_view sql,
     101              :       ::googlesql::Catalog* catalog,
     102            1 :       bool all_statements) {
     103            1 :     ::googlesql::AnalyzerOptions options = MakeAnalyzerOptions(all_statements);
     104            1 :     ::googlesql::TypeFactory type_factory;
     105            1 :     std::unique_ptr<const ::googlesql::AnalyzerOutput> output;
     106            1 :     absl::Status s = ::googlesql::AnalyzeStatement(
     107            1 :         sql, options, catalog, &type_factory, &output);
     108            1 :     if (!s.ok()) return s;
     109            1 :     return output;
     110            1 :   }
     111              : 
     112              :   fs::path data_dir_{};
     113              :   std::unique_ptr<storage::duckdb::DuckDBStorage> storage_{};
     114              :   std::unique_ptr<DuckDbExecutor> executor_{};
     115              : };
     116              : 
     117            1 : TEST_F(DuckDbExecutorStructCastTest, ExecuteUnnestArrayStructPositionalCast) {
     118            1 :   static constexpr char kSql[] = R"sql(
     119            1 : SELECT t.x, t.y
     120            1 :   FROM UNNEST(ARRAY<STRUCT<x INT64, y STRING>>[STRUCT(1, 'a'), STRUCT(2, 'b')]) AS t
     121            1 : )sql";
     122            1 :   const std::string sql(kSql);
     123            1 :   CatalogBundle bundle = MakeCatalog();
     124            1 :   auto analyzed = Analyze(sql, bundle.catalog.get(), /*all_statements=*/false);
     125            2 :   ASSERT_TRUE(analyzed.ok()) << analyzed.status();
     126            1 :   const ::googlesql::ResolvedStatement* stmt =
     127            1 :       (*analyzed)->resolved_statement();
     128            1 :   ASSERT_NE(stmt, nullptr);
     129              : 
     130            1 :   absl::StatusOr<std::unique_ptr<RowSource>> source =
     131            1 :       executor_->ExecuteQuery(MakeRequest(sql), *stmt, bundle.catalog.get());
     132            2 :   ASSERT_TRUE(source.ok()) << source.status();
     133              : 
     134            1 :   storage::Row row;
     135            1 :   auto has = (*source)->Next(&row);
     136            2 :   ASSERT_TRUE(has.ok()) << has.status();
     137            1 :   ASSERT_TRUE(*has);
     138            1 :   ASSERT_EQ(row.cells.size(), 2u);
     139            1 :   EXPECT_EQ(row.cells[0].int64_value(), 1);
     140            1 :   EXPECT_EQ(row.cells[1].string_value(), "a");
     141              : 
     142            1 :   has = (*source)->Next(&row);
     143            2 :   ASSERT_TRUE(has.ok()) << has.status();
     144            1 :   ASSERT_TRUE(*has);
     145            1 :   EXPECT_EQ(row.cells[0].int64_value(), 2);
     146            1 :   EXPECT_EQ(row.cells[1].string_value(), "b");
     147              : 
     148            1 :   has = (*source)->Next(&row);
     149            2 :   ASSERT_TRUE(has.ok()) << has.status();
     150            1 :   EXPECT_FALSE(*has);
     151            1 : }
     152              : 
     153              : }  // namespace
     154              : }  // namespace duckdb
     155              : }  // namespace engine
     156              : }  // namespace backend
     157              : }  // namespace bigquery_emulator
        

Generated by: LCOV version 2.0-1