# -*- coding: utf-8 -*- # Copyright 2010 - 2017 RhodeCode GmbH and the AppEnlight project authors # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """initial tables Revision ID: 55b6e612672f Revises: None Create Date: 2014-10-13 23:47:38.295159 """ # revision identifiers, used by Alembic. revision = "55b6e612672f" down_revision = None from alembic import op import sqlalchemy as sa def upgrade(): op.add_column("users", sa.Column("first_name", sa.Unicode(25))) op.add_column("users", sa.Column("last_name", sa.Unicode(50))) op.add_column("users", sa.Column("company_name", sa.Unicode(255))) op.add_column("users", sa.Column("company_address", sa.Unicode(255))) op.add_column("users", sa.Column("phone1", sa.Unicode(25))) op.add_column("users", sa.Column("phone2", sa.Unicode(25))) op.add_column("users", sa.Column("zip_code", sa.Unicode(25))) op.add_column( "users", sa.Column( "default_report_sort", sa.Unicode(20), nullable=False, server_default="newest", ), ) op.add_column("users", sa.Column("city", sa.Unicode(128))) op.add_column("users", sa.Column("notes", sa.UnicodeText, server_default="")) op.add_column( "users", sa.Column("notifications", sa.Boolean(), nullable=False, server_default="true"), ) op.add_column( "users", sa.Column("registration_ip", sa.Unicode(40), nullable=False, server_default=""), ) op.create_table( "integrations", sa.Column("id", sa.Integer(), primary_key=True), sa.Column( "resource_id", sa.Integer(), sa.ForeignKey( "resources.resource_id", onupdate="cascade", ondelete="cascade" ), ), sa.Column("integration_name", sa.Unicode(64)), sa.Column("config", sa.dialects.postgresql.JSON, nullable=False), sa.Column( "modified_date", sa.DateTime(), nullable=False, server_default=sa.func.now() ), sa.Column("external_id", sa.Unicode(255)), sa.Column("external_id2", sa.Unicode(255)), ) op.create_table( "alert_channels", sa.Column( "owner_id", sa.Integer(), sa.ForeignKey("users.id", onupdate="cascade", ondelete="cascade"), nullable=False, ), sa.Column("channel_name", sa.Unicode(25), nullable=False), sa.Column("channel_value", sa.Unicode(80), nullable=False), sa.Column("channel_json_conf", sa.dialects.postgresql.JSON, nullable=False), sa.Column( "channel_validated", sa.Boolean, nullable=False, server_default="False" ), sa.Column("send_alerts", sa.Boolean, nullable=False, server_default="True"), sa.Column( "notify_only_first", sa.Boolean, nullable=False, server_default="False" ), sa.Column("daily_digest", sa.Boolean, nullable=False, server_default="True"), sa.Column("pkey", sa.Integer(), primary_key=True), sa.Column( "integration_id", sa.Integer, sa.ForeignKey("integrations.id", onupdate="cascade", ondelete="cascade"), ), ) op.create_unique_constraint( "uq_alert_channels", "alert_channels", ["owner_id", "channel_name", "channel_value"], ) op.create_table( "alert_channels_actions", sa.Column("owner_id", sa.Integer(), nullable=False), sa.Column( "resource_id", sa.Integer(), sa.ForeignKey( "resources.resource_id", onupdate="cascade", ondelete="cascade" ), ), sa.Column("pkey", sa.Integer(), primary_key=True), sa.Column("action", sa.Unicode(10), nullable=False, server_default="always"), sa.Column("rule", sa.dialects.postgresql.JSON), sa.Column("type", sa.Unicode(10), index=True), sa.Column("other_id", sa.Unicode(40), index=True), sa.Column("config", sa.dialects.postgresql.JSON), sa.Column("name", sa.Unicode(255), server_default=""), ) op.create_table( "application_postprocess_conf", sa.Column("pkey", sa.Integer(), primary_key=True), sa.Column("do", sa.Unicode(25), nullable=False), sa.Column("new_value", sa.UnicodeText(), nullable=False, server_default=""), sa.Column( "resource_id", sa.Integer(), sa.ForeignKey( "resources.resource_id", onupdate="cascade", ondelete="cascade" ), nullable=False, ), sa.Column("rule", sa.dialects.postgresql.JSON), ) op.create_table( "applications", sa.Column( "resource_id", sa.Integer(), sa.ForeignKey( "resources.resource_id", onupdate="cascade", ondelete="cascade" ), nullable=False, primary_key=True, autoincrement=False, ), sa.Column("domains", sa.UnicodeText, nullable=False), sa.Column("api_key", sa.Unicode(32), nullable=False, index=True), sa.Column( "default_grouping", sa.Unicode(20), nullable=False, server_default="url_type", ), sa.Column("public_key", sa.Unicode(32), nullable=False, index=True), sa.Column( "error_report_threshold", sa.Integer(), server_default="10", nullable=False ), sa.Column( "slow_report_threshold", sa.Integer(), server_default="10", nullable=False ), sa.Column("apdex_threshold", sa.Float(), server_default="0.7", nullable=False), sa.Column( "allow_permanent_storage", sa.Boolean(), server_default="false", nullable=False, ), ) op.create_unique_constraint(None, "applications", ["public_key"]) op.create_unique_constraint(None, "applications", ["api_key"]) op.create_table( "metrics", sa.Column("pkey", sa.types.BigInteger, nullable=False, primary_key=True), sa.Column( "resource_id", sa.Integer(), sa.ForeignKey( "resources.resource_id", onupdate="cascade", ondelete="cascade" ), ), sa.Column("timestamp", sa.DateTime), sa.Column("namespace", sa.Unicode(255)), sa.Column("tags", sa.dialects.postgresql.JSON, server_default="{}"), ) op.create_table( "events", sa.Column("id", sa.Integer, nullable=False, primary_key=True), sa.Column("start_date", sa.DateTime, nullable=False, index=True), sa.Column("end_date", sa.DateTime), sa.Column("status", sa.Integer(), nullable=False, index=True), sa.Column("event_type", sa.Integer(), nullable=False, index=True), sa.Column("origin_user_id", sa.Integer()), sa.Column("target_user_id", sa.Integer()), sa.Column("resource_id", sa.Integer(), index=True), sa.Column("text", sa.UnicodeText, server_default=""), sa.Column("values", sa.dialects.postgresql.JSON), sa.Column("target_id", sa.Integer()), sa.Column("target_uuid", sa.Unicode(40), index=True), ) op.create_table( "logs", sa.Column("log_id", sa.types.BigInteger, nullable=False, primary_key=True), sa.Column( "resource_id", sa.Integer(), sa.ForeignKey( "resources.resource_id", onupdate="cascade", ondelete="cascade" ), ), sa.Column("log_level", sa.SmallInteger(), nullable=False), sa.Column("primary_key", sa.Unicode(128), nullable=True), sa.Column("message", sa.UnicodeText, nullable=False, server_default=""), sa.Column("timestamp", sa.DateTime), sa.Column("namespace", sa.Unicode(255)), sa.Column("request_id", sa.Unicode(40)), sa.Column("tags", sa.dialects.postgresql.JSON, server_default="{}"), sa.Column("permanent", sa.Boolean(), server_default="false", nullable=False), ) op.create_table( "reports_groups", sa.Column("id", sa.types.BigInteger, primary_key=True), sa.Column( "resource_id", sa.Integer, sa.ForeignKey( "resources.resource_id", onupdate="cascade", ondelete="cascade" ), nullable=False, ), sa.Column("priority", sa.Integer, nullable=False, server_default="5"), sa.Column( "first_timestamp", sa.DateTime(), nullable=False, server_default=sa.func.now(), ), sa.Column("last_timestamp", sa.DateTime()), sa.Column("error", sa.UnicodeText, nullable=False, server_default=""), sa.Column("grouping_hash", sa.Unicode(40), nullable=False, server_default=""), sa.Column( "triggered_postprocesses_ids", sa.dialects.postgresql.JSON, nullable=False, server_default="[]", ), sa.Column("report_type", sa.Integer, nullable=False, server_default="0"), sa.Column("total_reports", sa.Integer, nullable=False, server_default="0"), sa.Column("last_report", sa.Integer, nullable=False, server_default="0"), sa.Column("occurences", sa.Integer, nullable=False, server_default="1"), sa.Column("average_duration", sa.Float(), nullable=False, server_default="0"), sa.Column("summed_duration", sa.Float(), nullable=False, server_default="0"), sa.Column("notified", sa.Boolean, nullable=False, server_default="False"), sa.Column("fixed", sa.Boolean, nullable=False, server_default="False"), sa.Column("public", sa.Boolean, nullable=False, server_default="False"), sa.Column("read", sa.Boolean, nullable=False, server_default="False"), ) op.create_table( "reports", sa.Column("id", sa.types.BigInteger, primary_key=True), sa.Column( "group_id", sa.types.BigInteger, sa.ForeignKey("reports_groups.id", onupdate="cascade", ondelete="cascade"), nullable=False, index=True, ), sa.Column("resource_id", sa.Integer, nullable=False, index=True), sa.Column("report_type", sa.Integer, nullable=False, server_default="0"), sa.Column("error", sa.UnicodeText, nullable=False, server_default=""), sa.Column( "extra", sa.dialects.postgresql.JSON, nullable=False, server_default="{}" ), sa.Column( "request", sa.dialects.postgresql.JSON, nullable=False, server_default="{}" ), sa.Column( "tags", sa.dialects.postgresql.JSON, nullable=False, server_default="{}" ), sa.Column("ip", sa.Unicode(39), nullable=False, server_default=""), sa.Column("username", sa.Unicode(255), nullable=False, server_default=""), sa.Column("user_agent", sa.Unicode(512), nullable=False, server_default=""), sa.Column("url", sa.UnicodeText, nullable=False, server_default=""), sa.Column("request_id", sa.Unicode(40), nullable=False, server_default=""), sa.Column( "request_stats", sa.dialects.postgresql.JSON, nullable=False, server_default="{}", ), sa.Column( "traceback", sa.dialects.postgresql.JSON, nullable=False, server_default="{}", ), sa.Column("traceback_hash", sa.Unicode(40), nullable=False, server_default=""), sa.Column( "start_time", sa.DateTime(), nullable=False, server_default=sa.func.now() ), sa.Column("end_time", sa.DateTime()), sa.Column( "report_group_time", sa.DateTime, index=True, nullable=False, server_default=sa.func.now(), ), sa.Column("duration", sa.Float(), nullable=False, server_default="0"), sa.Column("http_status", sa.Integer, index=True), sa.Column("url_domain", sa.Unicode(128)), sa.Column("url_path", sa.UnicodeText), sa.Column("language", sa.Integer, server_default="0"), ) op.create_index(None, "reports", [sa.text("(tags ->> 'server_name')")]) op.create_index(None, "reports", [sa.text("(tags ->> 'view_name')")]) op.create_table( "reports_assignments", sa.Column("group_id", sa.types.BigInteger, nullable=False, primary_key=True), sa.Column( "owner_id", sa.Integer, sa.ForeignKey("users.id", onupdate="cascade", ondelete="cascade"), nullable=False, primary_key=True, ), sa.Column("report_time", sa.DateTime, nullable=False), ) op.create_table( "reports_comments", sa.Column("comment_id", sa.Integer, primary_key=True), sa.Column("body", sa.UnicodeText, nullable=False, server_default=""), sa.Column( "owner_id", sa.Integer, sa.ForeignKey("users.id", onupdate="cascade", ondelete="set null"), nullable=True, ), sa.Column( "created_timestamp", sa.DateTime, nullable=False, server_default=sa.func.now(), ), sa.Column("report_time", sa.DateTime, nullable=False), sa.Column("group_id", sa.types.BigInteger, nullable=False), ) op.create_table( "reports_stats", sa.Column("resource_id", sa.Integer, nullable=False, index=True), sa.Column("start_interval", sa.DateTime, nullable=False, index=True), sa.Column("group_id", sa.types.BigInteger, index=True), sa.Column( "occurences", sa.Integer, nullable=False, server_default="0", index=True ), sa.Column("owner_user_id", sa.Integer), sa.Column("type", sa.Integer, index=True, nullable=False), sa.Column("duration", sa.Float(), server_default="0"), sa.Column("server_name", sa.Unicode(128), server_default=""), sa.Column("view_name", sa.Unicode(128), server_default=""), sa.Column("id", sa.BigInteger(), nullable=False, primary_key=True), ) op.create_index( "ix_reports_stats_start_interval_group_id", "reports_stats", ["start_interval", "group_id"], ) op.create_table( "slow_calls", sa.Column("id", sa.types.BigInteger, primary_key=True), sa.Column( "report_id", sa.types.BigInteger, sa.ForeignKey("reports.id", onupdate="cascade", ondelete="cascade"), nullable=False, index=True, ), sa.Column( "duration", sa.Float(), nullable=False, server_default="0", index=True ), sa.Column( "timestamp", sa.DateTime, nullable=False, server_default=sa.func.now(), index=True, ), sa.Column( "report_group_time", sa.DateTime, index=True, nullable=False, server_default=sa.func.now(), ), sa.Column("type", sa.Unicode(16), nullable=False, index=True), sa.Column("statement", sa.UnicodeText, nullable=False, server_default=""), sa.Column("parameters", sa.dialects.postgresql.JSON, nullable=False), sa.Column("location", sa.UnicodeText, server_default=""), sa.Column("subtype", sa.Unicode(16), nullable=False, index=True), sa.Column("resource_id", sa.Integer, nullable=False, index=True), sa.Column("statement_hash", sa.Unicode(60), index=True), ) op.create_table( "tags", sa.Column("id", sa.types.BigInteger, primary_key=True), sa.Column( "resource_id", sa.Integer, sa.ForeignKey( "resources.resource_id", onupdate="cascade", ondelete="cascade" ), ), sa.Column( "first_timestamp", sa.DateTime, nullable=False, server_default=sa.func.now() ), sa.Column( "last_timestamp", sa.DateTime, nullable=False, server_default=sa.func.now() ), sa.Column("name", sa.Unicode(32), nullable=False), sa.Column("value", sa.dialects.postgresql.JSON, nullable=False), sa.Column("times_seen", sa.Integer, nullable=False, server_default="1"), ) op.create_table( "auth_tokens", sa.Column("id", sa.Integer, nullable=False, primary_key=True), sa.Column("token", sa.Unicode), sa.Column( "creation_date", sa.DateTime, nullable=False, server_default=sa.func.now() ), sa.Column("expires", sa.DateTime), sa.Column( "owner_id", sa.Integer, sa.ForeignKey("users.id", onupdate="cascade", ondelete="cascade"), ), sa.Column("description", sa.Unicode), ) op.create_table( "channels_actions", sa.Column( "channel_pkey", sa.Integer, sa.ForeignKey( "alert_channels.pkey", ondelete="CASCADE", onupdate="CASCADE" ), ), sa.Column( "action_pkey", sa.Integer, sa.ForeignKey( "alert_channels_actions.pkey", ondelete="CASCADE", onupdate="CASCADE" ), ), ) op.create_table( "config", sa.Column("key", sa.Unicode(128), primary_key=True), sa.Column("section", sa.Unicode(128), primary_key=True), sa.Column("value", sa.dialects.postgresql.JSON, server_default="{}"), ) op.create_table( "plugin_configs", sa.Column("id", sa.Integer, primary_key=True), sa.Column("plugin_name", sa.Unicode(128)), sa.Column("section", sa.Unicode(128)), sa.Column("config", sa.dialects.postgresql.JSON, server_default="{}"), sa.Column( "resource_id", sa.Integer(), sa.ForeignKey( "resources.resource_id", onupdate="cascade", ondelete="cascade" ), ), sa.Column( "owner_id", sa.Integer(), sa.ForeignKey("users.id", onupdate="cascade", ondelete="cascade"), ), ) op.create_table( "rc_versions", sa.Column("name", sa.Unicode(40), primary_key=True), sa.Column("value", sa.Unicode(40)), ) version_table = sa.table( "rc_versions", sa.Column("name", sa.Unicode(40)), sa.Column("value", sa.Unicode(40)), ) insert = version_table.insert().values(name="es_reports") op.execute(insert) insert = version_table.insert().values(name="es_reports_groups") op.execute(insert) insert = version_table.insert().values(name="es_reports_stats") op.execute(insert) insert = version_table.insert().values(name="es_logs") op.execute(insert) insert = version_table.insert().values(name="es_metrics") op.execute(insert) insert = version_table.insert().values(name="es_slow_calls") op.execute(insert) op.execute( """ CREATE OR REPLACE FUNCTION floor_time_5min(timestamp without time zone) RETURNS timestamp without time zone AS $BODY$SELECT date_trunc('hour', $1) + INTERVAL '5 min' * FLOOR(date_part('minute', $1) / 5.0)$BODY$ LANGUAGE sql VOLATILE; """ ) op.execute( """ CREATE OR REPLACE FUNCTION partition_logs() RETURNS trigger LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE main_table varchar := 'logs'; partitioned_table varchar := ''; BEGIN IF NEW.permanent THEN partitioned_table := main_table || '_p_' || date_part('year', NEW.timestamp)::TEXT || '_' || DATE_part('month', NEW.timestamp); ELSE partitioned_table := main_table || '_p_' || date_part('year', NEW.timestamp)::TEXT || '_' || DATE_part('month', NEW.timestamp) || '_' || DATE_part('day', NEW.timestamp); END IF; BEGIN EXECUTE 'INSERT INTO ' || partitioned_table || ' SELECT(' || TG_TABLE_NAME || ' ' || quote_literal(NEW) || ').*;'; EXCEPTION WHEN undefined_table THEN RAISE NOTICE 'A partition has been created %', partitioned_table; IF NEW.permanent THEN EXECUTE format('CREATE TABLE IF NOT EXISTS %s ( CHECK( timestamp >= DATE %s AND timestamp < DATE %s)) INHERITS (%s)', partitioned_table, quote_literal(date_trunc('month', NEW.timestamp)::date) , quote_literal((date_trunc('month', NEW.timestamp)::date + interval '1 month')::text), main_table); EXECUTE format('ALTER TABLE %s ADD CONSTRAINT pk_%s PRIMARY KEY(log_id);', partitioned_table, partitioned_table); EXECUTE format('ALTER TABLE %s ADD CONSTRAINT fk_%s_resource_id FOREIGN KEY (resource_id) REFERENCES resources (resource_id) MATCH SIMPLE ON UPDATE CASCADE ON DELETE CASCADE;', partitioned_table, partitioned_table); EXECUTE format('CREATE INDEX ix_%s_timestamp ON %s (timestamp);', partitioned_table, partitioned_table); EXECUTE format('CREATE INDEX ix_%s_namespace_resource_id ON %s (namespace, resource_id);', partitioned_table, partitioned_table); EXECUTE format('CREATE INDEX ix_%s_resource_id ON %s (resource_id);', partitioned_table, partitioned_table); EXECUTE format('CREATE INDEX ix_%s_pkey_namespace ON %s (primary_key, namespace);', partitioned_table, partitioned_table); ELSE EXECUTE format('CREATE TABLE IF NOT EXISTS %s ( CHECK( timestamp >= DATE %s AND timestamp < DATE %s)) INHERITS (%s)', partitioned_table, quote_literal(date_trunc('day', NEW.timestamp)::date) , quote_literal((date_trunc('day', NEW.timestamp)::date + interval '1 day')::text), main_table); EXECUTE format('ALTER TABLE %s ADD CONSTRAINT pk_%s_ PRIMARY KEY(log_id);', partitioned_table, partitioned_table); EXECUTE format('ALTER TABLE %s ADD CONSTRAINT fk_%s_resource_id FOREIGN KEY (resource_id) REFERENCES resources (resource_id) MATCH SIMPLE ON UPDATE CASCADE ON DELETE CASCADE;', partitioned_table, partitioned_table); EXECUTE format('CREATE INDEX ix_%s_timestamp ON %s (timestamp);', partitioned_table, partitioned_table); EXECUTE format('CREATE INDEX ix_%s_namespace_resource_id ON %s (namespace, resource_id);', partitioned_table, partitioned_table); EXECUTE format('CREATE INDEX ix_%s_resource_id ON %s (resource_id);', partitioned_table, partitioned_table); EXECUTE format('CREATE INDEX ix_%s_primary_key_namespace ON %s (primary_key,namespace);', partitioned_table, partitioned_table); END IF; EXECUTE 'INSERT INTO ' || partitioned_table || ' SELECT(' || TG_TABLE_NAME || ' ' || quote_literal(NEW) || ').*;'; END; RETURN NULL; END $$; """ ) op.execute( """ CREATE TRIGGER partition_logs BEFORE INSERT ON logs FOR EACH ROW EXECUTE PROCEDURE partition_logs(); """ ) op.execute( """ CREATE OR REPLACE FUNCTION partition_metrics() RETURNS trigger LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE main_table varchar := 'metrics'; partitioned_table varchar := ''; BEGIN partitioned_table := main_table || '_p_' || date_part('year', NEW.timestamp)::TEXT || '_' || DATE_part('month', NEW.timestamp) || '_' || DATE_part('day', NEW.timestamp); BEGIN EXECUTE 'INSERT INTO ' || partitioned_table || ' SELECT(' || TG_TABLE_NAME || ' ' || quote_literal(NEW) || ').*;'; EXCEPTION WHEN undefined_table THEN RAISE NOTICE 'A partition has been created %', partitioned_table; EXECUTE format('CREATE TABLE IF NOT EXISTS %s ( CHECK( timestamp >= DATE %s AND timestamp < DATE %s)) INHERITS (%s)', partitioned_table, quote_literal(date_trunc('day', NEW.timestamp)::date) , quote_literal((date_trunc('day', NEW.timestamp)::date + interval '1 day')::text), main_table); EXECUTE format('ALTER TABLE %s ADD CONSTRAINT pk_%s PRIMARY KEY(pkey);', partitioned_table, partitioned_table); EXECUTE format('ALTER TABLE %s ADD CONSTRAINT fk_%s_resource_id FOREIGN KEY (resource_id) REFERENCES resources (resource_id) MATCH SIMPLE ON UPDATE CASCADE ON DELETE CASCADE;', partitioned_table, partitioned_table); EXECUTE format('CREATE INDEX ix_%s_timestamp ON %s (timestamp);', partitioned_table, partitioned_table); EXECUTE format('CREATE INDEX ix_%s_resource_id ON %s (resource_id);', partitioned_table, partitioned_table); EXECUTE 'INSERT INTO ' || partitioned_table || ' SELECT(' || TG_TABLE_NAME || ' ' || quote_literal(NEW) || ').*;'; END; RETURN NULL; END $$; """ ) op.execute( """ CREATE TRIGGER partition_metrics BEFORE INSERT ON metrics FOR EACH ROW EXECUTE PROCEDURE partition_metrics(); """ ) op.execute( """ CREATE FUNCTION partition_reports_stats() RETURNS trigger LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE main_table varchar := 'reports_stats'; partitioned_table varchar := ''; BEGIN partitioned_table := main_table || '_p_' || date_part('year', NEW.start_interval)::TEXT || '_' || DATE_part('month', NEW.start_interval); BEGIN EXECUTE 'INSERT INTO ' || partitioned_table || ' SELECT(' || TG_TABLE_NAME || ' ' || quote_literal(NEW) || ').*;'; EXCEPTION WHEN undefined_table THEN RAISE NOTICE 'A partition has been created %', partitioned_table; EXECUTE format('CREATE TABLE IF NOT EXISTS %s ( CHECK( start_interval >= DATE %s AND start_interval < DATE %s )) INHERITS (%s)', partitioned_table, quote_literal(date_trunc('month', NEW.start_interval)::date) , quote_literal((date_trunc('month', NEW.start_interval)::date + interval '1 month')::text), main_table); EXECUTE format('ALTER TABLE %s ADD CONSTRAINT pk_%s PRIMARY KEY(id);', partitioned_table, partitioned_table); EXECUTE format('CREATE INDEX ix_%s_start_interval ON %s USING btree (start_interval);', partitioned_table, partitioned_table); EXECUTE format('CREATE INDEX ix_%s_type ON %s USING btree (type);', partitioned_table, partitioned_table); EXECUTE format('CREATE INDEX ix_%s_resource_id ON %s USING btree (resource_id);', partitioned_table, partitioned_table); EXECUTE 'INSERT INTO ' || partitioned_table || ' SELECT(' || TG_TABLE_NAME || ' ' || quote_literal(NEW) || ').*;'; END; RETURN NULL; END $$; """ ) op.execute( """ CREATE TRIGGER partition_reports_stats BEFORE INSERT ON reports_stats FOR EACH ROW EXECUTE PROCEDURE partition_reports_stats(); """ ) op.execute( """ CREATE OR REPLACE FUNCTION partition_reports_groups() RETURNS trigger LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE main_table varchar := 'reports_groups'; partitioned_table varchar := ''; BEGIN partitioned_table := main_table || '_p_' || date_part('year', NEW.first_timestamp)::TEXT || '_' || DATE_part('month', NEW.first_timestamp); BEGIN EXECUTE 'INSERT INTO ' || partitioned_table || ' SELECT(' || TG_TABLE_NAME || ' ' || quote_literal(NEW) || ').*;'; EXCEPTION WHEN undefined_table THEN RAISE NOTICE 'A partition has been created %', partitioned_table; EXECUTE format('CREATE TABLE IF NOT EXISTS %s ( CHECK( first_timestamp >= DATE %s AND first_timestamp < DATE %s )) INHERITS (%s)', partitioned_table, quote_literal(date_trunc('month', NEW.first_timestamp)::date) , quote_literal((date_trunc('month', NEW.first_timestamp)::date + interval '1 month')::text), main_table); EXECUTE format('ALTER TABLE %s ADD CONSTRAINT pk_%s PRIMARY KEY(id);', partitioned_table, partitioned_table); EXECUTE format('ALTER TABLE %s ADD CONSTRAINT fk_%s_resource_id FOREIGN KEY (resource_id) REFERENCES resources (resource_id) MATCH SIMPLE ON UPDATE CASCADE ON DELETE CASCADE;', partitioned_table, partitioned_table); EXECUTE 'INSERT INTO ' || partitioned_table || ' SELECT(' || TG_TABLE_NAME || ' ' || quote_literal(NEW) || ').*;'; END; RETURN NULL; END $$; """ ) op.execute( """ CREATE TRIGGER partition_reports_groups BEFORE INSERT ON reports_groups FOR EACH ROW EXECUTE PROCEDURE partition_reports_groups(); """ ) op.execute( """ CREATE OR REPLACE FUNCTION partition_reports() RETURNS trigger LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE main_table varchar := 'reports'; partitioned_table varchar := ''; partitioned_parent_table varchar := ''; BEGIN partitioned_table := main_table || '_p_' || date_part('year', NEW.report_group_time)::TEXT || '_' || DATE_part('month', NEW.report_group_time); partitioned_parent_table := 'reports_groups_p_' || date_part('year', NEW.report_group_time)::TEXT || '_' || DATE_part('month', NEW.report_group_time); BEGIN EXECUTE 'INSERT INTO ' || partitioned_table || ' SELECT(' || TG_TABLE_NAME || ' ' || quote_literal(NEW) || ').*;'; EXCEPTION WHEN undefined_table THEN RAISE NOTICE 'A partition has been created %', partitioned_table; EXECUTE format('CREATE TABLE IF NOT EXISTS %s ( CHECK( report_group_time >= DATE %s AND report_group_time < DATE %s )) INHERITS (%s)', partitioned_table, quote_literal(date_trunc('month', NEW.report_group_time)::date) , quote_literal((date_trunc('month', NEW.report_group_time)::date + interval '1 month')::text), main_table); EXECUTE format('ALTER TABLE %s ADD CONSTRAINT pk_%s PRIMARY KEY(id);', partitioned_table, partitioned_table); EXECUTE format('ALTER TABLE %s ADD CONSTRAINT fk_%s_resource_id FOREIGN KEY (resource_id) REFERENCES resources (resource_id) MATCH SIMPLE ON UPDATE CASCADE ON DELETE CASCADE;', partitioned_table, partitioned_table); EXECUTE format('ALTER TABLE %s ADD CONSTRAINT fk_%s_group_id FOREIGN KEY (group_id) REFERENCES %s (id) MATCH SIMPLE ON UPDATE CASCADE ON DELETE CASCADE;', partitioned_table, partitioned_table, partitioned_parent_table); EXECUTE format('CREATE INDEX ix_%s_report_group_time ON %s USING btree (report_group_time);', partitioned_table, partitioned_table); EXECUTE format('CREATE INDEX ix_%s_group_id ON %s USING btree (group_id);', partitioned_table, partitioned_table); EXECUTE format('CREATE INDEX ix_%s_resource_id ON %s USING btree (resource_id);', partitioned_table, partitioned_table); EXECUTE 'INSERT INTO ' || partitioned_table || ' SELECT(' || TG_TABLE_NAME || ' ' || quote_literal(NEW) || ').*;'; END; RETURN NULL; END $$; """ ) op.execute( """ CREATE TRIGGER partition_reports BEFORE INSERT ON reports FOR EACH ROW EXECUTE PROCEDURE partition_reports(); """ ) op.execute( """ CREATE OR REPLACE FUNCTION partition_slow_calls() RETURNS trigger LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE main_table varchar := 'slow_calls'; partitioned_table varchar := ''; partitioned_parent_table varchar := ''; BEGIN partitioned_table := main_table || '_p_' || date_part('year', NEW.report_group_time)::TEXT || '_' || DATE_part('month', NEW.report_group_time); partitioned_parent_table := 'reports_p_' || date_part('year', NEW.report_group_time)::TEXT || '_' || DATE_part('month', NEW.report_group_time); BEGIN EXECUTE 'INSERT INTO ' || partitioned_table || ' SELECT(' || TG_TABLE_NAME || ' ' || quote_literal(NEW) || ').*;'; EXCEPTION WHEN undefined_table THEN RAISE NOTICE 'A partition has been created %', partitioned_table; EXECUTE format('CREATE TABLE IF NOT EXISTS %s ( CHECK( report_group_time >= DATE %s AND report_group_time < DATE %s )) INHERITS (%s)', partitioned_table, quote_literal(date_trunc('month', NEW.report_group_time)::date) , quote_literal((date_trunc('month', NEW.report_group_time)::date + interval '1 month')::text), main_table); EXECUTE format('ALTER TABLE %s ADD CONSTRAINT pk_%s PRIMARY KEY(id);', partitioned_table, partitioned_table); EXECUTE format('ALTER TABLE %s ADD CONSTRAINT fk_%s_resource_id FOREIGN KEY (resource_id) REFERENCES resources (resource_id) MATCH SIMPLE ON UPDATE CASCADE ON DELETE CASCADE;', partitioned_table, partitioned_table); EXECUTE format('ALTER TABLE %s ADD CONSTRAINT fk_%s_report_id FOREIGN KEY (report_id) REFERENCES %s (id) MATCH SIMPLE ON UPDATE CASCADE ON DELETE CASCADE;', partitioned_table, partitioned_table, partitioned_parent_table); EXECUTE format('CREATE INDEX ix_%s_resource_id ON %s USING btree (resource_id);', partitioned_table, partitioned_table); EXECUTE format('CREATE INDEX ix_%s_report_id ON %s USING btree (report_id);', partitioned_table, partitioned_table); EXECUTE format('CREATE INDEX ix_%s_timestamp ON %s USING btree (timestamp);', partitioned_table, partitioned_table); EXECUTE 'INSERT INTO ' || partitioned_table || ' SELECT(' || TG_TABLE_NAME || ' ' || quote_literal(NEW) || ').*;'; END; RETURN NULL; END $$; """ ) op.execute( """ CREATE TRIGGER partition_slow_calls BEFORE INSERT ON slow_calls FOR EACH ROW EXECUTE PROCEDURE partition_slow_calls(); """ ) def downgrade(): pass