lib/chrono_model/adapter.rb in chrono_model-0.3.0 vs lib/chrono_model/adapter.rb in chrono_model-0.3.1

- old
+ new

@@ -6,13 +6,18 @@ # This class implements all ActiveRecord::ConnectionAdapters::SchemaStatements # methods adding support for temporal extensions. It inherits from the Postgres # adapter for a clean override of its methods using super. # class Adapter < ActiveRecord::ConnectionAdapters::PostgreSQLAdapter - TEMPORAL_SCHEMA = 'temporal' # The schema holding current data - HISTORY_SCHEMA = 'history' # The schema holding historical data + # The schema holding current data + TEMPORAL_SCHEMA = 'temporal' + # The schema holding historical data + HISTORY_SCHEMA = 'history' + + # Chronomodel is supported starting with PostgreSQL >= 9.0 + # def chrono_supported? postgresql_version >= 90000 end # Creates the given table, possibly creating the temporal schema @@ -85,10 +90,28 @@ execute "ALTER TABLE #{table_name} SET SCHEMA #{TEMPORAL_SCHEMA}" _on_history_schema { chrono_create_history_for(table_name) } chrono_create_view_for(table_name) TableCache.add! table_name + + # Optionally copy the plain table data, setting up history + # retroactively. + # + if options[:copy_data] + seq = _on_history_schema { serial_sequence(table_name, primary_key(table_name)) } + from = options[:validity] || '0001-01-01 00:00:00' + + execute %[ + INSERT INTO #{HISTORY_SCHEMA}.#{table_name} + SELECT *, + nextval('#{seq}') AS hid, + timestamp '#{from}' AS valid_from, + timestamp '9999-12-31 00:00:00' AS valid_to, + timezone('UTC', now()) AS recorded_at + FROM #{TEMPORAL_SCHEMA}.#{table_name} + ] + end end chrono_alter(table_name) { super table_name, options, &block } else if options[:temporal] == false && is_chrono?(table_name) @@ -351,19 +374,35 @@ fields, values = columns.join(', '), columns.map {|c| "new.#{c}"}.join(', ') # INSERT - inert data both in the temporal table and in the history one. # - execute <<-SQL - CREATE OR REPLACE RULE #{table}_ins AS ON INSERT TO #{table} DO INSTEAD ( + if sequence.present? + execute <<-SQL + CREATE OR REPLACE RULE #{table}_ins AS ON INSERT TO #{table} DO INSTEAD ( - INSERT INTO #{current} ( #{fields} ) VALUES ( #{values} ); + INSERT INTO #{current} ( #{fields} ) VALUES ( #{values} ); - INSERT INTO #{history} ( #{pk}, #{fields}, valid_from ) - VALUES ( currval('#{sequence}'), #{values}, timezone('UTC', now()) ) - RETURNING #{pk}, #{fields} - ) - SQL + INSERT INTO #{history} ( #{pk}, #{fields}, valid_from ) + VALUES ( currval('#{sequence}'), #{values}, timezone('UTC', now()) ) + RETURNING #{pk}, #{fields} + ) + SQL + else + fields_with_pk = "#{pk}, " << fields + values_with_pk = "new.#{pk}, " << values + + execute <<-SQL + CREATE OR REPLACE RULE #{table}_ins AS ON INSERT TO #{table} DO INSTEAD ( + + INSERT INTO #{current} ( #{fields_with_pk} ) VALUES ( #{values_with_pk} ); + + INSERT INTO #{history} ( #{fields_with_pk}, valid_from ) + VALUES ( #{values_with_pk}, timezone('UTC', now()) ) + RETURNING #{fields_with_pk} + ) + SQL + end # UPDATE - set the last history entry validity to now, save the current data # in a new history entry and update the temporal table with the new data. # execute <<-SQL