sqlstmt 0.2.8 → 0.2.9
Sign up to get free protection for your applications and to get access to all the features.
- 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
|