tsql_parser 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/parsing/formatter.rb +1 -1
- data/lib/parsing/formatters/strategy/base_formatter.rb +28 -0
- data/lib/parsing/formatters/strategy/insert_formatter.rb +66 -0
- data/lib/parsing/formatters/strategy/join_formatter.rb +44 -0
- data/lib/parsing/formatters/strategy/select_formatter.rb +52 -0
- data/lib/parsing/formatters/strategy/set_formatter.rb +130 -0
- data/lib/parsing/formatters/strategy/update_formatter.rb +50 -0
- data/lib/parsing/formatters/strategy/where_formatter.rb +72 -0
- data/lib/parsing/formatters/text_formatter.rb +50 -0
- data/lib/parsing/tokenizer.rb +1 -1
- metadata +9 -2
- data/lib/parsing/text_formatter.rb +0 -250
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 62f81b6f4b6195e663c823dbb1fda82a8a7530d534ed73a9ba27a048fdacb42d
|
4
|
+
data.tar.gz: c1af50b7c5421316290044ca548bd9983b568c8f6446402ba1d6b25ef46920ea
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 24c95e57e20af4b3dff6d7b15cb38e0736bddc683f025c9bc5300e6662992045e110990343497c13952524de2868fdc5341214ab744d6305416a2f001388d555
|
7
|
+
data.tar.gz: 06d257666a82067a42990e8c0f9a4ac8dd1644ec399f7f8aa453cfe7d2465dc680a349c7623bcf92e6f1f0d412a6a4ea2440f818bdf1b239510896b601e891cd
|
data/lib/parsing/formatter.rb
CHANGED
@@ -17,7 +17,7 @@
|
|
17
17
|
module TSqlParser::Parsing
|
18
18
|
require_relative "iterator"
|
19
19
|
require_relative "parser"
|
20
|
-
require_relative "text_formatter"
|
20
|
+
require_relative "formatters/text_formatter"
|
21
21
|
require_relative "model/sql_container"
|
22
22
|
require_relative "model/flat_sql_container"
|
23
23
|
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# __ .__
|
2
|
+
# _/ |_ ___________| | ___________ _______ ______ ___________
|
3
|
+
# \ __\/ ___/ ____/ | ______ \____ \__ \\_ __ \/ ___// __ \_ __ \
|
4
|
+
# | | \___ < <_| | |__ /_____/ | |_> > __ \| | \/\___ \\ ___/| | \/
|
5
|
+
# |__| /____ >__ |____/ | __(____ /__| /____ >\___ >__|
|
6
|
+
# \/ |__| |__| \/ \/ \/
|
7
|
+
#
|
8
|
+
# A very light-weight and opinionated T-SQL parser and formatter.
|
9
|
+
#
|
10
|
+
# github.com/scstauf
|
11
|
+
#
|
12
|
+
# path:
|
13
|
+
# parsing/formatters/base_formatter.rb
|
14
|
+
# object:
|
15
|
+
# TSqlParser::Parsing::Formatters::SetFormatter
|
16
|
+
|
17
|
+
module TSqlParser::Parsing::Formatters
|
18
|
+
class BaseFormatter
|
19
|
+
def self.get_tab_count(line, tab = " ")
|
20
|
+
tab_count = 0
|
21
|
+
while line.start_with? tab
|
22
|
+
tab_count += 1
|
23
|
+
line = line.sub(tab, "")
|
24
|
+
end
|
25
|
+
tab_count
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# __ .__
|
2
|
+
# _/ |_ ___________| | ___________ _______ ______ ___________
|
3
|
+
# \ __\/ ___/ ____/ | ______ \____ \__ \\_ __ \/ ___// __ \_ __ \
|
4
|
+
# | | \___ < <_| | |__ /_____/ | |_> > __ \| | \/\___ \\ ___/| | \/
|
5
|
+
# |__| /____ >__ |____/ | __(____ /__| /____ >\___ >__|
|
6
|
+
# \/ |__| |__| \/ \/ \/
|
7
|
+
#
|
8
|
+
# A very light-weight and opinionated T-SQL parser and formatter.
|
9
|
+
#
|
10
|
+
# github.com/scstauf
|
11
|
+
#
|
12
|
+
# path:
|
13
|
+
# parsing/formatters/insert_formatter.rb
|
14
|
+
# object:
|
15
|
+
# TSqlParser::Parsing::Formatters::InsertFormatter
|
16
|
+
|
17
|
+
module TSqlParser::Parsing::Formatters
|
18
|
+
require_relative "base_formatter"
|
19
|
+
|
20
|
+
class InsertFormatter < BaseFormatter
|
21
|
+
def self.format(text, tab = " ")
|
22
|
+
formatted = []
|
23
|
+
lines = text.split("\n")
|
24
|
+
search = "INSERT INTO"
|
25
|
+
lines.each do |line|
|
26
|
+
first = line.strip.split(" ").first
|
27
|
+
if first != "INSERT"
|
28
|
+
formatted << line
|
29
|
+
next
|
30
|
+
end
|
31
|
+
|
32
|
+
tab_count = self.get_tab_count(line, tab)
|
33
|
+
insert = line.strip[search.size + 1..]
|
34
|
+
new_insert = self.format_insert(insert, tab_count, tab)
|
35
|
+
if new_insert.nil?
|
36
|
+
formatted << line
|
37
|
+
next
|
38
|
+
end
|
39
|
+
formatted << line.sub(insert, new_insert)
|
40
|
+
end
|
41
|
+
formatted.join("\n")
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def self.format_insert(s, tab_count = 0, tab = " ")
|
47
|
+
return s if s.nil?
|
48
|
+
formatted = []
|
49
|
+
if s.include? ") VALUES ("
|
50
|
+
tokens = s.split(") VALUES (")
|
51
|
+
table = tokens[0][..tokens[0].index("(") - 2]
|
52
|
+
columns = tokens[0][tokens[0].index("(") + 1..]
|
53
|
+
values = tokens[1][..-2]
|
54
|
+
formatted << "\n#{tab * (tab_count + 1)}#{table}"
|
55
|
+
formatted << "#{tab * (tab_count + 2)}(#{columns})"
|
56
|
+
formatted << "#{tab * (tab_count + 1)}VALUES"
|
57
|
+
if s.end_with? ");"
|
58
|
+
formatted << "#{tab * (tab_count + 2)}(#{values}"
|
59
|
+
else
|
60
|
+
formatted << "#{tab * (tab_count + 2)}(#{values})"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
formatted.join("\n") unless formatted.empty?
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# __ .__
|
2
|
+
# _/ |_ ___________| | ___________ _______ ______ ___________
|
3
|
+
# \ __\/ ___/ ____/ | ______ \____ \__ \\_ __ \/ ___// __ \_ __ \
|
4
|
+
# | | \___ < <_| | |__ /_____/ | |_> > __ \| | \/\___ \\ ___/| | \/
|
5
|
+
# |__| /____ >__ |____/ | __(____ /__| /____ >\___ >__|
|
6
|
+
# \/ |__| |__| \/ \/ \/
|
7
|
+
#
|
8
|
+
# A very light-weight and opinionated T-SQL parser and formatter.
|
9
|
+
#
|
10
|
+
# github.com/scstauf
|
11
|
+
#
|
12
|
+
# path:
|
13
|
+
# parsing/formatters/join_formatter.rb
|
14
|
+
# object:
|
15
|
+
# TSqlParser::Parsing::Formatters::JoinFormatter
|
16
|
+
|
17
|
+
module TSqlParser::Parsing::Formatters
|
18
|
+
require_relative "base_formatter"
|
19
|
+
|
20
|
+
class JoinFormatter < BaseFormatter
|
21
|
+
def self.format(text, tab = " ")
|
22
|
+
text = text.gsub(/INNER\s+JOIN/, "INNER JOIN")
|
23
|
+
.gsub(/LEFT\s+JOIN/, "LEFT JOIN")
|
24
|
+
lines = text.split("\n")
|
25
|
+
new_text = []
|
26
|
+
|
27
|
+
lines.each do |line|
|
28
|
+
first = line.strip.split(" ").first
|
29
|
+
if line.include? " WHERE " and first != "WHERE" and not first.start_with? "--" and not first.start_with? "/*"
|
30
|
+
tab_count = self.get_tab_count(line, tab)
|
31
|
+
where_parts = line.strip.split(" WHERE ")
|
32
|
+
where_text = []
|
33
|
+
where_text << "#{tab * tab_count}#{where_parts[0]}"
|
34
|
+
where_text << "#{tab * tab_count}WHERE #{where_parts[1]}"
|
35
|
+
new_text << where_text.join("\n")
|
36
|
+
else
|
37
|
+
new_text << line
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
new_text.join("\n")
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# __ .__
|
2
|
+
# _/ |_ ___________| | ___________ _______ ______ ___________
|
3
|
+
# \ __\/ ___/ ____/ | ______ \____ \__ \\_ __ \/ ___// __ \_ __ \
|
4
|
+
# | | \___ < <_| | |__ /_____/ | |_> > __ \| | \/\___ \\ ___/| | \/
|
5
|
+
# |__| /____ >__ |____/ | __(____ /__| /____ >\___ >__|
|
6
|
+
# \/ |__| |__| \/ \/ \/
|
7
|
+
#
|
8
|
+
# A very light-weight and opinionated T-SQL parser and formatter.
|
9
|
+
#
|
10
|
+
# github.com/scstauf
|
11
|
+
#
|
12
|
+
# path:
|
13
|
+
# parsing/formatters/select_formatter.rb
|
14
|
+
# object:
|
15
|
+
# TSqlParser::Parsing::Formatters::SelectFormatter
|
16
|
+
|
17
|
+
module TSqlParser::Parsing::Formatters
|
18
|
+
require_relative "base_formatter"
|
19
|
+
|
20
|
+
class SelectFormatter < BaseFormatter
|
21
|
+
def self.format(text, tab = " ")
|
22
|
+
formatted = []
|
23
|
+
lines = text.split("\n")
|
24
|
+
lines.each do |line|
|
25
|
+
first = line.strip.split(" ").first
|
26
|
+
if first != "SELECT"
|
27
|
+
formatted << line
|
28
|
+
next
|
29
|
+
end
|
30
|
+
|
31
|
+
tab_count = self.get_tab_count(line, tab)
|
32
|
+
select_sql = line.strip[first.size + 1..]
|
33
|
+
new_select = self.format_select(select_sql, tab_count, tab)
|
34
|
+
if new_select.nil?
|
35
|
+
formatted << line
|
36
|
+
next
|
37
|
+
end
|
38
|
+
formatted << line.sub(select_sql, new_select)
|
39
|
+
end
|
40
|
+
formatted.join("\n")
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def self.format_select(s, tab_count = 0, tab = " ")
|
46
|
+
return s if s.nil?
|
47
|
+
|
48
|
+
tokens = s.split(", ")
|
49
|
+
"\n#{tokens.map { |t| "#{tab * (tab_count + 1)}#{t}" }.join(",\n")}"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,130 @@
|
|
1
|
+
# __ .__
|
2
|
+
# _/ |_ ___________| | ___________ _______ ______ ___________
|
3
|
+
# \ __\/ ___/ ____/ | ______ \____ \__ \\_ __ \/ ___// __ \_ __ \
|
4
|
+
# | | \___ < <_| | |__ /_____/ | |_> > __ \| | \/\___ \\ ___/| | \/
|
5
|
+
# |__| /____ >__ |____/ | __(____ /__| /____ >\___ >__|
|
6
|
+
# \/ |__| |__| \/ \/ \/
|
7
|
+
#
|
8
|
+
# A very light-weight and opinionated T-SQL parser and formatter.
|
9
|
+
#
|
10
|
+
# github.com/scstauf
|
11
|
+
#
|
12
|
+
# path:
|
13
|
+
# parsing/formatters/set_formatter.rb
|
14
|
+
# object:
|
15
|
+
# TSqlParser::Parsing::Formatters::SetFormatter
|
16
|
+
|
17
|
+
module TSqlParser::Parsing::Formatters
|
18
|
+
require_relative 'base_formatter'
|
19
|
+
|
20
|
+
class SetFormatter < BaseFormatter
|
21
|
+
def self.format(text, tab = " ")
|
22
|
+
formatted = []
|
23
|
+
lines = text.split("\n")
|
24
|
+
wait = false
|
25
|
+
set_lines = []
|
26
|
+
special_set_keywords = %w[ANSI_DEFAULTS ANSI_NULL_DFLT_OFF ANSI_NULL_DFLT_ON ANSI_NULLS ANSI_PADDING ANSI_WARNINGS ARITHABORT ARITHIGNORE CONCAT_NULL_YIELDS_NULL CURSOR_CLOSE_ON_COMMIT DATEFIRST DATEFORMAT DEADLOCK_PRIORITY FIPS_FLAGGER FMTONLY FORCEPLAN IDENTITY_INSERT IMPLICIT_TRANSACTIONS LANGUAGE LOCK_TIMEOUT NOCOUNT NOEXEC NUMERIC_ROUNDABORT OFFSETS PARSEONLY QUERY_GOVERNOR_COST_LIMIT QUOTED_IDENTIFIER REMOTE_PROC_TRANSACTIONS ROWCOUNT SHOWPLAN_ALL SHOWPLAN_TEXT SHOWPLAN_XML STATISTICS TEXTSIZE TRANSACTION XACT_ABORT]
|
27
|
+
|
28
|
+
lines.each do |line|
|
29
|
+
tokens = line.strip.split(" ")
|
30
|
+
first = tokens.first
|
31
|
+
next_token = tokens[1] if tokens.size > 1
|
32
|
+
|
33
|
+
if %w[FROM WHERE].include? first and wait
|
34
|
+
wait = false
|
35
|
+
tab_count = self.get_tab_count(line, tab)
|
36
|
+
set_text = set_lines.join("\n")
|
37
|
+
first = set_text.strip.split(" ").first
|
38
|
+
set = set_text.strip[first.size + 1..]
|
39
|
+
new_set = self.format_set(set, tab_count, tab)
|
40
|
+
if new_set.nil?
|
41
|
+
formatted << line
|
42
|
+
next
|
43
|
+
end
|
44
|
+
formatted << "#{tab * tab_count}SET #{new_set}"
|
45
|
+
formatted << line
|
46
|
+
set_lines = []
|
47
|
+
next
|
48
|
+
end
|
49
|
+
|
50
|
+
if first == "SET" and not line.strip.start_with? "SET @"
|
51
|
+
if not next_token.nil? and not special_set_keywords.include? next_token
|
52
|
+
wait = true
|
53
|
+
set_lines << line
|
54
|
+
else
|
55
|
+
formatted << line
|
56
|
+
formatted << ""
|
57
|
+
end
|
58
|
+
elsif first != "SET" and line.include? " SET "
|
59
|
+
parts = line.strip.split(" SET ")
|
60
|
+
tab_count = self.get_tab_count(line, tab)
|
61
|
+
formatted << "#{tab * tab_count}#{parts[0]}\n"
|
62
|
+
parts[1..].each { |p| formatted << "#{tab * tab_count}SET #{p}" }
|
63
|
+
elsif wait
|
64
|
+
set_lines << line
|
65
|
+
else
|
66
|
+
formatted << line
|
67
|
+
end
|
68
|
+
end
|
69
|
+
formatted.join("\n")
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def self.format_set(s, tab_count = 0, tab = " ")
|
75
|
+
return s if s.nil?
|
76
|
+
parts = []
|
77
|
+
builder = ""
|
78
|
+
parenthesis = 0
|
79
|
+
tokens = s.split("")
|
80
|
+
comment = false
|
81
|
+
skip_count = 0
|
82
|
+
tokens.each_with_index do |c, i|
|
83
|
+
if skip_count > 0
|
84
|
+
skip_count -= 1
|
85
|
+
next
|
86
|
+
end
|
87
|
+
|
88
|
+
if comment
|
89
|
+
if c == "\n"
|
90
|
+
comment = false
|
91
|
+
parts << builder unless builder.empty?
|
92
|
+
builder = ""
|
93
|
+
else
|
94
|
+
builder << c
|
95
|
+
end
|
96
|
+
next
|
97
|
+
end
|
98
|
+
|
99
|
+
parenthesis += 1 if c == "("
|
100
|
+
parenthesis -= 1 if c == ")"
|
101
|
+
next_c = tokens[i + 1] if tokens.size > i + 1
|
102
|
+
if c == ","
|
103
|
+
if parenthesis > 0
|
104
|
+
builder << c
|
105
|
+
else
|
106
|
+
parts << builder unless builder.empty?
|
107
|
+
builder = ""
|
108
|
+
end
|
109
|
+
elsif c == "\n"
|
110
|
+
parts << builder unless builder.empty?
|
111
|
+
builder = ""
|
112
|
+
elsif c == "-"
|
113
|
+
if next_c == "-"
|
114
|
+
comment = true
|
115
|
+
skip_count = 1
|
116
|
+
parts << builder unless builder.empty?
|
117
|
+
builder = "--"
|
118
|
+
else
|
119
|
+
builder << c
|
120
|
+
end
|
121
|
+
else
|
122
|
+
builder << c
|
123
|
+
end
|
124
|
+
end
|
125
|
+
parts << builder unless builder.empty?
|
126
|
+
parts = parts.map { |p| p.strip }.select { |p| not p.empty? }
|
127
|
+
"\n#{parts.map { |p| "#{tab * (tab_count + 1)}#{p.strip}" }.join(",\n")}"
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# __ .__
|
2
|
+
# _/ |_ ___________| | ___________ _______ ______ ___________
|
3
|
+
# \ __\/ ___/ ____/ | ______ \____ \__ \\_ __ \/ ___// __ \_ __ \
|
4
|
+
# | | \___ < <_| | |__ /_____/ | |_> > __ \| | \/\___ \\ ___/| | \/
|
5
|
+
# |__| /____ >__ |____/ | __(____ /__| /____ >\___ >__|
|
6
|
+
# \/ |__| |__| \/ \/ \/
|
7
|
+
#
|
8
|
+
# A very light-weight and opinionated T-SQL parser and formatter.
|
9
|
+
#
|
10
|
+
# github.com/scstauf
|
11
|
+
#
|
12
|
+
# path:
|
13
|
+
# parsing/formatters/update_formatter.rb
|
14
|
+
# object:
|
15
|
+
# TSqlParser::Parsing::Formatters::UpdateFormatter
|
16
|
+
|
17
|
+
module TSqlParser::Parsing::Formatters
|
18
|
+
require_relative "base_formatter"
|
19
|
+
|
20
|
+
class UpdateFormatter < BaseFormatter
|
21
|
+
def self.format(text, tab = " ")
|
22
|
+
formatted = []
|
23
|
+
lines = text.split("\n")
|
24
|
+
lines.each do |line|
|
25
|
+
first = line.strip.split(" ").first
|
26
|
+
if first != "UPDATE"
|
27
|
+
formatted << line
|
28
|
+
next
|
29
|
+
end
|
30
|
+
|
31
|
+
tab_count = self.get_tab_count(line, tab)
|
32
|
+
update = line.strip[first.size + 1..]
|
33
|
+
new_update = self.format_update(update, tab_count, tab)
|
34
|
+
if new_update.nil?
|
35
|
+
formatted << line
|
36
|
+
next
|
37
|
+
end
|
38
|
+
formatted << line.sub(update, new_update)
|
39
|
+
end
|
40
|
+
formatted.join("\n")
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def self.format_update(s, tab_count = 0, tab = " ")
|
46
|
+
return s if s.nil?
|
47
|
+
"\n#{tab * (tab_count + 1)}#{s}"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# __ .__
|
2
|
+
# _/ |_ ___________| | ___________ _______ ______ ___________
|
3
|
+
# \ __\/ ___/ ____/ | ______ \____ \__ \\_ __ \/ ___// __ \_ __ \
|
4
|
+
# | | \___ < <_| | |__ /_____/ | |_> > __ \| | \/\___ \\ ___/| | \/
|
5
|
+
# |__| /____ >__ |____/ | __(____ /__| /____ >\___ >__|
|
6
|
+
# \/ |__| |__| \/ \/ \/
|
7
|
+
#
|
8
|
+
# A very light-weight and opinionated T-SQL parser and formatter.
|
9
|
+
#
|
10
|
+
# github.com/scstauf
|
11
|
+
#
|
12
|
+
# path:
|
13
|
+
# parsing/formatters/where_formatter.rb
|
14
|
+
# object:
|
15
|
+
# TSqlParser::Parsing::Formatters::WhereFormatter
|
16
|
+
|
17
|
+
module TSqlParser::Parsing::Formatters
|
18
|
+
require_relative "base_formatter"
|
19
|
+
|
20
|
+
class WhereFormatter < BaseFormatter
|
21
|
+
def self.format(text, tab = " ")
|
22
|
+
formatted = []
|
23
|
+
text.split("\n").each do |line|
|
24
|
+
first = line.strip.split(" ").first
|
25
|
+
if first != "WHERE"
|
26
|
+
formatted << line
|
27
|
+
next
|
28
|
+
end
|
29
|
+
|
30
|
+
tab_count = self.get_tab_count(line, tab)
|
31
|
+
predicate = line.strip[first.size + 1..]
|
32
|
+
new_predicate = self.format_predicate(predicate, tab_count, tab)
|
33
|
+
if new_predicate.nil?
|
34
|
+
formatted << line
|
35
|
+
next
|
36
|
+
end
|
37
|
+
formatted << line.sub(predicate, new_predicate)
|
38
|
+
end
|
39
|
+
|
40
|
+
formatted.join("\n")
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def self.format_predicate(s, tab_count = 0, tab = " ")
|
46
|
+
return s if s.nil?
|
47
|
+
indented = []
|
48
|
+
formatted = []
|
49
|
+
builder = []
|
50
|
+
|
51
|
+
tokens = s.split(" ")
|
52
|
+
tokens.each do |t|
|
53
|
+
if %w[AND OR].include? t
|
54
|
+
formatted << builder.join(" ") unless builder.empty?
|
55
|
+
builder = [t]
|
56
|
+
else
|
57
|
+
builder << t
|
58
|
+
end
|
59
|
+
end
|
60
|
+
formatted << builder.join(" ")
|
61
|
+
|
62
|
+
level = tab_count
|
63
|
+
formatted.each_with_index do |f, i|
|
64
|
+
indented << "#{tab * (level + 1)}#{f}"
|
65
|
+
level -= f.count(")")
|
66
|
+
level += f.count("(")
|
67
|
+
end
|
68
|
+
|
69
|
+
"\n#{indented.join("\n")}"
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# __ .__
|
2
|
+
# _/ |_ ___________| | ___________ _______ ______ ___________
|
3
|
+
# \ __\/ ___/ ____/ | ______ \____ \__ \\_ __ \/ ___// __ \_ __ \
|
4
|
+
# | | \___ < <_| | |__ /_____/ | |_> > __ \| | \/\___ \\ ___/| | \/
|
5
|
+
# |__| /____ >__ |____/ | __(____ /__| /____ >\___ >__|
|
6
|
+
# \/ |__| |__| \/ \/ \/
|
7
|
+
#
|
8
|
+
# A very light-weight and opinionated T-SQL parser and formatter.
|
9
|
+
#
|
10
|
+
# github.com/scstauf
|
11
|
+
#
|
12
|
+
# path:
|
13
|
+
# parsing/formatter.rb
|
14
|
+
# object:
|
15
|
+
# TSqlParser::Parsing::Formatter
|
16
|
+
|
17
|
+
module TSqlParser::Parsing
|
18
|
+
require_relative 'strategy/set_formatter'
|
19
|
+
require_relative 'strategy/join_formatter'
|
20
|
+
require_relative 'strategy/insert_formatter'
|
21
|
+
require_relative 'strategy/select_formatter'
|
22
|
+
require_relative 'strategy/update_formatter'
|
23
|
+
require_relative 'strategy/where_formatter'
|
24
|
+
|
25
|
+
class TextFormatter
|
26
|
+
def self.format_sets(text, tab = " ")
|
27
|
+
Formatters::SetFormatter.format(text, tab)
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.format_joins(text, tab = " ")
|
31
|
+
Formatters::JoinFormatter.format(text, tab)
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.format_updates(text, tab = " ")
|
35
|
+
Formatters::UpdateFormatter.format(text, tab)
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.format_inserts(text, tab = " ")
|
39
|
+
Formatters::InsertFormatter.format(text, tab)
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.format_selects(text, tab = " ")
|
43
|
+
Formatters::SelectFormatter.format(text, tab)
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.format_wheres(text, tab = " ")
|
47
|
+
Formatters::WhereFormatter.format(text, tab)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
data/lib/parsing/tokenizer.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tsql_parser
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Scott Stauffer
|
@@ -17,12 +17,19 @@ extensions: []
|
|
17
17
|
extra_rdoc_files: []
|
18
18
|
files:
|
19
19
|
- lib/parsing/formatter.rb
|
20
|
+
- lib/parsing/formatters/strategy/base_formatter.rb
|
21
|
+
- lib/parsing/formatters/strategy/insert_formatter.rb
|
22
|
+
- lib/parsing/formatters/strategy/join_formatter.rb
|
23
|
+
- lib/parsing/formatters/strategy/select_formatter.rb
|
24
|
+
- lib/parsing/formatters/strategy/set_formatter.rb
|
25
|
+
- lib/parsing/formatters/strategy/update_formatter.rb
|
26
|
+
- lib/parsing/formatters/strategy/where_formatter.rb
|
27
|
+
- lib/parsing/formatters/text_formatter.rb
|
20
28
|
- lib/parsing/iterator.rb
|
21
29
|
- lib/parsing/keyword.rb
|
22
30
|
- lib/parsing/model/flat_sql_container.rb
|
23
31
|
- lib/parsing/model/sql_container.rb
|
24
32
|
- lib/parsing/parser.rb
|
25
|
-
- lib/parsing/text_formatter.rb
|
26
33
|
- lib/parsing/tokenizer.rb
|
27
34
|
- lib/tsql_parser.rb
|
28
35
|
homepage: https://rubygems.org/gems/tsql_parser
|
@@ -1,250 +0,0 @@
|
|
1
|
-
# __ .__
|
2
|
-
# _/ |_ ___________| | ___________ _______ ______ ___________
|
3
|
-
# \ __\/ ___/ ____/ | ______ \____ \__ \\_ __ \/ ___// __ \_ __ \
|
4
|
-
# | | \___ < <_| | |__ /_____/ | |_> > __ \| | \/\___ \\ ___/| | \/
|
5
|
-
# |__| /____ >__ |____/ | __(____ /__| /____ >\___ >__|
|
6
|
-
# \/ |__| |__| \/ \/ \/
|
7
|
-
#
|
8
|
-
# A very light-weight and opinionated T-SQL parser and formatter.
|
9
|
-
#
|
10
|
-
# github.com/scstauf
|
11
|
-
#
|
12
|
-
# path:
|
13
|
-
# parsing/formatter.rb
|
14
|
-
# object:
|
15
|
-
# TSqlParser::Parsing::Formatter
|
16
|
-
|
17
|
-
module TSqlParser::Parsing
|
18
|
-
class TextFormatter
|
19
|
-
def self.format_sets(text, tab = " ")
|
20
|
-
formatted = []
|
21
|
-
lines = text.split("\n")
|
22
|
-
lines.each do |line|
|
23
|
-
first = line.strip.split(" ").first
|
24
|
-
if first == "SET" and not line.strip.start_with? "SET @"
|
25
|
-
tab_count = self.get_tab_count(line, tab)
|
26
|
-
set = line.strip[first.size + 1..]
|
27
|
-
new_set = self.format_set(set, tab_count, tab)
|
28
|
-
if new_set.nil?
|
29
|
-
formatted << line
|
30
|
-
next
|
31
|
-
end
|
32
|
-
formatted << line.sub(set, new_set)
|
33
|
-
elsif first != "SET" and line.include? " SET "
|
34
|
-
parts = line.strip.split(" SET ")
|
35
|
-
tab_count = self.get_tab_count(line, tab)
|
36
|
-
formatted << "#{tab * tab_count}#{parts[0]}\n"
|
37
|
-
parts[1..].each { |p| formatted << "#{tab * tab_count}SET #{p}" }
|
38
|
-
else
|
39
|
-
formatted << line
|
40
|
-
end
|
41
|
-
end
|
42
|
-
formatted.join("\n")
|
43
|
-
end
|
44
|
-
|
45
|
-
def self.format_joins(text, tab = " ")
|
46
|
-
text = text.gsub(/INNER\s+JOIN/, "INNER JOIN")
|
47
|
-
.gsub(/LEFT\s+JOIN/, "LEFT JOIN")
|
48
|
-
lines = text.split("\n")
|
49
|
-
new_text = []
|
50
|
-
|
51
|
-
lines.each do |line|
|
52
|
-
first = line.strip.split(" ").first
|
53
|
-
if line.include? " WHERE " and first != "WHERE" and not first.start_with? "--" and not first.start_with? "/*"
|
54
|
-
tab_count = self.get_tab_count(line, tab)
|
55
|
-
where_parts = line.strip.split(" WHERE ")
|
56
|
-
where_text = []
|
57
|
-
where_text << "#{tab * tab_count}#{where_parts[0]}"
|
58
|
-
where_text << "#{tab * tab_count}WHERE #{where_parts[1]}"
|
59
|
-
new_text << where_text.join("\n")
|
60
|
-
else
|
61
|
-
new_text << line
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
new_text.join("\n")
|
66
|
-
end
|
67
|
-
|
68
|
-
def self.format_updates(text, tab = " ")
|
69
|
-
formatted = []
|
70
|
-
lines = text.split("\n")
|
71
|
-
lines.each do |line|
|
72
|
-
first = line.strip.split(" ").first
|
73
|
-
if first != "UPDATE"
|
74
|
-
formatted << line
|
75
|
-
next
|
76
|
-
end
|
77
|
-
|
78
|
-
tab_count = self.get_tab_count(line, tab)
|
79
|
-
update = line.strip[first.size + 1..]
|
80
|
-
new_update = self.format_update(update, tab_count, tab)
|
81
|
-
if new_update.nil?
|
82
|
-
formatted << line
|
83
|
-
next
|
84
|
-
end
|
85
|
-
formatted << line.sub(update, new_update)
|
86
|
-
end
|
87
|
-
formatted.join("\n")
|
88
|
-
end
|
89
|
-
|
90
|
-
def self.format_inserts(text, tab = " ")
|
91
|
-
formatted = []
|
92
|
-
lines = text.split("\n")
|
93
|
-
search = "INSERT INTO"
|
94
|
-
lines.each do |line|
|
95
|
-
first = line.strip.split(" ").first
|
96
|
-
if first != "INSERT"
|
97
|
-
formatted << line
|
98
|
-
next
|
99
|
-
end
|
100
|
-
|
101
|
-
tab_count = self.get_tab_count(line, tab)
|
102
|
-
insert = line.strip[search.size + 1..]
|
103
|
-
new_insert = self.format_insert(insert, tab_count, tab)
|
104
|
-
if new_insert.nil?
|
105
|
-
formatted << line
|
106
|
-
next
|
107
|
-
end
|
108
|
-
formatted << line.sub(insert, new_insert)
|
109
|
-
end
|
110
|
-
formatted.join("\n")
|
111
|
-
end
|
112
|
-
|
113
|
-
def self.format_selects(text, tab = " ")
|
114
|
-
formatted = []
|
115
|
-
lines = text.split("\n")
|
116
|
-
lines.each do |line|
|
117
|
-
first = line.strip.split(" ").first
|
118
|
-
if first != "SELECT"
|
119
|
-
formatted << line
|
120
|
-
next
|
121
|
-
end
|
122
|
-
|
123
|
-
tab_count = self.get_tab_count(line, tab)
|
124
|
-
select_sql = line.strip[first.size + 1..]
|
125
|
-
new_select = self.format_select(select_sql, tab_count, tab)
|
126
|
-
if new_select.nil?
|
127
|
-
formatted << line
|
128
|
-
next
|
129
|
-
end
|
130
|
-
formatted << line.sub(select_sql, new_select)
|
131
|
-
end
|
132
|
-
formatted.join("\n")
|
133
|
-
end
|
134
|
-
|
135
|
-
def self.format_wheres(text, tab = " ")
|
136
|
-
formatted = []
|
137
|
-
text.split("\n").each do |line|
|
138
|
-
first = line.strip.split(" ").first
|
139
|
-
if first != "WHERE"
|
140
|
-
formatted << line
|
141
|
-
next
|
142
|
-
end
|
143
|
-
|
144
|
-
tab_count = self.get_tab_count(line, tab)
|
145
|
-
predicate = line.strip[first.size + 1..]
|
146
|
-
new_predicate = self.format_predicate(predicate, tab_count, tab)
|
147
|
-
if new_predicate.nil?
|
148
|
-
formatted << line
|
149
|
-
next
|
150
|
-
end
|
151
|
-
formatted << line.sub(predicate, new_predicate)
|
152
|
-
end
|
153
|
-
|
154
|
-
formatted.join("\n")
|
155
|
-
end
|
156
|
-
|
157
|
-
private
|
158
|
-
|
159
|
-
def self.format_set(s, tab_count = 0, tab = " ")
|
160
|
-
return s if s.nil?
|
161
|
-
parts = []
|
162
|
-
builder = ""
|
163
|
-
parenthesis = 0
|
164
|
-
s.split("").each do |c|
|
165
|
-
parenthesis += 1 if c == "("
|
166
|
-
parenthesis -= 1 if c == ")"
|
167
|
-
if c == ","
|
168
|
-
if parenthesis > 0
|
169
|
-
builder << c
|
170
|
-
else
|
171
|
-
parts << builder
|
172
|
-
builder = ""
|
173
|
-
end
|
174
|
-
else
|
175
|
-
builder << c
|
176
|
-
end
|
177
|
-
end
|
178
|
-
parts << builder unless builder.empty?
|
179
|
-
"\n#{parts.map { |p| "#{tab * (tab_count + 1)}#{p.strip}" }.join(",\n")}"
|
180
|
-
end
|
181
|
-
|
182
|
-
def self.format_update(s, tab_count = 0, tab = " ")
|
183
|
-
return s if s.nil?
|
184
|
-
"\n#{tab * (tab_count + 1)}#{s}"
|
185
|
-
end
|
186
|
-
|
187
|
-
def self.format_insert(s, tab_count = 0, tab = " ")
|
188
|
-
return s if s.nil?
|
189
|
-
formatted = []
|
190
|
-
if s.include? ") VALUES ("
|
191
|
-
tokens = s.split(") VALUES (")
|
192
|
-
table = tokens[0][..tokens[0].index("(") - 2]
|
193
|
-
columns = tokens[0][tokens[0].index("(") + 1..]
|
194
|
-
values = tokens[1][..-2]
|
195
|
-
formatted << "\n#{tab * (tab_count + 1)}#{table}"
|
196
|
-
formatted << "#{tab * (tab_count + 2)}(#{columns})"
|
197
|
-
formatted << "#{tab * (tab_count + 1)}VALUES"
|
198
|
-
if s.end_with? ");"
|
199
|
-
formatted << "#{tab * (tab_count + 2)}(#{values}"
|
200
|
-
else
|
201
|
-
formatted << "#{tab * (tab_count + 2)}(#{values})"
|
202
|
-
end
|
203
|
-
end
|
204
|
-
formatted.join("\n") unless formatted.empty?
|
205
|
-
end
|
206
|
-
|
207
|
-
def self.format_select(s, tab_count = 0, tab = " ")
|
208
|
-
return s if s.nil?
|
209
|
-
|
210
|
-
tokens = s.split(", ")
|
211
|
-
"\n#{tokens.map { |t| "#{tab * (tab_count + 1)}#{t}" }.join(",\n")}"
|
212
|
-
end
|
213
|
-
|
214
|
-
def self.format_predicate(s, tab_count = 0, tab = " ")
|
215
|
-
return s if s.nil?
|
216
|
-
indented = []
|
217
|
-
formatted = []
|
218
|
-
builder = []
|
219
|
-
|
220
|
-
tokens = s.split(" ")
|
221
|
-
tokens.each do |t|
|
222
|
-
if %w[AND OR].include? t
|
223
|
-
formatted << builder.join(" ") unless builder.empty?
|
224
|
-
builder = [t]
|
225
|
-
else
|
226
|
-
builder << t
|
227
|
-
end
|
228
|
-
end
|
229
|
-
formatted << builder.join(" ")
|
230
|
-
|
231
|
-
level = tab_count
|
232
|
-
formatted.each_with_index do |f, i|
|
233
|
-
indented << "#{tab * (level + 1)}#{f}"
|
234
|
-
level -= f.count(")")
|
235
|
-
level += f.count("(")
|
236
|
-
end
|
237
|
-
|
238
|
-
"\n#{indented.join("\n")}"
|
239
|
-
end
|
240
|
-
|
241
|
-
def self.get_tab_count(line, tab = " ")
|
242
|
-
tab_count = 0
|
243
|
-
while line.start_with? tab
|
244
|
-
tab_count += 1
|
245
|
-
line = line.sub(tab, "")
|
246
|
-
end
|
247
|
-
tab_count
|
248
|
-
end
|
249
|
-
end
|
250
|
-
end
|