CUBRID Engine  latest
transaction_cl.c
Go to the documentation of this file.
1 /*
2  * Copyright 2008 Search Solution Corporation
3  * Copyright 2016 CUBRID Corporation
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  */
18 
19 /*
20  * transaction_cl.c -
21  */
22 
23 #ident "$Id$"
24 
25 #include "config.h"
26 
27 #if !defined(WINDOWS)
28 #include <unistd.h>
29 #else /* !WINDOWS */
30 #include <process.h>
31 #endif /* !WINDOWS */
32 #include <stdio.h>
33 #if !defined(WINDOWS)
34 #include <sys/param.h>
35 #endif
36 #if defined(SOLARIS)
37 /* for MAXHOSTNAMELEN */
38 #include <netdb.h>
39 #endif
40 
41 #include "dbi.h"
42 #include "misc_string.h"
43 #include "transaction_cl.h"
44 #include "memory_alloc.h"
45 #include "locator_cl.h"
46 #include "work_space.h"
47 #include "server_interface.h"
48 #include "log_comm.h"
49 #include "log_lsa.hpp"
50 #include "db_query.h"
51 #include "boot_cl.h"
52 #include "virtual_object.h"
53 #include "schema_manager.h"
54 #include "trigger_manager.h"
55 #include "system_parameter.h"
56 #include "db.h" /* for db_Connect_status */
57 #include "porting.h"
58 #include "network_interface_cl.h"
59 
60 #if defined(WINDOWS)
61 #include "wintcp.h"
62 #else /* WINDOWS */
63 #include "tcp.h"
64 #endif /* WINDOWS */
65 
68 bool tm_Tran_async_ws = false;
71 int tm_Tran_ID = -1;
73 LOCK tm_Tran_rep_read_lock = NULL_LOCK; /* used in RR transaction locking to not lock twice. */
74 
75 /* read fetch version for current command of transaction
76  * must be set before each transaction command.
77  */
80 
81 /* Timeout(milli seconds) for queries.
82  *
83  * JDBC can send a bundle of queries to a CAS by setting CCI_EXEC_QUERY_ALL flag.
84  * In this case, we will apply this timeout to all of queries.
85  * So, every queries should be executed within this timeout.
86  *
87  * 0 means "unlimited", and negative value means "do not calculate timeout".
88  *
89  * tm_Is_libcas indicates fn_xxx functions called by libcas_main(i.e, JSP).
90  */
91 static UINT64 tm_Query_begin = 0;
92 static int tm_Query_timeout = 0;
93 static bool tm_Is_libcas = false;
94 
95 /* this is a local list of user-defined savepoints. It may be updated upon
96  * the following calls:
97  * tran_savepoint() -> tm_add_savepoint()
98  * tran_commit() -> tran_free_savepoint_list()
99  * tran_abort() -> tran_free_savepoint_list()
100  * tran_abort_upto_savepoint() -> tm_free_list_upto_savepoint()
101  */
103 
104 static int tran_add_savepoint (const char *savept_name);
105 static void tran_free_list_upto_savepoint (const char *savept_name);
106 
107 /*
108  * tran_cache_tran_settings - Cache transaction settings
109  *
110  * return:
111  *
112  * tran_index(in): Transaction index assigned to client
113  * lock_timeout(in): Transaction lock wait assigned to client transaction
114  * tran_isolation(in): Transaction isolation assigned to client transactions
115  *
116  * Note: Transaction settings are cached for future retieval.
117  * If tm_Tran_index is NULL then we can safely assume that the
118  * database connect flag can be turned off. i.e., db_Connect_status=0
119  */
120 void
121 tran_cache_tran_settings (int tran_index, int lock_timeout, TRAN_ISOLATION tran_isolation)
122 {
123  tm_Tran_index = tran_index;
124  tm_Tran_wait_msecs = lock_timeout;
125  tm_Tran_isolation = tran_isolation;
126 
127  /* This is a dirty, but quick, method by which we can flag that the database connection has been terminated. This
128  * flag is used by the C API calls to determine if a database connection exists. */
130  {
132  }
133 }
134 
135 /*
136  * tran_get_tran_settings - Get transaction settings
137  *
138  * return: nothing
139  *
140  * lock_wait(in/out): Transaction lock wait assigned to client transaction
141  * tran_isolation(in/out): Transaction isolation assigned to client
142  * transactions
143  * async_ws(in/out): async_workspace assigned to client transactions
144  *
145  * Note: Retrieve transaction settings.
146  */
147 void
148 tran_get_tran_settings (int *lock_wait_in_msecs, TRAN_ISOLATION * tran_isolation, bool * async_ws)
149 {
150  *lock_wait_in_msecs = TM_TRAN_WAIT_MSECS ();
151  /* lock timeout in milliseconds */ ;
152  *tran_isolation = TM_TRAN_ISOLATION ();
153  *async_ws = TM_TRAN_ASYNC_WS ();
154 }
155 
156 /*
157  * tran_reset_wait_times - Reset future waiting times for client transactions
158  *
159  * return: The old wait_msecs.
160  *
161  * wait_in_msecs(in): Wait for at least this number of milliseconds to acquire a lock
162  * before the transaction is timed out.
163  * A negative value (e.g., -1) means wait forever until a lock
164  * is granted or transaction is selected as a victim of a
165  * deadlock.
166  * A value of zero means do not wait at all, timeout immediately
167  *
168  * NOTE: Reset the default waiting time for the client transactions.
169  */
170 int
171 tran_reset_wait_times (int wait_in_msecs)
172 {
173  tm_Tran_wait_msecs = wait_in_msecs;
174 
176 }
177 
178 /*
179  * tran_reset_isolation - Reset isolation level of client session (transaction
180  * index)
181  *
182  * return: NO_ERROR if all OK, ER_ status otherwise
183  *
184  * isolation(in): New Isolation level. One of the following:
185  * TRAN_SERIALIZABLE
186  * TRAN_REPEATABLE_READ
187  * TRAN_READ_COMMITTED
188  * async_ws(in): New async_workspace
189  *
190  * NOTE: Reset the default isolation level for the current transaction
191  * index (client). It is recommended that the isolation level of
192  * a client session get reseted at the beginning of a transaction
193  * (i.e., just after client restart, abort or commit). If this is
194  * not done some of the current acquired locks of the transaction
195  * may be released according to the new isolation level.
196  */
197 int
198 tran_reset_isolation (TRAN_ISOLATION isolation, bool async_ws)
199 {
200  int error_code = NO_ERROR;
201 
202  if (!IS_VALID_ISOLATION_LEVEL (isolation))
203  {
206  }
207 
208  if (tm_Tran_isolation != isolation)
209  {
210  error_code = log_reset_isolation (isolation);
211  if (error_code == NO_ERROR)
212  {
213  tm_Tran_isolation = isolation;
214  }
215  }
216 
217  if (error_code == NO_ERROR)
218  {
219  tm_Tran_async_ws = async_ws;
220  }
221 
222  return error_code;
223 }
224 
225 /* only loaddb changes this setting */
227 
228 int
230 {
231  int err;
232 
233  if (!ws_need_flush ())
234  {
235  return NO_ERROR;
236  }
237 
239  {
241  }
242 
243  /* Flush all dirty objects */
244  /* Flush virtual objects first so that locator_all_flush doesn't see any */
245  err = locator_all_flush ();
246 
247  return err;
248 }
249 
250 /*
251  * tran_commit - COMMIT THE CURRENT TRANSACTION
252  *
253  * return:
254  *
255  * retain_lock(in): false = release locks (default)
256  * true = retain locks
257  *
258  * NOTE: commit the current transaction. All objects that have been
259  * updated by the transaction and are still dirty in the
260  * workspace are flushed to the page buffer pool (server). Then,
261  * the commit statement is forwarded to the transaction manager
262  * in the server. The transaction manager in the server will do a
263  * few things and the notify the recovery manager of the commit.
264  * The recovery manager commits the transaction and may notify of
265  * some loose end actions that need to be executed in the client
266  * as part of the commit (after commit actions). As a result of
267  * the commit all changes made by the transaction are made
268  * permanent and all acquired locks are released. Any locks
269  * cached in the workspace are cleared.
270  */
271 int
272 tran_commit (bool retain_lock)
273 {
274  TRAN_STATE state;
275  int error_code = NO_ERROR;
276  bool query_end_notify_server;
277 
278  /* check deferred trigger activities, these may prevent the transaction from being committed. */
280  if (error_code != NO_ERROR)
281  {
282  return error_code;
283  }
284 
285  /* tell the schema manager to flush any transaction caches */
287 
288  error_code = tran_flush_to_commit ();
289  if (error_code != NO_ERROR)
290  {
291  return error_code;
292  }
293 
296  {
297  /* Query ended with latest executed query. No need to notify server. */
298  query_end_notify_server = false;
299  }
300  else
301  {
302  query_end_notify_server = true;
304  }
305 
306  /* Clear all the queries */
307  db_clear_client_query_result (query_end_notify_server, false);
308 
309  /* if the commit fails or not, we should clear the clients savepoint list */
311 
313  {
314  /* Forward the commit the transaction manager in the server */
315  state = tran_server_commit (retain_lock);
316 
317  switch (state)
318  {
321  /* Successful commit */
322  error_code = NO_ERROR;
323  break;
324 
328  /* The commit failed */
329  ASSERT_ERROR_AND_SET (error_code);
330 #if defined(CUBRID_DEBUG)
331  er_log_debug (ARG_FILE_LINE, "tran_commit: Unable to commit. Transaction was aborted\n");
332 #endif /* CUBRID_DEBUG */
333  break;
334 
336  if (!BOOT_IS_CLIENT_RESTARTED ())
337  {
338  ASSERT_ERROR_AND_SET (error_code);
339  break;
340  }
341  /* Fall Thru */
342  case TRAN_RECOVERY:
343  case TRAN_ACTIVE:
350  default:
351  ASSERT_ERROR_AND_SET (error_code);
352 #if defined(CUBRID_DEBUG)
353  er_log_debug (ARG_FILE_LINE, "tran_commit: Unknown commit state = %s at client\n", log_state_string (state));
354 #endif /* CUBRID_DEBUG */
355  break;
356  }
357  }
359  {
360  /*
361  * fail-back action
362  * make the client to reconnect to the active server
363  */
365  er_log_debug (ARG_FILE_LINE, "tran_commit: DB_CONNECTION_STATUS_RESET\n");
366  }
367 
368  /* Increment snapshot version in work space */
370 
371  /* clear workspace information and any open query cursors */
372  if (error_code == NO_ERROR || BOOT_IS_CLIENT_RESTARTED ())
373  {
374  ws_clear_all_hints (retain_lock);
376  }
377 
378  /* allow triggers AFTER the commit */
379  if (error_code == NO_ERROR)
380  {
382  }
383 
385 
387 
388  return error_code;
389 }
390 
391 /*
392  * tran_abort - ABORT THE CURRENT TRANSACTION
393  *
394  * return: NO_ERROR if all OK, ER status otherwise
395  *
396  * NOTE:Abort the current transaction. All objects updated by the
397  * current transaction that are still dirty are removed from the
398  * workspace and then the abort is forwarded to the transaction
399  * manager in the server. In the server all updates made to the
400  * database and the page buffer pool are rolled back, and the
401  * transaction is declared as aborted. The server may notify the
402  * the transaction manager in the client of any loose end undoes
403  * that need to be executed in the client as part of the abort.
404  * As a result of the abort all changes made by the transaction
405  * are rolled back and acquired locks are released. Any locks
406  * cached in the workspace are cleared.
407  */
408 int
410 {
411  TRAN_STATE state;
412  int error_code = NO_ERROR;
413  bool query_end_notify_server;
414 
415  /*
416  * inform the trigger manager of the event, triggers can't prevent a
417  * rollback, might not want to do this if we're being unilaterally
418  * aborted ?
419  */
421 
422  /* tell the schema manager to flush any transaction caches */
424 
425 #if defined(SA_MODE)
426  ws_clear ();
427 #else /* SA_MODE */
428  /* Remove any dirty objects and remove any hints */
429  ws_abort_mops (false);
430  ws_filter_dirty ();
431 #endif /* SA_MODE */
432 
433  /* free the local list of savepoint names */
435 
436  /* Clear any query cursor */
438 
440  {
441  /* Query ended with latest executed query. No need to notify server. */
442  query_end_notify_server = false;
443  }
444  else
445  {
446  query_end_notify_server = true;
448  }
449  db_clear_client_query_result (query_end_notify_server, true);
450 
452  {
453  /* Forward the abort the transaction manager in the server */
454  state = tran_server_abort ();
455 
456  switch (state)
457  {
460  /* Successful abort */
461  break;
462 
464  if (!BOOT_IS_CLIENT_RESTARTED ())
465  {
466  ASSERT_ERROR_AND_SET (error_code);
467  break;
468  }
469  /* Fall Thru */
470  case TRAN_RECOVERY:
471  case TRAN_ACTIVE:
481  default:
482  ASSERT_ERROR_AND_SET (error_code);
483 #if defined(CUBRID_DEBUG)
484  er_log_debug (ARG_FILE_LINE, "tran_abort: Unknown abort state = %s\n", log_state_string (state));
485 #endif /* CUBRID_DEBUG */
486  break;
487  }
488  }
489 
490  /* Increment snapshot version in work space */
492 
494 
495  /* can these do anything useful ? */
497 
499 
501 
502  return error_code;
503 }
504 
505 /*
506  * tran_unilaterally_abort - Unilaterally abort the current transaction
507  *
508  * return: NO_ERROR if all OK, ER status otherwise
509  *
510  * NOTE:The current transaction is unilaterally aborted by a client
511  * module of the system.
512  * Execute tran_abort & set an error message
513  */
514 int
516 {
517  int error_code = NO_ERROR;
518  char user_name[L_cuserid + 1];
519  char host[CUB_MAXHOSTNAMELEN];
520  int pid;
521 
522  /* Get the user name, host, and process identifier */
523  if (getuserid (user_name, L_cuserid) == NULL)
524  {
525  strcpy (user_name, "(unknown)");
526  }
527  if (GETHOSTNAME (host, CUB_MAXHOSTNAMELEN) != 0)
528  {
529  /* unknown error */
530  strcpy (host, "(unknown)");
531  }
532  pid = getpid ();
533 
535 
536  error_code = tran_abort ();
537 
538  /* does it make sense to have these ? */
540 
541  return error_code;
542 }
543 
544 /*
545  * tran_abort_only_client - Abort the current transaction only at the client
546  * level. (the server aborted the transaction)
547  *
548  * return: NO_ERROR if all OK, ER status otherwise
549  *
550  * is_server_down(in):Was the transaction aborted because of the server crash?
551  *
552  * NOTE: The current transaction is aborted only at the client level,
553  * since the transaction has already been aborted at the server.
554  * All dirty objects in the workspace are removed and cached
555  * locks are cleared.
556  * This function is called when the transaction component (e.g.,
557  * transaction object locator) finds that the transaction was
558  * unilaterally aborted.
559  */
560 int
561 tran_abort_only_client (bool is_server_down)
562 {
563  if (!BOOT_IS_CLIENT_RESTARTED ())
564  {
565  if (is_server_down)
566  {
569  }
570 
571  return NO_ERROR;
572  }
573 
574  /* Remove any dirty objects and close all open query cursors */
575  ws_abort_mops (true);
576  ws_filter_dirty ();
577  db_clear_client_query_result (false, true);
578 
580 
581  if (is_server_down == false)
582  {
584  return NO_ERROR;
585  }
586  else
587  {
590  }
591 
592  return NO_ERROR;
593 }
594 
595 /*
596  * tran_has_updated - HAS TRANSACTION UPDATED THE DATABASE ?
597  *
598  * return:
599  *
600  * NOTE:Find if the transaction has dirtied the database.
601  */
602 bool
604 {
605  return (ws_has_updated () || tran_server_has_updated ());
606 }
607 
608 /*
609  * tran_is_active_and_has_updated - Find if transaction is active and
610  * has updated the database ?
611  *
612  * return:
613  *
614  * NOTE:Find if the transaction is active and has updated/dirtied the
615  * database.
616  */
617 bool
619 {
621 }
622 
623 /*
624  * tran_set_global_tran_info - Set global transaction information
625  *
626  * return: NO_ERROR if all OK, ER status otherwise
627  *
628  * gtrid(in): global transaction identifier
629  * info(in): pointer to the user information to be set
630  * size(in): size of the user information to be set
631  *
632  * Note:Set the user information related with the global transaction.
633  * The global transaction identified by the 'gtrid' should exist
634  * and should be the value returned by 'db_2pc_start_transaction'
635  * You can use this function to set the longer format of global
636  * transaction identifier such as XID of XA interface.
637  */
638 int
639 tran_set_global_tran_info (int gtrid, void *info, int size)
640 {
641  if (tran_server_set_global_tran_info (gtrid, info, size) == NO_ERROR)
642  {
643  return NO_ERROR;
644  }
645  else
646  {
647  assert (er_errid () != NO_ERROR);
648  return er_errid ();
649  }
650 }
651 
652 /*
653  * tran_get_global_tran_info - Get global transaction information
654  *
655  * return: NO_ERROR if all OK, ER status otherwise
656  *
657  * gtrid(in): global transaction identifier
658  * buffer(in):pointer to the buffer into which the user information is stored
659  * size(in): size of the buffer
660  *
661  * NOTE: Get the user information of the global transaction identified
662  * by the 'gtrid'.
663  * You can use this function to get the longer format of global
664  * transaction identifier such as XID of XA interface. This
665  * function is designed to use if you want to get XID after
666  * calling 'db_2pc_prepared_transactions' to support xa_recover()
667  */
668 int
669 tran_get_global_tran_info (int gtrid, void *buffer, int size)
670 {
671  int error_code = NO_ERROR;
672 
673  if (tran_server_get_global_tran_info (gtrid, buffer, size) != NO_ERROR)
674  {
675  assert (er_errid () != NO_ERROR);
676  error_code = er_errid ();
677  }
678 
679  return error_code;
680 }
681 
682 /*
683  * tran_2pc_start - Start transaction as a part of global transaction
684  *
685  * return: return global transaction identifier
686  *
687  * NOTE: Make current transaction as a part of a global transaction by
688  * assigning a global transaction identifier(gtrid).
689  * It is recommended to call this function just after the end of
690  * a transaction(commit or abort) before executing other works.
691  * This function is one way of getting gtrid of the transaction.
692  * The other way is to use 'db_2pc_prepare_to_commit_transaction'
693  * The function 'db_2pc_prepare_transaction' should be used if
694  * this function is called.
695  */
696 int
698 {
699  return tran_server_2pc_start ();
700 }
701 
702 /*
703  * tran_2pc_prepare - Prepare transaction to commit
704  *
705  * return: NO_ERROR if all OK, ER_ status otherwise
706  *
707  * NOTE: Prepare the current transaction for commitment in 2PC. The
708  * transaction should be made as a part of a global transaction
709  * before by 'db_2pc_start_transaction', a pair one of this
710  * function.
711  * The system promises not to unilaterally abort the transaction.
712  * After this function call, the only API functions that should
713  * be executed are 'db_commit_transaction' &
714  * 'db_abort_transaction'.
715  */
716 int
718 {
719  TRAN_STATE state;
720  int error_code = NO_ERROR;
721 
722  /* flush all dirty objects */
723  error_code = locator_all_flush ();
724  if (error_code != NO_ERROR)
725  {
726 #if defined(CUBRID_DEBUG)
727  er_log_debug (ARG_FILE_LINE, "tm_2pc_prepare: Unable to prepare. Flush failed\n");
728 #endif /* CUBRID_DEBUG */
729  goto end;
730  }
731 
732  /* forward the prepare to the transaction manager in the server */
733  state = tran_server_2pc_prepare ();
734  switch (state)
735  {
736  case TRAN_ACTIVE:
737  /* The preparation to commit failed probably due to inproper state; Transaction is still active */
738  assert (er_errid () != NO_ERROR);
739  error_code = er_errid ();
740 #if defined(CUBRID_DEBUG)
741  er_log_debug (ARG_FILE_LINE, "tm_2pc_prepare: Unable to prepare. Transaction is still active\n");
742 #endif /* CUBRID_DEBUG */
743  break;
744 
746  /* Successful to prepare (or repeated preparation). */
747  error_code = NO_ERROR;
748  break;
749 
752  /* The preparation to commit failed; Transaction has been aborted */
753  assert (er_errid () != NO_ERROR);
754  error_code = er_errid ();
755 #if defined(CUBRID_DEBUG)
756  er_log_debug (ARG_FILE_LINE, "tm_2pc_prepare: Unable to prepare. Transaction was aborted\n");
757 #endif /* CUBRID_DEBUG */
758  break;
759 
761  /* The transaction was committed. There is not a need for 2PC prepare. This could happend for read only
762  * transactions. */
763  error_code = NO_ERROR;
764  break;
765 
767  if (!BOOT_IS_CLIENT_RESTARTED ())
768  {
769  assert (er_errid () != NO_ERROR);
770  error_code = er_errid ();
771  break;
772  }
773  /* fall thru */
774 
775  case TRAN_RECOVERY:
783  default:
784  assert (er_errid () != NO_ERROR);
785  error_code = er_errid ();
786 #if defined(CUBRID_DEBUG)
787  er_log_debug (ARG_FILE_LINE, "tm_2pc_prepare: Unexpected prepare. state = %s\n", log_state_string (state));
788 #endif /* CUBRID_DEBUG */
789  break;
790  } /* switch (state) */
791 
792  /* clear workspace information and any open query cursors */
793  if (error_code == NO_ERROR || BOOT_IS_CLIENT_RESTARTED ())
794  {
795  db_clear_client_query_result (true, true);
796  ws_clear_all_hints (false);
798  }
799 
800 end:
801  return error_code;
802 }
803 
804 /*
805  * tran_2pc_recovery_prepared - Obtain list of prepared transactions
806  *
807  * return: the number of ids copied into 'gtrids[]'
808  *
809  * gtrids(in): array into which global transaction identifiers are copied
810  * size(in): size of 'gtrids[]' array
811  *
812  * NOTE: For restart recovery of global transactions, this function
813  * returns gtrids of transactions in prepared state, which was
814  * a part of a global transaction.
815  * If the return value is less than the 'size', there's no more
816  * transactions to recover.
817  */
818 int
819 tran_2pc_recovery_prepared (int gtrids[], int size)
820 {
821  return tran_server_2pc_recovery_prepared (gtrids, size);
822 }
823 
824 /*
825  * tran_2pc_attach_global_tran - Attach to a loose end 2pc transaction
826  *
827  * return: NO_ERROR if all OK, ER_ status otherwise
828  *
829  * gtrid(in): Global transaction identifier
830  *
831  * NOTE: Attaches the user to the transaction that was the local
832  * part of the specified global transaction. The current
833  * transaction is aborted before the attachement takes place. The
834  * current transaction must not be in the middle of a 2PC.
835  * It is recommended to attach a client to a 2PC loose end
836  * transaction just after the client restart or after a commit
837  * or abort.
838  */
839 int
841 {
842  int new_tran_index;
843 
844  new_tran_index = tran_server_2pc_attach_global_tran (gtrid);
845  if (new_tran_index == NULL_TRAN_INDEX)
846  {
847  assert (er_errid () != NO_ERROR);
848  return er_errid ();
849  }
850 
851  tm_Tran_index = new_tran_index;
852 
853  return NO_ERROR;
854 }
855 
856 /*
857  * tran_2pc_prepare_global_tran - Prepare to commit the current transaction
858  *
859  * return: NO_ERROR if all OK, ER_ status otherwise
860  *
861  * gtrid(in): Identifier of the global transaction
862  *
863  * NOTE: This function prepares the transaction identified by "gtrid"
864  * for commitment. All objects that have been updated by the
865  * transaction and are still dirty in the workspace are flushed
866  * to the page buffer pool (server). Then, prepare statement is
867  * forwarded to the transaction manager to guarantee the
868  * the commitment.
869  */
870 int
872 {
873  TRAN_STATE state;
874  int error_code = NO_ERROR;
875 
876  /* Flush all dirty objects */
877  error_code = locator_all_flush ();
878  if (error_code != NO_ERROR)
879  {
880 #if defined(CUBRID_DEBUG)
881  er_log_debug (ARG_FILE_LINE, "tm_2pc_prepare: Unable to prepare to commit. \nFlush failed\n");
882 #endif /* CUBRID_DEBUG */
883  return error_code;
884  }
885 
886  /* Forward the prepare to commit to the transaction manager in the server */
887  state = tran_server_2pc_prepare_global_tran (gtrid);
888  switch (state)
889  {
890  case TRAN_ACTIVE:
891  /* The preperation to commit failed probabely due to the given global transaction identifier; Transaction is
892  * still active */
893  assert (er_errid () != NO_ERROR);
894  error_code = er_errid ();
895 #if defined(CUBRID_DEBUG)
896  er_log_debug (ARG_FILE_LINE, "tm_2pc_prepare: Unable to prepare to commit.\n %s",
897  "Transaction is still active\n");
898 #endif /* CUBRID_DEBUG */
899  break;
900 
902  /* Successful preperation to commit */
903  error_code = NO_ERROR;
904  break;
905 
908  /* The preperation to commit failed; Transaction has been aborted */
909  assert (er_errid () != NO_ERROR);
910  error_code = er_errid ();
911 #if defined(CUBRID_DEBUG)
912  er_log_debug (ARG_FILE_LINE, "tm_2pc_prepare: Unable to prepare to commit.\n %s", "Transaction was aborted\n");
913 #endif /* CUBRID_DEBUG */
914  break;
915 
917  /*
918  * The transaction was committed. There is not a need for 2PC prepare.
919  * This could happen for read only transactions
920  */
921  error_code = NO_ERROR;
922  break;
923 
925  if (!BOOT_IS_CLIENT_RESTARTED ())
926  {
927  assert (er_errid () != NO_ERROR);
928  error_code = er_errid ();
929  break;
930  }
931  /* Fall Thru */
932 
933  case TRAN_RECOVERY:
941  default:
942  assert (er_errid () != NO_ERROR);
943  error_code = er_errid ();
944 #if defined(CUBRID_DEBUG)
945  er_log_debug (ARG_FILE_LINE, "tm_2pc_prepare: Unexpected prepare to commit state = %s\n",
946  log_state_string (state));
947 #endif /* CUBRID_DEBUG */
948  break;
949  }
950 
951  /* clear workspace information and any open query cursors */
952  if (error_code == NO_ERROR || BOOT_IS_CLIENT_RESTARTED ())
953  {
954  db_clear_client_query_result (true, true);
955  ws_clear_all_hints (false);
957  }
958 
959  return error_code;
960 }
961 
962 /*
963  * tran_add_savepoint -
964  *
965  * return: NO_ERROR if all OK, ER_ status otherwise
966  *
967  * savept_name(in):
968  *
969  * NOTE:insert a savepoint name into the front of the list. This way, the list
970  * is sorted in reverse chronological order
971  */
972 static int
973 tran_add_savepoint (const char *savept_name)
974 {
975  DB_NAMELIST *sp;
976 
977  sp = (DB_NAMELIST *) db_ws_alloc (sizeof (DB_NAMELIST));
978  if (sp == NULL)
979  {
980  assert (er_errid () != NO_ERROR);
981  return er_errid ();
982  }
983 
984  sp->name = ws_copy_string (savept_name);
985  if (sp->name == NULL)
986  {
987  db_ws_free (sp);
988 
989  assert (er_errid () != NO_ERROR);
990  return er_errid ();
991  }
993  user_savepoint_list = sp;
994 
995  return NO_ERROR;
996 }
997 
998 /*
999  * tran_free_savepoint_list -
1000  *
1001  * return:
1002  *
1003  * NOTE:free the entire user savepoint list. Called during abort, commit, or
1004  * restart
1005  */
1006 void
1008 {
1009  nlist_free (user_savepoint_list);
1010  user_savepoint_list = NULL;
1011 }
1012 
1013 /*
1014  * tran_free_list_upto_savepoint -
1015  *
1016  * return:
1017  *
1018  * savept_name(in):
1019  *
1020  * NOTE:frees the latest savepoints from the list up to, but not including, the
1021  * given savepoint. Called during rollback to savepoint command.
1022  */
1023 static void
1024 tran_free_list_upto_savepoint (const char *savept_name)
1025 {
1026  DB_NAMELIST *sp, *temp;
1027  bool found = false;
1028 
1029  /* first, check to see if it's in the list */
1030  for (sp = user_savepoint_list; sp && !found; sp = sp->next)
1031  {
1032  if (intl_mbs_casecmp (sp->name, savept_name) == 0)
1033  {
1034  found = true;
1035  }
1036  }
1037 
1038  /* not 'found' is not necessarily an error. We may be rolling back to a system-defined savepoint rather than a
1039  * user-defined savepoint. In that case, the name would not appear on the user savepoint list and the list should be
1040  * preserved. We should be able to guarantee that any rollback to a system-defined savepoint will affect only the
1041  * latest atomic command and not overlap any user-defined savepoint. That is, system invoked partial rollbacks
1042  * should never rollback farther than the last user-defined savepoint. */
1043  if (found == true)
1044  {
1045  for (sp = user_savepoint_list; sp;)
1046  {
1047  if (intl_mbs_casecmp (sp->name, savept_name) == 0)
1048  {
1049  break;
1050  }
1051 
1052  temp = sp;
1053  sp = sp->next;
1054  db_ws_free ((char *) temp->name);
1055  db_ws_free (temp);
1056  }
1057  user_savepoint_list = sp;
1058  }
1059 }
1060 
1061 /*
1062  * tran_system_savepoint -
1063  *
1064  * return: NO_ERROR if all OK, ER_ status otherwise
1065  *
1066  * savept_name(in): Name of the savepoint
1067  *
1068  */
1069 int
1070 tran_system_savepoint (const char *savept_name)
1071 {
1072  return tran_savepoint_internal (savept_name, SYSTEM_SAVEPOINT);
1073 }
1074 
1075 /*
1076  * tran_savepoint_internal - Declare a user savepoint
1077  *
1078  * return: NO_ERROR if all OK, ER_ status otherwise
1079  *
1080  * savept_name(in): Name of the savepoint
1081  * savepoint_type(in):
1082  *
1083  * NOTE: A savepoint is established for the current transaction, so
1084  * that future transaction actions can be rolled back to this
1085  * established savepoint. We call this operation a partial abort
1086  * (rollback). That is, all database actions affected by the
1087  * transaction after the savepoint are "undone", and all effects
1088  * of the transaction preceding the savepoint remain. The
1089  * transaction can then continue executing other database
1090  * statement. It is permissible to abort to the same savepoint
1091  * repeatedly within the same transaction.
1092  * If the same savepoint name is used in multiple savepoint
1093  * declarations within the same transaction, then only the latest
1094  * savepoint with that name is available for aborts and the
1095  * others are forgotten.
1096  * There are no limits on the number of savepoints that a
1097  * transaction can have.
1098  */
1099 int
1101 {
1102  LOG_LSA savept_lsa;
1103  int error_code = NO_ERROR;
1104 
1105  /* Flush all dirty objects */
1106  if (ws_need_flush ())
1107  {
1108  error_code = locator_all_flush ();
1109  if (error_code != NO_ERROR)
1110  {
1111 #if defined(CUBRID_DEBUG)
1112  er_log_debug (ARG_FILE_LINE, "tran_savepoint: Unable to start a top operation\n Flush failed.\nerrmsg = %s",
1113  er_msg ());
1114 #endif /* CUBRID_DEBUG */
1115  return error_code;
1116  }
1117  }
1118 
1119  if (tran_server_savepoint (savept_name, &savept_lsa) != NO_ERROR)
1120  {
1121  assert (er_errid () != NO_ERROR);
1122  error_code = er_errid ();
1123  return error_code;
1124  }
1125 
1126  /* add savepoint to local list */
1127  if (savepoint_type == USER_SAVEPOINT)
1128  {
1129  error_code = tran_add_savepoint (savept_name);
1130  if (error_code != NO_ERROR)
1131  {
1132  return error_code;
1133  }
1134  }
1135 
1136  return error_code;
1137 }
1138 
1139 /*
1140  * tran_abort_upto_system_savepoint -
1141  *
1142  * return: state of partial aborted operation (i.e., notify if
1143  * there are client actions that need to be undone).
1144  *
1145  * savepoint_name(in): Name of the savepoint
1146  */
1147 int
1148 tran_abort_upto_system_savepoint (const char *savepoint_name)
1149 {
1150  return tran_internal_abort_upto_savepoint (savepoint_name, SYSTEM_SAVEPOINT, false);
1151 }
1152 
1153 /*
1154  * tran_abort_upto_user_savepoint -
1155  *
1156  * return: state of partial aborted operation (i.e., notify if
1157  * there are client actions that need to be undone).
1158  * savepoint_name(in): Name of the savepoint
1159  */
1160 int
1161 tran_abort_upto_user_savepoint (const char *savepoint_name)
1162 {
1163  /* delete client's local copy of savepoint names back to here */
1164  tran_free_list_upto_savepoint (savepoint_name);
1165 
1166  return tran_internal_abort_upto_savepoint (savepoint_name, USER_SAVEPOINT, false);
1167 }
1168 
1169 /*
1170  * tran_internal_abort_upto_savepoint - Abort operations of a transaction
1171  * upto a savepoint
1172  *
1173  * return: state of partial aborted operation (i.e., notify if
1174  * there are client actions that need to be undone).
1175  *
1176  * savepoint_name(in): Name of the savepoint
1177  * savepoint_type(in):
1178  * client_decache_all_but_norealclasses(in):
1179  *
1180  * NOTE: All the effects of the current transaction after the
1181  * given savepoint are undone, and all effects of the transaction
1182  * preceding the given savepoint remain. After the partial abort
1183  * the transaction can continue its normal execution as if
1184  * the statements that were undone were never executed.
1185  * All objects updated by the current transaction that are still
1186  * dirty are removed from the workspace and then the partial
1187  * abort is forwarded to the transaction manager in the server.
1188  * In the server all updates made to the database and the page
1189  * buffer pool after the given savepoint are rolled back. The
1190  * server may notify the transaction manager in the client of
1191  * any client loose_end undoes that need to be executed at the
1192  * client as part of the partial abort.
1193  * The locks in the workspace will need to be cleared since we do
1194  * not know in the client what objects were rolled back. This is
1195  * needed since the client does not request the objects from the
1196  * server if the desired lock has been already acquired (cached
1197  * in the workspace). Therefore, from the point of view of the
1198  * workspace, the transaction will need to validate the objects
1199  * that need to be accessed in the future.
1200  */
1201 int
1203  bool client_decache_all_but_norealclasses)
1204 {
1205  int error_code = NO_ERROR;
1206  LOG_LSA savept_lsa;
1207  TRAN_STATE state;
1208 
1209  /* tell the schema manager to flush any transaction caches */
1211 
1212  /*
1213  * We need to start all over since we do not know what set of objects are
1214  * going to be rolled back.. Thuis, we need to remove any kind of hints
1215  * cached in the workspace.
1216  */
1217 
1218  if (client_decache_all_but_norealclasses == true)
1219  {
1221  ws_filter_dirty ();
1222  }
1223  else
1224  {
1225 #if defined(SA_MODE)
1226  ws_clear ();
1227 #else /* SA_MODE */
1228  /* Remove any dirty objects and remove any hints */
1229  ws_abort_mops (false);
1230  ws_filter_dirty ();
1231 #endif /* SA_MODE */
1232  }
1233 
1234  state = tran_server_partial_abort (savepoint_name, &savept_lsa);
1235  if (state != TRAN_UNACTIVE_ABORTED)
1236  {
1237  assert (er_errid () != NO_ERROR);
1238  error_code = er_errid ();
1239  if (savepoint_type == SYSTEM_SAVEPOINT && state == TRAN_UNACTIVE_UNKNOWN && error_code != NO_ERROR
1240  && !tran_has_updated ())
1241  {
1242  /*
1243  * maybe transaction has been unilaterally aborted by the system
1244  * and ER_LK_UNILATERALLY_ABORTED was overwritten by a consecutive error.
1245  */
1246  (void) tran_unilaterally_abort ();
1247  }
1248 #if defined(CUBRID_DEBUG)
1249  if (error_code != ER_TM_SERVER_DOWN_UNILATERALLY_ABORTED && error_code != ER_NET_SERVER_CRASHED)
1250  {
1251  er_log_debug (ARG_FILE_LINE, "tm_abort_upto_savepoint: oper failed with state = %s %s",
1252  log_state_string (state), " at client.\n");
1253  }
1254 #endif /* CUBRID_DEBUG */
1255  }
1256 
1257  return error_code;
1258 }
1259 
1260 static UINT64
1262 {
1263  struct timeval tv;
1264  UINT64 msecs;
1265 
1266  gettimeofday (&tv, NULL);
1267  msecs = (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
1268 
1269  return msecs;
1270 }
1271 
1272 /*
1273  * tran_set_query_timeout() -
1274  * return: void
1275  * query_timeout(in): timeout in milliseconds to be shipped to "query_execute_query"
1276  * and "query_prepare_and_execute_query"
1277  */
1278 void
1280 {
1283 }
1284 
1285 /*
1286  * tran_get_query_timeout() -
1287  * return: timeout (milliseconds)
1288  */
1289 int
1291 {
1292  UINT64 elapsed;
1293  int timeout;
1294 
1295  if (tm_Query_timeout <= 0)
1296  {
1297  return 0;
1298  }
1299 
1300  elapsed = tran_current_timemillis () - tm_Query_begin;
1301  timeout = (int) (tm_Query_timeout - elapsed);
1302  if (timeout <= 0)
1303  {
1304  /* already expired */
1305  timeout = -2;
1306  }
1307 
1308  return timeout;
1309 }
1310 
1311 /*
1312  * tran_begin_libcas_function() -
1313  */
1314 void
1316 {
1317  tm_Is_libcas = true;
1318 }
1319 
1320 /*
1321  * tran_end_libcas_function() -
1322  * return: void
1323  */
1324 void
1326 {
1327  tm_Is_libcas = false;
1328 }
1329 
1330 /*
1331  * tran_is_in_libcas() -
1332  * return: bool
1333  */
1334 bool
1336 {
1337  return tm_Is_libcas;
1338 }
1339 
1340 /*
1341  * tran_set_check_interrupt() -
1342  * return:
1343  * flag(in):
1344  */
1345 bool
1347 {
1348  bool old_val = true;
1349 
1350  old_val = tm_Tran_check_interrupt;
1351  tm_Tran_check_interrupt = flag;
1352 
1353  return old_val;
1354 }
1355 
1356 /*
1357  * tran_get_check_interrupt() -
1358  * return:
1359  */
1360 bool
1362 {
1363  return tm_Tran_check_interrupt;
1364 }
1365 
1367 {
1368  // 0000: default, none
1369  // 1000: aborted
1370  // 1001: aborted, reset required
1371  // 0100: ended, not committed
1372  // 0110: ended, committed
1373  // 0111: ended, committed, reset required
1374  NONE = 0,
1375  ABORTED = 0x1000,
1376  QUERY_ENDED = 0x100,
1377  COMMITTED = 0x10,
1379 };
1380 
1381 /*
1382  * tran_set_latest_query_status : set latest transaction query execution status
1383  * return: nothing
1384  * end_query_result(in): end query result
1385  * tran_state(in): transaction state
1386  * should_conn_reset(in): non zero, is reset needed
1387  *
1388  * Note : This function must be called after query execution with commit.
1389  */
1390 void
1391 tran_set_latest_query_status (int end_query_result, int tran_state, int should_conn_reset)
1392 {
1394 
1396 
1397  if (end_query_result != NO_ERROR)
1398  {
1399  // it is active
1400  return;
1401  }
1402 
1404 
1406  {
1408  }
1409  else if (tran_state == TRAN_UNACTIVE_ABORTED || tran_state == TRAN_UNACTIVE_ABORTED_INFORMING_PARTICIPANTS)
1410  {
1412  }
1413 
1414  if (should_conn_reset != 0)
1415  {
1417  || tran_state == TRAN_UNACTIVE_ABORTED || tran_state == TRAN_UNACTIVE_ABORTED_INFORMING_PARTICIPANTS);
1419  }
1420 }
1421 
1422 /*
1423  * tran_reset_latest_query_status : reset latest transaction query execution status
1424  * return: nothing
1425  */
1426 void
1428 {
1430 }
1431 
1432 /*
1433  * tran_was_latest_query_ended : check whether latest query was ended
1434  * return: true, if query ended
1435  */
1436 bool
1438 {
1440 }
1441 
1442 /*
1443  * tran_was_latest_query_committed : check whether latest query was executed with commit
1444  * return: true, if query executed with commit
1445  */
1446 bool
1448 {
1450 }
1451 
1452 /*
1453  * tran_was_latest_query_aborted : check whether latest query was aborted
1454  * return: true, if query executed with abort
1455  */
1456 bool
1458 {
1460 }
1461 
1462 /*
1463  * tran_is_reset_required : check whether reset is required
1464  * return: true, if reset is required
1465  */
1466 bool
1468 {
1470 }
int tran_savepoint_internal(const char *savept_name, SAVEPOINT_TYPE savepoint_type)
LC_FETCH_VERSION_TYPE
Definition: locator.h:178
#define ER_LK_UNILATERALLY_ABORTED
Definition: error_code.h:130
void tran_set_query_timeout(int query_timeout)
int tran_system_savepoint(const char *savept_name)
void tran_free_savepoint_list(void)
int tm_Tran_ID
#define NO_ERROR
Definition: error_code.h:46
int tran_get_query_timeout(void)
bool ws_has_updated(void)
Definition: work_space.c:2536
void tran_reset_latest_query_status(void)
int tm_Tran_wait_msecs
void tran_end_libcas_function(void)
#define L_cuserid
Definition: porting.h:58
int tran_unilaterally_abort(void)
void tran_get_tran_settings(int *lock_wait_in_msecs, TRAN_ISOLATION *tran_isolation, bool *async_ws)
static DB_NAMELIST * user_savepoint_list
int db_Connect_status
Definition: db_macro.c:88
int tm_Tran_index
int tran_get_global_tran_info(int gtrid, void *buffer, int size)
int tran_server_savepoint(const char *savept_name, LOG_LSA *savept_lsa)
bool tran_has_updated(void)
void nlist_free(DB_NAMELIST *list)
Definition: work_space.c:4344
#define ASSERT_ERROR_AND_SET(error_code)
TRAN_STATE tran_server_2pc_prepare_global_tran(int gtrid)
#define BOOT_IS_CLIENT_RESTARTED()
Definition: boot_cl.h:42
int tran_2pc_prepare_global_tran(int gtrid)
int tran_server_2pc_recovery_prepared(int gtrids[], int size)
bool tran_was_latest_query_ended(void)
void ws_decache_allxlockmops_but_norealclasses(void)
Definition: work_space.c:3426
bool tran_is_active_and_has_updated(void)
static bool tm_Is_libcas
int tm_Tran_invalidate_snapshot
int er_errid(void)
int tran_abort(void)
int tran_server_set_global_tran_info(int gtrid, void *info, int size)
const char * log_state_string(TRAN_STATE state)
Definition: log_comm.c:125
#define er_log_debug(...)
char * getuserid(char *string, int size)
Definition: porting.c:1271
void db_ws_free(void *ptr)
Definition: quick_fit.c:194
static UINT64 tran_current_timemillis(void)
void ws_increment_mvcc_snapshot_version(void)
Definition: work_space.c:4987
int tran_2pc_start(void)
int tran_server_2pc_start(void)
void sm_transaction_boundary(void)
static int tm_Query_timeout
LOCK
const char * name
Definition: dbtype_def.h:431
int tran_reset_wait_times(int wait_in_msecs)
void er_set(int severity, const char *file_name, const int line_no, int err_id, int num_args,...)
void tran_set_latest_query_status(int end_query_result, int tran_state, int should_conn_reset)
int tran_abort_upto_system_savepoint(const char *savepoint_name)
#define assert(x)
void tran_begin_libcas_function(void)
#define DB_CONNECTION_STATUS_RESET
Definition: db.h:48
int tran_server_has_updated(void)
int tran_2pc_prepare(void)
bool tm_Tran_async_ws
void er_stack_clearall(void)
int tran_commit(bool retain_lock)
bool tran_was_latest_query_committed(void)
#define TM_TRAN_WAIT_MSECS()
bool tran_get_check_interrupt(void)
TRAN_STATE tran_server_commit(bool retain_lock)
#define TRAN_LOCK_INFINITE_WAIT
Definition: log_comm.h:29
DB_TRAN_ISOLATION
Definition: dbtran_def.h:26
void db_clear_client_query_result(int notify_server, bool end_holdable)
Definition: db_query.c:1208
int tran_abort_only_client(bool is_server_down)
int locator_assign_all_permanent_oids(void)
Definition: locator_cl.c:6836
struct db_namelist * next
Definition: dbtype_def.h:430
TRAN_STATE tran_server_2pc_prepare(void)
#define NULL
Definition: freelistheap.h:34
const char * er_msg(void)
pid_t pid
Definition: dynamic_load.c:955
LC_FETCH_VERSION_TYPE tm_Tran_read_fetch_instance_version
bool tran_is_reset_required(void)
TRAN_ISOLATION tm_Tran_isolation
#define err(fd,...)
Definition: porting.h:431
bool tm_Use_OID_preflush
int tran_server_2pc_attach_global_tran(int gtrid)
LOCK tm_Tran_rep_read_lock
bool tran_is_in_libcas(void)
int tran_flush_to_commit(void)
int tran_internal_abort_upto_savepoint(const char *savepoint_name, SAVEPOINT_TYPE savepoint_type, bool client_decache_all_but_norealclasses)
int tran_reset_isolation(TRAN_ISOLATION isolation, bool async_ws)
TRAN_STATE tran_server_abort(void)
#define NULL_TRAN_INDEX
LATEST_QUERY_STATUS
bool tran_was_latest_query_aborted(void)
void ws_clear_all_hints(bool retain_lock)
Definition: work_space.c:3305
int tm_Tran_latest_query_status
void tran_cache_tran_settings(int tran_index, int lock_timeout, TRAN_ISOLATION tran_isolation)
int tran_server_get_global_tran_info(int gtrid, void *buffer, int size)
#define ARG_FILE_LINE
Definition: error_manager.h:44
#define ER_MVCC_LOG_INVALID_ISOLATION_LEVEL
Definition: error_code.h:1483
static UINT64 tm_Query_begin
int tran_2pc_attach_global_tran(int gtrid)
int locator_all_flush(void)
Definition: locator_cl.c:5457
void tr_check_abort_triggers(void)
int tran_abort_upto_user_savepoint(const char *savepoint_name)
#define TM_TRAN_ASYNC_WS()
int query_timeout
Definition: cas.c:160
static void tran_free_list_upto_savepoint(const char *savept_name)
int intl_mbs_casecmp(const char *mbs1, const char *mbs2)
Definition: intl_support.c:358
bool tm_Tran_check_interrupt
void * db_ws_alloc(size_t size)
Definition: quick_fit.c:73
#define ER_TM_SERVER_DOWN_UNILATERALLY_ABORTED
Definition: error_code.h:171
enum savepoint_type SAVEPOINT_TYPE
savepoint_type
#define ER_NET_SERVER_CRASHED
Definition: error_code.h:269
#define IS_VALID_ISOLATION_LEVEL(isolation_level)
Definition: dbtran_def.h:54
TRAN_STATE
Definition: log_comm.h:36
void ws_clear(void)
Definition: work_space.c:2526
void ws_filter_dirty(void)
Definition: work_space.c:1859
void ws_abort_mops(bool only_unpinned)
Definition: work_space.c:3353
TRAN_STATE tran_server_partial_abort(const char *savept_name, LOG_LSA *savept_lsa)
int tran_2pc_recovery_prepared(int gtrids[], int size)
int log_reset_isolation(TRAN_ISOLATION isolation)
int tr_check_commit_triggers(DB_TRIGGER_TIME time)
bool log_does_allow_replication(void)
Definition: log_comm.c:270
char * ws_copy_string(const char *str)
Definition: work_space.c:3457
#define CUB_MAXHOSTNAMELEN
Definition: porting.h:379
static char * host
bool ws_need_flush(void)
Definition: work_space.c:3813
#define DB_CONNECTION_STATUS_NOT_CONNECTED
Definition: db.h:46
#define GETHOSTNAME(p, l)
Definition: porting.h:381
bool tran_set_check_interrupt(bool flag)
int tran_set_global_tran_info(int gtrid, void *info, int size)
int tran_server_is_active_and_has_updated(void)
#define TM_TRAN_ISOLATION()
static int tran_add_savepoint(const char *savept_name)
void tr_check_rollback_triggers(DB_TRIGGER_TIME time)
int log_reset_wait_msecs(int wait_msecs)