aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Gemfile2
-rw-r--r--Gemfile.lock2
-rw-r--r--app/workers/referential_cloning_worker.rb2
-rw-r--r--lib/af83/schema_cloner.rb38
-rw-r--r--spec/lib/af83/cloning/clone_schema_spec.rb150
-rw-r--r--spec/lib/af83/stored_procedures/clone_schema_spec.rb4
6 files changed, 193 insertions, 5 deletions
diff --git a/Gemfile b/Gemfile
index 55e88f78a..814a5ef14 100644
--- a/Gemfile
+++ b/Gemfile
@@ -110,6 +110,8 @@ gem 'ransack'
gem "squeel", github: 'activerecord-hackery/squeel'
gem 'active_attr'
+gem 'sequel'
+
gem 'draper'
gem 'enumerize', '~> 0.10.0'
diff --git a/Gemfile.lock b/Gemfile.lock
index 992e44af0..cc3458802 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -462,6 +462,7 @@ GEM
rdoc (~> 4.0)
select2-rails (4.0.3)
thor (~> 0.14)
+ sequel (4.47.0)
shoulda-matchers (3.1.1)
activesupport (>= 4.0.0)
sidekiq (4.2.10)
@@ -647,6 +648,7 @@ DEPENDENCIES
sawyer (~> 0.6.0)
sdoc (~> 0.4.0)
select2-rails (~> 4.0, >= 4.0.3)
+ sequel
shoulda-matchers (~> 3.1)
sidekiq
simple_form (~> 3.1.0)
diff --git a/app/workers/referential_cloning_worker.rb b/app/workers/referential_cloning_worker.rb
index 2a524dbcd..c74566966 100644
--- a/app/workers/referential_cloning_worker.rb
+++ b/app/workers/referential_cloning_worker.rb
@@ -16,7 +16,7 @@ class ReferentialCloningWorker
def clone_schema ref_cloning, source_schema, target_schema
ref_cloning.run!
- StoredProcedures.invoke_stored_procedure(:clone_schema, source_schema, target_schema)
+ StoredProcedures.invoke_stored_procedure(:clone_schema, source_schema, target_schema, true)
ref_cloning.successful!
rescue Exception => e
diff --git a/lib/af83/schema_cloner.rb b/lib/af83/schema_cloner.rb
new file mode 100644
index 000000000..933fffc3d
--- /dev/null
+++ b/lib/af83/schema_cloner.rb
@@ -0,0 +1,38 @@
+module AF83
+ class SchemaCloner
+
+ attr_reader :source_schema, :target_schema, :include_records
+ def clone_schema(source_schema, target_schema, include_records: true)
+ @source_schema = source_schema
+ @target_schema = target_schema
+ @include_records = include_records
+
+ clone_schema_
+ end
+
+ private
+ def assure_schema_preconditons
+ raise RuntimeError, "Target Schema #{target_schema} does already exist" unless
+ execute("SELECT oid FROM pg_namespace WHERE nspname = '#{target_schema}' LIMIT 1").empty?
+
+ raise RuntimeError, "Source Schema #{source_schema} does not exist" unless source
+ end
+
+ def clone_schema_
+ assure_schema_preconditons
+ end
+ def connection
+ @__connection__ ||= ActiveRecord::Base.connection
+ end
+ def execute(str)
+ connection.execute(str).to_a
+ end
+
+ def source
+ @__source__ ||= execute("SELECT oid FROM pg_namespace WHERE nspname = '#{source_schema}' LIMIT 1").first;
+ end
+ def source_oid
+ @__source_oid__ ||= source["oid"].to_i;
+ end
+ end
+end
diff --git a/spec/lib/af83/cloning/clone_schema_spec.rb b/spec/lib/af83/cloning/clone_schema_spec.rb
new file mode 100644
index 000000000..aa74bb372
--- /dev/null
+++ b/spec/lib/af83/cloning/clone_schema_spec.rb
@@ -0,0 +1,150 @@
+include Support::PGCatalog
+
+RSpec.describe AF83::SchemaCloner do
+ let( :source_schema ){ "source_schema" }
+ let( :target_schema ){ "target_schema" }
+ let( :child_table ){ "children" }
+ let( :parent_table ){ "parents" }
+
+ subject { described_class.new }
+
+ before do
+ create_schema_with_tables
+ end
+
+ context "before cloning" do
+ it "target schema does not exist" do
+ expect( get_schema_oid(target_schema) ).to be_nil
+ end
+ end
+
+ shared_examples_for "after cloning schema" do
+
+ let( :expected_target_parent_count ){ include_recs ? 1 : 0 }
+ let( :expected_target_child_count ){ include_recs ? 1 : 0 }
+
+ it "table information is correctly read" do
+ expect(get_table_information(source_schema, child_table))
+ .to eq([{"table_schema"=>"source_schema",
+ "table_name"=>"children",
+ "table_type"=>"BASE TABLE",
+ "self_referencing_column_name"=>nil,
+ "reference_generation"=>nil,
+ "user_defined_type_catalog"=>nil,
+ "user_defined_type_schema"=>nil,
+ "user_defined_type_name"=>nil,
+ "is_insertable_into"=>"YES",
+ "is_typed"=>"NO",
+ "commit_action"=>nil}])
+
+ expect( get_table_information(target_schema, child_table))
+ .to eq([{"table_schema"=>"target_schema",
+ "table_name"=>"children",
+ "table_type"=>"BASE TABLE",
+ "self_referencing_column_name"=>nil,
+ "reference_generation"=>nil,
+ "user_defined_type_catalog"=>nil,
+ "user_defined_type_schema"=>nil,
+ "user_defined_type_name"=>nil,
+ "is_insertable_into"=>"YES",
+ "is_typed"=>"NO",
+ "commit_action"=>nil}])
+ end
+
+ it "has the correct sequences" do
+ expect(get_sequences(target_schema, child_table))
+ .to eq([{"sequence_name"=>"#{child_table}_id_seq",
+ "last_value"=>"1",
+ "start_value"=>"1",
+ "increment_by"=>"1",
+ "max_value"=>"9223372036854775807",
+ "min_value"=>"1",
+ "cache_value"=>"1",
+ "log_cnt"=>"0",
+ "is_cycled"=>"f",
+ "is_called"=>"f"}])
+
+ expect(get_sequences(target_schema, parent_table))
+ .to eq([{"sequence_name"=>"#{parent_table}_id_seq",
+ "last_value"=>"1",
+ "start_value"=>"1",
+ "increment_by"=>"1",
+ "max_value"=>"9223372036854775807",
+ "min_value"=>"1",
+ "cache_value"=>"1",
+ "log_cnt"=>"0",
+ "is_cycled"=>"f",
+ "is_called"=>"f"}])
+ end
+
+ it "has the correct foreign keys" do
+ expect( get_foreign_keys(target_schema, child_table) )
+ .to eq([{
+ "constraint_name" => "children_parents",
+ "constraint_def" => "FOREIGN KEY (parents_id) REFERENCES target_schema.parents(id)"}])
+ end
+
+ it "the data has been copied or not" do
+ source_pt_count = count_records(source_schema, parent_table)
+ source_ch_count = count_records(source_schema, child_table)
+ target_pt_count = count_records(target_schema, parent_table)
+ target_ch_count = count_records(target_schema, child_table)
+
+ expect( source_pt_count ).to eq( 1 )
+ expect( source_ch_count ).to eq( 1 )
+ expect( target_pt_count ).to eq( expected_target_parent_count )
+ expect( target_ch_count ).to eq( expected_target_child_count )
+ end
+ end
+
+ context "step by step", :wip do
+ # before do
+ # subject.clone_schema(source_schema, target_schema)
+ # end
+ it "assure target schema nonexistance" do
+ expect{ subject.clone_schema(source_schema, source_schema) }.to raise_error(RuntimeError)
+ end
+ it "assure source schema's existance" do
+ expect{ subject.clone_schema(target_schema, target_schema) }.to raise_error(RuntimeError)
+ end
+
+ end
+
+ context "after cloning" do
+ before do
+ subject.clone_schema(source_schema, target_schema, include_recs: include_recs)
+ end
+
+ context "without including records" do
+ let( :include_recs ){ false }
+ it_behaves_like 'after cloning schema'
+ end
+
+ context "with including records" do
+ let( :include_recs ){ true }
+ it_behaves_like 'after cloning schema'
+ end
+ end
+
+end
+
+def create_schema_with_tables
+ execute <<-EOSQL
+ DROP SCHEMA IF EXISTS #{source_schema} CASCADE;
+ CREATE SCHEMA #{source_schema};
+
+ CREATE TABLE #{source_schema}.#{parent_table} (
+ id bigserial PRIMARY KEY
+ );
+ CREATE TABLE #{source_schema}.#{child_table} (
+ id bigserial PRIMARY KEY,
+ #{parent_table}_id bigint
+ );
+ ALTER TABLE #{source_schema}.#{child_table}
+ ADD CONSTRAINT #{child_table}_#{parent_table}
+ FOREIGN KEY( #{parent_table}_id ) REFERENCES #{source_schema}.#{parent_table}(id);
+ INSERT INTO #{source_schema}.#{parent_table} VALUES (100);
+ INSERT INTO #{source_schema}.#{child_table} VALUES (1, 100);
+ EOSQL
+end
+
diff --git a/spec/lib/af83/stored_procedures/clone_schema_spec.rb b/spec/lib/af83/stored_procedures/clone_schema_spec.rb
index c0502f1ad..bdc3bd6cc 100644
--- a/spec/lib/af83/stored_procedures/clone_schema_spec.rb
+++ b/spec/lib/af83/stored_procedures/clone_schema_spec.rb
@@ -78,10 +78,6 @@ RSpec.describe StoredProcedures do
let( :expected_target_parent_count ){ include_recs ? 1 : 0 }
let( :expected_target_child_count ){ include_recs ? 1 : 0 }
- it "target schema does exist" do
- expect( get_schema_oid(target_schema) ).not_to be_nil
- end
-
it "table information is correctly read" do
expect(get_table_information(source_schema, child_table))
.to eq([{"table_schema"=>"source_schema",