sqlstmt 0.2.10 → 0.2.11

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: 0f15173de416b124dcceb20abbd5ed52b75a02fa
4
- data.tar.gz: 32cf06f1abe2899c9bd4a576f41652434a5f5c15
3
+ metadata.gz: e5516750b663f84d0ee52a37951a305e8c003914
4
+ data.tar.gz: 2c80189865a625385c7da900c8cc8fe494a3860c
5
5
  SHA512:
6
- metadata.gz: 493cc16b5693113549812f69a8eae2088cbee81afbd12ca3b7f84c7fdf83364e8e2bc8fbb01fa5af205430c465aa18acf5f6cd4041f4a91889f877d221cbbd6b
7
- data.tar.gz: 53df18c4c4c03a10ccb8415f66cd803e29c829528f8c49352b7176dbf7d5959c285c25fe1afd17ccaf83c98c19856fa38b305162f8cdf5f22530bd61f5f4991e
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
- # in the case where only one identifier is specified, :name and :alias are both set to that value
7
- # this may be the wrong approach, but for now at least, it seems the most intuitive/useful option
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
- SqlData = Struct.new(
11
- :stmt_type,
12
- :tables,
13
- :joins,
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
- # set of all table names and aliases
16
- # this includes ones added by a join
17
- :table_ids,
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
- :wheres,
20
- :where_behavior,
21
- :fields,
22
- :values,
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
- self.fields = []
44
- self.values = []
45
- self.having = []
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
- self.tables = orig.tables.dup
56
- self.joins = orig.joins.dup
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
@@ -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.fields.join(',')
18
- return "SELECT #{straight_join_str}#{distinct_str}#{select_str}#{build_from_clause}#{@data.outfile}"
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.values.join(',')
29
- start_str = "#{keyword} #{@data.ignore}INTO #{@data.into_table} "
30
- if !@data.fields.empty?
31
- field_list = @data.fields.join(',')
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.having.empty? ? '' : " HAVING #{@data.having.join(' AND ')}"
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.fields.each_with_index do |field, index|
63
- set_exprs << "#{field} = #{@data.values[index]}"
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(join_ary)
84
- kwstr, tbl, on_expr = join_ary
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 {|ary| join_to_str(ary)}.uniq.join(' ')
96
+ return ' ' + @data.joins.map {|join| join_to_str(join)}.uniq.join(' ')
96
97
  end
97
98
  end
98
99
 
@@ -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 @data.called_get
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.fields.empty?
45
- raise SqlStmtError, "must not call :set on select statement" if !@data.values.empty?
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.values.empty?
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.values.empty?
54
- raise SqlStmtError, "must call :into on insert statement" if @data.into_table.nil?
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.values.empty?
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
@@ -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 don't the choice of SQL dialect to be allowed at any time
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 << [kwstr, tbl, "ON #{exprs.join(' AND ')}"]
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
- def get(*exprs)
100
- @data.fields.concat(exprs)
101
- @data.called_get = true
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
- raise SqlStmtError, "trying to include field #{field} again" if @data.fields.include?(field)
107
- # this is to support the special case of INSERT INTO table SELECT * FROM ...
108
- # where * specified with no matching insert field list specified
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.fields << field
105
+ @data.set_fields << field
111
106
  end
112
107
  value = value.is_a?(String) ? value : value.to_sql
113
- @data.values << value
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
- ###### to be sorted
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
- def straight_join
160
- @data.straight_join = true
161
- return self
162
- end
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
- def ignore
170
- @data.ignore = 'IGNORE '
171
- return self
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
- def outfile(str)
175
- @data.outfile = " INTO OUTFILE #{str}"
176
- return self
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.10
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-03 00:00:00.000000000 Z
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