diff --git a/CHANGELOG.md b/CHANGELOG.md index d655b8e..6a776d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,13 @@ +# 0.0.7 (2018-08-11) + +No breaking changes. + +## Feattues + +- New configuration parameter `Rule#in_values_num` + for controlling number of values in IN clause per line. + + # 0.0.6 (2018-03-31) No breaking changes. diff --git a/bin/anbt-sql-formatter b/bin/anbt-sql-formatter index 972dbc4..7f0ab12 100755 --- a/bin/anbt-sql-formatter +++ b/bin/anbt-sql-formatter @@ -5,13 +5,8 @@ begin require "anbt-sql-formatter/formatter" rescue LoadError $LOAD_PATH << File.expand_path( - File.join( - __FILE__, - "..", - "..", - "lib" - ) - ) + File.join(__FILE__, "..", "..", "lib") + ) require "anbt-sql-formatter/formatter" end @@ -36,7 +31,6 @@ def main rule.function_names << func_name.upcase } - #rule.indentString = "('-')" rule.indent_string = " " formatter = AnbtSql::Formatter.new(rule) diff --git a/lib/anbt-sql-formatter/formatter.rb b/lib/anbt-sql-formatter/formatter.rb index f9de279..ef7a771 100644 --- a/lib/anbt-sql-formatter/formatter.rb +++ b/lib/anbt-sql-formatter/formatter.rb @@ -3,6 +3,7 @@ require "anbt-sql-formatter/rule" require "anbt-sql-formatter/parser" require "anbt-sql-formatter/exception" +require "anbt-sql-formatter/in_values_checker" require "anbt-sql-formatter/helper" # Stack @@ -16,7 +17,7 @@ class Formatter def initialize(rule) @rule = rule @parser = AnbtSql::Parser.new(@rule) - + # 丸カッコが関数のものかどうかを記憶 @function_bracket = Stack.new end @@ -52,7 +53,7 @@ def format(sql_str) if sql_str.end_with?("\n") is_sql_ends_with_new_line = true end - + tokens = @parser.parse(sql_str) statements = split_to_statements(tokens) @@ -74,12 +75,12 @@ def format(sql_str) end end - + def modify_keyword_case(tokens) # SQLキーワードは大文字とする。or ... tokens.each{ |token| next if token._type != AnbtSql::TokenConstants::KEYWORD - + case @rule.keyword when AnbtSql::Rule::KEYWORD_NONE ; @@ -91,7 +92,7 @@ def modify_keyword_case(tokens) } end - + ## # . # ["(", "+", ")"] => ["(+)"] @@ -101,7 +102,7 @@ def concat_operator_for_oracle(tokens) while index < tokens.size - 2 if (tokens[index ].string == "(" && tokens[index + 1].string == "+" && - tokens[index + 2].string == ")") + tokens[index + 2].string == ")") tokens[index].string = "(+)" ArrayUtil.remove(tokens, index + 1) ArrayUtil.remove(tokens, index + 1) @@ -142,21 +143,21 @@ def insert_space_between_tokens(tokens) token = ArrayUtil.get(tokens, index ) if (prev._type != AnbtSql::TokenConstants::SPACE && - token._type != AnbtSql::TokenConstants::SPACE) + token._type != AnbtSql::TokenConstants::SPACE) # カンマの後にはスペース入れない if not @rule.space_after_comma if prev.string == "," index += 1 ; next end end - + # 関数名の後ろにはスペースは入れない # no space after function name if (@rule.function?(prev.string) && token.string == "(") index += 1 ; next end - + ArrayUtil.add(tokens, index, AnbtSql::Token.new(AnbtSql::TokenConstants::SPACE, " ") ) @@ -164,8 +165,8 @@ def insert_space_between_tokens(tokens) index += 1 end end - - + + def format_list_main_loop(tokens) # インデントを整える。 indent = 0 @@ -176,13 +177,14 @@ def format_list_main_loop(tokens) " ") encounter_between = false + in_values_checker = nil index = 0 # Length of tokens changes in loop! while index < tokens.size token = ArrayUtil.get(tokens, index) - - if token._type == AnbtSql::TokenConstants::SYMBOL # **** + + if token._type == AnbtSql::TokenConstants::SYMBOL # indentを1つ増やし、'('のあとで改行。 if token.string == "(" @@ -196,18 +198,23 @@ def format_list_main_loop(tokens) indent = (bracket_indent.pop()).to_i index += insert_return_and_indent(tokens, index, indent) @function_bracket.pop() - + # ','の前で改行 elsif token.string == "," - index += insert_return_and_indent(tokens, index, indent, "x") + if in_values_checker.nil? || in_values_checker.check() + index += insert_return_and_indent(tokens, index, indent, "x") + end elsif token.string == ";" # 2005.07.26 Tosiki Iga とりあえずセミコロンでSQL文がつぶれないように改良 indent = 0 index += insert_return_and_indent(tokens, index, indent) end - - elsif token._type == AnbtSql::TokenConstants::KEYWORD # **** + + elsif token._type == AnbtSql::TokenConstants::KEYWORD + in_values_checker = nil if in_values_checker + + in_values_checker = AnbtSql::InValuesChecker.new(@rule) if equals_ignore_case(token.string, "IN") # indentを2つ増やし、キーワードの後ろで改行 if (equals_ignore_case(token.string, "DELETE") || @@ -254,7 +261,7 @@ def format_list_main_loop(tokens) # キーワードの前で改行。indentを強制的に0にする。 if (equals_ignore_case(token.string, "UNION" ) || equals_ignore_case(token.string, "INTERSECT") || - equals_ignore_case(token.string, "EXCEPT" ) ) + equals_ignore_case(token.string, "EXCEPT" ) ) indent -= 2 index += insert_return_and_indent(tokens, index , indent) index += insert_return_and_indent(tokens, index + 1, indent) @@ -272,7 +279,7 @@ def format_list_main_loop(tokens) encounter_between = false end - elsif (token._type == AnbtSql::TokenConstants::COMMENT) # **** + elsif (token._type == AnbtSql::TokenConstants::COMMENT) if token.string.start_with?("/*") # マルチラインコメントの後に改行を入れる。 @@ -282,12 +289,12 @@ def format_list_main_loop(tokens) end end prev = token - + index += 1 end end - - + + # before: [..., "(", space, "X", space, ")", ...] # after: [..., "(X)", ...] # ただし、これでは "(X)" という一つの symbol トークンになってしまう。 @@ -295,7 +302,7 @@ def format_list_main_loop(tokens) # せっかくなので symbol/X/symbol と分けたい。 def special_treatment_for_parenthesis_with_one_element(tokens) (tokens.size - 1).downto(4).each{|index| - next if (index >= tokens.size()) + next if (index >= tokens.size()) t0 = ArrayUtil.get(tokens, index ) t1 = ArrayUtil.get(tokens, index - 1) @@ -305,7 +312,7 @@ def special_treatment_for_parenthesis_with_one_element(tokens) if (equals_ignore_case(t4.string , "(") && equals_ignore_case(t3.string.strip, "" ) && - equals_ignore_case(t1.string.strip, "" ) && + equals_ignore_case(t1.string.strip, "" ) && equals_ignore_case(t0.string , ")") ) t4.string = t4.string + t2.string + t0.string ArrayUtil.remove(tokens, index ) @@ -316,7 +323,7 @@ def special_treatment_for_parenthesis_with_one_element(tokens) } end - + def format_list(tokens) return [] if tokens.empty? @@ -328,7 +335,7 @@ def format_list(tokens) ArrayUtil.remove(tokens, 0) end return [] if tokens.empty? - + token = ArrayUtil.get(tokens, tokens.size() - 1) if token._type == AnbtSql::TokenConstants::SPACE ArrayUtil.remove(tokens, tokens.size() - 1) @@ -357,11 +364,11 @@ def insert_return_and_indent(tokens, index, indent, opt=nil) # 関数内では改行は挿入しない # No linefeed in function. return 0 if (@function_bracket.include?(true)) - + begin # 挿入する文字列を作成する。 s = "\n" - + # インデントをつける。 indent = 0 if indent < 0 ## Java版と異なる s += @rule.indent_string * indent @@ -397,6 +404,6 @@ def insert_return_and_indent(tokens, index, indent, opt=nil) rescue => e raise e end - end + end end end diff --git a/lib/anbt-sql-formatter/in_values_checker.rb b/lib/anbt-sql-formatter/in_values_checker.rb new file mode 100644 index 0000000..b765686 --- /dev/null +++ b/lib/anbt-sql-formatter/in_values_checker.rb @@ -0,0 +1,32 @@ +class AnbtSql + class InValuesChecker + def initialize(rule) + if rule.in_values_num.nil? + @mode = :default + elsif rule.in_values_num == AnbtSql::Rule::ONELINE_IN_VALUES_NUM + @mode = :oneline + @num = rule.in_values_num + else + @mode = :compact + @num = rule.in_values_num + @counter = 0 + end + end + + def check + if @mode == :default + true + elsif @mode == :oneline + false + else + @counter += 1 + if @counter == @num + @counter = 0 + true + else + false + end + end + end + end +end diff --git a/lib/anbt-sql-formatter/rule.rb b/lib/anbt-sql-formatter/rule.rb index 583569e..763aa91 100644 --- a/lib/anbt-sql-formatter/rule.rb +++ b/lib/anbt-sql-formatter/rule.rb @@ -32,15 +32,24 @@ class Rule attr_accessor :kw_nl_x attr_accessor :kw_nl_x_plus1_indent + # Limit number of values per line in IN clause to this value. + # + # nil:: one value per line (default) + # n (>=2):: n values per line + # ONELINE_IN_VALUES_NUM:: all values in one line + attr_accessor :in_values_num + # キーワードの変換規則: 何もしない KEYWORD_NONE = 0 # キーワードの変換規則: 大文字にする KEYWORD_UPPER_CASE = 1 - + # キーワードの変換規則: 小文字にする KEYWORD_LOWER_CASE = 2 + # IN の値を一行表示する場合の in_values_num 値 + ONELINE_IN_VALUES_NUM = 0 def initialize # キーワードの変換規則. @@ -51,7 +60,7 @@ def initialize @indent_string = " " @space_after_comma = false - + # __foo # ____KW @kw_plus1_indent_x_nl = %w(INSERT INTO CREATE DROP TRUNCATE TABLE CASE) @@ -65,12 +74,12 @@ def initialize # __foo # ____KW @kw_nl_x_plus1_indent = %w(ON USING) - + # __foo # __KW @kw_nl_x = %w(OR THEN ELSE) # @kw_nl_x = %w(OR WHEN ELSE) - + @kw_multi_words = ["ORDER BY", "GROUP BY"] # 関数の名前。 @@ -117,7 +126,7 @@ def function?(name) return true end end - + return false end end diff --git a/lib/anbt-sql-formatter/version.rb b/lib/anbt-sql-formatter/version.rb index d75eba9..ddb569e 100644 --- a/lib/anbt-sql-formatter/version.rb +++ b/lib/anbt-sql-formatter/version.rb @@ -1,7 +1,7 @@ module Anbt module Sql module Formatter - VERSION = "0.0.6" + VERSION = "0.0.7" end end end diff --git a/test/test_in_values.rb b/test/test_in_values.rb new file mode 100644 index 0000000..90f99f9 --- /dev/null +++ b/test/test_in_values.rb @@ -0,0 +1,193 @@ +# -*- coding: utf-8 -*- + +require File.join(File.expand_path(File.dirname(__FILE__)), "helper") + +require "anbt-sql-formatter/formatter" + +class TestAnbtSqlInValues < Test::Unit::TestCase + def test_format_without_in_values_num + rule = base_rule + @fmt = AnbtSql::Formatter.new(rule) + msg = "without in_values_num setting" + sql = "select * from users where id in (" + (1...30).to_a.join(",") + ")" + expected = <