/* * Copyright (c) 2008, 2018, Oracle and/or its affiliates. All rights reserved. * 2020, 2022 MariaDB Corporation AB * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2.0, as * published by the Free Software Foundation. * * This program is also distributed with certain software (including * but not limited to OpenSSL) that is licensed under separate terms, * as designated in a particular file or component or in included license * documentation. The authors of MySQL hereby grant you an * additional permission to link the program and your derivative works * with the separately licensed software that they have included with * MySQL. * * Without limiting anything contained in the foregoing, this file, * which is part of MySQL Connector/C++, is also subject to the * Universal FOSS Exception, version 1.0, a copy of which can be found at * http://oss.oracle.com/licenses/universal-foss-exception. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License, version 2.0, for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "conncpp.hpp" #include "connectionmetadata.h" #include #include namespace testsuite { namespace classes { void connectionmetadata::getSchemata() { logMsg("connectionmetadata::getSchemata() - MySQL_ConnectionMetaData::getSchemata"); SKIP("Not supported methods"); bool schema_found=false; std::stringstream msg; try { DatabaseMetaData dbmeta(con->getMetaData()); /*ResultSet resdbm1(dbmeta->getSchemata()); checkResultSetScrolling(resdbm1); ResultSet resdbm2(dbmeta->getSchemaObjects(con->getCatalog(), "", "schema")); logMsg("... checking if getSchemata() and getSchemaObjects() report the same schematas"); resdbm1->beforeFirst(); while (resdbm1->next()) { schema_found=false; resdbm2->beforeFirst(); while (resdbm2->next()) if (resdbm2->getString("SCHEMA") == resdbm1->getString(1)) { schema_found=true; break; } if (!schema_found) FAIL("Schemata lists differ"); msg.str(""); msg << "... OK " << resdbm1->getString(1) << " = " << resdbm2->getString("SCHEMA"); logMsg(msg.str()); }*/ } catch (sql::SQLException &e) { logErr(e.what()); logErr("SQLState: " + std::string(e.getSQLState())); fail(e.what(), __FILE__, __LINE__); } } void connectionmetadata::getSchemaObjects() { logMsg("connectionmetadata::getSchemaObject() - MySQL_ConnectionMetaData::getSchemaObjects"); try { DatabaseMetaData dbmeta(con->getMetaData()); ResultSet resdbm1(dbmeta->getSchemaObjects()); checkResultSetScrolling(resdbm1); ResultSet resdbm2; while (resdbm1->next()) { resdbm2.reset(dbmeta->getSchemaObjects(con->getCatalog(), "", resdbm1->getString(1))); checkResultSetScrolling(resdbm2); } } catch (sql::MethodNotImplementedException& /*e*/) { SKIP("Method has not been implemented"); } catch (sql::SQLException &e) { logErr(e.what()); logErr("SQLState: " + std::string(e.getSQLState())); fail(e.what(), __FILE__, __LINE__); } } void connectionmetadata::getAttributes() { logMsg("connectionmetadata::getAttributes() - MySQL_ConnectionMetaData::getAttributes"); unsigned int i; std::vector::iterator it; std::stringstream msg; try { DatabaseMetaData dbmeta(con->getMetaData()); res.reset(dbmeta->getAttributes(con->getCatalog(), con->getSchema(), "", "")); checkResultSetScrolling(res); ResultSetMetaData resmeta(res->getMetaData()); it=attributes.begin(); for (i=1; i <= resmeta->getColumnCount(); i++) { if (it == attributes.end()) FAIL("There are more columns than expected"); ASSERT_EQUALS(it->name, resmeta->getColumnName(i)); msg.str(""); msg << "... OK found column " << it->name; logMsg(msg.str()); it++; } if (it != attributes.end()) FAIL("There are less columns than expected"); res.reset(dbmeta->getAttributes("ABC", "DEF", "GHI", "JKL")); ASSERT(!res->next()); } catch (sql::SQLException &e) { logErr(e.what()); logErr("SQLState: " + std::string(e.getSQLState())); fail(e.what(), __FILE__, __LINE__); } } void connectionmetadata::getBestRowIdentifier() { logMsg("connectionmetadata::getBestRowIdentifier()"); std::vector::iterator it; std::stringstream msg; bool got_warning= false; try { DatabaseMetaData dbmeta(con->getMetaData()); stmt.reset(con->createStatement()); logMsg("... looping over all kinds of column types"); for (it= columns.begin(); it != columns.end(); it++) { stmt->execute("DROP TABLE IF EXISTS test"); msg.str(""); msg << "CREATE TABLE test(id " << it->sqldef << ", PRIMARY KEY(id))"; try { stmt->execute(msg.str()); } catch (sql::SQLException &) { msg.str(""); msg << "... skipping " << it->sqldef; logMsg(msg.str()); continue; } res.reset(dbmeta->getBestRowIdentifier(con->getCatalog(), con->getSchema(), "test", 0, false)); checkResultSetScrolling(res); ASSERT_EQUALS(true, res->next()); ASSERT_EQUALS(sql::DatabaseMetaData::bestRowSession, res->getInt(1)); ASSERT_EQUALS(res->getInt(1), res->getInt("SCOPE")); ASSERT_EQUALS("id", res->getString(2)); ASSERT_EQUALS(res->getString(2), res->getString("COLUMN_NAME")); if (it->ctype != res->getInt(3)) { msg.str(""); msg << "... \t\tWARNING - check DATA_TYPE for " << it->sqldef; msg << " - expecting type " << it->ctype << " got " << res->getInt(3); logMsg(msg.str()); ResultSet cres(stmt->executeQuery("SHOW CREATE TABLE test")); cres->next(); logMsg(cres->getString(2).c_str()); got_warning=true; } // TODO - ASSERT_EQUALS(it->ctype, res->getInt(3)); ASSERT_EQUALS(res->getInt(3), res->getInt("DATA_TYPE")); if (res->getString(4).caseCompare(it->name) != 0) { msg.str(""); msg << "... \t\tWARNING - check DATA_TYPE for " << it->sqldef; msg << " - expecting type name " << it->name << " got " << res->getString(4); logMsg(msg.str()); got_warning=true; } ASSERT_EQUALS(res->getString(4), res->getString("TYPE_NAME")); if (it->precision != res->getUInt64(5)) { msg.str(""); msg << "... \t\tWARNING - check COLUMN_SIZE for " << it->sqldef; msg << " - expecting precision " << it->precision << " got " << res->getInt(5); logMsg(msg.str()); got_warning=true; } ASSERT_EQUALS(res->getInt(5), res->getInt("COLUMN_SIZE")); ASSERT_EQUALS(0, res->getInt(6)); ASSERT_EQUALS(res->getInt(6), res->getInt("BUFFER_LENGTH")); if (it->decimal_digits != res->getInt(7)) { msg.str(""); msg << "... \t\tWARNING - check DECIMAL_DIGITS for " << it->sqldef; msg << " - expecting decimal digits = " << it->decimal_digits << " got " << res->getInt(7); logMsg(msg.str()); got_warning=true; } ASSERT_EQUALS(res->getInt(7), res->getInt("DECIMAL_DIGITS")); ASSERT_EQUALS(sql::DatabaseMetaData::bestRowNotPseudo, res->getInt(8)); ASSERT_EQUALS(res->getInt(8), res->getInt("PSEUDO_COLUMN")); stmt->execute("DROP TABLE test"); } if (got_warning) { logMsg("Were was Warnings!"); } stmt->execute("DROP TABLE IF EXISTS test"); // TODO - stmt->execute("CREATE TABLE test(col1 INT NOT NULL, col2 INT NOT NULL, PRIMARY KEY(col1, col2))"); res.reset(dbmeta->getBestRowIdentifier(con->getCatalog(), con->getSchema(), "test", 0, false)); ASSERT(!res->next()); } catch (sql::SQLException &e) { logErr(e.what()); logErr("SQLState: " + std::string(e.getSQLState())); fail(e.what(), __FILE__, __LINE__); } } void connectionmetadata::getColumnPrivileges() { logMsg("connectionmetadata::getColumnPrivileges() - MySQL_ConnectionMetaData::getColumnPrivileges"); int rows=0; bool got_warning=false; std::stringstream msg; DatabaseMetaData dbmeta; sql::SQLString userLocation(""); try { dbmeta.reset(con->getMetaData()); stmt.reset(con->createStatement()); stmt->execute("DROP TABLE IF EXISTS test_getcolpriv"); stmt->execute("CREATE TABLE test_getcolpriv(col1 INT, col2 INT)"); } catch (sql::SQLException & e) { logErr(e.what()); logErr("SQLState: " + std::string(e.getSQLState())); fail(e.what(), __FILE__, __LINE__); } try { stmt->execute("GRANT SELECT (col1,col2) ON test_getcolpriv TO '" + this->user + "'" + userLocation); stmt->execute("GRANT INSERT (col1) ON test_getcolpriv TO '" + this->user + "'" + userLocation); } catch (sql::SQLException & e) { logErr(e.what()); logErr("SQLState: " + std::string(e.getSQLState())); try { userLocation= "@'localhost'"; stmt->execute("GRANT SELECT (col1,col2) ON test_getcolpriv TO '" + this->user + "'" + userLocation); stmt->execute("GRANT INSERT (col1) ON test_getcolpriv TO '" + this->user + "'" + userLocation); } catch (sql::SQLException & e) { sql::SQLString failMsg("User: " + this->user + " [" + e.getSQLState() + "] "); logErr(e.what()); logErr("SQLState: " + e.getSQLState()); failMsg.append(e.what()); fail(failMsg.c_str(), __FILE__, __LINE__); } } try { res.reset(dbmeta->getColumnPrivileges(con->getCatalog(), con->getSchema(), "test_getcolpriv", "id")); ASSERT_EQUALS(false, res->next()); } catch (sql::SQLException & e) { logErr(e.what()); logErr("SQLState: " + std::string(e.getSQLState())); fail(e.what(), __FILE__, __LINE__); } try { res.reset(dbmeta->getColumnPrivileges(con->getCatalog(), con->getSchema(), "test_getcolpriv", "col1")); checkResultSetScrolling(res); rows=0; while (res->next()) { rows++; if (con->getCatalog() != "" && res->getString(1) != "" && con->getCatalog() != res->getString(1)) { got_warning=true; msg.str(""); msg << "... TABLE_CAT: expecting '" << con->getCatalog() << "' got "; msg << "'" << res->getString(1) << "'"; logMsg(msg.str()); } ASSERT_EQUALS(res->getString(1), res->getString("TABLE_CAT")); ASSERT_EQUALS(con->getSchema(), res->getString(2)); ASSERT_EQUALS(res->getString(2), res->getString("TABLE_SCHEM")); ASSERT_EQUALS("test_getcolpriv", res->getString(3)); ASSERT_EQUALS(res->getString(3), res->getString("TABLE_NAME")); ASSERT_EQUALS(res->getString(4), res->getString("COLUMN_NAME")); ASSERT_EQUALS("", res->getString(5)); ASSERT_EQUALS(res->getString(5), res->getString("GRANTOR")); ASSERT_EQUALS(res->getString(6), res->getString("GRANTEE")); ASSERT_EQUALS(res->getString(7), res->getString("PRIVILEGE")); ASSERT_EQUALS(res->getString(8), res->getString("IS_GRANTABLE")); if (("NO" != res->getString(8)) && ("YES" != res->getString(8)) && ("" != res->getString(8))) { // Let's be optimistic that the column does not hold this exact value... ASSERT_EQUALS("Any of 'YES', 'NO' and empty string ''", res->getString(8)); } } ASSERT_GT(2, rows); if (got_warning) { TODO("See --verbose warnings"); } } catch (sql::SQLException & e) { logErr(e.what()); logErr("SQLState: " + std::string(e.getSQLState())); fail(e.what(), __FILE__, __LINE__); } try { res.reset(dbmeta->getColumnPrivileges(con->getCatalog(), con->getSchema(), "test_getcolpriv", "col2")); ASSERT_EQUALS(true, res->next()); ASSERT_EQUALS("col2", res->getString("COLUMN_NAME")); ASSERT_EQUALS(res->getString(4), res->getString("COLUMN_NAME")); } catch (sql::SQLException & e) { logErr(e.what()); logErr("SQLState: " + std::string(e.getSQLState())); fail(e.what(), __FILE__, __LINE__); } try { stmt->execute("REVOKE SELECT (col1,col2) ON test_getcolpriv FROM '" + this->user + "'" + userLocation); stmt->execute("REVOKE INSERT (col1) ON test_getcolpriv FROM '" + this->user + "'" + userLocation); stmt->execute("DROP TABLE IF EXISTS test_getcolpriv"); res.reset(dbmeta->getColumnPrivileges(con->getCatalog(), con->getSchema(), "test_getcolpriv", "col2")); ASSERT(!res->next()); } catch (sql::SQLException &e) { logErr(e.what()); logErr("SQLState: " + std::string(e.getSQLState())); fail(e.what(), __FILE__, __LINE__); } if (got_warning) { FAIL("TODO - See --verbose warnings"); } } void connectionmetadata::getColumns() { logMsg("connectionmetadata::getColumn() - MySQL_ConnectionMetaData::getColumns"); if (getServerVersion(con) < 800000) { SKIP("Due to changes on the VARBINARY, this test is disabled."); return; } std::vector::iterator it; std::stringstream msg; bool got_warning=false; bool got_todo_warning=false; try { DatabaseMetaData dbmeta(con->getMetaData()); stmt.reset(con->createStatement()); bool isVer6=dbmeta->getDatabaseMajorVersion() == 6; int32_t serverVersion = getServerVersion(con); logMsg("... looping over all kinds of column types"); for (it=columns.begin(); it != columns.end(); it++) { stmt->execute("DROP TABLE IF EXISTS test"); msg.str(""); msg << "CREATE TABLE test(dummy TIMESTAMP, id " << it->sqldef << ")"; try { stmt->execute(msg.str()); msg.str(""); msg << "... testing " << it->sqldef; logMsg(msg.str()); } catch (sql::SQLException &) { msg.str(""); msg << "... skipping " << it->sqldef; logMsg(msg.str()); continue; } res.reset(dbmeta->getColumns(con->getCatalog(), con->getSchema(), "test", "id")); checkResultSetScrolling(res); ASSERT_EQUALS(true, res->next()); if (con->getCatalog() != "" && res->getString(1) != "" && con->getCatalog() != res->getString("TABLE_CAT")) { got_todo_warning=true; msg.str(""); msg << "...\t\tWARNING - expecting TABLE_CAT = '" << con->getCatalog() << "'"; msg << " got '" << res->getString("TABLE_CAT") << "'"; logMsg(msg.str()); } ASSERT_EQUALS(res->getString(1), res->getString("TABLE_CAT")); ASSERT_EQUALS(con->getSchema(), res->getString("TABLE_SCHEM")); ASSERT_EQUALS(res->getString(2), res->getString("TABLE_SCHEM")); ASSERT_EQUALS("test", res->getString("TABLE_NAME")); ASSERT_EQUALS(res->getString(3), res->getString("TABLE_NAME")); ASSERT_EQUALS("id", res->getString("COLUMN_NAME")); ASSERT_EQUALS(res->getString(4), res->getString("COLUMN_NAME")); if (it->ctype != res->getInt("DATA_TYPE")) { msg.str(""); msg << "... \t\tWARNING - check DATA_TYPE for " << it->sqldef; msg << " - expecting type " << it->ctype << " got " << res->getInt("DATA_TYPE"); logMsg(msg.str()); got_warning=true; } // ASSERT_EQUALS(it->ctype, res->getInt("DATA_TYPE")); ASSERT_EQUALS(res->getInt(5), res->getInt("DATA_TYPE")); if (it->name != res->getString("TYPE_NAME")) { msg.str(""); msg << "... \t\tWARNING - check TYPE_NAME for " << it->sqldef; msg << " - expecting type " << it->name << " got " << res->getString("TYPE_NAME"); logMsg(msg.str()); got_warning=true; } // ASSERT_EQUALS(it->name, res->getString("TYPE_NAME")); ASSERT_EQUALS(res->getString(6), res->getString("TYPE_NAME")); if (it->precision != res->getUInt64(7)) { msg.str(""); msg << "... \t\tWARNING - check COLUMN_SIZE for " << it->sqldef; msg << " - expecting pecision " << it->precision << " got " << res->getUInt64(7); logMsg(msg.str()); got_warning=true; } ASSERT_EQUALS(res->getUInt(7), res->getUInt("COLUMN_SIZE")); ASSERT_EQUALS(65535, res->getInt(8)); ASSERT_EQUALS(res->getInt(8), res->getInt("BUFFER_LENGTH")); ASSERT_EQUALS(it->decimal_digits, res->getInt(9)); ASSERT_EQUALS(res->getInt(9), res->getInt("DECIMAL_DIGITS")); ASSERT_EQUALS(it->num_prec_radix, res->getInt(10)); ASSERT_EQUALS(res->getInt(10), res->getInt("NUM_PREC_RADIX")); if (it->nullable != res->getInt(11)) { msg.str(""); msg << "... \t\tWARNING - check NULLABLE for " << it->sqldef; msg << " - expecting nullable = " << it->nullable << " got " << res->getInt(11); msg << " columnNoNull = " << sql::DatabaseMetaData::columnNoNulls << ", "; msg << " columnNullable = " << sql::DatabaseMetaData::columnNullable << ", "; msg << " columnNullableUnknown = " << sql::DatabaseMetaData::columnNullableUnknown; logMsg(msg.str()); got_warning=true; } ASSERT_EQUALS(it->nullable, res->getInt(11)); ASSERT_EQUALS(res->getInt(11), res->getInt("NULLABLE")); ASSERT_EQUALS(it->remarks, res->getString(12)); ASSERT_EQUALS(res->getString(12), res->getString("REMARKS")); if(it->column_def != res->getString(13)) { msg.str(""); msg << "... \t\tWARNING - check COLUMN_def for " << it->sqldef; msg << " - expecting COLUMN_def = " << it->column_def << " got " << res->getString(13); logMsg(msg.str()); got_warning=true; } //res->isNull(13) /* Looks like 10.1 returns '' where we expect NULL, and there are also other problems with it. Thus, skipping this check altogether on 10.1*/ if ((std::getenv("srv") != nullptr && (strcmp(std::getenv("srv"), "mysql") == 0 && serverVersion < 800000)) || serverVersion > 1001999) { ASSERT_EQUALS(it->column_def, res->getString(13)); } ASSERT_EQUALS(res->getString(13), res->getString("COLUMN_DEF")); ASSERT_EQUALS(res->getInt(14), res->getInt("SQL_DATA_TYPE")); ASSERT_EQUALS(res->getInt(15), res->getInt("SQL_DATETIME_SUB")); if (it->char_octet_length != 0 && (it->ctype == sql::DataType::CHAR || it->ctype == sql::DataType::VARCHAR)) { size_t expected_len=it->char_octet_length; if ((it->ctype == sql::DataType::CHAR || it->ctype == sql::DataType::VARCHAR) && isVer6 && it->sqldef.find("TINYTEXT") == std::string::npos && (it->sqldef.find("utf8") != std::string::npos || it->sqldef.find("NATIONAL") != std::string::npos)) { expected_len=(expected_len / 3)*4; } if (res->getUInt64(16) != expected_len) { msg.str(""); msg << "... \t\tWARNING - check CHAR_OCTET_LENGTH for " << it->sqldef; msg << " - expecting char_octet_length " << it->char_octet_length << " got " << res->getUInt64(16); logMsg(msg.str()); got_warning=true; } } ASSERT_EQUALS(res->getUInt64(16), res->getUInt64("CHAR_OCTET_LENGTH")); ASSERT_EQUALS(2, res->getInt(17)); ASSERT_EQUALS(res->getInt(17), res->getInt("ORDINAL_POSITION")); if (((it->nullable == sql::DatabaseMetaData::columnNoNulls) && (res->getString(18) != "NO")) || ((it->nullable == sql::DatabaseMetaData::columnNullable) && (res->getString(18) != "YES")) || ((it->nullable == sql::DatabaseMetaData::columnNullableUnknown) && (res->getString(18) != ""))) { msg.str(""); msg << "... \t\tWARNING - check IS_NULLABLE for " << it->sqldef; msg << " - expecting nullable = " << it->nullable << " got is_nullable = '" << res->getInt(18) << "'"; logMsg(msg.str()); got_warning=true; } ASSERT_EQUALS(res->getString(18), res->getString("IS_NULLABLE")); ASSERT_EQUALS("", res->getString(19)); ASSERT_EQUALS(res->getString(19), res->getString("SCOPE_CATALOG")); ASSERT_EQUALS("", res->getString(20)); ASSERT_EQUALS(res->getString(20), res->getString("SCOPE_SCHEMA")); ASSERT_EQUALS("", res->getString(21)); ASSERT_EQUALS(res->getString(21), res->getString("SCOPE_TABLE")); ASSERT_EQUALS("", res->getString(22)); ASSERT_EQUALS(res->getString(22), res->getString("SOURCE_DATA_TYPE")); ASSERT_EQUALS(it->is_autoincrement, res->getString(23)); ASSERT_EQUALS(res->getString(23), res->getString("IS_AUTOINCREMENT")); stmt->execute("DROP TABLE IF EXISTS test"); } if (got_warning) logMsg("See --verbose warnings!");//FAIL if (got_todo_warning) { TODO("See --verbose warnings!"); FAIL("TODO - see --verbose warnings"); } try { bool input_value=true; bool output_value=false; void * input; void * output; stmt->execute("CREATE TABLE test(id INT,val VARCHAR(20))"); input=(static_cast (&input_value)); output=(static_cast (&output_value)); con->setClientOption("metadataUseInfoSchema", input); con->getClientOption("metadataUseInfoSchema", output); ASSERT_EQUALS(input_value, output_value); dbmeta.reset(con->getMetaData()); res.reset(dbmeta->getColumns(con->getCatalog(), "", "test", "%")); ASSERT(res->rowsCount() == 2); input_value=false; output_value=true; con->setClientOption("metadataUseInfoSchema", input); con->getClientOption("metadataUseInfoSchema", output); ASSERT_EQUALS(input_value, output_value); dbmeta.reset(con->getMetaData()); res.reset(dbmeta->getColumns(con->getCatalog(), "", "test", "%")); ASSERT(res->rowsCount() == 2); } catch (sql::SQLFeatureNotImplementedException & e) { logMsg(e.what()); } catch (sql::SQLException &) { FAIL("getColumns() does not work properly for metadataUseInfoSchema"); } stmt->execute("DROP TABLE IF EXISTS test"); res.reset(dbmeta->getColumns(con->getCatalog(), con->getSchema(), "test", "id")); ASSERT(!res->next()); } catch (sql::SQLException &e) { logErr(e.what()); logErr("SQLState: " + std::string(e.getSQLState())); fail(e.what(), __FILE__, __LINE__); } } void connectionmetadata::getConnection() { logMsg("connectionmetadata::getConnection() - MySQL_ConnectionMetaData::getConnection"); sql::Connection *same_con; try { stmt.reset(con->createStatement()); stmt->execute("SET @this_is_my_connection_id=101"); DatabaseMetaData dbmeta(con->getMetaData()); same_con= dbmeta->getConnection(); stmt.reset(same_con->createStatement()); res.reset(stmt->executeQuery("SELECT @this_is_my_connection_id AS _connection_id")); ASSERT(res->next()); ASSERT_EQUALS(101, res->getInt("_connection_id")); ASSERT_EQUALS(res->getInt(1), res->getInt("_connection_id")); } catch (sql::SQLException &e) { logErr(e.what()); logErr("SQLState: " + std::string(e.getSQLState())); fail(e.what(), __FILE__, __LINE__); } } void connectionmetadata::getDatabaseVersions() { logMsg("connectionmetadata::getDatabaseVersions() - MySQL_ConnectionMetaData::getDatabase[Minor|Major|Patch]Version()"); std::stringstream prodversion; try { DatabaseMetaData dbmeta(con->getMetaData()); ASSERT_GT(5, dbmeta->getDatabaseMajorVersion()); ASSERT_LT(10, dbmeta->getDatabaseMajorVersion()); ASSERT_LT(100, dbmeta->getDatabaseMinorVersion()); ASSERT_LT(100, dbmeta->getDatabasePatchVersion()); //ASSERT_EQUALS("MariaDB", dbmeta->getDatabaseProductName()); prodversion.str(""); prodversion << dbmeta->getDatabaseMajorVersion() << "." << dbmeta->getDatabaseMinorVersion(); prodversion << "." << dbmeta->getDatabasePatchVersion(); if (prodversion.str().length() < dbmeta->getDatabaseProductVersion().length()) { // Check only left prefix, database could have "-alpha" or something in its product versin ASSERT_EQUALS(prodversion.str(), dbmeta->getDatabaseProductVersion().substr(0, prodversion.str().length())); } else { ASSERT_EQUALS(prodversion.str(), dbmeta->getDatabaseProductVersion()); } } catch (sql::SQLException &e) { logErr(e.what()); logErr("SQLState: " + std::string(e.getSQLState())); fail(e.what(), __FILE__, __LINE__); } } void connectionmetadata::getDriverVersions() { logMsg("connectionmetadata::getDriverVersions() - MySQL_ConnectionMetaData::getDriver[Minor|Major|Patch]Version()"); std::stringstream prodversion; try { DatabaseMetaData dbmeta(con->getMetaData()); ASSERT_GT(0, dbmeta->getDriverMajorVersion()); ASSERT_LT(2, dbmeta->getDriverMajorVersion()); ASSERT_LT(100, dbmeta->getDriverMinorVersion()); ASSERT_LT(100, dbmeta->getDriverPatchVersion()); ASSERT_EQUALS("MariaDB Connector/C++", dbmeta->getDriverName()); prodversion.str(""); prodversion << dbmeta->getDriverMajorVersion() << "." << dbmeta->getDriverMinorVersion(); prodversion << "." << dbmeta->getDriverPatchVersion(); if (prodversion.str().length() < dbmeta->getDriverVersion().length()) { // Check only left prefix, Driver could have "-alpha" or something in its product versin ASSERT_EQUALS(prodversion.str(), dbmeta->getDriverVersion().substr(0, prodversion.str().length())); } else { ASSERT_EQUALS(prodversion.str(), dbmeta->getDriverVersion()); } } catch (sql::SQLException &e) { logErr(e.what()); logErr("SQLState: " + std::string(e.getSQLState())); fail(e.what(), __FILE__, __LINE__); } } void connectionmetadata::getDefaultTransactionIsolation() { logMsg("connectionmetadata::getDefaultTransactionIsolation() - MySQL_ConnectionMetaData::getDefaultTransactionIsolation()"); int server_version; try { DatabaseMetaData dbmeta(con->getMetaData()); server_version=(10000 * dbmeta->getDatabaseMajorVersion()) + (100 * dbmeta->getDriverMinorVersion()) + dbmeta->getDriverPatchVersion(); if (server_version < 32336) FAIL("Sorry guys - we do not support MySQL <5.1. This test will not handle this case."); ASSERT_EQUALS(sql::TRANSACTION_REPEATABLE_READ, dbmeta->getDefaultTransactionIsolation()); ASSERT(sql::TRANSACTION_NONE != dbmeta->getDefaultTransactionIsolation()); ASSERT(sql::TRANSACTION_READ_UNCOMMITTED != dbmeta->getDefaultTransactionIsolation()); ASSERT(sql::TRANSACTION_READ_COMMITTED != dbmeta->getDefaultTransactionIsolation()); ASSERT(sql::TRANSACTION_SERIALIZABLE != dbmeta->getDefaultTransactionIsolation()); } catch (sql::SQLException &e) { logErr(e.what()); logErr("SQLState: " + std::string(e.getSQLState())); fail(e.what(), __FILE__, __LINE__); } } void connectionmetadata::getExtraNameCharacters() { logMsg("connectionmetadata::getExtraNameCharacters() - MySQL_ConnectionMetaData::getExtraNameCharacters()"); try { DatabaseMetaData dbmeta(con->getMetaData()); ASSERT_EQUALS("#@", dbmeta->getExtraNameCharacters()); } catch (sql::SQLException &e) { logErr(e.what()); logErr("SQLState: " + std::string(e.getSQLState())); fail(e.what(), __FILE__, __LINE__); } } void connectionmetadata::getIdentifierQuoteString() { logMsg("connectionmetadata::getIdentifierQuoteString() - MySQL_ConnectionMetaData::getIdentifierQuoteString()"); try { DatabaseMetaData dbmeta(con->getMetaData()); stmt.reset(con->createStatement()); try { stmt->execute("SET @@sql_mode = ''"); res.reset(stmt->executeQuery("SELECT @@sql_mode AS _sql_mode")); ASSERT(res->next()); ASSERT_EQUALS("", res->getString("_sql_mode")); } catch (sql::SQLException &) { SKIP("Cannot set SQL_MODE, skipping test"); } ASSERT_EQUALS("`", dbmeta->getIdentifierQuoteString()); stmt->execute("SET @@sql_mode = 'ANSI_QUOTES,ALLOW_INVALID_DATES'"); res.reset(stmt->executeQuery("SELECT @@sql_mode AS _sql_mode")); ASSERT(res->next()); ASSERT_EQUALS("ANSI_QUOTES,ALLOW_INVALID_DATES", res->getString("_sql_mode")); // "`" still works foe identifier quotation with ANSI_QUOTES ASSERT_EQUALS("`", dbmeta->getIdentifierQuoteString()); } catch (sql::SQLException &e) { logErr(e.what()); logErr("SQLState: " + std::string(e.getSQLState())); fail(e.what(), __FILE__, __LINE__); } } void connectionmetadata::getImportedKeys() { logMsg("connectionmetadata::getImportedKeys() - MySQL_ConnectionMetaData::getImportedKeys()"); try { DatabaseMetaData dbmeta(con->getMetaData()); stmt.reset(con->createStatement()); stmt->execute("DROP TABLE IF EXISTS child"); stmt->execute("DROP TABLE IF EXISTS parent"); try { stmt->execute("CREATE TABLE parent(pid INT NOT NULL, PRIMARY KEY(pid)) ENGINE=INNODB;"); stmt->execute("CREATE TABLE child(cid INT NOT NULL, cpid INT, " "INDEX idx_parent_id(cpid), FOREIGN KEY idx_parent_id(cpid) " "REFERENCES parent(pid) ON DELETE CASCADE ON UPDATE CASCADE, PRIMARY KEY(cid)) ENGINE=INNODB;"); } catch (sql::SQLException &) { SKIP("Cannot create necessary FK tables"); } int num_res=0; res.reset(dbmeta->getImportedKeys(con->getCatalog(), con->getSchema(), "parent")); ASSERT(!res->next()); res.reset(dbmeta->getImportedKeys(con->getCatalog(), con->getSchema(), "child")); checkResultSetScrolling(res); ASSERT(res->next()); logMsg("... calling checkForeignKey for child"); checkForeignKey(con, res); ASSERT(!res->next()); stmt->execute("DROP TABLE IF EXISTS child"); stmt->execute("DROP TABLE IF EXISTS parent"); stmt->execute("CREATE TABLE parent(pid1 INT NOT NULL, pid2 INT NOT NULL, PRIMARY KEY(pid1, pid2)) ENGINE=INNODB;"); stmt->execute("CREATE TABLE child(cid INT NOT NULL, cpid2 INT, cpid1 INT, " "INDEX idx_parent_id(cpid1, cpid2), FOREIGN KEY idx_parent_id(cpid1, cpid2) " "REFERENCES parent(pid1, pid2) ON DELETE CASCADE ON UPDATE CASCADE, PRIMARY KEY(cid)) ENGINE=INNODB;"); res.reset(dbmeta->getImportedKeys(con->getCatalog(), con->getSchema(), "child")); num_res=0; while (res->next()) { ++num_res; switch (num_res) { case 1: ASSERT_EQUALS("cpid1", res->getString("FKCOLUMN_NAME")); break; case 2: ASSERT_EQUALS("cpid2", res->getString("FKCOLUMN_NAME")); break; default: FAIL("Expecting only two rows"); break; } } ASSERT_EQUALS(2, num_res); stmt->execute("DROP TABLE IF EXISTS child"); stmt->execute("DROP TABLE IF EXISTS parent"); res.reset(dbmeta->getImportedKeys(con->getCatalog(), con->getSchema(), "child")); ASSERT(!res->next()); } catch (sql::SQLException &e) { logErr(e.what()); logErr("SQLState: " + std::string(e.getSQLState())); fail(e.what(), __FILE__, __LINE__); } } void connectionmetadata::getExportedKeys() { logMsg("connectionmetadata::getExportedKeys() - MySQL_ConnectionMetaData::getExportedKeys()"); try { DatabaseMetaData dbmeta(con->getMetaData()); stmt.reset(con->createStatement()); stmt->execute("DROP TABLE IF EXISTS child"); stmt->execute("DROP TABLE IF EXISTS parent"); try { stmt->execute("CREATE TABLE parent(pid INT NOT NULL, PRIMARY KEY(pid)) ENGINE=INNODB;"); stmt->execute("CREATE TABLE child(cid INT NOT NULL, cpid INT, " "INDEX idx_parent_id(cpid), FOREIGN KEY idx_parent_id(cpid) " "REFERENCES parent(pid) ON DELETE CASCADE ON UPDATE CASCADE, PRIMARY KEY(cid)) ENGINE=INNODB;"); } catch (sql::SQLException &) { SKIP("Cannot create necessary FK tables"); } int num_res=0; res.reset(dbmeta->getExportedKeys(con->getCatalog(), con->getSchema(), "child")); ASSERT(!res->next()); res.reset(dbmeta->getExportedKeys(con->getCatalog(), con->getSchema(), "parent")); ASSERT(res->next()); logMsg("... calling checkForeignKey for parent"); checkForeignKey(con, res); ASSERT(!res->next()); stmt->execute("DROP TABLE IF EXISTS child"); stmt->execute("DROP TABLE IF EXISTS parent"); stmt->execute("CREATE TABLE parent(pid1 INT NOT NULL, pid2 INT NOT NULL, PRIMARY KEY(pid1, pid2)) ENGINE=INNODB;"); stmt->execute("CREATE TABLE child(cid INT NOT NULL, cpid2 INT, cpid1 INT, " "INDEX idx_parent_id(cpid1, cpid2), FOREIGN KEY idx_parent_id(cpid1, cpid2) " "REFERENCES parent(pid1, pid2) ON DELETE CASCADE ON UPDATE CASCADE, PRIMARY KEY(cid)) ENGINE=INNODB;"); res.reset(dbmeta->getExportedKeys(con->getCatalog(), con->getSchema(), "parent")); checkResultSetScrolling(res); num_res=0; while (res->next()) { num_res++; switch (num_res) { case 1: ASSERT_EQUALS("cpid1", res->getString("FKCOLUMN_NAME")); break; case 2: ASSERT_EQUALS("cpid2", res->getString("FKCOLUMN_NAME")); break; default: FAIL("Expecting only two rows"); break; } } ASSERT_EQUALS(2, num_res); stmt->execute("DROP TABLE IF EXISTS child"); stmt->execute("DROP TABLE IF EXISTS parent"); res.reset(dbmeta->getExportedKeys(con->getCatalog(), con->getSchema(), "child")); ASSERT(!res->next()); } catch (sql::MethodNotImplementedException &e) { logMsg(e.what()); SKIP("Server version is too old, MethodNotImplementedException!"); } catch (sql::SQLException &e) { logErr(e.what()); logErr("SQLState: " + std::string(e.getSQLState())); fail(e.what(), __FILE__, __LINE__); } } void connectionmetadata::checkForeignKey(Connection &mycon, ResultSet &myres) { bool got_warning=false; std::stringstream msg; if (mycon->getCatalog() != "" && mycon->getCatalog() != myres->getString(1)) { got_warning=true; msg.str(""); msg << "... WARNING expecting PKTABLE_CAT = '" << mycon->getCatalog() << "'"; msg << " got '" << myres->getString(1) << "'"; logMsg(msg.str()); } ASSERT_EQUALS(myres->getString(1), myres->getString("PKTABLE_CAT")); ASSERT_EQUALS(mycon->getSchema(), myres->getString(2)); ASSERT_EQUALS(myres->getString(2), myres->getString("PKTABLE_SCHEM")); ASSERT_EQUALS("parent", myres->getString(3)); ASSERT_EQUALS(myres->getString(3), myres->getString("PKTABLE_NAME")); ASSERT_EQUALS("pid", myres->getString(4)); ASSERT_EQUALS(myres->getString(4), myres->getString("PKCOLUMN_NAME")); if (mycon->getCatalog() != "" && mycon->getCatalog() != myres->getString(5)) { got_warning=true; msg.str(""); msg << "... WARNING expecting FKTABLE_CAT = '" << mycon->getCatalog() << "'"; msg << " got '" << myres->getString(1) << "'"; logMsg(msg.str()); } ASSERT_EQUALS(myres->getString(5), myres->getString("FKTABLE_CAT")); ASSERT_EQUALS(mycon->getSchema(), myres->getString(6)); ASSERT_EQUALS(myres->getString(6), myres->getString("FKTABLE_SCHEM")); ASSERT_EQUALS("child", myres->getString(7)); ASSERT_EQUALS(myres->getString(7), myres->getString("FKTABLE_NAME")); ASSERT_EQUALS("cpid", myres->getString(8)); ASSERT_EQUALS(myres->getString(8), myres->getString("FKCOLUMN_NAME")); ASSERT_EQUALS(1, myres->getInt(9)); ASSERT_EQUALS(myres->getInt(9), myres->getInt("KEY_SEQ")); ASSERT_EQUALS((int64_t) sql::DatabaseMetaData::importedKeyCascade, myres->getInt64(10)); ASSERT_EQUALS(myres->getInt64(10), myres->getInt64("UPDATE_RULE")); ASSERT(sql::DatabaseMetaData::importedKeyNoAction != myres->getInt64(10)); ASSERT(sql::DatabaseMetaData::importedKeySetNull != myres->getInt64(10)); ASSERT(sql::DatabaseMetaData::importedKeySetDefault != myres->getInt64(10)); ASSERT(sql::DatabaseMetaData::importedKeyRestrict != myres->getInt64(10)); ASSERT_EQUALS((int64_t) sql::DatabaseMetaData::importedKeyCascade, myres->getInt64(11)); ASSERT_EQUALS(myres->getInt64(11), myres->getInt64("DELETE_RULE")); ASSERT(sql::DatabaseMetaData::importedKeyNoAction != myres->getInt64(11)); ASSERT(sql::DatabaseMetaData::importedKeySetNull != myres->getInt64(11)); ASSERT(sql::DatabaseMetaData::importedKeySetDefault != myres->getInt64(11)); ASSERT(sql::DatabaseMetaData::importedKeyRestrict != myres->getInt64(11)); // InnoDB should give the FK a name ASSERT("" != myres->getString("FK_NAME")); ASSERT_EQUALS(myres->getString(12), myres->getString("FK_NAME")); // We would have PK_NAME only if I_S. By default "SHOW CREATE TABLE" result is parsed, and it doesn't have this info, and thus NULL is returned if (!myres->isNull(13)) { ASSERT_EQUALS("PRIMARY", myres->getString("PK_NAME")); } ASSERT_EQUALS(myres->getString(13), myres->getString("PK_NAME")); ASSERT_EQUALS((int64_t) sql::DatabaseMetaData::importedKeyNotDeferrable, myres->getInt64(14)); ASSERT(sql::DatabaseMetaData::importedKeyInitiallyDeferred != myres->getInt64(10)); ASSERT(sql::DatabaseMetaData::importedKeyInitiallyImmediate != myres->getInt64(10)); if (got_warning) { TODO("See --verbose warnings!"); FAIL("TODO - See --verbose warnings!"); } } void connectionmetadata::getIndexInfo() { logMsg("connectionmetadata::getIndexInfo() - MySQL_ConnectionMetaData::getIndexInfo()"); std::stringstream msg; bool got_warning=false; bool got_todo_warning=false; try { DatabaseMetaData dbmeta(con->getMetaData()); stmt.reset(con->createStatement()); stmt->execute("DROP TABLE IF EXISTS test"); stmt->execute("CREATE TABLE test(col1 INT NOT NULL, col2 INT NOT NULL, col3 INT NOT NULL, col4 INT, col5 INT, PRIMARY KEY(col1))"); stmt->execute("INSERT INTO test(col1, col2, col3) VALUES (1, 1, 1)"); res.reset(dbmeta->getIndexInfo(con->getCatalog(), con->getSchema(), "test", false, false)); ASSERT(res->next()); if (con->getCatalog() != "" && res->getString(1) != "" && con->getCatalog() != res->getString(1)) { got_todo_warning=true; msg.str(""); msg << "...\t\tWARNING expecting TABLE_CAT = '" << con->getCatalog() << "'"; msg << " got '" << res->getString(1) << "'"; logMsg(msg.str()); } ASSERT_EQUALS(res->getString(1), res->getString("TABLE_CAT")); ASSERT_EQUALS(con->getSchema(), res->getString(2)); ASSERT_EQUALS(res->getString(2), res->getString("TABLE_SCHEM")); ASSERT_EQUALS("test", res->getString(3)); ASSERT_EQUALS(res->getString(3), res->getString("TABLE_NAME")); ASSERT_EQUALS(false, res->getBoolean("NON_UNIQUE")); ASSERT_EQUALS(res->getBoolean(4), res->getBoolean("NON_UNIQUE")); ASSERT_EQUALS(res->getString("TABLE_SCHEM"), res->getString(5)); ASSERT_EQUALS(res->getString(5), res->getString("INDEX_QUALIFIER")); ASSERT_EQUALS("PRIMARY", res->getString(6)); ASSERT_EQUALS(res->getString(6), res->getString("INDEX_NAME")); ASSERT_EQUALS(sql::DatabaseMetaData::tableIndexOther, res->getInt(7)); ASSERT_EQUALS(res->getInt(7), res->getInt("TYPE")); ASSERT(sql::DatabaseMetaData::tableIndexStatistic != res->getInt(7)); ASSERT(sql::DatabaseMetaData::tableIndexClustered != res->getInt(7)); ASSERT(sql::DatabaseMetaData::tableIndexHashed != res->getInt(7)); ASSERT_EQUALS(1, res->getInt(8)); ASSERT_EQUALS(res->getInt(8), res->getInt("ORDINAL_POSITION")); ASSERT_EQUALS("col1", res->getString(9)); ASSERT_EQUALS(res->getString(9), res->getString("COLUMN_NAME")); ASSERT_EQUALS("A", res->getString(10)); ASSERT_EQUALS(res->getString(10), res->getString("ASC_OR_DESC")); if (res->getInt(11) != 1) { got_warning=true; msg.str(""); msg << "... \t\tWARNING: There is one row in the table and PK should have a "; msg << "cardinality of 1, got " << res->getInt(11); logMsg(msg.str()); } ASSERT_EQUALS(res->getInt(11), res->getInt("CARDINALITY")); ASSERT_EQUALS(0, res->getInt(12)); ASSERT_EQUALS(res->getInt(12), res->getInt("PAGES")); ASSERT_EQUALS("", res->getString(13)); ASSERT_EQUALS(res->getString(13), res->getString("FILTER_CONDITION")); ASSERT(!res->next()); // New unique index stmt->execute("CREATE UNIQUE INDEX an_idx_col3 ON test(col3 ASC)"); res.reset(dbmeta->getIndexInfo(con->getCatalog(), con->getSchema(), "test", false, false)); checkResultSetScrolling(res); ASSERT(res->next()); ASSERT_EQUALS("an_idx_col3", res->getString("INDEX_NAME")); ASSERT_EQUALS(false, res->getBoolean("NON_UNIQUE")); ASSERT(res->next()); ASSERT_EQUALS("PRIMARY", res->getString("INDEX_NAME")); ASSERT_EQUALS(false, res->getBoolean("NON_UNIQUE")); ASSERT(!res->next()); // Now we have three indexes, unique PK, unique an_idx_col3 and non-unique idx_col2 stmt->execute("CREATE INDEX idx_col2 ON test(col2 ASC)"); // Show only the unique ones... res.reset(dbmeta->getIndexInfo(con->getCatalog(), con->getSchema(), "test", true, false)); ASSERT(res->next()); ASSERT_EQUALS("an_idx_col3", res->getString("INDEX_NAME")); ASSERT_EQUALS(false, res->getBoolean("NON_UNIQUE")); ASSERT(res->next()); ASSERT_EQUALS("PRIMARY", res->getString("INDEX_NAME")); ASSERT_EQUALS(false, res->getBoolean("NON_UNIQUE")); if (res->next()) { got_warning=true; msg.str(""); msg << "... \t\tWARNING: requesting only unique keys but got also non-unique key "; msg << "'" << res->getString("INDEX_NAME") << "', UNIQUE = " << std::boolalpha; msg << !res->getBoolean("NON_UNIQUE"); logMsg(msg.str()); } ASSERT(!res->next()); // Another index. Should appear in the sort order prior to the idx_col2 one... // Sort order is: NON_UNIQUE, TYPE, INDEX_NAME stmt->execute("CREATE INDEX an_a_idx_col4 ON test(col4 DESC)"); res.reset(dbmeta->getIndexInfo(con->getCatalog(), con->getSchema(), "test", false, false)); ASSERT(res->next()); ASSERT_EQUALS(sql::DatabaseMetaData::tableIndexOther, res->getInt(7)); ASSERT_EQUALS("an_idx_col3", res->getString("INDEX_NAME")); ASSERT_EQUALS(false, res->getBoolean("NON_UNIQUE")); ASSERT(res->next()); ASSERT_EQUALS("PRIMARY", res->getString("INDEX_NAME")); ASSERT_EQUALS(false, res->getBoolean("NON_UNIQUE")); ASSERT_EQUALS(sql::DatabaseMetaData::tableIndexOther, res->getInt(7)); ASSERT(res->next()); ASSERT_EQUALS("an_a_idx_col4", res->getString("INDEX_NAME")); ASSERT_EQUALS(true, res->getBoolean("NON_UNIQUE")); ASSERT_EQUALS(sql::DatabaseMetaData::tableIndexOther, res->getInt(7)); ASSERT(res->next()); ASSERT_EQUALS("idx_col2", res->getString("INDEX_NAME")); ASSERT_EQUALS(true, res->getBoolean("NON_UNIQUE")); ASSERT_EQUALS(sql::DatabaseMetaData::tableIndexOther, res->getInt(7)); ASSERT(!res->next()); //Was wrong on previous versions.... if (getServerVersion(con) >= 800000) { stmt->execute("DROP TABLE IF EXISTS test"); stmt->execute("CREATE TABLE test(col1 INT NOT NULL, col2 INT NOT NULL, col3 INT NOT NULL, col4 INT, col5 INT, PRIMARY KEY(col1))"); stmt->execute("CREATE INDEX idx_col4_col5 ON test(col5 DESC, col4 ASC)"); res.reset(dbmeta->getIndexInfo(con->getCatalog(), con->getSchema(), "test", false, false)); ASSERT(res->next()); ASSERT_EQUALS("PRIMARY", res->getString("INDEX_NAME")); ASSERT_EQUALS(false, res->getBoolean("NON_UNIQUE")); ASSERT(res->next()); ASSERT_EQUALS("idx_col4_col5", res->getString("INDEX_NAME")); ASSERT_EQUALS((("MariaDB" != dbmeta->getDatabaseProductName() && getServerVersion(con) > 800000) || getServerVersion(con) > 1008000) ? "D" : "A", res->getString("ASC_OR_DESC")); ASSERT_EQUALS("col5", res->getString("COLUMN_NAME")); ASSERT_EQUALS(true, res->getBoolean("NON_UNIQUE")); ASSERT(res->next()); ASSERT_EQUALS("idx_col4_col5", res->getString("INDEX_NAME")); ASSERT_EQUALS("A", res->getString("ASC_OR_DESC")); ASSERT_EQUALS("col4", res->getString("COLUMN_NAME")); ASSERT_EQUALS(true, res->getBoolean("NON_UNIQUE")); ASSERT(!res->next()); } try { stmt->execute("DROP TABLE IF EXISTS test"); stmt->execute("CREATE TABLE test(col1 INT NOT NULL, col2 INT NOT NULL, PRIMARY KEY(col1)) ENGINE=MEMORY"); stmt->execute("CREATE INDEX idx_col2 USING HASH ON test(col2 DESC)"); res.reset(dbmeta->getIndexInfo(con->getCatalog(), con->getSchema(), "test", false, false)); ASSERT(res->next()); ASSERT_EQUALS("PRIMARY", res->getString("INDEX_NAME")); ASSERT_EQUALS(false, res->getBoolean("NON_UNIQUE")); ASSERT(res->next()); ASSERT_EQUALS("idx_col2", res->getString("INDEX_NAME")); // There is no order when using HASH ASSERT_EQUALS("", res->getString("ASC_OR_DESC")); ASSERT_EQUALS("col2", res->getString("COLUMN_NAME")); ASSERT_EQUALS(sql::DatabaseMetaData::tableIndexOther, res->getInt("TYPE")); ASSERT_EQUALS(true, res->getBoolean("NON_UNIQUE")); ASSERT(!res->next()); } catch (sql::SQLException &) { } stmt->execute("DROP TABLE IF EXISTS test"); res.reset(dbmeta->getIndexInfo(con->getCatalog(), con->getSchema(), "test", false, false)); ASSERT(!res->next()); if (got_warning) { FAIL("See above warnings!"); } } catch (sql::SQLException &e) { logErr(e.what()); logErr("SQLState: " + std::string(e.getSQLState())); fail(e.what(), __FILE__, __LINE__); } if (got_todo_warning) { TODO("See --verbose warnings!"); FAIL("TODO - see --verbose warnings"); } } void connectionmetadata::getLimitsAndStuff() { logMsg("connectionmetadata::getLimitsAndStuff() - MySQL_ConnectionMetaData::getLimitsAndStuff()"); std::string funcs("ASCII,BIN,BIT_LENGTH,CAST,CHAR,CHARACTER_LENGTH,CHAR_LENGTH,CONCAT,"\ "CONCAT_WS,CONV,CONVERT,ELT,EXPORT_SET,EXTRACTVALUE,FIELD,FIND_IN_SET,FORMAT,FROM_BASE64,HEX,INSERT,"\ "INSTR,LCASE,LEFT,LENGTH,LIKE,LOAD_FILE,LOCATE,LOWER,LPAD,"\ "LTRIM,MAKE_SET,MATCH AGAINST,MID,NOT LIKE,NOT REGEXP,OCT,OCTET_LENGTH,ORD,POSITION,"\ "QUOTE,REPEAT,REPLACE,REVERSE,RIGHT,RPAD,RTRIM,SOUNDEX,SOUNDS LIKE,"\ "SPACE,STRCMP,SUBSTR,SUBSTRING,SUBSTRING_INDEX,TO_BASE64,TRIM,UCASE,UNHEX,UPDATEXML,UPPER,WEIGHT_STRING"); std::string sys_funcs("DATABASE,USER,SYSTEM_USER,SESSION_USER,PASSWORD,ENCRYPT,LAST_INSERT_ID,VERSION"); try { DatabaseMetaData dbmeta(con->getMetaData()); try { ASSERT_EQUALS(3, dbmeta->getCDBCMajorVersion()); } catch (sql::SQLFeatureNotSupportedException& e) { logMsg(e.getMessage()); } try { ASSERT_EQUALS(0, dbmeta->getCDBCMinorVersion()); } catch (sql::SQLFeatureNotSupportedException & e) { logMsg(e.getMessage()); } ASSERT_EQUALS(16777208, dbmeta->getMaxBinaryLiteralLength()); ASSERT_EQUALS(0, dbmeta->getMaxCatalogNameLength()); ASSERT_EQUALS(16777208, dbmeta->getMaxCharLiteralLength()); ASSERT_EQUALS(64, dbmeta->getMaxColumnNameLength()); ASSERT_EQUALS(64, dbmeta->getMaxColumnsInGroupBy()); ASSERT_EQUALS(16, dbmeta->getMaxColumnsInIndex()); ASSERT_EQUALS(64, dbmeta->getMaxColumnsInOrderBy()); ASSERT_EQUALS(256, dbmeta->getMaxColumnsInSelect()); ASSERT_EQUALS(0, dbmeta->getMaxColumnsInTable()); stmt.reset(con->createStatement()); res.reset(stmt->executeQuery("SELECT @@max_connections AS _max")); ASSERT(res->next()); ASSERT_EQUALS(dbmeta->getMaxConnections(), res->getInt("_max")); ASSERT_EQUALS(64, dbmeta->getMaxCursorNameLength()); ASSERT_EQUALS(256, dbmeta->getMaxIndexLength()); ASSERT_EQUALS(64, dbmeta->getMaxProcedureNameLength()); ASSERT_EQUALS(0, dbmeta->getMaxRowSize()); ASSERT_EQUALS(64, dbmeta->getMaxSchemaNameLength()); stmt.reset(con->createStatement()); res.reset(stmt->executeQuery("SHOW VARIABLES LIKE 'max_allowed_packet'")); ASSERT(res->next()); ASSERT_EQUALS(/*res->getInt(2) - 4*/0, dbmeta->getMaxStatementLength()); //TODO ASSERT_EQUALS(0, dbmeta->getMaxStatements()); ASSERT_EQUALS(64, dbmeta->getMaxTableNameLength()); ASSERT_EQUALS(256, dbmeta->getMaxTablesInSelect()); ASSERT_EQUALS(16, dbmeta->getMaxUserNameLength()); ASSERT_EQUALS("ABS,ACOS,ASIN,ATAN,ATAN2,BIT_COUNT,CEIL,CEILING,CONV,COS," "COT,CRC32,DEGREES,DIV,EXP,FLOOR,GREATEST,LEAST,LN,LOG,LOG10,LOG2,MAX,MIN,MOD,OCT,PI,POW," "POWER,RADIANS,RAND,ROUND,SIGN,SIN,SQRT,TAN,TRUNCATE" , dbmeta->getNumericFunctions()); ASSERT_EQUALS(true, dbmeta->allProceduresAreCallable()); ASSERT_EQUALS(true, dbmeta->allTablesAreSelectable()); ASSERT_EQUALS(true, dbmeta->dataDefinitionCausesTransactionCommit()); ASSERT_EQUALS(false, dbmeta->dataDefinitionIgnoredInTransactions()); ASSERT_EQUALS(false, dbmeta->deletesAreDetected(-1)); ASSERT_EQUALS(false, dbmeta->deletesAreDetected(0)); ASSERT_EQUALS(false, dbmeta->deletesAreDetected(1)); ASSERT_EQUALS(true, dbmeta->dataDefinitionCausesTransactionCommit()); ASSERT_EQUALS(false, dbmeta->doesMaxRowSizeIncludeBlobs()); ASSERT_EQUALS(sql::DatabaseMetaData::sqlStateSQL99, dbmeta->getSQLStateType()); ASSERT(sql::DatabaseMetaData::sqlStateXOpen != dbmeta->getSQLStateType()); ASSERT_EQUALS(funcs, dbmeta->getStringFunctions()); ASSERT_EQUALS(sys_funcs, dbmeta->getSystemFunctions()); ASSERT_EQUALS(false, dbmeta->insertsAreDetected(-1)); ASSERT_EQUALS(false, dbmeta->insertsAreDetected(0)); ASSERT_EQUALS(false, dbmeta->insertsAreDetected(1)); ASSERT_EQUALS(true, dbmeta->isCatalogAtStart()); ASSERT_EQUALS(false, dbmeta->isReadOnly()); ASSERT_EQUALS(true, dbmeta->nullPlusNonNullIsNull()); ASSERT_EQUALS(false, dbmeta->nullsAreSortedAtEnd()); ASSERT_EQUALS(true, dbmeta->nullsAreSortedAtStart()); // KLUDGE - the code takes care of some exotic MySQL 4.x, however, we don't support 4.x ASSERT_EQUALS(false, dbmeta->nullsAreSortedHigh()); ASSERT_EQUALS(!dbmeta->nullsAreSortedLow(), dbmeta->nullsAreSortedHigh()); ASSERT_EQUALS(false, dbmeta->othersDeletesAreVisible(-1)); ASSERT_EQUALS(false, dbmeta->othersDeletesAreVisible(0)); ASSERT_EQUALS(false, dbmeta->othersDeletesAreVisible(1)); ASSERT_EQUALS(false, dbmeta->othersInsertsAreVisible(-1)); ASSERT_EQUALS(false, dbmeta->othersInsertsAreVisible(0)); ASSERT_EQUALS(false, dbmeta->othersInsertsAreVisible(1)); ASSERT_EQUALS(false, dbmeta->othersUpdatesAreVisible(-1)); ASSERT_EQUALS(false, dbmeta->othersUpdatesAreVisible(0)); ASSERT_EQUALS(false, dbmeta->othersUpdatesAreVisible(1)); ASSERT_EQUALS(false, dbmeta->ownDeletesAreVisible(-1)); ASSERT_EQUALS(false, dbmeta->ownDeletesAreVisible(0)); ASSERT_EQUALS(false, dbmeta->ownDeletesAreVisible(1)); ASSERT_EQUALS(false, dbmeta->ownInsertsAreVisible(-1)); ASSERT_EQUALS(false, dbmeta->ownInsertsAreVisible(0)); ASSERT_EQUALS(false, dbmeta->ownInsertsAreVisible(1)); ASSERT_EQUALS(false, dbmeta->ownUpdatesAreVisible(-1)); ASSERT_EQUALS(false, dbmeta->ownUpdatesAreVisible(0)); ASSERT_EQUALS(false, dbmeta->ownUpdatesAreVisible(1)); ASSERT_EQUALS(false, dbmeta->storesUpperCaseIdentifiers()); ASSERT_EQUALS(false, dbmeta->storesUpperCaseQuotedIdentifiers()); ASSERT_EQUALS(true, dbmeta->supportsAlterTableWithAddColumn()); ASSERT_EQUALS(true, dbmeta->supportsAlterTableWithDropColumn()); ASSERT_EQUALS(true, dbmeta->supportsANSI92EntryLevelSQL()); ASSERT_EQUALS(true, dbmeta->supportsANSI92FullSQL()); ASSERT_EQUALS(true, dbmeta->supportsANSI92IntermediateSQL()); ASSERT_EQUALS(true, dbmeta->supportsBatchUpdates()); ASSERT_EQUALS(false, dbmeta->supportsCatalogsInDataManipulation()); ASSERT_EQUALS(false, dbmeta->supportsCatalogsInIndexDefinitions()); ASSERT_EQUALS(false, dbmeta->supportsCatalogsInPrivilegeDefinitions()); ASSERT_EQUALS(false, dbmeta->supportsCatalogsInProcedureCalls()); ASSERT_EQUALS(false, dbmeta->supportsCatalogsInTableDefinitions()); ASSERT_EQUALS(true, dbmeta->supportsColumnAliasing()); ASSERT_EQUALS(false, dbmeta->supportsConvert()); ASSERT_EQUALS(true, dbmeta->supportsCoreSQLGrammar()); /* We support MySQL 5.1+ . It must be true */ ASSERT_EQUALS(true, dbmeta->supportsCorrelatedSubqueries()); ASSERT_EQUALS(true, dbmeta->supportsDataDefinitionAndDataManipulationTransactions()); ASSERT_EQUALS(false, dbmeta->supportsDataManipulationTransactionsOnly()); ASSERT_EQUALS(true, dbmeta->supportsDifferentTableCorrelationNames()); ASSERT_EQUALS(true, dbmeta->supportsExpressionsInOrderBy()); ASSERT_EQUALS(false, dbmeta->supportsExtendedSQLGrammar()); ASSERT_EQUALS(false, dbmeta->supportsFullOuterJoins()); ASSERT_EQUALS(true, dbmeta->supportsGetGeneratedKeys()); ASSERT_EQUALS(true, dbmeta->supportsGroupBy()); ASSERT_EQUALS(true, dbmeta->supportsGroupByBeyondSelect()); ASSERT_EQUALS(true, dbmeta->supportsGroupByUnrelated()); ASSERT_EQUALS(true, dbmeta->supportsLikeEscapeClause()); ASSERT_EQUALS(true, dbmeta->supportsLimitedOuterJoins()); ASSERT_EQUALS(true, dbmeta->supportsMinimumSQLGrammar()); ASSERT_EQUALS(false, dbmeta->supportsMultipleOpenResults()); ASSERT_EQUALS(false, dbmeta->supportsMultipleResultSets()); ASSERT_EQUALS(true, dbmeta->supportsMultipleTransactions()); ASSERT_EQUALS(false, dbmeta->supportsNamedParameters()); ASSERT_EQUALS(true, dbmeta->supportsNonNullableColumns()); ASSERT_EQUALS(true, dbmeta->supportsOpenCursorsAcrossCommit()); ASSERT_EQUALS(true, dbmeta->supportsOpenCursorsAcrossRollback()); ASSERT_EQUALS(true, dbmeta->supportsOpenStatementsAcrossCommit()); ASSERT_EQUALS(true, dbmeta->supportsOpenStatementsAcrossRollback()); ASSERT_EQUALS(true, dbmeta->supportsOrderByUnrelated()); ASSERT_EQUALS(true, dbmeta->supportsOuterJoins()); ASSERT_EQUALS(false, dbmeta->supportsPositionedDelete()); ASSERT_EQUALS(false, dbmeta->supportsPositionedUpdate()); ASSERT_EQUALS(true, dbmeta->supportsResultSetHoldability(sql::ResultSet::HOLD_CURSORS_OVER_COMMIT)); ASSERT_EQUALS(false, dbmeta->supportsResultSetHoldability(sql::ResultSet::CLOSE_CURSORS_AT_COMMIT)); ASSERT_EQUALS(true, dbmeta->supportsResultSetType(sql::ResultSet::TYPE_SCROLL_INSENSITIVE)); ASSERT_EQUALS(false, dbmeta->supportsResultSetType(sql::ResultSet::TYPE_SCROLL_SENSITIVE)); ASSERT_EQUALS(true, dbmeta->supportsResultSetType(sql::ResultSet::TYPE_FORWARD_ONLY)); /* We support MySQL 5.1+ . It must be true */ ASSERT_EQUALS(true, dbmeta->supportsSavepoints()); ASSERT_EQUALS(true, dbmeta->supportsSchemasInDataManipulation()); ASSERT_EQUALS(true, dbmeta->supportsSchemasInDataManipulation()); ASSERT_EQUALS(true, dbmeta->supportsSchemasInIndexDefinitions()); ASSERT_EQUALS(true, dbmeta->supportsSchemasInPrivilegeDefinitions()); ASSERT_EQUALS(true, dbmeta->supportsSchemasInProcedureCalls()); ASSERT_EQUALS(true, dbmeta->supportsSchemasInTableDefinitions()); /* We support MySQL 5.1+ . It must be true */ ASSERT_EQUALS(true, dbmeta->supportsSelectForUpdate()); ASSERT_EQUALS(false, dbmeta->supportsStatementPooling()); ASSERT_EQUALS(true, dbmeta->supportsStoredProcedures()); ASSERT_EQUALS(true, dbmeta->supportsSubqueriesInComparisons()); ASSERT_EQUALS(true, dbmeta->supportsSubqueriesInExists()); ASSERT_EQUALS(true, dbmeta->supportsSubqueriesInIns()); ASSERT_EQUALS(true, dbmeta->supportsSubqueriesInQuantifieds()); ASSERT_EQUALS(true, dbmeta->supportsTableCorrelationNames()); /* We support MySQL 5.1+ . It must be true */ ASSERT_EQUALS(true, dbmeta->supportsTransactionIsolationLevel(sql::TRANSACTION_NONE)); ASSERT_EQUALS(true, dbmeta->supportsTransactionIsolationLevel(sql::TRANSACTION_READ_COMMITTED)); ASSERT_EQUALS(true, dbmeta->supportsTransactionIsolationLevel(sql::TRANSACTION_READ_UNCOMMITTED)); ASSERT_EQUALS(true, dbmeta->supportsTransactionIsolationLevel(sql::TRANSACTION_REPEATABLE_READ)); ASSERT_EQUALS(true, dbmeta->supportsTransactionIsolationLevel(sql::TRANSACTION_SERIALIZABLE)); ASSERT_EQUALS(true, dbmeta->supportsTransactions()); ASSERT_EQUALS(true, dbmeta->supportsTypeConversion()); /* We support MySQL 5.1+ . It must be true */ ASSERT_EQUALS(true, dbmeta->supportsUnion()); ASSERT_EQUALS(true, dbmeta->supportsUnionAll()); ASSERT_EQUALS(false, dbmeta->updatesAreDetected(sql::ResultSet::TYPE_FORWARD_ONLY)); ASSERT_EQUALS(false, dbmeta->updatesAreDetected(sql::ResultSet::TYPE_SCROLL_INSENSITIVE)); ASSERT_EQUALS(false, dbmeta->updatesAreDetected(sql::ResultSet::TYPE_SCROLL_SENSITIVE)); ASSERT_EQUALS(false, dbmeta->usesLocalFilePerTable()); ASSERT_EQUALS(false, dbmeta->usesLocalFiles()); } catch (sql::SQLException &e) { logErr(e.what()); logErr("SQLState: " + std::string(e.getSQLState())); fail(e.what(), __FILE__, __LINE__); } } void connectionmetadata::getPrimaryKeys() { logMsg("connectionmetadata::getPrimaryKeys() - MySQL_ConnectionMetaData::getPrimaryKeys"); int row_num; std::string catalog; std::string schema(""); std::stringstream msg; bool got_warning=false; try { DatabaseMetaData dbmeta(con->getMetaData()); stmt.reset(con->createStatement()); stmt->execute("DROP TABLE IF EXISTS test"); stmt->execute("CREATE TABLE test(col2 INT NOT NULL, col1 INT NOT NULL, PRIMARY KEY(col2, col1))"); // The descriptions are ordered by the column COLUMN_NAME, will they? res.reset(dbmeta->getPrimaryKeys(con->getCatalog(), con->getSchema(), "test")); checkResultSetScrolling(res); row_num=0; while (res->next()) { row_num++; if (con->getCatalog() != "" && res->getString("TABLE_CAT") != "" && con->getCatalog() != res->getString("TABLE_CAT")) { got_warning=true; msg.str(""); msg << "...\t\tWARNING expecting TABLE_CAT = '" << con->getCatalog() << "'"; msg << " got '" << res->getString("TABLE_CAT") << "'"; logMsg(msg.str()); } ASSERT_EQUALS(con->getSchema(), res->getString("TABLE_SCHEM")); ASSERT_EQUALS("test", res->getString("TABLE_NAME")); switch (row_num) { case 1: // getPrimaryKeys results ordered by COLUMN_NAME only. Thus col1, and then col2 ASSERT_EQUALS("col1", res->getString("COLUMN_NAME")); ASSERT_EQUALS(2, res->getInt("KEY_SEQ")); break; case 2: ASSERT_EQUALS("col2", res->getString("COLUMN_NAME")); ASSERT_EQUALS(1, res->getInt("KEY_SEQ")); break; default: FAIL("Too many PK columns reported"); break; } ASSERT_EQUALS("PRIMARY", res->getString("PK_NAME")); } ASSERT_EQUALS(2, row_num); // catalog - a string ... "" retrieves pk from tables wo catalog, NULL = catalog should not be used to narrow the search res.reset(dbmeta->getPrimaryKeys(catalog, con->getSchema(), "test")); ASSERT_EQUALS(true, res->next()); ASSERT_EQUALS("test", res->getString("TABLE_NAME")); res.reset(dbmeta->getPrimaryKeys(catalog, schema, "test")); ASSERT(!res->next()); stmt->execute("DROP TABLE IF EXISTS test"); res.reset(dbmeta->getPrimaryKeys(con->getCatalog(), con->getSchema(), "test")); ASSERT_EQUALS(false, res->next()); } catch (sql::SQLException &e) { logErr(e.what()); logErr("SQLState: " + std::string(e.getSQLState())); fail(e.what(), __FILE__, __LINE__); } if (got_warning) { TODO("See --verbose warnings!"); FAIL("TODO - see --verbose warnings!"); } } void connectionmetadata::getProcedures() { logMsg("connectionmetadata::getProcedures() - MySQL_ConnectionMetaData::getProcedures"); bool got_warning=false; std::stringstream msg; bool autoCommit = con->getAutoCommit(); try { DatabaseMetaData dbmeta(con->getMetaData()); stmt.reset(con->createStatement()); try { stmt->execute("DROP PROCEDURE IF EXISTS p1"); stmt->execute("CREATE PROCEDURE p1(OUT param1 INT) BEGIN SELECT 1 INTO param1; END"); } catch (sql::SQLException &) { SKIP("Cannot create procedure"); } if (isSkySqlHA()) { con->setAutoCommit(false); } // Verify if the procedure creally has been created... stmt->execute("SET @myvar = -1"); stmt->execute("CALL p1(@myvar)"); res.reset(stmt->executeQuery("SELECT @myvar AS _myvar")); ASSERT(res->next()); ASSERT_EQUALS(1, res->getInt("_myvar")); logMsg("...who is the bad guy?"); res.reset(dbmeta->getProcedures(con->getCatalog(), con->getSchema(), "p1")); checkResultSetScrolling(res); logMsg("...is it you, getProcedures()?"); ASSERT(res->next()); if (con->getCatalog() != "" && res->getString("PROCEDURE_CAT") != "" && con->getCatalog() != res->getString("PROCEDURE_CAT")) { got_warning=true; msg.str(""); msg << "\t\tWARNING expecting PROCEDURE_CAT = '" << con->getCatalog() << "'"; msg << " got '" << res->getString("PROCEDURE_CAT") << "'"; logMsg(msg.str()); } ASSERT_EQUALS(res->getString(1), res->getString("PROCEDURE_CAT")); ASSERT_EQUALS(con->getSchema(), res->getString("PROCEDURE_SCHEM")); ASSERT_EQUALS(res->getString(2), res->getString("PROCEDURE_SCHEM")); ASSERT_EQUALS("p1", res->getString(3)); ASSERT_EQUALS(res->getString(3), res->getString("PROCEDURE_NAME")); ASSERT_EQUALS("", res->getString(4)); ASSERT_EQUALS("", res->getString(5)); ASSERT_EQUALS("", res->getString(6)); ASSERT_EQUALS("", res->getString(7)); ASSERT_EQUALS(res->getString("REMARKS"), res->getString(7)); ASSERT_EQUALS(sql::DatabaseMetaData::procedureNoResult, res->getInt("PROCEDURE_TYPE")); ASSERT(sql::DatabaseMetaData::procedureReturnsResult != res->getInt(8)); ASSERT(sql::DatabaseMetaData::procedureResultUnknown != res->getInt(8)); ASSERT(!res->next()); if (isSkySqlHA()) { con->commit(); con->setAutoCommit(autoCommit); } } catch (sql::SQLException &e) { if (isSkySqlHA()) { con->rollback(); con->setAutoCommit(autoCommit); } logErr(e.what()); logErr("SQLState: " + std::string(e.getSQLState())); fail(e.what(), __FILE__, __LINE__); } if (got_warning) { TODO("See --verbose warnings!"); FAIL("TODO - see --verbose warnings!"); } } void connectionmetadata::getProcedureColumns() { logMsg("connectionmetadata::getProcedureColumns() - MySQL_ConnectionMetaData::getProcedureColumns()"); int server_version; SKIP("method not implemented"); try { DatabaseMetaData dbmeta(con->getMetaData()); stmt.reset(con->createStatement()); try { stmt->execute("DROP PROCEDURE IF EXISTS p1"); stmt->execute("CREATE PROCEDURE p1(OUT param1 INT) BEGIN SELECT 1 INTO param1; END"); } catch (sql::SQLException &) { SKIP("Cannot create procedure"); } // Verify if the procedure creally has been created... stmt->execute("SET @myvar = -1"); stmt->execute("CALL p1(@myvar)"); res.reset(stmt->executeQuery("SELECT @myvar AS _myvar")); checkResultSetScrolling(res); ASSERT(res->next()); ASSERT_EQUALS(1, res->getInt("_myvar")); res.reset(dbmeta->getProcedureColumns(con->getCatalog(), con->getSchema(), "p1", "%")); server_version=(10000 * dbmeta->getDatabaseMajorVersion()) + (100 * dbmeta->getDriverMinorVersion()) + dbmeta->getDriverPatchVersion(); if (server_version < 50206) ASSERT(!res->next()); else { ASSERT(res->next()); FAIL("Theres a new I_S table PARAMETERS. The test should use it"); } } catch (sql::SQLException &e) { logErr(e.what()); logErr("SQLState: " + std::string(e.getSQLState())); fail(e.what(), __FILE__, __LINE__); } } void connectionmetadata::getCatalogs() { logMsg("connectionmetadata::getCatalogs() - MySQL_ConnectionMetaData::getCatalogs()"); try { DatabaseMetaData dbmeta(con->getMetaData()); res.reset(dbmeta->getCatalogs()); //ASSERT(res->next()); ASSERT(!res->next()); ResultSetMetaData resmeta(res->getMetaData()); /* http://java.sun.com/j2se/1.4.2/docs/api/java/sql/DatabaseMetaData.html#getCatalogs() */ ASSERT_EQUALS((unsigned int) 1, resmeta->getColumnCount()); ASSERT_EQUALS("TABLE_CAT", resmeta->getColumnLabel(1)); } catch (sql::SQLException &e) { logErr(e.what()); logErr("SQLState: " + std::string(e.getSQLState())); fail(e.what(), __FILE__, __LINE__); } } void connectionmetadata::getCatalogSeparator() { logMsg("connectionmetadata::getCatalogSeparator() - MySQL_ConnectionMetaData::getCatalogSeparator()"); try { DatabaseMetaData dbmeta(con->getMetaData()); ASSERT_EQUALS("", dbmeta->getCatalogSeparator()); } catch (sql::SQLException &e) { logErr(e.what()); logErr("SQLState: " + std::string(e.getSQLState())); fail(e.what(), __FILE__, __LINE__); } } void connectionmetadata::getCatalogTerm() { logMsg("connectionmetadata::getCatalogTerm() - MySQL_ConnectionMetaData::getCatalogTerm()"); try { DatabaseMetaData dbmeta(con->getMetaData()); ASSERT_EQUALS("", dbmeta->getCatalogTerm()); } catch (sql::SQLException &e) { logErr(e.what()); logErr("SQLState: " + std::string(e.getSQLState())); fail(e.what(), __FILE__, __LINE__); } } void connectionmetadata::getCrossReference() { logMsg("connectionmetadata::getCrossReference() - MySQL_ConnectionMetaData::getCrossReference()"); try { DatabaseMetaData dbmeta(con->getMetaData()); stmt.reset(con->createStatement()); stmt->execute("DROP TABLE IF EXISTS child"); stmt->execute("DROP TABLE IF EXISTS parent"); try { stmt->execute("CREATE TABLE parent(pid INT NOT NULL, PRIMARY KEY(pid)) ENGINE=INNODB;"); stmt->execute("CREATE TABLE child(cid INT NOT NULL, cpid INT, " "INDEX idx_parent_id(cpid), FOREIGN KEY idx_parent_id(cpid) " "REFERENCES parent(pid) ON DELETE CASCADE ON UPDATE CASCADE, PRIMARY KEY(cid)) ENGINE=INNODB;"); } catch (sql::SQLException &) { SKIP("Cannot create necessary FK tables"); } logMsg("... checking parent->child"); res.reset(dbmeta->getCrossReference(con->getCatalog(), con->getSchema(), "parent", con->getCatalog(), con->getSchema(), "child")); checkResultSetScrolling(res); ASSERT(res->next()); checkForeignKey(con, res); logMsg("... checking child->parent"); stmt->execute("DROP TABLE IF EXISTS child"); stmt->execute("DROP TABLE IF EXISTS parent"); res.reset(dbmeta->getCrossReference(con->getCatalog(), con->getSchema(), "child", con->getCatalog(), con->getSchema(), "parent")); ASSERT(!res->next()); } catch (sql::MethodNotImplementedException &e) { logMsg(e.what()); SKIP("Server version is too old, MethodNotImplementedException!"); } catch (sql::SQLException &e) { logErr(e.what()); logErr("SQLState: " + std::string(e.getSQLState())); fail(e.what(), __FILE__, __LINE__); } } void connectionmetadata::getProcedureTerm() { logMsg("connectionmetadata::getProcedureTerm() - MySQL_ConnectionMetaData::getProcedureTerm"); try { DatabaseMetaData dbmeta(con->getMetaData()); ASSERT_EQUALS("procedure", dbmeta->getProcedureTerm()); } catch (sql::SQLException &e) { logErr(e.what()); logErr("SQLState: " + std::string(e.getSQLState())); fail(e.what(), __FILE__, __LINE__); } } void connectionmetadata::getResultSetHoldability() { logMsg("connectionmetadata::getResultSetHoldability() - MySQL_ConnectionMetaData::getResultSetHoldability()"); try { DatabaseMetaData dbmeta(con->getMetaData()); ASSERT_EQUALS(sql::ResultSet::HOLD_CURSORS_OVER_COMMIT, dbmeta->getResultSetHoldability()); ASSERT(sql::ResultSet::CLOSE_CURSORS_AT_COMMIT != dbmeta->getResultSetHoldability()); } catch (sql::SQLException &e) { logErr(e.what()); logErr("SQLState: " + std::string(e.getSQLState())); fail(e.what(), __FILE__, __LINE__); } } void connectionmetadata::getSchemaTerm() { logMsg("connectionmetadata::getSchemaTerm() - MySQL_ConnectionMetaData::getSchemaTerm()"); try { DatabaseMetaData dbmeta(con->getMetaData()); ASSERT_EQUALS("schema", dbmeta->getSchemaTerm()); } catch (sql::SQLException &e) { logErr(e.what()); logErr("SQLState: " + std::string(e.getSQLState())); fail(e.what(), __FILE__, __LINE__); } } void connectionmetadata::getSearchStringEscape() { logMsg("connectionmetadata::getSearchStringEscape - MySQL_ConnectionMetaData::getSearchStringEscape()"); try { DatabaseMetaData dbmeta(con->getMetaData()); ASSERT_EQUALS("\\", dbmeta->getSearchStringEscape()); } catch (sql::SQLException &e) { logErr(e.what()); logErr("SQLState: " + std::string(e.getSQLState())); fail(e.what(), __FILE__, __LINE__); } } void connectionmetadata::getSQLKeywords() { logMsg("connectionmetadata::getSQLKeywords - MySQL_ConnectionMetaData::getSQLKeywords()"); std::string keywords( "ACCESSIBLE,ANALYZE,ASENSITIVE,BEFORE,BIGINT,BINARY,BLOB,CALL,CHANGE,CONDITION,DATABASE,DATABASES," "DAY_HOUR,DAY_MICROSECOND,DAY_MINUTE,DAY_SECOND,DELAYED,DETERMINISTIC,DISTINCTROW,DIV,DUAL,EACH," "ELSEIF,ENCLOSED,ESCAPED,EXIT,EXPLAIN,FLOAT4,FLOAT8,FORCE,FULLTEXT,GENERAL,HIGH_PRIORITY," "HOUR_MICROSECOND,HOUR_MINUTE,HOUR_SECOND,IF,IGNORE,IGNORE_SERVER_IDS,INDEX,INFILE,INOUT,INT1,INT2," "INT3,INT4,INT8,ITERATE,KEY,KEYS,KILL,LEAVE,LIMIT,LINEAR,LINES,LOAD,LOCALTIME,LOCALTIMESTAMP,LOCK," "LONG,LONGBLOB,LONGTEXT,LOOP,LOW_PRIORITY,MASTER_HEARTBEAT_PERIOD,MASTER_SSL_VERIFY_SERVER_CERT," "MAXVALUE,MEDIUMBLOB,MEDIUMINT,MEDIUMTEXT,MIDDLEINT,MINUTE_MICROSECOND,MINUTE_SECOND,MOD,MODIFIES," "NO_WRITE_TO_BINLOG,OPTIMIZE,OPTIONALLY,OUT,OUTFILE,PURGE,RANGE,READ_WRITE,READS,REGEXP,RELEASE," "RENAME,REPEAT,REPLACE,REQUIRE,RESIGNAL,RESTRICT,RETURN,RLIKE,SCHEMAS,SECOND_MICROSECOND,SENSITIVE," "SEPARATOR,SHOW,SIGNAL,SLOW,SPATIAL,SPECIFIC,SQL_BIG_RESULT,SQL_CALC_FOUND_ROWS,SQL_SMALL_RESULT," "SQLEXCEPTION,SSL,STARTING,STRAIGHT_JOIN,TERMINATED,TINYBLOB,TINYINT,TINYTEXT,TRIGGER,UNDO,UNLOCK," "UNSIGNED,USE,UTC_DATE,UTC_TIME,UTC_TIMESTAMP,VARBINARY,VARCHARACTER,WHILE,XOR,YEAR_MONTH,ZEROFILL"); try { DatabaseMetaData dbmeta(con->getMetaData()); ASSERT_EQUALS(keywords, dbmeta->getSQLKeywords()); } catch (sql::SQLException &e) { logErr(e.what()); logErr("SQLState: " + std::string(e.getSQLState())); fail(e.what(), __FILE__, __LINE__); } } void connectionmetadata::getSuperTables() { logMsg("connectionmetadata::getSuperTables - MySQL_ConnectionMetaData::getSuperTables()"); try { DatabaseMetaData dbmeta(con->getMetaData()); stmt.reset(con->createStatement()); stmt->execute("DROP TABLE IF EXISTS test"); stmt->execute("CREATE TABLE test(id INT)"); res.reset(dbmeta->getSuperTables(con->getCatalog(), con->getSchema(), "test")); checkResultSetScrolling(res); ASSERT(!res->next()); ResultSetMetaData resmeta(res->getMetaData()); ASSERT_EQUALS((unsigned int) 4, resmeta->getColumnCount()); ASSERT_EQUALS("TABLE_CAT", resmeta->getColumnLabel(1)); ASSERT_EQUALS("TABLE_SCHEM", resmeta->getColumnLabel(2)); ASSERT_EQUALS("TABLE_NAME", resmeta->getColumnLabel(3)); ASSERT_EQUALS("SUPERTABLE_NAME", resmeta->getColumnLabel(4)); stmt->execute("DROP TABLE IF EXISTS test"); } catch (sql::SQLException &e) { logErr(e.what()); logErr("SQLState: " + std::string(e.getSQLState())); fail(e.what(), __FILE__, __LINE__); } } void connectionmetadata::getSuperTypes() { logMsg("connectionmetadata::getSuperTypes - MySQL_ConnectionMetaData::getSuperTypes()"); try { DatabaseMetaData dbmeta(con->getMetaData()); stmt.reset(con->createStatement()); stmt->execute("DROP TABLE IF EXISTS test"); stmt->execute("CREATE TABLE test(id INT)"); res.reset(dbmeta->getSuperTypes(con->getCatalog(), con->getSchema(), "test")); checkResultSetScrolling(res); ASSERT(!res->next()); ResultSetMetaData resmeta(res->getMetaData()); ASSERT_EQUALS((unsigned int) 6, resmeta->getColumnCount()); ASSERT_EQUALS("TYPE_CAT", resmeta->getColumnLabel(1)); ASSERT_EQUALS("TYPE_SCHEM", resmeta->getColumnLabel(2)); ASSERT_EQUALS("TYPE_NAME", resmeta->getColumnLabel(3)); ASSERT_EQUALS("SUPERTYPE_CAT", resmeta->getColumnLabel(4)); ASSERT_EQUALS("SUPERTYPE_SCHEM", resmeta->getColumnLabel(5)); ASSERT_EQUALS("SUPERTYPE_NAME", resmeta->getColumnLabel(6)); stmt->execute("DROP TABLE IF EXISTS test"); } catch (sql::SQLException &e) { logErr(e.what()); logErr("SQLState: " + std::string(e.getSQLState())); fail(e.what(), __FILE__, __LINE__); } } void connectionmetadata::classAttributes() { logMsg("connectionmetadata::classAttributes - MySQL_ConnectionMetaData class attributes"); TODO("Check if JDBC compliance requires certain values"); try { DatabaseMetaData dbmeta(con->getMetaData()); ASSERT_EQUALS(0, dbmeta->attributeNoNulls); ASSERT_EQUALS(1, dbmeta->attributeNullable); ASSERT_EQUALS(2, dbmeta->attributeNullableUnknown); ASSERT_EQUALS(1, dbmeta->bestRowNotPseudo); // BUG - ASSERT_EQUALS(2, dbmeta->bestRowPseudo); ASSERT_EQUALS(2, dbmeta->bestRowSession); ASSERT_EQUALS(0, dbmeta->bestRowTemporary); ASSERT_EQUALS(1, dbmeta->bestRowTransaction); ASSERT_EQUALS(0, dbmeta->bestRowUnknown); ASSERT_EQUALS(0, dbmeta->columnNoNulls); ASSERT_EQUALS(1, dbmeta->columnNullable); ASSERT_EQUALS(2, dbmeta->columnNullableUnknown); ASSERT_EQUALS(0, dbmeta->importedKeyCascade); // BUG - got 1 - ASSERT_EQUALS(5, dbmeta->importedKeyInitiallyDeferred); // BUG - got 2 - ASSERT_EQUALS(6, dbmeta->importedKeyInitiallyImmediate); ASSERT_EQUALS(3, dbmeta->importedKeyNoAction); // BUG - got 4 - ASSERT_EQUALS(7, dbmeta->importedKeyNotDeferrable); // BUG - got 5 - ASSERT_EQUALS(1, dbmeta->importedKeyRestrict); // BUG - got 6 - ASSERT_EQUALS(4, dbmeta->importedKeySetDefault); // BUG - got 7 - ASSERT_EQUALS(2, dbmeta->importedKeySetNull); // BUG - got 0 - ASSERT_EQUALS(1, dbmeta->procedureColumnIn); // BUG - got 1 - ASSERT_EQUALS(2, dbmeta->procedureColumnInOut); // BUG - got 2 - ASSERT_EQUALS(4, dbmeta->procedureColumnOut); ASSERT_EQUALS(3, dbmeta->procedureColumnResult); // BUG - got 4 - ASSERT_EQUALS(5, dbmeta->procedureColumnReturn); // BUG - got 5 - ASSERT_EQUALS(1, dbmeta->importedKeyRestrict); // BUG - got 6 - ASSERT_EQUALS(4, dbmeta->importedKeySetDefault); // BUG - got 7 - ASSERT_EQUALS(2, dbmeta->importedKeySetNull); // BUG - got 0 - ASSERT_EQUALS(1, dbmeta->procedureColumnIn); ASSERT_EQUALS(1, dbmeta->procedureColumnInOut); // BUG - got 2 - ASSERT_EQUALS(4, dbmeta->procedureColumnOut); ASSERT_EQUALS(3, dbmeta->procedureColumnResult); // BUG - got 4 - ASSERT_EQUALS(5, dbmeta->procedureColumnReturn); // BUG - got 5 - ASSERT_EQUALS(0, dbmeta->procedureColumnUnknown); // BUG - got 6 - ASSERT_EQUALS(0, dbmeta->procedureNoNulls); // BUG - got 7 - ASSERT_EQUALS(1, dbmeta->procedureNoResult); // BUG - got 8 - ASSERT_EQUALS(1, dbmeta->procedureNullable); // BUG - got 9 - ASSERT_EQUALS(0, dbmeta->procedureNullableUnknown); // BUG - got 10 - ASSERT_EQUALS(2, dbmeta->procedureResultUnknown); // BUG - got 11 - ASSERT_EQUALS(2, dbmeta->procedureReturnsResult); // BUG - got 0 - ASSERT_EQUALS(2, dbmeta->sqlStateSQL99); ASSERT_EQUALS(1, dbmeta->sqlStateXOpen); // BUG - got 0 - ASSERT_EQUALS(1, dbmeta->tableIndexClustered); // BUG - got 1 - ASSERT_EQUALS(2, dbmeta->tableIndexHashed); // BUG - got 2 - ASSERT_EQUALS(3, dbmeta->tableIndexOther); // BUG - got 3 - ASSERT_EQUALS(0, dbmeta->tableIndexStatistic); ASSERT_EQUALS(0, dbmeta->typeNoNulls); ASSERT_EQUALS(1, dbmeta->typeNullable); ASSERT_EQUALS(2, dbmeta->typeNullableUnknown); ASSERT_EQUALS(2, dbmeta->typePredBasic); ASSERT_EQUALS(1, dbmeta->typePredChar); ASSERT_EQUALS(0, dbmeta->typePredNone); ASSERT_EQUALS(3, dbmeta->typeSearchable); ASSERT_EQUALS(1, dbmeta->versionColumnNotPseudo); ASSERT_EQUALS(2, dbmeta->versionColumnPseudo); ASSERT_EQUALS(0, dbmeta->versionColumnUnknown); } catch (sql::SQLException &e) { logErr(e.what()); logErr("SQLState: " + std::string(e.getSQLState())); fail(e.what(), __FILE__, __LINE__); } } void connectionmetadata::getColumnsTypeConversions() { logMsg("connectionmetadata::getColumnsTypeConversions() - MySQL_ConnectionMetaData::getColumns"); std::vector::iterator it; std::stringstream msg; int i; bool got_warning; SKIP("It's not clear whether some conversion should really work in the way this test expects. Thus either test or connector should be fixed at some point."); try { DatabaseMetaData dbmeta(con->getMetaData()); stmt.reset(con->createStatement()); got_warning=false; logMsg("... looping over all kinds of column types"); for (it=columns.begin(), i=0; it != columns.end(); i++, it++) { stmt->execute("DROP TABLE IF EXISTS test"); msg.str(""); msg << "CREATE TABLE test(dummy TIMESTAMP, id " << it->sqldef << ")"; try { stmt->execute(msg.str()); msg.str(""); msg << "... testing " << it->sqldef; logMsg(msg.str()); } catch (sql::SQLException &) { msg.str(""); msg << "... skipping " << it->sqldef; logMsg(msg.str()); continue; } res.reset(dbmeta->getColumns(con->getCatalog(), con->getSchema(), "test", "id")); checkResultSetScrolling(res); ASSERT_EQUALS(true, res->next()); // string -> xyz ASSERT_EQUALS("test", res->getString("TABLE_NAME")); ASSERT_EQUALS(false, res->wasNull()); ASSERT_EQUALS(res->getString(3), res->getString("TABLE_NAME")); ASSERT_EQUALS(true, res->getBoolean("TABLE_NAME")); ASSERT_EQUALS(false, res->wasNull()); ASSERT_EQUALS(res->getBoolean(3), res->getBoolean("TABLE_NAME")); ASSERT_EQUALS((int64_t) 0, res->getInt64("TABLE_NAME")); ASSERT_EQUALS(false, res->wasNull()); ASSERT_EQUALS(res->getInt64(3), res->getInt64("TABLE_NAME")); ASSERT_EQUALS((uint64_t) 0, res->getUInt64("TABLE_NAME")); ASSERT_EQUALS(false, res->wasNull()); ASSERT_EQUALS(res->getUInt64(3), res->getUInt64("TABLE_NAME")); ASSERT_EQUALS((double) 0, res->getDouble("TABLE_NAME")); ASSERT_EQUALS(false, res->wasNull()); ASSERT_EQUALS(res->getDouble(3), res->getDouble("TABLE_NAME")); ASSERT_EQUALS((int) 0, res->getInt("TABLE_NAME")); ASSERT_EQUALS(false, res->wasNull()); ASSERT_EQUALS(res->getInt(3), res->getInt("TABLE_NAME")); ASSERT_EQUALS((unsigned int) 0, res->getUInt("TABLE_NAME")); ASSERT_EQUALS(false, res->wasNull()); ASSERT_EQUALS(res->getUInt(3), res->getUInt("TABLE_NAME")); ASSERT_EQUALS(false, res->isNull("TABLE_NAME")); ASSERT_EQUALS(false, res->wasNull()); ASSERT_EQUALS(res->isNull(3), res->isNull("TABLE_NAME")); // integer -> xyz if (it->decimal_digits != res->getInt("DECIMAL_DIGITS")) { msg.str(""); msg << "...\t\tWARNING: expecting DECIMAL_DIGITS = (int)'" << it->decimal_digits << "'"; msg << " got (int)'" << res->getString("DECIMAL_DIGITS") << "'"; logMsg(msg.str()); got_warning=true; } ASSERT_EQUALS(false, res->wasNull()); ASSERT_EQUALS(res->getInt(9), res->getInt("DECIMAL_DIGITS")); msg.str(""); if (0 == it->decimal_digits) { msg << "0"; } else { msg << it->decimal_digits; } if (msg.str() != res->getString("DECIMAL_DIGITS")) { msg.str(""); msg << "...\t\tWARNING: expecting DECIMAL_DIGITS = '" << it->decimal_digits << "'"; msg << " length() is '" << msg.str().length() << "'"; msg << " got '" << res->getString("DECIMAL_DIGITS") << "'"; msg << " length() is '" << res->getString("DECIMAL_DIGITS").length() << "'"; logMsg(msg.str()); got_warning=true; } else { // If string and int are broken, the rest is broken as well - you can bet! ASSERT_EQUALS(false, res->wasNull()); ASSERT_EQUALS(res->getString(9), res->getString("DECIMAL_DIGITS")); ASSERT_EQUALS(it->decimal_digits != 0, res->getBoolean("DECIMAL_DIGITS")); ASSERT_EQUALS(false, res->wasNull()); ASSERT_EQUALS(res->getBoolean(9), res->getBoolean("DECIMAL_DIGITS")); ASSERT_EQUALS((int64_t) it->decimal_digits, res->getInt64("DECIMAL_DIGITS")); ASSERT_EQUALS(false, res->wasNull()); ASSERT_EQUALS(res->getInt64(9), res->getInt64("DECIMAL_DIGITS")); ASSERT_EQUALS((uint64_t) it->decimal_digits, res->getUInt64("DECIMAL_DIGITS")); ASSERT_EQUALS(false, res->wasNull()); ASSERT_EQUALS(res->getUInt64(9), res->getUInt64("DECIMAL_DIGITS")); ASSERT_EQUALS((double) it->decimal_digits, res->getDouble("DECIMAL_DIGITS")); ASSERT_EQUALS(false, res->wasNull()); ASSERT_EQUALS(res->getDouble(9), res->getDouble("DECIMAL_DIGITS")); ASSERT_EQUALS((int32_t) it->decimal_digits, res->getInt("DECIMAL_DIGITS")); ASSERT_EQUALS(false, res->wasNull()); ASSERT_EQUALS(res->getInt(9), res->getInt("DECIMAL_DIGITS")); ASSERT_EQUALS((uint32_t) it->decimal_digits, res->getUInt("DECIMAL_DIGITS")); ASSERT_EQUALS(false, res->wasNull()); ASSERT_EQUALS(res->getUInt(9), res->getUInt("DECIMAL_DIGITS")); ASSERT_EQUALS(false, res->isNull(9)); ASSERT_EQUALS(res->isNull(9), res->isNull("DECIMAL_DIGITS")); ASSERT_EQUALS(false, res->wasNull()); } try { res->isNull(0); FAIL("Invalid column index"); } catch (sql::SQLException &) { } try { res->isNull("invalid column index"); FAIL("Invalid column index"); } catch (sql::SQLException &) { } stmt->execute("DROP TABLE IF EXISTS test"); } stmt->execute("DROP TABLE IF EXISTS test"); res.reset(dbmeta->getColumns(con->getCatalog(), con->getSchema(), "test", "id")); ASSERT(!res->next()); } catch (sql::SQLException &e) { logErr(e.what()); logErr("SQLState: " + std::string(e.getSQLState())); fail(e.what(), __FILE__, __LINE__); } try { res->isNull(1); FAIL("Not on resultset, should fail"); } catch (sql::SQLException &) { } try { res->isNull("not on resultset and unknown column"); FAIL("Not on resultset, should fail"); } catch (sql::SQLException &) { } if (got_warning) FAIL("See --verbose warnings!"); } /* Simple testcase of getBestRowIdentifier returns columns making UNIQUE not Null filters in case of primary key is not present */ void connectionmetadata::bestIdUniqueNotNull() { createSchemaObject("TABLE", "bestIdUniqueNotNull", "(id int not null, value varchar(25)," "UNIQUE INDEX(id))"); createSchemaObject("TABLE", "bestIdUniqueNull", "(id int, value varchar(25)," "UNIQUE INDEX(id))"); DatabaseMetaData dbmeta(con->getMetaData()); res.reset(dbmeta->getBestRowIdentifier(con->getCatalog(), con->getSchema(), "bestIdUniqueNotNull", 0, false)); ASSERT(res->next()); ASSERT_EQUALS("id", res->getString(2)); ASSERT(!res->next()); res.reset(dbmeta->getBestRowIdentifier(con->getCatalog(), con->getSchema(), "bestIdUniqueNull", 0, false)); ASSERT(!res->next()); } void connectionmetadata::getSchemaCollation() { logMsg("connectionmetadata::getSchemaCollation - MySQL_ConnectionMetaData::getSchemaCollation()"); try { ResultSetMetaData resmeta; DatabaseMetaData dbmeta(con->getMetaData()); stmt.reset(con->createStatement()); stmt->execute("DROP DATABASE IF EXISTS collationTestDatabase"); stmt->execute("CREATE DATABASE collationTestDatabase CHARACTER SET utf8 COLLATE utf8_bin"); /* SchemaCollation */ res.reset(dbmeta->getSchemaCollation(con->getCatalog(), "collationTestDatabase")); ASSERT(res->next()); resmeta.reset(res->getMetaData()); ASSERT_EQUALS((unsigned int) 3, resmeta->getColumnCount()); ASSERT_EQUALS("SCHEMA_CAT", resmeta->getColumnLabel(1)); ASSERT_EQUALS("SCHEMA_NAME", resmeta->getColumnLabel(2)); ASSERT_EQUALS("SCHEMA_COLLATION", resmeta->getColumnLabel(3)); ASSERT(res->getString("SCHEMA_NAME").caseCompare("collationTestDatabase") == 0); ASSERT_EQUALS("utf8_bin", res->getString("SCHEMA_COLLATION")); stmt->execute("DROP DATABASE IF EXISTS collationTestDatabase"); } catch (sql::SQLFeatureNotImplementedException &e) { SKIP(e.what()); } catch (sql::SQLException &e) { logErr(e.what()); logErr("SQLState: " + std::string(e.getSQLState())); fail(e.what(), __FILE__, __LINE__); } } void connectionmetadata::getSchemaCharset() { logMsg("connectionmetadata::getSchemaCharset - MySQL_ConnectionMetaData::getSchemaCharset()"); try { ResultSetMetaData resmeta; DatabaseMetaData dbmeta(con->getMetaData()); stmt.reset(con->createStatement()); stmt->execute("DROP DATABASE IF EXISTS charsetTestDatabase"); stmt->execute("CREATE DATABASE charsetTestDatabase CHARACTER SET utf8 COLLATE utf8_bin"); /* SchemaCharset */ res.reset(dbmeta->getSchemaCharset(con->getCatalog(), "charsetTestDatabase")); ASSERT(res->next()); resmeta.reset(res->getMetaData()); ASSERT_EQUALS((unsigned int) 3, resmeta->getColumnCount()); ASSERT_EQUALS("SCHEMA_CAT", resmeta->getColumnLabel(1)); ASSERT_EQUALS("SCHEMA_NAME", resmeta->getColumnLabel(2)); ASSERT_EQUALS("SCHEMA_CHARSET", resmeta->getColumnLabel(3)); ASSERT(res->getString("SCHEMA_NAME").caseCompare("charsetTestDatabase") == 0); ASSERT_EQUALS("utf8", res->getString("SCHEMA_CHARSET")); stmt->execute("DROP DATABASE IF EXISTS charsetTestDatabase"); } catch (sql::SQLFeatureNotImplementedException & e) { SKIP(e.what()); } catch (sql::SQLException &e) { logErr(e.what()); logErr("SQLState: " + std::string(e.getSQLState())); fail(e.what(), __FILE__, __LINE__); } } void connectionmetadata::getTableCollation() { logMsg("connectionmetadata::getTableCollation - MySQL_ConnectionMetaData::getTableCollation()"); try { ResultSetMetaData resmeta; DatabaseMetaData dbmeta(con->getMetaData()); stmt.reset(con->createStatement()); stmt->execute("DROP DATABASE IF EXISTS collationTestDatabase"); stmt->execute("CREATE DATABASE collationTestDatabase CHARACTER SET utf8 COLLATE utf8_bin"); stmt->execute("DROP TABLE IF EXISTS collationTestDatabase.collationTestTable"); stmt->execute("CREATE TABLE collationTestDatabase.collationTestTable(id INT) CHARACTER SET latin1 COLLATE latin1_general_ci"); stmt->execute("DROP TABLE IF EXISTS collationTestDatabase.collationTestTableAnother"); stmt->execute("CREATE TABLE collationTestDatabase.collationTestTableAnother(id INT) CHARACTER SET utf8 COLLATE utf8_bin"); /* TableCollation */ res.reset(dbmeta->getTableCollation(con->getCatalog(), "collationTestDatabase", "%collationTestTable%")); ASSERT(res->next()); resmeta.reset(res->getMetaData()); ASSERT_EQUALS((unsigned int) 4, resmeta->getColumnCount()); ASSERT_EQUALS("TABLE_CAT", resmeta->getColumnLabel(1)); ASSERT_EQUALS("TABLE_SCHEMA", resmeta->getColumnLabel(2)); ASSERT_EQUALS("TABLE_NAME", resmeta->getColumnLabel(3)); ASSERT_EQUALS("TABLE_COLLATION", resmeta->getColumnLabel(4)); ASSERT(res->getString("TABLE_SCHEMA").caseCompare("collationTestDatabase") == 0); ASSERT(res->getString("TABLE_NAME").caseCompare("collationTestTable") == 0); ASSERT_EQUALS("latin1_general_ci", res->getString("TABLE_COLLATION")); ASSERT(res->next()); ASSERT(res->getString("TABLE_SCHEMA").caseCompare("collationTestDatabase") == 0); ASSERT(res->getString("TABLE_NAME").caseCompare("collationTestTableAnother") == 0); ASSERT_EQUALS("utf8_bin", res->getString("TABLE_COLLATION")); stmt->execute("DROP DATABASE IF EXISTS collationTestDatabase"); } catch (sql::SQLFeatureNotImplementedException & e) { SKIP(e.what()); } catch (sql::SQLException &e) { logErr(e.what()); logErr("SQLState: " + std::string(e.getSQLState())); fail(e.what(), __FILE__, __LINE__); } } void connectionmetadata::getTableCharset() { logMsg("connectionmetadata::getTableCharset - MySQL_ConnectionMetaData::getTableCharset()"); try { ResultSetMetaData resmeta; DatabaseMetaData dbmeta(con->getMetaData()); stmt.reset(con->createStatement()); stmt->execute("DROP DATABASE IF EXISTS charsetTestDatabase"); stmt->execute("CREATE DATABASE charsetTestDatabase CHARACTER SET utf8 COLLATE utf8_bin"); stmt->execute("DROP TABLE IF EXISTS charsetTestDatabase.charsetTestTable"); stmt->execute("CREATE TABLE charsetTestDatabase.charsetTestTable(id INT) CHARACTER SET latin1 COLLATE latin1_general_ci"); stmt->execute("DROP TABLE IF EXISTS charsetTestDatabase.charsetTestTableAnother"); stmt->execute("CREATE TABLE charsetTestDatabase.charsetTestTableAnother(id INT) CHARACTER SET utf8 COLLATE utf8_bin"); /* TableCharset */ res.reset(dbmeta->getTableCharset(con->getCatalog(), "charsetTestDatabase", "%charsetTestTable%")); ASSERT(res->next()); resmeta.reset(res->getMetaData()); ASSERT_EQUALS((unsigned int) 4, resmeta->getColumnCount()); ASSERT_EQUALS("TABLE_CAT", resmeta->getColumnLabel(1)); ASSERT_EQUALS("TABLE_SCHEMA", resmeta->getColumnLabel(2)); ASSERT_EQUALS("TABLE_NAME", resmeta->getColumnLabel(3)); ASSERT_EQUALS("TABLE_CHARSET", resmeta->getColumnLabel(4)); ASSERT(res->getString("TABLE_SCHEMA").caseCompare("charsetTestDatabase") == 0); ASSERT(res->getString("TABLE_NAME").caseCompare("charsetTestTable") == 0); ASSERT_EQUALS("latin1", res->getString("TABLE_CHARSET")); ASSERT(res->next()); ASSERT(res->getString("TABLE_SCHEMA").caseCompare("charsetTestDatabase") == 0); ASSERT(res->getString("TABLE_NAME").caseCompare("charsetTestTableAnother") == 0); ASSERT_EQUALS("utf8", res->getString("TABLE_CHARSET")); stmt->execute("DROP DATABASE IF EXISTS charsetTestDatabase"); } catch (sql::SQLFeatureNotImplementedException & e) { SKIP(e.what()); } catch (sql::SQLException &e) { logErr(e.what()); logErr("SQLState: " + std::string(e.getSQLState())); fail(e.what(), __FILE__, __LINE__); } } void connectionmetadata::getTables() { logMsg("connectionmetadata::getTables - MySQL_ConnectionMetaData::getTables()"); try { DatabaseMetaData dbmeta(con->getMetaData()); std::list< sql::SQLString > tableTypes; stmt.reset(con->createStatement()); stmt->execute("DROP TABLE IF EXISTS testtable1"); stmt->execute("CREATE TABLE testtable1(id INT)"); stmt->execute("DROP VIEW IF EXISTS testview1"); stmt->execute("CREATE VIEW testview1 AS SELECT * FROM testtable1"); /* for tableType = TABLE */ tableTypes.clear(); tableTypes.push_back(sql::SQLString("TABLE")); res.reset(dbmeta->getTables("", "%", "testtable%", tableTypes)); ASSERT(res->next()); ASSERT_EQUALS(res->getString(3), "testtable1"); ASSERT_EQUALS(res->getString(4), "TABLE"); /* for tableType = VIEW */ tableTypes.clear(); tableTypes.push_back(sql::SQLString("VIEW")); res.reset(dbmeta->getTables("", "%", "testview%", tableTypes)); ASSERT(res->next()); ASSERT_EQUALS(res->getString(3), "testview1"); ASSERT_EQUALS(res->getString(4), "VIEW"); stmt->execute("DROP TABLE IF EXISTS testtable1"); stmt->execute("DROP VIEW IF EXISTS testview1"); } catch (sql::SQLException &e) { logErr(e.what()); logErr("SQLState: " + std::string(e.getSQLState())); fail(e.what(), __FILE__, __LINE__); } } void connectionmetadata::bugCpp25() { logMsg("bugs::bugCpp25"); DatabaseMetaData dbmeta(con->getMetaData()); uint64_t major = dbmeta->getDatabaseMajorVersion(), minor = dbmeta->getDatabaseMinorVersion(), patch = dbmeta->getDatabasePatchVersion(); logMsg("Server version from metadata object: " + std::to_string(major) + "." + std::to_string(minor) + "." + std::to_string(patch)); ASSERT(major > 2 && major < 100); res.reset(stmt->executeQuery("SELECT version()")); res->next(); sql::SQLString verFromServer = res->getString(1); logMsg("Server version from SQL query: " + verFromServer); size_t dash= verFromServer.find_first_of('-'); if (dash != std::string::npos) { verFromServer= verFromServer.substr(0, dash); } // More to test connector's split #ifndef _WIN32 // In case of mixing relese/debug versions, split may crash on Windows. Thus leaving it to be tested on other platforms // On Windows doing testing the same without split sql::mariadb::Tokens verParts(sql::mariadb::split(verFromServer, ".")); ASSERT_EQUALS(3ULL, static_cast(verParts->size())); ASSERT_EQUALS(major, std::stoul((*verParts)[0].c_str())); if (std::getenv("MAXSCALE_TEST_DISABLE") == nullptr) { ASSERT_EQUALS(minor, std::stoul((*verParts)[1].c_str())); std::size_t dashPos = (*verParts)[2].find_first_of('-'); ASSERT_EQUALS(patch, std::stoul(dashPos == std::string::npos ? (*verParts)[2].c_str() : (*verParts)[2].substr(0, dashPos).c_str())); } // And even some more testing of the internal split sql::mariadb::Tokens csv(sql::mariadb::split("575,1,,22,,", ",")); ASSERT_EQUALS(6ULL, static_cast(csv->size())); ASSERT_EQUALS("575", (*csv)[0]); ASSERT_EQUALS("1", (*csv)[1]); ASSERT_EQUALS("", (*csv)[2]); ASSERT_EQUALS("22", (*csv)[3]); ASSERT_EQUALS("", (*csv)[4]); ASSERT_EQUALS("", (*csv)[5]); #else TestList verParts; StringUtils::split(verParts, verFromServer.c_str(), ".", true, true); ASSERT_EQUALS(3ULL, static_cast(verParts.size())); ASSERT_EQUALS(major, std::stoul(verParts[0].c_str())); ASSERT_EQUALS(minor, std::stoul(verParts[1].c_str())); std::size_t dashPos = verParts[2].find_first_of('-'); if (std::getenv("MAXSCALE_TEST_DISABLE") == nullptr) { ASSERT_EQUALS(patch, std::stoul(dashPos == std::string::npos ? verParts[2].c_str() : verParts[2].substr(0, dashPos).c_str())); } #endif // !_WIN32 } } /* namespace connectionmetadata */ } /* namespace testsuite */