Browse code

db_mysql Add transaction support

Patch contributed via Google+ by Håkon Nassjöen <haakon.nassjoen@gmail.com>

Olle E. Johansson authored on 02/04/2013 12:13:01
Showing 4 changed files
... ...
@@ -116,6 +116,9 @@ int db_mysql_bind_api(db_func_t *dbb)
116 116
 	dbb->insert_update    = db_mysql_insert_update;
117 117
 	dbb->insert_delayed   = db_mysql_insert_delayed;
118 118
 	dbb->affected_rows    = db_mysql_affected_rows;
119
+	dbb->start_transaction= db_mysql_start_transaction;
120
+	dbb->end_transaction  = db_mysql_end_transaction;
121
+	dbb->abort_transaction= db_mysql_abort_transaction;
119 122
 
120 123
 	return 0;
121 124
 }
... ...
@@ -500,6 +500,142 @@ int db_mysql_affected_rows(const db1_con_t* _h)
500 500
 	return (int)mysql_affected_rows(CON_CONNECTION(_h));
501 501
 }
502 502
 
503
+/**
504
+ * Starts a single transaction that will consist of one or more queries (SQL BEGIN)
505
+ * \param _h database handle
506
+ * \return 0 on success, negative on failure
507
+ */
508
+int db_mysql_start_transaction(db1_con_t* _h, db_locking_t _l)
509
+{
510
+	str begin_str = str_init("BEGIN");
511
+	str lock_start_str = str_init("LOCK TABLE ");
512
+	str lock_end_str  = str_init(" WRITE");
513
+	str lock_str = {0, 0};
514
+
515
+	if (!_h) {
516
+		LM_ERR("invalid parameter value\n");
517
+		return -1;
518
+	}
519
+
520
+	if (CON_TRANSACTION(_h) == 1) {
521
+		LM_ERR("transaction already started\n");
522
+		return -1;
523
+	}
524
+
525
+	if (db_mysql_raw_query(_h, &begin_str, NULL) < 0)
526
+	{
527
+		LM_ERR("executing raw_query\n");
528
+		return -1;
529
+	}
530
+
531
+	CON_TRANSACTION(_h) = 1;
532
+
533
+	switch(_l)
534
+	{
535
+	case DB_LOCKING_NONE:
536
+		break;
537
+	case DB_LOCKING_FULL:
538
+		/* Fall-thru */
539
+	case DB_LOCKING_WRITE:
540
+		if ((lock_str.s = pkg_malloc((lock_start_str.len + CON_TABLE(_h)->len + lock_end_str.len) * sizeof(char))) == NULL)
541
+		{
542
+			LM_ERR("allocating pkg memory\n");
543
+			goto error;
544
+		}
545
+
546
+		memcpy(lock_str.s, lock_start_str.s, lock_start_str.len);
547
+		lock_str.len += lock_start_str.len;
548
+		memcpy(lock_str.s + lock_str.len, CON_TABLE(_h)->s, CON_TABLE(_h)->len);
549
+		lock_str.len += CON_TABLE(_h)->len;
550
+		memcpy(lock_str.s + lock_str.len, lock_end_str.s, lock_end_str.len);
551
+		lock_str.len += lock_end_str.len;
552
+
553
+		if (db_mysql_raw_query(_h, &lock_str, NULL) < 0)
554
+		{
555
+			LM_ERR("executing raw_query\n");
556
+			goto error;
557
+		}
558
+
559
+		if (lock_str.s) pkg_free(lock_str.s);
560
+		break;
561
+
562
+	default:
563
+		LM_WARN("unrecognised lock type\n");
564
+		goto error;
565
+	}
566
+
567
+	return 0;
568
+
569
+error:
570
+	if (lock_str.s) pkg_free(lock_str.s);
571
+	db_mysql_abort_transaction(_h);
572
+	return -1;
573
+}
574
+
575
+/**
576
+ * Ends a transaction and commits the changes (SQL COMMIT)
577
+ * \param _h database handle
578
+ * \return 0 on success, negative on failure
579
+ */
580
+int db_mysql_end_transaction(db1_con_t* _h)
581
+{
582
+	str query_str = str_init("COMMIT");
583
+
584
+	if (!_h) {
585
+		LM_ERR("invalid parameter value\n");
586
+		return -1;
587
+	}
588
+
589
+	if (CON_TRANSACTION(_h) == 0) {
590
+		LM_ERR("transaction not in progress\n");
591
+		return -1;
592
+	}
593
+
594
+	if (db_mysql_raw_query(_h, &query_str, NULL) < 0)
595
+	{
596
+		LM_ERR("executing raw_query\n");
597
+		return -1;
598
+	}
599
+
600
+	/* Only _end_ the transaction after the raw_query.  That way, if the
601
+ 	   raw_query fails, and the calling module does an abort_transaction()
602
+	   to clean-up, a ROLLBACK will be sent to the DB. */
603
+	CON_TRANSACTION(_h) = 0;
604
+	return 0;
605
+}
606
+
607
+/**
608
+ * Ends a transaction and rollsback the changes (SQL ROLLBACK)
609
+ * \param _h database handle
610
+ * \return 1 if there was something to rollback, 0 if not, negative on failure
611
+ */
612
+int db_mysql_abort_transaction(db1_con_t* _h)
613
+{
614
+	str query_str = str_init("ROLLBACK");
615
+
616
+	if (!_h) {
617
+		LM_ERR("invalid parameter value\n");
618
+		return -1;
619
+	}
620
+
621
+	if (CON_TRANSACTION(_h) == 0) {
622
+		LM_DBG("nothing to rollback\n");
623
+		return 0;
624
+	}
625
+
626
+	/* Whether the rollback succeeds or not we need to _end_ the
627
+ 	   transaction now or all future starts will fail */
628
+	CON_TRANSACTION(_h) = 0;
629
+
630
+	if (db_mysql_raw_query(_h, &query_str, NULL) < 0)
631
+	{
632
+		LM_ERR("executing raw_query\n");
633
+		return -1;
634
+	}
635
+
636
+	return 1;
637
+}
638
+
503 639
 
504 640
 /**
505 641
   * Insert a row into a specified table, update on duplicate key.
... ...
@@ -40,6 +40,7 @@
40 40
 #include "../../lib/srdb1/db_key.h"
41 41
 #include "../../lib/srdb1/db_op.h"
42 42
 #include "../../lib/srdb1/db_val.h"
43
+#include "../../lib/srdb1/db_locking.h"
43 44
 #include "../../str.h"
44 45
 
45 46
 /*! \brief
... ...
@@ -118,6 +119,21 @@ int db_mysql_last_inserted_id(const db1_con_t* _h);
118 119
  */
119 120
 int db_mysql_affected_rows(const db1_con_t* _h);
120 121
 
122
+/*! \brief
123
+ * Starts transaction
124
+ */
125
+int db_mysql_start_transaction(db1_con_t* _h, db_locking_t _l);
126
+
127
+/*! \brief
128
+ * Commits transaction
129
+ */
130
+int db_mysql_end_transaction(db1_con_t* _h);
131
+
132
+/*! \brief
133
+ * Aborts transaction
134
+ */
135
+int db_mysql_abort_transaction(db1_con_t* _h);
136
+
121 137
 
122 138
 /*! \brief
123 139
  * Insert a row into table, update on duplicate key
... ...
@@ -48,16 +48,18 @@ struct my_con {
48 48
 	MYSQL* con;              /*!< Connection representation */
49 49
 	MYSQL_ROW row;           /*!< Actual row in the result */
50 50
 	time_t timestamp;        /*!< Timestamp of last query */
51
+	int transaction;	 /*!< indicates whether a multi-query transaction is currently open */
51 52
 };
52 53
 
53 54
 
54 55
 /*
55 56
  * Some convenience wrappers
56 57
  */
57
-#define CON_RESULT(db_con)     (((struct my_con*)((db_con)->tail))->res)
58
-#define CON_CONNECTION(db_con) (((struct my_con*)((db_con)->tail))->con)
59
-#define CON_ROW(db_con)        (((struct my_con*)((db_con)->tail))->row)
60
-#define CON_TIMESTAMP(db_con)  (((struct my_con*)((db_con)->tail))->timestamp)
58
+#define CON_RESULT(db_con)      (((struct my_con*)((db_con)->tail))->res)
59
+#define CON_CONNECTION(db_con)  (((struct my_con*)((db_con)->tail))->con)
60
+#define CON_ROW(db_con)         (((struct my_con*)((db_con)->tail))->row)
61
+#define CON_TIMESTAMP(db_con)   (((struct my_con*)((db_con)->tail))->timestamp)
62
+#define CON_TRANSACTION(db_con) (((struct my_con*)((db_con)->tail))->transaction)
61 63
 
62 64
 
63 65
 /*! \brief