#!/usr/bin/env python
# coding=utf8

"""
Add component to association tables, allowing a package to exist in more than one component

@contact: Debian FTP Master <ftpmaster@debian.org>
@copyright: 2013 Varnish Software AS
@author: Tollef Fog Heen <tfheen@varnish-software.com>
@license: GNU General Public License version 2 or later
"""

# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.

# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

################################################################################

import psycopg2
from daklib.dak_exceptions import DBUpdateError
from daklib.config import Config

################################################################################
def do_update(self):
    print __doc__
    try:
        cnf = Config()

        c = self.db.cursor()

        c.execute("""CREATE OR REPLACE FUNCTION trigger_binsrc_assoc_update() RETURNS TRIGGER AS $$
DECLARE
  v_data RECORD;

  v_package audit.package_changes.package%TYPE;
  v_version audit.package_changes.version%TYPE;
  v_architecture audit.package_changes.architecture%TYPE;
  v_suite audit.package_changes.suite%TYPE;
  v_event audit.package_changes.event%TYPE;
  v_priority audit.package_changes.priority%TYPE;
  v_component audit.package_changes.component%TYPE;
  v_section audit.package_changes.section%TYPE;
BEGIN
  CASE TG_OP
    WHEN 'INSERT' THEN v_event := 'I'; v_data := NEW;
    WHEN 'DELETE' THEN v_event := 'D'; v_data := OLD;
    ELSE RAISE EXCEPTION 'trigger called for invalid operation (%)', TG_OP;
  END CASE;

  SELECT suite_name INTO STRICT v_suite FROM suite WHERE id = v_data.suite;
  SELECT name INTO STRICT v_component FROM component WHERE id = v_data.component;

  CASE TG_TABLE_NAME
    WHEN 'bin_associations' THEN
      SELECT package, version, arch_string
        INTO STRICT v_package, v_version, v_architecture
        FROM binaries LEFT JOIN architecture ON (architecture.id = binaries.architecture)
        WHERE binaries.id = v_data.bin;

      SELECT priority.priority, section.section
        INTO v_priority, v_section
        FROM override
             JOIN override_type ON (override.type = override_type.id)
             JOIN priority ON (priority.id = override.priority)
             JOIN section ON (section.id = override.section)
             JOIN suite ON (suite.id = override.suite)
        WHERE override_type.type != 'dsc'
              AND override.package = v_package AND suite.id = v_data.suite;

    WHEN 'src_associations' THEN
      SELECT source, version
        INTO STRICT v_package, v_version
        FROM source WHERE source.id = v_data.source;
      v_architecture := 'source';

      SELECT priority.priority, section.section
        INTO v_priority, v_section
        FROM override
             JOIN override_type ON (override.type = override_type.id)
             JOIN priority ON (priority.id = override.priority)
             JOIN section ON (section.id = override.section)
             JOIN suite ON (suite.id = override.suite)
        WHERE override_type.type = 'dsc'
              AND override.package = v_package AND suite.id = v_data.suite;

    ELSE RAISE EXCEPTION 'trigger called for invalid table (%)', TG_TABLE_NAME;
  END CASE;

  INSERT INTO audit.package_changes
    (package, version, architecture, suite, event, priority, component, section)
    VALUES (v_package, v_version, v_architecture, v_suite, v_event, v_priority, v_component, v_section);

  RETURN NEW;
END;
$$ LANGUAGE plpgsql VOLATILE SECURITY DEFINER
SET search_path = public, pg_temp""")


        c.execute("""ALTER TABLE src_associations DISABLE TRIGGER trigger_src_associations_audit""")
        c.execute("""ALTER TABLE src_associations DISABLE TRIGGER modified_src_associations""")
        c.execute("""ALTER TABLE src_associations DROP CONSTRAINT src_associations_suite_key""")

        c.execute("""ALTER TABLE src_associations ADD COLUMN component_id int REFERENCES component(id)""")

        c.execute("""
          INSERT INTO src_associations(suite, source, component_id, created, modified)
            SELECT sa.source, sa.suite, fam.component_id, sa.created, sa.modified FROM src_associations sa 
              JOIN source ON (sa.source = source.id)
              JOIN files_archive_map fam ON (source.file = fam.file_id)
        """)

        c.execute("""DELETE FROM src_associations WHERE component IS NULL""")
        c.execute("""ALTER TABLE src_associations ALTER COLUMN component SET NOT NULL""")
        c.execute("""ALTER TABLE src_associations ENABLE TRIGGER modified_src_associations""")
        c.execute("""ALTER TABLE src_associations ENABLE TRIGGER trigger_src_associations_audit""")

        c.execute("""ALTER TABLE bin_associations ADD COLUMN component_id int REFERENCES component(id)""")

        # Put existing sources into the right components
        c.execute("""ALTER TABLE bin_associations DISABLE TRIGGER trigger_bin_associations_audit""")
        c.execute("""ALTER TABLE bin_associations DISABLE TRIGGER modified_bin_associations""")
        c.execute("""
          INSERT INTO bin_associations(suite, bin, component_id, created, modified)
            SELECT suite, bin, fam.component_id, created, modified FROM bin_associations ba
              JOIN binaries ON (ba.bin = binaries.id)
              JOIN files_archive_map fam ON (binaries.file = fam.file_id)
        """)

        c.execute("""DELETE FROM bin_associations WHERE component IS NULL""")
        c.execute("""ALTER TABLE bin_associations ALTER COLUMN component SET NOT NULL""")
        c.execute("""ALTER TABLE bin_associations ENABLE TRIGGER modified_bin_associations""")
        c.execute("""ALTER TABLE bin_associations ENABLE TRIGGER trigger_bin_associations_audit""")

        self.db.commit()

    except psycopg2.ProgrammingError as msg:
        self.db.rollback()
        raise DBUpdateError('Unable to apply sick update 101, rollback issued. Error message: {0}'.format(msg))
