require 'spec_helper' describe 'MysqlAlterTableParser' do before do #load parser @parser_class = Mysql::ParserProvider.parser(:mysql, :mysql_alter_table) end let(:query) { "" } let(:parser) { @parser_class.new } subject { parser.parse(query).tree } describe '#parse' do context 'with alter table add column syntax' do context 'with normal alter table query' do let(:query) { "alter table test_table add column value varchar(26)" } it do expect(subject).to eq({ type: :alter_table, table_name: "test_table", actions: [{ action: :add_column, column: "value", type: "varchar(78)" }] }) end end context 'with different column types' do context 'with longblob' do let(:query) { "alter table test_table add column value longblob" } it do expect(subject).to eq({ type: :alter_table, table_name: "test_table", actions: [{ action: :add_column, column: "value", type: "varbinary(4294967295)" }] }) end end end context 'without the keyword "COLUMN"' do let(:query) { "alter table test_table add value varchar(26)" } it do expect(subject).to eq({ type: :alter_table, table_name: "test_table", actions: [{ action: :add_column, column: "value", type: "varchar(78)" }] }) end end context 'with multiple columns' do let(:query) { "alter table test_table add column (value1 varchar(26), value2 varchar(26))" } it do expect(subject).to eq({ type: :alter_table, table_name: "test_table", actions: [{ action: :add_column, column: "value1", type: "varchar(78)" },{ action: :add_column, column: "value2", type: "varchar(78)" }] }) end end context 'with multiple columns and without the keyword "COLUMN"' do let(:query) { "alter table test_table add ( value1 varchar(26), value2 varchar(26) )" } it do expect(subject).to eq({ type: :alter_table, table_name: "test_table", actions: [{ action: :add_column, column: "value1", type: "varchar(78)" },{ action: :add_column, column: "value2", type: "varchar(78)" }] }) end end context 'with multiple columns that have more detailed definitions,without the keyword "COLUMN"' do let(:query) { "alter table test_table add (value1 varchar(26) not null default 'flydata', value2 int auto_increment)" } it do expect(subject).to eq({ type: :alter_table, table_name: "test_table", actions: [{ action: :add_column, column: "value1", type: "varchar(78)", not_null: true, default: "flydata", },{ action: :add_column, column: "value2", type: "int4", auto_increment: true, }] }) end end context 'with backticks' do let(:query) { "alter table `test_table` add (`value1` varchar(26) not null default 'flydata', `value2` int auto_increment)" } it do expect(subject).to eq({ type: :alter_table, table_name: "test_table", actions: [{ action: :add_column, column: "value1", type: "varchar(78)", not_null: true, default: "flydata", },{ action: :add_column, column: "value2", type: "int4", auto_increment: true, }] }) end end context 'with after option' do let(:query) { "alter table test_table add column value varchar(26) after id" } it do expect(subject).to eq({ type: :alter_table, table_name: "test_table", actions: [{ action: :add_column, column: "value", type: "varchar(78)", after: 'id', }] }) end end context 'with after option, backticks only on table name and a comment' do let(:query) { %Q|alter table `test_table` add column value varchar(26) comment 'Ignorable comment' after id| } it do expect(subject).to eq({ type: :alter_table, table_name: "test_table", actions: [{ action: :add_column, column: "value", type: "varchar(78)", after: 'id', }] }) end end context 'with first option' do let(:query) { "alter table test_table add column value varchar(26) first" } it do expect(subject).to eq({ type: :alter_table, table_name: "test_table", actions: [{ action: :add_column, column: "value", type: "varchar(78)", position: :first, }] }) end end context 'with first, unique' do let(:query) { "alter table test_table add column value varchar(26) UNIQUE first" } it do expect(subject).to eq({ type: :alter_table, table_name: "test_table", actions: [{ action: :add_column, column: "value", type: "varchar(78)", unique: true, position: :first, }] }) end end context "with default option" do shared_examples "a query parser parsing a query adding a column with a default value" do let(:query) { "alter table test_table add column col #{data_type} default #{value}" } it do expect(subject).to eq({ type: :alter_table, table_name: "test_table", actions: [{ action: :add_column, column: "col", type: expected_data_type, default: expected_value, }] }) end end context 'for timestamp column' do let(:data_type) { "timestamp" } let(:expected_data_type) { "datetime" } context 'taking current_timestamp' do let(:value) { "current_timestamp" } let(:expected_value) { "current_timestamp" } it_behaves_like "a query parser parsing a query adding a column with a default value" end context 'taking localtime' do let(:value) { "localtime" } let(:expected_value) { "current_timestamp" } it_behaves_like "a query parser parsing a query adding a column with a default value" end context 'taking localtimestamp' do let(:value) { "localtimestamp" } let(:expected_value) { "current_timestamp" } it_behaves_like "a query parser parsing a query adding a column with a default value" end context 'taking now()' do let(:value) { "now()" } let(:expected_value) { "current_timestamp" } it_behaves_like "a query parser parsing a query adding a column with a default value" end end context 'for integer column' do let(:data_type) { "int(11)" } let(:expected_data_type) { "int4(11)" } context 'taking a positive number without sign' do let(:value) { "1" } let(:expected_value) { "1" } it_behaves_like "a query parser parsing a query adding a column with a default value" end context 'taking a positive number' do let(:value) { "+1" } let(:expected_value) { "1" } it_behaves_like "a query parser parsing a query adding a column with a default value" end context 'taking a negative number' do let(:value) { "-1" } let(:expected_value) { "-1" } it_behaves_like "a query parser parsing a query adding a column with a default value" end context 'taking a negative number string' do let(:value) { %Q|'-1'| } let(:expected_value) { "-1" } it_behaves_like "a query parser parsing a query adding a column with a default value" end context 'taking null' do let(:value) { %Q|NULL| } let(:expected_value) { nil } it_behaves_like "a query parser parsing a query adding a column with a default value" end end context 'for decimal column' do let(:data_type) { "decimal(10,2)" } let(:expected_data_type) { "numeric(10,2)" } context 'taking a positive number without sign' do let(:value) { "2.4" } let(:expected_value) { "2.4" } it_behaves_like "a query parser parsing a query adding a column with a default value" end context 'taking a positive number with sign' do let(:value) { "+0.4" } let(:expected_value) { "0.4" } it_behaves_like "a query parser parsing a query adding a column with a default value" end context 'taking a positive number no integer part' do let(:value) { ".05" } let(:expected_value) { "0.05" } it_behaves_like "a query parser parsing a query adding a column with a default value" end context 'taking a negative number' do let(:value) { "-20.43" } let(:expected_value) { "-20.43" } it_behaves_like "a query parser parsing a query adding a column with a default value" end context 'taking a negative number no integer part' do let(:value) { "-.43" } let(:expected_value) { "-0.43" } it_behaves_like "a query parser parsing a query adding a column with a default value" end end context 'for varchar column' do let(:data_type) { "varchar(10)" } let(:expected_data_type) { "varchar(30)" } context 'taking null' do let(:value) { "NULL" } let(:expected_value) { nil } it_behaves_like "a query parser parsing a query adding a column with a default value" end context 'taking empty string' do let(:value) { %Q|''| } let(:expected_value) { "" } it_behaves_like "a query parser parsing a query adding a column with a default value" end context 'taking a string with spaces' do let(:value) { %Q|'0 0 0 0 0 0 0'| } let(:expected_value) { "0 0 0 0 0 0 0" } it_behaves_like "a query parser parsing a query adding a column with a default value" end context 'taking a backslash' do let(:value) { %q|'\\\\'| } let(:expected_value) { %q|\\\\| } it_behaves_like "a query parser parsing a query adding a column with a default value" end context 'taking a string with special characters' do let(:value) { %q|'https://flydata.com $ \\\\\'\"\n\t'| } let(:expected_value) { %q|https://flydata.com $ \\\\\'\"\n\t| } it_behaves_like "a query parser parsing a query adding a column with a default value" end end end context 'with null option' do let(:length) { 45 } let(:query) { "alter table test_table add column extra varchar(#{length}) null" } it do expect(subject).to eq({ type: :alter_table, table_name: "test_table", actions: [{ action: :add_column, column: "extra", type: "varchar(#{length * 3})", }] }) end end end context 'with alter table drop column syntax' do let(:query) { "alter table test_table drop column value" } it do expect(subject).to eq( type: :alter_table, table_name: "test_table", actions: [{ action: :drop_column, column: "value" }]) end end context 'without column keyword' do let(:query) { "alter table test_table drop value" } it do expect(subject).to eq( type: :alter_table, table_name: "test_table", actions: [{ action: :drop_column, column: "value" }]) end end context 'when column_name is wrapped with backquote' do let(:query) { "alter table test_table drop `value`" } it do expect(subject).to eq( type: :alter_table, table_name: "test_table", actions: [{ action: :drop_column, column: "value" }]) end end context 'when table_name is wrapped with backquote' do let(:query) { "alter table `test_table` drop value" } it do expect(subject).to eq( type: :alter_table, table_name: "test_table", actions: [{ action: :drop_column, column: "value" }]) end end context 'when keywords are capitalized' do let(:query) { "ALTER TABLE `test_table` DROP value" } it do expect(subject).to eq( type: :alter_table, table_name: "test_table", actions: [{ action: :drop_column, column: "value" }]) end end context 'when table_name includes schema' do let(:query) { "alter table test_schema.test_table drop value" } it do expect(subject).to eq( type: :alter_table, schema_name: "test_schema", table_name: "test_table", actions: [{ action: :drop_column, column: "value" }]) end end context 'when table_name includes schema with quotes' do let(:query) { "alter table `test_schema`.`test_table` drop `value`" } it do expect(subject).to eq( type: :alter_table, schema_name: "test_schema", table_name: "test_table", actions: [{ action: :drop_column, column: "value" }]) end end context 'when query has unsupported drop dextension' do subject { parser.parse(query) } context 'with primary key drop' do let(:query) { "alter table test_table drop PRIMARY KEY" } it { expect(subject).to be_nil } end context 'with index drop' do let(:query) { "alter table test_table drop Index index_name" } it { expect(subject).to be_nil } end context 'with key drop' do let(:query) { "alter table test_table drop key index_name" } it { expect(subject).to be_nil } end context 'with foreign key drop' do let(:query) { "alter table test_table drop foreign key foreign_key_name" } it { expect(subject).to be_nil } end end context 'with multiple alter specs' do context 'with add column and drop column' do let(:query) { "alter table test_table add column value varchar(26) after id, drop old_value" } it do expect(subject).to eq({ type: :alter_table, table_name: "test_table", actions: [{ action: :add_column, column: "value", type: "varchar(78)", after: 'id', },{ action: :drop_column, column: "old_value", }] }) end end end end end