From 595a0ba55b9bb0e1f320a8eecd5df35122600477 Mon Sep 17 00:00:00 2001 From: Artemkin Pavel Date: Thu, 3 Aug 2017 21:23:56 +0500 Subject: [PATCH] #25 fix listing of tables and databases --- driver/attr.cpp | 20 ++++++++-- driver/environment.h | 1 + driver/info.cpp | 2 +- driver/odbc.cpp | 89 ++++++++++++++++++++++++++++++++++---------- driver/statement.cpp | 11 ++++++ driver/statement.h | 10 +++++ driver/utils.h | 10 +++-- 7 files changed, 116 insertions(+), 27 deletions(-) diff --git a/driver/attr.cpp b/driver/attr.cpp index 4c85ea01a..aba7828dd 100644 --- a/driver/attr.cpp +++ b/driver/attr.cpp @@ -38,6 +38,10 @@ impl_SQLSetEnvAttr(SQLHENV environment_handle, SQLINTEGER attribute, return SQL_SUCCESS; } + case SQL_ATTR_METADATA_ID: + environment.metadata_id = (SQLUINTEGER)value; + return SQL_SUCCESS; + default: throw std::runtime_error("Unsupported environment attribute."); } @@ -51,9 +55,10 @@ impl_SQLGetEnvAttr(SQLHENV environment_handle, SQLINTEGER attribute, { LOG(__FUNCTION__); - return doWith(environment_handle, [&](Environment & environment) + return doWith(environment_handle, [&](Environment & environment) -> RETCODE { LOG("attr: " << attribute); + const char * name = nullptr; switch (attribute) { @@ -61,6 +66,8 @@ impl_SQLGetEnvAttr(SQLHENV environment_handle, SQLINTEGER attribute, fillOutputNumber(environment.odbc_version, out_value, out_value_max_length, out_value_length); return SQL_SUCCESS; + CASE_NUM(SQL_ATTR_METADATA_ID, SQLUINTEGER, environment.metadata_id); + case SQL_ATTR_CONNECTION_POOLING: case SQL_ATTR_CP_MATCH: case SQL_ATTR_OUTPUT_NTS: @@ -92,12 +99,15 @@ impl_SQLSetConnectAttr(SQLHDBC connection_handle, SQLINTEGER attribute, return SQL_SUCCESS; } + case SQL_ATTR_CURRENT_CATALOG: + connection.database = stringFromSQLChar((SQLTCHAR *)value, value_length); + return SQL_SUCCESS; + case SQL_ATTR_ACCESS_MODE: case SQL_ATTR_ASYNC_ENABLE: case SQL_ATTR_AUTO_IPD: case SQL_ATTR_AUTOCOMMIT: case SQL_ATTR_CONNECTION_DEAD: - case SQL_ATTR_CURRENT_CATALOG: case SQL_ATTR_METADATA_ID: case SQL_ATTR_ODBC_CURSORS: case SQL_ATTR_PACKET_SIZE: @@ -176,6 +186,10 @@ impl_SQLSetStmtAttr(SQLHSTMT statement_handle, SQLINTEGER attribute, statement.setScanEscapeSequences((SQLULEN)value != SQL_NOSCAN_ON); return SQL_SUCCESS; + case SQL_ATTR_METADATA_ID: + statement.setMetadataId((SQLUINTEGER)value); + return SQL_SUCCESS; + case SQL_ATTR_APP_ROW_DESC: case SQL_ATTR_APP_PARAM_DESC: case SQL_ATTR_CURSOR_SCROLLABLE: @@ -266,7 +280,7 @@ impl_SQLGetStmtAttr(SQLHSTMT statement_handle, SQLINTEGER attribute, CASE_NUM(SQL_ATTR_ENABLE_AUTO_IPD, SQLULEN, SQL_FALSE); CASE_NUM(SQL_ATTR_MAX_LENGTH, SQLULEN, 0); CASE_NUM(SQL_ATTR_MAX_ROWS, SQLULEN, 0); - CASE_NUM(SQL_ATTR_METADATA_ID, SQLULEN, SQL_FALSE); + CASE_NUM(SQL_ATTR_METADATA_ID, SQLUINTEGER, statement.getMetadataId()); CASE_NUM(SQL_ATTR_NOSCAN, SQLULEN, (statement.getScanEscapeSequences() ? SQL_NOSCAN_OFF : SQL_NOSCAN_ON)); CASE_NUM(SQL_ATTR_QUERY_TIMEOUT, SQLULEN, 0); CASE_NUM(SQL_ATTR_RETRIEVE_DATA, SQLULEN, SQL_RD_ON); diff --git a/driver/environment.h b/driver/environment.h index 29cc94bdc..244aaabe8 100644 --- a/driver/environment.h +++ b/driver/environment.h @@ -46,6 +46,7 @@ struct Environment {"Array", TypeInfo{"TEXT", true, SQL_VARCHAR, 0xFFFFFF}}, }; + SQLUINTEGER metadata_id = SQL_FALSE; int odbc_version = SQL_OV_ODBC3_80; DiagnosticRecord diagnostic_record; }; diff --git a/driver/info.cpp b/driver/info.cpp index f378b3787..c4ddeab86 100644 --- a/driver/info.cpp +++ b/driver/info.cpp @@ -126,7 +126,6 @@ SQLGetInfo(HDBC connection_handle, CASE_NUM(SQL_GETDATA_EXTENSIONS, SQLUINTEGER, SQL_GD_ANY_COLUMN | SQL_GD_ANY_ORDER | SQL_GD_BOUND) CASE_NUM(SQL_INDEX_KEYWORDS, SQLUINTEGER, SQL_IK_NONE) CASE_NUM(SQL_INSERT_STATEMENT, SQLUINTEGER, SQL_IS_INSERT_LITERALS | SQL_IS_INSERT_SEARCHED) - CASE_NUM(SQL_SCHEMA_USAGE, SQLUINTEGER, SQL_SU_DML_STATEMENTS | SQL_SU_TABLE_DEFINITION) CASE_NUM(SQL_SCROLL_OPTIONS, SQLUINTEGER, SQL_SO_FORWARD_ONLY) CASE_NUM(SQL_SQL92_DATETIME_FUNCTIONS, SQLUINTEGER, SQL_SDF_CURRENT_DATE | SQL_SDF_CURRENT_TIME | SQL_SDF_CURRENT_TIMESTAMP) @@ -236,6 +235,7 @@ SQLGetInfo(HDBC connection_handle, CASE_FALLTHROUGH(SQL_STATIC_CURSOR_ATTRIBUTES2) CASE_FALLTHROUGH(SQL_INFO_SCHEMA_VIEWS) CASE_FALLTHROUGH(SQL_POS_OPERATIONS) + CASE_FALLTHROUGH(SQL_SCHEMA_USAGE) CASE_FALLTHROUGH(SQL_SYSTEM_FUNCTIONS) CASE_FALLTHROUGH(SQL_SQL92_FOREIGN_KEY_DELETE_RULE) CASE_FALLTHROUGH(SQL_SQL92_FOREIGN_KEY_UPDATE_RULE) diff --git a/driver/odbc.cpp b/driver/odbc.cpp index 3528ae00f..0e239c9ce 100644 --- a/driver/odbc.cpp +++ b/driver/odbc.cpp @@ -298,7 +298,7 @@ impl_SQLGetData(HSTMT statement_handle, { LOG(__FUNCTION__); - return doWith(statement_handle, [&](Statement & statement) + return doWith(statement_handle, [&](Statement & statement) -> RETCODE { if (column_or_param_number < 1 || column_or_param_number > statement.result.getNumColumns()) throw std::runtime_error("Column number is out of range."); @@ -609,7 +609,7 @@ SQLGetDiagField(SQLSMALLINT handle_type, SQLHANDLE handle, out_message_size); } - +/// Description: https://docs.microsoft.com/en-us/sql/relational-databases/native-client-odbc-api/sqltables RETCODE SQL_API SQLTables(HSTMT statement_handle, SQLTCHAR * catalog_name, SQLSMALLINT catalog_name_length, @@ -619,29 +619,78 @@ SQLTables(HSTMT statement_handle, { LOG(__FUNCTION__); + // TODO (artpaul) Take statement.getMetatadaId() into account. return doWith(statement_handle, [&](Statement & statement) { + const std::string catalog = stringFromSQLChar(catalog_name, catalog_name_length); + std::stringstream query; - query << "SELECT" + // Get a list of all tables in all databases. + if (catalog_name != nullptr && catalog == SQL_ALL_CATALOGS && + !schema_name && !table_name && !table_type) + { + query << "SELECT" " database AS TABLE_CAT" ", '' AS TABLE_SCHEM" ", name AS TABLE_NAME" ", 'TABLE' AS TABLE_TYPE" ", '' AS REMARKS" - " FROM system.tables" - " WHERE (1 == 1)"; - - if (catalog_name) - query << " AND TABLE_CAT LIKE '" << stringFromSQLChar(catalog_name, catalog_name_length) << "'"; - if (schema_name) - query << " AND TABLE_SCHEM LIKE '" << stringFromSQLChar(schema_name, schema_name_length) << "'"; - if (table_name) - query << " AND TABLE_NAME LIKE '" << stringFromSQLChar(table_name, table_name_length) << "'"; - /* if (table_type) - query << " AND TABLE_TYPE = '" << stringFromSQLChar(table_type, table_type_length) << "'";*/ - - query << " ORDER BY TABLE_TYPE, TABLE_CAT, TABLE_SCHEM, TABLE_NAME"; + " FROM system.tables" + " ORDER BY TABLE_TYPE, TABLE_CAT, TABLE_SCHEM, TABLE_NAME"; + } + // Get a list of all tables in the current database. + else if (!catalog_name && !schema_name && !table_name && !table_type) + { + query << "SELECT" + " database AS TABLE_CAT" + ", '' AS TABLE_SCHEM" + ", name AS TABLE_NAME" + ", 'TABLE' AS TABLE_TYPE" + ", '' AS REMARKS" + " FROM system.tables" + " WHERE (database == '"; + query << statement.connection.database << "')"; + query << " ORDER BY TABLE_TYPE, TABLE_CAT, TABLE_SCHEM, TABLE_NAME"; + } + // Get a list of databases on the current connection's server. + else if (!catalog.empty() && + schema_name != nullptr && schema_name_length == 0 && + table_name != nullptr && table_name_length == 0) + { + query << "SELECT" + " name AS TABLE_CAT" + ", '' AS TABLE_SCHEM" + ", '' AS TABLE_NAME" + ", '' AS TABLE_TYPE" + ", '' AS REMARKS" + " FROM system.databases" + " WHERE (1 == 1)"; + query << " AND TABLE_CAT LIKE '" << catalog << "'"; + query << " ORDER BY TABLE_CAT"; + } + else + { + query << "SELECT" + " database AS TABLE_CAT" + ", '' AS TABLE_SCHEM" + ", name AS TABLE_NAME" + ", 'TABLE' AS TABLE_TYPE" + ", '' AS REMARKS" + " FROM system.tables" + " WHERE (1 == 1)"; + + if (catalog_name_length) + query << " AND TABLE_CAT LIKE '" << stringFromSQLChar(catalog_name, catalog_name_length) << "'"; + if (schema_name_length) + query << " AND TABLE_SCHEM LIKE '" << stringFromSQLChar(schema_name, schema_name_length) << "'"; + if (table_name_length) + query << " AND TABLE_NAME LIKE '" << stringFromSQLChar(table_name, table_name_length) << "'"; + //if (table_type_length) + // query << " AND TABLE_TYPE = '" << stringFromSQLChar(table_type, table_type_length) << "'"; + + query << " ORDER BY TABLE_TYPE, TABLE_CAT, TABLE_SCHEM, TABLE_NAME"; + } statement.setQuery(query.str()); statement.sendRequest(); @@ -685,13 +734,13 @@ SQLColumns(HSTMT statement_handle, " FROM system.columns" " WHERE (1 == 1)"; - if (catalog_name) + if (catalog_name_length) query << " AND TABLE_CAT LIKE '" << stringFromSQLChar(catalog_name, catalog_name_length) << "'"; - if (schema_name) + if (schema_name_length) query << " AND TABLE_SCHEM LIKE '" << stringFromSQLChar(schema_name, schema_name_length) << "'"; - if (table_name) + if (table_name_length) query << " AND TABLE_NAME LIKE '" << stringFromSQLChar(table_name, table_name_length) << "'"; - if (column_name) + if (column_name_length) query << " AND COLUMN_NAME LIKE '" << stringFromSQLChar(column_name, column_name_length) << "'"; query << " ORDER BY TABLE_CAT, TABLE_SCHEM, TABLE_NAME, ORDINAL_POSITION"; diff --git a/driver/statement.cpp b/driver/statement.cpp index f411dc986..dd6cfcc0e 100644 --- a/driver/statement.cpp +++ b/driver/statement.cpp @@ -7,6 +7,7 @@ Statement::Statement(Connection & conn_) : connection(conn_) + , metadata_id(conn_.environment.metadata_id) { ard.reset(new DescriptorClass); apd.reset(new DescriptorClass); @@ -24,6 +25,16 @@ void Statement::setScanEscapeSequences(bool value) scan_escape_sequences = value; } +SQLUINTEGER Statement::getMetadataId() const +{ + return metadata_id; +} + +void Statement::setMetadataId(SQLUINTEGER id) +{ + metadata_id = id; +} + const std::string Statement::getQuery() const { return query; diff --git a/driver/statement.h b/driver/statement.h index 79097b887..1441d3043 100644 --- a/driver/statement.h +++ b/driver/statement.h @@ -32,6 +32,12 @@ class Statement /// Enable or disable scannign the SQL string for escape sequences. void setScanEscapeSequences(bool value); + /// Returns current value of SQL_ATTR_METADATA_ID. + SQLUINTEGER getMetadataId() const; + + /// Sets value of SQL_ATTR_METADATA_ID. + void setMetadataId(SQLUINTEGER id); + /// Returns original query. const std::string getQuery() const; @@ -76,6 +82,10 @@ class Statement private: std::unique_ptr response; + /// An SQLUINTEGER value that determines + /// how the string arguments of catalog functions are treated. + SQLUINTEGER metadata_id; + std::string query; std::string prepared_query; bool prepared = false; diff --git a/driver/utils.h b/driver/utils.h index 2a5b08882..386011ba1 100644 --- a/driver/utils.h +++ b/driver/utils.h @@ -66,10 +66,10 @@ static const char * nextKeyValuePair(const char * data, const char * end, String template std::string stringFromSQLChar(SQLTCHAR * data, SIZE_TYPE size) { - if (!data) + if (!data || size == 0) return {}; - - if (size < 0) + + if (size == SQL_NTS) { #ifdef UNICODE size = (SIZE_TYPE)wcslen(reinterpret_cast(data)); @@ -77,6 +77,10 @@ std::string stringFromSQLChar(SQLTCHAR * data, SIZE_TYPE size) size = (SIZE_TYPE)strlen(reinterpret_cast(data)); #endif } + else if (size < 0) + { + throw std::runtime_error("invalid size of string : " + std::to_string(size)); + } #ifdef UNICODE std::wstring wstr(reinterpret_cast(data), static_cast(size));