sqlstmt 0.1.28 → 0.2.0

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 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