From 1aa8ec1ddcfbd405b3b03f19758946dcb05f8fa1 Mon Sep 17 00:00:00 2001
From: Alexander Chemeris <Alexander.Chemeris@gmail.com>
Date: Fri, 30 Aug 2013 02:25:44 +0400
Subject: [PATCH 1/2] sms: Add a function to update DB scheme v3 to v4.

The v4 DB scheme removes sender ID from the DB and stores individual values instead (sender addr, ton, npi). To convert an old DB to the new format we have to read all values from the old table and re-add them to the new one.
---
 openbsc/include/openbsc/db.h     |    2 +-
 openbsc/src/libmsc/db.c          |  151 ++++++++++++++++++++++++++++++--------
 openbsc/src/osmo-nitb/bsc_hack.c |    2 +-
 openbsc/tests/db/db_test.c       |    2 +-
 4 files changed, 122 insertions(+), 35 deletions(-)

diff --git a/openbsc/include/openbsc/db.h b/openbsc/include/openbsc/db.h
index 25c2aea..e2c5f1b 100644
--- a/openbsc/include/openbsc/db.h
+++ b/openbsc/include/openbsc/db.h
@@ -31,7 +31,7 @@ struct gsm_subscriber;
 
 /* one time initialisation */
 int db_init(const char *name);
-int db_prepare(void);
+int db_prepare(struct gsm_network *net);
 int db_fini(void);
 
 /* subscriber management */
diff --git a/openbsc/src/libmsc/db.c b/openbsc/src/libmsc/db.c
index 77d2187..8a33f90 100644
--- a/openbsc/src/libmsc/db.c
+++ b/openbsc/src/libmsc/db.c
@@ -41,8 +41,36 @@
 static char *db_basename = NULL;
 static char *db_dirname = NULL;
 static dbi_conn conn;
-
-#define SCHEMA_REVISION "50000000"
+static struct gsm_sms *sms_from_result(struct gsm_network *net, dbi_result result);
+
+#define SCHEMA_REVISION "4"
+
+#define SMS_TABLE_CREATE_STMT \
+	"CREATE TABLE IF NOT EXISTS SMS (" \
+		/* metadata, not part of sms */ \
+		"id INTEGER PRIMARY KEY AUTOINCREMENT, " \
+		"created TIMESTAMP NOT NULL, " \
+		"sent TIMESTAMP, " \
+		"receiver_id INTEGER NOT NULL, " \
+		"deliver_attempts INTEGER NOT NULL DEFAULT 0, " \
+		/* data directly copied/derived from SMS */ \
+		"valid_until TIMESTAMP, " \
+		"reply_path_req INTEGER NOT NULL, " \
+		"status_rep_req INTEGER NOT NULL, " \
+		"protocol_id INTEGER NOT NULL, " \
+		"data_coding_scheme INTEGER NOT NULL, " \
+		"ud_hdr_ind INTEGER NOT NULL, " \
+		"src_addr TEXT NOT NULL, " \
+		"src_ton INTEGER NOT NULL, " \
+		"src_npi INTEGER NOT NULL, " \
+		"dest_addr TEXT NOT NULL, " \
+		"dest_ton INTEGER NOT NULL, " \
+		"dest_npi INTEGER NOT NULL, " \
+		"user_data BLOB, "	/* TP-UD */ \
+		/* additional data, interpreted from SMS */ \
+		"header BLOB, "		/* UD Header */ \
+		"text TEXT "		/* decoded UD after UDH */ \
+		")"
 
 static char *create_stmts[] = {
 	"CREATE TABLE IF NOT EXISTS Meta ("
@@ -90,31 +118,7 @@ static char *create_stmts[] = {
 		"equipment_id NUMERIC NOT NULL, "
 		"UNIQUE (subscriber_id, equipment_id) "
 		")",
-	"CREATE TABLE IF NOT EXISTS SMS ("
-		/* metadata, not part of sms */
-		"id INTEGER PRIMARY KEY AUTOINCREMENT, "
-		"created TIMESTAMP NOT NULL, "
-		"sent TIMESTAMP, "
-		"receiver_id INTEGER NOT NULL, "
-		"deliver_attempts INTEGER NOT NULL DEFAULT 0, "
-		/* data directly copied/derived from SMS */
-		"valid_until TIMESTAMP, "
-		"reply_path_req INTEGER NOT NULL, "
-		"status_rep_req INTEGER NOT NULL, "
-		"protocol_id INTEGER NOT NULL, "
-		"data_coding_scheme INTEGER NOT NULL, "
-		"ud_hdr_ind INTEGER NOT NULL, "
-		"src_addr TEXT NOT NULL, "
-		"src_ton INTEGER NOT NULL, "
-		"src_npi INTEGER NOT NULL, "
-		"dest_addr TEXT NOT NULL, "
-		"dest_ton INTEGER NOT NULL, "
-		"dest_npi INTEGER NOT NULL, "
-		"user_data BLOB, "	/* TP-UD */
-		/* additional data, interpreted from SMS */
-		"header BLOB, "		/* UD Header */
-		"text TEXT "		/* decoded UD after UDH */
-		")",
+	SMS_TABLE_CREATE_STMT,
 	"CREATE TABLE IF NOT EXISTS VLR ("
 		"id INTEGER PRIMARY KEY AUTOINCREMENT, "
 		"created TIMESTAMP NOT NULL, "
@@ -175,7 +179,7 @@ static int update_db_revision_2(void)
 				"TIMESTAMP DEFAULT NULL");
 	if (!result) {
 		LOGP(DDB, LOGL_ERROR,
-		     "Failed to alter table Subscriber (upgrade vom rev 2).\n");
+		     "Failed to alter table Subscriber (upgrade from rev 2).\n");
 		return -EINVAL;
 	}
 	dbi_result_free(result);
@@ -186,7 +190,84 @@ static int update_db_revision_2(void)
 				"WHERE key = 'revision'");
 	if (!result) {
 		LOGP(DDB, LOGL_ERROR,
-		     "Failed set new revision (upgrade vom rev 2).\n");
+		     "Failed to update DB schema revision  (upgrade from rev 2).\n");
+		return -EINVAL;
+	}
+	dbi_result_free(result);
+
+	return 0;
+}
+
+static int update_db_revision_3(struct gsm_network *net)
+{
+	dbi_result result;
+	struct gsm_sms *sms;
+	long long unsigned int sender_id;
+	struct gsm_subscriber *sender;
+
+	result = dbi_conn_query(conn,
+				"ALTER TABLE SMS "
+				"RENAME TO SMS_3");
+	if (!result) {
+		LOGP(DDB, LOGL_ERROR,
+		     "Failed to rename the old SMS table (upgrade from rev 3).\n");
+		return -EINVAL;
+	}
+	dbi_result_free(result);
+
+	result = dbi_conn_query(conn,
+				SMS_TABLE_CREATE_STMT);
+	if (!result) {
+		LOGP(DDB, LOGL_ERROR,
+		     "Failed to create a new SMS table (upgrade from rev 3).\n");
+		return -EINVAL;
+	}
+	dbi_result_free(result);
+
+	result = dbi_conn_queryf(conn,
+		"SELECT * FROM SMS_3");
+	if (!result) {
+		LOGP(DDB, LOGL_ERROR,
+		     "Failed fetch messages from the old SMS table (upgrade from rev 3).\n");
+		return -EINVAL;
+	}
+	while (dbi_result_next_row(result)) {
+		sms = sms_from_result(net, result);
+		sender_id = dbi_result_get_ulonglong(result, "sender_id");
+		sender = subscr_get_by_id(net, sender_id);
+		strncpy(sms->src.addr, sender->extension, sizeof(sms->src.addr)-1);
+		if (db_sms_store(sms) != 0) {
+			LOGP(DDB, LOGL_ERROR, "Failed to store message to the new SMS table(upgrade from rev 3).\n");
+		}
+		sms_free(sms);
+	}
+	dbi_result_free(result);
+
+	result = dbi_conn_query(conn,
+				"DROP TABLE SMS_3");
+	if (!result) {
+		LOGP(DDB, LOGL_ERROR,
+		     "Failed to drop the old SMS table (upgrade from rev 3).\n");
+		return -EINVAL;
+	}
+	dbi_result_free(result);
+
+	result = dbi_conn_query(conn,
+				"VACUUM");
+	if (!result) {
+		LOGP(DDB, LOGL_ERROR,
+		     "Failed VACUUM after droping the old SMS table (upgrade from rev 3).\n");
+		return -EINVAL;
+	}
+	dbi_result_free(result);
+
+	result = dbi_conn_query(conn,
+				"UPDATE Meta "
+				"SET value = '4' "
+				"WHERE key = 'revision'");
+	if (!result) {
+		LOGP(DDB, LOGL_ERROR,
+		     "Failed to update DB schema revision (upgrade from rev 3).\n");
 		return -EINVAL;
 	}
 	dbi_result_free(result);
@@ -194,7 +275,7 @@ static int update_db_revision_2(void)
 	return 0;
 }
 
-static int check_db_revision(void)
+static int check_db_revision(struct gsm_network *net)
 {
 	dbi_result result;
 	const char *rev_s;
@@ -219,6 +300,12 @@ static int check_db_revision(void)
 			dbi_result_free(result);
 			return -EINVAL;
 		}
+	} else if (!strcmp(rev_s, "3")) {
+			if (update_db_revision_3(net)) {
+				LOGP(DDB, LOGL_FATAL, "Failed to update database from schema revision '%s'.\n", rev_s);
+				dbi_result_free(result);
+				return -EINVAL;
+			}
 	} else if (!strcmp(rev_s, SCHEMA_REVISION)) {
 		/* everything is fine */
 	} else {
@@ -283,7 +370,7 @@ out_err:
 }
 
 
-int db_prepare(void)
+int db_prepare(struct gsm_network *net)
 {
 	dbi_result result;
 	int i;
@@ -298,7 +385,7 @@ int db_prepare(void)
 		dbi_result_free(result);
 	}
 
-	if (check_db_revision() < 0) {
+	if (check_db_revision(net) < 0) {
 		LOGP(DDB, LOGL_FATAL, "Database schema revision invalid, "
 			"please update your database schema\n");
                 return -1;
diff --git a/openbsc/src/osmo-nitb/bsc_hack.c b/openbsc/src/osmo-nitb/bsc_hack.c
index 172fb82..6608d4c 100644
--- a/openbsc/src/osmo-nitb/bsc_hack.c
+++ b/openbsc/src/osmo-nitb/bsc_hack.c
@@ -307,7 +307,7 @@ int main(int argc, char **argv)
 	}
 	printf("DB: Database initialized.\n");
 
-	if (db_prepare()) {
+	if (db_prepare(bsc_gsmnet)) {
 		printf("DB: Failed to prepare database.\n");
 		return -1;
 	}
diff --git a/openbsc/tests/db/db_test.c b/openbsc/tests/db/db_test.c
index c0ee618..8f5cf5a 100644
--- a/openbsc/tests/db/db_test.c
+++ b/openbsc/tests/db/db_test.c
@@ -68,7 +68,7 @@ int main()
 	}	 
 	printf("DB: Database initialized.\n");
 
-	if (db_prepare()) {
+	if (db_prepare(NULL)) {
 		printf("DB: Failed to prepare database.\n");
 		return 1;
 	}
-- 
1.7.9.5

