Drizzled Public API Documentation

transaction_services.cc
00001 /* -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*-
00002  *  vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
00003  *
00004  *  Copyright (C) 2008 Sun Microsystems, Inc.
00005  *  Copyright (C) 2010 Jay Pipes <jaypipes@gmail.com>
00006  *
00007  *  This program is free software; you can redistribute it and/or modify
00008  *  it under the terms of the GNU General Public License as published by
00009  *  the Free Software Foundation; version 2 of the License.
00010  *
00011  *  This program is distributed in the hope that it will be useful,
00012  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  *  GNU General Public License for more details.
00015  *
00016  *  You should have received a copy of the GNU General Public License
00017  *  along with this program; if not, write to the Free Software
00018  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00019  */
00020 
00050 #include <config.h>
00051 #include <drizzled/current_session.h>
00052 #include <drizzled/error.h>
00053 #include <drizzled/gettext.h>
00054 #include <drizzled/probes.h>
00055 #include <drizzled/sql_parse.h>
00056 #include <drizzled/session.h>
00057 #include <drizzled/session/times.h>
00058 #include <drizzled/sql_base.h>
00059 #include <drizzled/replication_services.h>
00060 #include <drizzled/transaction_services.h>
00061 #include <drizzled/transaction_context.h>
00062 #include <drizzled/message/transaction.pb.h>
00063 #include <drizzled/message/statement_transform.h>
00064 #include <drizzled/resource_context.h>
00065 #include <drizzled/lock.h>
00066 #include <drizzled/item/int.h>
00067 #include <drizzled/item/empty_string.h>
00068 #include <drizzled/field/epoch.h>
00069 #include <drizzled/plugin/client.h>
00070 #include <drizzled/plugin/monitored_in_transaction.h>
00071 #include <drizzled/plugin/transactional_storage_engine.h>
00072 #include <drizzled/plugin/xa_resource_manager.h>
00073 #include <drizzled/plugin/xa_storage_engine.h>
00074 #include <drizzled/internal/my_sys.h>
00075 #include <drizzled/statistics_variables.h>
00076 #include <drizzled/system_variables.h>
00077 #include <drizzled/session/transactions.h>
00078 
00079 #include <vector>
00080 #include <algorithm>
00081 #include <functional>
00082 #include <google/protobuf/repeated_field.h>
00083 
00084 using namespace std;
00085 using namespace google;
00086 
00087 namespace drizzled {
00088 
00307 static plugin::XaStorageEngine& xa_storage_engine()
00308 {
00309   static plugin::XaStorageEngine& engine= static_cast<plugin::XaStorageEngine&>(*plugin::StorageEngine::findByName("InnoDB"));
00310   return engine;
00311 }
00312 
00313 void TransactionServices::registerResourceForStatement(Session& session,
00314                                                        plugin::MonitoredInTransaction *monitored,
00315                                                        plugin::TransactionalStorageEngine *engine)
00316 {
00317   if (session_test_options(&session, OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))
00318   {
00319     /* 
00320      * Now we automatically register this resource manager for the
00321      * normal transaction.  This is fine because a statement
00322      * transaction registration should always enlist the resource
00323      * in the normal transaction which contains the statement
00324      * transaction.
00325      */
00326     registerResourceForTransaction(session, monitored, engine);
00327   }
00328 
00329   TransactionContext& trans= session.transaction.stmt;
00330   ResourceContext& resource_context= session.getResourceContext(*monitored, 0);
00331 
00332   if (resource_context.isStarted())
00333     return; /* already registered, return */
00334 
00335   assert(monitored->participatesInSqlTransaction());
00336   assert(not monitored->participatesInXaTransaction());
00337 
00338   resource_context.setMonitored(monitored);
00339   resource_context.setTransactionalStorageEngine(engine);
00340   trans.registerResource(&resource_context);
00341   trans.no_2pc= true;
00342 }
00343 
00344 void TransactionServices::registerResourceForStatement(Session& session,
00345                                                        plugin::MonitoredInTransaction *monitored,
00346                                                        plugin::TransactionalStorageEngine *engine,
00347                                                        plugin::XaResourceManager *resource_manager)
00348 {
00349   if (session_test_options(&session, OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))
00350   {
00351     /* 
00352      * Now we automatically register this resource manager for the
00353      * normal transaction.  This is fine because a statement
00354      * transaction registration should always enlist the resource
00355      * in the normal transaction which contains the statement
00356      * transaction.
00357      */
00358     registerResourceForTransaction(session, monitored, engine, resource_manager);
00359   }
00360 
00361   TransactionContext& trans= session.transaction.stmt;
00362   ResourceContext& resource_context= session.getResourceContext(*monitored, 0);
00363 
00364   if (resource_context.isStarted())
00365     return; /* already registered, return */
00366 
00367   assert(monitored->participatesInXaTransaction());
00368   assert(monitored->participatesInSqlTransaction());
00369 
00370   resource_context.setMonitored(monitored);
00371   resource_context.setTransactionalStorageEngine(engine);
00372   resource_context.setXaResourceManager(resource_manager);
00373   trans.registerResource(&resource_context);
00374 }
00375 
00376 void TransactionServices::registerResourceForTransaction(Session& session,
00377                                                          plugin::MonitoredInTransaction *monitored,
00378                                                          plugin::TransactionalStorageEngine *engine)
00379 {
00380   TransactionContext& trans= session.transaction.all;
00381   ResourceContext& resource_context= session.getResourceContext(*monitored, 1);
00382 
00383   if (resource_context.isStarted())
00384     return; /* already registered, return */
00385 
00386   session.server_status|= SERVER_STATUS_IN_TRANS;
00387 
00388   trans.registerResource(&resource_context);
00389 
00390   assert(monitored->participatesInSqlTransaction());
00391   assert(not monitored->participatesInXaTransaction());
00392 
00393   resource_context.setMonitored(monitored);
00394   resource_context.setTransactionalStorageEngine(engine);
00395   trans.no_2pc= true;
00396 
00397   if (session.transaction.xid_state.xid.is_null())
00398     session.transaction.xid_state.xid.set(session.getQueryId());
00399 
00400   /* Only true if user is executing a BEGIN WORK/START TRANSACTION */
00401   if (not session.getResourceContext(*monitored, 0).isStarted())
00402     registerResourceForStatement(session, monitored, engine);
00403 }
00404 
00405 void TransactionServices::registerResourceForTransaction(Session& session,
00406                                                          plugin::MonitoredInTransaction *monitored,
00407                                                          plugin::TransactionalStorageEngine *engine,
00408                                                          plugin::XaResourceManager *resource_manager)
00409 {
00410   TransactionContext *trans= &session.transaction.all;
00411   ResourceContext& resource_context= session.getResourceContext(*monitored, 1);
00412 
00413   if (resource_context.isStarted())
00414     return; /* already registered, return */
00415 
00416   session.server_status|= SERVER_STATUS_IN_TRANS;
00417 
00418   trans->registerResource(&resource_context);
00419 
00420   assert(monitored->participatesInSqlTransaction());
00421 
00422   resource_context.setMonitored(monitored);
00423   resource_context.setXaResourceManager(resource_manager);
00424   resource_context.setTransactionalStorageEngine(engine);
00425   trans->no_2pc= true;
00426 
00427   if (session.transaction.xid_state.xid.is_null())
00428     session.transaction.xid_state.xid.set(session.getQueryId());
00429 
00430   engine->startTransaction(&session, START_TRANS_NO_OPTIONS);
00431 
00432   /* Only true if user is executing a BEGIN WORK/START TRANSACTION */
00433   if (! session.getResourceContext(*monitored, 0).isStarted())
00434     registerResourceForStatement(session, monitored, engine, resource_manager);
00435 }
00436 
00437 void TransactionServices::allocateNewTransactionId()
00438 {
00439   if (! ReplicationServices::isActive())
00440   {
00441     return;
00442   }
00443 
00444   Session *my_session= current_session;
00445   uint64_t xa_id= xa_storage_engine().getNewTransactionId(my_session);
00446   my_session->setXaId(xa_id);
00447 }
00448 
00449 uint64_t TransactionServices::getCurrentTransactionId(Session& session)
00450 {
00451   if (session.getXaId() == 0)
00452   {
00453     session.setXaId(xa_storage_engine().getNewTransactionId(&session)); 
00454   }
00455 
00456   return session.getXaId();
00457 }
00458 
00459 int TransactionServices::commitTransaction(Session& session,
00460                                            bool normal_transaction)
00461 {
00462   int error= 0, cookie= 0;
00463   /*
00464     'all' means that this is either an explicit commit issued by
00465     user, or an implicit commit issued by a DDL.
00466   */
00467   TransactionContext *trans= normal_transaction ? &session.transaction.all : &session.transaction.stmt;
00468   TransactionContext::ResourceContexts &resource_contexts= trans->getResourceContexts();
00469 
00470   bool is_real_trans= normal_transaction || session.transaction.all.getResourceContexts().empty();
00471 
00472   /*
00473     We must not commit the normal transaction if a statement
00474     transaction is pending. Otherwise statement transaction
00475     flags will not get propagated to its normal transaction's
00476     counterpart.
00477   */
00478   assert(session.transaction.stmt.getResourceContexts().empty() ||
00479               trans == &session.transaction.stmt);
00480 
00481   if (resource_contexts.empty() == false)
00482   {
00483     if (is_real_trans && session.wait_if_global_read_lock(false, false))
00484     {
00485       rollbackTransaction(session, normal_transaction);
00486       return 1;
00487     }
00488 
00489     /*
00490      * If replication is on, we do a PREPARE on the resource managers, push the
00491      * Transaction message across the replication stream, and then COMMIT if the
00492      * replication stream returned successfully.
00493      */
00494     if (shouldConstructMessages())
00495     {
00496       BOOST_FOREACH(TransactionContext::ResourceContexts::reference resource_context, resource_contexts)
00497       {
00498         if (error)
00499           break;
00500         /*
00501           Do not call two-phase commit if this particular
00502           transaction is read-only. This allows for simpler
00503           implementation in engines that are always read-only.
00504         */
00505         if (! resource_context->hasModifiedData())
00506           continue;
00507 
00508         plugin::MonitoredInTransaction *resource= resource_context->getMonitored();
00509 
00510         if (resource->participatesInXaTransaction())
00511         {
00512           if (int err= resource_context->getXaResourceManager()->xaPrepare(&session, normal_transaction))
00513           {
00514             my_error(ER_ERROR_DURING_COMMIT, MYF(0), err);
00515             error= 1;
00516           }
00517           else
00518           {
00519             session.status_var.ha_prepare_count++;
00520           }
00521         }
00522       }
00523       if (error == 0 && is_real_trans)
00524       {
00525         /*
00526          * Push the constructed Transaction messages across to
00527          * replicators and appliers.
00528          */
00529         error= commitTransactionMessage(session);
00530       }
00531       if (error)
00532       {
00533         rollbackTransaction(session, normal_transaction);
00534         error= 1;
00535         goto end;
00536       }
00537     }
00538     error= commitPhaseOne(session, normal_transaction) ? (cookie ? 2 : 1) : 0;
00539 end:
00540     if (is_real_trans)
00541       session.startWaitingGlobalReadLock();
00542   }
00543   return error;
00544 }
00545 
00550 int TransactionServices::commitPhaseOne(Session& session,
00551                                         bool normal_transaction)
00552 {
00553   int error=0;
00554   TransactionContext *trans= normal_transaction ? &session.transaction.all : &session.transaction.stmt;
00555   TransactionContext::ResourceContexts &resource_contexts= trans->getResourceContexts();
00556 
00557   bool is_real_trans= normal_transaction || session.transaction.all.getResourceContexts().empty();
00558   bool all= normal_transaction;
00559 
00560   /* If we're in autocommit then we have a real transaction to commit
00561      (except if it's BEGIN)
00562   */
00563   if (! session_test_options(&session, OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))
00564     all= true;
00565 
00566   if (resource_contexts.empty() == false)
00567   {
00568     BOOST_FOREACH(TransactionContext::ResourceContexts::reference resource_context, resource_contexts)
00569     {
00570       plugin::MonitoredInTransaction *resource= resource_context->getMonitored();
00571 
00572       if (resource->participatesInXaTransaction())
00573       {
00574         if (int err= resource_context->getXaResourceManager()->xaCommit(&session, all))
00575         {
00576           my_error(ER_ERROR_DURING_COMMIT, MYF(0), err);
00577           error= 1;
00578         }
00579         else if (normal_transaction)
00580         {
00581           session.status_var.ha_commit_count++;
00582         }
00583       }
00584       else if (resource->participatesInSqlTransaction())
00585       {
00586         if (int err= resource_context->getTransactionalStorageEngine()->commit(&session, all))
00587         {
00588           my_error(ER_ERROR_DURING_COMMIT, MYF(0), err);
00589           error= 1;
00590         }
00591         else if (normal_transaction)
00592         {
00593           session.status_var.ha_commit_count++;
00594         }
00595       }
00596       resource_context->reset(); /* keep it conveniently zero-filled */
00597     }
00598 
00599     if (is_real_trans)
00600       session.transaction.xid_state.xid.set_null();
00601 
00602     if (normal_transaction)
00603     {
00604       session.variables.tx_isolation= session.session_tx_isolation;
00605       session.transaction.cleanup();
00606     }
00607   }
00608   trans->reset();
00609   return error;
00610 }
00611 
00612 int TransactionServices::rollbackTransaction(Session& session,
00613                                              bool normal_transaction)
00614 {
00615   int error= 0;
00616   TransactionContext *trans= normal_transaction ? &session.transaction.all : &session.transaction.stmt;
00617   TransactionContext::ResourceContexts &resource_contexts= trans->getResourceContexts();
00618 
00619   bool is_real_trans= normal_transaction || session.transaction.all.getResourceContexts().empty();
00620   bool all = normal_transaction || !session_test_options(&session, OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN);
00621 
00622   /*
00623     We must not rollback the normal transaction if a statement
00624     transaction is pending.
00625   */
00626   assert(session.transaction.stmt.getResourceContexts().empty() || trans == &session.transaction.stmt);
00627 
00628   if (resource_contexts.empty() == false)
00629   {
00630     BOOST_FOREACH(TransactionContext::ResourceContexts::reference resource_context, resource_contexts)
00631     {
00632       plugin::MonitoredInTransaction *resource= resource_context->getMonitored();
00633 
00634       if (resource->participatesInXaTransaction())
00635       {
00636         if (int err= resource_context->getXaResourceManager()->xaRollback(&session, all))
00637         {
00638           my_error(ER_ERROR_DURING_ROLLBACK, MYF(0), err);
00639           error= 1;
00640         }
00641         else if (normal_transaction)
00642         {
00643           session.status_var.ha_rollback_count++;
00644         }
00645       }
00646       else if (resource->participatesInSqlTransaction())
00647       {
00648         if (int err= resource_context->getTransactionalStorageEngine()->rollback(&session, all))
00649         {
00650           my_error(ER_ERROR_DURING_ROLLBACK, MYF(0), err);
00651           error= 1;
00652         }
00653         else if (normal_transaction)
00654         {
00655           session.status_var.ha_rollback_count++;
00656         }
00657       }
00658       resource_context->reset(); /* keep it conveniently zero-filled */
00659     }
00660     
00661     /* 
00662      * We need to signal the ROLLBACK to ReplicationServices here
00663      * BEFORE we set the transaction ID to NULL.  This is because
00664      * if a bulk segment was sent to replicators, we need to send
00665      * a rollback statement with the corresponding transaction ID
00666      * to rollback.
00667      */
00668     if (all)
00669       rollbackTransactionMessage(session);
00670     else
00671       rollbackStatementMessage(session);
00672 
00673     if (is_real_trans)
00674       session.transaction.xid_state.xid.set_null();
00675     if (normal_transaction)
00676     {
00677       session.variables.tx_isolation=session.session_tx_isolation;
00678       session.transaction.cleanup();
00679     }
00680   }
00681   if (normal_transaction)
00682     session.transaction_rollback_request= false;
00683 
00684   /*
00685    * If a non-transactional table was updated, warn the user
00686    */
00687   if (is_real_trans &&
00688       session.transaction.all.hasModifiedNonTransData() &&
00689       session.getKilled() != Session::KILL_CONNECTION)
00690   {
00691     push_warning(&session, DRIZZLE_ERROR::WARN_LEVEL_WARN,
00692                  ER_WARNING_NOT_COMPLETE_ROLLBACK,
00693                  ER(ER_WARNING_NOT_COMPLETE_ROLLBACK));
00694   }
00695   trans->reset();
00696   return error;
00697 }
00698 
00699 int TransactionServices::autocommitOrRollback(Session& session,
00700                                               int error)
00701 {
00702   /* One GPB Statement message per SQL statement */
00703   message::Statement *statement= session.getStatementMessage();
00704   if ((statement != NULL) && (! error))
00705     finalizeStatementMessage(*statement, session);
00706 
00707   if (session.transaction.stmt.getResourceContexts().empty() == false)
00708   {
00709     TransactionContext *trans = &session.transaction.stmt;
00710     TransactionContext::ResourceContexts &resource_contexts= trans->getResourceContexts();
00711     BOOST_FOREACH(TransactionContext::ResourceContexts::reference resource_context, resource_contexts)
00712     {
00713       resource_context->getTransactionalStorageEngine()->endStatement(&session);
00714     }
00715 
00716     if (! error)
00717     {
00718       if (commitTransaction(session, false))
00719         error= 1;
00720     }
00721     else
00722     {
00723       (void) rollbackTransaction(session, false);
00724       if (session.transaction_rollback_request)
00725       {
00726         (void) rollbackTransaction(session, true);
00727         session.server_status&= ~SERVER_STATUS_IN_TRANS;
00728       }
00729     }
00730 
00731     session.variables.tx_isolation= session.session_tx_isolation;
00732   }
00733 
00734   return error;
00735 }
00736 
00737 struct ResourceContextCompare : public std::binary_function<ResourceContext *, ResourceContext *, bool>
00738 {
00739   result_type operator()(const ResourceContext *lhs, const ResourceContext *rhs) const
00740   {
00741     /* The below is perfectly fine, since we're simply comparing addresses for the underlying
00742      * resources aren't the same... */
00743     return reinterpret_cast<uint64_t>(lhs->getMonitored()) < reinterpret_cast<uint64_t>(rhs->getMonitored());
00744   }
00745 };
00746 
00747 int TransactionServices::rollbackToSavepoint(Session& session,
00748                                              NamedSavepoint &sv)
00749 {
00750   int error= 0;
00751   TransactionContext *trans= &session.transaction.all;
00752   TransactionContext::ResourceContexts &tran_resource_contexts= trans->getResourceContexts();
00753   TransactionContext::ResourceContexts &sv_resource_contexts= sv.getResourceContexts();
00754 
00755   trans->no_2pc= false;
00756   /*
00757     rolling back to savepoint in all storage engines that were part of the
00758     transaction when the savepoint was set
00759   */
00760   BOOST_FOREACH(TransactionContext::ResourceContexts::reference resource_context, sv_resource_contexts)
00761   {
00762     plugin::MonitoredInTransaction *resource= resource_context->getMonitored();
00763 
00764     if (resource->participatesInSqlTransaction())
00765     {
00766       if (int err= resource_context->getTransactionalStorageEngine()->rollbackToSavepoint(&session, sv))
00767       {
00768         my_error(ER_ERROR_DURING_ROLLBACK, MYF(0), err);
00769         error= 1;
00770       }
00771       else
00772       {
00773         session.status_var.ha_savepoint_rollback_count++;
00774       }
00775     }
00776     trans->no_2pc|= not resource->participatesInXaTransaction();
00777   }
00778   /*
00779     rolling back the transaction in all storage engines that were not part of
00780     the transaction when the savepoint was set
00781   */
00782   {
00783     TransactionContext::ResourceContexts sorted_tran_resource_contexts(tran_resource_contexts);
00784     TransactionContext::ResourceContexts sorted_sv_resource_contexts(sv_resource_contexts);
00785     TransactionContext::ResourceContexts set_difference_contexts;
00786 
00787     /* 
00788      * Bug #542299: segfault during set_difference() below.  copy<>() requires pre-allocation
00789      * of all elements, including the target, which is why we pre-allocate the set_difference_contexts
00790      * here
00791      */
00792     set_difference_contexts.reserve(max(tran_resource_contexts.size(), sv_resource_contexts.size()));
00793 
00794     sort(sorted_tran_resource_contexts.begin(),
00795          sorted_tran_resource_contexts.end(),
00796          ResourceContextCompare());
00797     sort(sorted_sv_resource_contexts.begin(),
00798          sorted_sv_resource_contexts.end(),
00799          ResourceContextCompare());
00800     set_difference(sorted_tran_resource_contexts.begin(),
00801                    sorted_tran_resource_contexts.end(),
00802                    sorted_sv_resource_contexts.begin(),
00803                    sorted_sv_resource_contexts.end(),
00804                    set_difference_contexts.begin(),
00805                    ResourceContextCompare());
00806     /* 
00807      * set_difference_contexts now contains all resource contexts
00808      * which are in the transaction context but were NOT in the
00809      * savepoint's resource contexts.
00810      */
00811         
00812     BOOST_FOREACH(TransactionContext::ResourceContexts::reference resource_context, set_difference_contexts)
00813     {
00814       plugin::MonitoredInTransaction *resource= resource_context->getMonitored();
00815 
00816       if (resource->participatesInSqlTransaction())
00817       {
00818         if (int err= resource_context->getTransactionalStorageEngine()->rollback(&session, true))
00819         {
00820           my_error(ER_ERROR_DURING_ROLLBACK, MYF(0), err);
00821           error= 1;
00822         }
00823         else
00824         {
00825           session.status_var.ha_rollback_count++;
00826         }
00827       }
00828       resource_context->reset(); /* keep it conveniently zero-filled */
00829     }
00830   }
00831   trans->setResourceContexts(sv_resource_contexts);
00832 
00833   if (shouldConstructMessages())
00834   {
00835     cleanupTransactionMessage(getActiveTransactionMessage(session), session);
00836     message::Transaction *savepoint_transaction= sv.getTransactionMessage();
00837     if (savepoint_transaction != NULL)
00838     {
00839       /* Make a copy of the savepoint transaction, this is necessary to assure proper cleanup. 
00840          Upon commit the savepoint_transaction_copy will be cleaned up by a call to 
00841          cleanupTransactionMessage(). The Transaction message in NamedSavepoint will be cleaned
00842          up when the savepoint is cleaned up. This avoids calling delete twice on the Transaction.
00843       */ 
00844       message::Transaction *savepoint_transaction_copy= new message::Transaction(*sv.getTransactionMessage());
00845       uint32_t num_statements = savepoint_transaction_copy->statement_size();
00846       if (num_statements == 0)
00847       {    
00848         session.setStatementMessage(NULL);
00849       }    
00850       else 
00851       {
00852         session.setStatementMessage(savepoint_transaction_copy->mutable_statement(num_statements - 1));    
00853       }    
00854       session.setTransactionMessage(savepoint_transaction_copy);
00855     }
00856   }
00857 
00858   return error;
00859 }
00860 
00867 int TransactionServices::setSavepoint(Session& session,
00868                                       NamedSavepoint &sv)
00869 {
00870   int error= 0;
00871   TransactionContext *trans= &session.transaction.all;
00872   TransactionContext::ResourceContexts &resource_contexts= trans->getResourceContexts();
00873 
00874   if (resource_contexts.empty() == false)
00875   {
00876     BOOST_FOREACH(TransactionContext::ResourceContexts::reference resource_context, resource_contexts)
00877     {
00878       plugin::MonitoredInTransaction *resource= resource_context->getMonitored();
00879 
00880       if (resource->participatesInSqlTransaction())
00881       {
00882         if (int err= resource_context->getTransactionalStorageEngine()->setSavepoint(&session, sv))
00883         {
00884           my_error(ER_GET_ERRNO, MYF(0), err);
00885           error= 1;
00886         }
00887         else
00888         {
00889           session.status_var.ha_savepoint_count++;
00890         }
00891       }
00892     }
00893   }
00894   /*
00895     Remember the list of registered storage engines.
00896   */
00897   sv.setResourceContexts(resource_contexts);
00898 
00899   if (shouldConstructMessages())
00900   {
00901     message::Transaction *transaction= session.getTransactionMessage();
00902                   
00903     if (transaction != NULL)
00904     {
00905       message::Transaction *transaction_savepoint= 
00906         new message::Transaction(*transaction);
00907       sv.setTransactionMessage(transaction_savepoint);
00908     }
00909   } 
00910 
00911   return error;
00912 }
00913 
00914 int TransactionServices::releaseSavepoint(Session& session,
00915                                           NamedSavepoint &sv)
00916 {
00917   int error= 0;
00918 
00919   TransactionContext::ResourceContexts &resource_contexts= sv.getResourceContexts();
00920 
00921   BOOST_FOREACH(TransactionContext::ResourceContexts::reference resource_context, resource_contexts)
00922   {
00923     plugin::MonitoredInTransaction *resource= resource_context->getMonitored();
00924 
00925     if (resource->participatesInSqlTransaction())
00926     {
00927       if (int err= resource_context->getTransactionalStorageEngine()->releaseSavepoint(&session, sv))
00928       {
00929         my_error(ER_GET_ERRNO, MYF(0), err);
00930         error= 1;
00931       }
00932     }
00933   }
00934   
00935   return error;
00936 }
00937 
00938 bool TransactionServices::shouldConstructMessages()
00939 {
00940   return ReplicationServices::isActive();
00941 }
00942 
00943 message::Transaction *TransactionServices::getActiveTransactionMessage(Session& session,
00944                                                                        bool should_inc_trx_id)
00945 {
00946   message::Transaction *transaction= session.getTransactionMessage();
00947 
00948   if (unlikely(transaction == NULL))
00949   {
00950     /* 
00951      * Allocate and initialize a new transaction message 
00952      * for this Session object.  Session is responsible for
00953      * deleting transaction message when done with it.
00954      */
00955     transaction= new message::Transaction();
00956     initTransactionMessage(*transaction, session, should_inc_trx_id);
00957     session.setTransactionMessage(transaction);
00958   }
00959   return transaction;
00960 }
00961 
00962 void TransactionServices::initTransactionMessage(message::Transaction &transaction,
00963                                                  Session& session,
00964                                                  bool should_inc_trx_id)
00965 {
00966   message::TransactionContext *trx= transaction.mutable_transaction_context();
00967   trx->set_server_id(session.getServerId());
00968 
00969   if (should_inc_trx_id)
00970   {
00971     trx->set_transaction_id(getCurrentTransactionId(session));
00972     session.setXaId(0);
00973   }
00974   else
00975   {
00976     /* trx and seg id will get set properly elsewhere */
00977     trx->set_transaction_id(0);
00978   }
00979 
00980   trx->set_start_timestamp(session.times.getCurrentTimestamp());
00981   
00982   /* segment info may get set elsewhere as needed */
00983   transaction.set_segment_id(1);
00984   transaction.set_end_segment(true);
00985 }
00986 
00987 void TransactionServices::finalizeTransactionMessage(message::Transaction &transaction,
00988                                                      const Session& session)
00989 {
00990   message::TransactionContext *trx= transaction.mutable_transaction_context();
00991   trx->set_end_timestamp(session.times.getCurrentTimestamp());
00992 }
00993 
00994 void TransactionServices::cleanupTransactionMessage(message::Transaction *transaction,
00995                                                     Session& session)
00996 {
00997   delete transaction;
00998   session.setStatementMessage(NULL);
00999   session.setTransactionMessage(NULL);
01000   session.setXaId(0);
01001 }
01002 
01003 int TransactionServices::commitTransactionMessage(Session& session)
01004 {
01005   if (! ReplicationServices::isActive())
01006     return 0;
01007 
01008   /*
01009    * If no Transaction message was ever created, then no data modification
01010    * occurred inside the transaction, so nothing to do.
01011    */
01012   if (session.getTransactionMessage() == NULL)
01013     return 0;
01014   
01015   /* If there is an active statement message, finalize it. */
01016   message::Statement *statement= session.getStatementMessage();
01017 
01018   if (statement != NULL)
01019   {
01020     finalizeStatementMessage(*statement, session);
01021   }
01022 
01023   message::Transaction* transaction= getActiveTransactionMessage(session);
01024 
01025   /*
01026    * It is possible that we could have a Transaction without any Statements
01027    * if we had created a Statement but had to roll it back due to it failing
01028    * mid-execution, and no subsequent Statements were added to the Transaction
01029    * message. In this case, we simply clean up the message and not push it.
01030    */
01031   if (transaction->statement_size() == 0)
01032   {
01033     cleanupTransactionMessage(transaction, session);
01034     return 0;
01035   }
01036   
01037   finalizeTransactionMessage(*transaction, session);
01038   
01039   plugin::ReplicationReturnCode result= ReplicationServices::pushTransactionMessage(session, *transaction);
01040 
01041   cleanupTransactionMessage(transaction, session);
01042 
01043   return static_cast<int>(result);
01044 }
01045 
01046 void TransactionServices::initStatementMessage(message::Statement &statement,
01047                                                message::Statement::Type type,
01048                                                const Session& session)
01049 {
01050   statement.set_type(type);
01051   statement.set_start_timestamp(session.times.getCurrentTimestamp());
01052 
01053   if (session.variables.replicate_query)
01054     statement.set_sql(session.getQueryString()->c_str());
01055 }
01056 
01057 void TransactionServices::finalizeStatementMessage(message::Statement &statement,
01058                                                    Session& session)
01059 {
01060   statement.set_end_timestamp(session.times.getCurrentTimestamp());
01061   session.setStatementMessage(NULL);
01062 }
01063 
01064 void TransactionServices::rollbackTransactionMessage(Session& session)
01065 {
01066   if (! ReplicationServices::isActive())
01067     return;
01068   
01069   message::Transaction *transaction= getActiveTransactionMessage(session);
01070 
01071   /*
01072    * OK, so there are two situations that we need to deal with here:
01073    *
01074    * 1) We receive an instruction to ROLLBACK the current transaction
01075    *    and the currently-stored Transaction message is *self-contained*, 
01076    *    meaning that no Statement messages in the Transaction message
01077    *    contain a message having its segment_id member greater than 1.  If
01078    *    no non-segment ID 1 members are found, we can simply clear the
01079    *    current Transaction message and remove it from memory.
01080    *
01081    * 2) If the Transaction message does indeed have a non-end segment, that
01082    *    means that a bulk update/delete/insert Transaction message segment
01083    *    has previously been sent over the wire to replicators.  In this case, 
01084    *    we need to package a Transaction with a Statement message of type
01085    *    ROLLBACK to indicate to replicators that previously-transmitted
01086    *    messages must be un-applied.
01087    */
01088   if (unlikely(message::transactionContainsBulkSegment(*transaction)))
01089   {
01090     /* Remember the transaction ID so we can re-use it */
01091     uint64_t trx_id= transaction->transaction_context().transaction_id();
01092     uint32_t seg_id= transaction->segment_id();
01093 
01094     /*
01095      * Clear the transaction, create a Rollback statement message, 
01096      * attach it to the transaction, and push it to replicators.
01097      */
01098     transaction->Clear();
01099     initTransactionMessage(*transaction, session, false);
01100 
01101     /* Set the transaction ID to match the previous messages */
01102     transaction->mutable_transaction_context()->set_transaction_id(trx_id);
01103     transaction->set_segment_id(seg_id);
01104     transaction->set_end_segment(true);
01105 
01106     message::Statement *statement= transaction->add_statement();
01107 
01108     initStatementMessage(*statement, message::Statement::ROLLBACK, session);
01109     finalizeStatementMessage(*statement, session);
01110 
01111     finalizeTransactionMessage(*transaction, session);
01112     
01113     (void) ReplicationServices::pushTransactionMessage(session, *transaction);
01114   }
01115 
01116   cleanupTransactionMessage(transaction, session);
01117 }
01118 
01119 void TransactionServices::rollbackStatementMessage(Session& session)
01120 {
01121   if (! ReplicationServices::isActive())
01122     return;
01123 
01124   message::Statement *current_statement= session.getStatementMessage();
01125 
01126   /* If we never added a Statement message, nothing to undo. */
01127   if (current_statement == NULL)
01128     return;
01129 
01130   /*
01131    * If the Statement has been segmented, then we've already pushed a portion
01132    * of this Statement's row changes through the replication stream and we
01133    * need to send a ROLLBACK_STATEMENT message. Otherwise, we can simply
01134    * delete the current Statement message.
01135    */
01136   bool is_segmented= false;
01137 
01138   switch (current_statement->type())
01139   {
01140     case message::Statement::INSERT:
01141       if (current_statement->insert_data().segment_id() > 1)
01142         is_segmented= true;
01143       break;
01144 
01145     case message::Statement::UPDATE:
01146       if (current_statement->update_data().segment_id() > 1)
01147         is_segmented= true;
01148       break;
01149 
01150     case message::Statement::DELETE:
01151       if (current_statement->delete_data().segment_id() > 1)
01152         is_segmented= true;
01153       break;
01154 
01155     default:
01156       break;
01157   }
01158 
01159   /*
01160    * Remove the Statement message we've been working with (same as
01161    * current_statement).
01162    */
01163   message::Transaction *transaction= getActiveTransactionMessage(session);
01164   google::protobuf::RepeatedPtrField<message::Statement> *statements_in_txn;
01165   statements_in_txn= transaction->mutable_statement();
01166   statements_in_txn->RemoveLast();
01167   session.setStatementMessage(NULL);
01168   
01169   /*
01170    * Create the ROLLBACK_STATEMENT message, if we need to. This serves as
01171    * an indicator to cancel the previous Statement message which should have
01172    * had its end_segment attribute set to false.
01173    */
01174   if (is_segmented)
01175   {
01176     current_statement= transaction->add_statement();
01177     initStatementMessage(*current_statement,
01178                          message::Statement::ROLLBACK_STATEMENT,
01179                          session);
01180     finalizeStatementMessage(*current_statement, session);
01181   }
01182 }
01183 
01184 message::Transaction *TransactionServices::segmentTransactionMessage(Session& session,
01185                                                                      message::Transaction *transaction)
01186 {
01187   uint64_t trx_id= transaction->transaction_context().transaction_id();
01188   uint32_t seg_id= transaction->segment_id();
01189   
01190   transaction->set_end_segment(false);
01191   commitTransactionMessage(session);
01192   transaction= getActiveTransactionMessage(session, false);
01193   
01194   /* Set the transaction ID to match the previous messages */
01195   transaction->mutable_transaction_context()->set_transaction_id(trx_id);
01196   transaction->set_segment_id(seg_id + 1);
01197   transaction->set_end_segment(true);
01198 
01199   return transaction;
01200 }
01201 
01202 message::Statement &TransactionServices::getInsertStatement(Session& session,
01203                                                             Table &table,
01204                                                             uint32_t *next_segment_id)
01205 {
01206   message::Statement *statement= session.getStatementMessage();
01207   message::Transaction *transaction= NULL;
01208   
01209   /*
01210    * If statement is NULL, this is a new statement.
01211    * If statement is NOT NULL, this a continuation of the same statement.
01212    * This is because autocommitOrRollback() finalizes the statement so that
01213    * we guarantee only one Statement message per statement (i.e., we no longer
01214    * share a single GPB message for multiple statements).
01215    */
01216   if (statement == NULL)
01217   {
01218     transaction= getActiveTransactionMessage(session);
01219 
01220     if (static_cast<size_t>(transaction->ByteSize()) >= 
01221         transaction_message_threshold)
01222     {
01223       transaction= segmentTransactionMessage(session, transaction);
01224     }
01225 
01226     statement= transaction->add_statement();
01227     setInsertHeader(*statement, session, table);
01228     session.setStatementMessage(statement);
01229   }
01230   else
01231   {
01232     transaction= getActiveTransactionMessage(session);
01233     
01234     /*
01235      * If we've passed our threshold for the statement size (possible for
01236      * a bulk insert), we'll finalize the Statement and Transaction (doing
01237      * the Transaction will keep it from getting huge).
01238      */
01239     if (static_cast<size_t>(transaction->ByteSize()) >= 
01240         transaction_message_threshold)
01241     {
01242       /* Remember the transaction ID so we can re-use it */
01243       uint64_t trx_id= transaction->transaction_context().transaction_id();
01244       uint32_t seg_id= transaction->segment_id();
01245       
01246       message::InsertData *current_data= statement->mutable_insert_data();
01247       
01248       /* Caller should use this value when adding a new record */
01249       *next_segment_id= current_data->segment_id() + 1;
01250       
01251       current_data->set_end_segment(false);
01252       transaction->set_end_segment(false);
01253       
01254       /* 
01255        * Send the trx message to replicators after finalizing the 
01256        * statement and transaction. This will also set the Transaction
01257        * and Statement objects in Session to NULL.
01258        */
01259       commitTransactionMessage(session);
01260       
01261       /*
01262        * Statement and Transaction should now be NULL, so new ones will get
01263        * created. We reuse the transaction id since we are segmenting
01264        * one transaction.
01265        */
01266       transaction= getActiveTransactionMessage(session, false);
01267       assert(transaction != NULL);
01268 
01269       statement= transaction->add_statement();
01270       setInsertHeader(*statement, session, table);
01271       session.setStatementMessage(statement);
01272             
01273       /* Set the transaction ID to match the previous messages */
01274       transaction->mutable_transaction_context()->set_transaction_id(trx_id);
01275       transaction->set_segment_id(seg_id + 1);
01276       transaction->set_end_segment(true);
01277     }
01278     else
01279     {
01280       /*
01281        * Continuation of the same statement. Carry forward the existing
01282        * segment id.
01283        */
01284       const message::InsertData &current_data= statement->insert_data();
01285       *next_segment_id= current_data.segment_id();
01286     }
01287   }
01288   
01289   return *statement;
01290 }
01291 
01292 void TransactionServices::setInsertHeader(message::Statement &statement,
01293                                           const Session& session,
01294                                           Table &table)
01295 {
01296   initStatementMessage(statement, message::Statement::INSERT, session);
01297 
01298   /* 
01299    * Now we construct the specialized InsertHeader message inside
01300    * the generalized message::Statement container...
01301    */
01302   /* Set up the insert header */
01303   message::InsertHeader *header= statement.mutable_insert_header();
01304   message::TableMetadata *table_metadata= header->mutable_table_metadata();
01305 
01306   table_metadata->set_schema_name(table.getShare()->getSchemaName());
01307   table_metadata->set_table_name(table.getShare()->getTableName());
01308 
01309   Field **table_fields= table.getFields();
01310 
01311   message::FieldMetadata *field_metadata;
01312 
01313   /* We will read all the table's fields... */
01314   table.setReadSet();
01315 
01316   while (Field* current_field= *table_fields++) 
01317   {
01318     field_metadata= header->add_field_metadata();
01319     field_metadata->set_name(current_field->field_name);
01320     field_metadata->set_type(message::internalFieldTypeToFieldProtoType(current_field->type()));
01321   }
01322 }
01323 
01324 bool TransactionServices::insertRecord(Session& session,
01325                                        Table &table)
01326 {
01327   if (! ReplicationServices::isActive())
01328     return false;
01329 
01330   if (not table.getShare()->is_replicated())
01331     return false;
01332 
01341   if (not table.getShare()->hasPrimaryKey())
01342   {
01343     my_error(ER_NO_PRIMARY_KEY_ON_REPLICATED_TABLE, MYF(0));
01344     return true;
01345   }
01346 
01347   uint32_t next_segment_id= 1;
01348   message::Statement &statement= getInsertStatement(session, table, &next_segment_id);
01349 
01350   message::InsertData *data= statement.mutable_insert_data();
01351   data->set_segment_id(next_segment_id);
01352   data->set_end_segment(true);
01353   message::InsertRecord *record= data->add_record();
01354 
01355   Field *current_field;
01356   Field **table_fields= table.getFields();
01357 
01358   String *string_value= new (session.mem_root) String(TransactionServices::DEFAULT_RECORD_SIZE);
01359   string_value->set_charset(system_charset_info);
01360 
01361   /* We will read all the table's fields... */
01362   table.setReadSet();
01363 
01364   while ((current_field= *table_fields++) != NULL) 
01365   {
01366     if (current_field->is_null())
01367     {
01368       record->add_is_null(true);
01369       record->add_insert_value("", 0);
01370     } 
01371     else 
01372     {
01373       string_value= current_field->val_str_internal(string_value);
01374       record->add_is_null(false);
01375       record->add_insert_value(string_value->c_ptr(), string_value->length());
01376       string_value->free();
01377     }
01378   }
01379   return false;
01380 }
01381 
01382 message::Statement &TransactionServices::getUpdateStatement(Session& session,
01383                                                             Table &table,
01384                                                             const unsigned char *old_record, 
01385                                                             const unsigned char *new_record,
01386                                                             uint32_t *next_segment_id)
01387 {
01388   message::Statement *statement= session.getStatementMessage();
01389   message::Transaction *transaction= NULL;
01390 
01391   /*
01392    * If statement is NULL, this is a new statement.
01393    * If statement is NOT NULL, this a continuation of the same statement.
01394    * This is because autocommitOrRollback() finalizes the statement so that
01395    * we guarantee only one Statement message per statement (i.e., we no longer
01396    * share a single GPB message for multiple statements).
01397    */
01398   if (statement == NULL)
01399   {
01400     transaction= getActiveTransactionMessage(session);
01401     
01402     if (static_cast<size_t>(transaction->ByteSize()) >= 
01403         transaction_message_threshold)
01404     {
01405       transaction= segmentTransactionMessage(session, transaction);
01406     }
01407     
01408     statement= transaction->add_statement();
01409     setUpdateHeader(*statement, session, table, old_record, new_record);
01410     session.setStatementMessage(statement);
01411   }
01412   else
01413   {
01414     transaction= getActiveTransactionMessage(session);
01415     
01416     /*
01417      * If we've passed our threshold for the statement size (possible for
01418      * a bulk insert), we'll finalize the Statement and Transaction (doing
01419      * the Transaction will keep it from getting huge).
01420      */
01421     if (static_cast<size_t>(transaction->ByteSize()) >= 
01422         transaction_message_threshold)
01423     {
01424       /* Remember the transaction ID so we can re-use it */
01425       uint64_t trx_id= transaction->transaction_context().transaction_id();
01426       uint32_t seg_id= transaction->segment_id();
01427       
01428       message::UpdateData *current_data= statement->mutable_update_data();
01429       
01430       /* Caller should use this value when adding a new record */
01431       *next_segment_id= current_data->segment_id() + 1;
01432       
01433       current_data->set_end_segment(false);
01434       transaction->set_end_segment(false);
01435       
01436       /* 
01437        * Send the trx message to replicators after finalizing the 
01438        * statement and transaction. This will also set the Transaction
01439        * and Statement objects in Session to NULL.
01440        */
01441       commitTransactionMessage(session);
01442       
01443       /*
01444        * Statement and Transaction should now be NULL, so new ones will get
01445        * created. We reuse the transaction id since we are segmenting
01446        * one transaction.
01447        */
01448       transaction= getActiveTransactionMessage(session, false);
01449       assert(transaction != NULL);
01450       
01451       statement= transaction->add_statement();
01452       setUpdateHeader(*statement, session, table, old_record, new_record);
01453       session.setStatementMessage(statement);
01454       
01455       /* Set the transaction ID to match the previous messages */
01456       transaction->mutable_transaction_context()->set_transaction_id(trx_id);
01457       transaction->set_segment_id(seg_id + 1);
01458       transaction->set_end_segment(true);
01459     }
01460     else
01461     {
01462       /*
01463        * Continuation of the same statement. Carry forward the existing
01464        * segment id.
01465        */
01466       const message::UpdateData &current_data= statement->update_data();
01467       *next_segment_id= current_data.segment_id();
01468     }
01469   }
01470   
01471   return *statement;
01472 }
01473 
01474 void TransactionServices::setUpdateHeader(message::Statement &statement,
01475                                           const Session& session,
01476                                           Table &table,
01477                                           const unsigned char *old_record, 
01478                                           const unsigned char *new_record)
01479 {
01480   initStatementMessage(statement, message::Statement::UPDATE, session);
01481 
01482   /* 
01483    * Now we construct the specialized UpdateHeader message inside
01484    * the generalized message::Statement container...
01485    */
01486   /* Set up the update header */
01487   message::UpdateHeader *header= statement.mutable_update_header();
01488   message::TableMetadata *table_metadata= header->mutable_table_metadata();
01489 
01490   table_metadata->set_schema_name(table.getShare()->getSchemaName());
01491   table_metadata->set_table_name(table.getShare()->getTableName());
01492 
01493   Field *current_field;
01494   Field **table_fields= table.getFields();
01495 
01496   message::FieldMetadata *field_metadata;
01497 
01498   /* We will read all the table's fields... */
01499   table.setReadSet();
01500 
01501   while ((current_field= *table_fields++) != NULL) 
01502   {
01503     /*
01504      * We add the "key field metadata" -- i.e. the fields which is
01505      * the primary key for the table.
01506      */
01507     if (table.getShare()->fieldInPrimaryKey(current_field))
01508     {
01509       field_metadata= header->add_key_field_metadata();
01510       field_metadata->set_name(current_field->field_name);
01511       field_metadata->set_type(message::internalFieldTypeToFieldProtoType(current_field->type()));
01512     }
01513 
01514     if (isFieldUpdated(current_field, table, old_record, new_record))
01515     {
01516       /* Field is changed from old to new */
01517       field_metadata= header->add_set_field_metadata();
01518       field_metadata->set_name(current_field->field_name);
01519       field_metadata->set_type(message::internalFieldTypeToFieldProtoType(current_field->type()));
01520     }
01521   }
01522 }
01523 
01524 void TransactionServices::updateRecord(Session& session,
01525                                        Table &table, 
01526                                        const unsigned char *old_record, 
01527                                        const unsigned char *new_record)
01528 {
01529   if (! ReplicationServices::isActive())
01530     return;
01531 
01532   if (not table.getShare()->is_replicated())
01533     return;
01534 
01535   uint32_t next_segment_id= 1;
01536   message::Statement &statement= getUpdateStatement(session, table, old_record, new_record, &next_segment_id);
01537 
01538   message::UpdateData *data= statement.mutable_update_data();
01539   data->set_segment_id(next_segment_id);
01540   data->set_end_segment(true);
01541   message::UpdateRecord *record= data->add_record();
01542 
01543   Field *current_field;
01544   Field **table_fields= table.getFields();
01545   String *string_value= new (session.mem_root) String(TransactionServices::DEFAULT_RECORD_SIZE);
01546   string_value->set_charset(system_charset_info);
01547 
01548   while ((current_field= *table_fields++) != NULL) 
01549   {
01550     /*
01551      * Here, we add the SET field values.  We used to do this in the setUpdateHeader() method, 
01552      * but then realized that an UPDATE statement could potentially have different values for
01553      * the SET field.  For instance, imagine this SQL scenario:
01554      *
01555      * CREATE TABLE t1 (id INT NOT NULL PRIMARY KEY, count INT NOT NULL);
01556      * INSERT INTO t1 (id, counter) VALUES (1,1),(2,2),(3,3);
01557      * UPDATE t1 SET counter = counter + 1 WHERE id IN (1,2);
01558      *
01559      * We will generate two UpdateRecord messages with different set_value byte arrays.
01560      */
01561     if (isFieldUpdated(current_field, table, old_record, new_record))
01562     {
01563       /* Store the original "read bit" for this field */
01564       bool is_read_set= current_field->isReadSet();
01565 
01566       /* We need to mark that we will "read" this field... */
01567       table.setReadSet(current_field->position());
01568 
01569       /* Read the string value of this field's contents */
01570       string_value= current_field->val_str_internal(string_value);
01571 
01572       /* 
01573        * Reset the read bit after reading field to its original state.  This 
01574        * prevents the field from being included in the WHERE clause
01575        */
01576       current_field->setReadSet(is_read_set);
01577 
01578       if (current_field->is_null())
01579       {
01580         record->add_is_null(true);
01581         record->add_after_value("", 0);
01582       }
01583       else
01584       {
01585         record->add_is_null(false);
01586         record->add_after_value(string_value->c_ptr(), string_value->length());
01587       }
01588       string_value->free();
01589     }
01590 
01591     /* 
01592      * Add the WHERE clause values now...for now, this means the
01593      * primary key field value.  Replication only supports tables
01594      * with a primary key.
01595      */
01596     if (table.getShare()->fieldInPrimaryKey(current_field))
01597     {
01603       string_value= current_field->val_str_internal(string_value,
01604                                                     old_record + 
01605                                                     current_field->offset(const_cast<unsigned char *>(new_record)));
01606       record->add_key_value(string_value->c_ptr(), string_value->length());
01607       string_value->free();
01608     }
01609 
01610   }
01611 }
01612 
01613 bool TransactionServices::isFieldUpdated(Field *current_field,
01614                                          Table &table,
01615                                          const unsigned char *old_record,
01616                                          const unsigned char *new_record)
01617 {
01618   /*
01619    * The below really should be moved into the Field API and Record API.  But for now
01620    * we do this crazy pointer fiddling to figure out if the current field
01621    * has been updated in the supplied record raw byte pointers.
01622    */
01623   const unsigned char *old_ptr= (const unsigned char *) old_record + (ptrdiff_t) (current_field->ptr - table.getInsertRecord());
01624   const unsigned char *new_ptr= (const unsigned char *) new_record + (ptrdiff_t) (current_field->ptr - table.getInsertRecord());
01625 
01626   uint32_t field_length= current_field->pack_length(); 
01628   bool old_value_is_null= current_field->is_null_in_record(old_record);
01629   bool new_value_is_null= current_field->is_null_in_record(new_record);
01630 
01631   bool isUpdated= false;
01632   if (old_value_is_null != new_value_is_null)
01633   {
01634     if ((old_value_is_null) && (! new_value_is_null)) /* old value is NULL, new value is non NULL */
01635     {
01636       isUpdated= true;
01637     }
01638     else if ((! old_value_is_null) && (new_value_is_null)) /* old value is non NULL, new value is NULL */
01639     {
01640       isUpdated= true;
01641     }
01642   }
01643 
01644   if (! isUpdated)
01645   {
01646     if (memcmp(old_ptr, new_ptr, field_length) != 0)
01647     {
01648       isUpdated= true;
01649     }
01650   }
01651   return isUpdated;
01652 }  
01653 
01654 message::Statement &TransactionServices::getDeleteStatement(Session& session,
01655                                                             Table &table,
01656                                                             uint32_t *next_segment_id)
01657 {
01658   message::Statement *statement= session.getStatementMessage();
01659   message::Transaction *transaction= NULL;
01660 
01661   /*
01662    * If statement is NULL, this is a new statement.
01663    * If statement is NOT NULL, this a continuation of the same statement.
01664    * This is because autocommitOrRollback() finalizes the statement so that
01665    * we guarantee only one Statement message per statement (i.e., we no longer
01666    * share a single GPB message for multiple statements).
01667    */
01668   if (statement == NULL)
01669   {
01670     transaction= getActiveTransactionMessage(session);
01671     
01672     if (static_cast<size_t>(transaction->ByteSize()) >= 
01673         transaction_message_threshold)
01674     {
01675       transaction= segmentTransactionMessage(session, transaction);
01676     }
01677     
01678     statement= transaction->add_statement();
01679     setDeleteHeader(*statement, session, table);
01680     session.setStatementMessage(statement);
01681   }
01682   else
01683   {
01684     transaction= getActiveTransactionMessage(session);
01685     
01686     /*
01687      * If we've passed our threshold for the statement size (possible for
01688      * a bulk insert), we'll finalize the Statement and Transaction (doing
01689      * the Transaction will keep it from getting huge).
01690      */
01691     if (static_cast<size_t>(transaction->ByteSize()) >= 
01692         transaction_message_threshold)
01693     {
01694       /* Remember the transaction ID so we can re-use it */
01695       uint64_t trx_id= transaction->transaction_context().transaction_id();
01696       uint32_t seg_id= transaction->segment_id();
01697       
01698       message::DeleteData *current_data= statement->mutable_delete_data();
01699       
01700       /* Caller should use this value when adding a new record */
01701       *next_segment_id= current_data->segment_id() + 1;
01702       
01703       current_data->set_end_segment(false);
01704       transaction->set_end_segment(false);
01705       
01706       /* 
01707        * Send the trx message to replicators after finalizing the 
01708        * statement and transaction. This will also set the Transaction
01709        * and Statement objects in Session to NULL.
01710        */
01711       commitTransactionMessage(session);
01712       
01713       /*
01714        * Statement and Transaction should now be NULL, so new ones will get
01715        * created. We reuse the transaction id since we are segmenting
01716        * one transaction.
01717        */
01718       transaction= getActiveTransactionMessage(session, false);
01719       assert(transaction != NULL);
01720       
01721       statement= transaction->add_statement();
01722       setDeleteHeader(*statement, session, table);
01723       session.setStatementMessage(statement);
01724       
01725       /* Set the transaction ID to match the previous messages */
01726       transaction->mutable_transaction_context()->set_transaction_id(trx_id);
01727       transaction->set_segment_id(seg_id + 1);
01728       transaction->set_end_segment(true);
01729     }
01730     else
01731     {
01732       /*
01733        * Continuation of the same statement. Carry forward the existing
01734        * segment id.
01735        */
01736       const message::DeleteData &current_data= statement->delete_data();
01737       *next_segment_id= current_data.segment_id();
01738     }
01739   }
01740   
01741   return *statement;
01742 }
01743 
01744 void TransactionServices::setDeleteHeader(message::Statement &statement,
01745                                           const Session& session,
01746                                           Table &table)
01747 {
01748   initStatementMessage(statement, message::Statement::DELETE, session);
01749 
01750   /* 
01751    * Now we construct the specialized DeleteHeader message inside
01752    * the generalized message::Statement container...
01753    */
01754   message::DeleteHeader *header= statement.mutable_delete_header();
01755   message::TableMetadata *table_metadata= header->mutable_table_metadata();
01756 
01757   table_metadata->set_schema_name(table.getShare()->getSchemaName());
01758   table_metadata->set_table_name(table.getShare()->getTableName());
01759 
01760   Field *current_field;
01761   Field **table_fields= table.getFields();
01762 
01763   message::FieldMetadata *field_metadata;
01764 
01765   while ((current_field= *table_fields++) != NULL) 
01766   {
01767     /* 
01768      * Add the WHERE clause values now...for now, this means the
01769      * primary key field value.  Replication only supports tables
01770      * with a primary key.
01771      */
01772     if (table.getShare()->fieldInPrimaryKey(current_field))
01773     {
01774       field_metadata= header->add_key_field_metadata();
01775       field_metadata->set_name(current_field->field_name);
01776       field_metadata->set_type(message::internalFieldTypeToFieldProtoType(current_field->type()));
01777     }
01778   }
01779 }
01780 
01781 void TransactionServices::deleteRecord(Session& session,
01782                                        Table &table,
01783                                        bool use_update_record)
01784 {
01785   if (! ReplicationServices::isActive())
01786     return;
01787 
01788   if (not table.getShare()->is_replicated())
01789     return;
01790 
01791   uint32_t next_segment_id= 1;
01792   message::Statement &statement= getDeleteStatement(session, table, &next_segment_id);
01793 
01794   message::DeleteData *data= statement.mutable_delete_data();
01795   data->set_segment_id(next_segment_id);
01796   data->set_end_segment(true);
01797   message::DeleteRecord *record= data->add_record();
01798 
01799   Field *current_field;
01800   Field **table_fields= table.getFields();
01801   String *string_value= new (session.mem_root) String(TransactionServices::DEFAULT_RECORD_SIZE);
01802   string_value->set_charset(system_charset_info);
01803 
01804   while ((current_field= *table_fields++) != NULL) 
01805   {
01806     /* 
01807      * Add the WHERE clause values now...for now, this means the
01808      * primary key field value.  Replication only supports tables
01809      * with a primary key.
01810      */
01811     if (table.getShare()->fieldInPrimaryKey(current_field))
01812     {
01813       if (use_update_record)
01814       {
01815         /*
01816          * Temporarily point to the update record to get its value.
01817          * This is pretty much a hack in order to get the PK value from
01818          * the update record rather than the insert record. Field::val_str()
01819          * should not change anything in Field::ptr, so this should be safe.
01820          * We are careful not to change anything in old_ptr.
01821          */
01822         const unsigned char *old_ptr= current_field->ptr;
01823         current_field->ptr= table.getUpdateRecord() + static_cast<ptrdiff_t>(old_ptr - table.getInsertRecord());
01824         string_value= current_field->val_str_internal(string_value);
01825         current_field->ptr= const_cast<unsigned char *>(old_ptr);
01826       }
01827       else
01828       {
01829         string_value= current_field->val_str_internal(string_value);
01833       }
01834       record->add_key_value(string_value->c_ptr(), string_value->length());
01835       string_value->free();
01836     }
01837   }
01838 }
01839 
01840 void TransactionServices::createTable(Session& session,
01841                                       const message::Table &table)
01842 {
01843   if (not ReplicationServices::isActive())
01844     return;
01845 
01846   if (not message::is_replicated(table))
01847     return;
01848 
01849   message::Transaction *transaction= getActiveTransactionMessage(session);
01850   message::Statement *statement= transaction->add_statement();
01851 
01852   initStatementMessage(*statement, message::Statement::CREATE_TABLE, session);
01853 
01854   /* 
01855    * Construct the specialized CreateTableStatement message and attach
01856    * it to the generic Statement message
01857    */
01858   message::CreateTableStatement *create_table_statement= statement->mutable_create_table_statement();
01859   message::Table *new_table_message= create_table_statement->mutable_table();
01860   *new_table_message= table;
01861 
01862   finalizeStatementMessage(*statement, session);
01863 
01864   finalizeTransactionMessage(*transaction, session);
01865   
01866   (void) ReplicationServices::pushTransactionMessage(session, *transaction);
01867 
01868   cleanupTransactionMessage(transaction, session);
01869 
01870 }
01871 
01872 void TransactionServices::createSchema(Session& session,
01873                                        const message::Schema &schema)
01874 {
01875   if (! ReplicationServices::isActive())
01876     return;
01877 
01878   if (not message::is_replicated(schema))
01879     return;
01880 
01881   message::Transaction *transaction= getActiveTransactionMessage(session);
01882   message::Statement *statement= transaction->add_statement();
01883 
01884   initStatementMessage(*statement, message::Statement::CREATE_SCHEMA, session);
01885 
01886   /* 
01887    * Construct the specialized CreateSchemaStatement message and attach
01888    * it to the generic Statement message
01889    */
01890   message::CreateSchemaStatement *create_schema_statement= statement->mutable_create_schema_statement();
01891   message::Schema *new_schema_message= create_schema_statement->mutable_schema();
01892   *new_schema_message= schema;
01893 
01894   finalizeStatementMessage(*statement, session);
01895 
01896   finalizeTransactionMessage(*transaction, session);
01897   
01898   (void) ReplicationServices::pushTransactionMessage(session, *transaction);
01899 
01900   cleanupTransactionMessage(transaction, session);
01901 
01902 }
01903 
01904 void TransactionServices::dropSchema(Session& session,
01905                                      const identifier::Schema& identifier,
01906                                      message::schema::const_reference schema)
01907 {
01908   if (not ReplicationServices::isActive())
01909     return;
01910 
01911   if (not message::is_replicated(schema))
01912     return;
01913 
01914   message::Transaction *transaction= getActiveTransactionMessage(session);
01915   message::Statement *statement= transaction->add_statement();
01916 
01917   initStatementMessage(*statement, message::Statement::DROP_SCHEMA, session);
01918 
01919   /* 
01920    * Construct the specialized DropSchemaStatement message and attach
01921    * it to the generic Statement message
01922    */
01923   message::DropSchemaStatement *drop_schema_statement= statement->mutable_drop_schema_statement();
01924 
01925   drop_schema_statement->set_schema_name(identifier.getSchemaName());
01926 
01927   finalizeStatementMessage(*statement, session);
01928 
01929   finalizeTransactionMessage(*transaction, session);
01930   
01931   (void) ReplicationServices::pushTransactionMessage(session, *transaction);
01932 
01933   cleanupTransactionMessage(transaction, session);
01934 }
01935 
01936 void TransactionServices::alterSchema(Session& session,
01937                                       const message::Schema &old_schema,
01938                                       const message::Schema &new_schema)
01939 {
01940   if (! ReplicationServices::isActive())
01941     return;
01942 
01943   if (not message::is_replicated(old_schema))
01944     return;
01945 
01946   message::Transaction *transaction= getActiveTransactionMessage(session);
01947   message::Statement *statement= transaction->add_statement();
01948 
01949   initStatementMessage(*statement, message::Statement::ALTER_SCHEMA, session);
01950 
01951   /* 
01952    * Construct the specialized AlterSchemaStatement message and attach
01953    * it to the generic Statement message
01954    */
01955   message::AlterSchemaStatement *alter_schema_statement= statement->mutable_alter_schema_statement();
01956 
01957   message::Schema *before= alter_schema_statement->mutable_before();
01958   message::Schema *after= alter_schema_statement->mutable_after();
01959 
01960   *before= old_schema;
01961   *after= new_schema;
01962 
01963   finalizeStatementMessage(*statement, session);
01964 
01965   finalizeTransactionMessage(*transaction, session);
01966   
01967   (void) ReplicationServices::pushTransactionMessage(session, *transaction);
01968 
01969   cleanupTransactionMessage(transaction, session);
01970 }
01971 
01972 void TransactionServices::dropTable(Session& session,
01973                                     const identifier::Table& identifier,
01974                                     message::table::const_reference table,
01975                                     bool if_exists)
01976 {
01977   if (! ReplicationServices::isActive())
01978     return;
01979 
01980   if (not message::is_replicated(table))
01981     return;
01982 
01983   message::Transaction *transaction= getActiveTransactionMessage(session);
01984   message::Statement *statement= transaction->add_statement();
01985 
01986   initStatementMessage(*statement, message::Statement::DROP_TABLE, session);
01987 
01988   /* 
01989    * Construct the specialized DropTableStatement message and attach
01990    * it to the generic Statement message
01991    */
01992   message::DropTableStatement *drop_table_statement= statement->mutable_drop_table_statement();
01993 
01994   drop_table_statement->set_if_exists_clause(if_exists);
01995 
01996   message::TableMetadata *table_metadata= drop_table_statement->mutable_table_metadata();
01997 
01998   table_metadata->set_schema_name(identifier.getSchemaName());
01999   table_metadata->set_table_name(identifier.getTableName());
02000 
02001   finalizeStatementMessage(*statement, session);
02002 
02003   finalizeTransactionMessage(*transaction, session);
02004   
02005   (void) ReplicationServices::pushTransactionMessage(session, *transaction);
02006 
02007   cleanupTransactionMessage(transaction, session);
02008 }
02009 
02010 void TransactionServices::truncateTable(Session& session, Table &table)
02011 {
02012   if (! ReplicationServices::isActive())
02013     return;
02014 
02015   if (not table.getShare()->is_replicated())
02016     return;
02017 
02018   message::Transaction *transaction= getActiveTransactionMessage(session);
02019   message::Statement *statement= transaction->add_statement();
02020 
02021   initStatementMessage(*statement, message::Statement::TRUNCATE_TABLE, session);
02022 
02023   /* 
02024    * Construct the specialized TruncateTableStatement message and attach
02025    * it to the generic Statement message
02026    */
02027   message::TruncateTableStatement *truncate_statement= statement->mutable_truncate_table_statement();
02028   message::TableMetadata *table_metadata= truncate_statement->mutable_table_metadata();
02029 
02030   table_metadata->set_schema_name(table.getShare()->getSchemaName());
02031   table_metadata->set_table_name(table.getShare()->getTableName());
02032 
02033   finalizeStatementMessage(*statement, session);
02034 
02035   finalizeTransactionMessage(*transaction, session);
02036   
02037   (void) ReplicationServices::pushTransactionMessage(session, *transaction);
02038 
02039   cleanupTransactionMessage(transaction, session);
02040 }
02041 
02042 void TransactionServices::rawStatement(Session& session,
02043                                        const string &query,
02044                                        const string &schema)
02045 {
02046   if (! ReplicationServices::isActive())
02047     return;
02048  
02049   message::Transaction *transaction= getActiveTransactionMessage(session);
02050   message::Statement *statement= transaction->add_statement();
02051 
02052   initStatementMessage(*statement, message::Statement::RAW_SQL, session);
02053   statement->set_sql(query);
02054   if (not schema.empty())
02055     statement->set_raw_sql_schema(schema);
02056   finalizeStatementMessage(*statement, session);
02057 
02058   finalizeTransactionMessage(*transaction, session);
02059   
02060   (void) ReplicationServices::pushTransactionMessage(session, *transaction);
02061 
02062   cleanupTransactionMessage(transaction, session);
02063 }
02064 
02065 int TransactionServices::sendEvent(Session& session, const message::Event &event)
02066 {
02067   if (not ReplicationServices::isActive())
02068     return 0;
02069   message::Transaction transaction;
02070 
02071   // set server id, start timestamp
02072   initTransactionMessage(transaction, session, true);
02073 
02074   // set end timestamp
02075   finalizeTransactionMessage(transaction, session);
02076 
02077   message::Event *trx_event= transaction.mutable_event();
02078   trx_event->CopyFrom(event);
02079   plugin::ReplicationReturnCode result= ReplicationServices::pushTransactionMessage(session, transaction);
02080   return result;
02081 }
02082 
02083 bool TransactionServices::sendStartupEvent(Session& session)
02084 {
02085   message::Event event;
02086   event.set_type(message::Event::STARTUP);
02087   return not sendEvent(session, event);
02088 }
02089 
02090 bool TransactionServices::sendShutdownEvent(Session& session)
02091 {
02092   message::Event event;
02093   event.set_type(message::Event::SHUTDOWN);
02094   return not sendEvent(session, event);
02095 }
02096 
02097 } /* namespace drizzled */