# ALTER TABLE DDL grammer definition via treetop # treetop is a parser generator based on parsing expression grammers. # See https://github.com/nathansobo/treetop for details. # # This grammer is based on the mysql 5.6 alter table syntax. # http://dev.mysql.com/doc/refman/5.6/en/alter-table.html # # Supported syntax # - ADD COLUMN col_name col_def # - ADD COLUMN (col_name, col_def, ...) # - DROP COLUMN col_name require 'flydata/table_def/mysql_table_def' grammar MysqlAlterTable rule alter_table alter_key sp online_option ignore_option table_key sp tbl_name sp alter_commands { def tree value = { type: :alter_table, table_name: tbl_name.table_part.value, actions: alter_commands.actions } value[:schema_name] = tbl_name.schema_part.value if tbl_name.has_schema? value end } end rule online_option ( ('online'i / 'offline'i) sp )? end rule ignore_option ( 'ignore'i sp )? end rule alter_commands discard_sym sp tablespace_sym { def actions [{ action: :discard_tablespace, query: text_value }] end } / import_sym sp tablespace_sym { def actions [{ action: :import_tablespace, query: text_value }] end } / ( partition_rules ) 1..1 { # This does not exist in sql_yacc.yy def actions action = elements[0].action action[:query] = text_value action[:support_level] = :nonbreaking [action] end } / alter_list (sp partition_opts)? { def actions alter_list.actions end } end rule alter_list alter_list_item ( comma alter_list_item )* { def actions ret = [] ret.concat(actions_with_query(alter_list_item)) 0.upto(elements[1].elements.size - 1) do |i| ret.concat(actions_with_query(elements[1].elements[i].elements[1])) end ret end def actions_with_query(alter_list_item) action = alter_list_item.action if action.kind_of?(Array) actions = action else actions = [action] end actions.each {|a| a[:query] = alter_list_item.text_value } actions end } end rule alter_list_item add_key sp key_def { def action action_hash = key_def.action action_hash end } / add_key (sp col_key)? sp col_name_def ( sp pos_def )? { def action ret = { action: :add_column } ret.merge!(col_name_def.column_def) if elements[4].nonterminal? ret.merge!(elements[4].elements[1].pos_def_option) end ret end } / add_key (sp col_key)? nsp '(' nsp col_name_def ( comma col_name_def )* nsp ')' { def action ret = [col_name_def.column_def] ret += 0.upto(elements[6].elements.count - 1).collect do |i| elements[6].elements[i].elements[1].column_def end ret.collect{|v| {action: :add_column}.merge(v) } end } / drop_key sp primary_sym sp key_sym { def action { action: :drop_primary_key } end } / drop_key sp foreign_sym sp key_sym sp field_ident { def action { action: :drop_foreign_key, support_level: :nonbreaking } end } / drop_key sp key_or_index sp field_ident { def action { action: "drop_#{key_or_index.text_value.downcase}".to_sym, support_level: :nonbreaking } end } / drop_key (sp col_key)? sp col_name { def action { action: :drop_column, column: col_name.value, } end } / enable_sym sp keys_sym { def action { action: :enable_keys, support_level: :nonbreaking, } end } / disable_sym sp keys_sym { def action { action: :disable_keys, support_level: :nonbreaking, } end } / rename_sym opt_to nsp table_ident { def action { action: :rename_table } end } / alter_order_clause { def action { action: :order_table } end } / convert_sym sp to_sym sp charset sp charset_name_or_default opt_collate { def action { action: :convert_charset } end } / create_table_options_space_separated { def action { action: :default_charset } end } /force_sym { def action { action: :force, support_level: :nonbreaking, } end } / [^,]+ { def action raise "unsupported ALTER TABLE query. Contact FlyData Support" end } end rule key_def normal_key_type key_alg nsp '(' key_list ')' normal_key_options { def action { action: :add_index, support_level: :nonbreaking, } end } / normal_key_type opt_ident key_alg nsp '(' key_list ')' normal_key_options { def action { action: :add_index, support_level: :nonbreaking, } end } / fulltext opt_key_or_index opt_ident init_key_options nsp '(' key_list ')' fulltext_key_options { def action { action: :add_index, support_level: :nonbreaking, } end } / spatial opt_key_or_index opt_ident init_key_options nsp '(' key_list ')' spatial_key_options { def action { action: :add_index, support_level: :nonbreaking, } end } / constraint_sym sp constraint_key_type key_alg nsp '(' key_list ')' normal_key_options { def action case constraint_key_type.value when :unique { action: :add_unique_constraint, support_level: :nonbreaking } when :primary_key { action: :add_primary_key_constraint, keys: key_list.value } else raise "Unsupported constraint key type `#{constraint_key_type.value}" end end } / constraint_sym sp constraint_key_type opt_ident key_alg nsp '(' key_list ')' normal_key_options { def action case constraint_key_type.value when :unique { action: :add_unique_constraint, support_level: :nonbreaking } when :primary_key { action: :add_primary_key_constraint, keys: key_list.value } else raise "Unsupported constraint key type `#{constraint_key_type.value}" end end } / opt_constraint constraint_key_type key_alg nsp '(' key_list ')' normal_key_options { def action case constraint_key_type.value when :unique { action: :add_unique_constraint, support_level: :nonbreaking } when :primary_key { action: :add_primary_key_constraint, keys: key_list.value } else raise "Unsupported constraint key type `#{constraint_key_type.value}" end end } / opt_constraint constraint_key_type opt_ident key_alg nsp '(' key_list ')' normal_key_options { def action case constraint_key_type.value when :unique { action: :add_unique_constraint, support_level: :nonbreaking } when :primary_key { action: :add_primary_key_constraint, keys: key_list.value } else raise "Unsupported constraint key type `#{constraint_key_type.value}" end end } / opt_constraint 'foreign'i key_sym opt_ident nsp '(' key_list ')' sp references / constraint_sym sp check_constraint { def action { action: :add_check_constraint, support_level: :nonbreaking, } end } / opt_constraint check_constraint { def action { action: :add_check_constraint, support_level: :nonbreaking, } end } end rule check_constraint 'check'i nsp '(' nsp expr nsp ')' end rule expr ( !')' . )+ end rule references 'references'i sp table_ident opt_ref_list opt_match_clause opt_on_update_delete end rule table_ident ident '.' ident / '.' ident / ident end rule opt_ref_list ( nsp '(' nsp ref_list nsp ')' )? end rule ref_list ident comma ref_list / ident end rule opt_match_clause ( sp ( 'match'i sp 'full'i / 'match'i sp 'partial'i / 'match'i sp 'simple'i ) )? end rule opt_on_update_delete ( sp ( 'on'i sp 'update'i delete_option sp 'on'i sp 'delete'i sp delete_option / 'on'i sp 'delete'i delete_option sp 'on'i sp 'update'i sp delete_option / 'on'i sp 'update'i sp delete_option / 'on'i sp 'delete'i sp delete_option ) )? end rule delete_option 'restrict'i / 'cascade'i / 'set'i sp 'null'i / 'no'i sp 'action'i / 'set'i sp 'default'i end rule opt_constraint ( constraint sp )? end rule constraint constraint_sym opt_ident end rule constraint_sym 'constraint'i end rule constraint_key_type primary_sym sp key_sym { # #value returns a normalized value of the element while #text_value # returns its text as is def value :primary_key end } / unique_sym opt_key_or_index { def value :unique end } end rule primary_sym 'primary'i end rule key_sym 'key'i end rule keys_sym 'keys'i end rule unique_sym 'unique'i end rule index_sym 'index'i end rule enable_sym 'enable'i end rule disable_sym 'disable'i end rule foreign_sym 'foreign'i end rule fulltext 'fulltext'i end rule fulltext_key_options ( sp fulltext_key_opts )? end rule fulltext_key_opts fulltext_key_opt sp fulltext_key_opts / fulltext_key_opt end rule fulltext_key_opt all_key_opt / 'with parser'i sp ident_sys end rule spatial 'spatial'i end rule spatial_key_options ( sp spatial_key_opts )? end rule spatial_key_opts spatial_key_opt sp spatial_key_opts / spatial_key_opt end rule spatial_key_opt all_key_opt end rule opt_key_or_index ( sp key_or_index )? end rule normal_key_type key_or_index end rule key_or_index key_sym / index_sym end rule opt_ident ( sp field_ident )? end rule field_ident ident '.' ident '.' ident / ident '.' ident / '.' ident / ident end rule key_alg sp init_key_options key_using_alg / init_key_options end rule init_key_options '' # In original, this is doing some initialization end rule key_using_alg 'using'i sp btree_or_rtree / type_sym sp btree_or_rtree end rule btree_or_rtree 'btree'i / 'rtree'i / 'hash'i end rule type_sym 'type'i end rule key_list nsp key_part order_dir nsp ',' nsp key_list nsp { # #value returns a normalized value of the element while #text_value # returns its text as is def value order_dir_value = order_dir.value if order_dir_value && !order_dir_value.empty? order_dir_value = " " + order_dir_value end [ "#{key_part.value}#{order_dir_value}" ] + key_list.value end } / nsp key_part order_dir nsp { def value order_dir_value = order_dir.value if order_dir_value && !order_dir_value.empty? order_dir_value = " " + order_dir_value end [ "#{key_part.value}#{order_dir_value}" ] end } end rule key_part ident nsp '(' nsp num nsp ')' { # #value returns a normalized value of the element while #text_value # returns its text as is def value "#{ident.value}(#{num.text_value})" end } / ident end rule order_dir # Treetop creates an empty SyntaxNode if the rule results in "match none" # The SyntaxNode instance does not have a custom method as deinfed here, # so it causes an issue to other nodes expecting the method. # 1..1 is a magic trick to force Treetop creating a SyntaxNode with # the custom method. (( sp ('asc'i / 'desc'i) )?) 1..1 { def value text_value.strip.downcase end } end rule normal_key_options ( sp normal_key_opts )? end rule normal_key_opts normal_key_opt sp normal_key_opts / normal_key_opt end rule normal_key_opt all_key_opt / key_using_alg end rule all_key_opt key_block_size nsp equal nsp ulong_num / key_block_size sp ulong_num / comment_opt end rule opt_equal ( nsp equal )? end rule equal '=' / ':=' end rule key_block_size 'key_block_size'i end rule col_name_def col_name sp col_def { def column_def {column: col_name.value}.merge(col_def.column_def) end } end rule pos_def 'first'i { def pos_def_option { position: :first } end } / 'after'i sp col_name { def pos_def_option { after: col_name.value, } end } end ######## partition opts rule partition_opts 'PARTITION BY' .* end ######## col_def rule col_def data_type ( col_opts )? { def column_def ret = data_type.data_type col_opts = elements[1] if col_opts.nonterminal? ret.merge!(col_opts.column_options) end ret end } end ######## data_type rule data_type data_type_name meta_text unsigned zerofill { def data_type meta = (meta_text.text_value.size > 1) ? meta_text.text_value : '' type = data_type_name.text_value.downcase + meta type = Flydata::TableDef::MysqlTableDef.convert_to_flydata_type(type) type << " unsigned" if !unsigned.terminal? ret = { type: type } ret[:zefofill] = true if !zerofill.terminal? ret end } end rule unsigned ( sp 'unsigned'i )? end rule zerofill ( sp 'zerofill'i )? end rule data_type_name ident_sym '' end rule meta_text '(' meta_value ')' / '' end rule meta_value values '' end ######## col_opts rule col_opts ( sp col_opt )+ { def column_options ret = elements.inject({}) do |h, element| h.merge(element.elements[1].option) end ret end } end rule col_opt null_opt { def option; null_opt_option; end } / default_opt { def option; default_opt_option; end } / auto_increment_opt { def option; { auto_increment: true }; end } / key_opt { def option; key_opt_option; end } / comment_opt { def option; comment_opt_option; end } / column_format_opt { def option; column_format_opt_option; end } / storage_opt { def option; storage_opt_option; end } #TODO: / reference_definition end rule null_opt 'not'i sp 'null'i { def null_opt_option; { not_null: true }; end } / 'null'i { def null_opt_option; { }; end } end rule default_opt 'default'i sp default_value { def default_opt_option; { default: default_value.default_value }; end } end rule default_value now { def default_value; text_value; end } / signed_literal { def default_value; text_value; end } end rule now 'current_timestamp'i / 'localtimestamp'i { def text_value; 'current_timestamp'; end } / 'localtime'i { def text_value; 'current_timestamp'; end } / 'now()'i { def text_value; 'current_timestamp'; end } end rule signed_literal literal / '+' num_literal { def text_value; num_literal.text_value; end } / '-' num_literal { def text_value; "-#{num_literal.text_value}"; end } end rule literal text_literal / num_literal # / temporal_literal # TODO / null_sym / false_sym / true_sym # / hex_num # TODO # / bin_num # TODO # / underscore_charset hex_num # TODO # / underscore_charset bin_num # TODO end rule text_literal quoted_value { def text_value; text_raw_value; end } # text_string # TODO # / nchar_string # TODO # / underscore_charset text_string # TODO # / text_literal text_string_literal # TODO end rule null_sym 'null'i { def text_value; nil; end } end rule false_sym 'false'i end rule true_sym 'true'i end rule num_literal [0-9]* '.' [0-9]+ { def text_value; v = super; v.start_with?('.') ? "0#{v}" : v; end } / [0-9]+ end rule num [0-9]+ end rule ulong_num [0-9]+ # TODO May not be the correct definition end rule real_ulong_num [0-9]+ # TODO - May have to expand later end rule auto_increment_opt 'auto_increment'i end rule key_opt 'unique'i ( sp 'key'i )? { def key_opt_option; { unique: true }; end } / ( 'primary'i sp )? 'key'i { def key_opt_option; { primary_key: true }; end } end rule comment_opt 'comment'i sp value { def comment_opt_option; #{ comment: value.raw_value }; {} # Not supported end } end rule column_format_opt 'column_format'i sp ( 'fixed'i / 'dynamic'i / 'default'i ) { def column_format_opt_option #{ column_format: elements[2].text_value } {} # Not supported end } end rule storage_opt 'storage'i sp ( 'disk'i / 'memory'i / 'default'i ) { def storage_opt_option { storage: elements[2].text_value } #{} # Not supported end } end rule rename_sym 'rename'i ![A-Za-z0-9_] end rule to_sym 'to'i ![A-Za-z0-9_] end rule eq_sym '=' end rule as_sym 'as'i end rule opt_to (nsp (to_sym / eq_sym / as_sym) )? end rule order_sym 'order'i end rule by_sym 'by'i end rule alter_order_clause order_sym sp by_sym sp alter_order_list end rule alter_order_list alter_order_item comma alter_order_list / alter_order_item end rule alter_order_item nsp simple_ident_nospvar order_dir end rule simple_ident_nospvar ident '.' ident '.' ident / ident '.' ident / '.' ident '.' ident / ident end rule convert_sym 'convert'i ![A-Za-z0-9_] end rule character_sym 'character'i ![A-Za-z0-9_] end rule set_sym 'set'i ![A-Za-z0-9_] end rule charset_sym 'charset'i ![A-Za-z0-9_] end rule default_sym 'default'i ![A-Za-z0-9_] end rule binary_sym 'binary'i ![A-Za-z0-9_] end rule collate_sym 'collate'i ![A-Za-z0-9_] end rule force_sym 'force'i ![A-Za-z0-9_] end rule discard_sym 'discard'i ![A-Za-z0-9_] end rule import_sym 'import'i ![A-Za-z0-9_] end rule tablespace_sym 'tablespace'i ![A-Za-z0-9_] end rule ident_or_text ident # TEXT_STRING # TODO - To be implemented when required # LEX_HOSTNAME # TODO end rule charset_name binary_sym / ident_or_text end rule charset character_sym sp set_sym / charset_sym end rule charset_name_or_default default_sym / charset_name end rule collation_name ident_or_text end rule collation_name_or_default default_sym / collation_name end rule opt_collate (sp collate_sym sp collation_name_or_default)? end rule create_table_options_space_separated create_table_option sp create_table_options_space_separated / create_table_option end rule create_table_option default_charset / default_collation #TODO - There are other rules which need to be implemented when required end rule default_charset opt_default nsp charset opt_equal nsp charset_name_or_default end rule opt_default ( nsp default_sym )? end rule default_collation opt_default nsp collate_sym opt_equal nsp collation_name_or_default end ######## keys rule alter_key 'alter'i end rule table_key 'table'i end rule add_key 'add'i end rule drop_key 'drop'i end rule col_key 'column'i end ######## Common rules rule tbl_name ( schema_part dot )? table_part { def has_schema? elements[0].respond_to? :schema_part end def schema_part if has_schema? elements[0].schema_part else nil end end } end rule schema_part ident_sys end rule table_part ident_sys end rule col_name ident_sys end rule values value ( comma value )* end rule value quoted_value { def raw_value; text_raw_value; end } / ident_sym { def raw_value; text_value; end } end rule quoted_value "'" text "'" { def text_raw_value; text.text_value; end } end rule text ("\\\\" / "\\'" / !"'" . )* end rule ident ident_sys / keyword end rule keyword ([a-zA-Z_]+) 1..1 { # TODO list all keywords def value text_value end } end rule ident_sys ident_sym / ident_quoted end rule ident_sym # 1..1 prevents Treetop from embedding the SyntaxNode to an upper node # without the custom method. ([0-9a-zA-Z_]+) 1..1 { def value text_value end } end rule ident_quoted '`' ident_sym '`' { # #value returns a normalized value of the element while #text_value # returns its text as is def value ident_sym.value end } end rule comma nsp ',' nsp end rule sp [\s]+ end rule nsp [\s]* end rule dot '.' end ## Partition related rules rule partition_rules add_partition_rule { def action { action: :add_partition } end } / drop_sym sp partition_sym sp alt_part_name_list { def action { action: :drop_partition } end } / rebuild_sym sp partition_sym opt_no_write_to_binlog sp all_or_alt_part_name_list { def action { action: :rebuild_partition } end } / optimize_sym sp partition_sym opt_no_write_to_binlog sp all_or_alt_part_name_list { def action { action: :optimize_partition } end } / analyze_sym sp partition_sym opt_no_write_to_binlog sp all_or_alt_part_name_list { def action { action: :analyze_partition } end } / check_sym sp partition_sym sp all_or_alt_part_name_list opt_mi_check_type { def action { action: :check_partition } end } / repair_sym sp partition_sym opt_no_write_to_binlog sp all_or_alt_part_name_list opt_mi_repair_type { def action { action: :repair_partition } end } / coalesce_sym sp partition_sym opt_no_write_to_binlog sp real_ulong_num { def action { action: :coalesce_partition } end } / truncate_sym sp partition_sym sp all_or_alt_part_name_list { def action { action: :truncate_partition } end } / reorg_partition_rule { def action { action: :reorganize_partition } end } / exchange_sym sp partition_sym sp alt_part_name_item sp with_sym sp table_sym sp table_ident opt_validation { def action { action: :exchange_partition } end } / discard_sym sp partition_sym sp all_or_alt_part_name_list sp tablespace_sym { def action { action: :discard_partition_tablespace } end } / import_sym sp partition_sym sp all_or_alt_part_name_list sp tablespace_sym { def action { action: :import_partition_tablespace } end } / remove_partitioning { def action { action: :remove_partitioning } end } end rule remove_partitioning remove_sym sp partitioning_sym end rule add_partition_rule add_sym sp partition_sym opt_no_write_to_binlog nsp add_part_extra end rule reorg_partition_rule reorganize_sym sp partition_sym opt_no_write_to_binlog nsp reorg_parts_rule end rule opt_validation ( sp (with_sym / without_sym) sp validation_sym )? end rule remove_sym 'remove'i ![A-Za-z0-9_] end rule all_sym 'all'i ![A-Za-z0-9_] end rule check_sym 'check'i ![A-Za-z0-9_] end rule table_sym 'table'i ![A-Za-z0-9_] end rule with_sym 'with'i ![A-Za-z0-9_] end rule without_sym 'without'i ![A-Za-z0-9_] end rule validation_sym 'validation'i ![A-Za-z0-9_] end rule exchange_sym 'exchange'i ![A-Za-z0-9_] end rule reorganize_sym 'reorganize'i ![A-Za-z0-9_] end rule truncate_sym 'truncate'i ![A-Za-z0-9_] end rule coalesce_sym 'coalesce'i ![A-Za-z0-9_] end rule repair_sym 'repair'i ![A-Za-z0-9_] end rule analyze_sym 'analyze'i ![A-Za-z0-9_] end rule optimize_sym 'optimize'i ![A-Za-z0-9_] end rule rebuild_sym 'rebuild'i ![A-Za-z0-9_] end rule drop_sym 'drop'i ![A-Za-z0-9_] end rule add_sym 'add'i ![A-Za-z0-9_] end rule partition_sym 'partition'i ![A-Za-z0-9_] end rule partitioning_sym 'partitioning'i ![A-Za-z0-9_] end rule local_sym 'local'i ![A-Za-z0-9_] end rule for_sym 'for'i ![A-Za-z0-9_] end rule upgrade_sym 'upgrade'i ![A-Za-z0-9_] end rule changed_sym 'changed'i ![A-Za-z0-9_] end rule extended_sym 'extended'i ![A-Za-z0-9_] end rule medium_sym 'medium'i ![A-Za-z0-9_] end rule fast_sym 'fast'i ![A-Za-z0-9_] end rule quick_sym 'quick'i ![A-Za-z0-9_] end rule use_frm_sym 'use_frm'i ![A-Za-z0-9_] end # query we parse may never have this, including for the sake of completeness rule no_write_to_binlog_sym 'no_write_to_binlog'i ![A-Za-z0-9_] end rule opt_no_write_to_binlog ( sp (no_write_to_binlog_sym / local_sym) )? end # Generic implementation(matches anything). Should be modified when required rule add_part_extra .* end # Generic implementation (matches anything). Should be modified when required rule reorg_parts_rule .* end rule all_or_alt_part_name_list all_sym / alt_part_name_list end rule alt_part_name_list alt_part_name_item comma alt_part_name_list / alt_part_name_item end rule alt_part_name_item ident end rule opt_mi_check_type ( sp mi_check_types )? end rule mi_check_types mi_check_type sp mi_check_types / mi_check_type end rule mi_check_type quick_sym / fast_sym / medium_sym / extended_sym / changed_sym / for_sym sp upgrade_sym end rule opt_mi_repair_type ( sp mi_repair_types )? end rule mi_repair_types mi_repair_type sp mi_repair_types / mi_repair_type end rule mi_repair_type quick_sym / extended_sym / use_frm_sym end end