From 84916715d8ef1b5625f4bae609a2ba07bc2ab5ae Mon Sep 17 00:00:00 2001 From: currantw Date: Thu, 14 Nov 2024 13:58:30 -0800 Subject: [PATCH] Initial updates to add `IsServerless` configuration option. Signed-off-by: currantw --- docs/dev/BUILD_INSTRUCTIONS.md | 4 +- docs/user/configuration_options.md | 3 +- src/sqlodbc/dlg_specific.c | 60 +++++++++++++++++++----- src/sqlodbc/dlg_specific.h | 5 +- src/sqlodbc/opensearch_communication.cpp | 15 +++--- src/sqlodbc/opensearch_connection.cpp | 1 + src/sqlodbc/opensearch_odbc.h | 1 + src/sqlodbc/opensearch_types.h | 1 + 8 files changed, 64 insertions(+), 26 deletions(-) diff --git a/docs/dev/BUILD_INSTRUCTIONS.md b/docs/dev/BUILD_INSTRUCTIONS.md index dfd3d77..f63e72b 100644 --- a/docs/dev/BUILD_INSTRUCTIONS.md +++ b/docs/dev/BUILD_INSTRUCTIONS.md @@ -107,7 +107,7 @@ See [run_tests.md](./run_tests.md) **BUILD_WITH_TESTS** -(Defaults to ON) If disabled, all tests and and test dependencies will be excluded from build which will optimize the installer package size. This option can set with the command line (using `-D`). +(Defaults to ON) If disabled, all tests and test dependencies will be excluded from build which will optimize the installer package size. This option can set with the command line (using `-D`). ### Working With SSL/TLS @@ -119,7 +119,7 @@ If you plan to use OpenSearch Dashboards, as suggested for this project, you mus ### Setting up a DSN -A **D**ata **S**ouce **N**ame is used to store driver information in the system. By storing the information in the system, the information does not need to be specified each time the driver connects. +A **D**ata **S**ource **N**ame is used to store driver information in the system. By storing the information in the system, the information does not need to be specified each time the driver connects. #### Windows diff --git a/docs/user/configuration_options.md b/docs/user/configuration_options.md index 0a8201c..2aa5092 100644 --- a/docs/user/configuration_options.md +++ b/docs/user/configuration_options.md @@ -27,9 +27,10 @@ | Option | Description | Type | Acceptable Values | Default | |------------------------|------------------------------------------------------------------------------------------------------------------------------------------|---------|---------------------------------|-------------| | `UseSSL` | Whether to establish the connection over SSL/TLS | boolean | `0` or `1` | `0` (false) | -| `HostnameVerification` | Indicate whether certificate hostname verification should be performed for an SSL/TLS connection. | boolean | `0` or `1` | `1` (true) | +| `HostnameVerification` | Whether certificate hostname verification should be performed for an SSL/TLS connection. | boolean | `0` or `1` | `1` (true) | | `ResponseTimeout` | The maximum time to wait for responses from the `Host`, in seconds. | integer | | `10` | | `FetchSize` | The page size for all cursor requests. The default value (-1) uses server-defined page size. Set FetchSize to 0 for non-cursor behavior. | integer | `-1`, `0` or any positive value | `-1` | +| `IsServerless` | Whether the connection is to an OpenSearch Serverless cluster. The default value (-1) means it is determined by parsing the server URL. | integer | `-1`, `0` or `1` | `-1` | #### Logging Options diff --git a/src/sqlodbc/dlg_specific.c b/src/sqlodbc/dlg_specific.c index 3d2f8f8..ad0006d 100644 --- a/src/sqlodbc/dlg_specific.c +++ b/src/sqlodbc/dlg_specific.c @@ -37,20 +37,44 @@ void makeConnectString(char *connect_string, const ConnInfo *ci, UWORD len) { encode(ci->password, encoded_item, sizeof(encoded_item)); /* fundamental info */ nlen = MAX_CONNECT_STRING; + + // Format string + const char* connect_string_format = + "%s=%s;" + INI_SERVER "=%s;" + "database=OpenSearch;" + INI_PORT "=%s;" + INI_USERNAME_ABBR "=%s;" + INI_PASSWORD_ABBR "=%s;" + INI_AUTH_MODE "=%s;" + INI_REGION "=%s;" + INI_TUNNEL_HOST "=%s;" + INI_IS_SERVERLESS "=%d" + INI_SSL_USE "=%d;" + INI_SSL_HOST_VERIFY "=%d;" + INI_LOG_LEVEL "=%d;" + INI_LOG_OUTPUT "=%s;" + INI_TIMEOUT "=%s;" + INI_FETCH_SIZE "=%s;"; + olen = snprintf( connect_string, nlen, - "%s=%s;" INI_SERVER - "=%s;" - "database=OpenSearch;" INI_PORT "=%s;" INI_USERNAME_ABBR - "=%s;" INI_PASSWORD_ABBR "=%s;" INI_AUTH_MODE "=%s;" INI_REGION - "=%s;" INI_TUNNEL_HOST "=%s;" INI_SSL_USE "=%d;" INI_SSL_HOST_VERIFY - "=%d;" INI_LOG_LEVEL "=%d;" INI_LOG_OUTPUT "=%s;" INI_TIMEOUT "=%s;" - INI_FETCH_SIZE "=%s;", + connect_string_format, got_dsn ? "DSN" : "DRIVER", got_dsn ? ci->dsn : ci->drivername, - ci->server, ci->port, ci->username, encoded_item, ci->authtype, - ci->region, ci->tunnel_host, (int)ci->use_ssl, (int)ci->verify_server, - (int)ci->drivers.loglevel, ci->drivers.output_dir, - ci->response_timeout, ci->fetch_size); + ci->server, + ci->port, + ci->username, + encoded_item, + ci->authtype, + ci->region, + ci->tunnel_host, + (int)ci->is_serverless, + (int)ci->use_ssl, + (int)ci->verify_server, + (int)ci->drivers.loglevel, + ci->drivers.output_dir, + ci->response_timeout, + ci->fetch_size); if (olen < 0 || olen >= nlen) { connect_string[0] = '\0'; return; @@ -112,6 +136,8 @@ BOOL copyConnAttributes(ConnInfo *ci, const char *attribute, STRCPY_FIXED(ci->region, value); else if (stricmp(attribute, INI_TUNNEL_HOST) == 0) STRCPY_FIXED(ci->tunnel_host, value); + else if (stricmp(attribute, INI_IS_SERVERLESS) == 0) + STRCPY_FIXED(ci->is_serverless, value); else if (stricmp(attribute, INI_SSL_USE) == 0) ci->use_ssl = (char)atoi(value); else if (stricmp(attribute, INI_SSL_HOST_VERIFY) == 0) @@ -141,7 +167,7 @@ static void getCiDefaults(ConnInfo *ci) { strncpy(ci->port, DEFAULT_PORT, SMALL_REGISTRY_LEN); strncpy(ci->response_timeout, DEFAULT_RESPONSE_TIMEOUT_STR, SMALL_REGISTRY_LEN); - strncpy(ci->fetch_size, DEFAULT_FETCH_SIZE_STR, + strncpy(ci->fetch_size, DEFAULT_FETCH_SIZE, SMALL_REGISTRY_LEN); strncpy(ci->authtype, DEFAULT_AUTHTYPE, MEDIUM_REGISTRY_LEN); if (ci->password.name != NULL) @@ -150,6 +176,7 @@ static void getCiDefaults(ConnInfo *ci) { strncpy(ci->username, DEFAULT_USERNAME, MEDIUM_REGISTRY_LEN); strncpy(ci->region, DEFAULT_REGION, MEDIUM_REGISTRY_LEN); strncpy(ci->tunnel_host, DEFAULT_TUNNEL_HOST, MEDIUM_REGISTRY_LEN); + strncpy(ci->is_serverless, DEFAULT_IS_SERVERLESS, SMALL_REGISTRY_LEN); ci->use_ssl = DEFAULT_USE_SSL; ci->verify_server = DEFAULT_VERIFY_SERVER; strcpy(ci->drivers.output_dir, "C:\\"); @@ -258,6 +285,10 @@ void getDSNinfo(ConnInfo *ci, const char *configDrvrname) { sizeof(temp), ODBC_INI) > 0) STRCPY_FIXED(ci->tunnel_host, temp); + if (SQLGetPrivateProfileString(DSN, INI_IS_SERVERLESS, NULL_STRING, temp, + sizeof(temp), ODBC_INI) + > 0) + STRCPY_FIXED(ci->is_serverless, temp); if (SQLGetPrivateProfileString(DSN, INI_SSL_USE, NULL_STRING, temp, sizeof(temp), ODBC_INI) > 0) @@ -313,6 +344,7 @@ void writeDSNinfo(const ConnInfo *ci) { SQLWritePrivateProfileString(DSN, INI_AUTH_MODE, ci->authtype, ODBC_INI); SQLWritePrivateProfileString(DSN, INI_REGION, ci->region, ODBC_INI); SQLWritePrivateProfileString(DSN, INI_TUNNEL_HOST, ci->tunnel_host, ODBC_INI); + SQLWritePrivateProfileString(DSN, INI_IS_SERVERLESS, ci->is_serverless, ODBC_INI); ITOA_FIXED(temp, ci->use_ssl); SQLWritePrivateProfileString(DSN, INI_SSL_USE, temp, ODBC_INI); ITOA_FIXED(temp, ci->verify_server); @@ -455,7 +487,7 @@ void CC_conninfo_init(ConnInfo *conninfo, UInt4 option) { strncpy(conninfo->port, DEFAULT_PORT, SMALL_REGISTRY_LEN); strncpy(conninfo->response_timeout, DEFAULT_RESPONSE_TIMEOUT_STR, SMALL_REGISTRY_LEN); - strncpy(conninfo->fetch_size, DEFAULT_FETCH_SIZE_STR, + strncpy(conninfo->fetch_size, DEFAULT_FETCH_SIZE, SMALL_REGISTRY_LEN); strncpy(conninfo->authtype, DEFAULT_AUTHTYPE, MEDIUM_REGISTRY_LEN); if (conninfo->password.name != NULL) @@ -464,6 +496,7 @@ void CC_conninfo_init(ConnInfo *conninfo, UInt4 option) { strncpy(conninfo->username, DEFAULT_USERNAME, MEDIUM_REGISTRY_LEN); strncpy(conninfo->region, DEFAULT_REGION, MEDIUM_REGISTRY_LEN); strncpy(conninfo->tunnel_host, DEFAULT_TUNNEL_HOST, MEDIUM_REGISTRY_LEN); + strncpy(conninfo->is_serverless, DEFAULT_IS_SERVERLESS, SMALL_REGISTRY_LEN); conninfo->use_ssl = DEFAULT_USE_SSL; conninfo->verify_server = DEFAULT_VERIFY_SERVER; @@ -505,6 +538,7 @@ void CC_copy_conninfo(ConnInfo *ci, const ConnInfo *sci) { CORR_STRCPY(authtype); CORR_STRCPY(region); CORR_STRCPY(tunnel_host); + CORR_STRCPY(is_serverless); NAME_TO_NAME(ci->password, sci->password); CORR_VALCPY(use_ssl); CORR_VALCPY(verify_server); diff --git a/src/sqlodbc/dlg_specific.h b/src/sqlodbc/dlg_specific.h index c93fcad..1321745 100644 --- a/src/sqlodbc/dlg_specific.h +++ b/src/sqlodbc/dlg_specific.h @@ -51,9 +51,9 @@ extern "C" { #define INI_LOG_OUTPUT "logOutput" #define INI_TIMEOUT "responseTimeout" #define INI_FETCH_SIZE "fetchSize" +#define INI_IS_SERVERLESS "isServerless" -#define DEFAULT_FETCH_SIZE -1 -#define DEFAULT_FETCH_SIZE_STR "-1" +#define DEFAULT_FETCH_SIZE "-1" #define DEFAULT_RESPONSE_TIMEOUT 10 // Seconds #define DEFAULT_RESPONSE_TIMEOUT_STR "10" #define DEFAULT_AUTHTYPE "NONE" @@ -65,6 +65,7 @@ extern "C" { #define DEFAULT_DESC "" #define DEFAULT_DSN "" #define DEFAULT_VERIFY_SERVER 1 +#define DEFAULT_IS_SERVERLESS "-1" #define AUTHTYPE_NONE "NONE" #define AUTHTYPE_BASIC "BASIC" diff --git a/src/sqlodbc/opensearch_communication.cpp b/src/sqlodbc/opensearch_communication.cpp index 99147ed..ffe43ea 100644 --- a/src/sqlodbc/opensearch_communication.cpp +++ b/src/sqlodbc/opensearch_communication.cpp @@ -1076,7 +1076,7 @@ std::string OpenSearchCommunication::GetClusterName() { */ void OpenSearchCommunication::SetSqlEndpoint() { - // TODO #70: Support serverless + // Serverless Elasticsearch is not supported. if (isServerless()) { sql_endpoint = SQL_ENDPOINT_OPENSEARCH; return; @@ -1094,17 +1094,16 @@ void OpenSearchCommunication::SetSqlEndpoint() { /** * Returns whether this is connecting to an OpenSearch Serverless cluster. - * @see - * https://docs.aws.amazon.com/opensearch-service/latest/developerguide/serverless-overview.html */ bool OpenSearchCommunication::isServerless() { - // TODO #70: Support serverless + // Specified in DSN configuration + const std::string& is_serverless = m_rt_opts.conn.is_serverless; - // Parse the server URL. - if(m_rt_opts.conn.server.find("aoss.amazonaws.com") != std::string::npos) { - return true; + if(is_serverless != DEFAULT_IS_SERVERLESS) { + return std::stoi(is_serverless); } - return false; + // Parsed from server URL. + return m_rt_opts.conn.server.find("aoss.amazonaws.com") != std::string::npos; } diff --git a/src/sqlodbc/opensearch_connection.cpp b/src/sqlodbc/opensearch_connection.cpp index 1262a5d..00ab72f 100644 --- a/src/sqlodbc/opensearch_connection.cpp +++ b/src/sqlodbc/opensearch_connection.cpp @@ -117,6 +117,7 @@ int LIBOPENSEARCH_connect(ConnectionClass *self) { } rt_opts.conn.port.assign(self->connInfo.port); rt_opts.conn.timeout.assign(self->connInfo.response_timeout); + rt_opts.conn.is_serverless.assign(self->connInfo.is_serverless); // Authentication rt_opts.auth.auth_type.assign(self->connInfo.authtype); diff --git a/src/sqlodbc/opensearch_odbc.h b/src/sqlodbc/opensearch_odbc.h index a20ce48..48ee0a8 100644 --- a/src/sqlodbc/opensearch_odbc.h +++ b/src/sqlodbc/opensearch_odbc.h @@ -606,6 +606,7 @@ typedef struct { char port[SMALL_REGISTRY_LEN]; char response_timeout[SMALL_REGISTRY_LEN]; char fetch_size[SMALL_REGISTRY_LEN]; + char is_serverless[SMALL_REGISTRY_LEN];; // Authentication char authtype[MEDIUM_REGISTRY_LEN]; diff --git a/src/sqlodbc/opensearch_types.h b/src/sqlodbc/opensearch_types.h index 3c61b4a..05ce11b 100644 --- a/src/sqlodbc/opensearch_types.h +++ b/src/sqlodbc/opensearch_types.h @@ -272,6 +272,7 @@ typedef struct connection_options { std::string port; std::string timeout; std::string fetch_size; + std::string is_serverless; } connection_options; typedef struct runtime_options {