--
-- Change ID of any revision having start_time at a later time than
-- any other revision with higher ID
--
-- New IDs for revisions to be moved will be assigned in
-- start_time order and start after the highest existing
-- revision ID.
--
-- The number of moved revisions is returned.
--
-- {
CREATE OR REPLACE FUNCTION ver_fix_revision_disorder()
RETURNS bigint AS
$FIX$
DECLARE

  v_rec RECORD;
  v_newid bigint;
  v_numversionedtables int;
  v_totdisordered bigint;
  v_numdisordered bigint;

BEGIN

-- 1. Make sure sequence is set to stop filling gaps

  PERFORM setval('@extschema@.revision_id_seq',
    greatest(
      (select max(id) FROM @extschema@.revision),
      (select last_value from @extschema@.revision_id_seq)
    ), true);

-- 2. Prepare update queries
  v_numversionedtables := 0;
  FOR v_rec IN SELECT schema_name, table_name
               FROM table_version.versioned_tables
  LOOP
    BEGIN
      EXECUTE format('PREPARE "p_uc%s" AS '
          'UPDATE table_version.%s_%s_revision '
          'SET _revision_created = $1 WHERE _revision_created = $2',
          v_numversionedtables, v_rec.schema_name, v_rec.table_name);
      EXECUTE format('PREPARE "p_ue%s" AS '
          'UPDATE table_version.%s_%s_revision '
          'SET _revision_expired = $1 WHERE _revision_expired = $2',
          v_numversionedtables, v_rec.schema_name, v_rec.table_name);
      v_numversionedtables := v_numversionedtables + 1;
    EXCEPTION WHEN UNDEFINED_TABLE THEN

      RAISE WARNING 'Spurious record in table_version.versioned_tables '
                    'for table % in schema %: % does not exist)',
                    v_rec.table_name, v_rec.schema_name,
                    format('table_version.%s_%s_revision',
                      v_rec.schema_name, v_rec.table_name);
    END;
  END LOOP;

-- 3. For each misplaced revision, move to correct place

  v_totdisordered := 0;

  LOOP

    v_numdisordered := 0;

    FOR v_rec IN
      WITH revs_by_id AS (
        SELECT
          row_number() OVER (ORDER BY id) seq,
          id,
          start_time
        FROM
          table_version.revision
      ),
      revs_by_time AS (
        SELECT
          row_number() OVER (ORDER BY start_time) seq,
          id,
          start_time
        FROM
          table_version.revision
      )
      SELECT
          a.id,
          a.start_time atm,
          b.id as bi,
          b.start_time btm
        FROM revs_by_id a, revs_by_time b
       WHERE a.seq = b.seq
         AND a.id < b.id
         AND a.start_time > b.start_time
       ORDER by a.start_time

    LOOP

      -- Revision v_rec.id has to be moved to nextval('@extschema@.revision_id_seq');

      v_numdisordered := v_numdisordered + 1;

      -- Create new revision v_record

      INSERT INTO @extschema@.revision
        (id, revision_time, start_time, user_name, schema_change, comment)
      SELECT
        nextval('@extschema@.revision_id_seq'::regclass),
        revision_time, start_time, user_name, schema_change,
        comment
      FROM @extschema@.revision
        WHERE id = v_rec.id
      RETURNING id
      INTO v_newid;

      RAISE NOTICE 'Revision % has start_time %, while % has start_time %: renamed % to %',
        v_rec.id, v_rec.atm, v_rec.bi, v_rec.btm, v_rec.id, v_newid;

      -- Update @extschema@.tables_changed

      UPDATE @extschema@.tables_changed
        SET revision = v_newid
        WHERE revision = v_rec.id;
        
      -- Update all revisions of all revisioned tables


      FOR v_i IN 0..v_numversionedtables-1
      LOOP
        EXECUTE format('EXECUTE "p_uc%s"(%s, %s)', v_i, v_newid, v_rec.id);
        EXECUTE format('EXECUTE "p_ue%s"(%s, %s)', v_i, v_newid, v_rec.id);
      END LOOP;

      -- Delete now hopefully unreferenced old revision

      DELETE FROM @extschema@.revision WHERE id = v_rec.id;

    END LOOP;

    v_totdisordered := v_totdisordered + v_numdisordered;

    IF v_numdisordered = 0 THEN
      EXIT;
    END IF;

  END LOOP;

-- 4. Deallocate prepared update queries
  FOR i IN 0..v_numversionedtables-1
  LOOP
    EXECUTE format('DEALLOCATE "p_uc%s"', i);
    EXECUTE format('DEALLOCATE "p_ue%s"', i);
  END LOOP;

  RETURN v_totdisordered;

END;
$FIX$
LANGUAGE 'plpgsql' VOLATILE;
--}
