/* * Copyright (c) 2009, 2018, Oracle and/or its affiliates. All rights reserved. * * 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 "PreparedStatement.hpp" #include "Connection.hpp" #include "Warning.hpp" #include "preparedstatementtest.h" #include #include namespace testsuite { namespace classes { void preparedstatement::InsertSelectAllTypes() { logMsg("preparedstatement::InsertSelectAllTypes() - MySQL_PreparedStatement::*"); //TODO: Enable it after fixing std::stringstream sql; std::vector::iterator it; stmt.reset(con->createStatement()); bool got_warning=false; size_t len; try { bool blobTestMakesSense= true; for (it = columns.end(), it--; it != columns.begin(); it--) { stmt->execute("DROP TABLE IF EXISTS test"); sql.str(""); sql << "CREATE TABLE test(dummy TIMESTAMP, id " << it->sqldef << ")"; try { stmt->execute(sql.str()); sql.str(""); sql << "... testing '" << it->sqldef << "'"; logMsg(sql.str()); } catch (sql::SQLException&) { sql.str(""); sql << "... skipping '" << it->sqldef << "'"; logMsg(sql.str()); continue; } pstmt.reset(con->prepareStatement("INSERT INTO test(id) VALUES (?)")); if (it->name == "BIT") { pstmt->setLong(1, std::stoull(it->value)); } else { pstmt->setString(1, it->value); } ASSERT_EQUALS(1, pstmt->executeUpdate()); pstmt.reset(con->prepareStatement("SELECT id, NULL FROM test")); res.reset(pstmt->executeQuery()); checkResultSetScrolling(res); ASSERT(res->next()); res.reset(); res.reset(pstmt->executeQuery()); checkResultSetScrolling(res); ASSERT(res->next()); if (it->check_as_string && (res->getString(1) != it->as_string)) { sql.str(""); sql << "... \t\tWARNING - SQL: '" << it->sqldef << "' - expecting '" << it->as_string << "'"; sql << " got '" << res->getString(1) << "'"; logMsg(sql.str()); got_warning = true; } sql::SQLString value = res->getString(1); ASSERT_EQUALS(res->getString("id"), String(value.c_str(), value.length())); try { res->getString(0); FAIL("Invalid argument not detected"); } catch (sql::InvalidArgumentException&) { } try { res->getString(3); FAIL("Invalid argument not detected"); } catch (sql::InvalidArgumentException&) { } res->beforeFirst(); try { res->getString(1); FAIL("Invalid argument not detected"); } catch (sql::SQLDataException&) { } res->afterLast(); try { res->getString(1); FAIL("Invalid argument not detected"); } catch (sql::SQLDataException&) { } res->first(); try { ASSERT_EQUALS(res->getDouble("id"), res->getDouble(1)); try { res->getDouble(0); FAIL("Invalid argument not detected"); } catch (sql::InvalidArgumentException&) { } try { res->getDouble(3); FAIL("Invalid argument not detected"); } catch (sql::InvalidArgumentException&) { } res->beforeFirst(); try { res->getDouble(1); FAIL("Invalid argument not detected"); } catch (sql::SQLDataException&) { } res->afterLast(); try { res->getDouble(1); FAIL("Invalid argument not detected"); } catch (sql::SQLDataException&) { } res->first(); ASSERT_EQUALS(res->getInt(1), res->getInt("id")); try { res->getInt(0); FAIL("Invalid argument not detected"); } catch (sql::InvalidArgumentException&) { } try { res->getInt(3); FAIL("Invalid argument not detected"); } catch (sql::InvalidArgumentException&) { } res->beforeFirst(); try { res->getInt(1); FAIL("Invalid argument not detected"); } catch (sql::SQLDataException&) { } res->afterLast(); try { res->getInt(1); FAIL("Invalid argument not detected"); } catch (sql::SQLDataException&) { } res->first(); ASSERT_EQUALS(res->getUInt(1), res->getUInt("id")); try { res->getUInt(0); FAIL("Invalid argument not detected"); } catch (sql::InvalidArgumentException&) { } try { res->getUInt(3); FAIL("Invalid argument not detected"); } catch (sql::InvalidArgumentException&) { } res->beforeFirst(); try { res->getUInt(1); FAIL("Invalid argument not detected"); } catch (sql::SQLDataException&) { } res->afterLast(); try { res->getUInt(1); FAIL("Invalid argument not detected"); } catch (sql::SQLDataException&) { } res->first(); ASSERT_EQUALS(res->getInt64("id"), res->getInt64(1)); try { res->getInt64(0); FAIL("Invalid argument not detected"); } catch (sql::InvalidArgumentException&) { } try { res->getInt64(3); FAIL("Invalid argument not detected"); } catch (sql::InvalidArgumentException&) { } res->beforeFirst(); try { res->getInt64(1); FAIL("Invalid argument not detected"); } catch (sql::SQLDataException&) { } res->afterLast(); try { res->getInt64(1); FAIL("Invalid argument not detected"); } catch (sql::SQLDataException&) { } res->first(); ASSERT_EQUALS(res->getUInt64("id"), res->getUInt64(1)); try { res->getUInt64(0); FAIL("Invalid argument not detected"); } catch (sql::InvalidArgumentException&) { } try { res->getUInt64(3); FAIL("Invalid argument not detected"); } catch (sql::InvalidArgumentException&) { } res->beforeFirst(); try { res->getUInt64(1); FAIL("Invalid argument not detected"); } catch (sql::SQLDataException&) { } res->afterLast(); try { res->getUInt64(1); FAIL("Invalid argument not detected"); } catch (sql::SQLDataException&) { } } catch (sql::SQLException & e) { //All is good if ((e.getErrorCode() != 1264 || e.getSQLState().compare("22003") != 0) && !e.getMessage().startsWith("getDouble not available for data field type")) { throw e; } } res->first(); ASSERT_EQUALS(res->getBoolean("id"), res->getBoolean(1)); try { res->getBoolean(0); FAIL("Invalid argument not detected"); } catch (sql::InvalidArgumentException&) { } try { res->getBoolean(3); FAIL("Invalid argument not detected"); } catch (sql::InvalidArgumentException&) { } res->beforeFirst(); try { res->getBoolean(1); FAIL("Invalid argument not detected"); } catch (sql::SQLDataException&) { } res->afterLast(); try { res->getBoolean(1); FAIL("Invalid argument not detected"); } catch (sql::SQLDataException&) { } res->first(); /* YEAR is first type after char/bin types(traversing backwards). The way getBlob test is written now, it will work correctly only for those types(and maybe some others), and YEAR is firdt type it will not */ if (it->name == "YEAR") { blobTestMakesSense= false; } // TODO - make BLOB if (it->check_as_string && blobTestMakesSense) { { std::unique_ptr blob_output_stream(res->getBlob(1)); len=it->as_string.length(); std::unique_ptr blob_out(new char[len]); blob_output_stream->read(blob_out.get(), len); if (it->as_string.compare(0, blob_output_stream->gcount() , blob_out.get(), blob_output_stream->gcount())) { sql.str(""); sql << "... \t\tWARNING - SQL: '" << it->sqldef << "' - expecting '" << it->as_string << "'"; sql << " got '" << res->getString(1) << "'"; logMsg(sql.str()); got_warning=false; } } { std::unique_ptr blob_output_stream(res->getBlob("id")); len=it->as_string.length(); std::unique_ptr blob_out(new char[len]); blob_output_stream->read(blob_out.get(), len); if (it->as_string.compare(0, blob_output_stream->gcount() , blob_out.get(), blob_output_stream->gcount())) { sql.str(""); sql << "... \t\tWARNING - SQL: '" << it->sqldef << "' - expecting '" << it->as_string << "'"; sql << " got '" << res->getString(1) << "'"; logMsg(sql.str()); got_warning=false; } } } try { res->getBlob(0); FAIL("Invalid argument not detected"); } catch (sql::InvalidArgumentException &) { } try { res->getBlob(3); FAIL("Invalid argument not detected"); } catch (sql::InvalidArgumentException &) { } res->beforeFirst(); try { res->getBlob(1); FAIL("Invalid argument not detected"); } catch (sql::SQLDataException&) { } res->afterLast(); try { res->getBlob(1); FAIL("Invalid argument not detected"); } catch (sql::SQLDataException&) { } res->first(); } stmt->execute("DROP TABLE IF EXISTS test"); if (got_warning) FAIL("See warnings"); } catch (sql::SQLException &e) { logErr(e.what()); logErr("SQLState: " + std::string(e.getSQLState())); fail(e.what(), __FILE__, __LINE__); } } void preparedstatement::assortedSetType() { logMsg("preparedstatement::assortedSetType() - MySQL_PreparedStatement::set*"); //TODO: Enable it after fixing //SKIP("Removed until fixed"); std::stringstream sql; std::vector::iterator it; stmt.reset(con->createStatement()); bool got_warning=false; try { for (it=columns.end(), it--; it != columns.begin(); it--) { stmt->execute("DROP TABLE IF EXISTS test"); sql.str(""); sql << "CREATE TABLE test(dummy TIMESTAMP, id " << it->sqldef << ")"; try { stmt->execute(sql.str()); sql.str(""); sql << "... testing '" << it->sqldef << "'"; logMsg(sql.str()); } catch (sql::SQLException &) { sql.str(""); sql << "... skipping '" << it->sqldef << "'"; logMsg(sql.str()); continue; } pstmt.reset(con->prepareStatement("INSERT INTO test(id) VALUES (?)")); if (it->name == "BIT") { pstmt->setLong(1, std::stoull(it->value)); ASSERT_EQUALS(1, pstmt->executeUpdate()); } else { pstmt->setString(1, it->value); ASSERT_EQUALS(1, pstmt->executeUpdate()); pstmt->clearParameters(); pstmt->setDateTime(1, it->value); ASSERT_EQUALS(1, pstmt->executeUpdate()); pstmt->clearParameters(); pstmt->setBigInt(1, it->value); ASSERT_EQUALS(1, pstmt->executeUpdate()); } pstmt->clearParameters(); try { pstmt->setString(0, "overflow"); FAIL("Invalid argument not detected"); } catch (sql::SQLException&) { } pstmt->clearParameters(); try { pstmt->setString(2, "invalid"); FAIL("Invalid argument not detected"); } catch (sql::SQLException&) { } pstmt->clearParameters(); try { pstmt->setBigInt(0, it->value); FAIL("Invalid argument not detected"); } catch (sql::SQLException&) { } pstmt->clearParameters(); try { pstmt->setBigInt(2, it->value); FAIL("Invalid argument not detected"); } catch (sql::SQLException&) { } pstmt->clearParameters(); // enum won't accept 0 as a value pstmt->setBoolean(1, it->name.compare("ENUM") == 0); ASSERT_EQUALS(1, pstmt->executeUpdate()); pstmt->clearParameters(); try { pstmt->setBoolean(0, false); FAIL("Invalid argument not detected"); } catch (sql::SQLException&) { } pstmt->clearParameters(); try { pstmt->setBoolean(2, false); FAIL("Invalid argument not detected"); } catch (sql::SQLException&) { } pstmt->clearParameters(); try { pstmt->setDateTime(0, it->value); FAIL("Invalid argument not detected"); } catch (sql::SQLException&) { } pstmt->clearParameters(); try { pstmt->setDateTime(2, it->value); FAIL("Invalid argument not detected"); } catch (sql::SQLException&) { } pstmt->clearParameters(); pstmt->setDouble(1, 1.23); if (it->name != "TIMESTAMP" && it->name != "DATETIME" && it->name != "DATE") { try { ASSERT_EQUALS(1, pstmt->executeUpdate()); } catch(sql::SQLException& e) { if (!((it->name == "VARBINARY" || it->name == "BINARY" || it->name == "CHAR") && it->precision < 8 && e.getSQLState() == "22001" && e.getErrorCode() == 1406)) { throw e; } } pstmt->clearParameters(); try { pstmt->setDouble(0, 1.23); FAIL("Invalid argument not detected"); } catch (sql::SQLException&) { } pstmt->clearParameters(); try { pstmt->setDouble(2, 1.23); FAIL("Invalid argument not detected"); } catch (sql::SQLException&) { } pstmt->clearParameters(); pstmt->setInt(1, 1); ASSERT_EQUALS(1, pstmt->executeUpdate()); pstmt->clearParameters(); try { pstmt->setInt(0, (int32_t)-1); FAIL("Invalid argument not detected"); } catch (sql::SQLException&) { } pstmt->clearParameters(); try { pstmt->setInt(2, (int32_t)-1); FAIL("Invalid argument not detected"); } catch (sql::SQLException&) { } pstmt->clearParameters(); pstmt->setUInt(1, (uint32_t)1); ASSERT_EQUALS(1, pstmt->executeUpdate()); pstmt->clearParameters(); try { pstmt->setUInt(0, (uint32_t)1); FAIL("Invalid argument not detected"); } catch (sql::SQLException&) { } pstmt->clearParameters(); try { pstmt->setUInt(2, (uint32_t)1); FAIL("Invalid argument not detected"); } catch (sql::SQLException&) { } pstmt->clearParameters(); pstmt->setInt64(1, 1); ASSERT_EQUALS(1, pstmt->executeUpdate()); pstmt->clearParameters(); pstmt->setUInt64(1, (uint64_t)1); ASSERT_EQUALS(1, pstmt->executeUpdate()); pstmt->clearParameters(); try { pstmt->setUInt64(0, (uint64_t)1); FAIL("Invalid argument not detected"); } catch (sql::SQLException&) { } pstmt->clearParameters(); try { pstmt->setUInt64(2, (uint64_t)-1); FAIL("Invalid argument not detected"); } catch (sql::SQLException&) { } } if (it->is_nullable) { pstmt->clearParameters(); pstmt->setNull(1, it->ctype); ASSERT_EQUALS(1, pstmt->executeUpdate()); pstmt->clearParameters(); try { pstmt->setNull(0, it->ctype); FAIL("Invalid argument not detected"); } catch (sql::SQLException&) { } pstmt->clearParameters(); try { pstmt->setNull(2, it->ctype); FAIL("Invalid argument not detected"); } catch (sql::SQLException&) { } } pstmt->clearParameters(); pstmt.reset(con->prepareStatement("INSERT INTO test(id) VALUES (?)")); std::stringstream blob_input_stream; blob_input_stream.str(it->value); pstmt->setBlob(1, &blob_input_stream); try { ASSERT_EQUALS(1, pstmt->executeUpdate()); } catch (sql::SQLException & e) { if (!(e.getErrorCode() == 1265 || e.getErrorCode() == 1406) || (it->name.compare("SET") != 0 && it->name.compare("ENUM") != 0 && it->name.compare("BIT") != 0)) { throw e; } } pstmt->clearParameters(); try { pstmt->setBlob(0, &blob_input_stream); FAIL("Invalid argument not detected"); } catch (sql::SQLException&) { } pstmt->clearParameters(); try { pstmt->setBlob(2, &blob_input_stream); FAIL("Invalid argument not detected"); } catch (sql::SQLException&) { } pstmt.reset(con->prepareStatement("SELECT COUNT(IFNULL(id, 1)) AS _num FROM test")); res.reset(pstmt->executeQuery()); checkResultSetScrolling(res); ASSERT(res->next()); if (res->getInt("_num") != (11 + (int) it->is_nullable)) { sql.str(""); sql << "....\t\tWARNING, SQL: " << it->sqldef << ", nullable " << std::boolalpha; sql << it->is_nullable << ", found " << res->getInt(1) << "columns but"; sql << " expecting " << (11 + (int) it->is_nullable); logMsg(sql.str()); got_warning=true; } } stmt->execute("DROP TABLE IF EXISTS test"); if (got_warning) { SKIP("There were warnings, but that is due to changes in the test, that made detecting of warnings obsolete"); } } catch (sql::SQLException &e) { logErr(e.what()); logErr("SQLState: " + std::string(e.getSQLState())); fail(e.what(), __FILE__, __LINE__); } } void preparedstatement::setNull() { logMsg("preparedstatement::setNull() - MySQL_PreparedStatement::*"); std::stringstream sql; stmt.reset(con->createStatement()); try { stmt->execute("DROP TABLE IF EXISTS test"); stmt->execute("CREATE TABLE test(id INT)"); pstmt.reset(con->prepareStatement("INSERT INTO test(id) VALUES (?)")); pstmt->setNull(1, sql::DataType::INTEGER); ASSERT_EQUALS(1, pstmt->executeUpdate()); pstmt.reset(con->prepareStatement("SELECT id FROM test")); res.reset(pstmt->executeQuery()); checkResultSetScrolling(res); ASSERT(res->next()); ASSERT(res->isNull(1)); } catch (sql::SQLException &e) { logErr(e.what()); logErr("SQLState: " + std::string(e.getSQLState())); fail(e.what(), __FILE__, __LINE__); } try { stmt->execute("DROP TABLE IF EXISTS test"); stmt->execute("CREATE TABLE test(id INT NOT NULL)"); pstmt.reset(con->prepareStatement("INSERT INTO test(id) VALUES (?)")); pstmt->setNull(1, sql::DataType::INTEGER); pstmt->executeUpdate(); FAIL("Should fail"); } catch (sql::SQLException &) { } } void preparedstatement::checkClosed() { logMsg("preparedstatement::checkClosed() - MySQL_PreparedStatement::close()"); try { pstmt.reset(con->prepareStatement("SELECT 1")); pstmt->close(); } catch (sql::SQLException &e) { logErr(e.what()); logErr("SQLState: " + std::string(e.getSQLState())); fail(e.what(), __FILE__, __LINE__); } } void preparedstatement::getMetaData() { logMsg("preparedstatement::getMetaData() - MySQL_PreparedStatement::getMetaData()"); std::stringstream sql; std::vector::iterator it; stmt.reset(con->createStatement()); ResultSetMetaData meta_ps; ResultSetMetaData meta_st; ResultSet res_st; bool got_warning=false; unsigned int i; try { for (it=columns.end(), it--; it != columns.begin(); it--) { stmt->execute("DROP TABLE IF EXISTS test"); sql.str(""); sql << "CREATE TABLE test(dummy TIMESTAMP, id " << it->sqldef << ")"; try { stmt->execute(sql.str()); sql.str(""); sql << "... testing '" << it->sqldef << "'"; logMsg(sql.str()); } catch (sql::SQLException &) { sql.str(""); sql << "... skipping '" << it->sqldef << "'"; logMsg(sql.str()); continue; } pstmt.reset(con->prepareStatement("INSERT INTO test(id) VALUES (?)")); if (it->name == "BIT") { pstmt->setLong(1, std::stoull(it->value)); } else { pstmt->setString(1, it->value); } ASSERT_EQUALS(1, pstmt->executeUpdate()); pstmt.reset(con->prepareStatement("SELECT id, dummy, NULL, -1.1234, 'Warum nicht...' FROM test")); res.reset(pstmt->executeQuery()); meta_ps.reset(res->getMetaData()); res_st.reset(stmt->executeQuery("SELECT id, dummy, NULL, -1.1234, 'Warum nicht...' FROM test")); meta_st.reset(res->getMetaData()); ASSERT_EQUALS(meta_ps->getColumnCount(), meta_st->getColumnCount()); for (i=1; i <= meta_ps->getColumnCount(); i++) { ASSERT_EQUALS(meta_ps->getCatalogName(i), meta_st->getCatalogName(i)); ASSERT_EQUALS(meta_ps->getColumnDisplaySize(i), meta_st->getColumnDisplaySize(i)); ASSERT_EQUALS(meta_ps->getColumnLabel(i), meta_st->getColumnLabel(i)); ASSERT_EQUALS(meta_ps->getColumnName(i), meta_st->getColumnName(i)); ASSERT_EQUALS(meta_ps->getColumnType(i), meta_st->getColumnType(i)); ASSERT_EQUALS(meta_ps->getColumnTypeName(i), meta_st->getColumnTypeName(i)); ASSERT_EQUALS(meta_ps->getPrecision(i), meta_st->getPrecision(i)); ASSERT_EQUALS(meta_ps->getScale(i), meta_st->getScale(i)); ASSERT_EQUALS(meta_ps->getSchemaName(i), meta_st->getSchemaName(i)); ASSERT_EQUALS(meta_ps->getTableName(i), meta_st->getTableName(i)); ASSERT_EQUALS(meta_ps->isAutoIncrement(i), meta_st->isAutoIncrement(i)); ASSERT_EQUALS(meta_ps->isCaseSensitive(i), meta_st->isCaseSensitive(i)); ASSERT_EQUALS(meta_ps->isCurrency(i), meta_st->isCurrency(i)); ASSERT_EQUALS(meta_ps->isDefinitelyWritable(i), meta_st->isDefinitelyWritable(i)); ASSERT_EQUALS(meta_ps->isNullable(i), meta_st->isNullable(i)); ASSERT_EQUALS(meta_ps->isReadOnly(i), meta_st->isReadOnly(i)); ASSERT_EQUALS(meta_ps->isSearchable(i), meta_st->isSearchable(i)); ASSERT_EQUALS(meta_ps->isSigned(i), meta_st->isSigned(i)); ASSERT_EQUALS(meta_ps->isWritable(i), meta_st->isWritable(i)); } try { meta_ps->getCatalogName(0); FAIL("Invalid argument not detected"); } catch (sql::InvalidArgumentException&) { } } stmt->execute("DROP TABLE IF EXISTS test"); if (got_warning) FAIL("See warnings"); } catch (sql::SQLException &e) { logErr(e.what()); logErr("SQLState: " + std::string(e.getSQLState())); fail(e.what(), __FILE__, __LINE__); } } bool preparedstatement::createSP(std::string sp_code) { try { stmt.reset(con->createStatement()); stmt->execute("DROP PROCEDURE IF EXISTS p"); } catch (sql::SQLException &e) { logMsg(e.what()); return false; } try { pstmt.reset(con->prepareStatement(sp_code)); ASSERT(!pstmt->execute()); logMsg("... using PS for everything"); } catch (sql::SQLException &e) { if (e.getErrorCode() != 1295) { logErr(e.what()); std::stringstream msg; msg.str(""); msg << "SQLState: " << e.getSQLState() << ", MySQL error code: " << e.getErrorCode(); logErr(msg.str()); fail(e.what(), __FILE__, __LINE__); } stmt->execute(sp_code); } return true; } void preparedstatement::callSP() { logMsg("preparedstatement::callSP() - MySQL_PreparedStatement::*()"); std::string sp_code("CREATE PROCEDURE p(OUT ver_param VARCHAR(250)) BEGIN SELECT VERSION() INTO ver_param; END;"); try { if (!createSP(sp_code)) { logMsg("... skipping:"); return; } DatabaseMetaData dbmeta(con->getMetaData()); try { pstmt.reset(con->prepareStatement("CALL p(@version)")); ASSERT(!pstmt->execute()); ASSERT(!pstmt->execute()); } catch (sql::SQLException &e) { if (e.getErrorCode() != 1295) { logErr(e.what()); std::stringstream msg; msg.str(""); msg << "SQLState: " << e.getSQLState() << ", MySQL error code: " << e.getErrorCode(); logErr(msg.str()); fail(e.what(), __FILE__, __LINE__); } // PS protocol does not support CALL return; } pstmt.reset(con->prepareStatement("SELECT @version AS _version")); res.reset(pstmt->executeQuery()); ASSERT(res->next()); ASSERT_EQUALS(dbmeta->getDatabaseProductVersion(), res->getString("_version")); pstmt.reset(con->prepareStatement("SET @version='no_version'")); ASSERT(!pstmt->execute()); pstmt.reset(con->prepareStatement("CALL p(@version)")); ASSERT(!pstmt->execute()); pstmt.reset(con->prepareStatement("SELECT @version AS _version")); res.reset(pstmt->executeQuery()); ASSERT(res->next()); ASSERT_EQUALS(dbmeta->getDatabaseProductVersion(), res->getString("_version")); } catch (sql::SQLException &e) { logErr(e.what()); std::stringstream msg; msg.str(""); msg << "SQLState: " << e.getSQLState() << ", MySQL error code: " << e.getErrorCode(); logErr(msg.str()); fail(e.what(), __FILE__, __LINE__); } } void preparedstatement::callSPInOut() { logMsg("preparedstatement::callSPInOut() - MySQL_PreparedStatement::*()"); std::string sp_code("CREATE PROCEDURE p(IN ver_in VARCHAR(25), OUT ver_out VARCHAR(25)) BEGIN SELECT ver_in INTO ver_out; END;"); try { if (!createSP(sp_code)) { logMsg("... skipping: cannot create SP"); return; } try { cstmt.reset(con->prepareCall("CALL p('myver', @version)")); ASSERT(!cstmt->execute()); } catch (sql::SQLException &e) { if (e.getErrorCode() != 1295) { logErr(e.what()); std::stringstream msg1; msg1.str(""); msg1 << "SQLState: " << e.getSQLState() << ", MySQL error code: " << e.getErrorCode(); logErr(msg1.str()); fail(e.what(), __FILE__, __LINE__); } // PS protocol does not support CALL logMsg("... skipping: PS protocol does not support CALL"); return; } pstmt.reset(con->prepareStatement("SELECT @version AS _version")); res.reset(pstmt->executeQuery()); ASSERT(res->next()); ASSERT_EQUALS("myver", res->getString("_version")); } catch (sql::SQLException &e) { logErr(e.what()); std::stringstream msg2; msg2.str(""); msg2 << "SQLState: " << e.getSQLState() << ", MySQL error code: " << e.getErrorCode(); logErr(msg2.str()); fail(e.what(), __FILE__, __LINE__); } } void preparedstatement::callSPWithPS() { logMsg("preparedstatement::callSPWithPS() - MySQL_PreparedStatement::*()"); try { std::string sp_code("CREATE PROCEDURE p(IN val VARCHAR(25)) BEGIN SET @sql = CONCAT('SELECT \"', val, '\"'); PREPARE stmt FROM @sql; EXECUTE stmt; DROP PREPARE stmt; END;"); if (!createSP(sp_code)) { logMsg("... skipping:"); return; } try { cstmt.reset(con->prepareCall("CALL p('abc')")); res.reset(cstmt->executeQuery()); } catch (sql::SQLException &e) { if (e.getErrorCode() != 1295) { logErr(e.what()); std::stringstream msg1; msg1.str(""); msg1 << "SQLState: " << e.getSQLState() << ", MySQL error code: " << e.getErrorCode(); logErr(msg1.str()); fail(e.what(), __FILE__, __LINE__); } // PS interface cannot call this kind of statement return; } ASSERT(res->next()); ASSERT_EQUALS("abc", res->getString(1)); std::stringstream msg2; msg2.str(""); msg2 << "... val = '" << res->getString(1) << "'"; logMsg(msg2.str()); while(cstmt->getMoreResults()) {} try { cstmt.reset(con->prepareCall("CALL p(?)")); cstmt->setString(1, "123"); res.reset(cstmt->executeQuery()); ASSERT(res->next()); ASSERT_EQUALS("123", res->getString(1)); ASSERT(!res->next()); } catch (sql::SQLException &e) { if (e.getErrorCode() != 1295) { logErr(e.what()); std::stringstream msg3; msg3.str(""); msg3 << "SQLState: " << e.getSQLState() << ", MySQL error code: " << e.getErrorCode(); logErr(msg3.str()); fail(e.what(), __FILE__, __LINE__); } // PS interface cannot call this kind of statement return; } res->close(); } catch (sql::SQLException &e) { if (e.getErrorCode() != 1295) { logErr(e.what()); std::stringstream msg4; msg4.str(""); msg4 << "SQLState: " << e.getSQLState() << ", MySQL error code: " << e.getErrorCode(); logErr(msg4.str()); fail(e.what(), __FILE__, __LINE__); } } } void preparedstatement::callSPMultiRes() { logMsg("preparedstatement::callSPMultiRes() - MySQL_PreparedStatement::*()"); try { std::string sp_code("CREATE PROCEDURE p() BEGIN SELECT 1; SELECT 2; SELECT 3; END;"); if (!createSP(sp_code)) { logMsg("... skipping:"); return; } try { pstmt.reset(con->prepareStatement("CALL p()")); ASSERT(pstmt->execute()); } catch (sql::SQLException &e) { if (e.getErrorCode() != 1295) { logErr(e.what()); std::stringstream msg1; msg1.str(""); msg1 << "SQLState: " << e.getSQLState() << ", MySQL error code: " << e.getErrorCode(); logErr(msg1.str()); fail(e.what(), __FILE__, __LINE__); } // PS interface cannot call this kind of statement return; } // Should not work prior to MySQL 6.0 std::stringstream msg2; msg2.str(""); do { res.reset(pstmt->getResultSet()); while (res->next()) { msg2 << res->getString(1); } } while (pstmt->getMoreResults()); ASSERT_EQUALS("123", msg2.str()); } catch (sql::SQLException &e) { logErr(e.what()); std::stringstream msg3; msg3.str(""); msg3 << "SQLState: " << e.getSQLState() << ", MySQL error code: " << e.getErrorCode(); logErr(msg3.str()); fail(e.what(), __FILE__, __LINE__); } } void preparedstatement::anonymousSelect() { logMsg("preparedstatement::anonymousSelect() - MySQL_PreparedStatement::*, MYSQL_PS_Resultset::*"); try { pstmt.reset(con->prepareStatement("SELECT ' ', NULL")); res.reset(pstmt->executeQuery()); ASSERT(res->next()); ASSERT_EQUALS(" ", res->getString(1)); std::string mynull(res->getString(2)); ASSERT(res->isNull(2)); ASSERT(res->wasNull()); } catch (sql::SQLException &e) { logErr(e.what()); logErr("SQLState: " + std::string(e.getSQLState())); fail(e.what(), __FILE__, __LINE__); } } void preparedstatement::crash() { logMsg("preparedstatement::crash() - MySQL_PreparedStatement::*"); try { stmt.reset(con->createStatement()); stmt->execute("DROP TABLE IF EXISTS test"); stmt->execute("CREATE TABLE test(dummy TIMESTAMP, id VARCHAR(1))"); pstmt.reset(con->prepareStatement("INSERT INTO test(id) VALUES (?)")); pstmt->clearParameters(); pstmt->setDouble(1, 1.23); ASSERT_EQUALS(1, pstmt->executeUpdate()); } catch (sql::SQLException &e) { logErr(e.what()); logErr("SQLState: " + e.getSQLState()); ASSERT_EQUALS("22001", e.getSQLState()); ASSERT_EQUALS(1406, e.getErrorCode()); logErr("Error of field overflow is expected"); } } void preparedstatement::getWarnings() { logMsg("preparedstatement::getWarnings() - MySQL_PreparedStatement::get|clearWarnings()"); //TODO: Enable it after fixing SKIP("Testcase needs to be fixed"); std::stringstream msg; stmt.reset(con->createStatement()); try { stmt->execute("DROP TABLE IF EXISTS test"); stmt->execute("CREATE TABLE test(id INT UNSIGNED)"); // Generating 2 warnings to make sure we get only the last 1 - won't hurt // Lets hope that this will always cause a 1264 or similar warning pstmt.reset(con->prepareStatement("INSERT INTO test(id) VALUES (?)")); pstmt->setInt(1, -2); pstmt->executeUpdate(); pstmt->setInt(1, -1); pstmt->executeUpdate(); int count= 0; for (const sql::SQLWarning* warn=pstmt->getWarnings(); warn; warn=warn->getNextWarning()) { msg.str(""); msg << "... ErrorCode = '" << warn->getErrorCode() << "', "; msg << "SQLState = '" << warn->getSQLState() << "', "; msg << "ErrorMessage = '" << warn->getMessage() << "'"; logMsg(msg.str()); ASSERT((0 != warn->getErrorCode())); if (1264 == warn->getErrorCode()) { ASSERT_EQUALS("22003", warn->getSQLState()); } else { ASSERT(("" != warn->getSQLState())); } ASSERT(("" != warn->getMessage())); ++count; } ASSERT_EQUALS(1, count); for (const sql::SQLWarning* warn=pstmt->getWarnings(); warn; warn=warn->getNextWarning()) { msg.str(""); msg << "... ErrorCode = '" << warn->getErrorCode() << "', "; msg << "SQLState = '" << warn->getSQLState() << "', "; msg << "ErrorMessage = '" << warn->getMessage() << "'"; logMsg(msg.str()); ASSERT((0 != warn->getErrorCode())); if (1264 == warn->getErrorCode()) { ASSERT_EQUALS("22003", warn->getSQLState()); } else { ASSERT(("" != warn->getSQLState())); } ASSERT(("" != warn->getMessage())); } pstmt->clearWarnings(); for (const sql::SQLWarning* warn=pstmt->getWarnings(); warn; warn=warn->getNextWarning()) { FAIL("There should be no more warnings!"); } pstmt->setInt(1, -3); pstmt->executeUpdate(); ASSERT(pstmt->getWarnings() != NULL); // Statement without tables access does not reset warnings. pstmt.reset(con->prepareStatement("SELECT 1")); res.reset(pstmt->executeQuery()); ASSERT(pstmt->getWarnings() == NULL); ASSERT(res->next()); // TODO - how to use getNextWarning() ? 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 preparedstatement::blob() { logMsg("preparedstatement::blob() - MySQL_PreparedStatement::*"); char blob_input[512]; std::stringstream blob_input_stream; std::stringstream msg; char blob_output[512]; int id; int offset=0; try { pstmt.reset(con->prepareStatement("DROP TABLE IF EXISTS test")); pstmt->execute(); pstmt.reset(con->prepareStatement("CREATE TABLE test(id INT, col1 TINYBLOB, col2 TINYBLOB)")); pstmt->execute(); // Most basic INSERT/SELECT... pstmt.reset(con->prepareStatement("INSERT INTO test(id, col1) VALUES (?, ?)")); for (char ascii_code=CHAR_MIN; ascii_code < CHAR_MAX; ascii_code++) { blob_output[offset]='\0'; blob_input[offset++]=ascii_code; } blob_input[offset]='\0'; blob_output[offset]='\0'; for (char ascii_code=CHAR_MAX; ascii_code > CHAR_MIN; ascii_code--) { blob_output[offset]='\0'; blob_input[offset++]=ascii_code; } blob_input[offset]='\0'; blob_output[offset]='\0'; id=1; blob_input_stream << blob_input; pstmt->setInt(1, id); pstmt->setBlob(2, &blob_input_stream); try { pstmt->setBlob(3, &blob_input_stream); FAIL("Invalid index not detected"); } catch (sql::SQLException&) { } pstmt->execute(); pstmt.reset(con->prepareStatement("SELECT id, col1 FROM test WHERE id = 1")); res.reset(pstmt->executeQuery()); ASSERT(res->next()); msg.str(""); msg << "... simple INSERT/SELECT, '" << std::endl << blob_input << std::endl << "' =? '" << std::endl << res->getString(2) << "'"; logMsg(msg.str()); ASSERT_EQUALS(res->getInt(1), id); ASSERT_EQUALS(res->getString(2), blob_input_stream.str()); ASSERT_EQUALS(res->getString(2), blob_input); ASSERT_EQUALS(res->getString("col1"), blob_input_stream.str()); ASSERT_EQUALS(res->getString("col1"), blob_input); std::unique_ptr< std::istream > blob_output_stream(res->getBlob(2)); blob_output_stream->seekg(std::ios::beg); blob_output_stream->get(blob_output, offset + 1); ASSERT_EQUALS(blob_input_stream.str(), blob_output); blob_output_stream.reset(res->getBlob("col1")); blob_output_stream->seekg(std::ios::beg); blob_output_stream->get(blob_output, offset + 1); ASSERT_EQUALS(blob_input, blob_output); msg.str(""); msg << "... second check, '"<< std::endl << blob_input << std::endl << "' =? '" << std::endl << blob_output << "'"; logMsg(msg.str()); ASSERT(!res->next()); res->close(); msg.str(""); // Data is too long to be stored in a TINYBLOB column msg << "... this is more than TINYBLOB can hold: "; msg << "01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"; pstmt.reset(con->prepareStatement("INSERT INTO test(id, col1) VALUES (?, ?)")); id=2; pstmt->setInt(1, id); pstmt->setBlob(2, &msg); try { pstmt->execute(); pstmt.reset(con->prepareStatement("SELECT id, col1 FROM test WHERE id = 2")); res.reset(pstmt->executeQuery()); ASSERT(res->next()); ASSERT_EQUALS(res->getInt(1), id); ASSERT_GT((int)(res->getString(2).length()), (int)(msg.str().length())); ASSERT(!res->next()); res->close(); msg << "- what has happened to the stream?"; logMsg(msg.str()); } catch (sql::SQLException & e) { // If the server is in the strict mode, inserting too long data causes error if (e.getErrorCode() == 1406 && e.getSQLState().compare("22001") == 0) { logMsg("The server is in the strict mode - couldn't insert too long stream"); } else { logErr(e.what()); logErr("SQLState: " + std::string(e.getSQLState())); fail(e.what(), __FILE__, __LINE__); } } pstmt.reset(con->prepareStatement("DROP TABLE IF EXISTS test")); pstmt->execute(); } catch (sql::SQLException &e) { logErr(e.what()); logErr("SQLState: " + std::string(e.getSQLState())); fail(e.what(), __FILE__, __LINE__); } } void preparedstatement::executeQuery() { logMsg("preparedstatement::executeQuery() - MySQL_PreparedStatement::executeQuery"); try { const sql::SQLString option("defaultPreparedStatementResultType"); int value=sql::ResultSet::TYPE_FORWARD_ONLY; con->setClientOption(option, static_cast (&value)); } catch (sql::MethodNotImplementedException &/*e*/) { /* not available */ return; } try { stmt.reset(con->createStatement()); stmt->execute("DROP TABLE IF EXISTS test"); stmt->execute("CREATE TABLE test(id INT UNSIGNED)"); stmt->execute("INSERT INTO test(id) VALUES (1), (2), (3)"); pstmt.reset(con->prepareStatement("SELECT id FROM test ORDER BY id ASC")); res.reset(pstmt->executeQuery()); ASSERT(res->next()); ASSERT_EQUALS(res->getInt("id"), 1); pstmt.reset(con->prepareStatement("DROP TABLE IF EXISTS test")); pstmt->execute(); } catch (sql::SQLException &e) { logErr(e.what()); std::stringstream msg; msg.str(""); msg << "SQLState: " << e.getSQLState() << ", MySQL error code: " << e.getErrorCode(); logErr(msg.str()); fail(e.what(), __FILE__, __LINE__); } } void preparedstatement::addBatch() { stmt->executeUpdate("DROP TABLE IF EXISTS testAddBatchPs"); stmt->executeUpdate("CREATE TABLE testAddBatchPs " "(id int not NULL)"); pstmt.reset(con->prepareStatement("INSERT INTO testAddBatchPs VALUES(?)")); pstmt->setInt(1, 1); pstmt->addBatch(); pstmt->setInt(1, 2); pstmt->addBatch(); pstmt->setInt(1, 3); pstmt->addBatch(); const sql::Ints& batchRes = pstmt->executeBatch(); res.reset(stmt->executeQuery("SELECT MIN(id), MAX(id), SUM(id), count(*) FROM testAddBatchPs")); ASSERT(res->next()); ASSERT_EQUALS(1, res->getInt(1)); ASSERT_EQUALS(3, res->getInt(2)); ASSERT_EQUALS(6, res->getInt(3)); ASSERT_EQUALS(3, res->getInt(4)); ASSERT_EQUALS(3ULL, static_cast(batchRes.size())); ASSERT_EQUALS(1, batchRes[0]); ASSERT_EQUALS(1, batchRes[1]); ASSERT_EQUALS(1, batchRes[2]); ////// The same, but for executeLargeBatch stmt->executeUpdate("DELETE FROM testAddBatchPs"); pstmt->clearBatch(); pstmt->clearParameters(); pstmt->setInt(1, 4); pstmt->addBatch(); pstmt->setInt(1, 5); pstmt->addBatch(); pstmt->setInt(1, 6); pstmt->addBatch(); const sql::Longs& batchLRes = pstmt->executeLargeBatch(); res.reset(stmt->executeQuery("SELECT MIN(id), MAX(id), SUM(id), count(*) FROM testAddBatchPs")); ASSERT(res->next()); ASSERT_EQUALS(4, res->getInt(1)); ASSERT_EQUALS(6, res->getInt(2)); ASSERT_EQUALS(15, res->getInt(3)); ASSERT_EQUALS(3, res->getInt(4)); ASSERT_EQUALS(3ULL, static_cast(batchLRes.size())); ASSERT_EQUALS(1LL, batchLRes[0]); ASSERT_EQUALS(1LL, batchLRes[1]); ASSERT_EQUALS(1LL, batchLRes[2]); stmt->executeUpdate("DROP TABLE testAddBatchPs"); } } /* namespace preparedstatement */ } /* namespace testsuite */