require 'spec_helper' require 'flydata-core/table_def/postgresql_table_def' module FlydataCore module TableDef describe PostgresqlTableDef do let(:subject_object) { described_class.new(table_def, table_name, columns, column_def, default_charset, default_charset_postgresql, comment) } let(:table_def) { double('table_def') } let(:table_name) { double('table_name') } let(:columns) { double('columns') } let(:column_def) { double('column_def') } let(:default_charset) { double('default_charset') } let(:default_charset_postgresql) { double('default_charset_postgresql') } let(:comment) { double('comment') } let(:information_schema_columns) { {} } describe '.convert_to_flydata_type' do subject { described_class.convert_to_flydata_type(information_schema_columns) } before do information_schema_columns["data_type"] = data_type end let(:type_hash) { PostgresqlTableDef::TYPE_MAP_P2F[data_type] } let(:numeric_precision) { nil } let(:numeric_scale) { nil } let(:width_values) do a = [ numeric_precision, numeric_scale ] a.pop until a.empty? || a.last a.empty? ? nil : a end before do allow(described_class).to receive(:get_width_values). with(information_schema_columns, type_hash). and_return(width_values) end context "with data type numeric" do let(:data_type) { "numeric" } before do information_schema_columns["numeric_precision"] = numeric_precision information_schema_columns["numeric_scale"] = numeric_scale end context "with precision nil" do let(:numeric_precision) { nil } context "with scale nil" do let(:numeric_scale) { nil } it { is_expected.to eq "numeric" } end end context "with precision 1000" do let(:numeric_precision) { 1000 } context "with scale 0 (default value given by PostgreSQL)" do let(:numeric_scale) { 0 } it { is_expected.to eq "numeric(1000,0)" } end context "with scale 2" do let(:numeric_scale) { 2 } it { is_expected.to eq "numeric(1000,2)" } end end end # TODO Add data type specific specs here context "with data type money" do let(:data_type) { "money" } before do # Note that PostgreSQL's information_schema table does not have these # values for the money type. The user of this method has to populate # these values manually. information_schema_columns["numeric_precision"] = numeric_precision information_schema_columns["numeric_scale"] = numeric_scale end let(:numeric_precision) { 19 } context 'when the money scale is 0' do let(:numeric_scale) { 0 } it { is_expected.to eq "money(19,0)" } end context 'when the moeny scale is 2' do let(:numeric_scale) { 2 } it { is_expected.to eq "money(19,2)" } end end context "with time data type" do let(:data_type) { "time" } it { is_expected.to eq "time" } end context "with time without time zone data type" do let(:data_type) { "time without time zone" } it { is_expected.to eq "time" } end context "with time with time zone data type" do let(:data_type) { "time with time zone" } it { is_expected.to eq "timetz" } end context "with timestamp data type" do let(:data_type) { "timestamp" } it { is_expected.to eq "datetime" } end context "with timestamp without time zone data type" do let(:data_type) { "timestamp without time zone" } it { is_expected.to eq "datetime" } end context "with timestamp with time zone data type" do let(:data_type) { "timestamp with time zone" } it { is_expected.to eq "datetimetz" } end context "with bit data type" do let(:data_type) { "bit" } it { is_expected.to eq "bit" } end context "with bit varying data type" do let(:data_type) { "bit varying" } it { is_expected.to eq "varbit" } end context "with varbit data type" do let(:data_type) { "varbit" } it { is_expected.to eq "varbit" } end context "with unknown data type" do let(:data_type) { "duck" } let(:type_hash) { PostgresqlTableDef::TYPE_MAP_P2F[:default] } it { is_expected.to eq "_unsupported" } end context "with no data type" do let(:data_type) { nil } it { expect{subject}.to raise_error /Unknown PostgreSQL type/ } end context "with test data type" do let(:data_type) { test_data_type } before do allow(PostgresqlTableDef::TYPE_MAP_P2F).to receive(:has_key?). with(test_data_type).and_return true allow(PostgresqlTableDef::TYPE_MAP_P2F).to receive(:[]). with(test_data_type).and_return test_type_hash end let(:test_data_type) { double('test_data_type') } let(:type_hash) { test_type_hash } let(:test_type_hash) { {type: test_fd_type} } let(:test_fd_type) { "testfdtype" } let(:override_proc) { double('override_proc') } let(:overriden_type) { double('overriden_type') } context "no width values" do let(:width_values) { nil } it { is_expected.to eq test_fd_type } end context "single width value" do let(:width_values) { [10] } it { is_expected.to eq "#{test_fd_type}(10)" } end context "two width values" do let(:width_values) { [10,2] } it { is_expected.to eq "#{test_fd_type}(10,2)"} end context "with override proc" do before do test_type_hash[:override] = override_proc end it 'calls the override proc of the type and return its result' do expect(override_proc).to receive(:call). with(test_fd_type, test_data_type, test_fd_type). and_return(overriden_type) is_expected.to eq overriden_type end end end end describe '.get_width_values(information_schema_columns, type_hash)' do subject { described_class.send(:get_width_values, information_schema_columns, type_hash) } let(:type_hash) { {} } context 'with no width attr' do before do type_hash.delete(:width_attrs) end context 'without default' do before do type_hash.delete(:def_width) end it { is_expected.to be_nil } end context 'with default' do before do type_hash[:def_width] = [10] end it { expect{ subject }.to raise_error } end end context 'with a single width attr' do before do type_hash[:width_attrs] = ["char_octet_length"] end context 'without default' do before do type_hash.delete(:def_width) end context 'when attr value exists' do before do information_schema_columns["char_octet_length"] = 1024 end it { is_expected.to eq [ 1024 ] } end context 'when attr value does not exist' do before do information_schema_columns.delete("char_octet_length") end it { is_expected.to be_nil } end end context 'with default' do before do type_hash[:def_width] = [10] end context 'when attr value exists' do before do information_schema_columns["char_octet_length"] = 1024 end it { is_expected.to eq [1024] } end context 'when attr value does not exist' do before do information_schema_columns.delete("char_octet_length") end it { is_expected.to eq [10] } end end end context 'with two width attrs' do before do type_hash[:width_attrs] = ["precision", "scale"] end context 'without default' do before do type_hash.delete(:def_width) end context 'when the 1st attr value exists' do before do information_schema_columns["precision"] = 23 end context 'when the 2nd attr value exists' do before do information_schema_columns["scale"] = 2 end it { is_expected.to eq [23, 2] } end context 'when the 2nd attr value does not exist' do before do information_schema_columns.delete("scale") end it 'omits trailing nils' do is_expected.to eq [23] end end end context 'when the 1st attr value does not exist' do before do information_schema_columns.delete("precision") end context 'when the 2nd attr value exists' do before do information_schema_columns["scale"] = 2 end it { expect{subject}.to raise_error } end context 'when the 2nd attr value does not exist' do before do information_schema_columns.delete("scale") end it { is_expected.to be_nil } end end end context 'with default' do before do type_hash[:def_width] = [10, 0] end context 'when the 1st attr value exists' do before do information_schema_columns["precision"] = 23 end context 'when the 2nd attr value exists' do before do information_schema_columns["scale"] = 2 end it { is_expected.to eq [23, 2] } end context 'when the 2nd attr value does not exist' do before do information_schema_columns.delete("scale") end it { is_expected.to eq [23, 0] } end end context 'when the 1st attr value does not exist' do before do information_schema_columns.delete("precision") end context 'when the 2nd attr value exists' do before do information_schema_columns["scale"] = 2 end it { is_expected.to eq [10, 2] } end context 'when the 2nd attr value does not exist' do before do information_schema_columns.delete("scale") end it { is_expected.to eq [10, 0] } end end end context 'when the number of default values does not match the attr number' do before do type_hash[:def_width] = [10] end context 'when the 1st attr value exists' do before do information_schema_columns["precision"] = 23 end context 'when the 2nd attr value exists' do before do information_schema_columns["scale"] = 2 end it { expect{subject}.to raise_error } end context 'when the 2nd attr value does not exist' do before do information_schema_columns.delete("scale") end it { expect{subject}.to raise_error } end end context 'when the 1st attr value does not exist' do before do information_schema_columns.delete("precision") end context 'when the 2nd attr value exists' do before do information_schema_columns["scale"] = 2 end it { expect{subject}.to raise_error } end context 'when the 2nd attr value does not exist' do before do information_schema_columns.delete("scale") end it { expect{subject}.to raise_error } end end end end end describe '.parse_one_column_def' do subject {described_class.parse_one_column_def(information_schema_column)} let(:information_schema_column) { {} } let(:type) { double('type') } let(:is_primary) { double('is_primary') } let(:column_default) { double('column_default') } before do information_schema_column["is_primary"] = is_primary information_schema_column["column_default"] = column_default allow(described_class).to receive(:convert_to_flydata_type). with(information_schema_column). and_return(type) end # PRIMARY KEY context 'when the column is primary key' do let(:is_primary) { 't' } it { expect(subject[:primary_key]).to eq true} end context 'when the column is index key' do let(:is_primary) { 'f' } it { expect(subject[:primary_key]).to eq nil} end context 'when the column is not primary key or index key' do let(:is_primary) { nil } it { expect(subject[:primary_key]).to eq nil} end # DEFAULT expression context 'when the column type is a supported type' do context 'when DEFAULT value is not specified' do let(:type) { 'int4' } let(:column_default) { nil } it { expect(subject[:default]).to eq column_default } end context 'when DEFAULT value is specified' do let(:type) { 'varchar(96)' } let(:column_default) { "'FlyData, Inc.'"} it { expect(subject[:default]).to eq column_default } end end context 'when the column type is a unsupported' do context 'when DEFAULT value is not specified' do let(:type) { '_unsupported' } let(:column_default) { nil } it { expect(subject[:default]).to eq column_default } end context 'when DEFAULT value is specified' do let(:type) { '_unsupported' } let(:column_default) { "'{}'" } it { expect(subject[:default]).to eq nil } end end end end end end