// 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.

#ifndef ADBC_VALIDATION_H
#define ADBC_VALIDATION_H

#include <optional>
#include <string>
#include <variant>
#include <vector>

#include <arrow-adbc/adbc.h>
#include <gtest/gtest.h>
#include <nanoarrow/nanoarrow.h>

#include "adbc_validation_util.h"

namespace adbc_validation {

#define ADBCV_STRINGIFY(s) #s
#define ADBCV_STRINGIFY_VALUE(s) ADBCV_STRINGIFY(s)

using SqlInfoValue = std::variant<std::string, int64_t>;

/// \brief Configuration for driver-specific behavior.
class DriverQuirks {
 public:
  /// \brief Do any initialization between New and Init.
  virtual AdbcStatusCode SetupDatabase(struct AdbcDatabase* database,
                                       struct AdbcError* error) const {
    return ADBC_STATUS_OK;
  }

  /// \brief Drop the given table. Used by tests to reset state.
  virtual AdbcStatusCode DropTable(struct AdbcConnection* connection,
                                   const std::string& name,
                                   struct AdbcError* error) const {
    return ADBC_STATUS_OK;
  }

  virtual AdbcStatusCode DropTable(struct AdbcConnection* connection,
                                   const std::string& name, const std::string& db_schema,
                                   struct AdbcError* error) const {
    return ADBC_STATUS_NOT_IMPLEMENTED;
  }

  /// \brief Drop the given temporary table. Used by tests to reset state.
  virtual AdbcStatusCode DropTempTable(struct AdbcConnection* connection,
                                       const std::string& name,
                                       struct AdbcError* error) const {
    return ADBC_STATUS_OK;
  }

  /// \brief Drop the given view. Used by tests to reset state.
  virtual AdbcStatusCode DropView(struct AdbcConnection* connection,
                                  const std::string& name,
                                  struct AdbcError* error) const {
    return ADBC_STATUS_NOT_IMPLEMENTED;
  }

  virtual AdbcStatusCode EnsureSampleTable(struct AdbcConnection* connection,
                                           const std::string& name,
                                           struct AdbcError* error) const;

  /// \brief Create a schema for testing.
  virtual AdbcStatusCode EnsureDbSchema(struct AdbcConnection* connection,
                                        const std::string& name,
                                        struct AdbcError* error) const {
    return ADBC_STATUS_NOT_IMPLEMENTED;
  }

  /// \brief Create a table of sample data with a fixed schema for testing.
  ///
  /// The table should have two columns:
  /// - "int64s" with Arrow type int64.
  /// - "strings" with Arrow type utf8.
  virtual AdbcStatusCode CreateSampleTable(struct AdbcConnection* connection,
                                           const std::string& name,
                                           struct AdbcError* error) const;

  /// \brief Create a table of sample data with a fixed schema for testing.
  ///
  /// Create it in the given schema.  Specify "" for the default schema.
  /// Return NOT_IMPLEMENTED if not supported by this backend.
  ///
  /// The table should have two columns:
  /// - "int64s" with Arrow type int64.
  /// - "strings" with Arrow type utf8.
  virtual AdbcStatusCode CreateSampleTable(struct AdbcConnection* connection,
                                           const std::string& name,
                                           const std::string& schema,
                                           struct AdbcError* error) const;

  /// \brief Get the statement to create a table with a primary key, or nullopt if not
  /// supported.
  ///
  /// The table should have one column:
  /// - "id" with Arrow type int64 (primary key)
  virtual std::optional<std::string> PrimaryKeyTableDdl(std::string_view name) const {
    return std::nullopt;
  }

  /// \brief Get the statement to create a table with a primary key, or
  ///   nullopt if not supported.  This is used to test ingestion into a table
  ///   with an auto-incrementing primary key (which should not require the
  ///   data to contain the primary key).
  ///
  /// The table should have two columns:
  /// - "id" which should be an auto-incrementing primary key compatible with int64
  /// - "value" with Arrow type int64
  virtual std::optional<std::string> PrimaryKeyIngestTableDdl(
      std::string_view name) const {
    return std::nullopt;
  }

  /// \brief Get the statement to create a table with a composite primary key,
  /// or nullopt if not supported.
  ///
  /// The table should have two columns:
  /// - "id_primary_col1" of Arrow type int64 (together forming a composite primary key)
  /// - "id_primary_col2" of Arrow type int64 (together forming a composite primary key)
  virtual std::optional<std::string> CompositePrimaryKeyTableDdl(
      std::string_view name) const {
    return std::nullopt;
  }

  /// \brief Get the statement to create a child table with foreign keys,
  /// or nullopt if not supported.
  ///
  /// The child table should have three columns:
  /// - "id_child_col1" of Arrow type int64 (primary key, foreign key) referencing "id" in
  /// the parent 1 primary key table
  /// - "id_child_col2" of Arrow type int64 (composite foreign key) together with:
  /// - "id_child_col3" of Arrow type int64 (composite foreign key) referencing
  /// "(id_primary_col1, id_primary_col2)" in the parent 2 primary key table
  virtual std::optional<std::string> ForeignKeyChildTableDdl(
      std::string_view child_name, std::string_view parent_name_1,
      std::string_view parent_name_2) const {
    return std::nullopt;
  }

  /// \brief Return the SQL to reference the bind parameter of the given index
  virtual std::string BindParameter(int index) const { return "?"; }

  /// \brief For a given Arrow type of ingested data, what Arrow type
  ///   will the database return when that column is selected?
  virtual ArrowType IngestSelectRoundTripType(ArrowType ingest_type) const {
    return ingest_type;
  }

  /// \brief For a given Arrow type of (possibly nested) ingested data, what Arrow type
  ///   will the database return when that column is selected?
  virtual SchemaField IngestSelectRoundTripType(SchemaField ingest_field) const {
    SchemaField out(ingest_field.name, IngestSelectRoundTripType(ingest_field.type),
                    ingest_field.nullable);
    for (const auto& child : ingest_field.children) {
      out.children.push_back(IngestSelectRoundTripType(child));
    }

    return out;
  }

  /// \brief Whether bulk ingest is supported
  virtual bool supports_bulk_ingest(const char* mode) const { return true; }

  /// \brief Whether bulk ingest to a specific catalog is supported
  virtual bool supports_bulk_ingest_catalog() const { return false; }

  /// \brief Whether bulk ingest to a specific schema is supported
  virtual bool supports_bulk_ingest_db_schema() const { return false; }

  /// \brief Whether bulk ingest to a temporary table is supported
  virtual bool supports_bulk_ingest_temporary() const { return false; }

  /// \brief Whether we can cancel queries.
  virtual bool supports_cancel() const { return false; }

  /// \brief Whether two statements can be used at the same time on a
  ///   single connection
  virtual bool supports_concurrent_statements() const { return false; }

  /// \brief Whether AdbcStatementExecuteSchema should work
  virtual bool supports_execute_schema() const { return false; }

  /// \brief Whether GetOption* should work
  virtual bool supports_get_option() const { return true; }

  /// \brief Whether AdbcStatementExecutePartitions should work
  virtual bool supports_partitioned_data() const { return false; }

  /// \brief Whether transaction methods are implemented
  virtual bool supports_transactions() const { return true; }

  /// \brief Whether or not DDL implicitly commits a transaction
  virtual bool ddl_implicit_commit_txn() const { return false; }

  /// \brief Whether GetSqlInfo is implemented
  virtual bool supports_get_sql_info() const { return true; }

  /// \brief The expected value for a given info code
  virtual std::optional<SqlInfoValue> supports_get_sql_info(uint32_t info_code) const {
    return std::nullopt;
  }

  /// \brief Whether GetObjects is implemented
  virtual bool supports_get_objects() const { return true; }

  /// \brief Whether we can get ADBC_CONNECTION_OPTION_CURRENT_CATALOG
  virtual bool supports_metadata_current_catalog() const { return false; }

  /// \brief Whether we can get ADBC_CONNECTION_OPTION_CURRENT_DB_SCHEMA
  virtual bool supports_metadata_current_db_schema() const { return false; }

  /// \brief Whether dynamic parameter bindings are supported for prepare
  virtual bool supports_dynamic_parameter_binding() const { return true; }

  /// \brief Whether ExecuteQuery sets rows_affected appropriately
  virtual bool supports_rows_affected() const { return true; }

  /// \brief Whether we can get statistics
  virtual bool supports_statistics() const { return false; }

  /// \brief Whether ingest errors on an incompatible schema or simply performs
  /// column matching.
  virtual bool supports_error_on_incompatible_schema() const { return true; }

  /// \brief Whether ingestion supports StringView/BinaryView types
  virtual bool supports_ingest_view_types() const { return true; }

  /// \brief Whether ingestion supports Float16
  virtual bool supports_ingest_float16() const { return true; }

  /// \brief Default catalog to use for tests
  virtual std::string catalog() const { return ""; }

  /// \brief Default database schema to use for tests
  virtual std::string db_schema() const { return ""; }
};

class DatabaseTest {
 public:
  virtual const DriverQuirks* quirks() const = 0;

  void SetUpTest();
  void TearDownTest();

  // Test methods
  void TestNewInit();
  void TestRelease();

 protected:
  struct AdbcError error;
  struct AdbcDatabase database;
};

#define ADBCV_TEST_DATABASE(FIXTURE)                                            \
  static_assert(std::is_base_of<adbc_validation::DatabaseTest, FIXTURE>::value, \
                ADBCV_STRINGIFY(FIXTURE) " must inherit from DatabaseTest");    \
  TEST_F(FIXTURE, NewInit) { TestNewInit(); }                                   \
  TEST_F(FIXTURE, Release) { TestRelease(); }

class ConnectionTest {
 public:
  virtual const DriverQuirks* quirks() const = 0;

  void SetUpTest();
  void TearDownTest();

  // Test methods
  void TestNewInit();
  void TestRelease();
  void TestConcurrent();
  void TestAutocommitDefault();

  void TestAutocommitToggle();

  void TestMetadataCurrentCatalog();
  void TestMetadataCurrentDbSchema();

  void TestMetadataGetInfo();
  void TestMetadataGetTableSchema();
  void TestMetadataGetTableSchemaDbSchema();
  void TestMetadataGetTableSchemaEscaping();
  void TestMetadataGetTableSchemaNotFound();
  void TestMetadataGetTableTypes();

  void TestMetadataGetObjectsCatalogs();
  void TestMetadataGetObjectsDbSchemas();
  void TestMetadataGetObjectsTables();
  void TestMetadataGetObjectsTablesTypes();
  void TestMetadataGetObjectsColumns();
  void TestMetadataGetObjectsConstraints();
  void TestMetadataGetObjectsPrimaryKey();
  void TestMetadataGetObjectsForeignKey();
  void TestMetadataGetObjectsCancel();

  void TestMetadataGetStatisticNames();

 protected:
  struct AdbcError error;
  struct AdbcDatabase database;
  struct AdbcConnection connection;
};

#define ADBCV_TEST_CONNECTION(FIXTURE)                                                  \
  static_assert(std::is_base_of<adbc_validation::ConnectionTest, FIXTURE>::value,       \
                ADBCV_STRINGIFY(FIXTURE) " must inherit from ConnectionTest");          \
  TEST_F(FIXTURE, NewInit) { TestNewInit(); }                                           \
  TEST_F(FIXTURE, Release) { TestRelease(); }                                           \
  TEST_F(FIXTURE, Concurrent) { TestConcurrent(); }                                     \
  TEST_F(FIXTURE, AutocommitDefault) { TestAutocommitDefault(); }                       \
  TEST_F(FIXTURE, AutocommitToggle) { TestAutocommitToggle(); }                         \
  TEST_F(FIXTURE, MetadataCurrentCatalog) { TestMetadataCurrentCatalog(); }             \
  TEST_F(FIXTURE, MetadataCurrentDbSchema) { TestMetadataCurrentDbSchema(); }           \
  TEST_F(FIXTURE, MetadataGetInfo) { TestMetadataGetInfo(); }                           \
  TEST_F(FIXTURE, MetadataGetTableSchema) { TestMetadataGetTableSchema(); }             \
  TEST_F(FIXTURE, MetadataGetTableSchemaDbSchema) {                                     \
    TestMetadataGetTableSchemaDbSchema();                                               \
  }                                                                                     \
  TEST_F(FIXTURE, MetadataGetTableSchemaEscaping) {                                     \
    TestMetadataGetTableSchemaEscaping();                                               \
  }                                                                                     \
  TEST_F(FIXTURE, MetadataGetTableSchemaNotFound) {                                     \
    TestMetadataGetTableSchemaNotFound();                                               \
  }                                                                                     \
  TEST_F(FIXTURE, MetadataGetTableTypes) { TestMetadataGetTableTypes(); }               \
  TEST_F(FIXTURE, MetadataGetObjectsCatalogs) { TestMetadataGetObjectsCatalogs(); }     \
  TEST_F(FIXTURE, MetadataGetObjectsDbSchemas) { TestMetadataGetObjectsDbSchemas(); }   \
  TEST_F(FIXTURE, MetadataGetObjectsTables) { TestMetadataGetObjectsTables(); }         \
  TEST_F(FIXTURE, MetadataGetObjectsTablesTypes) {                                      \
    TestMetadataGetObjectsTablesTypes();                                                \
  }                                                                                     \
  TEST_F(FIXTURE, MetadataGetObjectsColumns) { TestMetadataGetObjectsColumns(); }       \
  TEST_F(FIXTURE, MetadataGetObjectsConstraints) {                                      \
    TestMetadataGetObjectsConstraints();                                                \
  }                                                                                     \
  TEST_F(FIXTURE, MetadataGetObjectsPrimaryKey) { TestMetadataGetObjectsPrimaryKey(); } \
  TEST_F(FIXTURE, MetadataGetObjectsForeignKey) { TestMetadataGetObjectsForeignKey(); } \
  TEST_F(FIXTURE, MetadataGetObjectsCancel) { TestMetadataGetObjectsCancel(); }         \
  TEST_F(FIXTURE, MetadataGetStatisticNames) { TestMetadataGetStatisticNames(); }

class StatementTest {
 public:
  virtual const DriverQuirks* quirks() const = 0;

  void SetUpTest();
  void TearDownTest();

  // Test methods
  void TestNewInit();
  void TestRelease();

  // ---- Type-specific ingest tests -------------

  void TestSqlIngestBool();

  // Integers
  void TestSqlIngestInt8();
  void TestSqlIngestInt16();
  void TestSqlIngestInt32();
  void TestSqlIngestInt64();
  void TestSqlIngestUInt8();
  void TestSqlIngestUInt16();
  void TestSqlIngestUInt32();
  void TestSqlIngestUInt64();

  // Floats
  void TestSqlIngestFloat16();
  void TestSqlIngestFloat32();
  void TestSqlIngestFloat64();

  // Strings
  void TestSqlIngestString();
  void TestSqlIngestLargeString();
  void TestSqlIngestStringView();
  void TestSqlIngestBinary();
  void TestSqlIngestLargeBinary();
  void TestSqlIngestFixedSizeBinary();
  void TestSqlIngestBinaryView();

  // Temporal
  void TestSqlIngestDuration();
  void TestSqlIngestDate32();
  void TestSqlIngestTimestamp();
  void TestSqlIngestTimestampTz();
  void TestSqlIngestInterval();

  // Dictionary-encoded
  void TestSqlIngestStringDictionary();

  // Nested
  void TestSqlIngestListOfInt32();
  void TestSqlIngestListOfString();

  void TestSqlIngestStreamZeroArrays();

  // ---- End Type-specific tests ----------------

  void TestSqlIngestTableEscaping();
  void TestSqlIngestColumnEscaping();
  void TestSqlIngestAppend();
  void TestSqlIngestReplace();
  void TestSqlIngestCreateAppend();
  void TestSqlIngestErrors();
  void TestSqlIngestMultipleConnections();
  void TestSqlIngestSample();
  void TestSqlIngestTargetCatalog();
  void TestSqlIngestTargetSchema();
  void TestSqlIngestTargetCatalogSchema();
  void TestSqlIngestTemporary();
  void TestSqlIngestTemporaryAppend();
  void TestSqlIngestTemporaryReplace();
  void TestSqlIngestTemporaryExclusive();
  void TestSqlIngestPrimaryKey();

  void TestSqlPartitionedInts();

  void TestSqlPrepareGetParameterSchema();
  void TestSqlPrepareSelectNoParams();
  void TestSqlPrepareSelectParams();
  void TestSqlPrepareUpdate();
  void TestSqlPrepareUpdateNoParams();
  void TestSqlPrepareUpdateStream();
  void TestSqlPrepareErrorNoQuery();
  void TestSqlPrepareErrorParamCountMismatch();

  void TestSqlBind();

  void TestSqlQueryEmpty();
  void TestSqlQueryInts();
  void TestSqlQueryFloats();
  void TestSqlQueryStrings();

  void TestSqlQueryInsertRollback();

  void TestSqlQueryCancel();
  void TestSqlQueryErrors();
  void TestSqlQueryTrailingSemicolons();
  void TestSqlQueryRowsAffectedDelete();
  void TestSqlQueryRowsAffectedDeleteStream();

  void TestSqlSchemaInts();
  void TestSqlSchemaFloats();
  void TestSqlSchemaStrings();

  void TestSqlSchemaErrors();

  void TestTransactions();

  void TestConcurrentStatements();
  void TestErrorCompatibility();
  void TestResultIndependence();
  void TestResultInvalidation();

 protected:
  struct AdbcError error;
  struct AdbcDatabase database;
  struct AdbcConnection connection;
  struct AdbcStatement statement;

  template <typename CType>
  void TestSqlIngestType(SchemaField type,
                         const std::vector<std::optional<CType>>& values,
                         bool dictionary_encode);

  template <typename CType>
  void TestSqlIngestType(ArrowType type, const std::vector<std::optional<CType>>& values,
                         bool dictionary_encode);

  template <typename CType>
  void TestSqlIngestNumericType(ArrowType type);

  template <ArrowType type, enum ArrowTimeUnit TU>
  void TestSqlIngestTemporalType(const char* timezone);

  virtual void ValidateIngestedTemporalData(struct ArrowArrayView* values, ArrowType type,
                                            enum ArrowTimeUnit unit,
                                            const char* timezone);
};

template <typename CType>
void StatementTest::TestSqlIngestType(ArrowType type,
                                      const std::vector<std::optional<CType>>& values,
                                      bool dictionary_encode) {
  SchemaField field("col", type);
  TestSqlIngestType<CType>(field, values, dictionary_encode);
}

#define ADBCV_TEST_STATEMENT(FIXTURE)                                                   \
  static_assert(std::is_base_of<adbc_validation::StatementTest, FIXTURE>::value,        \
                ADBCV_STRINGIFY(FIXTURE) " must inherit from StatementTest");           \
  TEST_F(FIXTURE, NewInit) { TestNewInit(); }                                           \
  TEST_F(FIXTURE, Release) { TestRelease(); }                                           \
  TEST_F(FIXTURE, SqlIngestBool) { TestSqlIngestBool(); }                               \
  TEST_F(FIXTURE, SqlIngestInt8) { TestSqlIngestInt8(); }                               \
  TEST_F(FIXTURE, SqlIngestInt16) { TestSqlIngestInt16(); }                             \
  TEST_F(FIXTURE, SqlIngestInt32) { TestSqlIngestInt32(); }                             \
  TEST_F(FIXTURE, SqlIngestInt64) { TestSqlIngestInt64(); }                             \
  TEST_F(FIXTURE, SqlIngestUInt8) { TestSqlIngestUInt8(); }                             \
  TEST_F(FIXTURE, SqlIngestUInt16) { TestSqlIngestUInt16(); }                           \
  TEST_F(FIXTURE, SqlIngestUInt32) { TestSqlIngestUInt32(); }                           \
  TEST_F(FIXTURE, SqlIngestUInt64) { TestSqlIngestUInt64(); }                           \
  TEST_F(FIXTURE, SqlIngestFloat16) { TestSqlIngestFloat16(); }                         \
  TEST_F(FIXTURE, SqlIngestFloat32) { TestSqlIngestFloat32(); }                         \
  TEST_F(FIXTURE, SqlIngestFloat64) { TestSqlIngestFloat64(); }                         \
  TEST_F(FIXTURE, SqlIngestString) { TestSqlIngestString(); }                           \
  TEST_F(FIXTURE, SqlIngestLargeString) { TestSqlIngestLargeString(); }                 \
  TEST_F(FIXTURE, SqlIngestStringView) { TestSqlIngestStringView(); }                   \
  TEST_F(FIXTURE, SqlIngestBinary) { TestSqlIngestBinary(); }                           \
  TEST_F(FIXTURE, SqlIngestLargeBinary) { TestSqlIngestLargeBinary(); }                 \
  TEST_F(FIXTURE, SqlIngestFixedSizeBinary) { TestSqlIngestFixedSizeBinary(); }         \
  TEST_F(FIXTURE, SqlIngestBinaryView) { TestSqlIngestBinaryView(); }                   \
  TEST_F(FIXTURE, SqlIngestDuration) { TestSqlIngestDuration(); }                       \
  TEST_F(FIXTURE, SqlIngestDate32) { TestSqlIngestDate32(); }                           \
  TEST_F(FIXTURE, SqlIngestTimestamp) { TestSqlIngestTimestamp(); }                     \
  TEST_F(FIXTURE, SqlIngestTimestampTz) { TestSqlIngestTimestampTz(); }                 \
  TEST_F(FIXTURE, SqlIngestInterval) { TestSqlIngestInterval(); }                       \
  TEST_F(FIXTURE, SqlIngestStringDictionary) { TestSqlIngestStringDictionary(); }       \
  TEST_F(FIXTURE, SqlIngestListOfInt32) { TestSqlIngestListOfInt32(); }                 \
  TEST_F(FIXTURE, SqlIngestListOfString) { TestSqlIngestListOfString(); }               \
  TEST_F(FIXTURE, TestSqlIngestStreamZeroArrays) { TestSqlIngestStreamZeroArrays(); }   \
  TEST_F(FIXTURE, SqlIngestTableEscaping) { TestSqlIngestTableEscaping(); }             \
  TEST_F(FIXTURE, SqlIngestColumnEscaping) { TestSqlIngestColumnEscaping(); }           \
  TEST_F(FIXTURE, SqlIngestAppend) { TestSqlIngestAppend(); }                           \
  TEST_F(FIXTURE, SqlIngestReplace) { TestSqlIngestReplace(); }                         \
  TEST_F(FIXTURE, SqlIngestCreateAppend) { TestSqlIngestCreateAppend(); }               \
  TEST_F(FIXTURE, SqlIngestErrors) { TestSqlIngestErrors(); }                           \
  TEST_F(FIXTURE, SqlIngestMultipleConnections) { TestSqlIngestMultipleConnections(); } \
  TEST_F(FIXTURE, SqlIngestSample) { TestSqlIngestSample(); }                           \
  TEST_F(FIXTURE, SqlIngestTargetCatalog) { TestSqlIngestTargetCatalog(); }             \
  TEST_F(FIXTURE, SqlIngestTargetSchema) { TestSqlIngestTargetSchema(); }               \
  TEST_F(FIXTURE, SqlIngestTargetCatalogSchema) { TestSqlIngestTargetCatalogSchema(); } \
  TEST_F(FIXTURE, SqlIngestTemporary) { TestSqlIngestTemporary(); }                     \
  TEST_F(FIXTURE, SqlIngestTemporaryAppend) { TestSqlIngestTemporaryAppend(); }         \
  TEST_F(FIXTURE, SqlIngestTemporaryReplace) { TestSqlIngestTemporaryReplace(); }       \
  TEST_F(FIXTURE, SqlIngestTemporaryExclusive) { TestSqlIngestTemporaryExclusive(); }   \
  TEST_F(FIXTURE, SqlIngestPrimaryKey) { TestSqlIngestPrimaryKey(); }                   \
  TEST_F(FIXTURE, SqlPartitionedInts) { TestSqlPartitionedInts(); }                     \
  TEST_F(FIXTURE, SqlPrepareGetParameterSchema) { TestSqlPrepareGetParameterSchema(); } \
  TEST_F(FIXTURE, SqlPrepareSelectNoParams) { TestSqlPrepareSelectNoParams(); }         \
  TEST_F(FIXTURE, SqlPrepareSelectParams) { TestSqlPrepareSelectParams(); }             \
  TEST_F(FIXTURE, SqlPrepareUpdate) { TestSqlPrepareUpdate(); }                         \
  TEST_F(FIXTURE, SqlPrepareUpdateNoParams) { TestSqlPrepareUpdateNoParams(); }         \
  TEST_F(FIXTURE, SqlPrepareUpdateStream) { TestSqlPrepareUpdateStream(); }             \
  TEST_F(FIXTURE, SqlPrepareErrorNoQuery) { TestSqlPrepareErrorNoQuery(); }             \
  TEST_F(FIXTURE, SqlPrepareErrorParamCountMismatch) {                                  \
    TestSqlPrepareErrorParamCountMismatch();                                            \
  }                                                                                     \
  TEST_F(FIXTURE, SqlBind) { TestSqlBind(); }                                           \
  TEST_F(FIXTURE, SqlQueryEmpty) { TestSqlQueryEmpty(); }                               \
  TEST_F(FIXTURE, SqlQueryInts) { TestSqlQueryInts(); }                                 \
  TEST_F(FIXTURE, SqlQueryFloats) { TestSqlQueryFloats(); }                             \
  TEST_F(FIXTURE, SqlQueryStrings) { TestSqlQueryStrings(); }                           \
  TEST_F(FIXTURE, SqlQueryInsertRollback) { TestSqlQueryInsertRollback(); }             \
  TEST_F(FIXTURE, SqlQueryCancel) { TestSqlQueryCancel(); }                             \
  TEST_F(FIXTURE, SqlQueryErrors) { TestSqlQueryErrors(); }                             \
  TEST_F(FIXTURE, SqlQueryTrailingSemicolons) { TestSqlQueryTrailingSemicolons(); }     \
  TEST_F(FIXTURE, SqlQueryRowsAffectedDelete) { TestSqlQueryRowsAffectedDelete(); }     \
  TEST_F(FIXTURE, SqlQueryRowsAffectedDeleteStream) {                                   \
    TestSqlQueryRowsAffectedDeleteStream();                                             \
  }                                                                                     \
  TEST_F(FIXTURE, SqlSchemaInts) { TestSqlSchemaInts(); }                               \
  TEST_F(FIXTURE, SqlSchemaFloats) { TestSqlSchemaFloats(); }                           \
  TEST_F(FIXTURE, SqlSchemaStrings) { TestSqlSchemaStrings(); }                         \
  TEST_F(FIXTURE, SqlSchemaErrors) { TestSqlSchemaErrors(); }                           \
  TEST_F(FIXTURE, Transactions) { TestTransactions(); }                                 \
  TEST_F(FIXTURE, ConcurrentStatements) { TestConcurrentStatements(); }                 \
  TEST_F(FIXTURE, ErrorCompatibility) { TestErrorCompatibility(); }                     \
  TEST_F(FIXTURE, ResultIndependence) { TestResultIndependence(); }                     \
  TEST_F(FIXTURE, ResultInvalidation) { TestResultInvalidation(); }

}  // namespace adbc_validation

#endif  // ADBC_VALIDATION_H
