# 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_specs (sp partition_opts)? { def tree { type: :alter_table, table_name: tbl_name.text_value, actions: alter_specs.actions } end } end rule online_option ( ('online'i / 'offline'i) sp )? end rule ignore_option ( 'ignore'i sp )? end rule alter_specs alter_spec ( comma alter_spec )* { def actions ret = [] concat_action!(ret, alter_spec.action) 0.upto(elements[1].elements.size - 1) do |i| action = elements[1].elements[i].elements[1].action concat_action!(ret, action) end ret end def concat_action!(array, action) if action.kind_of?(Array) actions = action else actions = [action] end array.concat(actions) end } end rule alter_spec 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)? sp '(' 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 col_key)? sp col_name { def action { action: :drop_column, column: col_name.text_value, } end } / [^,]+ end rule col_name_def col_name sp col_def { def column_def {column: col_name.text_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.text_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 '' 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 } / sp '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 'null'i { def default_value; nil; end } / value { def default_value; raw_value; end } 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 ######## 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 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 { def raw_value; value; end } end rule quoted_value '\'' text '\'' { def text_raw_value; text.text_value; end } end rule text ( '\\\'' / !'\'' . )* end rule ident_sys ident / ident_quoted end rule ident [0-9a-zA-Z_]+ end rule ident_quoted '`' ident '`' { def text_value ident.text_value end } end rule comma nsp ',' nsp end rule sp [\s]+ end rule nsp [\s]* end end