Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
310 changes: 257 additions & 53 deletions .github/workflows/compatibility_test.yaml

Large diffs are not rendered by default.

453 changes: 303 additions & 150 deletions .github/workflows/integration_test.yaml

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions driver/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,11 @@ set(INC
${CMAKE_CURRENT_SOURCE_DIR}/util/init_plugin_helper.h
${CMAKE_CURRENT_SOURCE_DIR}/util/odbc_helper.h

# Dialects
${CMAKE_CURRENT_SOURCE_DIR}/dialect/dialect_aurora_mysql.h
${CMAKE_CURRENT_SOURCE_DIR}/dialect/dialect_aurora_postgres.h
${CMAKE_CURRENT_SOURCE_DIR}/dialect/dialect.h

# Core
${CMAKE_CURRENT_SOURCE_DIR}/driver.h
${CMAKE_CURRENT_SOURCE_DIR}/error.h
Expand Down
6 changes: 4 additions & 2 deletions driver/dialect/dialect.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,14 @@
typedef enum {
AURORA_POSTGRESQL,
AURORA_POSTGRESQL_LIMITLESS,
AURORA_MYSQL,
UNKNOWN_DIALECT
} DatabaseDialectType;

static std::map<std::string, DatabaseDialectType> const database_dialect_table = {
{VALUE_DB_DIALECT_AURORA_POSTGRESQL, DatabaseDialectType::AURORA_POSTGRESQL},
{VALUE_DB_DIALECT_AURORA_POSTGRESQL_LIMITLESS, DatabaseDialectType::AURORA_POSTGRESQL_LIMITLESS}
{VALUE_DB_DIALECT_AURORA_POSTGRESQL, DatabaseDialectType::AURORA_POSTGRESQL},
{VALUE_DB_DIALECT_AURORA_POSTGRESQL_LIMITLESS, DatabaseDialectType::AURORA_POSTGRESQL_LIMITLESS},
{VALUE_DB_DIALECT_AURORA_MYSQL, DatabaseDialectType::AURORA_MYSQL}
};

class Dialect {
Expand Down
82 changes: 82 additions & 0 deletions driver/dialect/dialect_aurora_mysql.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
//
// Licensed 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 DIALECT_AURORA_MYSQL_H
#define DIALECT_AURORA_MYSQL_H

#include <algorithm>

#include "dialect.h"

#include <vector>

#include "../util/rds_strings.h"

class DialectAuroraMySql : virtual public Dialect {
public:
int GetDefaultPort() override { return DEFAULT_MYSQL_PORT; };
std::string GetTopologyQuery() override { return TOPOLOGY_QUERY; };
std::string GetWriterIdQuery() override { return WRITER_ID_QUERY; };
std::string GetNodeIdQuery() override { return NODE_ID_QUERY; };
std::string GetIsReaderQuery() override { return IS_READER_QUERY; };

bool IsSqlStateAccessError(const char* sql_state) override {
std::string state(sql_state);
return std::ranges::any_of(ACCESS_ERRORS, [&state](const std::string &prefix) {
return state.rfind(prefix, 0) == 0;
});
};

bool IsSqlStateNetworkError(const char* sql_state) override {
std::string state(sql_state);
return std::ranges::any_of(NETWORK_ERRORS, [&state](const std::string &prefix) {
return state.rfind(prefix, 0) == 0;
});
};

private:
const int DEFAULT_MYSQL_PORT = 3306;
const std::string TOPOLOGY_QUERY =
"SELECT SERVER_ID, CASE WHEN SESSION_ID = 'MASTER_SESSION_ID' THEN TRUE ELSE FALSE END, \
CPU, REPLICA_LAG_IN_MILLISECONDS, LAST_UPDATE_TIMESTAMP \
FROM information_schema.replica_host_status \
WHERE time_to_sec(timediff(now(), LAST_UPDATE_TIMESTAMP)) <= 300 OR SESSION_ID = 'MASTER_SESSION_ID'";

const std::string WRITER_ID_QUERY =
"SELECT SERVER_ID FROM information_schema.replica_host_status \
WHERE SESSION_ID = 'MASTER_SESSION_ID' AND SERVER_ID = @@aurora_server_id";

const std::string NODE_ID_QUERY = "SELECT @@aurora_server_id";

const std::string IS_READER_QUERY = "SELECT @@innodb_read_only";

const std::vector<std::string> ACCESS_ERRORS = {
"28P01",
"28000" // PAM authentication errors
};

const std::vector<std::string> NETWORK_ERRORS = {
"53", // insufficient resources
"57P01", // admin shutdown
"57P02", // crash shutdown
"57P03", // cannot connect now
"58", // system error (backend)
"08", // connection error
"99", // unexpected error
"F0", // configuration file error (backend)
"XX" // internal error (backend)
};
};

#endif // DIALECT_AURORA_MYSQL_H
3 changes: 2 additions & 1 deletion driver/gui/setup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,8 @@ const std::vector<std::pair<std::string, std::string>> LIMITLESS_MODES = {
const std::vector<std::pair<std::string, std::string>> DB_DIALECTS = {
{"", ""},
{"Aurora PostgreSQL", VALUE_DB_DIALECT_AURORA_POSTGRESQL},
{"Aurora PostgreSQL Limitless", VALUE_DB_DIALECT_AURORA_POSTGRESQL_LIMITLESS}
{"Aurora PostgreSQL Limitless", VALUE_DB_DIALECT_AURORA_POSTGRESQL_LIMITLESS},
{"Aurora MySQL", VALUE_DB_DIALECT_AURORA_MYSQL}
};

HINSTANCE ghInstance;
Expand Down
2 changes: 1 addition & 1 deletion driver/plugin/failover/cluster_topology_monitor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -548,7 +548,7 @@ void ClusterTopologyMonitor::NodeMonitoringThread::HandleReconnect() {
RDS_AllocDbc(main_monitor_->henv_, &hdbc_);
local_dbc = static_cast<DBC*>(hdbc_);
local_dbc->conn_attr = conn_info_;
main_monitor_->plugin_head_->Connect(hdbc_, nullptr, nullptr, SQL_NTS, nullptr, SQL_DRIVER_NOPROMPT);
main_monitor_->plugin_head_->Connect(hdbc_, nullptr, nullptr, 0, nullptr, SQL_DRIVER_NOPROMPT);
}

void ClusterTopologyMonitor::NodeMonitoringThread::HandleWriterConn() {
Expand Down
1 change: 1 addition & 0 deletions driver/util/connection_string_keys.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@
#define KEY_DATABASE_DIALECT "DATABASE_DIALECT"
#define VALUE_DB_DIALECT_AURORA_POSTGRESQL "AURORA_POSTGRESQL"
#define VALUE_DB_DIALECT_AURORA_POSTGRESQL_LIMITLESS "AURORA_POSTGRESQL_LIMITLESS"
#define VALUE_DB_DIALECT_AURORA_MYSQL "AURORA_MYSQL"

/* Failover */
#define KEY_ENABLE_FAILOVER "ENABLE_CLUSTER_FAILOVER"
Expand Down
9 changes: 7 additions & 2 deletions driver/util/init_plugin_helper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,14 @@

#include <map>

#include "../dialect/dialect.h"
#include "../dialect/dialect_aurora_postgres.h"
#include "init_plugin_helper.h"

#include "rds_utils.h"

#include "../dialect/dialect.h"
#include "../dialect/dialect_aurora_mysql.h"
#include "../dialect/dialect_aurora_postgres.h"

std::shared_ptr<Dialect> InitDialect(std::map<std::string, std::string> conn_info)
{
DatabaseDialectType dialect = DatabaseDialectType::UNKNOWN_DIALECT;
Expand All @@ -43,6 +46,8 @@ std::shared_ptr<Dialect> InitDialect(std::map<std::string, std::string> conn_inf
return std::make_shared<DialectAuroraPostgres>();
case DatabaseDialectType::AURORA_POSTGRESQL_LIMITLESS:
return std::make_shared<DialectAuroraPostgresLimitless>();
case DatabaseDialectType::AURORA_MYSQL:
return std::make_shared<DialectAuroraMySql>();
default:
return std::make_shared<Dialect>();
}
Expand Down
4 changes: 2 additions & 2 deletions scripts/aws_rds_helper_unix.sh
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ function add_ip_to_db_sg {
cidrBlock="$LocalIp/32"

# Allow inbound traffic from the local IP address to the DB security group using AWS CLI
aws ec2 authorize-security-group-ingress --group-id $securityGroupId --protocol tcp --cidr $cidrBlock --port 5432 --region $Region || true
aws ec2 authorize-security-group-ingress --group-id $securityGroupId --protocol tcp --cidr $cidrBlock --port 0-65535 --region $Region || true

# Sleep to ensure IP is added and propagated to DBs before moving onto next step
sleep 60
Expand Down Expand Up @@ -76,7 +76,7 @@ function remove_ip_from_db_sg {
cidrBlock="$LocalIp/32"

# Revoke Inbound traffic
aws ec2 revoke-security-group-ingress --group-id $securityGroupId --protocol tcp --cidr $cidrBlock --port 5432 --region $Region || true
aws ec2 revoke-security-group-ingress --group-id $securityGroupId --protocol tcp --cidr $cidrBlock --port 0-65565 --region $Region || true

# Check if revoked successfully
if [ $? -eq 0 ]; then
Expand Down
2 changes: 1 addition & 1 deletion scripts/aws_rds_helper_win.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ function Add-Ip-To-Db-Sg {
$cidrBlock = "$localIp/32"

# Allow inbound traffic from the local IP address to the DB security group using AWS CLI
$authorizeResult = aws ec2 authorize-security-group-ingress --group-id $securityGroupId --protocol tcp --cidr $cidrBlock --port 5432 --region $Region
$authorizeResult = aws ec2 authorize-security-group-ingress --group-id $securityGroupId --protocol tcp --cidr $cidrBlock --port 0-65535 --region $Region

# Check if the ingress rule was successfully added
if ($?) {
Expand Down
4 changes: 4 additions & 0 deletions scripts/init_local_mysql_query.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
CREATE DATABASE IF NOT EXISTS test_database;
CREATE USER 'test_username'@'localhost' IDENTIFIED WITH caching_sha2_password BY 'test_password';
GRANT ALL on *.* TO 'test_username'@'localhost';
FLUSH PRIVILEGES;
1 change: 1 addition & 0 deletions test/common/base_connection_test.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ class BaseConnectionTest : public testing::Test {
int test_port = TEST_UTILS::StrToInt(TEST_UTILS::GetEnvVar("TEST_PORT", "5432"));
std::string test_region = TEST_UTILS::GetEnvVar("TEST_REGION", "us-west-1");
std::string test_server = TEST_UTILS::GetEnvVar("TEST_SERVER");
std::string test_dialect = TEST_UTILS::GetEnvVar("TEST_DIALECT");

std::string conn_str = "";

Expand Down
23 changes: 10 additions & 13 deletions test/compatibility/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -102,20 +102,17 @@ target_link_libraries(${PROJECT_NAME} PRIVATE ${EXTERNAL_LIBRARIES})
# ODBC Unix DSN ---------------------------------------------------------------------------------------------
SET(ODBC_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../resources/)

# Common Settings
# DSN Settings
SET(THREADING "0")
SET(TEST_SERVER ${TEST_SERVER},${TEST_PORT})
SET(TEST_DATABASE ${TEST_DATABASE})

# Test Driver
SET(TEST_DSN "wrapper-dsn")
SET(TEST_DRIVER_NAME "aws-odbc-wrapper")
SET(TEST_DRIVER_PATH ${TEST_DRIVER_PATH})

# Base PG Driver
SET(BASE_PG_DSN "pg-dsn")
SET(BASE_PG_DRIVER_NAME "community-pg")
SET(BASE_PG_DRIVER_PATH ${BASE_PG_DRIVER_PATH})

# To be set via flags
# -D TEST_DRIVER_PATH
# -D PG_DRIVER_PATH
# -D PG_SERVER
# -D PG_DATABASE
# -D MYSQL_DRIVER_PATH
# -D MYSQL_SERVER
# -D MYSQL_DATABASE

CONFIGURE_FILE(
${ODBC_PATH}/odbcinst.ini.in
Expand Down
23 changes: 10 additions & 13 deletions test/integration/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -157,20 +157,17 @@ ENDIF()
# ODBC Unix DSN ---------------------------------------------------------------------------------------------
SET(ODBC_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../resources/)

# Common Settings
# DSN Settings
SET(THREADING "0")
SET(TEST_SERVER ${TEST_SERVER},${TEST_PORT})
SET(TEST_DATABASE ${TEST_DATABASE})

# Test Driver
SET(TEST_DSN ${TEST_DSN})
SET(TEST_DRIVER_NAME "aws-odbc-wrapper")
SET(TEST_DRIVER_PATH ${TEST_DRIVER_PATH})

# Base PG Driver
SET(BASE_PG_DSN "pg-dsn")
SET(BASE_PG_DRIVER_NAME "community-pg")
SET(BASE_PG_DRIVER_PATH ${BASE_PG_DRIVER_PATH})

# To be set via flags
# -D TEST_DRIVER_PATH
# -D PG_DRIVER_PATH
# -D PG_SERVER
# -D PG_DATABASE
# -D MYSQL_DRIVER_PATH
# -D MYSQL_SERVER
# -D MYSQL_DATABASE

CONFIGURE_FILE(
${ODBC_PATH}/odbcinst.ini.in
Expand Down
11 changes: 10 additions & 1 deletion test/integration/base_failover_integration_test.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,9 @@ class BaseFailoverIntegrationTest : public BaseConnectionTest {
// Test Queries
std::string DROP_TABLE_QUERY = "DROP TABLE IF EXISTS failover_transaction";
std::string CREATE_TABLE_QUERY = "CREATE TABLE failover_transaction (id INT NOT NULL PRIMARY KEY, failover_transaction_field VARCHAR(255) NOT NULL)";
std::string SERVER_ID_QUERY = "SELECT aurora_db_instance_identifier()";
std::string COUNT_FAILOVER_TRANSACTION_ROWS_QUERY = "SELECT count(*) FROM failover_transaction";
// Based off of dialect
std::string SERVER_ID_QUERY = "";

static void SetUpTestSuite() {
BaseConnectionTest::SetUpTestSuite();
Expand All @@ -112,6 +113,14 @@ class BaseFailoverIntegrationTest : public BaseConnectionTest {
test_server.substr(cluster_id_prefix_index + cluster_prefix.size(), test_server.size());
db_conn_str_suffix = "." + instance_endpoint;
cluster_ro_url = ".cluster-ro-" + instance_endpoint;

if ("AURORA_POSTGRESQL" == test_dialect) {
SERVER_ID_QUERY = "SELECT pg_catalog.aurora_db_instance_identifier();";
} else if ("AURORA_MYSQL" == test_dialect) {
SERVER_ID_QUERY = "SELECT @@aurora_server_id";
} else {
GTEST_SKIP() << "Failover requires database dialect to know which query to call.";
}
}

void TearDown() override {
Expand Down
5 changes: 4 additions & 1 deletion test/integration/failover_integration_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ class FailoverIntegrationTest : public BaseFailoverIntegrationTest {
.withPWD(test_pwd)
.withDatabase(test_db)
.withEnableClusterFailover(true)
.withDatabaseDialect(test_dialect)
.getString();

// Check to see if cluster is available.
Expand Down Expand Up @@ -112,7 +113,9 @@ TEST_F(FailoverIntegrationTest, WriterFailWithinTransaction_DisableAutocommit) {
EXPECT_EQ(SQL_SUCCESS, ODBC_HELPER::ExecuteQuery(handle, CREATE_TABLE_QUERY));

// Execute queries within the transaction
std::string transaction_insert_query = "BEGIN; INSERT INTO failover_transaction VALUES (1, 'test field string 1')";
std::string begin_query = "BEGIN;";
EXPECT_EQ(SQL_SUCCESS, ODBC_HELPER::ExecuteQuery(handle, begin_query));
std::string transaction_insert_query = "INSERT INTO failover_transaction VALUES (1, 'test field string 1')";
EXPECT_EQ(SQL_SUCCESS, ODBC_HELPER::ExecuteQuery(handle, transaction_insert_query));

FailoverClusterWaitDesiredWriter(rds_client, cluster_id, writer_id, target_writer_id);
Expand Down
3 changes: 3 additions & 0 deletions test/integration/iam_authentication_integration_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ class IamAuthenticationIntegrationTest : public BaseConnectionTest {

void SetUp() override {
BaseConnectionTest::SetUp();
if (test_dialect == "AURORA_MYSQL") {
GTEST_SKIP() << "MySQL ODBC Connector does not support IAM with session tokens due to limitations on connection key-value pairs.";
}
}

void TearDown() override {
Expand Down
31 changes: 25 additions & 6 deletions test/resources/odbc.ini.in
Original file line number Diff line number Diff line change
@@ -1,13 +1,32 @@
[ODBC Data Sources]
data_source_name = @TEST_DSN@
[wrapper-pg-dsn]
Driver = @TEST_DRIVER_PATH@
DATABASE = @PG_DATABASE@
SERVER = @PG_SERVER@
BASE_DRIVER = @PG_DRIVER_PATH@

[wrapper-mysql-dsn]
Driver = @TEST_DRIVER_PATH@
DATABASE = @MYSQL_DATABASE@
SERVER = @MYSQL_SERVER@
BASE_DRIVER = @MYSQL_DRIVER_PATH@

[pg-dsn]
Driver = @PG_DRIVER_PATH@
DATABASE = @PG_DATABASE@
SERVER = @PG_SERVER@

[mysql-dsn]
Driver = @MYSQL_DRIVER_PATH@
DATABASE = @MYSQL_DATABASE@
SERVER = @MYSQL_SERVER@

[@TEST_DSN@]
[inte-wrapper-dsn]
Driver = @TEST_DRIVER_PATH@
DATABASE = @TEST_DATABASE@
SERVER = @TEST_SERVER@
BASE_DRIVER = @BASE_PG_DRIVER_PATH@
BASE_DRIVER = @BASE_DRIVER_PATH@

[@BASE_PG_DSN@]
Driver = @BASE_PG_DRIVER_PATH@
[inte-base-dsn]
Driver = @BASE_DRIVER_PATH@
DATABASE = @TEST_DATABASE@
SERVER = @TEST_SERVER@
11 changes: 8 additions & 3 deletions test/resources/odbcinst.ini.in
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
[ODBC Drivers]
@TEST_DRIVER_NAME@ = Installed
@BASE_PG_DRIVER_NAME@ = Installed
community-pg = Installed
community-mysql = Installed

[@TEST_DRIVER_NAME@]
Driver = @TEST_DRIVER_PATH@
Threading = @THREADING@

[@BASE_PG_DRIVER_NAME@]
Driver = @BASE_PG_DRIVER_PATH@
[community-pg]
Driver = @PG_DRIVER_PATH@
Threading = @THREADING@

[community-mysql]
Driver = @MYSQL_DRIVER_PATH@
Threading = @THREADING@
Loading