]> git.aero2k.de Git - tmp/jakarta-migration.git/commitdiff
DRYD-835: Add uoc upgrade script, make others re-runnable.
authorRay Lee <ray.lee@lyrasis.org>
Fri, 3 Apr 2020 05:05:41 +0000 (01:05 -0400)
committerRay Lee <ray.lee@lyrasis.org>
Fri, 3 Apr 2020 05:05:41 +0000 (01:05 -0400)
services/common/src/main/java/org/collectionspace/services/common/ServiceMain.java
services/common/src/main/java/org/collectionspace/services/common/storage/JDBCTools.java
src/main/resources/db/postgresql/upgrade/6.0.0/post-init/01_organization.sql
src/main/resources/db/postgresql/upgrade/6.0.0/post-init/02_intake.sql
src/main/resources/db/postgresql/upgrade/6.0.0/post-init/04_anthro-collectionobject.sql
src/main/resources/db/postgresql/upgrade/6.0.0/post-init/05_uoc.sql [new file with mode: 0644]

index 77776f2a8459b0235a1061df1805abb062baee23..08466017174ee459904f8cfe79aa02cc0fac41b4 100644 (file)
@@ -477,10 +477,15 @@ public class ServiceMain {
        }
 
        private void upgradeRepository(String dataSourceName, String repositoryName, String cspaceInstanceId) throws Exception {
-               // Install the pgcrypto extension so that the gen_random_uuid function will be available
-               // to upgrade scripts.
+               // Install the uuid-ossp extension so that the uuid_generate_v4 function will be available to
+               // upgrade scripts.
 
-               JDBCTools.executeUpdate(JDBCTools.CSADMIN_NUXEO_DATASOURCE_NAME, repositoryName, cspaceInstanceId, "CREATE EXTENSION IF NOT EXISTS \"pgcrypto\"");
+               try {
+                       JDBCTools.executeUpdate(JDBCTools.CSADMIN_NUXEO_DATASOURCE_NAME, repositoryName, cspaceInstanceId, "CREATE EXTENSION IF NOT EXISTS \"uuid-ossp\"");
+               }
+               catch(Exception e) {
+                       logger.warn("Could not install uuid-ossp postgresql extension. Database upgrades may fail without this extension. On some platforms you may need to manually install this extension as a superuser.");
+               }
 
                String stage = "post-init";
                Connection conn = null;
index a6bbb646be2320dd97bc9bee61147532842dd6e2..3384052710ea4bf025c9ff89495b3c582899c1e7 100644 (file)
@@ -32,7 +32,6 @@ import java.io.BufferedReader;
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.FileReader;
-import java.io.Reader;
 import java.sql.Connection;
 import java.sql.DriverManager;
 import java.sql.PreparedStatement;
@@ -826,6 +825,7 @@ public class JDBCTools {
 
         scriptRunner.setAutoCommit(false);
         scriptRunner.setStopOnError(true);
+        scriptRunner.setSendFullScript(true);
 
         scriptRunner.runScript(new BufferedReader(new FileReader(scriptFile)));
     }
index c07fc48cd6ede45861355c0a28e42ae73847c15f..d6ceb804718ec8eb2ece571b897063f54f726606 100644 (file)
@@ -1,10 +1,87 @@
 -- Upgrade organization. Move contact names into the new repeating contact group (DRYD-566).
 
-INSERT INTO hierarchy (id, parentid, pos, name, isproperty, primarytype)
-  SELECT gen_random_uuid(), id, pos, 'organizations_common:contactGroupList', TRUE, 'contactGroup'
-    FROM organizations_common_contactnames;
-
-INSERT INTO contactgroup (id, contactname)
-  SELECT h.id, occ.item
-    FROM organizations_common_contactnames occ
-    INNER JOIN hierarchy h ON h.parentid = occ.id AND h.pos = occ.pos AND h.name = 'organizations_common:contactGroupList';
+DO $$
+DECLARE
+    trow record;
+    maxpos int;
+    uuid varchar(36);
+BEGIN
+
+    -- For new install, if organizations_common_contactnames does not exist, there is nothing to migrate.
+
+    IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='organizations_common_contactnames') THEN
+        FOR trow IN
+            -- Get record in organizations_common_contactnames that does not have an existing/matching record in contactgroup:
+            -- Verify that there is data to migrate.
+
+            SELECT id AS parentid, item, pos
+            FROM public.organizations_common_contactnames
+            WHERE ROW(id, pos) NOT IN (
+                SELECT occ.id, occ.pos
+                FROM public.organizations_common_contactnames occ
+                JOIN public.hierarchy h ON (occ.id = h.parentid)
+                JOIN public.contactgroup cg ON (
+                    h.id = cg.id
+                    AND occ.item IS NOT DISTINCT FROM cg.contactname
+                )
+            )
+            AND (item IS NOT NULL)
+            ORDER BY pos
+
+            LOOP
+                -- Get max pos value for the organization record's contact group, and generate a new uuid:
+
+                SELECT
+                    coalesce(max(pos), -1),
+                    uuid_generate_v4()::varchar
+                INTO
+                    maxpos,
+                    uuid
+                FROM public.hierarchy
+                WHERE parentid = trow.parentid
+                    AND name = 'organizations_common:contactGroupList'
+                    AND primarytype = 'contactGroup';
+
+                -- Insert new record into hierarchy table first, due to foreign key on contactgroup table:
+
+                INSERT INTO public.hierarchy (
+                    id,
+                    parentid,
+                    pos,
+                    name,
+                    isproperty,
+                    primarytype)
+                VALUES (
+                    uuid,
+                    trow.parentid,
+                    maxpos + 1,
+                    'organizations_common:contactGroupList',
+                    TRUE,
+                    'contactGroup');
+
+                -- Migrate organization contact data into contactgroup table:
+
+                INSERT INTO public.contactgroup (
+                    id,
+                    contactname)
+                VALUES (
+                    uuid,
+                    trow.item);
+
+            END LOOP;
+
+    ELSE
+        RAISE NOTICE 'No v5.2 organization data to migrate: organizations_common_contactnames does not exist';
+
+    END IF;
+END
+$$;
+
+-- INSERT INTO hierarchy (id, parentid, pos, name, isproperty, primarytype)
+--   SELECT uuid_generate_v4(), id, pos, 'organizations_common:contactGroupList', TRUE, 'contactGroup'
+--     FROM organizations_common_contactnames;
+
+-- INSERT INTO contactgroup (id, contactname)
+--   SELECT h.id, occ.item
+--     FROM organizations_common_contactnames occ
+--     INNER JOIN hierarchy h ON h.parentid = occ.id AND h.pos = occ.pos AND h.name = 'organizations_common:contactGroupList';
index 7babdf4024b1353624c27d9236c684066f88c821..d14936a4aecf98e932d53b3e3a26048dd74be448 100644 (file)
 -- Upgrade intake. Move depositor and currentowner into repeating fields (DRYD-801).
 
-INSERT INTO intakes_common_currentowners (id, pos, item)
-  SELECT id, 0, currentowner
-    FROM intakes_common
-    WHERE currentowner IS NOT NULL;
-
-INSERT INTO hierarchy (id, parentid, pos, name, isproperty, primarytype)
-  SELECT gen_random_uuid(), id, 0, 'intakes_common:depositorGroupList', TRUE, 'depositorGroup'
-    FROM intakes_common
-    WHERE depositor IS NOT NULL OR depositorsrequirements IS NOT NULL;
-
-INSERT INTO depositorgroup (id, depositor, depositorsrequirements)
-  SELECT h.id, ic.depositor, ic.depositorsrequirements
-    FROM intakes_common ic
-    INNER JOIN hierarchy h ON h.parentid = ic.id AND h.name = 'intakes_common:depositorGroupList'
-    WHERE ic.depositor IS NOT NULL OR ic.depositorsrequirements IS NOT NULL;
+DO $$
+DECLARE
+    trow record;
+    maxpos int;
+BEGIN
+    -- For new install, if intakes_common.currentowner does not exist, there is nothing to migrate.
+
+    IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='intakes_common' AND column_name='currentowner') THEN
+        FOR trow IN
+            -- Get record in intakes_common that does not have an existing/matching record in intakes_common_currentowners:
+
+            SELECT ic.id, ic.currentowner
+            FROM public.intakes_common ic
+            LEFT OUTER JOIN public.intakes_common_currentowners icc ON (ic.id = icc.id AND ic.currentowner = icc.item)
+            WHERE ic.currentowner IS NOT NULL AND ic.currentowner != '' AND icc.item IS NULL
+
+            LOOP
+                -- Get max pos value for the intake record's current owner field:
+
+                SELECT coalesce(max(pos), -1) INTO maxpos
+                FROM public.intakes_common_currentowners
+                WHERE id = trow.id;
+
+                -- Migrate intakes_common current owner data to intakes_common_currentowners table:
+
+                INSERT INTO public.intakes_common_currentowners (id, pos, item)
+                VALUES (trow.id, maxpos + 1, trow.currentowner);
+
+            END LOOP;
+
+    ELSE
+        RAISE NOTICE 'No v5.2 intake current owner data to migrate: intakes_common.currentowner does not exist';
+
+    END IF;
+END
+$$;
+
+-- INSERT INTO intakes_common_currentowners (id, pos, item)
+--   SELECT id, 0, currentowner
+--     FROM intakes_common
+--     WHERE currentowner IS NOT NULL;
+
+DO $$
+DECLARE
+    trow record;
+    maxpos int;
+    uuid varchar(36);
+BEGIN
+
+    -- For new install, if intakes_common.depositor does not exist, there is nothing to migrate.
+
+    IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='intakes_common' AND column_name='depositor') THEN
+        FOR trow IN
+            -- Get record in intakes_common that does not have an existing/matching record in depositorgroup:
+            -- Verify that there is data to migrate.
+
+            SELECT id AS parentid, depositor, depositorsrequirements
+            FROM public.intakes_common
+            WHERE id NOT IN (
+                SELECT ic.id
+                FROM public.intakes_common ic
+                JOIN public.hierarchy h ON (ic.id = h.parentid)
+                JOIN public.depositorgroup dg ON (
+                    h.id = dg.id
+                    AND ic.depositor IS NOT DISTINCT FROM dg.depositor
+                    AND ic.depositorsrequirements IS NOT DISTINCT FROM dg.depositorsrequirements
+                )
+            )
+            AND (depositor IS NOT NULL OR depositorsrequirements IS NOT NULL)
+
+            LOOP
+                -- Get max pos value for the intake record's depositor group, and generate a new uuid:
+
+                SELECT
+                    coalesce(max(pos), -1),
+                    uuid_generate_v4()::varchar
+                INTO
+                    maxpos,
+                    uuid
+                FROM public.hierarchy
+                WHERE parentid = trow.parentid
+                AND name = 'intakes_common:depositorGroupList'
+                AND primarytype = 'depositorGroup';
+
+                -- Insert new record into hierarchy table first, due to foreign key on depositorgroup table:
+
+                INSERT INTO public.hierarchy (
+                    id,
+                    parentid,
+                    pos,
+                    name,
+                    isproperty,
+                    primarytype)
+                VALUES (
+                    uuid,
+                    trow.parentid,
+                    maxpos + 1,
+                    'intakes_common:depositorGroupList',
+                    TRUE,
+                    'depositorGroup');
+
+                -- Migrate intake depositor data into depositorgroup table:
+
+                INSERT INTO public.depositorgroup (
+                    id,
+                    depositor,
+                    depositorsrequirements)
+                VALUES (
+                    uuid,
+                    trow.depositor,
+                    trow.depositorsrequirements);
+
+            END LOOP;
+
+    ELSE
+        RAISE NOTICE 'No v5.2 intake depositor data to migrate: intakes_common.depositor does not exist';
+
+    END IF;
+END
+$$;
+
+-- INSERT INTO hierarchy (id, parentid, pos, name, isproperty, primarytype)
+--   SELECT uuid_generate_v4(), id, 0, 'intakes_common:depositorGroupList', TRUE, 'depositorGroup'
+--     FROM intakes_common
+--     WHERE depositor IS NOT NULL OR depositorsrequirements IS NOT NULL;
+
+-- INSERT INTO depositorgroup (id, depositor, depositorsrequirements)
+--   SELECT h.id, ic.depositor, ic.depositorsrequirements
+--     FROM intakes_common ic
+--     INNER JOIN hierarchy h ON h.parentid = ic.id AND h.name = 'intakes_common:depositorGroupList'
+--     WHERE ic.depositor IS NOT NULL OR ic.depositorsrequirements IS NOT NULL;
index 33f985f0e50369a1f8f928d1260a0793c37bbe61..890474cd0eac0949a69e58886f02b141c9231243 100644 (file)
@@ -1,11 +1,88 @@
 -- Upgrade the anthropology collectionobject extension. Move nagpra cultural determinations into
 -- the the note field in the new nagpra determination group (DRYD-820).
 
-INSERT INTO hierarchy (id, parentid, pos, name, isproperty, primarytype)
-  SELECT gen_random_uuid(), id, pos, 'collectionobjects_nagpra:nagpraDetermGroupList', TRUE, 'nagpraDetermGroup'
-    FROM collectionobjects_nagpra_nagpraculturaldeterminations;
-
-INSERT INTO nagpradetermgroup (id, nagpradetermnote)
-  SELECT h.id, cnn.item
-    FROM collectionobjects_nagpra_nagpraculturaldeterminations cnn
-    INNER JOIN hierarchy h ON h.parentid = cnn.id AND h.pos = cnn.pos AND h.name = 'collectionobjects_nagpra:nagpraDetermGroupList';
+DO $$
+DECLARE
+    trow record;
+    maxpos int;
+    uuid varchar(36);
+BEGIN
+
+    -- For new install, if collectionobjects_nagpra_nagpraculturaldeterminations does not exist, there is nothing to migrate.
+
+    IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='collectionobjects_nagpra_nagpraculturaldeterminations') THEN
+        FOR trow IN
+            -- Get record in collectionobjects_nagpra_nagpraculturaldeterminations that does not have an existing/matching record in nagpradetermgroup:
+            -- Verify that there is data to migrate.
+
+            SELECT id AS parentid, item, pos
+            FROM public.collectionobjects_nagpra_nagpraculturaldeterminations
+            WHERE ROW(id, pos) NOT IN (
+                SELECT cnn.id, cnn.pos
+                FROM public.collectionobjects_nagpra_nagpraculturaldeterminations cnn
+                JOIN public.hierarchy h ON (cnn.id = h.parentid)
+                JOIN public.nagpradetermgroup ndg ON (
+                    h.id = ndg.id
+                    AND cnn.item IS NOT DISTINCT FROM ndg.nagpradetermnote
+                )
+            )
+            AND (item IS NOT NULL)
+            ORDER BY pos
+
+            LOOP
+                -- Get max pos value for the collectionobject record's nagpra determination group, and generate a new uuid:
+
+                SELECT
+                    coalesce(max(pos), -1),
+                    uuid_generate_v4()::varchar
+                INTO
+                    maxpos,
+                    uuid
+                FROM public.hierarchy
+                WHERE parentid = trow.parentid
+                    AND name = 'collectionobjects_nagpra:nagpraDetermGroupList'
+                    AND primarytype = 'nagpraDetermGroup';
+
+                -- Insert new record into hierarchy table first, due to foreign key on nagpradetermgroup table:
+
+                INSERT INTO public.hierarchy (
+                    id,
+                    parentid,
+                    pos,
+                    name,
+                    isproperty,
+                    primarytype)
+                VALUES (
+                    uuid,
+                    trow.parentid,
+                    maxpos + 1,
+                    'collectionobjects_nagpra:nagpraDetermGroupList',
+                    TRUE,
+                    'nagpraDetermGroup');
+
+                -- Migrate collectionobject nagpra cultural determination data into nagpradetermgroup table:
+
+                INSERT INTO public.nagpradetermgroup (
+                    id,
+                    nagpradetermnote)
+                VALUES (
+                    uuid,
+                    trow.item);
+
+            END LOOP;
+
+    ELSE
+        RAISE NOTICE 'No v5.2 collectionobject nagpra cultural determination data to migrate: collectionobjects_nagpra_nagpraculturaldeterminations does not exist';
+
+    END IF;
+END
+$$;
+
+-- INSERT INTO hierarchy (id, parentid, pos, name, isproperty, primarytype)
+--   SELECT uuid_generate_v4(), id, pos, 'collectionobjects_nagpra:nagpraDetermGroupList', TRUE, 'nagpraDetermGroup'
+--     FROM collectionobjects_nagpra_nagpraculturaldeterminations;
+
+-- INSERT INTO nagpradetermgroup (id, nagpradetermnote)
+--   SELECT h.id, cnn.item
+--     FROM collectionobjects_nagpra_nagpraculturaldeterminations cnn
+--     INNER JOIN hierarchy h ON h.parentid = cnn.id AND h.pos = cnn.pos AND h.name = 'collectionobjects_nagpra:nagpraDetermGroupList';
diff --git a/src/main/resources/db/postgresql/upgrade/6.0.0/post-init/05_uoc.sql b/src/main/resources/db/postgresql/upgrade/6.0.0/post-init/05_uoc.sql
new file mode 100644 (file)
index 0000000..18b0b12
--- /dev/null
@@ -0,0 +1,395 @@
+/* Use of Collections Data Migration from Version 5.2
+
+-- This SQL script migrates existing Use of Collections data from Version 5.2 to incorporate new features developed by the UCB CSpace team, based on the following assumptions:
+
+    -- The appropriate database is specified in executing this script.
+       This script does not contain commands to connect to the appropriate database.
+
+    -- New database changes have been made: e.g. new tables created, foreign keys added.
+
+    -- New records should not exist in newly created tables.
+       But, since this script may possibly be run repeatedly, it checks for data in the new tables
+       and it only creates a new record if the record has not yet been migrated.
+
+    -- The uuid_generate_v4() function is required to generate UUID for new records.
+       Installing the uuid-ossp extension will make all UUID generation functions available.
+
+    -- To install the uuid-ossp extension and make all the UUID functions available:
+        CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
+
+    -- To only install the uuid_generate_v4() function to generate Type 4 UUIDs:
+        CREATE OR REPLACE FUNCTION public.uuid_generate_v4()
+            RETURNS uuid
+            LANGUAGE c
+            PARALLEL SAFE STRICT
+        AS '$libdir/uuid-ossp', $function$uuid_generate_v4$function$;
+
+    -- Once existing data has been migrated, this script does not delete data from,
+       nor drop the newly obsolete columns.
+
+-- Version 5.2 Use of Collections tables:
+
+    public.uoc_common ============> MIGRATION NEEDED: various fields updated to repeatable fields/groups.
+    public.uoc_common_methodlist => NO MIGRATION NEEDED.
+    public.usergroup =============> NO MIGRATION NEEDED, although 3 new fields were added to the table.
+
+-- Version 5.2 uoc_common table description and migration note:
+
+                                Table "public.uoc_common"
+         Column        |            Type             | Nullable | Migrate To
+    -------------------+-----------------------------+----------+-------------------------
+     id                | character varying(36)       | not null |
+     enddate           | timestamp without time zone |          |
+     location          | character varying           |          | uoc_common_locationlist
+     authorizationdate | timestamp without time zone |          | authorizationgroup
+     title             | character varying           |          |
+     note              | character varying           |          |
+     provisos          | character varying           |          |
+     result            | character varying           |          |
+     referencenumber   | character varying           |          |
+     authorizationnote | character varying           |          | authorizationgroup
+     authorizedby      | character varying           |          | authorizationgroup
+     startsingledate   | timestamp without time zone |          | usedategroup
+    Indexes:
+        "uoc_common_pk" PRIMARY KEY, btree (id)
+    Foreign-key constraints:
+        "uoc_common_id_hierarchy_fk" FOREIGN KEY (id) REFERENCES hierarchy(id) ON DELETE CASCADE
+
+-- NO CHANGES are required for the following uoc_common columns:
+
+    uoc_common.id
+    uoc_common.enddate
+    uoc_common.title
+    uoc_common.note
+    uoc_common.provisos
+    uoc_common.result
+    uoc_common.referencenumber
+
+-- REPEATABLE FIELDS: The following uoc_common columns are now repeatable fields; migration path:
+
+    uoc_common.location ==========> uoc_common_locationlist.item
+    uoc_common.authorizationdate => authorizationgroup.authorizationdate
+    uoc_common.authorizationnote => authorizationgroup.authorizationnote
+    uoc_common.authorizedby ======> authorizationgroup.authorizedby
+    uoc_common.startsingledate ===> usedategroup.usedate
+
+*/
+
+
+-- 1) Create function uuid_generate_v4() for generating UUID before migration:
+
+-- CREATE OR REPLACE FUNCTION public.uuid_generate_v4()
+--  RETURNS uuid
+--  LANGUAGE c
+--  PARALLEL SAFE STRICT
+-- AS '$libdir/uuid-ossp', $function$uuid_generate_v4$function$
+
+
+/* 2) Migrate v5.2 UOC Location data to uoc_common_locationlist table:
+
+-- NEW uoc_common_locationlist table description:
+
+        Table "public.uoc_common_locationlist"
+     Column |         Type          | Modifiers
+    --------+-----------------------+-----------
+     id     | character varying(36) | not null
+     pos    | integer               |
+     item   | character varying     |
+    Indexes:
+        "uoc_common_locationlist_id_idx" btree (id)
+    Foreign-key constraints:
+        "uoc_common_locationlist_id_hierarchy_fk" FOREIGN KEY (id) REFERENCES hierarchy(id) ON DELETE CASCADE
+
+-- Migration path for uoc_common.location:
+
+    uoc_common.id ========> uoc_common_locationlist.id
+    0 or next pos value ==> uoc_common_locationlist.pos
+    uoc_common.location ==> uoc_common_locationlist.item
+
+-- Only add a new record to uoc_common_locationlist when there is a value for location; do not create empty records.
+-- Only add the record if it does NOT already exist.
+-- In the case of a new install, check for the uoc_common.location column and do nothing if it does not exist.
+
+*/
+
+-- Insert a new record into uoc_common_locationlist table.
+
+DO $$
+DECLARE
+    trow record;
+    maxpos int;
+BEGIN
+    -- For new install, if uoc_common.location does not exist, there is nothing to migrate.
+
+    IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='uoc_common' AND column_name='location') THEN
+        FOR trow IN
+            -- Get record in uoc_common that does not have an existing/matching record in uoc_common_locationlist:
+
+            SELECT uc.id, uc.location
+            FROM public.uoc_common uc
+            LEFT OUTER JOIN public.uoc_common_locationlist ucll ON (uc.id = ucll.id AND uc.location = ucll.item)
+            WHERE uc.location IS NOT NULL AND uc.location != '' AND ucll.item IS NULL
+
+            LOOP
+                -- Get max pos value for the UOC record's location field:
+
+                SELECT coalesce(max(pos), -1) INTO maxpos
+                FROM public.uoc_common_locationlist
+                WHERE id = trow.id;
+
+                -- Migrate uoc_common Location data to uoc_common_locationlist table:
+
+                INSERT INTO public.uoc_common_locationlist (id, pos, item)
+                VALUES (trow.id, maxpos + 1, trow.location);
+
+            END LOOP;
+
+    ELSE
+        RAISE NOTICE 'No v5.2 uoc location data to migrate: uoc_common.location does not exist';
+
+    END IF;
+END
+$$;
+
+
+/* 3) Migrate v5.2 UOC Authorization data to authorizationgroup table:
+
+-- NEW authorizationgroup table description:
+
+                Table "public.authorizationgroup"
+           Column        |            Type             | Modifiers
+    ---------------------+-----------------------------+-----------
+     id                  | character varying(36)       | not null
+     authorizationnote   | character varying           |
+     authorizedby        | character varying           |
+     authorizationdate   | timestamp without time zone |
+     authorizationstatus | character varying           |
+    Indexes:
+        "authorizationgroup_pk" PRIMARY KEY, btree (id)
+    Foreign-key constraints:
+        "authorizationgroup_id_hierarchy_fk" FOREIGN KEY (id) REFERENCES hierarchy(id) ON DELETE CASCADE
+
+-- Migrate/add data from uoc_common to hierarchy.
+-- The foreign key on the authorizationgroup table requires first adding new records to hierarchy.
+-- Use the uuid_generate_v4() function to generate a new type 4 UUID for the new record.
+
+    uuid_generate_v4()::varchar AS id ====> hierarchy.id
+    uoc_common.id ========================> hierarchy.parentid
+    0 ====================================> hierarchy.pos
+    'uoc_common:authorizationGroupList' ==> hierarchy.name
+    True =================================> hierarchy.isproperty
+    'authorizationGroup' =================> hierarchy.primarytype
+
+-- Migrate data from uoc_common to authorizationgroup:
+-- Only add a new record when there is a value for authorizationdate, authorizationnote, or authorizedby.
+-- Do not create empty records in authorizationgroup.
+
+    hierarchy.id ==> authorizationgroup.id
+    uoc_common.authorizationdate =======> authorizationgroup.authorizationdate
+    uoc_common.authorizationnote =======> authorizationgroup.authorizationnote
+    uoc_common.authorizedby ============> authorizationgroup.authorizedby
+
+*/
+
+-- Migrate/add Authorization data to hierarchy, authorizationgroup tables.
+
+DO $$
+DECLARE
+    trow record;
+    maxpos int;
+    uuid varchar(36);
+BEGIN
+
+    -- For new install, if uoc_common.authorizedby does not exist, there is nothing to migrate.
+
+    IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='uoc_common' AND column_name='authorizedby') THEN
+        FOR trow IN
+            -- Get record in uoc_common that does not have an existing/matching record in authorizationgroup:
+            -- Verify that there is data to migrate.
+
+            SELECT id AS parentid, authorizedby, authorizationdate, authorizationnote
+            FROM public.uoc_common
+            WHERE id NOT IN (
+                SELECT uc.id
+                FROM public.uoc_common uc
+                JOIN public.hierarchy h ON (uc.id = h.parentid)
+                JOIN public.authorizationgroup ag ON (
+                    h.id = ag.id
+                    AND uc.authorizedby IS NOT DISTINCT FROM ag.authorizedby
+                    AND uc.authorizationdate IS NOT DISTINCT FROM ag.authorizationdate
+                    AND uc.authorizationnote IS NOT DISTINCT FROM ag.authorizationnote
+                )
+            )
+            AND (authorizedby IS NOT NULL OR authorizationdate IS NOT NULL OR authorizationnote IS NOT NULL)
+
+            LOOP
+                -- Get max pos value for the uoc record's authorization group, and generate a new uuid:
+
+                SELECT
+                    coalesce(max(pos), -1),
+                    uuid_generate_v4()::varchar
+                INTO
+                    maxpos,
+                    uuid
+                FROM public.hierarchy
+                WHERE parentid = trow.parentid
+                AND name = 'uoc_common:authorizationGroupList'
+                AND primarytype = 'authorizationGroup';
+
+                -- Insert new record into hierarchy table first, due to foreign key on authorizationgroup table:
+
+                INSERT INTO public.hierarchy (
+                    id,
+                    parentid,
+                    pos,
+                    name,
+                    isproperty,
+                    primarytype)
+                VALUES (
+                    uuid,
+                    trow.parentid,
+                    maxpos + 1,
+                    'uoc_common:authorizationGroupList',
+                    TRUE,
+                    'authorizationGroup');
+
+                -- Migrate uoc_common authorization data into authorizationgroup table:
+
+                INSERT INTO public.authorizationgroup (
+                    id,
+                    authorizedby,
+                    authorizationdate,
+                    authorizationnote)
+                VALUES (
+                    uuid,
+                    trow.authorizedby,
+                    trow.authorizationdate,
+                    trow.authorizationnote);
+
+            END LOOP;
+
+    ELSE
+        RAISE NOTICE 'No v5.2 uoc authorization data to migrate: uoc_common.authorizedby does not exist';
+
+    END IF;
+END
+$$;
+
+
+/* 4) Migrate v5.2 UOC Start/single date data to usedategroup table:
+
+-- NEW usedategroup table description:
+
+                        Table "public.usedategroup"
+             Column          |            Type             | Modifiers
+    -------------------------+-----------------------------+-----------
+     id                      | character varying(36)       | not null
+     usedate                 | timestamp without time zone |
+     usedatenumberofvisitors | bigint                      |
+     usedatevisitornote      | character varying           |
+     usedatehoursspent       | double precision            |
+     usedatetimenote         | character varying           |
+    Indexes:
+        "usedategroup_pk" PRIMARY KEY, btree (id)
+    Foreign-key constraints:
+        "usedategroup_id_hierarchy_fk" FOREIGN KEY (id) REFERENCES hierarchy(id) ON DELETE CASCADE
+
+-- Migrate/add data from uoc_common to hierarchy.
+-- The foreign key on the usedategroup table requires first adding new records to hierarchy.
+-- Use the uuid_generate_v4() function to generate a new type 4 UUID for the new record.
+
+    uuid_generate_v4()::varchar AS id ====> hierarchy.id
+    uoc_common.id ========================> hierarchy.parentid
+    0 ====================================> hierarchy.pos
+    'uoc_common:useDateGroupList' ========> hierarchy.name
+    True =================================> hierarchy.isproperty
+    'useDateGroup' =======================> hierarchy.primarytype
+
+-- Migrate data from uoc_common to usedategroup.
+-- Only add a new record when there is a value for startsingledate.
+-- Do not create empty records in usedategroup.
+
+    hierarchy.id ==> usedategroup.id
+    uoc_common.startsingledate =========> usedategroup.usedate
+
+*/
+
+-- Migrate/add Start/single date data to hierarchy, usedategroup tables.
+
+DO $$
+DECLARE
+    trow record;
+    maxpos int;
+    uuid varchar(36);
+BEGIN
+
+    -- For new install, if uoc_common.startsingledate does not exist, there is nothing to migrate.
+
+    IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='uoc_common' AND column_name='startsingledate') THEN
+        FOR trow IN
+            -- Get record in uoc_common that does not have an existing/matching record in usedategroup.
+            -- Verify that there is data to migrate.
+
+            SELECT id AS parentid, startsingledate AS usedate
+            FROM public.uoc_common
+            WHERE id NOT IN (
+                SELECT uc.id
+                FROM public.uoc_common uc
+                JOIN public.hierarchy h ON (uc.id = h.parentid)
+                JOIN public.usedategroup udg ON (
+                    h.id = udg.id
+                    AND uc.startsingledate IS NOT DISTINCT FROM udg.usedate
+                )
+            )
+            AND startsingledate IS NOT NULL
+
+            LOOP
+                -- Get max pos value for the uoc record's use date group, and generate a new uuid:
+
+                SELECT
+                    coalesce(max(pos), -1),
+                    uuid_generate_v4()::varchar
+                INTO
+                    maxpos,
+                    uuid
+                FROM public.hierarchy
+                WHERE parentid = trow.parentid
+                AND name = 'uoc_common:useDateGroupList'
+                AND primarytype = 'useDateGroup';
+
+                -- Insert new record into hierarchy table first, due to foreign key on usedategroup table:
+
+                INSERT INTO public.hierarchy (
+                    id,
+                    parentid,
+                    pos,
+                    name,
+                    isproperty,
+                    primarytype)
+                VALUES (
+                    uuid,
+                    trow.parentid,
+                    maxpos + 1,
+                    'uoc_common:useDateGroupList',
+                    true,
+                    'useDateGroup');
+
+                -- Insert new record into authorizationgroup table:
+
+                INSERT INTO public.usedategroup (
+                    id,
+                    usedate)
+                VALUES (
+                    uuid,
+                    trow.usedate);
+
+            END LOOP;
+
+    ELSE
+        RAISE NOTICE 'No v5.2 uoc start/single date data to migrate: uoc_common.startsingledate does not exist';
+
+    END IF;
+END
+$$;
+
+-- END OF MIGRATION