lib/pgslice.rb in pgslice-0.1.7 vs lib/pgslice.rb in pgslice-0.2.0
- old
+ new
@@ -77,21 +77,24 @@
sql_format = SQL_FORMAT[period.to_sym]
queries << <<-SQL
CREATE FUNCTION #{trigger_name}()
RETURNS trigger AS $$
BEGIN
- EXECUTE 'INSERT INTO #{table}_' || to_char(NEW.#{column}, '#{sql_format}') || ' VALUES ($1.*)' USING NEW;
- RETURN NULL;
+ RAISE EXCEPTION 'Date out of range. Create partitions first.';
END;
$$ LANGUAGE plpgsql;
SQL
queries << <<-SQL
CREATE TRIGGER #{trigger_name}
BEFORE INSERT ON #{intermediate_table}
FOR EACH ROW EXECUTE PROCEDURE #{trigger_name}();
SQL
+
+ queries << <<-SQL
+COMMENT ON TRIGGER #{trigger_name} ON #{intermediate_table} is 'column:#{column},period:#{period}';
+SQL
end
run_queries(queries)
end
@@ -128,18 +131,29 @@
index_defs = execute("select pg_get_indexdef(indexrelid) from pg_index where indrelid = $1::regclass AND indisprimary = 'f'", [original_table]).map { |r| r["pg_get_indexdef"] }
primary_key = self.primary_key(table)
queries = []
- period, field = settings_from_table(original_table, table)
+ comment = execute("SELECT obj_description(oid, 'pg_trigger') AS comment FROM pg_trigger WHERE tgname = $1 AND tgrelid = $2::regclass", [trigger_name, table]).first
+ if comment
+ field, period = comment["comment"].split(",").map { |v| v.split(":").last } rescue [nil, nil]
+ end
+
+ unless period
+ period, field = settings_from_table(original_table, table)
+ queries << "COMMENT ON TRIGGER #{trigger_name} ON #{table} is 'column:#{field},period:#{period}';"
+ end
abort "Could not read settings" unless period
- today = round_date(Date.today, period)
+ # today = utc date
+ today = round_date(DateTime.now.new_offset(0).to_date, period)
+ added_partitions = []
range.each do |n|
day = advance_date(today, period, n)
partition_name = "#{original_table}_#{day.strftime(name_format(period))}"
next if table_exists?(partition_name)
+ added_partitions << partition_name
queries << <<-SQL
CREATE TABLE #{partition_name}
(CHECK (#{field} >= #{sql_date(day)} AND #{field} < #{sql_date(advance_date(day, period, 1))}))
INHERITS (#{table});
@@ -148,9 +162,51 @@
queries << "ALTER TABLE #{partition_name} ADD PRIMARY KEY (#{primary_key});" if primary_key
index_defs.each do |index_def|
queries << index_def.sub(" ON #{original_table} USING ", " ON #{partition_name} USING ").sub(/ INDEX .+ ON /, " INDEX ON ") + ";"
end
+ end
+
+ # update trigger based on existing partitions
+ current_defs = []
+ future_defs = []
+ past_defs = []
+ name_format = self.name_format(period)
+ existing_tables = self.existing_tables(like: "#{original_table}_%").select { |t| /#{Regexp.escape("#{original_table}_")}(\d{4,6})/.match(t) }
+ existing_tables = (existing_tables + added_partitions).uniq.sort
+
+ existing_tables.each do |table|
+ day = DateTime.strptime(table.split("_").last, name_format)
+ partition_name = "#{original_table}_#{day.strftime(name_format(period))}"
+
+ sql = "(NEW.#{field} >= #{sql_date(day)} AND NEW.#{field} < #{sql_date(advance_date(day, period, 1))}) THEN
+ INSERT INTO #{partition_name} VALUES (NEW.*);"
+
+ if day.to_date < today
+ past_defs << sql
+ elsif advance_date(day, period, 1) < today
+ current_defs << sql
+ else
+ future_defs << sql
+ end
+ end
+
+ # order by current period, future periods asc, past periods desc
+ trigger_defs = current_defs + future_defs + past_defs.reverse
+
+ if trigger_defs.any?
+ queries << <<-SQL
+CREATE OR REPLACE FUNCTION #{trigger_name}()
+ RETURNS trigger AS $$
+ BEGIN
+ IF #{trigger_defs.join("\n ELSIF ")}
+ ELSE
+ RAISE EXCEPTION 'Date out of range. Ensure partitions are created.';
+ END IF;
+ RETURN NULL;
+ END;
+ $$ LANGUAGE plpgsql;
+ SQL
end
run_queries(queries) if queries.any?
end