sqlstmt 0.2.11 → 0.2.12
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/MIT-LICENSE +1 -1
- data/lib/sqlstmt/core_to_sql.rb +4 -2
- data/lib/sqlstmt/data.rb +1 -1
- data/lib/sqlstmt/mysql/build.rb +75 -35
- data/lib/sqlstmt/sqlstmt.rb +13 -2
- data/test/insert_select_test.rb +6 -0
- data/test/select_test.rb +82 -50
- data/test/to_sql_test.rb +13 -5
- metadata +4 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 618fd088e704565e2bb73f6b7887db32cf60f7be
|
4
|
+
data.tar.gz: b632cba8862643ac1003cea40b7cf1b15f862900
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ea60dddfea616466f657e597065ef6051cd091d7d123f06be08d12c93fc43e0f3e28d6cfebb2c0ff6358e28ddc5a234110b5fe8ac220e3c3b99789cb2733653d
|
7
|
+
data.tar.gz: fa8802b7ec27baee278410180119d9eae21cbc31d13cecd20e2f207eb6eaa7a7c5fd4b545a2122bfcda915de8153887de18bdce069c64a2582770f8da2e8a9b6
|
data/MIT-LICENSE
CHANGED
data/lib/sqlstmt/core_to_sql.rb
CHANGED
@@ -4,8 +4,10 @@ require 'date'
|
|
4
4
|
class String
|
5
5
|
unless method_defined?(:to_sql)
|
6
6
|
def to_sql
|
7
|
-
|
8
|
-
"'
|
7
|
+
str = gsub('\\') {'\\\\'}
|
8
|
+
str = str.gsub("'") {"\\'"}
|
9
|
+
str = str.gsub('"') {'\\"'}
|
10
|
+
return "'#{str}'"
|
9
11
|
end
|
10
12
|
end
|
11
13
|
end
|
data/lib/sqlstmt/data.rb
CHANGED
@@ -4,7 +4,7 @@ module SqlStmtLib
|
|
4
4
|
extend self
|
5
5
|
|
6
6
|
FLAG_KEYWORDS = %i(distinct ignore replace straight_join with_rollup).freeze
|
7
|
-
SINGLE_VALUE_KEYWORDS = %i(group_by into limit offset order_by outfile).freeze
|
7
|
+
SINGLE_VALUE_KEYWORDS = %i(group_by into limit offset order_by outfile on_duplicate ignore_duplicate).freeze
|
8
8
|
MULTI_VALUE_KEYWORDS = %i(get having where).freeze
|
9
9
|
|
10
10
|
# :str is the full original string specifying the table, like 'frog f' or 'frog AS f' or 'frog'
|
data/lib/sqlstmt/mysql/build.rb
CHANGED
@@ -11,52 +11,99 @@ class MysqlBuilder
|
|
11
11
|
return send(method_name)
|
12
12
|
end
|
13
13
|
|
14
|
+
def combine_parts(parts)
|
15
|
+
parts.each do |str|
|
16
|
+
str.strip!
|
17
|
+
end
|
18
|
+
parts.reject! do |str|
|
19
|
+
str.nil? || str.empty?
|
20
|
+
end
|
21
|
+
return parts.join(' ')
|
22
|
+
end
|
23
|
+
|
14
24
|
def build_stmt_select
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
25
|
+
parts = []
|
26
|
+
parts << shared_select(@data.gets)
|
27
|
+
if @data.outfile
|
28
|
+
parts << "INTO OUTFILE #{@data.outfile}"
|
29
|
+
end
|
30
|
+
return combine_parts(parts)
|
20
31
|
end
|
21
32
|
|
22
33
|
def build_stmt_update
|
23
|
-
|
24
|
-
|
34
|
+
parts = [
|
35
|
+
'UPDATE',
|
36
|
+
build_table_list,
|
37
|
+
build_join_clause,
|
38
|
+
'SET',
|
39
|
+
build_set_clause,
|
40
|
+
build_where_clause,
|
41
|
+
simple_clause('LIMIT', @data.limit),
|
42
|
+
]
|
43
|
+
return combine_parts(parts)
|
25
44
|
end
|
26
45
|
|
27
46
|
def build_stmt_insert
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
47
|
+
parts = []
|
48
|
+
if @data.replace
|
49
|
+
parts << 'REPLACE'
|
50
|
+
else
|
51
|
+
parts << 'INSERT'
|
52
|
+
end
|
53
|
+
if @data.ignore
|
54
|
+
parts << 'IGNORE'
|
55
|
+
end
|
56
|
+
parts << "INTO #{@data.into}"
|
32
57
|
if !@data.set_fields.empty?
|
33
58
|
field_list = @data.set_fields.join(',')
|
34
|
-
|
59
|
+
parts << "(#{field_list})"
|
35
60
|
end
|
36
|
-
|
37
|
-
|
38
|
-
|
61
|
+
parts << shared_select(@data.set_values)
|
62
|
+
if @data.on_duplicate
|
63
|
+
parts << "ON DUPLICATE KEY UPDATE #{@data.on_duplicate}"
|
64
|
+
elsif @data.ignore_duplicate
|
65
|
+
parts << "ON DUPLICATE KEY UPDATE #{@data.ignore_duplicate} = #{@data.ignore_duplicate}"
|
66
|
+
end
|
67
|
+
return combine_parts(parts)
|
39
68
|
end
|
40
69
|
|
41
70
|
def build_stmt_delete
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
table_clause = ' ' + @data.tables_to_delete.join(',')
|
71
|
+
parts = ['DELETE']
|
72
|
+
if !@data.tables_to_delete.empty?
|
73
|
+
parts << @data.tables_to_delete.join(',')
|
46
74
|
end
|
47
|
-
|
75
|
+
parts << build_from_clause
|
76
|
+
return combine_parts(parts)
|
77
|
+
end
|
78
|
+
|
79
|
+
def shared_select(fields)
|
80
|
+
parts = ['SELECT']
|
81
|
+
if @data.straight_join
|
82
|
+
parts << 'STRAIGHT_JOIN'
|
83
|
+
end
|
84
|
+
if @data.distinct
|
85
|
+
parts << 'DISTINCT'
|
86
|
+
end
|
87
|
+
parts << fields.join(',')
|
88
|
+
parts << build_from_clause
|
89
|
+
return combine_parts(parts)
|
48
90
|
end
|
49
91
|
|
50
92
|
def build_from_clause
|
51
|
-
|
52
|
-
|
93
|
+
parts = ['FROM']
|
94
|
+
parts << build_table_list
|
95
|
+
parts << build_join_clause
|
96
|
+
parts << build_where_clause
|
97
|
+
parts << simple_clause('GROUP BY', @data.group_by)
|
53
98
|
if @data.with_rollup
|
54
|
-
|
99
|
+
parts << 'WITH ROLLUP'
|
100
|
+
end
|
101
|
+
if !@data.havings.empty?
|
102
|
+
parts << "HAVING #{@data.havings.join(' AND ')}"
|
55
103
|
end
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
return " FROM #{build_table_list}#{join_clause}#{build_where_clause}#{group_clause}#{having_clause}#{order_clause}#{limit_clause}"
|
104
|
+
parts << simple_clause('ORDER BY', @data.order_by)
|
105
|
+
parts << simple_clause('LIMIT', @data.limit)
|
106
|
+
return combine_parts(parts)
|
60
107
|
end
|
61
108
|
|
62
109
|
def build_set_clause
|
@@ -87,14 +134,7 @@ class MysqlBuilder
|
|
87
134
|
end
|
88
135
|
|
89
136
|
def build_join_clause
|
90
|
-
|
91
|
-
return ''
|
92
|
-
else
|
93
|
-
# we call uniq here to be tolerant of a table being joined to multiple times in an identical fashion
|
94
|
-
# where the intention is not actually to include the table multiple times
|
95
|
-
# but I'm thinking we may want to reconsider, or at least warn when this happens so the source bug can be fixed
|
96
|
-
return ' ' + @data.joins.map {|join| join_to_str(join)}.uniq.join(' ')
|
97
|
-
end
|
137
|
+
return ' ' + @data.joins.map {|join| join_to_str(join)}.uniq.join(' ')
|
98
138
|
end
|
99
139
|
|
100
140
|
def build_where_clause
|
data/lib/sqlstmt/sqlstmt.rb
CHANGED
@@ -47,7 +47,7 @@ class SqlStmt
|
|
47
47
|
end
|
48
48
|
|
49
49
|
def type(stmt_type)
|
50
|
-
if @data.stmt_type
|
50
|
+
if @data.stmt_type && @data.stmt_type != stmt_type
|
51
51
|
raise SqlStmtError, "statement type already set to #{@data.stmt_type}"
|
52
52
|
end
|
53
53
|
@data.stmt_type = stmt_type
|
@@ -79,13 +79,24 @@ class SqlStmt
|
|
79
79
|
return @data.table_ids.include?(table_to_find)
|
80
80
|
end
|
81
81
|
|
82
|
-
###### where
|
82
|
+
###### where - because the lack of a where clause can be so impactful,
|
83
|
+
###### the default behavior is to require one, but this behavior can be customized using the 3 methods below
|
84
|
+
###### note that each of these override the other calls, so whichever is one called last will take effect
|
83
85
|
|
86
|
+
# if no where clauses have been added, the statement will fail to build
|
87
|
+
# this is the default value, but in case it got changed, we can reset it back
|
88
|
+
def require_where
|
89
|
+
@data.where_behavior = :require
|
90
|
+
return self
|
91
|
+
end
|
92
|
+
|
93
|
+
# if any where clauses have been added, the statement will fail to build
|
84
94
|
def no_where
|
85
95
|
@data.where_behavior = :exclude
|
86
96
|
return self
|
87
97
|
end
|
88
98
|
|
99
|
+
# this disables a where clause requirement
|
89
100
|
def optional_where
|
90
101
|
@data.where_behavior = :optional
|
91
102
|
return self
|
data/test/insert_select_test.rb
CHANGED
@@ -16,6 +16,12 @@ class TestInsertSelect < Minitest::Test
|
|
16
16
|
assert_equal('INSERT INTO target SELECT * FROM source', SqlStmt.new.insert.into('target').table('source').set(nil, '*').no_where.to_s)
|
17
17
|
end
|
18
18
|
|
19
|
+
def test_ignore_duplicate
|
20
|
+
sqlt = SqlStmt.new.insert.into('target').table('source').set(nil, '*').no_where
|
21
|
+
assert_equal('INSERT INTO target SELECT * FROM source ON DUPLICATE KEY UPDATE idkey = idkey', sqlt.dup.ignore_duplicate('idkey').to_s)
|
22
|
+
assert_equal('INSERT INTO target SELECT * FROM source ON DUPLICATE KEY UPDATE idkey = idkey + 1', sqlt.dup.on_duplicate('idkey = idkey + 1').to_s)
|
23
|
+
end
|
24
|
+
|
19
25
|
def test_dup
|
20
26
|
shared_builder = SqlStmt.new.insert.into('target')
|
21
27
|
first_builder = shared_builder
|
data/test/select_test.rb
CHANGED
@@ -1,64 +1,96 @@
|
|
1
1
|
require_relative 'helper'
|
2
2
|
|
3
|
+
# adding this extra line so lines can match up perfectly for now with the swift implementation
|
3
4
|
class TestSelect < Minitest::Test
|
4
|
-
def
|
5
|
-
|
6
|
-
assert(sqlb.includes_table?('target'))
|
7
|
-
assert_equal(['target'], sqlb.data.table_ids.to_a)
|
8
|
-
|
9
|
-
sqlb = SqlStmt.new.select.table('target t')
|
10
|
-
assert(sqlb.includes_table?('target'))
|
11
|
-
assert(sqlb.includes_table?('t'))
|
12
|
-
assert(!sqlb.includes_table?('blah'))
|
13
|
-
|
14
|
-
sqlb = SqlStmt.new.select.table('target AS t')
|
15
|
-
assert(sqlb.includes_table?('target'))
|
16
|
-
assert(sqlb.includes_table?('t'))
|
17
|
-
end
|
5
|
+
def test_gradually
|
6
|
+
sqlt = SqlStmt.new
|
18
7
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
end
|
8
|
+
# keep these chained together, so we have at least one test of the return value of all the method calls
|
9
|
+
# in ruby this isn't particular tricky or unique, but still it's good to test
|
10
|
+
sqlt.select.type('select')
|
11
|
+
assert_raises(SqlStmtError) { sqlt.update() }
|
24
12
|
|
25
|
-
|
26
|
-
|
27
|
-
assert_raises(SqlStmtError) {
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
13
|
+
sqlt.table('target')
|
14
|
+
assert_equal(['target'], sqlt.data.table_ids.to_a())
|
15
|
+
assert_raises(SqlStmtError) { sqlt.to_s() }
|
16
|
+
|
17
|
+
sqlt.get('blah')
|
18
|
+
assert_raises(SqlStmtError) { sqlt.to_s() }
|
19
|
+
|
20
|
+
sqlt.no_where()
|
21
|
+
assert_equal('SELECT blah FROM target', sqlt.to_sql())
|
32
22
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
assert_equal('SELECT blah FROM
|
38
|
-
|
39
|
-
|
40
|
-
assert_equal('SELECT blah
|
41
|
-
|
23
|
+
sqlt.require_where()
|
24
|
+
assert_raises(SqlStmtError) { sqlt.to_s }
|
25
|
+
|
26
|
+
sqlt.optional_where()
|
27
|
+
assert_equal('SELECT blah FROM target', sqlt.to_sql())
|
28
|
+
|
29
|
+
sqlt.where('frog = 1')
|
30
|
+
assert_equal('SELECT blah FROM target WHERE frog = 1', sqlt.to_sql())
|
31
|
+
|
32
|
+
sqlt.join('other o', 'target.id = o.id')
|
33
|
+
assert_equal(['target', 'other', 'o'], sqlt.data.table_ids.to_a)
|
34
|
+
assert_equal('SELECT blah FROM target JOIN other o ON target.id = o.id WHERE frog = 1', sqlt.to_sql())
|
42
35
|
end
|
43
36
|
|
44
|
-
def
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
37
|
+
def test_simple_with_small_variations
|
38
|
+
tmpl = SqlStmt.new
|
39
|
+
tmpl.select().table('target t').get('blah').no_where()
|
40
|
+
assert_equal(['target', 't'], tmpl.data.table_ids.to_a())
|
41
|
+
|
42
|
+
sqlt = tmpl.dup()
|
43
|
+
sqlt.distinct
|
44
|
+
assert_equal('SELECT DISTINCT blah FROM target t', sqlt.to_sql())
|
45
|
+
|
46
|
+
sqlt = tmpl.dup()
|
47
|
+
sqlt.join('other o', 't.blah_id = o.blah_id', 't.blee_id = o.blee_id')
|
48
|
+
assert_equal('SELECT blah FROM target t JOIN other o ON t.blah_id = o.blah_id AND t.blee_id = o.blee_id', sqlt.to_sql())
|
49
|
+
|
50
|
+
sqlt = tmpl.dup()
|
51
|
+
sqlt.left_join('other o', 't.blah_id = o.blah_id')
|
52
|
+
assert_equal('SELECT blah FROM target t LEFT JOIN other o ON t.blah_id = o.blah_id', sqlt.to_sql())
|
53
|
+
|
54
|
+
sqlt = tmpl.dup()
|
55
|
+
sqlt.get('blee', 'bloo')
|
56
|
+
assert_equal('SELECT blah,blee,bloo FROM target t', sqlt.to_sql())
|
57
|
+
|
58
|
+
sqlt = tmpl.dup()
|
59
|
+
sqlt.having('blah > 0')
|
60
|
+
assert_equal('SELECT blah FROM target t HAVING blah > 0', sqlt.to_sql())
|
61
|
+
|
62
|
+
sqlt = tmpl.dup()
|
63
|
+
sqlt.group_by('blah')
|
64
|
+
assert_equal('SELECT blah FROM target t GROUP BY blah', sqlt.to_sql())
|
65
|
+
sqlt.with_rollup
|
66
|
+
assert_equal('SELECT blah FROM target t GROUP BY blah WITH ROLLUP', sqlt.to_sql())
|
49
67
|
end
|
50
68
|
|
51
|
-
def
|
52
|
-
|
53
|
-
|
54
|
-
|
69
|
+
def test_tables
|
70
|
+
sqlt = SqlStmt.new.select().table('target t', 'blee').no_where.get('t.blah')
|
71
|
+
assert_equal('SELECT t.blah FROM target t USE INDEX (blee)', sqlt.to_sql())
|
72
|
+
|
73
|
+
sqlt = SqlStmt.new.select().table('target')
|
74
|
+
assert(sqlt.includes_table?('target'))
|
75
|
+
assert(!sqlt.includes_table?('blah'))
|
76
|
+
|
77
|
+
sqlt = SqlStmt.new.select().table('target t')
|
78
|
+
assert(sqlt.includes_table?('target'))
|
79
|
+
assert(sqlt.includes_table?('t'))
|
80
|
+
|
81
|
+
sqlt = SqlStmt.new.select().table('target AS t')
|
82
|
+
assert(sqlt.includes_table?('target'))
|
83
|
+
assert(sqlt.includes_table?('t'))
|
84
|
+
|
85
|
+
sqlt.join('other o', 't.blah_id = o.blah_id')
|
86
|
+
assert(sqlt.includes_table?('other'))
|
87
|
+
assert(sqlt.includes_table?('o'))
|
55
88
|
end
|
56
89
|
|
57
|
-
def
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
end
|
90
|
+
def test_duplicate_joins
|
91
|
+
sqlt = SqlStmt.new.select().table('source s').get('frog').no_where
|
92
|
+
4.times { sqlt.join('other o', 's.blah_id = o.blah_id') }
|
93
|
+
assert_equal('SELECT frog FROM source s JOIN other o ON s.blah_id = o.blah_id', sqlt.to_s)
|
94
|
+
assert_equal('other o', sqlt.data.joins.first.table.str)
|
63
95
|
end
|
64
96
|
end
|
data/test/to_sql_test.rb
CHANGED
@@ -1,15 +1,23 @@
|
|
1
1
|
require_relative 'helper'
|
2
2
|
|
3
3
|
class Test_to_sql < Minitest::Test
|
4
|
-
def
|
5
|
-
assert_equal("'blah'", 'blah'.to_sql)
|
4
|
+
def test_to_sql
|
6
5
|
assert_equal('NULL', nil.to_sql)
|
6
|
+
|
7
|
+
assert_equal("'blah'", 'blah'.to_sql)
|
8
|
+
assert_equal("'b\\\\lah'", "b\\lah".to_sql())
|
9
|
+
assert_equal("'b\\'lah'", "b'lah".to_sql())
|
10
|
+
assert_equal("'b\\\"lah'", "b\"lah".to_sql())
|
11
|
+
|
7
12
|
assert_equal('3', 3.to_sql)
|
8
|
-
assert_equal(
|
9
|
-
|
13
|
+
assert_equal('10.0', BigDecimal.new('10').to_sql)
|
14
|
+
|
10
15
|
assert_equal('1', true.to_sql)
|
11
16
|
assert_equal('0', false.to_sql)
|
12
|
-
|
17
|
+
|
18
|
+
assert_equal("'2008-09-24'", Date.new(2008,9,24).to_sql)
|
19
|
+
assert_equal("'2008-09-24 09:30:04'", DateTime.new(2008,9,24,9,30,4).to_sql)
|
20
|
+
|
13
21
|
assert_equal("('a','b','c')", ['a', 'b', 'c'].to_sql)
|
14
22
|
end
|
15
23
|
end
|
metadata
CHANGED
@@ -1,15 +1,14 @@
|
|
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.12
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
|
-
-
|
8
|
-
- Kem Mason
|
7
|
+
- ATPSoft
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date:
|
11
|
+
date: 2019-09-10 00:00:00.000000000 Z
|
13
12
|
dependencies: []
|
14
13
|
description: Build SQL statements using method calls instead of strings. This is not
|
15
14
|
an ORM. It has only been used and tested with MySQL so far but the intention is
|
@@ -57,7 +56,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
57
56
|
version: '0'
|
58
57
|
requirements: []
|
59
58
|
rubyforge_project:
|
60
|
-
rubygems_version: 2.6.
|
59
|
+
rubygems_version: 2.6.12
|
61
60
|
signing_key:
|
62
61
|
specification_version: 4
|
63
62
|
summary: build SQL statements using method calls instead of strings
|