# Library for initializing database tables

Every time an app instance is started it needs to check if the database already contains the desired tables and otherwise create them. After software updates it might also need to add new columns to existing tables. This library provides a small collection of tasks that facilitate that job.

## File information

File | common/lib/tasks_postgresql.htm
Classes | TaskPostgreSQLInitTable TaskPostgreSQLInitEnum
Examples | Initializing a database using multiple sequential tasks

# Classes

## TaskPostgreSQLInitTable

  A task that creates a single table including columns, constraints and indices, if not existing. Additional columns, constraints and indices are added if they don't exist.

      class TaskPostgreSQLInitTable : public ITask, public UDatabase {
          ...
      public:
          TaskPostgreSQLInitTable(class IDatabase * database, const char * tableName);
          virtual ~TaskPostgreSQLInitTable();
          void AddColumn(const char * name, const char * type);
          void AddConstraint(const char * name, const char * value);
          void AddIndex(const char * name, const char * value, bool unique);
          void Start(class UTask * user) override;
      };

### TaskPostgreSQLInitTable

  Contructor used to initialize the class.

  #### Parameters

  class IDatabase * database | The IDatabase object representing the connection to the database.
  const char * tableName | The name of the table. The CREATE statement puts the tableName inside double quotes. Pay attention about the casing and chars inside the tableName. If you e.g. use uppercase letters or spaces within the table name, you must later double quote the table name in your queries yourself too!

### AddColumn

  Specifies a column for the database table.

  #### Parameters

  const char * name | The name of the column.
  const char * type | The type of the column, e.g. "VARCHAR(32) UNIQUE NOT NULL".

### AddConstraint

  Specifies the constraint of a column.

  #### Parameters

  const char * name | The name of the column.
  const char * value | The constraint of the column, e.g. "UNIQUE (domain_id, name)".

### AddIndex

  Adds an index to the table. Indices can include a single column or multiple columns.

  #### Parameters

  const char * name | The name of the index.
  const char * value | A comma separated list of column names, e.g. "domain_id, name".
  bool unique | Tells if the data in the specified columns is unique.

### Start

  Starts the task of checking / creating the table.

  #### Parameters

  class UTask * user | The UTask object that shall receive the TaskComplete or TaskFailed callback after completion.

## TaskPostgreSQLInitEnum

A task that creates an enum in the database, if it does not already exist.

    class TaskPostgreSQLInitEnum : public ITask, public UDatabase {
        ...
    public:
        TaskPostgreSQLInitEnum(IDatabase * const database, const char * enumName);
        virtual ~TaskPostgreSQLInitEnum();
        void AddValue(const char * name);
        void Start(class UTask * user) override;
    };

### TaskPostgreSQLInitEnum

  Contructor used to initialize the class.

  #### Parameters

  class IDatabase * database | The IDatabase object representing the connection to the database.
  const char * enumName | The name of the enum.

### AddValue

  Adds a single value to the enum.

  #### Parameters

  const char * name | The value to be added, e.g. "DEMO_TYPE_1"

### Start

  Starts the task of checking / creating the enum.

  #### Parameters

  class UTask * user | The UTask object that shall receive the TaskComplete or TaskFailed callback after completion.

# Code Examples

## Initializing a database using multiple sequential tasks

    #include "platform/platform.h"
    #include "common/interface/database.h"
    #include "common/interface/task.h"
    #include "common/lib/tasks_postgresql.h"
    #include "sysadmin_tables.h"

    TaskDatabaseInit::TaskDatabaseInit(IDatabase * database) :
    initEnum(database, "demo_type"),
    initDomains(database, "domains"),
    initLocations(database, "locations"),
    {
        // enums
        initEnum.AddValue("DEMO_TYPE_1");
        initEnum.AddValue("DEMO_TYPE_2");
        initEnum.AddValue("DEMO_TYPE_3");
        // domains
        initDomains.AddColumn("id", "BIGSERIAL PRIMARY KEY NOT NULL");
        initDomains.AddColumn("name", "VARCHAR(32) UNIQUE NOT NULL");
        initDomains.AddColum("enum_demo", "demo_type"); // Type of the field = name of the enum
        initDomains.AddIndex("domains_index_name", "name", false);
        // locations
        initLocations.AddColumn("id", "BIGSERIAL PRIMARY KEY NOT NULL");
        initLocations.AddColumn("domain_id", "BIGINT REFERENCES domains(id)");
        initLocations.AddColumn("name", "VARCHAR(32) NOT NULL");
        initLocations.AddConstraint("locations_unique_name", "UNIQUE (domain_id, name)");
        initLocations.AddIndex("locations_index_name", "domain_id, name", true);  // index on several columns
    }

    TaskDatabaseInit::~TaskDatabaseInit()
    {
    }

    void TaskDatabaseInit::Start(class UTask * user)
    {
        this->user = user;
        initEnums.Start(this);
    }

    void TaskDatabaseInit::TaskComplete(class ITask * const task)
    {
        if (task == &initEnum) initDomains.Start(this);
        else if (task == &initDomains) initLocations.Start(this);
        else if (task == &initLocations) user->TaskComplete(this);
        else ASSERT(false, "TaskDatabaseInit::TaskComplete");
    }

    void TaskDatabaseInit::TaskFailed(class ITask * const task)
    {
        user->TaskFailed(this);
    }