diff --git a/.github/workflows/db-partial-migrations.yml b/.github/workflows/db-partial-migrations.yml new file mode 100644 index 000000000..b6bd22395 --- /dev/null +++ b/.github/workflows/db-partial-migrations.yml @@ -0,0 +1,50 @@ +# This is a test of legacy migrations in various states. +# This test workflow should not be required after knex has been removed. + +name: Partial Database Migrations + +on: + push: + pull_request: + +jobs: + db-partial-migration-tests: + strategy: + matrix: + previous-migrations: + - none + - legacy-first-1 + - legacy-first-10 + - legacy-first-100 + - legacy-all + - new-first-1-as-legacy + - new-first-10-as-legacy + - all + timeout-minutes: 2 + # TODO should we use the same container as circle & central? + runs-on: ubuntu-latest + services: + # see: https://docs.github.com/en/enterprise-server@3.5/actions/using-containerized-services/creating-postgresql-service-containers + postgres: + image: postgres:14.10 + env: + POSTGRES_PASSWORD: odktest + ports: + - 5432:5432 + # Set health checks to wait until postgres has started + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + steps: + - run: sudo apt-get install tree + - uses: actions/checkout@v4 + - name: Set node version + uses: actions/setup-node@v4 + with: + node-version: 22.14.0 + cache: 'npm' + - run: npm ci + - run: node lib/bin/create-docker-databases.js + - run: test/db-partial-migrations/test.sh ${{ matrix.previous-migrations }} diff --git a/Makefile b/Makefile index 2710b4899..f45143277 100644 --- a/Makefile +++ b/Makefile @@ -73,6 +73,9 @@ fake-s3-server-persistent: .PHONY: migrations migrations: node_version node lib/bin/run-migrations.js +.PHONY: migrations-legacy +migrations-legacy: node_version + node lib/bin/run-migrations-legacy.js ################################################################################ diff --git a/lib/bin/run-migrations-legacy.js b/lib/bin/run-migrations-legacy.js new file mode 100644 index 000000000..c9d2170f9 --- /dev/null +++ b/lib/bin/run-migrations-legacy.js @@ -0,0 +1,20 @@ +// Copyright 2022 ODK Central Developers +// See the NOTICE file at the top-level directory of this distribution and at +// https://github.com/getodk/central-backend/blob/master/NOTICE. +// This file is part of ODK Central. It is subject to the license terms in +// the LICENSE file found in the top-level directory of this distribution and at +// https://www.apache.org/licenses/LICENSE-2.0. No part of ODK Central, +// including this file, may be copied, modified, propagated, or distributed +// except according to the terms contained in the LICENSE file. + +const { withKnex, migrate } = require('../model/knex-migrator-legacy'); + +(async () => { + try { + await withKnex(require('config').get('default.database'))(migrate); + } catch (err) { + if (err.detail) console.error('Error:', err.message, `(${err.detail})`); + else console.error('Error:', err.message); // eslint-disable-line no-multi-spaces + process.exit(1); + } +})(); diff --git a/lib/bin/run-migrations.js b/lib/bin/run-migrations.js index bc0a6d0d1..c10215a40 100644 --- a/lib/bin/run-migrations.js +++ b/lib/bin/run-migrations.js @@ -7,11 +7,13 @@ // including this file, may be copied, modified, propagated, or distributed // except according to the terms contained in the LICENSE file. -const { withKnex, migrate } = require('../model/knex-migrator'); +const { withKnex, migrate: knexMigrate } = require('../model/knex-migrator'); +const { withPg, migrate: pgMigrate } = require('../model/pg-migrator'); (async () => { try { - await withKnex(require('config').get('default.database'))(migrate); + await withKnex(require('config').get('default.database'))(knexMigrate); + await withPg(require('config').get('default.database'))(pgMigrate); } catch (err) { if (err.detail) console.error('Error:', err.message, `(${err.detail})`); else console.error('Error:', err.message); // eslint-disable-line no-multi-spaces diff --git a/lib/model/knex-migrator-legacy.js b/lib/model/knex-migrator-legacy.js new file mode 100644 index 000000000..d2c3f8bd7 --- /dev/null +++ b/lib/model/knex-migrator-legacy.js @@ -0,0 +1,29 @@ +// Copyright 2017 ODK Central Developers +// See the NOTICE file at the top-level directory of this distribution and at +// https://github.com/getodk/central-backend/blob/master/NOTICE. +// This file is part of ODK Central. It is subject to the license terms in +// the LICENSE file found in the top-level directory of this distribution and at +// https://www.apache.org/licenses/LICENSE-2.0. No part of ODK Central, +// including this file, may be copied, modified, propagated, or distributed +// except according to the terms contained in the LICENSE file. +// +// This is a variety of functions helpful for connecting to and performing +// top-level operations with a database, like migrations. + +const knex = require('knex'); +const { knexConnection } = require('../util/db'); + +// Connects to the postgres database specified in configuration and returns it. +const knexConnect = (config) => knex({ client: 'pg', connection: knexConnection(config) }); + +// Connects to a database, passes it to a function for operations, then ensures its closure. +const withKnex = (config) => (mutator) => { + const db = knexConnect(config); + return mutator(db).finally(() => db.destroy()); +}; + +// Given a database, initiates migrations on it. +const migrate = (db) => db.migrate.latest({ directory: `${__dirname}/migrations` }); + +module.exports = { knexConnect, withKnex, migrate }; + diff --git a/lib/model/knex-migrator.js b/lib/model/knex-migrator.js index d2c3f8bd7..e568262b1 100644 --- a/lib/model/knex-migrator.js +++ b/lib/model/knex-migrator.js @@ -23,7 +23,12 @@ const withKnex = (config) => (mutator) => { }; // Given a database, initiates migrations on it. -const migrate = (db) => db.migrate.latest({ directory: `${__dirname}/migrations` }); +// disableMigrationsListValidation: because knex-migrator now shares a DB table +// with pg-migrator, knex's built-in check that all migrations found in the db +// have corresponding migration files in `directory` will fail. +// REVIEW note that we could maintain this check if we duplicate migration +// tables and store pg migrations separately. +const migrate = (db) => db.migrate.latest({ directory: `${__dirname}/migrations/legacy`, disableMigrationsListValidation: true }); module.exports = { knexConnect, withKnex, migrate }; diff --git a/lib/model/migrations/20230802-01-delete-orphan-submissions.js b/lib/model/migrations/20230802-01-delete-orphan-submissions.js index 39cb1c8e3..9986e0969 100644 --- a/lib/model/migrations/20230802-01-delete-orphan-submissions.js +++ b/lib/model/migrations/20230802-01-delete-orphan-submissions.js @@ -9,7 +9,7 @@ // Delete draft Submissions that don't have any definition - cb#911 const up = async (db) => { - await db.raw(` + await db.query(` DELETE FROM submissions s WHERE draft AND id IN ( SELECT s.id FROM submissions s diff --git a/lib/model/migrations/20230818-01-remove-schemaId-from-dsPropertyFields.js b/lib/model/migrations/20230818-01-remove-schemaId-from-dsPropertyFields.js index 9ec11e119..506c8f924 100644 --- a/lib/model/migrations/20230818-01-remove-schemaId-from-dsPropertyFields.js +++ b/lib/model/migrations/20230818-01-remove-schemaId-from-dsPropertyFields.js @@ -8,7 +8,7 @@ // except according to the terms contained in the LICENSE file. const up = async (db) => { - await db.raw('ALTER TABLE ds_property_fields DROP COLUMN "schemaId"'); + await db.query('ALTER TABLE ds_property_fields DROP COLUMN "schemaId"'); }; const down = () => {}; diff --git a/lib/model/migrations/20230824-01-add-entity-version.js b/lib/model/migrations/20230824-01-add-entity-version.js index f5ddc8650..4f8437a4a 100644 --- a/lib/model/migrations/20230824-01-add-entity-version.js +++ b/lib/model/migrations/20230824-01-add-entity-version.js @@ -8,10 +8,10 @@ // except according to the terms contained in the LICENSE file. const up = async (db) => { - await db.raw('ALTER TABLE entity_defs ADD COLUMN version INT4 NOT NULL DEFAULT 1'); + await db.query('ALTER TABLE entity_defs ADD COLUMN version INT4 NOT NULL DEFAULT 1'); // Sets the correct version number for existing entities - await db.raw(` + await db.query(` UPDATE entity_defs SET "version" = vt.rownumber FROM ( SELECT ROW_NUMBER() OVER(PARTITION BY "entityId" ORDER BY id ) rownumber, id FROM entity_defs @@ -20,6 +20,6 @@ const up = async (db) => { `); }; -const down = (db) => db.raw('ALTER TABLE entity_defs DROP COLUMN version'); +const down = (db) => db.query('ALTER TABLE entity_defs DROP COLUMN version'); module.exports = { up, down }; diff --git a/lib/model/migrations/20230830-01-remove-entity-label-from-audits.js b/lib/model/migrations/20230830-01-remove-entity-label-from-audits.js index 0360602e3..5ab55b8cd 100644 --- a/lib/model/migrations/20230830-01-remove-entity-label-from-audits.js +++ b/lib/model/migrations/20230830-01-remove-entity-label-from-audits.js @@ -7,7 +7,7 @@ // including this file, may be copied, modified, propagated, or distributed // except according to the terms contained in the LICENSE file. -const up = (db) => db.raw(` +const up = (db) => db.query(` update audits set details=(details #- '{entity,label}') where action='entity.create'`); diff --git a/lib/model/migrations/20230907-01-opened-form-verb.js b/lib/model/migrations/20230907-01-opened-form-verb.js index be295a859..a86371e5e 100644 --- a/lib/model/migrations/20230907-01-opened-form-verb.js +++ b/lib/model/migrations/20230907-01-opened-form-verb.js @@ -7,11 +7,11 @@ // including this file, may be copied, modified, propagated, or distributed // except according to the terms contained in the LICENSE file. -const up = (db) => db.raw(` +const up = (db) => db.query(` UPDATE roles SET verbs = REPLACE(verbs ::TEXT, 'form', 'open_form')::JSONB WHERE system IN ('app-user', 'formview', 'formfill', 'pub-link')`); -const down = (db) => db.raw(` +const down = (db) => db.query(` UPDATE roles SET verbs = REPLACE(verbs ::TEXT, 'open_form', 'form')::JSONB WHERE system IN ('app-user', 'formview', 'formfill', 'pub-link')`); diff --git a/lib/model/migrations/20231002-01-add-conflict-details.js b/lib/model/migrations/20231002-01-add-conflict-details.js index 3c232d391..88004dab2 100644 --- a/lib/model/migrations/20231002-01-add-conflict-details.js +++ b/lib/model/migrations/20231002-01-add-conflict-details.js @@ -8,11 +8,11 @@ // except according to the terms contained in the LICENSE file. const up = async (db) => { - await db.raw(`CREATE TYPE "conflictType" AS ENUM ('soft', 'hard')`); + await db.query(`CREATE TYPE "conflictType" AS ENUM ('soft', 'hard')`); - await db.raw('ALTER TABLE entities ADD COLUMN conflict "conflictType" NULL'); + await db.query('ALTER TABLE entities ADD COLUMN conflict "conflictType" NULL'); - await db.raw(`ALTER TABLE entity_defs + await db.query(`ALTER TABLE entity_defs ADD COLUMN "dataReceived" JSONB NOT NULL DEFAULT '{}'::jsonb, -- null means, it's a first version @@ -25,13 +25,13 @@ const up = async (db) => { `); // Sets the value for "dataReceived" and "baseVersion" for existing row - await db.raw(`UPDATE entity_defs SET "dataReceived" = data || jsonb_build_object('label', "label"), "baseVersion" = CASE WHEN version > 1 THEN version - 1 ELSE NULL END`); + await db.query(`UPDATE entity_defs SET "dataReceived" = data || jsonb_build_object('label', "label"), "baseVersion" = CASE WHEN version > 1 THEN version - 1 ELSE NULL END`); }; const down = async (db) => { - await db.raw('ALTER TABLE entities DROP COLUMN conflict'); - await db.raw('ALTER TABLE entity_defs DROP COLUMN "dataReceived", DROP COLUMN "baseVersion"'); + await db.query('ALTER TABLE entities DROP COLUMN conflict'); + await db.query('ALTER TABLE entity_defs DROP COLUMN "dataReceived", DROP COLUMN "baseVersion"'); }; module.exports = { up, down }; diff --git a/lib/model/migrations/20231013-01-change-entity-error-action.js b/lib/model/migrations/20231013-01-change-entity-error-action.js index e52fbd6e8..fb4b198d3 100644 --- a/lib/model/migrations/20231013-01-change-entity-error-action.js +++ b/lib/model/migrations/20231013-01-change-entity-error-action.js @@ -8,12 +8,12 @@ // except according to the terms contained in the LICENSE file. const up = async (db) => { - await db.raw('UPDATE audits SET "action" = \'entity.error\' WHERE "action" = \'entity.create.error\''); + await db.query('UPDATE audits SET "action" = \'entity.error\' WHERE "action" = \'entity.create.error\''); }; const down = async (db) => { // will set any error back to create error, which isn't necessarily right - await db.raw('UPDATE audits SET "action" = \'entity.create.error\' WHERE "action" = \'entity.error\''); + await db.query('UPDATE audits SET "action" = \'entity.create.error\' WHERE "action" = \'entity.error\''); }; module.exports = { up, down }; diff --git a/lib/model/migrations/20231208-01-dataset-form-def-actions.js b/lib/model/migrations/20231208-01-dataset-form-def-actions.js index c6a3cf7bf..411efbccb 100644 --- a/lib/model/migrations/20231208-01-dataset-form-def-actions.js +++ b/lib/model/migrations/20231208-01-dataset-form-def-actions.js @@ -8,12 +8,12 @@ // except according to the terms contained in the LICENSE file. const up = async (db) => { - await db.raw('ALTER TABLE dataset_form_defs ADD COLUMN actions jsonb'); - await db.raw(`UPDATE dataset_form_defs SET actions = '["create"]'`); - await db.raw('ALTER TABLE dataset_form_defs ALTER COLUMN actions SET NOT NULL'); + await db.query('ALTER TABLE dataset_form_defs ADD COLUMN actions jsonb'); + await db.query(`UPDATE dataset_form_defs SET actions = '["create"]'`); + await db.query('ALTER TABLE dataset_form_defs ALTER COLUMN actions SET NOT NULL'); }; const down = (db) => - db.raw('ALTER TABLE dataset_form_defs DROP COLUMN actions'); + db.query('ALTER TABLE dataset_form_defs DROP COLUMN actions'); module.exports = { up, down }; diff --git a/lib/model/migrations/20240215-01-entity-delete-verb.js b/lib/model/migrations/20240215-01-entity-delete-verb.js index da83905a9..c66cc8c84 100644 --- a/lib/model/migrations/20240215-01-entity-delete-verb.js +++ b/lib/model/migrations/20240215-01-entity-delete-verb.js @@ -7,12 +7,12 @@ // including this file, may be copied, modified, propagated, or distributed // except according to the terms contained in the LICENSE file. -const up = (db) => db.raw(` +const up = (db) => db.query(` UPDATE roles SET verbs = verbs || '["entity.delete"]'::jsonb WHERE system IN ('admin', 'manager')`); -const down = (db) => db.raw(` +const down = (db) => db.query(` UPDATE roles SET verbs = verbs - 'entity.delete' WHERE system IN ('admin', 'manager')`); diff --git a/lib/model/migrations/20240215-02-dedupe-verbs.js b/lib/model/migrations/20240215-02-dedupe-verbs.js index cf81984ca..3287227a2 100644 --- a/lib/model/migrations/20240215-02-dedupe-verbs.js +++ b/lib/model/migrations/20240215-02-dedupe-verbs.js @@ -7,7 +7,7 @@ // including this file, may be copied, modified, propagated, or distributed // except according to the terms contained in the LICENSE file. -const up = (db) => db.raw(` +const up = (db) => db.query(` UPDATE roles SET verbs = (verbs - 'submission.update') || '["submission.update"]'::jsonb WHERE system IN ('admin', 'manager')`); diff --git a/lib/model/migrations/20240312-01-add-dataset-create-verb.js b/lib/model/migrations/20240312-01-add-dataset-create-verb.js index 58d6e76c6..c02e770ed 100644 --- a/lib/model/migrations/20240312-01-add-dataset-create-verb.js +++ b/lib/model/migrations/20240312-01-add-dataset-create-verb.js @@ -7,13 +7,13 @@ // including this file, may be copied, modified, propagated, or distributed // except according to the terms contained in the LICENSE file. -const up = (db) => db.raw(` +const up = (db) => db.query(` UPDATE roles SET verbs = verbs || '["dataset.create"]'::jsonb WHERE system in ('admin', 'manager') `); -const down = (db) => db.raw(` +const down = (db) => db.query(` UPDATE roles SET verbs = (verbs - 'dataset.create') WHERE system in ('admin', 'manager') diff --git a/lib/model/migrations/20240322-01-add-entity-source-index-to-audits.js b/lib/model/migrations/20240322-01-add-entity-source-index-to-audits.js index a68d78c38..590411861 100644 --- a/lib/model/migrations/20240322-01-add-entity-source-index-to-audits.js +++ b/lib/model/migrations/20240322-01-add-entity-source-index-to-audits.js @@ -8,13 +8,13 @@ // except according to the terms contained in the LICENSE file. const up = async (db) => { - await db.raw("CREATE INDEX audits_details_entity_def_index ON audits USING HASH (((details ->> 'entityDefId')::INTEGER))"); - await db.raw("CREATE INDEX audits_details_entity_source_index ON audits USING HASH (((details ->> 'sourceId')::INTEGER))"); + await db.query("CREATE INDEX audits_details_entity_def_index ON audits USING HASH (((details ->> 'entityDefId')::INTEGER))"); + await db.query("CREATE INDEX audits_details_entity_source_index ON audits USING HASH (((details ->> 'sourceId')::INTEGER))"); }; const down = async (db) => { - await db.raw('DROP INDEX audits_details_entity_def_index'); - await db.raw('DROP INDEX audits_details_entity_source_index'); + await db.query('DROP INDEX audits_details_entity_def_index'); + await db.query('DROP INDEX audits_details_entity_source_index'); }; module.exports = { up, down }; diff --git a/lib/model/migrations/20240515-01-entity-tz-precision.js b/lib/model/migrations/20240515-01-entity-tz-precision.js index 3b76190e8..1db2bdc63 100644 --- a/lib/model/migrations/20240515-01-entity-tz-precision.js +++ b/lib/model/migrations/20240515-01-entity-tz-precision.js @@ -8,11 +8,11 @@ // except according to the terms contained in the LICENSE file. const up = async (db) => { - await db.raw('ALTER TABLE entities ALTER COLUMN "deletedAt" TYPE timestamptz(3)'); + await db.query('ALTER TABLE entities ALTER COLUMN "deletedAt" TYPE timestamptz(3)'); }; const down = async (db) => { - await db.raw('ALTER TABLE entities ALTER COLUMN "deletedAt" TYPE timestamp'); + await db.query('ALTER TABLE entities ALTER COLUMN "deletedAt" TYPE timestamp'); }; module.exports = { up, down }; diff --git a/lib/model/migrations/20240607-01-add-offline-entity-branch-trunk-info.js b/lib/model/migrations/20240607-01-add-offline-entity-branch-trunk-info.js index a0fe0acbd..873614b09 100644 --- a/lib/model/migrations/20240607-01-add-offline-entity-branch-trunk-info.js +++ b/lib/model/migrations/20240607-01-add-offline-entity-branch-trunk-info.js @@ -8,13 +8,13 @@ // except according to the terms contained in the LICENSE file. const up = async (db) => { - await db.raw(`ALTER TABLE entity_defs + await db.query(`ALTER TABLE entity_defs ADD COLUMN "branchId" UUID, ADD COLUMN "trunkVersion" INT4, ADD COLUMN "branchBaseVersion" INT4`); }; -const down = (db) => db.raw(`ALTER TABLE entity_defs +const down = (db) => db.query(`ALTER TABLE entity_defs DROP COLUMN "branchId", DROP COLUMN "trunkVersion", DROP COLUMN "branchBaseVersion" diff --git a/lib/model/migrations/20240607-02-add-submission-backlog.js b/lib/model/migrations/20240607-02-add-submission-backlog.js index 0cda52ed5..5c712ebf1 100644 --- a/lib/model/migrations/20240607-02-add-submission-backlog.js +++ b/lib/model/migrations/20240607-02-add-submission-backlog.js @@ -8,7 +8,7 @@ // except according to the terms contained in the LICENSE file. const up = async (db) => { - await db.raw(`CREATE TABLE entity_submission_backlog ( + await db.query(`CREATE TABLE entity_submission_backlog ( "submissionId" INT4 NOT NULL, "submissionDefId" INT4 NOT NULL, "branchId" UUID NOT NULL, @@ -26,6 +26,6 @@ const up = async (db) => { )`); }; -const down = (db) => db.raw('DROP TABLE entity_submission_backlog'); +const down = (db) => db.query('DROP TABLE entity_submission_backlog'); module.exports = { up, down }; diff --git a/lib/model/migrations/20240715-01-backlog-add-event-entityuuid.js b/lib/model/migrations/20240715-01-backlog-add-event-entityuuid.js index 2915d90e8..284d6a3aa 100644 --- a/lib/model/migrations/20240715-01-backlog-add-event-entityuuid.js +++ b/lib/model/migrations/20240715-01-backlog-add-event-entityuuid.js @@ -8,7 +8,7 @@ // except according to the terms contained in the LICENSE file. const up = async (db) => { - await db.raw(`ALTER TABLE entity_submission_backlog + await db.query(`ALTER TABLE entity_submission_backlog ADD COLUMN "auditId" INT4 NOT NULL, ADD COLUMN "entityUuid" UUID NOT NULL, ADD CONSTRAINT fk_audit_id @@ -17,7 +17,7 @@ const up = async (db) => { ON DELETE CASCADE`); }; -const down = (db) => db.raw(`ALTER TABLE entity_submission_backlog +const down = (db) => db.query(`ALTER TABLE entity_submission_backlog DROP COLUMN "auditId", DROP COLUMN "entityUuid" `); diff --git a/lib/model/migrations/20240913-01-add-blob-s3.js b/lib/model/migrations/20240913-01-add-blob-s3.js index 048f31b4a..a34590bf7 100644 --- a/lib/model/migrations/20240913-01-add-blob-s3.js +++ b/lib/model/migrations/20240913-01-add-blob-s3.js @@ -8,8 +8,8 @@ // except according to the terms contained in the LICENSE file. const up = async (db) => { - await db.raw(`CREATE TYPE S3_UPLOAD_STATUS AS ENUM ('pending', 'uploaded', 'failed')`); - await db.raw(` + await db.query(`CREATE TYPE S3_UPLOAD_STATUS AS ENUM ('pending', 'uploaded', 'failed')`); + await db.query(` ALTER TABLE blobs ADD COLUMN s3_status S3_UPLOAD_STATUS NOT NULL DEFAULT 'pending', ALTER COLUMN content DROP NOT NULL diff --git a/lib/model/migrations/20240914-01-add-submission-delete-verb.js b/lib/model/migrations/20240914-01-add-submission-delete-verb.js index d9bf1f7bd..7b308162c 100644 --- a/lib/model/migrations/20240914-01-add-submission-delete-verb.js +++ b/lib/model/migrations/20240914-01-add-submission-delete-verb.js @@ -7,13 +7,13 @@ // including this file, may be copied, modified, propagated, or distributed // except according to the terms contained in the LICENSE file. -const up = (db) => db.raw(` +const up = (db) => db.query(` UPDATE roles SET verbs = verbs || '["submission.delete", "submission.restore"]'::jsonb WHERE system in ('admin', 'manager') `); -const down = (db) => db.raw(` +const down = (db) => db.query(` UPDATE roles SET verbs = (verbs - 'submission.delete' - 'submission.restore') WHERE system in ('admin', 'manager') diff --git a/lib/model/migrations/20240914-02-remove-orphaned-client-audits.js b/lib/model/migrations/20240914-02-remove-orphaned-client-audits.js index 9dd633a71..ee603c1f2 100644 --- a/lib/model/migrations/20240914-02-remove-orphaned-client-audits.js +++ b/lib/model/migrations/20240914-02-remove-orphaned-client-audits.js @@ -12,7 +12,7 @@ // This migration deletes those client audit rows, allowing the blobs to finally // be de-referenced and also eventually purged (by the puring cron job). -const up = (db) => db.raw(` +const up = (db) => db.query(` DELETE FROM client_audits WHERE "blobId" NOT IN ( SELECT "blobId" FROM submission_attachments diff --git a/lib/model/migrations/20241001-01-index-on-session-table.js b/lib/model/migrations/20241001-01-index-on-session-table.js index d1b8f00a4..6aa507330 100644 --- a/lib/model/migrations/20241001-01-index-on-session-table.js +++ b/lib/model/migrations/20241001-01-index-on-session-table.js @@ -7,11 +7,11 @@ // including this file, may be copied, modified, propagated, or distributed // except according to the terms contained in the LICENSE file. -const up = (db) => db.raw(` +const up = (db) => db.query(` CREATE UNIQUE INDEX "sessions_token_index" ON sessions (token); `); -const down = (db) => db.raw(` +const down = (db) => db.query(` DROP INDEX "sessions_token_index"; `); diff --git a/lib/model/migrations/20241008-01-add-user_preferences.js b/lib/model/migrations/20241008-01-add-user_preferences.js index 94862cfba..3b7564883 100644 --- a/lib/model/migrations/20241008-01-add-user_preferences.js +++ b/lib/model/migrations/20241008-01-add-user_preferences.js @@ -7,7 +7,7 @@ // including this file, may be copied, modified, propagated, or distributed // except according to the terms contained in the LICENSE file. -const up = (db) => db.raw(` +const up = (db) => db.query(` CREATE TABLE user_site_preferences ( "userId" integer NOT NULL REFERENCES users ("actorId"), "propertyName" text NOT NULL CHECK (length("propertyName") > 0), @@ -29,7 +29,7 @@ const up = (db) => db.raw(` CREATE INDEX ON "user_project_preferences" ("userId"); `); -const down = (db) => db.raw(` +const down = (db) => db.query(` DROP TABLE user_site_preferences; DROP TABLE user_project_preferences; `); diff --git a/lib/model/migrations/20241010-01-schedule-entity-form-upgrade.js b/lib/model/migrations/20241010-01-schedule-entity-form-upgrade.js index 1aa35ac5f..1538cfd54 100644 --- a/lib/model/migrations/20241010-01-schedule-entity-form-upgrade.js +++ b/lib/model/migrations/20241010-01-schedule-entity-form-upgrade.js @@ -7,7 +7,7 @@ // including this file, may be copied, modified, propagated, or distributed // except according to the terms contained in the LICENSE file. -const up = (db) => db.raw(` +const up = (db) => db.query(` INSERT INTO audits ("action", "acteeId", "loggedAt", "details") SELECT 'upgrade.process.form.entities_version', forms."acteeId", clock_timestamp(), '{"upgrade": "As part of upgrading Central to v2024.3, this form is being updated to the latest entities-version spec."}' diff --git a/lib/model/migrations/20241029-01-schedule-entity-form-upgrade-create-forms.js b/lib/model/migrations/20241029-01-schedule-entity-form-upgrade-create-forms.js index 793898943..34b004ad7 100644 --- a/lib/model/migrations/20241029-01-schedule-entity-form-upgrade-create-forms.js +++ b/lib/model/migrations/20241029-01-schedule-entity-form-upgrade-create-forms.js @@ -15,7 +15,7 @@ // Basically, every existing form should be flagged, but I didn't want // to change an old migration. -const up = (db) => db.raw(` +const up = (db) => db.query(` INSERT INTO audits ("action", "acteeId", "loggedAt", "details") SELECT 'upgrade.process.form.entities_version', forms."acteeId", clock_timestamp(), '{"upgrade": "As part of upgrading Central to v2024.3, this form is being updated to the latest entities-version spec."}' diff --git a/lib/model/migrations/20241030-01-add-force-entity-def-source.js b/lib/model/migrations/20241030-01-add-force-entity-def-source.js index 1c54134c5..4b77c05d5 100644 --- a/lib/model/migrations/20241030-01-add-force-entity-def-source.js +++ b/lib/model/migrations/20241030-01-add-force-entity-def-source.js @@ -7,12 +7,12 @@ // including this file, may be copied, modified, propagated, or distributed // except according to the terms contained in the LICENSE file. -const up = (db) => db.raw(` +const up = (db) => db.query(` ALTER TABLE entity_def_sources ADD COLUMN "forceProcessed" BOOLEAN `); -const down = (db) => db.raw(` +const down = (db) => db.query(` ALTER TABLE entity_def_sources DROP COLUMN "forceProcessed" `); diff --git a/lib/model/migrations/20241224-01-entity-restore-verb.js b/lib/model/migrations/20241224-01-entity-restore-verb.js index 5d948899c..7d73d48e0 100644 --- a/lib/model/migrations/20241224-01-entity-restore-verb.js +++ b/lib/model/migrations/20241224-01-entity-restore-verb.js @@ -7,12 +7,12 @@ // including this file, may be copied, modified, propagated, or distributed // except according to the terms contained in the LICENSE file. -const up = (db) => db.raw(` +const up = (db) => db.query(` UPDATE roles SET verbs = verbs || '["entity.restore"]'::jsonb WHERE system IN ('admin', 'manager')`); -const down = (db) => db.raw(` +const down = (db) => db.query(` UPDATE roles SET verbs = verbs - 'entity.restore' WHERE system IN ('admin', 'manager')`); diff --git a/lib/model/migrations/20241224-02-cascade-entity-purge.js b/lib/model/migrations/20241224-02-cascade-entity-purge.js index 7d39083c2..4a359995b 100644 --- a/lib/model/migrations/20241224-02-cascade-entity-purge.js +++ b/lib/model/migrations/20241224-02-cascade-entity-purge.js @@ -7,12 +7,12 @@ // including this file, may be copied, modified, propagated, or distributed // except according to the terms contained in the LICENSE file. -const up = (db) => db.raw(` +const up = (db) => db.query(` ALTER TABLE public.entity_defs DROP CONSTRAINT entity_defs_entityid_foreign; ALTER TABLE public.entity_defs ADD CONSTRAINT entity_defs_entityid_foreign FOREIGN KEY ("entityId") REFERENCES public.entities(id) ON DELETE CASCADE; `); -const down = ((db) => db.raw(` +const down = ((db) => db.query(` ALTER TABLE public.entity_defs DROP CONSTRAINT entity_defs_entityid_foreign; ALTER TABLE public.entity_defs ADD CONSTRAINT entity_defs_entityid_foreign FOREIGN KEY ("entityId") REFERENCES public.entities(id); `)); diff --git a/lib/model/migrations/20241226-01-indices-for-purging-entities.js b/lib/model/migrations/20241226-01-indices-for-purging-entities.js index 6ca38cc66..47ba7e551 100644 --- a/lib/model/migrations/20241226-01-indices-for-purging-entities.js +++ b/lib/model/migrations/20241226-01-indices-for-purging-entities.js @@ -7,7 +7,7 @@ // including this file, may be copied, modified, propagated, or distributed // except according to the terms contained in the LICENSE file. -const up = (db) => db.raw(` +const up = (db) => db.query(` CREATE INDEX audits_details_entity_uuid ON public.audits USING hash ((details->'entity'->>'uuid')) WHERE ACTION IN ('entity.create', 'entity.update', 'entity.update.version', 'entity.update.resolve', 'entity.delete', 'entity.restore'); @@ -15,7 +15,7 @@ CREATE INDEX audits_details_entityUuids ON audits USING gin ((details -> 'entity WHERE ACTION = 'entity.purge'; `); -const down = ((db) => db.raw(` +const down = ((db) => db.query(` DROP INDEX audits_details_entity_uuid; DROP INDEX audits_details_entityUuids; `)); diff --git a/lib/model/migrations/20241227-01-backfill-audit-entity-uuid.js b/lib/model/migrations/20241227-01-backfill-audit-entity-uuid.js index 47ff576a5..464b37cb6 100644 --- a/lib/model/migrations/20241227-01-backfill-audit-entity-uuid.js +++ b/lib/model/migrations/20241227-01-backfill-audit-entity-uuid.js @@ -7,9 +7,9 @@ // including this file, may be copied, modified, propagated, or distributed // except according to the terms contained in the LICENSE file. -const up = (db) => db.raw(` +const up = (db) => db.query(` UPDATE audits SET details = ('{ "entity":{ "uuid":"' || (details->>'uuid') || '"}}')::JSONB -WHERE action = 'entity.delete' AND details \\? 'uuid'; +WHERE action = 'entity.delete' AND JSONB_EXISTS(details, 'uuid'); `); const down = () => {}; diff --git a/lib/model/migrations/20250113-01-add-webformsenabled-formtable-column.js b/lib/model/migrations/20250113-01-add-webformsenabled-formtable-column.js index b79162a2f..50bd28c1e 100644 --- a/lib/model/migrations/20250113-01-add-webformsenabled-formtable-column.js +++ b/lib/model/migrations/20250113-01-add-webformsenabled-formtable-column.js @@ -7,12 +7,12 @@ // including this file, may be copied, modified, propagated, or distributed // except according to the terms contained in the LICENSE file. -const up = (db) => db.raw(` +const up = (db) => db.query(` ALTER TABLE forms ADD COLUMN "webformsEnabled" boolean NOT NULL DEFAULT FALSE; `); -const down = (db) => db.raw(` +const down = (db) => db.query(` ALTER TABLE forms DROP COLUMN "webformsEnabled"; `); diff --git a/lib/model/migrations/20250113-01-disable-nullable-blob-content-types.js b/lib/model/migrations/20250113-01-disable-nullable-blob-content-types.js index b3844bc3a..9d57cae54 100644 --- a/lib/model/migrations/20250113-01-disable-nullable-blob-content-types.js +++ b/lib/model/migrations/20250113-01-disable-nullable-blob-content-types.js @@ -7,14 +7,14 @@ // including this file, may be copied, modified, propagated, or distributed // except according to the terms contained in the LICENSE file. -const up = (db) => db.raw(` +const up = (db) => db.query(` UPDATE blobs SET "contentType"='application/octet-stream' WHERE "contentType" IS NULL; ALTER TABLE blobs ALTER COLUMN "contentType" SET DEFAULT 'application/octet-stream', ALTER COLUMN "contentType" SET NOT NULL `); -const down = (db) => db.raw(` +const down = (db) => db.query(` ALTER TABLE blobs ALTER COLUMN "contentType" DROP NOT NULL, ALTER COLUMN "contentType" DROP DEFAULT diff --git a/lib/model/migrations/20250221-01-deletedAt-index-entity.js b/lib/model/migrations/20250221-01-deletedAt-index-entity.js index b0fc599b8..5c092eb13 100644 --- a/lib/model/migrations/20250221-01-deletedAt-index-entity.js +++ b/lib/model/migrations/20250221-01-deletedAt-index-entity.js @@ -9,13 +9,13 @@ const up = async (db) => { - await db.raw('CREATE INDEX entities_deletedat_index ON public.entities USING btree ("deletedAt")'); - await db.raw('CREATE INDEX entity_defs_sourceid_index ON public.entity_defs USING btree ("sourceId");'); + await db.query('CREATE INDEX entities_deletedat_index ON public.entities USING btree ("deletedAt")'); + await db.query('CREATE INDEX entity_defs_sourceid_index ON public.entity_defs USING btree ("sourceId");'); }; const down = async (db) => { - await db.raw('DROP INDEX entities_deletedat_index'); - await db.raw('DROP INDEX entity_defs_sourceid_index'); + await db.query('DROP INDEX entities_deletedat_index'); + await db.query('DROP INDEX entity_defs_sourceid_index'); }; module.exports = { up, down }; diff --git a/lib/model/migrations/20250307-01-purged-entities-table.js b/lib/model/migrations/20250307-01-purged-entities-table.js index e3a8cc476..c8952fb45 100644 --- a/lib/model/migrations/20250307-01-purged-entities-table.js +++ b/lib/model/migrations/20250307-01-purged-entities-table.js @@ -8,18 +8,18 @@ // except according to the terms contained in the LICENSE file. const up = async (db) => { - await db.raw(` + await db.query(` CREATE TABLE purged_entities ( "entityUuid" varchar NOT NULL, "acteeId" varchar NOT NULL, "auditId" serial4 NOT NULL, CONSTRAINT purged_entities_pkey PRIMARY KEY ("entityUuid") );`); - await db.raw(`CREATE INDEX purged_entities_actee_uuid_index ON purged_entities ("acteeId", "entityUuid");`); + await db.query(`CREATE INDEX purged_entities_actee_uuid_index ON purged_entities ("acteeId", "entityUuid");`); }; const down = async (db) => { - await db.raw(`DROP TABLE purged_entities`); + await db.query(`DROP TABLE purged_entities`); }; module.exports = { up, down }; diff --git a/lib/model/migrations/20250415-01-client-audit-remainder.js b/lib/model/migrations/20250415-01-client-audit-remainder.js index 2476771ad..78c5e9935 100644 --- a/lib/model/migrations/20250415-01-client-audit-remainder.js +++ b/lib/model/migrations/20250415-01-client-audit-remainder.js @@ -8,13 +8,13 @@ // except according to the terms contained in the LICENSE file. const up = async (db) => { - await db.raw(` + await db.query(` ALTER TABLE client_audits ADD COLUMN "user" TEXT, ADD COLUMN "change-reason" TEXT `); - await db.raw(` + await db.query(` UPDATE client_audits SET "user" = remainder->>'user', @@ -25,7 +25,7 @@ const up = async (db) => { }; const down = async (db) => { - await db.raw(` + await db.query(` UPDATE client_audits SET remainder = jsonb_set( @@ -40,7 +40,7 @@ const down = async (db) => { WHERE "user" IS NOT NULL OR "change-reason" IS NOT NULL `); - await db.raw(` + await db.query(` ALTER TABLE client_audits DROP COLUMN "user", DROP COLUMN "change-reason" diff --git a/lib/model/migrations/20250428-01-audit-indices.js b/lib/model/migrations/20250428-01-audit-indices.js index 93ff5f8c7..ded31c542 100644 --- a/lib/model/migrations/20250428-01-audit-indices.js +++ b/lib/model/migrations/20250428-01-audit-indices.js @@ -8,7 +8,7 @@ // except according to the terms contained in the LICENSE file. const up = async (db) => { - await db.raw(` + await db.query(` CREATE INDEX audits_details_entity_uuid_index ON audits USING hash ((details->'entity'->>'uuid')); DROP INDEX audits_details_submission_index; CREATE INDEX audits_details_submission_index ON audits USING hash (((details -> 'submissionId')::integer)); @@ -16,7 +16,7 @@ const up = async (db) => { }; const down = async (db) => { - await db.raw(` + await db.query(` DROP INDEX audits_details_entity_uuid_index; DROP INDEX audits_details_submission_index; CREATE INDEX audits_details_submission_index ON audits USING gin (((details -> 'submissionId'::text))); diff --git a/lib/model/migrations/20170920-01-initial.js b/lib/model/migrations/legacy/20170920-01-initial.js similarity index 100% rename from lib/model/migrations/20170920-01-initial.js rename to lib/model/migrations/legacy/20170920-01-initial.js diff --git a/lib/model/migrations/20171010-01-auth.js b/lib/model/migrations/legacy/20171010-01-auth.js similarity index 100% rename from lib/model/migrations/20171010-01-auth.js rename to lib/model/migrations/legacy/20171010-01-auth.js diff --git a/lib/model/migrations/20171023-01-authz-forms.js b/lib/model/migrations/legacy/20171023-01-authz-forms.js similarity index 100% rename from lib/model/migrations/20171023-01-authz-forms.js rename to lib/model/migrations/legacy/20171023-01-authz-forms.js diff --git a/lib/model/migrations/20171030-01-add-default-authz-records.js b/lib/model/migrations/legacy/20171030-01-add-default-authz-records.js similarity index 100% rename from lib/model/migrations/20171030-01-add-default-authz-records.js rename to lib/model/migrations/legacy/20171030-01-add-default-authz-records.js diff --git a/lib/model/migrations/20171106-01-remove-user-update-timestamp.js b/lib/model/migrations/legacy/20171106-01-remove-user-update-timestamp.js similarity index 100% rename from lib/model/migrations/20171106-01-remove-user-update-timestamp.js rename to lib/model/migrations/legacy/20171106-01-remove-user-update-timestamp.js diff --git a/lib/model/migrations/20171121-01-add-submissions-constraint.js b/lib/model/migrations/legacy/20171121-01-add-submissions-constraint.js similarity index 100% rename from lib/model/migrations/20171121-01-add-submissions-constraint.js rename to lib/model/migrations/legacy/20171121-01-add-submissions-constraint.js diff --git a/lib/model/migrations/20171121-02-add-submitter.js b/lib/model/migrations/legacy/20171121-02-add-submitter.js similarity index 100% rename from lib/model/migrations/20171121-02-add-submitter.js rename to lib/model/migrations/legacy/20171121-02-add-submitter.js diff --git a/lib/model/migrations/20171213-01-unrequire-display-name.js b/lib/model/migrations/legacy/20171213-01-unrequire-display-name.js similarity index 100% rename from lib/model/migrations/20171213-01-unrequire-display-name.js rename to lib/model/migrations/legacy/20171213-01-unrequire-display-name.js diff --git a/lib/model/migrations/20180108-01-expiring-actors.js b/lib/model/migrations/legacy/20180108-01-expiring-actors.js similarity index 100% rename from lib/model/migrations/20180108-01-expiring-actors.js rename to lib/model/migrations/legacy/20180108-01-expiring-actors.js diff --git a/lib/model/migrations/20180108-02-enum-to-varchar.js b/lib/model/migrations/legacy/20180108-02-enum-to-varchar.js similarity index 100% rename from lib/model/migrations/20180108-02-enum-to-varchar.js rename to lib/model/migrations/legacy/20180108-02-enum-to-varchar.js diff --git a/lib/model/migrations/20180112-01-audit-table.js b/lib/model/migrations/legacy/20180112-01-audit-table.js similarity index 100% rename from lib/model/migrations/20180112-01-audit-table.js rename to lib/model/migrations/legacy/20180112-01-audit-table.js diff --git a/lib/model/migrations/20180112-02-add-field-keys.js b/lib/model/migrations/legacy/20180112-02-add-field-keys.js similarity index 100% rename from lib/model/migrations/20180112-02-add-field-keys.js rename to lib/model/migrations/legacy/20180112-02-add-field-keys.js diff --git a/lib/model/migrations/20180118-01-rerequire-display-name.js b/lib/model/migrations/legacy/20180118-01-rerequire-display-name.js similarity index 100% rename from lib/model/migrations/20180118-01-rerequire-display-name.js rename to lib/model/migrations/legacy/20180118-01-rerequire-display-name.js diff --git a/lib/model/migrations/20180125-01-add-form-detail-fields.js b/lib/model/migrations/legacy/20180125-01-add-form-detail-fields.js similarity index 100% rename from lib/model/migrations/20180125-01-add-form-detail-fields.js rename to lib/model/migrations/legacy/20180125-01-add-form-detail-fields.js diff --git a/lib/model/migrations/20180125-02-more-field-key-grants.js b/lib/model/migrations/legacy/20180125-02-more-field-key-grants.js similarity index 100% rename from lib/model/migrations/20180125-02-more-field-key-grants.js rename to lib/model/migrations/legacy/20180125-02-more-field-key-grants.js diff --git a/lib/model/migrations/20180125-03-add-blob-tables.js b/lib/model/migrations/legacy/20180125-03-add-blob-tables.js similarity index 100% rename from lib/model/migrations/20180125-03-add-blob-tables.js rename to lib/model/migrations/legacy/20180125-03-add-blob-tables.js diff --git a/lib/model/migrations/20180301-01-configuration.js b/lib/model/migrations/legacy/20180301-01-configuration.js similarity index 100% rename from lib/model/migrations/20180301-01-configuration.js rename to lib/model/migrations/legacy/20180301-01-configuration.js diff --git a/lib/model/migrations/20180322-01-additional-form-options.js b/lib/model/migrations/legacy/20180322-01-additional-form-options.js similarity index 100% rename from lib/model/migrations/20180322-01-additional-form-options.js rename to lib/model/migrations/legacy/20180322-01-additional-form-options.js diff --git a/lib/model/migrations/20180327-01-update-form-constraints.js b/lib/model/migrations/legacy/20180327-01-update-form-constraints.js similarity index 100% rename from lib/model/migrations/20180327-01-update-form-constraints.js rename to lib/model/migrations/legacy/20180327-01-update-form-constraints.js diff --git a/lib/model/migrations/20180501-01-add-configs-timestamp.js b/lib/model/migrations/legacy/20180501-01-add-configs-timestamp.js similarity index 100% rename from lib/model/migrations/20180501-01-add-configs-timestamp.js rename to lib/model/migrations/legacy/20180501-01-add-configs-timestamp.js diff --git a/lib/model/migrations/20180501-02-fix-date-columns.js b/lib/model/migrations/legacy/20180501-02-fix-date-columns.js similarity index 100% rename from lib/model/migrations/20180501-02-fix-date-columns.js rename to lib/model/migrations/legacy/20180501-02-fix-date-columns.js diff --git a/lib/model/migrations/20180515-01-enforce-nonnull-form-version.js b/lib/model/migrations/legacy/20180515-01-enforce-nonnull-form-version.js similarity index 100% rename from lib/model/migrations/20180515-01-enforce-nonnull-form-version.js rename to lib/model/migrations/legacy/20180515-01-enforce-nonnull-form-version.js diff --git a/lib/model/migrations/20180727-01-rename-attachments-table.js b/lib/model/migrations/legacy/20180727-01-rename-attachments-table.js similarity index 100% rename from lib/model/migrations/20180727-01-rename-attachments-table.js rename to lib/model/migrations/legacy/20180727-01-rename-attachments-table.js diff --git a/lib/model/migrations/20180727-02-add-md5-to-blobs.js b/lib/model/migrations/legacy/20180727-02-add-md5-to-blobs.js similarity index 91% rename from lib/model/migrations/20180727-02-add-md5-to-blobs.js rename to lib/model/migrations/legacy/20180727-02-add-md5-to-blobs.js index c66eb52bb..5fa121135 100644 --- a/lib/model/migrations/20180727-02-add-md5-to-blobs.js +++ b/lib/model/migrations/legacy/20180727-02-add-md5-to-blobs.js @@ -8,7 +8,7 @@ // except according to the terms contained in the LICENSE file. // -const { md5sum } = require('../../util/crypto'); // eslint-disable-line no-restricted-modules +const { md5sum } = require('../../../util/crypto'); // eslint-disable-line no-restricted-modules const up = (knex) => knex.schema.table('blobs', (blobs) => { blobs.string('md5', 32); }) diff --git a/lib/model/migrations/20180727-03-add-form-attachments-table.js b/lib/model/migrations/legacy/20180727-03-add-form-attachments-table.js similarity index 93% rename from lib/model/migrations/20180727-03-add-form-attachments-table.js rename to lib/model/migrations/legacy/20180727-03-add-form-attachments-table.js index 7334d567f..286099e6c 100644 --- a/lib/model/migrations/20180727-03-add-form-attachments-table.js +++ b/lib/model/migrations/legacy/20180727-03-add-form-attachments-table.js @@ -23,7 +23,7 @@ const up = (knex) => fa.index([ 'formId' ]); }).then(() => { - const { expectedFormAttachments } = require('../../data/schema'); // eslint-disable-line no-restricted-modules + const { expectedFormAttachments } = require('../../../data/schema'); // eslint-disable-line no-restricted-modules const { uniq, pluck } = require('ramda'); // now add all expected attachments on extant forms. diff --git a/lib/model/migrations/20181011-make-email-case-insensitive.js b/lib/model/migrations/legacy/20181011-make-email-case-insensitive.js similarity index 100% rename from lib/model/migrations/20181011-make-email-case-insensitive.js rename to lib/model/migrations/legacy/20181011-make-email-case-insensitive.js diff --git a/lib/model/migrations/20181012-01-add-submissions-createdat-index.js b/lib/model/migrations/legacy/20181012-01-add-submissions-createdat-index.js similarity index 100% rename from lib/model/migrations/20181012-01-add-submissions-createdat-index.js rename to lib/model/migrations/legacy/20181012-01-add-submissions-createdat-index.js diff --git a/lib/model/migrations/20181206-01-add-projects.js b/lib/model/migrations/legacy/20181206-01-add-projects.js similarity index 100% rename from lib/model/migrations/20181206-01-add-projects.js rename to lib/model/migrations/legacy/20181206-01-add-projects.js diff --git a/lib/model/migrations/20181207-01-grant-verbs-to-text.js b/lib/model/migrations/legacy/20181207-01-grant-verbs-to-text.js similarity index 100% rename from lib/model/migrations/20181207-01-grant-verbs-to-text.js rename to lib/model/migrations/legacy/20181207-01-grant-verbs-to-text.js diff --git a/lib/model/migrations/20181207-02-rename-grant-verbs.js b/lib/model/migrations/legacy/20181207-02-rename-grant-verbs.js similarity index 100% rename from lib/model/migrations/20181207-02-rename-grant-verbs.js rename to lib/model/migrations/legacy/20181207-02-rename-grant-verbs.js diff --git a/lib/model/migrations/20181211-01-audit-verbs-to-text.js b/lib/model/migrations/legacy/20181211-01-audit-verbs-to-text.js similarity index 100% rename from lib/model/migrations/20181211-01-audit-verbs-to-text.js rename to lib/model/migrations/legacy/20181211-01-audit-verbs-to-text.js diff --git a/lib/model/migrations/20181211-02-rename-audit-actions.js b/lib/model/migrations/legacy/20181211-02-rename-audit-actions.js similarity index 100% rename from lib/model/migrations/20181211-02-rename-audit-actions.js rename to lib/model/migrations/legacy/20181211-02-rename-audit-actions.js diff --git a/lib/model/migrations/20181212-00-fix-user-type.js b/lib/model/migrations/legacy/20181212-00-fix-user-type.js similarity index 100% rename from lib/model/migrations/20181212-00-fix-user-type.js rename to lib/model/migrations/legacy/20181212-00-fix-user-type.js diff --git a/lib/model/migrations/20181212-01-add-roles.js b/lib/model/migrations/legacy/20181212-01-add-roles.js similarity index 100% rename from lib/model/migrations/20181212-01-add-roles.js rename to lib/model/migrations/legacy/20181212-01-add-roles.js diff --git a/lib/model/migrations/20181212-02-remove-groups.js b/lib/model/migrations/legacy/20181212-02-remove-groups.js similarity index 100% rename from lib/model/migrations/20181212-02-remove-groups.js rename to lib/model/migrations/legacy/20181212-02-remove-groups.js diff --git a/lib/model/migrations/20181212-03-add-single-use-roles.js b/lib/model/migrations/legacy/20181212-03-add-single-use-roles.js similarity index 100% rename from lib/model/migrations/20181212-03-add-single-use-roles.js rename to lib/model/migrations/legacy/20181212-03-add-single-use-roles.js diff --git a/lib/model/migrations/20181219-01-add-submission-update-verb.js b/lib/model/migrations/legacy/20181219-01-add-submission-update-verb.js similarity index 100% rename from lib/model/migrations/20181219-01-add-submission-update-verb.js rename to lib/model/migrations/legacy/20181219-01-add-submission-update-verb.js diff --git a/lib/model/migrations/20181221-01-nullable-submission-blobs.js b/lib/model/migrations/legacy/20181221-01-nullable-submission-blobs.js similarity index 100% rename from lib/model/migrations/20181221-01-nullable-submission-blobs.js rename to lib/model/migrations/legacy/20181221-01-nullable-submission-blobs.js diff --git a/lib/model/migrations/20181230-01-add-device-id-to-submission.js b/lib/model/migrations/legacy/20181230-01-add-device-id-to-submission.js similarity index 100% rename from lib/model/migrations/20181230-01-add-device-id-to-submission.js rename to lib/model/migrations/legacy/20181230-01-add-device-id-to-submission.js diff --git a/lib/model/migrations/20190225-01-add-actor-trigram-indices.js b/lib/model/migrations/legacy/20190225-01-add-actor-trigram-indices.js similarity index 100% rename from lib/model/migrations/20190225-01-add-actor-trigram-indices.js rename to lib/model/migrations/legacy/20190225-01-add-actor-trigram-indices.js diff --git a/lib/model/migrations/20190225-02-add-role-grants.js b/lib/model/migrations/legacy/20190225-02-add-role-grants.js similarity index 100% rename from lib/model/migrations/20190225-02-add-role-grants.js rename to lib/model/migrations/legacy/20190225-02-add-role-grants.js diff --git a/lib/model/migrations/20190226-01-convert-verbs-to-jsonb.js b/lib/model/migrations/legacy/20190226-01-convert-verbs-to-jsonb.js similarity index 100% rename from lib/model/migrations/20190226-01-convert-verbs-to-jsonb.js rename to lib/model/migrations/legacy/20190226-01-convert-verbs-to-jsonb.js diff --git a/lib/model/migrations/20190226-02-add-role-actee-species.js b/lib/model/migrations/legacy/20190226-02-add-role-actee-species.js similarity index 100% rename from lib/model/migrations/20190226-02-add-role-actee-species.js rename to lib/model/migrations/legacy/20190226-02-add-role-actee-species.js diff --git a/lib/model/migrations/20190226-03-add-assignment-verbs.js b/lib/model/migrations/legacy/20190226-03-add-assignment-verbs.js similarity index 100% rename from lib/model/migrations/20190226-03-add-assignment-verbs.js rename to lib/model/migrations/legacy/20190226-03-add-assignment-verbs.js diff --git a/lib/model/migrations/20190226-04-add-assignment-actee-species.js b/lib/model/migrations/legacy/20190226-04-add-assignment-actee-species.js similarity index 100% rename from lib/model/migrations/20190226-04-add-assignment-actee-species.js rename to lib/model/migrations/legacy/20190226-04-add-assignment-actee-species.js diff --git a/lib/model/migrations/20190227-01-add-project-manager-role.js b/lib/model/migrations/legacy/20190227-01-add-project-manager-role.js similarity index 100% rename from lib/model/migrations/20190227-01-add-project-manager-role.js rename to lib/model/migrations/legacy/20190227-01-add-project-manager-role.js diff --git a/lib/model/migrations/20190405-01-add-project-archival-flag.js b/lib/model/migrations/legacy/20190405-01-add-project-archival-flag.js similarity index 100% rename from lib/model/migrations/20190405-01-add-project-archival-flag.js rename to lib/model/migrations/legacy/20190405-01-add-project-archival-flag.js diff --git a/lib/model/migrations/20190416-01-email-uniqueness.js b/lib/model/migrations/legacy/20190416-01-email-uniqueness.js similarity index 100% rename from lib/model/migrations/20190416-01-email-uniqueness.js rename to lib/model/migrations/legacy/20190416-01-email-uniqueness.js diff --git a/lib/model/migrations/20190416-02-add-user-delete-verb.js b/lib/model/migrations/legacy/20190416-02-add-user-delete-verb.js similarity index 100% rename from lib/model/migrations/20190416-02-add-user-delete-verb.js rename to lib/model/migrations/legacy/20190416-02-add-user-delete-verb.js diff --git a/lib/model/migrations/20190520-01-add-form-versioning.js b/lib/model/migrations/legacy/20190520-01-add-form-versioning.js similarity index 99% rename from lib/model/migrations/20190520-01-add-form-versioning.js rename to lib/model/migrations/legacy/20190520-01-add-form-versioning.js index 9148d1ac6..623e966c9 100644 --- a/lib/model/migrations/20190520-01-add-form-versioning.js +++ b/lib/model/migrations/legacy/20190520-01-add-form-versioning.js @@ -7,7 +7,7 @@ // including this file, may be copied, modified, propagated, or distributed // except according to the terms contained in the LICENSE file. -const { shasum, sha256sum } = require('../../util/crypto'); // eslint-disable-line no-restricted-modules +const { shasum, sha256sum } = require('../../../util/crypto'); // eslint-disable-line no-restricted-modules const assert = require('assert').strict; const check = (message, query) => diff --git a/lib/model/migrations/20190523-01-add-form-state-constraint.js b/lib/model/migrations/legacy/20190523-01-add-form-state-constraint.js similarity index 100% rename from lib/model/migrations/20190523-01-add-form-state-constraint.js rename to lib/model/migrations/legacy/20190523-01-add-form-state-constraint.js diff --git a/lib/model/migrations/20190605-01-reformat-audits.js b/lib/model/migrations/legacy/20190605-01-reformat-audits.js similarity index 100% rename from lib/model/migrations/20190605-01-reformat-audits.js rename to lib/model/migrations/legacy/20190605-01-reformat-audits.js diff --git a/lib/model/migrations/20190607-01-convert-audit-details-to-jsonb.js b/lib/model/migrations/legacy/20190607-01-convert-audit-details-to-jsonb.js similarity index 100% rename from lib/model/migrations/20190607-01-convert-audit-details-to-jsonb.js rename to lib/model/migrations/legacy/20190607-01-convert-audit-details-to-jsonb.js diff --git a/lib/model/migrations/20190607-02-standardize-attachment-actees.js b/lib/model/migrations/legacy/20190607-02-standardize-attachment-actees.js similarity index 100% rename from lib/model/migrations/20190607-02-standardize-attachment-actees.js rename to lib/model/migrations/legacy/20190607-02-standardize-attachment-actees.js diff --git a/lib/model/migrations/20190607-03-rename-sub-attachment-audits.js b/lib/model/migrations/legacy/20190607-03-rename-sub-attachment-audits.js similarity index 100% rename from lib/model/migrations/20190607-03-rename-sub-attachment-audits.js rename to lib/model/migrations/legacy/20190607-03-rename-sub-attachment-audits.js diff --git a/lib/model/migrations/20190610-01-add-audits-verbs.js b/lib/model/migrations/legacy/20190610-01-add-audits-verbs.js similarity index 100% rename from lib/model/migrations/20190610-01-add-audits-verbs.js rename to lib/model/migrations/legacy/20190610-01-add-audits-verbs.js diff --git a/lib/model/migrations/20190610-02-backfill-submission-audit-instanceids.js b/lib/model/migrations/legacy/20190610-02-backfill-submission-audit-instanceids.js similarity index 100% rename from lib/model/migrations/20190610-02-backfill-submission-audit-instanceids.js rename to lib/model/migrations/legacy/20190610-02-backfill-submission-audit-instanceids.js diff --git a/lib/model/migrations/20190611-01-add-updatedat-to-form-attachments.js b/lib/model/migrations/legacy/20190611-01-add-updatedat-to-form-attachments.js similarity index 100% rename from lib/model/migrations/20190611-01-add-updatedat-to-form-attachments.js rename to lib/model/migrations/legacy/20190611-01-add-updatedat-to-form-attachments.js diff --git a/lib/model/migrations/20190618-01-add-csrf-token.js b/lib/model/migrations/legacy/20190618-01-add-csrf-token.js similarity index 100% rename from lib/model/migrations/20190618-01-add-csrf-token.js rename to lib/model/migrations/legacy/20190618-01-add-csrf-token.js diff --git a/lib/model/migrations/20190618-01-add-encryption-tracking.js b/lib/model/migrations/legacy/20190618-01-add-encryption-tracking.js similarity index 100% rename from lib/model/migrations/20190618-01-add-encryption-tracking.js rename to lib/model/migrations/legacy/20190618-01-add-encryption-tracking.js diff --git a/lib/model/migrations/20190701-01-add-managed-encryption-key-check.js b/lib/model/migrations/legacy/20190701-01-add-managed-encryption-key-check.js similarity index 100% rename from lib/model/migrations/20190701-01-add-managed-encryption-key-check.js rename to lib/model/migrations/legacy/20190701-01-add-managed-encryption-key-check.js diff --git a/lib/model/migrations/20190916-01-granularize-app-user-permissions.js b/lib/model/migrations/legacy/20190916-01-granularize-app-user-permissions.js similarity index 100% rename from lib/model/migrations/20190916-01-granularize-app-user-permissions.js rename to lib/model/migrations/legacy/20190916-01-granularize-app-user-permissions.js diff --git a/lib/model/migrations/20190917-01-cleanup-app-user-role.js b/lib/model/migrations/legacy/20190917-01-cleanup-app-user-role.js similarity index 100% rename from lib/model/migrations/20190917-01-cleanup-app-user-role.js rename to lib/model/migrations/legacy/20190917-01-cleanup-app-user-role.js diff --git a/lib/model/migrations/20190923-01-add-project-viewer-role.js b/lib/model/migrations/legacy/20190923-01-add-project-viewer-role.js similarity index 100% rename from lib/model/migrations/20190923-01-add-project-viewer-role.js rename to lib/model/migrations/legacy/20190923-01-add-project-viewer-role.js diff --git a/lib/model/migrations/20190925-01-add-client-audits.js b/lib/model/migrations/legacy/20190925-01-add-client-audits.js similarity index 100% rename from lib/model/migrations/20190925-01-add-client-audits.js rename to lib/model/migrations/legacy/20190925-01-add-client-audits.js diff --git a/lib/model/migrations/20191007-01-backfill-client-audits.js b/lib/model/migrations/legacy/20191007-01-backfill-client-audits.js similarity index 89% rename from lib/model/migrations/20191007-01-backfill-client-audits.js rename to lib/model/migrations/legacy/20191007-01-backfill-client-audits.js index bb3c8ceef..5f79a3821 100644 --- a/lib/model/migrations/20191007-01-backfill-client-audits.js +++ b/lib/model/migrations/legacy/20191007-01-backfill-client-audits.js @@ -7,9 +7,9 @@ // including this file, may be copied, modified, propagated, or distributed // except according to the terms contained in the LICENSE file. -const { parseClientAudits } = require('../../data/client-audits'); // eslint-disable-line no-restricted-modules -const { getFormFields } = require('../../data/schema'); // eslint-disable-line no-restricted-modules -const { traverseXml, findOne, root, node, text } = require('../../util/xml'); // eslint-disable-line no-restricted-modules +const { parseClientAudits } = require('../../../data/client-audits'); // eslint-disable-line no-restricted-modules +const { getFormFields } = require('../../../data/schema'); // eslint-disable-line no-restricted-modules +const { traverseXml, findOne, root, node, text } = require('../../../util/xml'); // eslint-disable-line no-restricted-modules const up = (db) => new Promise((resolve, reject) => { const work = []; diff --git a/lib/model/migrations/20191010-01-add-excel-blob-reference.js b/lib/model/migrations/legacy/20191010-01-add-excel-blob-reference.js similarity index 100% rename from lib/model/migrations/20191010-01-add-excel-blob-reference.js rename to lib/model/migrations/legacy/20191010-01-add-excel-blob-reference.js diff --git a/lib/model/migrations/20191023-01-add-worker-columns-to-audits.js b/lib/model/migrations/legacy/20191023-01-add-worker-columns-to-audits.js similarity index 100% rename from lib/model/migrations/20191023-01-add-worker-columns-to-audits.js rename to lib/model/migrations/legacy/20191023-01-add-worker-columns-to-audits.js diff --git a/lib/model/migrations/20191025-01-add-id-to-audits.js b/lib/model/migrations/legacy/20191025-01-add-id-to-audits.js similarity index 100% rename from lib/model/migrations/20191025-01-add-id-to-audits.js rename to lib/model/migrations/legacy/20191025-01-add-id-to-audits.js diff --git a/lib/model/migrations/20191106-01-remove-deleted-actor-assignments.js b/lib/model/migrations/legacy/20191106-01-remove-deleted-actor-assignments.js similarity index 100% rename from lib/model/migrations/20191106-01-remove-deleted-actor-assignments.js rename to lib/model/migrations/legacy/20191106-01-remove-deleted-actor-assignments.js diff --git a/lib/model/migrations/20191231-01-remove-transformations.js b/lib/model/migrations/legacy/20191231-01-remove-transformations.js similarity index 100% rename from lib/model/migrations/20191231-01-remove-transformations.js rename to lib/model/migrations/legacy/20191231-01-remove-transformations.js diff --git a/lib/model/migrations/20191231-02-add-schema-storage.js b/lib/model/migrations/legacy/20191231-02-add-schema-storage.js similarity index 93% rename from lib/model/migrations/20191231-02-add-schema-storage.js rename to lib/model/migrations/legacy/20191231-02-add-schema-storage.js index f99a37c7a..aff5ca728 100644 --- a/lib/model/migrations/20191231-02-add-schema-storage.js +++ b/lib/model/migrations/legacy/20191231-02-add-schema-storage.js @@ -7,7 +7,7 @@ // including this file, may be copied, modified, propagated, or distributed // except according to the terms contained in the LICENSE file. -const { getFormFields } = require('../../data/schema'); // eslint-disable-line no-restricted-modules +const { getFormFields } = require('../../../data/schema'); // eslint-disable-line no-restricted-modules const up = async (db) => { await db.schema.createTable('form_fields', (fields) => { @@ -51,7 +51,7 @@ const up = async (db) => { // this config hardcoding would be dangerous with tests except that // tests will never invoke this path. const config = require('config').get('default.database'); - const db2 = require('../knex-migrator').knexConnect(config); // eslint-disable-line no-restricted-modules + const db2 = require('../../knex-migrator').knexConnect(config); // eslint-disable-line no-restricted-modules return db2.select('projectId', 'xmlFormId').from('forms').where({ currentDefId: formDefId }) .then(([{ projectId, xmlFormId }]) => { process.stderr.write(`\n!!!!\nThe database upgrade to v0.8 has failed because the Form '${xmlFormId}' in Project ${projectId} has an invalid schema. It tries to bind multiple instance nodes at the path ${path}.\n!!!!\n\n`); diff --git a/lib/model/migrations/20200110-01-add-drafts.js b/lib/model/migrations/legacy/20200110-01-add-drafts.js similarity index 100% rename from lib/model/migrations/20200110-01-add-drafts.js rename to lib/model/migrations/legacy/20200110-01-add-drafts.js diff --git a/lib/model/migrations/20200112-01-check-field-collisions.js b/lib/model/migrations/legacy/20200112-01-check-field-collisions.js similarity index 100% rename from lib/model/migrations/20200112-01-check-field-collisions.js rename to lib/model/migrations/legacy/20200112-01-check-field-collisions.js diff --git a/lib/model/migrations/20200114-01-remove-formid-sha256-constraint.js b/lib/model/migrations/legacy/20200114-01-remove-formid-sha256-constraint.js similarity index 100% rename from lib/model/migrations/20200114-01-remove-formid-sha256-constraint.js rename to lib/model/migrations/legacy/20200114-01-remove-formid-sha256-constraint.js diff --git a/lib/model/migrations/20200117-01-draft-test-submissions.js b/lib/model/migrations/legacy/20200117-01-draft-test-submissions.js similarity index 100% rename from lib/model/migrations/20200117-01-draft-test-submissions.js rename to lib/model/migrations/legacy/20200117-01-draft-test-submissions.js diff --git a/lib/model/migrations/20200121-01-add-draft-keys.js b/lib/model/migrations/legacy/20200121-01-add-draft-keys.js similarity index 100% rename from lib/model/migrations/20200121-01-add-draft-keys.js rename to lib/model/migrations/legacy/20200121-01-add-draft-keys.js diff --git a/lib/model/migrations/20200122-01-remove-draft-form-state.js b/lib/model/migrations/legacy/20200122-01-remove-draft-form-state.js similarity index 100% rename from lib/model/migrations/20200122-01-remove-draft-form-state.js rename to lib/model/migrations/legacy/20200122-01-remove-draft-form-state.js diff --git a/lib/model/migrations/20200129-01-cascade-submission-deletes.js b/lib/model/migrations/legacy/20200129-01-cascade-submission-deletes.js similarity index 100% rename from lib/model/migrations/20200129-01-cascade-submission-deletes.js rename to lib/model/migrations/legacy/20200129-01-cascade-submission-deletes.js diff --git a/lib/model/migrations/20200220-01-repair-submission-parsing.js b/lib/model/migrations/legacy/20200220-01-repair-submission-parsing.js similarity index 93% rename from lib/model/migrations/20200220-01-repair-submission-parsing.js rename to lib/model/migrations/legacy/20200220-01-repair-submission-parsing.js index 279977ce3..d4020bc25 100644 --- a/lib/model/migrations/20200220-01-repair-submission-parsing.js +++ b/lib/model/migrations/legacy/20200220-01-repair-submission-parsing.js @@ -7,7 +7,7 @@ // including this file, may be copied, modified, propagated, or distributed // except according to the terms contained in the LICENSE file. -const { Submission } = require('../frames'); // eslint-disable-line no-restricted-modules +const { Submission } = require('../../frames'); // eslint-disable-line no-restricted-modules const up = async (db) => { const work = []; diff --git a/lib/model/migrations/20200403-01-add-performance-indices.js b/lib/model/migrations/legacy/20200403-01-add-performance-indices.js similarity index 100% rename from lib/model/migrations/20200403-01-add-performance-indices.js rename to lib/model/migrations/legacy/20200403-01-add-performance-indices.js diff --git a/lib/model/migrations/20200407-01-allow-actorless-submission-defs.js b/lib/model/migrations/legacy/20200407-01-allow-actorless-submission-defs.js similarity index 100% rename from lib/model/migrations/20200407-01-allow-actorless-submission-defs.js rename to lib/model/migrations/legacy/20200407-01-allow-actorless-submission-defs.js diff --git a/lib/model/migrations/20200423-01-fix-field-insert-performance.js b/lib/model/migrations/legacy/20200423-01-fix-field-insert-performance.js similarity index 100% rename from lib/model/migrations/20200423-01-fix-field-insert-performance.js rename to lib/model/migrations/legacy/20200423-01-fix-field-insert-performance.js diff --git a/lib/model/migrations/20200428-01-allow-string-downcast.js b/lib/model/migrations/legacy/20200428-01-allow-string-downcast.js similarity index 100% rename from lib/model/migrations/20200428-01-allow-string-downcast.js rename to lib/model/migrations/legacy/20200428-01-allow-string-downcast.js diff --git a/lib/model/migrations/20200519-01-add-enketo-id.js b/lib/model/migrations/legacy/20200519-01-add-enketo-id.js similarity index 100% rename from lib/model/migrations/20200519-01-add-enketo-id.js rename to lib/model/migrations/legacy/20200519-01-add-enketo-id.js diff --git a/lib/model/migrations/20200519-02-add-form-viewer-role.js b/lib/model/migrations/legacy/20200519-02-add-form-viewer-role.js similarity index 100% rename from lib/model/migrations/20200519-02-add-form-viewer-role.js rename to lib/model/migrations/legacy/20200519-02-add-form-viewer-role.js diff --git a/lib/model/migrations/20200520-01-backfill-enketo.js b/lib/model/migrations/legacy/20200520-01-backfill-enketo.js similarity index 100% rename from lib/model/migrations/20200520-01-backfill-enketo.js rename to lib/model/migrations/legacy/20200520-01-backfill-enketo.js diff --git a/lib/model/migrations/20200715-01-add-data-collector-role.js b/lib/model/migrations/legacy/20200715-01-add-data-collector-role.js similarity index 100% rename from lib/model/migrations/20200715-01-add-data-collector-role.js rename to lib/model/migrations/legacy/20200715-01-add-data-collector-role.js diff --git a/lib/model/migrations/20200721-01-add-public-links.js b/lib/model/migrations/legacy/20200721-01-add-public-links.js similarity index 100% rename from lib/model/migrations/20200721-01-add-public-links.js rename to lib/model/migrations/legacy/20200721-01-add-public-links.js diff --git a/lib/model/migrations/20200728-01-add-enketo-single-token-to-forms.js b/lib/model/migrations/legacy/20200728-01-add-enketo-single-token-to-forms.js similarity index 100% rename from lib/model/migrations/20200728-01-add-enketo-single-token-to-forms.js rename to lib/model/migrations/legacy/20200728-01-add-enketo-single-token-to-forms.js diff --git a/lib/model/migrations/20200731-01-allow-project-managers-to-end-sessions.js b/lib/model/migrations/legacy/20200731-01-allow-project-managers-to-end-sessions.js similarity index 100% rename from lib/model/migrations/20200731-01-allow-project-managers-to-end-sessions.js rename to lib/model/migrations/legacy/20200731-01-allow-project-managers-to-end-sessions.js diff --git a/lib/model/migrations/20200810-01-reschedule-enketo-processing.js b/lib/model/migrations/legacy/20200810-01-reschedule-enketo-processing.js similarity index 100% rename from lib/model/migrations/20200810-01-reschedule-enketo-processing.js rename to lib/model/migrations/legacy/20200810-01-reschedule-enketo-processing.js diff --git a/lib/model/migrations/20200918-01-repair-publishedat-dates.js b/lib/model/migrations/legacy/20200918-01-repair-publishedat-dates.js similarity index 100% rename from lib/model/migrations/20200918-01-repair-publishedat-dates.js rename to lib/model/migrations/legacy/20200918-01-repair-publishedat-dates.js diff --git a/lib/model/migrations/20200930-01-add-backup-run-verb.js b/lib/model/migrations/legacy/20200930-01-add-backup-run-verb.js similarity index 100% rename from lib/model/migrations/20200930-01-add-backup-run-verb.js rename to lib/model/migrations/legacy/20200930-01-add-backup-run-verb.js diff --git a/lib/model/migrations/20201117-01-remove-deleted-actor-assignments-again.js b/lib/model/migrations/legacy/20201117-01-remove-deleted-actor-assignments-again.js similarity index 100% rename from lib/model/migrations/20201117-01-remove-deleted-actor-assignments-again.js rename to lib/model/migrations/legacy/20201117-01-remove-deleted-actor-assignments-again.js diff --git a/lib/model/migrations/20201207-01-harmonize-submitter-id-columns.js b/lib/model/migrations/legacy/20201207-01-harmonize-submitter-id-columns.js similarity index 100% rename from lib/model/migrations/20201207-01-harmonize-submitter-id-columns.js rename to lib/model/migrations/legacy/20201207-01-harmonize-submitter-id-columns.js diff --git a/lib/model/migrations/20210118-01-add-current-flag-to-submission-defs.js b/lib/model/migrations/legacy/20210118-01-add-current-flag-to-submission-defs.js similarity index 100% rename from lib/model/migrations/20210118-01-add-current-flag-to-submission-defs.js rename to lib/model/migrations/legacy/20210118-01-add-current-flag-to-submission-defs.js diff --git a/lib/model/migrations/20210120-01-instance-names.js b/lib/model/migrations/legacy/20210120-01-instance-names.js similarity index 93% rename from lib/model/migrations/20210120-01-instance-names.js rename to lib/model/migrations/legacy/20210120-01-instance-names.js index a16d515b4..6c6cde9d9 100644 --- a/lib/model/migrations/20210120-01-instance-names.js +++ b/lib/model/migrations/legacy/20210120-01-instance-names.js @@ -7,7 +7,7 @@ // including this file, may be copied, modified, propagated, or distributed // except according to the terms contained in the LICENSE file. -const { Submission } = require('../frames'); // eslint-disable-line no-restricted-modules +const { Submission } = require('../../frames'); // eslint-disable-line no-restricted-modules const up = async (db) => { await db.schema.table('submission_defs', (sds) => { diff --git a/lib/model/migrations/20210203-01-add-hierarchy-to-actees.js b/lib/model/migrations/legacy/20210203-01-add-hierarchy-to-actees.js similarity index 100% rename from lib/model/migrations/20210203-01-add-hierarchy-to-actees.js rename to lib/model/migrations/legacy/20210203-01-add-hierarchy-to-actees.js diff --git a/lib/model/migrations/20210210-01-add-instanceid-to-submission-defs.js b/lib/model/migrations/legacy/20210210-01-add-instanceid-to-submission-defs.js similarity index 100% rename from lib/model/migrations/20210210-01-add-instanceid-to-submission-defs.js rename to lib/model/migrations/legacy/20210210-01-add-instanceid-to-submission-defs.js diff --git a/lib/model/migrations/20210218-01-add-submission-edit-verbs.js b/lib/model/migrations/legacy/20210218-01-add-submission-edit-verbs.js similarity index 100% rename from lib/model/migrations/20210218-01-add-submission-edit-verbs.js rename to lib/model/migrations/legacy/20210218-01-add-submission-edit-verbs.js diff --git a/lib/model/migrations/20210218-02-add-draft-to-submissions-unique.js b/lib/model/migrations/legacy/20210218-02-add-draft-to-submissions-unique.js similarity index 100% rename from lib/model/migrations/20210218-02-add-draft-to-submissions-unique.js rename to lib/model/migrations/legacy/20210218-02-add-draft-to-submissions-unique.js diff --git a/lib/model/migrations/20210219-01-add-review-state.js b/lib/model/migrations/legacy/20210219-01-add-review-state.js similarity index 100% rename from lib/model/migrations/20210219-01-add-review-state.js rename to lib/model/migrations/legacy/20210219-01-add-review-state.js diff --git a/lib/model/migrations/20210219-02-add-notes-and-index-to-audits.js b/lib/model/migrations/legacy/20210219-02-add-notes-and-index-to-audits.js similarity index 100% rename from lib/model/migrations/20210219-02-add-notes-and-index-to-audits.js rename to lib/model/migrations/legacy/20210219-02-add-notes-and-index-to-audits.js diff --git a/lib/model/migrations/20210324-01-add-submission-edit-verbs-to-managers.js b/lib/model/migrations/legacy/20210324-01-add-submission-edit-verbs-to-managers.js similarity index 100% rename from lib/model/migrations/20210324-01-add-submission-edit-verbs-to-managers.js rename to lib/model/migrations/legacy/20210324-01-add-submission-edit-verbs-to-managers.js diff --git a/lib/model/migrations/20210325-01-remove-project.list-verb.js b/lib/model/migrations/legacy/20210325-01-remove-project.list-verb.js similarity index 100% rename from lib/model/migrations/20210325-01-remove-project.list-verb.js rename to lib/model/migrations/legacy/20210325-01-remove-project.list-verb.js diff --git a/lib/model/migrations/20210408-01-drop-public-link-createdat.js b/lib/model/migrations/legacy/20210408-01-drop-public-link-createdat.js similarity index 100% rename from lib/model/migrations/20210408-01-drop-public-link-createdat.js rename to lib/model/migrations/legacy/20210408-01-drop-public-link-createdat.js diff --git a/lib/model/migrations/20210408-02-backfill-specialized-actor-audits.js b/lib/model/migrations/legacy/20210408-02-backfill-specialized-actor-audits.js similarity index 100% rename from lib/model/migrations/20210408-02-backfill-specialized-actor-audits.js rename to lib/model/migrations/legacy/20210408-02-backfill-specialized-actor-audits.js diff --git a/lib/model/migrations/20210409-01-add-comments.js b/lib/model/migrations/legacy/20210409-01-add-comments.js similarity index 100% rename from lib/model/migrations/20210409-01-add-comments.js rename to lib/model/migrations/legacy/20210409-01-add-comments.js diff --git a/lib/model/migrations/20210409-02-update-review-states.js b/lib/model/migrations/legacy/20210409-02-update-review-states.js similarity index 100% rename from lib/model/migrations/20210409-02-update-review-states.js rename to lib/model/migrations/legacy/20210409-02-update-review-states.js diff --git a/lib/model/migrations/20210423-01-add-name-to-form-def.js b/lib/model/migrations/legacy/20210423-01-add-name-to-form-def.js similarity index 100% rename from lib/model/migrations/20210423-01-add-name-to-form-def.js rename to lib/model/migrations/legacy/20210423-01-add-name-to-form-def.js diff --git a/lib/model/migrations/20210423-02-drop-form-name.js b/lib/model/migrations/legacy/20210423-02-drop-form-name.js similarity index 100% rename from lib/model/migrations/20210423-02-drop-form-name.js rename to lib/model/migrations/legacy/20210423-02-drop-form-name.js diff --git a/lib/model/migrations/20210716-01-config-value-jsonb.js b/lib/model/migrations/legacy/20210716-01-config-value-jsonb.js similarity index 100% rename from lib/model/migrations/20210716-01-config-value-jsonb.js rename to lib/model/migrations/legacy/20210716-01-config-value-jsonb.js diff --git a/lib/model/migrations/20210721-01-add-config-set-verb.js b/lib/model/migrations/legacy/20210721-01-add-config-set-verb.js similarity index 100% rename from lib/model/migrations/20210721-01-add-config-set-verb.js rename to lib/model/migrations/legacy/20210721-01-add-config-set-verb.js diff --git a/lib/model/migrations/20210817-01-disallow-structure-downcast-to-string.js b/lib/model/migrations/legacy/20210817-01-disallow-structure-downcast-to-string.js similarity index 100% rename from lib/model/migrations/20210817-01-disallow-structure-downcast-to-string.js rename to lib/model/migrations/legacy/20210817-01-disallow-structure-downcast-to-string.js diff --git a/lib/model/migrations/20210825-01-add-analytics-read-verb.js b/lib/model/migrations/legacy/20210825-01-add-analytics-read-verb.js similarity index 100% rename from lib/model/migrations/20210825-01-add-analytics-read-verb.js rename to lib/model/migrations/legacy/20210825-01-add-analytics-read-verb.js diff --git a/lib/model/migrations/20210903-01-backfill-encrypted-client-audits.js b/lib/model/migrations/legacy/20210903-01-backfill-encrypted-client-audits.js similarity index 100% rename from lib/model/migrations/20210903-01-backfill-encrypted-client-audits.js rename to lib/model/migrations/legacy/20210903-01-backfill-encrypted-client-audits.js diff --git a/lib/model/migrations/20210927-01-revert-disallow-structure-downcast.js b/lib/model/migrations/legacy/20210927-01-revert-disallow-structure-downcast.js similarity index 100% rename from lib/model/migrations/20210927-01-revert-disallow-structure-downcast.js rename to lib/model/migrations/legacy/20210927-01-revert-disallow-structure-downcast.js diff --git a/lib/model/migrations/20211008-01-track-select-many-options.js b/lib/model/migrations/legacy/20211008-01-track-select-many-options.js similarity index 89% rename from lib/model/migrations/20211008-01-track-select-many-options.js rename to lib/model/migrations/legacy/20211008-01-track-select-many-options.js index 62397fc8f..09f1efa92 100644 --- a/lib/model/migrations/20211008-01-track-select-many-options.js +++ b/lib/model/migrations/legacy/20211008-01-track-select-many-options.js @@ -8,10 +8,10 @@ // except according to the terms contained in the LICENSE file. const { map } = require('ramda'); -const { getFormFields } = require('../../data/schema'); // eslint-disable-line no-restricted-modules -const { getSelectMultipleResponses } = require('../../data/submission'); // eslint-disable-line no-restricted-modules -const { Form } = require('../frames'); // eslint-disable-line no-restricted-modules -const { construct } = require('../../util/util'); // eslint-disable-line no-restricted-modules +const { getFormFields } = require('../../../data/schema'); // eslint-disable-line no-restricted-modules +const { getSelectMultipleResponses } = require('../../../data/submission'); // eslint-disable-line no-restricted-modules +const { Form } = require('../../frames'); // eslint-disable-line no-restricted-modules +const { construct } = require('../../../util/util'); // eslint-disable-line no-restricted-modules const up = async (db) => { // add select many flag, options field to fields diff --git a/lib/model/migrations/20211021-remove-hashes-from-audits.js b/lib/model/migrations/legacy/20211021-remove-hashes-from-audits.js similarity index 100% rename from lib/model/migrations/20211021-remove-hashes-from-audits.js rename to lib/model/migrations/legacy/20211021-remove-hashes-from-audits.js diff --git a/lib/model/migrations/20211109-01-add-user-agent-to-submissions.js b/lib/model/migrations/legacy/20211109-01-add-user-agent-to-submissions.js similarity index 100% rename from lib/model/migrations/20211109-01-add-user-agent-to-submissions.js rename to lib/model/migrations/legacy/20211109-01-add-user-agent-to-submissions.js diff --git a/lib/model/migrations/20211114-01-flag-initial-submission-def.js b/lib/model/migrations/legacy/20211114-01-flag-initial-submission-def.js similarity index 100% rename from lib/model/migrations/20211114-01-flag-initial-submission-def.js rename to lib/model/migrations/legacy/20211114-01-flag-initial-submission-def.js diff --git a/lib/model/migrations/20211117-01-add-form-restore-verb.js b/lib/model/migrations/legacy/20211117-01-add-form-restore-verb.js similarity index 100% rename from lib/model/migrations/20211117-01-add-form-restore-verb.js rename to lib/model/migrations/legacy/20211117-01-add-form-restore-verb.js diff --git a/lib/model/migrations/20211129-01-add-purged-details-to-actees.js b/lib/model/migrations/legacy/20211129-01-add-purged-details-to-actees.js similarity index 100% rename from lib/model/migrations/20211129-01-add-purged-details-to-actees.js rename to lib/model/migrations/legacy/20211129-01-add-purged-details-to-actees.js diff --git a/lib/model/migrations/20220121-01-form-cascade-delete.js b/lib/model/migrations/legacy/20220121-01-form-cascade-delete.js similarity index 100% rename from lib/model/migrations/20220121-01-form-cascade-delete.js rename to lib/model/migrations/legacy/20220121-01-form-cascade-delete.js diff --git a/lib/model/migrations/20220121-02-purge-deleted-forms.js b/lib/model/migrations/legacy/20220121-02-purge-deleted-forms.js similarity index 100% rename from lib/model/migrations/20220121-02-purge-deleted-forms.js rename to lib/model/migrations/legacy/20220121-02-purge-deleted-forms.js diff --git a/lib/model/migrations/20220209-01-purge-unneeded-drafts.js b/lib/model/migrations/legacy/20220209-01-purge-unneeded-drafts.js similarity index 100% rename from lib/model/migrations/20220209-01-purge-unneeded-drafts.js rename to lib/model/migrations/legacy/20220209-01-purge-unneeded-drafts.js diff --git a/lib/model/migrations/20220309-01-add-project-description.js b/lib/model/migrations/legacy/20220309-01-add-project-description.js similarity index 100% rename from lib/model/migrations/20220309-01-add-project-description.js rename to lib/model/migrations/legacy/20220309-01-add-project-description.js diff --git a/lib/model/migrations/20220803-01-create-entities-schema.js b/lib/model/migrations/legacy/20220803-01-create-entities-schema.js similarity index 100% rename from lib/model/migrations/20220803-01-create-entities-schema.js rename to lib/model/migrations/legacy/20220803-01-create-entities-schema.js diff --git a/lib/model/migrations/20221003-01-add-dataset-verbs.js b/lib/model/migrations/legacy/20221003-01-add-dataset-verbs.js similarity index 100% rename from lib/model/migrations/20221003-01-add-dataset-verbs.js rename to lib/model/migrations/legacy/20221003-01-add-dataset-verbs.js diff --git a/lib/model/migrations/20221114-01-explict-dataset-publish.js b/lib/model/migrations/legacy/20221114-01-explict-dataset-publish.js similarity index 100% rename from lib/model/migrations/20221114-01-explict-dataset-publish.js rename to lib/model/migrations/legacy/20221114-01-explict-dataset-publish.js diff --git a/lib/model/migrations/20221117-01-check-datasetId-is-null-for-non-file-type.js b/lib/model/migrations/legacy/20221117-01-check-datasetId-is-null-for-non-file-type.js similarity index 100% rename from lib/model/migrations/20221117-01-check-datasetId-is-null-for-non-file-type.js rename to lib/model/migrations/legacy/20221117-01-check-datasetId-is-null-for-non-file-type.js diff --git a/lib/model/migrations/20221118-01-make-entities-columns-not-null.js b/lib/model/migrations/legacy/20221118-01-make-entities-columns-not-null.js similarity index 100% rename from lib/model/migrations/20221118-01-make-entities-columns-not-null.js rename to lib/model/migrations/legacy/20221118-01-make-entities-columns-not-null.js diff --git a/lib/model/migrations/20221208-01-reduce-tz-precision.js b/lib/model/migrations/legacy/20221208-01-reduce-tz-precision.js similarity index 100% rename from lib/model/migrations/20221208-01-reduce-tz-precision.js rename to lib/model/migrations/legacy/20221208-01-reduce-tz-precision.js diff --git a/lib/model/migrations/20230106-01-remove-revision-number.js b/lib/model/migrations/legacy/20230106-01-remove-revision-number.js similarity index 100% rename from lib/model/migrations/20230106-01-remove-revision-number.js rename to lib/model/migrations/legacy/20230106-01-remove-revision-number.js diff --git a/lib/model/migrations/20230109-01-add-form-schema.js b/lib/model/migrations/legacy/20230109-01-add-form-schema.js similarity index 98% rename from lib/model/migrations/20230109-01-add-form-schema.js rename to lib/model/migrations/legacy/20230109-01-add-form-schema.js index 8b0446e2e..63c2cd903 100644 --- a/lib/model/migrations/20230109-01-add-form-schema.js +++ b/lib/model/migrations/legacy/20230109-01-add-form-schema.js @@ -7,7 +7,7 @@ // including this file, may be copied, modified, propagated, or distributed // except according to the terms contained in the LICENSE file. -const { getFormFields, compare } = require('../../data/schema'); // eslint-disable-line no-restricted-modules +const { getFormFields, compare } = require('../../../data/schema'); // eslint-disable-line no-restricted-modules /* Steps of this migration 1. remove check field collision trigger diff --git a/lib/model/migrations/20230123-01-remove-google-backups.js b/lib/model/migrations/legacy/20230123-01-remove-google-backups.js similarity index 100% rename from lib/model/migrations/20230123-01-remove-google-backups.js rename to lib/model/migrations/legacy/20230123-01-remove-google-backups.js diff --git a/lib/model/migrations/20230126-01-add-entity-indices.js b/lib/model/migrations/legacy/20230126-01-add-entity-indices.js similarity index 100% rename from lib/model/migrations/20230126-01-add-entity-indices.js rename to lib/model/migrations/legacy/20230126-01-add-entity-indices.js diff --git a/lib/model/migrations/20230127-01-rename-entity-created-by.js b/lib/model/migrations/legacy/20230127-01-rename-entity-created-by.js similarity index 100% rename from lib/model/migrations/20230127-01-rename-entity-created-by.js rename to lib/model/migrations/legacy/20230127-01-rename-entity-created-by.js diff --git a/lib/model/migrations/20230324-01-edit-dataset-verbs.js b/lib/model/migrations/legacy/20230324-01-edit-dataset-verbs.js similarity index 100% rename from lib/model/migrations/20230324-01-edit-dataset-verbs.js rename to lib/model/migrations/legacy/20230324-01-edit-dataset-verbs.js diff --git a/lib/model/migrations/20230406-01-add-entity-def-fields.js b/lib/model/migrations/legacy/20230406-01-add-entity-def-fields.js similarity index 100% rename from lib/model/migrations/20230406-01-add-entity-def-fields.js rename to lib/model/migrations/legacy/20230406-01-add-entity-def-fields.js diff --git a/lib/model/migrations/20230406-02-move-entity-label-add-deletedAt.js b/lib/model/migrations/legacy/20230406-02-move-entity-label-add-deletedAt.js similarity index 100% rename from lib/model/migrations/20230406-02-move-entity-label-add-deletedAt.js rename to lib/model/migrations/legacy/20230406-02-move-entity-label-add-deletedAt.js diff --git a/lib/model/migrations/20230414-01-remove-user-mfa-secret.js b/lib/model/migrations/legacy/20230414-01-remove-user-mfa-secret.js similarity index 100% rename from lib/model/migrations/20230414-01-remove-user-mfa-secret.js rename to lib/model/migrations/legacy/20230414-01-remove-user-mfa-secret.js diff --git a/lib/model/migrations/20230419-01-optimize-indices-sub-defs.js b/lib/model/migrations/legacy/20230419-01-optimize-indices-sub-defs.js similarity index 100% rename from lib/model/migrations/20230419-01-optimize-indices-sub-defs.js rename to lib/model/migrations/legacy/20230419-01-optimize-indices-sub-defs.js diff --git a/lib/model/migrations/20230509-01-dataset-approval-flag.js b/lib/model/migrations/legacy/20230509-01-dataset-approval-flag.js similarity index 100% rename from lib/model/migrations/20230509-01-dataset-approval-flag.js rename to lib/model/migrations/legacy/20230509-01-dataset-approval-flag.js diff --git a/lib/model/migrations/20230512-01-add-entity-root.js b/lib/model/migrations/legacy/20230512-01-add-entity-root.js similarity index 100% rename from lib/model/migrations/20230512-01-add-entity-root.js rename to lib/model/migrations/legacy/20230512-01-add-entity-root.js diff --git a/lib/model/migrations/20230512-02-backfill-entity-id.js b/lib/model/migrations/legacy/20230512-02-backfill-entity-id.js similarity index 100% rename from lib/model/migrations/20230512-02-backfill-entity-id.js rename to lib/model/migrations/legacy/20230512-02-backfill-entity-id.js diff --git a/lib/model/migrations/20230512-03-add-entity-source.js b/lib/model/migrations/legacy/20230512-03-add-entity-source.js similarity index 100% rename from lib/model/migrations/20230512-03-add-entity-source.js rename to lib/model/migrations/legacy/20230512-03-add-entity-source.js diff --git a/lib/model/migrations/20230518-01-add-entity-index-to-audits.js b/lib/model/migrations/legacy/20230518-01-add-entity-index-to-audits.js similarity index 100% rename from lib/model/migrations/20230518-01-add-entity-index-to-audits.js rename to lib/model/migrations/legacy/20230518-01-add-entity-index-to-audits.js diff --git a/lib/model/pg-migrator.js b/lib/model/pg-migrator.js new file mode 100644 index 000000000..e92c3a30e --- /dev/null +++ b/lib/model/pg-migrator.js @@ -0,0 +1,189 @@ +// Copyright 2025 ODK Central Developers +// See the NOTICE file at the top-level directory of this distribution and at +// https://github.com/getodk/central-backend/blob/master/NOTICE. +// This file is part of ODK Central. It is subject to the license terms in +// the LICENSE file found in the top-level directory of this distribution and at +// https://www.apache.org/licenses/LICENSE-2.0. No part of ODK Central, +// including this file, may be copied, modified, propagated, or distributed +// except according to the terms contained in the LICENSE file. + +const { lstatSync, readdirSync } = require('node:fs'); + +const _ = require('lodash'); // eslint-disable-line import/no-extraneous-dependencies +const pg = require('pg'); + +const migrationsDir = `${__dirname}/migrations`; + +const validateName = name => { + if (!name.match(/^\d{8}-\d{2}-[a-zA-Z0-9_-]+.js/)) { + throw new Error(`Illegal name format for migration '${name}'`); + } +}; + +const withPg = (config) => async fn => { + const log = (...args) => console.log('[withPg]', ...args); // eslint-disable-line no-console + log('ENTRY'); + + const { Client } = pg; + const client = new Client(config); + + log('client created'); + + log('Connecting to client...'); + await client.connect(); + log('Client connected OK.'); + + try { + await fn(client); + } finally { + log('Ending client...'); + await client.end(); + log('Client ended.'); + } +}; + +const getMigrationsToRun = async client => { + const log = (...args) => console.log('[getMigrationsToRun]', ...args); // eslint-disable-line no-console + log('ENTRY'); + + const allMigrations = readdirSync(migrationsDir) + .filter(f => f.endsWith('.js') && lstatSync(`${migrationsDir}/${f}`).isFile()) + .sort(); // match sorting in knex/lib/migrate/sources/fs-migrations.js + log('allMigrations:', allMigrations); + + const alreadyRun = (await client.query('SELECT name FROM knex_migrations')).rows.map(r => r.name); + log('alreadyRun:', alreadyRun); + + const toRunNames = allMigrations.filter(m => !alreadyRun.includes(m)); + log('toRunNames:', toRunNames); + + const toRun = toRunNames.map(name => { + const path = `${migrationsDir}/${name}`; + const migration = require(path); // eslint-disable-line import/no-dynamic-require + return { name, path, migration }; + }); + log('toRun:', toRun); + + return toRun; +}; + +// In the main, this migrator is written to behave similarly to knex's: +// +// * uses existing knex_migrations and knex_migrations_lock tables +// * expects transaction property async .up({ raw }) +// * provides implementation of db.raw() +// * runs all new migrations in the same transaction +// +// Notable differences +// +// * ONLY provides db.raw()-equivalent function to transactions - no knex query builder etc. +// * ONLY implements up(); will throw if a transaction has other properties, except for `down()`. +// To reverse a migration, run e.g.: +// await require('./lib/model/pg-migrator') +// .withPg(require('config').get('default.database'))( +// require('./lib/model/migrations/20231208-01-dataset-form-def-actions').down, +// ) +// * gets list of migrations to run _after_ acquiring db lock (knex checks before acquiring lock, +// and then has to re-check afterwards) +// * sets migration_time to be the start of the migration batch's transaction rather than some +// other intermediate time +// * instead of attempting to acquire a lock on the single row in the knex_migrations_lock table, +// this code takes the simpler approach of locking the whole table. This table could be +// discarded completely by instead locking the knex_migrations table, but backwards- +// compatibility is essential to prevent concurrent running of knex-based and pg-based +// migrators. +// * does not check that all migrations listed in the database table actually exist in the +// filesystem +const migrate = async (client) => { + const log = (...args) => console.log('[pgMigrations]', ...args); // eslint-disable-line no-console + + try { + log('Starting transaction...'); + await client.query('BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE'); + log('Transaction started.'); + + log('Creating tables if they do not exist...'); + // N.B. these tables are created to be similar to the legacy knex-created table. + const nameMaxLen = 255; + await client.query(` + CREATE TABLE IF NOT EXISTS knex_migrations ( + id SERIAL PRIMARY KEY, + name VARCHAR(${nameMaxLen}) NOT NULL, + batch INTEGER, + migration_time TIMESTAMP(3) WITH TIME ZONE + ); + CREATE TABLE IF NOT EXISTS knex_migrations_lock ( + index SERIAL PRIMARY KEY, + is_locked INTEGER NOT NULL + ); + `); + log('Tables now definitely exists.'); + + log('Acquiring lock on knex_migrations_lock table...'); + await client.query('LOCK TABLE knex_migrations_lock IN EXCLUSIVE MODE NOWAIT'); + log('Lock acquired.'); + + const toRun = await getMigrationsToRun(client); + + if (!toRun.length) { + log('No migrations to run - exiting.'); + await client.query('ROLLBACK'); + return; + } + + log('Validating', toRun.length, 'migrations...'); + for (const { migration, name } of toRun) { + log('Validating migration:', name, '...'); + + if (name.length > nameMaxLen) throw new Error(`Migration name '${name}' is too long - max length is ${nameMaxLen}, but got ${name.length}`); + + validateName(name); + + const keys = Object.keys(migration); + const unexpectedKeys = _.omit(keys, 'up', 'down'); + if (unexpectedKeys.length) throw new Error(`Unexpected key(s) found in migration ${name}: ${unexpectedKeys}`); + + if (!migration.up) throw new Error(`Required prop .up not found in migration ${name}`); + if (typeof migration.up !== 'function') { + throw new Error(`Required prop .up of migration ${name} has incorrect type - expected 'function', but got '${typeof migration.up}'`); + } + + if (migration.down && typeof migration.down !== 'function') { + throw new Error(`Optional prop .down of migration ${name} has incorrect type - expected 'function' but got '${typeof migration.down}'`); + } + + log('Migration', name, 'looks valid.'); + } + log(toRun.length, 'migrations look valid.'); + + log('Running', toRun.length, 'migrations...'); + const { lastBatch } = (await client.query(`SELECT COALESCE(MAX(batch), 0) AS "lastBatch" FROM knex_migrations`)).rows[0]; + const batchNumber = lastBatch + 1; + log(' batch number:', batchNumber); + + /* eslint-disable no-await-in-loop */ + for (const { migration, name } of toRun) { + log('Running migration:', name); + await migration.up(client); + // `CLOCK_TIMESTAMP()` used to match knex migrator's `new Date()`. + await client.query(` + INSERT INTO knex_migrations + (name, batch, migration_time) + VALUES($1, $2, CLOCK_TIMESTAMP()) + `, [ name, batchNumber ]); + log('Migration complete:', name); + } + /* eslint-enable no-await-in-loop */ + log(toRun.length, 'migrations ran OK.'); + + log('Committing migrations...'); + await client.query('COMMIT'); + log('Migrations committed.'); + } catch (err) { + log('Caught error; rolling back', err); + await client.query('ROLLBACK'); + throw err; + } +}; + +module.exports = { withPg, migrate }; diff --git a/test/db-migrations/20250113-01-disable-nullable-blob-content-types.spec.js b/test/db-migrations/20250113-01-disable-nullable-blob-content-types.spec.js index 768e20539..d0861e147 100644 --- a/test/db-migrations/20250113-01-disable-nullable-blob-content-types.spec.js +++ b/test/db-migrations/20250113-01-disable-nullable-blob-content-types.spec.js @@ -14,6 +14,7 @@ describeMigration('20250113-01-disable-nullable-blob-content-types', ({ runMigra before(async () => { await rowsExistFor('blobs', blob1, blob2); + await assertTableContents('blobs', blob1, blob2); // should fail if old migration still exists await runMigrationBeingTested(); }); diff --git a/test/db-migrations/migrator.js b/test/db-migrations/migrator.js index 034f94fbd..c84c85534 100644 --- a/test/db-migrations/migrator.js +++ b/test/db-migrations/migrator.js @@ -15,95 +15,100 @@ const fs = require('node:fs'); const { execSync } = require('node:child_process'); -const migrationsDir = './lib/model/migrations'; -const holdingPen = './test/db-migrations/.holding-pen'; +const legacy = createMigrator('Legacy', './lib/model/migrations/legacy', './test/db-migrations/.holding-pen/legacy'); // eslint-disable-line no-use-before-define, no-multi-spaces +const modern = createMigrator('Modern', './lib/model/migrations', './test/db-migrations/.holding-pen/pg', legacy); // eslint-disable-line no-use-before-define, no-multi-spaces -fs.mkdirSync(holdingPen, { recursive: true }); +module.exports = { legacy, modern }; -restoreMigrations(); // eslint-disable-line no-use-before-define -const allMigrations = loadMigrationsList(); // eslint-disable-line no-use-before-define -moveMigrationsToHoldingPen(); // eslint-disable-line no-use-before-define +function createMigrator(name, migrationsDir, holdingPen, previousMigrator) { + fs.mkdirSync(holdingPen, { recursive: true }); -let lastRunIdx = -1; + restoreMigrations(); // eslint-disable-line no-use-before-define + const allMigrations = loadMigrationsList(); // eslint-disable-line no-use-before-define + moveMigrationsToHoldingPen(); // eslint-disable-line no-use-before-define -function runBefore(migrationName) { - const idx = getIndex(migrationName); // eslint-disable-line no-use-before-define - if (idx === 0) return; + let lastRunIdx = -1; - const previousMigration = allMigrations[idx - 1]; + return { + exists, // eslint-disable-line no-use-before-define, no-multi-spaces + hasRun, // eslint-disable-line no-use-before-define, no-multi-spaces + runBefore, // eslint-disable-line no-use-before-define, no-multi-spaces + runIncluding, // eslint-disable-line no-use-before-define, no-multi-spaces + restoreMigrations, // eslint-disable-line no-use-before-define + }; - return runIncluding(previousMigration); // eslint-disable-line no-use-before-define -} - -function runIncluding(lastMigrationToRun) { - const finalIdx = getIndex(lastMigrationToRun); // eslint-disable-line no-use-before-define + function runBefore(migrationName) { + const idx = getIndex(migrationName); // eslint-disable-line no-use-before-define + runUntilIndex(idx - 1); // eslint-disable-line no-use-before-define + } - for (let restoreIdx=lastRunIdx+1; restoreIdx<=finalIdx; ++restoreIdx) { // eslint-disable-line no-plusplus - const f = allMigrations[restoreIdx] + '.js'; - fs.renameSync(`${holdingPen}/${f}`, `${migrationsDir}/${f}`); + function runIncluding(lastMigrationToRun) { + runUntilIndex(getIndex(lastMigrationToRun)); // eslint-disable-line no-use-before-define } - log('Running migrations until:', lastMigrationToRun, '...'); - const res = execSync(`node ./lib/bin/run-migrations.js`, { encoding: 'utf8' }); + function runUntilIndex(finalIdx) { + for (let restoreIdx=lastRunIdx+1; restoreIdx<=finalIdx; ++restoreIdx) { // eslint-disable-line no-plusplus + const f = allMigrations[restoreIdx] + '.js'; + fs.renameSync(`${holdingPen}/${f}`, `${migrationsDir}/${f}`); + } - lastRunIdx = finalIdx; + if (previousMigrator) previousMigrator.restoreMigrations(); - log(`Ran migrations up-to-and-including ${lastMigrationToRun}:\n`, res); -} + const lastMigrationToRun = allMigrations[finalIdx]; + log('Running migrations until:', lastMigrationToRun, '...'); + const res = execSync(`node ./lib/bin/run-migrations.js`, { encoding: 'utf8' }); -function getIndex(migrationName) { - const idx = allMigrations.indexOf(migrationName); - log('getIndex()', migrationName, 'found at', idx); - if (idx === -1) throw new Error(`Unknown migration: ${migrationName}`); - return idx; -} + lastRunIdx = finalIdx; -function restoreMigrations() { - moveAll(holdingPen, migrationsDir); // eslint-disable-line no-use-before-define -} + log(`Ran migrations up-to-and-including ${lastMigrationToRun}:\n`, res); + } -function moveMigrationsToHoldingPen() { - moveAll(migrationsDir, holdingPen); // eslint-disable-line no-use-before-define -} + function getIndex(migrationName) { + const idx = allMigrations.indexOf(migrationName); + log('getIndex()', migrationName, 'found at', idx); + if (idx === -1) throw new Error(`Unknown migration: ${migrationName}`); + return idx; + } -function moveAll(src, tgt) { - fs.readdirSync(src) - .filter(f => f.endsWith('.js')) - .forEach(f => fs.renameSync(`${src}/${f}`, `${tgt}/${f}`)); -} + function restoreMigrations() { + moveAll(holdingPen, migrationsDir); // eslint-disable-line no-use-before-define + } -function loadMigrationsList() { - const migrations = fs.readdirSync(migrationsDir) - .filter(f => f.endsWith('.js')) - .map(f => f.replace(/\.js$/, '')) - .sort(); // match sorting in pg-migrator and knex's fs-migrations.js - log(); - log('All migrations:'); - log(); - migrations.forEach(m => log('*', m)); - log(); - log('Total:', migrations.length); - log(); - return migrations; -} + function moveMigrationsToHoldingPen() { + moveAll(migrationsDir, holdingPen); // eslint-disable-line no-use-before-define + } -function exists(migrationName) { - try { - getIndex(migrationName); - return true; - } catch (err) { - return false; + function moveAll(src, tgt) { + fs.readdirSync(src) + .filter(f => f.endsWith('.js')) + .forEach(f => fs.renameSync(`${src}/${f}`, `${tgt}/${f}`)); } -} -function hasRun(migrationName) { - return lastRunIdx >= getIndex(migrationName); -} + function loadMigrationsList() { + const migrations = fs.readdirSync(migrationsDir) + .filter(f => f.endsWith('.js')) + .map(f => f.replace(/\.js$/, '')) + .sort(); // match sorting in pg-migrator and knex's fs-migrations.js + log(); + log(`${name} migrations:`); + log(); + migrations.forEach(m => log('*', m)); + log(); + log('Total:', migrations.length); + log(); + return migrations; + } + + function exists(migrationName) { + try { + getIndex(migrationName); + return true; + } catch (err) { + return false; + } + } -module.exports = { - exists, - hasRun, - runBefore, - runIncluding, - restoreMigrations, -}; + function hasRun(migrationName) { + return lastRunIdx >= getIndex(migrationName); + } +} diff --git a/test/db-migrations/utils.js b/test/db-migrations/utils.js index 90d9b7af2..91b1b0e74 100644 --- a/test/db-migrations/utils.js +++ b/test/db-migrations/utils.js @@ -2,8 +2,8 @@ const assert = require('node:assert/strict'); const _ = require('lodash'); const migrator = require('./migrator'); -function _describeMigration(describeFn, migrationName, fn) { - assert.strictEqual(arguments.length, 3, 'Incorrect argument count.'); +function _describeMigration(migrator, describeFn, migrationName, fn) { // eslint-disable-line no-shadow + assert.strictEqual(arguments.length, 4, 'Incorrect argument count.'); assert.strictEqual(typeof describeFn, 'function'); @@ -29,9 +29,14 @@ function _describeMigration(describeFn, migrationName, fn) { return fn({ runMigrationBeingTested }); }); } -function describeMigration(...args) { return _describeMigration(describe, ...args); } -describeMigration.only = (...args) => _describeMigration(describe.only, ...args); // eslint-disable-line no-only-tests/no-only-tests, no-multi-spaces -describeMigration.skip = (...args) => _describeMigration(describe.skip, ...args); // eslint-disable-line no-multi-spaces + +function describeLegacyMigration(...args) { return _describeMigration(migrator.legacy, describe, ...args); } // eslint-disable-line no-multi-spaces +describeLegacyMigration.only = (...args) => _describeMigration(migrator.legacy, describe.only, ...args); // eslint-disable-line no-only-tests/no-only-tests, no-multi-spaces +describeLegacyMigration.skip = (...args) => _describeMigration(migrator.legacy, describe.skip, ...args); // eslint-disable-line no-multi-spaces + +function describeMigration(...args) { return _describeMigration(migrator.modern, describe, ...args); } // eslint-disable-line no-multi-spaces +describeMigration.only = (...args) => _describeMigration(migrator.modern, describe.only, ...args); // eslint-disable-line no-only-tests/no-only-tests, no-multi-spaces +describeMigration.skip = (...args) => _describeMigration(migrator.modern, describe.skip, ...args); // eslint-disable-line no-multi-spaces async function assertIndexExists(tableName, expected) { if (arguments.length !== 2) throw new Error('Incorrect arg count.'); @@ -180,6 +185,7 @@ module.exports = { assertTableDoesNotExist, assertTableSchema, + describeLegacyMigration, describeMigration, rowsExistFor, diff --git a/test/db-partial-migrations/expected-migrations-table-contents.sql b/test/db-partial-migrations/expected-migrations-table-contents.sql new file mode 100644 index 000000000..bb8d3f97f --- /dev/null +++ b/test/db-partial-migrations/expected-migrations-table-contents.sql @@ -0,0 +1,187 @@ + id | name +-----+---------------------------------------------------------- + 1 | 20170920-01-initial.js + 2 | 20171010-01-auth.js + 3 | 20171023-01-authz-forms.js + 4 | 20171030-01-add-default-authz-records.js + 5 | 20171106-01-remove-user-update-timestamp.js + 6 | 20171121-01-add-submissions-constraint.js + 7 | 20171121-02-add-submitter.js + 8 | 20171213-01-unrequire-display-name.js + 9 | 20180108-01-expiring-actors.js + 10 | 20180108-02-enum-to-varchar.js + 11 | 20180112-01-audit-table.js + 12 | 20180112-02-add-field-keys.js + 13 | 20180118-01-rerequire-display-name.js + 14 | 20180125-01-add-form-detail-fields.js + 15 | 20180125-02-more-field-key-grants.js + 16 | 20180125-03-add-blob-tables.js + 17 | 20180301-01-configuration.js + 18 | 20180322-01-additional-form-options.js + 19 | 20180327-01-update-form-constraints.js + 20 | 20180501-01-add-configs-timestamp.js + 21 | 20180501-02-fix-date-columns.js + 22 | 20180515-01-enforce-nonnull-form-version.js + 23 | 20180727-01-rename-attachments-table.js + 24 | 20180727-02-add-md5-to-blobs.js + 25 | 20180727-03-add-form-attachments-table.js + 26 | 20181011-make-email-case-insensitive.js + 27 | 20181012-01-add-submissions-createdat-index.js + 28 | 20181206-01-add-projects.js + 29 | 20181207-01-grant-verbs-to-text.js + 30 | 20181207-02-rename-grant-verbs.js + 31 | 20181211-01-audit-verbs-to-text.js + 32 | 20181211-02-rename-audit-actions.js + 33 | 20181212-00-fix-user-type.js + 34 | 20181212-01-add-roles.js + 35 | 20181212-02-remove-groups.js + 36 | 20181212-03-add-single-use-roles.js + 37 | 20181219-01-add-submission-update-verb.js + 38 | 20181221-01-nullable-submission-blobs.js + 39 | 20181230-01-add-device-id-to-submission.js + 40 | 20190225-01-add-actor-trigram-indices.js + 41 | 20190225-02-add-role-grants.js + 42 | 20190226-01-convert-verbs-to-jsonb.js + 43 | 20190226-02-add-role-actee-species.js + 44 | 20190226-03-add-assignment-verbs.js + 45 | 20190226-04-add-assignment-actee-species.js + 46 | 20190227-01-add-project-manager-role.js + 47 | 20190405-01-add-project-archival-flag.js + 48 | 20190416-01-email-uniqueness.js + 49 | 20190416-02-add-user-delete-verb.js + 50 | 20190520-01-add-form-versioning.js + 51 | 20190523-01-add-form-state-constraint.js + 52 | 20190605-01-reformat-audits.js + 53 | 20190607-01-convert-audit-details-to-jsonb.js + 54 | 20190607-02-standardize-attachment-actees.js + 55 | 20190607-03-rename-sub-attachment-audits.js + 56 | 20190610-01-add-audits-verbs.js + 57 | 20190610-02-backfill-submission-audit-instanceids.js + 58 | 20190611-01-add-updatedat-to-form-attachments.js + 59 | 20190618-01-add-csrf-token.js + 60 | 20190618-01-add-encryption-tracking.js + 61 | 20190701-01-add-managed-encryption-key-check.js + 62 | 20190916-01-granularize-app-user-permissions.js + 63 | 20190917-01-cleanup-app-user-role.js + 64 | 20190923-01-add-project-viewer-role.js + 65 | 20190925-01-add-client-audits.js + 66 | 20191007-01-backfill-client-audits.js + 67 | 20191010-01-add-excel-blob-reference.js + 68 | 20191023-01-add-worker-columns-to-audits.js + 69 | 20191025-01-add-id-to-audits.js + 70 | 20191106-01-remove-deleted-actor-assignments.js + 71 | 20191231-01-remove-transformations.js + 72 | 20191231-02-add-schema-storage.js + 73 | 20200110-01-add-drafts.js + 74 | 20200112-01-check-field-collisions.js + 75 | 20200114-01-remove-formid-sha256-constraint.js + 76 | 20200117-01-draft-test-submissions.js + 77 | 20200121-01-add-draft-keys.js + 78 | 20200122-01-remove-draft-form-state.js + 79 | 20200129-01-cascade-submission-deletes.js + 80 | 20200220-01-repair-submission-parsing.js + 81 | 20200403-01-add-performance-indices.js + 82 | 20200407-01-allow-actorless-submission-defs.js + 83 | 20200423-01-fix-field-insert-performance.js + 84 | 20200428-01-allow-string-downcast.js + 85 | 20200519-01-add-enketo-id.js + 86 | 20200519-02-add-form-viewer-role.js + 87 | 20200520-01-backfill-enketo.js + 88 | 20200715-01-add-data-collector-role.js + 89 | 20200721-01-add-public-links.js + 90 | 20200728-01-add-enketo-single-token-to-forms.js + 91 | 20200731-01-allow-project-managers-to-end-sessions.js + 92 | 20200810-01-reschedule-enketo-processing.js + 93 | 20200918-01-repair-publishedat-dates.js + 94 | 20200930-01-add-backup-run-verb.js + 95 | 20201117-01-remove-deleted-actor-assignments-again.js + 96 | 20201207-01-harmonize-submitter-id-columns.js + 97 | 20210118-01-add-current-flag-to-submission-defs.js + 98 | 20210120-01-instance-names.js + 99 | 20210203-01-add-hierarchy-to-actees.js + 100 | 20210210-01-add-instanceid-to-submission-defs.js + 101 | 20210218-01-add-submission-edit-verbs.js + 102 | 20210218-02-add-draft-to-submissions-unique.js + 103 | 20210219-01-add-review-state.js + 104 | 20210219-02-add-notes-and-index-to-audits.js + 105 | 20210324-01-add-submission-edit-verbs-to-managers.js + 106 | 20210325-01-remove-project.list-verb.js + 107 | 20210408-01-drop-public-link-createdat.js + 108 | 20210408-02-backfill-specialized-actor-audits.js + 109 | 20210409-01-add-comments.js + 110 | 20210409-02-update-review-states.js + 111 | 20210423-01-add-name-to-form-def.js + 112 | 20210423-02-drop-form-name.js + 113 | 20210716-01-config-value-jsonb.js + 114 | 20210721-01-add-config-set-verb.js + 115 | 20210817-01-disallow-structure-downcast-to-string.js + 116 | 20210825-01-add-analytics-read-verb.js + 117 | 20210903-01-backfill-encrypted-client-audits.js + 118 | 20210927-01-revert-disallow-structure-downcast.js + 119 | 20211008-01-track-select-many-options.js + 120 | 20211021-remove-hashes-from-audits.js + 121 | 20211109-01-add-user-agent-to-submissions.js + 122 | 20211114-01-flag-initial-submission-def.js + 123 | 20211117-01-add-form-restore-verb.js + 124 | 20211129-01-add-purged-details-to-actees.js + 125 | 20220121-01-form-cascade-delete.js + 126 | 20220121-02-purge-deleted-forms.js + 127 | 20220209-01-purge-unneeded-drafts.js + 128 | 20220309-01-add-project-description.js + 129 | 20220803-01-create-entities-schema.js + 130 | 20221003-01-add-dataset-verbs.js + 131 | 20221114-01-explict-dataset-publish.js + 132 | 20221117-01-check-datasetId-is-null-for-non-file-type.js + 133 | 20221118-01-make-entities-columns-not-null.js + 134 | 20221208-01-reduce-tz-precision.js + 135 | 20230106-01-remove-revision-number.js + 136 | 20230109-01-add-form-schema.js + 137 | 20230123-01-remove-google-backups.js + 138 | 20230126-01-add-entity-indices.js + 139 | 20230127-01-rename-entity-created-by.js + 140 | 20230324-01-edit-dataset-verbs.js + 141 | 20230406-01-add-entity-def-fields.js + 142 | 20230406-02-move-entity-label-add-deletedAt.js + 143 | 20230414-01-remove-user-mfa-secret.js + 144 | 20230419-01-optimize-indices-sub-defs.js + 145 | 20230509-01-dataset-approval-flag.js + 146 | 20230512-01-add-entity-root.js + 147 | 20230512-02-backfill-entity-id.js + 148 | 20230512-03-add-entity-source.js + 149 | 20230518-01-add-entity-index-to-audits.js + 150 | 20230802-01-delete-orphan-submissions.js + 151 | 20230818-01-remove-schemaId-from-dsPropertyFields.js + 152 | 20230824-01-add-entity-version.js + 153 | 20230830-01-remove-entity-label-from-audits.js + 154 | 20230907-01-opened-form-verb.js + 155 | 20231002-01-add-conflict-details.js + 156 | 20231013-01-change-entity-error-action.js + 157 | 20231208-01-dataset-form-def-actions.js + 158 | 20240215-01-entity-delete-verb.js + 159 | 20240215-02-dedupe-verbs.js + 160 | 20240312-01-add-dataset-create-verb.js + 161 | 20240322-01-add-entity-source-index-to-audits.js + 162 | 20240515-01-entity-tz-precision.js + 163 | 20240607-01-add-offline-entity-branch-trunk-info.js + 164 | 20240607-02-add-submission-backlog.js + 165 | 20240715-01-backlog-add-event-entityuuid.js + 166 | 20240913-01-add-blob-s3.js + 167 | 20240914-01-add-submission-delete-verb.js + 168 | 20240914-02-remove-orphaned-client-audits.js + 169 | 20241001-01-index-on-session-table.js + 170 | 20241008-01-add-user_preferences.js + 171 | 20241010-01-schedule-entity-form-upgrade.js + 172 | 20241029-01-schedule-entity-form-upgrade-create-forms.js + 173 | 20241030-01-add-force-entity-def-source.js + 174 | 20241224-01-entity-restore-verb.js + 175 | 20241224-02-cascade-entity-purge.js + 176 | 20241226-01-indices-for-purging-entities.js + 177 | 20241227-01-backfill-audit-entity-uuid.js + 178 | 20250113-01-add-webformsenabled-formtable-column.js + 179 | 20250113-01-disable-nullable-blob-content-types.js + 180 | 20250221-01-deletedAt-index-entity.js + 181 | 20250307-01-purged-entities-table.js + 182 | 20250415-01-client-audit-remainder.js + 183 | 20250428-01-audit-indices.js +(183 rows) + diff --git a/test/db-partial-migrations/expected-schema.sql b/test/db-partial-migrations/expected-schema.sql new file mode 100644 index 000000000..cfd322750 --- /dev/null +++ b/test/db-partial-migrations/expected-schema.sql @@ -0,0 +1,2683 @@ +-- +-- PostgreSQL database dump +-- + +-- Dumped from database version 14.10 (Debian 14.10-1.pgdg120+1) +-- Dumped by pg_dump version 16.8 (Ubuntu 16.8-1.pgdg24.04+1) + +SET statement_timeout = 0; +SET lock_timeout = 0; +SET idle_in_transaction_session_timeout = 0; +SET client_encoding = 'UTF8'; +SET standard_conforming_strings = on; +SELECT pg_catalog.set_config('search_path', '', false); +SET check_function_bodies = false; +SET xmloption = content; +SET client_min_messages = warning; +SET row_security = off; + +-- +-- Name: public; Type: SCHEMA; Schema: -; Owner: postgres +-- + +-- *not* creating schema, since initdb creates it + + +ALTER SCHEMA public OWNER TO postgres; + +-- +-- Name: citext; Type: EXTENSION; Schema: -; Owner: - +-- + +CREATE EXTENSION IF NOT EXISTS citext WITH SCHEMA public; + + +-- +-- Name: EXTENSION citext; Type: COMMENT; Schema: -; Owner: +-- + +COMMENT ON EXTENSION citext IS 'data type for case-insensitive character strings'; + + +-- +-- Name: pg_trgm; Type: EXTENSION; Schema: -; Owner: - +-- + +CREATE EXTENSION IF NOT EXISTS pg_trgm WITH SCHEMA public; + + +-- +-- Name: EXTENSION pg_trgm; Type: COMMENT; Schema: -; Owner: +-- + +COMMENT ON EXTENSION pg_trgm IS 'text similarity measurement and index searching based on trigrams'; + + +-- +-- Name: pgrowlocks; Type: EXTENSION; Schema: -; Owner: - +-- + +CREATE EXTENSION IF NOT EXISTS pgrowlocks WITH SCHEMA public; + + +-- +-- Name: EXTENSION pgrowlocks; Type: COMMENT; Schema: -; Owner: +-- + +COMMENT ON EXTENSION pgrowlocks IS 'show row-level locking information'; + + +-- +-- Name: conflictType; Type: TYPE; Schema: public; Owner: jubilant +-- + +CREATE TYPE public."conflictType" AS ENUM ( + 'soft', + 'hard' +); + + +ALTER TYPE public."conflictType" OWNER TO jubilant; + +-- +-- Name: s3_upload_status; Type: TYPE; Schema: public; Owner: jubilant +-- + +CREATE TYPE public.s3_upload_status AS ENUM ( + 'pending', + 'uploaded', + 'failed' +); + + +ALTER TYPE public.s3_upload_status OWNER TO jubilant; + +-- +-- Name: check_email(); Type: FUNCTION; Schema: public; Owner: jubilant +-- + +CREATE FUNCTION public.check_email() RETURNS trigger + LANGUAGE plpgsql + AS $$ + declare extant int; + begin + select count(*) into extant from users inner join + (select id from actors where "deletedAt" is null and id != NEW."actorId") + as actors on actors.id=users."actorId" + where email=NEW.email limit 1; + if extant > 0 then + raise exception 'ODK01:%', NEW.email; + end if; + return NEW; + end; +$$; + + +ALTER FUNCTION public.check_email() OWNER TO jubilant; + +-- +-- Name: check_field_collisions(); Type: FUNCTION; Schema: public; Owner: jubilant +-- + +CREATE FUNCTION public.check_field_collisions() RETURNS trigger + LANGUAGE plpgsql + AS $$ + declare extant int; + declare extformid int; + declare extpath text; + declare exttype text; + begin + -- factoring the repeated joins here into a CTE explodes the cost by 10x + + select count(distinct type), form_fields."formId", form_fields.path into extant, extformid, extpath + from form_fields + + -- finds canonical formDefIds (published, or active draft) + left outer join (select id from form_defs where "publishedAt" is not null) as form_defs + on form_defs.id = form_fields."formDefId" + left outer join (select id, "draftDefId" from forms) as forms + on forms."draftDefId" = form_fields."formDefId" + + -- weeds out paths whose latest def indicates they are a string. first figure + -- out latest def, then knock out latest strings from conflict detection. + inner join + (select form_fields."formId", max("formDefId") as "latestDefId" from form_fields + -- this is a repeat of the above canonical-def subquery + left outer join (select id from form_defs where "publishedAt" is not null) as ifds + on ifds.id = form_fields."formDefId" + left outer join (select id, "draftDefId" from forms) as ifs + on ifs."draftDefId" = form_fields."formDefId" + where ifs.id is not null or ifds.id is not null + group by form_fields."formId" + ) as tail + on tail."formId" = form_fields."formId" + inner join + (select "formDefId", path from form_fields where type != 'string') as nonstring + on "latestDefId" = nonstring."formDefId" and form_fields.path = nonstring.path + + where forms.id is not null or form_defs.id is not null + group by form_fields."formId", form_fields.path having count(distinct type) > 1; + + if extant > 0 then + select type into exttype + from form_fields + where "formId" = extformid and path = extpath + order by "formDefId" desc + limit 1 + offset 1; + + raise exception using message = format('ODK05:%s:%s', extpath, exttype); + end if; + + return NEW; + end; +$$; + + +ALTER FUNCTION public.check_field_collisions() OWNER TO jubilant; + +-- +-- Name: check_form_state(); Type: FUNCTION; Schema: public; Owner: jubilant +-- + +CREATE FUNCTION public.check_form_state() RETURNS trigger + LANGUAGE plpgsql + AS $$ + begin + if NEW.state is null or NEW.state not in ('open', 'closing', 'closed') then + raise exception 'ODK03:%', NEW.state; + end if; + return NEW; + end; +$$; + + +ALTER FUNCTION public.check_form_state() OWNER TO jubilant; + +-- +-- Name: check_form_version(); Type: FUNCTION; Schema: public; Owner: jubilant +-- + +CREATE FUNCTION public.check_form_version() RETURNS trigger + LANGUAGE plpgsql + AS $$ + declare extant int; + declare pid int; + declare xmlid text; + declare vstr text; + begin + select count(*), "projectId", "xmlFormId", version into extant, pid, xmlid, vstr + from form_defs + inner join (select id, "xmlFormId", "projectId" from forms) + as forms on forms.id = form_defs."formId" + where "publishedAt" is not null + group by "projectId", "xmlFormId", version + having count(form_defs.id) > 1; + + if extant > 0 then + raise exception using message = format('ODK02:%s:%L:%L', pid, xmlid, vstr); + end if; + + return NEW; + end; +$$; + + +ALTER FUNCTION public.check_form_version() OWNER TO jubilant; + +-- +-- Name: check_instanceid_unique(); Type: FUNCTION; Schema: public; Owner: jubilant +-- + +CREATE FUNCTION public.check_instanceid_unique() RETURNS trigger + LANGUAGE plpgsql + AS $$ + declare fid int; + declare drft boolean; + declare found int; + begin + select "formId", draft into fid, drft from submissions where submissions.id=NEW."submissionId"; + select count(*) into found from submissions + join submission_defs on submissions.id=submission_defs."submissionId" + where "formId"=fid and submission_defs."instanceId"=NEW."instanceId" and draft=drft; + + if found > 1 then + raise exception using message = format('ODK06:%s', NEW."instanceId"); + end if; + + return NEW; + end; +$$; + + +ALTER FUNCTION public.check_instanceid_unique() OWNER TO jubilant; + +-- +-- Name: check_managed_key(); Type: FUNCTION; Schema: public; Owner: jubilant +-- + +CREATE FUNCTION public.check_managed_key() RETURNS trigger + LANGUAGE plpgsql + AS $$ + declare "projectKeyId" int; + begin + select "keyId" into "projectKeyId" from forms + inner join projects on projects.id = forms."projectId" + where forms.id = NEW."formId"; + if "projectKeyId" is not null and NEW."keyId" is null then + raise exception 'ODK04'; + end if; + return NEW; + end; +$$; + + +ALTER FUNCTION public.check_managed_key() OWNER TO jubilant; + +-- +-- Name: check_review_state(); Type: FUNCTION; Schema: public; Owner: jubilant +-- + +CREATE FUNCTION public.check_review_state() RETURNS trigger + LANGUAGE plpgsql + AS $$ + begin + if NEW."reviewState" is not null and NEW."reviewState" not in ('hasIssues', 'edited', 'approved', 'rejected') then + raise exception 'ODK03:%', NEW."reviewState"; + end if; + return NEW; + end; +$$; + + +ALTER FUNCTION public.check_review_state() OWNER TO jubilant; + +SET default_tablespace = ''; + +SET default_table_access_method = heap; + +-- +-- Name: actees; Type: TABLE; Schema: public; Owner: jubilant +-- + +CREATE TABLE public.actees ( + id character varying(36) NOT NULL, + species character varying(36), + parent character varying(36), + "purgedAt" timestamp(3) with time zone, + "purgedName" text, + details jsonb +); + + +ALTER TABLE public.actees OWNER TO jubilant; + +-- +-- Name: actors; Type: TABLE; Schema: public; Owner: jubilant +-- + +CREATE TABLE public.actors ( + id integer NOT NULL, + type character varying(15), + "acteeId" character varying(36) NOT NULL, + "displayName" character varying(64) NOT NULL, + meta jsonb, + "createdAt" timestamp(3) with time zone, + "updatedAt" timestamp(3) with time zone, + "deletedAt" timestamp(3) with time zone, + "expiresAt" timestamp(3) with time zone +); + + +ALTER TABLE public.actors OWNER TO jubilant; + +-- +-- Name: actors_id_seq; Type: SEQUENCE; Schema: public; Owner: jubilant +-- + +CREATE SEQUENCE public.actors_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER SEQUENCE public.actors_id_seq OWNER TO jubilant; + +-- +-- Name: actors_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: jubilant +-- + +ALTER SEQUENCE public.actors_id_seq OWNED BY public.actors.id; + + +-- +-- Name: assignments; Type: TABLE; Schema: public; Owner: jubilant +-- + +CREATE TABLE public.assignments ( + "actorId" integer NOT NULL, + "roleId" integer NOT NULL, + "acteeId" character varying(36) NOT NULL +); + + +ALTER TABLE public.assignments OWNER TO jubilant; + +-- +-- Name: audits; Type: TABLE; Schema: public; Owner: jubilant +-- + +CREATE TABLE public.audits ( + "actorId" integer, + action text NOT NULL, + "acteeId" character varying(36), + details jsonb, + "loggedAt" timestamp(3) with time zone, + claimed timestamp(3) with time zone, + processed timestamp(3) with time zone, + "lastFailure" timestamp(3) with time zone, + failures integer DEFAULT 0, + id integer NOT NULL, + notes text +); + + +ALTER TABLE public.audits OWNER TO jubilant; + +-- +-- Name: audits_id_seq; Type: SEQUENCE; Schema: public; Owner: jubilant +-- + +CREATE SEQUENCE public.audits_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER SEQUENCE public.audits_id_seq OWNER TO jubilant; + +-- +-- Name: audits_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: jubilant +-- + +ALTER SEQUENCE public.audits_id_seq OWNED BY public.audits.id; + + +-- +-- Name: blobs; Type: TABLE; Schema: public; Owner: jubilant +-- + +CREATE TABLE public.blobs ( + id integer NOT NULL, + sha character varying(40) NOT NULL, + content bytea, + "contentType" text DEFAULT 'application/octet-stream'::text NOT NULL, + md5 character varying(32) NOT NULL, + s3_status public.s3_upload_status DEFAULT 'pending'::public.s3_upload_status NOT NULL +); + + +ALTER TABLE public.blobs OWNER TO jubilant; + +-- +-- Name: blobs_id_seq; Type: SEQUENCE; Schema: public; Owner: jubilant +-- + +CREATE SEQUENCE public.blobs_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER SEQUENCE public.blobs_id_seq OWNER TO jubilant; + +-- +-- Name: blobs_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: jubilant +-- + +ALTER SEQUENCE public.blobs_id_seq OWNED BY public.blobs.id; + + +-- +-- Name: client_audits; Type: TABLE; Schema: public; Owner: jubilant +-- + +CREATE TABLE public.client_audits ( + "blobId" integer NOT NULL, + event text, + node text, + start text, + "end" text, + latitude text, + longitude text, + accuracy text, + "old-value" text, + "new-value" text, + remainder jsonb, + "user" text, + "change-reason" text +); + + +ALTER TABLE public.client_audits OWNER TO jubilant; + +-- +-- Name: comments; Type: TABLE; Schema: public; Owner: jubilant +-- + +CREATE TABLE public.comments ( + id integer NOT NULL, + "submissionId" integer NOT NULL, + "actorId" integer NOT NULL, + body text NOT NULL, + "createdAt" timestamp(3) with time zone +); + + +ALTER TABLE public.comments OWNER TO jubilant; + +-- +-- Name: comments_id_seq; Type: SEQUENCE; Schema: public; Owner: jubilant +-- + +CREATE SEQUENCE public.comments_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER SEQUENCE public.comments_id_seq OWNER TO jubilant; + +-- +-- Name: comments_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: jubilant +-- + +ALTER SEQUENCE public.comments_id_seq OWNED BY public.comments.id; + + +-- +-- Name: config; Type: TABLE; Schema: public; Owner: jubilant +-- + +CREATE TABLE public.config ( + key character varying(40) NOT NULL, + value jsonb, + "setAt" timestamp(3) with time zone +); + + +ALTER TABLE public.config OWNER TO jubilant; + +-- +-- Name: dataset_form_defs; Type: TABLE; Schema: public; Owner: jubilant +-- + +CREATE TABLE public.dataset_form_defs ( + "datasetId" integer NOT NULL, + "formDefId" integer NOT NULL, + actions jsonb NOT NULL +); + + +ALTER TABLE public.dataset_form_defs OWNER TO jubilant; + +-- +-- Name: datasets; Type: TABLE; Schema: public; Owner: jubilant +-- + +CREATE TABLE public.datasets ( + id integer NOT NULL, + name text NOT NULL, + "acteeId" character varying(36) NOT NULL, + "createdAt" timestamp(3) with time zone NOT NULL, + "projectId" integer NOT NULL, + "publishedAt" timestamp(3) with time zone, + "approvalRequired" boolean DEFAULT false NOT NULL +); + + +ALTER TABLE public.datasets OWNER TO jubilant; + +-- +-- Name: datasets_id_seq; Type: SEQUENCE; Schema: public; Owner: jubilant +-- + +CREATE SEQUENCE public.datasets_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER SEQUENCE public.datasets_id_seq OWNER TO jubilant; + +-- +-- Name: datasets_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: jubilant +-- + +ALTER SEQUENCE public.datasets_id_seq OWNED BY public.datasets.id; + + +-- +-- Name: ds_properties; Type: TABLE; Schema: public; Owner: jubilant +-- + +CREATE TABLE public.ds_properties ( + id integer NOT NULL, + name text NOT NULL, + "datasetId" integer NOT NULL, + "publishedAt" timestamp(3) with time zone +); + + +ALTER TABLE public.ds_properties OWNER TO jubilant; + +-- +-- Name: ds_properties_id_seq; Type: SEQUENCE; Schema: public; Owner: jubilant +-- + +CREATE SEQUENCE public.ds_properties_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER SEQUENCE public.ds_properties_id_seq OWNER TO jubilant; + +-- +-- Name: ds_properties_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: jubilant +-- + +ALTER SEQUENCE public.ds_properties_id_seq OWNED BY public.ds_properties.id; + + +-- +-- Name: ds_property_fields; Type: TABLE; Schema: public; Owner: jubilant +-- + +CREATE TABLE public.ds_property_fields ( + "dsPropertyId" integer, + "formDefId" integer, + path text +); + + +ALTER TABLE public.ds_property_fields OWNER TO jubilant; + +-- +-- Name: entities; Type: TABLE; Schema: public; Owner: jubilant +-- + +CREATE TABLE public.entities ( + id integer NOT NULL, + uuid character varying(255) NOT NULL, + "datasetId" integer, + "createdAt" timestamp(3) with time zone NOT NULL, + "creatorId" integer NOT NULL, + "updatedAt" timestamp(3) with time zone, + "deletedAt" timestamp(3) with time zone, + conflict public."conflictType" +); + + +ALTER TABLE public.entities OWNER TO jubilant; + +-- +-- Name: entities_id_seq; Type: SEQUENCE; Schema: public; Owner: jubilant +-- + +CREATE SEQUENCE public.entities_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER SEQUENCE public.entities_id_seq OWNER TO jubilant; + +-- +-- Name: entities_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: jubilant +-- + +ALTER SEQUENCE public.entities_id_seq OWNED BY public.entities.id; + + +-- +-- Name: entity_def_sources; Type: TABLE; Schema: public; Owner: jubilant +-- + +CREATE TABLE public.entity_def_sources ( + id integer NOT NULL, + type character varying(36) NOT NULL, + "auditId" integer, + "submissionDefId" integer, + details jsonb, + "forceProcessed" boolean +); + + +ALTER TABLE public.entity_def_sources OWNER TO jubilant; + +-- +-- Name: entity_def_sources_id_seq; Type: SEQUENCE; Schema: public; Owner: jubilant +-- + +CREATE SEQUENCE public.entity_def_sources_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER SEQUENCE public.entity_def_sources_id_seq OWNER TO jubilant; + +-- +-- Name: entity_def_sources_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: jubilant +-- + +ALTER SEQUENCE public.entity_def_sources_id_seq OWNED BY public.entity_def_sources.id; + + +-- +-- Name: entity_defs; Type: TABLE; Schema: public; Owner: jubilant +-- + +CREATE TABLE public.entity_defs ( + id integer NOT NULL, + "entityId" integer NOT NULL, + "createdAt" timestamp(3) with time zone NOT NULL, + current boolean, + data jsonb NOT NULL, + "creatorId" integer NOT NULL, + "userAgent" character varying(255), + label text NOT NULL, + root boolean DEFAULT false NOT NULL, + "sourceId" integer, + version integer DEFAULT 1 NOT NULL, + "dataReceived" jsonb DEFAULT '{}'::jsonb NOT NULL, + "baseVersion" integer, + "conflictingProperties" jsonb, + "branchId" uuid, + "trunkVersion" integer, + "branchBaseVersion" integer +); + + +ALTER TABLE public.entity_defs OWNER TO jubilant; + +-- +-- Name: entity_defs_id_seq; Type: SEQUENCE; Schema: public; Owner: jubilant +-- + +CREATE SEQUENCE public.entity_defs_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER SEQUENCE public.entity_defs_id_seq OWNER TO jubilant; + +-- +-- Name: entity_defs_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: jubilant +-- + +ALTER SEQUENCE public.entity_defs_id_seq OWNED BY public.entity_defs.id; + + +-- +-- Name: entity_submission_backlog; Type: TABLE; Schema: public; Owner: jubilant +-- + +CREATE TABLE public.entity_submission_backlog ( + "submissionId" integer NOT NULL, + "submissionDefId" integer NOT NULL, + "branchId" uuid NOT NULL, + "branchBaseVersion" integer NOT NULL, + "loggedAt" timestamp(3) with time zone NOT NULL, + "auditId" integer NOT NULL, + "entityUuid" uuid NOT NULL +); + + +ALTER TABLE public.entity_submission_backlog OWNER TO jubilant; + +-- +-- Name: field_keys; Type: TABLE; Schema: public; Owner: jubilant +-- + +CREATE TABLE public.field_keys ( + "actorId" integer NOT NULL, + "createdBy" integer NOT NULL, + "projectId" integer +); + + +ALTER TABLE public.field_keys OWNER TO jubilant; + +-- +-- Name: form_attachments; Type: TABLE; Schema: public; Owner: jubilant +-- + +CREATE TABLE public.form_attachments ( + "formId" integer NOT NULL, + "blobId" integer, + name text NOT NULL, + type text, + "formDefId" integer NOT NULL, + "updatedAt" timestamp(3) with time zone, + "datasetId" integer, + CONSTRAINT "check_blobId_or_datasetId_is_null" CHECK ((("blobId" IS NULL) OR ("datasetId" IS NULL))), + CONSTRAINT "check_datasetId_is_null_for_non_file" CHECK (((type = 'file'::text) OR ("datasetId" IS NULL))) +); + + +ALTER TABLE public.form_attachments OWNER TO jubilant; + +-- +-- Name: form_defs; Type: TABLE; Schema: public; Owner: jubilant +-- + +CREATE TABLE public.form_defs ( + id integer NOT NULL, + "formId" integer, + xml text NOT NULL, + hash character varying(32) NOT NULL, + sha character varying(40) NOT NULL, + sha256 character varying(64) NOT NULL, + version text NOT NULL, + "createdAt" timestamp(3) with time zone, + "keyId" integer, + "xlsBlobId" integer, + "publishedAt" timestamp(3) with time zone, + "draftToken" character varying(64), + "enketoId" character varying(255), + name text, + "schemaId" integer +); + + +ALTER TABLE public.form_defs OWNER TO jubilant; + +-- +-- Name: form_defs_id_seq; Type: SEQUENCE; Schema: public; Owner: jubilant +-- + +CREATE SEQUENCE public.form_defs_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER SEQUENCE public.form_defs_id_seq OWNER TO jubilant; + +-- +-- Name: form_defs_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: jubilant +-- + +ALTER SEQUENCE public.form_defs_id_seq OWNED BY public.form_defs.id; + + +-- +-- Name: form_field_values; Type: TABLE; Schema: public; Owner: jubilant +-- + +CREATE TABLE public.form_field_values ( + "formId" integer NOT NULL, + "submissionDefId" integer NOT NULL, + path text NOT NULL, + value text +); + + +ALTER TABLE public.form_field_values OWNER TO jubilant; + +-- +-- Name: form_fields; Type: TABLE; Schema: public; Owner: jubilant +-- + +CREATE TABLE public.form_fields ( + "formId" integer NOT NULL, + path text NOT NULL, + name text NOT NULL, + type character varying(32) NOT NULL, + "binary" boolean, + "order" integer NOT NULL, + "selectMultiple" boolean, + "schemaId" integer NOT NULL +); + + +ALTER TABLE public.form_fields OWNER TO jubilant; + +-- +-- Name: form_schemas; Type: TABLE; Schema: public; Owner: jubilant +-- + +CREATE TABLE public.form_schemas ( + id integer NOT NULL +); + + +ALTER TABLE public.form_schemas OWNER TO jubilant; + +-- +-- Name: form_schemas_id_seq; Type: SEQUENCE; Schema: public; Owner: jubilant +-- + +CREATE SEQUENCE public.form_schemas_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER SEQUENCE public.form_schemas_id_seq OWNER TO jubilant; + +-- +-- Name: form_schemas_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: jubilant +-- + +ALTER SEQUENCE public.form_schemas_id_seq OWNED BY public.form_schemas.id; + + +-- +-- Name: forms; Type: TABLE; Schema: public; Owner: jubilant +-- + +CREATE TABLE public.forms ( + id integer NOT NULL, + "xmlFormId" character varying(64) NOT NULL, + "createdAt" timestamp(3) with time zone, + "updatedAt" timestamp(3) with time zone, + "deletedAt" timestamp(3) with time zone, + "acteeId" character varying(36) NOT NULL, + state text DEFAULT 'open'::text, + "projectId" integer NOT NULL, + "currentDefId" integer, + "draftDefId" integer, + "enketoId" character varying(255), + "enketoOnceId" text, + "webformsEnabled" boolean DEFAULT false NOT NULL +); + + +ALTER TABLE public.forms OWNER TO jubilant; + +-- +-- Name: forms_id_seq; Type: SEQUENCE; Schema: public; Owner: jubilant +-- + +CREATE SEQUENCE public.forms_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER SEQUENCE public.forms_id_seq OWNER TO jubilant; + +-- +-- Name: forms_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: jubilant +-- + +ALTER SEQUENCE public.forms_id_seq OWNED BY public.forms.id; + + +-- +-- Name: keys; Type: TABLE; Schema: public; Owner: jubilant +-- + +CREATE TABLE public.keys ( + id integer NOT NULL, + public text NOT NULL, + private jsonb, + managed boolean, + hint text, + "createdAt" timestamp(3) with time zone +); + + +ALTER TABLE public.keys OWNER TO jubilant; + +-- +-- Name: keys_id_seq; Type: SEQUENCE; Schema: public; Owner: jubilant +-- + +CREATE SEQUENCE public.keys_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER SEQUENCE public.keys_id_seq OWNER TO jubilant; + +-- +-- Name: keys_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: jubilant +-- + +ALTER SEQUENCE public.keys_id_seq OWNED BY public.keys.id; + + +-- +-- Name: knex_migrations; Type: TABLE; Schema: public; Owner: jubilant +-- + +CREATE TABLE public.knex_migrations ( + id integer NOT NULL, + name character varying(255), + batch integer, + migration_time timestamp(3) with time zone +); + + +ALTER TABLE public.knex_migrations OWNER TO jubilant; + +-- +-- Name: knex_migrations_id_seq; Type: SEQUENCE; Schema: public; Owner: jubilant +-- + +CREATE SEQUENCE public.knex_migrations_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER SEQUENCE public.knex_migrations_id_seq OWNER TO jubilant; + +-- +-- Name: knex_migrations_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: jubilant +-- + +ALTER SEQUENCE public.knex_migrations_id_seq OWNED BY public.knex_migrations.id; + + +-- +-- Name: knex_migrations_lock; Type: TABLE; Schema: public; Owner: jubilant +-- + +CREATE TABLE public.knex_migrations_lock ( + index integer NOT NULL, + is_locked integer +); + + +ALTER TABLE public.knex_migrations_lock OWNER TO jubilant; + +-- +-- Name: knex_migrations_lock_index_seq; Type: SEQUENCE; Schema: public; Owner: jubilant +-- + +CREATE SEQUENCE public.knex_migrations_lock_index_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER SEQUENCE public.knex_migrations_lock_index_seq OWNER TO jubilant; + +-- +-- Name: knex_migrations_lock_index_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: jubilant +-- + +ALTER SEQUENCE public.knex_migrations_lock_index_seq OWNED BY public.knex_migrations_lock.index; + + +-- +-- Name: projects; Type: TABLE; Schema: public; Owner: jubilant +-- + +CREATE TABLE public.projects ( + id integer NOT NULL, + name text NOT NULL, + "acteeId" character varying(36) NOT NULL, + "createdAt" timestamp(3) with time zone, + "updatedAt" timestamp(3) with time zone, + "deletedAt" timestamp(3) with time zone, + archived boolean, + "keyId" integer, + description text +); + + +ALTER TABLE public.projects OWNER TO jubilant; + +-- +-- Name: projects_id_seq; Type: SEQUENCE; Schema: public; Owner: jubilant +-- + +CREATE SEQUENCE public.projects_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER SEQUENCE public.projects_id_seq OWNER TO jubilant; + +-- +-- Name: projects_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: jubilant +-- + +ALTER SEQUENCE public.projects_id_seq OWNED BY public.projects.id; + + +-- +-- Name: public_links; Type: TABLE; Schema: public; Owner: jubilant +-- + +CREATE TABLE public.public_links ( + "actorId" integer NOT NULL, + "createdBy" integer NOT NULL, + "formId" integer NOT NULL, + once boolean +); + + +ALTER TABLE public.public_links OWNER TO jubilant; + +-- +-- Name: purged_entities; Type: TABLE; Schema: public; Owner: jubilant +-- + +CREATE TABLE public.purged_entities ( + "entityUuid" character varying NOT NULL, + "acteeId" character varying NOT NULL, + "auditId" integer NOT NULL +); + + +ALTER TABLE public.purged_entities OWNER TO jubilant; + +-- +-- Name: purged_entities_auditId_seq; Type: SEQUENCE; Schema: public; Owner: jubilant +-- + +CREATE SEQUENCE public."purged_entities_auditId_seq" + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER SEQUENCE public."purged_entities_auditId_seq" OWNER TO jubilant; + +-- +-- Name: purged_entities_auditId_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: jubilant +-- + +ALTER SEQUENCE public."purged_entities_auditId_seq" OWNED BY public.purged_entities."auditId"; + + +-- +-- Name: roles; Type: TABLE; Schema: public; Owner: jubilant +-- + +CREATE TABLE public.roles ( + id integer NOT NULL, + name text NOT NULL, + system character varying(8), + "createdAt" timestamp(3) with time zone, + "updatedAt" timestamp(3) with time zone, + verbs jsonb +); + + +ALTER TABLE public.roles OWNER TO jubilant; + +-- +-- Name: roles_id_seq; Type: SEQUENCE; Schema: public; Owner: jubilant +-- + +CREATE SEQUENCE public.roles_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER SEQUENCE public.roles_id_seq OWNER TO jubilant; + +-- +-- Name: roles_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: jubilant +-- + +ALTER SEQUENCE public.roles_id_seq OWNED BY public.roles.id; + + +-- +-- Name: sessions; Type: TABLE; Schema: public; Owner: jubilant +-- + +CREATE TABLE public.sessions ( + "actorId" integer NOT NULL, + token character varying(64) NOT NULL, + "expiresAt" timestamp(3) with time zone NOT NULL, + "createdAt" timestamp(3) with time zone, + csrf character varying(64) +); + + +ALTER TABLE public.sessions OWNER TO jubilant; + +-- +-- Name: submission_attachments; Type: TABLE; Schema: public; Owner: jubilant +-- + +CREATE TABLE public.submission_attachments ( + "blobId" integer, + name text NOT NULL, + "submissionDefId" integer NOT NULL, + index integer, + "isClientAudit" boolean +); + + +ALTER TABLE public.submission_attachments OWNER TO jubilant; + +-- +-- Name: submission_defs; Type: TABLE; Schema: public; Owner: jubilant +-- + +CREATE TABLE public.submission_defs ( + id integer NOT NULL, + "submissionId" integer NOT NULL, + xml text NOT NULL, + "formDefId" integer NOT NULL, + "submitterId" integer, + "createdAt" timestamp(3) with time zone, + "encDataAttachmentName" character varying(255), + "localKey" text, + signature text, + current boolean, + "instanceName" text, + "instanceId" character varying(64) NOT NULL, + "userAgent" character varying(255), + "deviceId" character varying(255), + root boolean +); + + +ALTER TABLE public.submission_defs OWNER TO jubilant; + +-- +-- Name: submission_defs_id_seq; Type: SEQUENCE; Schema: public; Owner: jubilant +-- + +CREATE SEQUENCE public.submission_defs_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER SEQUENCE public.submission_defs_id_seq OWNER TO jubilant; + +-- +-- Name: submission_defs_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: jubilant +-- + +ALTER SEQUENCE public.submission_defs_id_seq OWNED BY public.submission_defs.id; + + +-- +-- Name: submissions; Type: TABLE; Schema: public; Owner: jubilant +-- + +CREATE TABLE public.submissions ( + id integer NOT NULL, + "formId" integer NOT NULL, + "instanceId" character varying(64) NOT NULL, + "createdAt" timestamp(3) with time zone, + "updatedAt" timestamp(3) with time zone, + "deletedAt" timestamp(3) with time zone, + "submitterId" integer, + "deviceId" character varying(255), + draft boolean NOT NULL, + "reviewState" text +); + + +ALTER TABLE public.submissions OWNER TO jubilant; + +-- +-- Name: submissions_id_seq; Type: SEQUENCE; Schema: public; Owner: jubilant +-- + +CREATE SEQUENCE public.submissions_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER SEQUENCE public.submissions_id_seq OWNER TO jubilant; + +-- +-- Name: submissions_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: jubilant +-- + +ALTER SEQUENCE public.submissions_id_seq OWNED BY public.submissions.id; + + +-- +-- Name: user_project_preferences; Type: TABLE; Schema: public; Owner: jubilant +-- + +CREATE TABLE public.user_project_preferences ( + "userId" integer NOT NULL, + "projectId" integer NOT NULL, + "propertyName" text NOT NULL, + "propertyValue" jsonb NOT NULL, + CONSTRAINT "user_project_preferences_propertyName_check" CHECK ((length("propertyName") > 0)) +); + + +ALTER TABLE public.user_project_preferences OWNER TO jubilant; + +-- +-- Name: user_site_preferences; Type: TABLE; Schema: public; Owner: jubilant +-- + +CREATE TABLE public.user_site_preferences ( + "userId" integer NOT NULL, + "propertyName" text NOT NULL, + "propertyValue" jsonb NOT NULL, + CONSTRAINT "user_site_preferences_propertyName_check" CHECK ((length("propertyName") > 0)) +); + + +ALTER TABLE public.user_site_preferences OWNER TO jubilant; + +-- +-- Name: users; Type: TABLE; Schema: public; Owner: jubilant +-- + +CREATE TABLE public.users ( + "actorId" integer NOT NULL, + password character varying(64), + email public.citext NOT NULL +); + + +ALTER TABLE public.users OWNER TO jubilant; + +-- +-- Name: actors id; Type: DEFAULT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.actors ALTER COLUMN id SET DEFAULT nextval('public.actors_id_seq'::regclass); + + +-- +-- Name: audits id; Type: DEFAULT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.audits ALTER COLUMN id SET DEFAULT nextval('public.audits_id_seq'::regclass); + + +-- +-- Name: blobs id; Type: DEFAULT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.blobs ALTER COLUMN id SET DEFAULT nextval('public.blobs_id_seq'::regclass); + + +-- +-- Name: comments id; Type: DEFAULT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.comments ALTER COLUMN id SET DEFAULT nextval('public.comments_id_seq'::regclass); + + +-- +-- Name: datasets id; Type: DEFAULT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.datasets ALTER COLUMN id SET DEFAULT nextval('public.datasets_id_seq'::regclass); + + +-- +-- Name: ds_properties id; Type: DEFAULT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.ds_properties ALTER COLUMN id SET DEFAULT nextval('public.ds_properties_id_seq'::regclass); + + +-- +-- Name: entities id; Type: DEFAULT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.entities ALTER COLUMN id SET DEFAULT nextval('public.entities_id_seq'::regclass); + + +-- +-- Name: entity_def_sources id; Type: DEFAULT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.entity_def_sources ALTER COLUMN id SET DEFAULT nextval('public.entity_def_sources_id_seq'::regclass); + + +-- +-- Name: entity_defs id; Type: DEFAULT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.entity_defs ALTER COLUMN id SET DEFAULT nextval('public.entity_defs_id_seq'::regclass); + + +-- +-- Name: form_defs id; Type: DEFAULT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.form_defs ALTER COLUMN id SET DEFAULT nextval('public.form_defs_id_seq'::regclass); + + +-- +-- Name: form_schemas id; Type: DEFAULT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.form_schemas ALTER COLUMN id SET DEFAULT nextval('public.form_schemas_id_seq'::regclass); + + +-- +-- Name: forms id; Type: DEFAULT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.forms ALTER COLUMN id SET DEFAULT nextval('public.forms_id_seq'::regclass); + + +-- +-- Name: keys id; Type: DEFAULT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.keys ALTER COLUMN id SET DEFAULT nextval('public.keys_id_seq'::regclass); + + +-- +-- Name: knex_migrations id; Type: DEFAULT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.knex_migrations ALTER COLUMN id SET DEFAULT nextval('public.knex_migrations_id_seq'::regclass); + + +-- +-- Name: knex_migrations_lock index; Type: DEFAULT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.knex_migrations_lock ALTER COLUMN index SET DEFAULT nextval('public.knex_migrations_lock_index_seq'::regclass); + + +-- +-- Name: projects id; Type: DEFAULT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.projects ALTER COLUMN id SET DEFAULT nextval('public.projects_id_seq'::regclass); + + +-- +-- Name: purged_entities auditId; Type: DEFAULT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.purged_entities ALTER COLUMN "auditId" SET DEFAULT nextval('public."purged_entities_auditId_seq"'::regclass); + + +-- +-- Name: roles id; Type: DEFAULT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.roles ALTER COLUMN id SET DEFAULT nextval('public.roles_id_seq'::regclass); + + +-- +-- Name: submission_defs id; Type: DEFAULT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.submission_defs ALTER COLUMN id SET DEFAULT nextval('public.submission_defs_id_seq'::regclass); + + +-- +-- Name: submissions id; Type: DEFAULT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.submissions ALTER COLUMN id SET DEFAULT nextval('public.submissions_id_seq'::regclass); + + +-- +-- Name: actees actees_pkey; Type: CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.actees + ADD CONSTRAINT actees_pkey PRIMARY KEY (id); + + +-- +-- Name: actors actors_pkey; Type: CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.actors + ADD CONSTRAINT actors_pkey PRIMARY KEY (id); + + +-- +-- Name: assignments assignments_pkey; Type: CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.assignments + ADD CONSTRAINT assignments_pkey PRIMARY KEY ("actorId", "roleId", "acteeId"); + + +-- +-- Name: audits audits_pkey; Type: CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.audits + ADD CONSTRAINT audits_pkey PRIMARY KEY (id); + + +-- +-- Name: blobs blobs_pkey; Type: CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.blobs + ADD CONSTRAINT blobs_pkey PRIMARY KEY (id); + + +-- +-- Name: blobs blobs_sha_unique; Type: CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.blobs + ADD CONSTRAINT blobs_sha_unique UNIQUE (sha); + + +-- +-- Name: comments comments_pkey; Type: CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.comments + ADD CONSTRAINT comments_pkey PRIMARY KEY (id); + + +-- +-- Name: config config_pkey; Type: CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.config + ADD CONSTRAINT config_pkey PRIMARY KEY (key); + + +-- +-- Name: dataset_form_defs dataset_form_defs_datasetid_formdefid_unique; Type: CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.dataset_form_defs + ADD CONSTRAINT dataset_form_defs_datasetid_formdefid_unique UNIQUE ("datasetId", "formDefId"); + + +-- +-- Name: datasets datasets_name_projectid_unique; Type: CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.datasets + ADD CONSTRAINT datasets_name_projectid_unique UNIQUE (name, "projectId"); + + +-- +-- Name: datasets datasets_pkey; Type: CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.datasets + ADD CONSTRAINT datasets_pkey PRIMARY KEY (id); + + +-- +-- Name: ds_properties ds_properties_name_datasetid_unique; Type: CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.ds_properties + ADD CONSTRAINT ds_properties_name_datasetid_unique UNIQUE (name, "datasetId"); + + +-- +-- Name: ds_properties ds_properties_pkey; Type: CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.ds_properties + ADD CONSTRAINT ds_properties_pkey PRIMARY KEY (id); + + +-- +-- Name: ds_property_fields ds_property_fields_dspropertyid_formdefid_unique; Type: CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.ds_property_fields + ADD CONSTRAINT ds_property_fields_dspropertyid_formdefid_unique UNIQUE ("dsPropertyId", "formDefId"); + + +-- +-- Name: ds_property_fields ds_property_fields_dspropertyid_path_formdefid_unique; Type: CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.ds_property_fields + ADD CONSTRAINT ds_property_fields_dspropertyid_path_formdefid_unique UNIQUE ("dsPropertyId", path, "formDefId"); + + +-- +-- Name: entities entities_pkey; Type: CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.entities + ADD CONSTRAINT entities_pkey PRIMARY KEY (id); + + +-- +-- Name: entities entities_uuid_unique; Type: CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.entities + ADD CONSTRAINT entities_uuid_unique UNIQUE (uuid); + + +-- +-- Name: entity_def_sources entity_def_sources_pkey; Type: CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.entity_def_sources + ADD CONSTRAINT entity_def_sources_pkey PRIMARY KEY (id); + + +-- +-- Name: entity_defs entity_defs_pkey; Type: CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.entity_defs + ADD CONSTRAINT entity_defs_pkey PRIMARY KEY (id); + + +-- +-- Name: entity_submission_backlog entity_submission_backlog_branchId_branchBaseVersion_key; Type: CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.entity_submission_backlog + ADD CONSTRAINT "entity_submission_backlog_branchId_branchBaseVersion_key" UNIQUE ("branchId", "branchBaseVersion"); + + +-- +-- Name: field_keys field_keys_pkey; Type: CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.field_keys + ADD CONSTRAINT field_keys_pkey PRIMARY KEY ("actorId"); + + +-- +-- Name: form_attachments form_attachments_pkey; Type: CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.form_attachments + ADD CONSTRAINT form_attachments_pkey PRIMARY KEY ("formDefId", name); + + +-- +-- Name: form_defs form_defs_pkey; Type: CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.form_defs + ADD CONSTRAINT form_defs_pkey PRIMARY KEY (id); + + +-- +-- Name: form_fields form_fields_pkey; Type: CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.form_fields + ADD CONSTRAINT form_fields_pkey PRIMARY KEY ("schemaId", path); + + +-- +-- Name: form_schemas form_schemas_pkey; Type: CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.form_schemas + ADD CONSTRAINT form_schemas_pkey PRIMARY KEY (id); + + +-- +-- Name: forms forms_pkey; Type: CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.forms + ADD CONSTRAINT forms_pkey PRIMARY KEY (id); + + +-- +-- Name: keys keys_pkey; Type: CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.keys + ADD CONSTRAINT keys_pkey PRIMARY KEY (id); + + +-- +-- Name: keys keys_public_unique; Type: CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.keys + ADD CONSTRAINT keys_public_unique UNIQUE (public); + + +-- +-- Name: knex_migrations_lock knex_migrations_lock_pkey; Type: CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.knex_migrations_lock + ADD CONSTRAINT knex_migrations_lock_pkey PRIMARY KEY (index); + + +-- +-- Name: knex_migrations knex_migrations_pkey; Type: CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.knex_migrations + ADD CONSTRAINT knex_migrations_pkey PRIMARY KEY (id); + + +-- +-- Name: projects projects_acteeid_unique; Type: CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.projects + ADD CONSTRAINT projects_acteeid_unique UNIQUE ("acteeId"); + + +-- +-- Name: projects projects_pkey; Type: CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.projects + ADD CONSTRAINT projects_pkey PRIMARY KEY (id); + + +-- +-- Name: public_links public_links_pkey; Type: CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.public_links + ADD CONSTRAINT public_links_pkey PRIMARY KEY ("actorId"); + + +-- +-- Name: purged_entities purged_entities_pkey; Type: CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.purged_entities + ADD CONSTRAINT purged_entities_pkey PRIMARY KEY ("entityUuid"); + + +-- +-- Name: roles roles_pkey; Type: CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.roles + ADD CONSTRAINT roles_pkey PRIMARY KEY (id); + + +-- +-- Name: submission_attachments submission_attachments_pkey; Type: CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.submission_attachments + ADD CONSTRAINT submission_attachments_pkey PRIMARY KEY ("submissionDefId", name); + + +-- +-- Name: submission_defs submission_defs_pkey; Type: CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.submission_defs + ADD CONSTRAINT submission_defs_pkey PRIMARY KEY (id); + + +-- +-- Name: submissions submissions_formid_instanceid_draft_unique; Type: CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.submissions + ADD CONSTRAINT submissions_formid_instanceid_draft_unique UNIQUE ("formId", "instanceId", draft); + + +-- +-- Name: submissions submissions_pkey; Type: CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.submissions + ADD CONSTRAINT submissions_pkey PRIMARY KEY (id); + + +-- +-- Name: user_project_preferences user_project_preferences_primary_key; Type: CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.user_project_preferences + ADD CONSTRAINT user_project_preferences_primary_key PRIMARY KEY ("userId", "projectId", "propertyName"); + + +-- +-- Name: user_site_preferences user_site_preferences_primary_key; Type: CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.user_site_preferences + ADD CONSTRAINT user_site_preferences_primary_key PRIMARY KEY ("userId", "propertyName"); + + +-- +-- Name: users users_pkey; Type: CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.users + ADD CONSTRAINT users_pkey PRIMARY KEY ("actorId"); + + +-- +-- Name: actees_parent_index; Type: INDEX; Schema: public; Owner: jubilant +-- + +CREATE INDEX actees_parent_index ON public.actees USING btree (parent); + + +-- +-- Name: actors_displayname_gist_index; Type: INDEX; Schema: public; Owner: jubilant +-- + +CREATE INDEX actors_displayname_gist_index ON public.actors USING gist ("displayName" public.gist_trgm_ops); + + +-- +-- Name: actors_type_index; Type: INDEX; Schema: public; Owner: jubilant +-- + +CREATE INDEX actors_type_index ON public.actors USING btree (type); + + +-- +-- Name: assignments_actorid_index; Type: INDEX; Schema: public; Owner: jubilant +-- + +CREATE INDEX assignments_actorid_index ON public.assignments USING btree ("actorId"); + + +-- +-- Name: audits_acteeid_loggedat_index; Type: INDEX; Schema: public; Owner: jubilant +-- + +CREATE INDEX audits_acteeid_loggedat_index ON public.audits USING btree ("acteeId", "loggedAt"); + + +-- +-- Name: audits_action_acteeid_loggedat_index; Type: INDEX; Schema: public; Owner: jubilant +-- + +CREATE INDEX audits_action_acteeid_loggedat_index ON public.audits USING btree (action, "acteeId", "loggedAt"); + + +-- +-- Name: audits_actorid_action_loggedat_index; Type: INDEX; Schema: public; Owner: jubilant +-- + +CREATE INDEX audits_actorid_action_loggedat_index ON public.audits USING btree ("actorId", action, "loggedAt"); + + +-- +-- Name: audits_actorid_loggedat_index; Type: INDEX; Schema: public; Owner: jubilant +-- + +CREATE INDEX audits_actorid_loggedat_index ON public.audits USING btree ("actorId", "loggedAt"); + + +-- +-- Name: audits_claimed_processed_index; Type: INDEX; Schema: public; Owner: jubilant +-- + +CREATE INDEX audits_claimed_processed_index ON public.audits USING btree (claimed, processed); + + +-- +-- Name: audits_details_entity_def_index; Type: INDEX; Schema: public; Owner: jubilant +-- + +CREATE INDEX audits_details_entity_def_index ON public.audits USING hash ((((details ->> 'entityDefId'::text))::integer)); + + +-- +-- Name: audits_details_entity_index; Type: INDEX; Schema: public; Owner: jubilant +-- + +CREATE INDEX audits_details_entity_index ON public.audits USING hash ((((details ->> 'entityId'::text))::integer)); + + +-- +-- Name: audits_details_entity_source_index; Type: INDEX; Schema: public; Owner: jubilant +-- + +CREATE INDEX audits_details_entity_source_index ON public.audits USING hash ((((details ->> 'sourceId'::text))::integer)); + + +-- +-- Name: audits_details_entity_uuid; Type: INDEX; Schema: public; Owner: jubilant +-- + +CREATE INDEX audits_details_entity_uuid ON public.audits USING hash ((((details -> 'entity'::text) ->> 'uuid'::text))) WHERE (action = ANY (ARRAY['entity.create'::text, 'entity.update'::text, 'entity.update.version'::text, 'entity.update.resolve'::text, 'entity.delete'::text, 'entity.restore'::text])); + + +-- +-- Name: audits_details_entity_uuid_index; Type: INDEX; Schema: public; Owner: jubilant +-- + +CREATE INDEX audits_details_entity_uuid_index ON public.audits USING hash ((((details -> 'entity'::text) ->> 'uuid'::text))); + + +-- +-- Name: audits_details_entityuuids; Type: INDEX; Schema: public; Owner: jubilant +-- + +CREATE INDEX audits_details_entityuuids ON public.audits USING gin (((details -> 'entityUuids'::text)) jsonb_path_ops) WHERE (action = 'entity.purge'::text); + + +-- +-- Name: audits_details_submission_index; Type: INDEX; Schema: public; Owner: jubilant +-- + +CREATE INDEX audits_details_submission_index ON public.audits USING hash ((((details -> 'submissionId'::text))::integer)); + + +-- +-- Name: blobs_sha_index; Type: INDEX; Schema: public; Owner: jubilant +-- + +CREATE INDEX blobs_sha_index ON public.blobs USING btree (sha); + + +-- +-- Name: client_audits_start_index; Type: INDEX; Schema: public; Owner: jubilant +-- + +CREATE INDEX client_audits_start_index ON public.client_audits USING btree (start); + + +-- +-- Name: comments_submissionid_index; Type: INDEX; Schema: public; Owner: jubilant +-- + +CREATE INDEX comments_submissionid_index ON public.comments USING btree ("submissionId"); + + +-- +-- Name: entities_datasetid_createdat_id_index; Type: INDEX; Schema: public; Owner: jubilant +-- + +CREATE INDEX entities_datasetid_createdat_id_index ON public.entities USING btree ("datasetId", "createdAt", id); + + +-- +-- Name: entities_deletedat_index; Type: INDEX; Schema: public; Owner: jubilant +-- + +CREATE INDEX entities_deletedat_index ON public.entities USING btree ("deletedAt"); + + +-- +-- Name: entity_defs_entityid_current_index; Type: INDEX; Schema: public; Owner: jubilant +-- + +CREATE INDEX entity_defs_entityid_current_index ON public.entity_defs USING btree ("entityId", current); + + +-- +-- Name: entity_defs_sourceid_index; Type: INDEX; Schema: public; Owner: jubilant +-- + +CREATE INDEX entity_defs_sourceid_index ON public.entity_defs USING btree ("sourceId"); + + +-- +-- Name: field_keys_actorid_projectid_index; Type: INDEX; Schema: public; Owner: jubilant +-- + +CREATE INDEX field_keys_actorid_projectid_index ON public.field_keys USING btree ("actorId", "projectId"); + + +-- +-- Name: form_attachments_formid_index; Type: INDEX; Schema: public; Owner: jubilant +-- + +CREATE INDEX form_attachments_formid_index ON public.form_attachments USING btree ("formId"); + + +-- +-- Name: form_defs_formid_publishedat_index; Type: INDEX; Schema: public; Owner: jubilant +-- + +CREATE INDEX form_defs_formid_publishedat_index ON public.form_defs USING btree ("formId", "publishedAt"); + + +-- +-- Name: form_field_values_formid_index; Type: INDEX; Schema: public; Owner: jubilant +-- + +CREATE INDEX form_field_values_formid_index ON public.form_field_values USING btree ("formId"); + + +-- +-- Name: form_field_values_formid_submissiondefid_path_index; Type: INDEX; Schema: public; Owner: jubilant +-- + +CREATE INDEX form_field_values_formid_submissiondefid_path_index ON public.form_field_values USING btree ("formId", "submissionDefId", path); + + +-- +-- Name: form_field_values_submissiondefid_index; Type: INDEX; Schema: public; Owner: jubilant +-- + +CREATE INDEX form_field_values_submissiondefid_index ON public.form_field_values USING btree ("submissionDefId"); + + +-- +-- Name: form_fields_formid_path_type_index; Type: INDEX; Schema: public; Owner: jubilant +-- + +CREATE INDEX form_fields_formid_path_type_index ON public.form_fields USING btree ("formId", path, type); + + +-- +-- Name: form_fields_schemaid_binary_index; Type: INDEX; Schema: public; Owner: jubilant +-- + +CREATE INDEX form_fields_schemaid_binary_index ON public.form_fields USING btree ("schemaId", "binary"); + + +-- +-- Name: form_fields_schemaid_order_index; Type: INDEX; Schema: public; Owner: jubilant +-- + +CREATE INDEX form_fields_schemaid_order_index ON public.form_fields USING btree ("schemaId", "order"); + + +-- +-- Name: forms_deletedat_state_index; Type: INDEX; Schema: public; Owner: jubilant +-- + +CREATE INDEX forms_deletedat_state_index ON public.forms USING btree ("deletedAt", state); + + +-- +-- Name: forms_projectid_xmlformid_deletedat_unique; Type: INDEX; Schema: public; Owner: jubilant +-- + +CREATE UNIQUE INDEX forms_projectid_xmlformid_deletedat_unique ON public.forms USING btree ("projectId", "xmlFormId") WHERE ("deletedAt" IS NULL); + + +-- +-- Name: forms_xmlformid_index; Type: INDEX; Schema: public; Owner: jubilant +-- + +CREATE INDEX forms_xmlformid_index ON public.forms USING btree ("xmlFormId"); + + +-- +-- Name: keys_public_index; Type: INDEX; Schema: public; Owner: jubilant +-- + +CREATE INDEX keys_public_index ON public.keys USING btree (public); + + +-- +-- Name: purged_entities_actee_uuid_index; Type: INDEX; Schema: public; Owner: jubilant +-- + +CREATE INDEX purged_entities_actee_uuid_index ON public.purged_entities USING btree ("acteeId", "entityUuid"); + + +-- +-- Name: roles_verbs_gin_index; Type: INDEX; Schema: public; Owner: jubilant +-- + +CREATE INDEX roles_verbs_gin_index ON public.roles USING gin (verbs jsonb_path_ops); + + +-- +-- Name: sessions_actorid_expires_index; Type: INDEX; Schema: public; Owner: jubilant +-- + +CREATE INDEX sessions_actorid_expires_index ON public.sessions USING btree ("actorId", "expiresAt"); + + +-- +-- Name: sessions_token_index; Type: INDEX; Schema: public; Owner: jubilant +-- + +CREATE UNIQUE INDEX sessions_token_index ON public.sessions USING btree (token); + + +-- +-- Name: submission_defs_createdat_id_index; Type: INDEX; Schema: public; Owner: jubilant +-- + +CREATE INDEX submission_defs_createdat_id_index ON public.submission_defs USING btree ("createdAt", id); + + +-- +-- Name: submission_defs_submissionid_current_index; Type: INDEX; Schema: public; Owner: jubilant +-- + +CREATE INDEX submission_defs_submissionid_current_index ON public.submission_defs USING btree ("submissionId", current); + + +-- +-- Name: submission_defs_submissionid_instanceid_index; Type: INDEX; Schema: public; Owner: jubilant +-- + +CREATE INDEX submission_defs_submissionid_instanceid_index ON public.submission_defs USING btree ("submissionId", "instanceId"); + + +-- +-- Name: submissions_formid_createdat_id_index; Type: INDEX; Schema: public; Owner: jubilant +-- + +CREATE INDEX submissions_formid_createdat_id_index ON public.submissions USING btree ("formId", "createdAt", id); + + +-- +-- Name: user_project_preferences_userId_idx; Type: INDEX; Schema: public; Owner: jubilant +-- + +CREATE INDEX "user_project_preferences_userId_idx" ON public.user_project_preferences USING btree ("userId"); + + +-- +-- Name: user_site_preferences_userId_idx; Type: INDEX; Schema: public; Owner: jubilant +-- + +CREATE INDEX "user_site_preferences_userId_idx" ON public.user_site_preferences USING btree ("userId"); + + +-- +-- Name: users_email_gist_index; Type: INDEX; Schema: public; Owner: jubilant +-- + +CREATE INDEX users_email_gist_index ON public.users USING gist (email public.gist_trgm_ops); + + +-- +-- Name: users_email_index; Type: INDEX; Schema: public; Owner: jubilant +-- + +CREATE INDEX users_email_index ON public.users USING btree (email); + + +-- +-- Name: users check_email; Type: TRIGGER; Schema: public; Owner: jubilant +-- + +CREATE TRIGGER check_email BEFORE INSERT OR UPDATE ON public.users FOR EACH ROW EXECUTE FUNCTION public.check_email(); + + +-- +-- Name: forms check_form_state; Type: TRIGGER; Schema: public; Owner: jubilant +-- + +CREATE TRIGGER check_form_state BEFORE INSERT OR UPDATE ON public.forms FOR EACH ROW EXECUTE FUNCTION public.check_form_state(); + + +-- +-- Name: form_defs check_form_version; Type: TRIGGER; Schema: public; Owner: jubilant +-- + +CREATE TRIGGER check_form_version AFTER INSERT OR UPDATE ON public.form_defs FOR EACH ROW EXECUTE FUNCTION public.check_form_version(); + + +-- +-- Name: submission_defs check_instanceid_unique; Type: TRIGGER; Schema: public; Owner: jubilant +-- + +CREATE TRIGGER check_instanceid_unique AFTER INSERT ON public.submission_defs FOR EACH ROW EXECUTE FUNCTION public.check_instanceid_unique(); + + +-- +-- Name: form_defs check_managed_key; Type: TRIGGER; Schema: public; Owner: jubilant +-- + +CREATE TRIGGER check_managed_key AFTER INSERT OR UPDATE ON public.form_defs FOR EACH ROW EXECUTE FUNCTION public.check_managed_key(); + + +-- +-- Name: submissions check_review_state; Type: TRIGGER; Schema: public; Owner: jubilant +-- + +CREATE TRIGGER check_review_state BEFORE INSERT OR UPDATE ON public.submissions FOR EACH ROW EXECUTE FUNCTION public.check_review_state(); + + +-- +-- Name: actors actors_acteeid_foreign; Type: FK CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.actors + ADD CONSTRAINT actors_acteeid_foreign FOREIGN KEY ("acteeId") REFERENCES public.actees(id); + + +-- +-- Name: assignments assignments_acteeid_foreign; Type: FK CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.assignments + ADD CONSTRAINT assignments_acteeid_foreign FOREIGN KEY ("acteeId") REFERENCES public.actees(id); + + +-- +-- Name: assignments assignments_actorid_foreign; Type: FK CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.assignments + ADD CONSTRAINT assignments_actorid_foreign FOREIGN KEY ("actorId") REFERENCES public.actors(id); + + +-- +-- Name: assignments assignments_roleid_foreign; Type: FK CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.assignments + ADD CONSTRAINT assignments_roleid_foreign FOREIGN KEY ("roleId") REFERENCES public.roles(id); + + +-- +-- Name: submission_attachments attachments_blobid_foreign; Type: FK CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.submission_attachments + ADD CONSTRAINT attachments_blobid_foreign FOREIGN KEY ("blobId") REFERENCES public.blobs(id); + + +-- +-- Name: audits audits_acteeid_foreign; Type: FK CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.audits + ADD CONSTRAINT audits_acteeid_foreign FOREIGN KEY ("acteeId") REFERENCES public.actees(id); + + +-- +-- Name: audits audits_actorid_foreign; Type: FK CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.audits + ADD CONSTRAINT audits_actorid_foreign FOREIGN KEY ("actorId") REFERENCES public.actors(id); + + +-- +-- Name: client_audits client_audits_blobid_foreign; Type: FK CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.client_audits + ADD CONSTRAINT client_audits_blobid_foreign FOREIGN KEY ("blobId") REFERENCES public.blobs(id); + + +-- +-- Name: comments comments_actorid_foreign; Type: FK CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.comments + ADD CONSTRAINT comments_actorid_foreign FOREIGN KEY ("actorId") REFERENCES public.actors(id); + + +-- +-- Name: comments comments_submissionid_foreign; Type: FK CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.comments + ADD CONSTRAINT comments_submissionid_foreign FOREIGN KEY ("submissionId") REFERENCES public.submissions(id) ON DELETE CASCADE; + + +-- +-- Name: dataset_form_defs dataset_form_defs_datasetid_foreign; Type: FK CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.dataset_form_defs + ADD CONSTRAINT dataset_form_defs_datasetid_foreign FOREIGN KEY ("datasetId") REFERENCES public.datasets(id); + + +-- +-- Name: dataset_form_defs dataset_form_defs_formdefid_foreign; Type: FK CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.dataset_form_defs + ADD CONSTRAINT dataset_form_defs_formdefid_foreign FOREIGN KEY ("formDefId") REFERENCES public.form_defs(id) ON DELETE CASCADE; + + +-- +-- Name: datasets datasets_projectid_foreign; Type: FK CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.datasets + ADD CONSTRAINT datasets_projectid_foreign FOREIGN KEY ("projectId") REFERENCES public.projects(id); + + +-- +-- Name: ds_properties ds_properties_datasetid_foreign; Type: FK CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.ds_properties + ADD CONSTRAINT ds_properties_datasetid_foreign FOREIGN KEY ("datasetId") REFERENCES public.datasets(id); + + +-- +-- Name: ds_property_fields ds_property_fields_dspropertyid_foreign; Type: FK CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.ds_property_fields + ADD CONSTRAINT ds_property_fields_dspropertyid_foreign FOREIGN KEY ("dsPropertyId") REFERENCES public.ds_properties(id); + + +-- +-- Name: entities entities_createdby_foreign; Type: FK CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.entities + ADD CONSTRAINT entities_createdby_foreign FOREIGN KEY ("creatorId") REFERENCES public.actors(id); + + +-- +-- Name: entities entities_datasetid_foreign; Type: FK CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.entities + ADD CONSTRAINT entities_datasetid_foreign FOREIGN KEY ("datasetId") REFERENCES public.datasets(id); + + +-- +-- Name: entity_def_sources entity_def_sources_auditid_foreign; Type: FK CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.entity_def_sources + ADD CONSTRAINT entity_def_sources_auditid_foreign FOREIGN KEY ("auditId") REFERENCES public.audits(id) ON DELETE SET NULL; + + +-- +-- Name: entity_def_sources entity_def_sources_submissiondefid_foreign; Type: FK CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.entity_def_sources + ADD CONSTRAINT entity_def_sources_submissiondefid_foreign FOREIGN KEY ("submissionDefId") REFERENCES public.submission_defs(id) ON DELETE SET NULL; + + +-- +-- Name: entity_defs entity_defs_entityid_foreign; Type: FK CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.entity_defs + ADD CONSTRAINT entity_defs_entityid_foreign FOREIGN KEY ("entityId") REFERENCES public.entities(id) ON DELETE CASCADE; + + +-- +-- Name: entity_defs entity_defs_sourceid_foreign; Type: FK CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.entity_defs + ADD CONSTRAINT entity_defs_sourceid_foreign FOREIGN KEY ("sourceId") REFERENCES public.entity_def_sources(id); + + +-- +-- Name: field_keys field_keys_actorid_foreign; Type: FK CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.field_keys + ADD CONSTRAINT field_keys_actorid_foreign FOREIGN KEY ("actorId") REFERENCES public.actors(id); + + +-- +-- Name: field_keys field_keys_createdby_foreign; Type: FK CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.field_keys + ADD CONSTRAINT field_keys_createdby_foreign FOREIGN KEY ("createdBy") REFERENCES public.actors(id); + + +-- +-- Name: field_keys field_keys_projectid_foreign; Type: FK CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.field_keys + ADD CONSTRAINT field_keys_projectid_foreign FOREIGN KEY ("projectId") REFERENCES public.projects(id); + + +-- +-- Name: entity_submission_backlog fk_audit_id; Type: FK CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.entity_submission_backlog + ADD CONSTRAINT fk_audit_id FOREIGN KEY ("auditId") REFERENCES public.audits(id) ON DELETE CASCADE; + + +-- +-- Name: entity_submission_backlog fk_submission_defs; Type: FK CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.entity_submission_backlog + ADD CONSTRAINT fk_submission_defs FOREIGN KEY ("submissionDefId") REFERENCES public.submission_defs(id) ON DELETE CASCADE; + + +-- +-- Name: entity_submission_backlog fk_submissions; Type: FK CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.entity_submission_backlog + ADD CONSTRAINT fk_submissions FOREIGN KEY ("submissionId") REFERENCES public.submissions(id) ON DELETE CASCADE; + + +-- +-- Name: form_attachments form_attachments_blobid_foreign; Type: FK CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.form_attachments + ADD CONSTRAINT form_attachments_blobid_foreign FOREIGN KEY ("blobId") REFERENCES public.blobs(id); + + +-- +-- Name: form_attachments form_attachments_datasetid_foreign; Type: FK CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.form_attachments + ADD CONSTRAINT form_attachments_datasetid_foreign FOREIGN KEY ("datasetId") REFERENCES public.datasets(id); + + +-- +-- Name: form_attachments form_attachments_formdefid_foreign; Type: FK CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.form_attachments + ADD CONSTRAINT form_attachments_formdefid_foreign FOREIGN KEY ("formDefId") REFERENCES public.form_defs(id) ON DELETE CASCADE; + + +-- +-- Name: form_attachments form_attachments_formid_foreign; Type: FK CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.form_attachments + ADD CONSTRAINT form_attachments_formid_foreign FOREIGN KEY ("formId") REFERENCES public.forms(id) ON DELETE CASCADE; + + +-- +-- Name: form_defs form_defs_formid_foreign; Type: FK CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.form_defs + ADD CONSTRAINT form_defs_formid_foreign FOREIGN KEY ("formId") REFERENCES public.forms(id) ON DELETE CASCADE; + + +-- +-- Name: form_defs form_defs_keyid_foreign; Type: FK CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.form_defs + ADD CONSTRAINT form_defs_keyid_foreign FOREIGN KEY ("keyId") REFERENCES public.keys(id); + + +-- +-- Name: form_defs form_defs_schemaid_foreign; Type: FK CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.form_defs + ADD CONSTRAINT form_defs_schemaid_foreign FOREIGN KEY ("schemaId") REFERENCES public.form_schemas(id); + + +-- +-- Name: form_defs form_defs_xlsblobid_foreign; Type: FK CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.form_defs + ADD CONSTRAINT form_defs_xlsblobid_foreign FOREIGN KEY ("xlsBlobId") REFERENCES public.blobs(id); + + +-- +-- Name: form_field_values form_field_values_formid_foreign; Type: FK CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.form_field_values + ADD CONSTRAINT form_field_values_formid_foreign FOREIGN KEY ("formId") REFERENCES public.forms(id) ON DELETE CASCADE; + + +-- +-- Name: form_field_values form_field_values_submissiondefid_foreign; Type: FK CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.form_field_values + ADD CONSTRAINT form_field_values_submissiondefid_foreign FOREIGN KEY ("submissionDefId") REFERENCES public.submission_defs(id) ON DELETE CASCADE; + + +-- +-- Name: form_fields form_fields_formid_foreign; Type: FK CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.form_fields + ADD CONSTRAINT form_fields_formid_foreign FOREIGN KEY ("formId") REFERENCES public.forms(id) ON DELETE CASCADE; + + +-- +-- Name: form_fields form_fields_schemaid_foreign; Type: FK CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.form_fields + ADD CONSTRAINT form_fields_schemaid_foreign FOREIGN KEY ("schemaId") REFERENCES public.form_schemas(id) ON DELETE CASCADE; + + +-- +-- Name: forms forms_acteeid_foreign; Type: FK CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.forms + ADD CONSTRAINT forms_acteeid_foreign FOREIGN KEY ("acteeId") REFERENCES public.actees(id); + + +-- +-- Name: forms forms_currentdefid_foreign; Type: FK CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.forms + ADD CONSTRAINT forms_currentdefid_foreign FOREIGN KEY ("currentDefId") REFERENCES public.form_defs(id); + + +-- +-- Name: forms forms_draftdefid_foreign; Type: FK CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.forms + ADD CONSTRAINT forms_draftdefid_foreign FOREIGN KEY ("draftDefId") REFERENCES public.form_defs(id); + + +-- +-- Name: forms forms_projectid_foreign; Type: FK CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.forms + ADD CONSTRAINT forms_projectid_foreign FOREIGN KEY ("projectId") REFERENCES public.projects(id); + + +-- +-- Name: projects projects_keyid_foreign; Type: FK CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.projects + ADD CONSTRAINT projects_keyid_foreign FOREIGN KEY ("keyId") REFERENCES public.keys(id); + + +-- +-- Name: public_links public_links_actorid_foreign; Type: FK CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.public_links + ADD CONSTRAINT public_links_actorid_foreign FOREIGN KEY ("actorId") REFERENCES public.actors(id); + + +-- +-- Name: public_links public_links_createdby_foreign; Type: FK CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.public_links + ADD CONSTRAINT public_links_createdby_foreign FOREIGN KEY ("createdBy") REFERENCES public.actors(id); + + +-- +-- Name: public_links public_links_formid_foreign; Type: FK CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.public_links + ADD CONSTRAINT public_links_formid_foreign FOREIGN KEY ("formId") REFERENCES public.forms(id) ON DELETE CASCADE; + + +-- +-- Name: sessions sessions_actorid_foreign; Type: FK CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.sessions + ADD CONSTRAINT sessions_actorid_foreign FOREIGN KEY ("actorId") REFERENCES public.actors(id); + + +-- +-- Name: submission_attachments submission_attachments_submissiondefid_foreign; Type: FK CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.submission_attachments + ADD CONSTRAINT submission_attachments_submissiondefid_foreign FOREIGN KEY ("submissionDefId") REFERENCES public.submission_defs(id) ON DELETE CASCADE; + + +-- +-- Name: submission_defs submission_defs_actorid_foreign; Type: FK CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.submission_defs + ADD CONSTRAINT submission_defs_actorid_foreign FOREIGN KEY ("submitterId") REFERENCES public.actors(id); + + +-- +-- Name: submission_defs submission_defs_formdefid_foreign; Type: FK CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.submission_defs + ADD CONSTRAINT submission_defs_formdefid_foreign FOREIGN KEY ("formDefId") REFERENCES public.form_defs(id) ON DELETE CASCADE; + + +-- +-- Name: submission_defs submission_defs_submissionid_foreign; Type: FK CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.submission_defs + ADD CONSTRAINT submission_defs_submissionid_foreign FOREIGN KEY ("submissionId") REFERENCES public.submissions(id) ON DELETE CASCADE; + + +-- +-- Name: submissions submissions_formid_foreign; Type: FK CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.submissions + ADD CONSTRAINT submissions_formid_foreign FOREIGN KEY ("formId") REFERENCES public.forms(id) ON DELETE CASCADE; + + +-- +-- Name: submissions submissions_submitter_foreign; Type: FK CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.submissions + ADD CONSTRAINT submissions_submitter_foreign FOREIGN KEY ("submitterId") REFERENCES public.actors(id); + + +-- +-- Name: user_project_preferences user_project_preferences_projectId_fkey; Type: FK CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.user_project_preferences + ADD CONSTRAINT "user_project_preferences_projectId_fkey" FOREIGN KEY ("projectId") REFERENCES public.projects(id); + + +-- +-- Name: user_project_preferences user_project_preferences_userId_fkey; Type: FK CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.user_project_preferences + ADD CONSTRAINT "user_project_preferences_userId_fkey" FOREIGN KEY ("userId") REFERENCES public.users("actorId"); + + +-- +-- Name: user_site_preferences user_site_preferences_userId_fkey; Type: FK CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.user_site_preferences + ADD CONSTRAINT "user_site_preferences_userId_fkey" FOREIGN KEY ("userId") REFERENCES public.users("actorId"); + + +-- +-- Name: users users_actorid_foreign; Type: FK CONSTRAINT; Schema: public; Owner: jubilant +-- + +ALTER TABLE ONLY public.users + ADD CONSTRAINT users_actorid_foreign FOREIGN KEY ("actorId") REFERENCES public.actors(id); + + +-- +-- Name: SCHEMA public; Type: ACL; Schema: -; Owner: postgres +-- + +REVOKE USAGE ON SCHEMA public FROM PUBLIC; +GRANT ALL ON SCHEMA public TO PUBLIC; + + +-- +-- PostgreSQL database dump complete +-- + diff --git a/test/db-partial-migrations/test.sh b/test/db-partial-migrations/test.sh new file mode 100755 index 000000000..a65bfc6d5 --- /dev/null +++ b/test/db-partial-migrations/test.sh @@ -0,0 +1,133 @@ +#!/bin/bash -eu +set -o pipefail +shopt -s inherit_errexit + +log() { echo "[test/db-partial-migrations/prepare] $*"; } + +if ! [[ "$(git status --porcelain)" = "" ]]; then + log "!!!" + log "!!! You have uncomitted changes in your local git repo." + log "!!!" + log "!!! This command will make destructive changes." + log "!!!" + log "!!! Please revert or commit these changes before continuing." + log "!!!" + exit 1 +fi + +show_migrations() { + tree lib/model/migrations + sleep 1 # wait for output to flush - seems slow in CI +} + +prerun="$1" +log "Requested for prerun: $prerun" + +migrations_legacy=./lib/model/migrations/legacy +migrations_new=./lib/model/migrations + +rmExceptFirst() { + local skipCount="$1" + local src="$2" + find "$src" -maxdepth 1 -type f -name \*.js -printf '%p\n' | + sort | + tail -n +$((skipCount+1)) | + xargs rm +} + +fixLegacyMigrations() { + # fix relative require() paths + sed -E -i -e "s:\.\./\.\./:../:" -- "$migrations_legacy"/*.js + mv "$migrations_legacy"/*.js "$migrations_new/" +} + +fixNewMigrations() { + # convert pg API to knex API + sed -E -i -e "s/db\.query/db.raw/" -- "$migrations_new"/*.js +} + +log "Re-arranging migrations..." +case "$prerun" in + none) + rm "$migrations_new"/*.js + rm "$migrations_legacy"/*.js + ;; + legacy-all) + rm "$migrations_new"/*.js + fixLegacyMigrations + ;; + legacy-first-*) + rm "$migrations_new"/*.js + rmExceptFirst "$(sed s/legacy-first-// <<<"$prerun")" "$migrations_legacy" + fixLegacyMigrations + ;; + new-first-*-as-legacy) + rmExceptFirst "$(sed "s/new-first-\(.*\)-as-legacy/\1/" <<<"$prerun")" "$migrations_new" + fixNewMigrations + fixLegacyMigrations + ;; + all) + fixNewMigrations + fixLegacyMigrations + ;; + *) + log "!!!" + log "!!! No rule found matching '$prerun'" + log "!!!" + exit 1 + ;; +esac +log "Initial migrations structure:" +show_migrations + +log "Running legacy migrations..." +make migrations-legacy + +pgConnectionString="$(node -e ' + const { host, database, user, password } = require("config").get("default.database"); + console.log(`postgres://${user}:${password}@${host}/${database}`); +')" +migrationsTable="knex_migrations" + +expectedMigrations="$(cd lib/model/migrations && find -maxdepth 1 -type f -name \*.js -printf '%p\n' | sort)" +actualMigrations="$(psql --quiet --tuples-only --no-align "$pgConnectionString" -c "SELECT './' || name FROM $migrationsTable")" +if ! diff <(echo "$expectedMigrations") <(echo "$actualMigrations"); then + log "!!!" + log "!!! Migrations ran differed from expected. See above for details." + log "!!!" + exit 1 +fi + +log "Re-instating unrun migrations..." +git checkout -- lib/model/migrations +git clean -dfx -- lib/model/migrations + +log "Final migrations structure:" +show_migrations + +log "Running modern migrations..." +make migrations + +log "Checking final database schema..." +if ! diff \ + test/db-partial-migrations/expected-schema.sql \ + <(pg_dump --schema-only "$pgConnectionString"); then + sleep 1 # wait for output to flush - seems slow in CI + log "!!!" + log "!!! Schema differences detected. See above for details." + log "!!!" + exit 1 +fi + +log "Checking migrations table..." +if ! diff \ + test/db-partial-migrations/expected-migrations-table-contents.sql \ + <(psql "$pgConnectionString" -c "SELECT id, name FROM $migrationsTable"); then + sleep 1 # wait for output to flush - seems slow in CI + log "!!!" + log "!!! $migrationsTable table content differences detected. See above for details." + log "!!!" + exit 1 +fi + +log "Completed OK." diff --git a/test/integration/other/knex-migrations.js b/test/integration/other/knex-migrations.js index 897ce4443..124d1dffc 100644 --- a/test/integration/other/knex-migrations.js +++ b/test/integration/other/knex-migrations.js @@ -15,7 +15,7 @@ const populateForms = require('../fixtures/02-forms'); const { getFormFields } = require('../../../lib/data/schema'); const withTestDatabase = withKnex(config.get('test.database')); -const migrationsDir = appRoot + '/lib/model/migrations'; +const migrationsDir = appRoot + '/lib/model/migrations/legacy'; const upToMigration = (toName, inclusive = true) => withTestDatabase(async (migrator) => { await migrator.raw('drop owned by current_user'); const migrations = await migrator.migrate.list({ directory: migrationsDir }); @@ -41,7 +41,7 @@ const testMigration = (filename, tests, options = {}) => { // eslint-disable-next-line no-only-tests/no-only-tests ? describe.only.bind(describe) : (skip ? describe.skip.bind(describe) : describe); - f(`database migrations: ${filename}`, function() { + f(`knex migrations: ${filename}`, function() { this.timeout(20000); beforeEach(() => upToMigration(filename, false)); @@ -59,7 +59,7 @@ testMigration.skip = (filename, tests) => // column to projects and forms, it is not possible to migrate part way // (before the new column) and populate the data when frames expect the // new column to exist. -describe.skip('database migrations', function() { +describe.skip('knex migrations', function() { this.timeout(8000); it('should purge deleted forms via migration', testServiceFullTrx(async (service, container) => { @@ -922,7 +922,8 @@ describe.skip('database migration: 20231002-01-add-conflict-details.js', functio })); }); -testMigration('20240215-01-entity-delete-verb.js', () => { +// Skip because 1. this migration now runs on new migrator, and (2) it relies on application code. +testMigration.skip('20240215-01-entity-delete-verb.js', () => { it('should add entity.delete verb to correct roles', testServiceFullTrx(async (service) => { const verbsByRole = async () => { const { body: roles } = await service.get('/v1/roles').expect(200); @@ -951,7 +952,8 @@ testMigration('20240215-01-entity-delete-verb.js', () => { })); }); -testMigration('20240215-02-dedupe-verbs.js', () => { +// Skip because 1. this migration now runs on new migrator, and (2) it relies on application code. +testMigration.skip('20240215-02-dedupe-verbs.js', () => { it('should remove duplicate submission.update verb', testServiceFullTrx(async (service) => { const verbsByRole = async () => { const { body: roles } = await service.get('/v1/roles').expect(200); diff --git a/test/integration/setup.js b/test/integration/setup.js index ba40ccfba..98f4a1305 100644 --- a/test/integration/setup.js +++ b/test/integration/setup.js @@ -1,3 +1,4 @@ +const { execSync } = require('node:child_process'); const { readFileSync } = require('fs'); const appRoot = require('app-root-path'); const { mergeRight } = require('ramda'); @@ -5,18 +6,16 @@ const { sql } = require('slonik'); const { readdirSync } = require('fs'); const { join } = require('path'); const request = require('supertest'); -const { noop } = require(appRoot + '/lib/util/util'); const { task } = require(appRoot + '/lib/task/task'); const authenticateUser = require('../util/authenticate-user'); const testData = require('../data/xml'); - -// knex things. const config = require('config'); -const { knexConnect } = require(appRoot + '/lib/model/knex-migrator'); +const { withPg } = require(appRoot + '/lib/model/pg-migrator'); // slonik connection pool const { slonikPool } = require(appRoot + '/lib/external/slonik'); -const db = slonikPool(config.get('test.database')); +const dbConfig = config.get('test.database'); +const db = slonikPool(dbConfig); // set up our mailer. const env = config.get('default.env'); @@ -72,18 +71,8 @@ const populate = (container, [ head, ...tail ] = fixtures) => // this hook won't run if `test-unit` is called, as this directory is skipped // in that case. const initialize = async () => { - const migrator = knexConnect(config.get('test.database')); - const { log } = console; - try { - await migrator.raw('drop owned by current_user'); - // Silence logging from migrations. - console.log = noop; // eslint-disable-line no-console - await migrator.migrate.latest({ directory: appRoot + '/lib/model/migrations' }); - } finally { - console.log = log; // eslint-disable-line no-console - await migrator.destroy(); - } - + await withPg(dbConfig)(client => client.query('DROP OWNED BY CURRENT_USER')); + execSync('make migrations', { env: { ...process.env, NODE_CONFIG: JSON.stringify({ default: { database: dbConfig } }) } }); return withDefaults({ db, context, enketo, env, s3 }).transacting(populate); };