diff options
| author | Robert | 2017-06-06 15:50:54 +0200 | 
|---|---|---|
| committer | Robert | 2017-06-06 15:50:54 +0200 | 
| commit | 15b7cf8213d730fa36740a74bc75baa5fc56dd62 (patch) | |
| tree | ad1901d30d814257e738a0348d190171c212d6e5 | |
| parent | 12290bbdabb8f53c4dddb1a647296a426b88709e (diff) | |
| download | chouette-core-15b7cf8213d730fa36740a74bc75baa5fc56dd62.tar.bz2 | |
Refs: #3604; rewriting schema cloner in Ruby
| -rw-r--r-- | Gemfile | 2 | ||||
| -rw-r--r-- | Gemfile.lock | 2 | ||||
| -rw-r--r-- | app/workers/referential_cloning_worker.rb | 2 | ||||
| -rw-r--r-- | lib/af83/schema_cloner.rb | 38 | ||||
| -rw-r--r-- | spec/lib/af83/cloning/clone_schema_spec.rb | 150 | ||||
| -rw-r--r-- | spec/lib/af83/stored_procedures/clone_schema_spec.rb | 4 | 
6 files changed, 193 insertions, 5 deletions
| @@ -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", | 
