From cf31de7f5c6df77bb7ef4af30bf02bc1c764769e Mon Sep 17 00:00:00 2001 From: pinzolo Date: Sat, 28 Jul 2018 19:45:07 +0900 Subject: [PATCH 1/5] enable compact in values --- lib/anbt-sql-formatter/formatter.rb | 59 +++--- lib/anbt-sql-formatter/in_values_checker.rb | 32 ++++ lib/anbt-sql-formatter/rule.rb | 14 +- test/test_in_values.rb | 193 ++++++++++++++++++++ 4 files changed, 267 insertions(+), 31 deletions(-) create mode 100644 lib/anbt-sql-formatter/in_values_checker.rb create mode 100644 test/test_in_values.rb diff --git a/lib/anbt-sql-formatter/formatter.rb b/lib/anbt-sql-formatter/formatter.rb index f9de279..c75b18a 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,12 +177,13 @@ 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 # **** # indentを1つ増やし、'('のあとで改行。 @@ -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 # **** + 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) @@ -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..469613f 100644 --- a/lib/anbt-sql-formatter/rule.rb +++ b/lib/anbt-sql-formatter/rule.rb @@ -32,15 +32,19 @@ class Rule attr_accessor :kw_nl_x attr_accessor :kw_nl_x_plus1_indent + 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 +55,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 +69,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 +121,7 @@ def function?(name) return true end end - + return false 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 = < Date: Thu, 9 Aug 2018 06:44:01 +0900 Subject: [PATCH 2/5] Remove unnecessary comments --- bin/anbt-sql-formatter | 1 - lib/anbt-sql-formatter/formatter.rb | 6 +++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/bin/anbt-sql-formatter b/bin/anbt-sql-formatter index 972dbc4..f25bd33 100755 --- a/bin/anbt-sql-formatter +++ b/bin/anbt-sql-formatter @@ -36,7 +36,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 c75b18a..ef7a771 100644 --- a/lib/anbt-sql-formatter/formatter.rb +++ b/lib/anbt-sql-formatter/formatter.rb @@ -184,7 +184,7 @@ def format_list_main_loop(tokens) 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 == "(" @@ -211,7 +211,7 @@ def format_list_main_loop(tokens) 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") @@ -279,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?("/*") # マルチラインコメントの後に改行を入れる。 From 56f0729e043fbc51642dd37aa21e94c813c32f16 Mon Sep 17 00:00:00 2001 From: sonota88 Date: Thu, 9 Aug 2018 06:49:28 +0900 Subject: [PATCH 3/5] Format (bin/anbt-sql-formatter) --- bin/anbt-sql-formatter | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/bin/anbt-sql-formatter b/bin/anbt-sql-formatter index f25bd33..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 From 2e9e797feb75d81792c32476c4d4abae1c24c07c Mon Sep 17 00:00:00 2001 From: sonota88 Date: Thu, 9 Aug 2018 07:03:19 +0900 Subject: [PATCH 4/5] Add comments for Rule#in_values_num --- lib/anbt-sql-formatter/rule.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/anbt-sql-formatter/rule.rb b/lib/anbt-sql-formatter/rule.rb index 469613f..763aa91 100644 --- a/lib/anbt-sql-formatter/rule.rb +++ b/lib/anbt-sql-formatter/rule.rb @@ -32,6 +32,11 @@ 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 # キーワードの変換規則: 何もしない From 4e7b25b53db0285168c8dc22d34210fdbb3d17c8 Mon Sep 17 00:00:00 2001 From: sonota88 Date: Sat, 11 Aug 2018 11:42:18 +0900 Subject: [PATCH 5/5] Bump version to 0.0.7 --- CHANGELOG.md | 10 ++++++++++ lib/anbt-sql-formatter/version.rb | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) 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/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