c/validation/adbc_validation.h (220 lines of code) (raw):

// 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 <vector> #include <adbc.h> #include <gtest/gtest.h> #include <nanoarrow/nanoarrow.h> namespace adbc_validation { #define ADBCV_STRINGIFY(s) #s #define ADBCV_STRINGIFY_VALUE(s) ADBCV_STRINGIFY(s) /// \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; } /// \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 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 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 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 Whether two statements can be used at the same time on a /// single connection virtual bool supports_concurrent_statements() const { return false; } /// \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 Whether GetObjects is implemented virtual bool supports_get_objects() const { return true; } /// \brief Whether bulk ingest is supported virtual bool supports_bulk_ingest() const { return true; } /// \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 Default catalog to use for tests virtual std::string catalog() const { return ""; } /// \brief Default 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 TestMetadataGetInfo(); void TestMetadataGetTableSchema(); void TestMetadataGetTableTypes(); void TestMetadataGetObjectsCatalogs(); void TestMetadataGetObjectsDbSchemas(); void TestMetadataGetObjectsTables(); void TestMetadataGetObjectsTablesTypes(); void TestMetadataGetObjectsColumns(); void TestMetadataGetObjectsConstraints(); void TestMetadataGetObjectsPrimaryKey(); 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, MetadataGetInfo) { TestMetadataGetInfo(); } \ TEST_F(FIXTURE, MetadataGetTableSchema) { TestMetadataGetTableSchema(); } \ 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(); } class StatementTest { public: virtual const DriverQuirks* quirks() const = 0; void SetUpTest(); void TearDownTest(); // Test methods void TestNewInit(); void TestRelease(); // ---- Type-specific tests -------------------- // Integers void TestSqlIngestInt8(); void TestSqlIngestInt16(); void TestSqlIngestInt32(); void TestSqlIngestInt64(); void TestSqlIngestUInt8(); void TestSqlIngestUInt16(); void TestSqlIngestUInt32(); void TestSqlIngestUInt64(); // Floats void TestSqlIngestFloat32(); void TestSqlIngestFloat64(); // Strings void TestSqlIngestString(); void TestSqlIngestBinary(); // Temporal void TestSqlIngestDate32(); void TestSqlIngestTimestamp(); void TestSqlIngestTimestampTz(); void TestSqlIngestInterval(); // ---- End Type-specific tests ---------------- void TestSqlIngestTableEscaping(); void TestSqlIngestAppend(); void TestSqlIngestErrors(); void TestSqlIngestMultipleConnections(); void TestSqlIngestSample(); void TestSqlPartitionedInts(); void TestSqlPrepareGetParameterSchema(); void TestSqlPrepareSelectNoParams(); void TestSqlPrepareSelectParams(); void TestSqlPrepareUpdate(); void TestSqlPrepareUpdateNoParams(); void TestSqlPrepareUpdateStream(); void TestSqlPrepareErrorNoQuery(); void TestSqlPrepareErrorParamCountMismatch(); void TestSqlQueryInts(); void TestSqlQueryFloats(); void TestSqlQueryStrings(); void TestSqlQueryErrors(); void TestTransactions(); void TestConcurrentStatements(); void TestResultInvalidation(); protected: struct AdbcError error; struct AdbcDatabase database; struct AdbcConnection connection; struct AdbcStatement statement; template <typename CType> void TestSqlIngestType(ArrowType type, const std::vector<std::optional<CType>>& values); template <typename CType> void TestSqlIngestNumericType(ArrowType type); template <enum ArrowTimeUnit TU> void TestSqlIngestTimestampType(const char* timezone); virtual void ValidateIngestedTimestampData(struct ArrowArrayView* values, enum ArrowTimeUnit unit, const char* timezone); }; #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, 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, SqlIngestFloat32) { TestSqlIngestFloat32(); } \ TEST_F(FIXTURE, SqlIngestFloat64) { TestSqlIngestFloat64(); } \ TEST_F(FIXTURE, SqlIngestString) { TestSqlIngestString(); } \ TEST_F(FIXTURE, SqlIngestBinary) { TestSqlIngestBinary(); } \ TEST_F(FIXTURE, SqlIngestDate32) { TestSqlIngestDate32(); } \ TEST_F(FIXTURE, SqlIngestTimestamp) { TestSqlIngestTimestamp(); } \ TEST_F(FIXTURE, SqlIngestTimestampTz) { TestSqlIngestTimestampTz(); } \ TEST_F(FIXTURE, SqlIngestInterval) { TestSqlIngestInterval(); } \ TEST_F(FIXTURE, SqlIngestTableEscaping) { TestSqlIngestTableEscaping(); } \ TEST_F(FIXTURE, SqlIngestAppend) { TestSqlIngestAppend(); } \ TEST_F(FIXTURE, SqlIngestErrors) { TestSqlIngestErrors(); } \ TEST_F(FIXTURE, SqlIngestMultipleConnections) { TestSqlIngestMultipleConnections(); } \ TEST_F(FIXTURE, SqlIngestSample) { TestSqlIngestSample(); } \ 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, SqlQueryInts) { TestSqlQueryInts(); } \ TEST_F(FIXTURE, SqlQueryFloats) { TestSqlQueryFloats(); } \ TEST_F(FIXTURE, SqlQueryStrings) { TestSqlQueryStrings(); } \ TEST_F(FIXTURE, SqlQueryErrors) { TestSqlQueryErrors(); } \ TEST_F(FIXTURE, Transactions) { TestTransactions(); } \ TEST_F(FIXTURE, ConcurrentStatements) { TestConcurrentStatements(); } \ TEST_F(FIXTURE, ResultInvalidation) { TestResultInvalidation(); } } // namespace adbc_validation #endif // ADBC_VALIDATION_H