# Microsoft SQL Server

Source: https://docs.interop.io/manager/databases/microsoft-sql/index.html

## 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](https://github.com/InteropIO/manager-examples/tree/main/db-mssql) 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](https://docs.interop.io/manager/deployment/index.md) approach you have chosen, you have the following options:

- If you are using the [basic deployment scenario](https://github.com/InteropIO/manager-examples/tree/main/manager-template/1-basic) from the [template repository](https://docs.interop.io/manager/deployment/index.md#template_repository) approach, you must set properly the necessary environment variables.

- If you are using the [NPM packages](https://docs.interop.io/manager/deployment/index.md#npm_packages) for deployment, or the [advanced deployment scenario](https://github.com/InteropIO/manager-examples/tree/main/manager-template/2-advanced) from the template repository approach, you must provide the necessary settings via the configuration object for initializing the **io.Manager** Server.

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](#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`. <br> ⚠️ *Note that `snapshot` requires the database to have `ALLOW_SNAPSHOT_ISOLATION` enabled - otherwise, the server won't start.* <br> *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:

```cmd
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](https://docs.interop.io/manager/configuration/server/index.md#environment_variables) 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:

```javascript
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](#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](https://knexjs.org/) 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](https://knexjs.org/) pool configuration. For more details, see the [official Knex.js documentation](https://knexjs.org/guide/#pool). |
| `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"`. <br> ⚠️ *Note that `"snapshot"` requires the database to have `ALLOW_SNAPSHOT_ISOLATION` enabled - otherwise, the server won't start.* <br> *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](https://docs.interop.io/manager/configuration/server/index.md#configuration_object) 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](https://knexjs.org/guide/migrations.html) 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:*

```sql
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](https://github.com/InteropIO/manager-examples/tree/main/manager-template/1-basic) from the [template repository](https://docs.interop.io/manager/deployment/index.md#template_repository) approach, you must set the `API_STORE_MSSQL_CREATE_DB` environment variable to `false`:

```cmd
API_STORE_MSSQL_CREATE_DB=false
```

If you are using the [NPM packages](https://docs.interop.io/manager/deployment/index.md#npm_packages) for deployment, or the [advanced deployment scenario](https://github.com/InteropIO/manager-examples/tree/main/manager-template/2-advanced) from the template repository approach, you must set the `createDatabaseAndTables` property to `false` in the optional `Config` object for initializing the **io.Manager** Server:

```javascript
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.*

```sql
-- 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;
```
