sqlstmt 0.2.10 → 0.2.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/sqlstmt/data.rb +26 -43
- data/lib/sqlstmt/mysql/build.rb +14 -13
- data/lib/sqlstmt/mysql/check.rb +7 -7
- data/lib/sqlstmt/sqlstmt.rb +29 -69
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e5516750b663f84d0ee52a37951a305e8c003914
|
4
|
+
data.tar.gz: 2c80189865a625385c7da900c8cc8fe494a3860c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 07d9232c1d6473704cda766e7c12436a8677f2589939201b3dd8681801f66bf6ca72fb9c2b5b9c301c2a945d3d3792d430687a82e2d4755aa94d4d0a81d5f33d
|
7
|
+
data.tar.gz: a08886bb3a3fdfbce82fed743ba2ec339c16d7acc33bb2b789025b24cc0bca0a58853da1ec50f6cc34f314e6c0d6ad10fdc6b839937e17f621e3fd76c47c0164
|
data/lib/sqlstmt/data.rb
CHANGED
@@ -3,62 +3,45 @@ require 'set'
|
|
3
3
|
module SqlStmtLib
|
4
4
|
extend self
|
5
5
|
|
6
|
-
|
7
|
-
|
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
|
8
|
+
MULTI_VALUE_KEYWORDS = %i(get having where).freeze
|
9
|
+
|
10
|
+
# :str is the full original string specifying the table, like 'frog f' or 'frog AS f' or 'frog'
|
11
|
+
# :name is the full name of the table
|
12
|
+
# :alias is the alias specified for the table, or if none is specified, it's the same as :name
|
13
|
+
# this may be the wrong approach, but for now at least, it seems the most intuitive/useful option
|
14
|
+
# :index is used to specify a "USE INDEX" clause
|
8
15
|
SqlTable = Struct.new(:str, :name, :alias, :index)
|
9
16
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
17
|
+
# kwstr is the keyword string, like 'JOIN' or 'LEFT JOIN'
|
18
|
+
# table is a SqlTable object, representing the table being joined to
|
19
|
+
# on_expr is the ON expression for the join
|
20
|
+
SqlJoin = Struct.new(:kwstr, :table, :on_expr)
|
14
21
|
|
15
|
-
|
16
|
-
|
17
|
-
|
22
|
+
# :table_ids is a set of all table names and aliases, including ones added by a join
|
23
|
+
SPECIAL_DATA_FIELDS = %i(stmt_type table_ids where_behavior).freeze
|
24
|
+
ARRAY_DATA_FIELDS = MULTI_VALUE_KEYWORDS.map {|keyword| "#{keyword}s".to_sym} + %i(tables joins set_fields set_values tables_to_delete).freeze
|
18
25
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
:group_by,
|
24
|
-
:order_by,
|
25
|
-
:limit,
|
26
|
-
:having,
|
27
|
-
:into_table,
|
28
|
-
:tables_to_delete,
|
29
|
-
:distinct,
|
30
|
-
:straight_join,
|
31
|
-
:replace,
|
32
|
-
:ignore,
|
33
|
-
:outfile,
|
34
|
-
:with_rollup,
|
35
|
-
:called_get,
|
36
|
-
) do
|
26
|
+
# calling uniq on this in case some fields end up in multiple categories
|
27
|
+
ALL_DATA_FIELDS = (FLAG_KEYWORDS + SINGLE_VALUE_KEYWORDS + ARRAY_DATA_FIELDS + SPECIAL_DATA_FIELDS).uniq
|
28
|
+
|
29
|
+
SqlData = Struct.new(*ALL_DATA_FIELDS) do
|
37
30
|
def initialize
|
38
|
-
self.tables = []
|
39
|
-
self.joins = []
|
40
31
|
self.table_ids = Set.new
|
41
|
-
self.wheres = []
|
42
32
|
self.where_behavior = :require
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
self.tables_to_delete = []
|
47
|
-
self.ignore = ''
|
48
|
-
self.outfile = ''
|
49
|
-
self.called_get = false
|
33
|
+
ARRAY_DATA_FIELDS.each do |field|
|
34
|
+
self[field] = []
|
35
|
+
end
|
50
36
|
end
|
51
37
|
|
52
38
|
def initialize_copy(orig)
|
53
39
|
# without this call to super, any field that we aren't dup'ing here won't be copied
|
54
40
|
super
|
55
|
-
|
56
|
-
|
41
|
+
ARRAY_DATA_FIELDS.each do |field|
|
42
|
+
self[field] = self[field].dup
|
43
|
+
end
|
57
44
|
self.table_ids = orig.table_ids.dup
|
58
|
-
self.wheres = orig.wheres.dup
|
59
|
-
self.fields = orig.fields.dup
|
60
|
-
self.values = orig.values.dup
|
61
|
-
self.having = orig.having.dup
|
62
45
|
end
|
63
46
|
|
64
47
|
end
|
data/lib/sqlstmt/mysql/build.rb
CHANGED
@@ -14,8 +14,9 @@ class MysqlBuilder
|
|
14
14
|
def build_stmt_select
|
15
15
|
straight_join_str = @data.straight_join ? 'STRAIGHT_JOIN ' : ''
|
16
16
|
distinct_str = @data.distinct ? 'DISTINCT ' : ''
|
17
|
-
select_str = @data.
|
18
|
-
|
17
|
+
select_str = @data.gets.join(',')
|
18
|
+
outfile_str = @data.outfile ? " INTO OUTFILE #{@data.outfile}" : ''
|
19
|
+
return "SELECT #{straight_join_str}#{distinct_str}#{select_str}#{build_from_clause}#{outfile_str}"
|
19
20
|
end
|
20
21
|
|
21
22
|
def build_stmt_update
|
@@ -25,10 +26,11 @@ class MysqlBuilder
|
|
25
26
|
|
26
27
|
def build_stmt_insert
|
27
28
|
keyword = @data.replace ? 'REPLACE' : 'INSERT'
|
28
|
-
value_list = @data.
|
29
|
-
|
30
|
-
|
31
|
-
|
29
|
+
value_list = @data.set_values.join(',')
|
30
|
+
ignore_str = @data.ignore ? 'IGNORE ' : ''
|
31
|
+
start_str = "#{keyword} #{ignore_str}INTO #{@data.into} "
|
32
|
+
if !@data.set_fields.empty?
|
33
|
+
field_list = @data.set_fields.join(',')
|
32
34
|
start_str += "(#{field_list}) "
|
33
35
|
end
|
34
36
|
|
@@ -53,14 +55,14 @@ class MysqlBuilder
|
|
53
55
|
end
|
54
56
|
order_clause = simple_clause('ORDER BY', @data.order_by)
|
55
57
|
limit_clause = simple_clause('LIMIT', @data.limit)
|
56
|
-
having_clause = @data.
|
58
|
+
having_clause = @data.havings.empty? ? '' : " HAVING #{@data.havings.join(' AND ')}"
|
57
59
|
return " FROM #{build_table_list}#{join_clause}#{build_where_clause}#{group_clause}#{having_clause}#{order_clause}#{limit_clause}"
|
58
60
|
end
|
59
61
|
|
60
62
|
def build_set_clause
|
61
63
|
set_exprs = []
|
62
|
-
@data.
|
63
|
-
set_exprs << "#{field} = #{@data.
|
64
|
+
@data.set_fields.each_with_index do |field, index|
|
65
|
+
set_exprs << "#{field} = #{@data.set_values[index]}"
|
64
66
|
end
|
65
67
|
return set_exprs.join(', ')
|
66
68
|
end
|
@@ -80,9 +82,8 @@ class MysqlBuilder
|
|
80
82
|
return value ? " #{keywords} #{value}" : ''
|
81
83
|
end
|
82
84
|
|
83
|
-
def join_to_str(
|
84
|
-
kwstr,
|
85
|
-
return [kwstr, tbl.str, on_expr].join(' ')
|
85
|
+
def join_to_str(join)
|
86
|
+
return [join.kwstr, join.table.str, join.on_expr].join(' ')
|
86
87
|
end
|
87
88
|
|
88
89
|
def build_join_clause
|
@@ -92,7 +93,7 @@ class MysqlBuilder
|
|
92
93
|
# we call uniq here to be tolerant of a table being joined to multiple times in an identical fashion
|
93
94
|
# where the intention is not actually to include the table multiple times
|
94
95
|
# but I'm thinking we may want to reconsider, or at least warn when this happens so the source bug can be fixed
|
95
|
-
return ' ' + @data.joins.map {|
|
96
|
+
return ' ' + @data.joins.map {|join| join_to_str(join)}.uniq.join(' ')
|
96
97
|
end
|
97
98
|
end
|
98
99
|
|
data/lib/sqlstmt/mysql/check.rb
CHANGED
@@ -36,26 +36,26 @@ class MysqlChecker
|
|
36
36
|
send(method_name)
|
37
37
|
|
38
38
|
if @data.stmt_type != 'select'
|
39
|
-
raise SqlStmtError, "must not call :get on #{@data.stmt_type} statement" if
|
39
|
+
raise SqlStmtError, "must not call :get on #{@data.stmt_type} statement" if !@data.gets.empty?
|
40
40
|
end
|
41
41
|
end
|
42
42
|
|
43
43
|
def check_stmt_select
|
44
|
-
raise SqlStmtError, "must call :get on select statement" if @data.
|
45
|
-
raise SqlStmtError, "must not call :set on select statement" if !@data.
|
44
|
+
raise SqlStmtError, "must call :get on select statement" if @data.gets.empty?
|
45
|
+
raise SqlStmtError, "must not call :set on select statement" if !@data.set_values.empty?
|
46
46
|
end
|
47
47
|
|
48
48
|
def check_stmt_update
|
49
|
-
raise SqlStmtError, "must call :set on update statement" if @data.
|
49
|
+
raise SqlStmtError, "must call :set on update statement" if @data.set_values.empty?
|
50
50
|
end
|
51
51
|
|
52
52
|
def check_stmt_insert
|
53
|
-
raise SqlStmtError, "must call :set on insert statement" if @data.
|
54
|
-
raise SqlStmtError, "must call :into on insert statement" if @data.
|
53
|
+
raise SqlStmtError, "must call :set on insert statement" if @data.set_values.empty?
|
54
|
+
raise SqlStmtError, "must call :into on insert statement" if @data.into.nil?
|
55
55
|
end
|
56
56
|
|
57
57
|
def check_stmt_delete
|
58
|
-
raise SqlStmtError, "must not call :set on delete statement" if !@data.
|
58
|
+
raise SqlStmtError, "must not call :set on delete statement" if !@data.set_values.empty?
|
59
59
|
if @data.tables_to_delete.empty? && ((@data.tables.size + @data.joins.size) > 1)
|
60
60
|
raise SqlStmtError, "must specify tables to delete when including multiple tables"
|
61
61
|
end
|
data/lib/sqlstmt/sqlstmt.rb
CHANGED
@@ -10,7 +10,7 @@ require 'sqlstmt/error'
|
|
10
10
|
# for example, we might build a statement and add a where clause to it
|
11
11
|
# and some step later on would determine the statement type
|
12
12
|
# also, looking to the future of supporting other dialects of SQL, I think the same will be true there
|
13
|
-
# meaning, we
|
13
|
+
# meaning, we allow the choice of SQL dialect to be made at any time
|
14
14
|
|
15
15
|
# unless there is something better to return, methods return self so they can be chained together
|
16
16
|
class SqlStmt
|
@@ -21,6 +21,8 @@ class SqlStmt
|
|
21
21
|
end
|
22
22
|
|
23
23
|
def initialize_copy(_orig)
|
24
|
+
# I don't really know why this is necessary, but it seems to be
|
25
|
+
super
|
24
26
|
@data = @data.dup
|
25
27
|
end
|
26
28
|
|
@@ -69,7 +71,7 @@ class SqlStmt
|
|
69
71
|
|
70
72
|
def any_join(kwstr, ref, exprs)
|
71
73
|
tbl = include_table(ref)
|
72
|
-
@data.joins <<
|
74
|
+
@data.joins << SqlStmtLib::SqlJoin.new(kwstr, tbl, "ON #{exprs.join(' AND ')}")
|
73
75
|
return self
|
74
76
|
end
|
75
77
|
|
@@ -79,11 +81,6 @@ class SqlStmt
|
|
79
81
|
|
80
82
|
###### where
|
81
83
|
|
82
|
-
def where(*expr)
|
83
|
-
@data.wheres.concat(expr)
|
84
|
-
return self
|
85
|
-
end
|
86
|
-
|
87
84
|
def no_where
|
88
85
|
@data.where_behavior = :exclude
|
89
86
|
return self
|
@@ -96,21 +93,19 @@ class SqlStmt
|
|
96
93
|
|
97
94
|
###### fields & values
|
98
95
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
return self
|
103
|
-
end
|
104
|
-
|
96
|
+
# nil can be passed in for the field, in which case it won't be added
|
97
|
+
# this is only for the special case of INSERT INTO table SELECT b.* FROM blah b WHERE ...
|
98
|
+
# where there are no specific fields listed
|
105
99
|
def set(field, value)
|
106
|
-
|
107
|
-
|
108
|
-
|
100
|
+
if @data.set_fields.include?(field)
|
101
|
+
raise SqlStmtError, "trying to set field #{field} again"
|
102
|
+
end
|
103
|
+
|
109
104
|
if field
|
110
|
-
@data.
|
105
|
+
@data.set_fields << field
|
111
106
|
end
|
112
107
|
value = value.is_a?(String) ? value : value.to_sql
|
113
|
-
@data.
|
108
|
+
@data.set_values << value
|
114
109
|
return self
|
115
110
|
end
|
116
111
|
|
@@ -118,62 +113,27 @@ class SqlStmt
|
|
118
113
|
return set(field, value.to_sql)
|
119
114
|
end
|
120
115
|
|
121
|
-
######
|
122
|
-
|
123
|
-
def group_by(expr)
|
124
|
-
@data.group_by = expr
|
125
|
-
return self
|
126
|
-
end
|
127
|
-
|
128
|
-
def order_by(expr)
|
129
|
-
@data.order_by = expr
|
130
|
-
return self
|
131
|
-
end
|
132
|
-
|
133
|
-
def limit(clause)
|
134
|
-
@data.limit = clause
|
135
|
-
return self
|
136
|
-
end
|
137
|
-
|
138
|
-
def having(*expr)
|
139
|
-
@data.having.concat(expr)
|
140
|
-
return self
|
141
|
-
end
|
142
|
-
|
143
|
-
def with_rollup
|
144
|
-
@data.with_rollup = true
|
145
|
-
return self
|
146
|
-
end
|
147
|
-
|
148
|
-
# used with INSERT statements only
|
149
|
-
def into(into_table)
|
150
|
-
@data.into_table = into_table
|
151
|
-
return self
|
152
|
-
end
|
153
|
-
|
154
|
-
def distinct
|
155
|
-
@data.distinct = true
|
156
|
-
return self
|
157
|
-
end
|
116
|
+
###### these are simple wrappers around various keywords
|
158
117
|
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
def replace
|
165
|
-
@data.replace = true
|
166
|
-
return self
|
118
|
+
SqlStmtLib::FLAG_KEYWORDS.each do |keyword|
|
119
|
+
define_method(keyword) do
|
120
|
+
@data[keyword] = true
|
121
|
+
return self
|
122
|
+
end
|
167
123
|
end
|
168
124
|
|
169
|
-
|
170
|
-
|
171
|
-
|
125
|
+
SqlStmtLib::SINGLE_VALUE_KEYWORDS.each do |keyword|
|
126
|
+
define_method(keyword) do |value|
|
127
|
+
@data[keyword] = value
|
128
|
+
return self
|
129
|
+
end
|
172
130
|
end
|
173
131
|
|
174
|
-
|
175
|
-
|
176
|
-
|
132
|
+
SqlStmtLib::MULTI_VALUE_KEYWORDS.each do |keyword|
|
133
|
+
define_method(keyword) do |*values|
|
134
|
+
@data["#{keyword}s"].concat(values)
|
135
|
+
return self
|
136
|
+
end
|
177
137
|
end
|
178
138
|
|
179
139
|
private
|
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.11
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Makani Mason
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2018-08-
|
12
|
+
date: 2018-08-13 00:00:00.000000000 Z
|
13
13
|
dependencies: []
|
14
14
|
description: Build SQL statements using method calls instead of strings. This is not
|
15
15
|
an ORM. It has only been used and tested with MySQL so far but the intention is
|