From ea16e525b57c1b8b9d9d53ca0b291b42567beba6 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 11 Apr 2006 02:10:08 +1000 Subject: [PATCH 01/65] BUG#18966 Change in stop/shutdown behaviour fix behaviour of ALL STOP and SHUTDOWN in relation to MGM nodes ndb/src/mgmclient/CommandInterpreter.cpp: Shutdown management servers properly (one we're connected to last). Fix potential problems if disconnect from one mgmd and connect to another (that's a different cluster) ndb/src/mgmsrv/MgmtSrvr.cpp: fix regression in how nodes are stopped ALL STOP - db nodes only SHUTDOWN - db and mgm nodes fix race where mgmd could stop itself before it sends the response to stop. In ~MgmApiSession() we now do the disconnect. We have (in various places) a return stopSelf which tells the caller if they need to stop or restart this daemon. ndb/src/mgmsrv/MgmtSrvr.hpp: add stopSelf return parameter to stopNodes and restartNodes. Rename stop to shutdownDB as this name better reflects what it does Rename restart to restartDB as this name better reflects what it does ndb/src/mgmsrv/Services.cpp: Stop or restart server only on mgm session disconnect (of session that asked us to) ndb/src/mgmsrv/Services.hpp: add m_stopSelf member for tracking what we should do --- ndb/src/mgmclient/CommandInterpreter.cpp | 250 +++++++++++++---------- ndb/src/mgmsrv/MgmtSrvr.cpp | 73 ++++--- ndb/src/mgmsrv/MgmtSrvr.hpp | 20 +- ndb/src/mgmsrv/Services.cpp | 15 +- ndb/src/mgmsrv/Services.hpp | 1 + 5 files changed, 205 insertions(+), 154 deletions(-) diff --git a/ndb/src/mgmclient/CommandInterpreter.cpp b/ndb/src/mgmclient/CommandInterpreter.cpp index 39c84fd8055..0fdb81989b3 100644 --- a/ndb/src/mgmclient/CommandInterpreter.cpp +++ b/ndb/src/mgmclient/CommandInterpreter.cpp @@ -162,6 +162,7 @@ private: NdbMgmHandle m_mgmsrv; NdbMgmHandle m_mgmsrv2; + const char *m_constr; bool m_connected; int m_verbose; int try_reconnect; @@ -390,22 +391,7 @@ convert(const char* s, int& val) { CommandInterpreter::CommandInterpreter(const char *_host,int verbose) : m_verbose(verbose) { - m_mgmsrv = ndb_mgm_create_handle(); - if(m_mgmsrv == NULL) { - ndbout_c("Cannot create handle to management server."); - exit(-1); - } - m_mgmsrv2 = ndb_mgm_create_handle(); - if(m_mgmsrv2 == NULL) { - ndbout_c("Cannot create 2:nd handle to management server."); - exit(-1); - } - if (ndb_mgm_set_connectstring(m_mgmsrv, _host)) - { - printError(); - exit(-1); - } - + m_constr= _host; m_connected= false; m_event_thread= 0; try_reconnect = 0; @@ -422,8 +408,6 @@ CommandInterpreter::CommandInterpreter(const char *_host,int verbose) CommandInterpreter::~CommandInterpreter() { disconnect(); - ndb_mgm_destroy_handle(&m_mgmsrv); - ndb_mgm_destroy_handle(&m_mgmsrv2); } static bool @@ -447,7 +431,6 @@ CommandInterpreter::printError() { if (ndb_mgm_check_connection(m_mgmsrv)) { - m_connected= false; disconnect(); } ndbout_c("* %5d: %s", @@ -497,78 +480,97 @@ event_thread_run(void* m) } bool -CommandInterpreter::connect() +CommandInterpreter::connect() { DBUG_ENTER("CommandInterpreter::connect"); - if(!m_connected) + + if(m_connected) + DBUG_RETURN(m_connected); + + m_mgmsrv = ndb_mgm_create_handle(); + if(m_mgmsrv == NULL) { + ndbout_c("Cannot create handle to management server."); + exit(-1); + } + m_mgmsrv2 = ndb_mgm_create_handle(); + if(m_mgmsrv2 == NULL) { + ndbout_c("Cannot create 2:nd handle to management server."); + exit(-1); + } + + if (ndb_mgm_set_connectstring(m_mgmsrv, m_constr)) { - if(!ndb_mgm_connect(m_mgmsrv, try_reconnect-1, 5, 1)) + printError(); + exit(-1); + } + + if(ndb_mgm_connect(m_mgmsrv, try_reconnect-1, 5, 1)) + DBUG_RETURN(m_connected); // couldn't connect, always false + + const char *host= ndb_mgm_get_connected_host(m_mgmsrv); + unsigned port= ndb_mgm_get_connected_port(m_mgmsrv); + BaseString constr; + constr.assfmt("%s:%d",host,port); + if(!ndb_mgm_set_connectstring(m_mgmsrv2, constr.c_str()) && + !ndb_mgm_connect(m_mgmsrv2, try_reconnect-1, 5, 1)) + { + DBUG_PRINT("info",("2:ndb connected to Management Server ok at: %s:%d", + host, port)); + assert(m_event_thread == 0); + assert(do_event_thread == 0); + do_event_thread= 0; + m_event_thread = NdbThread_Create(event_thread_run, + (void**)&m_mgmsrv2, + 32768, + "CommandInterpreted_event_thread", + NDB_THREAD_PRIO_LOW); + if (m_event_thread != 0) { - const char *host= ndb_mgm_get_connected_host(m_mgmsrv); - unsigned port= ndb_mgm_get_connected_port(m_mgmsrv); - BaseString constr; - constr.assfmt("%s:%d",host,port); - if(!ndb_mgm_set_connectstring(m_mgmsrv2, constr.c_str()) && - !ndb_mgm_connect(m_mgmsrv2, try_reconnect-1, 5, 1)) + DBUG_PRINT("info",("Thread created ok, waiting for started...")); + int iter= 1000; // try for 30 seconds + while(do_event_thread == 0 && + iter-- > 0) + NdbSleep_MilliSleep(30); + } + if (m_event_thread == 0 || + do_event_thread == 0 || + do_event_thread == -1) + { + DBUG_PRINT("info",("Warning, event thread startup failed, " + "degraded printouts as result, errno=%d", + errno)); + printf("Warning, event thread startup failed, " + "degraded printouts as result, errno=%d\n", errno); + do_event_thread= 0; + if (m_event_thread) { - DBUG_PRINT("info",("2:ndb connected to Management Server ok at: %s:%d", - host, port)); - assert(m_event_thread == 0); - assert(do_event_thread == 0); - do_event_thread= 0; - m_event_thread = NdbThread_Create(event_thread_run, - (void**)&m_mgmsrv2, - 32768, - "CommandInterpreted_event_thread", - NDB_THREAD_PRIO_LOW); - if (m_event_thread != 0) - { - DBUG_PRINT("info",("Thread created ok, waiting for started...")); - int iter= 1000; // try for 30 seconds - while(do_event_thread == 0 && - iter-- > 0) - NdbSleep_MilliSleep(30); - } - if (m_event_thread == 0 || - do_event_thread == 0 || - do_event_thread == -1) - { - DBUG_PRINT("info",("Warning, event thread startup failed, " - "degraded printouts as result, errno=%d", - errno)); - printf("Warning, event thread startup failed, " - "degraded printouts as result, errno=%d\n", errno); - do_event_thread= 0; - if (m_event_thread) - { - void *res; - NdbThread_WaitFor(m_event_thread, &res); - NdbThread_Destroy(&m_event_thread); - } - ndb_mgm_disconnect(m_mgmsrv2); - } - } - else - { - DBUG_PRINT("warning", - ("Could not do 2:nd connect to mgmtserver for event listening")); - DBUG_PRINT("info", ("code: %d, msg: %s", - ndb_mgm_get_latest_error(m_mgmsrv2), - ndb_mgm_get_latest_error_msg(m_mgmsrv2))); - printf("Warning, event connect failed, degraded printouts as result\n"); - printf("code: %d, msg: %s\n", - ndb_mgm_get_latest_error(m_mgmsrv2), - ndb_mgm_get_latest_error_msg(m_mgmsrv2)); - } - m_connected= true; - DBUG_PRINT("info",("Connected to Management Server at: %s:%d", host, port)); - if (m_verbose) - { - printf("Connected to Management Server at: %s:%d\n", - host, port); + void *res; + NdbThread_WaitFor(m_event_thread, &res); + NdbThread_Destroy(&m_event_thread); } + ndb_mgm_disconnect(m_mgmsrv2); } } + else + { + DBUG_PRINT("warning", + ("Could not do 2:nd connect to mgmtserver for event listening")); + DBUG_PRINT("info", ("code: %d, msg: %s", + ndb_mgm_get_latest_error(m_mgmsrv2), + ndb_mgm_get_latest_error_msg(m_mgmsrv2))); + printf("Warning, event connect failed, degraded printouts as result\n"); + printf("code: %d, msg: %s\n", + ndb_mgm_get_latest_error(m_mgmsrv2), + ndb_mgm_get_latest_error_msg(m_mgmsrv2)); + } + m_connected= true; + DBUG_PRINT("info",("Connected to Management Server at: %s:%d", host, port)); + if (m_verbose) + { + printf("Connected to Management Server at: %s:%d\n", + host, port); + } + DBUG_RETURN(m_connected); } @@ -576,20 +578,18 @@ bool CommandInterpreter::disconnect() { DBUG_ENTER("CommandInterpreter::disconnect"); + if (m_event_thread) { void *res; do_event_thread= 0; NdbThread_WaitFor(m_event_thread, &res); NdbThread_Destroy(&m_event_thread); m_event_thread= 0; - ndb_mgm_disconnect(m_mgmsrv2); + ndb_mgm_destroy_handle(&m_mgmsrv2); } if (m_connected) { - if (ndb_mgm_disconnect(m_mgmsrv) == -1) { - ndbout_c("Could not disconnect from management server"); - printError(); - } + ndb_mgm_destroy_handle(&m_mgmsrv); m_connected= false; } DBUG_RETURN(true); @@ -1066,28 +1066,39 @@ CommandInterpreter::executeShutdown(char* parameters) ndbout << result << " NDB Cluster node(s) have shutdown." << endl; - int mgm_id= 0; - mgm_id= ndb_mgm_get_mgmd_nodeid(m_mgmsrv); - if (mgm_id == 0) + int nodeId= 0; + int this_mgmd= 0; + this_mgmd= ndb_mgm_get_mgmd_nodeid(m_mgmsrv); + while(get_next_nodeid(state, &nodeId, NDB_MGM_NODE_TYPE_MGM)) { - ndbout << "Unable to locate management server, " - << "shutdown manually with STOP" - << endl; - return 1; + if(nodeId==this_mgmd) + continue; + ndbout << "Shutting down NDB Cluster management server nodeId=" + << nodeId << "..."; + result = ndb_mgm_stop(m_mgmsrv, 1, &nodeId); + if (result <= 0) { + ndbout << " failed." << endl; + printError(); + } + else + ndbout << "Done." << endl; } - result = ndb_mgm_stop(m_mgmsrv, 1, &mgm_id); + ndbout << "Shutting down NDB Cluster management server nodeId=" + << this_mgmd << "..."; + result= ndb_mgm_stop(m_mgmsrv, 1, &this_mgmd); if (result <= 0) { - ndbout << "Shutdown of NDB Cluster management server failed." << endl; + ndbout << " failed." << endl; printError(); - if (result == 0) - return 1; - return result; } - - m_connected= false; - disconnect(); - ndbout << "NDB Cluster management server shutdown." << endl; + else + { + ndbout << "Done." << endl; + ndbout << "Disconnecting to allow management server to shutdown." + << endl; + disconnect(); + } + ndbout << "NDB Cluster management servers shutdown." << endl; return 0; } @@ -1311,12 +1322,7 @@ CommandInterpreter::executeConnect(char* parameters) { disconnect(); if (!emptyString(parameters)) { - if (ndb_mgm_set_connectstring(m_mgmsrv, - BaseString(parameters).trim().c_str())) - { - printError(); - return; - } + m_constr= BaseString(parameters).trim().c_str(); } connect(); } @@ -1507,10 +1513,25 @@ CommandInterpreter::executeStop(Vector &command_list, ndbout_c("NDB Cluster has shutdown."); else { + int mgm_id= 0; + int need_reconnect= 0; + mgm_id= ndb_mgm_get_mgmd_nodeid(m_mgmsrv); ndbout << "Node"; for (int i= 0; i < no_of_nodes; i++) - ndbout << " " << node_ids[i]; + { + if(node_ids[i] == mgm_id) + need_reconnect= 1; + else + ndbout << " " << node_ids[i]; + } ndbout_c(" has shutdown."); + if(need_reconnect) + { + ndbout << "You are connected to node " << mgm_id + << ", disconnecting to allow it to shutdown" + << endl; + disconnect(); + } } } } @@ -1640,9 +1661,16 @@ CommandInterpreter::executeRestart(Vector &command_list, ndbout_c("NDB Cluster is being restarted."); else { + int mgm_id= 0; + mgm_id= ndb_mgm_get_mgmd_nodeid(m_mgmsrv); + ndbout << "Node"; for (int i= 0; i < no_of_nodes; i++) + { + if(node_ids[i] == mgm_id) + disconnect(); ndbout << " " << node_ids[i]; + } ndbout_c(" is being restarted"); } } diff --git a/ndb/src/mgmsrv/MgmtSrvr.cpp b/ndb/src/mgmsrv/MgmtSrvr.cpp index 9b518ba938b..4c3631488e6 100644 --- a/ndb/src/mgmsrv/MgmtSrvr.cpp +++ b/ndb/src/mgmsrv/MgmtSrvr.cpp @@ -60,9 +60,6 @@ #include -extern bool g_StopServer; -extern bool g_RestartServer; - //#define MGM_SRV_DEBUG #ifdef MGM_SRV_DEBUG #define DEBUG(x) do ndbout << x << endl; while(0) @@ -932,6 +929,13 @@ int MgmtSrvr::sendStopMgmd(NodeId nodeId, * client connection to that mgmd and stop it that way. * This allows us to stop mgm servers when there isn't any real * distributed communication up. + * + * node_ids.size()==0 means to stop all DB nodes. + * MGM nodes will *NOT* be stopped. + * + * If we work out we should be stopping or restarting ourselves, + * we return <0 in stopSelf for restart, >0 for stop + * and 0 for do nothing. */ int MgmtSrvr::sendSTOP_REQ(const Vector &node_ids, @@ -941,7 +945,8 @@ int MgmtSrvr::sendSTOP_REQ(const Vector &node_ids, bool stop, bool restart, bool nostart, - bool initialStart) + bool initialStart, + int* stopSelf) { int error = 0; DBUG_ENTER("MgmtSrvr::sendSTOP_REQ"); @@ -990,12 +995,13 @@ int MgmtSrvr::sendSTOP_REQ(const Vector &node_ids, NodeId nodeId= 0; int use_master_node= 0; int do_send= 0; - int do_stop_self= 0; + *stopSelf= 0; NdbNodeBitmask nodes_to_stop; { for (unsigned i= 0; i < node_ids.size(); i++) { nodeId= node_ids[i]; + ndbout << "asked to stop " << nodeId << endl; if (getNodeType(nodeId) != NDB_MGM_NODE_TYPE_MGM) nodes_to_stop.set(nodeId); else if (nodeId != getOwnNodeId()) @@ -1006,7 +1012,11 @@ int MgmtSrvr::sendSTOP_REQ(const Vector &node_ids, stoppedNodes.set(nodeId); } else - do_stop_self= 1;; + { + ndbout << "which is me" << endl; + *stopSelf= (restart)? -1 : 1; + stoppedNodes.set(nodeId); + } } } int no_of_nodes_to_stop= nodes_to_stop.count(); @@ -1039,14 +1049,6 @@ int MgmtSrvr::sendSTOP_REQ(const Vector &node_ids, nodes.set(nodeId); } } - nodeId= 0; - while(getNextNodeId(&nodeId, NDB_MGM_NODE_TYPE_MGM)) - { - if(nodeId==getOwnNodeId()) - continue; - if(sendStopMgmd(nodeId, abort, stop, restart, nostart, initialStart)==0) - stoppedNodes.set(nodeId); - } } // now wait for the replies @@ -1153,11 +1155,9 @@ int MgmtSrvr::sendSTOP_REQ(const Vector &node_ids, DBUG_RETURN(SEND_OR_RECEIVE_FAILED); } } - if (!error && do_stop_self) + if (error && *stopSelf) { - if (restart) - g_RestartServer= true; - g_StopServer= true; + *stopSelf= 0; } DBUG_RETURN(error); } @@ -1167,7 +1167,7 @@ int MgmtSrvr::sendSTOP_REQ(const Vector &node_ids, */ int MgmtSrvr::stopNodes(const Vector &node_ids, - int *stopCount, bool abort) + int *stopCount, bool abort, int* stopSelf) { if (!abort) { @@ -1189,20 +1189,25 @@ int MgmtSrvr::stopNodes(const Vector &node_ids, false, false, false, - false); + false, + stopSelf); if (stopCount) *stopCount= nodes.count(); return ret; } /* - * Perform system shutdown + * Perform DB nodes shutdown. + * MGM servers are left in their current state */ -int MgmtSrvr::stop(int * stopCount, bool abort) +int MgmtSrvr::shutdownDB(int * stopCount, bool abort) { NodeBitmask nodes; Vector node_ids; + + int tmp; + int ret = sendSTOP_REQ(node_ids, nodes, 0, @@ -1210,7 +1215,8 @@ int MgmtSrvr::stop(int * stopCount, bool abort) true, false, false, - false); + false, + &tmp); if (stopCount) *stopCount = nodes.count(); return ret; @@ -1235,6 +1241,7 @@ int MgmtSrvr::enterSingleUser(int * stopCount, Uint32 singleUserNodeId) } NodeBitmask nodes; Vector node_ids; + int stopSelf; int ret = sendSTOP_REQ(node_ids, nodes, singleUserNodeId, @@ -1242,7 +1249,8 @@ int MgmtSrvr::enterSingleUser(int * stopCount, Uint32 singleUserNodeId) false, false, false, - false); + false, + &stopSelf); if (stopCount) *stopCount = nodes.count(); return ret; @@ -1254,7 +1262,8 @@ int MgmtSrvr::enterSingleUser(int * stopCount, Uint32 singleUserNodeId) int MgmtSrvr::restartNodes(const Vector &node_ids, int * stopCount, bool nostart, - bool initialStart, bool abort) + bool initialStart, bool abort, + int *stopSelf) { NodeBitmask nodes; int ret= sendSTOP_REQ(node_ids, @@ -1264,21 +1273,24 @@ int MgmtSrvr::restartNodes(const Vector &node_ids, false, true, nostart, - initialStart); + initialStart, + stopSelf); if (stopCount) *stopCount = nodes.count(); return ret; } /* - * Perform system restart + * Perform restart of all DB nodes */ -int MgmtSrvr::restart(bool nostart, bool initialStart, - bool abort, int * stopCount ) +int MgmtSrvr::restartDB(bool nostart, bool initialStart, + bool abort, int * stopCount) { NodeBitmask nodes; Vector node_ids; + int tmp; + int ret = sendSTOP_REQ(node_ids, nodes, 0, @@ -1286,7 +1298,8 @@ int MgmtSrvr::restart(bool nostart, bool initialStart, true, true, true, - initialStart); + initialStart, + &tmp); if (ret) return ret; diff --git a/ndb/src/mgmsrv/MgmtSrvr.hpp b/ndb/src/mgmsrv/MgmtSrvr.hpp index fe1603a1953..b47ee69fd9f 100644 --- a/ndb/src/mgmsrv/MgmtSrvr.hpp +++ b/ndb/src/mgmsrv/MgmtSrvr.hpp @@ -253,12 +253,13 @@ public: * @param processId: Id of the DB process to stop * @return 0 if succeeded, otherwise: as stated above, plus: */ - int stopNodes(const Vector &node_ids, int *stopCount, bool abort); + int stopNodes(const Vector &node_ids, int *stopCount, bool abort, + int *stopSelf); /** - * Stop the system + * shutdown the DB nodes */ - int stop(int * cnt = 0, bool abort = false); + int shutdownDB(int * cnt = 0, bool abort = false); /** * print version info about a node @@ -292,14 +293,14 @@ public: */ int restartNodes(const Vector &node_ids, int *stopCount, bool nostart, - bool initialStart, bool abort); + bool initialStart, bool abort, int *stopSelf); /** - * Restart the system + * Restart all DB nodes */ - int restart(bool nostart, bool initialStart, - bool abort = false, - int * stopCount = 0); + int restartDB(bool nostart, bool initialStart, + bool abort = false, + int * stopCount = 0); struct BackupEvent { enum Event { @@ -503,7 +504,8 @@ private: bool stop, bool restart, bool nostart, - bool initialStart); + bool initialStart, + int *stopSelf); /** * Check if it is possible to send a signal to a (DB) process diff --git a/ndb/src/mgmsrv/Services.cpp b/ndb/src/mgmsrv/Services.cpp index a80827abd8f..a6c483fcdcd 100644 --- a/ndb/src/mgmsrv/Services.cpp +++ b/ndb/src/mgmsrv/Services.cpp @@ -35,6 +35,7 @@ #include extern bool g_StopServer; +extern bool g_RestartServer; extern EventLogger g_eventLogger; static const unsigned int MAX_READ_TIMEOUT = 1000 ; @@ -267,6 +268,7 @@ MgmApiSession::MgmApiSession(class MgmtSrvr & mgm, NDB_SOCKET_TYPE sock) m_output = new SocketOutputStream(sock); m_parser = new Parser_t(commands, *m_input, true, true, true); m_allocated_resources= new MgmtSrvr::Allocated_resources(m_mgmsrv); + m_stopSelf= 0; DBUG_VOID_RETURN; } @@ -286,6 +288,10 @@ MgmApiSession::~MgmApiSession() NDB_CLOSE_SOCKET(m_socket); m_socket= NDB_INVALID_SOCKET; } + if(m_stopSelf < 0) + g_RestartServer= true; + if(m_stopSelf) + g_StopServer= true; DBUG_VOID_RETURN; } @@ -870,7 +876,8 @@ MgmApiSession::restart(Parser::Context &, &restarted, nostart != 0, initialstart != 0, - abort != 0); + abort != 0, + &m_stopSelf); m_output->println("restart reply"); if(result != 0){ @@ -894,7 +901,7 @@ MgmApiSession::restartAll(Parser::Context &, args.get("nostart", &nostart); int count = 0; - int result = m_mgmsrv.restart(nostart, initialstart, abort, &count); + int result = m_mgmsrv.restartDB(nostart, initialstart, abort, &count); m_output->println("restart reply"); if(result != 0) @@ -1013,7 +1020,7 @@ MgmApiSession::stop(Parser::Context &, int stopped= 0; int result= 0; if (nodes.size()) - result= m_mgmsrv.stopNodes(nodes, &stopped, abort != 0); + result= m_mgmsrv.stopNodes(nodes, &stopped, abort != 0, &m_stopSelf); m_output->println("stop reply"); if(result != 0) @@ -1032,7 +1039,7 @@ MgmApiSession::stopAll(Parser::Context &, Uint32 abort; args.get("abort", &abort); - int result = m_mgmsrv.stop(&stopped, abort != 0); + int result = m_mgmsrv.shutdownDB(&stopped, abort != 0); m_output->println("stop reply"); if(result != 0) diff --git a/ndb/src/mgmsrv/Services.hpp b/ndb/src/mgmsrv/Services.hpp index f97223750a1..b8a73f5e4bc 100644 --- a/ndb/src/mgmsrv/Services.hpp +++ b/ndb/src/mgmsrv/Services.hpp @@ -40,6 +40,7 @@ private: Parser_t *m_parser; MgmtSrvr::Allocated_resources *m_allocated_resources; char m_err_str[1024]; + int m_stopSelf; // -1 is restart, 0 do nothing, 1 stop void getConfig_common(Parser_t::Context &ctx, const class Properties &args, From 51e0c5187b9615d40134067adee9c96f6bc635ce Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 5 May 2006 10:45:58 +0400 Subject: [PATCH 02/65] WL#3153 "Split logs". Recommit with post-review fixes sql/ha_ndbcluster_binlog.cc: use MYSQL_BIN_LOG instead of MYSQL_LOG sql/log.cc: Split MYSQL_LOG into base MYSQL_LOG and MYSQL_GENERAL_LOG, MYSQL_SLOW_LOG, MYSQL_BIN_LOG sql/log.h: Split MYSQL_LOG into base MYSQL_LOG and MYSQL_GENERAL_LOG, MYSQL_SLOW_LOG, MYSQL_BIN_LOG sql/log_event.h: use MYSQL_BIN_LOG instead of MYSQL_LOG sql/mysql_priv.h: use MYSQL_BIN_LOG instead of MYSQL_LOG sql/mysqld.cc: fix appropriate comments: use MYSQL_BIN_LOG instead of MYSQL_LOG sql/rpl_injector.cc: use MYSQL_BIN_LOG instead of MYSQL_LOG sql/rpl_injector.h: use MYSQL_BIN_LOG instead of MYSQL_LOG sql/rpl_rli.h: use MYSQL_BIN_LOG instead of MYSQL_LOG sql/slave.cc: Fix appropriate comments: use MYSQL_BIN_LOG instead of MYSQL_LOG. Fix usage of new_file(): now we don't need to pass locking-related info to the function. sql/slave.h: Use MYSQL_BIN_LOG instead of MYSQL_LOG in appropriate comments --- sql/ha_ndbcluster_binlog.cc | 4 +- sql/log.cc | 1002 ++++++++++++++++++++--------------- sql/log.h | 135 +++-- sql/log_event.h | 2 +- sql/mysql_priv.h | 2 +- sql/mysqld.cc | 6 +- sql/rpl_injector.cc | 2 +- sql/rpl_injector.h | 4 +- sql/rpl_rli.h | 2 +- sql/slave.cc | 7 +- sql/slave.h | 4 +- 11 files changed, 690 insertions(+), 480 deletions(-) diff --git a/sql/ha_ndbcluster_binlog.cc b/sql/ha_ndbcluster_binlog.cc index ac4b67928b1..637388dc441 100644 --- a/sql/ha_ndbcluster_binlog.cc +++ b/sql/ha_ndbcluster_binlog.cc @@ -428,7 +428,7 @@ static void ndbcluster_binlog_wait(THD *thd) } /* - Called from MYSQL_LOG::reset_logs in log.cc when binlog is emptied + Called from MYSQL_BIN_LOG::reset_logs in log.cc when binlog is emptied */ static int ndbcluster_reset_logs(THD *thd) { @@ -452,7 +452,7 @@ static int ndbcluster_reset_logs(THD *thd) } /* - Called from MYSQL_LOG::purge_logs in log.cc when the binlog "file" + Called from MYSQL_BIN_LOG::purge_logs in log.cc when the binlog "file" is removed */ diff --git a/sql/log.cc b/sql/log.cc index e9a5011db4d..d1c33afcda7 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -42,7 +42,7 @@ extern char *opt_logname, *opt_slow_logname; LOGGER logger; -MYSQL_LOG mysql_bin_log; +MYSQL_BIN_LOG mysql_bin_log; ulong sync_binlog_counter= 0; static bool test_if_number(const char *str, @@ -557,9 +557,8 @@ void Log_to_file_event_handler::cleanup() void Log_to_file_event_handler::flush() { - /* reopen log files */ - mysql_log.new_file(1); - mysql_slow_log.new_file(1); + mysql_log.reopen_file(); + mysql_slow_log.reopen_file(); } /* @@ -1054,7 +1053,7 @@ void Log_to_csv_event_handler:: /* this function is mostly a placeholder. - conceptually, binlog initialization (now mostly done in MYSQL_LOG::open) + conceptually, binlog initialization (now mostly done in MYSQL_BIN_LOG::open) should be moved here. */ @@ -1128,7 +1127,7 @@ static int binlog_prepare(THD *thd, bool all) do nothing. just pretend we can do 2pc, so that MySQL won't switch to 1pc. - real work will be done in MYSQL_LOG::log() + real work will be done in MYSQL_BIN_LOG::log() */ return 0; } @@ -1144,7 +1143,7 @@ static int binlog_commit(THD *thd, bool all) if (trx_data->empty()) { - // we're here because trans_log was flushed in MYSQL_LOG::log() + // we're here because trans_log was flushed in MYSQL_BIN_LOG::log() DBUG_RETURN(0); } Query_log_event qev(thd, STRING_WITH_LEN("COMMIT"), TRUE, FALSE); @@ -1382,12 +1381,125 @@ static int find_uniq_filename(char *name) } +void MYSQL_LOG::init(enum_log_type log_type_arg, + enum cache_type io_cache_type_arg) +{ + DBUG_ENTER("MYSQL_LOG::init"); + log_type= log_type_arg; + io_cache_type= io_cache_type_arg; + DBUG_PRINT("info",("log_type: %d", log_type)); + DBUG_VOID_RETURN; +} + + +/* + Open a (new) log file. + + SYNOPSIS + open() + + log_name The name of the log to open + log_type_arg The type of the log. E.g. LOG_NORMAL + new_name The new name for the logfile. This is only needed + when the method is used to open the binlog file. + io_cache_type_arg The type of the IO_CACHE to use for this log file + + DESCRIPTION + Open the logfile, init IO_CACHE and write startup messages + (in case of general and slow query logs). + + RETURN VALUES + 0 ok + 1 error +*/ + +bool MYSQL_LOG::open(const char *log_name, enum_log_type log_type_arg, + const char *new_name, enum cache_type io_cache_type_arg) +{ + char buff[FN_REFLEN]; + File file= -1; + int open_flags= O_CREAT | O_BINARY; + DBUG_ENTER("MYSQL_LOG::open"); + DBUG_PRINT("enter", ("log_type: %d", (int) log_type_arg)); + + write_error= 0; + + init(log_type_arg, io_cache_type_arg); + + if (!(name= my_strdup(log_name, MYF(MY_WME)))) + { + name= (char *)log_name; // for the error message + goto err; + } + + if (new_name) + strmov(log_file_name, new_name); + else if (generate_new_name(log_file_name, name)) + goto err; + + if (io_cache_type == SEQ_READ_APPEND) + open_flags |= O_RDWR | O_APPEND; + else + open_flags |= O_WRONLY | (log_type == LOG_BIN ? 0 : O_APPEND); + + db[0]= 0; + + if ((file= my_open(log_file_name, open_flags, + MYF(MY_WME | ME_WAITTANG))) < 0 || + init_io_cache(&log_file, file, IO_SIZE, io_cache_type, + my_tell(file, MYF(MY_WME)), 0, + MYF(MY_WME | MY_NABP | + ((log_type == LOG_BIN) ? MY_WAIT_IF_FULL : 0)))) + goto err; + + switch (log_type) { + case LOG_NORMAL: + { + char *end; + int len=my_snprintf(buff, sizeof(buff), "%s, Version: %s. " +#ifdef EMBEDDED_LIBRARY + "embedded library\n", my_progname, server_version +#elif __NT__ + "started with:\nTCP Port: %d, Named Pipe: %s\n", + my_progname, server_version, mysqld_port, + mysqld_unix_port +#else + "started with:\nTcp port: %d Unix socket: %s\n", + my_progname, server_version, mysqld_port, + mysqld_unix_port +#endif + ); + end= strnmov(buff + len, "Time Id Command Argument\n", + sizeof(buff) - len); + if (my_b_write(&log_file, (byte*) buff, (uint) (end-buff)) || + flush_io_cache(&log_file)) + goto err; + break; + } + case LOG_CLOSED: // Impossible + case LOG_TO_BE_OPENED: + DBUG_ASSERT(1); + break; + } + + DBUG_RETURN(0); + +err: + sql_print_error("Could not use %s for logging (error %d). \ +Turning logging off for the whole duration of the MySQL server process. \ +To turn it on again: fix the cause, \ +shutdown the MySQL server and restart it.", name, errno); + if (file >= 0) + my_close(file, MYF(0)); + end_io_cache(&log_file); + safeFree(name); + log_type= LOG_CLOSED; + DBUG_RETURN(1); +} + MYSQL_LOG::MYSQL_LOG() - :bytes_written(0), last_time(0), query_start(0), name(0), - prepared_xids(0), log_type(LOG_CLOSED), file_id(1), open_count(1), - write_error(FALSE), inited(FALSE), need_start_event(TRUE), - m_table_map_version(0), - description_event_for_exec(0), description_event_for_queue(0) + : name(0), log_type(LOG_CLOSED), write_error(FALSE), + inited(FALSE), last_time(0) { /* We don't want to initialize LOCK_Log here as such initialization depends on @@ -1395,9 +1507,54 @@ MYSQL_LOG::MYSQL_LOG() called only in main(). Doing initialization here would make it happen before main(). */ - index_file_name[0] = 0; - bzero((char*) &log_file,sizeof(log_file)); - bzero((char*) &index_file, sizeof(index_file)); + bzero((char*) &log_file, sizeof(log_file)); +} + +void MYSQL_LOG::init_pthread_objects() +{ + DBUG_ASSERT(inited == 0); + inited= 1; + (void) pthread_mutex_init(&LOCK_log, MY_MUTEX_INIT_SLOW); +} + +/* + Close the log file + + SYNOPSIS + close() + exiting Bitmask. For the slow and general logs the only used bit is + LOG_CLOSE_TO_BE_OPENED. This is used if we intend to call + open at once after close. + + NOTES + One can do an open on the object at once after doing a close. + The internal structures are not freed until cleanup() is called +*/ + +void MYSQL_LOG::close(uint exiting) +{ // One can't set log_type here! + DBUG_ENTER("MYSQL_LOG::close"); + DBUG_PRINT("enter",("exiting: %d", (int) exiting)); + if (log_type != LOG_CLOSED && log_type != LOG_TO_BE_OPENED) + { + end_io_cache(&log_file); + + if (my_sync(log_file.file, MYF(MY_WME)) && ! write_error) + { + write_error= 1; + sql_print_error(ER(ER_ERROR_ON_WRITE), name, errno); + } + + if (my_close(log_file.file, MYF(MY_WME)) && ! write_error) + { + write_error= 1; + sql_print_error(ER(ER_ERROR_ON_WRITE), name, errno); + } + } + + log_type= (exiting & LOG_CLOSE_TO_BE_OPENED) ? LOG_TO_BE_OPENED : LOG_CLOSED; + safeFree(name); + DBUG_VOID_RETURN; } /* this is called only once */ @@ -1408,17 +1565,57 @@ void MYSQL_LOG::cleanup() if (inited) { inited= 0; - close(LOG_CLOSE_INDEX|LOG_CLOSE_STOP_EVENT); - delete description_event_for_queue; - delete description_event_for_exec; (void) pthread_mutex_destroy(&LOCK_log); - (void) pthread_mutex_destroy(&LOCK_index); - (void) pthread_cond_destroy(&update_cond); + close(0); } DBUG_VOID_RETURN; } +/* + Reopen the log file + + SYNOPSIS + reopen_file() + + DESCRIPTION + Reopen the log file. The method is used during FLUSH LOGS + and locks LOCK_log mutex +*/ + + +void MYSQL_LOG::reopen_file() +{ + enum_log_type save_log_type; + char *save_name; + + DBUG_ENTER("MYSQL_LOG::reopen_file"); + if (!is_open()) + { + DBUG_PRINT("info",("log is closed")); + DBUG_VOID_RETURN; + } + + pthread_mutex_lock(&LOCK_log); + + save_name= name; + save_log_type= log_type; + name= 0; // Don't free name + close(LOG_CLOSE_TO_BE_OPENED); + + /* + Note that at this point, log_type != LOG_CLOSED (important for is_open()). + */ + + open(save_name, save_log_type, 0, io_cache_type); + my_free(save_name, MYF(0)); + + pthread_mutex_unlock(&LOCK_log); + + DBUG_VOID_RETURN; +} + + int MYSQL_LOG::generate_new_name(char *new_name, const char *log_name) { fn_format(new_name,log_name,mysql_data_home,"",4); @@ -1437,33 +1634,245 @@ int MYSQL_LOG::generate_new_name(char *new_name, const char *log_name) } -void MYSQL_LOG::init(enum_log_type log_type_arg, - enum cache_type io_cache_type_arg, - bool no_auto_events_arg, - ulong max_size_arg) +/* + Write a command to traditional general log file + + SYNOPSIS + write() + + event_time command start timestamp + user_host the pointer to the string with user@host info + user_host_len length of the user_host string. this is computed once + and passed to all general log event handlers + thread_id Id of the thread, issued a query + command_type the type of the command being logged + command_type_len the length of the string above + sql_text the very text of the query being executed + sql_text_len the length of sql_text string + + DESCRIPTION + + Log given command to to normal (not rotable) log file + + RETURN + FASE - OK + TRUE - error occured +*/ + +bool MYSQL_GENERAL_LOG::write(time_t event_time, const char *user_host, + uint user_host_len, int thread_id, + const char *command_type, uint command_type_len, + const char *sql_text, uint sql_text_len) { - DBUG_ENTER("MYSQL_LOG::init"); - log_type = log_type_arg; - io_cache_type = io_cache_type_arg; - no_auto_events = no_auto_events_arg; - max_size=max_size_arg; - DBUG_PRINT("info",("log_type: %d max_size: %lu", log_type, max_size)); - DBUG_VOID_RETURN; + char buff[32]; + uint length= 0; + char time_buff[MAX_TIME_SIZE]; + struct tm start; + uint time_buff_len= 0; + + /* Test if someone closed between the is_open test and lock */ + if (is_open()) + { + /* Note that my_b_write() assumes it knows the length for this */ + if (event_time != last_time) + { + last_time= event_time; + + localtime_r(&event_time, &start); + + time_buff_len= my_snprintf(time_buff, MAX_TIME_SIZE, + "%02d%02d%02d %2d:%02d:%02d", + start.tm_year % 100, start.tm_mon + 1, + start.tm_mday, start.tm_hour, + start.tm_min, start.tm_sec); + + if (my_b_write(&log_file, (byte*) &time_buff, time_buff_len)) + goto err; + } + else + if (my_b_write(&log_file, (byte*) "\t\t" ,2) < 0) + goto err; + + /* command_type, thread_id */ + length= my_snprintf(buff, 32, "%5ld ", thread_id); + + if (my_b_write(&log_file, (byte*) buff, length)) + goto err; + + if (my_b_write(&log_file, (byte*) command_type, command_type_len)) + goto err; + + if (my_b_write(&log_file, (byte*) "\t", 1)) + goto err; + + /* sql_text */ + if (my_b_write(&log_file, (byte*) sql_text, sql_text_len)) + goto err; + + if (my_b_write(&log_file, (byte*) "\n", 1) || + flush_io_cache(&log_file)) + goto err; + } + + return FALSE; +err: + + if (!write_error) + { + write_error= 1; + sql_print_error(ER(ER_ERROR_ON_WRITE), name, errno); + } + return TRUE; } -void MYSQL_LOG::init_pthread_objects() +/* + Log a query to the traditional slow log file + + SYNOPSIS + write() + + thd THD of the query + current_time current timestamp + query_start_arg command start timestamp + user_host the pointer to the string with user@host info + user_host_len length of the user_host string. this is computed once + and passed to all general log event handlers + query_time Amount of time the query took to execute (in seconds) + lock_time Amount of time the query was locked (in seconds) + is_command The flag, which determines, whether the sql_text is a + query or an administrator command. + sql_text the very text of the query or administrator command + processed + sql_text_len the length of sql_text string + + DESCRIPTION + + Log a query to the slow log file. + + RETURN + FALSE - OK + TRUE - error occured +*/ + +bool MYSQL_SLOW_LOG::write(THD *thd, time_t current_time, + time_t query_start_arg, const char *user_host, + uint user_host_len, longlong query_time, + longlong lock_time, bool is_command, + const char *sql_text, uint sql_text_len) { - DBUG_ASSERT(inited == 0); - inited= 1; - (void) pthread_mutex_init(&LOCK_log,MY_MUTEX_INIT_SLOW); - (void) pthread_mutex_init(&LOCK_index, MY_MUTEX_INIT_SLOW); - (void) pthread_cond_init(&update_cond, 0); + bool error= 0; + DBUG_ENTER("MYSQL_SLOW_LOG::write"); + + if (!is_open()) + DBUG_RETURN(0); + + if (is_open()) + { // Safety agains reopen + int tmp_errno= 0; + char buff[80], *end; + uint buff_len; + end= buff; + + if (!(specialflag & SPECIAL_SHORT_LOG_FORMAT)) + { + Security_context *sctx= thd->security_ctx; + if (current_time != last_time) + { + last_time= current_time; + struct tm start; + localtime_r(¤t_time, &start); + + buff_len= my_snprintf(buff, sizeof buff, + "# Time: %02d%02d%02d %2d:%02d:%02d\n", + start.tm_year % 100, start.tm_mon + 1, + start.tm_mday, start.tm_hour, + start.tm_min, start.tm_sec); + + /* Note that my_b_write() assumes it knows the length for this */ + if (my_b_write(&log_file, (byte*) buff, buff_len)) + tmp_errno= errno; + } + if (my_b_printf(&log_file, "# User@Host: ", sizeof("# User@Host: ") - 1) + != sizeof("# User@Host: ") - 1) + tmp_errno= errno; + if (my_b_printf(&log_file, user_host, user_host_len) != user_host_len) + tmp_errno= errno; + if (my_b_write(&log_file, (byte*) "\n", 1)) + tmp_errno= errno; + } + /* For slow query log */ + if (my_b_printf(&log_file, + "# Query_time: %lu Lock_time: %lu" + " Rows_sent: %lu Rows_examined: %lu\n", + (ulong) query_time, (ulong) lock_time, + (ulong) thd->sent_row_count, + (ulong) thd->examined_row_count) == (uint) -1) + tmp_errno= errno; + if (thd->db && strcmp(thd->db, db)) + { // Database changed + if (my_b_printf(&log_file,"use %s;\n",thd->db) == (uint) -1) + tmp_errno= errno; + strmov(db,thd->db); + } + if (thd->last_insert_id_used) + { + end=strmov(end, ",last_insert_id="); + end=longlong10_to_str((longlong) thd->current_insert_id, end, -10); + } + // Save value if we do an insert. + if (thd->insert_id_used) + { + if (!(specialflag & SPECIAL_SHORT_LOG_FORMAT)) + { + end=strmov(end,",insert_id="); + end=longlong10_to_str((longlong) thd->last_insert_id, end, -10); + } + } + + /* + This info used to show up randomly, depending on whether the query + checked the query start time or not. now we always write current + timestamp to the slow log + */ + end= strmov(end, ",timestamp="); + end= int10_to_str((long) current_time, end, 10); + + if (end != buff) + { + *end++=';'; + *end='\n'; + if (my_b_write(&log_file, (byte*) "SET ", 4) || + my_b_write(&log_file, (byte*) buff + 1, (uint) (end-buff))) + tmp_errno= errno; + } + if (is_command) + { + end= strxmov(buff, "# administrator command: ", NullS); + buff_len= (ulong) (end - buff); + my_b_write(&log_file, (byte*) buff, buff_len); + } + if (my_b_write(&log_file, (byte*) sql_text, sql_text_len) || + my_b_write(&log_file, (byte*) ";\n",2) || + flush_io_cache(&log_file)) + tmp_errno= errno; + if (tmp_errno) + { + error= 1; + if (! write_error) + { + write_error= 1; + sql_print_error(ER(ER_ERROR_ON_WRITE), name, error); + } + } + } + DBUG_RETURN(error); } + const char *MYSQL_LOG::generate_name(const char *log_name, - const char *suffix, - bool strip_ext, char *buff) + const char *suffix, + bool strip_ext, char *buff) { if (!log_name || !log_name[0]) { @@ -1471,22 +1880,78 @@ const char *MYSQL_LOG::generate_name(const char *log_name, TODO: The following should be using fn_format(); We just need to first change fn_format() to cut the file name if it's too long. */ - strmake(buff,glob_hostname,FN_REFLEN-5); - strmov(fn_ext(buff),suffix); + strmake(buff, glob_hostname, FN_REFLEN - 5); + strmov(fn_ext(buff), suffix); return (const char *)buff; } // get rid of extension if the log is binary to avoid problems if (strip_ext) { - char *p = fn_ext(log_name); - uint length=(uint) (p-log_name); - strmake(buff,log_name,min(length,FN_REFLEN)); + char *p= fn_ext(log_name); + uint length= (uint) (p - log_name); + strmake(buff, log_name, min(length, FN_REFLEN)); return (const char*)buff; } return log_name; } -bool MYSQL_LOG::open_index_file(const char *index_file_name_arg, + +MYSQL_BIN_LOG::MYSQL_BIN_LOG() + :bytes_written(0), prepared_xids(0), file_id(1), open_count(1), + need_start_event(TRUE), m_table_map_version(0), + description_event_for_exec(0), description_event_for_queue(0) +{ + /* + We don't want to initialize locks here as such initialization depends on + safe_mutex (when using safe_mutex) which depends on MY_INIT(), which is + called only in main(). Doing initialization here would make it happen + before main(). + */ + index_file_name[0] = 0; + bzero((char*) &index_file, sizeof(index_file)); +} + +/* this is called only once */ + +void MYSQL_BIN_LOG::cleanup() +{ + DBUG_ENTER("cleanup"); + if (inited) + { + inited= 0; + close(LOG_CLOSE_INDEX|LOG_CLOSE_STOP_EVENT); + delete description_event_for_queue; + delete description_event_for_exec; + (void) pthread_mutex_destroy(&LOCK_log); + (void) pthread_mutex_destroy(&LOCK_index); + (void) pthread_cond_destroy(&update_cond); + } + DBUG_VOID_RETURN; +} + + +/* Init binlog-specific vars */ +void MYSQL_BIN_LOG::init(bool no_auto_events_arg, ulong max_size_arg) +{ + DBUG_ENTER("MYSQL_BIN_LOG::init"); + no_auto_events= no_auto_events_arg; + max_size= max_size_arg; + DBUG_PRINT("info",("max_size: %lu", max_size)); + DBUG_VOID_RETURN; +} + + +void MYSQL_BIN_LOG::init_pthread_objects() +{ + DBUG_ASSERT(inited == 0); + inited= 1; + (void) pthread_mutex_init(&LOCK_log, MY_MUTEX_INIT_SLOW); + (void) pthread_mutex_init(&LOCK_index, MY_MUTEX_INIT_SLOW); + (void) pthread_cond_init(&update_cond, 0); +} + + +bool MYSQL_BIN_LOG::open_index_file(const char *index_file_name_arg, const char *log_name) { File index_file_nr= -1; @@ -1523,10 +1988,10 @@ bool MYSQL_LOG::open_index_file(const char *index_file_name_arg, /* - Open a (new) log file. + Open a (new) binlog file. DESCRIPTION - - If binary logs, also open the index file and register the new + - Open the log file and the index file. Register the new file name in it - When calling this when the file is in use, you must have a locks on LOCK_log and LOCK_index. @@ -1536,94 +2001,33 @@ bool MYSQL_LOG::open_index_file(const char *index_file_name_arg, 1 error */ -bool MYSQL_LOG::open(const char *log_name, - enum_log_type log_type_arg, - const char *new_name, - enum cache_type io_cache_type_arg, - bool no_auto_events_arg, - ulong max_size_arg, - bool null_created_arg) +bool MYSQL_BIN_LOG::open(const char *log_name, + enum_log_type log_type_arg, + const char *new_name, + enum cache_type io_cache_type_arg, + bool no_auto_events_arg, + ulong max_size_arg, + bool null_created_arg) { char buff[FN_REFLEN]; File file= -1; int open_flags = O_CREAT | O_BINARY; - DBUG_ENTER("MYSQL_LOG::open"); + DBUG_ENTER("MYSQL_BIN_LOG::open"); DBUG_PRINT("enter",("log_type: %d",(int) log_type_arg)); - last_time=query_start=0; + last_time= 0; write_error=0; - init(log_type_arg,io_cache_type_arg,no_auto_events_arg,max_size_arg); + /* open the main log file */ + if (MYSQL_LOG::open(log_name, log_type_arg, new_name, io_cache_type_arg)) + DBUG_RETURN(1); /* all warnings issued */ - if (!(name=my_strdup(log_name,MYF(MY_WME)))) - { - name= (char *)log_name; // for the error message - goto err; - } - if (new_name) - strmov(log_file_name,new_name); - else if (generate_new_name(log_file_name, name)) - goto err; + init(no_auto_events_arg, max_size_arg); - if (io_cache_type == SEQ_READ_APPEND) - open_flags |= O_RDWR | O_APPEND; - else - open_flags |= O_WRONLY | (log_type == LOG_BIN ? 0 : O_APPEND); - - db[0]=0; open_count++; - if ((file=my_open(log_file_name,open_flags, - MYF(MY_WME | ME_WAITTANG))) < 0 || - init_io_cache(&log_file, file, IO_SIZE, io_cache_type, - my_tell(file,MYF(MY_WME)), 0, - MYF(MY_WME | MY_NABP | - ((log_type == LOG_BIN) ? MY_WAIT_IF_FULL : 0)))) - goto err; - switch (log_type) { - case LOG_NORMAL: - { - char *end; - int len=my_snprintf(buff, sizeof(buff), "%s, Version: %s. " -#ifdef EMBEDDED_LIBRARY - "embedded library\n", my_progname, server_version -#elif __NT__ - "started with:\nTCP Port: %d, Named Pipe: %s\n", - my_progname, server_version, mysqld_port, mysqld_unix_port -#else - "started with:\nTcp port: %d Unix socket: %s\n", - my_progname,server_version,mysqld_port,mysqld_unix_port -#endif - ); - end=strnmov(buff+len,"Time Id Command Argument\n", - sizeof(buff)-len); - if (my_b_write(&log_file, (byte*) buff,(uint) (end-buff)) || - flush_io_cache(&log_file)) - goto err; - break; - } - case LOG_NEW: - { - uint len; - time_t skr=time(NULL); - struct tm tm_tmp; + DBUG_ASSERT(log_type == LOG_BIN); - localtime_r(&skr,&tm_tmp); - len= my_snprintf(buff,sizeof(buff), - "# %s, Version: %s at %02d%02d%02d %2d:%02d:%02d\n", - my_progname,server_version, - tm_tmp.tm_year % 100, - tm_tmp.tm_mon+1, - tm_tmp.tm_mday, - tm_tmp.tm_hour, - tm_tmp.tm_min, - tm_tmp.tm_sec); - if (my_b_write(&log_file, (byte*) buff, len) || - flush_io_cache(&log_file)) - goto err; - break; - } - case LOG_BIN: { bool write_file_name_to_index_file=0; @@ -1714,12 +2118,6 @@ bool MYSQL_LOG::open(const char *log_name, my_sync(index_file.file, MYF(MY_WME))) goto err; } - break; - } - case LOG_CLOSED: // Impossible - case LOG_TO_BE_OPENED: - DBUG_ASSERT(1); - break; } DBUG_RETURN(0); @@ -1738,7 +2136,7 @@ shutdown the MySQL server and restart it.", name, errno); } -int MYSQL_LOG::get_current_log(LOG_INFO* linfo) +int MYSQL_BIN_LOG::get_current_log(LOG_INFO* linfo) { pthread_mutex_lock(&LOCK_log); strmake(linfo->log_file_name, log_file_name, sizeof(linfo->log_file_name)-1); @@ -1825,7 +2223,7 @@ err: LOG_INFO_IO Got IO error while reading file */ -int MYSQL_LOG::find_log_pos(LOG_INFO *linfo, const char *log_name, +int MYSQL_BIN_LOG::find_log_pos(LOG_INFO *linfo, const char *log_name, bool need_lock) { int error= 0; @@ -1899,7 +2297,7 @@ int MYSQL_LOG::find_log_pos(LOG_INFO *linfo, const char *log_name, LOG_INFO_IO Got IO error while reading file */ -int MYSQL_LOG::find_next_log(LOG_INFO* linfo, bool need_lock) +int MYSQL_BIN_LOG::find_next_log(LOG_INFO* linfo, bool need_lock) { int error= 0; uint length; @@ -1947,7 +2345,7 @@ err: 1 error */ -bool MYSQL_LOG::reset_logs(THD* thd) +bool MYSQL_BIN_LOG::reset_logs(THD* thd) { LOG_INFO linfo; bool error=0; @@ -2047,7 +2445,7 @@ err: #ifdef HAVE_REPLICATION -int MYSQL_LOG::purge_first_log(struct st_relay_log_info* rli, bool included) +int MYSQL_BIN_LOG::purge_first_log(struct st_relay_log_info* rli, bool included) { int error; DBUG_ENTER("purge_first_log"); @@ -2123,7 +2521,7 @@ err: Update log index_file */ -int MYSQL_LOG::update_log_index(LOG_INFO* log_info, bool need_update_threads) +int MYSQL_BIN_LOG::update_log_index(LOG_INFO* log_info, bool need_update_threads) { if (copy_up_file_and_fill(&index_file, log_info->index_file_start_offset)) return LOG_INFO_IO; @@ -2156,7 +2554,7 @@ int MYSQL_LOG::update_log_index(LOG_INFO* log_info, bool need_update_threads) LOG_INFO_EOF to_log not found */ -int MYSQL_LOG::purge_logs(const char *to_log, +int MYSQL_BIN_LOG::purge_logs(const char *to_log, bool included, bool need_mutex, bool need_update_threads, @@ -2242,7 +2640,7 @@ err: LOG_INFO_PURGE_NO_ROTATE Binary file that can't be rotated */ -int MYSQL_LOG::purge_logs_before_date(time_t purge_time) +int MYSQL_BIN_LOG::purge_logs_before_date(time_t purge_time) { int error; LOG_INFO log_info; @@ -2301,7 +2699,7 @@ err: If file name will be longer then FN_REFLEN it will be truncated */ -void MYSQL_LOG::make_log_name(char* buf, const char* log_ident) +void MYSQL_BIN_LOG::make_log_name(char* buf, const char* log_ident) { uint dir_len = dirname_length(log_file_name); if (dir_len > FN_REFLEN) @@ -2315,29 +2713,49 @@ void MYSQL_LOG::make_log_name(char* buf, const char* log_ident) Check if we are writing/reading to the given log file */ -bool MYSQL_LOG::is_active(const char *log_file_name_arg) +bool MYSQL_BIN_LOG::is_active(const char *log_file_name_arg) { return !strcmp(log_file_name, log_file_name_arg); } +/* + Wrappers around new_file_impl to avoid using argument + to control locking. The argument 1) less readable 2) breaks + incapsulation 3) allows external access to the class without + a lock (which is not possible with private new_file_without_locking + method). +*/ + +void MYSQL_BIN_LOG::new_file() +{ + new_file_impl(1); +} + + +void MYSQL_BIN_LOG::new_file_without_locking() +{ + new_file_impl(0); +} + + /* Start writing to a new log file or reopen the old file SYNOPSIS - new_file() + new_file_impl() need_lock Set to 1 if caller has not locked LOCK_log NOTE The new file name is stored last in the index file */ -void MYSQL_LOG::new_file(bool need_lock) +void MYSQL_BIN_LOG::new_file_impl(bool need_lock) { char new_name[FN_REFLEN], *new_name_ptr, *old_name; enum_log_type save_log_type; - DBUG_ENTER("MYSQL_LOG::new_file"); + DBUG_ENTER("MYSQL_BIN_LOG::new_file_impl"); if (!is_open()) { DBUG_PRINT("info",("log is closed")); @@ -2433,11 +2851,11 @@ end: } -bool MYSQL_LOG::append(Log_event* ev) +bool MYSQL_BIN_LOG::append(Log_event* ev) { bool error = 0; pthread_mutex_lock(&LOCK_log); - DBUG_ENTER("MYSQL_LOG::append"); + DBUG_ENTER("MYSQL_BIN_LOG::append"); DBUG_ASSERT(log_file.type == SEQ_READ_APPEND); /* @@ -2452,7 +2870,7 @@ bool MYSQL_LOG::append(Log_event* ev) bytes_written+= ev->data_written; DBUG_PRINT("info",("max_size: %lu",max_size)); if ((uint) my_b_append_tell(&log_file) > max_size) - new_file(0); + new_file_without_locking(); err: pthread_mutex_unlock(&LOCK_log); @@ -2461,10 +2879,10 @@ err: } -bool MYSQL_LOG::appendv(const char* buf, uint len,...) +bool MYSQL_BIN_LOG::appendv(const char* buf, uint len,...) { bool error= 0; - DBUG_ENTER("MYSQL_LOG::appendv"); + DBUG_ENTER("MYSQL_BIN_LOG::appendv"); va_list(args); va_start(args,len); @@ -2482,7 +2900,7 @@ bool MYSQL_LOG::appendv(const char* buf, uint len,...) } while ((buf=va_arg(args,const char*)) && (len=va_arg(args,uint))); DBUG_PRINT("info",("max_size: %lu",max_size)); if ((uint) my_b_append_tell(&log_file) > max_size) - new_file(0); + new_file_without_locking(); err: if (!error) @@ -2491,99 +2909,7 @@ err: } -/* - Write a command to traditional general log file - - SYNOPSIS - write() - - event_time command start timestamp - user_host the pointer to the string with user@host info - user_host_len length of the user_host string. this is computed once - and passed to all general log event handlers - thread_id Id of the thread, issued a query - command_type the type of the command being logged - command_type_len the length of the string above - sql_text the very text of the query being executed - sql_text_len the length of sql_text string - - DESCRIPTION - - Log given command to to normal (not rotable) log file - - RETURN - FASE - OK - TRUE - error occured -*/ - -bool MYSQL_LOG::write(time_t event_time, const char *user_host, - uint user_host_len, int thread_id, - const char *command_type, uint command_type_len, - const char *sql_text, uint sql_text_len) -{ - char buff[32]; - uint length= 0; - char time_buff[MAX_TIME_SIZE]; - struct tm start; - uint time_buff_len= 0; - - /* Test if someone closed between the is_open test and lock */ - if (is_open()) - { - /* Note that my_b_write() assumes it knows the length for this */ - if (event_time != last_time) - { - last_time= event_time; - - localtime_r(&event_time, &start); - - time_buff_len= my_snprintf(time_buff, MAX_TIME_SIZE, - "%02d%02d%02d %2d:%02d:%02d", - start.tm_year % 100, start.tm_mon + 1, - start.tm_mday, start.tm_hour, - start.tm_min, start.tm_sec); - - if (my_b_write(&log_file, (byte*) &time_buff, time_buff_len)) - goto err; - } - else - if (my_b_write(&log_file, (byte*) "\t\t" ,2) < 0) - goto err; - - /* command_type, thread_id */ - length= my_snprintf(buff, 32, "%5ld ", thread_id); - - if (my_b_write(&log_file, (byte*) buff, length)) - goto err; - - if (my_b_write(&log_file, (byte*) command_type, command_type_len)) - goto err; - - if (my_b_write(&log_file, (byte*) "\t", 1)) - goto err; - - /* sql_text */ - if (my_b_write(&log_file, (byte*) sql_text, sql_text_len)) - goto err; - - if (my_b_write(&log_file, (byte*) "\n", 1) || - flush_io_cache(&log_file)) - goto err; - } - - return FALSE; -err: - - if (!write_error) - { - write_error= 1; - sql_print_error(ER(ER_ERROR_ON_WRITE), name, errno); - } - return TRUE; -} - - -bool MYSQL_LOG::flush_and_sync() +bool MYSQL_BIN_LOG::flush_and_sync() { int err=0, fd=log_file.file; safe_mutex_assert_owner(&LOCK_log); @@ -2597,7 +2923,7 @@ bool MYSQL_LOG::flush_and_sync() return err; } -void MYSQL_LOG::start_union_events(THD *thd) +void MYSQL_BIN_LOG::start_union_events(THD *thd) { DBUG_ASSERT(!thd->binlog_evt_union.do_union); thd->binlog_evt_union.do_union= TRUE; @@ -2606,13 +2932,13 @@ void MYSQL_LOG::start_union_events(THD *thd) thd->binlog_evt_union.first_query_id= thd->query_id; } -void MYSQL_LOG::stop_union_events(THD *thd) +void MYSQL_BIN_LOG::stop_union_events(THD *thd) { DBUG_ASSERT(thd->binlog_evt_union.do_union); thd->binlog_evt_union.do_union= FALSE; } -bool MYSQL_LOG::is_query_in_union(THD *thd, query_id_t query_id_param) +bool MYSQL_BIN_LOG::is_query_in_union(THD *thd, query_id_t query_id_param) { return (thd->binlog_evt_union.do_union && query_id_param >= thd->binlog_evt_union.first_query_id); @@ -2716,9 +3042,10 @@ THD::binlog_set_pending_rows_event(Rows_log_event* ev) (either cached binlog if transaction, or disk binlog). Sets a new pending event. */ -int MYSQL_LOG::flush_and_set_pending_rows_event(THD *thd, Rows_log_event* event) +int MYSQL_BIN_LOG:: + flush_and_set_pending_rows_event(THD *thd, Rows_log_event* event) { - DBUG_ENTER("MYSQL_LOG::flush_and_set_pending_rows_event(event)"); + DBUG_ENTER("MYSQL_BIN_LOG::flush_and_set_pending_rows_event(event)"); DBUG_ASSERT(thd->current_stmt_binlog_row_based && mysql_bin_log.is_open()); DBUG_PRINT("enter", ("event=%p", event)); @@ -2801,11 +3128,11 @@ int MYSQL_LOG::flush_and_set_pending_rows_event(THD *thd, Rows_log_event* event) Write an event to the binary log */ -bool MYSQL_LOG::write(Log_event *event_info) +bool MYSQL_BIN_LOG::write(Log_event *event_info) { THD *thd= event_info->thd; bool error= 1; - DBUG_ENTER("MYSQL_LOG::write(Log_event *)"); + DBUG_ENTER("MYSQL_BIN_LOG::write(Log_event *)"); if (thd->binlog_evt_union.do_union) { @@ -3014,14 +3341,14 @@ bool general_log_print(THD *thd, enum enum_server_command command, return error; } -void MYSQL_LOG::rotate_and_purge(uint flags) +void MYSQL_BIN_LOG::rotate_and_purge(uint flags) { if (!(flags & RP_LOCK_LOG_IS_ALREADY_LOCKED)) pthread_mutex_lock(&LOCK_log); if ((flags & RP_FORCE_ROTATE) || (my_b_tell(&log_file) >= (my_off_t) max_size)) { - new_file(0); + new_file_without_locking(); #ifdef HAVE_REPLICATION if (expire_logs_days) { @@ -3035,7 +3362,7 @@ void MYSQL_LOG::rotate_and_purge(uint flags) pthread_mutex_unlock(&LOCK_log); } -uint MYSQL_LOG::next_file_id() +uint MYSQL_BIN_LOG::next_file_id() { uint res; pthread_mutex_lock(&LOCK_log); @@ -3066,9 +3393,9 @@ uint MYSQL_LOG::next_file_id() that the same updates are run on the slave. */ -bool MYSQL_LOG::write(THD *thd, IO_CACHE *cache, Log_event *commit_event) +bool MYSQL_BIN_LOG::write(THD *thd, IO_CACHE *cache, Log_event *commit_event) { - DBUG_ENTER("MYSQL_LOG::write(THD *, IO_CACHE *, Log_event *)"); + DBUG_ENTER("MYSQL_BIN_LOG::write(THD *, IO_CACHE *, Log_event *)"); VOID(pthread_mutex_lock(&LOCK_log)); if (likely(is_open())) // Should always be true @@ -3163,148 +3490,6 @@ err: } -/* - Log a query to the traditional slow log file - - SYNOPSIS - write() - - thd THD of the query - current_time current timestamp - query_start_arg command start timestamp - user_host the pointer to the string with user@host info - user_host_len length of the user_host string. this is computed once - and passed to all general log event handlers - query_time Amount of time the query took to execute (in seconds) - lock_time Amount of time the query was locked (in seconds) - is_command The flag, which determines, whether the sql_text is a - query or an administrator command. - sql_text the very text of the query or administrator command - processed - sql_text_len the length of sql_text string - - DESCRIPTION - - Log a query to the slow log file. - - RETURN - FALSE - OK - TRUE - error occured -*/ - -bool MYSQL_LOG::write(THD *thd, time_t current_time, time_t query_start_arg, - const char *user_host, uint user_host_len, - longlong query_time, longlong lock_time, bool is_command, - const char *sql_text, uint sql_text_len) -{ - bool error= 0; - DBUG_ENTER("MYSQL_LOG::write"); - - if (!is_open()) - DBUG_RETURN(0); - - if (is_open()) - { // Safety agains reopen - int tmp_errno= 0; - char buff[80], *end; - uint buff_len; - end= buff; - - if (!(specialflag & SPECIAL_SHORT_LOG_FORMAT)) - { - Security_context *sctx= thd->security_ctx; - if (current_time != last_time) - { - last_time= current_time; - struct tm start; - localtime_r(¤t_time, &start); - - buff_len= my_snprintf(buff, sizeof buff, - "# Time: %02d%02d%02d %2d:%02d:%02d\n", - start.tm_year % 100, start.tm_mon + 1, - start.tm_mday, start.tm_hour, - start.tm_min, start.tm_sec); - - /* Note that my_b_write() assumes it knows the length for this */ - if (my_b_write(&log_file, (byte*) buff, buff_len)) - tmp_errno=errno; - } - if (my_b_printf(&log_file, "# User@Host: ", sizeof("# User@Host: ") - 1)) - tmp_errno=errno; - if (my_b_printf(&log_file, user_host, user_host_len)) - tmp_errno=errno; - if (my_b_write(&log_file, (byte*) "\n", 1)) - tmp_errno=errno; - } - /* For slow query log */ - if (my_b_printf(&log_file, - "# Query_time: %lu Lock_time: %lu" - " Rows_sent: %lu Rows_examined: %lu\n", - (ulong) query_time, (ulong) lock_time, - (ulong) thd->sent_row_count, - (ulong) thd->examined_row_count) == (uint) -1) - tmp_errno=errno; - if (thd->db && strcmp(thd->db,db)) - { // Database changed - if (my_b_printf(&log_file,"use %s;\n",thd->db) == (uint) -1) - tmp_errno=errno; - strmov(db,thd->db); - } - if (thd->last_insert_id_used) - { - end=strmov(end,",last_insert_id="); - end=longlong10_to_str((longlong) thd->current_insert_id,end,-10); - } - // Save value if we do an insert. - if (thd->insert_id_used) - { - if (!(specialflag & SPECIAL_SHORT_LOG_FORMAT)) - { - end=strmov(end,",insert_id="); - end=longlong10_to_str((longlong) thd->last_insert_id,end,-10); - } - } - - /* - This info used to show up randomly, depending on whether the query - checked the query start time or not. now we always write current - timestamp to the slow log - */ - end= strmov(end, ",timestamp="); - end= int10_to_str((long) current_time, end, 10); - - if (end != buff) - { - *end++=';'; - *end='\n'; - if (my_b_write(&log_file, (byte*) "SET ",4) || - my_b_write(&log_file, (byte*) buff+1,(uint) (end-buff))) - tmp_errno=errno; - } - if (is_command) - { - end= strxmov(buff, "# administrator command: ", NullS); - buff_len= (ulong) (end - buff); - my_b_write(&log_file, (byte*) buff, buff_len); - } - if (my_b_write(&log_file, (byte*) sql_text, sql_text_len) || - my_b_write(&log_file, (byte*) ";\n",2) || - flush_io_cache(&log_file)) - tmp_errno=errno; - if (tmp_errno) - { - error=1; - if (! write_error) - { - write_error=1; - sql_print_error(ER(ER_ERROR_ON_WRITE),name,error); - } - } - } - DBUG_RETURN(error); -} - - /* Wait until we get a signal that the binary log has been updated @@ -3321,7 +3506,7 @@ bool MYSQL_LOG::write(THD *thd, time_t current_time, time_t query_start_arg, THD::enter_cond() (see NOTES in sql_class.h). */ -void MYSQL_LOG::wait_for_update(THD* thd, bool is_slave) +void MYSQL_BIN_LOG::wait_for_update(THD* thd, bool is_slave) { const char *old_msg; DBUG_ENTER("wait_for_update"); @@ -3354,9 +3539,9 @@ void MYSQL_LOG::wait_for_update(THD* thd, bool is_slave) The internal structures are not freed until cleanup() is called */ -void MYSQL_LOG::close(uint exiting) +void MYSQL_BIN_LOG::close(uint exiting) { // One can't set log_type here! - DBUG_ENTER("MYSQL_LOG::close"); + DBUG_ENTER("MYSQL_BIN_LOG::close"); DBUG_PRINT("enter",("exiting: %d", (int) exiting)); if (log_type != LOG_CLOSED && log_type != LOG_TO_BE_OPENED) { @@ -3370,7 +3555,6 @@ void MYSQL_LOG::close(uint exiting) signal_update(); } #endif /* HAVE_REPLICATION */ - end_io_cache(&log_file); /* don't pwrite in a file opened with O_APPEND - it doesn't work */ if (log_file.type == WRITE_CACHE && log_type == LOG_BIN) @@ -3380,16 +3564,8 @@ void MYSQL_LOG::close(uint exiting) my_pwrite(log_file.file, &flags, 1, offset, MYF(0)); } - if (my_sync(log_file.file,MYF(MY_WME)) && ! write_error) - { - write_error=1; - sql_print_error(ER(ER_ERROR_ON_WRITE), name, errno); - } - if (my_close(log_file.file,MYF(MY_WME)) && ! write_error) - { - write_error=1; - sql_print_error(ER(ER_ERROR_ON_WRITE), name, errno); - } + /* this will cleanup IO_CACHE, sync and close the file */ + MYSQL_LOG::close(exiting); } /* @@ -3412,7 +3588,7 @@ void MYSQL_LOG::close(uint exiting) } -void MYSQL_LOG::set_max_size(ulong max_size_arg) +void MYSQL_BIN_LOG::set_max_size(ulong max_size_arg) { /* We need to take locks, otherwise this may happen: @@ -3421,7 +3597,7 @@ void MYSQL_LOG::set_max_size(ulong max_size_arg) uses the old_max_size argument, so max_size_arg has been overwritten and it's like if the SET command was never run. */ - DBUG_ENTER("MYSQL_LOG::set_max_size"); + DBUG_ENTER("MYSQL_BIN_LOG::set_max_size"); pthread_mutex_lock(&LOCK_log); if (is_open()) max_size= max_size_arg; @@ -3570,9 +3746,9 @@ bool flush_error_log() return result; } -void MYSQL_LOG::signal_update() +void MYSQL_BIN_LOG::signal_update() { - DBUG_ENTER("MYSQL_LOG::signal_update"); + DBUG_ENTER("MYSQL_BIN_LOG::signal_update"); pthread_cond_broadcast(&update_cond); DBUG_VOID_RETURN; } @@ -4175,7 +4351,7 @@ int TC_LOG::using_heuristic_recover() } /****** transaction coordinator log for 2pc - binlog() based solution ******/ -#define TC_LOG_BINLOG MYSQL_LOG +#define TC_LOG_BINLOG MYSQL_BIN_LOG /* TODO keep in-memory list of prepared transactions diff --git a/sql/log.h b/sql/log.h index 03d5466e549..589932dcac0 100644 --- a/sql/log.h +++ b/sql/log.h @@ -147,33 +147,89 @@ typedef struct st_log_info class Log_event; class Rows_log_event; -enum enum_log_type { LOG_CLOSED, LOG_TO_BE_OPENED, LOG_NORMAL, LOG_NEW, LOG_BIN}; +enum enum_log_type { LOG_CLOSED, LOG_TO_BE_OPENED, LOG_NORMAL, LOG_BIN}; /* - TODO split MYSQL_LOG into base MYSQL_LOG and - MYSQL_QUERY_LOG, MYSQL_SLOW_LOG, MYSQL_BIN_LOG - most of the code from MYSQL_LOG should be in the MYSQL_BIN_LOG - only (TC_LOG included) - TODO use mmap instead of IO_CACHE for binlog (mmap+fsync is two times faster than write+fsync) */ -class MYSQL_LOG: public TC_LOG +class MYSQL_LOG +{ +public: + MYSQL_LOG(); + void init_pthread_objects(); + void cleanup(); + void reopen_file(); + bool open(const char *log_name, + enum_log_type log_type, + const char *new_name, + enum cache_type io_cache_type_arg); + void init(enum_log_type log_type_arg, + enum cache_type io_cache_type_arg); + void close(uint exiting); + inline bool is_open() { return log_type != LOG_CLOSED; } + const char *generate_name(const char *log_name, const char *suffix, + bool strip_ext, char *buff); + int generate_new_name(char *new_name, const char *log_name); + protected: + /* LOCK_log is inited by init_pthread_objects() */ + pthread_mutex_t LOCK_log; + char *name; + char log_file_name[FN_REFLEN]; + char time_buff[20], db[NAME_LEN + 1]; + bool write_error, inited; + IO_CACHE log_file; + volatile enum_log_type log_type; + enum cache_type io_cache_type; + time_t last_time; + + friend class Log_event; +}; + +class MYSQL_GENERAL_LOG: public MYSQL_LOG +{ +public: + MYSQL_GENERAL_LOG() {} /* get rid of gcc warning */ + bool write(time_t event_time, const char *user_host, + uint user_host_len, int thread_id, + const char *command_type, uint command_type_len, + const char *sql_text, uint sql_text_len); + bool open_query_log(const char *log_name) + { + char buf[FN_REFLEN]; + return open(generate_name(log_name, ".log", 0, buf), LOG_NORMAL, 0, + WRITE_CACHE); + } +}; + +class MYSQL_SLOW_LOG: public MYSQL_LOG +{ +public: + MYSQL_SLOW_LOG() {} /* get rid of gcc warning */ + bool write(THD *thd, time_t current_time, time_t query_start_arg, + const char *user_host, uint user_host_len, + longlong query_time, longlong lock_time, bool is_command, + const char *sql_text, uint sql_text_len); + bool open_slow_log(const char *log_name) + { + char buf[FN_REFLEN]; + return open(generate_name(log_name, "-slow.log", 0, buf), LOG_NORMAL, 0, + WRITE_CACHE); + } +}; + +class MYSQL_BIN_LOG: public TC_LOG, private MYSQL_LOG { private: /* LOCK_log and LOCK_index are inited by init_pthread_objects() */ - pthread_mutex_t LOCK_log, LOCK_index; + pthread_mutex_t LOCK_index; pthread_mutex_t LOCK_prep_xids; pthread_cond_t COND_prep_xids; pthread_cond_t update_cond; ulonglong bytes_written; - time_t last_time,query_start; - IO_CACHE log_file; IO_CACHE index_file; - char *name; - char time_buff[20],db[NAME_LEN+1]; - char log_file_name[FN_REFLEN],index_file_name[FN_REFLEN]; + char index_file_name[FN_REFLEN]; /* The max size before rotation (usable only if log_type == LOG_BIN: binary logs and relay logs). @@ -186,13 +242,10 @@ class MYSQL_LOG: public TC_LOG */ ulong max_size; ulong prepared_xids; /* for tc log - number of xids to remember */ - volatile enum_log_type log_type; - enum cache_type io_cache_type; // current file sequence number for load data infile binary logging uint file_id; uint open_count; // For replication int readers_count; - bool write_error, inited; bool need_start_event; /* no_auto_events means we don't want any of these automatic events : @@ -202,13 +255,20 @@ class MYSQL_LOG: public TC_LOG In 5.0 it's 0 for relay logs too! */ bool no_auto_events; - friend class Log_event; ulonglong m_table_map_version; int write_to_file(IO_CACHE *cache); + /* + This is used to start writing to a new log file. The difference from + new_file() is locking. new_file_without_locking() does not acquire + LOCK_log. + */ + void new_file_without_locking(); + void new_file_impl(bool need_lock); public: + MYSQL_LOG::generate_name; /* These describe the log's format. This is used only for relay logs. _for_exec is used by the SQL thread, _for_queue by the I/O thread. It's @@ -220,9 +280,9 @@ public: Format_description_log_event *description_event_for_exec, *description_event_for_queue; - MYSQL_LOG(); + MYSQL_BIN_LOG(); /* - note that there's no destructor ~MYSQL_LOG() ! + note that there's no destructor ~MYSQL_BIN_LOG() ! The reason is that we don't want it to be automatically called on exit() - but only during the correct shutdown process */ @@ -264,9 +324,7 @@ public: void signal_update(); void wait_for_update(THD* thd, bool master_or_slave); void set_need_start_event() { need_start_event = 1; } - void init(enum_log_type log_type_arg, - enum cache_type io_cache_type_arg, - bool no_auto_events_arg, ulong max_size); + void init(bool no_auto_events_arg, ulong max_size); void init_pthread_objects(); void cleanup(); bool open(const char *log_name, @@ -275,35 +333,10 @@ public: enum cache_type io_cache_type_arg, bool no_auto_events_arg, ulong max_size, bool null_created); - const char *generate_name(const char *log_name, const char *suffix, - bool strip_ext, char *buff); - /* simplified open_xxx wrappers for the gigantic open above */ - bool open_query_log(const char *log_name) - { - char buf[FN_REFLEN]; - return open(generate_name(log_name, ".log", 0, buf), - LOG_NORMAL, 0, WRITE_CACHE, 0, 0, 0); - } - bool open_slow_log(const char *log_name) - { - char buf[FN_REFLEN]; - return open(generate_name(log_name, "-slow.log", 0, buf), - LOG_NORMAL, 0, WRITE_CACHE, 0, 0, 0); - } bool open_index_file(const char *index_file_name_arg, const char *log_name); - void new_file(bool need_lock); - /* log a command to the old-fashioned general log */ - bool write(time_t event_time, const char *user_host, - uint user_host_len, int thread_id, - const char *command_type, uint command_type_len, - const char *sql_text, uint sql_text_len); - - /* log a query to the old-fashioned slow query log */ - bool write(THD *thd, time_t current_time, time_t query_start_arg, - const char *user_host, uint user_host_len, - longlong query_time, longlong lock_time, bool is_command, - const char *sql_text, uint sql_text_len); + /* Use this to start writing a new log file */ + void new_file(); bool write(Log_event* event_info); // binary log write bool write(THD *thd, IO_CACHE *cache, Log_event *commit_event); @@ -319,7 +352,6 @@ public: bool appendv(const char* buf,uint len,...); bool append(Log_event* ev); - int generate_new_name(char *new_name,const char *old_name); void make_log_name(char* buf, const char* log_ident); bool is_active(const char* log_file_name); int update_log_index(LOG_INFO* linfo, bool need_update_threads); @@ -416,7 +448,8 @@ public: class Log_to_file_event_handler: public Log_event_handler { - MYSQL_LOG mysql_log, mysql_slow_log; + MYSQL_GENERAL_LOG mysql_log; + MYSQL_SLOW_LOG mysql_slow_log; bool is_initialized; public: Log_to_file_event_handler(): is_initialized(FALSE) diff --git a/sql/log_event.h b/sql/log_event.h index b24686514e3..b94d94c8513 100644 --- a/sql/log_event.h +++ b/sql/log_event.h @@ -470,7 +470,7 @@ enum Int_event_type #ifndef MYSQL_CLIENT class String; -class MYSQL_LOG; +class MYSQL_BIN_LOG; class THD; #endif diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 749a968a5af..2f7450e6261 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -1525,7 +1525,7 @@ extern char *default_tz_name; extern my_bool opt_large_pages; extern uint opt_large_page_size; -extern MYSQL_LOG mysql_bin_log; +extern MYSQL_BIN_LOG mysql_bin_log; extern LOGGER logger; extern TABLE_LIST general_log, slow_log; extern FILE *bootstrap_file; diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 7d89b67b583..dec1cb81a4c 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -2610,10 +2610,10 @@ static int init_common_variables(const char *conf_file_name, int argc, global_system_variables.time_zone= my_tz_SYSTEM; /* - Init mutexes for the global MYSQL_LOG objects. + Init mutexes for the global MYSQL_BIN_LOG objects. As safe_mutex depends on what MY_INIT() does, we can't init the mutexes of - global MYSQL_LOGs in their constructors, because then they would be inited - before MY_INIT(). So we do it here. + global MYSQL_BIN_LOGs in their constructors, because then they would be + inited before MY_INIT(). So we do it here. */ mysql_bin_log.init_pthread_objects(); diff --git a/sql/rpl_injector.cc b/sql/rpl_injector.cc index 265f5f61213..ec0add4165f 100644 --- a/sql/rpl_injector.cc +++ b/sql/rpl_injector.cc @@ -25,7 +25,7 @@ /* inline since it's called below */ inline -injector::transaction::transaction(MYSQL_LOG *log, THD *thd) +injector::transaction::transaction(MYSQL_BIN_LOG *log, THD *thd) : m_state(START_STATE), m_thd(thd) { /* diff --git a/sql/rpl_injector.h b/sql/rpl_injector.h index 14d5cca9b6c..3b0857e0833 100644 --- a/sql/rpl_injector.h +++ b/sql/rpl_injector.h @@ -26,7 +26,7 @@ /* Forward declarations */ class handler; -class MYSQL_LOG; +class MYSQL_BIN_LOG; class st_table; typedef st_table TABLE; @@ -219,7 +219,7 @@ public: private: /* Only the injector may construct these object */ - transaction(MYSQL_LOG *, THD *); + transaction(MYSQL_BIN_LOG *, THD *); void swap(transaction& o) { /* std::swap(m_start_pos, o.m_start_pos); */ diff --git a/sql/rpl_rli.h b/sql/rpl_rli.h index cacae1aa4c2..5fb0f85d8fe 100644 --- a/sql/rpl_rli.h +++ b/sql/rpl_rli.h @@ -69,7 +69,7 @@ typedef struct st_relay_log_info Protected with internal locks. Must get data_lock when resetting the logs. */ - MYSQL_LOG relay_log; + MYSQL_BIN_LOG relay_log; LOG_INFO linfo; IO_CACHE cache_buf,*cur_log; diff --git a/sql/slave.cc b/sql/slave.cc index 4ab9e951813..4ed78ea2de1 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -4570,7 +4570,7 @@ static Log_event* next_event(RELAY_LOG_INFO* rli) When the relay log is created when the I/O thread starts, easy: the master will send the description event and we will queue it. But if the relay log is created by new_file(): then the solution is: - MYSQL_LOG::open() will write the buffered description event. + MYSQL_BIN_LOG::open() will write the buffered description event. */ if ((ev=Log_event::read_log_event(cur_log,0, rli->relay_log.description_event_for_exec))) @@ -4832,7 +4832,8 @@ err: Rotate a relay log (this is used only by FLUSH LOGS; the automatic rotation because of size is simpler because when we do it we already have all relevant locks; here we don't, so this function is mainly taking locks). - Returns nothing as we cannot catch any error (MYSQL_LOG::new_file() is void). + Returns nothing as we cannot catch any error (MYSQL_BIN_LOG::new_file() + is void). */ void rotate_relay_log(MASTER_INFO* mi) @@ -4854,7 +4855,7 @@ void rotate_relay_log(MASTER_INFO* mi) } /* If the relay log is closed, new_file() will do nothing. */ - rli->relay_log.new_file(1); + rli->relay_log.new_file(); /* We harvest now, because otherwise BIN_LOG_HEADER_SIZE will not immediately diff --git a/sql/slave.h b/sql/slave.h index 0b77d7f7c4f..053358dc686 100644 --- a/sql/slave.h +++ b/sql/slave.h @@ -73,7 +73,7 @@ run_lock protects all information about the run state: slave_running, and the existence of the I/O thread (to stop/start it, you need this mutex). data_lock protects some moving members of the struct: counters (log name, - position) and relay log (MYSQL_LOG object). + position) and relay log (MYSQL_BIN_LOG object). In RELAY_LOG_INFO: run_lock, data_lock see MASTER_INFO @@ -81,7 +81,7 @@ Order of acquisition: if you want to have LOCK_active_mi and a run_lock, you must acquire LOCK_active_mi first. - In MYSQL_LOG: LOCK_log, LOCK_index of the binlog and the relay log + In MYSQL_BIN_LOG: LOCK_log, LOCK_index of the binlog and the relay log LOCK_log: when you write to it. LOCK_index: when you create/delete a binlog (so that you have to update the .index file). */ From 564ba97b6244bd865ae82a4939e5a3badec104eb Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 18 May 2006 21:32:24 +1000 Subject: [PATCH 03/65] BUG#18966 Change in stop/shutdown behaviour Now need this fix to prevent crash of ndb_mgm on invalid mgm protocol. ndb/src/mgmclient/CommandInterpreter.cpp: fix crash on invalid mgm protocol --- ndb/src/mgmclient/CommandInterpreter.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ndb/src/mgmclient/CommandInterpreter.cpp b/ndb/src/mgmclient/CommandInterpreter.cpp index 0fdb81989b3..9e1224ab3ef 100644 --- a/ndb/src/mgmclient/CommandInterpreter.cpp +++ b/ndb/src/mgmclient/CommandInterpreter.cpp @@ -429,14 +429,14 @@ emptyString(const char* s) void CommandInterpreter::printError() { - if (ndb_mgm_check_connection(m_mgmsrv)) - { - disconnect(); - } ndbout_c("* %5d: %s", ndb_mgm_get_latest_error(m_mgmsrv), ndb_mgm_get_latest_error_msg(m_mgmsrv)); ndbout_c("* %s", ndb_mgm_get_latest_error_desc(m_mgmsrv)); + if (ndb_mgm_check_connection(m_mgmsrv)) + { + disconnect(); + } } //***************************************************************************** From 5632d02c8de3af3bfcb0395a9b3a7f80120af21a Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 18 May 2006 17:10:58 +0500 Subject: [PATCH 04/65] Fix for bug #15558: truncate doesn't clear table on archive storage engine tables. mysql-test/r/archive.result: Fix for bug #15558: truncate doesn't clear table on archive storage engine tables. - adjusted result mysql-test/t/archive.test: Fix for bug #15558: truncate doesn't clear table on archive storage engine tables. - adjusted test sql/examples/ha_archive.cc: Fix for bug #15558: truncate doesn't clear table on archive storage engine tables. - return an error as we don't suport delete_all_rows. --- mysql-test/r/archive.result | 2 ++ mysql-test/t/archive.test | 2 ++ sql/examples/ha_archive.cc | 2 +- 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/archive.result b/mysql-test/r/archive.result index 246e96bf993..2626b439059 100644 --- a/mysql-test/r/archive.result +++ b/mysql-test/r/archive.result @@ -2616,6 +2616,7 @@ select count(*) from t4; count(*) 1203 DELETE FROM t2; +ERROR HY000: Table storage engine for 't2' doesn't have this option SELECT * FROM t2; auto fld1 companynr fld3 fld4 fld5 fld6 1 000001 00 Omaha teethe neat @@ -5033,6 +5034,7 @@ auto fld1 companynr fld3 fld4 fld5 fld6 3 011402 37 Romans scholastics jarring 4 011403 37 intercepted audiology tinily TRUNCATE TABLE t2; +ERROR HY000: Table storage engine for 't2' doesn't have this option SELECT * FROM t2; auto fld1 companynr fld3 fld4 fld5 fld6 1 000001 00 Omaha teethe neat diff --git a/mysql-test/t/archive.test b/mysql-test/t/archive.test index f10ff0f648e..7b3ee45de5f 100644 --- a/mysql-test/t/archive.test +++ b/mysql-test/t/archive.test @@ -1316,12 +1316,14 @@ select count(*) from t4; # # For bug #12836 # Delete was allowing all rows to be removed +--error 1031 DELETE FROM t2; SELECT * FROM t2; INSERT INTO t2 VALUES (2,011401,37,'breaking','dreaded','Steinberg','W'); INSERT INTO t2 VALUES (3,011402,37,'Romans','scholastics','jarring',''); INSERT INTO t2 VALUES (4,011403,37,'intercepted','audiology','tinily',''); SELECT * FROM t2; +--error 1031 TRUNCATE TABLE t2; SELECT * FROM t2; diff --git a/sql/examples/ha_archive.cc b/sql/examples/ha_archive.cc index ee4cad25460..55fc359348f 100644 --- a/sql/examples/ha_archive.cc +++ b/sql/examples/ha_archive.cc @@ -1010,6 +1010,6 @@ ha_rows ha_archive::records_in_range(uint inx, key_range *min_key, int ha_archive::delete_all_rows() { DBUG_ENTER("ha_archive::delete_all_rows"); - DBUG_RETURN(0); + DBUG_RETURN(HA_ERR_WRONG_COMMAND); } #endif /* HAVE_ARCHIVE_DB */ From 96ceaeb5442b864227a2590fd8857d94484e50ec Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 19 May 2006 01:26:19 +0400 Subject: [PATCH 05/65] WL#3244 "CSV engine: convert mmap to read/write calls" mysql-test/r/csv.result: Correct result file storage/csv/ha_tina.cc: Get rid of mmap in tina. Use usual reads/writes to access the file. storage/csv/ha_tina.h: Add Transparent_file class, which hides details of access to the file. Perform a cleanup & add new functions. --- mysql-test/r/csv.result | 4 +- storage/csv/ha_tina.cc | 463 +++++++++++++++++++++++++--------------- storage/csv/ha_tina.h | 69 +++++- 3 files changed, 357 insertions(+), 179 deletions(-) diff --git a/mysql-test/r/csv.result b/mysql-test/r/csv.result index 3adcc895474..04f0636d400 100644 --- a/mysql-test/r/csv.result +++ b/mysql-test/r/csv.result @@ -4944,10 +4944,10 @@ val UPDATE bug13894 SET val=6 WHERE val=10; SELECT * FROM bug13894; val +6 +6 5 11 -6 -6 DROP TABLE bug13894; DROP TABLE IF EXISTS bug14672; CREATE TABLE bug14672 (c1 integer) engine = CSV; diff --git a/storage/csv/ha_tina.cc b/storage/csv/ha_tina.cc index de69df90ed5..409364f59c6 100644 --- a/storage/csv/ha_tina.cc +++ b/storage/csv/ha_tina.cc @@ -61,7 +61,7 @@ TODO: /* The file extension */ #define CSV_EXT ".CSV" // The data file -#define CSN_EXT ".CSN" // Files used during repair +#define CSN_EXT ".CSN" // Files used during repair and update #define CSM_EXT ".CSM" // Meta file @@ -114,9 +114,59 @@ handlerton tina_hton= { NULL, /* Fill FILES Table */ HTON_CAN_RECREATE, NULL, /* binlog_func */ - NULL /* binlog_log_query */ + NULL, /* binlog_log_query */ + NULL /* release_temporary_latches */ }; + +off_t Transparent_file::read_next() +{ + off_t bytes_read; + + /* + No need to seek here, as the file managed by Transparent_file class + always points to upper_bound byte + */ + if ((bytes_read= my_read(filedes, buff, buff_size, MYF(0))) == MY_FILE_ERROR) + return -1; + + /* end of file */ + if (!bytes_read) + return -1; + + lower_bound= upper_bound; + upper_bound+= bytes_read; + + return lower_bound; +} + + +char Transparent_file::get_value(off_t offset) +{ + off_t bytes_read; + + /* check boundaries */ + if ((lower_bound <= offset) && (offset < upper_bound)) + return buff[offset - lower_bound]; + else + { + VOID(my_seek(filedes, offset, MY_SEEK_SET, MYF(0))); + /* read appropriate portion of the file */ + if ((bytes_read= my_read(filedes, buff, buff_size, + MYF(0))) == MY_FILE_ERROR) + return 0; + + lower_bound= offset; + upper_bound= lower_bound + bytes_read; + + /* end of file */ + if (upper_bound == offset) + return 0; + + return buff[0]; + } +} + /***************************************************************************** ** TINA tables *****************************************************************************/ @@ -130,7 +180,7 @@ int sort_set (tina_set *a, tina_set *b) We assume that intervals do not intersect. So, it is enought to compare any two points. Here we take start of intervals for comparison. */ - return ( a->begin > b->begin ? -1 : ( a->begin < b->begin ? 1 : 0 ) ); + return ( a->begin > b->begin ? 1 : ( a->begin < b->begin ? -1 : 0 ) ); } static byte* tina_get_key(TINA_SHARE *share,uint *length, @@ -140,48 +190,6 @@ static byte* tina_get_key(TINA_SHARE *share,uint *length, return (byte*) share->table_name; } -/* - Reloads the mmap file. -*/ -int get_mmap(TINA_SHARE *share, int write) -{ - DBUG_ENTER("ha_tina::get_mmap"); - if (share->mapped_file && my_munmap(share->mapped_file, - share->file_stat.st_size)) - DBUG_RETURN(1); - - if (my_fstat(share->data_file, &share->file_stat, MYF(MY_WME)) == -1) - DBUG_RETURN(1); - - if (share->file_stat.st_size) - { - if (write) - share->mapped_file= (byte *)my_mmap(NULL, share->file_stat.st_size, - PROT_READ|PROT_WRITE, MAP_SHARED, - share->data_file, 0); - else - share->mapped_file= (byte *)my_mmap(NULL, share->file_stat.st_size, - PROT_READ, MAP_PRIVATE, - share->data_file, 0); - if ((share->mapped_file == MAP_FAILED)) - { - /* - Bad idea you think? See the problem is that nothing actually checks - the return value of ::rnd_init(), so tossing an error is about - it for us. - Never going to happen right? :) - */ - my_message(errno, "Woops, blew up opening a mapped file", 0); - DBUG_ASSERT(0); - DBUG_RETURN(1); - } - } - else - share->mapped_file= NULL; - - DBUG_RETURN(0); -} - static int tina_init_func() { @@ -218,6 +226,7 @@ static TINA_SHARE *get_share(const char *table_name, TABLE *table) { TINA_SHARE *share; char meta_file_name[FN_REFLEN]; + MY_STAT file_stat; /* Stat information for the data file */ char *tmp_name; uint length; @@ -250,6 +259,8 @@ static TINA_SHARE *get_share(const char *table_name, TABLE *table) share->table_name= tmp_name; share->crashed= FALSE; share->rows_recorded= 0; + share->update_file_opened= FALSE; + share->tina_write_opened= FALSE; strmov(share->table_name, table_name); fn_format(share->data_file_name, table_name, "", CSV_EXT, MY_REPLACE_EXT|MY_UNPACK_FILENAME); @@ -277,27 +288,16 @@ static TINA_SHARE *get_share(const char *table_name, TABLE *table) */ if (read_meta_file(share->meta_file, &share->rows_recorded)) share->crashed= TRUE; - else - (void)write_meta_file(share->meta_file, share->rows_recorded, TRUE); - if ((share->data_file= my_open(share->data_file_name, O_RDWR|O_APPEND, - MYF(0))) == -1) + if (my_stat(share->data_file_name, &file_stat, MYF(MY_WME)) == NULL) goto error2; - - share->mapped_file= NULL; // We don't know the state as we just allocated it - if (get_mmap(share, 0) > 0) - goto error3; - - /* init file length value used by readers */ - share->saved_data_file_length= share->file_stat.st_size; + share->saved_data_file_length= file_stat.st_size; } share->use_count++; pthread_mutex_unlock(&tina_mutex); return share; -error3: - my_close(share->data_file,MYF(0)); error2: thr_lock_delete(&share->lock); pthread_mutex_destroy(&share->mutex); @@ -425,6 +425,30 @@ bool ha_tina::check_and_repair(THD *thd) } +int ha_tina::init_tina_writer() +{ + DBUG_ENTER("ha_tina::init_tina_writer"); + + /* + Mark the file as crashed. We will set the flag back when we close + the file. In the case of the crash it will remain marked crashed, + which enforce recovery. + */ + (void)write_meta_file(share->meta_file, share->rows_recorded, TRUE); + + if ((share->tina_write_filedes= + my_open(share->data_file_name, O_RDWR|O_APPEND, MYF(0))) == -1) + { + DBUG_PRINT("info", ("Could not open tina file writes")); + share->crashed= TRUE; + DBUG_RETURN(1); + } + share->tina_write_opened= TRUE; + + DBUG_RETURN(0); +} + + bool ha_tina::is_crashed() const { DBUG_ENTER("ha_tina::is_crashed"); @@ -445,10 +469,13 @@ static int free_share(TINA_SHARE *share) share->crashed ? TRUE :FALSE); if (my_close(share->meta_file, MYF(0))) result_code= 1; - if (share->mapped_file) - my_munmap(share->mapped_file, share->file_stat.st_size); - share->mapped_file= NULL; - result_code= my_close(share->data_file,MYF(0)); + if (share->tina_write_opened) + { + if (my_close(share->tina_write_filedes, MYF(0))) + result_code= 1; + share->tina_write_opened= FALSE; + } + hash_delete(&tina_open_tables, (byte*) share); thr_lock_delete(&share->lock); pthread_mutex_destroy(&share->mutex); @@ -468,16 +495,16 @@ int tina_end(ha_panic_function type) Finds the end of a line. Currently only supports files written on a UNIX OS. */ -byte * find_eoln(byte *data, off_t begin, off_t end) + +off_t find_eoln_buff(Transparent_file *data_buff, off_t begin, off_t end) { for (off_t x= begin; x < end; x++) - if (data[x] == '\n') - return data + x; + if (data_buff->get_value(x) == '\n') + return x; return 0; } - static handler *tina_create_handler(TABLE_SHARE *table) { return new ha_tina(table); @@ -491,7 +518,7 @@ ha_tina::ha_tina(TABLE_SHARE *table_arg) They are not probably completely right. */ current_position(0), next_position(0), local_saved_data_file_length(0), - chain_alloced(0), chain_size(DEFAULT_CHAIN_LENGTH), + file_buff(0), chain_alloced(0), chain_size(DEFAULT_CHAIN_LENGTH), records_is_known(0) { /* Set our original buffers from pre-allocated memory */ @@ -619,50 +646,50 @@ int ha_tina::chain_append() */ int ha_tina::find_current_row(byte *buf) { - byte *mapped_ptr; - byte *end_ptr; + off_t end_offset, curr_offset= current_position; DBUG_ENTER("ha_tina::find_current_row"); - mapped_ptr= (byte *)share->mapped_file + current_position; - /* We do not read further then local_saved_data_file_length in order not to conflict with undergoing concurrent insert. */ - if ((end_ptr= find_eoln(share->mapped_file, current_position, - local_saved_data_file_length)) == 0) + if ((end_offset= find_eoln_buff(file_buff, current_position, + local_saved_data_file_length)) == 0) DBUG_RETURN(HA_ERR_END_OF_FILE); for (Field **field=table->field ; *field ; field++) { buffer.length(0); - if (*mapped_ptr == '"') - mapped_ptr++; // Increment past the first quote + if (file_buff->get_value(curr_offset) == '"') + curr_offset++; // Incrementpast the first quote else DBUG_RETURN(HA_ERR_CRASHED_ON_USAGE); - for(;mapped_ptr != end_ptr; mapped_ptr++) + for(;curr_offset != end_offset; curr_offset++) { // Need to convert line feeds! - if (*mapped_ptr == '"' && - (((mapped_ptr[1] == ',') && (mapped_ptr[2] == '"')) || - (mapped_ptr == end_ptr -1 ))) + if (file_buff->get_value(curr_offset) == '"' && + (((file_buff->get_value(curr_offset + 1) == ',') && + (file_buff->get_value(curr_offset + 2) == '"')) || + (curr_offset == end_offset -1 ))) { - mapped_ptr += 2; // Move past the , and the " + curr_offset+= 2; // Move past the , and the " break; } - if (*mapped_ptr == '\\' && mapped_ptr != (end_ptr - 1)) + if (file_buff->get_value(curr_offset) == '\\' && + curr_offset != (end_offset - 1)) { - mapped_ptr++; - if (*mapped_ptr == 'r') + curr_offset++; + if (file_buff->get_value(curr_offset) == 'r') buffer.append('\r'); - else if (*mapped_ptr == 'n' ) + else if (file_buff->get_value(curr_offset) == 'n' ) buffer.append('\n'); - else if ((*mapped_ptr == '\\') || (*mapped_ptr == '"')) - buffer.append(*mapped_ptr); + else if ((file_buff->get_value(curr_offset) == '\\') || + (file_buff->get_value(curr_offset) == '"')) + buffer.append(file_buff->get_value(curr_offset)); else /* This could only happed with an externally created file */ { buffer.append('\\'); - buffer.append(*mapped_ptr); + buffer.append(file_buff->get_value(curr_offset)); } } else // ordinary symbol @@ -671,14 +698,14 @@ int ha_tina::find_current_row(byte *buf) We are at final symbol and no last quote was found => we are working with a damaged file. */ - if (mapped_ptr == end_ptr -1) + if (curr_offset == end_offset - 1) DBUG_RETURN(HA_ERR_CRASHED_ON_USAGE); - buffer.append(*mapped_ptr); + buffer.append(file_buff->get_value(curr_offset)); } } (*field)->store(buffer.ptr(), buffer.length(), system_charset_info); } - next_position= (end_ptr - share->mapped_file)+1; + next_position= end_offset + 1; /* Maybe use \N for null? */ memset(buf, 0, table->s->null_bytes); /* We do not implement nulls! */ @@ -776,7 +803,7 @@ void ha_tina::get_status() void ha_tina::update_status() { /* correct local_saved_data_file_length for writers */ - share->saved_data_file_length= share->file_stat.st_size; + share->saved_data_file_length= local_saved_data_file_length; } @@ -827,6 +854,11 @@ int ha_tina::open(const char *name, int mode, uint open_options) DBUG_RETURN(HA_ERR_CRASHED_ON_USAGE); } + if ((data_file= my_open(share->data_file_name, O_RDONLY, MYF(0))) == -1) + DBUG_RETURN(0); + + file_buff= new Transparent_file(data_file); + /* Init locking. Pass handler object to the locking routines, so that they could save/update local_saved_data_file_length value @@ -845,12 +877,14 @@ int ha_tina::open(const char *name, int mode, uint open_options) /* Close a database file. We remove ourselves from the shared strucutre. - If it is empty we destroy it and free the mapped file. + If it is empty we destroy it. */ int ha_tina::close(void) { + int rc= 0; DBUG_ENTER("ha_tina::close"); - DBUG_RETURN(free_share(share)); + rc= my_close(data_file, MYF(0)); + DBUG_RETURN(free_share(share) || rc); } /* @@ -873,22 +907,17 @@ int ha_tina::write_row(byte * buf) size= encode_quote(buf); - if (my_write(share->data_file, (byte*)buffer.ptr(), size, + if (!share->tina_write_opened) + if (init_tina_writer()) + DBUG_RETURN(-1); + + /* use pwrite, as concurrent reader could have changed the position */ + if (my_write(share->tina_write_filedes, buffer.ptr(), size, MYF(MY_WME | MY_NABP))) DBUG_RETURN(-1); - /* - Ok, this is means that we will be doing potentially bad things - during a bulk insert on some OS'es. What we need is a cleanup - call for ::write_row that would let us fix up everything after the bulk - insert. The archive handler does this with an extra mutx call, which - might be a solution for this. - */ - if (get_mmap(share, 0) > 0) - DBUG_RETURN(-1); - /* update local copy of the max position to see our own changes */ - local_saved_data_file_length= share->file_stat.st_size; + local_saved_data_file_length+= size; /* update shared info */ pthread_mutex_lock(&share->mutex); @@ -903,6 +932,23 @@ int ha_tina::write_row(byte * buf) } +int ha_tina::open_update_temp_file_if_needed() +{ + char updated_fname[FN_REFLEN]; + + if (!share->update_file_opened) + { + if ((update_temp_file= + my_create(fn_format(updated_fname, share->table_name, + "", CSN_EXT, + MY_REPLACE_EXT | MY_UNPACK_FILENAME), + 0, O_RDWR | O_TRUNC, MYF(MY_WME))) < 0) + return 1; + share->update_file_opened= TRUE; + } + return 0; +} + /* This is called for an update. Make sure you put in code to increment the auto increment, also @@ -926,16 +972,16 @@ int ha_tina::update_row(const byte * old_data, byte * new_data) if (chain_append()) DBUG_RETURN(-1); - if (my_write(share->data_file, (byte*)buffer.ptr(), size, + if (open_update_temp_file_if_needed()) + DBUG_RETURN(-1); + + if (my_write(update_temp_file, (byte*)buffer.ptr(), size, MYF(MY_WME | MY_NABP))) DBUG_RETURN(-1); /* UPDATE should never happen on the log tables */ DBUG_ASSERT(!share->is_log_table); - /* update local copy of the max position to see our own changes */ - local_saved_data_file_length= share->file_stat.st_size; - DBUG_RETURN(0); } @@ -1001,6 +1047,8 @@ int ha_tina::rnd_init(bool scan) { DBUG_ENTER("ha_tina::rnd_init"); + /* set buffer to the beginning of the file */ + file_buff->init_buff(data_file); if (share->crashed) DBUG_RETURN(HA_ERR_CRASHED_ON_USAGE); @@ -1008,11 +1056,6 @@ int ha_tina::rnd_init(bool scan) records= 0; records_is_known= 0; chain_ptr= chain; -#ifdef HAVE_MADVISE - if (scan) - (void) madvise(share->mapped_file, share->file_stat.st_size, - MADV_SEQUENTIAL); -#endif DBUG_RETURN(0); } @@ -1042,8 +1085,11 @@ int ha_tina::rnd_next(byte *buf) ha_statistic_increment(&SSV::ha_read_rnd_next_count); current_position= next_position; - if (!share->mapped_file) + + /* don't scan an empty file */ + if (!local_saved_data_file_length) DBUG_RETURN(HA_ERR_END_OF_FILE); + if ((rc= find_current_row(buf))) DBUG_RETURN(rc); @@ -1112,6 +1158,22 @@ int ha_tina::extra(enum ha_extra_function operation) DBUG_RETURN(0); } +/* + Set end_pos to the last valid byte of continuous area, closest + to the given "hole", stored in the buffer. "Valid" here means, + not listed in the chain of deleted records ("holes"). +*/ +bool ha_tina::get_write_pos(off_t *end_pos, tina_set *closest_hole) +{ + if (closest_hole == chain_ptr) /* no more chains */ + *end_pos= file_buff->end(); + else + *end_pos= min(file_buff->end(), + closest_hole->begin); + return (closest_hole != chain_ptr) && (*end_pos == closest_hole->begin); +} + + /* Called after each table scan. In particular after deletes, and updates. In the last case we employ chain of deleted @@ -1120,53 +1182,99 @@ int ha_tina::extra(enum ha_extra_function operation) */ int ha_tina::rnd_end() { + char updated_fname[FN_REFLEN]; + off_t file_buffer_start= 0; DBUG_ENTER("ha_tina::rnd_end"); records_is_known= 1; - /* First position will be truncate position, second will be increment */ if ((chain_ptr - chain) > 0) { - tina_set *ptr; - size_t length; + tina_set *ptr= chain; /* - Setting up writable map, this will contain all of the data after the - get_mmap call that we have added to the file. + Re-read the beginning of a file (as the buffer should point to the + end of file after the scan). */ - if (get_mmap(share, 1) > 0) - DBUG_RETURN(-1); - length= share->file_stat.st_size; + file_buff->init_buff(data_file); /* - The sort handles updates/deletes with random orders. - It also sorts so that we move the final blocks to the - beginning so that we move the smallest amount of data possible. + The sort is needed when there were updates/deletes with random orders. + It sorts so that we move the firts blocks to the beginning. */ qsort(chain, (size_t)(chain_ptr - chain), sizeof(tina_set), (qsort_cmp)sort_set); - for (ptr= chain; ptr < chain_ptr; ptr++) + + off_t write_begin= 0, write_end; + + /* create the file to write updated table if it wasn't yet created */ + if (open_update_temp_file_if_needed()) + DBUG_RETURN(-1); + + /* write the file with updated info */ + while ((file_buffer_start != -1)) // while not end of file { - memmove(share->mapped_file + ptr->begin, share->mapped_file + ptr->end, - length - (size_t)ptr->end); - length= length - (size_t)(ptr->end - ptr->begin); + bool in_hole= get_write_pos(&write_end, ptr); + + /* if there is something to write, write it */ + if ((write_end - write_begin) && + (my_write(update_temp_file, + file_buff->ptr() + (write_begin - file_buff->start()), + write_end - write_begin, MYF_RW))) + goto error; + + if (in_hole) + { + /* skip hole */ + while (file_buff->end() <= ptr->end && file_buffer_start != -1) + file_buffer_start= file_buff->read_next(); + write_begin= ptr->end; + ptr++; + } + else + write_begin= write_end; + + if (write_end == file_buff->end()) + file_buffer_start= file_buff->read_next(); /* shift the buffer */ + } - /* Unmap the file before the new size is set */ - if (my_munmap(share->mapped_file, share->file_stat.st_size)) + if (my_close(update_temp_file, MYF(0))) DBUG_RETURN(-1); - /* We set it to null so that get_mmap() won't try to unmap it */ - share->mapped_file= NULL; + share->update_file_opened= FALSE; - /* Set the file to the new size */ - if (my_chsize(share->data_file, length, 0, MYF(MY_WME))) + if (share->tina_write_opened) + { + if (my_close(share->tina_write_filedes, MYF(0))) + DBUG_RETURN(-1); + /* + Mark that the writer fd is closed, so that init_tina_writer() + will reopen it later. + */ + share->tina_write_opened= FALSE; + } + + /* + Close opened fildes's. Then move updated file in place + of the old datafile. + */ + if (my_close(data_file, MYF(0)) || + my_rename(fn_format(updated_fname, share->table_name, "", CSN_EXT, + MY_REPLACE_EXT | MY_UNPACK_FILENAME), + share->data_file_name, MYF(0))) DBUG_RETURN(-1); - if (get_mmap(share, 0) > 0) + /* Open the file again and sync it */ + if (((data_file= my_open(share->data_file_name, O_RDONLY, MYF(0))) == -1) + || my_sync(data_file, MYF(MY_WME))) DBUG_RETURN(-1); } DBUG_RETURN(0); +error: + my_close(update_temp_file, MYF(0)); + share->update_file_opened= FALSE; + DBUG_RETURN(-1); } @@ -1195,10 +1303,11 @@ int ha_tina::repair(THD* thd, HA_CHECK_OPT* check_opt) File repair_file; int rc; ha_rows rows_repaired= 0; + off_t write_begin= 0, write_end; DBUG_ENTER("ha_tina::repair"); /* empty file */ - if (!share->mapped_file) + if (!share->saved_data_file_length) { share->rows_recorded= 0; goto end; @@ -1207,12 +1316,15 @@ int ha_tina::repair(THD* thd, HA_CHECK_OPT* check_opt) if (!(buf= (byte*) my_malloc(table->s->reclength, MYF(MY_WME)))) DBUG_RETURN(HA_ERR_OUT_OF_MEM); + /* position buffer to the start of the file */ + file_buff->init_buff(data_file); + /* Local_saved_data_file_length is initialized during the lock phase. Sometimes this is not getting executed before ::repair (e.g. for the log tables). We set it manually here. */ - local_saved_data_file_length= share->file_stat.st_size; + local_saved_data_file_length= share->saved_data_file_length; /* set current position to the beginning of the file */ current_position= next_position= 0; @@ -1227,11 +1339,10 @@ int ha_tina::repair(THD* thd, HA_CHECK_OPT* check_opt) if (rc == HA_ERR_END_OF_FILE) { - /* All rows were read ok until end of file, the file does not need repair. */ - /* - If rows_recorded != rows_repaired, we should update - rows_recorded value to the current amount of rows. + All rows were read ok until end of file, the file does not need repair. + If rows_recorded != rows_repaired, we should update rows_recorded value + to the current amount of rows. */ share->rows_recorded= rows_repaired; goto end; @@ -1247,36 +1358,45 @@ int ha_tina::repair(THD* thd, HA_CHECK_OPT* check_opt) 0, O_RDWR | O_TRUNC,MYF(MY_WME))) < 0) DBUG_RETURN(HA_ERR_CRASHED_ON_REPAIR); - if (my_write(repair_file, (byte*)share->mapped_file, current_position, - MYF(MY_NABP))) - DBUG_RETURN(HA_ERR_CRASHED_ON_USAGE); - my_close(repair_file, MYF(0)); + file_buff->init_buff(data_file); + + /* we just truncated the file up to the first bad row. update rows count. */ share->rows_recorded= rows_repaired; - if (my_munmap(share->mapped_file, share->file_stat.st_size)) - DBUG_RETURN(-1); - /* We set it to null so that get_mmap() won't try to unmap it */ - share->mapped_file= NULL; + /* write repaired file */ + while (1) + { + write_end= min(file_buff->end(), current_position); + if ((write_end - write_begin) && + (my_write(repair_file, file_buff->ptr(), + write_end - write_begin, MYF_RW))) + DBUG_RETURN(-1); + + write_begin= write_end; + if (write_end== current_position) + break; + else + file_buff->read_next(); /* shift the buffer */ + } /* - Close the "to"-file before renaming - On Windows one cannot rename a file, which descriptor - is still open. EACCES will be returned when trying to delete - the "to"-file in my_rename() + Close the files and rename repaired file to the datafile. + We have to close the files, as on Windows one cannot rename + a file, which descriptor is still open. EACCES will be returned + when trying to delete the "to"-file in my_rename(). */ - my_close(share->data_file,MYF(0)); - - if (my_rename(repaired_fname, share->data_file_name, MYF(0))) + if (my_close(data_file,MYF(0)) || my_close(repair_file, MYF(0)) || + my_rename(repaired_fname, share->data_file_name, MYF(0))) DBUG_RETURN(-1); /* Open the file again, it should now be repaired */ - if ((share->data_file= my_open(share->data_file_name, O_RDWR|O_APPEND, - MYF(0))) == -1) + if ((data_file= my_open(share->data_file_name, O_RDWR|O_APPEND, + MYF(0))) == -1) DBUG_RETURN(-1); - if (get_mmap(share, 0) > 0) - DBUG_RETURN(-1); + /* Set new file size. The file size will be updated by ::update_status() */ + local_saved_data_file_length= (size_t) current_position; end: share->crashed= FALSE; @@ -1295,17 +1415,12 @@ int ha_tina::delete_all_rows() if (!records_is_known) DBUG_RETURN(my_errno=HA_ERR_WRONG_COMMAND); - /* Unmap the file before the new size is set */ - if (share->mapped_file && my_munmap(share->mapped_file, - share->file_stat.st_size)) - DBUG_RETURN(-1); - share->mapped_file= NULL; + if (!share->tina_write_opened) + if (init_tina_writer()) + DBUG_RETURN(-1); /* Truncate the file to zero size */ - rc= my_chsize(share->data_file, 0, 0, MYF(MY_WME)); - - if (get_mmap(share, 0) > 0) - DBUG_RETURN(-1); + rc= my_chsize(share->tina_write_filedes, 0, 0, MYF(MY_WME)); records=0; DBUG_RETURN(rc); @@ -1367,12 +1482,15 @@ int ha_tina::check(THD* thd, HA_CHECK_OPT* check_opt) if (!(buf= (byte*) my_malloc(table->s->reclength, MYF(MY_WME)))) DBUG_RETURN(HA_ERR_OUT_OF_MEM); + /* position buffer to the start of the file */ + file_buff->init_buff(data_file); + /* Local_saved_data_file_length is initialized during the lock phase. Check does not use store_lock in certain cases. So, we set it manually here. */ - local_saved_data_file_length= share->file_stat.st_size; + local_saved_data_file_length= share->saved_data_file_length; /* set current position to the beginning of the file */ current_position= next_position= 0; /* Read the file row-by-row. If everything is ok, repair is not needed. */ @@ -1412,6 +1530,7 @@ mysql_declare_plugin(csv) tina_init_func, /* Plugin Init */ tina_done_func, /* Plugin Deinit */ 0x0100 /* 1.0 */, + 0 } mysql_declare_plugin_end; diff --git a/storage/csv/ha_tina.h b/storage/csv/ha_tina.h index d155a614780..eb30f150bbe 100644 --- a/storage/csv/ha_tina.h +++ b/storage/csv/ha_tina.h @@ -19,6 +19,7 @@ #include #define DEFAULT_CHAIN_LENGTH 512 +#define DEFAULT_FILE_WINDOW_SIZE 4096 /* Version for file format. 1 - Initial Version. That is, the version when the metafile was introduced. @@ -29,15 +30,12 @@ typedef struct st_tina_share { char *table_name; char data_file_name[FN_REFLEN]; - byte *mapped_file; /* mapped region of file */ uint table_name_length, use_count; /* Below flag is needed to make log tables work with concurrent insert. For more details see comment to ha_tina::update_status. */ my_bool is_log_table; - MY_STAT file_stat; /* Stat information for the data file */ - File data_file; /* Current open data file */ /* Here we save the length of the file for readers. This is updated by inserts, updates and deletes. The var is initialized along with the @@ -46,7 +44,10 @@ typedef struct st_tina_share { off_t saved_data_file_length; pthread_mutex_t mutex; THR_LOCK lock; + bool update_file_opened; + bool tina_write_opened; File meta_file; /* Meta file we use */ + File tina_write_filedes; /* File handler for readers */ bool crashed; /* Meta file is crashed */ ha_rows rows_recorded; /* Number of rows in tables */ } TINA_SHARE; @@ -56,6 +57,54 @@ struct tina_set { off_t end; }; +class Transparent_file +{ + File filedes; + byte *buff; /* in-memory window to the file or mmaped area */ + /* current window sizes */ + off_t lower_bound; + off_t upper_bound; + uint buff_size; + + public: + + Transparent_file(File filedes_arg) : lower_bound(0), + buff_size(DEFAULT_FILE_WINDOW_SIZE) + { + buff= (byte *) my_malloc(buff_size*sizeof(byte), MYF(MY_WME)); + /* read the beginning of the file */ + init_buff(filedes_arg); + } + + ~Transparent_file() + { my_free(buff, MYF(MY_ALLOW_ZERO_PTR)); } + + void init_buff(File filedes_arg) + { + filedes= filedes_arg; + /* read the beginning of the file */ + lower_bound= 0; + VOID(my_seek(filedes, 0, MY_SEEK_SET, MYF(0))); + if (filedes && buff) + upper_bound= my_read(filedes, buff, buff_size, MYF(0)); + } + + byte *ptr() + { return buff; } + + off_t start() + { return lower_bound; } + + off_t end() + { return upper_bound; } + + /* get a char from the given position in the file */ + char get_value (off_t offset); + /* shift a buffer windows to see the next part of the file */ + off_t read_next(); + +}; + class ha_tina: public handler { THR_LOCK_DATA lock; /* MySQL lock */ @@ -64,6 +113,9 @@ class ha_tina: public handler off_t next_position; /* Next position in the file scan */ off_t local_saved_data_file_length; /* save position for reads */ byte byte_buffer[IO_SIZE]; + Transparent_file *file_buff; + File data_file; /* File handler for readers */ + File update_temp_file; String buffer; /* The chain contains "holes" in the file, occured because of @@ -77,12 +129,19 @@ class ha_tina: public handler uint32 chain_size; bool records_is_known; +private: + bool get_write_pos(off_t *end_pos, tina_set *closest_hole); + int open_update_temp_file_if_needed(); + int init_tina_writer(); + public: ha_tina(TABLE_SHARE *table_arg); - ~ha_tina() + ~ha_tina() { if (chain_alloced) - my_free((gptr)chain,0); + my_free((gptr)chain, 0); + if (file_buff) + delete file_buff; } const char *table_type() const { return "CSV"; } const char *index_type(uint inx) { return "NONE"; } From 2b546d8ccc44558caeea20b1714b9bae100c4506 Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 20 May 2006 02:41:30 +0400 Subject: [PATCH 06/65] WL#3244 "CSV engine: convert mmap to read/write calls" post-review fixes storage/csv/ha_tina.cc: post-review fixes storage/csv/ha_tina.h: post-review fixes --- storage/csv/ha_tina.cc | 12 ++++++++---- storage/csv/ha_tina.h | 10 ++-------- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/storage/csv/ha_tina.cc b/storage/csv/ha_tina.cc index 409364f59c6..eaabea2454d 100644 --- a/storage/csv/ha_tina.cc +++ b/storage/csv/ha_tina.cc @@ -282,8 +282,7 @@ static TINA_SHARE *get_share(const char *table_name, TABLE *table) share->crashed= TRUE; /* - After we read, we set the file to dirty. When we close, we will do the - opposite. If the meta file will not open we assume it is crashed and + If the meta file will not open we assume it is crashed and mark it as such. */ if (read_meta_file(share->meta_file, &share->rows_recorded)) @@ -524,6 +523,7 @@ ha_tina::ha_tina(TABLE_SHARE *table_arg) /* Set our original buffers from pre-allocated memory */ buffer.set((char*)byte_buffer, IO_SIZE, system_charset_info); chain= chain_buffer; + file_buff= new Transparent_file(); } @@ -857,8 +857,6 @@ int ha_tina::open(const char *name, int mode, uint open_options) if ((data_file= my_open(share->data_file_name, O_RDONLY, MYF(0))) == -1) DBUG_RETURN(0); - file_buff= new Transparent_file(data_file); - /* Init locking. Pass handler object to the locking routines, so that they could save/update local_saved_data_file_length value @@ -1268,6 +1266,12 @@ int ha_tina::rnd_end() if (((data_file= my_open(share->data_file_name, O_RDONLY, MYF(0))) == -1) || my_sync(data_file, MYF(MY_WME))) DBUG_RETURN(-1); + /* + The datafile is consistent at this point and the write filedes is + closed, so nothing worrying will happen to it in case of a crash. + Here we record this fact to the meta-file. + */ + (void)write_meta_file(share->meta_file, share->rows_recorded, FALSE); } DBUG_RETURN(0); diff --git a/storage/csv/ha_tina.h b/storage/csv/ha_tina.h index eb30f150bbe..df5e4143a5c 100644 --- a/storage/csv/ha_tina.h +++ b/storage/csv/ha_tina.h @@ -19,7 +19,6 @@ #include #define DEFAULT_CHAIN_LENGTH 512 -#define DEFAULT_FILE_WINDOW_SIZE 4096 /* Version for file format. 1 - Initial Version. That is, the version when the metafile was introduced. @@ -68,13 +67,8 @@ class Transparent_file public: - Transparent_file(File filedes_arg) : lower_bound(0), - buff_size(DEFAULT_FILE_WINDOW_SIZE) - { - buff= (byte *) my_malloc(buff_size*sizeof(byte), MYF(MY_WME)); - /* read the beginning of the file */ - init_buff(filedes_arg); - } + Transparent_file() : lower_bound(0), buff_size(IO_SIZE) + { buff= (byte *) my_malloc(buff_size*sizeof(byte), MYF(MY_WME)); } ~Transparent_file() { my_free(buff, MYF(MY_ALLOW_ZERO_PTR)); } From e94ce460dbf17817d85f38cd2ecaf8e721a75d07 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 23 May 2006 14:45:56 +1000 Subject: [PATCH 07/65] BUG#20016 error in mgm protocol parser error message incorrect fix display of mgm protocol parser error in mgmapi ndb/src/mgmapi/mgmapi.cpp: fix display of mgm protocol parser error. --- ndb/src/mgmapi/mgmapi.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ndb/src/mgmapi/mgmapi.cpp b/ndb/src/mgmapi/mgmapi.cpp index 061360428af..b6c68962e4b 100644 --- a/ndb/src/mgmapi/mgmapi.cpp +++ b/ndb/src/mgmapi/mgmapi.cpp @@ -361,8 +361,9 @@ ndb_mgm_call(NdbMgmHandle handle, const ParserRow *command_reply, * Print some info about why the parser returns NULL */ fprintf(handle->errstream, - "Error in mgm protocol parser. cmd: >%s< status: %d curr: %d\n", - cmd, (Uint32)ctx.m_status, ctx.m_currentToken); + "Error in mgm protocol parser. cmd: >%s< status: %d curr: %s\n", + cmd, (Uint32)ctx.m_status, + (ctx.m_currentToken)?ctx.m_currentToken:"NULL"); DBUG_PRINT("info",("ctx.status: %d, ctx.m_currentToken: %s", ctx.m_status, ctx.m_currentToken)); } From 466b548f15f28db0840cc9ad91f2c52a3b6f721f Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 23 May 2006 14:51:04 +1000 Subject: [PATCH 08/65] BUG#19932 mgmapi crash if disconnect in ndb_mgm_get_status correctly detect and report errors in talking to mgm server and memory allocation ndb/src/mgmapi/mgmapi.cpp: correctly detect and return errors in ndb_mgm_get_status --- ndb/src/mgmapi/mgmapi.cpp | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/ndb/src/mgmapi/mgmapi.cpp b/ndb/src/mgmapi/mgmapi.cpp index 061360428af..70c492be8b1 100644 --- a/ndb/src/mgmapi/mgmapi.cpp +++ b/ndb/src/mgmapi/mgmapi.cpp @@ -678,7 +678,11 @@ ndb_mgm_get_status(NdbMgmHandle handle) out.println(""); char buf[1024]; - in.gets(buf, sizeof(buf)); + if(!in.gets(buf, sizeof(buf))) + { + SET_ERROR(handle, NDB_MGM_ILLEGAL_SERVER_REPLY, "Probably disconnected"); + return NULL; + } if(buf[strlen(buf)-1] == '\n') buf[strlen(buf)-1] = '\0'; @@ -687,7 +691,11 @@ ndb_mgm_get_status(NdbMgmHandle handle) return NULL; } - in.gets(buf, sizeof(buf)); + if(!in.gets(buf, sizeof(buf))) + { + SET_ERROR(handle, NDB_MGM_ILLEGAL_SERVER_REPLY, "Probably disconnected"); + return NULL; + } if(buf[strlen(buf)-1] == '\n') buf[strlen(buf)-1] = '\0'; @@ -710,6 +718,13 @@ ndb_mgm_get_status(NdbMgmHandle handle) malloc(sizeof(ndb_mgm_cluster_state)+ noOfNodes*(sizeof(ndb_mgm_node_state)+sizeof("000.000.000.000#"))); + if(!state) + { + SET_ERROR(handle, NDB_MGM_OUT_OF_MEMORY, + "Allocating ndb_mgm_cluster_state"); + return NULL; + } + state->no_of_nodes= noOfNodes; ndb_mgm_node_state * ptr = &state->node_states[0]; int nodeId = 0; @@ -719,7 +734,13 @@ ndb_mgm_get_status(NdbMgmHandle handle) } i = -1; ptr--; for(; i Date: Tue, 23 May 2006 16:24:26 +1000 Subject: [PATCH 09/65] BUG#18966 Change in stop/shutdown behaviour Improvements that were discussed with Tomas. Maintain protocol backwards/forwards compatibilty for client and server and support the commands from ndb_mgm 'as expected' ndb/include/mgmapi/mgmapi.h: Add ndb_mgm_stop3 mgmapi function. - This supports stopping all DB nodes, or all DB and MGM nodes. - It also returns to mgmapi program if it needs to disconnect to apply changes. Add ndb_mgm_restart3 mgmapi function. - Tells mgmapi program if it needs to disconnect to apply changes Add (internal) ndb_mgm_get_version - designed to be used to find out what protocol version we need to speak to the server. ndb/src/mgmapi/mgmapi.cpp: Add cache of mgmd version to ndb_mgm_handle. Only filled out in functions that need to know the version of the mgmd we're talking to. Initialize these members in create handle. added ndb_mgm_get_version which asks the mgm server what version it is. This call has been supported since the dawn of time, no compatibility issues here. Add implementation of ndb_mgm_stop3 Check what version of the protocol the server speaks, and speak it. Add compatibility for ndb_mgm_stop2 Same for ndb_mgm_restart3. ndb/src/mgmclient/CommandInterpreter.cpp: Simplify stop and restart code. Use the new ndb_mgm_(stop|restart)3 calls to find out if we need to disconnect. ndb/src/mgmsrv/MgmtSrvr.cpp: Add nice call for shutting down MGM servers (like shutdownDB) ndb/src/mgmsrv/MgmtSrvr.hpp: add prototype for shutdownMGM ndb/src/mgmsrv/Services.cpp: Add restart node v2, stop v2, stop all v2 while maintaining protocol backwards compatibility. Unfortunately we can't add result lines due to protocol errors in clients :( Neither can we add extra things to the 'result: Ok' line due to the use of strcmp instead of strncmp. ndb/src/mgmsrv/Services.hpp: Add prototypes for restart, stop and stopall v1 and v2 --- ndb/include/mgmapi/mgmapi.h | 57 +++++++ ndb/src/mgmapi/mgmapi.cpp | 196 ++++++++++++++++++++--- ndb/src/mgmclient/CommandInterpreter.cpp | 75 +++------ ndb/src/mgmsrv/MgmtSrvr.cpp | 21 +++ ndb/src/mgmsrv/MgmtSrvr.hpp | 2 + ndb/src/mgmsrv/Services.cpp | 81 ++++++++-- ndb/src/mgmsrv/Services.hpp | 12 +- 7 files changed, 352 insertions(+), 92 deletions(-) diff --git a/ndb/include/mgmapi/mgmapi.h b/ndb/include/mgmapi/mgmapi.h index c6c0fdebb65..798e09f0c2b 100644 --- a/ndb/include/mgmapi/mgmapi.h +++ b/ndb/include/mgmapi/mgmapi.h @@ -694,6 +694,28 @@ extern "C" { int ndb_mgm_stop2(NdbMgmHandle handle, int no_of_nodes, const int * node_list, int abort); + /** + * Stops cluster nodes + * + * @param handle Management handle. + * @param no_of_nodes Number of database nodes to stop
+ * -1: All database and management nodes
+ * 0: All database nodes in cluster
+ * n: Stop the n node(s) specified in + * the array node_list + * @param node_list List of node IDs of database nodes to be stopped + * @param abort Don't perform graceful stop, + * but rather stop immediately + * @param disconnect Returns true if you need to disconnect to apply + * the stop command (e.g. stopping the mgm server + * that handle is connected to) + * + * @return Number of nodes stopped (-1 on error). + */ + int ndb_mgm_stop3(NdbMgmHandle handle, int no_of_nodes, + const int * node_list, int abort, int *disconnect); + + /** * Restart database nodes * @@ -733,6 +755,31 @@ extern "C" { const int * node_list, int initial, int nostart, int abort); + /** + * Restart nodes + * + * @param handle Management handle. + * @param no_of_nodes Number of database nodes to be restarted:
+ * 0: Restart all database nodes in the cluster
+ * n: Restart the n node(s) specified in the + * array node_list + * @param node_list List of node IDs of database nodes to be restarted + * @param initial Remove filesystem from restarting node(s) + * @param nostart Don't actually start node(s) but leave them + * waiting for start command + * @param abort Don't perform graceful restart, + * but rather restart immediately + * @param disconnect Returns true if mgmapi client must disconnect from + * server to apply the requested operation. (e.g. + * restart the management server) + * + * + * @return Number of nodes stopped (-1 on error). + */ + int ndb_mgm_restart3(NdbMgmHandle handle, int no_of_nodes, + const int * node_list, int initial, + int nostart, int abort, int *disconnect); + /** * Start database nodes * @@ -1023,6 +1070,16 @@ extern "C" { */ Uint32 ndb_mgm_get_mgmd_nodeid(NdbMgmHandle handle); + /** + * Get the version of the mgm server we're talking to. + * Designed to allow switching of protocol depending on version + * so that new clients can speak to old servers in a compat mode + */ + int ndb_mgm_get_version(NdbMgmHandle handle, + int *major, int *minor, int* build, + int len, char* str); + + /** * Config iterator */ diff --git a/ndb/src/mgmapi/mgmapi.cpp b/ndb/src/mgmapi/mgmapi.cpp index b02367a8870..3109cea7782 100644 --- a/ndb/src/mgmapi/mgmapi.cpp +++ b/ndb/src/mgmapi/mgmapi.cpp @@ -27,6 +27,7 @@ #include #include "mgmapi_configuration.hpp" #include +#include #include #include @@ -103,6 +104,9 @@ struct ndb_mgm_handle { #endif FILE *errstream; char *m_name; + int mgmd_version_major; + int mgmd_version_minor; + int mgmd_version_build; }; #define SET_ERROR(h, e, s) setError(h, e, __LINE__, s) @@ -168,6 +172,10 @@ ndb_mgm_create_handle() h->logfile = 0; #endif + h->mgmd_version_major= -1; + h->mgmd_version_minor= -1; + h->mgmd_version_build= -1; + DBUG_PRINT("info", ("handle=0x%x", (UintPtr)h)); DBUG_RETURN(h); } @@ -826,37 +834,75 @@ ndb_mgm_stop(NdbMgmHandle handle, int no_of_nodes, const int * node_list) return ndb_mgm_stop2(handle, no_of_nodes, node_list, 0); } - extern "C" -int +int ndb_mgm_stop2(NdbMgmHandle handle, int no_of_nodes, const int * node_list, int abort) { - SET_ERROR(handle, NDB_MGM_NO_ERROR, "Executing: ndb_mgm_stop2"); - const ParserRow stop_reply[] = { + int disconnect; + return ndb_mgm_stop3(handle, no_of_nodes, node_list, abort, &disconnect); +} + + +extern "C" +int +ndb_mgm_stop3(NdbMgmHandle handle, int no_of_nodes, const int * node_list, + int abort, int *disconnect) +{ + SET_ERROR(handle, NDB_MGM_NO_ERROR, "Executing: ndb_mgm_stop3"); + const ParserRow stop_reply_v1[] = { MGM_CMD("stop reply", NULL, ""), MGM_ARG("stopped", Int, Optional, "No of stopped nodes"), MGM_ARG("result", String, Mandatory, "Error message"), MGM_END() }; + const ParserRow stop_reply_v2[] = { + MGM_CMD("stop reply", NULL, ""), + MGM_ARG("stopped", Int, Optional, "No of stopped nodes"), + MGM_ARG("result", String, Mandatory, "Error message"), + MGM_ARG("disconnect", Int, Mandatory, "Need to disconnect"), + MGM_END() + }; + CHECK_HANDLE(handle, -1); CHECK_CONNECTED(handle, -1); - if(no_of_nodes < 0){ + if(handle->mgmd_version_build==-1) + { + char verstr[50]; + ndb_mgm_get_version(handle, + &(handle->mgmd_version_major), + &(handle->mgmd_version_minor), + &(handle->mgmd_version_build), + sizeof(verstr), + verstr); + } + int use_v2= (handle->mgmd_version_major==5) + && ( + (handle->mgmd_version_minor==0 && handle->mgmd_version_build>=21) + ||(handle->mgmd_version_minor==1 && handle->mgmd_version_build>=12) + ); + + if(no_of_nodes < -1){ SET_ERROR(handle, NDB_MGM_ILLEGAL_NUMBER_OF_NODES, "Negative number of nodes requested to stop"); return -1; } Uint32 stoppedNoOfNodes = 0; - if(no_of_nodes == 0){ + if(no_of_nodes <= 0){ /** - * All database nodes should be stopped + * All nodes should be stopped (all or just db) */ Properties args; args.put("abort", abort); + if(use_v2) + args.put("stop", (no_of_nodes==-1)?"mgm,db":"db"); const Properties *reply; - reply = ndb_mgm_call(handle, stop_reply, "stop all", &args); + if(use_v2) + reply = ndb_mgm_call(handle, stop_reply_v2, "stop all v2", &args); + else + reply = ndb_mgm_call(handle, stop_reply_v1, "stop all", &args); CHECK_REPLY(reply, -1); if(!reply->get("stopped", &stoppedNoOfNodes)){ @@ -865,6 +911,10 @@ ndb_mgm_stop2(NdbMgmHandle handle, int no_of_nodes, const int * node_list, delete reply; return -1; } + if(use_v2) + reply->get("disconnect", (Uint32*)disconnect); + else + *disconnect= 0; BaseString result; reply->get("result", result); if(strcmp(result.c_str(), "Ok") != 0) { @@ -890,7 +940,11 @@ ndb_mgm_stop2(NdbMgmHandle handle, int no_of_nodes, const int * node_list, args.put("abort", abort); const Properties *reply; - reply = ndb_mgm_call(handle, stop_reply, "stop", &args); + if(use_v2) + reply = ndb_mgm_call(handle, stop_reply_v2, "stop v2", &args); + else + reply = ndb_mgm_call(handle, stop_reply_v1, "stop", &args); + CHECK_REPLY(reply, stoppedNoOfNodes); if(!reply->get("stopped", &stoppedNoOfNodes)){ SET_ERROR(handle, NDB_MGM_STOP_FAILED, @@ -898,6 +952,10 @@ ndb_mgm_stop2(NdbMgmHandle handle, int no_of_nodes, const int * node_list, delete reply; return -1; } + if(use_v2) + reply->get("disconnect", (Uint32*)disconnect); + else + *disconnect= 0; BaseString result; reply->get("result", result); if(strcmp(result.c_str(), "Ok") != 0) { @@ -909,22 +967,65 @@ ndb_mgm_stop2(NdbMgmHandle handle, int no_of_nodes, const int * node_list, return stoppedNoOfNodes; } +extern "C" +int +ndb_mgm_restart(NdbMgmHandle handle, int no_of_nodes, const int *node_list) +{ + SET_ERROR(handle, NDB_MGM_NO_ERROR, "Executing: ndb_mgm_restart"); + return ndb_mgm_restart2(handle, no_of_nodes, node_list, 0, 0, 0); +} + extern "C" int ndb_mgm_restart2(NdbMgmHandle handle, int no_of_nodes, const int * node_list, int initial, int nostart, int abort) { - SET_ERROR(handle, NDB_MGM_NO_ERROR, "Executing: ndb_mgm_restart2"); + int disconnect; + + return ndb_mgm_restart3(handle, no_of_nodes, node_list, initial, nostart, + abort, &disconnect); +} + +extern "C" +int +ndb_mgm_restart3(NdbMgmHandle handle, int no_of_nodes, const int * node_list, + int initial, int nostart, int abort, int *disconnect) +{ + SET_ERROR(handle, NDB_MGM_NO_ERROR, "Executing: ndb_mgm_restart3"); Uint32 restarted = 0; - const ParserRow restart_reply[] = { + const ParserRow restart_reply_v1[] = { MGM_CMD("restart reply", NULL, ""), MGM_ARG("result", String, Mandatory, "Error message"), MGM_ARG("restarted", Int, Optional, "No of restarted nodes"), MGM_END() }; + const ParserRow restart_reply_v2[] = { + MGM_CMD("restart reply", NULL, ""), + MGM_ARG("result", String, Mandatory, "Error message"), + MGM_ARG("restarted", Int, Optional, "No of restarted nodes"), + MGM_ARG("disconnect", Int, Optional, "Disconnect to apply"), + MGM_END() + }; + CHECK_HANDLE(handle, -1); CHECK_CONNECTED(handle, -1); - + + if(handle->mgmd_version_build==-1) + { + char verstr[50]; + ndb_mgm_get_version(handle, + &(handle->mgmd_version_major), + &(handle->mgmd_version_minor), + &(handle->mgmd_version_build), + sizeof(verstr), + verstr); + } + int use_v2= (handle->mgmd_version_major==5) + && ( + (handle->mgmd_version_minor==0 && handle->mgmd_version_build>=21) + ||(handle->mgmd_version_minor==1 && handle->mgmd_version_build>=12) + ); + if(no_of_nodes < 0){ SET_ERROR(handle, NDB_MGM_RESTART_FAILED, "Restart requested of negative number of nodes"); @@ -939,7 +1040,7 @@ ndb_mgm_restart2(NdbMgmHandle handle, int no_of_nodes, const int * node_list, const Properties *reply; const int timeout = handle->read_timeout; handle->read_timeout= 5*60*1000; // 5 minutes - reply = ndb_mgm_call(handle, restart_reply, "restart all", &args); + reply = ndb_mgm_call(handle, restart_reply_v1, "restart all", &args); handle->read_timeout= timeout; CHECK_REPLY(reply, -1); @@ -975,7 +1076,10 @@ ndb_mgm_restart2(NdbMgmHandle handle, int no_of_nodes, const int * node_list, const Properties *reply; const int timeout = handle->read_timeout; handle->read_timeout= 5*60*1000; // 5 minutes - reply = ndb_mgm_call(handle, restart_reply, "restart node", &args); + if(use_v2) + reply = ndb_mgm_call(handle, restart_reply_v2, "restart node v2", &args); + else + reply = ndb_mgm_call(handle, restart_reply_v1, "restart node", &args); handle->read_timeout= timeout; if(reply != NULL) { BaseString result; @@ -986,20 +1090,16 @@ ndb_mgm_restart2(NdbMgmHandle handle, int no_of_nodes, const int * node_list, return -1; } reply->get("restarted", &restarted); + if(use_v2) + reply->get("disconnect", (Uint32*)disconnect); + else + *disconnect= 0; delete reply; } return restarted; } -extern "C" -int -ndb_mgm_restart(NdbMgmHandle handle, int no_of_nodes, const int *node_list) -{ - SET_ERROR(handle, NDB_MGM_NO_ERROR, "Executing: ndb_mgm_restart"); - return ndb_mgm_restart2(handle, no_of_nodes, node_list, 0, 0, 0); -} - static const char *clusterlog_severity_names[]= { "enabled", "debug", "info", "warning", "error", "critical", "alert" }; @@ -2353,4 +2453,56 @@ int ndb_mgm_end_session(NdbMgmHandle handle) DBUG_RETURN(0); } +extern "C" +int ndb_mgm_get_version(NdbMgmHandle handle, + int *major, int *minor, int *build, int len, char* str) +{ + DBUG_ENTER("ndb_mgm_get_version"); + CHECK_HANDLE(handle, 0); + CHECK_CONNECTED(handle, 0); + + Properties args; + + const ParserRow reply[]= { + MGM_CMD("version", NULL, ""), + MGM_ARG("id", Int, Mandatory, "ID"), + MGM_ARG("major", Int, Mandatory, "Major"), + MGM_ARG("minor", Int, Mandatory, "Minor"), + MGM_ARG("string", String, Mandatory, "String"), + MGM_END() + }; + + const Properties *prop; + prop = ndb_mgm_call(handle, reply, "get version", &args); + CHECK_REPLY(prop, 0); + + Uint32 id; + if(!prop->get("id",&id)){ + fprintf(handle->errstream, "Unable to get value\n"); + return 0; + } + *build= getBuild(id); + + if(!prop->get("major",(Uint32*)major)){ + fprintf(handle->errstream, "Unable to get value\n"); + return 0; + } + + if(!prop->get("minor",(Uint32*)minor)){ + fprintf(handle->errstream, "Unable to get value\n"); + return 0; + } + + BaseString result; + if(!prop->get("string", result)){ + fprintf(handle->errstream, "Unable to get value\n"); + return 0; + } + + strncpy(str, result.c_str(), len); + + delete prop; + DBUG_RETURN(1); +} + template class Vector*>; diff --git a/ndb/src/mgmclient/CommandInterpreter.cpp b/ndb/src/mgmclient/CommandInterpreter.cpp index 9e1224ab3ef..290f57a881a 100644 --- a/ndb/src/mgmclient/CommandInterpreter.cpp +++ b/ndb/src/mgmclient/CommandInterpreter.cpp @@ -1057,7 +1057,8 @@ CommandInterpreter::executeShutdown(char* parameters) NdbAutoPtr ap1((char*)state); int result = 0; - result = ndb_mgm_stop(m_mgmsrv, 0, 0); + int need_disconnect; + result = ndb_mgm_stop3(m_mgmsrv, -1, 0, 0, &need_disconnect); if (result < 0) { ndbout << "Shutdown of NDB Cluster node(s) failed." << endl; printError(); @@ -1066,39 +1067,11 @@ CommandInterpreter::executeShutdown(char* parameters) ndbout << result << " NDB Cluster node(s) have shutdown." << endl; - int nodeId= 0; - int this_mgmd= 0; - this_mgmd= ndb_mgm_get_mgmd_nodeid(m_mgmsrv); - while(get_next_nodeid(state, &nodeId, NDB_MGM_NODE_TYPE_MGM)) - { - if(nodeId==this_mgmd) - continue; - ndbout << "Shutting down NDB Cluster management server nodeId=" - << nodeId << "..."; - result = ndb_mgm_stop(m_mgmsrv, 1, &nodeId); - if (result <= 0) { - ndbout << " failed." << endl; - printError(); - } - else - ndbout << "Done." << endl; - } - - ndbout << "Shutting down NDB Cluster management server nodeId=" - << this_mgmd << "..."; - result= ndb_mgm_stop(m_mgmsrv, 1, &this_mgmd); - if (result <= 0) { - ndbout << " failed." << endl; - printError(); - } - else - { - ndbout << "Done." << endl; + if(need_disconnect) { ndbout << "Disconnecting to allow management server to shutdown." << endl; disconnect(); } - ndbout << "NDB Cluster management servers shutdown." << endl; return 0; } @@ -1487,6 +1460,7 @@ CommandInterpreter::executeStop(Vector &command_list, unsigned command_pos, int *node_ids, int no_of_nodes) { + int need_disconnect; int abort= 0; for (; command_pos < command_list.size(); command_pos++) { @@ -1501,7 +1475,8 @@ CommandInterpreter::executeStop(Vector &command_list, return; } - int result= ndb_mgm_stop2(m_mgmsrv, no_of_nodes, node_ids, abort); + int result= ndb_mgm_stop3(m_mgmsrv, no_of_nodes, node_ids, abort, + &need_disconnect); if (result < 0) { ndbout_c("Shutdown failed."); @@ -1513,27 +1488,19 @@ CommandInterpreter::executeStop(Vector &command_list, ndbout_c("NDB Cluster has shutdown."); else { - int mgm_id= 0; - int need_reconnect= 0; - mgm_id= ndb_mgm_get_mgmd_nodeid(m_mgmsrv); ndbout << "Node"; for (int i= 0; i < no_of_nodes; i++) - { - if(node_ids[i] == mgm_id) - need_reconnect= 1; - else ndbout << " " << node_ids[i]; - } ndbout_c(" has shutdown."); - if(need_reconnect) - { - ndbout << "You are connected to node " << mgm_id - << ", disconnecting to allow it to shutdown" - << endl; - disconnect(); - } } } + + if(need_disconnect) + { + ndbout << "Disconnecting to allow Management Server to shutdown" << endl; + disconnect(); + } + } void @@ -1624,6 +1591,7 @@ CommandInterpreter::executeRestart(Vector &command_list, int nostart= 0; int initialstart= 0; int abort= 0; + int need_disconnect= 0; for (; command_pos < command_list.size(); command_pos++) { @@ -1648,9 +1616,9 @@ CommandInterpreter::executeRestart(Vector &command_list, return; } - result= ndb_mgm_restart2(m_mgmsrv, no_of_nodes, node_ids, - initialstart, nostart, abort); - + result= ndb_mgm_restart3(m_mgmsrv, no_of_nodes, node_ids, + initialstart, nostart, abort, &need_disconnect); + if (result <= 0) { ndbout_c("Restart failed."); printError(); @@ -1661,18 +1629,13 @@ CommandInterpreter::executeRestart(Vector &command_list, ndbout_c("NDB Cluster is being restarted."); else { - int mgm_id= 0; - mgm_id= ndb_mgm_get_mgmd_nodeid(m_mgmsrv); - ndbout << "Node"; for (int i= 0; i < no_of_nodes; i++) - { - if(node_ids[i] == mgm_id) - disconnect(); ndbout << " " << node_ids[i]; - } ndbout_c(" is being restarted"); } + if(need_disconnect) + disconnect(); } } diff --git a/ndb/src/mgmsrv/MgmtSrvr.cpp b/ndb/src/mgmsrv/MgmtSrvr.cpp index 4c3631488e6..8a9a33779d8 100644 --- a/ndb/src/mgmsrv/MgmtSrvr.cpp +++ b/ndb/src/mgmsrv/MgmtSrvr.cpp @@ -1196,6 +1196,27 @@ int MgmtSrvr::stopNodes(const Vector &node_ids, return ret; } +int MgmtSrvr::shutdownMGM(int *stopCount, bool abort, int *stopSelf) +{ + NodeId nodeId = 0; + int error; + + while(getNextNodeId(&nodeId, NDB_MGM_NODE_TYPE_MGM)) + { + if(nodeId==getOwnNodeId()) + continue; + error= sendStopMgmd(nodeId, abort, true, false, + false, false); + if (error == 0) + *stopCount++; + } + + *stopSelf= 1; + *stopCount++; + + return 0; +} + /* * Perform DB nodes shutdown. * MGM servers are left in their current state diff --git a/ndb/src/mgmsrv/MgmtSrvr.hpp b/ndb/src/mgmsrv/MgmtSrvr.hpp index b47ee69fd9f..2102897eb61 100644 --- a/ndb/src/mgmsrv/MgmtSrvr.hpp +++ b/ndb/src/mgmsrv/MgmtSrvr.hpp @@ -256,6 +256,8 @@ public: int stopNodes(const Vector &node_ids, int *stopCount, bool abort, int *stopSelf); + int shutdownMGM(int *stopCount, bool abort, int *stopSelf); + /** * shutdown the DB nodes */ diff --git a/ndb/src/mgmsrv/Services.cpp b/ndb/src/mgmsrv/Services.cpp index a6c483fcdcd..01f11d600df 100644 --- a/ndb/src/mgmsrv/Services.cpp +++ b/ndb/src/mgmsrv/Services.cpp @@ -145,7 +145,13 @@ ParserRow commands[] = { MGM_CMD("get info clusterlog", &MgmApiSession::getInfoClusterLog, ""), - MGM_CMD("restart node", &MgmApiSession::restart, ""), + MGM_CMD("restart node", &MgmApiSession::restart_v1, ""), + MGM_ARG("node", String, Mandatory, "Nodes to restart"), + MGM_ARG("initialstart", Int, Optional, "Initial start"), + MGM_ARG("nostart", Int, Optional, "No start"), + MGM_ARG("abort", Int, Optional, "Abort"), + + MGM_CMD("restart node v2", &MgmApiSession::restart_v2, ""), MGM_ARG("node", String, Mandatory, "Nodes to restart"), MGM_ARG("initialstart", Int, Optional, "Initial start"), MGM_ARG("nostart", Int, Optional, "No start"), @@ -186,13 +192,21 @@ ParserRow commands[] = { MGM_CMD("abort backup", &MgmApiSession::abortBackup, ""), MGM_ARG("id", Int, Mandatory, "Backup id"), - MGM_CMD("stop", &MgmApiSession::stop, ""), + MGM_CMD("stop", &MgmApiSession::stop_v1, ""), MGM_ARG("node", String, Mandatory, "Node"), MGM_ARG("abort", Int, Mandatory, "Node"), - MGM_CMD("stop all", &MgmApiSession::stopAll, ""), + MGM_CMD("stop v2", &MgmApiSession::stop_v2, ""), + MGM_ARG("node", String, Mandatory, "Node"), MGM_ARG("abort", Int, Mandatory, "Node"), - + + MGM_CMD("stop all", &MgmApiSession::stopAll_v1, ""), + MGM_ARG("abort", Int, Mandatory, "Node"), + + MGM_CMD("stop all v2", &MgmApiSession::stopAll_v2, ""), + MGM_ARG("abort", Int, Mandatory, "Node"), + MGM_ARG("stop", String, Mandatory, "MGM/DB or both"), + MGM_CMD("enter single user", &MgmApiSession::enterSingleUser, ""), MGM_ARG("nodeId", Int, Mandatory, "Node"), @@ -850,8 +864,19 @@ MgmApiSession::stopSignalLog(Parser::Context &, } void -MgmApiSession::restart(Parser::Context &, +MgmApiSession::restart_v1(Parser::Context &, Properties const &args) { + restart(args,1); +} + +void +MgmApiSession::restart_v2(Parser::Context &, + Properties const &args) { + restart(args,2); +} + +void +MgmApiSession::restart(Properties const &args, int version) { Uint32 nostart = 0, initialstart = 0, @@ -885,6 +910,8 @@ MgmApiSession::restart(Parser::Context &, } else m_output->println("result: Ok"); m_output->println("restarted: %d", restarted); + if(version>1) + m_output->println("disconnect: %d", (m_stopSelf)?1:0); m_output->println(""); } @@ -994,8 +1021,19 @@ MgmApiSession::getInfoClusterLog(Parser::Context &, } void -MgmApiSession::stop(Parser::Context &, - Properties const &args) { +MgmApiSession::stop_v1(Parser::Context &, + Properties const &args) { + stop(args,1); +} + +void +MgmApiSession::stop_v2(Parser::Context &, + Properties const &args) { + stop(args,2); +} + +void +MgmApiSession::stop(Properties const &args, int version) { Uint32 abort; char *nodes_str; Vector nodes; @@ -1028,25 +1066,46 @@ MgmApiSession::stop(Parser::Context &, else m_output->println("result: Ok"); m_output->println("stopped: %d", stopped); + if(version>1) + m_output->println("disconnect: %d", (m_stopSelf)?1:0); m_output->println(""); } void -MgmApiSession::stopAll(Parser::Context &, +MgmApiSession::stopAll_v1(Parser::Context &, Properties const &args) { - int stopped = 0; + stopAll(args,"db",1); +} + +void +MgmApiSession::stopAll_v2(Parser::Context &, + Properties const &args) { + BaseString tostop; + args.get("stop", tostop); + stopAll(args, tostop.c_str(), 2); +} + +void +MgmApiSession::stopAll(Properties const &args, const char* tostop, int ver) { + int stopped[2] = {0,0}; Uint32 abort; args.get("abort", &abort); - int result = m_mgmsrv.shutdownDB(&stopped, abort != 0); + int result= 0; + if(strstr(tostop,"db")) + result= m_mgmsrv.shutdownDB(&stopped[0], abort != 0); + if(strstr(tostop,"mgm")) + result= m_mgmsrv.shutdownMGM(&stopped[1], abort!=0, &m_stopSelf); m_output->println("stop reply"); if(result != 0) m_output->println("result: %s", get_error_text(result)); else m_output->println("result: Ok"); - m_output->println("stopped: %d", stopped); + m_output->println("stopped: %d", stopped[0]+stopped[1]); + if(ver >1) + m_output->println("disconnect: %d", (m_stopSelf)?1:0); m_output->println(""); } diff --git a/ndb/src/mgmsrv/Services.hpp b/ndb/src/mgmsrv/Services.hpp index b8a73f5e4bc..0d4331b128b 100644 --- a/ndb/src/mgmsrv/Services.hpp +++ b/ndb/src/mgmsrv/Services.hpp @@ -63,7 +63,9 @@ public: void getVersion(Parser_t::Context &ctx, const class Properties &args); void getStatus(Parser_t::Context &ctx, const class Properties &args); void getInfoClusterLog(Parser_t::Context &ctx, const class Properties &args); - void restart(Parser_t::Context &ctx, const class Properties &args); + void restart(const class Properties &args, int version); + void restart_v1(Parser_t::Context &ctx, const class Properties &args); + void restart_v2(Parser_t::Context &ctx, const class Properties &args); void restartAll(Parser_t::Context &ctx, const class Properties &args); void insertError(Parser_t::Context &ctx, const class Properties &args); void setTrace(Parser_t::Context &ctx, const class Properties &args); @@ -75,8 +77,12 @@ public: void abortBackup(Parser_t::Context &ctx, const class Properties &args); void enterSingleUser(Parser_t::Context &ctx, const class Properties &args); void exitSingleUser(Parser_t::Context &ctx, const class Properties &args); - void stop(Parser_t::Context &ctx, const class Properties &args); - void stopAll(Parser_t::Context &ctx, const class Properties &args); + void stop_v1(Parser_t::Context &ctx, const class Properties &args); + void stop_v2(Parser_t::Context &ctx, const class Properties &args); + void stop(const class Properties &args, int version); + void stopAll_v1(Parser_t::Context &ctx, const class Properties &args); + void stopAll_v2(Parser_t::Context &ctx, const class Properties &args); + void stopAll(Properties const &args, const char* tostop, int ver); void start(Parser_t::Context &ctx, const class Properties &args); void startAll(Parser_t::Context &ctx, const class Properties &args); void bye(Parser_t::Context &ctx, const class Properties &args); From 17c4ddeb3af71f6c3d262a64f1ba196bc83f4ae5 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 23 May 2006 19:36:06 +0200 Subject: [PATCH 10/65] don't let bugfix for bug#8303 break the bugfix for bug#8378 revert the fix for bug#8303 correct the test for bug#8378 mysql-test/r/ctype_sjis.result: updated mysql-test/t/ctype_sjis.test: updated sql/sql_lex.cc: revert the fix for bug#8303 tests/mysql_client_test.c: correct the test for bug#8378 --- mysql-test/r/ctype_sjis.result | 4 ++-- mysql-test/t/ctype_sjis.test | 2 +- sql/sql_lex.cc | 30 +++++------------------------- tests/mysql_client_test.c | 21 ++++++++++++++------- 4 files changed, 22 insertions(+), 35 deletions(-) diff --git a/mysql-test/r/ctype_sjis.result b/mysql-test/r/ctype_sjis.result index d1976a516d2..dab5991b505 100644 --- a/mysql-test/r/ctype_sjis.result +++ b/mysql-test/r/ctype_sjis.result @@ -172,6 +172,6 @@ c2h ab_def drop table t1; SET NAMES sjis; -SELECT HEX('²“‘@\Œ\') FROM DUAL; -HEX('²“‘@_Œ\') +SELECT HEX('²“‘@Œ\') FROM DUAL; +HEX('²“‘@Œ\') 8DB2939181408C5C diff --git a/mysql-test/t/ctype_sjis.test b/mysql-test/t/ctype_sjis.test index 1d807b5e9a8..01e0b334554 100644 --- a/mysql-test/t/ctype_sjis.test +++ b/mysql-test/t/ctype_sjis.test @@ -78,6 +78,6 @@ SET collation_connection='sjis_bin'; --character_set sjis SET NAMES sjis; -SELECT HEX('²“‘@\Œ\') FROM DUAL; +SELECT HEX('²“‘@Œ\') FROM DUAL; # End of 4.1 tests diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 16641ad6dd5..fbc8403cbbc 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -295,18 +295,7 @@ static char *get_text(LEX *lex) found_escape=1; if (lex->ptr == lex->end_of_query) return 0; -#ifdef USE_MB - int l; - if (use_mb(cs) && - (l = my_ismbchar(cs, - (const char *)lex->ptr, - (const char *)lex->end_of_query))) { - lex->ptr += l; - continue; - } - else -#endif - yySkip(); + yySkip(); } else if (c == sep) { @@ -335,9 +324,6 @@ static char *get_text(LEX *lex) { uchar *to; - /* Re-use found_escape for tracking state of escapes */ - found_escape= 0; - for (to=start ; str != end ; str++) { #ifdef USE_MB @@ -351,7 +337,7 @@ static char *get_text(LEX *lex) continue; } #endif - if (!found_escape && *str == '\\' && str+1 != end) + if (*str == '\\' && str+1 != end) { switch(*++str) { case 'n': @@ -377,20 +363,14 @@ static char *get_text(LEX *lex) *to++= '\\'; // remember prefix for wildcard /* Fall through */ default: - found_escape= 1; - str--; + *to++= *str; break; } } - else if (!found_escape && *str == sep) - { - found_escape= 1; - } + else if (*str == sep) + *to++= *str++; // Two ' or " else - { *to++ = *str; - found_escape= 0; - } } *to=0; lex->yytoklen=(uint) (to-start); diff --git a/tests/mysql_client_test.c b/tests/mysql_client_test.c index 71ae5e4f052..662fac6116a 100644 --- a/tests/mysql_client_test.c +++ b/tests/mysql_client_test.c @@ -11554,25 +11554,26 @@ static void test_bug7990() static void test_bug8378() { #if defined(HAVE_CHARSET_gbk) && !defined(EMBEDDED_LIBRARY) - MYSQL *lmysql; + MYSQL *old_mysql=mysql; char out[9]; /* strlen(TEST_BUG8378)*2+1 */ - int len; + char buf[256]; + int len, rc; myheader("test_bug8378"); if (!opt_silent) fprintf(stdout, "\n Establishing a test connection ..."); - if (!(lmysql= mysql_init(NULL))) + if (!(mysql= mysql_init(NULL))) { myerror("mysql_init() failed"); exit(1); } - if (mysql_options(lmysql, MYSQL_SET_CHARSET_NAME, "gbk")) + if (mysql_options(mysql, MYSQL_SET_CHARSET_NAME, "gbk")) { myerror("mysql_options() failed"); exit(1); } - if (!(mysql_real_connect(lmysql, opt_host, opt_user, + if (!(mysql_real_connect(mysql, opt_host, opt_user, opt_password, current_db, opt_port, opt_unix_socket, 0))) { @@ -11582,12 +11583,18 @@ static void test_bug8378() if (!opt_silent) fprintf(stdout, " OK"); - len= mysql_real_escape_string(lmysql, out, TEST_BUG8378_IN, 4); + len= mysql_real_escape_string(mysql, out, TEST_BUG8378_IN, 4); /* No escaping should have actually happened. */ DIE_UNLESS(memcmp(out, TEST_BUG8378_OUT, len) == 0); - mysql_close(lmysql); + sprintf(buf, "SELECT '%s'", out); + rc=mysql_real_query(mysql, buf, strlen(buf)); + myquery(rc); + + mysql_close(mysql); + + mysql=old_mysql; #endif } From 1991a87d831bb48b31989c622e35acda11df7fef Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 26 May 2006 01:24:14 +0400 Subject: [PATCH 11/65] Fixed bug#16716: subselect in concat() may lead to a wrong result. The Item_func_concat::val_str() function tries to make as less re-allocations as possible. This results in appending strings returned by 2nd and next arguments to the string returned by 1st argument if the buffer for the first argument has enough free space. A constant subselect is evaluated only once and its result is stored in an Item_cache_str. In the case when the first argument of the concat() function is such a subselect Item_cache_str returns the stored value and Item_func_concat::val_str() append values of other arguments to it. But for the next row the value in the Item_cache_str isn't restored because the subselect is a constant one and it isn't evaluated second time. This results in appending string values of 2nd and next arguments to the result of the previous Item_func_concat::val_str() call. The Item_func_concat::val_str() function now checks whether the first argument is a constant one and if so it doesn't append values of 2nd and next arguments to the string value returned by it. mysql-test/t/func_concat.test: Added test case for bug#16716: subselect in concat() may lead to a wrong result. mysql-test/r/func_concat.result: Added test case for bug#16716: subselect in concat() may lead to a wrong result. sql/item_strfunc.cc: Fixed bug#16716: subselect in concat() may lead to a wrong result. The Item_func_concat::val_str() function now checks whether the first argument is a constant one and if so it doesn't append values of 2nd and next arguments to the string value returned by it. --- mysql-test/r/func_concat.result | 7 +++++++ mysql-test/t/func_concat.test | 7 +++++++ sql/item_strfunc.cc | 5 ++++- 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/func_concat.result b/mysql-test/r/func_concat.result index cf6fbf2da4f..94f1f640523 100644 --- a/mysql-test/r/func_concat.result +++ b/mysql-test/r/func_concat.result @@ -64,3 +64,10 @@ select 'a' union select concat('a', -0.0); a a good +select concat((select x from (select 'a' as x) as t1 ), +(select y from (select 'b' as y) as t2 )) from (select 1 union select 2 ) +as t3; +concat((select x from (select 'a' as x) as t1 ), +(select y from (select 'b' as y) as t2 )) +ab +ab diff --git a/mysql-test/t/func_concat.test b/mysql-test/t/func_concat.test index 69ce10c83c9..f546a25f647 100644 --- a/mysql-test/t/func_concat.test +++ b/mysql-test/t/func_concat.test @@ -50,4 +50,11 @@ select 'a' union select concat('a', -0); --replace_result 'a-0.0' good 'a0.0' good select 'a' union select concat('a', -0.0); +# +# Bug#16716: subselect in concat() may lead to a wrong result +# +select concat((select x from (select 'a' as x) as t1 ), + (select y from (select 'b' as y) as t2 )) from (select 1 union select 2 ) + as t3; + # End of 4.1 tests diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index e74d0100b55..0dfbf3b7a1d 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -252,11 +252,13 @@ String *Item_func_concat::val_str(String *str) DBUG_ASSERT(fixed == 1); String *res,*res2,*use_as_buff; uint i; + bool is_const= 0; null_value=0; if (!(res=args[0]->val_str(str))) goto null; use_as_buff= &tmp_value; + is_const= args[0]->const_item(); for (i=1 ; i < arg_count ; i++) { if (res->length() == 0) @@ -279,7 +281,7 @@ String *Item_func_concat::val_str(String *str) current_thd->variables.max_allowed_packet); goto null; } - if (res->alloced_length() >= res->length()+res2->length()) + if (!is_const && res->alloced_length() >= res->length()+res2->length()) { // Use old buffer res->append(*res2); } @@ -334,6 +336,7 @@ String *Item_func_concat::val_str(String *str) res= &tmp_value; use_as_buff=str; } + is_const= 0; } } res->set_charset(collation.collation); From fd7d4d742993cb33641565e5f9923ae6a21fdb61 Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 28 May 2006 22:01:38 +0400 Subject: [PATCH 12/65] Fixed bug#15351: Wrong collation used for comparison of md5() and sha() argument can lead to a wrong result. md5() and sha() functions treat their arguments as case sensitive strings. But when they are compared their arguments were compared as a case insensitive strings which leads to two functions with different arguments and thus different results to being identical. This can lead to a wrong decision made in the range optimizer and thus lead to a wrong result set. Item_func_md5::fix_length_and_dec() and Item_func_sha::fix_length_and_dec() functions now set binary collation on their arguments. sql/item_strfunc.cc: Fixed bug#15351: Wrong collation used for comparison of md5() and sha() argument can lead to a wrong result. Item_func_md5::fix_length_and_dec() and Item_func_sha::fix_length_and_dec() functions now set binary collation on their arguments. mysql-test/r/func_str.result: Added test case for the bug#15351: Wrong collation used for comparison of md5() and sha() argument can lead to a wrong result. mysql-test/t/func_str.test: Added test case for the bug#15351: Wrong collation used for comparison of md5() and sha() argument can lead to a wrong result. --- mysql-test/r/func_str.result | 15 +++++++++++++++ mysql-test/t/func_str.test | 12 ++++++++++++ sql/item_strfunc.cc | 20 ++++++++++++++++++-- 3 files changed, 45 insertions(+), 2 deletions(-) diff --git a/mysql-test/r/func_str.result b/mysql-test/r/func_str.result index 0609624af18..24e6bb6f38a 100644 --- a/mysql-test/r/func_str.result +++ b/mysql-test/r/func_str.result @@ -1006,4 +1006,19 @@ NULL select ifnull(load_file("lkjlkj"),"it's null"); ifnull(load_file("lkjlkj"),"it's null") it's null +create table t1 (f1 varchar(4), f2 varchar(64), unique key k1 (f1,f2)); +insert into t1 values ( 'test',md5('test')), ('test', sha('test')); +select * from t1 where f1='test' and (f2= md5("test") or f2= md5("TEST")); +f1 f2 +test 098f6bcd4621d373cade4e832627b4f6 +select * from t1 where f1='test' and (f2= md5("TEST") or f2= md5("test")); +f1 f2 +test 098f6bcd4621d373cade4e832627b4f6 +select * from t1 where f1='test' and (f2= sha("test") or f2= sha("TEST")); +f1 f2 +test a94a8fe5ccb19ba61c4c0873d391e987982fbbd3 +select * from t1 where f1='test' and (f2= sha("TEST") or f2= sha("test")); +f1 f2 +test a94a8fe5ccb19ba61c4c0873d391e987982fbbd3 +drop table t1; End of 4.1 tests diff --git a/mysql-test/t/func_str.test b/mysql-test/t/func_str.test index c2f76dbac43..c36e15a08b9 100644 --- a/mysql-test/t/func_str.test +++ b/mysql-test/t/func_str.test @@ -669,4 +669,16 @@ drop table t1; select load_file("lkjlkj"); select ifnull(load_file("lkjlkj"),"it's null"); +# +# Bug#15351: Wrong collation used for comparison of md5() and sha() +# parameter can lead to a wrong result. +# +create table t1 (f1 varchar(4), f2 varchar(64), unique key k1 (f1,f2)); +insert into t1 values ( 'test',md5('test')), ('test', sha('test')); +select * from t1 where f1='test' and (f2= md5("test") or f2= md5("TEST")); +select * from t1 where f1='test' and (f2= md5("TEST") or f2= md5("test")); +select * from t1 where f1='test' and (f2= sha("test") or f2= sha("TEST")); +select * from t1 where f1='test' and (f2= sha("TEST") or f2= sha("test")); +drop table t1; + --echo End of 4.1 tests diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index e74d0100b55..e817edac6c0 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -118,7 +118,15 @@ String *Item_func_md5::val_str(String *str) void Item_func_md5::fix_length_and_dec() { - max_length=32; + max_length=32; + /* + The MD5() function treats its parameter as being a case sensitive. Thus + we set binary collation on it so different instances of MD5() will be + compared properly. + */ + args[0]->collation.set( + get_charset_by_csname(args[0]->collation.collation->csname, + MY_CS_BINSORT,MYF(0)), DERIVATION_COERCIBLE); } @@ -159,7 +167,15 @@ String *Item_func_sha::val_str(String *str) void Item_func_sha::fix_length_and_dec() { - max_length=SHA1_HASH_SIZE*2; // size of hex representation of hash + max_length=SHA1_HASH_SIZE*2; // size of hex representation of hash + /* + The SHA() function treats its parameter as being a case sensitive. Thus + we set binary collation on it so different instances of MD5() will be + compared properly. + */ + args[0]->collation.set( + get_charset_by_csname(args[0]->collation.collation->csname, + MY_CS_BINSORT,MYF(0)), DERIVATION_COERCIBLE); } From 2264b644735e869fdbb16397c75170d86e2de127 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 29 May 2006 00:32:59 +0400 Subject: [PATCH 13/65] Fixed bug#19225: unchecked error results in server crash In multi-table delete a table for delete can't be used for selecting in subselects. Appropriate error was raised but wasn't checked which leads to a crash at the execution phase. The mysql_execute_command() now checks for errors before executing select for multi-delete. mysql-test/t/multi_update.test: Added test case for bug#19225: unchecked error results in server crash mysql-test/r/multi_update.result: Added test case for bug#19225: unchecked error results in server crash sql/sql_parse.cc: Fixed bug#19225: unchecked error results in server crash The mysql_execute_command() now checks for errors before executing select for multi-delete. --- mysql-test/r/multi_update.result | 5 +++++ mysql-test/t/multi_update.test | 8 ++++++++ sql/sql_parse.cc | 4 ++-- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/mysql-test/r/multi_update.result b/mysql-test/r/multi_update.result index dba10296063..ee62bd3bce6 100644 --- a/mysql-test/r/multi_update.result +++ b/mysql-test/r/multi_update.result @@ -475,3 +475,8 @@ aclid bigint, index idx_acl(aclid) insert into t2 values(1,null); delete t2, t1 from t2 left join t1 on (t2.aclid=t1.aclid) where t2.refid='1'; drop table t1, t2; +create table t1(a int); +create table t2(a int); +delete from t1,t2 using t1,t2 where t1.a=(select a from t1); +ERROR HY000: You can't specify target table 't1' for update in FROM clause +drop table t1, t2; diff --git a/mysql-test/t/multi_update.test b/mysql-test/t/multi_update.test index 27bdf4df3d7..e628e900c73 100644 --- a/mysql-test/t/multi_update.test +++ b/mysql-test/t/multi_update.test @@ -448,4 +448,12 @@ insert into t2 values(1,null); delete t2, t1 from t2 left join t1 on (t2.aclid=t1.aclid) where t2.refid='1'; drop table t1, t2; +# +# Bug#19225: unchecked error leads to server crash +# +create table t1(a int); +create table t2(a int); +--error 1093 +delete from t1,t2 using t1,t2 where t1.a=(select a from t1); +drop table t1, t2; # End of 4.1 tests diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 51ef3f31b26..06005f31198 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -3047,8 +3047,8 @@ unsent_create_error: } } - if (!thd->is_fatal_error && (result= new multi_delete(thd,aux_tables, - table_count))) + if (!res && !thd->is_fatal_error && + (result= new multi_delete(thd,aux_tables, table_count))) { res= mysql_select(thd, &select_lex->ref_pointer_array, select_lex->get_table_list(), From c007dabaf62b7ac7eb41e2460b12d652b132bb7f Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 30 May 2006 00:36:48 +0400 Subject: [PATCH 14/65] Fixed bug#18360: Incorrect type coercion in IN() results in false comparison The IN() function uses agg_cmp_type() to aggregate all types of its arguments to find out some common type for comparisons. In this particular case the char() and the int was aggregated to double because char() can contain values like '1.5'. But all strings which do not start from a digit are converted to 0. thus 'a' and 'z' become equal. This behaviour is reasonable when all function arguments are constants. But when there is a field or an expression this can lead to false comparisons. In this case it makes more sense to coerce constants to the type of the field argument. The agg_cmp_type() function now aggregates types of constant and non-constant items separately. If some non-constant items will be found then their aggregated type will be returned. Thus after the aggregation constants will be coerced to the aggregated type. mysql-test/t/func_in.test: Added test case for bug#18360: Incorrect type coercion in IN() results in false comparison. mysql-test/r/func_in.result: Added test case for bug#18360: Incorrect type coercion in IN() results in false comparison. sql/item_cmpfunc.cc: Fixed bug#18360: Incorrect type coercion in IN() results in false comparison. The agg_cmp_type() function now aggregates types of constant and non-constant items separately. If some non-constant items will be found then their aggregated type will be returned. Thus after the aggregation constants will be coerced to the aggregated type. --- mysql-test/r/func_in.result | 21 +++++++++++++++++++++ mysql-test/t/func_in.test | 12 ++++++++++++ sql/item_cmpfunc.cc | 37 +++++++++++++++++++++++++++++++++++-- 3 files changed, 68 insertions(+), 2 deletions(-) diff --git a/mysql-test/r/func_in.result b/mysql-test/r/func_in.result index 3cf2afc83d1..0632dddb87e 100644 --- a/mysql-test/r/func_in.result +++ b/mysql-test/r/func_in.result @@ -202,3 +202,24 @@ select count(*) from t1 where id not in (1,2); count(*) 1 drop table t1; +create table t1 (f1 char(1), f2 int); +insert into t1 values (1,0),('a',1),('z',2); +select f1 from t1 where f1 in (1,'z'); +f1 +1 +z +select f2 from t1 where f2 in (1,'z'); +f2 +0 +1 +select f1 from t1 where 'z' in (1,f1); +f1 +z +select * from t1 where 'z' in (f2,f1); +f1 f2 +z 2 +select * from t1 where 1 in (f2,f1); +f1 f2 +1 0 +a 1 +drop table t1; diff --git a/mysql-test/t/func_in.test b/mysql-test/t/func_in.test index 2ffe5a2d5f7..3481f2c7b11 100644 --- a/mysql-test/t/func_in.test +++ b/mysql-test/t/func_in.test @@ -109,4 +109,16 @@ select count(*) from t1 where id not in (1); select count(*) from t1 where id not in (1,2); drop table t1; +# +# Bug#18360 Incorrect type coercion in IN() results in false comparison +# +create table t1 (f1 char(1), f2 int); +insert into t1 values (1,0),('a',1),('z',2); +select f1 from t1 where f1 in (1,'z'); +select f2 from t1 where f2 in (1,'z'); +select f1 from t1 where 'z' in (1,f1); +select * from t1 where 'z' in (f2,f1); +select * from t1 where 1 in (f2,f1); +drop table t1; + # End of 4.1 tests diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 3c41fb56d89..bfbb3355004 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -58,12 +58,45 @@ static void agg_result_type(Item_result *type, Item **items, uint nitems) } } + +/* + Aggregates result types from the array of items. + + SYNOPSIS: + agg_cmp_type() + type [out] the aggregated type + items array of items to aggregate the type from + nitems number of items in the array + + DESCRIPTION + This function aggregates result types from the array of items. Found type + supposed to be used later for comparison of values of these items. + Aggregation itself is performed by the item_cmp_type() function. + + NOTES + Aggregation rules: + If all items are constants the type will be aggregated from all items. + If there are some non-constant items then only types of non-constant + items will be used for aggregation. +*/ static void agg_cmp_type(Item_result *type, Item **items, uint nitems) { uint i; type[0]= items[0]->result_type(); - for (i=1 ; i < nitems ; i++) - type[0]= item_cmp_type(type[0], items[i]->result_type()); + /* Reset to 0 on first occurence of non-const item. 1 otherwise */ + bool is_const= items[0]->const_item(); + + for (i= 1 ; i < nitems ; i++) + { + if (!items[i]->const_item()) + { + type[0]= is_const ? items[i]->result_type() : + item_cmp_type(type[0], items[i]->result_type()); + is_const= 0; + } + else if (is_const) + type[0]= item_cmp_type(type[0], items[i]->result_type()); + } } static void my_coll_agg_error(DTCollation &c1, DTCollation &c2, From ba67edc47c6dbb17f423d51a0e51413dbd250cc0 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 30 May 2006 10:48:32 +0400 Subject: [PATCH 15/65] fix windows build of CSV storage/csv/ha_tina.cc: portability fix --- storage/csv/ha_tina.cc | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/storage/csv/ha_tina.cc b/storage/csv/ha_tina.cc index b7d00caed8f..3f9320b7e19 100644 --- a/storage/csv/ha_tina.cc +++ b/storage/csv/ha_tina.cc @@ -939,7 +939,7 @@ int ha_tina::write_row(byte * buf) DBUG_RETURN(-1); /* use pwrite, as concurrent reader could have changed the position */ - if (my_write(share->tina_write_filedes, buffer.ptr(), size, + if (my_write(share->tina_write_filedes, (byte*)buffer.ptr(), size, MYF(MY_WME | MY_NABP))) DBUG_RETURN(-1); @@ -1246,7 +1246,8 @@ int ha_tina::rnd_end() /* if there is something to write, write it */ if ((write_end - write_begin) && (my_write(update_temp_file, - file_buff->ptr() + (write_begin - file_buff->start()), + (byte*)(file_buff->ptr() + + (write_begin - file_buff->start())), write_end - write_begin, MYF_RW))) goto error; @@ -1402,7 +1403,7 @@ int ha_tina::repair(THD* thd, HA_CHECK_OPT* check_opt) { write_end= min(file_buff->end(), current_position); if ((write_end - write_begin) && - (my_write(repair_file, file_buff->ptr(), + (my_write(repair_file, (byte*)file_buff->ptr(), write_end - write_begin, MYF_RW))) DBUG_RETURN(-1); From d948704bbaeff69310d7ccaa293b8d36f3777a59 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 30 May 2006 18:44:26 +0400 Subject: [PATCH 16/65] one more portability fix storage/csv/ha_tina.h: portability fix --- storage/csv/ha_tina.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage/csv/ha_tina.h b/storage/csv/ha_tina.h index df5e4143a5c..6b72eef49ff 100644 --- a/storage/csv/ha_tina.h +++ b/storage/csv/ha_tina.h @@ -71,7 +71,7 @@ class Transparent_file { buff= (byte *) my_malloc(buff_size*sizeof(byte), MYF(MY_WME)); } ~Transparent_file() - { my_free(buff, MYF(MY_ALLOW_ZERO_PTR)); } + { my_free((gptr)buff, MYF(MY_ALLOW_ZERO_PTR)); } void init_buff(File filedes_arg) { From 63d6336465c13c010977c04a037468e55b68e9e0 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 30 May 2006 23:05:34 +0400 Subject: [PATCH 17/65] item_cmpfunc.cc, func_in.result, multi_update.result: After merge fix mysql-test/r/multi_update.result: After merge fix mysql-test/r/func_in.result: After merge fix sql/item_cmpfunc.cc: After merge fix --- mysql-test/r/func_in.result | 8 ++++++++ mysql-test/r/multi_update.result | 1 + sql/item_cmpfunc.cc | 23 +++++++++++++++++++++++ 3 files changed, 32 insertions(+) diff --git a/mysql-test/r/func_in.result b/mysql-test/r/func_in.result index dd355c693d7..f758e2adfd9 100644 --- a/mysql-test/r/func_in.result +++ b/mysql-test/r/func_in.result @@ -212,12 +212,20 @@ select f2 from t1 where f2 in (1,'z'); f2 0 1 +Warnings: +Warning 1292 Truncated incorrect INTEGER value: 'z' select f1 from t1 where 'z' in (1,f1); f1 z select * from t1 where 'z' in (f2,f1); f1 f2 +1 0 +a 1 z 2 +Warnings: +Warning 1292 Truncated incorrect DOUBLE value: 'z' +Warning 1292 Truncated incorrect DOUBLE value: 'z' +Warning 1292 Truncated incorrect DOUBLE value: 'z' select * from t1 where 1 in (f2,f1); f1 f2 1 0 diff --git a/mysql-test/r/multi_update.result b/mysql-test/r/multi_update.result index 8d56cf6f2dc..b4a7aa5cb76 100644 --- a/mysql-test/r/multi_update.result +++ b/mysql-test/r/multi_update.result @@ -480,6 +480,7 @@ create table t1(a int); create table t2(a int); delete from t1,t2 using t1,t2 where t1.a=(select a from t1); ERROR HY000: You can't specify target table 't1' for update in FROM clause +drop table t1, t2; create table t1 ( c char(8) not null ) engine=innodb; insert into t1 values ('0'),('1'),('2'),('3'),('4'),('5'),('6'),('7'),('8'),('9'); insert into t1 values ('A'),('B'),('C'),('D'),('E'),('F'); diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 1701af2325f..cbf77507b84 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -63,6 +63,27 @@ static void agg_result_type(Item_result *type, Item **items, uint nitems) } +/* + Aggregates result types from the array of items. + + SYNOPSIS: + agg_cmp_type() + type [out] the aggregated type + items array of items to aggregate the type from + nitems number of items in the array + + DESCRIPTION + This function aggregates result types from the array of items. Found type + supposed to be used later for comparison of values of these items. + Aggregation itself is performed by the item_cmp_type() function. + + NOTES + Aggregation rules: + If all items are constants the type will be aggregated from all items. + If there are some non-constant items then only types of non-constant + items will be used for aggregation. +*/ + static void agg_cmp_type(THD *thd, Item_result *type, Item **items, uint nitems) { uint i; @@ -89,6 +110,8 @@ static void agg_cmp_type(THD *thd, Item_result *type, Item **items, uint nitems) } else if (is_const) type[0]= item_cmp_type(type[0], items[i]->result_type()); + else if (field && convert_constant_item(thd, field, &items[i])) + type[0]= INT_RESULT; } } From 883f72a6b4e21f5d133bb09260f77481ac6f49d2 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 31 May 2006 10:50:35 +0400 Subject: [PATCH 18/65] fix csv test failure on aix storage/csv/ha_tina.cc: one cannot sync file, opened for reading --- storage/csv/ha_tina.cc | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/storage/csv/ha_tina.cc b/storage/csv/ha_tina.cc index 3f9320b7e19..35d7fc8d4a3 100644 --- a/storage/csv/ha_tina.cc +++ b/storage/csv/ha_tina.cc @@ -1267,8 +1267,10 @@ int ha_tina::rnd_end() } - if (my_close(update_temp_file, MYF(0))) + if (my_sync(update_temp_file, MYF(MY_WME)) || + my_close(update_temp_file, MYF(0))) DBUG_RETURN(-1); + share->update_file_opened= FALSE; if (share->tina_write_opened) @@ -1292,9 +1294,8 @@ int ha_tina::rnd_end() share->data_file_name, MYF(0))) DBUG_RETURN(-1); - /* Open the file again and sync it */ - if (((data_file= my_open(share->data_file_name, O_RDONLY, MYF(0))) == -1) - || my_sync(data_file, MYF(MY_WME))) + /* Open the file again */ + if (((data_file= my_open(share->data_file_name, O_RDONLY, MYF(0))) == -1)) DBUG_RETURN(-1); /* The datafile is consistent at this point and the write filedes is From 3390eaa08582d9c0d0e12db7259ae00ede9741e7 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 2 Jun 2006 12:04:03 +0300 Subject: [PATCH 19/65] Bug #4981: 4.x and 5.x produce non-optimal execution path, 3.23 regression test failure The member SEL_ARG::min_flag was not initialized, due to which the condition for no GEOM_FLAG in function key_or did not choose "Range checked for each record" as the correct access method. mysql-test/r/select.result: testcase for 'Range checked' access method mysql-test/t/select.test: testcase for 'Range checked' access method sql/opt_range.cc: All of the class members initialized --- mysql-test/r/select.result | 14 ++++++++++++++ mysql-test/t/select.test | 14 ++++++++++++++ sql/opt_range.cc | 2 +- 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/select.result b/mysql-test/r/select.result index b80ca2b195e..5511a4bb66b 100644 --- a/mysql-test/r/select.result +++ b/mysql-test/r/select.result @@ -2714,3 +2714,17 @@ select * from t1 where f1 in (select f3 from t2 where (f3,f4)= (select f3,f4 fro f1 f2 1 1 drop table t1,t2; +CREATE TABLE t1(a int, b int, c int, KEY b(b), KEY c(c)); +insert into t1 values (1,0,0),(2,0,0); +CREATE TABLE t2 (a int, b varchar(2), c varchar(2), PRIMARY KEY(a)); +insert into t2 values (1,'',''), (2,'',''); +CREATE TABLE t3 (a int, b int, PRIMARY KEY (a,b), KEY a (a), KEY b (b)); +insert into t3 values (1,1),(1,2); +explain select straight_join DISTINCT t2.a,t2.b, t1.c from t1, t3, t2 +where (t1.c=t2.a or (t1.c=t3.a and t2.a=t3.b)) and t1.b=556476786 and +t2.b like '%%' order by t2.b limit 0,1; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ref b,c b 5 const 1 Using where; Using temporary; Using filesort +1 SIMPLE t3 index PRIMARY,a,b PRIMARY 8 NULL 2 Using index +1 SIMPLE t2 ALL PRIMARY NULL NULL NULL 2 Range checked for each record (index map: 0x1) +DROP TABLE t1,t2,t3; diff --git a/mysql-test/t/select.test b/mysql-test/t/select.test index 996d5854854..3dcc16f1625 100644 --- a/mysql-test/t/select.test +++ b/mysql-test/t/select.test @@ -2248,4 +2248,18 @@ insert into t2 values(1,1); select * from t1 where f1 in (select f3 from t2 where (f3,f4)= (select f3,f4 from t2)); drop table t1,t2; +# +# Bug #4981: 4.x and 5.x produce non-optimal execution path, 3.23 regression test failure +# +CREATE TABLE t1(a int, b int, c int, KEY b(b), KEY c(c)); +insert into t1 values (1,0,0),(2,0,0); +CREATE TABLE t2 (a int, b varchar(2), c varchar(2), PRIMARY KEY(a)); +insert into t2 values (1,'',''), (2,'',''); +CREATE TABLE t3 (a int, b int, PRIMARY KEY (a,b), KEY a (a), KEY b (b)); +insert into t3 values (1,1),(1,2); +# must have "range checked" for t2 +explain select straight_join DISTINCT t2.a,t2.b, t1.c from t1, t3, t2 + where (t1.c=t2.a or (t1.c=t3.a and t2.a=t3.b)) and t1.b=556476786 and + t2.b like '%%' order by t2.b limit 0,1; +DROP TABLE t1,t2,t3; # End of 4.1 tests diff --git a/sql/opt_range.cc b/sql/opt_range.cc index 71f937f90c6..67141aab6ce 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -64,7 +64,7 @@ public: uint8 min_flag, uint8 max_flag, uint8 maybe_flag); SEL_ARG(enum Type type_arg) :elements(1),use_count(1),left(0),next_key_part(0),color(BLACK), - type(type_arg) + type(type_arg),min_flag(0) {} inline bool is_same(SEL_ARG *arg) { From ea3f845325ba6db648f20c21564d9f2b54b2db9e Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 2 Jun 2006 17:22:21 +0300 Subject: [PATCH 20/65] bad merge fixed for b4981. --- mysql-test/r/select.result | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/mysql-test/r/select.result b/mysql-test/r/select.result index ea8ae804ac3..bcf4c9c27fe 100644 --- a/mysql-test/r/select.result +++ b/mysql-test/r/select.result @@ -2716,6 +2716,20 @@ select * from t1 where f1 in (select f3 from t2 where (f3,f4)= (select f3,f4 fro f1 f2 1 1 drop table t1,t2; +CREATE TABLE t1(a int, b int, c int, KEY b(b), KEY c(c)); +insert into t1 values (1,0,0),(2,0,0); +CREATE TABLE t2 (a int, b varchar(2), c varchar(2), PRIMARY KEY(a)); +insert into t2 values (1,'',''), (2,'',''); +CREATE TABLE t3 (a int, b int, PRIMARY KEY (a,b), KEY a (a), KEY b (b)); +insert into t3 values (1,1),(1,2); +explain select straight_join DISTINCT t2.a,t2.b, t1.c from t1, t3, t2 +where (t1.c=t2.a or (t1.c=t3.a and t2.a=t3.b)) and t1.b=556476786 and +t2.b like '%%' order by t2.b limit 0,1; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ref b,c b 5 const 1 Using where; Using temporary; Using filesort +1 SIMPLE t3 index PRIMARY,a,b PRIMARY 8 NULL 2 Using index +1 SIMPLE t2 ALL PRIMARY NULL NULL NULL 2 Range checked for each record (index map: 0x1) +DROP TABLE t1,t2,t3; CREATE TABLE t1 ( city char(30) ); INSERT INTO t1 VALUES ('London'); INSERT INTO t1 VALUES ('Paris'); @@ -3446,17 +3460,3 @@ id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 range PRIMARY,b b 5 NULL 3 Using where 1 SIMPLE t2 ref c c 5 test.t1.a 2 Using where DROP TABLE t1, t2; -CREATE TABLE t1(a int, b int, c int, KEY b(b), KEY c(c)); -insert into t1 values (1,0,0),(2,0,0); -CREATE TABLE t2 (a int, b varchar(2), c varchar(2), PRIMARY KEY(a)); -insert into t2 values (1,'',''), (2,'',''); -CREATE TABLE t3 (a int, b int, PRIMARY KEY (a,b), KEY a (a), KEY b (b)); -insert into t3 values (1,1),(1,2); -explain select straight_join DISTINCT t2.a,t2.b, t1.c from t1, t3, t2 -where (t1.c=t2.a or (t1.c=t3.a and t2.a=t3.b)) and t1.b=556476786 and -t2.b like '%%' order by t2.b limit 0,1; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ref b,c b 5 const 1 Using where; Using temporary; Using filesort -1 SIMPLE t3 index PRIMARY,a,b PRIMARY 8 NULL 2 Using index -1 SIMPLE t2 ALL PRIMARY NULL NULL NULL 2 Range checked for each record (index map: 0x1) -DROP TABLE t1,t2,t3; From cbbd0fdb2c14984e8abf5f9dc8ee8280eda367a9 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 2 Jun 2006 18:02:22 +0300 Subject: [PATCH 21/65] Removed duplicate tests from select.test --- mysql-test/r/select.result | 101 ------------------------------------- mysql-test/t/select.test | 94 ---------------------------------- 2 files changed, 195 deletions(-) diff --git a/mysql-test/r/select.result b/mysql-test/r/select.result index bcf4c9c27fe..4c1e64cc1cb 100644 --- a/mysql-test/r/select.result +++ b/mysql-test/r/select.result @@ -2656,16 +2656,6 @@ t11 MyISAM 10 Dynamic 0 0 X X X X X X X X latin1_swedish_ci NULL select 123 as a from t1 where f1 is null; a drop table t1,t11; -CREATE TABLE t1 (a INT, b INT); -(SELECT a, b AS c FROM t1) ORDER BY c+1; -a c -(SELECT a, b AS c FROM t1) ORDER BY b+1; -a c -SELECT a, b AS c FROM t1 ORDER BY c+1; -a c -SELECT a, b AS c FROM t1 ORDER BY b+1; -a c -drop table t1; CREATE TABLE t1 ( a INT NOT NULL, b INT NOT NULL, UNIQUE idx (a,b) ); INSERT INTO t1 VALUES (1,1),(1,2),(1,3),(1,4); CREATE TABLE t2 ( a INT NOT NULL, b INT NOT NULL, e INT ); @@ -2730,61 +2720,6 @@ id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t3 index PRIMARY,a,b PRIMARY 8 NULL 2 Using index 1 SIMPLE t2 ALL PRIMARY NULL NULL NULL 2 Range checked for each record (index map: 0x1) DROP TABLE t1,t2,t3; -CREATE TABLE t1 ( city char(30) ); -INSERT INTO t1 VALUES ('London'); -INSERT INTO t1 VALUES ('Paris'); -SELECT * FROM t1 WHERE city='London'; -city -London -SELECT * FROM t1 WHERE city='london'; -city -London -EXPLAIN SELECT * FROM t1 WHERE city='London' AND city='london'; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ALL NULL NULL NULL NULL 2 Using where -SELECT * FROM t1 WHERE city='London' AND city='london'; -city -London -EXPLAIN SELECT * FROM t1 WHERE city LIKE '%london%' AND city='London'; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ALL NULL NULL NULL NULL 2 Using where -SELECT * FROM t1 WHERE city LIKE '%london%' AND city='London'; -city -London -DROP TABLE t1; -create table t1 (a int(11) unsigned, b int(11) unsigned); -insert into t1 values (1,0), (1,1), (1,2); -select a-b from t1 order by 1; -a-b -0 -1 -18446744073709551615 -select a-b , (a-b < 0) from t1 order by 1; -a-b (a-b < 0) -0 0 -1 0 -18446744073709551615 0 -select a-b as d, (a-b >= 0), b from t1 group by b having d >= 0; -d (a-b >= 0) b -1 1 0 -0 1 1 -18446744073709551615 1 2 -select cast((a - b) as unsigned) from t1 order by 1; -cast((a - b) as unsigned) -0 -1 -18446744073709551615 -drop table t1; -create table t1 (a int(11)); -select all all * from t1; -a -select distinct distinct * from t1; -a -select all distinct * from t1; -ERROR HY000: Incorrect usage of ALL and DISTINCT -select distinct all * from t1; -ERROR HY000: Incorrect usage of ALL and DISTINCT -drop table t1; CREATE TABLE t1 ( K2C4 varchar(4) character set latin1 collate latin1_bin NOT NULL default '', K4N4 varchar(4) character set latin1 collate latin1_bin NOT NULL default '0000', @@ -2818,19 +2753,6 @@ WART 0100 1 WART 0200 1 WART 0300 3 DROP TABLE t1; -CREATE TABLE t1 ( a BLOB, INDEX (a(20)) ); -CREATE TABLE t2 ( a BLOB, INDEX (a(20)) ); -INSERT INTO t1 VALUES ('one'),('two'),('three'),('four'),('five'); -INSERT INTO t2 VALUES ('one'),('two'),('three'),('four'),('five'); -EXPLAIN SELECT * FROM t1 LEFT JOIN t2 USE INDEX (a) ON t1.a=t2.a; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ALL NULL NULL NULL NULL 5 -1 SIMPLE t2 ref a a 23 test.t1.a 2 -EXPLAIN SELECT * FROM t1 LEFT JOIN t2 FORCE INDEX (a) ON t1.a=t2.a; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ALL NULL NULL NULL NULL 5 -1 SIMPLE t2 ref a a 23 test.t1.a 2 -DROP TABLE t1, t2; create table t1 (a int, b int); create table t2 like t1; select t1.a from (t1 inner join t2 on t1.a=t2.a) where t2.a=1; @@ -2861,29 +2783,6 @@ x NULL 1.0000 drop table t1; -create table t1 (a int(11)); -select all all * from t1; -a -select distinct distinct * from t1; -a -select all distinct * from t1; -ERROR HY000: Incorrect usage of ALL and DISTINCT -select distinct all * from t1; -ERROR HY000: Incorrect usage of ALL and DISTINCT -drop table t1; -CREATE TABLE t1 ( a BLOB, INDEX (a(20)) ); -CREATE TABLE t2 ( a BLOB, INDEX (a(20)) ); -INSERT INTO t1 VALUES ('one'),('two'),('three'),('four'),('five'); -INSERT INTO t2 VALUES ('one'),('two'),('three'),('four'),('five'); -EXPLAIN SELECT * FROM t1 LEFT JOIN t2 USE INDEX (a) ON t1.a=t2.a; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ALL NULL NULL NULL NULL 5 -1 SIMPLE t2 ref a a 23 test.t1.a 2 -EXPLAIN SELECT * FROM t1 LEFT JOIN t2 FORCE INDEX (a) ON t1.a=t2.a; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ALL NULL NULL NULL NULL 5 -1 SIMPLE t2 ref a a 23 test.t1.a 2 -DROP TABLE t1, t2; CREATE TABLE t1 (a int); CREATE TABLE t2 (a int); INSERT INTO t1 VALUES (1), (2), (3), (4), (5); diff --git a/mysql-test/t/select.test b/mysql-test/t/select.test index 6e7882b1047..4ed7c3d1de9 100644 --- a/mysql-test/t/select.test +++ b/mysql-test/t/select.test @@ -2217,15 +2217,6 @@ show table status like 't1%'; select 123 as a from t1 where f1 is null; drop table t1,t11; -# Bug 7672 Unknown column error in order clause -# -CREATE TABLE t1 (a INT, b INT); -(SELECT a, b AS c FROM t1) ORDER BY c+1; -(SELECT a, b AS c FROM t1) ORDER BY b+1; -SELECT a, b AS c FROM t1 ORDER BY c+1; -SELECT a, b AS c FROM t1 ORDER BY b+1; -drop table t1; - # # Bug #3874 (function in GROUP and LEFT JOIN) # @@ -2280,48 +2271,6 @@ explain select straight_join DISTINCT t2.a,t2.b, t1.c from t1, t3, t2 DROP TABLE t1,t2,t3; # End of 4.1 tests -# -# Test case for bug 7098: substitution of a constant for a string field -# - -CREATE TABLE t1 ( city char(30) ); -INSERT INTO t1 VALUES ('London'); -INSERT INTO t1 VALUES ('Paris'); - -SELECT * FROM t1 WHERE city='London'; -SELECT * FROM t1 WHERE city='london'; -EXPLAIN SELECT * FROM t1 WHERE city='London' AND city='london'; -SELECT * FROM t1 WHERE city='London' AND city='london'; -EXPLAIN SELECT * FROM t1 WHERE city LIKE '%london%' AND city='London'; -SELECT * FROM t1 WHERE city LIKE '%london%' AND city='London'; - -DROP TABLE t1; - -# -# Bug#7425 inconsistent sort order on unsigned columns result of substraction -# - -create table t1 (a int(11) unsigned, b int(11) unsigned); -insert into t1 values (1,0), (1,1), (1,2); -select a-b from t1 order by 1; -select a-b , (a-b < 0) from t1 order by 1; -select a-b as d, (a-b >= 0), b from t1 group by b having d >= 0; -select cast((a - b) as unsigned) from t1 order by 1; -drop table t1; - - -# -# Bug#8733 server accepts malformed query (multiply mentioned distinct) -# -create table t1 (a int(11)); -select all all * from t1; -select distinct distinct * from t1; ---error 1221 -select all distinct * from t1; ---error 1221 -select distinct all * from t1; -drop table t1; - # # Test for bug #6474 # @@ -2356,21 +2305,6 @@ SELECT K2C4, K4N4, F2I4 FROM t1 WHERE K2C4 = 'WART' AND (K2C4 = 'WART' OR K4N4 = '0200'); DROP TABLE t1; -# -# Test case for bug 7520: a wrong cost of the index for a BLOB field -# - -CREATE TABLE t1 ( a BLOB, INDEX (a(20)) ); -CREATE TABLE t2 ( a BLOB, INDEX (a(20)) ); - -INSERT INTO t1 VALUES ('one'),('two'),('three'),('four'),('five'); -INSERT INTO t2 VALUES ('one'),('two'),('three'),('four'),('five'); - -EXPLAIN SELECT * FROM t1 LEFT JOIN t2 USE INDEX (a) ON t1.a=t2.a; -EXPLAIN SELECT * FROM t1 LEFT JOIN t2 FORCE INDEX (a) ON t1.a=t2.a; - -DROP TABLE t1, t2; - # # Bug#8670 # @@ -2409,34 +2343,6 @@ select distinct avg(s1) as x from t1 group by s1 with rollup; drop table t1; -# -# Bug#8733 server accepts malformed query (multiply mentioned distinct) -# -create table t1 (a int(11)); -select all all * from t1; -select distinct distinct * from t1; ---error 1221 -select all distinct * from t1; ---error 1221 -select distinct all * from t1; -drop table t1; - - -# -# Test case for bug 7520: a wrong cost of the index for a BLOB field -# - -CREATE TABLE t1 ( a BLOB, INDEX (a(20)) ); -CREATE TABLE t2 ( a BLOB, INDEX (a(20)) ); - -INSERT INTO t1 VALUES ('one'),('two'),('three'),('four'),('five'); -INSERT INTO t2 VALUES ('one'),('two'),('three'),('four'),('five'); - -EXPLAIN SELECT * FROM t1 LEFT JOIN t2 USE INDEX (a) ON t1.a=t2.a; -EXPLAIN SELECT * FROM t1 LEFT JOIN t2 FORCE INDEX (a) ON t1.a=t2.a; - -DROP TABLE t1, t2; - # # Test for bug #10084: STRAIGHT_JOIN with ON expression # From e3e0658779bffacd5323efb54ecb7f42ded19231 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 2 Jun 2006 14:14:57 -0700 Subject: [PATCH 22/65] Fixed bug #18206. The bug report revealed two problems related to min/max optimization: 1. If the length of a constant key used in a SARGable condition for for the MIN/MAX fields is greater than the length of the field an unwanted warning on key truncation is issued; 2. If MIN/MAX optimization is applied to a partial index, like INDEX(b(4)) than can lead to returning a wrong result set. mysql-test/r/func_group.result: Added test cases for bug #18206. mysql-test/t/func_group.test: Added test cases for bug #18206. sql/opt_sum.cc: Fixed bug #18206. Suppressed the warning about data truncation when store_val_in_field was used to store keys for the field used in MIN/MAX optimization. Blocked MIN/MAX optimization for partial keys, such as in INDEX(b(4)). sql/sql_select.cc: Fixed bug #18206. Added a parameter for the function store_val_in_field allowing to control setting warnings about data truncation in the function. sql/sql_select.h: Fixed bug #18206. Added a parameter for the function store_val_in_field allowing to control setting warnings about data truncation in the function. --- mysql-test/r/func_group.result | 24 ++++++++++++++++++++++++ mysql-test/t/func_group.test | 19 +++++++++++++++++++ sql/opt_sum.cc | 17 ++++++++++++++--- sql/sql_select.cc | 6 +++--- sql/sql_select.h | 2 +- 5 files changed, 61 insertions(+), 7 deletions(-) diff --git a/mysql-test/r/func_group.result b/mysql-test/r/func_group.result index 67966b999d4..ffa68f279f3 100644 --- a/mysql-test/r/func_group.result +++ b/mysql-test/r/func_group.result @@ -916,3 +916,27 @@ select count(*), min(7), max(7) from t2m, t1i; count(*) min(7) max(7) 0 NULL NULL drop table t1m, t1i, t2m, t2i; +CREATE TABLE t1 (id int PRIMARY KEY, b char(3), INDEX(b)); +INSERT INTO t1 VALUES (1,'xx'), (2,'aa'); +SELECT * FROM t1; +id b +1 xx +2 aa +SELECT MAX(b) FROM t1 WHERE b < 'ppppp'; +MAX(b) +aa +SHOW WARNINGS; +Level Code Message +SELECT MAX(b) FROM t1 WHERE b < 'pp'; +MAX(b) +aa +DROP TABLE t1; +CREATE TABLE t1 (id int PRIMARY KEY, b char(16), INDEX(b(4))); +INSERT INTO t1 VALUES (1, 'xxxxbbbb'), (2, 'xxxxaaaa'); +SELECT MAX(b) FROM t1; +MAX(b) +xxxxbbbb +EXPLAIN SELECT MAX(b) FROM t1; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 2 +DROP TABLE t1; diff --git a/mysql-test/t/func_group.test b/mysql-test/t/func_group.test index dc647dbcfeb..f8a3ed0f25e 100644 --- a/mysql-test/t/func_group.test +++ b/mysql-test/t/func_group.test @@ -598,4 +598,23 @@ select count(*), min(7), max(7) from t2m, t1i; drop table t1m, t1i, t2m, t2i; +# +# Bug #18206: min/max optimization cannot be applied to partial index +# + +CREATE TABLE t1 (id int PRIMARY KEY, b char(3), INDEX(b)); +INSERT INTO t1 VALUES (1,'xx'), (2,'aa'); +SELECT * FROM t1; + +SELECT MAX(b) FROM t1 WHERE b < 'ppppp'; +SHOW WARNINGS; +SELECT MAX(b) FROM t1 WHERE b < 'pp'; +DROP TABLE t1; + +CREATE TABLE t1 (id int PRIMARY KEY, b char(16), INDEX(b(4))); +INSERT INTO t1 VALUES (1, 'xxxxbbbb'), (2, 'xxxxaaaa'); +SELECT MAX(b) FROM t1; +EXPLAIN SELECT MAX(b) FROM t1; +DROP TABLE t1; + # End of 4.1 tests diff --git a/sql/opt_sum.cc b/sql/opt_sum.cc index cfb5b3695a3..82211120c57 100644 --- a/sql/opt_sum.cc +++ b/sql/opt_sum.cc @@ -543,6 +543,10 @@ static bool matching_cond(bool max_fl, TABLE_REF *ref, KEY *keyinfo, break; // Found a part od the key for the field } +#if 0 + if (part->length != (((Item_field*) args[0])->field)->field_length) + return 0; +#endif bool is_field_part= part == field_part; if (!(is_field_part || eq_type)) return 0; @@ -582,7 +586,8 @@ static bool matching_cond(bool max_fl, TABLE_REF *ref, KEY *keyinfo, } else { - store_val_in_field(part->field, args[between && max_fl ? 2 : 1]); + store_val_in_field(part->field, args[between && max_fl ? 2 : 1], + CHECK_FIELD_IGNORE); if (part->null_bit) *key_ptr++= (byte) test(part->field->is_null()); part->field->get_key_image((char*) key_ptr, part->length, @@ -638,6 +643,8 @@ static bool matching_cond(bool max_fl, TABLE_REF *ref, KEY *keyinfo, field BETWEEN const1 AND const2 3. all references to the columns from the same table as column field occur only in conjucts mentioned above. + 4. each of k first components the index is not partial, i.e. is not + defined on a fixed length proper prefix of the field. If such an index exists the function through the ref parameter returns the key value to find max/min for the field using the index, @@ -647,8 +654,8 @@ static bool matching_cond(bool max_fl, TABLE_REF *ref, KEY *keyinfo, of the whole search key) NOTE - This function may set table->key_read to 1, which must be reset after - index is used! (This can only happen when function returns 1) + This function may set table->key_read to 1, which must be reset after + index is used! (This can only happen when function returns 1) RETURN 0 Index can not be used to optimize MIN(field)/MAX(field) @@ -682,6 +689,10 @@ static bool find_key_for_maxmin(bool max_fl, TABLE_REF *ref, if (!(table->file->index_flags(idx, jdx, 0) & HA_READ_ORDER)) return 0; + /* Check whether the index component is partial */ + if (part->length < table->field[part->fieldnr-1]->pack_length()) + break; + if (field->eq(part->field)) { ref->key= idx; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 57fb9738612..5a7e9e52aed 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -3434,7 +3434,7 @@ get_store_key(THD *thd, KEYUSE *keyuse, table_map used_tables, */ bool -store_val_in_field(Field *field,Item *item) +store_val_in_field(Field *field, Item *item, enum_check_fields check_flag) { bool error; THD *thd=current_thd; @@ -3445,7 +3445,7 @@ store_val_in_field(Field *field,Item *item) with select_insert, which make count_cuted_fields= 1 */ enum_check_fields old_count_cuted_fields= thd->count_cuted_fields; - thd->count_cuted_fields= CHECK_FIELD_WARN; + thd->count_cuted_fields= check_flag; error= item->save_in_field(field, 1); thd->count_cuted_fields= old_count_cuted_fields; return error || cuted_fields != thd->cuted_fields; @@ -7097,7 +7097,7 @@ static bool test_if_ref(Item_field *left_item,Item *right_item) field->real_type() != FIELD_TYPE_VAR_STRING && (field->type() != FIELD_TYPE_FLOAT || field->decimals() == 0)) { - return !store_val_in_field(field,right_item); + return !store_val_in_field(field, right_item, CHECK_FIELD_WARN); } } } diff --git a/sql/sql_select.h b/sql/sql_select.h index 94cc8839466..75cd0b4d797 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -335,7 +335,7 @@ extern const char *join_type_str[]; void TEST_join(JOIN *join); /* Extern functions in sql_select.cc */ -bool store_val_in_field(Field *field,Item *val); +bool store_val_in_field(Field *field, Item *val, enum_check_fields check_flag); TABLE *create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List &fields, ORDER *group, bool distinct, bool save_sum_fields, ulong select_options, ha_rows rows_limit, From e815f93bbfffd0ddc2c54df1b32c307b1f19c4b7 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 2 Jun 2006 19:15:32 -0700 Subject: [PATCH 23/65] Cleanup for the fix of bug 18206. sql/opt_sum.cc: Cleanup --- sql/opt_sum.cc | 4 ---- 1 file changed, 4 deletions(-) diff --git a/sql/opt_sum.cc b/sql/opt_sum.cc index 82211120c57..97e271121d3 100644 --- a/sql/opt_sum.cc +++ b/sql/opt_sum.cc @@ -543,10 +543,6 @@ static bool matching_cond(bool max_fl, TABLE_REF *ref, KEY *keyinfo, break; // Found a part od the key for the field } -#if 0 - if (part->length != (((Item_field*) args[0])->field)->field_length) - return 0; -#endif bool is_field_part= part == field_part; if (!(is_field_part || eq_type)) return 0; From b23b410584be6d4766ac76b274c310798c571e66 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 5 Jun 2006 03:50:32 +0400 Subject: [PATCH 24/65] fix test failure in the team tree storage/csv/ha_tina.cc: fix windows csv test failure --- storage/csv/ha_tina.cc | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/storage/csv/ha_tina.cc b/storage/csv/ha_tina.cc index 35d7fc8d4a3..934df0c054e 100644 --- a/storage/csv/ha_tina.cc +++ b/storage/csv/ha_tina.cc @@ -503,28 +503,25 @@ int tina_end(ha_panic_function type) off_t find_eoln_buff(Transparent_file *data_buff, off_t begin, off_t end, int *eoln_len) { + *eoln_len= 0; + for (off_t x= begin; x < end; x++) { /* Unix (includes Mac OS X) */ if (data_buff->get_value(x) == '\n') - { *eoln_len= 1; + else + if (data_buff->get_value(x) == '\r') // Mac or Dos + { + /* old Mac line ending */ + if (x + 1 == end || (data_buff->get_value(x + 1) != '\n')) + *eoln_len= 1; + else // DOS style ending + *eoln_len= 2; + } + + if (*eoln_len) // end of line was found return x; - } - if (data_buff->get_value(x) == '\r') // Mac or Dos - { - /* old Mac line ending */ - if (x + 1 == end || (data_buff->get_value(x + 1) != '\n')) - { - *eoln_len= 1; - return x; - } - else // DOS style ending - { - *eoln_len= 2; - return x + 1; - } - } } return 0; From b092d16c882aceb3c6f931ccdb00f29296cfbf98 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 5 Jun 2006 05:34:34 +0400 Subject: [PATCH 25/65] WL #3153 "Split logs" post-review fixes (after andrei's review) sql/log.cc: post-review fixes: * split log_type to log_state and log_type * rename log_type to log_table_type where appropriate (to avoid confusion) * merge MYSQL_SLOW_LOG and MYSQL_GENERAL_LOG to MYSQL_QUERY_LOG sql/log.h: post-review fixes: * move last_time and reopen_file() to MYSQL_QUERY_LOG from the base class --- sql/log.cc | 140 ++++++++++++++++++++++++----------------------------- sql/log.h | 42 ++++++++-------- 2 files changed, 83 insertions(+), 99 deletions(-) diff --git a/sql/log.cc b/sql/log.cc index d1c33afcda7..0c7b5278f41 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -120,8 +120,8 @@ handlerton binlog_hton = { SYNOPSIS open_log_table() - log_type type of the log table to open: QUERY_LOG_GENERAL - or QUERY_LOG_SLOW + log_table_type type of the log table to open: QUERY_LOG_GENERAL + or QUERY_LOG_SLOW DESCRIPTION @@ -136,14 +136,14 @@ handlerton binlog_hton = { TRUE - error occured */ -bool Log_to_csv_event_handler::open_log_table(uint log_type) +bool Log_to_csv_event_handler::open_log_table(uint log_table_type) { THD *log_thd, *curr= current_thd; TABLE_LIST *table; bool error= FALSE; DBUG_ENTER("open_log_table"); - switch (log_type) { + switch (log_table_type) { case QUERY_LOG_GENERAL: log_thd= general_log_thd; table= &general_log; @@ -254,8 +254,8 @@ Log_to_csv_event_handler::~Log_to_csv_event_handler() SYNOPSIS reopen_log_table() - log_type type of the log table to open: QUERY_LOG_GENERAL - or QUERY_LOG_SLOW + log_table_type type of the log table to open: QUERY_LOG_GENERAL + or QUERY_LOG_SLOW DESCRIPTION @@ -272,12 +272,12 @@ Log_to_csv_event_handler::~Log_to_csv_event_handler() TRUE - open_log_table() returned an error */ -bool Log_to_csv_event_handler::reopen_log_table(uint log_type) +bool Log_to_csv_event_handler::reopen_log_table(uint log_table_type) { /* don't open the log table, if it wasn't enabled during startup */ if (!logger.is_log_tables_initialized) return FALSE; - return open_log_table(log_type); + return open_log_table(log_table_type); } void Log_to_csv_event_handler::cleanup() @@ -613,9 +613,9 @@ void LOGGER::cleanup_end() } -void LOGGER::close_log_table(uint log_type, bool lock_in_use) +void LOGGER::close_log_table(uint log_table_type, bool lock_in_use) { - table_log_handler->close_log_table(log_type, lock_in_use); + table_log_handler->close_log_table(log_table_type, lock_in_use); } @@ -655,9 +655,9 @@ void LOGGER::init_log_tables() } -bool LOGGER::reopen_log_table(uint log_type) +bool LOGGER::reopen_log_table(uint log_table_type) { - return table_log_handler->reopen_log_table(log_type); + return table_log_handler->reopen_log_table(log_table_type); } @@ -992,9 +992,9 @@ int LOGGER::set_handlers(uint error_log_printer, SYNOPSIS close_log_table() - log_type type of the log table to close: QUERY_LOG_GENERAL - or QUERY_LOG_SLOW - lock_in_use Set to TRUE if the caller owns LOCK_open. FALSE otherwise. + log_table_type type of the log table to close: QUERY_LOG_GENERAL + or QUERY_LOG_SLOW + lock_in_use Set to TRUE if the caller owns LOCK_open. FALSE otherwise. DESCRIPTION @@ -1004,7 +1004,7 @@ int LOGGER::set_handlers(uint error_log_printer, */ void Log_to_csv_event_handler:: - close_log_table(uint log_type, bool lock_in_use) + close_log_table(uint log_table_type, bool lock_in_use) { THD *log_thd, *curr= current_thd; TABLE_LIST *table; @@ -1012,7 +1012,7 @@ void Log_to_csv_event_handler:: if (!logger.is_log_tables_initialized) return; /* do nothing */ - switch (log_type) { + switch (log_table_type) { case QUERY_LOG_GENERAL: log_thd= general_log_thd; table= &general_log; @@ -1382,7 +1382,7 @@ static int find_uniq_filename(char *name) void MYSQL_LOG::init(enum_log_type log_type_arg, - enum cache_type io_cache_type_arg) + enum cache_type io_cache_type_arg) { DBUG_ENTER("MYSQL_LOG::init"); log_type= log_type_arg; @@ -1452,8 +1452,7 @@ bool MYSQL_LOG::open(const char *log_name, enum_log_type log_type_arg, ((log_type == LOG_BIN) ? MY_WAIT_IF_FULL : 0)))) goto err; - switch (log_type) { - case LOG_NORMAL: + if (log_type == LOG_NORMAL) { char *end; int len=my_snprintf(buff, sizeof(buff), "%s, Version: %s. " @@ -1474,14 +1473,9 @@ bool MYSQL_LOG::open(const char *log_name, enum_log_type log_type_arg, if (my_b_write(&log_file, (byte*) buff, (uint) (end-buff)) || flush_io_cache(&log_file)) goto err; - break; - } - case LOG_CLOSED: // Impossible - case LOG_TO_BE_OPENED: - DBUG_ASSERT(1); - break; } + log_state= LOG_OPENED; DBUG_RETURN(0); err: @@ -1493,13 +1487,13 @@ shutdown the MySQL server and restart it.", name, errno); my_close(file, MYF(0)); end_io_cache(&log_file); safeFree(name); - log_type= LOG_CLOSED; + log_state= LOG_CLOSED; DBUG_RETURN(1); } MYSQL_LOG::MYSQL_LOG() - : name(0), log_type(LOG_CLOSED), write_error(FALSE), - inited(FALSE), last_time(0) + : name(0), log_type(LOG_UNKNOWN), log_state(LOG_CLOSED), write_error(FALSE), + inited(FALSE) { /* We don't want to initialize LOCK_Log here as such initialization depends on @@ -1535,7 +1529,7 @@ void MYSQL_LOG::close(uint exiting) { // One can't set log_type here! DBUG_ENTER("MYSQL_LOG::close"); DBUG_PRINT("enter",("exiting: %d", (int) exiting)); - if (log_type != LOG_CLOSED && log_type != LOG_TO_BE_OPENED) + if (log_state == LOG_OPENED) { end_io_cache(&log_file); @@ -1552,7 +1546,7 @@ void MYSQL_LOG::close(uint exiting) } } - log_type= (exiting & LOG_CLOSE_TO_BE_OPENED) ? LOG_TO_BE_OPENED : LOG_CLOSED; + log_state= (exiting & LOG_CLOSE_TO_BE_OPENED) ? LOG_TO_BE_OPENED : LOG_CLOSED; safeFree(name); DBUG_VOID_RETURN; } @@ -1572,6 +1566,24 @@ void MYSQL_LOG::cleanup() } +int MYSQL_LOG::generate_new_name(char *new_name, const char *log_name) +{ + fn_format(new_name, log_name, mysql_data_home, "", 4); + if (log_type == LOG_BIN) + { + if (!fn_ext(log_name)[0]) + { + if (find_uniq_filename(new_name)) + { + sql_print_error(ER(ER_NO_UNIQUE_LOGFILE), log_name); + return 1; + } + } + } + return 0; +} + + /* Reopen the log file @@ -1584,9 +1596,8 @@ void MYSQL_LOG::cleanup() */ -void MYSQL_LOG::reopen_file() +void MYSQL_QUERY_LOG::reopen_file() { - enum_log_type save_log_type; char *save_name; DBUG_ENTER("MYSQL_LOG::reopen_file"); @@ -1599,15 +1610,14 @@ void MYSQL_LOG::reopen_file() pthread_mutex_lock(&LOCK_log); save_name= name; - save_log_type= log_type; name= 0; // Don't free name close(LOG_CLOSE_TO_BE_OPENED); /* - Note that at this point, log_type != LOG_CLOSED (important for is_open()). + Note that at this point, log_state != LOG_CLOSED (important for is_open()). */ - open(save_name, save_log_type, 0, io_cache_type); + open(save_name, log_type, 0, io_cache_type); my_free(save_name, MYF(0)); pthread_mutex_unlock(&LOCK_log); @@ -1616,24 +1626,6 @@ void MYSQL_LOG::reopen_file() } -int MYSQL_LOG::generate_new_name(char *new_name, const char *log_name) -{ - fn_format(new_name,log_name,mysql_data_home,"",4); - if (log_type != LOG_NORMAL) - { - if (!fn_ext(log_name)[0]) - { - if (find_uniq_filename(new_name)) - { - sql_print_error(ER(ER_NO_UNIQUE_LOGFILE), log_name); - return 1; - } - } - } - return 0; -} - - /* Write a command to traditional general log file @@ -1659,10 +1651,10 @@ int MYSQL_LOG::generate_new_name(char *new_name, const char *log_name) TRUE - error occured */ -bool MYSQL_GENERAL_LOG::write(time_t event_time, const char *user_host, - uint user_host_len, int thread_id, - const char *command_type, uint command_type_len, - const char *sql_text, uint sql_text_len) +bool MYSQL_QUERY_LOG::write(time_t event_time, const char *user_host, + uint user_host_len, int thread_id, + const char *command_type, uint command_type_len, + const char *sql_text, uint sql_text_len) { char buff[32]; uint length= 0; @@ -1755,14 +1747,14 @@ err: TRUE - error occured */ -bool MYSQL_SLOW_LOG::write(THD *thd, time_t current_time, - time_t query_start_arg, const char *user_host, - uint user_host_len, longlong query_time, - longlong lock_time, bool is_command, - const char *sql_text, uint sql_text_len) +bool MYSQL_QUERY_LOG::write(THD *thd, time_t current_time, + time_t query_start_arg, const char *user_host, + uint user_host_len, longlong query_time, + longlong lock_time, bool is_command, + const char *sql_text, uint sql_text_len) { bool error= 0; - DBUG_ENTER("MYSQL_SLOW_LOG::write"); + DBUG_ENTER("MYSQL_QUERY_LOG::write"); if (!is_open()) DBUG_RETURN(0); @@ -2015,7 +2007,6 @@ bool MYSQL_BIN_LOG::open(const char *log_name, DBUG_ENTER("MYSQL_BIN_LOG::open"); DBUG_PRINT("enter",("log_type: %d",(int) log_type_arg)); - last_time= 0; write_error=0; /* open the main log file */ @@ -2119,6 +2110,8 @@ bool MYSQL_BIN_LOG::open(const char *log_name, goto err; } } + log_state= LOG_OPENED; + DBUG_RETURN(0); err: @@ -2131,7 +2124,7 @@ shutdown the MySQL server and restart it.", name, errno); end_io_cache(&log_file); end_io_cache(&index_file); safeFree(name); - log_type= LOG_CLOSED; + log_state= LOG_CLOSED; DBUG_RETURN(1); } @@ -2350,7 +2343,6 @@ bool MYSQL_BIN_LOG::reset_logs(THD* thd) LOG_INFO linfo; bool error=0; const char* save_name; - enum_log_type save_log_type; DBUG_ENTER("reset_logs"); ha_reset_logs(thd); @@ -2372,7 +2364,6 @@ bool MYSQL_BIN_LOG::reset_logs(THD* thd) /* Save variables so that we can reopen the log */ save_name=name; name=0; // Protect against free - save_log_type=log_type; close(LOG_CLOSE_TO_BE_OPENED); /* First delete all old log files */ @@ -2396,8 +2387,7 @@ bool MYSQL_BIN_LOG::reset_logs(THD* thd) if (!thd->slave_thread) need_start_event=1; if (!open_index_file(index_file_name, 0)) - open(save_name, save_log_type, 0, - io_cache_type, no_auto_events, max_size, 0); + open(save_name, log_type, 0, io_cache_type, no_auto_events, max_size, 0); my_free((gptr) save_name, MYF(0)); err: @@ -2753,7 +2743,6 @@ void MYSQL_BIN_LOG::new_file_without_locking() void MYSQL_BIN_LOG::new_file_impl(bool need_lock) { char new_name[FN_REFLEN], *new_name_ptr, *old_name; - enum_log_type save_log_type; DBUG_ENTER("MYSQL_BIN_LOG::new_file_impl"); if (!is_open()) @@ -2821,12 +2810,11 @@ void MYSQL_BIN_LOG::new_file_impl(bool need_lock) signal_update(); } old_name=name; - save_log_type=log_type; name=0; // Don't free name close(LOG_CLOSE_TO_BE_OPENED); /* - Note that at this point, log_type != LOG_CLOSED (important for is_open()). + Note that at this point, log_state != LOG_CLOSED (important for is_open()). */ /* @@ -2838,7 +2826,7 @@ void MYSQL_BIN_LOG::new_file_impl(bool need_lock) trigger temp tables deletion on slaves. */ - open(old_name, save_log_type, new_name_ptr, + open(old_name, log_type, new_name_ptr, io_cache_type, no_auto_events, max_size, 1); my_free(old_name,MYF(0)); @@ -3543,7 +3531,7 @@ void MYSQL_BIN_LOG::close(uint exiting) { // One can't set log_type here! DBUG_ENTER("MYSQL_BIN_LOG::close"); DBUG_PRINT("enter",("exiting: %d", (int) exiting)); - if (log_type != LOG_CLOSED && log_type != LOG_TO_BE_OPENED) + if (log_state == LOG_OPENED) { #ifdef HAVE_REPLICATION if (log_type == LOG_BIN && !no_auto_events && @@ -3582,7 +3570,7 @@ void MYSQL_BIN_LOG::close(uint exiting) sql_print_error(ER(ER_ERROR_ON_WRITE), index_file_name, errno); } } - log_type= (exiting & LOG_CLOSE_TO_BE_OPENED) ? LOG_TO_BE_OPENED : LOG_CLOSED; + log_state= (exiting & LOG_CLOSE_TO_BE_OPENED) ? LOG_TO_BE_OPENED : LOG_CLOSED; safeFree(name); DBUG_VOID_RETURN; } diff --git a/sql/log.h b/sql/log.h index 589932dcac0..8a41cd9053d 100644 --- a/sql/log.h +++ b/sql/log.h @@ -147,7 +147,8 @@ typedef struct st_log_info class Log_event; class Rows_log_event; -enum enum_log_type { LOG_CLOSED, LOG_TO_BE_OPENED, LOG_NORMAL, LOG_BIN}; +enum enum_log_type { LOG_UNKNOWN, LOG_NORMAL, LOG_BIN }; +enum enum_log_state { LOG_OPENED, LOG_CLOSED, LOG_TO_BE_OPENED }; /* TODO use mmap instead of IO_CACHE for binlog @@ -160,7 +161,6 @@ public: MYSQL_LOG(); void init_pthread_objects(); void cleanup(); - void reopen_file(); bool open(const char *log_name, enum_log_type log_type, const char *new_name, @@ -168,7 +168,7 @@ public: void init(enum_log_type log_type_arg, enum cache_type io_cache_type_arg); void close(uint exiting); - inline bool is_open() { return log_type != LOG_CLOSED; } + inline bool is_open() { return log_state != LOG_CLOSED; } const char *generate_name(const char *log_name, const char *suffix, bool strip_ext, char *buff); int generate_new_name(char *new_name, const char *log_name); @@ -180,33 +180,21 @@ public: char time_buff[20], db[NAME_LEN + 1]; bool write_error, inited; IO_CACHE log_file; - volatile enum_log_type log_type; + enum_log_type log_type; + volatile enum_log_state log_state; enum cache_type io_cache_type; - time_t last_time; - friend class Log_event; }; -class MYSQL_GENERAL_LOG: public MYSQL_LOG +class MYSQL_QUERY_LOG: public MYSQL_LOG { public: - MYSQL_GENERAL_LOG() {} /* get rid of gcc warning */ + MYSQL_QUERY_LOG() : last_time(0) {} + void reopen_file(); bool write(time_t event_time, const char *user_host, uint user_host_len, int thread_id, const char *command_type, uint command_type_len, const char *sql_text, uint sql_text_len); - bool open_query_log(const char *log_name) - { - char buf[FN_REFLEN]; - return open(generate_name(log_name, ".log", 0, buf), LOG_NORMAL, 0, - WRITE_CACHE); - } -}; - -class MYSQL_SLOW_LOG: public MYSQL_LOG -{ -public: - MYSQL_SLOW_LOG() {} /* get rid of gcc warning */ bool write(THD *thd, time_t current_time, time_t query_start_arg, const char *user_host, uint user_host_len, longlong query_time, longlong lock_time, bool is_command, @@ -217,6 +205,14 @@ public: return open(generate_name(log_name, "-slow.log", 0, buf), LOG_NORMAL, 0, WRITE_CACHE); } + bool open_query_log(const char *log_name) + { + char buf[FN_REFLEN]; + return open(generate_name(log_name, ".log", 0, buf), LOG_NORMAL, 0, + WRITE_CACHE); + } +private: + time_t last_time; }; class MYSQL_BIN_LOG: public TC_LOG, private MYSQL_LOG @@ -269,6 +265,7 @@ class MYSQL_BIN_LOG: public TC_LOG, private MYSQL_LOG public: MYSQL_LOG::generate_name; + MYSQL_LOG::is_open; /* These describe the log's format. This is used only for relay logs. _for_exec is used by the SQL thread, _for_queue by the I/O thread. It's @@ -371,7 +368,6 @@ public: int find_next_log(LOG_INFO* linfo, bool need_mutex); int get_current_log(LOG_INFO* linfo); uint next_file_id(); - inline bool is_open() { return log_type != LOG_CLOSED; } inline char* get_index_fname() { return index_file_name;} inline char* get_log_fname() { return log_file_name; } inline char* get_name() { return name; } @@ -448,8 +444,8 @@ public: class Log_to_file_event_handler: public Log_event_handler { - MYSQL_GENERAL_LOG mysql_log; - MYSQL_SLOW_LOG mysql_slow_log; + MYSQL_QUERY_LOG mysql_log; + MYSQL_QUERY_LOG mysql_slow_log; bool is_initialized; public: Log_to_file_event_handler(): is_initialized(FALSE) From 6e80e9a4f93180f680b5d908e47221af67d7a3d6 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 7 Jun 2006 01:10:23 +0400 Subject: [PATCH 26/65] Fixed bug#15962: CONCAT() in UNION may lead to a data trucation. To calculate its max_length the CONCAT() function is simply sums max_lengths of its arguments but when the collation of an argument differs from the collation of the CONCAT() max_length will be wrong. This may lead to a data truncation when a tmp table is used, in UNIONS for example. The Item_func_concat::fix_length_and_dec() function now recalculates the max_length of an argument when the mbmaxlen of the argument differs from the mbmaxlen of the CONCAT(). mysql-test/t/func_concat.test: Added test case for bug#15962:CONCAT() in UNION may lead to a data trucation. mysql-test/r/func_concat.result: Added test case for bug#15962:CONCAT() in UNION may lead to a data trucation. sql/item_strfunc.cc: Fixed bug#15962: CONCAT() in UNION may lead to a data trucation. The Item_func_concat::fix_length_and_dec() function now recalculates the max_length of an argument when the mbmaxlen of the argument differs from the mbmaxlen of the CONCAT(). --- mysql-test/r/func_concat.result | 7 +++++++ mysql-test/t/func_concat.test | 8 ++++++++ sql/item_strfunc.cc | 9 ++++++++- 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/func_concat.result b/mysql-test/r/func_concat.result index 0bd53b32dd7..712f0c0495e 100644 --- a/mysql-test/r/func_concat.result +++ b/mysql-test/r/func_concat.result @@ -68,3 +68,10 @@ select 'a' union select concat('a', -0.0000); a a a0.0000 +create table t1(f1 varchar(6)) charset=utf8; +insert into t1 values ("123456"); +select concat(f1, 2) a from t1 union select 'x' a from t1; +a +1234562 +x +drop table t1; diff --git a/mysql-test/t/func_concat.test b/mysql-test/t/func_concat.test index 37fc0e105b8..42201961222 100644 --- a/mysql-test/t/func_concat.test +++ b/mysql-test/t/func_concat.test @@ -53,3 +53,11 @@ select 'a' union select concat('a', -0.0); select 'a' union select concat('a', -0.0000); # End of 4.1 tests + +# +# Bug#15962: CONCAT() in UNION may lead to a data trucation. +# +create table t1(f1 varchar(6)) charset=utf8; +insert into t1 values ("123456"); +select concat(f1, 2) a from t1 union select 'x' a from t1; +drop table t1; diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index ce9897afeed..d8082440f1a 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -389,7 +389,14 @@ void Item_func_concat::fix_length_and_dec() return; for (uint i=0 ; i < arg_count ; i++) - max_result_length+= args[i]->max_length; + { + if (args[i]->collation.collation->mbmaxlen != collation.collation->mbmaxlen) + max_result_length+= (args[i]->max_length / + args[i]->collation.collation->mbmaxlen) * + collation.collation->mbmaxlen; + else + max_result_length+= args[i]->max_length; + } if (max_result_length >= MAX_BLOB_WIDTH) { From d1b37c77ef202f4d9b276b36000cd4ac4c19626a Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 7 Jun 2006 16:20:57 +1000 Subject: [PATCH 27/65] BUG#18966 CHange in stop/shutdown behaviour Fixes based on review by Tomas ndb/src/mgmapi/mgmapi.cpp: Return immediately if ndb_mgm_get_version fails. correctly use new protocol for versions > 5.1 ndb/src/mgmsrv/Services.cpp: Only have 1 version of 'stop all' with reply being dependent on if the optional stop parameter is supplied. ndb/src/mgmsrv/Services.hpp: Only 1 version of stopAll --- ndb/src/mgmapi/mgmapi.cpp | 30 +++++++++++++++++++++--------- ndb/src/mgmsrv/Services.cpp | 36 ++++++++++++++---------------------- ndb/src/mgmsrv/Services.hpp | 4 +--- 3 files changed, 36 insertions(+), 34 deletions(-) diff --git a/ndb/src/mgmapi/mgmapi.cpp b/ndb/src/mgmapi/mgmapi.cpp index 3109cea7782..d514397f8de 100644 --- a/ndb/src/mgmapi/mgmapi.cpp +++ b/ndb/src/mgmapi/mgmapi.cpp @@ -870,18 +870,24 @@ ndb_mgm_stop3(NdbMgmHandle handle, int no_of_nodes, const int * node_list, if(handle->mgmd_version_build==-1) { char verstr[50]; - ndb_mgm_get_version(handle, + if(!ndb_mgm_get_version(handle, &(handle->mgmd_version_major), &(handle->mgmd_version_minor), &(handle->mgmd_version_build), sizeof(verstr), - verstr); + verstr)) + { + return -1; + } } - int use_v2= (handle->mgmd_version_major==5) + int use_v2= ((handle->mgmd_version_major==5) && ( (handle->mgmd_version_minor==0 && handle->mgmd_version_build>=21) ||(handle->mgmd_version_minor==1 && handle->mgmd_version_build>=12) - ); + ||(handle->mgmd_version_minor>1) + ) + ) + || (handle->mgmd_version_major>5); if(no_of_nodes < -1){ SET_ERROR(handle, NDB_MGM_ILLEGAL_NUMBER_OF_NODES, @@ -900,7 +906,7 @@ ndb_mgm_stop3(NdbMgmHandle handle, int no_of_nodes, const int * node_list, args.put("stop", (no_of_nodes==-1)?"mgm,db":"db"); const Properties *reply; if(use_v2) - reply = ndb_mgm_call(handle, stop_reply_v2, "stop all v2", &args); + reply = ndb_mgm_call(handle, stop_reply_v2, "stop all", &args); else reply = ndb_mgm_call(handle, stop_reply_v1, "stop all", &args); CHECK_REPLY(reply, -1); @@ -1013,18 +1019,24 @@ ndb_mgm_restart3(NdbMgmHandle handle, int no_of_nodes, const int * node_list, if(handle->mgmd_version_build==-1) { char verstr[50]; - ndb_mgm_get_version(handle, + if(!ndb_mgm_get_version(handle, &(handle->mgmd_version_major), &(handle->mgmd_version_minor), &(handle->mgmd_version_build), sizeof(verstr), - verstr); + verstr)) + { + return -1; + } } - int use_v2= (handle->mgmd_version_major==5) + int use_v2= ((handle->mgmd_version_major==5) && ( (handle->mgmd_version_minor==0 && handle->mgmd_version_build>=21) ||(handle->mgmd_version_minor==1 && handle->mgmd_version_build>=12) - ); + ||(handle->mgmd_version_minor>1) + ) + ) + || (handle->mgmd_version_major>5); if(no_of_nodes < 0){ SET_ERROR(handle, NDB_MGM_RESTART_FAILED, diff --git a/ndb/src/mgmsrv/Services.cpp b/ndb/src/mgmsrv/Services.cpp index 01f11d600df..cdadec5cea1 100644 --- a/ndb/src/mgmsrv/Services.cpp +++ b/ndb/src/mgmsrv/Services.cpp @@ -200,12 +200,9 @@ ParserRow commands[] = { MGM_ARG("node", String, Mandatory, "Node"), MGM_ARG("abort", Int, Mandatory, "Node"), - MGM_CMD("stop all", &MgmApiSession::stopAll_v1, ""), + MGM_CMD("stop all", &MgmApiSession::stopAll, ""), MGM_ARG("abort", Int, Mandatory, "Node"), - - MGM_CMD("stop all v2", &MgmApiSession::stopAll_v2, ""), - MGM_ARG("abort", Int, Mandatory, "Node"), - MGM_ARG("stop", String, Mandatory, "MGM/DB or both"), + MGM_ARG("stop", String, Optional, "MGM/DB or both"), MGM_CMD("enter single user", &MgmApiSession::enterSingleUser, ""), MGM_ARG("nodeId", Int, Mandatory, "Node"), @@ -1071,31 +1068,26 @@ MgmApiSession::stop(Properties const &args, int version) { m_output->println(""); } - void -MgmApiSession::stopAll_v1(Parser::Context &, - Properties const &args) { - stopAll(args,"db",1); -} - -void -MgmApiSession::stopAll_v2(Parser::Context &, - Properties const &args) { - BaseString tostop; - args.get("stop", tostop); - stopAll(args, tostop.c_str(), 2); -} - -void -MgmApiSession::stopAll(Properties const &args, const char* tostop, int ver) { +MgmApiSession::stopAll(Parser::Context &, + Properties const &args) { int stopped[2] = {0,0}; Uint32 abort; args.get("abort", &abort); + BaseString stop; + const char* tostop= "db"; + int ver=1; + if (args.get("stop", stop)) + { + tostop= stop.c_str(); + ver= 2; + } + int result= 0; if(strstr(tostop,"db")) result= m_mgmsrv.shutdownDB(&stopped[0], abort != 0); - if(strstr(tostop,"mgm")) + if(!result && strstr(tostop,"mgm")) result= m_mgmsrv.shutdownMGM(&stopped[1], abort!=0, &m_stopSelf); m_output->println("stop reply"); diff --git a/ndb/src/mgmsrv/Services.hpp b/ndb/src/mgmsrv/Services.hpp index 0d4331b128b..6d293ced8c8 100644 --- a/ndb/src/mgmsrv/Services.hpp +++ b/ndb/src/mgmsrv/Services.hpp @@ -80,9 +80,7 @@ public: void stop_v1(Parser_t::Context &ctx, const class Properties &args); void stop_v2(Parser_t::Context &ctx, const class Properties &args); void stop(const class Properties &args, int version); - void stopAll_v1(Parser_t::Context &ctx, const class Properties &args); - void stopAll_v2(Parser_t::Context &ctx, const class Properties &args); - void stopAll(Properties const &args, const char* tostop, int ver); + void stopAll(Parser_t::Context &ctx, const class Properties &args); void start(Parser_t::Context &ctx, const class Properties &args); void startAll(Parser_t::Context &ctx, const class Properties &args); void bye(Parser_t::Context &ctx, const class Properties &args); From febe7eb64a7472823294ae7295194af2be1b3497 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 7 Jun 2006 14:01:10 +0500 Subject: [PATCH 28/65] Fix for bug #6880: LAST_INSERT_ID() within a statement mysql-test/r/auto_increment.result: Fix for bug #6880: LAST_INSERT_ID() within a statement - test result mysql-test/r/rpl_log.result: Fix for bug #6880: LAST_INSERT_ID() within a statement - test result mysql-test/t/auto_increment.test: Fix for bug #6880: LAST_INSERT_ID() within a statement - test case mysql-test/t/rpl_log.test: Fix for bug #6880: LAST_INSERT_ID() within a statement - test case sql/item_func.cc: Fix for bug #6880: LAST_INSERT_ID() within a statement - return the first thd->last_insert_id set (within a query) --- mysql-test/r/auto_increment.result | 22 ++++++++++++++++++++++ mysql-test/r/rpl_log.result | 17 +++++++++++++++++ mysql-test/t/auto_increment.test | 17 +++++++++++++++++ mysql-test/t/rpl_log.test | 14 ++++++++++++++ sql/item_func.cc | 7 ++++--- 5 files changed, 74 insertions(+), 3 deletions(-) diff --git a/mysql-test/r/auto_increment.result b/mysql-test/r/auto_increment.result index 426f20212c3..c1b7b753bd4 100644 --- a/mysql-test/r/auto_increment.result +++ b/mysql-test/r/auto_increment.result @@ -378,4 +378,26 @@ t1 CREATE TABLE `t1` ( KEY `t1_name` (`t1_name`) ) ENGINE=MyISAM AUTO_INCREMENT=1003 DEFAULT CHARSET=latin1 DROP TABLE `t1`; +create table t1(a int not null auto_increment primary key); +create table t2(a int not null auto_increment primary key, t1a int); +insert into t1 values(NULL); +insert into t2 values (NULL, LAST_INSERT_ID()), (NULL, LAST_INSERT_ID()); +insert into t1 values (NULL); +insert into t2 values (NULL, LAST_INSERT_ID()), (NULL, LAST_INSERT_ID()), +(NULL, LAST_INSERT_ID()); +insert into t1 values (NULL); +insert into t2 values (NULL, LAST_INSERT_ID()), (NULL, LAST_INSERT_ID()), +(NULL, LAST_INSERT_ID()), (NULL, LAST_INSERT_ID()); +select * from t2; +a t1a +1 1 +2 1 +3 2 +4 2 +5 2 +6 3 +7 3 +8 3 +9 3 +drop table t1, t2; End of 4.1 tests diff --git a/mysql-test/r/rpl_log.result b/mysql-test/r/rpl_log.result index 9fcab2a7cbe..3833800bfeb 100644 --- a/mysql-test/r/rpl_log.result +++ b/mysql-test/r/rpl_log.result @@ -99,3 +99,20 @@ Slave_IO_State Master_Host Master_User Master_Port Connect_Retry Master_Log_File # 127.0.0.1 root MASTER_PORT 1 master-bin.000002 276 slave-relay-bin.000003 214 master-bin.000002 Yes Yes 0 0 276 214 None 0 No # show binlog events in 'slave-bin.000005' from 4; ERROR HY000: Error when executing command SHOW BINLOG EVENTS: Could not find target log +create table t1(a int auto_increment primary key, b int); +insert into t1 values (NULL, 1); +reset master; +set insert_id=5; +insert into t1 values (NULL, last_insert_id()), (NULL, last_insert_id()); +show binlog events; +Log_name Pos Event_type Server_id Orig_log_pos Info +slave-bin.000001 4 Start 2 4 Server ver: VERSION, Binlog ver: 3 +slave-bin.000001 79 Intvar 2 79 LAST_INSERT_ID=1 +slave-bin.000001 107 Intvar 2 107 INSERT_ID=5 +slave-bin.000001 135 Query 2 135 use `test`; insert into t1 values (NULL, last_insert_id()), (NULL, last_insert_id()) +select * from t1; +a b +1 1 +5 1 +6 1 +drop table t1; diff --git a/mysql-test/t/auto_increment.test b/mysql-test/t/auto_increment.test index eed2ea44d05..37b92b32bfb 100644 --- a/mysql-test/t/auto_increment.test +++ b/mysql-test/t/auto_increment.test @@ -238,4 +238,21 @@ SHOW CREATE TABLE `t1`; DROP TABLE `t1`; +# +# Bug #6880: LAST_INSERT_ID() within a statement +# + +create table t1(a int not null auto_increment primary key); +create table t2(a int not null auto_increment primary key, t1a int); +insert into t1 values(NULL); +insert into t2 values (NULL, LAST_INSERT_ID()), (NULL, LAST_INSERT_ID()); +insert into t1 values (NULL); +insert into t2 values (NULL, LAST_INSERT_ID()), (NULL, LAST_INSERT_ID()), +(NULL, LAST_INSERT_ID()); +insert into t1 values (NULL); +insert into t2 values (NULL, LAST_INSERT_ID()), (NULL, LAST_INSERT_ID()), +(NULL, LAST_INSERT_ID()), (NULL, LAST_INSERT_ID()); +select * from t2; +drop table t1, t2; + --echo End of 4.1 tests diff --git a/mysql-test/t/rpl_log.test b/mysql-test/t/rpl_log.test index 7a67ab2311a..08aa3b28850 100644 --- a/mysql-test/t/rpl_log.test +++ b/mysql-test/t/rpl_log.test @@ -108,4 +108,18 @@ show slave status; --error 1220 show binlog events in 'slave-bin.000005' from 4; +# +# Bug #6880: LAST_INSERT_ID() within a statement +# + +create table t1(a int auto_increment primary key, b int); +insert into t1 values (NULL, 1); +reset master; +set insert_id=5; +insert into t1 values (NULL, last_insert_id()), (NULL, last_insert_id()); +--replace_result $VERSION VERSION +show binlog events; +select * from t1; +drop table t1; + # End of 4.1 tests diff --git a/sql/item_func.cc b/sql/item_func.cc index 66300d129d4..91ccef6511f 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -2233,15 +2233,16 @@ longlong Item_func_release_lock::val_int() longlong Item_func_last_insert_id::val_int() { DBUG_ASSERT(fixed == 1); + THD* thd= current_thd; if (arg_count) { longlong value=args[0]->val_int(); - current_thd->insert_id(value); + thd->insert_id(value); null_value=args[0]->null_value; } else - current_thd->lex->uncacheable(UNCACHEABLE_SIDEEFFECT); - return current_thd->insert_id(); + thd->lex->uncacheable(UNCACHEABLE_SIDEEFFECT); + return thd->last_insert_id_used ? thd->current_insert_id : thd->insert_id(); } /* This function is just used to test speed of different functions */ From 683ebcd101f4f33d4fd04bd44b0344353fe282a1 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 7 Jun 2006 16:17:56 +0400 Subject: [PATCH 29/65] Fixed bug#19789: REPLACE was allowed for a VIEW with CHECK OPTION enabled. The st_lex::which_check_option_applicable() function controls for which statements WITH CHECK OPTION clause should be taken into account. REPLACE and REPLACE_SELECT wasn't in the list which results in allowing REPLACE to insert wrong rows in a such view. The st_lex::which_check_option_applicable() now includes REPLACE and REPLACE_SELECT in the list of statements for which WITH CHECK OPTION clause is applicable. mysql-test/t/replace.test: Added test case for bug#19789: REPLACE was allowed for a VIEW with CHECK OPTION enabled. mysql-test/r/replace.result: Added test case for bug#19789: REPLACE was allowed for a VIEW with CHECK OPTION enabled. sql/sql_lex.h: Fixed bug#19789: REPLACE was allowed for a VIEW with CHECK OPTION enabled. The st_lex::which_check_option_applicable() now includes REPLACE and REPLACE_SELECT in the list of statements for which WITH CHECK OPTION clause is applicable. --- mysql-test/r/replace.result | 6 ++++++ mysql-test/t/replace.test | 10 ++++++++++ sql/sql_lex.h | 2 ++ 3 files changed, 18 insertions(+) diff --git a/mysql-test/r/replace.result b/mysql-test/r/replace.result index a7d59fcfa62..5a5e4571ba9 100644 --- a/mysql-test/r/replace.result +++ b/mysql-test/r/replace.result @@ -24,3 +24,9 @@ a b 63 default_value 127 last drop table t1; +CREATE TABLE t1 (f1 INT); +CREATE VIEW v1 AS SELECT f1 FROM t1 WHERE f1 = 0 WITH CHECK OPTION; +REPLACE INTO v1 (f1) VALUES (1); +ERROR HY000: CHECK OPTION failed 'test.v1' +DROP TABLE t1; +DROP VIEW v1; diff --git a/mysql-test/t/replace.test b/mysql-test/t/replace.test index 10703eaafb8..269854fb180 100644 --- a/mysql-test/t/replace.test +++ b/mysql-test/t/replace.test @@ -35,3 +35,13 @@ select * from t1; drop table t1; # End of 4.1 tests + +# +# Bug#19789: REPLACE was allowed for a VIEW with CHECK OPTION enabled. +# +CREATE TABLE t1 (f1 INT); +CREATE VIEW v1 AS SELECT f1 FROM t1 WHERE f1 = 0 WITH CHECK OPTION; +--error 1369 +REPLACE INTO v1 (f1) VALUES (1); +DROP TABLE t1; +DROP VIEW v1; diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 34e7ee969b6..306a726cab9 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -1000,6 +1000,8 @@ typedef struct st_lex case SQLCOM_UPDATE_MULTI: case SQLCOM_INSERT: case SQLCOM_INSERT_SELECT: + case SQLCOM_REPLACE: + case SQLCOM_REPLACE_SELECT: case SQLCOM_LOAD: return TRUE; default: From a7566db788a2ce71751bce8fecf98507b8e37b8b Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 8 Jun 2006 13:34:03 +0300 Subject: [PATCH 30/65] Bug #15355: Common natural join column not resolved in prepared statement nested query Problem: There was a wrong context assigned to the columns that were added in insert_fields() when expanding a '*'. When this is done in a prepared statement it causes fix_fields() to fail to find the table that these columns reference. Actually the right context is set in setup_natural_join_row_types() called at the end of setup_tables(). However when executed in a context of a prepared statement setup_tables() resets the context, but setup_natural_join_row_types() was not setting it to the correct value assuming it has already done so. Solution: The top-most, left-most NATURAL/USING join must be set as a first_name_resolution_table in context even when operating on prepared statements. mysql-test/r/join.result: testsuite for the bug mysql-test/t/join.test: testsuite for the bug sql/sql_base.cc: The context must be set even when executed as prepared statement. --- mysql-test/r/join.result | 20 ++++++++++++++++++++ mysql-test/t/join.test | 25 +++++++++++++++++++++++++ sql/sql_base.cc | 22 +++++++++++----------- 3 files changed, 56 insertions(+), 11 deletions(-) diff --git a/mysql-test/r/join.result b/mysql-test/r/join.result index 86288caf398..48b7730481f 100644 --- a/mysql-test/r/join.result +++ b/mysql-test/r/join.result @@ -744,3 +744,23 @@ select a2 from ((t1 natural join t2) join t3 on b=c1) natural join t4; a2 1 drop table t1,t2,t3,t4; +create table t1 (c int, b int); +create table t2 (a int, b int); +create table t3 (b int, c int); +create table t4 (y int, c int); +create table t5 (y int, z int); +insert into t1 values (3,2); +insert into t2 values (1,2); +insert into t3 values (2,3); +insert into t4 values (1,3); +insert into t5 values (1,4); +prepare stmt1 from "select * from ((t3 natural join (t1 natural join t2)) +natural join t4) natural join t5"; +execute stmt1; +y c b a z +1 3 2 1 4 +select * from ((t3 natural join (t1 natural join t2)) natural join t4) +natural join t5; +y c b a z +1 3 2 1 4 +drop table t1, t2, t3, t4, t5; diff --git a/mysql-test/t/join.test b/mysql-test/t/join.test index f6a57d5e230..27558a31d68 100644 --- a/mysql-test/t/join.test +++ b/mysql-test/t/join.test @@ -563,4 +563,29 @@ select a2 from ((t1 natural join t2) join t3 on b=c1) natural join t4; drop table t1,t2,t3,t4; +# +# BUG#15355: Common natural join column not resolved in prepared statement nested query +# +create table t1 (c int, b int); +create table t2 (a int, b int); +create table t3 (b int, c int); +create table t4 (y int, c int); +create table t5 (y int, z int); + +insert into t1 values (3,2); +insert into t2 values (1,2); +insert into t3 values (2,3); +insert into t4 values (1,3); +insert into t5 values (1,4); + +-- this fails +prepare stmt1 from "select * from ((t3 natural join (t1 natural join t2)) +natural join t4) natural join t5"; +execute stmt1; + +-- this works +select * from ((t3 natural join (t1 natural join t2)) natural join t4) + natural join t5; +drop table t1, t2, t3, t4, t5; + # End of tests for WL#2486 - natural/using join diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 75b69c91846..7fe626c8f2d 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -4184,10 +4184,6 @@ static bool setup_natural_join_row_types(THD *thd, if (from_clause->elements == 0) return FALSE; /* We come here in the case of UNIONs. */ - /* For stored procedures do not redo work if already done. */ - if (!context->select_lex->first_execution) - return FALSE; - List_iterator_fast table_ref_it(*from_clause); TABLE_LIST *table_ref; /* Current table reference. */ /* Table reference to the left of the current. */ @@ -4200,14 +4196,18 @@ static bool setup_natural_join_row_types(THD *thd, { table_ref= left_neighbor; left_neighbor= table_ref_it++; - if (store_top_level_join_columns(thd, table_ref, - left_neighbor, right_neighbor)) - return TRUE; - if (left_neighbor) + /* For stored procedures do not redo work if already done. */ + if (context->select_lex->first_execution) { - TABLE_LIST *first_leaf_on_the_right; - first_leaf_on_the_right= table_ref->first_leaf_for_name_resolution(); - left_neighbor->next_name_resolution_table= first_leaf_on_the_right; + if (store_top_level_join_columns(thd, table_ref, + left_neighbor, right_neighbor)) + return TRUE; + if (left_neighbor) + { + TABLE_LIST *first_leaf_on_the_right; + first_leaf_on_the_right= table_ref->first_leaf_for_name_resolution(); + left_neighbor->next_name_resolution_table= first_leaf_on_the_right; + } } right_neighbor= table_ref; } From b10b25fbb59fb10527c207154bbb65270beddb84 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 12 Jun 2006 15:35:47 +0200 Subject: [PATCH 31/65] Added order by --- mysql-test/r/ndb_lock.result | 8 ++++---- mysql-test/t/ndb_lock.test | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/mysql-test/r/ndb_lock.result b/mysql-test/r/ndb_lock.result index 281b02b8228..875dcd775e6 100644 --- a/mysql-test/r/ndb_lock.result +++ b/mysql-test/r/ndb_lock.result @@ -78,10 +78,10 @@ ERROR HY000: Lock wait timeout exceeded; try restarting transaction rollback; commit; begin; -select * from t1 where y = 'one' or y = 'three' for update; +select * from t1 where y = 'one' or y = 'three' order by x for update; x y z -3 three 3 1 one 1 +3 three 3 begin; select * from t1 where x = 2 for update; x y z @@ -118,10 +118,10 @@ ERROR HY000: Lock wait timeout exceeded; try restarting transaction rollback; commit; begin; -select * from t1 where y = 'one' or y = 'three' lock in share mode; +select * from t1 where y = 'one' or y = 'three' order by x lock in share mode; x y z -3 three 3 1 one 1 +3 three 3 begin; select * from t1 where y = 'one' lock in share mode; x y z diff --git a/mysql-test/t/ndb_lock.test b/mysql-test/t/ndb_lock.test index db42b7ec2dd..e8f7c57ac96 100644 --- a/mysql-test/t/ndb_lock.test +++ b/mysql-test/t/ndb_lock.test @@ -93,7 +93,7 @@ commit; # table scan connection con1; begin; -select * from t1 where y = 'one' or y = 'three' for update; +select * from t1 where y = 'one' or y = 'three' order by x for update; connection con2; begin; @@ -145,7 +145,7 @@ commit; # table scan connection con1; begin; -select * from t1 where y = 'one' or y = 'three' lock in share mode; +select * from t1 where y = 'one' or y = 'three' order by x lock in share mode; connection con2; begin; From 3f19b05bb00366df88d72f9b6507f897f2f9370b Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 12 Jun 2006 15:36:12 +0200 Subject: [PATCH 32/65] Added order by --- mysql-test/r/ndb_lock.result | 8 ++++---- mysql-test/t/ndb_lock.test | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/mysql-test/r/ndb_lock.result b/mysql-test/r/ndb_lock.result index 92a73830662..3b433023843 100644 --- a/mysql-test/r/ndb_lock.result +++ b/mysql-test/r/ndb_lock.result @@ -78,10 +78,10 @@ ERROR HY000: Lock wait timeout exceeded; try restarting transaction rollback; commit; begin; -select * from t1 where y = 'one' or y = 'three' for update; +select * from t1 where y = 'one' or y = 'three' order by x for update; x y z -3 three 3 1 one 1 +3 three 3 begin; select * from t1 where x = 1 for update; ERROR HY000: Lock wait timeout exceeded; try restarting transaction @@ -115,10 +115,10 @@ ERROR HY000: Lock wait timeout exceeded; try restarting transaction rollback; commit; begin; -select * from t1 where y = 'one' or y = 'three' lock in share mode; +select * from t1 where y = 'one' or y = 'three' order by x lock in share mode; x y z -3 three 3 1 one 1 +3 three 3 begin; select * from t1 where y = 'one' lock in share mode; x y z diff --git a/mysql-test/t/ndb_lock.test b/mysql-test/t/ndb_lock.test index 6d3257aeb68..54214ee72ec 100644 --- a/mysql-test/t/ndb_lock.test +++ b/mysql-test/t/ndb_lock.test @@ -93,7 +93,7 @@ commit; # table scan connection con1; begin; -select * from t1 where y = 'one' or y = 'three' for update; +select * from t1 where y = 'one' or y = 'three' order by x for update; connection con2; begin; @@ -146,7 +146,7 @@ commit; # table scan connection con1; begin; -select * from t1 where y = 'one' or y = 'three' lock in share mode; +select * from t1 where y = 'one' or y = 'three' order by x lock in share mode; connection con2; begin; From e7daa497613cdd1845ec6b05d7cf3f9efaa2662c Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 12 Jun 2006 18:15:08 +0300 Subject: [PATCH 33/65] Bug #20363: Create view on just created view is now denied There was a wrong determination of the DB name (witch is not always the one in TABLE_LIST because derived tables may be calculated using temp tables that have their db name set to ""). The fix determines the database name according to the type of table reference, and calls the function check_access() with the correct db name so the correct set of grants is found. mysql-test/r/view_grant.result: testsuite for the bug mysql-test/t/view_grant.test: testsuite for the bug sql/sql_parse.cc: correct determination of the db name. --- mysql-test/r/view_grant.result | 29 +++++++++++++++++++++++++ mysql-test/t/view_grant.test | 39 ++++++++++++++++++++++++++++++++++ sql/sql_parse.cc | 9 +++++++- 3 files changed, 76 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/view_grant.result b/mysql-test/r/view_grant.result index f6559e6f838..a8619201810 100644 --- a/mysql-test/r/view_grant.result +++ b/mysql-test/r/view_grant.result @@ -618,3 +618,32 @@ ERROR HY000: There is no 'no-such-user'@'localhost' registered DROP VIEW v; DROP TABLE t1; USE test; +CREATE USER mysqltest_db1@localhost identified by 'PWD'; +GRANT ALL ON mysqltest_db1.* TO mysqltest_db1@localhost WITH GRANT OPTION; +CREATE SCHEMA mysqltest_db1 ; +USE mysqltest_db1 ; +CREATE TABLE t1 (f1 INTEGER); +CREATE VIEW view1 AS +SELECT * FROM t1; +SHOW CREATE VIEW view1; +View Create View +view1 CREATE ALGORITHM=UNDEFINED DEFINER=`mysqltest_db1`@`localhost` SQL SECURITY DEFINER VIEW `view1` AS select `t1`.`f1` AS `f1` from `t1` +CREATE VIEW view2 AS +SELECT * FROM view1; +# Here comes a suspicious warning +SHOW CREATE VIEW view2; +View Create View +view2 CREATE ALGORITHM=UNDEFINED DEFINER=`mysqltest_db1`@`localhost` SQL SECURITY DEFINER VIEW `view2` AS select `view1`.`f1` AS `f1` from `view1` +# But the view view2 is usable +SELECT * FROM view2; +f1 +CREATE VIEW view3 AS +SELECT * FROM view2; +SELECT * from view3; +f1 +DROP VIEW mysqltest_db1.view3; +DROP VIEW mysqltest_db1.view2; +DROP VIEW mysqltest_db1.view1; +DROP TABLE mysqltest_db1.t1; +DROP SCHEMA mysqltest_db1; +DROP USER mysqltest_db1@localhost; diff --git a/mysql-test/t/view_grant.test b/mysql-test/t/view_grant.test index 4663a667d25..f160de2d798 100644 --- a/mysql-test/t/view_grant.test +++ b/mysql-test/t/view_grant.test @@ -807,3 +807,42 @@ SELECT * FROM v; DROP VIEW v; DROP TABLE t1; USE test; + +# +# Bug#20363: Create view on just created view is now denied +# +eval CREATE USER mysqltest_db1@localhost identified by 'PWD'; +eval GRANT ALL ON mysqltest_db1.* TO mysqltest_db1@localhost WITH GRANT OPTION; + +# The session with the non root user is needed. +--replace_result $MASTER_MYPORT MYSQL_PORT $MASTER_MYSOCK MYSQL_SOCK +connect (session1,localhost,mysqltest_db1,PWD,test); + +CREATE SCHEMA mysqltest_db1 ; +USE mysqltest_db1 ; + +CREATE TABLE t1 (f1 INTEGER); + +CREATE VIEW view1 AS +SELECT * FROM t1; +SHOW CREATE VIEW view1; + +CREATE VIEW view2 AS +SELECT * FROM view1; +--echo # Here comes a suspicious warning +SHOW CREATE VIEW view2; +--echo # But the view view2 is usable +SELECT * FROM view2; + +CREATE VIEW view3 AS +SELECT * FROM view2; + +SELECT * from view3; + +connection default; +DROP VIEW mysqltest_db1.view3; +DROP VIEW mysqltest_db1.view2; +DROP VIEW mysqltest_db1.view1; +DROP TABLE mysqltest_db1.t1; +DROP SCHEMA mysqltest_db1; +DROP USER mysqltest_db1@localhost; diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 213a7730824..ba5c2ebf484 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -5004,7 +5004,14 @@ bool check_one_table_access(THD *thd, ulong privilege, TABLE_LIST *all_tables) if (all_tables->security_ctx) thd->security_ctx= all_tables->security_ctx; - if (check_access(thd, privilege, all_tables->db, + const char *db_name; + if ((all_tables->view || all_tables->field_translation) && + !all_tables->schema_table) + db_name= all_tables->view_db.str; + else + db_name= all_tables->db; + + if (check_access(thd, privilege, db_name, &all_tables->grant.privilege, 0, 0, test(all_tables->schema_table))) goto deny; From 47311e8e878a0f049c3f3b2a1c1dea454800d70a Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 13 Jun 2006 19:09:24 +0400 Subject: [PATCH 34/65] Fixed bug#16377: result of DATE/TIME functions were compared as strings which can lead to a wrong result. All date/time functions has the STRING result type thus their results are compared as strings. The string date representation allows a user to skip some of leading zeros. This can lead to wrong comparison result if a date/time function result is compared to such a string constant. The idea behind this bug fix is to compare results of date/time functions and data/time constants as ints, because that date/time representation is more exact. To achieve this the agg_cmp_type() is changed to take in the account that a date/time field or an date/time item should be compared as ints. This bug fix is partially back ported from 5.0. The agg_cmp_type() function now accepts THD as one of parameters. In addition, it now checks if a date/time field/function is present in the list. If so, it tries to coerce all constants to INT to make date/time comparison return correct result. The field for the constant coercion is taken from the Item_field or constructed from the Item_func. In latter case the constructed field will be freed after conversion of all constant items. Otherwise the result is same as before - aggregated with help of the item_cmp_type() function. From the Item_func_between::fix_length_and_dec() function removed the part which was converting date/time constants to int if possible. Now this is done by the agg_cmp_type() function. The new function result_as_longlong() is added to the Item class. It indicates that the item is a date/time item and result of it can be compared as int. Such items are date/time fields/functions. Correct val_int() methods are implemented for classes Item_date_typecast, Item_func_makedate, Item_time_typecast, Item_datetime_typecast. All these classes are derived from Item_str_func and Item_str_func::val_int() converts its string value to int without regard to the date/time type of these items. Arg_comparator::set_compare_func() and Arg_comparator::set_cmp_func() functions are changed to substitute result type of an item with the INT_RESULT if the item is a date/time item and another item is a constant. This is done to get a correct result of comparisons like date_time_function() = string_constant. mysql-test/r/cast.result: Fixed wrong test case result after bug fix#16377. sql/item_timefunc.h: Fixed bug#16377: result of DATE/TIME functions were compared as strings which can lead to a wrong result. The result_as_longlong() function is set to return TRUE for these classes: Item_date, Item_date_func, Item_func_curtime, Item_func_sec_to_time, Item_date_typecast, Item_time_typecast, Item_datetime_typecast, Item_func_makedate. sql/item_timefunc.cc: Fixed bug#16377: result of DATE/TIME functions were compared as strings which can lead to a wrong result.Correct val_int() methods are implemented for classes Item_date_typecast, Item_func_makedate, Item_time_typecast, Item_datetime_typecast. sql/item_cmpfunc.h: Fixed bug#16377: result of DATE/TIME functions were compared as strings which can lead to a wrong result. Arg_comparator::set_compare_func() and Arg_comparator::set_cmp_func() functions are changed to substitute result type of an item with the INT_RESULT if the item is a date/time item and another item is a constant. sql/field.cc: Fixed bug#16377: result of DATE/TIME functions were compared as strings which can lead to a wrong result. Field::set_warning(), Field::set_datetime_warning() now use current_thd to get thd if table isn't set. sql/item_cmpfunc.cc: Fixed bug#16377: result of DATE/TIME functions were compared as strings which can lead to a wrong result. The agg_cmp_type() function now accepts THD as one of parameters. In addition, it now checks if a date/time field/function is present in the list. If so, it tries to coerce all constants to INT to make date/time comparison return correct result. The field for the constant coercion is taken from the Item_field or constructed from the Item_func. In latter case the constructed field will be freed after conversion of all constant items. Otherwise the result is same as before - aggregated with help of the item_cmp_type() function. sql/item.h: The new function result_as_longlong() is added to the Item class. It indicates that the item is a date/time item and result of it can be compared as int. Such items are date/time fields/functions. mysql-test/t/func_time.test: Added test case fot bug#16377: result of DATE/TIME functions were compared as strings which can lead to a wrong result. mysql-test/r/func_time.result: Added test case fot bug#16377: result of DATE/TIME functions were compared as strings which can lead to a wrong result. --- mysql-test/r/cast.result | 2 +- mysql-test/r/func_time.result | 41 +++++++++ mysql-test/t/func_time.test | 20 +++++ sql/field.cc | 16 ++-- sql/item.h | 16 ++++ sql/item_cmpfunc.cc | 154 +++++++++++++++++++++++++++------- sql/item_cmpfunc.h | 14 +++- sql/item_timefunc.cc | 60 +++++++++++++ sql/item_timefunc.h | 12 +++ 9 files changed, 293 insertions(+), 42 deletions(-) diff --git a/mysql-test/r/cast.result b/mysql-test/r/cast.result index 68687670e17..2538fbfd61d 100644 --- a/mysql-test/r/cast.result +++ b/mysql-test/r/cast.result @@ -192,7 +192,7 @@ cast("2001-1-1" as datetime) = "2001-01-01 00:00:00" 1 select cast("1:2:3" as TIME) = "1:02:03"; cast("1:2:3" as TIME) = "1:02:03" -0 +1 select cast(NULL as DATE); cast(NULL as DATE) NULL diff --git a/mysql-test/r/func_time.result b/mysql-test/r/func_time.result index 02f3d2f7273..93d4542bf5b 100644 --- a/mysql-test/r/func_time.result +++ b/mysql-test/r/func_time.result @@ -630,3 +630,44 @@ select monthname(str_to_date(null, '%m')), monthname(str_to_date(null, '%m')), monthname(str_to_date(1, '%m')), monthname(str_to_date(0, '%m')); monthname(str_to_date(null, '%m')) monthname(str_to_date(null, '%m')) monthname(str_to_date(1, '%m')) monthname(str_to_date(0, '%m')) NULL NULL January NULL +create table t1(f1 date, f2 time, f3 datetime); +insert into t1 values ("2006-01-01", "12:01:01", "2006-01-01 12:01:01"); +insert into t1 values ("2006-01-02", "12:01:02", "2006-01-02 12:01:02"); +select f1 from t1 where f1 between "2006-1-1" and 20060101; +f1 +2006-01-01 +select f1 from t1 where f1 between "2006-1-1" and "2006.1.1"; +f1 +2006-01-01 +select f1 from t1 where date(f1) between "2006-1-1" and "2006.1.1"; +f1 +2006-01-01 +select f2 from t1 where f2 between "12:1:2" and "12:2:2"; +f2 +12:01:02 +select f2 from t1 where time(f2) between "12:1:2" and "12:2:2"; +f2 +12:01:02 +select f3 from t1 where f3 between "2006-1-1 12:1:1" and "2006-1-1 12:1:2"; +f3 +2006-01-01 12:01:01 +select f3 from t1 where timestamp(f3) between "2006-1-1 12:1:1" and "2006-1-1 12:1:2"; +f3 +2006-01-01 12:01:01 +select f1 from t1 where "2006-1-1" between f1 and f3; +f1 +2006-01-01 +select f1 from t1 where "2006-1-1" between date(f1) and date(f3); +f1 +2006-01-01 +select f1 from t1 where "2006-1-1" between f1 and 'zzz'; +f1 +Warnings: +Warning 1292 Truncated incorrect date value: 'zzz' +select f1 from t1 where makedate(2006,1) between date(f1) and date(f3); +f1 +2006-01-01 +select f1 from t1 where makedate(2006,2) between date(f1) and date(f3); +f1 +2006-01-02 +drop table t1; diff --git a/mysql-test/t/func_time.test b/mysql-test/t/func_time.test index 01e4e47d318..5e0b35522cf 100644 --- a/mysql-test/t/func_time.test +++ b/mysql-test/t/func_time.test @@ -322,4 +322,24 @@ select last_day('2005-01-00'); select monthname(str_to_date(null, '%m')), monthname(str_to_date(null, '%m')), monthname(str_to_date(1, '%m')), monthname(str_to_date(0, '%m')); +# +# Bug#16377 result of DATE/TIME functions were compared as strings which +# can lead to a wrong result. +# +create table t1(f1 date, f2 time, f3 datetime); +insert into t1 values ("2006-01-01", "12:01:01", "2006-01-01 12:01:01"); +insert into t1 values ("2006-01-02", "12:01:02", "2006-01-02 12:01:02"); +select f1 from t1 where f1 between "2006-1-1" and 20060101; +select f1 from t1 where f1 between "2006-1-1" and "2006.1.1"; +select f1 from t1 where date(f1) between "2006-1-1" and "2006.1.1"; +select f2 from t1 where f2 between "12:1:2" and "12:2:2"; +select f2 from t1 where time(f2) between "12:1:2" and "12:2:2"; +select f3 from t1 where f3 between "2006-1-1 12:1:1" and "2006-1-1 12:1:2"; +select f3 from t1 where timestamp(f3) between "2006-1-1 12:1:1" and "2006-1-1 12:1:2"; +select f1 from t1 where "2006-1-1" between f1 and f3; +select f1 from t1 where "2006-1-1" between date(f1) and date(f3); +select f1 from t1 where "2006-1-1" between f1 and 'zzz'; +select f1 from t1 where makedate(2006,1) between date(f1) and date(f3); +select f1 from t1 where makedate(2006,2) between date(f1) and date(f3); +drop table t1; # End of 4.1 tests diff --git a/sql/field.cc b/sql/field.cc index a64eaad7308..ec4d4b4e4f5 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -6841,7 +6841,11 @@ create_field::create_field(Field *old_field,Field *orig_field) bool Field::set_warning(const uint level, const uint code, int cuted_increment) { - THD *thd= table->in_use; + /* + If this field was created only for type conversion purposes it + will have table == NULL. + */ + THD *thd= table ? table->in_use : current_thd; if (thd->count_cuted_fields) { thd->cuted_fields+= cuted_increment; @@ -6876,7 +6880,8 @@ Field::set_datetime_warning(const uint level, const uint code, timestamp_type ts_type, int cuted_increment) { if (set_warning(level, code, cuted_increment)) - make_truncated_value_warning(table->in_use, str, str_length, ts_type); + make_truncated_value_warning(table ? table->in_use : current_thd, + str, str_length, ts_type); } @@ -6905,8 +6910,8 @@ Field::set_datetime_warning(const uint level, const uint code, { char str_nr[22]; char *str_end= longlong10_to_str(nr, str_nr, -10); - make_truncated_value_warning(table->in_use, str_nr, str_end - str_nr, - ts_type); + make_truncated_value_warning(table ? table->in_use : current_thd, + str_nr, str_end - str_nr, ts_type); } } @@ -6935,7 +6940,8 @@ Field::set_datetime_warning(const uint level, const uint code, /* DBL_DIG is enough to print '-[digits].E+###' */ char str_nr[DBL_DIG + 8]; uint str_len= my_sprintf(str_nr, (str_nr, "%g", nr)); - make_truncated_value_warning(table->in_use, str_nr, str_len, ts_type); + make_truncated_value_warning(table ? table->in_use : current_thd, + str_nr, str_len, ts_type); } } diff --git a/sql/item.h b/sql/item.h index 66da7ad4e74..eca8ee184a1 100644 --- a/sql/item.h +++ b/sql/item.h @@ -327,6 +327,14 @@ public: cleanup(); delete this; } + /* + result_as_longlong() must return TRUE for Items representing DATE/TIME + functions and DATE/TIME table fields. + Those Items have result_type()==STRING_RESULT (and not INT_RESULT), but + their values should be compared as integers (because the integer + representation is more precise than the string one). + */ + virtual bool result_as_longlong() { return FALSE; } }; @@ -450,6 +458,10 @@ public: Item *get_tmp_table_item(THD *thd); void cleanup(); inline uint32 max_disp_length() { return field->max_length(); } + bool result_as_longlong() + { + return field->can_be_compared_as_longlong(); + } friend class Item_default_value; friend class Item_insert_value; friend class st_select_lex_unit; @@ -973,6 +985,10 @@ public: } Item *real_item() { return *ref; } void print(String *str); + bool result_as_longlong() + { + return (*ref)->result_as_longlong(); + } }; diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index bfbb3355004..e92e1d30ca4 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -25,6 +25,8 @@ #include #include "sql_select.h" +static bool convert_constant_item(THD *thd, Field *field, Item **item); + static Item_result item_store_type(Item_result a,Item_result b) { if (a == STRING_RESULT || b == STRING_RESULT) @@ -64,6 +66,7 @@ static void agg_result_type(Item_result *type, Item **items, uint nitems) SYNOPSIS: agg_cmp_type() + thd thread handle type [out] the aggregated type items array of items to aggregate the type from nitems number of items in the array @@ -78,24 +81,133 @@ static void agg_result_type(Item_result *type, Item **items, uint nitems) If all items are constants the type will be aggregated from all items. If there are some non-constant items then only types of non-constant items will be used for aggregation. + If there are DATE/TIME fields/functions in the list and no string + fields/functions in the list then: + The INT_RESULT type will be used for aggregation instead of orginal + result type of any DATE/TIME field/function in the list + All constant items in the list will be converted to a DATE/TIME using + found field or result field of found function. + + Implementation notes: + The code is equvalent to: + 1. Check the list for presense of a STRING field/function. + Collect the is_const flag. + 2. Get a Field* object to use for type coercion + 3. Perform type conversion. + 1 and 2 are implemented in 2 loops. The first searches for a DATE/TIME + field/function and checks presense of a STRING field/function. + The second loop works only if a DATE/TIME field/function is found. + It checks presense of a STRING field/function in the rest of the list. + + TODO + 1) The current implementation can produce false comparison results for + expressions like: + date_time_field BETWEEN string_field_with_dates AND string_constant + if the string_constant will omit some of leading zeroes. + In order to fully implement correct comparison of DATE/TIME the new + DATETIME_RESULT result type should be introduced and agg_cmp_type() + should return the DATE/TIME field used for the conversion. Later + this field can be used by comparison functions like Item_func_between to + convert string values to ints on the fly and thus return correct results. + This modification will affect functions BETWEEN, IN and CASE. + + 2) If in the list a DATE field/function and a DATETIME field/function + are present in the list then the first found field/function will be + used for conversion. This may lead to wrong results and probably should + be fixed. */ -static void agg_cmp_type(Item_result *type, Item **items, uint nitems) + +static void agg_cmp_type(THD *thd, Item_result *type, Item **items, uint nitems) { uint i; - type[0]= items[0]->result_type(); + Item::Type res; + char *buff= NULL; + uchar null_byte; + Field *field= NULL; + + /* Search for date/time fields/functions */ + for (i= 0; i < nitems; i++) + { + if (!items[i]->result_as_longlong()) + { + /* Do not convert anything if a string field/function is present */ + if (!items[i]->const_item() && items[i]->result_type() == STRING_RESULT) + { + i= nitems; + break; + } + continue; + } + if ((res= items[i]->real_item()->type()) == Item::FIELD_ITEM) + { + field= ((Item_field *)items[i]->real_item())->field; + break; + } + else if (res == Item::FUNC_ITEM) + { + field= items[i]->tmp_table_field_from_field_type(0); + if (field) + buff= alloc_root(thd->mem_root, field->max_length()); + if (!buff || !field) + { + if (field) + delete field; + if (buff) + my_free(buff, MYF(MY_WME)); + field= 0; + } + else + field->move_field(buff, &null_byte, 0); + break; + } + } + if (field) + { + /* Check the rest of the list for presense of a string field/function. */ + for (i++ ; i < nitems; i++) + { + if (!items[i]->const_item() && items[i]->result_type() == STRING_RESULT && + !items[i]->result_as_longlong()) + { + field= 0; + break; + } + } + } /* Reset to 0 on first occurence of non-const item. 1 otherwise */ bool is_const= items[0]->const_item(); + /* + If the first item is a date/time function then its result should be + compared as int + */ + if (field) + { + /* Suppose we are comparing dates and some non-constant items are present. */ + type[0]= INT_RESULT; + is_const= 0; + } + else + type[0]= items[0]->result_type(); - for (i= 1 ; i < nitems ; i++) + for (i= 0; i < nitems ; i++) { if (!items[i]->const_item()) { - type[0]= is_const ? items[i]->result_type() : - item_cmp_type(type[0], items[i]->result_type()); + Item_result result= field && items[i]->result_as_longlong() ? + INT_RESULT : items[i]->result_type(); + type[0]= is_const ? result : item_cmp_type(type[0], result); is_const= 0; } else if (is_const) type[0]= item_cmp_type(type[0], items[i]->result_type()); + else if (field) + convert_constant_item(thd, field, &items[i]); + } + + if (res == Item::FUNC_ITEM && field) + { + delete field; + my_free(buff, MYF(MY_WME)); } } @@ -929,31 +1041,9 @@ void Item_func_between::fix_length_and_dec() */ if (!args[0] || !args[1] || !args[2]) return; - agg_cmp_type(&cmp_type, args, 3); - if (cmp_type == STRING_RESULT && - agg_arg_charsets(cmp_collation, args, 3, MY_COLL_CMP_CONV)) - return; - - /* - Make a special case of compare with date/time and longlong fields. - They are compared as integers, so for const item this time-consuming - conversion can be done only once, not for every single comparison - */ - if (args[0]->type() == FIELD_ITEM) - { - Field *field=((Item_field*) args[0])->field; - if (field->can_be_compared_as_longlong()) - { - /* - The following can't be recoded with || as convert_constant_item - changes the argument - */ - if (convert_constant_item(thd, field,&args[1])) - cmp_type=INT_RESULT; // Works for all types. - if (convert_constant_item(thd, field,&args[2])) - cmp_type=INT_RESULT; // Works for all types. - } - } + agg_cmp_type(thd, &cmp_type, args, 3); + if (cmp_type == STRING_RESULT) + agg_arg_charsets(cmp_collation, args, 3, MY_COLL_CMP_CONV); } @@ -1477,7 +1567,7 @@ void Item_func_case::fix_length_and_dec() for (nagg= 0; nagg < ncases/2 ; nagg++) agg[nagg+1]= args[nagg*2]; nagg++; - agg_cmp_type(&cmp_type, agg, nagg); + agg_cmp_type(current_thd, &cmp_type, agg, nagg); if ((cmp_type == STRING_RESULT) && agg_arg_charsets(cmp_collation, agg, nagg, MY_COLL_CMP_CONV)) return; @@ -1958,7 +2048,7 @@ void Item_func_in::fix_length_and_dec() uint const_itm= 1; THD *thd= current_thd; - agg_cmp_type(&cmp_type, args, arg_count); + agg_cmp_type(thd, &cmp_type, args, arg_count); if (cmp_type == STRING_RESULT && agg_arg_charsets(cmp_collation, args, arg_count, MY_COLL_CMP_CONV)) diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index ade09113c63..08d9de6ffd6 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -43,8 +43,11 @@ public: int set_compare_func(Item_bool_func2 *owner, Item_result type); inline int set_compare_func(Item_bool_func2 *owner_arg) { - return set_compare_func(owner_arg, item_cmp_type((*a)->result_type(), - (*b)->result_type())); + Item_result ar= (*a)->result_as_longlong() && (*b)->const_item() ? + INT_RESULT : (*a)->result_type(); + Item_result br= (*b)->result_as_longlong() && (*a)->const_item() ? + INT_RESULT : (*b)->result_type(); + return set_compare_func(owner_arg, item_cmp_type(ar, br)); } inline int set_cmp_func(Item_bool_func2 *owner_arg, Item **a1, Item **a2, @@ -57,8 +60,11 @@ public: inline int set_cmp_func(Item_bool_func2 *owner_arg, Item **a1, Item **a2) { - return set_cmp_func(owner_arg, a1, a2, item_cmp_type((*a1)->result_type(), - (*a2)->result_type())); + Item_result ar= (*a1)->result_as_longlong() && (*a2)->const_item() ? + INT_RESULT : (*a1)->result_type(); + Item_result br= (*a2)->result_as_longlong() && (*a1)->const_item() ? + INT_RESULT : (*a2)->result_type(); + return set_cmp_func(owner_arg, a1, a2, item_cmp_type(ar, br)); } inline int compare() { return (this->*func)(); } diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc index 5fdbd968df1..f7b4c9dd630 100644 --- a/sql/item_timefunc.cc +++ b/sql/item_timefunc.cc @@ -2306,6 +2306,20 @@ String *Item_datetime_typecast::val_str(String *str) } +longlong Item_datetime_typecast::val_int() +{ + DBUG_ASSERT(fixed == 1); + TIME ltime; + if (get_arg0_date(<ime,1)) + { + null_value= 1; + return 0; + } + + return TIME_to_ulonglong_datetime(<ime); +} + + bool Item_time_typecast::get_time(TIME *ltime) { bool res= get_arg0_time(ltime); @@ -2320,6 +2334,17 @@ bool Item_time_typecast::get_time(TIME *ltime) } +longlong Item_time_typecast::val_int() +{ + TIME ltime; + if (get_time(<ime)) + { + null_value= 1; + return 0; + } + return ltime.hour * 10000L + ltime.minute * 100 + ltime.second; +} + String *Item_time_typecast::val_str(String *str) { DBUG_ASSERT(fixed == 1); @@ -2359,6 +2384,14 @@ String *Item_date_typecast::val_str(String *str) return 0; } +longlong Item_date_typecast::val_int() +{ + DBUG_ASSERT(fixed == 1); + TIME ltime; + if (args[0]->get_date(<ime, TIME_FUZZY_DATE)) + return 0; + return (longlong) (ltime.year * 10000L + ltime.month * 100 + ltime.day); +} /* MAKEDATE(a,b) is a date function that creates a date value @@ -2395,6 +2428,33 @@ err: } +longlong Item_func_makedate::val_int() +{ + DBUG_ASSERT(fixed == 1); + TIME l_time; + long daynr= (long) args[1]->val_int(); + long yearnr= (long) args[0]->val_int(); + long days; + + if (args[0]->null_value || args[1]->null_value || + yearnr < 0 || daynr <= 0) + goto err; + + days= calc_daynr(yearnr,1,1) + daynr - 1; + /* Day number from year 0 to 9999-12-31 */ + if (days >= 0 && days < MAX_DAY_NUMBER) + { + null_value=0; + get_date_from_daynr(days,&l_time.year,&l_time.month,&l_time.day); + return (longlong) (l_time.year * 10000L + l_time.month * 100 + l_time.day); + } + +err: + null_value= 1; + return 0; +} + + void Item_func_add_time::fix_length_and_dec() { enum_field_types arg0_field_type; diff --git a/sql/item_timefunc.h b/sql/item_timefunc.h index 163b1591e52..95f1dfa75d1 100644 --- a/sql/item_timefunc.h +++ b/sql/item_timefunc.h @@ -339,6 +339,7 @@ public: { return (new Field_date(maybe_null, name, t_arg, &my_charset_bin)); } + bool result_as_longlong() { return TRUE; } }; @@ -354,6 +355,7 @@ public: { return (new Field_datetime(maybe_null, name, t_arg, &my_charset_bin)); } + bool result_as_longlong() { return TRUE; } }; @@ -383,6 +385,7 @@ public: TIME representation using UTC-SYSTEM or per-thread time zone. */ virtual void store_now_in_TIME(TIME *now_time)=0; + bool result_as_longlong() { return TRUE; } }; @@ -588,6 +591,7 @@ public: { return (new Field_time(maybe_null, name, t_arg, &my_charset_bin)); } + bool result_as_longlong() { return TRUE; } }; /* @@ -715,6 +719,8 @@ public: max_length= 10; maybe_null= 1; } + bool result_as_longlong() { return TRUE; } + longlong val_int(); }; @@ -731,6 +737,8 @@ public: { return (new Field_time(maybe_null, name, t_arg, &my_charset_bin)); } + bool result_as_longlong() { return TRUE; } + longlong val_int(); }; @@ -746,6 +754,8 @@ public: { return (new Field_datetime(maybe_null, name, t_arg, &my_charset_bin)); } + bool result_as_longlong() { return TRUE; } + longlong val_int(); }; class Item_func_makedate :public Item_str_func @@ -764,6 +774,8 @@ public: { return (new Field_date(maybe_null, name, t_arg, &my_charset_bin)); } + bool result_as_longlong() { return TRUE; } + longlong val_int(); }; From 01c9fd31d7456827587b4ea00facb14317fdd81b Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 13 Jun 2006 18:18:32 +0300 Subject: [PATCH 35/65] Bug #20195: INSERT DELAYED with auto_increment is assigned wrong values The INSERT DELAYED should not maintain its own private auto-increment counter, because this is assuming that other threads cannot insert into the table while the INSERT DELAYED thread is inserting, which is a wrong assumption. So the start of processing of a batch of INSERT rows in the INSERT DELAYED thread must be treated as a start of a new statement and cached next_insert_id must be cleared. mysql-test/r/delayed.result: test suite for the bug mysql-test/t/delayed.test: test suite for the bug sql/sql_insert.cc: Reset auto-increment cacheing before processing the next batch of inserts in the handler thread --- mysql-test/r/delayed.result | 30 +++++++++++++++++++++++ mysql-test/t/delayed.test | 49 +++++++++++++++++++++++++++++++++++++ sql/sql_insert.cc | 8 ++++++ 3 files changed, 87 insertions(+) diff --git a/mysql-test/r/delayed.result b/mysql-test/r/delayed.result index f8ae61b03fb..a336f3b4108 100644 --- a/mysql-test/r/delayed.result +++ b/mysql-test/r/delayed.result @@ -39,3 +39,33 @@ select * from t1; a 1 drop table t1; +CREATE TABLE t1 ( a int(10) NOT NULL auto_increment, PRIMARY KEY (a)); +insert delayed into t1 values(null); +insert into t1 values(null); +insert into t1 values(null); +insert delayed into t1 values(null); +insert delayed into t1 values(null); +insert delayed into t1 values(null); +insert into t1 values(null); +insert into t1 values(null); +insert into t1 values(null); +delete from t1 where a=6; +insert delayed into t1 values(null); +insert delayed into t1 values(null); +insert delayed into t1 values(null); +insert delayed into t1 values(null); +select * from t1 order by a; +a +1 +2 +3 +4 +5 +7 +8 +9 +10 +11 +12 +13 +DROP TABLE t1; diff --git a/mysql-test/t/delayed.test b/mysql-test/t/delayed.test index 5ae757b1fde..55e8f81f763 100644 --- a/mysql-test/t/delayed.test +++ b/mysql-test/t/delayed.test @@ -50,3 +50,52 @@ insert into t1 values (1); insert delayed into t1 values (1); select * from t1; drop table t1; + +# +# Bug #20195: INSERT DELAYED with auto_increment is assigned wrong values +# +CREATE TABLE t1 ( a int(10) NOT NULL auto_increment, PRIMARY KEY (a)); + +# Make one delayed insert to start the separate thread +insert delayed into t1 values(null); + +# Do some normal inserts +insert into t1 values(null); +insert into t1 values(null); + +# Discarded, since the delayed-counter is 2, which is already used +insert delayed into t1 values(null); + +# Discarded, since the delayed-counter is 3, which is already used +insert delayed into t1 values(null); + +# Works, since the delayed-counter is 4, which is unused +insert delayed into t1 values(null); + +# Do some more inserts +insert into t1 values(null); +insert into t1 values(null); +insert into t1 values(null); + +# Delete one of the above to make a hole +delete from t1 where a=6; + +# Discarded, since the delayed-counter is 5, which is already used +insert delayed into t1 values(null); + +# Works, since the delayed-counter is 6, which is unused (the row we deleted) +insert delayed into t1 values(null); + +# Discarded, since the delayed-counter is 7, which is already used +insert delayed into t1 values(null); + +# Works, since the delayed-counter is 8, which is unused +insert delayed into t1 values(null); + +# Check what we have now +# must wait so that the delayed thread finishes +# Note: this must be increased if the test fails +--sleep 1 +select * from t1 order by a; + +DROP TABLE t1; diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 26f3b6f5faa..5630702fe52 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -1938,6 +1938,14 @@ bool delayed_insert::handle_inserts(void) if (!using_bin_log) table->file->extra(HA_EXTRA_WRITE_CACHE); pthread_mutex_lock(&mutex); + + /* Reset auto-increment cacheing */ + if (thd.clear_next_insert_id) + { + thd.next_insert_id= 0; + thd.clear_next_insert_id= 0; + } + while ((row=rows.get())) { stacked_inserts--; From de5de7c3196533922169bb0538433fc9618f34f1 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 14 Jun 2006 00:38:34 +0200 Subject: [PATCH 36/65] ndb - bug#20406 multi-platform testing - not FIX --- mysql-test/r/ndb_condition_pushdown.result | 24 ++++++++++++++++++++++ mysql-test/t/ndb_condition_pushdown.test | 22 ++++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/mysql-test/r/ndb_condition_pushdown.result b/mysql-test/r/ndb_condition_pushdown.result index 27a510b252f..4e5597a4851 100644 --- a/mysql-test/r/ndb_condition_pushdown.result +++ b/mysql-test/r/ndb_condition_pushdown.result @@ -1842,5 +1842,29 @@ a b select * from t1 where b like 'abc' or b like 'abc'; a b 3 abc +drop table t1; +create table t1 ( fname varchar(255), lname varchar(255) ) +engine=ndbcluster; +insert into t1 values ("Young","Foo"); +set engine_condition_pushdown = 0; +SELECT fname, lname FROM t1 WHERE (fname like 'Y%') or (lname like 'F%'); +fname lname +Young Foo +set engine_condition_pushdown = 1; +SELECT fname, lname FROM t1 WHERE (fname like 'Y%') or (lname like 'F%'); +fname lname +Young Foo +insert into t1 values ("aaa", "aaa"); +insert into t1 values ("bbb", "bbb"); +insert into t1 values ("ccc", "ccc"); +insert into t1 values ("ddd", "ddd"); +set engine_condition_pushdown = 0; +SELECT fname, lname FROM t1 WHERE (fname like 'Y%') or (lname like 'F%'); +fname lname +Young Foo +set engine_condition_pushdown = 1; +SELECT fname, lname FROM t1 WHERE (fname like 'Y%') or (lname like 'F%'); +fname lname +Young Foo set engine_condition_pushdown = @old_ecpd; DROP TABLE t1,t2,t3,t4,t5; diff --git a/mysql-test/t/ndb_condition_pushdown.test b/mysql-test/t/ndb_condition_pushdown.test index 398ca3c502c..cc138b32b7e 100644 --- a/mysql-test/t/ndb_condition_pushdown.test +++ b/mysql-test/t/ndb_condition_pushdown.test @@ -1686,5 +1686,27 @@ select * from t1 where b like 'ab' or b like 'ab'; select * from t1 where b like 'abc'; select * from t1 where b like 'abc' or b like 'abc'; +# bug#20406 (maybe same as bug#17421 -1, not seen on 32-bit x86) +drop table t1; +create table t1 ( fname varchar(255), lname varchar(255) ) +engine=ndbcluster; +insert into t1 values ("Young","Foo"); + +set engine_condition_pushdown = 0; +SELECT fname, lname FROM t1 WHERE (fname like 'Y%') or (lname like 'F%'); +set engine_condition_pushdown = 1; +SELECT fname, lname FROM t1 WHERE (fname like 'Y%') or (lname like 'F%'); + +# make sure optimizer does not do some crazy shortcut +insert into t1 values ("aaa", "aaa"); +insert into t1 values ("bbb", "bbb"); +insert into t1 values ("ccc", "ccc"); +insert into t1 values ("ddd", "ddd"); + +set engine_condition_pushdown = 0; +SELECT fname, lname FROM t1 WHERE (fname like 'Y%') or (lname like 'F%'); +set engine_condition_pushdown = 1; +SELECT fname, lname FROM t1 WHERE (fname like 'Y%') or (lname like 'F%'); + set engine_condition_pushdown = @old_ecpd; DROP TABLE t1,t2,t3,t4,t5; From 36b49259b1965743f12cd072bd033162becfbd10 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 13 Jun 2006 19:57:50 -0700 Subject: [PATCH 37/65] Fixed bug #14896. This bug in Field_string::cmp resulted in a wrong comparison with keys in partial indexes over multi-byte character fields. Given field a is declared as a varchar(16) collate utf8_unicode_ci INDEX(a(4)) gives us an example of such an index. Wrong key comparisons could lead to wrong result sets if the selected query execution plan used a range scan by a partial index over a utf8 character field. This also caused wrong results in many other cases. mysql-test/r/ctype_utf8.result: Added test cases for bug #14896. mysql-test/t/ctype_utf8.test: Added test cases for bug #14896. --- mysql-test/r/ctype_utf8.result | 40 ++++++++++++++++++++++++++++++++++ mysql-test/t/ctype_utf8.test | 26 ++++++++++++++++++++++ sql/field.cc | 20 ++++++----------- 3 files changed, 73 insertions(+), 13 deletions(-) diff --git a/mysql-test/r/ctype_utf8.result b/mysql-test/r/ctype_utf8.result index 69d7577ee77..cc271176dfc 100644 --- a/mysql-test/r/ctype_utf8.result +++ b/mysql-test/r/ctype_utf8.result @@ -1124,3 +1124,43 @@ check table t1; Table Op Msg_type Msg_text test.t1 check status OK drop table t1; +SET NAMES utf8; +CREATE TABLE t1 (id int PRIMARY KEY, +a varchar(16) collate utf8_unicode_ci NOT NULL default '', +b int, +f varchar(128) default 'XXX', +INDEX (a(4)) +) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +INSERT INTO t1(id, a, b) VALUES +(1, 'cccc', 50), (2, 'cccc', 70), (3, 'cccc', 30), +(4, 'cccc', 30), (5, 'cccc', 20), (6, 'bbbbbb', 40), +(7, 'dddd', 30), (8, 'aaaa', 10), (9, 'aaaa', 50), +(10, 'eeeee', 40), (11, 'bbbbbb', 60); +SELECT id, a, b FROM t1; +id a b +1 cccc 50 +2 cccc 70 +3 cccc 30 +4 cccc 30 +5 cccc 20 +6 bbbbbb 40 +7 dddd 30 +8 aaaa 10 +9 aaaa 50 +10 eeeee 40 +11 bbbbbb 60 +SELECT id, a, b FROM t1 WHERE a BETWEEN 'aaaa' AND 'bbbbbb'; +id a b +8 aaaa 10 +9 aaaa 50 +6 bbbbbb 40 +11 bbbbbb 60 +SELECT id, a FROM t1 WHERE a='bbbbbb'; +id a +6 bbbbbb +11 bbbbbb +SELECT id, a FROM t1 WHERE a='bbbbbb' ORDER BY b; +id a +6 bbbbbb +11 bbbbbb +DROP TABLE t1; diff --git a/mysql-test/t/ctype_utf8.test b/mysql-test/t/ctype_utf8.test index 5044f7979f1..9b8e6590999 100644 --- a/mysql-test/t/ctype_utf8.test +++ b/mysql-test/t/ctype_utf8.test @@ -926,4 +926,30 @@ INSERT INTO t1 VALUES('uUABCDEFGHIGKLMNOPRSTUVWXYZ̈bbbbbbbbbbbbbbbbbbbbbbbbbbbb check table t1; drop table t1; +# +# Bug#14896: Comparison with a key in a partial index over mb chararacter field +# + +SET NAMES utf8; +CREATE TABLE t1 (id int PRIMARY KEY, + a varchar(16) collate utf8_unicode_ci NOT NULL default '', + b int, + f varchar(128) default 'XXX', + INDEX (a(4)) +) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +INSERT INTO t1(id, a, b) VALUES + (1, 'cccc', 50), (2, 'cccc', 70), (3, 'cccc', 30), + (4, 'cccc', 30), (5, 'cccc', 20), (6, 'bbbbbb', 40), + (7, 'dddd', 30), (8, 'aaaa', 10), (9, 'aaaa', 50), + (10, 'eeeee', 40), (11, 'bbbbbb', 60); + +SELECT id, a, b FROM t1; + +SELECT id, a, b FROM t1 WHERE a BETWEEN 'aaaa' AND 'bbbbbb'; + +SELECT id, a FROM t1 WHERE a='bbbbbb'; +SELECT id, a FROM t1 WHERE a='bbbbbb' ORDER BY b; + +DROP TABLE t1; + # End of 4.1 tests diff --git a/sql/field.cc b/sql/field.cc index a64eaad7308..6eea006fcc8 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -5072,17 +5072,6 @@ int Field_string::cmp(const char *a_ptr, const char *b_ptr) { uint a_len, b_len; - if (field_charset->strxfrm_multiply > 1) - { - /* - We have to remove end space to be able to compare multi-byte-characters - like in latin_de 'ae' and 0xe4 - */ - return field_charset->coll->strnncollsp(field_charset, - (const uchar*) a_ptr, field_length, - (const uchar*) b_ptr, - field_length); - } if (field_charset->mbmaxlen != 1) { uint char_len= field_length/field_charset->mbmaxlen; @@ -5091,8 +5080,13 @@ int Field_string::cmp(const char *a_ptr, const char *b_ptr) } else a_len= b_len= field_length; - return my_strnncoll(field_charset,(const uchar*) a_ptr, a_len, - (const uchar*) b_ptr, b_len); + /* + We have to remove end space to be able to compare multi-byte-characters + like in latin_de 'ae' and 0xe4 + */ + return field_charset->coll->strnncollsp(field_charset, + (const uchar*) a_ptr, a_len, + (const uchar*) b_ptr, b_len); } From 5eead315e038d705b69eb1ca354398b38b533441 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 13 Jun 2006 22:38:00 -0700 Subject: [PATCH 38/65] Post-review corrections of the fix for bug #18206. mysql-test/r/func_group.result: Added another test case for bug #18206. mysql-test/t/func_group.test: Added another test case for bug #18206. --- mysql-test/r/func_group.result | 13 +++++++++++++ mysql-test/t/func_group.test | 9 +++++++++ sql/opt_sum.cc | 6 ++++-- 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/mysql-test/r/func_group.result b/mysql-test/r/func_group.result index ffa68f279f3..f16fb01d0df 100644 --- a/mysql-test/r/func_group.result +++ b/mysql-test/r/func_group.result @@ -940,3 +940,16 @@ EXPLAIN SELECT MAX(b) FROM t1; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 ALL NULL NULL NULL NULL 2 DROP TABLE t1; +CREATE TABLE t1 (id int , b varchar(512), INDEX(b(250))) COLLATE latin1_bin; +Warnings: +Warning 1246 Converting column 'b' from CHAR to TEXT +INSERT INTO t1 VALUES +(1,CONCAT(REPEAT('_', 250), "qq")), (1,CONCAT(REPEAT('_', 250), "zz")), +(1,CONCAT(REPEAT('_', 250), "aa")), (1,CONCAT(REPEAT('_', 250), "ff")); +SELECT MAX(b) FROM t1; +MAX(b) +__________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________zz +EXPLAIN SELECT MAX(b) FROM t1; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 4 +DROP TABLE t1; diff --git a/mysql-test/t/func_group.test b/mysql-test/t/func_group.test index f8a3ed0f25e..7133b96f868 100644 --- a/mysql-test/t/func_group.test +++ b/mysql-test/t/func_group.test @@ -617,4 +617,13 @@ SELECT MAX(b) FROM t1; EXPLAIN SELECT MAX(b) FROM t1; DROP TABLE t1; +CREATE TABLE t1 (id int , b varchar(512), INDEX(b(250))) COLLATE latin1_bin; +INSERT INTO t1 VALUES + (1,CONCAT(REPEAT('_', 250), "qq")), (1,CONCAT(REPEAT('_', 250), "zz")), + (1,CONCAT(REPEAT('_', 250), "aa")), (1,CONCAT(REPEAT('_', 250), "ff")); + +SELECT MAX(b) FROM t1; +EXPLAIN SELECT MAX(b) FROM t1; +DROP TABLE t1; + # End of 4.1 tests diff --git a/sql/opt_sum.cc b/sql/opt_sum.cc index 97e271121d3..21c637f4faf 100644 --- a/sql/opt_sum.cc +++ b/sql/opt_sum.cc @@ -685,8 +685,10 @@ static bool find_key_for_maxmin(bool max_fl, TABLE_REF *ref, if (!(table->file->index_flags(idx, jdx, 0) & HA_READ_ORDER)) return 0; - /* Check whether the index component is partial */ - if (part->length < table->field[part->fieldnr-1]->pack_length()) + /* Check whether the index component is partial */ + Field *part_field= table->field[part->fieldnr-1]; + if ((part_field->flags & BLOB_FLAG) || + part->length < part_field->key_length()) break; if (field->eq(part->field)) From eff0000226253bf87d03f1f465c52bee09a679c8 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 14 Jun 2006 09:12:37 +0200 Subject: [PATCH 39/65] change test targets to be more modular. include unit tests into make test --- Makefile.am | 45 +++++++++++++++++++++++++-------------------- 1 file changed, 25 insertions(+), 20 deletions(-) diff --git a/Makefile.am b/Makefile.am index dfa6c7f0f07..8a575b3c365 100644 --- a/Makefile.am +++ b/Makefile.am @@ -101,7 +101,9 @@ dist-hook: tags: support-files/build-tags -.PHONY: init-db bin-dist + +.PHONY: init-db bin-dist test test-full test-ps test-nr \ + test-ns test-pr test-unit # Target 'test' will run the regression test suite using the built server. # @@ -111,29 +113,34 @@ tags: # will then calculate the various port numbers it needs from this, # making sure each user use different ports. -test: - cd mysql-test ; \ - ./mysql-test-run.pl --mysqld=--binlog-format=statement && \ - ./mysql-test-run.pl --ps-protocol --mysqld=--binlog-format=row +test-unit: + cd unittest && $(MAKE) test -test-full: +test-ps: cd mysql-test ; \ - ./mysql-test-run.pl --mysqld=--binlog-format=statement && \ - ./mysql-test-run.pl --ps-protocol --mysqld=--binlog-format=statement && \ - ./mysql-test-run.pl --mysqld=--binlog-format=row && \ - ./mysql-test-run.pl --ps-protocol --mysqld=--binlog-format=row + ./mysql-test-run.pl $(force) --ps-protocol --mysqld=--binlog-format=statement + +test-nr: + cd mysql-test ; \ + ./mysql-test-run.pl $(force) --mysqld=--binlog-format=row + +test-pr: + cd mysql-test ; \ + ./mysql-test-run.pl $(force) --ps-protocol --mysqld=--binlog-format=row + +test-ns: + cd mysql-test ; \ + ./mysql-test-run.pl $(force) --mysqld=--binlog-format=statement + +test: test-unit test-ns test-pr + +test-full: test test-nr test-ps test-force: - cd mysql-test ; \ - ./mysql-test-run.pl --force --mysqld=--binlog-format=statement && \ - ./mysql-test-run.pl --ps-protocol --force --mysqld=--binlog-format=row + $(MAKE) force=--force test test-force-full: - cd mysql-test ; \ - ./mysql-test-run.pl --force --mysqld=--binlog-format=statement && \ - ./mysql-test-run.pl --force --ps-protocol --mysqld=--binlog-format=statement && \ - ./mysql-test-run.pl --force --mysqld=--binlog-format=row && \ - ./mysql-test-run.pl --force --ps-protocol --mysqld=--binlog-format=row + $(MAKE) force=--force test-full # Keep these for a while test-pl: test @@ -141,8 +148,6 @@ test-full-pl: test-full test-force-pl: test-force test-force-full-pl: test-force-full - - # Don't update the files from bitkeeper %::SCCS/s.% From 115cb627b0698919da5aff221333af28fc6d0f00 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 14 Jun 2006 09:19:02 +0200 Subject: [PATCH 40/65] Bug#14708: Inconsistent treatment of NULLs in LEFT JOINed FULLTEXT matching without index Don't rely on table->null_row when no index is used - it may be a multi-table --- mysql-test/r/fulltext_left_join.result | 17 +++++++++++++++++ mysql-test/t/fulltext_left_join.test | 15 ++++++++++++++- sql/item_func.cc | 2 +- 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/mysql-test/r/fulltext_left_join.result b/mysql-test/r/fulltext_left_join.result index f3dad290525..68a424fa3a5 100644 --- a/mysql-test/r/fulltext_left_join.result +++ b/mysql-test/r/fulltext_left_join.result @@ -50,3 +50,20 @@ venue_id venue_text dt name entity_id 1 a1 2003-05-23 19:30:00 aberdeen town hall 1 NULL a2 2003-05-23 19:30:00 NULL NULL drop table t1,t2; +create table t1 (id int not null primary key, d char(200) not null, e char(200)); +insert into t1 values (1, 'aword', null), (2, 'aword', 'bword'), (3, 'bword', null), (4, 'bword', 'aword'), (5, 'aword and bword', null); +select * from t1 where match(d, e) against ('+aword +bword' in boolean mode); +id d e +2 aword bword +4 bword aword +5 aword and bword NULL +create table t2 (m_id int not null, f char(200), key (m_id)); +insert into t2 values (1, 'bword'), (3, 'aword'), (5, ''); +select * from t1 left join t2 on m_id = id where match(d, e, f) against ('+aword +bword' in boolean mode); +id d e m_id f +1 aword NULL 1 bword +2 aword bword NULL NULL +3 bword NULL 3 aword +4 bword aword NULL NULL +5 aword and bword NULL 5 +drop table t1,t2; diff --git a/mysql-test/t/fulltext_left_join.test b/mysql-test/t/fulltext_left_join.test index 3bb1f0b7309..7c22f49ed8c 100644 --- a/mysql-test/t/fulltext_left_join.test +++ b/mysql-test/t/fulltext_left_join.test @@ -32,7 +32,7 @@ select match(t1.texte,t1.sujet,t1.motsclefs) against('droit' IN BOOLEAN MODE) drop table t1, t2; # -# Bug #484, reported by Stephen Brandon +# BUG#484, reported by Stephen Brandon # create table t1 (venue_id int(11) default null, venue_text varchar(255) default null, dt datetime default null) engine=myisam; @@ -45,4 +45,17 @@ select * from t1 left join t2 on (venue_id = entity_id and match(name) against(' select * from t1 left join t2 on (venue_id = entity_id and match(name) against('aberdeen')) where dt = '2003-05-23 19:30:00'; drop table t1,t2; +# +# BUG#14708 +# Inconsistent treatment of NULLs in LEFT JOINed FULLTEXT matching without index +# + +create table t1 (id int not null primary key, d char(200) not null, e char(200)); +insert into t1 values (1, 'aword', null), (2, 'aword', 'bword'), (3, 'bword', null), (4, 'bword', 'aword'), (5, 'aword and bword', null); +select * from t1 where match(d, e) against ('+aword +bword' in boolean mode); +create table t2 (m_id int not null, f char(200), key (m_id)); +insert into t2 values (1, 'bword'), (3, 'aword'), (5, ''); +select * from t1 left join t2 on m_id = id where match(d, e, f) against ('+aword +bword' in boolean mode); +drop table t1,t2; + # End of 4.1 tests diff --git a/sql/item_func.cc b/sql/item_func.cc index 3b633295f20..051238843fa 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -4516,7 +4516,7 @@ double Item_func_match::val_real() if (ft_handler == NULL) DBUG_RETURN(-1.0); - if (table->null_row) /* NULL row from an outer join */ + if (key != NO_SUCH_KEY && table->null_row) /* NULL row from an outer join */ DBUG_RETURN(0.0); if (join_key) From 19d55e396eb73af5d630045554e49a9ebbadae69 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 14 Jun 2006 10:04:30 +0200 Subject: [PATCH 41/65] move --disable_warnings where it belongs --- mysql-test/t/ps_1general.test | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mysql-test/t/ps_1general.test b/mysql-test/t/ps_1general.test index 0b9a46ad4d3..76da296c0b0 100644 --- a/mysql-test/t/ps_1general.test +++ b/mysql-test/t/ps_1general.test @@ -320,16 +320,16 @@ prepare stmt4 from ' show status like ''Threads_running'' '; execute stmt4; prepare stmt4 from ' show variables like ''sql_mode'' '; execute stmt4; -prepare stmt4 from ' show engine bdb logs '; # The output depends on the bdb being enabled and on the history # history (actions of the bdb engine). # That is the reason why, we switch the output here off. # (The real output will be tested in ps_6bdb.test) --disable_warnings +prepare stmt4 from ' show engine bdb logs '; +--enable_warnings --disable_result_log execute stmt4; --enable_result_log ---enable_warnings prepare stmt4 from ' show grants for user '; --error 1295 prepare stmt4 from ' show create table t2 '; From e9bfc415148a9c3cb113a0f7f785c3d9578610dd Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 14 Jun 2006 13:58:51 +0200 Subject: [PATCH 42/65] ndb - bug#19285 : document and check what blob ops are allowed ndb/src/ndbapi/ndberror.c: distinguish blob method errors: 4265-wrong state 4275-wrong op type/lockmode ndb/src/ndbapi/NdbBlobImpl.hpp: distinguish blob method errors: 4265-wrong state 4275-wrong op type/lockmode fix 4269 -> 4270 ndb/test/ndbapi/testBlobs.cpp: test lock upgrade, test 4275 errors ndb/include/ndbapi/NdbScanOperation.hpp: fix comment ndb/include/ndbapi/NdbBlob.hpp: upgrade LM_CommittedRead to LM_Read check if write allowed (new error 4275) dont invalidate blob state on error (just general principle) ndb/src/ndbapi/NdbBlob.cpp: upgrade LM_CommittedRead to LM_Read check if write allowed (new error 4275) dont invalidate blob state on error (just general principle) --- ndb/include/ndbapi/NdbBlob.hpp | 50 ++++++--- ndb/include/ndbapi/NdbScanOperation.hpp | 2 +- ndb/src/ndbapi/NdbBlob.cpp | 56 +++++++--- ndb/src/ndbapi/NdbBlobImpl.hpp | 6 +- ndb/src/ndbapi/ndberror.c | 5 +- ndb/test/ndbapi/testBlobs.cpp | 131 ++++++++++++++++-------- 6 files changed, 180 insertions(+), 70 deletions(-) diff --git a/ndb/include/ndbapi/NdbBlob.hpp b/ndb/include/ndbapi/NdbBlob.hpp index 76aa8e710ac..6d2c493d1ed 100644 --- a/ndb/include/ndbapi/NdbBlob.hpp +++ b/ndb/include/ndbapi/NdbBlob.hpp @@ -74,19 +74,41 @@ class NdbColumnImpl; * NdbBlob methods return -1 on error and 0 on success, and use output * parameters when necessary. * - * Operation types: - * - insertTuple must use setValue if blob column is non-nullable - * - readTuple with exclusive lock can also update existing value - * - updateTuple can overwrite with setValue or update existing value - * - writeTuple always overwrites and must use setValue if non-nullable + * Usage notes for different operation types: + * + * - insertTuple must use setValue if blob attribute is non-nullable + * + * - readTuple or scan readTuples with lock mode LM_CommittedRead is + * automatically upgraded to lock mode LM_Read if any blob attributes + * are accessed (to guarantee consistent view) + * + * - readTuple (with any lock mode) can only read blob value + * + * - updateTuple can either overwrite existing value with setValue or + * update it in active phase + * + * - writeTuple always overwrites blob value and must use setValue if + * blob attribute is non-nullable + * * - deleteTuple creates implicit non-accessible blob handles - * - scan with exclusive lock can also update existing value - * - scan "lock takeover" update op must do its own getBlobHandle + * + * - scan readTuples (any lock mode) can use its blob handles only + * to read blob value + * + * - scan readTuples with lock mode LM_Exclusive can update row and blob + * value using updateCurrentTuple, where the operation returned must + * create its own blob handles explicitly + * + * - scan readTuples with lock mode LM_Exclusive can delete row (and + * therefore blob values) using deleteCurrentTuple, which creates + * implicit non-accessible blob handles + * + * - the operation returned by lockCurrentTuple cannot update blob value * * Bugs / limitations: - * - lock mode upgrade should be handled automatically - * - lock mode vs allowed operation is not checked + * * - too many pending blob ops can blow up i/o buffers + * * - table and its blob part tables are not created atomically */ #ifndef DOXYGEN_SHOULD_SKIP_INTERNAL @@ -194,6 +216,9 @@ public: /** * Return error object. The error may be blob specific (below) or may * be copied from a failed implicit operation. + * + * The error code is copied back to the operation unless the operation + * already has a non-zero error code. */ const NdbError& getNdbError() const; /** @@ -290,6 +315,7 @@ private: bool isWriteOp(); bool isDeleteOp(); bool isScanOp(); + bool isReadOnlyOp(); bool isTakeOverOp(); // computations Uint32 getPartNumber(Uint64 pos); @@ -323,9 +349,9 @@ private: int preCommit(); int atNextResult(); // errors - void setErrorCode(int anErrorCode, bool invalidFlag = true); - void setErrorCode(NdbOperation* anOp, bool invalidFlag = true); - void setErrorCode(NdbTransaction* aCon, bool invalidFlag = true); + void setErrorCode(int anErrorCode, bool invalidFlag = false); + void setErrorCode(NdbOperation* anOp, bool invalidFlag = false); + void setErrorCode(NdbTransaction* aCon, bool invalidFlag = false); #ifdef VM_TRACE int getOperationType() const; friend class NdbOut& operator<<(NdbOut&, const NdbBlob&); diff --git a/ndb/include/ndbapi/NdbScanOperation.hpp b/ndb/include/ndbapi/NdbScanOperation.hpp index 4a8425852b9..5581e830f82 100644 --- a/ndb/include/ndbapi/NdbScanOperation.hpp +++ b/ndb/include/ndbapi/NdbScanOperation.hpp @@ -41,7 +41,7 @@ public: * readTuples. */ enum ScanFlag { - SF_TupScan = (1 << 16), // scan TUP - only LM_CommittedRead + SF_TupScan = (1 << 16), // scan TUP SF_OrderBy = (1 << 24), // index scan in order SF_Descending = (2 << 24), // index scan in descending order SF_ReadRangeNo = (4 << 24), // enable @ref get_range_no diff --git a/ndb/src/ndbapi/NdbBlob.cpp b/ndb/src/ndbapi/NdbBlob.cpp index 7d2793047c2..3440c6416cb 100644 --- a/ndb/src/ndbapi/NdbBlob.cpp +++ b/ndb/src/ndbapi/NdbBlob.cpp @@ -265,6 +265,16 @@ NdbBlob::isScanOp() theNdbOp->theOperationType == NdbOperation::OpenRangeScanRequest; } +inline bool +NdbBlob::isReadOnlyOp() +{ + return ! ( + theNdbOp->theOperationType == NdbOperation::InsertRequest || + theNdbOp->theOperationType == NdbOperation::UpdateRequest || + theNdbOp->theOperationType == NdbOperation::WriteRequest + ); +} + inline bool NdbBlob::isTakeOverOp() { @@ -438,12 +448,12 @@ NdbBlob::getValue(void* data, Uint32 bytes) { DBUG_ENTER("NdbBlob::getValue"); DBUG_PRINT("info", ("data=%p bytes=%u", data, bytes)); - if (theGetFlag || theState != Prepared) { - setErrorCode(NdbBlobImpl::ErrState); + if (! isReadOp() && ! isScanOp()) { + setErrorCode(NdbBlobImpl::ErrCompat); DBUG_RETURN(-1); } - if (! isReadOp() && ! isScanOp()) { - setErrorCode(NdbBlobImpl::ErrUsage); + if (theGetFlag || theState != Prepared) { + setErrorCode(NdbBlobImpl::ErrState); DBUG_RETURN(-1); } if (data == NULL && bytes != 0) { @@ -461,12 +471,12 @@ NdbBlob::setValue(const void* data, Uint32 bytes) { DBUG_ENTER("NdbBlob::setValue"); DBUG_PRINT("info", ("data=%p bytes=%u", data, bytes)); - if (theSetFlag || theState != Prepared) { - setErrorCode(NdbBlobImpl::ErrState); + if (isReadOnlyOp()) { + setErrorCode(NdbBlobImpl::ErrCompat); DBUG_RETURN(-1); } - if (! isInsertOp() && ! isUpdateOp() && ! isWriteOp()) { - setErrorCode(NdbBlobImpl::ErrUsage); + if (theSetFlag || theState != Prepared) { + setErrorCode(NdbBlobImpl::ErrState); DBUG_RETURN(-1); } if (data == NULL && bytes != 0) { @@ -533,6 +543,10 @@ int NdbBlob::setNull() { DBUG_ENTER("NdbBlob::setNull"); + if (isReadOnlyOp()) { + setErrorCode(NdbBlobImpl::ErrCompat); + DBUG_RETURN(-1); + } if (theNullFlag == -1) { if (theState == Prepared) { DBUG_RETURN(setValue(0, 0)); @@ -571,6 +585,10 @@ NdbBlob::truncate(Uint64 length) { DBUG_ENTER("NdbBlob::truncate"); DBUG_PRINT("info", ("length=%llu", length)); + if (isReadOnlyOp()) { + setErrorCode(NdbBlobImpl::ErrCompat); + DBUG_RETURN(-1); + } if (theNullFlag == -1) { setErrorCode(NdbBlobImpl::ErrState); DBUG_RETURN(-1); @@ -628,12 +646,14 @@ NdbBlob::setPos(Uint64 pos) int NdbBlob::readData(void* data, Uint32& bytes) { + DBUG_ENTER("NdbBlob::readData"); if (theState != Active) { setErrorCode(NdbBlobImpl::ErrState); - return -1; + DBUG_RETURN(-1); } char* buf = static_cast(data); - return readDataPrivate(buf, bytes); + int ret = readDataPrivate(buf, bytes); + DBUG_RETURN(ret); } int @@ -722,12 +742,18 @@ NdbBlob::readDataPrivate(char* buf, Uint32& bytes) int NdbBlob::writeData(const void* data, Uint32 bytes) { + DBUG_ENTER("NdbBlob::writeData"); + if (isReadOnlyOp()) { + setErrorCode(NdbBlobImpl::ErrCompat); + DBUG_RETURN(-1); + } if (theState != Active) { setErrorCode(NdbBlobImpl::ErrState); - return -1; + DBUG_RETURN(-1); } const char* buf = static_cast(data); - return writeDataPrivate(buf, bytes); + int ret = writeDataPrivate(buf, bytes); + DBUG_RETURN(0); } int @@ -1130,6 +1156,9 @@ NdbBlob::atPrepare(NdbTransaction* aCon, NdbOperation* anOp, const NdbColumnImpl } } if (isReadOp()) { + // upgrade lock mode + if (theNdbOp->theLockMode == NdbOperation::LM_CommittedRead) + theNdbOp->theLockMode = NdbOperation::LM_Read; // add read of head+inline in this op if (getHeadInlineValue(theNdbOp) == -1) DBUG_RETURN(-1); @@ -1148,6 +1177,9 @@ NdbBlob::atPrepare(NdbTransaction* aCon, NdbOperation* anOp, const NdbColumnImpl supportedOp = true; } if (isScanOp()) { + // upgrade lock mode + if (theNdbOp->theLockMode == NdbOperation::LM_CommittedRead) + theNdbOp->theLockMode = NdbOperation::LM_Read; // add read of head+inline in this op if (getHeadInlineValue(theNdbOp) == -1) DBUG_RETURN(-1); diff --git a/ndb/src/ndbapi/NdbBlobImpl.hpp b/ndb/src/ndbapi/NdbBlobImpl.hpp index 0030e910c52..b56aabfd84e 100644 --- a/ndb/src/ndbapi/NdbBlobImpl.hpp +++ b/ndb/src/ndbapi/NdbBlobImpl.hpp @@ -24,7 +24,7 @@ public: STATIC_CONST( ErrTable = 4263 ); // "Invalid usage of blob attribute" STATIC_CONST( ErrUsage = 4264 ); - // "Method is not valid in current blob state" + // "The blob method is not valid in current blob state" STATIC_CONST( ErrState = 4265 ); // "Invalid blob seek position" STATIC_CONST( ErrSeek = 4266 ); @@ -33,7 +33,9 @@ public: // "Error in blob head update forced rollback of transaction" STATIC_CONST( ErrAbort = 4268 ); // "Unknown blob error" - STATIC_CONST( ErrUnknown = 4269 ); + STATIC_CONST( ErrUnknown = 4270 ); + // "The blob method is incompatible with operation type or lock mode" + STATIC_CONST( ErrCompat = 4275 ); }; #endif diff --git a/ndb/src/ndbapi/ndberror.c b/ndb/src/ndbapi/ndberror.c index a2b96b32630..6c9931ba21c 100644 --- a/ndb/src/ndbapi/ndberror.c +++ b/ndb/src/ndbapi/ndberror.c @@ -513,14 +513,15 @@ ErrorBundle ErrorCodes[] = { { 4262, UD, "NdbScanFilter: Condition is out of bounds"}, { 4263, IE, "Invalid blob attributes or invalid blob parts table" }, { 4264, AE, "Invalid usage of blob attribute" }, - { 4265, AE, "Method is not valid in current blob state" }, + { 4265, AE, "The blob method is not valid in current blob state" }, { 4266, AE, "Invalid blob seek position" }, { 4267, IE, "Corrupted blob value" }, { 4268, IE, "Error in blob head update forced rollback of transaction" }, { 4269, IE, "No connection to ndb management server" }, { 4270, IE, "Unknown blob error" }, { 4335, AE, "Only one autoincrement column allowed per table. Having a table without primary key uses an autoincremented hidden key, i.e. a table without a primary key can not have an autoincremented column" }, - { 4271, AE, "Invalid index object, not retrieved via getIndex()" } + { 4271, AE, "Invalid index object, not retrieved via getIndex()" }, + { 4275, IE, "The blob method is incompatible with operation type or lock mode" } }; static diff --git a/ndb/test/ndbapi/testBlobs.cpp b/ndb/test/ndbapi/testBlobs.cpp index c2a06c9ffa0..fae3a662ff9 100644 --- a/ndb/test/ndbapi/testBlobs.cpp +++ b/ndb/test/ndbapi/testBlobs.cpp @@ -226,7 +226,7 @@ dropTable() { NdbDictionary::Table tab(g_opt.m_tname); if (g_dic->getTable(g_opt.m_tname) != 0) - CHK(g_dic->dropTable(tab) == 0); + CHK(g_dic->dropTable(g_opt.m_tname) == 0); return 0; } @@ -297,13 +297,15 @@ createTable() struct Bval { char* m_val; unsigned m_len; - char* m_buf; + char* m_buf; // read/write buffer unsigned m_buflen; + int m_error_code; // for testing expected error code Bval() : m_val(0), m_len(0), - m_buf(0), // read/write buffer - m_buflen(0) + m_buf(0), + m_buflen(0), + m_error_code(0) {} ~Bval() { delete [] m_val; delete [] m_buf; } void alloc(unsigned buflen) { @@ -459,19 +461,23 @@ getBlobLength(NdbBlob* h, unsigned& len) // setValue / getValue static int -setBlobValue(NdbBlob* h, const Bval& v) +setBlobValue(NdbBlob* h, const Bval& v, int error_code = 0) { bool null = (v.m_val == 0); bool isNull; unsigned len; DBG("setValue " << h->getColumn()->getName() << " len=" << v.m_len << " null=" << null); if (null) { - CHK(h->setNull() == 0); + CHK(h->setNull() == 0 || h->getNdbError().code == error_code); + if (error_code) + return 0; isNull = false; CHK(h->getNull(isNull) == 0 && isNull == true); CHK(getBlobLength(h, len) == 0 && len == 0); } else { - CHK(h->setValue(v.m_val, v.m_len) == 0); + CHK(h->setValue(v.m_val, v.m_len) == 0 || h->getNdbError().code == error_code); + if (error_code) + return 0; CHK(h->getNull(isNull) == 0 && isNull == false); CHK(getBlobLength(h, len) == 0 && len == v.m_len); } @@ -479,11 +485,11 @@ setBlobValue(NdbBlob* h, const Bval& v) } static int -setBlobValue(const Tup& tup) +setBlobValue(const Tup& tup, int error_code = 0) { - CHK(setBlobValue(g_bh1, tup.m_blob1) == 0); + CHK(setBlobValue(g_bh1, tup.m_blob1, error_code) == 0); if (! g_opt.m_oneblob) - CHK(setBlobValue(g_bh2, tup.m_blob2) == 0); + CHK(setBlobValue(g_bh2, tup.m_blob2, error_code) == 0); return 0; } @@ -543,13 +549,18 @@ writeBlobData(NdbBlob* h, const Bval& v) bool isNull; unsigned len; DBG("write " << h->getColumn()->getName() << " len=" << v.m_len << " null=" << null); + int error_code = v.m_error_code; if (null) { - CHK(h->setNull() == 0); + CHK(h->setNull() == 0 || h->getNdbError().code == error_code); + if (error_code) + return 0; isNull = false; CHK(h->getNull(isNull) == 0 && isNull == true); CHK(getBlobLength(h, len) == 0 && len == 0); } else { - CHK(h->truncate(v.m_len) == 0); + CHK(h->truncate(v.m_len) == 0 || h->getNdbError().code == error_code); + if (error_code) + return 0; unsigned n = 0; do { unsigned m = g_opt.m_full ? v.m_len : urandom(v.m_len + 1); @@ -568,11 +579,14 @@ writeBlobData(NdbBlob* h, const Bval& v) } static int -writeBlobData(const Tup& tup) +writeBlobData(Tup& tup, int error_code = 0) { + tup.m_blob1.m_error_code = error_code; CHK(writeBlobData(g_bh1, tup.m_blob1) == 0); - if (! g_opt.m_oneblob) + if (! g_opt.m_oneblob) { + tup.m_blob2.m_error_code = error_code; CHK(writeBlobData(g_bh2, tup.m_blob2) == 0); + } return 0; } @@ -635,19 +649,20 @@ blobWriteHook(NdbBlob* h, void* arg) } static int -setBlobWriteHook(NdbBlob* h, Bval& v) +setBlobWriteHook(NdbBlob* h, Bval& v, int error_code = 0) { DBG("setBlobWriteHook"); + v.m_error_code = error_code; CHK(h->setActiveHook(blobWriteHook, &v) == 0); return 0; } static int -setBlobWriteHook(Tup& tup) +setBlobWriteHook(Tup& tup, int error_code = 0) { - CHK(setBlobWriteHook(g_bh1, tup.m_blob1) == 0); + CHK(setBlobWriteHook(g_bh1, tup.m_blob1, error_code) == 0); if (! g_opt.m_oneblob) - CHK(setBlobWriteHook(g_bh2, tup.m_blob2) == 0); + CHK(setBlobWriteHook(g_bh2, tup.m_blob2, error_code) == 0); return 0; } @@ -869,7 +884,10 @@ readPk(int style) DBG("readPk pk1=" << hex << tup.m_pk1); CHK((g_con = g_ndb->startTransaction()) != 0); CHK((g_opr = g_con->getNdbOperation(g_opt.m_tname)) != 0); - CHK(g_opr->readTuple() == 0); + if (urandom(2) == 0) + CHK(g_opr->readTuple() == 0); + else + CHK(g_opr->readTuple(NdbOperation::LM_CommittedRead) == 0); CHK(g_opr->equal("PK1", tup.m_pk1) == 0); if (g_opt.m_pk2len != 0) CHK(g_opr->equal("PK2", tup.m_pk2) == 0); @@ -883,6 +901,8 @@ readPk(int style) CHK(readBlobData(tup) == 0); } CHK(g_con->execute(Commit) == 0); + // verify lock mode upgrade + CHK(g_opr->getLockMode() == NdbOperation::LM_Read); if (style == 0 || style == 1) { CHK(verifyBlobValue(tup) == 0); } @@ -900,23 +920,40 @@ updatePk(int style) for (unsigned k = 0; k < g_opt.m_rows; k++) { Tup& tup = g_tups[k]; DBG("updatePk pk1=" << hex << tup.m_pk1); - CHK((g_con = g_ndb->startTransaction()) != 0); - CHK((g_opr = g_con->getNdbOperation(g_opt.m_tname)) != 0); - CHK(g_opr->updateTuple() == 0); - CHK(g_opr->equal("PK1", tup.m_pk1) == 0); - if (g_opt.m_pk2len != 0) - CHK(g_opr->equal("PK2", tup.m_pk2) == 0); - CHK(getBlobHandles(g_opr) == 0); - if (style == 0) { - CHK(setBlobValue(tup) == 0); - } else if (style == 1) { - CHK(setBlobWriteHook(tup) == 0); - } else { - CHK(g_con->execute(NoCommit) == 0); - CHK(writeBlobData(tup) == 0); + while (1) { + int mode = urandom(3); + int error_code = mode == 0 ? 0 : 4275; + CHK((g_con = g_ndb->startTransaction()) != 0); + CHK((g_opr = g_con->getNdbOperation(g_opt.m_tname)) != 0); + if (mode == 0) { + DBG("using updateTuple"); + CHK(g_opr->updateTuple() == 0); + } else if (mode == 1) { + DBG("using readTuple exclusive"); + CHK(g_opr->readTuple(NdbOperation::LM_Exclusive) == 0); + } else { + DBG("using readTuple - will fail and retry"); + CHK(g_opr->readTuple() == 0); + } + CHK(g_opr->equal("PK1", tup.m_pk1) == 0); + if (g_opt.m_pk2len != 0) + CHK(g_opr->equal("PK2", tup.m_pk2) == 0); + CHK(getBlobHandles(g_opr) == 0); + if (style == 0) { + CHK(setBlobValue(tup, error_code) == 0); + } else if (style == 1) { + CHK(setBlobWriteHook(tup, error_code) == 0); + } else { + CHK(g_con->execute(NoCommit) == 0); + CHK(writeBlobData(tup, error_code) == 0); + } + if (error_code == 0) { + CHK(g_con->execute(Commit) == 0); + g_ndb->closeTransaction(g_con); + break; + } + g_ndb->closeTransaction(g_con); } - CHK(g_con->execute(Commit) == 0); - g_ndb->closeTransaction(g_con); g_opr = 0; g_con = 0; tup.m_exists = true; @@ -1002,7 +1039,10 @@ readIdx(int style) DBG("readIdx pk1=" << hex << tup.m_pk1); CHK((g_con = g_ndb->startTransaction()) != 0); CHK((g_opx = g_con->getNdbIndexOperation(g_opt.m_x1name, g_opt.m_tname)) != 0); - CHK(g_opx->readTuple() == 0); + if (urandom(2) == 0) + CHK(g_opx->readTuple() == 0); + else + CHK(g_opx->readTuple(NdbOperation::LM_CommittedRead) == 0); CHK(g_opx->equal("PK2", tup.m_pk2) == 0); CHK(getBlobHandles(g_opx) == 0); if (style == 0) { @@ -1014,6 +1054,8 @@ readIdx(int style) CHK(readBlobData(tup) == 0); } CHK(g_con->execute(Commit) == 0); + // verify lock mode upgrade (already done by NdbIndexOperation) + CHK(g_opx->getLockMode() == NdbOperation::LM_Read); if (style == 0 || style == 1) { CHK(verifyBlobValue(tup) == 0); } @@ -1031,6 +1073,7 @@ updateIdx(int style) for (unsigned k = 0; k < g_opt.m_rows; k++) { Tup& tup = g_tups[k]; DBG("updateIdx pk1=" << hex << tup.m_pk1); + // skip 4275 testing CHK((g_con = g_ndb->startTransaction()) != 0); CHK((g_opx = g_con->getNdbIndexOperation(g_opt.m_x1name, g_opt.m_tname)) != 0); CHK(g_opx->updateTuple() == 0); @@ -1128,7 +1171,10 @@ readScan(int style, bool idx) } else { CHK((g_ops = g_con->getNdbIndexScanOperation(g_opt.m_x2name, g_opt.m_tname)) != 0); } - CHK(g_ops->readTuples(NdbScanOperation::LM_Read) == 0); + if (urandom(2) == 0) + CHK(g_ops->readTuples(NdbOperation::LM_Read) == 0); + else + CHK(g_ops->readTuples(NdbOperation::LM_CommittedRead) == 0); CHK(g_ops->getValue("PK1", (char*)&tup.m_pk1) != 0); if (g_opt.m_pk2len != 0) CHK(g_ops->getValue("PK2", tup.m_pk2) != 0); @@ -1139,6 +1185,8 @@ readScan(int style, bool idx) CHK(setBlobReadHook(tup) == 0); } CHK(g_con->execute(NoCommit) == 0); + // verify lock mode upgrade + CHK(g_ops->getLockMode() == NdbOperation::LM_Read); unsigned rows = 0; while (1) { int ret; @@ -1180,7 +1228,7 @@ updateScan(int style, bool idx) } else { CHK((g_ops = g_con->getNdbIndexScanOperation(g_opt.m_x2name, g_opt.m_tname)) != 0); } - CHK(g_ops->readTuples(NdbScanOperation::LM_Exclusive) == 0); + CHK(g_ops->readTuples(NdbOperation::LM_Exclusive) == 0); CHK(g_ops->getValue("PK1", (char*)&tup.m_pk1) != 0); if (g_opt.m_pk2len != 0) CHK(g_ops->getValue("PK2", tup.m_pk2) != 0); @@ -1199,6 +1247,7 @@ updateScan(int style, bool idx) // calculate new blob values calcBval(g_tups[k], false); tup.copyfrom(g_tups[k]); + // cannot do 4275 testing, scan op error code controls execution CHK((g_opr = g_ops->updateCurrentTuple()) != 0); CHK(getBlobHandles(g_opr) == 0); if (style == 0) { @@ -1232,7 +1281,7 @@ deleteScan(bool idx) } else { CHK((g_ops = g_con->getNdbIndexScanOperation(g_opt.m_x2name, g_opt.m_tname)) != 0); } - CHK(g_ops->readTuples(NdbScanOperation::LM_Exclusive) == 0); + CHK(g_ops->readTuples(NdbOperation::LM_Exclusive) == 0); CHK(g_ops->getValue("PK1", (char*)&tup.m_pk1) != 0); if (g_opt.m_pk2len != 0) CHK(g_ops->getValue("PK2", tup.m_pk2) != 0); @@ -1651,7 +1700,7 @@ testperf() char b[20]; CHK((g_con = g_ndb->startTransaction()) != 0); CHK((g_ops = g_con->getNdbScanOperation(tab.getName())) != 0); - CHK(g_ops->readTuples(NdbScanOperation::LM_Read) == 0); + CHK(g_ops->readTuples(NdbOperation::LM_Read) == 0); CHK(g_ops->getValue(cA, (char*)&a) != 0); CHK(g_ops->getValue(cB, b) != 0); CHK(g_con->execute(NoCommit) == 0); @@ -1680,7 +1729,7 @@ testperf() char c[20]; CHK((g_con = g_ndb->startTransaction()) != 0); CHK((g_ops = g_con->getNdbScanOperation(tab.getName())) != 0); - CHK(g_ops->readTuples(NdbScanOperation::LM_Read) == 0); + CHK(g_ops->readTuples(NdbOperation::LM_Read) == 0); CHK(g_ops->getValue(cA, (char*)&a) != 0); CHK((g_bh1 = g_ops->getBlobHandle(cC)) != 0); CHK(g_con->execute(NoCommit) == 0); From 89ce81ceedc465115cafd39f29a05689a6da7205 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 14 Jun 2006 15:57:23 +0300 Subject: [PATCH 43/65] Bug #18895: BIT values cause joins to fail The Field::eq() considered instances of Field_bit that differ only in bit_ptr/bit_ofs equal. This caused equality conditions optimization (build_equal_items_for_cond()) to make bad field substitutions that result in wrong predicates. Field_bit requires an overloaded eq() function that checks the bit_ptr/bit_ofs in addition to Field::eq(). mysql-test/r/select.result: Bug #18895: BIT values cause joins to fail - test case mysql-test/t/select.test: Bug #18895: BIT values cause joins to fail - test case sql/field.h: Bug #18895: BIT values cause joins to fail - eq() method overloaded for Field_bit --- mysql-test/r/select.result | 20 ++++++++++++++++++++ mysql-test/t/select.test | 26 ++++++++++++++++++++++++++ sql/field.h | 9 ++++++++- 3 files changed, 54 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/select.result b/mysql-test/r/select.result index 4c1e64cc1cb..e80e8f9ec41 100644 --- a/mysql-test/r/select.result +++ b/mysql-test/r/select.result @@ -3359,3 +3359,23 @@ id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 range PRIMARY,b b 5 NULL 3 Using where 1 SIMPLE t2 ref c c 5 test.t1.a 2 Using where DROP TABLE t1, t2; +create table t1 ( +a int unsigned not null auto_increment primary key, +b bit not null, +c bit not null +); +create table t2 ( +a int unsigned not null auto_increment primary key, +b bit not null, +c int unsigned not null, +d varchar(50) +); +insert into t1 (b,c) values (0,1), (0,1); +insert into t2 (b,c) values (0,1); +select t1.a, t1.b + 0, t1.c + 0, t2.a, t2.b + 0, t2.c, t2.d +from t1 left outer join t2 on t1.a = t2.c and t2.b <> 1 +where t1.b <> 1 order by t1.a; +a t1.b + 0 t1.c + 0 a t2.b + 0 c d +1 0 1 1 0 1 NULL +2 0 1 NULL NULL NULL NULL +drop table t1,t2; diff --git a/mysql-test/t/select.test b/mysql-test/t/select.test index 4ed7c3d1de9..ef7f9ca9d89 100644 --- a/mysql-test/t/select.test +++ b/mysql-test/t/select.test @@ -2840,3 +2840,29 @@ EXPLAIN SELECT a, c, d, f FROM t1,t2 WHERE a=c AND b BETWEEN 4 AND 6 AND a > 0; DROP TABLE t1, t2; + +# +# Bug #18895: BIT values cause joins to fail +# +create table t1 ( + a int unsigned not null auto_increment primary key, + b bit not null, + c bit not null +); + +create table t2 ( + a int unsigned not null auto_increment primary key, + b bit not null, + c int unsigned not null, + d varchar(50) +); + +insert into t1 (b,c) values (0,1), (0,1); +insert into t2 (b,c) values (0,1); + +-- Row 1 should succeed. Row 2 should fail. Both fail. +select t1.a, t1.b + 0, t1.c + 0, t2.a, t2.b + 0, t2.c, t2.d +from t1 left outer join t2 on t1.a = t2.c and t2.b <> 1 +where t1.b <> 1 order by t1.a; + +drop table t1,t2; diff --git a/sql/field.h b/sql/field.h index f4d27e46877..e7b7aa45c27 100644 --- a/sql/field.h +++ b/sql/field.h @@ -124,7 +124,7 @@ public: static bool type_can_have_key_part(enum_field_types); static enum_field_types field_type_merge(enum_field_types, enum_field_types); static Item_result result_merge_type(enum_field_types); - bool eq(Field *field) + virtual bool eq(Field *field) { return (ptr == field->ptr && null_ptr == field->null_ptr && null_bit == field->null_bit); @@ -1358,6 +1358,13 @@ public: bit_ptr= bit_ptr_arg; bit_ofs= bit_ofs_arg; } + bool eq(Field *field) + { + return (Field::eq(field) && + field->type() == type() && + bit_ptr == ((Field_bit *)field)->bit_ptr && + bit_ofs == ((Field_bit *)field)->bit_ofs); + } }; From 1a9fae81cb5018f8706d49f1032053355ce701c4 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 14 Jun 2006 21:06:02 +0400 Subject: [PATCH 44/65] Better comments in KEY_PART_INFO struct --- sql/structs.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/sql/structs.h b/sql/structs.h index 081ada88bf7..2037496635a 100644 --- a/sql/structs.h +++ b/sql/structs.h @@ -70,7 +70,13 @@ typedef struct st_key_part_info { /* Info about a key part */ Field *field; uint offset; /* offset in record (from 0) */ uint null_offset; /* Offset to null_bit in record */ - uint16 length; /* Length of key_part */ + uint16 length; /* Length of keypart value in bytes */ + /* + Number of bytes required to store the keypart value. This may be + different from the "length" field as it also counts + - possible NULL-flag byte (see HA_KEY_NULL_LENGTH) + - possible HA_KEY_BLOB_LENGTH bytes needed to store actual value length. + */ uint16 store_length; uint16 key_type; uint16 fieldnr; /* Fieldnum in UNIREG */ From 2a07ee7202f1ca198d60f89f3abd1cd88f87d717 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 14 Jun 2006 21:30:49 +0200 Subject: [PATCH 45/65] Bug#19191 Repeated crashes on OpenBSD for ssl test cases - Import patch from yaSSL extra/yassl/src/handshake.cpp: Import patch yassl.diff extra/yassl/src/socket_wrapper.cpp: Import patch yassl.diff --- extra/yassl/src/handshake.cpp | 8 ++++---- extra/yassl/src/socket_wrapper.cpp | 13 +++++++++++-- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/extra/yassl/src/handshake.cpp b/extra/yassl/src/handshake.cpp index 12b62f26e14..66ec64f4af8 100644 --- a/extra/yassl/src/handshake.cpp +++ b/extra/yassl/src/handshake.cpp @@ -880,7 +880,7 @@ int sendData(SSL& ssl, const void* buffer, int sz) ssl.SetError(no_error); ssl.verfiyHandShakeComplete(); - if (ssl.GetError()) return 0; + if (ssl.GetError()) return -1; int sent = 0; for (;;) { @@ -891,7 +891,7 @@ int sendData(SSL& ssl, const void* buffer, int sz) buildMessage(ssl, out, data); ssl.Send(out.get_buffer(), out.get_size()); - if (ssl.GetError()) return 0; + if (ssl.GetError()) return -1; sent += len; if (sent == sz) break; } @@ -918,14 +918,14 @@ int receiveData(SSL& ssl, Data& data) ssl.SetError(no_error); ssl.verfiyHandShakeComplete(); - if (ssl.GetError()) return 0; + if (ssl.GetError()) return -1; if (!ssl.bufferedData()) processReply(ssl); ssl.fillData(data); ssl.useLog().ShowData(data.get_length()); - if (ssl.GetError()) return 0; + if (ssl.GetError()) return -1; if (data.get_length() == 0 && ssl.getSocket().WouldBlock()) { ssl.SetError(YasslError(SSL_ERROR_WANT_READ)); diff --git a/extra/yassl/src/socket_wrapper.cpp b/extra/yassl/src/socket_wrapper.cpp index 803f4b01249..06b403c999d 100644 --- a/extra/yassl/src/socket_wrapper.cpp +++ b/extra/yassl/src/socket_wrapper.cpp @@ -113,13 +113,22 @@ uint Socket::get_ready() const uint Socket::send(const byte* buf, unsigned int sz, int flags) const { + const byte* pos = buf; + const byte* end = pos + sz; + assert(socket_ != INVALID_SOCKET); - int sent = ::send(socket_, reinterpret_cast(buf), sz, flags); + + while (pos != end) { + int sent = ::send(socket_, reinterpret_cast(pos), + static_cast(end - pos), flags); if (sent == -1) return 0; - return sent; + pos += sent; + } + + return sz; } From 7c1748a1a4e38dc49490a6c31761fe05a820d9e0 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 14 Jun 2006 23:09:03 +0200 Subject: [PATCH 46/65] cleanup String::set(integer) --- sql/field.cc | 5 +---- sql/item.cc | 5 +---- sql/item_func.cc | 20 ++++---------------- sql/item_sum.cc | 5 +---- sql/sql_string.cc | 16 +++------------- sql/sql_string.h | 7 +++++-- 6 files changed, 15 insertions(+), 43 deletions(-) diff --git a/sql/field.cc b/sql/field.cc index 1f65adca2d5..b51e5b63779 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -7125,10 +7125,7 @@ int Field_blob::store(double nr) int Field_blob::store(longlong nr, bool unsigned_val) { CHARSET_INFO *cs=charset(); - if (unsigned_val) - value.set((ulonglong) nr, cs); - else - value.set(nr, cs); + value.set(nr, unsigned_val, cs); return Field_blob::store(value.ptr(), (uint) value.length(), cs); } diff --git a/sql/item.cc b/sql/item.cc index 5fa3ad61c15..cb5bbab53a2 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -212,10 +212,7 @@ String *Item::val_string_from_int(String *str) longlong nr= val_int(); if (null_value) return 0; - if (unsigned_flag) - str->set((ulonglong) nr, &my_charset_bin); - else - str->set(nr, &my_charset_bin); + str->set(nr, unsigned_flag, &my_charset_bin); return str; } diff --git a/sql/item_func.cc b/sql/item_func.cc index 051238843fa..5030c9729cd 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -556,10 +556,7 @@ String *Item_int_func::val_str(String *str) longlong nr=val_int(); if (null_value) return 0; - if (!unsigned_flag) - str->set(nr,&my_charset_bin); - else - str->set((ulonglong) nr,&my_charset_bin); + str->set(nr, unsigned_flag, &my_charset_bin); return str; } @@ -701,10 +698,7 @@ String *Item_func_numhybrid::val_str(String *str) longlong nr= int_op(); if (null_value) return 0; /* purecov: inspected */ - if (!unsigned_flag) - str->set(nr,&my_charset_bin); - else - str->set((ulonglong) nr,&my_charset_bin); + str->set(nr, unsigned_flag, &my_charset_bin); break; } case REAL_RESULT: @@ -2058,10 +2052,7 @@ String *Item_func_min_max::val_str(String *str) longlong nr=val_int(); if (null_value) return 0; - if (!unsigned_flag) - str->set(nr,&my_charset_bin); - else - str->set((ulonglong) nr,&my_charset_bin); + str->set(nr, unsigned_flag, &my_charset_bin); return str; } case DECIMAL_RESULT: @@ -2847,10 +2838,7 @@ String *Item_func_udf_int::val_str(String *str) longlong nr=val_int(); if (null_value) return 0; - if (!unsigned_flag) - str->set(nr,&my_charset_bin); - else - str->set((ulonglong) nr,&my_charset_bin); + str->set(nr, unsigned_flag, &my_charset_bin); return str; } diff --git a/sql/item_sum.cc b/sql/item_sum.cc index 1285e842769..2479d181276 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -1584,10 +1584,7 @@ Item_sum_hybrid::val_str(String *str) my_decimal2string(E_DEC_FATAL_ERROR, &sum_dec, 0, 0, 0, str); return str; case INT_RESULT: - if (unsigned_flag) - str->set((ulonglong) sum_int, &my_charset_bin); - else - str->set((longlong) sum_int, &my_charset_bin); + str->set(sum_int, unsigned_flag, &my_charset_bin); break; case ROW_RESULT: default: diff --git a/sql/sql_string.cc b/sql/sql_string.cc index 19ee9f259dc..39395ae2ca5 100644 --- a/sql/sql_string.cc +++ b/sql/sql_string.cc @@ -96,24 +96,14 @@ bool String::realloc(uint32 alloc_length) return FALSE; } -bool String::set(longlong num, CHARSET_INFO *cs) +bool String::set(longlong num, bool unsigned_flag, CHARSET_INFO *cs) { uint l=20*cs->mbmaxlen+1; + int base= unsigned_flag ? 10 : -10; if (alloc(l)) return TRUE; - str_length=(uint32) (cs->cset->longlong10_to_str)(cs,Ptr,l,-10,num); - str_charset=cs; - return FALSE; -} - -bool String::set(ulonglong num, CHARSET_INFO *cs) -{ - uint l=20*cs->mbmaxlen+1; - - if (alloc(l)) - return TRUE; - str_length=(uint32) (cs->cset->longlong10_to_str)(cs,Ptr,l,10,num); + str_length=(uint32) (cs->cset->longlong10_to_str)(cs,Ptr,l,base,num); str_charset=cs; return FALSE; } diff --git a/sql/sql_string.h b/sql/sql_string.h index ddae6368228..4e1d3a4837e 100644 --- a/sql/sql_string.h +++ b/sql/sql_string.h @@ -139,8 +139,11 @@ public: } str_charset=cs; } - bool set(longlong num, CHARSET_INFO *cs); - bool set(ulonglong num, CHARSET_INFO *cs); + bool set(longlong num, bool unsigned_flag, CHARSET_INFO *cs); + bool set(longlong num, CHARSET_INFO *cs) + { return set(num, false, cs); } + bool set(ulonglong num, CHARSET_INFO *cs) + { return set((longlong)num, true, cs); } bool set(double num,uint decimals, CHARSET_INFO *cs); /* From b2f308160b761f98d5864b36b33b64d82273cdcf Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 15 Jun 2006 01:48:41 +0400 Subject: [PATCH 47/65] Many files: After merge fix mysql-test/r/func_time.result: After merge fix mysql-test/r/func_concat.result: After merge fix mysql-test/r/cast.result: After merge fix sql/item_cmpfunc.h: After merge fix sql/item_cmpfunc.cc: After merge fix sql/field.cc: After merge fix --- mysql-test/r/cast.result | 2 +- mysql-test/r/func_concat.result | 14 +++--- mysql-test/r/func_time.result | 4 +- sql/field.cc | 82 +++++++++++++-------------------- sql/item_cmpfunc.cc | 8 ++-- sql/item_cmpfunc.h | 15 ++---- 6 files changed, 52 insertions(+), 73 deletions(-) diff --git a/mysql-test/r/cast.result b/mysql-test/r/cast.result index 8f5a5eea99f..d60efa083e0 100644 --- a/mysql-test/r/cast.result +++ b/mysql-test/r/cast.result @@ -254,7 +254,7 @@ cast("2001-1-1" as datetime) = "2001-01-01 00:00:00" 1 select cast("1:2:3" as TIME) = "1:02:03"; cast("1:2:3" as TIME) = "1:02:03" -1 +0 select cast(NULL as DATE); cast(NULL as DATE) NULL diff --git a/mysql-test/r/func_concat.result b/mysql-test/r/func_concat.result index 7fb489f2a14..66808afd4e9 100644 --- a/mysql-test/r/func_concat.result +++ b/mysql-test/r/func_concat.result @@ -68,13 +68,6 @@ select 'a' union select concat('a', -0.0000); a a a0.0000 -create table t1(f1 varchar(6)) charset=utf8; -insert into t1 values ("123456"); -select concat(f1, 2) a from t1 union select 'x' a from t1; -a -1234562 -x -drop table t1; select concat((select x from (select 'a' as x) as t1 ), (select y from (select 'b' as y) as t2 )) from (select 1 union select 2 ) as t3; @@ -82,3 +75,10 @@ concat((select x from (select 'a' as x) as t1 ), (select y from (select 'b' as y) as t2 )) ab ab +create table t1(f1 varchar(6)) charset=utf8; +insert into t1 values ("123456"); +select concat(f1, 2) a from t1 union select 'x' a from t1; +a +1234562 +x +drop table t1; diff --git a/mysql-test/r/func_time.result b/mysql-test/r/func_time.result index a0faa05f988..cee5dc70c32 100644 --- a/mysql-test/r/func_time.result +++ b/mysql-test/r/func_time.result @@ -784,7 +784,9 @@ f1 select f1 from t1 where "2006-1-1" between f1 and 'zzz'; f1 Warnings: -Warning 1292 Truncated incorrect date value: 'zzz' +Warning 1292 Incorrect date value: 'zzz' for column 'f1' at row 1 +Warning 1292 Truncated incorrect INTEGER value: 'zzz' +Warning 1292 Truncated incorrect INTEGER value: 'zzz' select f1 from t1 where makedate(2006,1) between date(f1) and date(f3); f1 2006-01-01 diff --git a/sql/field.cc b/sql/field.cc index 64f888eb24d..b0a0633b6ce 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -4530,11 +4530,11 @@ int Field_timestamp::store(const char *from,uint len,CHARSET_INFO *cs) int error; bool have_smth_to_conv; my_bool in_dst_time_gap; - THD *thd= table->in_use; + THD *thd= table ? table->in_use : current_thd; /* We don't want to store invalid or fuzzy datetime values in TIMESTAMP */ have_smth_to_conv= (str_to_datetime(from, len, &l_time, - (table->in_use->variables.sql_mode & + (thd->variables.sql_mode & MODE_NO_ZERO_DATE) | MODE_NO_ZERO_IN_DATE, &error) > MYSQL_TIMESTAMP_ERROR); @@ -4599,7 +4599,7 @@ int Field_timestamp::store(longlong nr, bool unsigned_val) my_time_t timestamp= 0; int error; my_bool in_dst_time_gap; - THD *thd= table->in_use; + THD *thd= table ? table->in_use : current_thd; /* We don't want to store invalid or fuzzy datetime values in TIMESTAMP */ longlong tmp= number_to_datetime(nr, &l_time, (thd->variables.sql_mode & @@ -4653,7 +4653,7 @@ longlong Field_timestamp::val_int(void) { uint32 temp; TIME time_tmp; - THD *thd= table->in_use; + THD *thd= table ? table->in_use : current_thd; #ifdef WORDS_BIGENDIAN if (table->s->db_low_byte_first) @@ -4678,7 +4678,7 @@ String *Field_timestamp::val_str(String *val_buffer, String *val_ptr) { uint32 temp, temp2; TIME time_tmp; - THD *thd= table->in_use; + THD *thd= table ? table->in_use : current_thd; char *to; val_buffer->alloc(field_length+1); @@ -4749,7 +4749,7 @@ String *Field_timestamp::val_str(String *val_buffer, String *val_ptr) bool Field_timestamp::get_date(TIME *ltime, uint fuzzydate) { long temp; - THD *thd= table->in_use; + THD *thd= table ? table->in_use : current_thd; #ifdef WORDS_BIGENDIAN if (table->s->db_low_byte_first) temp=uint4korr(ptr); @@ -4832,7 +4832,8 @@ void Field_timestamp::sql_type(String &res) const void Field_timestamp::set_time() { - long tmp= (long) table->in_use->query_start(); + THD *thd= table ? table->in_use : current_thd; + long tmp= (long) thd->query_start(); set_notnull(); #ifdef WORDS_BIGENDIAN if (table->s->db_low_byte_first) @@ -5024,12 +5025,13 @@ String *Field_time::val_str(String *val_buffer, bool Field_time::get_date(TIME *ltime, uint fuzzydate) { long tmp; + THD *thd= table ? table->in_use : current_thd; if (!(fuzzydate & TIME_FUZZY_DATE)) { - push_warning_printf(table->in_use, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, ER(ER_WARN_DATA_OUT_OF_RANGE), field_name, - table->in_use->row_count); + thd->row_count); return 1; } tmp=(long) sint3korr(ptr); @@ -5217,9 +5219,10 @@ int Field_date::store(const char *from, uint len,CHARSET_INFO *cs) TIME l_time; uint32 tmp; int error; + THD *thd= table ? table->in_use : current_thd; if (str_to_datetime(from, len, &l_time, TIME_FUZZY_DATE | - (table->in_use->variables.sql_mode & + (thd->variables.sql_mode & (MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE | MODE_INVALID_DATES)), &error) <= MYSQL_TIMESTAMP_ERROR) @@ -5272,9 +5275,10 @@ int Field_date::store(longlong nr, bool unsigned_val) TIME not_used; int error; longlong initial_nr= nr; + THD *thd= table ? table->in_use : current_thd; nr= number_to_datetime(nr, ¬_used, (TIME_FUZZY_DATE | - (table->in_use->variables.sql_mode & + (thd->variables.sql_mode & (MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE | MODE_INVALID_DATES))), &error); @@ -5420,9 +5424,10 @@ int Field_newdate::store(const char *from,uint len,CHARSET_INFO *cs) TIME l_time; long tmp; int error; + THD *thd= table ? table->in_use : current_thd; if (str_to_datetime(from, len, &l_time, (TIME_FUZZY_DATE | - (table->in_use->variables.sql_mode & + (thd->variables.sql_mode & (MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE | MODE_INVALID_DATES))), &error) <= MYSQL_TIMESTAMP_ERROR) @@ -5460,9 +5465,10 @@ int Field_newdate::store(longlong nr, bool unsigned_val) TIME l_time; longlong tmp; int error; + THD *thd= table ? table->in_use : current_thd; if (number_to_datetime(nr, &l_time, (TIME_FUZZY_DATE | - (table->in_use->variables.sql_mode & + (thd->variables.sql_mode & (MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE | MODE_INVALID_DATES))), &error) == LL(-1)) @@ -5605,10 +5611,11 @@ int Field_datetime::store(const char *from,uint len,CHARSET_INFO *cs) int error; ulonglong tmp= 0; enum enum_mysql_timestamp_type func_res; + THD *thd= table ? table->in_use : current_thd; func_res= str_to_datetime(from, len, &time_tmp, (TIME_FUZZY_DATE | - (table->in_use->variables.sql_mode & + (thd->variables.sql_mode & (MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE | MODE_INVALID_DATES))), &error); @@ -5655,9 +5662,10 @@ int Field_datetime::store(longlong nr, bool unsigned_val) TIME not_used; int error; longlong initial_nr= nr; + THD *thd= table ? table->in_use : current_thd; nr= number_to_datetime(nr, ¬_used, (TIME_FUZZY_DATE | - (table->in_use->variables.sql_mode & + (thd->variables.sql_mode & (MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE | MODE_INVALID_DATES))), &error); @@ -9076,10 +9084,10 @@ Field::set_datetime_warning(MYSQL_ERROR::enum_warning_level level, uint code, const char *str, uint str_length, timestamp_type ts_type, int cuted_increment) { - if (table->in_use->really_abort_on_warning() || + THD *thd= table ? table->in_use : current_thd; + if (thd->really_abort_on_warning() || set_warning(level, code, cuted_increment)) - make_truncated_value_warning(table ? table->in_use : current_thd, - str, str_length, ts_type, + make_truncated_value_warning(thd, str, str_length, ts_type, field_name); } @@ -9106,13 +9114,13 @@ Field::set_datetime_warning(MYSQL_ERROR::enum_warning_level level, uint code, longlong nr, timestamp_type ts_type, int cuted_increment) { - if (table->in_use->really_abort_on_warning() || + THD *thd= table ? table->in_use : current_thd; + if (thd->really_abort_on_warning() || set_warning(level, code, cuted_increment)) { char str_nr[22]; char *str_end= longlong10_to_str(nr, str_nr, -10); - make_truncated_value_warning(table ? table->in_use : current_thd, - str_nr, (uint) (str_end - str_nr), + make_truncated_value_warning(thd, str_nr, (uint) (str_end - str_nr), ts_type, field_name); } } @@ -9138,41 +9146,15 @@ void Field::set_datetime_warning(MYSQL_ERROR::enum_warning_level level, uint code, double nr, timestamp_type ts_type) { - if (table->in_use->really_abort_on_warning() || + THD *thd= table ? table->in_use : current_thd; + if (thd->really_abort_on_warning() || set_warning(level, code, 1)) { /* DBL_DIG is enough to print '-[digits].E+###' */ char str_nr[DBL_DIG + 8]; uint str_len= my_sprintf(str_nr, (str_nr, "%g", nr)); - make_truncated_value_warning(table ? table->in_use : current_thd, - str_nr, str_len, ts_type, + make_truncated_value_warning(thd, str_nr, str_len, ts_type, field_name); } } -/* - maximum possible display length for blob - - SYNOPSIS - Field_blob::max_length() - - RETURN - length -*/ -uint32 Field_blob::max_length() -{ - switch (packlength) - { - case 1: - return 255 * field_charset->mbmaxlen; - case 2: - return 65535 * field_charset->mbmaxlen; - case 3: - return 16777215 * field_charset->mbmaxlen; - case 4: - return (uint32) 4294967295U; - default: - DBUG_ASSERT(0); // we should never go here - return 0; - } -} diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 6a1ec1de7e7..db1af9de0a2 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -366,18 +366,18 @@ static bool convert_constant_item(THD *thd, Field *field, Item **item) if (!(*item)->with_subselect && (*item)->const_item()) { /* For comparison purposes allow invalid dates like 2000-01-32 */ - ulong orig_sql_mode= field->table->in_use->variables.sql_mode; - field->table->in_use->variables.sql_mode|= MODE_INVALID_DATES; + ulong orig_sql_mode= thd->variables.sql_mode; + thd->variables.sql_mode|= MODE_INVALID_DATES; if (!(*item)->save_in_field(field, 1) && !((*item)->null_value)) { Item *tmp=new Item_int_with_ref(field->val_int(), *item, test(field->flags & UNSIGNED_FLAG)); - field->table->in_use->variables.sql_mode= orig_sql_mode; + thd->variables.sql_mode= orig_sql_mode; if (tmp) thd->change_item_tree(item, tmp); return 1; // Item was replaced } - field->table->in_use->variables.sql_mode= orig_sql_mode; + thd->variables.sql_mode= orig_sql_mode; } return 0; } diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index 340bf2bb2bf..a2b10eacc79 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -45,11 +45,8 @@ public: int set_compare_func(Item_bool_func2 *owner, Item_result type); inline int set_compare_func(Item_bool_func2 *owner_arg) { - Item_result ar= (*a)->result_as_longlong() && (*b)->const_item() ? - INT_RESULT : (*a)->result_type(); - Item_result br= (*b)->result_as_longlong() && (*a)->const_item() ? - INT_RESULT : (*b)->result_type(); - return set_compare_func(owner_arg, item_cmp_type(ar, br)); + return set_compare_func(owner_arg, item_cmp_type((*a)->result_type(), + (*b)->result_type())); } inline int set_cmp_func(Item_bool_func2 *owner_arg, Item **a1, Item **a2, @@ -62,11 +59,9 @@ public: inline int set_cmp_func(Item_bool_func2 *owner_arg, Item **a1, Item **a2) { - Item_result ar= (*a1)->result_as_longlong() && (*a2)->const_item() ? - INT_RESULT : (*a1)->result_type(); - Item_result br= (*a2)->result_as_longlong() && (*a1)->const_item() ? - INT_RESULT : (*a2)->result_type(); - return set_cmp_func(owner_arg, a1, a2, item_cmp_type(ar, br)); + return set_cmp_func(owner_arg, a1, a2, + item_cmp_type((*a1)->result_type(), + (*a2)->result_type())); } inline int compare() { return (this->*func)(); } From 689205ae947176220ef7ba7332ffa2b765c4796a Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 15 Jun 2006 16:24:02 +0500 Subject: [PATCH 48/65] Fix for bug#19667 group by a decimal expression yields wrong result mysql-test/r/type_newdecimal.result: Fix for bug#19667 group by a decimal expression yields wrong result test case mysql-test/t/type_newdecimal.test: Fix for bug#19667 group by a decimal expression yields wrong result test case --- mysql-test/r/type_newdecimal.result | 11 +++++++++++ mysql-test/t/type_newdecimal.test | 9 +++++++++ sql/item_buff.cc | 2 +- 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/type_newdecimal.result b/mysql-test/r/type_newdecimal.result index 968c6d3ec6f..4caec152a1f 100644 --- a/mysql-test/r/type_newdecimal.result +++ b/mysql-test/r/type_newdecimal.result @@ -1397,3 +1397,14 @@ c1 9999999999999999999999999999999999999999999999999999999999999999 9999999999999999999999999999999999999999999999999999999999999999 drop table t1; +create table t1 (i int, j int); +insert into t1 values (1,1), (1,2), (2,3), (2,4); +select i, count(distinct j) from t1 group by i; +i count(distinct j) +1 2 +2 2 +select i+0.0 as i2, count(distinct j) from t1 group by i2; +i2 count(distinct j) +1.0 2 +2.0 2 +drop table t1; diff --git a/mysql-test/t/type_newdecimal.test b/mysql-test/t/type_newdecimal.test index 74782a5bddb..35aff8b3c5a 100644 --- a/mysql-test/t/type_newdecimal.test +++ b/mysql-test/t/type_newdecimal.test @@ -1095,3 +1095,12 @@ insert into t1 values( insert into t1 values(1e100); select * from t1; drop table t1; + +# +# Bug#19667 group by a decimal expression yields wrong result +# +create table t1 (i int, j int); +insert into t1 values (1,1), (1,2), (2,3), (2,4); +select i, count(distinct j) from t1 group by i; +select i+0.0 as i2, count(distinct j) from t1 group by i2; +drop table t1; diff --git a/sql/item_buff.cc b/sql/item_buff.cc index 9db2f465080..1661f04a4ae 100644 --- a/sql/item_buff.cc +++ b/sql/item_buff.cc @@ -132,7 +132,7 @@ bool Cached_item_decimal::cmp() { my_decimal tmp; my_decimal *ptmp= item->val_decimal(&tmp); - if (null_value != item->null_value || my_decimal_cmp(&value, ptmp) == 0) + if (null_value != item->null_value || my_decimal_cmp(&value, ptmp)) { null_value= item->null_value; my_decimal2decimal(ptmp, &value); From e4bfa961ae40a5f55598d2ce183b9fa654b492d6 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 15 Jun 2006 16:39:18 +0400 Subject: [PATCH 49/65] item_cmpfunc.h, cast.result: Post fix for bug#16377 mysql-test/r/cast.result: Post fix for bug#16377 sql/item_cmpfunc.h: Post fix for bug#16377 --- mysql-test/r/cast.result | 2 +- sql/item_cmpfunc.h | 15 +++++---------- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/mysql-test/r/cast.result b/mysql-test/r/cast.result index 2538fbfd61d..68687670e17 100644 --- a/mysql-test/r/cast.result +++ b/mysql-test/r/cast.result @@ -192,7 +192,7 @@ cast("2001-1-1" as datetime) = "2001-01-01 00:00:00" 1 select cast("1:2:3" as TIME) = "1:02:03"; cast("1:2:3" as TIME) = "1:02:03" -1 +0 select cast(NULL as DATE); cast(NULL as DATE) NULL diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index 08d9de6ffd6..68852b5a5f6 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -43,11 +43,8 @@ public: int set_compare_func(Item_bool_func2 *owner, Item_result type); inline int set_compare_func(Item_bool_func2 *owner_arg) { - Item_result ar= (*a)->result_as_longlong() && (*b)->const_item() ? - INT_RESULT : (*a)->result_type(); - Item_result br= (*b)->result_as_longlong() && (*a)->const_item() ? - INT_RESULT : (*b)->result_type(); - return set_compare_func(owner_arg, item_cmp_type(ar, br)); + return set_compare_func(owner_arg, item_cmp_type((*a)->result_type(), + (*b)->result_type())); } inline int set_cmp_func(Item_bool_func2 *owner_arg, Item **a1, Item **a2, @@ -60,11 +57,9 @@ public: inline int set_cmp_func(Item_bool_func2 *owner_arg, Item **a1, Item **a2) { - Item_result ar= (*a1)->result_as_longlong() && (*a2)->const_item() ? - INT_RESULT : (*a1)->result_type(); - Item_result br= (*a2)->result_as_longlong() && (*a1)->const_item() ? - INT_RESULT : (*a2)->result_type(); - return set_cmp_func(owner_arg, a1, a2, item_cmp_type(ar, br)); + return set_cmp_func(owner_arg, a1, a2, + item_cmp_type((*a1)->result_type(), + (*a2)->result_type())); } inline int compare() { return (this->*func)(); } From 548b9aeff332060ffd8c5fb29dab7125f97c7979 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 15 Jun 2006 22:09:58 +0400 Subject: [PATCH 50/65] Fixed bug#18175: The nest_level counter wasn't decremented for union parts which resulted in a wrong error message. The nest_level counter indicates the depth of nesting for a subselect. It is needed to properly resolve aggregate functions in nested subselects. Obviously it shouldn't be incremented for UNION parts because they have the same level of nesting. This counter was incremented by 1 in the mysql_new_select() function for any new select and wasn't decremented for UNION parts. This resulted in wrongly reported error messages. Now the nest_level counter is decremented by 1 for any union part. mysql-test/t/union.test: Added test case for the bug#18175: The nest_level counter wasn't decremented for union parts which resulted in a wrong error message. mysql-test/r/union.result: Added test case for the bug#18175: The nest_level counter wasn't decremented for union parts which resulted in a wrong error message. sql/sql_yacc.yy: Fixed bug#18175: The nest_level counter wasn't decremented for union parts which resulted in a wrong error message. Now the nest_level counter is decremented by 1 for any union part. --- mysql-test/r/union.result | 45 ++++++++++++++++++++++++++++++++++++ mysql-test/t/union.test | 48 +++++++++++++++++++++++++++++++++++++++ sql/sql_yacc.yy | 2 ++ 3 files changed, 95 insertions(+) diff --git a/mysql-test/r/union.result b/mysql-test/r/union.result index eb9e10aa58d..426387e04f5 100644 --- a/mysql-test/r/union.result +++ b/mysql-test/r/union.result @@ -1306,3 +1306,48 @@ id 5 99 drop table t1; +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)); +avg(1) +NULL diff --git a/mysql-test/t/union.test b/mysql-test/t/union.test index 692f1f509fa..7dfe4ac482f 100644 --- a/mysql-test/t/union.test +++ b/mysql-test/t/union.test @@ -793,3 +793,51 @@ select id from t1 union all select 99 order by 1; drop table t1; # End of 4.1 tests + +# +# Bug#18175: Union select over 129 tables with a sum function fails. +# +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)); + diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index b2dbc517fa4..4f3cf4d8554 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -8752,6 +8752,8 @@ union_list: yyerror(ER(ER_SYNTAX_ERROR)); YYABORT; } + /* This counter shouldn't be incremented for UNION parts */ + Lex->nest_level--; if (mysql_new_select(lex, 0)) YYABORT; mysql_init_select(lex); From 33f38c317ad32820d8a0f7bcad665e442f643977 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 15 Jun 2006 21:43:29 +0200 Subject: [PATCH 51/65] don't call plugins' configure scripts manually, use AC_CONFIG_SUBDIRS don't add CFLAGS/CXXFLAGS to ./configure command line in config.status but _do_ add them before calling plugins' configure scripts config/ac-macros/plugins.m4: don't call plugins' configure scripts manually, use AC_CONFIG_SUBDIRS --- config/ac-macros/plugins.m4 | 2 +- configure.in | 29 +++++++---------------------- 2 files changed, 8 insertions(+), 23 deletions(-) diff --git a/config/ac-macros/plugins.m4 b/config/ac-macros/plugins.m4 index 4bf9292547d..cfc5f8dbcbe 100644 --- a/config/ac-macros/plugins.m4 +++ b/config/ac-macros/plugins.m4 @@ -406,7 +406,7 @@ dnl Although this is "pretty", it breaks libmysqld build mysql_plugin_dirs="$mysql_plugin_dirs $6" m4_syscmd(test -f "$6/configure") ifelse(m4_sysval, 0, - [other_configures="$other_configures $6/configure"], + [AC_CONFIG_SUBDIRS($6)], [AC_CONFIG_FILES($6/Makefile)] ) ifelse(m4_substr($6, 0, 8), [storage/], diff --git a/configure.in b/configure.in index be6f3c8eaec..120863dafbd 100644 --- a/configure.in +++ b/configure.in @@ -2474,10 +2474,6 @@ AC_SUBST(netware_dir) AC_SUBST(linked_netware_sources) AM_CONDITIONAL(HAVE_NETWARE, test "$netware_dir" = "netware") -# Ensure that table handlers gets all modifications to CFLAGS/CXXFLAGS -export CC CXX CFLAGS CXXFLAGS LD LDFLAGS AR -ac_configure_args="$ac_configure_args CFLAGS='$CFLAGS' CXXFLAGS='$CXXFLAGS'" - if test "$with_server" = "yes" -o "$THREAD_SAFE_CLIENT" != "no" then AC_DEFINE([THREAD], [1], @@ -2527,22 +2523,6 @@ AC_SUBST(CC) AC_SUBST(GXX) # Set configuration options for make_binary_distribution - -CONF_ARGS= -case $SYSTEM_TYPE in - *netware*) - MAKE_BINARY_DISTRIBUTION_OPTIONS=--no-strip - CONF_ARGS=--host="$MACHINE_TYPE-$SYSTEM_TYPE" - ;; - *) - MAKE_BINARY_DISTRIBUTION_OPTIONS= - ;; -esac - -for CONF in $other_configures; do - (cd `dirname $CONF`; ./`basename $CONF` $CONF_ARGS --build=$build_alias) -done - AC_SUBST(MAKE_BINARY_DISTRIBUTION_OPTIONS) # Output results @@ -2559,8 +2539,13 @@ AC_CONFIG_FILES(Makefile extra/Makefile mysys/Makefile dnl support-files/MacOSX/Makefile mysql-test/Makefile dnl mysql-test/ndb/Makefile netware/Makefile dnl include/mysql_version.h plugin/Makefile win/Makefile) - AC_CONFIG_COMMANDS([default], , test -z "$CONFIG_HEADERS" || echo timestamp > stamp-h) - AC_OUTPUT + +AC_CONFIG_COMMANDS([default], , test -z "$CONFIG_HEADERS" || echo timestamp > stamp-h) + +# Ensure that table handlers gets all modifications to CFLAGS/CXXFLAGS +AC_CONFIG_COMMANDS_POST(ac_configure_args="$ac_configure_args CFLAGS='$CFLAGS' CXXFLAGS='$CXXFLAGS'") + +AC_OUTPUT echo echo "MySQL has a Web site at http://www.mysql.com/ which carries details on the" From 9668198e14d575a12dd889a601233924b1f13479 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 15 Jun 2006 22:02:26 +0200 Subject: [PATCH 52/65] BUG#19940: Add Valgrind suppression for false alarm report. --- mysql-test/valgrind.supp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/mysql-test/valgrind.supp b/mysql-test/valgrind.supp index 3c2ac1a7ea5..65c5a82db36 100644 --- a/mysql-test/valgrind.supp +++ b/mysql-test/valgrind.supp @@ -292,3 +292,18 @@ futex(utime) fun:__lll_mutex_unlock_wake } + +# +# BUG#19940: NDB sends uninitialized parts of field buffers across the wire. +# This is "works as designed"; the uninitialized part is not used at the +# other end (but Valgrind cannot see this). +# +{ + bug19940 + Memcheck:Param + socketcall.sendto(msg) + fun:send + fun:_ZN15TCP_Transporter6doSendEv + fun:_ZN19TransporterRegistry11performSendEv + fun:_ZN19TransporterRegistry14forceSendCheckEi +} From 59d38c15bc8c010fcff11cdf72f497fd957a1ef6 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 15 Jun 2006 22:46:52 -0400 Subject: [PATCH 53/65] Fixing false alarm errors on windows build from a clean clone. win/build-vs71.bat: Don't trigger 'could not find the file specified' errors when running from a clean clone. win/build-vs8.bat: Don't trigger 'could not find the file specified' errors when running from a clean clone. --- win/build-vs71.bat | 2 +- win/build-vs8.bat | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/win/build-vs71.bat b/win/build-vs71.bat index 46f3bc32f6e..959067695c5 100644 --- a/win/build-vs71.bat +++ b/win/build-vs71.bat @@ -1,6 +1,6 @@ @echo off -del cmakecache.txt +if exist cmakecache.txt del cmakecache.txt copy win\vs71cache.txt cmakecache.txt cmake -G "Visual Studio 7 .NET 2003" copy cmakecache.txt win\vs71cache.txt diff --git a/win/build-vs8.bat b/win/build-vs8.bat index 349c3e78628..d9c06241a9b 100644 --- a/win/build-vs8.bat +++ b/win/build-vs8.bat @@ -1,6 +1,6 @@ @echo off -del cmakecache.txt +if exist cmakecache.txt del cmakecache.txt copy win\vs8cache.txt cmakecache.txt cmake -G "Visual Studio 8 2005" copy cmakecache.txt win\vs8cache.txt From b47705abf385274f3a5f4844c3496c1b22b15db6 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 15 Jun 2006 20:29:05 -0700 Subject: [PATCH 54/65] Post_merges fixes. --- mysql-test/r/func_group.result | 2 -- 1 file changed, 2 deletions(-) diff --git a/mysql-test/r/func_group.result b/mysql-test/r/func_group.result index 9f6189b558f..f693c6190d5 100644 --- a/mysql-test/r/func_group.result +++ b/mysql-test/r/func_group.result @@ -846,8 +846,6 @@ id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 ALL NULL NULL NULL NULL 2 DROP TABLE t1; CREATE TABLE t1 (id int , b varchar(512), INDEX(b(250))) COLLATE latin1_bin; -Warnings: -Warning 1246 Converting column 'b' from CHAR to TEXT INSERT INTO t1 VALUES (1,CONCAT(REPEAT('_', 250), "qq")), (1,CONCAT(REPEAT('_', 250), "zz")), (1,CONCAT(REPEAT('_', 250), "aa")), (1,CONCAT(REPEAT('_', 250), "ff")); From dd285aac1b005866c3d3ae3d1a4e6e890f838d42 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 16 Jun 2006 14:05:58 +0500 Subject: [PATCH 55/65] after-merge fixes. mysql-test/r/rpl_log.result: after-merge fix. sql/ha_archive.cc: after-merge fix. sql/item_func.cc: after-merge fix. sql/sql_class.cc: after-merge fix. sql/sql_class.h: after-merge fix. --- mysql-test/r/rpl_log.result | 10 +++++----- sql/ha_archive.cc | 2 +- sql/item_func.cc | 2 +- sql/sql_class.cc | 4 ++++ sql/sql_class.h | 3 ++- 5 files changed, 13 insertions(+), 8 deletions(-) diff --git a/mysql-test/r/rpl_log.result b/mysql-test/r/rpl_log.result index 6a41a9b9e02..6ee0eb283b5 100644 --- a/mysql-test/r/rpl_log.result +++ b/mysql-test/r/rpl_log.result @@ -107,11 +107,11 @@ reset master; set insert_id=5; insert into t1 values (NULL, last_insert_id()), (NULL, last_insert_id()); show binlog events; -Log_name Pos Event_type Server_id Orig_log_pos Info -slave-bin.000001 4 Start 2 4 Server ver: VERSION, Binlog ver: 3 -slave-bin.000001 79 Intvar 2 79 LAST_INSERT_ID=1 -slave-bin.000001 107 Intvar 2 107 INSERT_ID=5 -slave-bin.000001 135 Query 2 135 use `test`; insert into t1 values (NULL, last_insert_id()), (NULL, last_insert_id()) +Log_name Pos Event_type Server_id End_log_pos Info +slave-bin.000001 4 Format_desc 2 98 Server ver: VERSION, Binlog ver: 4 +slave-bin.000001 98 Intvar 2 126 LAST_INSERT_ID=1 +slave-bin.000001 126 Intvar 2 154 INSERT_ID=5 +slave-bin.000001 154 Query 2 289 use `test`; insert into t1 values (NULL, last_insert_id()), (NULL, last_insert_id()) select * from t1; a b 1 1 diff --git a/sql/ha_archive.cc b/sql/ha_archive.cc index bfb90b607ee..3885defb4d5 100644 --- a/sql/ha_archive.cc +++ b/sql/ha_archive.cc @@ -1131,7 +1131,7 @@ int ha_archive::end_bulk_insert() int ha_archive::delete_all_rows() { DBUG_ENTER("ha_archive::delete_all_rows"); - DBUG_RETURN(0); + DBUG_RETURN(HA_ERR_WRONG_COMMAND); } /* diff --git a/sql/item_func.cc b/sql/item_func.cc index 2c770bec1ad..c75480aaeae 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -3296,7 +3296,7 @@ longlong Item_func_last_insert_id::val_int() return value; // Avoid side effect of insert_id() } thd->lex->uncacheable(UNCACHEABLE_SIDEEFFECT); - return thd->insert_id(); + return thd->last_insert_id_used ? thd->current_insert_id : thd->insert_id(); } /* This function is just used to test speed of different functions */ diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 65fd4d3ac19..678048226af 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -2050,7 +2050,9 @@ void THD::reset_sub_statement_state(Sub_statement_state *backup, backup->enable_slow_log= enable_slow_log; backup->last_insert_id= last_insert_id; backup->next_insert_id= next_insert_id; + backup->current_insert_id= current_insert_id; backup->insert_id_used= insert_id_used; + backup->last_insert_id_used= last_insert_id_used; backup->clear_next_insert_id= clear_next_insert_id; backup->limit_found_rows= limit_found_rows; backup->examined_row_count= examined_row_count; @@ -2099,7 +2101,9 @@ void THD::restore_sub_statement_state(Sub_statement_state *backup) enable_slow_log= backup->enable_slow_log; last_insert_id= backup->last_insert_id; next_insert_id= backup->next_insert_id; + current_insert_id= backup->current_insert_id; insert_id_used= backup->insert_id_used; + last_insert_id_used= backup->last_insert_id_used; clear_next_insert_id= backup->clear_next_insert_id; limit_found_rows= backup->limit_found_rows; sent_row_count= backup->sent_row_count; diff --git a/sql/sql_class.h b/sql/sql_class.h index 0ddba0e6f05..2cfc9142453 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -1051,12 +1051,13 @@ class Sub_statement_state { public: ulonglong options; - ulonglong last_insert_id, next_insert_id; + ulonglong last_insert_id, next_insert_id, current_insert_id; ulonglong limit_found_rows; ha_rows cuted_fields, sent_row_count, examined_row_count; ulong client_capabilities; uint in_sub_stmt; bool enable_slow_log, insert_id_used, clear_next_insert_id; + bool last_insert_id_used; my_bool no_send_ok; SAVEPOINT *savepoints; }; From 0237d9b0af6c9ddc182fe9389d788a094608ec43 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 16 Jun 2006 12:17:20 +0200 Subject: [PATCH 56/65] String::set(double) and set(longlong) -> set_real() and set_int() fix Field::store(double) being used instead of store(longlong) NB: overloading functions is evil --- sql/field.cc | 10 +++++----- sql/item.cc | 16 ++++++++-------- sql/item_func.cc | 18 +++++++++--------- sql/item_strfunc.cc | 2 +- sql/item_sum.cc | 4 ++-- sql/item_timefunc.cc | 2 +- sql/log.cc | 18 +++++++++--------- sql/procedure.h | 10 +++++----- sql/protocol.cc | 4 ++-- sql/sp.cc | 14 +++++++------- sql/sql_acl.cc | 2 +- sql/sql_analyse.cc | 8 ++++---- sql/sql_analyse.h | 44 ++++++++++++++++++++++---------------------- sql/sql_show.cc | 2 +- sql/sql_string.cc | 4 ++-- sql/sql_string.h | 8 ++++---- 16 files changed, 83 insertions(+), 83 deletions(-) diff --git a/sql/field.cc b/sql/field.cc index b51e5b63779..6fc774e9806 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -4978,7 +4978,7 @@ int Field_time::store_time(TIME *ltime, timestamp_type type) (ltime->minute * 100 + ltime->second); if (ltime->neg) tmp= -tmp; - return Field_time::store((longlong) tmp); + return Field_time::store((longlong) tmp, TRUE); } @@ -4990,7 +4990,7 @@ int Field_time::store(double nr) if (nr > 8385959.0) { tmp=8385959L; - set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, + set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, nr, MYSQL_TIMESTAMP_TIME); error= 1; } @@ -7117,7 +7117,7 @@ int Field_blob::store(const char *from,uint length,CHARSET_INFO *cs) int Field_blob::store(double nr) { CHARSET_INFO *cs=charset(); - value.set(nr, 2, cs); + value.set_real(nr, 2, cs); return Field_blob::store(value.ptr(),(uint) value.length(), cs); } @@ -7125,7 +7125,7 @@ int Field_blob::store(double nr) int Field_blob::store(longlong nr, bool unsigned_val) { CHARSET_INFO *cs=charset(); - value.set(nr, unsigned_val, cs); + value.set_int(nr, unsigned_val, cs); return Field_blob::store(value.ptr(), (uint) value.length(), cs); } @@ -8216,7 +8216,7 @@ int Field_bit::store_decimal(const my_decimal *val) { int err= 0; longlong i= convert_decimal2longlong(val, 1, &err); - return test(err | store(i)); + return test(err | store(i, TRUE)); } diff --git a/sql/item.cc b/sql/item.cc index cb5bbab53a2..53797052788 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -61,7 +61,7 @@ Hybrid_type_traits::val_decimal(Hybrid_type *val, my_decimal *to) const String * Hybrid_type_traits::val_str(Hybrid_type *val, String *to, uint8 decimals) const { - to->set(val->real, decimals, &my_charset_bin); + to->set_real(val->real, decimals, &my_charset_bin); return to; } @@ -202,7 +202,7 @@ String *Item::val_string_from_real(String *str) double nr= val_real(); if (null_value) return 0; /* purecov: inspected */ - str->set(nr,decimals, &my_charset_bin); + str->set_real(nr,decimals, &my_charset_bin); return str; } @@ -212,7 +212,7 @@ String *Item::val_string_from_int(String *str) longlong nr= val_int(); if (null_value) return 0; - str->set(nr, unsigned_flag, &my_charset_bin); + str->set_int(nr, unsigned_flag, &my_charset_bin); return str; } @@ -2038,7 +2038,7 @@ String *Item_float::val_str(String *str) { // following assert is redundant, because fixed=1 assigned in constructor DBUG_ASSERT(fixed == 1); - str->set(value,decimals,&my_charset_bin); + str->set_real(value,decimals,&my_charset_bin); return str; } @@ -2633,7 +2633,7 @@ String *Item_param::val_str(String* str) case LONG_DATA_VALUE: return &str_value_ptr; case REAL_VALUE: - str->set(value.real, NOT_FIXED_DEC, &my_charset_bin); + str->set_real(value.real, NOT_FIXED_DEC, &my_charset_bin); return str; case INT_VALUE: str->set(value.integer, &my_charset_bin); @@ -2673,7 +2673,7 @@ const String *Item_param::query_val_str(String* str) const str->set(value.integer, &my_charset_bin); break; case REAL_VALUE: - str->set(value.real, NOT_FIXED_DEC, &my_charset_bin); + str->set_real(value.real, NOT_FIXED_DEC, &my_charset_bin); break; case DECIMAL_VALUE: if (my_decimal2string(E_DEC_FATAL_ERROR, &decimal_value, @@ -4326,7 +4326,7 @@ void Item_float::print(String *str) } char buffer[20]; String num(buffer, sizeof(buffer), &my_charset_bin); - num.set(value, decimals, &my_charset_bin); + num.set_real(value, decimals, &my_charset_bin); str->append(num); } @@ -5768,7 +5768,7 @@ longlong Item_cache_real::val_int() String* Item_cache_real::val_str(String *str) { DBUG_ASSERT(fixed == 1); - str->set(value, decimals, default_charset()); + str->set_real(value, decimals, default_charset()); return str; } diff --git a/sql/item_func.cc b/sql/item_func.cc index 5030c9729cd..91bb38c8f8a 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -416,7 +416,7 @@ String *Item_real_func::val_str(String *str) double nr= val_real(); if (null_value) return 0; /* purecov: inspected */ - str->set(nr,decimals, &my_charset_bin); + str->set_real(nr,decimals, &my_charset_bin); return str; } @@ -556,7 +556,7 @@ String *Item_int_func::val_str(String *str) longlong nr=val_int(); if (null_value) return 0; - str->set(nr, unsigned_flag, &my_charset_bin); + str->set_int(nr, unsigned_flag, &my_charset_bin); return str; } @@ -698,7 +698,7 @@ String *Item_func_numhybrid::val_str(String *str) longlong nr= int_op(); if (null_value) return 0; /* purecov: inspected */ - str->set(nr, unsigned_flag, &my_charset_bin); + str->set_int(nr, unsigned_flag, &my_charset_bin); break; } case REAL_RESULT: @@ -706,7 +706,7 @@ String *Item_func_numhybrid::val_str(String *str) double nr= real_op(); if (null_value) return 0; /* purecov: inspected */ - str->set(nr,decimals,&my_charset_bin); + str->set_real(nr,decimals,&my_charset_bin); break; } case STRING_RESULT: @@ -2052,7 +2052,7 @@ String *Item_func_min_max::val_str(String *str) longlong nr=val_int(); if (null_value) return 0; - str->set(nr, unsigned_flag, &my_charset_bin); + str->set_int(nr, unsigned_flag, &my_charset_bin); return str; } case DECIMAL_RESULT: @@ -2068,7 +2068,7 @@ String *Item_func_min_max::val_str(String *str) double nr= val_real(); if (null_value) return 0; /* purecov: inspected */ - str->set(nr,decimals,&my_charset_bin); + str->set_real(nr,decimals,&my_charset_bin); return str; } case STRING_RESULT: @@ -2819,7 +2819,7 @@ String *Item_func_udf_float::val_str(String *str) double nr= val_real(); if (null_value) return 0; /* purecov: inspected */ - str->set(nr,decimals,&my_charset_bin); + str->set_real(nr,decimals,&my_charset_bin); return str; } @@ -2838,7 +2838,7 @@ String *Item_func_udf_int::val_str(String *str) longlong nr=val_int(); if (null_value) return 0; - str->set(nr, unsigned_flag, &my_charset_bin); + str->set_int(nr, unsigned_flag, &my_charset_bin); return str; } @@ -3638,7 +3638,7 @@ String *user_var_entry::val_str(my_bool *null_value, String *str, switch (type) { case REAL_RESULT: - str->set(*(double*) value, decimals, &my_charset_bin); + str->set_real(*(double*) value, decimals, &my_charset_bin); break; case INT_RESULT: str->set(*(longlong*) value, &my_charset_bin); diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index eb1fbf4855d..3dead7fe1e7 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -1808,7 +1808,7 @@ String *Item_func_format::val_str(String *str) return 0; /* purecov: inspected */ nr= my_double_round(nr, decimals, FALSE); /* Here default_charset() is right as this is not an automatic conversion */ - str->set(nr,decimals, default_charset()); + str->set_real(nr,decimals, default_charset()); if (isnan(nr)) return str; str_length=str->length(); diff --git a/sql/item_sum.cc b/sql/item_sum.cc index 2479d181276..d6bc2c326d6 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -1578,13 +1578,13 @@ Item_sum_hybrid::val_str(String *str) case STRING_RESULT: return &value; case REAL_RESULT: - str->set(sum,decimals, &my_charset_bin); + str->set_real(sum,decimals, &my_charset_bin); break; case DECIMAL_RESULT: my_decimal2string(E_DEC_FATAL_ERROR, &sum_dec, 0, 0, 0, str); return str; case INT_RESULT: - str->set(sum_int, unsigned_flag, &my_charset_bin); + str->set_int(sum_int, unsigned_flag, &my_charset_bin); break; case ROW_RESULT: default: diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc index 06abbe60121..4ffb3324a5b 100644 --- a/sql/item_timefunc.cc +++ b/sql/item_timefunc.cc @@ -1503,7 +1503,7 @@ double Item_func_sysdate_local::val_real() { DBUG_ASSERT(fixed == 1); store_now_in_TIME(<ime); - return (longlong) TIME_to_ulonglong_datetime(<ime); + return ulonglong2double(TIME_to_ulonglong_datetime(<ime)); } diff --git a/sql/log.cc b/sql/log.cc index 5a17ef817d0..6dea50413eb 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -313,9 +313,9 @@ bool Log_to_csv_event_handler:: table->field[1]->store(user_host, user_host_len, client_cs); table->field[1]->set_notnull(); - table->field[2]->store((longlong) thread_id); + table->field[2]->store((longlong) thread_id, TRUE); table->field[2]->set_notnull(); - table->field[3]->store((longlong) server_id); + table->field[3]->store((longlong) server_id, TRUE); table->field[3]->set_notnull(); table->field[4]->store(command_type, command_type_len, client_cs); table->field[4]->set_notnull(); @@ -395,13 +395,13 @@ bool Log_to_csv_event_handler:: if (query_start_arg) { /* fill in query_time field */ - table->field[2]->store(query_time); + table->field[2]->store(query_time, TRUE); /* lock_time */ - table->field[3]->store(lock_time); + table->field[3]->store(lock_time, TRUE); /* rows_sent */ - table->field[4]->store((longlong) thd->sent_row_count); + table->field[4]->store((longlong) thd->sent_row_count, TRUE); /* rows_examined */ - table->field[5]->store((longlong) thd->examined_row_count); + table->field[5]->store((longlong) thd->examined_row_count, TRUE); } else { @@ -420,18 +420,18 @@ bool Log_to_csv_event_handler:: if (thd->last_insert_id_used) { - table->field[7]->store((longlong) thd->current_insert_id); + table->field[7]->store((longlong) thd->current_insert_id, TRUE); table->field[7]->set_notnull(); } /* set value if we do an insert on autoincrement column */ if (thd->insert_id_used) { - table->field[8]->store((longlong) thd->last_insert_id); + table->field[8]->store((longlong) thd->last_insert_id, TRUE); table->field[8]->set_notnull(); } - table->field[9]->store((longlong) server_id); + table->field[9]->store((longlong) server_id, TRUE); table->field[9]->set_notnull(); /* sql_text */ diff --git a/sql/procedure.h b/sql/procedure.h index aceadd10883..5c8a3387eec 100644 --- a/sql/procedure.h +++ b/sql/procedure.h @@ -68,11 +68,11 @@ public: longlong val_int() { return (longlong) value; } String *val_str(String *s) { - s->set(value,decimals,default_charset()); + s->set_real(value,decimals,default_charset()); return s; } my_decimal *val_decimal(my_decimal *); - unsigned int size_of() { return sizeof(*this);} + unsigned int size_of() { return sizeof(*this);} }; class Item_proc_int :public Item_proc @@ -91,7 +91,7 @@ public: longlong val_int() { return value; } String *val_str(String *s) { s->set(value, default_charset()); return s; } my_decimal *val_decimal(my_decimal *); - unsigned int size_of() { return sizeof(*this);} + unsigned int size_of() { return sizeof(*this);} }; @@ -102,12 +102,12 @@ public: { this->max_length=length; } enum Item_result result_type () const { return STRING_RESULT; } enum_field_types field_type() const { return MYSQL_TYPE_VARCHAR; } - void set(double nr) { str_value.set(nr, 2, default_charset()); } + void set(double nr) { str_value.set_real(nr, 2, default_charset()); } void set(longlong nr) { str_value.set(nr, default_charset()); } void set(const char *str, uint length, CHARSET_INFO *cs) { str_value.copy(str,length,cs); } double val_real() - { + { int err_not_used; char *end_not_used; CHARSET_INFO *cs= str_value.charset(); diff --git a/sql/protocol.cc b/sql/protocol.cc index bb0891cdbbe..2edc11b6b3e 100644 --- a/sql/protocol.cc +++ b/sql/protocol.cc @@ -897,7 +897,7 @@ bool Protocol_simple::store(float from, uint32 decimals, String *buffer) field_types[field_pos] == MYSQL_TYPE_FLOAT); field_pos++; #endif - buffer->set((double) from, decimals, thd->charset()); + buffer->set_real((double) from, decimals, thd->charset()); return net_store_data((char*) buffer->ptr(), buffer->length()); } @@ -909,7 +909,7 @@ bool Protocol_simple::store(double from, uint32 decimals, String *buffer) field_types[field_pos] == MYSQL_TYPE_DOUBLE); field_pos++; #endif - buffer->set(from, decimals, thd->charset()); + buffer->set_real(from, decimals, thd->charset()); return net_store_data((char*) buffer->ptr(), buffer->length()); } diff --git a/sql/sp.cc b/sql/sp.cc index 653c04ee11a..93e21170156 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -555,17 +555,17 @@ db_create_routine(THD *thd, int type, sp_head *sp) table->field[MYSQL_PROC_FIELD_NAME]-> store(sp->m_name.str, sp->m_name.length, system_charset_info); table->field[MYSQL_PROC_FIELD_TYPE]-> - store((longlong)type); + store((longlong)type, TRUE); table->field[MYSQL_PROC_FIELD_SPECIFIC_NAME]-> store(sp->m_name.str, sp->m_name.length, system_charset_info); if (sp->m_chistics->daccess != SP_DEFAULT_ACCESS) table->field[MYSQL_PROC_FIELD_ACCESS]-> - store((longlong)sp->m_chistics->daccess); + store((longlong)sp->m_chistics->daccess, TRUE); table->field[MYSQL_PROC_FIELD_DETERMINISTIC]-> - store((longlong)(sp->m_chistics->detistic ? 1 : 2)); + store((longlong)(sp->m_chistics->detistic ? 1 : 2), TRUE); if (sp->m_chistics->suid != SP_IS_DEFAULT_SUID) table->field[MYSQL_PROC_FIELD_SECURITY_TYPE]-> - store((longlong)sp->m_chistics->suid); + store((longlong)sp->m_chistics->suid, TRUE); table->field[MYSQL_PROC_FIELD_PARAM_LIST]-> store(sp->m_params.str, sp->m_params.length, system_charset_info); if (sp->m_type == TYPE_ENUM_FUNCTION) @@ -582,7 +582,7 @@ db_create_routine(THD *thd, int type, sp_head *sp) ((Field_timestamp *)table->field[MYSQL_PROC_FIELD_CREATED])->set_time(); ((Field_timestamp *)table->field[MYSQL_PROC_FIELD_MODIFIED])->set_time(); table->field[MYSQL_PROC_FIELD_SQL_MODE]-> - store((longlong)thd->variables.sql_mode); + store((longlong)thd->variables.sql_mode, TRUE); if (sp->m_chistics->comment.str) table->field[MYSQL_PROC_FIELD_COMMENT]-> store(sp->m_chistics->comment.str, sp->m_chistics->comment.length, @@ -686,10 +686,10 @@ db_update_routine(THD *thd, int type, sp_name *name, st_sp_chistics *chistics) ((Field_timestamp *)table->field[MYSQL_PROC_FIELD_MODIFIED])->set_time(); if (chistics->suid != SP_IS_DEFAULT_SUID) table->field[MYSQL_PROC_FIELD_SECURITY_TYPE]-> - store((longlong)chistics->suid); + store((longlong)chistics->suid, TRUE); if (chistics->daccess != SP_DEFAULT_ACCESS) table->field[MYSQL_PROC_FIELD_ACCESS]-> - store((longlong)chistics->daccess); + store((longlong)chistics->daccess, TRUE); if (chistics->comment.str) table->field[MYSQL_PROC_FIELD_COMMENT]->store(chistics->comment.str, chistics->comment.length, diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 4c2dfac6b8b..8066c41fd10 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -2030,7 +2030,7 @@ static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo, table->field[next_field+2]->store((longlong) mqh.conn_per_hour, TRUE); if (table->s->fields >= 36 && (mqh.specified_limits & USER_RESOURCES::USER_CONNECTIONS)) - table->field[next_field+3]->store((longlong) mqh.user_conn); + table->field[next_field+3]->store((longlong) mqh.user_conn, TRUE); mqh_used= mqh_used || mqh.questions || mqh.updates || mqh.conn_per_hour; } if (old_row_exists) diff --git a/sql/sql_analyse.cc b/sql/sql_analyse.cc index f372c55c13e..9f1a0561138 100644 --- a/sql/sql_analyse.cc +++ b/sql/sql_analyse.cc @@ -1024,7 +1024,7 @@ String *field_decimal::avg(String *s, ha_rows rows) { if (!(rows - nulls)) { - s->set((double) 0.0, 1,my_thd_charset); + s->set_real((double) 0.0, 1,my_thd_charset); return s; } my_decimal num, avg_val, rounded_avg; @@ -1045,7 +1045,7 @@ String *field_decimal::std(String *s, ha_rows rows) { if (!(rows - nulls)) { - s->set((double) 0.0, 1,my_thd_charset); + s->set_real((double) 0.0, 1,my_thd_charset); return s; } my_decimal num, tmp, sum2, sum2d; @@ -1058,7 +1058,7 @@ String *field_decimal::std(String *s, ha_rows rows) my_decimal_sub(E_DEC_FATAL_ERROR, &sum2, sum_sqr+cur_sum, &tmp); my_decimal_div(E_DEC_FATAL_ERROR, &tmp, &sum2, &num, prec_increment); my_decimal2double(E_DEC_FATAL_ERROR, &tmp, &std_sqr); - s->set(((double) std_sqr <= 0.0 ? 0.0 : sqrt(std_sqr)), + s->set_real(((double) std_sqr <= 0.0 ? 0.0 : sqrt(std_sqr)), min(item->decimals + prec_increment, NOT_FIXED_DEC), my_thd_charset); return s; @@ -1092,7 +1092,7 @@ int collect_real(double *element, element_count count __attribute__((unused)), else info->found = 1; info->str->append('\''); - s.set(*element, info->item->decimals, current_thd->charset()); + s.set_real(*element, info->item->decimals, current_thd->charset()); info->str->append(s); info->str->append('\''); return 0; diff --git a/sql/sql_analyse.h b/sql/sql_analyse.h index 9e5926fd9b1..0e7bd1b722a 100644 --- a/sql/sql_analyse.h +++ b/sql/sql_analyse.h @@ -128,9 +128,9 @@ public: String *avg(String *s, ha_rows rows) { if (!(rows - nulls)) - s->set((double) 0.0, 1,my_thd_charset); + s->set_real((double) 0.0, 1,my_thd_charset); else - s->set((ulonglong2double(sum) / ulonglong2double(rows - nulls)), + s->set_real((ulonglong2double(sum) / ulonglong2double(rows - nulls)), DEC_IN_AVG,my_thd_charset); return s; } @@ -190,34 +190,34 @@ public: void add(); void get_opt_type(String*, ha_rows); - String *get_min_arg(String *s) - { - s->set(min_arg, item->decimals,my_thd_charset); - return s; + String *get_min_arg(String *s) + { + s->set_real(min_arg, item->decimals, my_thd_charset); + return s; } - String *get_max_arg(String *s) - { - s->set(max_arg, item->decimals,my_thd_charset); - return s; + String *get_max_arg(String *s) + { + s->set_real(max_arg, item->decimals, my_thd_charset); + return s; } String *avg(String *s, ha_rows rows) { if (!(rows - nulls)) - s->set((double) 0.0, 1,my_thd_charset); + s->set_real((double) 0.0, 1,my_thd_charset); else - s->set(((double)sum / (double) (rows - nulls)), item->decimals,my_thd_charset); + s->set_real(((double)sum / (double) (rows - nulls)), item->decimals,my_thd_charset); return s; } String *std(String *s, ha_rows rows) { double tmp = ulonglong2double(rows); if (!(tmp - nulls)) - s->set((double) 0.0, 1,my_thd_charset); + s->set_real((double) 0.0, 1,my_thd_charset); else { double tmp2 = ((sum_sqr - sum * sum / (tmp - nulls)) / (tmp - nulls)); - s->set(((double) tmp2 <= 0.0 ? 0.0 : sqrt(tmp2)), item->decimals,my_thd_charset); + s->set_real(((double) tmp2 <= 0.0 ? 0.0 : sqrt(tmp2)), item->decimals,my_thd_charset); } return s; } @@ -249,21 +249,21 @@ public: String *avg(String *s, ha_rows rows) { if (!(rows - nulls)) - s->set((double) 0.0, 1,my_thd_charset); + s->set_real((double) 0.0, 1,my_thd_charset); else - s->set(((double) sum / (double) (rows - nulls)), DEC_IN_AVG,my_thd_charset); + s->set_real(((double) sum / (double) (rows - nulls)), DEC_IN_AVG,my_thd_charset); return s; } String *std(String *s, ha_rows rows) { double tmp = ulonglong2double(rows); if (!(tmp - nulls)) - s->set((double) 0.0, 1,my_thd_charset); + s->set_real((double) 0.0, 1,my_thd_charset); else { double tmp2 = ((sum_sqr - sum * sum / (tmp - nulls)) / (tmp - nulls)); - s->set(((double) tmp2 <= 0.0 ? 0.0 : sqrt(tmp2)), DEC_IN_AVG,my_thd_charset); + s->set_real(((double) tmp2 <= 0.0 ? 0.0 : sqrt(tmp2)), DEC_IN_AVG,my_thd_charset); } return s; } @@ -293,9 +293,9 @@ public: String *avg(String *s, ha_rows rows) { if (!(rows - nulls)) - s->set((double) 0.0, 1,my_thd_charset); + s->set_real((double) 0.0, 1,my_thd_charset); else - s->set((ulonglong2double(sum) / ulonglong2double(rows - nulls)), + s->set_real((ulonglong2double(sum) / ulonglong2double(rows - nulls)), DEC_IN_AVG,my_thd_charset); return s; } @@ -303,13 +303,13 @@ public: { double tmp = ulonglong2double(rows); if (!(tmp - nulls)) - s->set((double) 0.0, 1,my_thd_charset); + s->set_real((double) 0.0, 1,my_thd_charset); else { double tmp2 = ((ulonglong2double(sum_sqr) - ulonglong2double(sum * sum) / (tmp - nulls)) / (tmp - nulls)); - s->set(((double) tmp2 <= 0.0 ? 0.0 : sqrt(tmp2)), DEC_IN_AVG,my_thd_charset); + s->set_real(((double) tmp2 <= 0.0 ? 0.0 : sqrt(tmp2)), DEC_IN_AVG,my_thd_charset); } return s; } diff --git a/sql/sql_show.cc b/sql/sql_show.cc index b479eb32f36..dc598b786d5 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -3421,7 +3421,7 @@ static int get_schema_stat_record(THD *thd, struct st_table_list *tables, show_table->field[key_part->fieldnr-1]->key_length())) { table->field[10]->store((longlong) key_part->length / - key_part->field->charset()->mbmaxlen); + key_part->field->charset()->mbmaxlen, TRUE); table->field[10]->set_notnull(); } uint flags= key_part->field ? key_part->field->flags : 0; diff --git a/sql/sql_string.cc b/sql/sql_string.cc index 39395ae2ca5..f86c05d2f4f 100644 --- a/sql/sql_string.cc +++ b/sql/sql_string.cc @@ -96,7 +96,7 @@ bool String::realloc(uint32 alloc_length) return FALSE; } -bool String::set(longlong num, bool unsigned_flag, CHARSET_INFO *cs) +bool String::set_int(longlong num, bool unsigned_flag, CHARSET_INFO *cs) { uint l=20*cs->mbmaxlen+1; int base= unsigned_flag ? 10 : -10; @@ -108,7 +108,7 @@ bool String::set(longlong num, bool unsigned_flag, CHARSET_INFO *cs) return FALSE; } -bool String::set(double num,uint decimals, CHARSET_INFO *cs) +bool String::set_real(double num,uint decimals, CHARSET_INFO *cs) { char buff[331]; uint dummy_errors; diff --git a/sql/sql_string.h b/sql/sql_string.h index 4e1d3a4837e..b1d417be2c2 100644 --- a/sql/sql_string.h +++ b/sql/sql_string.h @@ -139,12 +139,12 @@ public: } str_charset=cs; } - bool set(longlong num, bool unsigned_flag, CHARSET_INFO *cs); + bool set_int(longlong num, bool unsigned_flag, CHARSET_INFO *cs); bool set(longlong num, CHARSET_INFO *cs) - { return set(num, false, cs); } + { return set_int(num, false, cs); } bool set(ulonglong num, CHARSET_INFO *cs) - { return set((longlong)num, true, cs); } - bool set(double num,uint decimals, CHARSET_INFO *cs); + { return set_int((longlong)num, true, cs); } + bool set_real(double num,uint decimals, CHARSET_INFO *cs); /* PMG 2004.11.12 From 6f1605321de19ea68cd2e4c2c43649b31c05196f Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 16 Jun 2006 16:26:30 +0200 Subject: [PATCH 57/65] remove wrong assert --- sql/field.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sql/field.cc b/sql/field.cc index 6fc774e9806..fa018b3eaba 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -3471,8 +3471,7 @@ int Field_long::store(longlong nr, bool unsigned_val) ASSERT_COLUMN_MARKED_FOR_WRITE; int error= 0; int32 res; - DBUG_ASSERT(table->in_use == current_thd); // General safety - + if (unsigned_flag) { if (nr < 0 && !unsigned_val) From 9224719d254ee5eba41a6b9e5e7d35c973a86853 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 16 Jun 2006 15:04:35 -0400 Subject: [PATCH 58/65] Fix for broken compile if not compiling with partitioning --- sql/sql_table.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sql/sql_table.cc b/sql/sql_table.cc index d379af3b3eb..bf0535567b4 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -5009,6 +5009,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, old_db_type= table->s->db_type; if (!create_info->db_type) { +#ifdef WITH_PARTITION_STORAGE_ENGINE if (table->part_info && create_info->used_fields & HA_CREATE_USED_ENGINE) { @@ -5022,6 +5023,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, create_info->db_type= table->part_info->default_engine_type; } else +#endif create_info->db_type= old_db_type; } From ca22a81b1c84ce81e1e9ea2c3ace7be1848027d8 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 16 Jun 2006 23:46:37 +0400 Subject: [PATCH 59/65] item_strfunc.cc: Fix for bug#16716 for --ps-protocol mode. item_cmpfunc.cc: Fix for a memory allocation/freeing problem in agg_cmp_type() after fix for bug#16377. Few language corrections. sql/item_cmpfunc.cc: Fix for a memory allocation/freeing problem in agg_cmp_type(). Few language corrections. sql/item_strfunc.cc: Fix for bug#16716 for --ps-protocol mode. --- sql/item_cmpfunc.cc | 32 +++++++++++--------------------- sql/item_strfunc.cc | 3 ++- 2 files changed, 13 insertions(+), 22 deletions(-) diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index e92e1d30ca4..126037a24d8 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -83,21 +83,21 @@ static void agg_result_type(Item_result *type, Item **items, uint nitems) items will be used for aggregation. If there are DATE/TIME fields/functions in the list and no string fields/functions in the list then: - The INT_RESULT type will be used for aggregation instead of orginal + The INT_RESULT type will be used for aggregation instead of original result type of any DATE/TIME field/function in the list All constant items in the list will be converted to a DATE/TIME using found field or result field of found function. Implementation notes: - The code is equvalent to: - 1. Check the list for presense of a STRING field/function. + The code is equivalent to: + 1. Check the list for presence of a STRING field/function. Collect the is_const flag. 2. Get a Field* object to use for type coercion 3. Perform type conversion. 1 and 2 are implemented in 2 loops. The first searches for a DATE/TIME - field/function and checks presense of a STRING field/function. + field/function and checks presence of a STRING field/function. The second loop works only if a DATE/TIME field/function is found. - It checks presense of a STRING field/function in the rest of the list. + It checks presence of a STRING field/function in the rest of the list. TODO 1) The current implementation can produce false comparison results for @@ -120,8 +120,9 @@ static void agg_result_type(Item_result *type, Item **items, uint nitems) static void agg_cmp_type(THD *thd, Item_result *type, Item **items, uint nitems) { uint i; - Item::Type res; - char *buff= NULL; + Item::Type res= (Item::Type)0; + /* Used only for date/time fields, max_length = 19 */ + char buff[20]; uchar null_byte; Field *field= NULL; @@ -147,28 +148,20 @@ static void agg_cmp_type(THD *thd, Item_result *type, Item **items, uint nitems) { field= items[i]->tmp_table_field_from_field_type(0); if (field) - buff= alloc_root(thd->mem_root, field->max_length()); - if (!buff || !field) - { - if (field) - delete field; - if (buff) - my_free(buff, MYF(MY_WME)); - field= 0; - } - else field->move_field(buff, &null_byte, 0); break; } } if (field) { - /* Check the rest of the list for presense of a string field/function. */ + /* Check the rest of the list for presence of a string field/function. */ for (i++ ; i < nitems; i++) { if (!items[i]->const_item() && items[i]->result_type() == STRING_RESULT && !items[i]->result_as_longlong()) { + if (res == Item::FUNC_ITEM) + delete field; field= 0; break; } @@ -205,10 +198,7 @@ static void agg_cmp_type(THD *thd, Item_result *type, Item **items, uint nitems) } if (res == Item::FUNC_ITEM && field) - { delete field; - my_free(buff, MYF(MY_WME)); - } } static void my_coll_agg_error(DTCollation &c1, DTCollation &c2, diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index deb3542f4a5..ee585649f8c 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -274,7 +274,8 @@ String *Item_func_concat::val_str(String *str) if (!(res=args[0]->val_str(str))) goto null; use_as_buff= &tmp_value; - is_const= args[0]->const_item(); + /* Item_subselect in --ps-protocol mode will state it as a non-const */ + is_const= args[0]->const_item() || !args[0]->used_tables(); for (i=1 ; i < arg_count ; i++) { if (res->length() == 0) From 60d55cc5500ad12738ae85e5d302327cda8d89ec Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 17 Jun 2006 02:52:14 +0400 Subject: [PATCH 60/65] select.result: After merge fix mysql-test/r/select.result: After merge fix --- mysql-test/r/select.result | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/mysql-test/r/select.result b/mysql-test/r/select.result index dceb0efe199..b385c576f2e 100644 --- a/mysql-test/r/select.result +++ b/mysql-test/r/select.result @@ -2720,6 +2720,16 @@ id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t3 index PRIMARY,a,b PRIMARY 8 NULL 2 Using index 1 SIMPLE t2 ALL PRIMARY NULL NULL NULL 2 Range checked for each record (index map: 0x1) DROP TABLE t1,t2,t3; +CREATE TABLE t1 (a int, INDEX idx(a)); +INSERT INTO t1 VALUES (2), (3), (1); +EXPLAIN SELECT * FROM t1 IGNORE INDEX (idx); +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 +EXPLAIN SELECT * FROM t1 IGNORE INDEX (a); +ERROR HY000: Key 'a' doesn't exist in table 't1' +EXPLAIN SELECT * FROM t1 FORCE INDEX (a); +ERROR HY000: Key 'a' doesn't exist in table 't1' +DROP TABLE t1; CREATE TABLE t1 ( K2C4 varchar(4) character set latin1 collate latin1_bin NOT NULL default '', K4N4 varchar(4) character set latin1 collate latin1_bin NOT NULL default '0000', From 76d2eceaf5b74ad71e848356281b908f3366e0eb Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 17 Jun 2006 13:00:17 +0200 Subject: [PATCH 61/65] ndb - bug#19285 minor fixes ndb/src/ndbapi/NdbBlob.cpp: bug#19285 minor fixes ndb/src/ndbapi/ndberror.c: bug#19285 minor fixes --- ndb/src/ndbapi/NdbBlob.cpp | 2 +- ndb/src/ndbapi/ndberror.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ndb/src/ndbapi/NdbBlob.cpp b/ndb/src/ndbapi/NdbBlob.cpp index 3440c6416cb..fdee8961337 100644 --- a/ndb/src/ndbapi/NdbBlob.cpp +++ b/ndb/src/ndbapi/NdbBlob.cpp @@ -753,7 +753,7 @@ NdbBlob::writeData(const void* data, Uint32 bytes) } const char* buf = static_cast(data); int ret = writeDataPrivate(buf, bytes); - DBUG_RETURN(0); + DBUG_RETURN(ret); } int diff --git a/ndb/src/ndbapi/ndberror.c b/ndb/src/ndbapi/ndberror.c index 6c9931ba21c..62d920755bd 100644 --- a/ndb/src/ndbapi/ndberror.c +++ b/ndb/src/ndbapi/ndberror.c @@ -521,7 +521,7 @@ ErrorBundle ErrorCodes[] = { { 4270, IE, "Unknown blob error" }, { 4335, AE, "Only one autoincrement column allowed per table. Having a table without primary key uses an autoincremented hidden key, i.e. a table without a primary key can not have an autoincremented column" }, { 4271, AE, "Invalid index object, not retrieved via getIndex()" }, - { 4275, IE, "The blob method is incompatible with operation type or lock mode" } + { 4275, AE, "The blob method is incompatible with operation type or lock mode" } }; static From 4f36bb01b16f3c3b4a4b923dbe6fe9d20ee3a259 Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 17 Jun 2006 13:12:38 +0200 Subject: [PATCH 62/65] ndb - bug#19285 post merge 5.0->5.1 fix (error codes) storage/ndb/src/ndbapi/ndberror.c: postmerge fix --- storage/ndb/src/ndbapi/ndberror.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/storage/ndb/src/ndbapi/ndberror.c b/storage/ndb/src/ndbapi/ndberror.c index 22252960e21..a867ad7315b 100644 --- a/storage/ndb/src/ndbapi/ndberror.c +++ b/storage/ndb/src/ndbapi/ndberror.c @@ -594,7 +594,7 @@ ErrorBundle ErrorCodes[] = { { 4262, DMEC, UD, "NdbScanFilter: Condition is out of bounds"}, { 4263, DMEC, IE, "Invalid blob attributes or invalid blob parts table" }, { 4264, DMEC, AE, "Invalid usage of blob attribute" }, - { 4265, DMEC, AE, "Method is not valid in current blob state" }, + { 4265, DMEC, AE, "The method is not valid in current blob state" }, { 4266, DMEC, AE, "Invalid blob seek position" }, { 4267, DMEC, IE, "Corrupted blob value" }, { 4268, DMEC, IE, "Error in blob head update forced rollback of transaction" }, @@ -604,7 +604,8 @@ ErrorBundle ErrorCodes[] = { { 4271, DMEC, AE, "Invalid index object, not retrieved via getIndex()" }, { 4272, DMEC, AE, "Table definition has undefined column" }, { 4273, DMEC, IE, "No blob table in dict cache" }, - { 4274, DMEC, IE, "Corrupted main table PK in blob operation" } + { 4274, DMEC, IE, "Corrupted main table PK in blob operation" }, + { 4275, DMEC, AE, "The blob method is incompatible with operation type or lock mode" } }; static From d1fb292251fc6ba5a455e39d59dad5f7d0d0023f Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 17 Jun 2006 16:20:39 +0200 Subject: [PATCH 63/65] atomic ops: my_atomic_XX_t -> intXX, no implicit locking anymore simplified framework, support for requested cleanups dbug/dbug.c: compiler warning include/atomic/nolock.h: my_atomic_XX_t -> intXX include/atomic/rwlock.h: my_atomic_XX_t -> intXX, no implicit locking anymore include/atomic/x86-gcc.h: my_atomic_XX_t -> intXX, no implicit locking anymore include/atomic/x86-msvc.h: my_atomic_XX_t -> intXX simplified defines support for cleanups include/my_atomic.h: my_atomic_XX_t -> intXX, no implicit locking anymore simplified framework, support for requested cleanups unittest/examples/no_plan-t.c: compiler warning unittest/mysys/Makefile.am: fix for dependencies unittest/mysys/my_atomic-t.c: my_atomic_XX_t -> intXX, no implicit locking anymore unittest/mytap/tap.c: cosmetic fix --- dbug/dbug.c | 2 +- include/atomic/nolock.h | 132 +--------------------------------- include/atomic/rwlock.h | 126 ++------------------------------ include/atomic/x86-gcc.h | 46 ++++++------ include/atomic/x86-msvc.h | 102 ++++++++++++++------------ include/my_atomic.h | 113 +++++++++++++++++++++++++---- unittest/examples/no_plan-t.c | 6 +- unittest/mysys/Makefile.am | 8 +-- unittest/mysys/my_atomic-t.c | 70 ++++++++++++------ unittest/mytap/tap.c | 4 +- 10 files changed, 245 insertions(+), 364 deletions(-) diff --git a/dbug/dbug.c b/dbug/dbug.c index 07f72a3e758..48b2398e9c6 100644 --- a/dbug/dbug.c +++ b/dbug/dbug.c @@ -1728,7 +1728,7 @@ static void DoPrefix(CODE_STATE *cs, uint _line_) struct tm *tm_p; if (gettimeofday(&tv, NULL) != -1) { - if ((tm_p= localtime(&tv.tv_sec))) + if ((tm_p= localtime((const time_t *)&tv.tv_sec))) { (void) fprintf (cs->stack->out_file, /* "%04d-%02d-%02d " */ diff --git a/include/atomic/nolock.h b/include/atomic/nolock.h index cf21a94c7de..458cb3e4f66 100644 --- a/include/atomic/nolock.h +++ b/include/atomic/nolock.h @@ -27,137 +27,9 @@ #endif #endif -#ifdef make_atomic_add_body8 +#ifdef make_atomic_add_body -#ifdef HAVE_INLINE - -#define make_atomic_add(S) \ -static inline uint ## S _my_atomic_add ## S( \ - my_atomic_ ## S ## _t *a, uint ## S v) \ -{ \ - make_atomic_add_body ## S; \ - return v; \ -} - -#define make_atomic_swap(S) \ -static inline uint ## S _my_atomic_swap ## S( \ - my_atomic_ ## S ## _t *a, uint ## S v) \ -{ \ - make_atomic_swap_body ## S; \ - return v; \ -} - -#define make_atomic_cas(S) \ -static inline uint _my_atomic_cas ## S(my_atomic_ ## S ## _t *a,\ - uint ## S *cmp, uint ## S set) \ -{ \ - uint8 ret; \ - make_atomic_cas_body ## S; \ - return ret; \ -} - -#define make_atomic_load(S) \ -static inline uint ## S _my_atomic_load ## S( \ - my_atomic_ ## S ## _t *a) \ -{ \ - uint ## S ret; \ - make_atomic_load_body ## S; \ - return ret; \ -} - -#define make_atomic_store(S) \ -static inline void _my_atomic_store ## S( \ - my_atomic_ ## S ## _t *a, uint ## S v) \ -{ \ - make_atomic_store_body ## S; \ -} - -#else /* no inline functions */ - -#define make_atomic_add(S) \ -extern uint ## S _my_atomic_add ## S( \ - my_atomic_ ## S ## _t *a, uint ## S v); - -#define make_atomic_swap(S) \ -extern uint ## S _my_atomic_swap ## S( \ - my_atomic_ ## S ## _t *a, uint ## S v); - -#define make_atomic_cas(S) \ -extern uint _my_atomic_cas ## S(my_atomic_ ## S ## _t *a, \ - uint ## S *cmp, uint ## S set); - -#define make_atomic_load(S) \ -extern uint ## S _my_atomic_load ## S( \ - my_atomic_ ## S ## _t *a); - -#define make_atomic_store(S) \ -extern void _my_atomic_store ## S( \ - my_atomic_ ## S ## _t *a, uint ## S v); - -#endif - -make_atomic_add( 8) -make_atomic_add(16) -make_atomic_add(32) - -make_atomic_cas( 8) -make_atomic_cas(16) -make_atomic_cas(32) - -make_atomic_load( 8) -make_atomic_load(16) -make_atomic_load(32) - -make_atomic_store( 8) -make_atomic_store(16) -make_atomic_store(32) - -make_atomic_swap( 8) -make_atomic_swap(16) -make_atomic_swap(32) - -#undef make_atomic_add_body8 -#undef make_atomic_cas_body8 -#undef make_atomic_load_body8 -#undef make_atomic_store_body8 -#undef make_atomic_swap_body8 -#undef make_atomic_add_body16 -#undef make_atomic_cas_body16 -#undef make_atomic_load_body16 -#undef make_atomic_store_body16 -#undef make_atomic_swap_body16 -#undef make_atomic_add_body32 -#undef make_atomic_cas_body32 -#undef make_atomic_load_body32 -#undef make_atomic_store_body32 -#undef make_atomic_swap_body32 -#undef make_atomic_add -#undef make_atomic_cas -#undef make_atomic_load -#undef make_atomic_store -#undef make_atomic_swap - -#define my_atomic_add8(a,v,L) _my_atomic_add8(a,v) -#define my_atomic_add16(a,v,L) _my_atomic_add16(a,v) -#define my_atomic_add32(a,v,L) _my_atomic_add32(a,v) - -#define my_atomic_cas8(a,c,v,L) _my_atomic_cas8(a,c,v) -#define my_atomic_cas16(a,c,v,L) _my_atomic_cas16(a,c,v) -#define my_atomic_cas32(a,c,v,L) _my_atomic_cas32(a,c,v) - -#define my_atomic_load8(a,L) _my_atomic_load8(a) -#define my_atomic_load16(a,L) _my_atomic_load16(a) -#define my_atomic_load32(a,L) _my_atomic_load32(a) - -#define my_atomic_store8(a,v,L) _my_atomic_store8(a,v) -#define my_atomic_store16(a,v,L) _my_atomic_store16(a,v) -#define my_atomic_store32(a,v,L) _my_atomic_store32(a,v) - -#define my_atomic_swap8(a,v,L) _my_atomic_swap8(a,v) -#define my_atomic_swap16(a,v,L) _my_atomic_swap16(a,v) -#define my_atomic_swap32(a,v,L) _my_atomic_swap32(a,v) - -#define my_atomic_rwlock_t typedef int +typedef struct { } my_atomic_rwlock_t; #define my_atomic_rwlock_destroy(name) #define my_atomic_rwlock_init(name) #define my_atomic_rwlock_rdlock(name) diff --git a/include/atomic/rwlock.h b/include/atomic/rwlock.h index ca5be29ab9b..12d0dd3f069 100644 --- a/include/atomic/rwlock.h +++ b/include/atomic/rwlock.h @@ -16,12 +16,6 @@ typedef struct {pthread_rwlock_t rw;} my_atomic_rwlock_t; -#ifdef MY_ATOMIC_EXTRA_DEBUG -#define CHECK_RW if (rw) if (a->rw) assert(rw == a->rw); else a->rw=rw; -#else -#define CHECK_RW -#endif - #ifdef MY_ATOMIC_MODE_DUMMY /* the following can never be enabled by ./configure, one need to put #define in @@ -36,6 +30,7 @@ typedef struct {pthread_rwlock_t rw;} my_atomic_rwlock_t; #define my_atomic_rwlock_wrlock(name) #define my_atomic_rwlock_rdunlock(name) #define my_atomic_rwlock_wrunlock(name) +#define MY_ATOMIC_MODE "dummy (non-atomic)" #else #define my_atomic_rwlock_destroy(name) pthread_rwlock_destroy(& (name)->rw) #define my_atomic_rwlock_init(name) pthread_rwlock_init(& (name)->rw, 0) @@ -43,119 +38,12 @@ typedef struct {pthread_rwlock_t rw;} my_atomic_rwlock_t; #define my_atomic_rwlock_wrlock(name) pthread_rwlock_wrlock(& (name)->rw) #define my_atomic_rwlock_rdunlock(name) pthread_rwlock_unlock(& (name)->rw) #define my_atomic_rwlock_wrunlock(name) pthread_rwlock_unlock(& (name)->rw) +#define MY_ATOMIC_MODE "rwlocks" #endif -#ifdef HAVE_INLINE - -#define make_atomic_add(S) \ -static inline uint ## S my_atomic_add ## S( \ - my_atomic_ ## S ## _t *a, uint ## S v, my_atomic_rwlock_t *rw) \ -{ \ - uint ## S ret; \ - CHECK_RW; \ - if (rw) my_atomic_rwlock_wrlock(rw); \ - ret= a->val; \ - a->val+= v; \ - if (rw) my_atomic_rwlock_wrunlock(rw); \ - return ret; \ -} - -#define make_atomic_swap(S) \ -static inline uint ## S my_atomic_swap ## S( \ - my_atomic_ ## S ## _t *a, uint ## S v, my_atomic_rwlock_t *rw) \ -{ \ - uint ## S ret; \ - CHECK_RW; \ - if (rw) my_atomic_rwlock_wrlock(rw); \ - ret= a->val; \ - a->val= v; \ - if (rw) my_atomic_rwlock_wrunlock(rw); \ - return ret; \ -} - -#define make_atomic_cas(S) \ -static inline uint my_atomic_cas ## S(my_atomic_ ## S ## _t *a, \ - uint ## S *cmp, uint ## S set, my_atomic_rwlock_t *rw) \ -{ \ - uint ret; \ - CHECK_RW; \ - if (rw) my_atomic_rwlock_wrlock(rw); \ - if (ret= (a->val == *cmp)) a->val= set; else *cmp=a->val; \ - if (rw) my_atomic_rwlock_wrunlock(rw); \ - return ret; \ -} - -#define make_atomic_load(S) \ -static inline uint ## S my_atomic_load ## S( \ - my_atomic_ ## S ## _t *a, my_atomic_rwlock_t *rw) \ -{ \ - uint ## S ret; \ - CHECK_RW; \ - if (rw) my_atomic_rwlock_wrlock(rw); \ - ret= a->val; \ - if (rw) my_atomic_rwlock_wrunlock(rw); \ - return ret; \ -} - -#define make_atomic_store(S) \ -static inline void my_atomic_store ## S( \ - my_atomic_ ## S ## _t *a, uint ## S v, my_atomic_rwlock_t *rw) \ -{ \ - CHECK_RW; \ - if (rw) my_atomic_rwlock_rdlock(rw); \ - (a)->val= (v); \ - if (rw) my_atomic_rwlock_rdunlock(rw); \ -} - -#else /* no inline functions */ - -#define make_atomic_add(S) \ -extern uint ## S my_atomic_add ## S( \ - my_atomic_ ## S ## _t *a, uint ## S v, my_atomic_rwlock_t *rw); - -#define make_atomic_swap(S) \ -extern uint ## S my_atomic_swap ## S( \ - my_atomic_ ## S ## _t *a, uint ## S v, my_atomic_rwlock_t *rw); - -#define make_atomic_cas(S) \ -extern uint my_atomic_cas ## S(my_atomic_ ## S ## _t *a, \ - uint ## S *cmp, uint ## S set, my_atomic_rwlock_t *rw); - -#define make_atomic_load(S) \ -extern uint ## S my_atomic_load ## S( \ - my_atomic_ ## S ## _t *a, my_atomic_rwlock_t *rw); - -#define make_atomic_store(S) \ -extern void my_atomic_store ## S( \ - my_atomic_ ## S ## _t *a, uint ## S v, my_atomic_rwlock_t *rw); - -#endif - -make_atomic_add( 8) -make_atomic_add(16) -make_atomic_add(32) -make_atomic_add(64) -make_atomic_cas( 8) -make_atomic_cas(16) -make_atomic_cas(32) -make_atomic_cas(64) -make_atomic_load( 8) -make_atomic_load(16) -make_atomic_load(32) -make_atomic_load(64) -make_atomic_store( 8) -make_atomic_store(16) -make_atomic_store(32) -make_atomic_store(64) -make_atomic_swap( 8) -make_atomic_swap(16) -make_atomic_swap(32) -make_atomic_swap(64) -#undef make_atomic_add -#undef make_atomic_cas -#undef make_atomic_load -#undef make_atomic_store -#undef make_atomic_swap -#undef CHECK_RW - +#define make_atomic_add_body(S) int ## S sav; sav= *a; *a+= v; v=sav; +#define make_atomic_swap_body(S) int ## S sav; sav= *a; *a= v; v=sav; +#define make_atomic_cas_body(S) if ((ret= (*a == *cmp))) *a= set; else *cmp=*a; +#define make_atomic_load_body(S) ret= *a; +#define make_atomic_store_body(S) *a= v; diff --git a/include/atomic/x86-gcc.h b/include/atomic/x86-gcc.h index 7576db54d69..df6fcdc5ad2 100644 --- a/include/atomic/x86-gcc.h +++ b/include/atomic/x86-gcc.h @@ -16,44 +16,38 @@ /* XXX 64-bit atomic operations can be implemented using - cmpxchg8b, if necessary + cmpxchg8b, if necessary. Though I've heard that not all 64-bit + architectures support double-word (128-bit) cas. */ -/* fix -ansi errors while maintaining readability */ -#define asm __asm__ +#define MY_ATOMIC_MODE "gcc-x86" ## LOCK -#define make_atomic_add_body8 \ - asm volatile (LOCK "xadd %0, %1;" : "+r" (v) , "+m" (a->val)) -#define make_atomic_swap_body8 \ - asm volatile ("xchg %0, %1;" : "+r" (v) , "+m" (a->val)) -#define make_atomic_cas_body8 \ +/* fix -ansi errors while maintaining readability */ +#ifndef asm +#define asm __asm__ +#endif + +#define make_atomic_add_body(S) \ + asm volatile (LOCK "xadd %0, %1;" : "+r" (v) , "+m" (*a)) +#define make_atomic_swap_body(S) \ + asm volatile ("xchg %0, %1;" : "+r" (v) , "+m" (*a)) +#define make_atomic_cas_body(S) \ asm volatile (LOCK "cmpxchg %3, %0; setz %2;" \ - : "+m" (a->val), "+a" (*cmp), "=q" (ret): "r" (set)) + : "+m" (*a), "+a" (*cmp), "=q" (ret): "r" (set)) #ifdef MY_ATOMIC_MODE_DUMMY -#define make_atomic_load_body8 ret=a->val -#define make_atomic_store_body8 a->val=v +#define make_atomic_load_body(S) ret=*a +#define make_atomic_store_body(S) *a=v #else /* Actually 32-bit reads/writes are always atomic on x86 But we add LOCK here anyway to force memory barriers */ -#define make_atomic_load_body8 \ +#define make_atomic_load_body(S) \ ret=0; \ asm volatile (LOCK "cmpxchg %2, %0" \ - : "+m" (a->val), "+a" (ret): "r" (ret)) -#define make_atomic_store_body8 \ - asm volatile ("xchg %0, %1;" : "+m" (a->val) : "r" (v)) + : "+m" (*a), "+a" (ret): "r" (ret)) +#define make_atomic_store_body(S) \ + asm volatile ("xchg %0, %1;" : "+m" (*a) : "r" (v)) #endif -#define make_atomic_add_body16 make_atomic_add_body8 -#define make_atomic_add_body32 make_atomic_add_body8 -#define make_atomic_cas_body16 make_atomic_cas_body8 -#define make_atomic_cas_body32 make_atomic_cas_body8 -#define make_atomic_load_body16 make_atomic_load_body8 -#define make_atomic_load_body32 make_atomic_load_body8 -#define make_atomic_store_body16 make_atomic_store_body8 -#define make_atomic_store_body32 make_atomic_store_body8 -#define make_atomic_swap_body16 make_atomic_swap_body8 -#define make_atomic_swap_body32 make_atomic_swap_body8 - diff --git a/include/atomic/x86-msvc.h b/include/atomic/x86-msvc.h index 19645551196..a7e14d0e926 100644 --- a/include/atomic/x86-msvc.h +++ b/include/atomic/x86-msvc.h @@ -23,63 +23,75 @@ // (InterlockedCompareExchange, InterlockedCompareExchange16 // InterlockedExchangeAdd, InterlockedExchange) -#define make_atomic_add_body(REG) \ - _asm { \ - _asm mov REG, v \ - _asm LOCK xadd a->val, REG \ - _asm movzx v, REG \ +#ifndef _atomic_h_cleanup_ +#define _atomic_h_cleanup_ "atomic/x86-msvc.h" + +#define MY_ATOMIC_MODE "msvc-x86" ## LOCK + +#define make_atomic_add_body(S) \ + _asm { \ + _asm mov reg_ ## S, v \ + _asm LOCK xadd *a, reg_ ## S \ + _asm movzx v, reg_ ## S \ } -#define make_atomic_cas_body(AREG,REG2) \ - _asm { \ - _asm mov AREG, *cmp \ - _asm mov REG2, set \ - _asm LOCK cmpxchg a->val, REG2 \ - _asm mov *cmp, AREG \ - _asm setz al \ - _asm movzx ret, al \ +#define make_atomic_cas_body(S) \ + _asm { \ + _asm mov areg_ ## S, *cmp \ + _asm mov reg2_ ## S, set \ + _asm LOCK cmpxchg *a, reg2_ ## S \ + _asm mov *cmp, areg_ ## S \ + _asm setz al \ + _asm movzx ret, al \ } -#define make_atomic_swap_body(REG) \ - _asm { \ - _asm mov REG, v \ - _asm xchg a->val, REG \ - _asm mov v, REG \ +#define make_atomic_swap_body(S) \ + _asm { \ + _asm mov reg_ ## S, v \ + _asm xchg *a, reg_ ## S \ + _asm mov v, reg_ ## S \ } #ifdef MY_ATOMIC_MODE_DUMMY -#define make_atomic_load_body(AREG,REG) ret=a->val -#define make_atomic_store_body(REG) a->val=v +#define make_atomic_load_body(S) ret=*a +#define make_atomic_store_body(S) *a=v #else /* Actually 32-bit reads/writes are always atomic on x86 But we add LOCK here anyway to force memory barriers */ -#define make_atomic_load_body(AREG,REG2) \ - _asm { \ - _asm mov AREG, 0 \ - _asm mov REG2, AREG \ - _asm LOCK cmpxchg a->val, REG2 \ - _asm mov ret, AREG \ +#define make_atomic_load_body(S) \ + _asm { \ + _asm mov areg_ ## S, 0 \ + _asm mov reg2_ ## S, areg_ ## S \ + _asm LOCK cmpxchg *a, reg2_ ## S \ + _asm mov ret, areg_ ## S \ } -#define make_atomic_store_body(REG) \ - _asm { \ - _asm mov REG, v \ - _asm xchg a->val, REG \ +#define make_atomic_store_body(S) \ + _asm { \ + _asm mov reg_ ## S, v \ + _asm xchg *a, reg_ ## S \ } #endif -#define make_atomic_add_body8 make_atomic_add_body(al) -#define make_atomic_add_body16 make_atomic_add_body(ax) -#define make_atomic_add_body32 make_atomic_add_body(eax) -#define make_atomic_cas_body8 make_atomic_cas_body(al, bl) -#define make_atomic_cas_body16 make_atomic_cas_body(ax, bx) -#define make_atomic_cas_body32 make_atomic_cas_body(eax, ebx) -#define make_atomic_load_body8 make_atomic_load_body(al, bl) -#define make_atomic_load_body16 make_atomic_load_body(ax, bx) -#define make_atomic_load_body32 make_atomic_load_body(eax, ebx) -#define make_atomic_store_body8 make_atomic_store_body(al) -#define make_atomic_store_body16 make_atomic_store_body(ax) -#define make_atomic_store_body32 make_atomic_store_body(eax) -#define make_atomic_swap_body8 make_atomic_swap_body(al) -#define make_atomic_swap_body16 make_atomic_swap_body(ax) -#define make_atomic_swap_body32 make_atomic_swap_body(eax) +#define reg_8 al +#define reg_16 ax +#define reg_32 eax +#define areg_8 al +#define areg_16 ax +#define areg_32 eax +#define reg2_8 bl +#define reg2_16 bx +#define reg2_32 ebx + +#else /* cleanup */ + +#undef reg_8 +#undef reg_16 +#undef reg_32 +#undef areg_8 +#undef areg_16 +#undef areg_32 +#undef reg2_8 +#undef reg2_16 +#undef reg2_32 +#endif diff --git a/include/my_atomic.h b/include/my_atomic.h index 091edc0f57b..877443fe705 100644 --- a/include/my_atomic.h +++ b/include/my_atomic.h @@ -14,21 +14,9 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef atomic_rwlock_init +#ifndef my_atomic_rwlock_init -#ifdef MY_ATOMIC_EXTRA_DEBUG -#ifndef MY_ATOMIC_MODE_RWLOCKS -#error MY_ATOMIC_EXTRA_DEBUG can be only used with MY_ATOMIC_MODE_RWLOCKS -#endif -#define LOCK_PTR void *rw; -#else -#define LOCK_PTR -#endif - -typedef volatile struct {uint8 val; LOCK_PTR} my_atomic_8_t; -typedef volatile struct {uint16 val; LOCK_PTR} my_atomic_16_t; -typedef volatile struct {uint32 val; LOCK_PTR} my_atomic_32_t; -typedef volatile struct {uint64 val; LOCK_PTR} my_atomic_64_t; +#define intptr void * #ifndef MY_ATOMIC_MODE_RWLOCKS #include "atomic/nolock.h" @@ -38,6 +26,103 @@ typedef volatile struct {uint64 val; LOCK_PTR} my_atomic_64_t; #include "atomic/rwlock.h" #endif +#ifdef HAVE_INLINE + +#define make_atomic_add(S) \ +static inline int ## S my_atomic_add ## S( \ + int ## S volatile *a, int ## S v) \ +{ \ + make_atomic_add_body(S); \ + return v; \ +} + +#define make_atomic_swap(S) \ +static inline int ## S my_atomic_swap ## S( \ + int ## S volatile *a, int ## S v) \ +{ \ + make_atomic_swap_body(S); \ + return v; \ +} + +#define make_atomic_cas(S) \ +static inline int my_atomic_cas ## S(int ## S volatile *a, \ + int ## S *cmp, int ## S set) \ +{ \ + int8 ret; \ + make_atomic_cas_body(S); \ + return ret; \ +} + +#define make_atomic_load(S) \ +static inline int ## S my_atomic_load ## S(int ## S volatile *a) \ +{ \ + int ## S ret; \ + make_atomic_load_body(S); \ + return ret; \ +} + +#define make_atomic_store(S) \ +static inline void my_atomic_store ## S( \ + int ## S volatile *a, int ## S v) \ +{ \ + make_atomic_store_body(S); \ +} + +#else /* no inline functions */ + +#define make_atomic_add(S) \ +extern int ## S my_atomic_add ## S(int ## S volatile *a, int ## S v); + +#define make_atomic_swap(S) \ +extern int ## S my_atomic_swap ## S(int ## S volatile *a, int ## S v); + +#define make_atomic_cas(S) \ +extern int my_atomic_cas ## S(int ## S volatile *a, int ## S *cmp, int ## S set); + +#define make_atomic_load(S) \ +extern int ## S my_atomic_load ## S(int ## S volatile *a); + +#define make_atomic_store(S) \ +extern void my_atomic_store ## S(int ## S volatile *a, int ## S v); + +#endif + +make_atomic_add( 8) +make_atomic_add(16) +make_atomic_add(32) + +make_atomic_cas( 8) +make_atomic_cas(16) +make_atomic_cas(32) +make_atomic_cas(ptr) + +make_atomic_load( 8) +make_atomic_load(16) +make_atomic_load(32) +make_atomic_load(ptr) + +make_atomic_store( 8) +make_atomic_store(16) +make_atomic_store(32) +make_atomic_store(ptr) + +make_atomic_swap( 8) +make_atomic_swap(16) +make_atomic_swap(32) +make_atomic_swap(ptr) + +#undef make_atomic_add +#undef make_atomic_cas +#undef make_atomic_load +#undef make_atomic_store +#undef make_atomic_swap +#undef intaptr + +#ifdef _atomic_h_cleanup_ +#include _atomic_h_cleanup_ +#undef _atomic_h_cleanup_ +#endif + #define MY_ATOMIC_OK 0 #define MY_ATOMIC_NOT_1CPU 1 extern int my_atomic_initialize(); diff --git a/unittest/examples/no_plan-t.c b/unittest/examples/no_plan-t.c index 98e4d06def6..a40b46dc278 100644 --- a/unittest/examples/no_plan-t.c +++ b/unittest/examples/no_plan-t.c @@ -14,8 +14,8 @@ tests are skipped for an unknown reason. */ int main() { - ok(1, NULL); - ok(1, NULL); - ok(1, NULL); + ok(1, " "); + ok(1, " "); + ok(1, " "); return exit_status(); } diff --git a/unittest/mysys/Makefile.am b/unittest/mysys/Makefile.am index b1e0356bac6..8e6255b9e68 100644 --- a/unittest/mysys/Makefile.am +++ b/unittest/mysys/Makefile.am @@ -2,10 +2,10 @@ AM_CPPFLAGS = @ZLIB_INCLUDES@ -I$(top_builddir)/include AM_CPPFLAGS += -I$(top_srcdir)/include -I$(top_srcdir)/unittest/mytap -AM_LDFLAGS = -L$(top_builddir)/unittest/mytap -L$(top_builddir)/mysys -AM_LDFLAGS += -L$(top_builddir)/strings -L$(top_builddir)/dbug - -LDADD = -lmytap -lmysys -ldbug -lmystrings +LDADD = $(top_builddir)/unittest/mytap/libmytap.a \ + $(top_builddir)/mysys/libmysys.a \ + $(top_builddir)/dbug/libdbug.a \ + $(top_builddir)/strings/libmystrings.a noinst_PROGRAMS = bitmap-t base64-t my_atomic-t diff --git a/unittest/mysys/my_atomic-t.c b/unittest/mysys/my_atomic-t.c index 8a3fe129b07..71408ce957f 100644 --- a/unittest/mysys/my_atomic-t.c +++ b/unittest/mysys/my_atomic-t.c @@ -14,13 +14,12 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include - #include +#include #include #include -my_atomic_32_t a32,b32,c32; +int32 a32,b32,c32; my_atomic_rwlock_t rwl; pthread_attr_t thr_attr; @@ -36,8 +35,13 @@ pthread_handler_t test_atomic_add_handler(void *arg) for (x=((int)(&m)); m ; m--) { x=x*m+0x87654321; - my_atomic_add32(&a32, x, &rwl); - my_atomic_add32(&a32, -x, &rwl); + my_atomic_rwlock_wrlock(&rwl); + my_atomic_add32(&a32, x); + my_atomic_rwlock_wrunlock(&rwl); + + my_atomic_rwlock_wrlock(&rwl); + my_atomic_add32(&a32, -x); + my_atomic_rwlock_wrunlock(&rwl); } pthread_mutex_lock(&mutex); N--; @@ -57,17 +61,33 @@ pthread_handler_t test_atomic_add_handler(void *arg) pthread_handler_t test_atomic_swap_handler(void *arg) { int m=*(int *)arg; - uint32 x=my_atomic_add32(&b32, 1, &rwl); + int32 x; - my_atomic_add32(&a32, x, &rwl); + my_atomic_rwlock_wrlock(&rwl); + x=my_atomic_add32(&b32, 1); + my_atomic_rwlock_wrunlock(&rwl); + + my_atomic_rwlock_wrlock(&rwl); + my_atomic_add32(&a32, x); + my_atomic_rwlock_wrunlock(&rwl); for (; m ; m--) - x=my_atomic_swap32(&c32, x,&rwl); + { + my_atomic_rwlock_wrlock(&rwl); + x=my_atomic_swap32(&c32, x); + my_atomic_rwlock_wrunlock(&rwl); + } if (!x) - x=my_atomic_swap32(&c32, x,&rwl); + { + my_atomic_rwlock_wrlock(&rwl); + x=my_atomic_swap32(&c32, x); + my_atomic_rwlock_wrunlock(&rwl); + } - my_atomic_add32(&a32, -x, &rwl); + my_atomic_rwlock_wrlock(&rwl); + my_atomic_add32(&a32, -x); + my_atomic_rwlock_wrunlock(&rwl); pthread_mutex_lock(&mutex); N--; @@ -82,14 +102,25 @@ pthread_handler_t test_atomic_swap_handler(void *arg) */ pthread_handler_t test_atomic_cas_handler(void *arg) { - int m=*(int *)arg; - int32 x; + int m=*(int *)arg, ok; + int32 x,y; for (x=((int)(&m)); m ; m--) { - uint32 y=my_atomic_load32(&a32, &rwl); + my_atomic_rwlock_wrlock(&rwl); + y=my_atomic_load32(&a32); + my_atomic_rwlock_wrunlock(&rwl); + x=x*m+0x87654321; - while (!my_atomic_cas32(&a32, &y, y+x, &rwl)) ; - while (!my_atomic_cas32(&a32, &y, y-x, &rwl)) ; + do { + my_atomic_rwlock_wrlock(&rwl); + ok=my_atomic_cas32(&a32, &y, y+x); + my_atomic_rwlock_wrunlock(&rwl); + } while (!ok); + do { + my_atomic_rwlock_wrlock(&rwl); + ok=my_atomic_cas32(&a32, &y, y-x); + my_atomic_rwlock_wrunlock(&rwl); + } while (!ok); } pthread_mutex_lock(&mutex); N--; @@ -103,9 +134,9 @@ void test_atomic(const char *test, pthread_handler handler, int n, int m) pthread_t t; ulonglong now=my_getsystime(); - my_atomic_store32(&a32, 0, &rwl); - my_atomic_store32(&b32, 0, &rwl); - my_atomic_store32(&c32, 0, &rwl); + a32= 0; + b32= 0; + c32= 0; diag("Testing %s with %d threads, %d iterations... ", test, n, m); for (N=n ; n ; n--) @@ -116,8 +147,7 @@ void test_atomic(const char *test, pthread_handler handler, int n, int m) pthread_cond_wait(&cond, &mutex); pthread_mutex_unlock(&mutex); now=my_getsystime()-now; - ok(my_atomic_load32(&a32, &rwl) == 0, - "tested %s in %g secs", test, ((double)now)/1e7); + ok(a32 == 0, "tested %s in %g secs", test, ((double)now)/1e7); } int main() diff --git a/unittest/mytap/tap.c b/unittest/mytap/tap.c index 2b728670cea..d3f5013b4c9 100644 --- a/unittest/mytap/tap.c +++ b/unittest/mytap/tap.c @@ -199,8 +199,8 @@ int exit_status() { if (g_test.plan != g_test.last) { - diag("%d tests planned but only %d executed", - g_test.plan, g_test.last); + diag("%d tests planned but%s %d executed", + g_test.plan, (g_test.plan > g_test.last ? " only" : ""), g_test.last); return EXIT_FAILURE; } From 9f8c532f0c7d8472cb809be6f80bb596d38b336b Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 19 Jun 2006 04:26:27 +0400 Subject: [PATCH 64/65] rpl_log.test, rpl_stm_log.result: Fixed failing test case field.cc, item_cmpfunc.cc, select.result: After merge fix mysql-test/r/select.result: After merge fix mysql-test/r/rpl_stm_log.result: Fixed failing test case sql/item_cmpfunc.cc: After merge fix sql/field.cc: After merge fix mysql-test/extra/rpl_tests/rpl_log.test: Fixed failing test case --- mysql-test/extra/rpl_tests/rpl_log.test | 11 +++-- mysql-test/r/rpl_stm_log.result | 14 +++--- mysql-test/r/select.result | 66 +------------------------ sql/field.cc | 4 +- sql/item_cmpfunc.cc | 23 +++++++-- 5 files changed, 36 insertions(+), 82 deletions(-) diff --git a/mysql-test/extra/rpl_tests/rpl_log.test b/mysql-test/extra/rpl_tests/rpl_log.test index d87030ba531..cfbc8110d06 100644 --- a/mysql-test/extra/rpl_tests/rpl_log.test +++ b/mysql-test/extra/rpl_tests/rpl_log.test @@ -125,6 +125,12 @@ show slave status; --error 1220 show binlog events in 'slave-bin.000005' from 4; +# The table drops caused Cluster Replication wrapper to fail as event ID would never be the same.# Moving drops here. + +DROP TABLE t1; +DROP TABLE t2; +DROP TABLE t3; + # # Bug #6880: LAST_INSERT_ID() within a statement # @@ -135,13 +141,10 @@ reset master; set insert_id=5; insert into t1 values (NULL, last_insert_id()), (NULL, last_insert_id()); --replace_result $VERSION VERSION +--replace_column 2 # 5 # show binlog events; select * from t1; drop table t1; # End of 4.1 tests -# The table drops caused Cluster Replication wrapper to fail as event ID would never be the same.# Moving drops here. -DROP TABLE t1; -DROP TABLE t2; -DROP TABLE t3; diff --git a/mysql-test/r/rpl_stm_log.result b/mysql-test/r/rpl_stm_log.result index 23639cb7a99..e0b1aa12c9b 100644 --- a/mysql-test/r/rpl_stm_log.result +++ b/mysql-test/r/rpl_stm_log.result @@ -95,6 +95,9 @@ Slave_IO_State Master_Host Master_User Master_Port Connect_Retry Master_Log_File # 127.0.0.1 root MASTER_PORT 1 master-bin.000002 388 # # master-bin.000002 Yes Yes # 0 0 388 # None 0 No # show binlog events in 'slave-bin.000005' from 4; ERROR HY000: Error when executing command SHOW BINLOG EVENTS: Could not find target log +DROP TABLE t1; +DROP TABLE t2; +DROP TABLE t3; create table t1(a int auto_increment primary key, b int); insert into t1 values (NULL, 1); reset master; @@ -102,16 +105,13 @@ set insert_id=5; insert into t1 values (NULL, last_insert_id()), (NULL, last_insert_id()); show binlog events; Log_name Pos Event_type Server_id End_log_pos Info -slave-bin.000001 4 Format_desc 2 98 Server ver: VERSION, Binlog ver: 4 -slave-bin.000001 98 Intvar 2 126 LAST_INSERT_ID=1 -slave-bin.000001 126 Intvar 2 154 INSERT_ID=5 -slave-bin.000001 154 Query 2 289 use `test`; insert into t1 values (NULL, last_insert_id()), (NULL, last_insert_id()) +slave-bin.000001 # Format_desc 2 # Server ver: VERSION, Binlog ver: 4 +slave-bin.000001 # Intvar 2 # LAST_INSERT_ID=1 +slave-bin.000001 # Intvar 2 # INSERT_ID=5 +slave-bin.000001 # Query 2 # use `test`; insert into t1 values (NULL, last_insert_id()), (NULL, last_insert_id()) select * from t1; a b 1 1 5 1 6 1 drop table t1; -DROP TABLE t1; -DROP TABLE t2; -DROP TABLE t3; diff --git a/mysql-test/r/select.result b/mysql-test/r/select.result index 1e72d12ba06..7e54acfae2a 100644 --- a/mysql-test/r/select.result +++ b/mysql-test/r/select.result @@ -2726,74 +2726,10 @@ EXPLAIN SELECT * FROM t1 IGNORE INDEX (idx); id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 ALL NULL NULL NULL NULL 3 EXPLAIN SELECT * FROM t1 IGNORE INDEX (a); -ERROR HY000: Key 'a' doesn't exist in table 't1' +ERROR 42000: Key 'a' doesn't exist in table 't1' EXPLAIN SELECT * FROM t1 FORCE INDEX (a); -ERROR HY000: Key 'a' doesn't exist in table 't1' -DROP TABLE t1; -CREATE TABLE t1 (a int, INDEX idx(a)); -INSERT INTO t1 VALUES (2), (3), (1); -EXPLAIN SELECT * FROM t1 IGNORE INDEX (idx); -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ALL NULL NULL NULL NULL 3 -EXPLAIN SELECT * FROM t1 IGNORE INDEX (a); -ERROR 42000: Key 'a' doesn't exist in table 't1' ERROR 42000: Key 'a' doesn't exist in table 't1' DROP TABLE t1; -CREATE TABLE t1 ( city char(30) ); -INSERT INTO t1 VALUES ('London'); -INSERT INTO t1 VALUES ('Paris'); -SELECT * FROM t1 WHERE city='London'; -city -London -SELECT * FROM t1 WHERE city='london'; -city -London -EXPLAIN SELECT * FROM t1 WHERE city='London' AND city='london'; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ALL NULL NULL NULL NULL 2 Using where -SELECT * FROM t1 WHERE city='London' AND city='london'; -city -London -EXPLAIN SELECT * FROM t1 WHERE city LIKE '%london%' AND city='London'; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ALL NULL NULL NULL NULL 2 Using where -SELECT * FROM t1 WHERE city LIKE '%london%' AND city='London'; -city -London -DROP TABLE t1; -create table t1 (a int(11) unsigned, b int(11) unsigned); -insert into t1 values (1,0), (1,1), (1,2); -select a-b from t1 order by 1; -a-b -0 -1 -18446744073709551615 -select a-b , (a-b < 0) from t1 order by 1; -a-b (a-b < 0) -0 0 -1 0 -18446744073709551615 0 -select a-b as d, (a-b >= 0), b from t1 group by b having d >= 0; -d (a-b >= 0) b -1 1 0 -0 1 1 -18446744073709551615 1 2 -select cast((a - b) as unsigned) from t1 order by 1; -cast((a - b) as unsigned) -0 -1 -18446744073709551615 -drop table t1; -create table t1 (a int(11)); -select all all * from t1; -a -select distinct distinct * from t1; -a -select all distinct * from t1; -ERROR HY000: Incorrect usage of ALL and DISTINCT -select distinct all * from t1; -ERROR HY000: Incorrect usage of ALL and DISTINCT -drop table t1; CREATE TABLE t1 ( K2C4 varchar(4) character set latin1 collate latin1_bin NOT NULL default '', K4N4 varchar(4) character set latin1 collate latin1_bin NOT NULL default '0000', diff --git a/sql/field.cc b/sql/field.cc index df735c59e08..af81214adda 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -50,8 +50,8 @@ const char field_separator=','; #define BLOB_PACK_LENGTH_TO_MAX_LENGH(arg) \ ((ulong) ((LL(1) << min(arg, 4) * 8) - LL(1))) -#define ASSERT_COLUMN_MARKED_FOR_READ DBUG_ASSERT(!table->read_set || bitmap_is_set(table->read_set, field_index)) -#define ASSERT_COLUMN_MARKED_FOR_WRITE DBUG_ASSERT(!table->write_set || bitmap_is_set(table->write_set, field_index)) +#define ASSERT_COLUMN_MARKED_FOR_READ DBUG_ASSERT(!table || (!table->read_set || bitmap_is_set(table->read_set, field_index))) +#define ASSERT_COLUMN_MARKED_FOR_WRITE DBUG_ASSERT(!table || (!table->write_set || bitmap_is_set(table->write_set, field_index))) /* Rules for merging different types of fields in UNION diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index a3464c3bc25..3bff7878f02 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -148,7 +148,7 @@ static void agg_cmp_type(THD *thd, Item_result *type, Item **items, uint nitems) } else if (res == Item::FUNC_ITEM) { - field= items[i]->tmp_table_field_from_field_type(0); + field= items[i]->tmp_table_field_from_field_type(0, 0); if (field) field->move_field(buff, &null_byte, 0); break; @@ -354,9 +354,21 @@ longlong Item_func_nop_all::val_int() static bool convert_constant_item(THD *thd, Field *field, Item **item) { int result= 0; + if (!(*item)->with_subselect && (*item)->const_item()) { + TABLE *table= field->table; + ulong orig_sql_mode= thd->variables.sql_mode; + my_bitmap_map *old_write_map; + my_bitmap_map *old_read_map; + + if (table) + { + old_write_map= dbug_tmp_use_all_columns(table, table->write_set); + old_read_map= dbug_tmp_use_all_columns(table, table->read_set); + } /* For comparison purposes allow invalid dates like 2000-01-32 */ + thd->variables.sql_mode|= MODE_INVALID_DATES; if (!(*item)->save_in_field(field, 1) && !((*item)->null_value)) { Item *tmp= new Item_int_with_ref(field->val_int(), *item, @@ -365,9 +377,12 @@ static bool convert_constant_item(THD *thd, Field *field, Item **item) thd->change_item_tree(item, tmp); result= 1; // Item was replaced } - table->in_use->variables.sql_mode= orig_sql_mode; - dbug_tmp_restore_column_map(table->write_set, old_write_map); - dbug_tmp_restore_column_map(table->read_set, old_read_map); + thd->variables.sql_mode= orig_sql_mode; + if (table) + { + dbug_tmp_restore_column_map(table->write_set, old_write_map); + dbug_tmp_restore_column_map(table->read_set, old_read_map); + } } return result; } From b7aa1c23d843b65eb37beaeaa7e5ac25a688a21f Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 20 Jun 2006 00:30:19 +1000 Subject: [PATCH 65/65] BUG#20073 information_schema.FILES for UNDO LOG give incorrect EXTENT_SIZE, FREE_EXTENTS, sql/ha_ndbcluster.cc: fix total extents display for FILES table. fix extra field to also contain UNDO_BUFFER_SIZE --- sql/ha_ndbcluster.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sql/ha_ndbcluster.cc b/sql/ha_ndbcluster.cc index 2d623702670..6c66ba620c9 100644 --- a/sql/ha_ndbcluster.cc +++ b/sql/ha_ndbcluster.cc @@ -10364,7 +10364,7 @@ static int ndbcluster_fill_files_table(THD *thd, TABLE_LIST *tables, table->field[c++]->set_null(); // DELETED_ROWS table->field[c++]->set_null(); // UPDATE_COUNT table->field[c++]->store(lfg.getUndoFreeWords()); // FREE_EXTENTS - table->field[c++]->store(lfg.getUndoBufferSize()); // TOTAL_EXTENTS + table->field[c++]->store(uf.getSize()/4); // TOTAL_EXTENTS table->field[c++]->store(4); // EXTENT_SIZE table->field[c++]->store(uf.getSize()); // INITIAL_SIZE @@ -10394,8 +10394,8 @@ static int ndbcluster_fill_files_table(THD *thd, TABLE_LIST *tables, table->field[c++]->store("NORMAL", 6, system_charset_info); - char extra[30]; - int len= my_snprintf(extra,sizeof(extra),"CLUSTER_NODE=%u",id); + char extra[100]; + int len= my_snprintf(extra,sizeof(extra),"CLUSTER_NODE=%u;UNDO_BUFFER_SIZE=%lu",id,lfg.getUndoBufferSize()); table->field[c]->store(extra, len, system_charset_info); schema_table_store_record(thd, table); }