sqlstmt 0.1.28 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 53ed3823c5c9a4102f77886a5c06af3ce9a8d662
4
- data.tar.gz: 9e8bfa9c05c72ad5d8000d2a0aae8dd49eb2e872
3
+ metadata.gz: 2b7746931f2110ac0ed453789e2374c05b4233ee
4
+ data.tar.gz: 46c5f8f2a3baa4023d08541c47eb7f45a7fd9cb1
5
5
  SHA512:
6
- metadata.gz: 48ce946d78bc93dfe48ff4790113292cd8a3faaa4a756eda857850ec5f62de2054ab92e77d3d7d72109d795e4d2f8d348464be03dc96b1530cca055867026637
7
- data.tar.gz: f6c805c20e1ba95458e3812f8a331ac33e8b52114763232ccff52361ec7a2fbe9a8c14e1307500d9f7ea44fdbb9d3277253f43681bfa6adb3c34baedc00a57cd
6
+ metadata.gz: 0d5a0ad3df986f73d4729334bab6353b3b77372f1bb78d65f2a5ff5ae626cc5a3a616f5be7ebe32887ea059da6452dbd5e8c1c9a9cf37a71df88ac77f5b9a08d
7
+ data.tar.gz: 840fdd50d95167f638d5fbb78e5fedeb72d42910a45781b3a50af50feb968121b790d9c7a954de653ee3d408deda9782cd18ac49e80d5fef1659dce94082dfbe
data/lib/sqlstmt/error.rb CHANGED
@@ -1,5 +1 @@
1
- module SqlStmt
2
-
3
- class Error < StandardError; end
4
-
5
- end
1
+ class SqlStmtError < StandardError; end
@@ -0,0 +1,343 @@
1
+ require 'sqlstmt/error'
2
+ require 'sqlstmt/to_sql'
3
+
4
+ class SqlStmt
5
+ attr_reader :fields, :tables, :joins, :wheres
6
+
7
+ def initialize
8
+ @stmt_type = nil
9
+ @tables = []
10
+ @joins = []
11
+ @wheres = []
12
+ @where_behavior = :require
13
+ @fields = []
14
+ @values = []
15
+ @group_by = nil
16
+ @order_by = nil
17
+ @limit = nil
18
+ @having = []
19
+ @into_table = nil
20
+ @rows = []
21
+ @tables_to_delete = []
22
+ @distinct = nil
23
+ @straight_join = nil
24
+ @replace = nil
25
+ @ignore = ''
26
+ @outfile = ''
27
+ end
28
+
29
+ def initialize_copy(orig)
30
+ super
31
+ @tables = @tables.dup
32
+ @joins = @joins.dup
33
+ @wheres = @wheres.dup
34
+ @fields = @fields.dup
35
+ @values = @values.dup
36
+ @having = @having.dup
37
+ @rows = @rows.dup
38
+ end
39
+
40
+ ###### pick statement type
41
+
42
+ def select
43
+ ensure_no_statement_type
44
+ @stmt_type = 'select'
45
+ return self
46
+ end
47
+
48
+ def update
49
+ ensure_no_statement_type
50
+ @stmt_type = 'update'
51
+ return self
52
+ end
53
+
54
+ def insert
55
+ ensure_no_statement_type
56
+ @stmt_type = 'insert'
57
+ return self
58
+ end
59
+
60
+ def delete(*tables)
61
+ ensure_no_statement_type
62
+ @stmt_type = 'delete'
63
+ @tables_to_delete = tables
64
+ return self
65
+ end
66
+
67
+ ###### common operations
68
+
69
+ def table(table)
70
+ @tables << table
71
+ return self
72
+ end
73
+
74
+ def join(table, *exprs)
75
+ return add_join('JOIN', table, exprs)
76
+ end
77
+
78
+ def left_join(table, *exprs)
79
+ return add_join('LEFT JOIN', table, exprs)
80
+ end
81
+
82
+ def where(*expr)
83
+ @wheres.concat(expr)
84
+ return self
85
+ end
86
+
87
+ def no_where
88
+ @where_behavior = :exclude
89
+ return self
90
+ end
91
+
92
+ def optional_where
93
+ @where_behavior = :optional
94
+ return self
95
+ end
96
+
97
+ def get(*exprs)
98
+ @fields.concat(exprs)
99
+ return self
100
+ end
101
+
102
+ def set(field, value)
103
+ raise "trying to include field #{field} again" if @fields.include?(field)
104
+ @fields << field
105
+ value = value.is_a?(String) ? value : value.to_sql
106
+ @values << value
107
+ return self
108
+ end
109
+
110
+ def setq(field, value)
111
+ return set(field, value.to_sql)
112
+ end
113
+
114
+ def group_by(expr)
115
+ @group_by = expr
116
+ return self
117
+ end
118
+
119
+ def order_by(expr)
120
+ @order_by = expr
121
+ return self
122
+ end
123
+
124
+ def limit(clause)
125
+ @limit = clause
126
+ return self
127
+ end
128
+
129
+ def having(*expr)
130
+ @having.concat(expr)
131
+ return self
132
+ end
133
+
134
+ # used with INSERT statements only
135
+ def into(into_table)
136
+ @into_table = into_table
137
+ return self
138
+ end
139
+
140
+ # used with INSERT VALUES statements only
141
+ def add_row(row)
142
+ @rows << row
143
+ end
144
+
145
+ def to_s
146
+ verify_minimum_requirements
147
+ return build_stmt
148
+ end
149
+ alias_method :to_sql, :to_s
150
+
151
+ ###### less commonly used methods
152
+
153
+ def distinct
154
+ @distinct = true
155
+ return self
156
+ end
157
+
158
+ def straight_join
159
+ @straight_join = true
160
+ return self
161
+ end
162
+
163
+ def replace
164
+ @replace = true
165
+ return self
166
+ end
167
+
168
+ def ignore
169
+ @ignore = 'IGNORE '
170
+ return self
171
+ end
172
+
173
+ def outfile(str)
174
+ @outfile = " INTO OUTFILE #{str}"
175
+ return self
176
+ end
177
+
178
+ ###### methods to analyze what the statement contains
179
+ def includes_table?(table_to_find)
180
+ retval = @tables.find { |table| table_names_match?(table, table_to_find) }
181
+ retval ||= @joins.find { |_, table, _| table_names_match?(table, table_to_find) }
182
+ return retval
183
+ end
184
+
185
+ ###### transition only mechanisms
186
+ def field(*args)
187
+ if ['update','insert'].include?(@stmt_type)
188
+ set(args[0], args[1])
189
+ else
190
+ get(*args)
191
+ end
192
+ end
193
+ alias_method :fieldq, :setq
194
+
195
+ def optional_join(table, expr)
196
+ unless includes_table?(table)
197
+ join(table, expr)
198
+ end
199
+ end
200
+
201
+ private
202
+ def ensure_no_statement_type
203
+ if @stmt_type
204
+ raise "statement type already set to #{@stmt_type}"
205
+ end
206
+ end
207
+
208
+ def add_join(keyword, table, exprs)
209
+ @joins << [keyword, table, "ON #{exprs.join(' AND ')}"]
210
+ return self
211
+ end
212
+
213
+ def table_names_match?(fullstr, tofind)
214
+ if tofind.index(' ') || !fullstr.index(' ')
215
+ return fullstr == tofind
216
+ end
217
+ orig_name, _, tblas = fullstr.partition(' ')
218
+ return (orig_name == tofind) || (tblas == tofind)
219
+ 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 specify statement type"
226
+ elsif (@where_behavior == :require) && @wheres.empty?
227
+ raise SqlStmtError, "unable to build sql - must call :where, :no_where, or :optional_where"
228
+ elsif (@where_behavior == :exclude) && !@wheres.empty?
229
+ raise SqlStmtError, "unable to build sql - :where and :no_where must not both be called, consider :optional_where instead"
230
+ end
231
+
232
+ if ['select','insert','delete'].include?(@stmt_type)
233
+ raise SqlStmtError, "unable to build sql - must call :table or :join (or one if it's variants)" if @tables.empty? && @joins.empty?
234
+ raise SqlStmtError, "unable to build sql - must call :table if using :join (or one if it's variants)" if @tables.empty? && !@joins.empty?
235
+ end
236
+
237
+ if @stmt_type == 'select'
238
+ raise SqlStmtError, "unable to build sql - must call :field" if @fields.empty?
239
+ end
240
+
241
+ if @stmt_type == 'insert_values'
242
+ raise SqlStmtError, "unable to build sql - must call :into" if @into_table.nil?
243
+ raise SqlStmtError, "unable to build sql - must call :field or :fieldq" if @fields.empty?
244
+ end
245
+
246
+ if @stmt_type == 'insert_select'
247
+ raise SqlStmtError, "unable to build sql - must call :into" if @into_table.nil?
248
+ raise SqlStmtError, "unable to build sql - must call :field or :fieldq" if @fields.empty?
249
+ end
250
+
251
+ if @stmt_type == 'update'
252
+ raise SqlStmtError, "unable to build sql - must call :table" if @tables.empty?
253
+ raise SqlStmtError, "unable to build sql - must call :field or :fieldq" if @fields.empty?
254
+ end
255
+
256
+ if @stmt_type == 'delete'
257
+ combined_table_count = @tables.size + @joins.size
258
+ raise SqlStmtError, "unable to build sql - must call :from when including multiple tables" if @tables_to_delete.empty? && (combined_table_count > 1)
259
+ end
260
+ end
261
+
262
+ def build_stmt
263
+ method_name = "build_stmt_#{@stmt_type}"
264
+ return send(method_name)
265
+ end
266
+
267
+ def build_stmt_select
268
+ straight_join_str = @straight_join ? 'STRAIGHT_JOIN ' : ''
269
+ distinct_str = @distinct ? 'DISTINCT ' : ''
270
+ select_str = @fields.join(',')
271
+ return "SELECT #{straight_join_str}#{distinct_str}#{select_str}#{build_from_clause}#{@outfile}"
272
+ end
273
+
274
+ def build_stmt_update
275
+ limit_clause = simple_clause('LIMIT', @limit)
276
+ return "UPDATE #{build_table_list}#{build_join_clause} SET #{build_set_clause}#{build_where_clause}#{limit_clause}"
277
+ end
278
+
279
+ def build_stmt_insert
280
+ if !@fields.empty? && !@rows.empty?
281
+ raise "unable to use INSERT SELECT and INSERT VALUES together, may only call :set or :add_row, but not both"
282
+ end
283
+
284
+ keyword = @replace ? 'REPLACE' : 'INSERT'
285
+ field_list = @fields.join(',')
286
+ value_list = @values.join(',')
287
+ start_str = "#{keyword} #{@ignore}INTO #{@into_table} (#{field_list}) "
288
+
289
+ if @rows.empty?
290
+ distinct_str = @distinct ? 'DISTINCT ' : ''
291
+ return "#{start_str}SELECT #{distinct_str}#{value_list}#{build_from_clause}"
292
+ else
293
+ raise "DISTINCT not supported when inserting values" if @distinct
294
+ return "#{start_str}VALUES (#{value_list})"
295
+ end
296
+ end
297
+
298
+ def build_stmt_delete
299
+ if @tables_to_delete.empty?
300
+ table_clause = ''
301
+ else
302
+ table_clause = ' ' + @tables_to_delete.join(',')
303
+ end
304
+ return "DELETE#{table_clause}#{build_from_clause}"
305
+ end
306
+
307
+ def build_from_clause
308
+ join_clause = build_join_clause
309
+ group_clause = simple_clause('GROUP BY', @group_by)
310
+ order_clause = simple_clause('ORDER BY', @order_by)
311
+ limit_clause = simple_clause('LIMIT', @limit)
312
+ having_clause = @having.empty? ? '' : " HAVING #{@having.join(' AND ')}"
313
+ return " FROM #{build_table_list}#{join_clause}#{build_where_clause}#{group_clause}#{having_clause}#{order_clause}#{limit_clause}"
314
+ end
315
+
316
+ def build_set_clause
317
+ set_exprs = []
318
+ @fields.each_with_index do |field, index|
319
+ set_exprs << "#{field} = #{@values[index]}"
320
+ end
321
+ return set_exprs.join(', ')
322
+ end
323
+
324
+ def build_table_list
325
+ return @tables.join(',')
326
+ end
327
+
328
+ def simple_clause(keywords, value)
329
+ return value ? " #{keywords} #{value}" : ''
330
+ end
331
+
332
+ def build_join_clause
333
+ if @joins.empty?
334
+ return ''
335
+ else
336
+ return ' ' + @joins.map {|ary| ary.join(' ')}.uniq.join(' ')
337
+ end
338
+ end
339
+
340
+ def build_where_clause
341
+ return @wheres.empty? ? '' : " WHERE #{@wheres.join(' AND ')}"
342
+ end
343
+ end
data/lib/sqlstmt.rb CHANGED
@@ -1,4 +1 @@
1
- require 'sqlstmt/select'
2
- require 'sqlstmt/update'
3
- require 'sqlstmt/insert_select'
4
- require 'sqlstmt/delete'
1
+ require 'sqlstmt/sqlstmt'
@@ -0,0 +1,14 @@
1
+ require_relative 'helper'
2
+
3
+ class TestDelete < Minitest::Test
4
+ def test_minimum_requirements
5
+ assert_raises(SqlStmtError) { SqlStmt.new.delete.table('target').to_s }
6
+ end
7
+
8
+ def test_simple
9
+ assert_equal('DELETE t FROM target t,other_table o', SqlStmt.new.delete('t').table('target t').table('other_table o').no_where.to_s)
10
+ assert_equal('DELETE t,o FROM t JOIN o ON t.id=o.id', SqlStmt.new.delete('t', 'o').table('t').join('o','t.id=o.id').no_where.to_s)
11
+ assert_equal('DELETE FROM target', SqlStmt.new.delete.table('target').no_where.to_s)
12
+ assert_equal('DELETE FROM target WHERE target_id = 1', SqlStmt.new.delete.table('target').where('target_id = 1').to_s)
13
+ end
14
+ end
data/test/helper.rb ADDED
@@ -0,0 +1,2 @@
1
+ require 'minitest/autorun'
2
+ require 'sqlstmt'
@@ -1,31 +1,29 @@
1
- require 'sqlstmt/insert_select'
1
+ require_relative 'helper'
2
2
 
3
- module SqlStmt
4
-
5
- class TestInsertSelect < DohTest::TestGroup
3
+ class TestInsertSelect < Minitest::Test
6
4
  def test_minimum_requirements
7
- assert_raises(SqlStmt::Error) { InsertSelect.new.insert_into('target').to_s }
8
- assert_raises(SqlStmt::Error) { InsertSelect.new.insert_into('target').no_where.to_s }
9
- assert_raises(SqlStmt::Error) { InsertSelect.new.insert_into('target').no_where.field('blah', 'blee').to_s }
5
+ assert_raises(SqlStmtError) { SqlStmt.new.insert.into('target').to_s }
6
+ assert_raises(SqlStmtError) { SqlStmt.new.insert.into('target').no_where.to_s }
7
+ assert_raises(SqlStmtError) { SqlStmt.new.insert.into('target').no_where.set('blah', 'blee').to_s }
10
8
  end
11
9
 
12
10
  def test_simple
13
- assert_equal('INSERT INTO target (blah) SELECT blee FROM source', InsertSelect.new.insert_into('target').table('source').field('blah', 'blee').no_where.to_s)
14
- assert_equal('INSERT INTO target (blah) SELECT blee FROM source WHERE source_id = 1', InsertSelect.new.insert_into('target').table('source').field('blah', 'blee').where('source_id = 1').to_s)
11
+ assert_equal('INSERT INTO target (blah) SELECT blee FROM source', SqlStmt.new.insert.into('target').table('source').set('blah', 'blee').no_where.to_s)
12
+ assert_equal('INSERT INTO target (blah) SELECT blee FROM source WHERE source_id = 1', SqlStmt.new.insert.into('target').table('source').set('blah', 'blee').where('source_id = 1').to_s)
15
13
  end
16
14
 
17
15
  def test_dup
18
- shared_builder = SqlStmt::InsertSelect.new.insert_into('target')
16
+ shared_builder = SqlStmt.new.insert.into('target')
19
17
  first_builder = shared_builder
20
18
  other_builder = shared_builder.dup
21
19
 
22
20
  first_builder.where('status="bad"')
23
- first_builder.field('created_at', 'NOW()')
21
+ first_builder.set('created_at', 'NOW()')
24
22
  first_builder.table('some_tbl s')
25
23
 
26
24
  other_builder.no_where
27
- other_builder.field('info', 'o.info')
28
- other_builder.field('created_at', 'then')
25
+ other_builder.set('info', 'o.info')
26
+ other_builder.set('created_at', 'then')
29
27
  other_builder.table('other_tbl o')
30
28
 
31
29
  assert_equal('INSERT INTO target (created_at) SELECT NOW() FROM some_tbl s WHERE status="bad"', first_builder.to_s)
@@ -33,9 +31,9 @@ class TestInsertSelect < DohTest::TestGroup
33
31
  end
34
32
 
35
33
  def test_complex
36
- shared_builder = SqlStmt::InsertSelect.new.insert_into('target')
34
+ shared_builder = SqlStmt.new.insert.into('target')
37
35
  shared_builder.table('shared_tbl')
38
- shared_builder.field('created_at', 'NOW()').field('duration', 5).fieldq('is_bad', 'b')
36
+ shared_builder.set('created_at', 'NOW()').set('duration', 5).setq('is_bad', 'b')
39
37
 
40
38
  first_builder = shared_builder
41
39
  other_builder = shared_builder.dup
@@ -43,13 +41,11 @@ class TestInsertSelect < DohTest::TestGroup
43
41
  first_builder.where("status='bad'")
44
42
 
45
43
  other_builder.table('other_tbl o')
46
- other_builder.field('info', 'o.info')
47
- other_builder.field('data', 'o.data')
44
+ other_builder.set('info', 'o.info')
45
+ other_builder.set('data', 'o.data')
48
46
  other_builder.where('s.id=o.shared_id', "status='good'")
49
47
 
50
48
  assert_equal("INSERT INTO target (created_at,duration,is_bad) SELECT NOW(),5,'b' FROM shared_tbl WHERE status='bad'", first_builder.to_s)
51
49
  assert_equal("INSERT INTO target (created_at,duration,is_bad,info,data) SELECT NOW(),5,'b',o.info,o.data FROM shared_tbl,other_tbl o WHERE s.id=o.shared_id AND status='good'", other_builder.to_s)
52
50
  end
53
51
  end
54
-
55
- end
@@ -0,0 +1,42 @@
1
+ require_relative 'helper'
2
+
3
+ class TestSelect < Minitest::Test
4
+ def test_includes_table
5
+ sqlb = SqlStmt.new.select.table('target t')
6
+ assert(sqlb.includes_table?('target'))
7
+ assert(sqlb.includes_table?('t'))
8
+ assert(!sqlb.includes_table?('blah'))
9
+ end
10
+
11
+ def test_minimum_requirements
12
+ assert_raises(SqlStmtError) { SqlStmt.new.select.table('target').to_s }
13
+ assert_raises(SqlStmtError) { SqlStmt.new.select.table('target').no_where.to_s }
14
+ assert_raises(SqlStmtError) { SqlStmt.new.select.table('target').optional_where.to_s }
15
+ assert_equal('SELECT blah FROM target', SqlStmt.new.select.table('target').optional_where.get('blah').to_sql)
16
+ end
17
+
18
+ def test_stuff
19
+ assert_equal('SELECT blah FROM source', SqlStmt.new.select.table('source').get('blah').no_where.to_s)
20
+ assert_equal('SELECT DISTINCT blah FROM source', SqlStmt.new.select.table('source').get('blah').no_where.distinct.to_s)
21
+ assert_equal('SELECT blah FROM source WHERE source_id = 1', SqlStmt.new.select.table('source').get('blah').where('source_id = 1').to_s)
22
+ assert_equal('SELECT blah FROM source s', SqlStmt.new.select.table('source s').get('blah').no_where.to_s)
23
+ assert_equal('SELECT blah FROM source s JOIN other o ON s.blah_id = o.blah_id', SqlStmt.new.select.table('source s').join('other o', 's.blah_id = o.blah_id').get('blah').no_where.to_s)
24
+ assert_equal('SELECT blah FROM source s LEFT JOIN other o ON s.blah_id = o.blah_id', SqlStmt.new.select.table('source s').left_join('other o', 's.blah_id = o.blah_id').get('blah').no_where.to_s)
25
+ assert_equal('SELECT blah,blee FROM source', SqlStmt.new.select.table('source').get('blah','blee').no_where.to_s)
26
+ assert_equal('SELECT blah FROM source HAVING blee > 0', SqlStmt.new.select.table('source').get('blah').no_where.having('blee > 0').to_s)
27
+ end
28
+
29
+ def test_duplicate_joins
30
+ sqlb = SqlStmt.new.select.table('source s').get('frog').no_where
31
+ 4.times { sqlb.join('other o', 's.blah_id = o.blah_id') }
32
+ assert_equal('SELECT frog FROM source s JOIN other o ON s.blah_id = o.blah_id', sqlb.to_s)
33
+ end
34
+
35
+ def test_join_with_multiple_conditions
36
+ %i(join left_join).each do |method|
37
+ sqlb = SqlStmt.new.select.table('source s').get('frog').no_where.send(method, 'other o', 'z.blee_id = o.blee_id', 'z.other_field = o.other_field')
38
+ method_sql = method.to_s.upcase.sub('_', ' ')
39
+ assert_equal("SELECT frog FROM source s #{method_sql} other o ON z.blee_id = o.blee_id AND z.other_field = o.other_field", sqlb.to_s)
40
+ end
41
+ end
42
+ end
@@ -1,8 +1,6 @@
1
- require 'sqlstmt/to_sql'
1
+ require_relative 'helper'
2
2
 
3
- module SqlStmt
4
-
5
- class Test_to_sql < DohTest::TestGroup
3
+ class Test_to_sql < Minitest::Test
6
4
  def test_stuff
7
5
  assert_equal("'blah'", 'blah'.to_sql)
8
6
  assert_equal('NULL', nil.to_sql)
@@ -15,6 +13,3 @@ class Test_to_sql < DohTest::TestGroup
15
13
  assert_equal("('a','b','c')", ['a', 'b', 'c'].to_sql)
16
14
  end
17
15
  end
18
-
19
- end
20
-
@@ -1,38 +1,36 @@
1
- require 'sqlstmt/update'
1
+ require_relative 'helper'
2
2
 
3
- module SqlStmt
4
-
5
- class TestUpdate < DohTest::TestGroup
3
+ class TestUpdate < Minitest::Test
6
4
  def test_minimum_requirements
7
- assert_raises(SqlStmt::Error) { Update.new.table('target').to_s }
8
- assert_raises(SqlStmt::Error) { Update.new.table('target').no_where.to_s }
5
+ assert_raises(SqlStmtError) { SqlStmt.new.update.table('target').to_s }
6
+ assert_raises(SqlStmtError) { SqlStmt.new.update.table('target').no_where.to_s }
9
7
  end
10
8
 
11
9
  def test_simple
12
- assert_equal('UPDATE target SET blah = blee', Update.new.table('target').field('blah', 'blee').no_where.to_s)
13
- assert_equal('UPDATE target SET blah = blee LIMIT 3', Update.new.table('target').field('blah', 'blee').no_where.limit(3).to_s)
14
- assert_equal('UPDATE target SET blah = blee WHERE target_id = 1', Update.new.table('target').field('blah', 'blee').where('target_id = 1').to_s)
10
+ assert_equal('UPDATE target SET blah = blee', SqlStmt.new.update.table('target').set('blah', 'blee').no_where.to_s)
11
+ assert_equal('UPDATE target SET blah = blee LIMIT 3', SqlStmt.new.update.table('target').set('blah', 'blee').no_where.limit(3).to_s)
12
+ assert_equal('UPDATE target SET blah = blee WHERE target_id = 1', SqlStmt.new.update.table('target').set('blah', 'blee').where('target_id = 1').to_s)
15
13
  end
16
14
 
17
15
  def test_join
18
- builder = SqlStmt::Update.new.table('main m').join('other o', 'm.main_id = o.main_id')
19
- builder.field('blah', 3)
16
+ builder = SqlStmt.new.update.table('main m').join('other o', 'm.main_id = o.main_id')
17
+ builder.set('blah', 3)
20
18
  builder.no_where
21
19
  assert_equal('UPDATE main m JOIN other o ON m.main_id = o.main_id SET blah = 3', builder.to_s)
22
20
  end
23
21
 
24
22
  def test_dup
25
- shared_builder = SqlStmt::Update.new.table('target')
23
+ shared_builder = SqlStmt.new.update.table('target')
26
24
  first_builder = shared_builder
27
25
  other_builder = shared_builder.dup
28
26
 
29
27
  first_builder.where('status="bad"')
30
- first_builder.field('created_at', 'NOW()')
28
+ first_builder.set('created_at', 'NOW()')
31
29
  first_builder.table('some_tbl s')
32
30
 
33
31
  other_builder.no_where
34
- other_builder.field('info', 'o.info')
35
- other_builder.field('created_at', 'then')
32
+ other_builder.set('info', 'o.info')
33
+ other_builder.set('created_at', 'then')
36
34
  other_builder.table('other_tbl o')
37
35
 
38
36
  assert_equal('UPDATE target,some_tbl s SET created_at = NOW() WHERE status="bad"', first_builder.to_s)
@@ -40,9 +38,9 @@ class TestUpdate < DohTest::TestGroup
40
38
  end
41
39
 
42
40
  def test_complex
43
- shared_builder = SqlStmt::Update.new.table('target')
41
+ shared_builder = SqlStmt.new.update.table('target')
44
42
  shared_builder.table('shared_tbl')
45
- shared_builder.field('created_at', 'NOW()').field('duration', 5).field('is_bad', 1)
43
+ shared_builder.set('created_at', 'NOW()').set('duration', 5).set('is_bad', 1)
46
44
 
47
45
  first_builder = shared_builder
48
46
  other_builder = shared_builder.dup
@@ -50,8 +48,8 @@ class TestUpdate < DohTest::TestGroup
50
48
  first_builder.where('status="bad"')
51
49
 
52
50
  other_builder.table('other_tbl o')
53
- other_builder.field('info', 'o.info')
54
- other_builder.field('data', 'o.data')
51
+ other_builder.set('info', 'o.info')
52
+ other_builder.set('data', 'o.data')
55
53
  other_builder.where('s.id=o.shared_id')
56
54
  other_builder.where('status="good"')
57
55
 
@@ -59,5 +57,3 @@ class TestUpdate < DohTest::TestGroup
59
57
  assert_equal('UPDATE target,shared_tbl,other_tbl o SET created_at = NOW(), duration = 5, is_bad = 1, info = o.info, data = o.data WHERE s.id=o.shared_id AND status="good"', other_builder.to_s)
60
58
  end
61
59
  end
62
-
63
- 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.1.28
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Makani Mason
@@ -9,36 +9,8 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2015-10-19 00:00:00.000000000 Z
13
- dependencies:
14
- - !ruby/object:Gem::Dependency
15
- name: dohutil
16
- requirement: !ruby/object:Gem::Requirement
17
- requirements:
18
- - - ">="
19
- - !ruby/object:Gem::Version
20
- version: 0.2.21
21
- type: :runtime
22
- prerelease: false
23
- version_requirements: !ruby/object:Gem::Requirement
24
- requirements:
25
- - - ">="
26
- - !ruby/object:Gem::Version
27
- version: 0.2.21
28
- - !ruby/object:Gem::Dependency
29
- name: dohtest
30
- requirement: !ruby/object:Gem::Requirement
31
- requirements:
32
- - - ">="
33
- - !ruby/object:Gem::Version
34
- version: 0.1.42
35
- type: :development
36
- prerelease: false
37
- version_requirements: !ruby/object:Gem::Requirement
38
- requirements:
39
- - - ">="
40
- - !ruby/object:Gem::Version
41
- version: 0.1.42
12
+ date: 2016-01-18 00:00:00.000000000 Z
13
+ dependencies: []
42
14
  description: build SQL statements in a modular fashion, one piece at a time; only
43
15
  used/tested with MySQL so far
44
16
  email:
@@ -50,21 +22,15 @@ extra_rdoc_files:
50
22
  files:
51
23
  - MIT-LICENSE
52
24
  - lib/sqlstmt.rb
53
- - lib/sqlstmt/delete.rb
54
25
  - lib/sqlstmt/error.rb
55
- - lib/sqlstmt/from_query.rb
56
- - lib/sqlstmt/insert_select.rb
57
- - lib/sqlstmt/insert_values.rb
58
- - lib/sqlstmt/query.rb
59
- - lib/sqlstmt/select.rb
26
+ - lib/sqlstmt/sqlstmt.rb
60
27
  - lib/sqlstmt/to_sql.rb
61
- - lib/sqlstmt/update.rb
62
- - lib/sqlstmt/value_util.rb
63
- - test/delete.dt.rb
64
- - test/insert_select.dt.rb
65
- - test/select.dt.rb
66
- - test/to_sql.dt.rb
67
- - test/update.dt.rb
28
+ - test/delete_test.rb
29
+ - test/helper.rb
30
+ - test/insert_select_test.rb
31
+ - test/select_test.rb
32
+ - test/to_sql_test.rb
33
+ - test/update_test.rb
68
34
  homepage: https://github.com/atpsoft/sqlstmt
69
35
  licenses:
70
36
  - MIT
@@ -90,8 +56,9 @@ signing_key:
90
56
  specification_version: 4
91
57
  summary: build SQL statements in a modular fashion, one piece at a time
92
58
  test_files:
93
- - test/delete.dt.rb
94
- - test/insert_select.dt.rb
95
- - test/select.dt.rb
96
- - test/to_sql.dt.rb
97
- - test/update.dt.rb
59
+ - test/delete_test.rb
60
+ - test/helper.rb
61
+ - test/insert_select_test.rb
62
+ - test/select_test.rb
63
+ - test/to_sql_test.rb
64
+ - test/update_test.rb
@@ -1,35 +0,0 @@
1
- require 'sqlstmt/from_query'
2
-
3
- module SqlStmt
4
-
5
- class Delete < FromQuery
6
- force_deep_copy :from_tables
7
-
8
- def initialize
9
- super
10
- @from_tables = []
11
- end
12
-
13
- def from(table)
14
- @from_tables << table
15
- self
16
- end
17
-
18
- private
19
- def verify_minimum_requirements
20
- super
21
- combined_table_count = @tables.size + @joins.size
22
- raise SqlStmt::Error, "unable to build sql - must call :from when including multiple tables" if @from_tables.empty? && (combined_table_count > 1)
23
- end
24
-
25
- def build_stmt
26
- if @from_tables.empty?
27
- table_clause = ''
28
- else
29
- table_clause = ' ' + @from_tables.join(',')
30
- end
31
- "DELETE#{table_clause}#{build_from_clause}"
32
- end
33
- end
34
-
35
- end
@@ -1,52 +0,0 @@
1
- require 'sqlstmt/query'
2
-
3
- module SqlStmt
4
-
5
- class FromQuery < Query
6
- def initialize
7
- super
8
- @group_by = nil
9
- @order_by = nil
10
- @having = []
11
- end
12
-
13
- def group_by(clause)
14
- @group_by = clause
15
- self
16
- end
17
-
18
- def order_by(clause)
19
- @order_by = clause
20
- self
21
- end
22
-
23
- def having(*sql)
24
- @having.concat(sql)
25
- self
26
- end
27
-
28
- private
29
- def verify_minimum_requirements
30
- super
31
- raise SqlStmt::Error, "unable to build sql - must call :table or :join (or one if it's variants)" if @tables.empty? && @joins.empty?
32
- raise SqlStmt::Error, "unable to build sql - must call :table if using :join (or one if it's variants)" if @tables.empty? && !@joins.empty?
33
- end
34
-
35
- def having_clause
36
- if @having.empty?
37
- ''
38
- else
39
- " HAVING #{@having.join(' AND ')}"
40
- end
41
- end
42
-
43
- def build_from_clause
44
- join_clause = build_join_clause
45
- group_clause = simple_clause('GROUP BY', @group_by)
46
- order_clause = simple_clause('ORDER BY', @order_by)
47
- limit_clause = simple_clause('LIMIT', @limit)
48
- " FROM #{build_table_list}#{join_clause}#{build_where_clause}#{group_clause}#{having_clause}#{order_clause}#{limit_clause}"
49
- end
50
- end
51
-
52
- end
@@ -1,48 +0,0 @@
1
- require 'sqlstmt/from_query'
2
- require 'sqlstmt/value_util'
3
-
4
- module SqlStmt
5
-
6
- class InsertSelect < FromQuery
7
- force_deep_copy :values
8
- include ValueUtil
9
-
10
- def initialize
11
- super
12
- @values = []
13
- @distinct = @ignore = false
14
- @into_table = nil
15
- end
16
-
17
- def distinct
18
- @distinct = true
19
- self
20
- end
21
-
22
- def ignore
23
- @ignore = true
24
- self
25
- end
26
-
27
- def insert_into(table)
28
- @into_table = table
29
- self
30
- end
31
-
32
- private
33
- def verify_minimum_requirements
34
- super
35
- raise SqlStmt::Error, "unable to build sql - must call :into" if @into_table.nil?
36
- raise SqlStmt::Error, "unable to build sql - must call :field or :fieldq" if @fields.empty?
37
- end
38
-
39
- def build_stmt
40
- distinct_str = if @distinct then 'DISTINCT ' else '' end
41
- ignore_str = if @ignore then 'IGNORE ' else '' end
42
- into_str = @fields.join(',')
43
- select_str = @values.join(',')
44
- "INSERT #{ignore_str}INTO #@into_table (#{into_str}) SELECT #{distinct_str}#{select_str}#{build_from_clause}"
45
- end
46
- end
47
-
48
- end
@@ -1,37 +0,0 @@
1
- require 'sqlstmt/from_query'
2
- require 'sqlstmt/value_util'
3
-
4
- module SqlStmt
5
-
6
- class InsertValues < FromQuery
7
- force_deep_copy :values
8
- include ValueUtil
9
-
10
- def initialize
11
- super
12
- @values = []
13
- @into_table = nil
14
- no_where
15
- end
16
-
17
- def into(table)
18
- @into_table = table
19
- table(table)
20
- self
21
- end
22
-
23
- private
24
- def verify_minimum_requirements
25
- super
26
- raise SqlStmt::Error, "unable to build sql - must call :into" if @into_table.nil?
27
- raise SqlStmt::Error, "unable to build sql - must call :field or :fieldq" if @fields.empty?
28
- end
29
-
30
- def build_stmt
31
- into_str = @fields.join(',')
32
- values_str = @values.join(',')
33
- "INSERT INTO #@into_table (#{into_str}) VALUES (#{values_str})"
34
- end
35
- end
36
-
37
- end
data/lib/sqlstmt/query.rb DELETED
@@ -1,110 +0,0 @@
1
- require 'dohutil/core_ext/force_deep_copy'
2
- require 'sqlstmt/error'
3
-
4
- module SqlStmt
5
-
6
- class Query
7
- force_deep_copy :fields, :tables, :joins, :wheres
8
- attr_reader :fields, :tables, :joins, :wheres
9
-
10
- def initialize
11
- @fields = []
12
- @tables = []
13
- @joins = []
14
- @wheres = []
15
- @where_behavior = :require
16
- @limit = nil
17
- end
18
-
19
- def table(table)
20
- @tables << table
21
- self
22
- end
23
-
24
- def join(table, *exprs)
25
- @joins << ['JOIN', table, "ON #{exprs.join(' AND ')}"]
26
- self
27
- end
28
-
29
- def optional_join(table, expr)
30
- unless includes_table?(table)
31
- join(table, expr)
32
- end
33
- end
34
-
35
- def left_join(table, *exprs)
36
- @joins << ['LEFT JOIN', table, "ON #{exprs.join(' AND ')}"]
37
- self
38
- end
39
-
40
- def where(*sql)
41
- @wheres.concat(sql)
42
- self
43
- end
44
-
45
- def no_where
46
- @where_behavior = :exclude
47
- self
48
- end
49
-
50
- def optional_where
51
- @where_behavior = :optional
52
- self
53
- end
54
-
55
- def limit(clause)
56
- @limit = clause
57
- self
58
- end
59
-
60
- def to_s
61
- verify_minimum_requirements
62
- build_stmt
63
- end
64
- alias_method :to_sql, :to_s
65
-
66
- def includes_table?(table_to_find)
67
- retval = @tables.find { |table| table_names_match?(table, table_to_find) }
68
- retval ||= @joins.find { |_, table, _| table_names_match?(table, table_to_find) }
69
- retval
70
- end
71
-
72
- private
73
- def table_names_match?(fullstr, tofind)
74
- if tofind.index(' ') || !fullstr.index(' ')
75
- return fullstr == tofind
76
- end
77
- orig_name, _, tblas = fullstr.partition(' ')
78
- (orig_name == tofind) || (tblas == tofind)
79
- end
80
-
81
- def verify_minimum_requirements
82
- if (@where_behavior == :require) && @wheres.empty?
83
- raise SqlStmt::Error, "unable to build sql - must call :where, :no_where, or :optional_where"
84
- elsif (@where_behavior == :exclude) && !@wheres.empty?
85
- raise SqlStmt::Error, "unable to build sql - :where and :no_where must not both be called, consider :optional_where instead"
86
- end
87
- end
88
-
89
- def build_table_list
90
- @tables.join(',')
91
- end
92
-
93
- def simple_clause(keywords, value)
94
- if value then " #{keywords} #{value}" else '' end
95
- end
96
-
97
- def build_join_clause
98
- if @joins.empty?
99
- ''
100
- else
101
- ' ' + @joins.collect{|ary| ary.join(' ')}.uniq.join(' ')
102
- end
103
- end
104
-
105
- def build_where_clause
106
- if @wheres.empty? then '' else " WHERE #{@wheres.join(' AND ')}" end
107
- end
108
- end
109
-
110
- end
@@ -1,48 +0,0 @@
1
- require 'sqlstmt/from_query'
2
-
3
- module SqlStmt
4
-
5
- class Select < FromQuery
6
- def initialize
7
- super
8
- @distinct = false
9
- @straight_join = false
10
- @into = nil
11
- end
12
-
13
- def field(*field_exprs)
14
- @fields.concat(field_exprs)
15
- self
16
- end
17
-
18
- def distinct
19
- @distinct = true
20
- self
21
- end
22
-
23
- def straight_join
24
- @straight_join = true
25
- self
26
- end
27
-
28
- def into_outfile(str)
29
- @into = "OUTFILE #{str}"
30
- self
31
- end
32
-
33
- private
34
- def verify_minimum_requirements
35
- super
36
- raise SqlStmt::Error, "unable to build sql - must call :field" if @fields.empty?
37
- end
38
-
39
- def build_stmt
40
- straight_join_str = if @straight_join then 'STRAIGHT_JOIN ' else '' end
41
- distinct_str = if @distinct then 'DISTINCT ' else '' end
42
- into_str = if @into then " INTO #{@into}" else '' end
43
- select_str = @fields.join(',')
44
- "SELECT #{straight_join_str}#{distinct_str}#{select_str}#{build_from_clause}#{into_str}"
45
- end
46
- end
47
-
48
- end
@@ -1,36 +0,0 @@
1
- require 'sqlstmt/query'
2
- require 'sqlstmt/value_util'
3
-
4
- module SqlStmt
5
-
6
- class Update < Query
7
- force_deep_copy :values
8
- include ValueUtil
9
-
10
- def initialize
11
- super
12
- @values = []
13
- end
14
-
15
- private
16
- def verify_minimum_requirements
17
- super
18
- raise SqlStmt::Error, "unable to build sql - must call :table" if @tables.empty?
19
- raise SqlStmt::Error, "unable to build sql - must call :field or :fieldq" if @fields.empty?
20
- end
21
-
22
- def build_set_clause
23
- set_exprs = []
24
- @fields.each_with_index do |field, index|
25
- set_exprs.push("#{field} = #{@values[index]}")
26
- end
27
- set_exprs.join(', ')
28
- end
29
-
30
- def build_stmt
31
- limit_clause = simple_clause('LIMIT', @limit)
32
- "UPDATE #{build_table_list}#{build_join_clause} SET #{build_set_clause}#{build_where_clause}#{limit_clause}"
33
- end
34
- end
35
-
36
- end
@@ -1,14 +0,0 @@
1
- require 'sqlstmt/to_sql'
2
-
3
- module ValueUtil
4
- def field(field, value)
5
- raise "trying to include field #{field} again" if @fields.include?(field)
6
- @fields.push(field)
7
- @values.push(if value.is_a?(String) then value else value.to_sql end)
8
- self
9
- end
10
-
11
- def fieldq(field, value)
12
- field(field, value.to_sql)
13
- end
14
- end
data/test/delete.dt.rb DELETED
@@ -1,18 +0,0 @@
1
- require 'sqlstmt/delete'
2
-
3
- module SqlStmt
4
-
5
- class TestDelete < DohTest::TestGroup
6
- def test_minimum_requirements
7
- assert_raises(SqlStmt::Error) { Delete.new.table('target').to_s }
8
- end
9
-
10
- def test_simple
11
- assert_equal('DELETE t FROM target t,other_table o', Delete.new.from('t').table('target t').table('other_table o').no_where.to_s)
12
- assert_equal('DELETE t,o FROM t JOIN o ON t.id=o.id', Delete.new.from('t').from('o').table('t').join('o','t.id=o.id').no_where.to_s)
13
- assert_equal('DELETE FROM target', Delete.new.table('target').no_where.to_s)
14
- assert_equal('DELETE FROM target WHERE target_id = 1', Delete.new.table('target').where('target_id = 1').to_s)
15
- end
16
- end
17
-
18
- end
data/test/select.dt.rb DELETED
@@ -1,53 +0,0 @@
1
- require 'sqlstmt/select'
2
-
3
- module SqlStmt
4
-
5
- class TestSelect < DohTest::TestGroup
6
- def test_includes_table
7
- sqlb = Select.new.table('target t')
8
- assert(sqlb.includes_table?('target'))
9
- assert(sqlb.includes_table?('t'))
10
- assert(!sqlb.includes_table?('blah'))
11
- end
12
-
13
- def test_minimum_requirements
14
- assert_raises(SqlStmt::Error) { Select.new.table('target').to_s }
15
- assert_raises(SqlStmt::Error) { Select.new.table('target').no_where.to_s }
16
- assert_raises(SqlStmt::Error) { Select.new.table('target').optional_where.to_s }
17
- assert_equal('SELECT blah FROM target', Select.new.table('target').optional_where.field('blah').to_sql)
18
- end
19
-
20
- def test_stuff
21
- assert_equal('SELECT blah FROM source', Select.new.table('source').field('blah').no_where.to_s)
22
- assert_equal('SELECT DISTINCT blah FROM source', Select.new.table('source').field('blah').no_where.distinct.to_s)
23
- assert_equal('SELECT blah FROM source WHERE source_id = 1', Select.new.table('source').field('blah').where('source_id = 1').to_s)
24
- assert_equal('SELECT blah FROM source s', Select.new.table('source s').field('blah').no_where.to_s)
25
- assert_equal('SELECT blah FROM source s JOIN other o ON s.blah_id = o.blah_id', Select.new.table('source s').join('other o', 's.blah_id = o.blah_id').field('blah').no_where.to_s)
26
- assert_equal('SELECT blah FROM source s LEFT JOIN other o ON s.blah_id = o.blah_id', Select.new.table('source s').left_join('other o', 's.blah_id = o.blah_id').field('blah').no_where.to_s)
27
- assert_equal('SELECT blah,blee FROM source', Select.new.table('source').field('blah','blee').no_where.to_s)
28
- assert_equal('SELECT blah FROM source HAVING blee > 0', Select.new.table('source').field('blah').no_where.having('blee > 0').to_s)
29
- end
30
-
31
- def test_duplicate_joins
32
- sqlb = Select.new.table('source s').field('frog').no_where
33
- 4.times { sqlb.join('other o', 's.blah_id = o.blah_id') }
34
- assert_equal('SELECT frog FROM source s JOIN other o ON s.blah_id = o.blah_id', sqlb.to_s)
35
- end
36
-
37
- def test_optional_join
38
- sqlb = Select.new.table('source s').field('frog').no_where
39
- sqlb.join('other o', 's.blah_id = o.blah_id')
40
- sqlb.optional_join('other o', 'z.blee_id = o.blee_id')
41
- assert_equal('SELECT frog FROM source s JOIN other o ON s.blah_id = o.blah_id', sqlb.to_s)
42
- end
43
-
44
- def test_join_with_multiple_conditions
45
- %i(join left_join).each do |method|
46
- sqlb = Select.new.table('source s').field('frog').no_where.send(method, 'other o', 'z.blee_id = o.blee_id', 'z.other_field = o.other_field')
47
- method_sql = method.to_s.upcase.sub('_', ' ')
48
- assert_equal("SELECT frog FROM source s #{method_sql} other o ON z.blee_id = o.blee_id AND z.other_field = o.other_field", sqlb.to_s)
49
- end
50
- end
51
- end
52
-
53
- end