Skip to main content

Databases

Overview

io.Manager supports connecting to Microsoft SQL Server databases. The following sections outline the steps and requirements for configuring io.Manager to work with Microsoft SQL Server databases and the options for initializing or migrating the database schema.

ℹ️ For a complete example of connecting io.Manager to a Microsoft SQL Server database, see the Microsoft SQL Server example on GitHub.

Connecting to Microsoft SQL Server Databases

To configure io.Manager to connect to a Microsoft SQL Server database, you must either set the necessary environment variables, or provide the required settings via the configuration object for initializing the io.Manager Server.

Depending on the deployment approach you have chosen, you have the following options:

The following sections provide examples of both options.

Environment Variables

To configure io.Manager to connect to a Microsoft SQL Server database, you must register all of the following environment variables with the proper values. The API_STORE_TYPE environment variable must be set to mssql. All other variables must be set with values according to your specific environment:

Environment Variable Description
API_STORE_TYPE Required. Type of the database. Must be set to mssql.
API_STORE_MSSQL_CREATE_DB If true (default), will automatically create and initialize the database and the tables necessary for io.Manager. Set to false if you want to do this separately. For more details on database schema creation and migration, see the Database Schema section.
API_STORE_MSSQL_DB_NAME Required. Database name for the Microsoft SQL Server connection URL.
API_STORE_MSSQL_DOMAIN Windows domain for login.
API_STORE_MSSQL_MIGRATION_RETRIES How many times store initialization - creating the database, creating the schema and running the migration scripts - is retried after a transient failure, such as another server instance initializing the same database concurrently. The error of the final attempt is propagated. Defaults to 3. Available since io.Manager 4.0.
API_STORE_MSSQL_MIGRATION_RETRY_DELAY_MS The delay in milliseconds between store initialization retry attempts. Defaults to 1000. Available since io.Manager 4.0.
API_STORE_MSSQL_PASSWORD Required. Password for authentication.
API_STORE_MSSQL_PORT Port for the Microsoft SQL Server connection URL.
API_STORE_MSSQL_SERVER Host for the Microsoft SQL Server connection URL.
API_STORE_MSSQL_TRANSACTION_ISOLATION The isolation level for the database transactions the server opens. Accepts database-default, read-committed, snapshot, or serializable as a value. database-default leaves the database's own default isolation level in effect. Defaults to database-default.
⚠️ Note that snapshot requires the database to have ALLOW_SNAPSHOT_ISOLATION enabled - otherwise, the server won't start.
Available since io.Manager 4.0.
API_STORE_MSSQL_TRANSACTION_RETRIES How many times a database transaction, or a standalone read, is re-run after a transient failure. Set to 0 to disable the retry. Defaults to 3. Available since io.Manager 4.0.
API_STORE_MSSQL_TRANSACTION_RETRY_DELAY_MS The base delay in milliseconds between retry attempts. The actual delay is randomized and increases with each retry. Defaults to 100. Available since io.Manager 4.0.
API_STORE_MSSQL_USERNAME Required. Username for authentication.

Example settings:

API_STORE_TYPE=mssql
API_STORE_MSSQL_SERVER=localhost
API_STORE_MSSQL_PORT=1433
API_STORE_MSSQL_DB_NAME=my_db
API_STORE_MSSQL_CREATE_DB=true
API_STORE_MSSQL_DOMAIN=MYDOMAIN
API_STORE_MSSQL_USERNAME=my_user
API_STORE_MSSQL_PASSWORD=password

ℹ️ For details on all available environment variables for configuring the io.Manager Server, see the Configuration > Server section.

Configuration Object

To configure io.Manager to connect to a Microsoft SQL Server database, you must provide the necessary settings when initializing the io.Manager Server. Use the store property of the optional Config object and provide an MSSQLStoreConfig object as its value.

The following example demonstrates configuring the connection to a Microsoft SQL Server database when initializing the io.Manager Server:

import { start } from "@interopio/manager";

// Configuration for the io.Manager Server.
const config = {
    name: "my-server",
    port: 4242,
    token: {
        secret: "my-secret"
    },
    // Configuration for connecting to a Microsoft SQL Server database.
    store: {
        type: "mssql",
        server: "localhost",
        port: 1433,
        dbName: "my_db",
        userName: "my_user",
        password: "password"
    }
};

// Initializing the io.Manager Server.
const server = await start(config);

The store object has the following properties:

Property Type Description
createDatabaseAndTables boolean If true (default), will automatically create and initialize the database and the tables necessary for io.Manager. Set to false if you want to do this separately. For more details on database schema creation and migration, see the Database Schema section.
dbName string Database name for the Microsoft SQL Server connection URL. Defaults to "test".
domain string Windows domain for login.
driver string Microsoft SQL Server driver name as used in the Knex.js library.
migrationRetries number How many times store initialization - creating the database, creating the schema and running the migration scripts - is retried after a transient failure, such as another server instance initializing the same database concurrently. The error of the final attempt is propagated. Defaults to 3. Available since io.Manager 4.0.
migrationRetryDelayMs number The delay in milliseconds between store initialization retry attempts. Defaults to 1000. Available since io.Manager 4.0.
password string Password for authentication.
poolConfig object Knex.js pool configuration. For more details, see the official Knex.js documentation.
port number Port for the Microsoft SQL Server connection URL.
server string Required. Host for the Microsoft SQL Server connection URL.
transactionIsolation "database-default" | "read-committed" | "snapshot" | "serializable" The isolation level for the database transactions the server opens. "database-default" leaves the database's own default isolation level in effect. Defaults to "database-default".
⚠️ Note that "snapshot" requires the database to have ALLOW_SNAPSHOT_ISOLATION enabled - otherwise, the server won't start.
Available since io.Manager 4.0.
transactionRetries number How many times a database transaction, or a standalone read, is re-run after a transient failure. Set to 0 to disable the retry. Defaults to 3. Available since io.Manager 4.0.
transactionRetryDelayMs number The base delay in milliseconds between retry attempts. The actual delay is randomized and increases with each retry. Defaults to 100. Available since io.Manager 4.0.
type "mssql" Required. Type of the data store. Must be set to "mssql" when using a Microsoft SQL Server database.
userName string Username for authentication.

ℹ️ For details on all available properties for configuring the io.Manager Server, see the Configuration > Server section.

Database Schema

The io.Manager schema for Microsoft SQL Server databases can be created or migrated in two ways: automatically, when starting the io.Manager Server, or by using a schema creation script.

Automatic Creation & Migration

io.Manager provides an automated functionality for creating or migrating the database schema. This functionality uses Knex.js migrations internally. The creation or migration of the database schema is executed on startup of the server, only if required.

To enable io.Manager to use this automated functionality, the user configured in io.Manager for connecting to the database must meet at least one of the following requirements:

  • the user has the CREATE DATABASE permission in the master database;
  • the user has the CREATE ANY DATABASE permission;
  • the user has the ALTER ANY DATABASE permission;

Schema Creation Script

If you don't want to use the automated functionality of io.Manager for creating and migrating the database schema, you can use the SQL script provided in this section. Execute the script before initializing the io.Manager Server in order to create the necessary database and tables.

⚠️ Note that when you create the database yourself, you must create it with collation as in the following example:

CREATE DATABASE my_db COLLATE SQL_Latin1_General_CP1_CS_AS

Prerequisites

Using this script means that you must disable the automated functionality of io.Manager for creating or migrating the database schema.

If you are using the basic deployment scenario from the template repository approach, you must set the API_STORE_MSSQL_CREATE_DB environment variable to false:

API_STORE_MSSQL_CREATE_DB=false

If you are using the NPM packages for deployment, or the advanced deployment scenario from the template repository approach, you must set the createDatabaseAndTables property to false in the optional Config object for initializing the io.Manager Server:

import { start } from "@interopio/manager";

const config = {
    name: "my-server",
    port: 4242,
    token: {
        secret: "my-secret"
    },
    store: {
        type: "mssql",
        server: "localhost",
        port: 1433,
        dbName: "my_db",
        userName: "my_user",
        password: "password",
        // Disable the automated functionality for creating the database.
        createDatabaseAndTables: false
    }
};

const server = await start(config);

SQL Script for Creating the Database

⚠️ Note that my_user in the script must already be created. Replace my_user with the actual user name where necessary.

-- io.Manager schema initialization script for Microsoft SQL Server databases.

-- A group of users. Groups are used to grant access to applications and layouts.
IF NOT EXISTS (SELECT * FROM sysobjects WHERE name='groups' AND xtype='U')
CREATE TABLE groups
(
    groups_id INT IDENTITY PRIMARY KEY, -- Primary key. Auto-assigned by the database and not used to identify the row outside it.
    name NVARCHAR(255) NOT NULL, -- Name of the group. Unique.
    description NVARCHAR(MAX) NULL, -- Description of the group.
    "expandsTo" NVARCHAR(MAX) NOT NULL CONSTRAINT DF_groups_expandsTo DEFAULT '[]', -- Groups this group also grants membership of. Empty when it expands into no others. JSON array of strings, stored as text.
    CONSTRAINT groups_name_unique UNIQUE (name)
);

-- A single row of change timestamps. An io.Connect platform polls these to learn whether it needs to re-fetch a kind of data.
IF NOT EXISTS (SELECT * FROM sysobjects WHERE name='last_updated' AND xtype='U')
CREATE TABLE last_updated
(
    last_updated_id INT IDENTITY PRIMARY KEY, -- Primary key. Auto-assigned by the database and not used to identify the row outside it.
    applications BIGINT NOT NULL, -- Time any application last changed. Epoch milliseconds (UTC).
    layouts BIGINT NOT NULL, -- Time any layout last changed. Epoch milliseconds (UTC).
    groups BIGINT NOT NULL, -- Unused. Epoch milliseconds (UTC).
    commands BIGINT NOT NULL, -- Time any command last changed. Epoch milliseconds (UTC).
    configs BIGINT NOT NULL, -- Time any system config last changed. Epoch milliseconds (UTC).
    others NVARCHAR(MAX) NULL -- Further change timestamps that do not have a column of their own. JSON, stored as text.
);

-- Layouts saved by an io.Connect platform.
IF NOT EXISTS (SELECT * FROM sysobjects WHERE name='layouts' AND xtype='U')
CREATE TABLE layouts
(
    layouts_id INT IDENTITY PRIMARY KEY, -- Primary key. Auto-assigned by the database and not used to identify the row outside it.
    id NVARCHAR(255) NOT NULL, -- Public identifier of the layout. Unique.
    type NVARCHAR(255) NOT NULL, -- Kind of layout, for example 'Global' or 'Workspace'.
    name NVARCHAR(255) NOT NULL, -- Name of the layout. Unique per type per owner.
    owner NVARCHAR(255) NULL, -- User the layout is private to. The literal '*' marks a common layout, whose visibility is then decided by the isPublic and accessList columns.
    "isPublic" BIT NOT NULL, -- Whether every user can read the layout. Applies only to common layouts. Boolean, stored as 0 or 1.
    disabled BIT NOT NULL, -- Whether the layout is withheld from all users. Boolean, stored as 0 or 1.
    "accessList" NVARCHAR(MAX) NULL, -- Groups allowed to read the layout when it is common and not public. JSON array of strings, stored as text.
    definition NVARCHAR(MAX) NOT NULL, -- The layout payload as saved by the io.Connect platform. JSON, stored as text.
    "createdBy" NVARCHAR(255) NOT NULL, -- User that created the layout.
    "createdOn" BIGINT NOT NULL, -- Creation time. Epoch milliseconds (UTC).
    "lastModifiedBy" NVARCHAR(255) NULL, -- User that last modified the layout.
    "lastModifiedOn" BIGINT NULL, -- Time of the last modification. Epoch milliseconds (UTC).
    "isDefault" BIT NULL, -- Whether the layout is restored when an io.Connect platform starts. Boolean, stored as 0 or 1.
    migrated BIT NULL, -- Whether this layout has already been migrated to the layouts_advanced table. Boolean, stored as 0 or 1.
    owner_lower AS LOWER([owner]) PERSISTED, -- The owner, lowercased, for resolving a username when username_case_sensitive is false. Derived by the database.
    CONSTRAINT layouts_id_unique UNIQUE (id),
    CONSTRAINT layouts__name__type__owner__unique UNIQUE ([name], [type], [owner])
);

IF NOT EXISTS (SELECT * FROM sys.indexes WHERE name = 'layouts__owner__lower' AND object_id = OBJECT_ID('layouts'))
CREATE INDEX layouts__owner__lower ON layouts (owner_lower);
IF NOT EXISTS (SELECT * FROM sys.indexes WHERE name = 'layouts__owner' AND object_id = OBJECT_ID('layouts'))
CREATE INDEX layouts__owner ON layouts ([owner]);

-- A machine an io.Connect platform has run on. Referenced by sessions.
IF NOT EXISTS (SELECT * FROM sysobjects WHERE name='machines' AND xtype='U')
CREATE TABLE machines
(
    machines_id INT IDENTITY PRIMARY KEY, -- Primary key. Auto-assigned by the database and not used to identify the row outside it.
    id NVARCHAR(255) NOT NULL, -- Public identifier of the machine. Unique.
    "user" NVARCHAR(255) NOT NULL, -- User the machine is associated with.
    os NVARCHAR(MAX) NULL, -- Operating-system details reported by the machine. JSON, stored as text.
    name NVARCHAR(255) NULL, -- Host name of the machine.
    displays NVARCHAR(MAX) NULL, -- Displays attached to the machine. JSON, stored as text.
    browser NVARCHAR(MAX) NULL, -- Browser details. Populated for io.Connect Browser sessions only. JSON, stored as text.
    user_lower AS LOWER([user]) PERSISTED, -- The user, lowercased, for resolving a username when username_case_sensitive is false. Derived by the database.
    CONSTRAINT machines_id_unique UNIQUE (id)
);

IF NOT EXISTS (SELECT * FROM sys.indexes WHERE name = 'machines__user__lower' AND object_id = OBJECT_ID('machines'))
CREATE INDEX machines__user__lower ON machines (user_lower);
IF NOT EXISTS (SELECT * FROM sys.indexes WHERE name = 'machines__user' AND object_id = OBJECT_ID('machines'))
CREATE INDEX machines__user ON machines ([user]);

-- Per-user, per-application preferences. One row per application and user.
IF NOT EXISTS (SELECT * FROM sysobjects WHERE name='prefs' AND xtype='U')
CREATE TABLE prefs
(
    prefs_id INT IDENTITY PRIMARY KEY, -- Primary key. Auto-assigned by the database and not used to identify the row outside it.
    id NVARCHAR(255) NOT NULL, -- Public identifier of the preference. Unique.
    app NVARCHAR(255) NOT NULL, -- Application the preference belongs to.
    "user" NVARCHAR(255) NOT NULL, -- User the preference belongs to.
    data NVARCHAR(MAX) NOT NULL, -- The preference payload. Arbitrary data. JSON, stored as text.
    "lastUpdate" BIGINT NOT NULL, -- Time of the last update. Epoch milliseconds (UTC).
    user_lower AS LOWER([user]) PERSISTED, -- The user, lowercased, for resolving a username when username_case_sensitive is false. Derived by the database.
    CONSTRAINT prefs_id_unique UNIQUE (id),
    CONSTRAINT prefs__app__user__unique UNIQUE ([app], [user])
);

IF NOT EXISTS (SELECT * FROM sys.indexes WHERE name = 'prefs__user__lower__app' AND object_id = OBJECT_ID('prefs'))
CREATE INDEX prefs__user__lower__app ON prefs (user_lower, app);
IF NOT EXISTS (SELECT * FROM sys.indexes WHERE name = 'prefs__user' AND object_id = OBJECT_ID('prefs'))
CREATE INDEX prefs__user ON prefs ([user]);

-- A run of an io.Connect platform by one user on one machine.
IF NOT EXISTS (SELECT * FROM sysobjects WHERE name='sessions' AND xtype='U')
CREATE TABLE sessions
(
    sessions_id INT IDENTITY PRIMARY KEY, -- Primary key. Auto-assigned by the database and not used to identify the row outside it.
    id NVARCHAR(255) NOT NULL, -- Public identifier of the session. Unique.
    machine NVARCHAR(255) NOT NULL, -- Machine the session ran on.
    start BIGINT NOT NULL, -- Time the session started. Epoch milliseconds (UTC).
    "user" NVARCHAR(255) NOT NULL, -- User that opened the session.
    glue NVARCHAR(MAX) NOT NULL, -- io.Connect platform details reported for the session. JSON, stored as text.
    "lastDataFetch" BIGINT NULL, -- Time the client last requested data from the server. Used to purge inactive sessions. Epoch milliseconds (UTC).
    "end" BIGINT NULL, -- Time the session was closed. Epoch milliseconds (UTC).
    closed BIT NULL, -- Whether the session has been closed. Boolean, stored as 0 or 1.
    "closeReason" NVARCHAR(255) NULL, -- Why the session was closed. 'clean' when an administrator closed it.
    product NVARCHAR(255) NOT NULL, -- The io.Connect product that opened the session.
    "productVersion" NVARCHAR(255) NOT NULL, -- Version of that product. Determines which commands the session supports.
    CONSTRAINT sessions_id_unique UNIQUE (id)
);

-- A user known to io.Manager.
IF NOT EXISTS (SELECT * FROM sysobjects WHERE name='users' AND xtype='U')
CREATE TABLE users
(
    users_id INT IDENTITY PRIMARY KEY, -- Primary key. Auto-assigned by the database and not used to identify the row outside it.
    id NVARCHAR(255) NOT NULL, -- Identifier of the user. Unique.
    email NVARCHAR(255) NULL, -- Email address of the user.
    password NVARCHAR(255) NULL, -- A bcrypt hash of the user's password. Populated only when basic authentication is enabled.
    apps NVARCHAR(MAX) NOT NULL, -- Applications granted directly to the user. JSON array of strings, stored as text.
    groups NVARCHAR(MAX) NOT NULL, -- Groups the user belongs to. JSON array of strings, stored as text.
    "lastUpdated" BIGINT NULL, -- Time the user last changed. Epoch milliseconds (UTC).
    "firstName" NVARCHAR(255) NULL, -- First name of the user.
    "lastName" NVARCHAR(255) NULL, -- Last name of the user.
    layouts NVARCHAR(MAX) NULL, -- Layouts granted directly to the user. JSON array, stored as text.
    others NVARCHAR(MAX) NULL, -- Per-user timestamps that do not have a column of their own. JSON, stored as text.
    id_lower AS LOWER(id) PERSISTED, -- The identifier, lowercased, for resolving a username when username_case_sensitive is false. Derived by the database.
    CONSTRAINT users_id_unique UNIQUE (id)
);

IF NOT EXISTS (SELECT * FROM sys.indexes WHERE name = 'users__id__lower' AND object_id = OBJECT_ID('users'))
CREATE INDEX users__id__lower ON users (id_lower);

-- System configuration served to an io.Connect platform. The best-matching row for a platform version, group and user wins.
IF NOT EXISTS (SELECT * FROM sysobjects WHERE name='glue42SystemConfig' AND xtype='U')
CREATE TABLE "glue42SystemConfig"
(
    "glue42SystemConfig_id" INT IDENTITY PRIMARY KEY, -- Primary key. Auto-assigned by the database and not used to identify the row outside it.
    identifier NVARCHAR(450) NOT NULL UNIQUE, -- The platform version, group and user this row applies to. Either of the group and user may be '*' to match any. JSON object, stored as text. Unique.
    configs NVARCHAR(MAX) NOT NULL, -- The configuration files this row supplies, each keyed by file name. JSON, stored as text.
    weight DECIMAL(8, 6) NULL, -- Precedence of this row when several rows match the same platform. Higher wins.
    CONSTRAINT "glue42SystemConfig_identifier_unique" UNIQUE (identifier)
);

-- A feedback report submitted by a user from an io.Connect platform.
IF NOT EXISTS (SELECT * FROM sysobjects WHERE name='feedback' AND xtype='U')
CREATE TABLE feedback
(
    feedback_id INT IDENTITY PRIMARY KEY, -- Primary key. Auto-assigned by the database and not used to identify the row outside it.
    id NVARCHAR(255) NOT NULL, -- Public identifier of the feedback report. Unique.
    date BIGINT NOT NULL, -- Time the report was filed. Epoch milliseconds (UTC).
    "user" NVARCHAR(255) NOT NULL, -- User that filed the report.
    session NVARCHAR(255) NOT NULL, -- Session the report was filed from.
    description NVARCHAR(MAX) NOT NULL, -- Free-text description entered by the user.
    attachment NVARCHAR(MAX) NOT NULL, -- File name of the attachment. The bytes are held in the blobs table.
    reviewed BIT NULL, -- Whether an administrator has reviewed the report. Boolean, stored as 0 or 1.
    comment NVARCHAR(MAX) NULL, -- Review notes left by an administrator.
    CONSTRAINT feedback_id_unique UNIQUE (id)
);

-- A crash reported by an io.Connect platform.
IF NOT EXISTS (SELECT * FROM sysobjects WHERE name='crashes' AND xtype='U')
CREATE TABLE crashes
(
    crashes_id INT IDENTITY PRIMARY KEY, -- Primary key. Auto-assigned by the database and not used to identify the row outside it.
    id NVARCHAR(255) NOT NULL, -- Public identifier of the crash. Unique.
    date BIGINT NOT NULL, -- Time the report reached io.Manager. Epoch milliseconds (UTC).
    "user" NVARCHAR(255) NULL, -- User of the session that produced the crash.
    info NVARCHAR(MAX) NOT NULL, -- Crash details reported by the io.Connect platform. JSON, stored as text.
    reviewed BIT NULL, -- Whether an administrator has reviewed the crash. Boolean, stored as 0 or 1.
    comment NVARCHAR(MAX) NULL, -- Review notes left by an administrator.
    CONSTRAINT crashes_id_unique UNIQUE (id)
);

-- The commands one io.Connect product version supports, as reported by a session of that version.
IF NOT EXISTS (SELECT * FROM sysobjects WHERE name='commands_for_version' AND xtype='U')
CREATE TABLE commands_for_version
(
    commands_for_version_id INT IDENTITY PRIMARY KEY, -- Primary key. Auto-assigned by the database and not used to identify the row outside it.
    version NVARCHAR(255) NOT NULL, -- Version of the io.Connect product.
    commands NVARCHAR(MAX) NOT NULL, -- The supported commands and their parameters. JSON, stored as text.
    product NVARCHAR(255) NOT NULL, -- The io.Connect product. Unique together with the version.
    CONSTRAINT commands_for_version_product_version_unique UNIQUE (product, version)
);

-- A command sent to a running io.Connect platform, and its result.
IF NOT EXISTS (SELECT * FROM sysobjects WHERE name='commands' AND xtype='U')
CREATE TABLE commands
(
    commands_id INT IDENTITY PRIMARY KEY, -- Primary key. Auto-assigned by the database and not used to identify the row outside it.
    id NVARCHAR(255) NOT NULL, -- Public identifier of the command. Unique.
    "user" NVARCHAR(255) NOT NULL, -- User the command targets.
    status NVARCHAR(255) NOT NULL, -- Progress of the command, for example 'Created' or 'Executed'.
    session NVARCHAR(255) NOT NULL, -- Session the command targets.
    machine NVARCHAR(255) NOT NULL, -- Machine the command targets.
    "createdBy" NVARCHAR(255) NOT NULL, -- User that invoked the command.
    "createdAt" BIGINT NOT NULL, -- Time the command was invoked. Epoch milliseconds (UTC).
    command NVARCHAR(255) NOT NULL, -- Kind of command, for example 'GetLogs' or 'SendFeedback'.
    "commandParams" NVARCHAR(255) NULL, -- Parameters passed to the command. An empty object when the command takes none. JSON, stored as text, limited to 255 characters.
    "resultData" NVARCHAR(MAX) NULL, -- The command result, or the file name when the result is a file held in the blobs table. JSON, stored as text.
    "resultAt" BIGINT NULL, -- Time the command was executed on the target machine. Epoch milliseconds (UTC).
    "resultType" NVARCHAR(255) NULL, -- Form of the result: 'JSON' or 'file'.
    "traceContext" NVARCHAR(MAX) NULL, -- OpenTelemetry span context the command was invoked under. JSON, stored as text.
    CONSTRAINT commands_id_unique UNIQUE (id)
);

-- An audit record of one operation performed through io.Manager.
IF NOT EXISTS (SELECT * FROM sysobjects WHERE name='audit' AND xtype='U')
CREATE TABLE audit
(
    audit_id INT IDENTITY PRIMARY KEY, -- Primary key. Auto-assigned by the database and not used to identify the row outside it.
    id NVARCHAR(255) NOT NULL, -- Public identifier of the audit record. Unique.
    "user" NVARCHAR(255) NULL, -- User that triggered the operation.
    parent NVARCHAR(255) NULL, -- Audit record of the operation that caused this one, when it was not triggered directly.
    date BIGINT NOT NULL, -- Time of the operation. Epoch milliseconds (UTC).
    session NVARCHAR(255) NULL, -- Session the operation was triggered from, when it came from an io.Connect platform.
    server NVARCHAR(255) NULL, -- Unused.
    "entityType" NVARCHAR(255) NOT NULL, -- Kind of entity the operation acted on, for example 'application' or 'layout'.
    "entityId" NVARCHAR(255) NULL, -- Identifier of the entity the operation acted on.
    "entityDisplayName" NVARCHAR(255) NULL, -- Display name of that entity.
    operation NVARCHAR(255) NOT NULL, -- The operation performed, for example 'create' or 'delete'.
    "operationComment" NVARCHAR(255) NULL, -- Note clarifying the operation, typically what it contributed to its parent operation.
    "newValue" NVARCHAR(MAX) NULL, -- The entity after the operation. JSON, stored as text.
    "oldValue" NVARCHAR(MAX) NULL, -- The entity before the operation. JSON, stored as text.
    request NVARCHAR(MAX) NULL, -- The request that triggered the operation. JSON, stored as text.
    response NVARCHAR(MAX) NULL, -- Unused. JSON, stored as text.
    CONSTRAINT audit_id_unique UNIQUE (id)
);

-- An application io.Manager serves to io.Connect platforms.
IF NOT EXISTS (SELECT * FROM sysobjects WHERE name='app' AND xtype='U')
CREATE TABLE app
(
    app_id INT IDENTITY PRIMARY KEY, -- Primary key. Auto-assigned by the database and not used to identify the row outside it.
    name NVARCHAR(255) NOT NULL, -- Name of the application. Unique.
    disabled BIT NOT NULL, -- Whether the application is withheld from all users. Boolean, stored as 0 or 1.
    definition NVARCHAR(MAX) NOT NULL, -- The application definition served to the io.Connect platform. JSON, stored as text.
    "isPublic" BIT NOT NULL, -- Whether every user can access the application. Boolean, stored as 0 or 1.
    "accessList" NVARCHAR(MAX) NULL, -- Groups allowed to access the application when it is not public. JSON array of strings, stored as text.
    "createdBy" NVARCHAR(255) NOT NULL, -- User that created the application.
    "createdOn" BIGINT NOT NULL, -- Creation time. Epoch milliseconds (UTC).
    "lastModifiedBy" NVARCHAR(255) NULL, -- User that last modified the application.
    "lastModifiedOn" BIGINT NULL, -- Time of the last modification. Epoch milliseconds (UTC).
    CONSTRAINT app_name_unique UNIQUE (name)
);

-- Binary content held for another entity, such as a feedback attachment or a command result file.
IF NOT EXISTS (SELECT * FROM sysobjects WHERE name='blobs' AND xtype='U')
CREATE TABLE blobs
(
    blobs_id INT IDENTITY PRIMARY KEY, -- Primary key. Auto-assigned by the database and not used to identify the row outside it.
    type NVARCHAR(255) NOT NULL, -- Kind of entity the content belongs to, which together with the id locates it.
    id NVARCHAR(255) NOT NULL, -- Identifier of the entity the content belongs to. Unique.
    "fileName" NVARCHAR(255) NOT NULL, -- File name to serve the content under.
    data VARBINARY(MAX) NOT NULL, -- The content itself. Binary content.
    encoding NVARCHAR(20) NULL, -- Encoding of the content: "raw" when it is the file's own bytes, "base64" when it is those bytes base64-encoded as text. Absent on content stored before the encoding was recorded.
    CONSTRAINT blobs_id_unique UNIQUE (id)
);

-- Layouts saved by an io.Connect platform, with per-entity access control.
IF NOT EXISTS (SELECT * FROM sysobjects WHERE name='layouts_advanced' AND xtype='U')
CREATE TABLE layouts_advanced
(
    layouts_advanced_id INT IDENTITY PRIMARY KEY, -- Primary key. Auto-assigned by the database and not used to identify the row outside it.
    id NVARCHAR(255) NOT NULL, -- Public identifier of the layout. Unique.
    type NVARCHAR(255) NOT NULL, -- Kind of layout, for example 'Global' or 'Workspace'.
    name NVARCHAR(255) NOT NULL, -- Name of the layout. Unique per type per owner while private, and unique per type across all owners once shared or public.
    disabled BIT NOT NULL, -- Whether the layout is withheld from all users. Boolean, stored as 0 or 1.
    accessLevel NVARCHAR(255) NOT NULL, -- Who can reach the layout: 'private', 'shared' or 'public'.
    owner NVARCHAR(255) NOT NULL, -- User the layout belongs to.
    "accessInfo" NVARCHAR(MAX) NULL, -- Users and groups granted access, with the level granted to each. Present only for shared layouts. JSON, stored as text.
    definition NVARCHAR(MAX) NOT NULL, -- The layout payload as saved by the io.Connect platform. JSON, stored as text.
    "createdBy" NVARCHAR(255) NOT NULL, -- User that created the layout.
    "createdOn" BIGINT NOT NULL, -- Creation time. Epoch milliseconds (UTC).
    "lastModifiedBy" NVARCHAR(255) NULL, -- User that last modified the layout.
    "lastModifiedOn" BIGINT NULL, -- Time of the last modification. Epoch milliseconds (UTC).
    CONSTRAINT layouts_advanced_id_unique UNIQUE (id)
);

IF NOT EXISTS (SELECT * FROM sys.indexes WHERE name = 'layouts_advanced__name__type__owner__unique' AND object_id = OBJECT_ID('layouts_advanced'))
CREATE UNIQUE INDEX layouts_advanced__name__type__owner__unique
    ON layouts_advanced (name, type, owner)
    WHERE accessLevel = 'private';

IF NOT EXISTS (SELECT * FROM sys.indexes WHERE name = 'layouts_advanced__name__type__nonprivate__unique' AND object_id = OBJECT_ID('layouts_advanced'))
CREATE UNIQUE INDEX layouts_advanced__name__type__nonprivate__unique
    ON layouts_advanced (name, type)
    WHERE accessLevel IN ('shared', 'public');

-- Server-wide state shared by every io.Manager instance, one row per named entry.
IF NOT EXISTS (SELECT * FROM sysobjects WHERE name='system_state' AND xtype='U')
CREATE TABLE system_state
(
    system_state_id INT IDENTITY PRIMARY KEY, -- Primary key. Auto-assigned by the database and not used to identify the row outside it.
    name NVARCHAR(255) NOT NULL, -- Name of the entry. Unique.
    value NVARCHAR(MAX) NOT NULL, -- The entry value. JSON, stored as text.
    version INT NOT NULL, -- Revision of the entry, raised on every write so concurrent instances cannot overwrite each other.
    CONSTRAINT system_state_name_unique UNIQUE (name)
);

-- Object documentation. Every table and column carries a description readable from the
-- database itself.

-- groups
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'groups', DEFAULT, DEFAULT))
    EXEC sp_addextendedproperty 'MS_Description', 'A group of users. Groups are used to grant access to applications and layouts.', 'SCHEMA', 'dbo', 'TABLE', 'groups';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'groups', 'COLUMN', 'groups_id'))
    EXEC sp_addextendedproperty 'MS_Description', 'Primary key. Auto-assigned by the database and not used to identify the row outside it.', 'SCHEMA', 'dbo', 'TABLE', 'groups', 'COLUMN', 'groups_id';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'groups', 'COLUMN', 'name'))
    EXEC sp_addextendedproperty 'MS_Description', 'Name of the group. Unique.', 'SCHEMA', 'dbo', 'TABLE', 'groups', 'COLUMN', 'name';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'groups', 'COLUMN', 'description'))
    EXEC sp_addextendedproperty 'MS_Description', 'Description of the group.', 'SCHEMA', 'dbo', 'TABLE', 'groups', 'COLUMN', 'description';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'groups', 'COLUMN', 'expandsTo'))
    EXEC sp_addextendedproperty 'MS_Description', 'Groups this group also grants membership of. Empty when it expands into no others. JSON array of strings, stored as text.', 'SCHEMA', 'dbo', 'TABLE', 'groups', 'COLUMN', 'expandsTo';

-- last_updated
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'last_updated', DEFAULT, DEFAULT))
    EXEC sp_addextendedproperty 'MS_Description', 'A single row of change timestamps. An io.Connect platform polls these to learn whether it needs to re-fetch a kind of data.', 'SCHEMA', 'dbo', 'TABLE', 'last_updated';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'last_updated', 'COLUMN', 'last_updated_id'))
    EXEC sp_addextendedproperty 'MS_Description', 'Primary key. Auto-assigned by the database and not used to identify the row outside it.', 'SCHEMA', 'dbo', 'TABLE', 'last_updated', 'COLUMN', 'last_updated_id';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'last_updated', 'COLUMN', 'applications'))
    EXEC sp_addextendedproperty 'MS_Description', 'Time any application last changed. Epoch milliseconds (UTC).', 'SCHEMA', 'dbo', 'TABLE', 'last_updated', 'COLUMN', 'applications';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'last_updated', 'COLUMN', 'layouts'))
    EXEC sp_addextendedproperty 'MS_Description', 'Time any layout last changed. Epoch milliseconds (UTC).', 'SCHEMA', 'dbo', 'TABLE', 'last_updated', 'COLUMN', 'layouts';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'last_updated', 'COLUMN', 'groups'))
    EXEC sp_addextendedproperty 'MS_Description', 'Unused. Epoch milliseconds (UTC).', 'SCHEMA', 'dbo', 'TABLE', 'last_updated', 'COLUMN', 'groups';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'last_updated', 'COLUMN', 'commands'))
    EXEC sp_addextendedproperty 'MS_Description', 'Time any command last changed. Epoch milliseconds (UTC).', 'SCHEMA', 'dbo', 'TABLE', 'last_updated', 'COLUMN', 'commands';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'last_updated', 'COLUMN', 'configs'))
    EXEC sp_addextendedproperty 'MS_Description', 'Time any system config last changed. Epoch milliseconds (UTC).', 'SCHEMA', 'dbo', 'TABLE', 'last_updated', 'COLUMN', 'configs';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'last_updated', 'COLUMN', 'others'))
    EXEC sp_addextendedproperty 'MS_Description', 'Further change timestamps that do not have a column of their own. JSON, stored as text.', 'SCHEMA', 'dbo', 'TABLE', 'last_updated', 'COLUMN', 'others';

-- layouts
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'layouts', DEFAULT, DEFAULT))
    EXEC sp_addextendedproperty 'MS_Description', 'Layouts saved by an io.Connect platform.', 'SCHEMA', 'dbo', 'TABLE', 'layouts';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'layouts', 'COLUMN', 'layouts_id'))
    EXEC sp_addextendedproperty 'MS_Description', 'Primary key. Auto-assigned by the database and not used to identify the row outside it.', 'SCHEMA', 'dbo', 'TABLE', 'layouts', 'COLUMN', 'layouts_id';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'layouts', 'COLUMN', 'id'))
    EXEC sp_addextendedproperty 'MS_Description', 'Public identifier of the layout. Unique.', 'SCHEMA', 'dbo', 'TABLE', 'layouts', 'COLUMN', 'id';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'layouts', 'COLUMN', 'type'))
    EXEC sp_addextendedproperty 'MS_Description', 'Kind of layout, for example ''Global'' or ''Workspace''.', 'SCHEMA', 'dbo', 'TABLE', 'layouts', 'COLUMN', 'type';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'layouts', 'COLUMN', 'name'))
    EXEC sp_addextendedproperty 'MS_Description', 'Name of the layout. Unique per type per owner.', 'SCHEMA', 'dbo', 'TABLE', 'layouts', 'COLUMN', 'name';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'layouts', 'COLUMN', 'owner'))
    EXEC sp_addextendedproperty 'MS_Description', 'User the layout is private to. The literal ''*'' marks a common layout, whose visibility is then decided by the isPublic and accessList columns.', 'SCHEMA', 'dbo', 'TABLE', 'layouts', 'COLUMN', 'owner';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'layouts', 'COLUMN', 'isPublic'))
    EXEC sp_addextendedproperty 'MS_Description', 'Whether every user can read the layout. Applies only to common layouts. Boolean, stored as 0 or 1.', 'SCHEMA', 'dbo', 'TABLE', 'layouts', 'COLUMN', 'isPublic';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'layouts', 'COLUMN', 'disabled'))
    EXEC sp_addextendedproperty 'MS_Description', 'Whether the layout is withheld from all users. Boolean, stored as 0 or 1.', 'SCHEMA', 'dbo', 'TABLE', 'layouts', 'COLUMN', 'disabled';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'layouts', 'COLUMN', 'accessList'))
    EXEC sp_addextendedproperty 'MS_Description', 'Groups allowed to read the layout when it is common and not public. JSON array of strings, stored as text.', 'SCHEMA', 'dbo', 'TABLE', 'layouts', 'COLUMN', 'accessList';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'layouts', 'COLUMN', 'definition'))
    EXEC sp_addextendedproperty 'MS_Description', 'The layout payload as saved by the io.Connect platform. JSON, stored as text.', 'SCHEMA', 'dbo', 'TABLE', 'layouts', 'COLUMN', 'definition';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'layouts', 'COLUMN', 'createdBy'))
    EXEC sp_addextendedproperty 'MS_Description', 'User that created the layout.', 'SCHEMA', 'dbo', 'TABLE', 'layouts', 'COLUMN', 'createdBy';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'layouts', 'COLUMN', 'createdOn'))
    EXEC sp_addextendedproperty 'MS_Description', 'Creation time. Epoch milliseconds (UTC).', 'SCHEMA', 'dbo', 'TABLE', 'layouts', 'COLUMN', 'createdOn';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'layouts', 'COLUMN', 'lastModifiedBy'))
    EXEC sp_addextendedproperty 'MS_Description', 'User that last modified the layout.', 'SCHEMA', 'dbo', 'TABLE', 'layouts', 'COLUMN', 'lastModifiedBy';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'layouts', 'COLUMN', 'lastModifiedOn'))
    EXEC sp_addextendedproperty 'MS_Description', 'Time of the last modification. Epoch milliseconds (UTC).', 'SCHEMA', 'dbo', 'TABLE', 'layouts', 'COLUMN', 'lastModifiedOn';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'layouts', 'COLUMN', 'isDefault'))
    EXEC sp_addextendedproperty 'MS_Description', 'Whether the layout is restored when an io.Connect platform starts. Boolean, stored as 0 or 1.', 'SCHEMA', 'dbo', 'TABLE', 'layouts', 'COLUMN', 'isDefault';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'layouts', 'COLUMN', 'migrated'))
    EXEC sp_addextendedproperty 'MS_Description', 'Whether this layout has already been migrated to the layouts_advanced table. Boolean, stored as 0 or 1.', 'SCHEMA', 'dbo', 'TABLE', 'layouts', 'COLUMN', 'migrated';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'layouts', 'COLUMN', 'owner_lower'))
    EXEC sp_addextendedproperty 'MS_Description', 'The owner, lowercased, for resolving a username when username_case_sensitive is false. Derived by the database.', 'SCHEMA', 'dbo', 'TABLE', 'layouts', 'COLUMN', 'owner_lower';

-- machines
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'machines', DEFAULT, DEFAULT))
    EXEC sp_addextendedproperty 'MS_Description', 'A machine an io.Connect platform has run on. Referenced by sessions.', 'SCHEMA', 'dbo', 'TABLE', 'machines';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'machines', 'COLUMN', 'machines_id'))
    EXEC sp_addextendedproperty 'MS_Description', 'Primary key. Auto-assigned by the database and not used to identify the row outside it.', 'SCHEMA', 'dbo', 'TABLE', 'machines', 'COLUMN', 'machines_id';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'machines', 'COLUMN', 'id'))
    EXEC sp_addextendedproperty 'MS_Description', 'Public identifier of the machine. Unique.', 'SCHEMA', 'dbo', 'TABLE', 'machines', 'COLUMN', 'id';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'machines', 'COLUMN', 'user'))
    EXEC sp_addextendedproperty 'MS_Description', 'User the machine is associated with.', 'SCHEMA', 'dbo', 'TABLE', 'machines', 'COLUMN', 'user';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'machines', 'COLUMN', 'os'))
    EXEC sp_addextendedproperty 'MS_Description', 'Operating-system details reported by the machine. JSON, stored as text.', 'SCHEMA', 'dbo', 'TABLE', 'machines', 'COLUMN', 'os';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'machines', 'COLUMN', 'name'))
    EXEC sp_addextendedproperty 'MS_Description', 'Host name of the machine.', 'SCHEMA', 'dbo', 'TABLE', 'machines', 'COLUMN', 'name';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'machines', 'COLUMN', 'displays'))
    EXEC sp_addextendedproperty 'MS_Description', 'Displays attached to the machine. JSON, stored as text.', 'SCHEMA', 'dbo', 'TABLE', 'machines', 'COLUMN', 'displays';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'machines', 'COLUMN', 'browser'))
    EXEC sp_addextendedproperty 'MS_Description', 'Browser details. Populated for io.Connect Browser sessions only. JSON, stored as text.', 'SCHEMA', 'dbo', 'TABLE', 'machines', 'COLUMN', 'browser';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'machines', 'COLUMN', 'user_lower'))
    EXEC sp_addextendedproperty 'MS_Description', 'The user, lowercased, for resolving a username when username_case_sensitive is false. Derived by the database.', 'SCHEMA', 'dbo', 'TABLE', 'machines', 'COLUMN', 'user_lower';

-- prefs
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'prefs', DEFAULT, DEFAULT))
    EXEC sp_addextendedproperty 'MS_Description', 'Per-user, per-application preferences. One row per application and user.', 'SCHEMA', 'dbo', 'TABLE', 'prefs';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'prefs', 'COLUMN', 'prefs_id'))
    EXEC sp_addextendedproperty 'MS_Description', 'Primary key. Auto-assigned by the database and not used to identify the row outside it.', 'SCHEMA', 'dbo', 'TABLE', 'prefs', 'COLUMN', 'prefs_id';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'prefs', 'COLUMN', 'id'))
    EXEC sp_addextendedproperty 'MS_Description', 'Public identifier of the preference. Unique.', 'SCHEMA', 'dbo', 'TABLE', 'prefs', 'COLUMN', 'id';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'prefs', 'COLUMN', 'app'))
    EXEC sp_addextendedproperty 'MS_Description', 'Application the preference belongs to.', 'SCHEMA', 'dbo', 'TABLE', 'prefs', 'COLUMN', 'app';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'prefs', 'COLUMN', 'user'))
    EXEC sp_addextendedproperty 'MS_Description', 'User the preference belongs to.', 'SCHEMA', 'dbo', 'TABLE', 'prefs', 'COLUMN', 'user';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'prefs', 'COLUMN', 'data'))
    EXEC sp_addextendedproperty 'MS_Description', 'The preference payload. Arbitrary data. JSON, stored as text.', 'SCHEMA', 'dbo', 'TABLE', 'prefs', 'COLUMN', 'data';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'prefs', 'COLUMN', 'lastUpdate'))
    EXEC sp_addextendedproperty 'MS_Description', 'Time of the last update. Epoch milliseconds (UTC).', 'SCHEMA', 'dbo', 'TABLE', 'prefs', 'COLUMN', 'lastUpdate';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'prefs', 'COLUMN', 'user_lower'))
    EXEC sp_addextendedproperty 'MS_Description', 'The user, lowercased, for resolving a username when username_case_sensitive is false. Derived by the database.', 'SCHEMA', 'dbo', 'TABLE', 'prefs', 'COLUMN', 'user_lower';

-- sessions
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'sessions', DEFAULT, DEFAULT))
    EXEC sp_addextendedproperty 'MS_Description', 'A run of an io.Connect platform by one user on one machine.', 'SCHEMA', 'dbo', 'TABLE', 'sessions';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'sessions', 'COLUMN', 'sessions_id'))
    EXEC sp_addextendedproperty 'MS_Description', 'Primary key. Auto-assigned by the database and not used to identify the row outside it.', 'SCHEMA', 'dbo', 'TABLE', 'sessions', 'COLUMN', 'sessions_id';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'sessions', 'COLUMN', 'id'))
    EXEC sp_addextendedproperty 'MS_Description', 'Public identifier of the session. Unique.', 'SCHEMA', 'dbo', 'TABLE', 'sessions', 'COLUMN', 'id';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'sessions', 'COLUMN', 'machine'))
    EXEC sp_addextendedproperty 'MS_Description', 'Machine the session ran on.', 'SCHEMA', 'dbo', 'TABLE', 'sessions', 'COLUMN', 'machine';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'sessions', 'COLUMN', 'start'))
    EXEC sp_addextendedproperty 'MS_Description', 'Time the session started. Epoch milliseconds (UTC).', 'SCHEMA', 'dbo', 'TABLE', 'sessions', 'COLUMN', 'start';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'sessions', 'COLUMN', 'user'))
    EXEC sp_addextendedproperty 'MS_Description', 'User that opened the session.', 'SCHEMA', 'dbo', 'TABLE', 'sessions', 'COLUMN', 'user';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'sessions', 'COLUMN', 'glue'))
    EXEC sp_addextendedproperty 'MS_Description', 'io.Connect platform details reported for the session. JSON, stored as text.', 'SCHEMA', 'dbo', 'TABLE', 'sessions', 'COLUMN', 'glue';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'sessions', 'COLUMN', 'lastDataFetch'))
    EXEC sp_addextendedproperty 'MS_Description', 'Time the client last requested data from the server. Used to purge inactive sessions. Epoch milliseconds (UTC).', 'SCHEMA', 'dbo', 'TABLE', 'sessions', 'COLUMN', 'lastDataFetch';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'sessions', 'COLUMN', 'end'))
    EXEC sp_addextendedproperty 'MS_Description', 'Time the session was closed. Epoch milliseconds (UTC).', 'SCHEMA', 'dbo', 'TABLE', 'sessions', 'COLUMN', 'end';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'sessions', 'COLUMN', 'closed'))
    EXEC sp_addextendedproperty 'MS_Description', 'Whether the session has been closed. Boolean, stored as 0 or 1.', 'SCHEMA', 'dbo', 'TABLE', 'sessions', 'COLUMN', 'closed';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'sessions', 'COLUMN', 'closeReason'))
    EXEC sp_addextendedproperty 'MS_Description', 'Why the session was closed. ''clean'' when an administrator closed it.', 'SCHEMA', 'dbo', 'TABLE', 'sessions', 'COLUMN', 'closeReason';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'sessions', 'COLUMN', 'product'))
    EXEC sp_addextendedproperty 'MS_Description', 'The io.Connect product that opened the session.', 'SCHEMA', 'dbo', 'TABLE', 'sessions', 'COLUMN', 'product';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'sessions', 'COLUMN', 'productVersion'))
    EXEC sp_addextendedproperty 'MS_Description', 'Version of that product. Determines which commands the session supports.', 'SCHEMA', 'dbo', 'TABLE', 'sessions', 'COLUMN', 'productVersion';

-- users
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'users', DEFAULT, DEFAULT))
    EXEC sp_addextendedproperty 'MS_Description', 'A user known to io.Manager.', 'SCHEMA', 'dbo', 'TABLE', 'users';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'users', 'COLUMN', 'users_id'))
    EXEC sp_addextendedproperty 'MS_Description', 'Primary key. Auto-assigned by the database and not used to identify the row outside it.', 'SCHEMA', 'dbo', 'TABLE', 'users', 'COLUMN', 'users_id';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'users', 'COLUMN', 'id'))
    EXEC sp_addextendedproperty 'MS_Description', 'Identifier of the user. Unique.', 'SCHEMA', 'dbo', 'TABLE', 'users', 'COLUMN', 'id';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'users', 'COLUMN', 'email'))
    EXEC sp_addextendedproperty 'MS_Description', 'Email address of the user.', 'SCHEMA', 'dbo', 'TABLE', 'users', 'COLUMN', 'email';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'users', 'COLUMN', 'password'))
    EXEC sp_addextendedproperty 'MS_Description', 'A bcrypt hash of the user''s password. Populated only when basic authentication is enabled.', 'SCHEMA', 'dbo', 'TABLE', 'users', 'COLUMN', 'password';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'users', 'COLUMN', 'apps'))
    EXEC sp_addextendedproperty 'MS_Description', 'Applications granted directly to the user. JSON array of strings, stored as text.', 'SCHEMA', 'dbo', 'TABLE', 'users', 'COLUMN', 'apps';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'users', 'COLUMN', 'groups'))
    EXEC sp_addextendedproperty 'MS_Description', 'Groups the user belongs to. JSON array of strings, stored as text.', 'SCHEMA', 'dbo', 'TABLE', 'users', 'COLUMN', 'groups';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'users', 'COLUMN', 'lastUpdated'))
    EXEC sp_addextendedproperty 'MS_Description', 'Time the user last changed. Epoch milliseconds (UTC).', 'SCHEMA', 'dbo', 'TABLE', 'users', 'COLUMN', 'lastUpdated';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'users', 'COLUMN', 'firstName'))
    EXEC sp_addextendedproperty 'MS_Description', 'First name of the user.', 'SCHEMA', 'dbo', 'TABLE', 'users', 'COLUMN', 'firstName';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'users', 'COLUMN', 'lastName'))
    EXEC sp_addextendedproperty 'MS_Description', 'Last name of the user.', 'SCHEMA', 'dbo', 'TABLE', 'users', 'COLUMN', 'lastName';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'users', 'COLUMN', 'layouts'))
    EXEC sp_addextendedproperty 'MS_Description', 'Layouts granted directly to the user. JSON array, stored as text.', 'SCHEMA', 'dbo', 'TABLE', 'users', 'COLUMN', 'layouts';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'users', 'COLUMN', 'others'))
    EXEC sp_addextendedproperty 'MS_Description', 'Per-user timestamps that do not have a column of their own. JSON, stored as text.', 'SCHEMA', 'dbo', 'TABLE', 'users', 'COLUMN', 'others';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'users', 'COLUMN', 'id_lower'))
    EXEC sp_addextendedproperty 'MS_Description', 'The identifier, lowercased, for resolving a username when username_case_sensitive is false. Derived by the database.', 'SCHEMA', 'dbo', 'TABLE', 'users', 'COLUMN', 'id_lower';

-- glue42SystemConfig
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'glue42SystemConfig', DEFAULT, DEFAULT))
    EXEC sp_addextendedproperty 'MS_Description', 'System configuration served to an io.Connect platform. The best-matching row for a platform version, group and user wins.', 'SCHEMA', 'dbo', 'TABLE', 'glue42SystemConfig';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'glue42SystemConfig', 'COLUMN', 'glue42SystemConfig_id'))
    EXEC sp_addextendedproperty 'MS_Description', 'Primary key. Auto-assigned by the database and not used to identify the row outside it.', 'SCHEMA', 'dbo', 'TABLE', 'glue42SystemConfig', 'COLUMN', 'glue42SystemConfig_id';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'glue42SystemConfig', 'COLUMN', 'identifier'))
    EXEC sp_addextendedproperty 'MS_Description', 'The platform version, group and user this row applies to. Either of the group and user may be ''*'' to match any. JSON object, stored as text. Unique.', 'SCHEMA', 'dbo', 'TABLE', 'glue42SystemConfig', 'COLUMN', 'identifier';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'glue42SystemConfig', 'COLUMN', 'configs'))
    EXEC sp_addextendedproperty 'MS_Description', 'The configuration files this row supplies, each keyed by file name. JSON, stored as text.', 'SCHEMA', 'dbo', 'TABLE', 'glue42SystemConfig', 'COLUMN', 'configs';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'glue42SystemConfig', 'COLUMN', 'weight'))
    EXEC sp_addextendedproperty 'MS_Description', 'Precedence of this row when several rows match the same platform. Higher wins.', 'SCHEMA', 'dbo', 'TABLE', 'glue42SystemConfig', 'COLUMN', 'weight';

-- feedback
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'feedback', DEFAULT, DEFAULT))
    EXEC sp_addextendedproperty 'MS_Description', 'A feedback report submitted by a user from an io.Connect platform.', 'SCHEMA', 'dbo', 'TABLE', 'feedback';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'feedback', 'COLUMN', 'feedback_id'))
    EXEC sp_addextendedproperty 'MS_Description', 'Primary key. Auto-assigned by the database and not used to identify the row outside it.', 'SCHEMA', 'dbo', 'TABLE', 'feedback', 'COLUMN', 'feedback_id';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'feedback', 'COLUMN', 'id'))
    EXEC sp_addextendedproperty 'MS_Description', 'Public identifier of the feedback report. Unique.', 'SCHEMA', 'dbo', 'TABLE', 'feedback', 'COLUMN', 'id';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'feedback', 'COLUMN', 'date'))
    EXEC sp_addextendedproperty 'MS_Description', 'Time the report was filed. Epoch milliseconds (UTC).', 'SCHEMA', 'dbo', 'TABLE', 'feedback', 'COLUMN', 'date';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'feedback', 'COLUMN', 'user'))
    EXEC sp_addextendedproperty 'MS_Description', 'User that filed the report.', 'SCHEMA', 'dbo', 'TABLE', 'feedback', 'COLUMN', 'user';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'feedback', 'COLUMN', 'session'))
    EXEC sp_addextendedproperty 'MS_Description', 'Session the report was filed from.', 'SCHEMA', 'dbo', 'TABLE', 'feedback', 'COLUMN', 'session';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'feedback', 'COLUMN', 'description'))
    EXEC sp_addextendedproperty 'MS_Description', 'Free-text description entered by the user.', 'SCHEMA', 'dbo', 'TABLE', 'feedback', 'COLUMN', 'description';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'feedback', 'COLUMN', 'attachment'))
    EXEC sp_addextendedproperty 'MS_Description', 'File name of the attachment. The bytes are held in the blobs table.', 'SCHEMA', 'dbo', 'TABLE', 'feedback', 'COLUMN', 'attachment';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'feedback', 'COLUMN', 'reviewed'))
    EXEC sp_addextendedproperty 'MS_Description', 'Whether an administrator has reviewed the report. Boolean, stored as 0 or 1.', 'SCHEMA', 'dbo', 'TABLE', 'feedback', 'COLUMN', 'reviewed';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'feedback', 'COLUMN', 'comment'))
    EXEC sp_addextendedproperty 'MS_Description', 'Review notes left by an administrator.', 'SCHEMA', 'dbo', 'TABLE', 'feedback', 'COLUMN', 'comment';

-- crashes
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'crashes', DEFAULT, DEFAULT))
    EXEC sp_addextendedproperty 'MS_Description', 'A crash reported by an io.Connect platform.', 'SCHEMA', 'dbo', 'TABLE', 'crashes';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'crashes', 'COLUMN', 'crashes_id'))
    EXEC sp_addextendedproperty 'MS_Description', 'Primary key. Auto-assigned by the database and not used to identify the row outside it.', 'SCHEMA', 'dbo', 'TABLE', 'crashes', 'COLUMN', 'crashes_id';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'crashes', 'COLUMN', 'id'))
    EXEC sp_addextendedproperty 'MS_Description', 'Public identifier of the crash. Unique.', 'SCHEMA', 'dbo', 'TABLE', 'crashes', 'COLUMN', 'id';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'crashes', 'COLUMN', 'date'))
    EXEC sp_addextendedproperty 'MS_Description', 'Time the report reached io.Manager. Epoch milliseconds (UTC).', 'SCHEMA', 'dbo', 'TABLE', 'crashes', 'COLUMN', 'date';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'crashes', 'COLUMN', 'user'))
    EXEC sp_addextendedproperty 'MS_Description', 'User of the session that produced the crash.', 'SCHEMA', 'dbo', 'TABLE', 'crashes', 'COLUMN', 'user';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'crashes', 'COLUMN', 'info'))
    EXEC sp_addextendedproperty 'MS_Description', 'Crash details reported by the io.Connect platform. JSON, stored as text.', 'SCHEMA', 'dbo', 'TABLE', 'crashes', 'COLUMN', 'info';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'crashes', 'COLUMN', 'reviewed'))
    EXEC sp_addextendedproperty 'MS_Description', 'Whether an administrator has reviewed the crash. Boolean, stored as 0 or 1.', 'SCHEMA', 'dbo', 'TABLE', 'crashes', 'COLUMN', 'reviewed';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'crashes', 'COLUMN', 'comment'))
    EXEC sp_addextendedproperty 'MS_Description', 'Review notes left by an administrator.', 'SCHEMA', 'dbo', 'TABLE', 'crashes', 'COLUMN', 'comment';

-- commands_for_version
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'commands_for_version', DEFAULT, DEFAULT))
    EXEC sp_addextendedproperty 'MS_Description', 'The commands one io.Connect product version supports, as reported by a session of that version.', 'SCHEMA', 'dbo', 'TABLE', 'commands_for_version';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'commands_for_version', 'COLUMN', 'commands_for_version_id'))
    EXEC sp_addextendedproperty 'MS_Description', 'Primary key. Auto-assigned by the database and not used to identify the row outside it.', 'SCHEMA', 'dbo', 'TABLE', 'commands_for_version', 'COLUMN', 'commands_for_version_id';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'commands_for_version', 'COLUMN', 'version'))
    EXEC sp_addextendedproperty 'MS_Description', 'Version of the io.Connect product.', 'SCHEMA', 'dbo', 'TABLE', 'commands_for_version', 'COLUMN', 'version';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'commands_for_version', 'COLUMN', 'commands'))
    EXEC sp_addextendedproperty 'MS_Description', 'The supported commands and their parameters. JSON, stored as text.', 'SCHEMA', 'dbo', 'TABLE', 'commands_for_version', 'COLUMN', 'commands';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'commands_for_version', 'COLUMN', 'product'))
    EXEC sp_addextendedproperty 'MS_Description', 'The io.Connect product. Unique together with the version.', 'SCHEMA', 'dbo', 'TABLE', 'commands_for_version', 'COLUMN', 'product';

-- commands
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'commands', DEFAULT, DEFAULT))
    EXEC sp_addextendedproperty 'MS_Description', 'A command sent to a running io.Connect platform, and its result.', 'SCHEMA', 'dbo', 'TABLE', 'commands';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'commands', 'COLUMN', 'commands_id'))
    EXEC sp_addextendedproperty 'MS_Description', 'Primary key. Auto-assigned by the database and not used to identify the row outside it.', 'SCHEMA', 'dbo', 'TABLE', 'commands', 'COLUMN', 'commands_id';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'commands', 'COLUMN', 'id'))
    EXEC sp_addextendedproperty 'MS_Description', 'Public identifier of the command. Unique.', 'SCHEMA', 'dbo', 'TABLE', 'commands', 'COLUMN', 'id';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'commands', 'COLUMN', 'user'))
    EXEC sp_addextendedproperty 'MS_Description', 'User the command targets.', 'SCHEMA', 'dbo', 'TABLE', 'commands', 'COLUMN', 'user';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'commands', 'COLUMN', 'status'))
    EXEC sp_addextendedproperty 'MS_Description', 'Progress of the command, for example ''Created'' or ''Executed''.', 'SCHEMA', 'dbo', 'TABLE', 'commands', 'COLUMN', 'status';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'commands', 'COLUMN', 'session'))
    EXEC sp_addextendedproperty 'MS_Description', 'Session the command targets.', 'SCHEMA', 'dbo', 'TABLE', 'commands', 'COLUMN', 'session';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'commands', 'COLUMN', 'machine'))
    EXEC sp_addextendedproperty 'MS_Description', 'Machine the command targets.', 'SCHEMA', 'dbo', 'TABLE', 'commands', 'COLUMN', 'machine';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'commands', 'COLUMN', 'createdBy'))
    EXEC sp_addextendedproperty 'MS_Description', 'User that invoked the command.', 'SCHEMA', 'dbo', 'TABLE', 'commands', 'COLUMN', 'createdBy';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'commands', 'COLUMN', 'createdAt'))
    EXEC sp_addextendedproperty 'MS_Description', 'Time the command was invoked. Epoch milliseconds (UTC).', 'SCHEMA', 'dbo', 'TABLE', 'commands', 'COLUMN', 'createdAt';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'commands', 'COLUMN', 'command'))
    EXEC sp_addextendedproperty 'MS_Description', 'Kind of command, for example ''GetLogs'' or ''SendFeedback''.', 'SCHEMA', 'dbo', 'TABLE', 'commands', 'COLUMN', 'command';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'commands', 'COLUMN', 'commandParams'))
    EXEC sp_addextendedproperty 'MS_Description', 'Parameters passed to the command. An empty object when the command takes none. JSON, stored as text, limited to 255 characters.', 'SCHEMA', 'dbo', 'TABLE', 'commands', 'COLUMN', 'commandParams';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'commands', 'COLUMN', 'resultData'))
    EXEC sp_addextendedproperty 'MS_Description', 'The command result, or the file name when the result is a file held in the blobs table. JSON, stored as text.', 'SCHEMA', 'dbo', 'TABLE', 'commands', 'COLUMN', 'resultData';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'commands', 'COLUMN', 'resultAt'))
    EXEC sp_addextendedproperty 'MS_Description', 'Time the command was executed on the target machine. Epoch milliseconds (UTC).', 'SCHEMA', 'dbo', 'TABLE', 'commands', 'COLUMN', 'resultAt';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'commands', 'COLUMN', 'resultType'))
    EXEC sp_addextendedproperty 'MS_Description', 'Form of the result: ''JSON'' or ''file''.', 'SCHEMA', 'dbo', 'TABLE', 'commands', 'COLUMN', 'resultType';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'commands', 'COLUMN', 'traceContext'))
    EXEC sp_addextendedproperty 'MS_Description', 'OpenTelemetry span context the command was invoked under. JSON, stored as text.', 'SCHEMA', 'dbo', 'TABLE', 'commands', 'COLUMN', 'traceContext';

-- audit
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'audit', DEFAULT, DEFAULT))
    EXEC sp_addextendedproperty 'MS_Description', 'An audit record of one operation performed through io.Manager.', 'SCHEMA', 'dbo', 'TABLE', 'audit';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'audit', 'COLUMN', 'audit_id'))
    EXEC sp_addextendedproperty 'MS_Description', 'Primary key. Auto-assigned by the database and not used to identify the row outside it.', 'SCHEMA', 'dbo', 'TABLE', 'audit', 'COLUMN', 'audit_id';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'audit', 'COLUMN', 'id'))
    EXEC sp_addextendedproperty 'MS_Description', 'Public identifier of the audit record. Unique.', 'SCHEMA', 'dbo', 'TABLE', 'audit', 'COLUMN', 'id';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'audit', 'COLUMN', 'user'))
    EXEC sp_addextendedproperty 'MS_Description', 'User that triggered the operation.', 'SCHEMA', 'dbo', 'TABLE', 'audit', 'COLUMN', 'user';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'audit', 'COLUMN', 'parent'))
    EXEC sp_addextendedproperty 'MS_Description', 'Audit record of the operation that caused this one, when it was not triggered directly.', 'SCHEMA', 'dbo', 'TABLE', 'audit', 'COLUMN', 'parent';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'audit', 'COLUMN', 'date'))
    EXEC sp_addextendedproperty 'MS_Description', 'Time of the operation. Epoch milliseconds (UTC).', 'SCHEMA', 'dbo', 'TABLE', 'audit', 'COLUMN', 'date';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'audit', 'COLUMN', 'session'))
    EXEC sp_addextendedproperty 'MS_Description', 'Session the operation was triggered from, when it came from an io.Connect platform.', 'SCHEMA', 'dbo', 'TABLE', 'audit', 'COLUMN', 'session';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'audit', 'COLUMN', 'server'))
    EXEC sp_addextendedproperty 'MS_Description', 'Unused.', 'SCHEMA', 'dbo', 'TABLE', 'audit', 'COLUMN', 'server';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'audit', 'COLUMN', 'entityType'))
    EXEC sp_addextendedproperty 'MS_Description', 'Kind of entity the operation acted on, for example ''application'' or ''layout''.', 'SCHEMA', 'dbo', 'TABLE', 'audit', 'COLUMN', 'entityType';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'audit', 'COLUMN', 'entityId'))
    EXEC sp_addextendedproperty 'MS_Description', 'Identifier of the entity the operation acted on.', 'SCHEMA', 'dbo', 'TABLE', 'audit', 'COLUMN', 'entityId';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'audit', 'COLUMN', 'entityDisplayName'))
    EXEC sp_addextendedproperty 'MS_Description', 'Display name of that entity.', 'SCHEMA', 'dbo', 'TABLE', 'audit', 'COLUMN', 'entityDisplayName';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'audit', 'COLUMN', 'operation'))
    EXEC sp_addextendedproperty 'MS_Description', 'The operation performed, for example ''create'' or ''delete''.', 'SCHEMA', 'dbo', 'TABLE', 'audit', 'COLUMN', 'operation';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'audit', 'COLUMN', 'operationComment'))
    EXEC sp_addextendedproperty 'MS_Description', 'Note clarifying the operation, typically what it contributed to its parent operation.', 'SCHEMA', 'dbo', 'TABLE', 'audit', 'COLUMN', 'operationComment';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'audit', 'COLUMN', 'newValue'))
    EXEC sp_addextendedproperty 'MS_Description', 'The entity after the operation. JSON, stored as text.', 'SCHEMA', 'dbo', 'TABLE', 'audit', 'COLUMN', 'newValue';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'audit', 'COLUMN', 'oldValue'))
    EXEC sp_addextendedproperty 'MS_Description', 'The entity before the operation. JSON, stored as text.', 'SCHEMA', 'dbo', 'TABLE', 'audit', 'COLUMN', 'oldValue';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'audit', 'COLUMN', 'request'))
    EXEC sp_addextendedproperty 'MS_Description', 'The request that triggered the operation. JSON, stored as text.', 'SCHEMA', 'dbo', 'TABLE', 'audit', 'COLUMN', 'request';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'audit', 'COLUMN', 'response'))
    EXEC sp_addextendedproperty 'MS_Description', 'Unused. JSON, stored as text.', 'SCHEMA', 'dbo', 'TABLE', 'audit', 'COLUMN', 'response';

-- app
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'app', DEFAULT, DEFAULT))
    EXEC sp_addextendedproperty 'MS_Description', 'An application io.Manager serves to io.Connect platforms.', 'SCHEMA', 'dbo', 'TABLE', 'app';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'app', 'COLUMN', 'app_id'))
    EXEC sp_addextendedproperty 'MS_Description', 'Primary key. Auto-assigned by the database and not used to identify the row outside it.', 'SCHEMA', 'dbo', 'TABLE', 'app', 'COLUMN', 'app_id';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'app', 'COLUMN', 'name'))
    EXEC sp_addextendedproperty 'MS_Description', 'Name of the application. Unique.', 'SCHEMA', 'dbo', 'TABLE', 'app', 'COLUMN', 'name';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'app', 'COLUMN', 'disabled'))
    EXEC sp_addextendedproperty 'MS_Description', 'Whether the application is withheld from all users. Boolean, stored as 0 or 1.', 'SCHEMA', 'dbo', 'TABLE', 'app', 'COLUMN', 'disabled';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'app', 'COLUMN', 'definition'))
    EXEC sp_addextendedproperty 'MS_Description', 'The application definition served to the io.Connect platform. JSON, stored as text.', 'SCHEMA', 'dbo', 'TABLE', 'app', 'COLUMN', 'definition';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'app', 'COLUMN', 'isPublic'))
    EXEC sp_addextendedproperty 'MS_Description', 'Whether every user can access the application. Boolean, stored as 0 or 1.', 'SCHEMA', 'dbo', 'TABLE', 'app', 'COLUMN', 'isPublic';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'app', 'COLUMN', 'accessList'))
    EXEC sp_addextendedproperty 'MS_Description', 'Groups allowed to access the application when it is not public. JSON array of strings, stored as text.', 'SCHEMA', 'dbo', 'TABLE', 'app', 'COLUMN', 'accessList';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'app', 'COLUMN', 'createdBy'))
    EXEC sp_addextendedproperty 'MS_Description', 'User that created the application.', 'SCHEMA', 'dbo', 'TABLE', 'app', 'COLUMN', 'createdBy';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'app', 'COLUMN', 'createdOn'))
    EXEC sp_addextendedproperty 'MS_Description', 'Creation time. Epoch milliseconds (UTC).', 'SCHEMA', 'dbo', 'TABLE', 'app', 'COLUMN', 'createdOn';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'app', 'COLUMN', 'lastModifiedBy'))
    EXEC sp_addextendedproperty 'MS_Description', 'User that last modified the application.', 'SCHEMA', 'dbo', 'TABLE', 'app', 'COLUMN', 'lastModifiedBy';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'app', 'COLUMN', 'lastModifiedOn'))
    EXEC sp_addextendedproperty 'MS_Description', 'Time of the last modification. Epoch milliseconds (UTC).', 'SCHEMA', 'dbo', 'TABLE', 'app', 'COLUMN', 'lastModifiedOn';

-- blobs
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'blobs', DEFAULT, DEFAULT))
    EXEC sp_addextendedproperty 'MS_Description', 'Binary content held for another entity, such as a feedback attachment or a command result file.', 'SCHEMA', 'dbo', 'TABLE', 'blobs';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'blobs', 'COLUMN', 'blobs_id'))
    EXEC sp_addextendedproperty 'MS_Description', 'Primary key. Auto-assigned by the database and not used to identify the row outside it.', 'SCHEMA', 'dbo', 'TABLE', 'blobs', 'COLUMN', 'blobs_id';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'blobs', 'COLUMN', 'type'))
    EXEC sp_addextendedproperty 'MS_Description', 'Kind of entity the content belongs to, which together with the id locates it.', 'SCHEMA', 'dbo', 'TABLE', 'blobs', 'COLUMN', 'type';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'blobs', 'COLUMN', 'id'))
    EXEC sp_addextendedproperty 'MS_Description', 'Identifier of the entity the content belongs to. Unique.', 'SCHEMA', 'dbo', 'TABLE', 'blobs', 'COLUMN', 'id';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'blobs', 'COLUMN', 'fileName'))
    EXEC sp_addextendedproperty 'MS_Description', 'File name to serve the content under.', 'SCHEMA', 'dbo', 'TABLE', 'blobs', 'COLUMN', 'fileName';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'blobs', 'COLUMN', 'data'))
    EXEC sp_addextendedproperty 'MS_Description', 'The content itself. Binary content.', 'SCHEMA', 'dbo', 'TABLE', 'blobs', 'COLUMN', 'data';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'blobs', 'COLUMN', 'encoding'))
    EXEC sp_addextendedproperty 'MS_Description', 'Encoding of the content: "raw" when it is the file''s own bytes, "base64" when it is those bytes base64-encoded as text. Absent on content stored before the encoding was recorded.', 'SCHEMA', 'dbo', 'TABLE', 'blobs', 'COLUMN', 'encoding';

-- layouts_advanced
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'layouts_advanced', DEFAULT, DEFAULT))
    EXEC sp_addextendedproperty 'MS_Description', 'Layouts saved by an io.Connect platform, with per-entity access control.', 'SCHEMA', 'dbo', 'TABLE', 'layouts_advanced';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'layouts_advanced', 'COLUMN', 'layouts_advanced_id'))
    EXEC sp_addextendedproperty 'MS_Description', 'Primary key. Auto-assigned by the database and not used to identify the row outside it.', 'SCHEMA', 'dbo', 'TABLE', 'layouts_advanced', 'COLUMN', 'layouts_advanced_id';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'layouts_advanced', 'COLUMN', 'id'))
    EXEC sp_addextendedproperty 'MS_Description', 'Public identifier of the layout. Unique.', 'SCHEMA', 'dbo', 'TABLE', 'layouts_advanced', 'COLUMN', 'id';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'layouts_advanced', 'COLUMN', 'type'))
    EXEC sp_addextendedproperty 'MS_Description', 'Kind of layout, for example ''Global'' or ''Workspace''.', 'SCHEMA', 'dbo', 'TABLE', 'layouts_advanced', 'COLUMN', 'type';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'layouts_advanced', 'COLUMN', 'name'))
    EXEC sp_addextendedproperty 'MS_Description', 'Name of the layout. Unique per type per owner while private, and unique per type across all owners once shared or public.', 'SCHEMA', 'dbo', 'TABLE', 'layouts_advanced', 'COLUMN', 'name';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'layouts_advanced', 'COLUMN', 'disabled'))
    EXEC sp_addextendedproperty 'MS_Description', 'Whether the layout is withheld from all users. Boolean, stored as 0 or 1.', 'SCHEMA', 'dbo', 'TABLE', 'layouts_advanced', 'COLUMN', 'disabled';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'layouts_advanced', 'COLUMN', 'accessLevel'))
    EXEC sp_addextendedproperty 'MS_Description', 'Who can reach the layout: ''private'', ''shared'' or ''public''.', 'SCHEMA', 'dbo', 'TABLE', 'layouts_advanced', 'COLUMN', 'accessLevel';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'layouts_advanced', 'COLUMN', 'owner'))
    EXEC sp_addextendedproperty 'MS_Description', 'User the layout belongs to.', 'SCHEMA', 'dbo', 'TABLE', 'layouts_advanced', 'COLUMN', 'owner';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'layouts_advanced', 'COLUMN', 'accessInfo'))
    EXEC sp_addextendedproperty 'MS_Description', 'Users and groups granted access, with the level granted to each. Present only for shared layouts. JSON, stored as text.', 'SCHEMA', 'dbo', 'TABLE', 'layouts_advanced', 'COLUMN', 'accessInfo';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'layouts_advanced', 'COLUMN', 'definition'))
    EXEC sp_addextendedproperty 'MS_Description', 'The layout payload as saved by the io.Connect platform. JSON, stored as text.', 'SCHEMA', 'dbo', 'TABLE', 'layouts_advanced', 'COLUMN', 'definition';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'layouts_advanced', 'COLUMN', 'createdBy'))
    EXEC sp_addextendedproperty 'MS_Description', 'User that created the layout.', 'SCHEMA', 'dbo', 'TABLE', 'layouts_advanced', 'COLUMN', 'createdBy';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'layouts_advanced', 'COLUMN', 'createdOn'))
    EXEC sp_addextendedproperty 'MS_Description', 'Creation time. Epoch milliseconds (UTC).', 'SCHEMA', 'dbo', 'TABLE', 'layouts_advanced', 'COLUMN', 'createdOn';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'layouts_advanced', 'COLUMN', 'lastModifiedBy'))
    EXEC sp_addextendedproperty 'MS_Description', 'User that last modified the layout.', 'SCHEMA', 'dbo', 'TABLE', 'layouts_advanced', 'COLUMN', 'lastModifiedBy';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'layouts_advanced', 'COLUMN', 'lastModifiedOn'))
    EXEC sp_addextendedproperty 'MS_Description', 'Time of the last modification. Epoch milliseconds (UTC).', 'SCHEMA', 'dbo', 'TABLE', 'layouts_advanced', 'COLUMN', 'lastModifiedOn';

-- system_state
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'system_state', DEFAULT, DEFAULT))
    EXEC sp_addextendedproperty 'MS_Description', 'Server-wide state shared by every io.Manager instance, one row per named entry.', 'SCHEMA', 'dbo', 'TABLE', 'system_state';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'system_state', 'COLUMN', 'system_state_id'))
    EXEC sp_addextendedproperty 'MS_Description', 'Primary key. Auto-assigned by the database and not used to identify the row outside it.', 'SCHEMA', 'dbo', 'TABLE', 'system_state', 'COLUMN', 'system_state_id';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'system_state', 'COLUMN', 'name'))
    EXEC sp_addextendedproperty 'MS_Description', 'Name of the entry. Unique.', 'SCHEMA', 'dbo', 'TABLE', 'system_state', 'COLUMN', 'name';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'system_state', 'COLUMN', 'value'))
    EXEC sp_addextendedproperty 'MS_Description', 'The entry value. JSON, stored as text.', 'SCHEMA', 'dbo', 'TABLE', 'system_state', 'COLUMN', 'value';
IF NOT EXISTS (SELECT 1 FROM fn_listextendedproperty('MS_Description', 'SCHEMA', 'dbo', 'TABLE', 'system_state', 'COLUMN', 'version'))
    EXEC sp_addextendedproperty 'MS_Description', 'Revision of the entry, raised on every write so concurrent instances cannot overwrite each other.', 'SCHEMA', 'dbo', 'TABLE', 'system_state', 'COLUMN', 'version';

-- Minimum required user permissions for the schema and the schema objects.
GRANT SELECT, INSERT, UPDATE, DELETE ON SCHEMA :: [dbo] TO my_user;