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

            Line data    Source code
       1              : // Routing-bug defense tests for the control-op executor. Kept in a
       2              : // separate file so `control_op_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/strings/str_cat.h"
      15              : #include "backend/catalog/googlesql_catalog.h"
      16              : #include "backend/engine/control/control_op_executor.h"
      17              : #include "backend/engine/engine.h"
      18              : #include "backend/storage/duckdb/duckdb_storage.h"
      19              : #include "googlesql/public/analyzer.h"
      20              : #include "googlesql/public/analyzer_options.h"
      21              : #include "googlesql/public/analyzer_output.h"
      22              : #include "googlesql/public/language_options.h"
      23              : #include "googlesql/public/options.pb.h"
      24              : #include "googlesql/public/types/type_factory.h"
      25              : #include "googlesql/resolved_ast/resolved_ast.h"
      26              : #include "gtest/gtest.h"
      27              : 
      28              : namespace bigquery_emulator {
      29              : namespace backend {
      30              : namespace engine {
      31              : namespace control {
      32              : namespace {
      33              : 
      34              : namespace fs = std::filesystem;
      35              : 
      36            4 : ::googlesql::LanguageOptions MakeLanguageOptions() {
      37            4 :   ::googlesql::LanguageOptions language;
      38            4 :   language.EnableMaximumLanguageFeatures();
      39            4 :   language.set_product_mode(::googlesql::PRODUCT_EXTERNAL);
      40            4 :   language.set_name_resolution_mode(::googlesql::NAME_RESOLUTION_DEFAULT);
      41            4 :   return language;
      42            4 : }
      43              : 
      44            2 : ::googlesql::AnalyzerOptions MakeAnalyzerOptions() {
      45            2 :   ::googlesql::AnalyzerOptions options(MakeLanguageOptions());
      46            2 :   options.set_error_message_mode(::googlesql::ERROR_MESSAGE_ONE_LINE);
      47            2 :   options.set_attach_error_location_payload(true);
      48            2 :   options.CreateDefaultArenasIfNotSet();
      49            2 :   options.mutable_language()->SetSupportsAllStatementKinds();
      50            2 :   return options;
      51            2 : }
      52              : 
      53              : class ControlOpRoutingTest : public ::testing::Test {
      54              :  protected:
      55            2 :   void SetUp() override {
      56            2 :     const char* tmpdir_env = std::getenv("TMPDIR");
      57            2 :     const std::string tmpdir = tmpdir_env != nullptr ? tmpdir_env : "/tmp";
      58            2 :     std::random_device rd;
      59            2 :     std::seed_seq seed{rd(), rd()};
      60            2 :     std::mt19937_64 rng(seed);
      61            2 :     data_dir_ = fs::path(tmpdir) /
      62            2 :                 absl::StrCat("bqemu-control-op-routing-test-", rng());
      63            2 :     std::error_code ec;
      64            2 :     fs::remove_all(data_dir_, ec);
      65            2 :     auto opened = storage::duckdb::DuckDBStorage::Open(data_dir_.string());
      66            4 :     ASSERT_TRUE(opened.ok()) << opened.status();
      67            2 :     storage_ = std::move(opened).value();
      68            2 :     executor_ = std::make_unique<ControlOpExecutor>(storage_.get());
      69            2 :     ASSERT_TRUE(storage_->CreateDataset({"proj-test", "ds"}, "US").ok());
      70            2 :   }
      71              : 
      72            2 :   void TearDown() override {
      73            2 :     executor_.reset();
      74            2 :     storage_.reset();
      75            2 :     std::error_code ec;
      76            2 :     fs::remove_all(data_dir_, ec);
      77            2 :   }
      78              : 
      79            2 :   QueryRequest MakeRequest(absl::string_view sql) {
      80            2 :     QueryRequest req;
      81            2 :     req.project_id = "proj-test";
      82            2 :     req.sql = std::string(sql);
      83            2 :     return req;
      84            2 :   }
      85              : 
      86              :   struct CatalogBundle {
      87              :     std::unique_ptr<::googlesql::TypeFactory> type_factory{};
      88              :     std::unique_ptr<catalog::GoogleSqlCatalog> catalog{};
      89              :   };
      90            2 :   CatalogBundle MakeCatalog() {
      91            2 :     auto type_factory = std::make_unique<::googlesql::TypeFactory>();
      92            2 :     auto catalog = std::make_unique<catalog::GoogleSqlCatalog>(
      93            2 :         "proj-test", storage_.get(), type_factory.get(), MakeLanguageOptions());
      94            2 :     return {std::move(type_factory), std::move(catalog)};
      95            2 :   }
      96              : 
      97              :   fs::path data_dir_{};
      98              :   std::unique_ptr<storage::duckdb::DuckDBStorage> storage_{};
      99              :   std::unique_ptr<ControlOpExecutor> executor_{};
     100              : };
     101              : 
     102            1 : TEST_F(ControlOpRoutingTest, ExecuteQueryRejectsControlOpStatement) {
     103            1 :   CatalogBundle bundle = MakeCatalog();
     104            1 :   ::googlesql::AnalyzerOptions options = MakeAnalyzerOptions();
     105            1 :   ::googlesql::TypeFactory type_factory;
     106            1 :   std::unique_ptr<const ::googlesql::AnalyzerOutput> output;
     107            1 :   ASSERT_TRUE(::googlesql::AnalyzeStatement("CREATE TABLE ds.t (id INT64)",
     108            1 :                                             options,
     109            1 :                                             bundle.catalog.get(),
     110            1 :                                             &type_factory,
     111            1 :                                             &output)
     112            1 :                   .ok());
     113            1 :   ASSERT_NE(output, nullptr);
     114            1 :   auto source =
     115            1 :       executor_->ExecuteQuery(MakeRequest("CREATE TABLE ds.t (id INT64)"),
     116            1 :                               *output->resolved_statement(),
     117            1 :                               bundle.catalog.get());
     118            1 :   ASSERT_FALSE(source.ok());
     119            2 :   EXPECT_EQ(source.status().code(), absl::StatusCode::kInvalidArgument)
     120            2 :       << source.status();
     121            1 : }
     122              : 
     123            1 : TEST_F(ControlOpRoutingTest, ExecuteDmlRejectsControlOpStatement) {
     124            1 :   CatalogBundle bundle = MakeCatalog();
     125            1 :   ::googlesql::AnalyzerOptions options = MakeAnalyzerOptions();
     126            1 :   ::googlesql::TypeFactory type_factory;
     127            1 :   std::unique_ptr<const ::googlesql::AnalyzerOutput> output;
     128            1 :   ASSERT_TRUE(::googlesql::AnalyzeStatement("CREATE TABLE ds.t (id INT64)",
     129            1 :                                             options,
     130            1 :                                             bundle.catalog.get(),
     131            1 :                                             &type_factory,
     132            1 :                                             &output)
     133            1 :                   .ok());
     134            1 :   ASSERT_NE(output, nullptr);
     135            1 :   auto stats =
     136            1 :       executor_->ExecuteDml(MakeRequest("CREATE TABLE ds.t (id INT64)"),
     137            1 :                             *output->resolved_statement(),
     138            1 :                             bundle.catalog.get());
     139            1 :   ASSERT_FALSE(stats.ok());
     140            2 :   EXPECT_EQ(stats.status().code(), absl::StatusCode::kInvalidArgument)
     141            2 :       << stats.status();
     142            1 : }
     143              : 
     144              : }  // namespace
     145              : }  // namespace control
     146              : }  // namespace engine
     147              : }  // namespace backend
     148              : }  // namespace bigquery_emulator
        

Generated by: LCOV version 2.0-1