sqlstmt 0.2.8 → 0.2.9
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/sqlstmt/build.rb +10 -0
- data/lib/sqlstmt/{to_sql.rb → core_to_sql.rb} +0 -0
- data/lib/sqlstmt/data.rb +58 -0
- data/lib/sqlstmt/mysql/build.rb +105 -0
- data/lib/sqlstmt/mysql/check.rb +50 -0
- data/lib/sqlstmt/sqlstmt.rb +71 -217
- data/lib/sqlstmt.rb +2 -0
- data/test/data_test.rb +17 -0
- metadata +14 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a699b087738e5ba85981e2b914a3b9538ef09d3b
|
4
|
+
data.tar.gz: d684bc830ce3a0750954c336890de5ebd594a87e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 715b1603957d23a2e6f13d89e55fe45a9a46eaf8fcb5687b8ed84ae837098982e621fc042d9704784ddd28641d0d6a3fc1401f788612df8e080d8616fd9f25ed
|
7
|
+
data.tar.gz: 2e1dd28397d42d82ecb81396f156e4d2325f118167084d831de17d35f21376e8b4360f9dc9715323c1733dd0ce6f060f597e5c9743b060fc26fc3ecd3523737a
|
File without changes
|
data/lib/sqlstmt/data.rb
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
module SqlStmtLib
|
2
|
+
extend self
|
3
|
+
|
4
|
+
SqlTable = Struct.new(:str, :name, :alias, :index)
|
5
|
+
|
6
|
+
SqlData = Struct.new(
|
7
|
+
:stmt_type,
|
8
|
+
:tables,
|
9
|
+
:joins,
|
10
|
+
:wheres,
|
11
|
+
:where_behavior,
|
12
|
+
:fields,
|
13
|
+
:values,
|
14
|
+
:group_by,
|
15
|
+
:order_by,
|
16
|
+
:limit,
|
17
|
+
:having,
|
18
|
+
:into_table,
|
19
|
+
:rows,
|
20
|
+
:tables_to_delete,
|
21
|
+
:distinct,
|
22
|
+
:straight_join,
|
23
|
+
:replace,
|
24
|
+
:ignore,
|
25
|
+
:outfile,
|
26
|
+
:with_rollup,
|
27
|
+
:called_get,
|
28
|
+
) do
|
29
|
+
def initialize
|
30
|
+
self.tables = []
|
31
|
+
self.joins = []
|
32
|
+
self.wheres = []
|
33
|
+
self.where_behavior = :require
|
34
|
+
self.fields = []
|
35
|
+
self.values = []
|
36
|
+
self.having = []
|
37
|
+
self.rows = []
|
38
|
+
self.tables_to_delete = []
|
39
|
+
self.ignore = ''
|
40
|
+
self.outfile = ''
|
41
|
+
self.called_get = false
|
42
|
+
end
|
43
|
+
|
44
|
+
def initialize_copy(orig)
|
45
|
+
# without this call to super, any field that we aren't dup'ing here won't be copied
|
46
|
+
super
|
47
|
+
self.tables = orig.tables.dup
|
48
|
+
self.joins = orig.joins.dup
|
49
|
+
self.wheres = orig.wheres.dup
|
50
|
+
self.fields = orig.fields.dup
|
51
|
+
self.values = orig.values.dup
|
52
|
+
self.having = orig.having.dup
|
53
|
+
self.rows = orig.rows.dup
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
module SqlStmtLib
|
2
|
+
extend self
|
3
|
+
|
4
|
+
class MysqlBuilder
|
5
|
+
def initialize(data)
|
6
|
+
@data = data
|
7
|
+
end
|
8
|
+
|
9
|
+
def build_stmt
|
10
|
+
method_name = "build_stmt_#{@data.stmt_type}"
|
11
|
+
return send(method_name)
|
12
|
+
end
|
13
|
+
|
14
|
+
def build_stmt_select
|
15
|
+
straight_join_str = @data.straight_join ? 'STRAIGHT_JOIN ' : ''
|
16
|
+
distinct_str = @data.distinct ? 'DISTINCT ' : ''
|
17
|
+
select_str = @data.fields.join(',')
|
18
|
+
return "SELECT #{straight_join_str}#{distinct_str}#{select_str}#{build_from_clause}#{@data.outfile}"
|
19
|
+
end
|
20
|
+
|
21
|
+
def build_stmt_update
|
22
|
+
limit_clause = simple_clause('LIMIT', @data.limit)
|
23
|
+
return "UPDATE #{build_table_list}#{build_join_clause} SET #{build_set_clause}#{build_where_clause}#{limit_clause}"
|
24
|
+
end
|
25
|
+
|
26
|
+
def build_stmt_insert
|
27
|
+
if !@data.fields.empty? && !@data.rows.empty?
|
28
|
+
raise "unable to use INSERT SELECT and INSERT VALUES together, may only call :set or :add_row, but not both"
|
29
|
+
end
|
30
|
+
|
31
|
+
keyword = @data.replace ? 'REPLACE' : 'INSERT'
|
32
|
+
value_list = @data.values.join(',')
|
33
|
+
start_str = "#{keyword} #{@data.ignore}INTO #{@data.into_table} "
|
34
|
+
if !@data.fields.empty?
|
35
|
+
field_list = @data.fields.join(',')
|
36
|
+
start_str += "(#{field_list}) "
|
37
|
+
end
|
38
|
+
|
39
|
+
if @data.rows.empty?
|
40
|
+
distinct_str = @data.distinct ? 'DISTINCT ' : ''
|
41
|
+
return "#{start_str}SELECT #{distinct_str}#{value_list}#{build_from_clause}"
|
42
|
+
else
|
43
|
+
raise "DISTINCT not supported when inserting values" if @data.distinct
|
44
|
+
return "#{start_str}VALUES (#{value_list})"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def build_stmt_delete
|
49
|
+
if @data.tables_to_delete.empty?
|
50
|
+
table_clause = ''
|
51
|
+
else
|
52
|
+
table_clause = ' ' + @data.tables_to_delete.join(',')
|
53
|
+
end
|
54
|
+
return "DELETE#{table_clause}#{build_from_clause}"
|
55
|
+
end
|
56
|
+
|
57
|
+
def build_from_clause
|
58
|
+
join_clause = build_join_clause
|
59
|
+
group_clause = simple_clause('GROUP BY', @data.group_by)
|
60
|
+
if @data.with_rollup
|
61
|
+
group_clause += ' WITH ROLLUP'
|
62
|
+
end
|
63
|
+
order_clause = simple_clause('ORDER BY', @data.order_by)
|
64
|
+
limit_clause = simple_clause('LIMIT', @data.limit)
|
65
|
+
having_clause = @data.having.empty? ? '' : " HAVING #{@data.having.join(' AND ')}"
|
66
|
+
return " FROM #{build_table_list}#{join_clause}#{build_where_clause}#{group_clause}#{having_clause}#{order_clause}#{limit_clause}"
|
67
|
+
end
|
68
|
+
|
69
|
+
def build_set_clause
|
70
|
+
set_exprs = []
|
71
|
+
@data.fields.each_with_index do |field, index|
|
72
|
+
set_exprs << "#{field} = #{@data.values[index]}"
|
73
|
+
end
|
74
|
+
return set_exprs.join(', ')
|
75
|
+
end
|
76
|
+
|
77
|
+
def table_to_str(table)
|
78
|
+
if table.index
|
79
|
+
return "#{table.str} USE INDEX (#{table.index})"
|
80
|
+
end
|
81
|
+
return table.str
|
82
|
+
end
|
83
|
+
|
84
|
+
def build_table_list
|
85
|
+
return @data.tables.map {|table| table_to_str(table) }.join(',')
|
86
|
+
end
|
87
|
+
|
88
|
+
def simple_clause(keywords, value)
|
89
|
+
return value ? " #{keywords} #{value}" : ''
|
90
|
+
end
|
91
|
+
|
92
|
+
def build_join_clause
|
93
|
+
if @data.joins.empty?
|
94
|
+
return ''
|
95
|
+
else
|
96
|
+
return ' ' + @data.joins.map {|ary| ary.join(' ')}.uniq.join(' ')
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def build_where_clause
|
101
|
+
return @data.wheres.empty? ? '' : " WHERE #{@data.wheres.join(' AND ')}"
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'sqlstmt/error'
|
2
|
+
|
3
|
+
module SqlStmtLib
|
4
|
+
extend self
|
5
|
+
|
6
|
+
class MysqlChecker
|
7
|
+
def initialize(data)
|
8
|
+
@data = data
|
9
|
+
end
|
10
|
+
|
11
|
+
def run
|
12
|
+
if !@data.stmt_type
|
13
|
+
raise SqlStmtError, "unable to build sql - must call :select, :update, :insert or :delete to specify statement type"
|
14
|
+
end
|
15
|
+
if @data.tables.empty?
|
16
|
+
raise SqlStmtError, "unable to build sql - must call :table"
|
17
|
+
end
|
18
|
+
|
19
|
+
if (@data.where_behavior == :require) && @data.wheres.empty?
|
20
|
+
raise SqlStmtError, "unable to build sql - must call :where, :no_where, or :optional_where"
|
21
|
+
elsif (@data.where_behavior == :exclude) && !@data.wheres.empty?
|
22
|
+
raise SqlStmtError, "unable to build sql - :where and :no_where must not both be called, consider :optional_where instead"
|
23
|
+
end
|
24
|
+
|
25
|
+
if @data.stmt_type == 'select'
|
26
|
+
raise SqlStmtError, "unable to build sql - must call :get" if @data.fields.empty?
|
27
|
+
raise SqlStmtError, "unable to build sql - must not call :set" if !@data.values.empty?
|
28
|
+
else
|
29
|
+
raise SqlStmtError, "unable to build sql - must not call :get" if @data.called_get
|
30
|
+
end
|
31
|
+
|
32
|
+
if ['update','insert'].include?(@data.stmt_type)
|
33
|
+
raise SqlStmtError, "unable to build sql - must call :set or :setq" if @data.values.empty?
|
34
|
+
raise SqlStmtError, "unable to build sql - must not call :get" if @data.called_get
|
35
|
+
end
|
36
|
+
|
37
|
+
if @data.stmt_type == 'insert'
|
38
|
+
raise SqlStmtError, "unable to build sql - must call :into" if @data.into_table.nil?
|
39
|
+
end
|
40
|
+
|
41
|
+
if @data.stmt_type == 'delete'
|
42
|
+
raise SqlStmtError, "unable to build sql - must not call :get or :set" if !@data.fields.empty?
|
43
|
+
if @data.tables_to_delete.empty? && ((@data.tables.size + @data.joins.size) > 1)
|
44
|
+
raise SqlStmtError, "unable to build sql - must specify tables to delete when including multiple tables"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
data/lib/sqlstmt/sqlstmt.rb
CHANGED
@@ -1,70 +1,63 @@
|
|
1
|
+
require 'sqlstmt/data'
|
1
2
|
require 'sqlstmt/error'
|
2
|
-
require 'sqlstmt/to_sql'
|
3
3
|
|
4
|
+
# in looking at the implementation, it could be asked:
|
5
|
+
# why are there not individual classes for each statement type?
|
6
|
+
# and indeed, it does seem a natural fit, and the first version was built that way
|
7
|
+
# however, this meant that the statement type had to be determined at object creation time
|
8
|
+
# and for some cases this was a limitation and in general went against the purposes of this library
|
9
|
+
# namely, to build the statement gradually and in no particular order, even the statement type
|
10
|
+
# for example, we might build a statement and add a where clause to it
|
11
|
+
# and some step later on would determine the statement type
|
12
|
+
# also, looking to the future of supporting other dialects of SQL, I think the same will be true there
|
13
|
+
# meaning, we don't the choice of SQL dialect to be allowed at any time
|
14
|
+
|
15
|
+
# unless there is something better to return, all methods return self so they can be chained together
|
4
16
|
class SqlStmt
|
5
|
-
attr_reader :
|
6
|
-
Table = Struct.new(:str, :name, :alias, :index)
|
17
|
+
attr_reader :data
|
7
18
|
|
8
19
|
def initialize
|
9
|
-
@
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
@
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
@
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
@
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
@
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
@
|
31
|
-
end
|
32
|
-
|
33
|
-
def initialize_copy(orig)
|
34
|
-
super
|
35
|
-
@tables = @tables.dup
|
36
|
-
@joins = @joins.dup
|
37
|
-
@wheres = @wheres.dup
|
38
|
-
@fields = @fields.dup
|
39
|
-
@values = @values.dup
|
40
|
-
@having = @having.dup
|
41
|
-
@rows = @rows.dup
|
20
|
+
@data = SqlStmtLib::SqlData.new
|
21
|
+
end
|
22
|
+
|
23
|
+
def initialize_copy(_orig)
|
24
|
+
@data = @data.dup
|
25
|
+
end
|
26
|
+
|
27
|
+
###### temporary transition methods
|
28
|
+
def fields
|
29
|
+
return @data.fields
|
30
|
+
end
|
31
|
+
|
32
|
+
def tables
|
33
|
+
return @data.tables
|
34
|
+
end
|
35
|
+
|
36
|
+
def joins
|
37
|
+
return @data.joins
|
38
|
+
end
|
39
|
+
|
40
|
+
def wheres
|
41
|
+
return @data.wheres
|
42
42
|
end
|
43
43
|
|
44
44
|
###### pick statement type
|
45
45
|
|
46
46
|
def select
|
47
|
-
|
48
|
-
@stmt_type = 'select'
|
49
|
-
return self
|
47
|
+
return set_statement_type('select')
|
50
48
|
end
|
51
49
|
|
52
50
|
def update
|
53
|
-
|
54
|
-
@stmt_type = 'update'
|
55
|
-
return self
|
51
|
+
return set_statement_type('update')
|
56
52
|
end
|
57
53
|
|
58
54
|
def insert
|
59
|
-
|
60
|
-
@stmt_type = 'insert'
|
61
|
-
return self
|
55
|
+
return set_statement_type('insert')
|
62
56
|
end
|
63
57
|
|
64
58
|
def delete(*tables)
|
65
|
-
|
66
|
-
@
|
67
|
-
@tables_to_delete = tables
|
59
|
+
set_statement_type('delete')
|
60
|
+
@data.tables_to_delete = tables
|
68
61
|
return self
|
69
62
|
end
|
70
63
|
|
@@ -72,8 +65,8 @@ class SqlStmt
|
|
72
65
|
|
73
66
|
def table(table_str, use_index = nil)
|
74
67
|
parts = table_str.split(' ')
|
75
|
-
table_obj =
|
76
|
-
@tables << table_obj
|
68
|
+
table_obj = SqlStmtLib::SqlTable.new(table_str, parts[0], parts[1], use_index)
|
69
|
+
@data.tables << table_obj
|
77
70
|
return self
|
78
71
|
end
|
79
72
|
|
@@ -86,35 +79,35 @@ class SqlStmt
|
|
86
79
|
end
|
87
80
|
|
88
81
|
def where(*expr)
|
89
|
-
@wheres.concat(expr)
|
82
|
+
@data.wheres.concat(expr)
|
90
83
|
return self
|
91
84
|
end
|
92
85
|
|
93
86
|
def no_where
|
94
|
-
@where_behavior = :exclude
|
87
|
+
@data.where_behavior = :exclude
|
95
88
|
return self
|
96
89
|
end
|
97
90
|
|
98
91
|
def optional_where
|
99
|
-
@where_behavior = :optional
|
92
|
+
@data.where_behavior = :optional
|
100
93
|
return self
|
101
94
|
end
|
102
95
|
|
103
96
|
def get(*exprs)
|
104
|
-
@fields.concat(exprs)
|
105
|
-
@called_get = true
|
97
|
+
@data.fields.concat(exprs)
|
98
|
+
@data.called_get = true
|
106
99
|
return self
|
107
100
|
end
|
108
101
|
|
109
102
|
def set(field, value)
|
110
|
-
raise "trying to include field #{field} again" if @fields.include?(field)
|
103
|
+
raise "trying to include field #{field} again" if @data.fields.include?(field)
|
111
104
|
# this is to support the special case of INSERT INTO table SELECT * FROM ...
|
112
105
|
# where * specified with no matching insert field list specified
|
113
106
|
if field
|
114
|
-
@fields << field
|
107
|
+
@data.fields << field
|
115
108
|
end
|
116
109
|
value = value.is_a?(String) ? value : value.to_sql
|
117
|
-
@values << value
|
110
|
+
@data.values << value
|
118
111
|
return self
|
119
112
|
end
|
120
113
|
|
@@ -123,90 +116,86 @@ class SqlStmt
|
|
123
116
|
end
|
124
117
|
|
125
118
|
def group_by(expr)
|
126
|
-
@group_by = expr
|
119
|
+
@data.group_by = expr
|
127
120
|
return self
|
128
121
|
end
|
129
122
|
|
130
123
|
def order_by(expr)
|
131
|
-
@order_by = expr
|
124
|
+
@data.order_by = expr
|
132
125
|
return self
|
133
126
|
end
|
134
127
|
|
135
128
|
def limit(clause)
|
136
|
-
@limit = clause
|
129
|
+
@data.limit = clause
|
137
130
|
return self
|
138
131
|
end
|
139
132
|
|
140
133
|
def having(*expr)
|
141
|
-
@having.concat(expr)
|
134
|
+
@data.having.concat(expr)
|
142
135
|
return self
|
143
136
|
end
|
144
137
|
|
145
138
|
def with_rollup
|
146
|
-
@with_rollup = true
|
139
|
+
@data.with_rollup = true
|
147
140
|
return self
|
148
141
|
end
|
149
142
|
|
150
143
|
# used with INSERT statements only
|
151
144
|
def into(into_table)
|
152
|
-
@into_table = into_table
|
145
|
+
@data.into_table = into_table
|
153
146
|
return self
|
154
147
|
end
|
155
148
|
|
156
149
|
# used with INSERT VALUES statements only
|
157
150
|
def add_row(row)
|
158
|
-
@rows << row
|
151
|
+
@data.rows << row
|
159
152
|
end
|
160
153
|
|
161
|
-
def to_s
|
162
|
-
verify_minimum_requirements
|
163
|
-
return build_stmt
|
164
|
-
end
|
165
|
-
alias_method :to_sql, :to_s
|
166
|
-
|
167
154
|
###### less commonly used methods
|
168
155
|
|
169
156
|
def distinct
|
170
|
-
@distinct = true
|
157
|
+
@data.distinct = true
|
171
158
|
return self
|
172
159
|
end
|
173
160
|
|
174
161
|
def straight_join
|
175
|
-
@straight_join = true
|
162
|
+
@data.straight_join = true
|
176
163
|
return self
|
177
164
|
end
|
178
165
|
|
179
166
|
def replace
|
180
|
-
@replace = true
|
167
|
+
@data.replace = true
|
181
168
|
return self
|
182
169
|
end
|
183
170
|
|
184
171
|
def ignore
|
185
|
-
@ignore = 'IGNORE '
|
172
|
+
@data.ignore = 'IGNORE '
|
186
173
|
return self
|
187
174
|
end
|
188
175
|
|
189
176
|
def outfile(str)
|
190
|
-
@outfile = " INTO OUTFILE #{str}"
|
177
|
+
@data.outfile = " INTO OUTFILE #{str}"
|
191
178
|
return self
|
192
179
|
end
|
193
180
|
|
194
181
|
###### methods to analyze what the statement contains
|
195
182
|
def includes_table?(table_to_find)
|
196
|
-
retval = @tables.find { |table| (table.name == table_to_find) || (table.alias == table_to_find) }
|
197
|
-
retval ||= @joins.find { |_, table, _| table_names_match?(table, table_to_find) }
|
183
|
+
retval = @data.tables.find { |table| (table.name == table_to_find) || (table.alias == table_to_find) }
|
184
|
+
retval ||= @data.joins.find { |_, table, _| table_names_match?(table, table_to_find) }
|
198
185
|
return retval
|
199
186
|
end
|
200
187
|
|
201
188
|
private
|
202
|
-
def
|
203
|
-
if @stmt_type
|
204
|
-
raise "statement type already set to #{@stmt_type}"
|
189
|
+
def set_statement_type(stmt_type)
|
190
|
+
if @data.stmt_type
|
191
|
+
raise "statement type already set to #{@data.stmt_type}"
|
205
192
|
end
|
193
|
+
@data.stmt_type = stmt_type
|
194
|
+
return self
|
206
195
|
end
|
207
196
|
|
208
197
|
def add_join(keyword, table, exprs)
|
209
|
-
@joins << [keyword, table, "ON #{exprs.join(' AND ')}"]
|
198
|
+
@data.joins << [keyword, table, "ON #{exprs.join(' AND ')}"]
|
210
199
|
return self
|
211
200
|
end
|
212
201
|
|
@@ -217,139 +206,4 @@ private
|
|
217
206
|
orig_name, _, tblas = fullstr.partition(' ')
|
218
207
|
return (orig_name == tofind) || (tblas == tofind)
|
219
208
|
end
|
220
|
-
|
221
|
-
###### the remainder of the methods are for verifying and building the completed statement string
|
222
|
-
|
223
|
-
def verify_minimum_requirements
|
224
|
-
if !@stmt_type
|
225
|
-
raise SqlStmtError, "unable to build sql - must call :select, :update, :insert or :delete to specify statement type"
|
226
|
-
end
|
227
|
-
if @tables.empty?
|
228
|
-
raise SqlStmtError, "unable to build sql - must call :table"
|
229
|
-
end
|
230
|
-
|
231
|
-
if (@where_behavior == :require) && @wheres.empty?
|
232
|
-
raise SqlStmtError, "unable to build sql - must call :where, :no_where, or :optional_where"
|
233
|
-
elsif (@where_behavior == :exclude) && !@wheres.empty?
|
234
|
-
raise SqlStmtError, "unable to build sql - :where and :no_where must not both be called, consider :optional_where instead"
|
235
|
-
end
|
236
|
-
|
237
|
-
if @stmt_type == 'select'
|
238
|
-
raise SqlStmtError, "unable to build sql - must call :get" if @fields.empty?
|
239
|
-
raise SqlStmtError, "unable to build sql - must not call :set" if !@values.empty?
|
240
|
-
else
|
241
|
-
raise SqlStmtError, "unable to build sql - must not call :get" if @called_get
|
242
|
-
end
|
243
|
-
|
244
|
-
if ['update','insert'].include?(@stmt_type)
|
245
|
-
raise SqlStmtError, "unable to build sql - must call :set or :setq" if @values.empty?
|
246
|
-
raise SqlStmtError, "unable to build sql - must not call :get" if @called_get
|
247
|
-
end
|
248
|
-
|
249
|
-
if @stmt_type == 'insert'
|
250
|
-
raise SqlStmtError, "unable to build sql - must call :into" if @into_table.nil?
|
251
|
-
end
|
252
|
-
|
253
|
-
if @stmt_type == 'delete'
|
254
|
-
raise SqlStmtError, "unable to build sql - must not call :get or :set" if !@fields.empty?
|
255
|
-
if @tables_to_delete.empty? && ((@tables.size + @joins.size) > 1)
|
256
|
-
raise SqlStmtError, "unable to build sql - must specify tables to delete when including multiple tables"
|
257
|
-
end
|
258
|
-
end
|
259
|
-
end
|
260
|
-
|
261
|
-
def build_stmt
|
262
|
-
method_name = "build_stmt_#{@stmt_type}"
|
263
|
-
return send(method_name)
|
264
|
-
end
|
265
|
-
|
266
|
-
def build_stmt_select
|
267
|
-
straight_join_str = @straight_join ? 'STRAIGHT_JOIN ' : ''
|
268
|
-
distinct_str = @distinct ? 'DISTINCT ' : ''
|
269
|
-
select_str = @fields.join(',')
|
270
|
-
return "SELECT #{straight_join_str}#{distinct_str}#{select_str}#{build_from_clause}#{@outfile}"
|
271
|
-
end
|
272
|
-
|
273
|
-
def build_stmt_update
|
274
|
-
limit_clause = simple_clause('LIMIT', @limit)
|
275
|
-
return "UPDATE #{build_table_list}#{build_join_clause} SET #{build_set_clause}#{build_where_clause}#{limit_clause}"
|
276
|
-
end
|
277
|
-
|
278
|
-
def build_stmt_insert
|
279
|
-
if !@fields.empty? && !@rows.empty?
|
280
|
-
raise "unable to use INSERT SELECT and INSERT VALUES together, may only call :set or :add_row, but not both"
|
281
|
-
end
|
282
|
-
|
283
|
-
keyword = @replace ? 'REPLACE' : 'INSERT'
|
284
|
-
value_list = @values.join(',')
|
285
|
-
start_str = "#{keyword} #{@ignore}INTO #{@into_table} "
|
286
|
-
if !@fields.empty?
|
287
|
-
field_list = @fields.join(',')
|
288
|
-
start_str += "(#{field_list}) "
|
289
|
-
end
|
290
|
-
|
291
|
-
if @rows.empty?
|
292
|
-
distinct_str = @distinct ? 'DISTINCT ' : ''
|
293
|
-
return "#{start_str}SELECT #{distinct_str}#{value_list}#{build_from_clause}"
|
294
|
-
else
|
295
|
-
raise "DISTINCT not supported when inserting values" if @distinct
|
296
|
-
return "#{start_str}VALUES (#{value_list})"
|
297
|
-
end
|
298
|
-
end
|
299
|
-
|
300
|
-
def build_stmt_delete
|
301
|
-
if @tables_to_delete.empty?
|
302
|
-
table_clause = ''
|
303
|
-
else
|
304
|
-
table_clause = ' ' + @tables_to_delete.join(',')
|
305
|
-
end
|
306
|
-
return "DELETE#{table_clause}#{build_from_clause}"
|
307
|
-
end
|
308
|
-
|
309
|
-
def build_from_clause
|
310
|
-
join_clause = build_join_clause
|
311
|
-
group_clause = simple_clause('GROUP BY', @group_by)
|
312
|
-
if @with_rollup
|
313
|
-
group_clause += ' WITH ROLLUP'
|
314
|
-
end
|
315
|
-
order_clause = simple_clause('ORDER BY', @order_by)
|
316
|
-
limit_clause = simple_clause('LIMIT', @limit)
|
317
|
-
having_clause = @having.empty? ? '' : " HAVING #{@having.join(' AND ')}"
|
318
|
-
return " FROM #{build_table_list}#{join_clause}#{build_where_clause}#{group_clause}#{having_clause}#{order_clause}#{limit_clause}"
|
319
|
-
end
|
320
|
-
|
321
|
-
def build_set_clause
|
322
|
-
set_exprs = []
|
323
|
-
@fields.each_with_index do |field, index|
|
324
|
-
set_exprs << "#{field} = #{@values[index]}"
|
325
|
-
end
|
326
|
-
return set_exprs.join(', ')
|
327
|
-
end
|
328
|
-
|
329
|
-
def table_to_str(table)
|
330
|
-
if table.index
|
331
|
-
return "#{table.str} USE INDEX (#{table.index})"
|
332
|
-
end
|
333
|
-
return table.str
|
334
|
-
end
|
335
|
-
|
336
|
-
def build_table_list
|
337
|
-
return @tables.map {|table| table_to_str(table) }.join(',')
|
338
|
-
end
|
339
|
-
|
340
|
-
def simple_clause(keywords, value)
|
341
|
-
return value ? " #{keywords} #{value}" : ''
|
342
|
-
end
|
343
|
-
|
344
|
-
def build_join_clause
|
345
|
-
if @joins.empty?
|
346
|
-
return ''
|
347
|
-
else
|
348
|
-
return ' ' + @joins.map {|ary| ary.join(' ')}.uniq.join(' ')
|
349
|
-
end
|
350
|
-
end
|
351
|
-
|
352
|
-
def build_where_clause
|
353
|
-
return @wheres.empty? ? '' : " WHERE #{@wheres.join(' AND ')}"
|
354
|
-
end
|
355
209
|
end
|
data/lib/sqlstmt.rb
CHANGED
data/test/data_test.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'sqlstmt/data'
|
3
|
+
|
4
|
+
class TestData < Minitest::Test
|
5
|
+
def test_deep_copy_of_arrays
|
6
|
+
orig = SqlStmtLib::SqlData.new
|
7
|
+
orig.tables << 1
|
8
|
+
assert_equal([1], orig.tables)
|
9
|
+
|
10
|
+
copy = orig.dup
|
11
|
+
assert_equal([1], copy.tables)
|
12
|
+
|
13
|
+
copy.tables << 2
|
14
|
+
assert_equal([1, 2], copy.tables)
|
15
|
+
assert_equal([1], orig.tables)
|
16
|
+
end
|
17
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sqlstmt
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.9
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Makani Mason
|
@@ -9,10 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2018-08-02 00:00:00.000000000 Z
|
13
13
|
dependencies: []
|
14
|
-
description:
|
15
|
-
used
|
14
|
+
description: Build SQL statements using method calls instead of strings. This is not
|
15
|
+
an ORM. It has only been used and tested with MySQL so far but the intention is
|
16
|
+
to make it SQL agnostic.
|
16
17
|
email:
|
17
18
|
- devinfo@atpsoft.com
|
18
19
|
executables: []
|
@@ -22,9 +23,14 @@ extra_rdoc_files:
|
|
22
23
|
files:
|
23
24
|
- MIT-LICENSE
|
24
25
|
- lib/sqlstmt.rb
|
26
|
+
- lib/sqlstmt/build.rb
|
27
|
+
- lib/sqlstmt/core_to_sql.rb
|
28
|
+
- lib/sqlstmt/data.rb
|
25
29
|
- lib/sqlstmt/error.rb
|
30
|
+
- lib/sqlstmt/mysql/build.rb
|
31
|
+
- lib/sqlstmt/mysql/check.rb
|
26
32
|
- lib/sqlstmt/sqlstmt.rb
|
27
|
-
-
|
33
|
+
- test/data_test.rb
|
28
34
|
- test/delete_test.rb
|
29
35
|
- test/helper.rb
|
30
36
|
- test/insert_select_test.rb
|
@@ -51,11 +57,12 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
51
57
|
version: '0'
|
52
58
|
requirements: []
|
53
59
|
rubyforge_project:
|
54
|
-
rubygems_version: 2.6.
|
60
|
+
rubygems_version: 2.6.13
|
55
61
|
signing_key:
|
56
62
|
specification_version: 4
|
57
|
-
summary: build SQL statements
|
63
|
+
summary: build SQL statements using method calls instead of strings
|
58
64
|
test_files:
|
65
|
+
- test/data_test.rb
|
59
66
|
- test/delete_test.rb
|
60
67
|
- test/helper.rb
|
61
68
|
- test/insert_select_test.rb
|