~drizzle-trunk/drizzle/development

1273.1.2 by Jay Pipes
This patch does not change any algorithms or code paths,
1
/* -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*-
2
 *  vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
3
 *
4
 *  Copyright (C) 2008 Sun Microsystems
1273.1.30 by Jay Pipes
* Completes the blueprint for splitting the XA Resource Manager
5
 *  Copyright (c) 2010 Jay Pipes <jaypipes@gmail.com>
1273.1.2 by Jay Pipes
This patch does not change any algorithms or code paths,
6
 *
7
 *  This program is free software; you can redistribute it and/or modify
8
 *  it under the terms of the GNU General Public License as published by
9
 *  the Free Software Foundation; version 2 of the License.
10
 *
11
 *  This program is distributed in the hope that it will be useful,
12
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 *  GNU General Public License for more details.
15
 *
16
 *  You should have received a copy of the GNU General Public License
17
 *  along with this program; if not, write to the Free Software
18
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19
 */
20
21
/**
22
 * @file Transaction processing code
1336.2.2 by Jay Pipes
NO CODE CHANGES - Simply moves pieces of ReplicationServices to TransactionServices. Preparation for making the ReplicationServices component only responsible for communication between replicators, appliers, publishers, and subscribers.
23
 *
24
 * @note
25
 *
26
 * The TransactionServices component takes internal events (for instance the start of a 
27
 * transaction, the changing of a record, or the rollback of a transaction) 
28
 * and constructs GPB Messages that are passed to the ReplicationServices
29
 * component and used during replication.
30
 *
31
 * The reason for this functionality is to encapsulate all communication
32
 * between the kernel and the replicator/applier plugins into GPB Messages.
33
 * Instead of the plugin having to understand the (often fluidly changing)
34
 * mechanics of the kernel, all the plugin needs to understand is the message
35
 * format, and GPB messages provide a nice, clear, and versioned format for 
36
 * these messages.
37
 *
38
 * @see /drizzled/message/transaction.proto
39
 *
40
 * @todo
41
 *
42
 * We really should store the raw bytes in the messages, not the
43
 * String value of the Field.  But, to do that, the
44
 * statement_transform library needs first to be updated
45
 * to include the transformation code to convert raw
46
 * Drizzle-internal Field byte representation into something
47
 * plugins can understand.
1273.1.2 by Jay Pipes
This patch does not change any algorithms or code paths,
48
 */
49
50
#include "config.h"
51
#include "drizzled/my_hash.h"
52
#include "drizzled/error.h"
53
#include "drizzled/gettext.h"
54
#include "drizzled/probes.h"
55
#include "drizzled/sql_parse.h"
56
#include "drizzled/session.h"
57
#include "drizzled/sql_base.h"
58
#include "drizzled/replication_services.h"
59
#include "drizzled/transaction_services.h"
1273.1.10 by Jay Pipes
* Renames Ha_trx_info to drizzled::ResourceContext
60
#include "drizzled/transaction_context.h"
1336.2.2 by Jay Pipes
NO CODE CHANGES - Simply moves pieces of ReplicationServices to TransactionServices. Preparation for making the ReplicationServices component only responsible for communication between replicators, appliers, publishers, and subscribers.
61
#include "drizzled/message/transaction.pb.h"
62
#include "drizzled/message/statement_transform.h"
1273.1.10 by Jay Pipes
* Renames Ha_trx_info to drizzled::ResourceContext
63
#include "drizzled/resource_context.h"
1273.1.2 by Jay Pipes
This patch does not change any algorithms or code paths,
64
#include "drizzled/lock.h"
65
#include "drizzled/item/int.h"
66
#include "drizzled/item/empty_string.h"
67
#include "drizzled/field/timestamp.h"
68
#include "drizzled/plugin/client.h"
1273.1.30 by Jay Pipes
* Completes the blueprint for splitting the XA Resource Manager
69
#include "drizzled/plugin/monitored_in_transaction.h"
70
#include "drizzled/plugin/transactional_storage_engine.h"
71
#include "drizzled/plugin/xa_resource_manager.h"
1273.1.2 by Jay Pipes
This patch does not change any algorithms or code paths,
72
#include "drizzled/internal/my_sys.h"
73
74
using namespace std;
75
1273.1.10 by Jay Pipes
* Renames Ha_trx_info to drizzled::ResourceContext
76
#include <vector>
77
#include <algorithm>
78
#include <functional>
79
1273.1.2 by Jay Pipes
This patch does not change any algorithms or code paths,
80
namespace drizzled
81
{
82
83
/**
1273.1.30 by Jay Pipes
* Completes the blueprint for splitting the XA Resource Manager
84
 * @defgroup Transactions
85
 *
86
 * @brief
87
 *
88
 * Transaction handling in the server
89
 *
90
 * @detail
91
 *
92
 * In each client connection, Drizzle maintains two transaction
93
 * contexts representing the state of the:
94
 *
95
 * 1) Statement Transaction
96
 * 2) Normal Transaction
97
 *
98
 * These two transaction contexts represent the transactional
99
 * state of a Session's SQL and XA transactions for a single
100
 * SQL statement or a series of SQL statements.
101
 *
102
 * When the Session's connection is in AUTOCOMMIT mode, there
103
 * is no practical difference between the statement and the
104
 * normal transaction, as each SQL statement is committed or
105
 * rolled back depending on the success or failure of the
106
 * indvidual SQL statement.
107
 *
108
 * When the Session's connection is NOT in AUTOCOMMIT mode, OR
109
 * the Session has explicitly begun a normal SQL transaction using
110
 * a BEGIN WORK/START TRANSACTION statement, then the normal
111
 * transaction context tracks the aggregate transaction state of
112
 * the SQL transaction's individual statements, and the SQL
113
 * transaction's commit or rollback is done atomically for all of
114
 * the SQL transaction's statement's data changes.
115
 *
116
 * Technically, a statement transaction can be viewed as a savepoint 
117
 * which is maintained automatically in order to make effects of one
118
 * statement atomic.
119
 *
120
 * The normal transaction is started by the user and is typically
121
 * ended (COMMIT or ROLLBACK) upon an explicity user request as well.
122
 * The exception to this is that DDL statements implicitly COMMIT
123
 * any previously active normal transaction before they begin executing.
124
 *
125
 * In Drizzle, unlike MySQL, plugins other than a storage engine
126
 * may participate in a transaction.  All plugin::TransactionalStorageEngine
127
 * plugins will automatically be monitored by Drizzle's transaction 
128
 * manager (implemented in this source file), as will all plugins which
129
 * implement plugin::XaResourceManager and register with the transaction
130
 * manager.
131
 *
132
 * If Drizzle's transaction manager sees that more than one resource
133
 * manager (transactional storage engine or XA resource manager) has modified
134
 * data state during a statement or normal transaction, the transaction
135
 * manager will automatically use a two-phase commit protocol for all
136
 * resources which support XA's distributed transaction protocol.  Unlike
137
 * MySQL, storage engines need not manually register with the transaction
138
 * manager during a statement's execution.  Previously, in MySQL, all
139
 * handlertons would have to call trans_register_ha() at some point after
140
 * modifying data state in order to have MySQL include that handler in
141
 * an XA transaction.  Drizzle does all of this grunt work behind the
142
 * scenes for the storage engine implementers.
143
 *
144
 * When a connection is closed, the current normal transaction, if
145
 * any is currently active, is rolled back.
146
 *
147
 * Transaction life cycle
148
 * ----------------------
149
 *
150
 * When a new connection is established, session->transaction
151
 * members are initialized to an empty state. If a statement uses any tables, 
152
 * all affected engines are registered in the statement engine list automatically
153
 * in plugin::StorageEngine::startStatement() and 
154
 * plugin::TransactionalStorageEngine::startTransaction().
155
 *
156
 * You can view the lifetime of a normal transaction in the following
157
 * call-sequence:
158
 *
159
 * drizzled::statement::Statement::execute()
160
 *   drizzled::plugin::TransactionalStorageEngine::startTransaction()
161
 *     drizzled::TransactionServices::registerResourceForTransaction()
162
 *     drizzled::TransactionServices::registerResourceForStatement()
163
 *     drizzled::plugin::StorageEngine::startStatement()
164
 *       drizzled::Cursor::write_row() <-- example...could be update_row(), etc
165
 *     drizzled::plugin::StorageEngine::endStatement()
166
 *   drizzled::TransactionServices::autocommitOrRollback()
167
 *     drizzled::TransactionalStorageEngine::commit() <-- or ::rollback()
168
 *     drizzled::XaResourceManager::xaCommit() <-- or rollback()
169
 *
170
 * Roles and responsibilities
171
 * --------------------------
172
 *
173
 * Beginning of SQL Statement (and Statement Transaction)
174
 * ------------------------------------------------------
175
 *
176
 * At the start of each SQL statement, for each storage engine
177
 * <strong>that is involved in the SQL statement</strong>, the kernel 
178
 * calls the engine's plugin::StoragEngine::startStatement() method.  If the
179
 * engine needs to track some data for the statement, it should use
180
 * this method invocation to initialize this data.  This is the
181
 * beginning of what is called the "statement transaction".
182
 *
183
 * <strong>For transaction storage engines (those storage engines
184
 * that inherit from plugin::TransactionalStorageEngine)</strong>, the
185
 * kernel automatically determines if the start of the SQL statement 
186
 * transaction should <em>also</em> begin the normal SQL transaction.
187
 * This occurs when the connection is in NOT in autocommit mode. If
188
 * the kernel detects this, then the kernel automatically starts the
189
 * normal transaction w/ plugin::TransactionalStorageEngine::startTransaction()
190
 * method and then calls plugin::StorageEngine::startStatement()
191
 * afterwards.
192
 *
193
 * Beginning of an SQL "Normal" Transaction
194
 * ----------------------------------------
195
 *
196
 * As noted above, a "normal SQL transaction" may be started when
197
 * an SQL statement is started in a connection and the connection is
198
 * NOT in AUTOCOMMIT mode.  This is automatically done by the kernel.
199
 *
200
 * In addition, when a user executes a START TRANSACTION or
201
 * BEGIN WORK statement in a connection, the kernel explicitly
202
 * calls each transactional storage engine's startTransaction() method.
203
 *
204
 * Ending of an SQL Statement (and Statement Transaction)
205
 * ------------------------------------------------------
206
 *
207
 * At the end of each SQL statement, for each of the aforementioned
208
 * involved storage engines, the kernel calls the engine's
209
 * plugin::StorageEngine::endStatement() method.  If the engine
210
 * has initialized or modified some internal data about the
211
 * statement transaction, it should use this method to reset or destroy
212
 * this data appropriately.
213
 *
214
 * Ending of an SQL "Normal" Transaction
215
 * -------------------------------------
216
 *
217
 * The end of a normal transaction is either a ROLLBACK or a COMMIT, 
218
 * depending on the success or failure of the statement transaction(s) 
219
 * it encloses.
220
 *
221
 * The end of a "normal transaction" occurs when any of the following
222
 * occurs:
223
 *
224
 * 1) If a statement transaction has completed and AUTOCOMMIT is ON,
225
 *    then the normal transaction which encloses the statement
226
 *    transaction ends
227
 * 2) If a COMMIT or ROLLBACK statement occurs on the connection
228
 * 3) Just before a DDL operation occurs, the kernel will implicitly
229
 *    commit the active normal transaction
230
 *
231
 * Transactions and Non-transactional Storage Engines
232
 * --------------------------------------------------
233
 *
234
 * For non-transactional engines, this call can be safely ignored, an
235
 * the kernel tracks whether a non-transactional engine has changed
236
 * any data state, and warns the user appropriately if a transaction
237
 * (statement or normal) is rolled back after such non-transactional
238
 * data changes have been made.
239
 *
240
 * XA Two-phase Commit Protocol
241
 * ----------------------------
242
 *
243
 * During statement execution, whenever any of data-modifying
244
 * PSEA API methods is used, e.g. Cursor::write_row() or
245
 * Cursor::update_row(), the read-write flag is raised in the
246
 * statement transaction for the involved engine.
247
 * Currently All PSEA calls are "traced", and the data can not be
248
 * changed in a way other than issuing a PSEA call. Important:
249
 * unless this invariant is preserved the server will not know that
250
 * a transaction in a given engine is read-write and will not
251
 * involve the two-phase commit protocol!
252
 *
253
 * At the end of a statement, TransactionServices::autocommitOrRollback()
254
 * is invoked. This call in turn
255
 * invokes plugin::XaResourceManager::xapPepare() for every involved XA
256
 * resource manager.
257
 *
258
 * Prepare is followed by a call to plugin::TransactionalStorageEngine::commit()
259
 * or plugin::XaResourceManager::xaCommit() (depending on what the resource
260
 * is...)
261
 * 
262
 * If a one-phase commit will suffice, plugin::StorageEngine::prepare() is not
263
 * invoked and the server only calls plugin::StorageEngine::commit_one_phase().
264
 * At statement commit, the statement-related read-write engine
265
 * flag is propagated to the corresponding flag in the normal
266
 * transaction.  When the commit is complete, the list of registered
267
 * engines is cleared.
268
 *
269
 * Rollback is handled in a similar fashion.
270
 *
271
 * Additional notes on DDL and the normal transaction.
272
 * ---------------------------------------------------
273
 *
274
 * CREATE TABLE .. SELECT can start a *new* normal transaction
275
 * because of the fact that SELECTs on a transactional storage
276
 * engine participate in the normal SQL transaction (due to
277
 * isolation level issues and consistent read views).
278
 *
279
 * Behaviour of the server in this case is currently badly
280
 * defined.
281
 *
282
 * DDL statements use a form of "semantic" logging
283
 * to maintain atomicity: if CREATE TABLE .. SELECT failed,
284
 * the newly created table is deleted.
285
 * 
286
 * In addition, some DDL statements issue interim transaction
287
 * commits: e.g. ALTER TABLE issues a COMMIT after data is copied
288
 * from the original table to the internal temporary table. Other
289
 * statements, e.g. CREATE TABLE ... SELECT do not always commit
290
 * after itself.
291
 *
292
 * And finally there is a group of DDL statements such as
293
 * RENAME/DROP TABLE that doesn't start a new transaction
294
 * and doesn't commit.
295
 *
296
 * A consistent behaviour is perhaps to always commit the normal
297
 * transaction after all DDLs, just like the statement transaction
298
 * is always committed at the end of all statements.
299
 */
1273.1.22 by Jay Pipes
Automates registration of statement transaction resources. No more need for storage engines to call TransactionServices::trans_register_ha(session, false, engine). yeah \o/
300
void TransactionServices::registerResourceForStatement(Session *session,
1273.1.30 by Jay Pipes
* Completes the blueprint for splitting the XA Resource Manager
301
                                                       plugin::MonitoredInTransaction *monitored,
1273.1.22 by Jay Pipes
Automates registration of statement transaction resources. No more need for storage engines to call TransactionServices::trans_register_ha(session, false, engine). yeah \o/
302
                                                       plugin::TransactionalStorageEngine *engine)
303
{
1273.1.27 by Jay Pipes
Completes the work of removing the weirdness around transaction
304
  if (session_test_options(session, OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))
305
  {
306
    /* 
307
     * Now we automatically register this resource manager for the
308
     * normal transaction.  This is fine because a statement
309
     * transaction registration should always enlist the resource
310
     * in the normal transaction which contains the statement
311
     * transaction.
312
     */
1273.1.30 by Jay Pipes
* Completes the blueprint for splitting the XA Resource Manager
313
    registerResourceForTransaction(session, monitored, engine);
314
  }
315
316
  TransactionContext *trans= &session->transaction.stmt;
317
  ResourceContext *resource_context= session->getResourceContext(monitored, 0);
318
319
  if (resource_context->isStarted())
320
    return; /* already registered, return */
321
322
  assert(monitored->participatesInSqlTransaction());
323
  assert(not monitored->participatesInXaTransaction());
324
325
  resource_context->setMonitored(monitored);
326
  resource_context->setTransactionalStorageEngine(engine);
327
  trans->registerResource(resource_context);
328
329
  trans->no_2pc|= true;
330
}
331
332
void TransactionServices::registerResourceForStatement(Session *session,
333
                                                       plugin::MonitoredInTransaction *monitored,
334
                                                       plugin::TransactionalStorageEngine *engine,
335
                                                       plugin::XaResourceManager *resource_manager)
336
{
337
  if (session_test_options(session, OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))
338
  {
339
    /* 
340
     * Now we automatically register this resource manager for the
341
     * normal transaction.  This is fine because a statement
342
     * transaction registration should always enlist the resource
343
     * in the normal transaction which contains the statement
344
     * transaction.
345
     */
346
    registerResourceForTransaction(session, monitored, engine, resource_manager);
347
  }
348
349
  TransactionContext *trans= &session->transaction.stmt;
350
  ResourceContext *resource_context= session->getResourceContext(monitored, 0);
351
352
  if (resource_context->isStarted())
353
    return; /* already registered, return */
354
355
  assert(monitored->participatesInXaTransaction());
356
  assert(monitored->participatesInSqlTransaction());
357
358
  resource_context->setMonitored(monitored);
359
  resource_context->setTransactionalStorageEngine(engine);
360
  resource_context->setXaResourceManager(resource_manager);
361
  trans->registerResource(resource_context);
362
363
  trans->no_2pc|= false;
1273.1.22 by Jay Pipes
Automates registration of statement transaction resources. No more need for storage engines to call TransactionServices::trans_register_ha(session, false, engine). yeah \o/
364
}
365
1273.1.27 by Jay Pipes
Completes the work of removing the weirdness around transaction
366
void TransactionServices::registerResourceForTransaction(Session *session,
1273.1.30 by Jay Pipes
* Completes the blueprint for splitting the XA Resource Manager
367
                                                         plugin::MonitoredInTransaction *monitored,
1273.1.27 by Jay Pipes
Completes the work of removing the weirdness around transaction
368
                                                         plugin::TransactionalStorageEngine *engine)
1273.1.2 by Jay Pipes
This patch does not change any algorithms or code paths,
369
{
1273.1.22 by Jay Pipes
Automates registration of statement transaction resources. No more need for storage engines to call TransactionServices::trans_register_ha(session, false, engine). yeah \o/
370
  TransactionContext *trans= &session->transaction.all;
1273.1.30 by Jay Pipes
* Completes the blueprint for splitting the XA Resource Manager
371
  ResourceContext *resource_context= session->getResourceContext(monitored, 1);
372
373
  if (resource_context->isStarted())
374
    return; /* already registered, return */
375
376
  session->server_status|= SERVER_STATUS_IN_TRANS;
377
378
  trans->registerResource(resource_context);
379
380
  assert(monitored->participatesInSqlTransaction());
381
  assert(not monitored->participatesInXaTransaction());
382
383
  resource_context->setMonitored(monitored);
384
  resource_context->setTransactionalStorageEngine(engine);
385
  trans->no_2pc|= true;
386
387
  if (session->transaction.xid_state.xid.is_null())
388
    session->transaction.xid_state.xid.set(session->getQueryId());
389
1333.1.1 by Jay Pipes
Manually issue a call to TransactionStorageEngine::startTransaction() inside
390
  engine->startTransaction(session, START_TRANS_NO_OPTIONS);
391
1273.1.30 by Jay Pipes
* Completes the blueprint for splitting the XA Resource Manager
392
  /* Only true if user is executing a BEGIN WORK/START TRANSACTION */
393
  if (! session->getResourceContext(monitored, 0)->isStarted())
394
    registerResourceForStatement(session, monitored, engine);
395
}
396
397
void TransactionServices::registerResourceForTransaction(Session *session,
398
                                                         plugin::MonitoredInTransaction *monitored,
399
                                                         plugin::TransactionalStorageEngine *engine,
400
                                                         plugin::XaResourceManager *resource_manager)
401
{
402
  TransactionContext *trans= &session->transaction.all;
403
  ResourceContext *resource_context= session->getResourceContext(monitored, 1);
404
405
  if (resource_context->isStarted())
406
    return; /* already registered, return */
407
408
  session->server_status|= SERVER_STATUS_IN_TRANS;
409
410
  trans->registerResource(resource_context);
411
412
  assert(monitored->participatesInSqlTransaction());
413
414
  resource_context->setMonitored(monitored);
415
  resource_context->setXaResourceManager(resource_manager);
416
  resource_context->setTransactionalStorageEngine(engine);
417
  trans->no_2pc|= true;
418
419
  if (session->transaction.xid_state.xid.is_null())
420
    session->transaction.xid_state.xid.set(session->getQueryId());
421
1333.1.1 by Jay Pipes
Manually issue a call to TransactionStorageEngine::startTransaction() inside
422
  engine->startTransaction(session, START_TRANS_NO_OPTIONS);
423
1273.1.30 by Jay Pipes
* Completes the blueprint for splitting the XA Resource Manager
424
  /* Only true if user is executing a BEGIN WORK/START TRANSACTION */
425
  if (! session->getResourceContext(monitored, 0)->isStarted())
426
    registerResourceForStatement(session, monitored, engine, resource_manager);
1273.1.2 by Jay Pipes
This patch does not change any algorithms or code paths,
427
}
428
429
/**
430
  @retval
431
    0   ok
432
  @retval
433
    1   transaction was rolled back
434
  @retval
435
    2   error during commit, data may be inconsistent
436
437
  @todo
438
    Since we don't support nested statement transactions in 5.0,
439
    we can't commit or rollback stmt transactions while we are inside
440
    stored functions or triggers. So we simply do nothing now.
441
    TODO: This should be fixed in later ( >= 5.1) releases.
442
*/
1405.3.5 by Jay Pipes
TransactionServices method names now meet code style guidelines.
443
int TransactionServices::commitTransaction(Session *session, bool normal_transaction)
1273.1.2 by Jay Pipes
This patch does not change any algorithms or code paths,
444
{
445
  int error= 0, cookie= 0;
446
  /*
447
    'all' means that this is either an explicit commit issued by
448
    user, or an implicit commit issued by a DDL.
449
  */
1273.1.10 by Jay Pipes
* Renames Ha_trx_info to drizzled::ResourceContext
450
  TransactionContext *trans= normal_transaction ? &session->transaction.all : &session->transaction.stmt;
451
  TransactionContext::ResourceContexts &resource_contexts= trans->getResourceContexts();
452
453
  bool is_real_trans= normal_transaction || session->transaction.all.getResourceContexts().empty();
1273.1.2 by Jay Pipes
This patch does not change any algorithms or code paths,
454
455
  /*
456
    We must not commit the normal transaction if a statement
457
    transaction is pending. Otherwise statement transaction
458
    flags will not get propagated to its normal transaction's
459
    counterpart.
460
  */
1273.1.10 by Jay Pipes
* Renames Ha_trx_info to drizzled::ResourceContext
461
  assert(session->transaction.stmt.getResourceContexts().empty() ||
1273.1.2 by Jay Pipes
This patch does not change any algorithms or code paths,
462
              trans == &session->transaction.stmt);
463
1273.1.10 by Jay Pipes
* Renames Ha_trx_info to drizzled::ResourceContext
464
  if (resource_contexts.empty() == false)
1273.1.2 by Jay Pipes
This patch does not change any algorithms or code paths,
465
  {
466
    if (is_real_trans && wait_if_global_read_lock(session, 0, 0))
467
    {
1405.3.5 by Jay Pipes
TransactionServices method names now meet code style guidelines.
468
      rollbackTransaction(session, normal_transaction);
1273.1.2 by Jay Pipes
This patch does not change any algorithms or code paths,
469
      return 1;
470
    }
471
1405.3.6 by Jay Pipes
Here, we do two main things:
472
    /*
473
     * If replication is on, we do a PREPARE on the resource managers, push the
474
     * Transaction message across the replication stream, and then COMMIT if the
475
     * replication stream returned successfully.
476
     */
477
    if (shouldConstructMessages())
1273.1.2 by Jay Pipes
This patch does not change any algorithms or code paths,
478
    {
1273.1.10 by Jay Pipes
* Renames Ha_trx_info to drizzled::ResourceContext
479
      for (TransactionContext::ResourceContexts::iterator it= resource_contexts.begin();
480
           it != resource_contexts.end() && ! error;
481
           ++it)
1273.1.2 by Jay Pipes
This patch does not change any algorithms or code paths,
482
      {
1273.1.10 by Jay Pipes
* Renames Ha_trx_info to drizzled::ResourceContext
483
        ResourceContext *resource_context= *it;
1273.1.2 by Jay Pipes
This patch does not change any algorithms or code paths,
484
        int err;
485
        /*
486
          Do not call two-phase commit if this particular
487
          transaction is read-only. This allows for simpler
488
          implementation in engines that are always read-only.
489
        */
1273.1.12 by Jay Pipes
Cleanup style and documentation for ResourceContext and setTransactionReadWrite
490
        if (! resource_context->hasModifiedData())
1273.1.2 by Jay Pipes
This patch does not change any algorithms or code paths,
491
          continue;
1273.1.15 by Jay Pipes
This patch completes the first step in the splitting of
492
1273.1.30 by Jay Pipes
* Completes the blueprint for splitting the XA Resource Manager
493
        plugin::MonitoredInTransaction *resource= resource_context->getMonitored();
494
495
        if (resource->participatesInXaTransaction())
1273.1.2 by Jay Pipes
This patch does not change any algorithms or code paths,
496
        {
1273.1.30 by Jay Pipes
* Completes the blueprint for splitting the XA Resource Manager
497
          if ((err= resource_context->getXaResourceManager()->xaPrepare(session, normal_transaction)))
498
          {
499
            my_error(ER_ERROR_DURING_COMMIT, MYF(0), err);
500
            error= 1;
501
          }
502
          else
503
          {
1689.5.1 by Joseph Daly
remove increment calls
504
            session->status_var.ha_prepare_count++;
1273.1.30 by Jay Pipes
* Completes the blueprint for splitting the XA Resource Manager
505
          }
1273.1.2 by Jay Pipes
This patch does not change any algorithms or code paths,
506
        }
507
      }
1405.3.6 by Jay Pipes
Here, we do two main things:
508
      if (error == 0 && is_real_trans)
509
      {
510
        /*
511
         * Push the constructed Transaction messages across to
512
         * replicators and appliers.
513
         */
514
        error= commitTransactionMessage(session);
515
      }
1273.1.2 by Jay Pipes
This patch does not change any algorithms or code paths,
516
      if (error)
517
      {
1405.3.5 by Jay Pipes
TransactionServices method names now meet code style guidelines.
518
        rollbackTransaction(session, normal_transaction);
1273.1.2 by Jay Pipes
This patch does not change any algorithms or code paths,
519
        error= 1;
520
        goto end;
521
      }
522
    }
1405.3.5 by Jay Pipes
TransactionServices method names now meet code style guidelines.
523
    error= commitPhaseOne(session, normal_transaction) ? (cookie ? 2 : 1) : 0;
1273.1.2 by Jay Pipes
This patch does not change any algorithms or code paths,
524
end:
525
    if (is_real_trans)
526
      start_waiting_global_read_lock(session);
527
  }
528
  return error;
529
}
530
531
/**
532
  @note
533
  This function does not care about global read lock. A caller should.
534
*/
1405.3.5 by Jay Pipes
TransactionServices method names now meet code style guidelines.
535
int TransactionServices::commitPhaseOne(Session *session, bool normal_transaction)
1273.1.2 by Jay Pipes
This patch does not change any algorithms or code paths,
536
{
537
  int error=0;
1273.1.10 by Jay Pipes
* Renames Ha_trx_info to drizzled::ResourceContext
538
  TransactionContext *trans= normal_transaction ? &session->transaction.all : &session->transaction.stmt;
539
  TransactionContext::ResourceContexts &resource_contexts= trans->getResourceContexts();
540
541
  bool is_real_trans= normal_transaction || session->transaction.all.getResourceContexts().empty();
542
543
  if (resource_contexts.empty() == false)
1273.1.2 by Jay Pipes
This patch does not change any algorithms or code paths,
544
  {
1273.1.10 by Jay Pipes
* Renames Ha_trx_info to drizzled::ResourceContext
545
    for (TransactionContext::ResourceContexts::iterator it= resource_contexts.begin();
546
         it != resource_contexts.end();
547
         ++it)
1273.1.2 by Jay Pipes
This patch does not change any algorithms or code paths,
548
    {
549
      int err;
1273.1.10 by Jay Pipes
* Renames Ha_trx_info to drizzled::ResourceContext
550
      ResourceContext *resource_context= *it;
1273.1.27 by Jay Pipes
Completes the work of removing the weirdness around transaction
551
1273.1.30 by Jay Pipes
* Completes the blueprint for splitting the XA Resource Manager
552
      plugin::MonitoredInTransaction *resource= resource_context->getMonitored();
553
554
      if (resource->participatesInXaTransaction())
555
      {
556
        if ((err= resource_context->getXaResourceManager()->xaCommit(session, normal_transaction)))
557
        {
558
          my_error(ER_ERROR_DURING_COMMIT, MYF(0), err);
559
          error= 1;
560
        }
1324.1.1 by Jay Pipes
Fixes Bug #535296 by only incrementing ha_commit_count when its a normal transaction commit.
561
        else if (normal_transaction)
1273.1.30 by Jay Pipes
* Completes the blueprint for splitting the XA Resource Manager
562
        {
1689.5.1 by Joseph Daly
remove increment calls
563
          session->status_var.ha_commit_count++;
1273.1.30 by Jay Pipes
* Completes the blueprint for splitting the XA Resource Manager
564
        }
565
      }
566
      else if (resource->participatesInSqlTransaction())
567
      {
568
        if ((err= resource_context->getTransactionalStorageEngine()->commit(session, normal_transaction)))
569
        {
570
          my_error(ER_ERROR_DURING_COMMIT, MYF(0), err);
571
          error= 1;
572
        }
1324.1.1 by Jay Pipes
Fixes Bug #535296 by only incrementing ha_commit_count when its a normal transaction commit.
573
        else if (normal_transaction)
1273.1.30 by Jay Pipes
* Completes the blueprint for splitting the XA Resource Manager
574
        {
1689.5.1 by Joseph Daly
remove increment calls
575
          session->status_var.ha_commit_count++;
1273.1.30 by Jay Pipes
* Completes the blueprint for splitting the XA Resource Manager
576
        }
577
      }
1273.1.10 by Jay Pipes
* Renames Ha_trx_info to drizzled::ResourceContext
578
      resource_context->reset(); /* keep it conveniently zero-filled */
1273.1.2 by Jay Pipes
This patch does not change any algorithms or code paths,
579
    }
1273.1.10 by Jay Pipes
* Renames Ha_trx_info to drizzled::ResourceContext
580
1273.1.2 by Jay Pipes
This patch does not change any algorithms or code paths,
581
    if (is_real_trans)
582
      session->transaction.xid_state.xid.null();
1273.1.10 by Jay Pipes
* Renames Ha_trx_info to drizzled::ResourceContext
583
584
    if (normal_transaction)
1273.1.2 by Jay Pipes
This patch does not change any algorithms or code paths,
585
    {
1273.1.10 by Jay Pipes
* Renames Ha_trx_info to drizzled::ResourceContext
586
      session->variables.tx_isolation= session->session_tx_isolation;
1273.1.2 by Jay Pipes
This patch does not change any algorithms or code paths,
587
      session->transaction.cleanup();
588
    }
589
  }
1273.1.27 by Jay Pipes
Completes the work of removing the weirdness around transaction
590
  trans->reset();
1273.1.2 by Jay Pipes
This patch does not change any algorithms or code paths,
591
  return error;
592
}
593
1405.3.5 by Jay Pipes
TransactionServices method names now meet code style guidelines.
594
int TransactionServices::rollbackTransaction(Session *session, bool normal_transaction)
1273.1.2 by Jay Pipes
This patch does not change any algorithms or code paths,
595
{
1273.1.10 by Jay Pipes
* Renames Ha_trx_info to drizzled::ResourceContext
596
  int error= 0;
597
  TransactionContext *trans= normal_transaction ? &session->transaction.all : &session->transaction.stmt;
598
  TransactionContext::ResourceContexts &resource_contexts= trans->getResourceContexts();
599
600
  bool is_real_trans= normal_transaction || session->transaction.all.getResourceContexts().empty();
1273.1.2 by Jay Pipes
This patch does not change any algorithms or code paths,
601
602
  /*
603
    We must not rollback the normal transaction if a statement
604
    transaction is pending.
605
  */
1273.1.10 by Jay Pipes
* Renames Ha_trx_info to drizzled::ResourceContext
606
  assert(session->transaction.stmt.getResourceContexts().empty() ||
1273.1.2 by Jay Pipes
This patch does not change any algorithms or code paths,
607
              trans == &session->transaction.stmt);
608
1273.1.10 by Jay Pipes
* Renames Ha_trx_info to drizzled::ResourceContext
609
  if (resource_contexts.empty() == false)
1273.1.2 by Jay Pipes
This patch does not change any algorithms or code paths,
610
  {
1273.1.10 by Jay Pipes
* Renames Ha_trx_info to drizzled::ResourceContext
611
    for (TransactionContext::ResourceContexts::iterator it= resource_contexts.begin();
612
         it != resource_contexts.end();
613
         ++it)
1273.1.2 by Jay Pipes
This patch does not change any algorithms or code paths,
614
    {
615
      int err;
1273.1.10 by Jay Pipes
* Renames Ha_trx_info to drizzled::ResourceContext
616
      ResourceContext *resource_context= *it;
1273.1.27 by Jay Pipes
Completes the work of removing the weirdness around transaction
617
1273.1.30 by Jay Pipes
* Completes the blueprint for splitting the XA Resource Manager
618
      plugin::MonitoredInTransaction *resource= resource_context->getMonitored();
619
620
      if (resource->participatesInXaTransaction())
621
      {
622
        if ((err= resource_context->getXaResourceManager()->xaRollback(session, normal_transaction)))
623
        {
624
          my_error(ER_ERROR_DURING_ROLLBACK, MYF(0), err);
625
          error= 1;
626
        }
1324.1.1 by Jay Pipes
Fixes Bug #535296 by only incrementing ha_commit_count when its a normal transaction commit.
627
        else if (normal_transaction)
1273.1.30 by Jay Pipes
* Completes the blueprint for splitting the XA Resource Manager
628
        {
1689.5.1 by Joseph Daly
remove increment calls
629
          session->status_var.ha_rollback_count++;
1273.1.30 by Jay Pipes
* Completes the blueprint for splitting the XA Resource Manager
630
        }
631
      }
632
      else if (resource->participatesInSqlTransaction())
633
      {
634
        if ((err= resource_context->getTransactionalStorageEngine()->rollback(session, normal_transaction)))
635
        {
636
          my_error(ER_ERROR_DURING_ROLLBACK, MYF(0), err);
637
          error= 1;
638
        }
1324.1.1 by Jay Pipes
Fixes Bug #535296 by only incrementing ha_commit_count when its a normal transaction commit.
639
        else if (normal_transaction)
1273.1.30 by Jay Pipes
* Completes the blueprint for splitting the XA Resource Manager
640
        {
1689.5.1 by Joseph Daly
remove increment calls
641
          session->status_var.ha_rollback_count++;
1273.1.30 by Jay Pipes
* Completes the blueprint for splitting the XA Resource Manager
642
        }
643
      }
1273.1.10 by Jay Pipes
* Renames Ha_trx_info to drizzled::ResourceContext
644
      resource_context->reset(); /* keep it conveniently zero-filled */
1273.1.2 by Jay Pipes
This patch does not change any algorithms or code paths,
645
    }
646
    
647
    /* 
648
     * We need to signal the ROLLBACK to ReplicationServices here
649
     * BEFORE we set the transaction ID to NULL.  This is because
650
     * if a bulk segment was sent to replicators, we need to send
651
     * a rollback statement with the corresponding transaction ID
652
     * to rollback.
653
     */
1336.2.2 by Jay Pipes
NO CODE CHANGES - Simply moves pieces of ReplicationServices to TransactionServices. Preparation for making the ReplicationServices component only responsible for communication between replicators, appliers, publishers, and subscribers.
654
    rollbackTransactionMessage(session);
1273.1.2 by Jay Pipes
This patch does not change any algorithms or code paths,
655
656
    if (is_real_trans)
657
      session->transaction.xid_state.xid.null();
1273.1.10 by Jay Pipes
* Renames Ha_trx_info to drizzled::ResourceContext
658
    if (normal_transaction)
1273.1.2 by Jay Pipes
This patch does not change any algorithms or code paths,
659
    {
660
      session->variables.tx_isolation=session->session_tx_isolation;
661
      session->transaction.cleanup();
662
    }
663
  }
1273.1.10 by Jay Pipes
* Renames Ha_trx_info to drizzled::ResourceContext
664
  if (normal_transaction)
1273.1.2 by Jay Pipes
This patch does not change any algorithms or code paths,
665
    session->transaction_rollback_request= false;
666
667
  /*
1273.1.27 by Jay Pipes
Completes the work of removing the weirdness around transaction
668
   * If a non-transactional table was updated, warn the user
669
   */
1273.1.10 by Jay Pipes
* Renames Ha_trx_info to drizzled::ResourceContext
670
  if (is_real_trans &&
1273.1.13 by Jay Pipes
Style cleanup around TransactionContext::modified_non_trans_table and dead code removal
671
      session->transaction.all.hasModifiedNonTransData() &&
1273.1.10 by Jay Pipes
* Renames Ha_trx_info to drizzled::ResourceContext
672
      session->killed != Session::KILL_CONNECTION)
673
  {
1273.1.2 by Jay Pipes
This patch does not change any algorithms or code paths,
674
    push_warning(session, DRIZZLE_ERROR::WARN_LEVEL_WARN,
675
                 ER_WARNING_NOT_COMPLETE_ROLLBACK,
676
                 ER(ER_WARNING_NOT_COMPLETE_ROLLBACK));
1273.1.10 by Jay Pipes
* Renames Ha_trx_info to drizzled::ResourceContext
677
  }
1273.1.27 by Jay Pipes
Completes the work of removing the weirdness around transaction
678
  trans->reset();
1273.1.2 by Jay Pipes
This patch does not change any algorithms or code paths,
679
  return error;
680
}
681
682
/**
683
  This is used to commit or rollback a single statement depending on
684
  the value of error.
685
686
  @note
687
    Note that if the autocommit is on, then the following call inside
688
    InnoDB will commit or rollback the whole transaction (= the statement). The
689
    autocommit mechanism built into InnoDB is based on counting locks, but if
690
    the user has used LOCK TABLES then that mechanism does not know to do the
691
    commit.
692
*/
1405.3.5 by Jay Pipes
TransactionServices method names now meet code style guidelines.
693
int TransactionServices::autocommitOrRollback(Session *session, int error)
1273.1.2 by Jay Pipes
This patch does not change any algorithms or code paths,
694
{
1273.1.10 by Jay Pipes
* Renames Ha_trx_info to drizzled::ResourceContext
695
  if (session->transaction.stmt.getResourceContexts().empty() == false)
1273.1.2 by Jay Pipes
This patch does not change any algorithms or code paths,
696
  {
1273.1.10 by Jay Pipes
* Renames Ha_trx_info to drizzled::ResourceContext
697
    if (! error)
1273.1.2 by Jay Pipes
This patch does not change any algorithms or code paths,
698
    {
1405.3.5 by Jay Pipes
TransactionServices method names now meet code style guidelines.
699
      if (commitTransaction(session, false))
1273.1.2 by Jay Pipes
This patch does not change any algorithms or code paths,
700
        error= 1;
701
    }
702
    else
703
    {
1405.3.5 by Jay Pipes
TransactionServices method names now meet code style guidelines.
704
      (void) rollbackTransaction(session, false);
1273.1.2 by Jay Pipes
This patch does not change any algorithms or code paths,
705
      if (session->transaction_rollback_request)
1405.3.5 by Jay Pipes
TransactionServices method names now meet code style guidelines.
706
        (void) rollbackTransaction(session, true);
1273.1.2 by Jay Pipes
This patch does not change any algorithms or code paths,
707
    }
708
1273.1.10 by Jay Pipes
* Renames Ha_trx_info to drizzled::ResourceContext
709
    session->variables.tx_isolation= session->session_tx_isolation;
1273.1.2 by Jay Pipes
This patch does not change any algorithms or code paths,
710
  }
711
  return error;
712
}
713
1273.1.10 by Jay Pipes
* Renames Ha_trx_info to drizzled::ResourceContext
714
struct ResourceContextCompare : public std::binary_function<ResourceContext *, ResourceContext *, bool>
715
{
716
  result_type operator()(const ResourceContext *lhs, const ResourceContext *rhs) const
717
  {
1273.1.30 by Jay Pipes
* Completes the blueprint for splitting the XA Resource Manager
718
    /* The below is perfectly fine, since we're simply comparing addresses for the underlying
719
     * resources aren't the same... */
720
    return reinterpret_cast<uint64_t>(lhs->getMonitored()) < reinterpret_cast<uint64_t>(rhs->getMonitored());
1273.1.10 by Jay Pipes
* Renames Ha_trx_info to drizzled::ResourceContext
721
  }
722
};
1273.1.2 by Jay Pipes
This patch does not change any algorithms or code paths,
723
1405.3.5 by Jay Pipes
TransactionServices method names now meet code style guidelines.
724
int TransactionServices::rollbackToSavepoint(Session *session, NamedSavepoint &sv)
1273.1.2 by Jay Pipes
This patch does not change any algorithms or code paths,
725
{
726
  int error= 0;
1273.1.10 by Jay Pipes
* Renames Ha_trx_info to drizzled::ResourceContext
727
  TransactionContext *trans= &session->transaction.all;
728
  TransactionContext::ResourceContexts &tran_resource_contexts= trans->getResourceContexts();
729
  TransactionContext::ResourceContexts &sv_resource_contexts= sv.getResourceContexts();
1273.1.2 by Jay Pipes
This patch does not change any algorithms or code paths,
730
1273.1.10 by Jay Pipes
* Renames Ha_trx_info to drizzled::ResourceContext
731
  trans->no_2pc= false;
1273.1.2 by Jay Pipes
This patch does not change any algorithms or code paths,
732
  /*
733
    rolling back to savepoint in all storage engines that were part of the
734
    transaction when the savepoint was set
735
  */
1273.1.10 by Jay Pipes
* Renames Ha_trx_info to drizzled::ResourceContext
736
  for (TransactionContext::ResourceContexts::iterator it= sv_resource_contexts.begin();
737
       it != sv_resource_contexts.end();
738
       ++it)
1273.1.2 by Jay Pipes
This patch does not change any algorithms or code paths,
739
  {
740
    int err;
1273.1.10 by Jay Pipes
* Renames Ha_trx_info to drizzled::ResourceContext
741
    ResourceContext *resource_context= *it;
1273.1.30 by Jay Pipes
* Completes the blueprint for splitting the XA Resource Manager
742
743
    plugin::MonitoredInTransaction *resource= resource_context->getMonitored();
744
745
    if (resource->participatesInSqlTransaction())
746
    {
747
      if ((err= resource_context->getTransactionalStorageEngine()->rollbackToSavepoint(session, sv)))
748
      {
749
        my_error(ER_ERROR_DURING_ROLLBACK, MYF(0), err);
750
        error= 1;
751
      }
752
      else
753
      {
1689.5.1 by Joseph Daly
remove increment calls
754
        session->status_var.ha_savepoint_rollback_count++;
1273.1.30 by Jay Pipes
* Completes the blueprint for splitting the XA Resource Manager
755
      }
1273.1.2 by Jay Pipes
This patch does not change any algorithms or code paths,
756
    }
1273.1.30 by Jay Pipes
* Completes the blueprint for splitting the XA Resource Manager
757
    trans->no_2pc|= not resource->participatesInXaTransaction();
1273.1.2 by Jay Pipes
This patch does not change any algorithms or code paths,
758
  }
759
  /*
760
    rolling back the transaction in all storage engines that were not part of
761
    the transaction when the savepoint was set
762
  */
763
  {
1273.1.10 by Jay Pipes
* Renames Ha_trx_info to drizzled::ResourceContext
764
    TransactionContext::ResourceContexts sorted_tran_resource_contexts(tran_resource_contexts);
765
    TransactionContext::ResourceContexts sorted_sv_resource_contexts(sv_resource_contexts);
766
    TransactionContext::ResourceContexts set_difference_contexts;
767
1491.2.3 by Jay Pipes
Fix for Bug #542299. The cause of the segfault was no pre-allocation of the target vector in set_difference(). Depending on STL implementations, set_difference() calls the STL's copy(), which requires pre-allocation of all targets and destinations. This meant the default constructor for set_difference_contexts was not adequate, and we should call vector<>::reserve() to allocate enough memory for pointers to the target elements.
768
    /* 
769
     * Bug #542299: segfault during set_difference() below.  copy<>() requires pre-allocation
770
     * of all elements, including the target, which is why we pre-allocate the set_difference_contexts
771
     * here
772
     */
773
    set_difference_contexts.reserve(max(tran_resource_contexts.size(), sv_resource_contexts.size()));
774
1273.1.10 by Jay Pipes
* Renames Ha_trx_info to drizzled::ResourceContext
775
    sort(sorted_tran_resource_contexts.begin(),
776
         sorted_tran_resource_contexts.end(),
777
         ResourceContextCompare());
778
    sort(sorted_sv_resource_contexts.begin(),
779
         sorted_sv_resource_contexts.end(),
780
         ResourceContextCompare());
781
    set_difference(sorted_tran_resource_contexts.begin(),
782
                   sorted_tran_resource_contexts.end(),
783
                   sorted_sv_resource_contexts.begin(),
784
                   sorted_sv_resource_contexts.end(),
785
                   set_difference_contexts.begin(),
786
                   ResourceContextCompare());
787
    /* 
788
     * set_difference_contexts now contains all resource contexts
789
     * which are in the transaction context but were NOT in the
790
     * savepoint's resource contexts.
791
     */
792
        
793
    for (TransactionContext::ResourceContexts::iterator it= set_difference_contexts.begin();
794
         it != set_difference_contexts.end();
795
         ++it)
796
    {
797
      ResourceContext *resource_context= *it;
798
      int err;
1273.1.30 by Jay Pipes
* Completes the blueprint for splitting the XA Resource Manager
799
800
      plugin::MonitoredInTransaction *resource= resource_context->getMonitored();
801
802
      if (resource->participatesInSqlTransaction())
803
      {
804
        if ((err= resource_context->getTransactionalStorageEngine()->rollback(session, !(0))))
805
        {
806
          my_error(ER_ERROR_DURING_ROLLBACK, MYF(0), err);
807
          error= 1;
808
        }
809
        else
810
        {
1689.5.1 by Joseph Daly
remove increment calls
811
          session->status_var.ha_rollback_count++;
1273.1.30 by Jay Pipes
* Completes the blueprint for splitting the XA Resource Manager
812
        }
1273.1.10 by Jay Pipes
* Renames Ha_trx_info to drizzled::ResourceContext
813
      }
814
      resource_context->reset(); /* keep it conveniently zero-filled */
1273.1.2 by Jay Pipes
This patch does not change any algorithms or code paths,
815
    }
816
  }
1273.1.10 by Jay Pipes
* Renames Ha_trx_info to drizzled::ResourceContext
817
  trans->setResourceContexts(sv_resource_contexts);
1273.1.2 by Jay Pipes
This patch does not change any algorithms or code paths,
818
  return error;
819
}
820
821
/**
822
  @note
823
  according to the sql standard (ISO/IEC 9075-2:2003)
824
  section "4.33.4 SQL-statements and transaction states",
1273.1.4 by Jay Pipes
This patch significantly reworks the way that
825
  NamedSavepoint is *not* transaction-initiating SQL-statement
1273.1.2 by Jay Pipes
This patch does not change any algorithms or code paths,
826
*/
1405.3.5 by Jay Pipes
TransactionServices method names now meet code style guidelines.
827
int TransactionServices::setSavepoint(Session *session, NamedSavepoint &sv)
1273.1.2 by Jay Pipes
This patch does not change any algorithms or code paths,
828
{
829
  int error= 0;
1273.1.10 by Jay Pipes
* Renames Ha_trx_info to drizzled::ResourceContext
830
  TransactionContext *trans= &session->transaction.all;
831
  TransactionContext::ResourceContexts &resource_contexts= trans->getResourceContexts();
832
833
  if (resource_contexts.empty() == false)
1273.1.2 by Jay Pipes
This patch does not change any algorithms or code paths,
834
  {
1273.1.10 by Jay Pipes
* Renames Ha_trx_info to drizzled::ResourceContext
835
    for (TransactionContext::ResourceContexts::iterator it= resource_contexts.begin();
836
         it != resource_contexts.end();
837
         ++it)
1273.1.2 by Jay Pipes
This patch does not change any algorithms or code paths,
838
    {
1273.1.10 by Jay Pipes
* Renames Ha_trx_info to drizzled::ResourceContext
839
      ResourceContext *resource_context= *it;
840
      int err;
1273.1.30 by Jay Pipes
* Completes the blueprint for splitting the XA Resource Manager
841
842
      plugin::MonitoredInTransaction *resource= resource_context->getMonitored();
843
844
      if (resource->participatesInSqlTransaction())
845
      {
846
        if ((err= resource_context->getTransactionalStorageEngine()->setSavepoint(session, sv)))
847
        {
848
          my_error(ER_GET_ERRNO, MYF(0), err);
849
          error= 1;
850
        }
851
        else
852
        {
1689.5.1 by Joseph Daly
remove increment calls
853
          session->status_var.ha_savepoint_count++;
1273.1.30 by Jay Pipes
* Completes the blueprint for splitting the XA Resource Manager
854
        }
1273.1.10 by Jay Pipes
* Renames Ha_trx_info to drizzled::ResourceContext
855
      }
1273.1.2 by Jay Pipes
This patch does not change any algorithms or code paths,
856
    }
857
  }
858
  /*
1273.1.10 by Jay Pipes
* Renames Ha_trx_info to drizzled::ResourceContext
859
    Remember the list of registered storage engines.
1273.1.2 by Jay Pipes
This patch does not change any algorithms or code paths,
860
  */
1273.1.10 by Jay Pipes
* Renames Ha_trx_info to drizzled::ResourceContext
861
  sv.setResourceContexts(resource_contexts);
1273.1.2 by Jay Pipes
This patch does not change any algorithms or code paths,
862
  return error;
863
}
864
1405.3.5 by Jay Pipes
TransactionServices method names now meet code style guidelines.
865
int TransactionServices::releaseSavepoint(Session *session, NamedSavepoint &sv)
1273.1.2 by Jay Pipes
This patch does not change any algorithms or code paths,
866
{
867
  int error= 0;
1273.1.10 by Jay Pipes
* Renames Ha_trx_info to drizzled::ResourceContext
868
869
  TransactionContext::ResourceContexts &resource_contexts= sv.getResourceContexts();
870
871
  for (TransactionContext::ResourceContexts::iterator it= resource_contexts.begin();
872
       it != resource_contexts.end();
873
       ++it)
1273.1.2 by Jay Pipes
This patch does not change any algorithms or code paths,
874
  {
875
    int err;
1273.1.10 by Jay Pipes
* Renames Ha_trx_info to drizzled::ResourceContext
876
    ResourceContext *resource_context= *it;
1273.1.30 by Jay Pipes
* Completes the blueprint for splitting the XA Resource Manager
877
878
    plugin::MonitoredInTransaction *resource= resource_context->getMonitored();
879
880
    if (resource->participatesInSqlTransaction())
881
    {
882
      if ((err= resource_context->getTransactionalStorageEngine()->releaseSavepoint(session, sv)))
883
      {
884
        my_error(ER_GET_ERRNO, MYF(0), err);
885
        error= 1;
886
      }
1273.1.2 by Jay Pipes
This patch does not change any algorithms or code paths,
887
    }
888
  }
889
  return error;
890
}
891
1405.4.10 by Jay Pipes
OK, Sun Studio still didn't like that...seems to think that inline means something different than other compilers think it is...
892
bool TransactionServices::shouldConstructMessages()
893
{
894
  ReplicationServices &replication_services= ReplicationServices::singleton();
895
  return replication_services.isActive();
896
}
897
1336.2.2 by Jay Pipes
NO CODE CHANGES - Simply moves pieces of ReplicationServices to TransactionServices. Preparation for making the ReplicationServices component only responsible for communication between replicators, appliers, publishers, and subscribers.
898
message::Transaction *TransactionServices::getActiveTransactionMessage(Session *in_session)
899
{
900
  message::Transaction *transaction= in_session->getTransactionMessage();
901
902
  if (unlikely(transaction == NULL))
903
  {
904
    /* 
905
     * Allocate and initialize a new transaction message 
906
     * for this Session object.  Session is responsible for
907
     * deleting transaction message when done with it.
908
     */
909
    transaction= new (nothrow) message::Transaction();
910
    initTransactionMessage(*transaction, in_session);
911
    in_session->setTransactionMessage(transaction);
912
    return transaction;
913
  }
914
  else
915
    return transaction;
916
}
917
918
void TransactionServices::initTransactionMessage(message::Transaction &in_transaction,
919
                                          Session *in_session)
920
{
921
  message::TransactionContext *trx= in_transaction.mutable_transaction_context();
922
  trx->set_server_id(in_session->getServerId());
1405.4.7 by Jay Pipes
* Fixes drizzled's atomics:
923
  trx->set_transaction_id(getNextTransactionId());
1336.2.2 by Jay Pipes
NO CODE CHANGES - Simply moves pieces of ReplicationServices to TransactionServices. Preparation for making the ReplicationServices component only responsible for communication between replicators, appliers, publishers, and subscribers.
924
  trx->set_start_timestamp(in_session->getCurrentTimestamp());
925
}
926
927
void TransactionServices::finalizeTransactionMessage(message::Transaction &in_transaction,
928
                                              Session *in_session)
929
{
930
  message::TransactionContext *trx= in_transaction.mutable_transaction_context();
931
  trx->set_end_timestamp(in_session->getCurrentTimestamp());
932
}
933
934
void TransactionServices::cleanupTransactionMessage(message::Transaction *in_transaction,
935
                                             Session *in_session)
936
{
937
  delete in_transaction;
938
  in_session->setStatementMessage(NULL);
939
  in_session->setTransactionMessage(NULL);
940
}
941
1405.3.6 by Jay Pipes
Here, we do two main things:
942
int TransactionServices::commitTransactionMessage(Session *in_session)
1336.2.2 by Jay Pipes
NO CODE CHANGES - Simply moves pieces of ReplicationServices to TransactionServices. Preparation for making the ReplicationServices component only responsible for communication between replicators, appliers, publishers, and subscribers.
943
{
944
  ReplicationServices &replication_services= ReplicationServices::singleton();
945
  if (! replication_services.isActive())
1405.3.6 by Jay Pipes
Here, we do two main things:
946
    return 0;
1336.2.2 by Jay Pipes
NO CODE CHANGES - Simply moves pieces of ReplicationServices to TransactionServices. Preparation for making the ReplicationServices component only responsible for communication between replicators, appliers, publishers, and subscribers.
947
948
  /* If there is an active statement message, finalize it */
949
  message::Statement *statement= in_session->getStatementMessage();
950
951
  if (statement != NULL)
952
  {
953
    finalizeStatementMessage(*statement, in_session);
954
  }
955
  else
1405.3.6 by Jay Pipes
Here, we do two main things:
956
    return 0; /* No data modification occurred inside the transaction */
1336.2.2 by Jay Pipes
NO CODE CHANGES - Simply moves pieces of ReplicationServices to TransactionServices. Preparation for making the ReplicationServices component only responsible for communication between replicators, appliers, publishers, and subscribers.
957
  
958
  message::Transaction* transaction= getActiveTransactionMessage(in_session);
959
960
  finalizeTransactionMessage(*transaction, in_session);
961
  
1405.3.6 by Jay Pipes
Here, we do two main things:
962
  plugin::ReplicationReturnCode result= replication_services.pushTransactionMessage(*in_session, *transaction);
1336.2.2 by Jay Pipes
NO CODE CHANGES - Simply moves pieces of ReplicationServices to TransactionServices. Preparation for making the ReplicationServices component only responsible for communication between replicators, appliers, publishers, and subscribers.
963
964
  cleanupTransactionMessage(transaction, in_session);
1405.3.6 by Jay Pipes
Here, we do two main things:
965
966
  return static_cast<int>(result);
1336.2.2 by Jay Pipes
NO CODE CHANGES - Simply moves pieces of ReplicationServices to TransactionServices. Preparation for making the ReplicationServices component only responsible for communication between replicators, appliers, publishers, and subscribers.
967
}
968
969
void TransactionServices::initStatementMessage(message::Statement &statement,
970
                                        message::Statement::Type in_type,
971
                                        Session *in_session)
972
{
973
  statement.set_type(in_type);
974
  statement.set_start_timestamp(in_session->getCurrentTimestamp());
975
  /** @TODO Set sql string optionally */
976
}
977
978
void TransactionServices::finalizeStatementMessage(message::Statement &statement,
979
                                            Session *in_session)
980
{
981
  statement.set_end_timestamp(in_session->getCurrentTimestamp());
982
  in_session->setStatementMessage(NULL);
983
}
984
985
void TransactionServices::rollbackTransactionMessage(Session *in_session)
986
{
987
  ReplicationServices &replication_services= ReplicationServices::singleton();
988
  if (! replication_services.isActive())
989
    return;
990
  
991
  message::Transaction *transaction= getActiveTransactionMessage(in_session);
992
993
  /*
994
   * OK, so there are two situations that we need to deal with here:
995
   *
996
   * 1) We receive an instruction to ROLLBACK the current transaction
997
   *    and the currently-stored Transaction message is *self-contained*, 
998
   *    meaning that no Statement messages in the Transaction message
999
   *    contain a message having its segment_id member greater than 1.  If
1000
   *    no non-segment ID 1 members are found, we can simply clear the
1001
   *    current Transaction message and remove it from memory.
1002
   *
1003
   * 2) If the Transaction message does indeed have a non-end segment, that
1004
   *    means that a bulk update/delete/insert Transaction message segment
1005
   *    has previously been sent over the wire to replicators.  In this case, 
1006
   *    we need to package a Transaction with a Statement message of type
1007
   *    ROLLBACK to indicate to replicators that previously-transmitted
1008
   *    messages must be un-applied.
1009
   */
1010
  if (unlikely(message::transactionContainsBulkSegment(*transaction)))
1011
  {
1012
    /*
1013
     * Clear the transaction, create a Rollback statement message, 
1014
     * attach it to the transaction, and push it to replicators.
1015
     */
1016
    transaction->Clear();
1017
    initTransactionMessage(*transaction, in_session);
1018
1019
    message::Statement *statement= transaction->add_statement();
1020
1021
    initStatementMessage(*statement, message::Statement::ROLLBACK, in_session);
1022
    finalizeStatementMessage(*statement, in_session);
1023
1024
    finalizeTransactionMessage(*transaction, in_session);
1025
    
1405.3.3 by Jay Pipes
Adds Session reference to replication API
1026
    (void) replication_services.pushTransactionMessage(*in_session, *transaction);
1336.2.2 by Jay Pipes
NO CODE CHANGES - Simply moves pieces of ReplicationServices to TransactionServices. Preparation for making the ReplicationServices component only responsible for communication between replicators, appliers, publishers, and subscribers.
1027
  }
1028
  cleanupTransactionMessage(transaction, in_session);
1029
}
1030
1031
message::Statement &TransactionServices::getInsertStatement(Session *in_session,
1032
                                                                 Table *in_table)
1033
{
1034
  message::Statement *statement= in_session->getStatementMessage();
1662.3.1 by Joe Daly
fix a problem with multiple tables being updated in the same transaction these should go into seperate records
1035
1036
  /* 
1037
   * Check the type for the current Statement message, if it is anything
1038
   * other then INSERT we need to call finalize, this will ensure a 
1039
   * new InsertStatement is created. If it is of type INSERT check
1040
   * what table the INSERT belongs to, if it is a different table
1041
   * call finalize, so a new InsertStatement can be created. 
1336.2.2 by Jay Pipes
NO CODE CHANGES - Simply moves pieces of ReplicationServices to TransactionServices. Preparation for making the ReplicationServices component only responsible for communication between replicators, appliers, publishers, and subscribers.
1042
   */
1662.3.1 by Joe Daly
fix a problem with multiple tables being updated in the same transaction these should go into seperate records
1043
  if (statement != NULL && statement->type() != message::Statement::INSERT)
1336.2.2 by Jay Pipes
NO CODE CHANGES - Simply moves pieces of ReplicationServices to TransactionServices. Preparation for making the ReplicationServices component only responsible for communication between replicators, appliers, publishers, and subscribers.
1044
  {
1045
    finalizeStatementMessage(*statement, in_session);
1046
    statement= in_session->getStatementMessage();
1662.3.1 by Joe Daly
fix a problem with multiple tables being updated in the same transaction these should go into seperate records
1047
  } 
1048
  else if (statement != NULL)
1049
  {
1050
    const message::InsertHeader &insert_header= statement->insert_header();
1051
    string old_table_name= insert_header.table_metadata().table_name();
1052
     
1053
    string current_table_name;
1054
    (void) in_table->getShare()->getTableName(current_table_name);
1055
    if (current_table_name.compare(old_table_name))
1056
    {
1057
      finalizeStatementMessage(*statement, in_session);
1058
      statement= in_session->getStatementMessage();
1059
    }
1060
  } 
1336.2.2 by Jay Pipes
NO CODE CHANGES - Simply moves pieces of ReplicationServices to TransactionServices. Preparation for making the ReplicationServices component only responsible for communication between replicators, appliers, publishers, and subscribers.
1061
1062
  if (statement == NULL)
1063
  {
1064
    message::Transaction *transaction= getActiveTransactionMessage(in_session);
1065
    /* 
1066
     * Transaction message initialized and set, but no statement created
1067
     * yet.  We construct one and initialize it, here, then return the
1068
     * message after attaching the new Statement message pointer to the 
1069
     * Session for easy retrieval later...
1070
     */
1071
    statement= transaction->add_statement();
1072
    setInsertHeader(*statement, in_session, in_table);
1073
    in_session->setStatementMessage(statement);
1074
  }
1075
  return *statement;
1076
}
1077
1078
void TransactionServices::setInsertHeader(message::Statement &statement,
1079
                                          Session *in_session,
1080
                                          Table *in_table)
1081
{
1082
  initStatementMessage(statement, message::Statement::INSERT, in_session);
1083
1084
  /* 
1085
   * Now we construct the specialized InsertHeader message inside
1086
   * the generalized message::Statement container...
1087
   */
1088
  /* Set up the insert header */
1089
  message::InsertHeader *header= statement.mutable_insert_header();
1090
  message::TableMetadata *table_metadata= header->mutable_table_metadata();
1091
1336.2.3 by Jay Pipes
Merge trunk and resolve
1092
  string schema_name;
1093
  (void) in_table->getShare()->getSchemaName(schema_name);
1094
  string table_name;
1095
  (void) in_table->getShare()->getTableName(table_name);
1336.2.2 by Jay Pipes
NO CODE CHANGES - Simply moves pieces of ReplicationServices to TransactionServices. Preparation for making the ReplicationServices component only responsible for communication between replicators, appliers, publishers, and subscribers.
1096
1336.2.3 by Jay Pipes
Merge trunk and resolve
1097
  table_metadata->set_schema_name(schema_name.c_str(), schema_name.length());
1098
  table_metadata->set_table_name(table_name.c_str(), table_name.length());
1336.2.2 by Jay Pipes
NO CODE CHANGES - Simply moves pieces of ReplicationServices to TransactionServices. Preparation for making the ReplicationServices component only responsible for communication between replicators, appliers, publishers, and subscribers.
1099
1100
  Field *current_field;
1578.2.16 by Brian Aker
Merge in change to getTable() to private the field objects.
1101
  Field **table_fields= in_table->getFields();
1336.2.2 by Jay Pipes
NO CODE CHANGES - Simply moves pieces of ReplicationServices to TransactionServices. Preparation for making the ReplicationServices component only responsible for communication between replicators, appliers, publishers, and subscribers.
1102
1103
  message::FieldMetadata *field_metadata;
1104
1105
  /* We will read all the table's fields... */
1106
  in_table->setReadSet();
1107
1108
  while ((current_field= *table_fields++) != NULL) 
1109
  {
1110
    field_metadata= header->add_field_metadata();
1111
    field_metadata->set_name(current_field->field_name);
1112
    field_metadata->set_type(message::internalFieldTypeToFieldProtoType(current_field->type()));
1113
  }
1114
}
1115
1116
bool TransactionServices::insertRecord(Session *in_session, Table *in_table)
1117
{
1118
  ReplicationServices &replication_services= ReplicationServices::singleton();
1119
  if (! replication_services.isActive())
1120
    return false;
1121
  /**
1122
   * We do this check here because we don't want to even create a 
1123
   * statement if there isn't a primary key on the table...
1124
   *
1125
   * @todo
1126
   *
1127
   * Multi-column primary keys are handled how exactly?
1128
   */
1618 by Brian Aker
This is a rollup set of patches for modifications to TableIdentifier to have
1129
  if (not in_table->getShare()->hasPrimaryKey())
1336.2.2 by Jay Pipes
NO CODE CHANGES - Simply moves pieces of ReplicationServices to TransactionServices. Preparation for making the ReplicationServices component only responsible for communication between replicators, appliers, publishers, and subscribers.
1130
  {
1131
    my_error(ER_NO_PRIMARY_KEY_ON_REPLICATED_TABLE, MYF(0));
1132
    return true;
1133
  }
1134
1135
  message::Statement &statement= getInsertStatement(in_session, in_table);
1136
1137
  message::InsertData *data= statement.mutable_insert_data();
1138
  data->set_segment_id(1);
1139
  data->set_end_segment(true);
1140
  message::InsertRecord *record= data->add_record();
1141
1142
  Field *current_field;
1578.2.16 by Brian Aker
Merge in change to getTable() to private the field objects.
1143
  Field **table_fields= in_table->getFields();
1336.2.2 by Jay Pipes
NO CODE CHANGES - Simply moves pieces of ReplicationServices to TransactionServices. Preparation for making the ReplicationServices component only responsible for communication between replicators, appliers, publishers, and subscribers.
1144
1145
  String *string_value= new (in_session->mem_root) String(TransactionServices::DEFAULT_RECORD_SIZE);
1146
  string_value->set_charset(system_charset_info);
1147
1148
  /* We will read all the table's fields... */
1149
  in_table->setReadSet();
1150
1151
  while ((current_field= *table_fields++) != NULL) 
1152
  {
1667.3.6 by Joe Daly
bug 594873 fix null handling in enum in the transaction log
1153
    if (current_field->is_null())
1154
    {
1155
      record->add_is_null(true);
1667.3.7 by Joe Daly
merge trunk
1156
      record->add_insert_value("", 0);
1667.3.6 by Joe Daly
bug 594873 fix null handling in enum in the transaction log
1157
    } 
1158
    else 
1159
    {
1160
      string_value= current_field->val_str(string_value);
1161
      record->add_is_null(false);
1162
      record->add_insert_value(string_value->c_ptr(), string_value->length());
1163
      string_value->free();
1164
    }
1336.2.2 by Jay Pipes
NO CODE CHANGES - Simply moves pieces of ReplicationServices to TransactionServices. Preparation for making the ReplicationServices component only responsible for communication between replicators, appliers, publishers, and subscribers.
1165
  }
1166
  return false;
1167
}
1168
1169
message::Statement &TransactionServices::getUpdateStatement(Session *in_session,
1170
                                                            Table *in_table,
1171
                                                            const unsigned char *old_record, 
1172
                                                            const unsigned char *new_record)
1173
{
1174
  message::Statement *statement= in_session->getStatementMessage();
1662.3.1 by Joe Daly
fix a problem with multiple tables being updated in the same transaction these should go into seperate records
1175
1336.2.2 by Jay Pipes
NO CODE CHANGES - Simply moves pieces of ReplicationServices to TransactionServices. Preparation for making the ReplicationServices component only responsible for communication between replicators, appliers, publishers, and subscribers.
1176
  /*
1662.3.1 by Joe Daly
fix a problem with multiple tables being updated in the same transaction these should go into seperate records
1177
   * Check the type for the current Statement message, if it is anything
1178
   * other then UPDATE we need to call finalize, this will ensure a
1179
   * new UpdateStatement is created. If it is of type UPDATE check
1180
   * what table the UPDATE belongs to, if it is a different table
1181
   * call finalize, so a new UpdateStatement can be created.
1336.2.2 by Jay Pipes
NO CODE CHANGES - Simply moves pieces of ReplicationServices to TransactionServices. Preparation for making the ReplicationServices component only responsible for communication between replicators, appliers, publishers, and subscribers.
1182
   */
1662.3.1 by Joe Daly
fix a problem with multiple tables being updated in the same transaction these should go into seperate records
1183
  if (statement != NULL && statement->type() != message::Statement::UPDATE)
1336.2.2 by Jay Pipes
NO CODE CHANGES - Simply moves pieces of ReplicationServices to TransactionServices. Preparation for making the ReplicationServices component only responsible for communication between replicators, appliers, publishers, and subscribers.
1184
  {
1185
    finalizeStatementMessage(*statement, in_session);
1186
    statement= in_session->getStatementMessage();
1187
  }
1662.3.1 by Joe Daly
fix a problem with multiple tables being updated in the same transaction these should go into seperate records
1188
  else if (statement != NULL)
1189
  {
1190
    const message::UpdateHeader &update_header= statement->update_header();
1191
    string old_table_name= update_header.table_metadata().table_name();
1192
1193
    string current_table_name;
1194
    (void) in_table->getShare()->getTableName(current_table_name);
1195
    if (current_table_name.compare(old_table_name))
1196
    {
1197
      finalizeStatementMessage(*statement, in_session);
1198
      statement= in_session->getStatementMessage();
1199
    }
1200
  }
1336.2.2 by Jay Pipes
NO CODE CHANGES - Simply moves pieces of ReplicationServices to TransactionServices. Preparation for making the ReplicationServices component only responsible for communication between replicators, appliers, publishers, and subscribers.
1201
1202
  if (statement == NULL)
1203
  {
1204
    message::Transaction *transaction= getActiveTransactionMessage(in_session);
1205
    /* 
1206
     * Transaction message initialized and set, but no statement created
1207
     * yet.  We construct one and initialize it, here, then return the
1208
     * message after attaching the new Statement message pointer to the 
1209
     * Session for easy retrieval later...
1210
     */
1211
    statement= transaction->add_statement();
1212
    setUpdateHeader(*statement, in_session, in_table, old_record, new_record);
1213
    in_session->setStatementMessage(statement);
1214
  }
1215
  return *statement;
1216
}
1217
1218
void TransactionServices::setUpdateHeader(message::Statement &statement,
1219
                                          Session *in_session,
1220
                                          Table *in_table,
1221
                                          const unsigned char *old_record, 
1222
                                          const unsigned char *new_record)
1223
{
1224
  initStatementMessage(statement, message::Statement::UPDATE, in_session);
1225
1226
  /* 
1227
   * Now we construct the specialized UpdateHeader message inside
1228
   * the generalized message::Statement container...
1229
   */
1230
  /* Set up the update header */
1231
  message::UpdateHeader *header= statement.mutable_update_header();
1232
  message::TableMetadata *table_metadata= header->mutable_table_metadata();
1233
1336.2.3 by Jay Pipes
Merge trunk and resolve
1234
  string schema_name;
1235
  (void) in_table->getShare()->getSchemaName(schema_name);
1236
  string table_name;
1237
  (void) in_table->getShare()->getTableName(table_name);
1336.2.2 by Jay Pipes
NO CODE CHANGES - Simply moves pieces of ReplicationServices to TransactionServices. Preparation for making the ReplicationServices component only responsible for communication between replicators, appliers, publishers, and subscribers.
1238
1336.2.3 by Jay Pipes
Merge trunk and resolve
1239
  table_metadata->set_schema_name(schema_name.c_str(), schema_name.length());
1240
  table_metadata->set_table_name(table_name.c_str(), table_name.length());
1336.2.2 by Jay Pipes
NO CODE CHANGES - Simply moves pieces of ReplicationServices to TransactionServices. Preparation for making the ReplicationServices component only responsible for communication between replicators, appliers, publishers, and subscribers.
1241
1242
  Field *current_field;
1578.2.16 by Brian Aker
Merge in change to getTable() to private the field objects.
1243
  Field **table_fields= in_table->getFields();
1336.2.2 by Jay Pipes
NO CODE CHANGES - Simply moves pieces of ReplicationServices to TransactionServices. Preparation for making the ReplicationServices component only responsible for communication between replicators, appliers, publishers, and subscribers.
1244
1245
  message::FieldMetadata *field_metadata;
1246
1247
  /* We will read all the table's fields... */
1248
  in_table->setReadSet();
1249
1250
  while ((current_field= *table_fields++) != NULL) 
1251
  {
1252
    /*
1253
     * We add the "key field metadata" -- i.e. the fields which is
1254
     * the primary key for the table.
1255
     */
1574 by Brian Aker
Rollup patch for hiding tableshare.
1256
    if (in_table->getShare()->fieldInPrimaryKey(current_field))
1336.2.2 by Jay Pipes
NO CODE CHANGES - Simply moves pieces of ReplicationServices to TransactionServices. Preparation for making the ReplicationServices component only responsible for communication between replicators, appliers, publishers, and subscribers.
1257
    {
1258
      field_metadata= header->add_key_field_metadata();
1259
      field_metadata->set_name(current_field->field_name);
1260
      field_metadata->set_type(message::internalFieldTypeToFieldProtoType(current_field->type()));
1261
    }
1262
1263
    /*
1264
     * The below really should be moved into the Field API and Record API.  But for now
1265
     * we do this crazy pointer fiddling to figure out if the current field
1266
     * has been updated in the supplied record raw byte pointers.
1267
     */
1672.3.6 by Brian Aker
First pass in encapsulating row
1268
    const unsigned char *old_ptr= (const unsigned char *) old_record + (ptrdiff_t) (current_field->ptr - in_table->getInsertRecord()); 
1269
    const unsigned char *new_ptr= (const unsigned char *) new_record + (ptrdiff_t) (current_field->ptr - in_table->getInsertRecord()); 
1336.2.2 by Jay Pipes
NO CODE CHANGES - Simply moves pieces of ReplicationServices to TransactionServices. Preparation for making the ReplicationServices component only responsible for communication between replicators, appliers, publishers, and subscribers.
1270
1271
    uint32_t field_length= current_field->pack_length(); /** @TODO This isn't always correct...check varchar diffs. */
1272
1273
    if (memcmp(old_ptr, new_ptr, field_length) != 0)
1274
    {
1275
      /* Field is changed from old to new */
1276
      field_metadata= header->add_set_field_metadata();
1277
      field_metadata->set_name(current_field->field_name);
1278
      field_metadata->set_type(message::internalFieldTypeToFieldProtoType(current_field->type()));
1279
    }
1280
  }
1281
}
1282
void TransactionServices::updateRecord(Session *in_session,
1283
                                       Table *in_table, 
1284
                                       const unsigned char *old_record, 
1285
                                       const unsigned char *new_record)
1286
{
1287
  ReplicationServices &replication_services= ReplicationServices::singleton();
1288
  if (! replication_services.isActive())
1289
    return;
1290
1291
  message::Statement &statement= getUpdateStatement(in_session, in_table, old_record, new_record);
1292
1293
  message::UpdateData *data= statement.mutable_update_data();
1294
  data->set_segment_id(1);
1295
  data->set_end_segment(true);
1296
  message::UpdateRecord *record= data->add_record();
1297
1298
  Field *current_field;
1578.2.16 by Brian Aker
Merge in change to getTable() to private the field objects.
1299
  Field **table_fields= in_table->getFields();
1336.2.2 by Jay Pipes
NO CODE CHANGES - Simply moves pieces of ReplicationServices to TransactionServices. Preparation for making the ReplicationServices component only responsible for communication between replicators, appliers, publishers, and subscribers.
1300
  String *string_value= new (in_session->mem_root) String(TransactionServices::DEFAULT_RECORD_SIZE);
1301
  string_value->set_charset(system_charset_info);
1302
1303
  while ((current_field= *table_fields++) != NULL) 
1304
  {
1305
    /*
1306
     * Here, we add the SET field values.  We used to do this in the setUpdateHeader() method, 
1307
     * but then realized that an UPDATE statement could potentially have different values for
1308
     * the SET field.  For instance, imagine this SQL scenario:
1309
     *
1310
     * CREATE TABLE t1 (id INT NOT NULL PRIMARY KEY, count INT NOT NULL);
1311
     * INSERT INTO t1 (id, counter) VALUES (1,1),(2,2),(3,3);
1312
     * UPDATE t1 SET counter = counter + 1 WHERE id IN (1,2);
1313
     *
1314
     * We will generate two UpdateRecord messages with different set_value byte arrays.
1315
     *
1316
     * The below really should be moved into the Field API and Record API.  But for now
1317
     * we do this crazy pointer fiddling to figure out if the current field
1318
     * has been updated in the supplied record raw byte pointers.
1319
     */
1672.3.6 by Brian Aker
First pass in encapsulating row
1320
    const unsigned char *old_ptr= (const unsigned char *) old_record + (ptrdiff_t) (current_field->ptr - in_table->getInsertRecord()); 
1321
    const unsigned char *new_ptr= (const unsigned char *) new_record + (ptrdiff_t) (current_field->ptr - in_table->getInsertRecord()); 
1336.2.2 by Jay Pipes
NO CODE CHANGES - Simply moves pieces of ReplicationServices to TransactionServices. Preparation for making the ReplicationServices component only responsible for communication between replicators, appliers, publishers, and subscribers.
1322
1323
    uint32_t field_length= current_field->pack_length(); /** @TODO This isn't always correct...check varchar diffs. */
1324
1325
    if (memcmp(old_ptr, new_ptr, field_length) != 0)
1326
    {
1327
      /* Store the original "read bit" for this field */
1328
      bool is_read_set= current_field->isReadSet();
1329
1330
      /* We need to mark that we will "read" this field... */
1331
      in_table->setReadSet(current_field->field_index);
1332
1333
      /* Read the string value of this field's contents */
1334
      string_value= current_field->val_str(string_value);
1335
1336
      /* 
1337
       * Reset the read bit after reading field to its original state.  This 
1338
       * prevents the field from being included in the WHERE clause
1339
       */
1340
      current_field->setReadSet(is_read_set);
1341
1667.3.6 by Joe Daly
bug 594873 fix null handling in enum in the transaction log
1342
      if (current_field->is_null())
1343
      {
1344
        record->add_is_null(true);
1667.3.7 by Joe Daly
merge trunk
1345
        record->add_after_value("", 0);
1667.3.6 by Joe Daly
bug 594873 fix null handling in enum in the transaction log
1346
      }
1347
      else
1348
      {
1349
        record->add_is_null(false);
1350
        record->add_after_value(string_value->c_ptr(), string_value->length());
1351
      }
1336.2.2 by Jay Pipes
NO CODE CHANGES - Simply moves pieces of ReplicationServices to TransactionServices. Preparation for making the ReplicationServices component only responsible for communication between replicators, appliers, publishers, and subscribers.
1352
      string_value->free();
1353
    }
1354
1355
    /* 
1356
     * Add the WHERE clause values now...for now, this means the
1357
     * primary key field value.  Replication only supports tables
1358
     * with a primary key.
1359
     */
1574 by Brian Aker
Rollup patch for hiding tableshare.
1360
    if (in_table->getShare()->fieldInPrimaryKey(current_field))
1336.2.2 by Jay Pipes
NO CODE CHANGES - Simply moves pieces of ReplicationServices to TransactionServices. Preparation for making the ReplicationServices component only responsible for communication between replicators, appliers, publishers, and subscribers.
1361
    {
1362
      /**
1363
       * To say the below is ugly is an understatement. But it works.
1364
       * 
1365
       * @todo Move this crap into a real Record API.
1366
       */
1367
      string_value= current_field->val_str(string_value,
1368
                                           old_record + 
1369
                                           current_field->offset(const_cast<unsigned char *>(new_record)));
1370
      record->add_key_value(string_value->c_ptr(), string_value->length());
1371
      string_value->free();
1372
    }
1373
1374
  }
1375
}
1376
1377
message::Statement &TransactionServices::getDeleteStatement(Session *in_session,
1378
                                                            Table *in_table)
1379
{
1380
  message::Statement *statement= in_session->getStatementMessage();
1662.3.1 by Joe Daly
fix a problem with multiple tables being updated in the same transaction these should go into seperate records
1381
1336.2.2 by Jay Pipes
NO CODE CHANGES - Simply moves pieces of ReplicationServices to TransactionServices. Preparation for making the ReplicationServices component only responsible for communication between replicators, appliers, publishers, and subscribers.
1382
  /*
1662.3.1 by Joe Daly
fix a problem with multiple tables being updated in the same transaction these should go into seperate records
1383
   * Check the type for the current Statement message, if it is anything
1384
   * other then DELETE we need to call finalize, this will ensure a
1385
   * new DeleteStatement is created. If it is of type DELETE check
1386
   * what table the DELETE belongs to, if it is a different table
1387
   * call finalize, so a new DeleteStatement can be created.
1336.2.2 by Jay Pipes
NO CODE CHANGES - Simply moves pieces of ReplicationServices to TransactionServices. Preparation for making the ReplicationServices component only responsible for communication between replicators, appliers, publishers, and subscribers.
1388
   */
1662.3.1 by Joe Daly
fix a problem with multiple tables being updated in the same transaction these should go into seperate records
1389
  if (statement != NULL && statement->type() != message::Statement::DELETE)
1336.2.2 by Jay Pipes
NO CODE CHANGES - Simply moves pieces of ReplicationServices to TransactionServices. Preparation for making the ReplicationServices component only responsible for communication between replicators, appliers, publishers, and subscribers.
1390
  {
1391
    finalizeStatementMessage(*statement, in_session);
1392
    statement= in_session->getStatementMessage();
1393
  }
1662.3.1 by Joe Daly
fix a problem with multiple tables being updated in the same transaction these should go into seperate records
1394
  else if (statement != NULL)
1395
  {
1396
    const message::DeleteHeader &delete_header= statement->delete_header();
1397
    string old_table_name= delete_header.table_metadata().table_name();
1398
1399
    string current_table_name;
1400
    (void) in_table->getShare()->getTableName(current_table_name);
1401
    if (current_table_name.compare(old_table_name))
1402
    {
1403
      finalizeStatementMessage(*statement, in_session);
1404
      statement= in_session->getStatementMessage();
1405
    }
1406
  }
1336.2.2 by Jay Pipes
NO CODE CHANGES - Simply moves pieces of ReplicationServices to TransactionServices. Preparation for making the ReplicationServices component only responsible for communication between replicators, appliers, publishers, and subscribers.
1407
1408
  if (statement == NULL)
1409
  {
1410
    message::Transaction *transaction= getActiveTransactionMessage(in_session);
1411
    /* 
1412
     * Transaction message initialized and set, but no statement created
1413
     * yet.  We construct one and initialize it, here, then return the
1414
     * message after attaching the new Statement message pointer to the 
1415
     * Session for easy retrieval later...
1416
     */
1417
    statement= transaction->add_statement();
1418
    setDeleteHeader(*statement, in_session, in_table);
1419
    in_session->setStatementMessage(statement);
1420
  }
1421
  return *statement;
1422
}
1423
1424
void TransactionServices::setDeleteHeader(message::Statement &statement,
1425
                                          Session *in_session,
1426
                                          Table *in_table)
1427
{
1428
  initStatementMessage(statement, message::Statement::DELETE, in_session);
1429
1430
  /* 
1431
   * Now we construct the specialized DeleteHeader message inside
1432
   * the generalized message::Statement container...
1433
   */
1434
  message::DeleteHeader *header= statement.mutable_delete_header();
1435
  message::TableMetadata *table_metadata= header->mutable_table_metadata();
1436
1336.2.3 by Jay Pipes
Merge trunk and resolve
1437
  string schema_name;
1438
  (void) in_table->getShare()->getSchemaName(schema_name);
1439
  string table_name;
1440
  (void) in_table->getShare()->getTableName(table_name);
1336.2.2 by Jay Pipes
NO CODE CHANGES - Simply moves pieces of ReplicationServices to TransactionServices. Preparation for making the ReplicationServices component only responsible for communication between replicators, appliers, publishers, and subscribers.
1441
1336.2.3 by Jay Pipes
Merge trunk and resolve
1442
  table_metadata->set_schema_name(schema_name.c_str(), schema_name.length());
1443
  table_metadata->set_table_name(table_name.c_str(), table_name.length());
1336.2.2 by Jay Pipes
NO CODE CHANGES - Simply moves pieces of ReplicationServices to TransactionServices. Preparation for making the ReplicationServices component only responsible for communication between replicators, appliers, publishers, and subscribers.
1444
1445
  Field *current_field;
1578.2.16 by Brian Aker
Merge in change to getTable() to private the field objects.
1446
  Field **table_fields= in_table->getFields();
1336.2.2 by Jay Pipes
NO CODE CHANGES - Simply moves pieces of ReplicationServices to TransactionServices. Preparation for making the ReplicationServices component only responsible for communication between replicators, appliers, publishers, and subscribers.
1447
1448
  message::FieldMetadata *field_metadata;
1449
1450
  while ((current_field= *table_fields++) != NULL) 
1451
  {
1452
    /* 
1453
     * Add the WHERE clause values now...for now, this means the
1454
     * primary key field value.  Replication only supports tables
1455
     * with a primary key.
1456
     */
1574 by Brian Aker
Rollup patch for hiding tableshare.
1457
    if (in_table->getShare()->fieldInPrimaryKey(current_field))
1336.2.2 by Jay Pipes
NO CODE CHANGES - Simply moves pieces of ReplicationServices to TransactionServices. Preparation for making the ReplicationServices component only responsible for communication between replicators, appliers, publishers, and subscribers.
1458
    {
1459
      field_metadata= header->add_key_field_metadata();
1460
      field_metadata->set_name(current_field->field_name);
1461
      field_metadata->set_type(message::internalFieldTypeToFieldProtoType(current_field->type()));
1462
    }
1463
  }
1464
}
1465
1466
void TransactionServices::deleteRecord(Session *in_session, Table *in_table)
1467
{
1468
  ReplicationServices &replication_services= ReplicationServices::singleton();
1469
  if (! replication_services.isActive())
1470
    return;
1471
1472
  message::Statement &statement= getDeleteStatement(in_session, in_table);
1473
1474
  message::DeleteData *data= statement.mutable_delete_data();
1475
  data->set_segment_id(1);
1476
  data->set_end_segment(true);
1477
  message::DeleteRecord *record= data->add_record();
1478
1479
  Field *current_field;
1578.2.16 by Brian Aker
Merge in change to getTable() to private the field objects.
1480
  Field **table_fields= in_table->getFields();
1336.2.2 by Jay Pipes
NO CODE CHANGES - Simply moves pieces of ReplicationServices to TransactionServices. Preparation for making the ReplicationServices component only responsible for communication between replicators, appliers, publishers, and subscribers.
1481
  String *string_value= new (in_session->mem_root) String(TransactionServices::DEFAULT_RECORD_SIZE);
1482
  string_value->set_charset(system_charset_info);
1483
1484
  while ((current_field= *table_fields++) != NULL) 
1485
  {
1667.3.5 by Joe Daly
fix up spacing again
1486
    /* 
1336.2.2 by Jay Pipes
NO CODE CHANGES - Simply moves pieces of ReplicationServices to TransactionServices. Preparation for making the ReplicationServices component only responsible for communication between replicators, appliers, publishers, and subscribers.
1487
     * Add the WHERE clause values now...for now, this means the
1488
     * primary key field value.  Replication only supports tables
1489
     * with a primary key.
1490
     */
1574 by Brian Aker
Rollup patch for hiding tableshare.
1491
    if (in_table->getShare()->fieldInPrimaryKey(current_field))
1336.2.2 by Jay Pipes
NO CODE CHANGES - Simply moves pieces of ReplicationServices to TransactionServices. Preparation for making the ReplicationServices component only responsible for communication between replicators, appliers, publishers, and subscribers.
1492
    {
1667.3.5 by Joe Daly
fix up spacing again
1493
      string_value= current_field->val_str(string_value);
1667.3.2 by Joe Daly
add a working test case, and more fixes so the test case works
1494
      record->add_key_value(string_value->c_ptr(), string_value->length());
1495
      /**
1667.3.4 by Joe Daly
fix up comment spacing
1496
       * @TODO Store optional old record value in the before data member
1497
       */
1667.3.2 by Joe Daly
add a working test case, and more fixes so the test case works
1498
      string_value->free();
1336.2.2 by Jay Pipes
NO CODE CHANGES - Simply moves pieces of ReplicationServices to TransactionServices. Preparation for making the ReplicationServices component only responsible for communication between replicators, appliers, publishers, and subscribers.
1499
    }
1500
  }
1501
}
1502
1503
void TransactionServices::createTable(Session *in_session,
1504
                                      const message::Table &table)
1505
{
1506
  ReplicationServices &replication_services= ReplicationServices::singleton();
1507
  if (! replication_services.isActive())
1508
    return;
1509
  
1510
  message::Transaction *transaction= getActiveTransactionMessage(in_session);
1511
  message::Statement *statement= transaction->add_statement();
1512
1513
  initStatementMessage(*statement, message::Statement::CREATE_TABLE, in_session);
1514
1515
  /* 
1516
   * Construct the specialized CreateTableStatement message and attach
1517
   * it to the generic Statement message
1518
   */
1519
  message::CreateTableStatement *create_table_statement= statement->mutable_create_table_statement();
1520
  message::Table *new_table_message= create_table_statement->mutable_table();
1521
  *new_table_message= table;
1522
1523
  finalizeStatementMessage(*statement, in_session);
1524
1525
  finalizeTransactionMessage(*transaction, in_session);
1526
  
1405.3.3 by Jay Pipes
Adds Session reference to replication API
1527
  (void) replication_services.pushTransactionMessage(*in_session, *transaction);
1336.2.2 by Jay Pipes
NO CODE CHANGES - Simply moves pieces of ReplicationServices to TransactionServices. Preparation for making the ReplicationServices component only responsible for communication between replicators, appliers, publishers, and subscribers.
1528
1529
  cleanupTransactionMessage(transaction, in_session);
1530
1531
}
1532
1533
void TransactionServices::createSchema(Session *in_session,
1534
                                       const message::Schema &schema)
1535
{
1536
  ReplicationServices &replication_services= ReplicationServices::singleton();
1537
  if (! replication_services.isActive())
1538
    return;
1539
  
1540
  message::Transaction *transaction= getActiveTransactionMessage(in_session);
1541
  message::Statement *statement= transaction->add_statement();
1542
1543
  initStatementMessage(*statement, message::Statement::CREATE_SCHEMA, in_session);
1544
1545
  /* 
1546
   * Construct the specialized CreateSchemaStatement message and attach
1547
   * it to the generic Statement message
1548
   */
1549
  message::CreateSchemaStatement *create_schema_statement= statement->mutable_create_schema_statement();
1550
  message::Schema *new_schema_message= create_schema_statement->mutable_schema();
1551
  *new_schema_message= schema;
1552
1553
  finalizeStatementMessage(*statement, in_session);
1554
1555
  finalizeTransactionMessage(*transaction, in_session);
1556
  
1405.3.3 by Jay Pipes
Adds Session reference to replication API
1557
  (void) replication_services.pushTransactionMessage(*in_session, *transaction);
1336.2.2 by Jay Pipes
NO CODE CHANGES - Simply moves pieces of ReplicationServices to TransactionServices. Preparation for making the ReplicationServices component only responsible for communication between replicators, appliers, publishers, and subscribers.
1558
1559
  cleanupTransactionMessage(transaction, in_session);
1560
1561
}
1562
1563
void TransactionServices::dropSchema(Session *in_session, const string &schema_name)
1564
{
1565
  ReplicationServices &replication_services= ReplicationServices::singleton();
1566
  if (! replication_services.isActive())
1567
    return;
1568
  
1569
  message::Transaction *transaction= getActiveTransactionMessage(in_session);
1570
  message::Statement *statement= transaction->add_statement();
1571
1572
  initStatementMessage(*statement, message::Statement::DROP_SCHEMA, in_session);
1573
1574
  /* 
1575
   * Construct the specialized DropSchemaStatement message and attach
1576
   * it to the generic Statement message
1577
   */
1578
  message::DropSchemaStatement *drop_schema_statement= statement->mutable_drop_schema_statement();
1579
1580
  drop_schema_statement->set_schema_name(schema_name);
1581
1582
  finalizeStatementMessage(*statement, in_session);
1583
1584
  finalizeTransactionMessage(*transaction, in_session);
1585
  
1405.3.3 by Jay Pipes
Adds Session reference to replication API
1586
  (void) replication_services.pushTransactionMessage(*in_session, *transaction);
1336.2.2 by Jay Pipes
NO CODE CHANGES - Simply moves pieces of ReplicationServices to TransactionServices. Preparation for making the ReplicationServices component only responsible for communication between replicators, appliers, publishers, and subscribers.
1587
1588
  cleanupTransactionMessage(transaction, in_session);
1589
}
1590
1591
void TransactionServices::dropTable(Session *in_session,
1592
                                    const string &schema_name,
1593
                                    const string &table_name,
1594
                                    bool if_exists)
1595
{
1596
  ReplicationServices &replication_services= ReplicationServices::singleton();
1597
  if (! replication_services.isActive())
1598
    return;
1599
  
1600
  message::Transaction *transaction= getActiveTransactionMessage(in_session);
1601
  message::Statement *statement= transaction->add_statement();
1602
1603
  initStatementMessage(*statement, message::Statement::DROP_TABLE, in_session);
1604
1605
  /* 
1606
   * Construct the specialized DropTableStatement message and attach
1607
   * it to the generic Statement message
1608
   */
1609
  message::DropTableStatement *drop_table_statement= statement->mutable_drop_table_statement();
1610
1611
  drop_table_statement->set_if_exists_clause(if_exists);
1612
1613
  message::TableMetadata *table_metadata= drop_table_statement->mutable_table_metadata();
1614
1615
  table_metadata->set_schema_name(schema_name);
1616
  table_metadata->set_table_name(table_name);
1617
1618
  finalizeStatementMessage(*statement, in_session);
1619
1620
  finalizeTransactionMessage(*transaction, in_session);
1621
  
1405.3.3 by Jay Pipes
Adds Session reference to replication API
1622
  (void) replication_services.pushTransactionMessage(*in_session, *transaction);
1336.2.2 by Jay Pipes
NO CODE CHANGES - Simply moves pieces of ReplicationServices to TransactionServices. Preparation for making the ReplicationServices component only responsible for communication between replicators, appliers, publishers, and subscribers.
1623
1624
  cleanupTransactionMessage(transaction, in_session);
1625
}
1626
1627
void TransactionServices::truncateTable(Session *in_session, Table *in_table)
1628
{
1629
  ReplicationServices &replication_services= ReplicationServices::singleton();
1630
  if (! replication_services.isActive())
1631
    return;
1632
  
1633
  message::Transaction *transaction= getActiveTransactionMessage(in_session);
1634
  message::Statement *statement= transaction->add_statement();
1635
1636
  initStatementMessage(*statement, message::Statement::TRUNCATE_TABLE, in_session);
1637
1638
  /* 
1639
   * Construct the specialized TruncateTableStatement message and attach
1640
   * it to the generic Statement message
1641
   */
1642
  message::TruncateTableStatement *truncate_statement= statement->mutable_truncate_table_statement();
1643
  message::TableMetadata *table_metadata= truncate_statement->mutable_table_metadata();
1644
1336.2.3 by Jay Pipes
Merge trunk and resolve
1645
  string schema_name;
1646
  (void) in_table->getShare()->getSchemaName(schema_name);
1647
  string table_name;
1648
  (void) in_table->getShare()->getTableName(table_name);
1336.2.2 by Jay Pipes
NO CODE CHANGES - Simply moves pieces of ReplicationServices to TransactionServices. Preparation for making the ReplicationServices component only responsible for communication between replicators, appliers, publishers, and subscribers.
1649
1336.2.3 by Jay Pipes
Merge trunk and resolve
1650
  table_metadata->set_schema_name(schema_name.c_str(), schema_name.length());
1651
  table_metadata->set_table_name(table_name.c_str(), table_name.length());
1336.2.2 by Jay Pipes
NO CODE CHANGES - Simply moves pieces of ReplicationServices to TransactionServices. Preparation for making the ReplicationServices component only responsible for communication between replicators, appliers, publishers, and subscribers.
1652
1653
  finalizeStatementMessage(*statement, in_session);
1654
1655
  finalizeTransactionMessage(*transaction, in_session);
1656
  
1405.3.3 by Jay Pipes
Adds Session reference to replication API
1657
  (void) replication_services.pushTransactionMessage(*in_session, *transaction);
1336.2.2 by Jay Pipes
NO CODE CHANGES - Simply moves pieces of ReplicationServices to TransactionServices. Preparation for making the ReplicationServices component only responsible for communication between replicators, appliers, publishers, and subscribers.
1658
1659
  cleanupTransactionMessage(transaction, in_session);
1660
}
1661
1662
void TransactionServices::rawStatement(Session *in_session, const string &query)
1663
{
1664
  ReplicationServices &replication_services= ReplicationServices::singleton();
1665
  if (! replication_services.isActive())
1666
    return;
1667
  
1668
  message::Transaction *transaction= getActiveTransactionMessage(in_session);
1669
  message::Statement *statement= transaction->add_statement();
1670
1671
  initStatementMessage(*statement, message::Statement::RAW_SQL, in_session);
1672
  statement->set_sql(query);
1673
  finalizeStatementMessage(*statement, in_session);
1674
1675
  finalizeTransactionMessage(*transaction, in_session);
1676
  
1405.3.3 by Jay Pipes
Adds Session reference to replication API
1677
  (void) replication_services.pushTransactionMessage(*in_session, *transaction);
1336.2.2 by Jay Pipes
NO CODE CHANGES - Simply moves pieces of ReplicationServices to TransactionServices. Preparation for making the ReplicationServices component only responsible for communication between replicators, appliers, publishers, and subscribers.
1678
1679
  cleanupTransactionMessage(transaction, in_session);
1680
}
1681
1273.1.2 by Jay Pipes
This patch does not change any algorithms or code paths,
1682
} /* namespace drizzled */