From 0b766c60aa2eb38d43401e06136ac1daa28abe5f Mon Sep 17 00:00:00 2001
From: Roger Sen <roger.sen@gmail.com>
Date: Sun, 15 Apr 2018 21:06:46 +0200
Subject: [PATCH 1/2] Create db.h and update c source files so they can find
 it.

---
 src/db.h   | 23 +++++++++++++++++++++++
 src/mud.h  | 11 +----------
 src/save.c |  1 +
 3 files changed, 25 insertions(+), 10 deletions(-)
 create mode 100644 src/db.h

diff --git a/src/db.h b/src/db.h
new file mode 100644
index 0000000..89c5ccf
--- /dev/null
+++ b/src/db.h
@@ -0,0 +1,23 @@
+/* file: db.h
+ *
+ * Headerfile for sqlite interface
+ */
+
+#ifndef _DB_HEADER
+#define _DB_HEADER
+
+#include <sqlite3.h>
+#include <stdbool.h>
+
+/*
+ * db.c
+ */
+bool           db_open        ( void );
+bool           db_close       ( void );
+bool           db_execute     ( const char *sql, ... );
+sqlite3_stmt  *db_prepare     (const char *sql, ...);
+int            db_step        (sqlite3_stmt *stmt);
+int            db_finalize    (sqlite3_stmt *stmt);
+void           db_migrate     ( void );
+  
+#endif
diff --git a/src/mud.h b/src/mud.h
index db2422f..6c16590 100644
--- a/src/mud.h
+++ b/src/mud.h
@@ -13,6 +13,7 @@
 #include "list.h"
 #include "stack.h"
 #include "crypt_blowfish-1.3-mini/ow-crypt.h"
+#include "db.h"
 
 /************************
  * Standard definitions *
@@ -317,16 +318,6 @@ void  save_player             ( D_M *dMob );
 D_M  *load_player             ( char *player );
 D_M  *load_profile            ( char *player );
 
-/*
- * db.c
- */
-bool           db_open        ( void );
-bool           db_close       ( void );
-bool           db_execute     ( const char *sql, ... );
-sqlite3_stmt  *db_prepare     (const char *sql, ...);
-int            db_step        (sqlite3_stmt *stmt);
-int            db_finalize    (sqlite3_stmt *stmt);
-void           db_migrate     ( void );
 
 /*******************************
  * End of prototype declartion *
diff --git a/src/save.c b/src/save.c
index 5040d26..b2fbedd 100644
--- a/src/save.c
+++ b/src/save.c
@@ -7,6 +7,7 @@
 
 /* main header file */
 #include "mud.h"
+#include "db.h"
 
 void save_player(D_MOBILE *dMob)
 {

From e974d23197d351b961c2503a32d1a41c9adbef39 Mon Sep 17 00:00:00 2001
From: Roger Sen <roger.sen@gmail.com>
Date: Sun, 15 Apr 2018 21:07:19 +0200
Subject: [PATCH 2/2] Update db_migrate logic so it can handle schema upgrades.

---
 src/db.c | 112 +++++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 108 insertions(+), 4 deletions(-)

diff --git a/src/db.c b/src/db.c
index f4cf28c..9f0bb5d 100644
--- a/src/db.c
+++ b/src/db.c
@@ -6,10 +6,37 @@
 
 /* main header file */
 #include "mud.h"
+#include "db.h"
 
 sqlite3     *  db;
 
 sqlite3_stmt *db_prepare_internal(const char *sql, va_list vars);
+int get_db_schema();
+
+/*
+ * Array of commands to be executed by db_migrate to apply changes to the database.
+ * 
+ */
+
+typedef struct migration_commands {
+    int schema;
+    char *action;
+} migration_commands;
+
+migration_commands command[] = {
+/*  { DB_SCHEMA, Action to upgrade the database to the DB_SCHEMA } */  
+    { 0, "CREATE TABLE IF NOT EXISTS players (id INTEGER PRIMARY KEY, name TEXT NOT NULL UNIQUE, password TEXT NOT NULL, level INTEGER)" },
+    { 1, "CREATE TABLE IF NOT EXISTS NEXT_ID (ID DECIMAL(9,0) NOT NULL)" },
+    { 1, "INSERT INTO NEXT_ID (ID) VALUES(1)"},
+    {-1, NULL } // Sentinel to identify end of commands. DO NOT REMOVE.
+}; 
+
+
+
+/*
+ * Open the global database.
+ * 
+ */
 
 bool db_open()
 {
@@ -25,6 +52,11 @@ bool db_open()
   return true;
 }
 
+/*
+ * Close the global database.
+ * 
+ */
+
 bool db_close()
 {
   if (sqlite3_close(db) != SQLITE_OK )
@@ -37,6 +69,11 @@ bool db_close()
   return true;
 }
 
+/*
+ * Execute a query to the database expecting no answer.
+ * Used for things like DROP, INSERT, CREATE TABLE...
+ */
+
 bool db_execute(const char *sql, ...)
 {
   va_list vars;
@@ -52,7 +89,6 @@ bool db_execute(const char *sql, ...)
     return false;
   }
 
-
   if ( db_step(stmt) != SQLITE_DONE ) {
     bug("Failed to step through statement: %s", sqlite3_errmsg(db));
 
@@ -174,18 +210,86 @@ sqlite3_stmt *db_prepare_internal(const char *sql, va_list vars)
 void db_migrate()
 {
 
+  int db_schema = get_db_schema();
+  int i = 0;
+
+  if ( !db_open() )
+  {
+    abort();
+  }
+
+  if (-1 == db_schema) {
+    if(  !db_execute("CREATE TABLE IF NOT EXISTS DB_SCHEMA (db_version DECIMAL(6,0) NOT NULL)")
+      || !db_execute("INSERT INTO DB_SCHEMA (db_version) VALUES(1)")) {
+      abort();
+    }
+  }
+
+  db_execute("BEGIN EXCLUSIVE TRANSACTION");
+  while(command[i].schema != -1) {
+    if (command[i].schema > db_schema) {
+      if ( !db_execute(command[i].action) ) {
+        db_execute("ROLLBACK TRANSACTION");  
+        abort();
+      }
+    }
+    if( !db_execute("UPDATE DB_SCHEMA SET db_version = %i", command[i].schema) ) {
+      db_execute("ROLLBACK TRANSACTION");  
+      abort();
+    }
+    i++;
+  }
+  db_execute("COMMIT TRANSACTION");
+
+
+  if(command[i-1].schema > db_schema ) {
+    log_string("db_schema updated to version %d", command[i-1].schema);
+  } else {
+    log_string("Current db_schema: %d", db_schema);
+  }
+
+  db_close();
+
+  return;
+}
+
+
+/*
+ * Retrieve the schema version.
+ * 
+ * SELECT db_version FROM DB_SCHEMA
+ * 
+ */
+
+int get_db_schema()
+{
+  sqlite3_stmt *stmt;
+  int db_schema = -1;
+  
   if ( !db_open() )
   {
     abort();
   }
 
-  /* players table */
-  if ( !db_execute("CREATE TABLE IF NOT EXISTS players (id INTEGER PRIMARY KEY, name TEXT NOT NULL UNIQUE, password TEXT NOT NULL, level INTEGER)") )
+  stmt = db_prepare("SELECT db_version FROM DB_SCHEMA");
+
+  if ( stmt == NULL )
   {
+    db_close();
+    return db_schema;
+  }
+
+  if ( db_step(stmt) == SQLITE_ROW ) {
+    db_schema      = sqlite3_column_int(stmt, 0);
+  }
+
+  if ( db_finalize(stmt) != SQLITE_OK ) {
+    bug("Failed to finalize statement: %s", sqlite3_errmsg(db));
+
     abort();
   }
 
   db_close();
 
-  return;
+  return db_schema;
 }