// 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 "arrow/flight/sql/odbc/tests/odbc_test_suite.h"

#include "arrow/flight/sql/odbc/odbc_impl/platform.h"

#include <sql.h>
#include <sqltypes.h>
#include <sqlucode.h>

#include <gtest/gtest.h>

namespace arrow::flight::sql::odbc {

template <typename T>
class GetFunctionsTest : public T {};

using TestTypes =
    ::testing::Types<FlightSQLODBCMockTestBase, FlightSQLODBCRemoteTestBase>;
TYPED_TEST_SUITE(GetFunctionsTest, TestTypes);

template <typename T>
class GetFunctionsOdbcV2Test : public T {};

using TestTypesOdbcV2 =
    ::testing::Types<FlightSQLOdbcV2MockTestBase, FlightSQLOdbcV2RemoteTestBase>;
TYPED_TEST_SUITE(GetFunctionsOdbcV2Test, TestTypesOdbcV2);

TYPED_TEST(GetFunctionsTest, TestSQLGetFunctionsAllFunctions) {
  // Verify driver manager return values for SQLGetFunctions

  SQLUSMALLINT api_exists[SQL_API_ODBC3_ALL_FUNCTIONS_SIZE];
  const std::vector<int> supported_functions = {
      SQL_API_SQLALLOCHANDLE, SQL_API_SQLBINDCOL, SQL_API_SQLGETDIAGFIELD,
      SQL_API_SQLCANCEL, SQL_API_SQLCLOSECURSOR, SQL_API_SQLGETDIAGREC,
      SQL_API_SQLCOLATTRIBUTE, SQL_API_SQLGETENVATTR, SQL_API_SQLCONNECT,
      SQL_API_SQLGETINFO, SQL_API_SQLGETSTMTATTR, SQL_API_SQLDESCRIBECOL,
      SQL_API_SQLGETTYPEINFO, SQL_API_SQLDISCONNECT, SQL_API_SQLNUMRESULTCOLS,
      SQL_API_SQLPREPARE, SQL_API_SQLEXECDIRECT, SQL_API_SQLEXECUTE, SQL_API_SQLROWCOUNT,
      SQL_API_SQLFETCH, SQL_API_SQLSETCONNECTATTR, SQL_API_SQLFETCHSCROLL,
      SQL_API_SQLFREEHANDLE, SQL_API_SQLFREESTMT, SQL_API_SQLGETCONNECTATTR,
      SQL_API_SQLSETENVATTR, SQL_API_SQLSETSTMTATTR, SQL_API_SQLGETDATA,
      SQL_API_SQLCOLUMNS, SQL_API_SQLTABLES, SQL_API_SQLNATIVESQL,
      SQL_API_SQLDRIVERCONNECT, SQL_API_SQLMORERESULTS, SQL_API_SQLPRIMARYKEYS,
      SQL_API_SQLFOREIGNKEYS,

      // ODBC 2.0 APIs
      SQL_API_SQLSETSTMTOPTION, SQL_API_SQLGETSTMTOPTION, SQL_API_SQLSETCONNECTOPTION,
      SQL_API_SQLGETCONNECTOPTION, SQL_API_SQLALLOCCONNECT, SQL_API_SQLALLOCENV,
      SQL_API_SQLALLOCSTMT, SQL_API_SQLFREEENV, SQL_API_SQLFREECONNECT,

      // Driver Manager APIs
      SQL_API_SQLGETFUNCTIONS, SQL_API_SQLDRIVERS, SQL_API_SQLDATASOURCES};
  const std::vector<int> unsupported_functions = {
      SQL_API_SQLPUTDATA,        SQL_API_SQLGETDESCFIELD,     SQL_API_SQLGETDESCREC,
      SQL_API_SQLCOPYDESC,       SQL_API_SQLPARAMDATA,        SQL_API_SQLENDTRAN,
      SQL_API_SQLSETCURSORNAME,  SQL_API_SQLSETDESCFIELD,     SQL_API_SQLSETDESCREC,
      SQL_API_SQLGETCURSORNAME,  SQL_API_SQLSTATISTICS,       SQL_API_SQLSPECIALCOLUMNS,
      SQL_API_SQLBINDPARAMETER,  SQL_API_SQLBROWSECONNECT,    SQL_API_SQLNUMPARAMS,
      SQL_API_SQLBULKOPERATIONS, SQL_API_SQLCOLUMNPRIVILEGES, SQL_API_SQLPROCEDURECOLUMNS,
      SQL_API_SQLDESCRIBEPARAM,  SQL_API_SQLPROCEDURES,       SQL_API_SQLSETPOS,
      SQL_API_SQLTABLEPRIVILEGES};

  ASSERT_EQ(SQL_SUCCESS,
            SQLGetFunctions(this->conn, SQL_API_ODBC3_ALL_FUNCTIONS, api_exists));

  for (int api : supported_functions) {
    EXPECT_EQ(SQL_TRUE, SQL_FUNC_EXISTS(api_exists, api));
  }

  for (int api : unsupported_functions) {
    EXPECT_EQ(SQL_FALSE, SQL_FUNC_EXISTS(api_exists, api));
  }
}

TYPED_TEST(GetFunctionsOdbcV2Test, TestSQLGetFunctionsAllFunctionsODBCVer2) {
  // Verify driver manager return values for SQLGetFunctions

  // ODBC 2.0 SQLGetFunctions returns 100 elements according to spec
  SQLUSMALLINT api_exists[100];
  const std::vector<int> supported_functions = {
      SQL_API_SQLCONNECT, SQL_API_SQLGETINFO, SQL_API_SQLDESCRIBECOL,
      SQL_API_SQLGETTYPEINFO, SQL_API_SQLDISCONNECT, SQL_API_SQLNUMRESULTCOLS,
      SQL_API_SQLPREPARE, SQL_API_SQLEXECDIRECT, SQL_API_SQLEXECUTE, SQL_API_SQLROWCOUNT,
      SQL_API_SQLFETCH, SQL_API_SQLFREESTMT, SQL_API_SQLGETDATA, SQL_API_SQLCOLUMNS,
      SQL_API_SQLTABLES, SQL_API_SQLNATIVESQL, SQL_API_SQLDRIVERCONNECT,
      SQL_API_SQLMORERESULTS, SQL_API_SQLSETSTMTOPTION, SQL_API_SQLGETSTMTOPTION,
      SQL_API_SQLSETCONNECTOPTION, SQL_API_SQLGETCONNECTOPTION, SQL_API_SQLALLOCCONNECT,
      SQL_API_SQLALLOCENV, SQL_API_SQLALLOCSTMT, SQL_API_SQLFREEENV,
      SQL_API_SQLFREECONNECT, SQL_API_SQLPRIMARYKEYS, SQL_API_SQLFOREIGNKEYS,

      // Driver Manager APIs
      SQL_API_SQLGETFUNCTIONS, SQL_API_SQLDRIVERS, SQL_API_SQLDATASOURCES};
  const std::vector<int> unsupported_functions = {
      SQL_API_SQLPUTDATA,        SQL_API_SQLPARAMDATA,        SQL_API_SQLSETCURSORNAME,
      SQL_API_SQLGETCURSORNAME,  SQL_API_SQLSTATISTICS,       SQL_API_SQLSPECIALCOLUMNS,
      SQL_API_SQLBINDPARAMETER,  SQL_API_SQLBROWSECONNECT,    SQL_API_SQLNUMPARAMS,
      SQL_API_SQLBULKOPERATIONS, SQL_API_SQLCOLUMNPRIVILEGES, SQL_API_SQLPROCEDURECOLUMNS,
      SQL_API_SQLDESCRIBEPARAM,  SQL_API_SQLPROCEDURES,       SQL_API_SQLSETPOS,
      SQL_API_SQLTABLEPRIVILEGES};
  ASSERT_EQ(SQL_SUCCESS, SQLGetFunctions(this->conn, SQL_API_ALL_FUNCTIONS, api_exists));

  for (int api : supported_functions) {
    EXPECT_EQ(SQL_TRUE, api_exists[api]);
  }

  for (int api : unsupported_functions) {
    EXPECT_EQ(SQL_FALSE, api_exists[api]);
  }
}

TYPED_TEST(GetFunctionsTest, TestSQLGetFunctionsSupportedSingleAPI) {
  const std::vector<SQLUSMALLINT> supported_functions = {
      SQL_API_SQLALLOCHANDLE, SQL_API_SQLBINDCOL, SQL_API_SQLGETDIAGFIELD,
      SQL_API_SQLCANCEL, SQL_API_SQLCLOSECURSOR, SQL_API_SQLGETDIAGREC,
      SQL_API_SQLCOLATTRIBUTE, SQL_API_SQLGETENVATTR, SQL_API_SQLCONNECT,
      SQL_API_SQLGETINFO, SQL_API_SQLGETSTMTATTR, SQL_API_SQLDESCRIBECOL,
      SQL_API_SQLGETTYPEINFO, SQL_API_SQLDISCONNECT, SQL_API_SQLNUMRESULTCOLS,
      SQL_API_SQLPREPARE, SQL_API_SQLEXECDIRECT, SQL_API_SQLEXECUTE, SQL_API_SQLROWCOUNT,
      SQL_API_SQLFETCH, SQL_API_SQLSETCONNECTATTR, SQL_API_SQLFETCHSCROLL,
      SQL_API_SQLFREEHANDLE, SQL_API_SQLFREESTMT, SQL_API_SQLGETCONNECTATTR,
      SQL_API_SQLSETENVATTR, SQL_API_SQLSETSTMTATTR, SQL_API_SQLGETDATA,
      SQL_API_SQLCOLUMNS, SQL_API_SQLTABLES, SQL_API_SQLNATIVESQL,
      SQL_API_SQLDRIVERCONNECT, SQL_API_SQLMORERESULTS, SQL_API_SQLPRIMARYKEYS,
      SQL_API_SQLFOREIGNKEYS,

      // ODBC 2.0 APIs
      SQL_API_SQLSETSTMTOPTION, SQL_API_SQLGETSTMTOPTION, SQL_API_SQLSETCONNECTOPTION,
      SQL_API_SQLGETCONNECTOPTION, SQL_API_SQLALLOCCONNECT, SQL_API_SQLALLOCENV,
      SQL_API_SQLALLOCSTMT, SQL_API_SQLFREEENV, SQL_API_SQLFREECONNECT,

      // Driver Manager APIs
      SQL_API_SQLGETFUNCTIONS, SQL_API_SQLDRIVERS, SQL_API_SQLDATASOURCES};
  SQLUSMALLINT api_exists;
  for (SQLUSMALLINT api : supported_functions) {
    ASSERT_EQ(SQL_SUCCESS, SQLGetFunctions(this->conn, api, &api_exists));

    EXPECT_EQ(SQL_TRUE, api_exists);

    api_exists = -1;
  }
}

TYPED_TEST(GetFunctionsTest, TestSQLGetFunctionsUnsupportedSingleAPI) {
  const std::vector<SQLUSMALLINT> unsupported_functions = {
      SQL_API_SQLPUTDATA,        SQL_API_SQLGETDESCFIELD,     SQL_API_SQLGETDESCREC,
      SQL_API_SQLCOPYDESC,       SQL_API_SQLPARAMDATA,        SQL_API_SQLENDTRAN,
      SQL_API_SQLSETCURSORNAME,  SQL_API_SQLSETDESCFIELD,     SQL_API_SQLSETDESCREC,
      SQL_API_SQLGETCURSORNAME,  SQL_API_SQLSTATISTICS,       SQL_API_SQLSPECIALCOLUMNS,
      SQL_API_SQLBINDPARAMETER,  SQL_API_SQLBROWSECONNECT,    SQL_API_SQLNUMPARAMS,
      SQL_API_SQLBULKOPERATIONS, SQL_API_SQLCOLUMNPRIVILEGES, SQL_API_SQLPROCEDURECOLUMNS,
      SQL_API_SQLDESCRIBEPARAM,  SQL_API_SQLPROCEDURES,       SQL_API_SQLSETPOS,
      SQL_API_SQLTABLEPRIVILEGES};
  SQLUSMALLINT api_exists;
  for (SQLUSMALLINT api : unsupported_functions) {
    ASSERT_EQ(SQL_SUCCESS, SQLGetFunctions(this->conn, api, &api_exists));

    EXPECT_EQ(SQL_FALSE, api_exists);

    api_exists = -1;
  }
}

TYPED_TEST(GetFunctionsOdbcV2Test, TestSQLGetFunctionsSupportedSingleAPIODBCVer2) {
  const std::vector<SQLUSMALLINT> supported_functions = {
      SQL_API_SQLCONNECT, SQL_API_SQLGETINFO, SQL_API_SQLDESCRIBECOL,
      SQL_API_SQLGETTYPEINFO, SQL_API_SQLDISCONNECT, SQL_API_SQLNUMRESULTCOLS,
      SQL_API_SQLPREPARE, SQL_API_SQLEXECDIRECT, SQL_API_SQLEXECUTE, SQL_API_SQLROWCOUNT,
      SQL_API_SQLFETCH, SQL_API_SQLFREESTMT, SQL_API_SQLGETDATA, SQL_API_SQLCOLUMNS,
      SQL_API_SQLTABLES, SQL_API_SQLNATIVESQL, SQL_API_SQLDRIVERCONNECT,
      SQL_API_SQLMORERESULTS, SQL_API_SQLSETSTMTOPTION, SQL_API_SQLGETSTMTOPTION,
      SQL_API_SQLSETCONNECTOPTION, SQL_API_SQLGETCONNECTOPTION, SQL_API_SQLALLOCCONNECT,
      SQL_API_SQLALLOCENV, SQL_API_SQLALLOCSTMT, SQL_API_SQLFREEENV,
      SQL_API_SQLFREECONNECT, SQL_API_SQLPRIMARYKEYS, SQL_API_SQLFOREIGNKEYS,

      // Driver Manager APIs
      SQL_API_SQLGETFUNCTIONS, SQL_API_SQLDRIVERS, SQL_API_SQLDATASOURCES};
  SQLUSMALLINT api_exists;
  for (SQLUSMALLINT api : supported_functions) {
    ASSERT_EQ(SQL_SUCCESS, SQLGetFunctions(this->conn, api, &api_exists));

    EXPECT_EQ(SQL_TRUE, api_exists);

    api_exists = -1;
  }
}

TYPED_TEST(GetFunctionsOdbcV2Test, TestSQLGetFunctionsUnsupportedSingleAPIODBCVer2) {
  const std::vector<SQLUSMALLINT> unsupported_functions = {
      SQL_API_SQLPUTDATA,        SQL_API_SQLPARAMDATA,        SQL_API_SQLSETCURSORNAME,
      SQL_API_SQLGETCURSORNAME,  SQL_API_SQLSTATISTICS,       SQL_API_SQLSPECIALCOLUMNS,
      SQL_API_SQLBINDPARAMETER,  SQL_API_SQLBROWSECONNECT,    SQL_API_SQLNUMPARAMS,
      SQL_API_SQLBULKOPERATIONS, SQL_API_SQLCOLUMNPRIVILEGES, SQL_API_SQLPROCEDURECOLUMNS,
      SQL_API_SQLDESCRIBEPARAM,  SQL_API_SQLPROCEDURES,       SQL_API_SQLSETPOS,
      SQL_API_SQLTABLEPRIVILEGES};
  SQLUSMALLINT api_exists;
  for (SQLUSMALLINT api : unsupported_functions) {
    ASSERT_EQ(SQL_SUCCESS, SQLGetFunctions(this->conn, api, &api_exists));

    EXPECT_EQ(SQL_FALSE, api_exists);

    api_exists = -1;
  }
}

}  // namespace arrow::flight::sql::odbc
