Skip to content
This repository has been archived by the owner on Aug 2, 2022. It is now read-only.

Commit

Permalink
ODBC: add ODBC 2.x functions called by Excel for Mac (#592)
Browse files Browse the repository at this point in the history
* add ODBC 2.x functions called by Excel for Mac

- change install location to one accessible by Mac Excel

* return empty string for catalog name in SQLTables/Columns results

* remove extraneous log statements

* fix ODBC 2.x function definition for 32-bit Windows

* update install location in docs
  • Loading branch information
jordanw-bq committed Jul 20, 2020
1 parent c66b85f commit af4ff91
Show file tree
Hide file tree
Showing 11 changed files with 210 additions and 14 deletions.
4 changes: 2 additions & 2 deletions sql-odbc/docs/user/mac_configure_dsn.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ Note: In order to use the Open Distro for Elasticsearch SQL ODBC driver with the
1. Go to the **ODBC Drivers** tab.
2. Click **Add a Driver**.
* **Description of the Driver**: The driver name used for ODBC connections (ex. `ODFE SQL ODBC Driver`)
* **Driver File Name**: The path to the driver file (default installed path: `/usr/local/lib/odfesqlodbc/bin/libodfesqlodbc.dylib`)
* **Setup File Name**: The path to the driver file (default installed path: `/usr/local/lib/odfesqlodbc/bin/libodfesqlodbc.dylib`)
* **Driver File Name**: The path to the driver file (default installed path: `/Library/ODBC/odfesqlodbc/bin/libodfesqlodbc.dylib`)
* **Setup File Name**: The path to the driver file (default installed path: `/Library/ODBC/odfesqlodbc/bin/libodfesqlodbc.dylib`)
* Set as a **User** driver
* Below is a screenshot of how the final screen should look.
3. Click **OK** to save the options.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -382,8 +382,7 @@ TEST_F(TestSQLColumns, FlightsValidation) {
ordinal++;
switch (ordinal) {
case 1:
EXPECT_TRUE((it.AsString() == flights_catalog_elas)
|| (it.AsString() == flights_catalog_odfe));
EXPECT_EQ(it.AsString(), "");
break;
case 3:
EXPECT_EQ(it.AsString(), flights_table_name);
Expand Down
4 changes: 4 additions & 0 deletions sql-odbc/src/IntegrationTests/ITODBCInfo/test_odbc_info.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,11 @@ int Ver1GEVer2(std::wstring ver_1_str, std::wstring ver_2_str) {
// Driver Info //
/////////////////

#if defined(WIN32)
TEST_SQL_GET_INFO_STRING(SQLDriverName, SQL_DRIVER_NAME, L"odfesqlodbc.dll");
#elif defined(__APPLE__)
TEST_SQL_GET_INFO_STRING(SQLDriverName, SQL_DRIVER_NAME, L"libodfesqlodbc.dylib");
#endif
TEST_SQL_GET_INFO_STRING(SQLDriverODBCVer, SQL_DRIVER_ODBC_VER, L"03.51");

std::wstring version =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ const int delay_offset_3_1 = 0;
const int delay_offset_3_2 = 180;
const SQLSMALLINT single_col_name_length = 6;
const SQLSMALLINT single_col_data_type = SQL_WVARCHAR;
const SQLULEN single_col_column_size = 15;
const SQLULEN single_col_column_size = 25;
const SQLSMALLINT single_col_decimal_digit = 0;
const SQLSMALLINT single_col_nullable = 2;
const std::wstring single_row = L"1";
Expand Down
5 changes: 2 additions & 3 deletions sql-odbc/src/installer/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,6 @@ if(WIN32)
else()
set(CPACK_GENERATOR "productbuild")

# This script will be run once the Driver component has finished installing.
set(CPACK_POSTFLIGHT_DRIVER_SCRIPT "${CMAKE_CURRENT_SOURCE_DIR}/postinstall")

# The productbuild generator copies files from this directory
set(CPACK_PRODUCTBUILD_RESOURCES_DIR "${CMAKE_CURRENT_SOURCE_DIR}/Resources")

Expand All @@ -74,6 +71,8 @@ else()
set(CPACK_RESOURCE_FILE_LICENSE "${PROJECT_ROOT}/LICENSE.txt")
set(CPACK_RESOURCE_FILE_README "${CMAKE_CURRENT_SOURCE_DIR}/Resources/README.txt")
set(CPACK_RESOURCE_FILE_WELCOME "${CMAKE_CURRENT_SOURCE_DIR}/Resources/Welcome.txt")

set(CPACK_PACKAGING_INSTALL_PREFIX "/Library/ODBC/odfe-sql-odbc")
endif()

# Set up components for installer
Expand Down
4 changes: 2 additions & 2 deletions sql-odbc/src/installer/Resources/README.txt
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
All files are available in '/usr/local/lib/odfe-sql-odbc' after installation.
All files are available in '/Library/ODBC/odfe-sql-odbc' after installation.

To setup a connection, you can use DSN to store your data source connection information,
1. Open 'iODBC Data Source Administrator'.
2. Go to 'User DSN'.
3. Select 'ODFE SQL ODBC DSN' and click on 'Configure'.
4. Update the connection string values. For the list of all supported options, check '/usr/local/lib/odfe-sql-odbc/doc/README.md'.
4. Update the connection string values. For the list of all supported options, check '/Library/ODBC/odfe-sql-odbc/doc/README.md'.
5. Click 'Ok' to save changes.

If using with ODBC compatible BI tools, refer to the tool documentation on configuring a new ODBC driver. The typical requirement is to make the tool aware of the location of the driver library file and then use it to setup database (i.e Elasticsearch) connections.
Expand Down
2 changes: 1 addition & 1 deletion sql-odbc/src/installer/postinstall
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/bin/bash

PKG_INSTALL_DIR=/Applications
FINAL_INSTALL_DIR=/usr/local/lib/odfe-sql-odbc
FINAL_INSTALL_DIR=/Library/ODBC/odfe-sql-odbc

# Remove install directory if it already exists
if [ -d "${FINAL_INSTALL_DIR}" ]; then
Expand Down
13 changes: 10 additions & 3 deletions sql-odbc/src/odfesqlodbc/es_info.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ class BindTemplateSQLCHAR : public BindTemplate {
}
void UpdateData(SQLPOINTER new_data, size_t size) {
m_data.clear();
SQLCHAR *data = (SQLCHAR *)new_data;
SQLCHAR *data = reinterpret_cast< SQLCHAR * >(new_data);
for (size_t i = 0; i < size; i++) {
m_data.push_back(*data++);
}
Expand Down Expand Up @@ -471,6 +471,11 @@ void SetTableTuples(QResultClass *res, const TableResultSet res_type,
};
auto AssignData = [&](auto *res, const auto &binds) {
TupleField *tuple = QR_AddNew(res);
// Since we do not support catalogs, we will return an empty string for
// catalog names. This is required for Excel for Mac, which uses this
// information for its Data Preview window.
std::string catalog("");
bind_tbl[TABLES_CATALOG_NAME]->UpdateData((void *)catalog.c_str(), 0);
for (size_t i = 0; i < binds.size(); i++)
binds[i]->AssignData(&tuple[i]);
};
Expand All @@ -482,7 +487,8 @@ void SetTableTuples(QResultClass *res, const TableResultSet res_type,
while (SQL_SUCCEEDED(result = ESAPI_Fetch(tbl_stmt))) {
if (bind_tbl[TABLES_TABLE_TYPE]->AsString() == "BASE TABLE") {
std::string table("TABLE");
bind_tbl[TABLES_TABLE_TYPE]->UpdateData(&table, table.size());
bind_tbl[TABLES_TABLE_TYPE]->UpdateData((void *)table.c_str(),
table.length());
}
if (list_of_columns != NULL && !list_of_columns->empty()) {
if (std::find(list_of_columns->begin(), list_of_columns->end(),
Expand Down Expand Up @@ -512,7 +518,8 @@ void SetTableTuples(QResultClass *res, const TableResultSet res_type,
// Replace BASE TABLE with TABLE for Excel & Power BI SQLTables call
if (bind_tbl[TABLES_TABLE_TYPE]->AsString() == "BASE TABLE") {
std::string table("TABLE");
bind_tbl[TABLES_TABLE_TYPE]->UpdateData(&table, table.size());
bind_tbl[TABLES_TABLE_TYPE]->UpdateData((void *)table.c_str(),
table.length());
}
if (std::find(table_types.begin(), table_types.end(),
bind_tbl[TABLES_TABLE_TYPE]->AsString())
Expand Down
2 changes: 2 additions & 0 deletions sql-odbc/src/odfesqlodbc/mylog.c
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,8 @@ static void MLOG_open() {
char filebuf[80], errbuf[160];
BOOL open_error = FALSE;

// TODO (#585): Add option to log to stderr stream
// MLOGFP = stderr;
if (MLOGFP)
return;

Expand Down
77 changes: 77 additions & 0 deletions sql-odbc/src/odfesqlodbc/odbcapi.c
Original file line number Diff line number Diff line change
Expand Up @@ -1291,3 +1291,80 @@ RETCODE SQL_API SQLBindParameter(HSTMT hstmt, SQLUSMALLINT ipar,
"SQLBindParameter");
return SQL_ERROR;
}

/* ODBC 2.x-specific functions */
// TODO (#590): Add implementations for remaining ODBC 2.x function

RETCODE SQL_API SQLAllocStmt(SQLHDBC InputHandle, SQLHSTMT *OutputHandle) {
RETCODE ret;
ConnectionClass *conn;
MYLOG(ES_TRACE, "entering\n");

conn = (ConnectionClass *)InputHandle;
ENTER_CONN_CS(conn);
ret = ESAPI_AllocStmt(
InputHandle, OutputHandle,
PODBC_EXTERNAL_STATEMENT | PODBC_INHERIT_CONNECT_OPTIONS);
if (*OutputHandle)
((StatementClass *)(*OutputHandle))->external = 1;
LEAVE_CONN_CS(conn);

return ret;
}

#ifndef UNICODE_SUPPORTXX
RETCODE SQL_API SQLGetConnectOption(HDBC ConnectionHandle, SQLUSMALLINT Option,
PTR Value) {
RETCODE ret;

MYLOG(ES_TRACE, "entering " FORMAT_UINTEGER "\n", Option);
ENTER_CONN_CS((ConnectionClass *)ConnectionHandle);
CC_clear_error((ConnectionClass *)ConnectionHandle);
ret = ESAPI_GetConnectOption(ConnectionHandle, Option, Value, NULL, 0);
LEAVE_CONN_CS((ConnectionClass *)ConnectionHandle);
return ret;
}

/* SQLSetConnectOption -> SQLSetConnectAttr */
RETCODE SQL_API SQLSetConnectOption(HDBC ConnectionHandle, SQLUSMALLINT Option,
SQLULEN Value) {
RETCODE ret;
ConnectionClass *conn = (ConnectionClass *)ConnectionHandle;

MYLOG(ES_TRACE, "entering " FORMAT_INTEGER "\n", Option);
ENTER_CONN_CS(conn);
CC_clear_error(conn);
ret = ESAPI_SetConnectOption(ConnectionHandle, Option, Value);
LEAVE_CONN_CS(conn);
return ret;
}

/* SQLColAttributes -> SQLColAttribute */
SQLRETURN SQL_API SQLColAttributes(SQLHSTMT StatementHandle,
SQLUSMALLINT ColumnNumber,
SQLUSMALLINT FieldIdentifier,
SQLPOINTER CharacterAttribute,
SQLSMALLINT BufferLength,
SQLSMALLINT *StringLength,
#if defined(_WIN64) || defined(_WIN32) || defined(SQLCOLATTRIBUTE_SQLLEN)
SQLLEN *NumericAttribute
#else
SQLPOINTER NumericAttribute
#endif
) {
RETCODE ret;
StatementClass *stmt = (StatementClass *)StatementHandle;

MYLOG(ES_TRACE, "entering\n");
if (SC_connection_lost_check(stmt, __FUNCTION__))
return SQL_ERROR;

ENTER_STMT_CS(stmt);
SC_clear_error(stmt);
ret = ESAPI_ColAttributes(StatementHandle, ColumnNumber, FieldIdentifier,
CharacterAttribute, BufferLength, StringLength,
NumericAttribute);
LEAVE_STMT_CS(stmt);
return ret;
}
#endif /* UNICODE_SUPPORTXX */
108 changes: 108 additions & 0 deletions sql-odbc/src/odfesqlodbc/odbcapiw.c
Original file line number Diff line number Diff line change
Expand Up @@ -897,3 +897,111 @@ RETCODE SQL_API SQLGetTypeInfoW(SQLHSTMT StatementHandle,
LEAVE_STMT_CS(stmt);
return ret;
}


/* ODBC 2.x-specific functions */
// TODO (#590): Add implementations for remaining ODBC 2.x function

SQLRETURN SQL_API SQLColAttributesW(SQLHSTMT hstmt, SQLUSMALLINT iCol,
SQLUSMALLINT iField, SQLPOINTER pCharAttr,
SQLSMALLINT cbCharAttrMax,
SQLSMALLINT *pcbCharAttr,
#if defined(_WIN64) || defined(_WIN32) || defined(SQLCOLATTRIBUTE_SQLLEN)
SQLLEN *pNumAttr
#else
SQLPOINTER pNumAttr
#endif
) {
CSTR func = "SQLColAttributeW";
RETCODE ret;
StatementClass *stmt = (StatementClass *)hstmt;
SQLSMALLINT *rgbL, blen = 0, bMax;
char *rgbD = NULL, *rgbDt;

MYLOG(ES_TRACE, "entering\n");
if (SC_connection_lost_check(stmt, __FUNCTION__))
return SQL_ERROR;

ENTER_STMT_CS(stmt);
SC_clear_error(stmt);
switch (iField) {
case SQL_DESC_BASE_COLUMN_NAME:
case SQL_DESC_BASE_TABLE_NAME:
case SQL_DESC_CATALOG_NAME:
case SQL_DESC_LABEL:
case SQL_DESC_LITERAL_PREFIX:
case SQL_DESC_LITERAL_SUFFIX:
case SQL_DESC_LOCAL_TYPE_NAME:
case SQL_DESC_NAME:
case SQL_DESC_SCHEMA_NAME:
case SQL_DESC_TABLE_NAME:
case SQL_DESC_TYPE_NAME:
case SQL_COLUMN_NAME:
bMax = cbCharAttrMax * 3 / WCLEN;
rgbD = malloc(bMax);
rgbL = &blen;
for (rgbDt = rgbD;; bMax = blen + 1, rgbDt = realloc(rgbD, bMax)) {
if (!rgbDt) {
ret = SQL_ERROR;
break;
}
rgbD = rgbDt;
ret = ESAPI_ColAttributes(hstmt, iCol, iField, rgbD, bMax, rgbL,
pNumAttr);
if (SQL_SUCCESS_WITH_INFO != ret || blen < bMax)
break;
}
if (SQL_SUCCEEDED(ret)) {
blen = (SQLSMALLINT)utf8_to_ucs2(
rgbD, blen, (SQLWCHAR *)pCharAttr, cbCharAttrMax / WCLEN);
if (SQL_SUCCESS == ret
&& blen * WCLEN >= (unsigned long)cbCharAttrMax) {
ret = SQL_SUCCESS_WITH_INFO;
SC_set_error(stmt, STMT_TRUNCATED,
"The buffer was too small for the pCharAttr.",
func);
}
if (pcbCharAttr)
*pcbCharAttr = blen * WCLEN;
}
if (rgbD)
free(rgbD);
break;
default:
rgbD = pCharAttr;
bMax = cbCharAttrMax;
rgbL = pcbCharAttr;
ret = ESAPI_ColAttributes(hstmt, iCol, iField, rgbD, bMax, rgbL,
pNumAttr);
break;
}
LEAVE_STMT_CS(stmt);

return ret;
}

RETCODE SQL_API SQLGetConnectOptionW(HDBC ConnectionHandle, SQLUSMALLINT Option,
PTR Value) {
ConnectionClass *conn = (ConnectionClass *)ConnectionHandle;
RETCODE ret;

ENTER_CONN_CS(conn);
CC_clear_error(conn);
MYLOG(ES_TRACE, "entering " FORMAT_UINTEGER "\n", Option);
ret = ESAPI_GetConnectOption(ConnectionHandle, Option, Value, NULL, 0);
LEAVE_CONN_CS(conn);
return ret;
}

RETCODE SQL_API SQLSetConnectOptionW(HDBC ConnectionHandle, SQLUSMALLINT Option,
SQLULEN Value) {
ConnectionClass *conn = (ConnectionClass *)ConnectionHandle;
RETCODE ret;

MYLOG(ES_TRACE, "entering " FORMAT_INTEGER "\n", Option);
ENTER_CONN_CS(conn);
CC_clear_error(conn);
ret = ESAPI_SetConnectOption(ConnectionHandle, Option, Value);
LEAVE_CONN_CS(conn);
return ret;
}

0 comments on commit af4ff91

Please sign in to comment.