sqb 1.0.2 → 1.0.3

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
  SHA256:
3
- metadata.gz: 36cfacce28d143bb31c2b714cc2b4a99744a7dfd117832e0952979767bfcd7af
4
- data.tar.gz: 72efb2953532af5f444b55833e3342138bfe68a94560567658e81981d38db043
3
+ metadata.gz: b3c57f511a3e18b3718cbbcd5e6703b52bd0b3d6e92ead8aca02a04ef46b087a
4
+ data.tar.gz: 2fd0e2d396c722bf2d0fcac632a8322be95ab3aa65b64ba70d86b62ca5859d98
5
5
  SHA512:
6
- metadata.gz: 9bad00a3fad97d424d51d7a150ca64055885fdd730d8c925712895176ac6fa61e575433f3bc18b90f165d5a2fa2bda5b32c5e07ba463d0460d39578c9cdcaa76
7
- data.tar.gz: 0b927ede58eaa630e8a665a1d5ee9e564eaaebfd96d8ed29a66a0b0606aab22fe000f75142428386084d512d6238f3450a1642ebf13bdce57bade20a449023a6
6
+ metadata.gz: ba298f55cf459a29818711e146c78b58911a1673c277879d013f2333e31729b733fa686af429931797aad1b1d5dfb0ea43c0b31364d4ac3c1f68f99ded75d4d9
7
+ data.tar.gz: 8e4c5e633e78f78689a2d60e8574bb5e86de9d0c0305ace9e4c46e64daadc9c971a3615da18fa5825e295081e2b3eabd6c61b43d9f185b18de10f98ebdfa83e0
data/lib/sqb.rb CHANGED
@@ -1,7 +1,12 @@
1
- require 'sqb/query'
2
1
  require 'sqb/version'
3
2
  require 'sqb/safe_string'
4
3
 
4
+ require 'sqb/query'
5
+ require 'sqb/select'
6
+ require 'sqb/update'
7
+ require 'sqb/delete'
8
+ require 'sqb/insert'
9
+
5
10
  module SQB
6
11
  def self.safe(string)
7
12
  SafeString.new(string)
data/lib/sqb/base.rb ADDED
@@ -0,0 +1,25 @@
1
+ require 'sqb/error'
2
+ require 'sqb/escaping'
3
+
4
+ module SQB
5
+ class Base
6
+
7
+ include SQB::Escaping
8
+
9
+ attr_reader :prepared_arguments
10
+
11
+ def initialize(table_name, options = {}, &block)
12
+ @table_name = table_name
13
+ @options = options
14
+ @prepared_arguments = []
15
+ block.call(self) if block_given?
16
+ end
17
+
18
+ # Generate the full SQL query for this query.
19
+ #
20
+ # @return [String]
21
+ def to_sql
22
+ end
23
+
24
+ end
25
+ end
@@ -0,0 +1,38 @@
1
+ module SQB
2
+ module Columns
3
+
4
+ # Add a column to the query
5
+ #
6
+ # @param column [String, Symbol, Hash] the column name (or a hash with table & column name)
7
+ # @option options [String] :function a function to wrap around the column
8
+ # @options options [String] :as the name to return this column as
9
+ # @return [Query] returns the query
10
+ def column(column, options = {})
11
+ @columns ||= []
12
+ with_table_and_column(column) do |table, column|
13
+ @columns << [].tap do |query|
14
+ if options[:function]
15
+ query << "#{escape_function(options[:function])}("
16
+ end
17
+ query << escape_and_join(table, column)
18
+ if options[:function]
19
+ query << ")"
20
+ end
21
+ if options[:as]
22
+ query << "AS"
23
+ query << escape(options[:as])
24
+ end
25
+ end.join(' ')
26
+ end
27
+ self
28
+ end
29
+
30
+ # Replace all existing columns with the given column
31
+ def column!(*args)
32
+ @columns = []
33
+ column(*args)
34
+ end
35
+
36
+
37
+ end
38
+ end
data/lib/sqb/delete.rb ADDED
@@ -0,0 +1,35 @@
1
+ require 'sqb/base'
2
+ require 'sqb/filtering'
3
+ require 'sqb/ordering'
4
+ require 'sqb/limiting'
5
+
6
+ module SQB
7
+ class Delete < Base
8
+
9
+ include SQB::Filtering
10
+ include SQB::Ordering
11
+ include SQB::Limiting
12
+
13
+ def to_sql
14
+ [].tap do |query|
15
+ query << "DELETE FROM"
16
+ query << escape_and_join(@options[:database_name], @table_name)
17
+
18
+ if @where && !@where.empty?
19
+ query << "WHERE"
20
+ query << @where.join(' AND ')
21
+ end
22
+
23
+ if @orders && !@orders.empty?
24
+ query << "ORDER BY"
25
+ query << @orders.join(', ')
26
+ end
27
+
28
+ if @limit
29
+ query << "LIMIT #{@limit.to_i}"
30
+ end
31
+ end.join(' ')
32
+ end
33
+
34
+ end
35
+ end
@@ -0,0 +1,10 @@
1
+ module SQB
2
+ module Distinct
3
+
4
+ def distinct
5
+ @distinct = true
6
+ self
7
+ end
8
+
9
+ end
10
+ end
data/lib/sqb/error.rb CHANGED
@@ -10,4 +10,7 @@ module SQB
10
10
 
11
11
  class InvalidOperatorError < Error
12
12
  end
13
+
14
+ class NoValuesError < Error
15
+ end
13
16
  end
@@ -0,0 +1,56 @@
1
+ module SQB
2
+ module Escaping
3
+
4
+ private
5
+
6
+ def escape(name)
7
+ if name.is_a?(SafeString)
8
+ name
9
+ else
10
+ "`#{name.to_s.gsub('`', '``')}`"
11
+ end
12
+ end
13
+
14
+ def escape_function(name)
15
+ if name.is_a?(SafeString)
16
+ name
17
+ else
18
+ name.to_s.gsub(/[^a-z0-9\_]/i, '').upcase
19
+ end
20
+ end
21
+
22
+ def value_escape(value)
23
+ if value == true
24
+ 1
25
+ elsif value == false
26
+ 0
27
+ elsif value.nil?
28
+ 'NULL'
29
+ elsif value.is_a?(Integer)
30
+ value.to_i
31
+ else
32
+ @prepared_arguments << value.to_s
33
+ '?'
34
+ end
35
+ end
36
+
37
+ def with_table_and_column(input, &block)
38
+ if input.is_a?(Hash)
39
+ input.each { |table, column| block.call(table, column) }
40
+ else
41
+ block.call(@table_name, input)
42
+ end
43
+ end
44
+
45
+ def escape_and_join(*parts)
46
+ if parts.last.is_a?(SafeString)
47
+ # If a safe string is provided as a column name, we'll
48
+ # always use this even if a table name is provided too.
49
+ parts.last
50
+ else
51
+ parts.compact.map { |part| escape(part) }.join('.')
52
+ end
53
+ end
54
+
55
+ end
56
+ end
@@ -0,0 +1,109 @@
1
+ module SQB
2
+ module Filtering
3
+
4
+ # Add a condition to the query by providing a hash of keys and values.
5
+ #
6
+ # @param hash [Hash]
7
+ # @return [Query]
8
+ def where(hash)
9
+ if @where_within_or && @where_within_or.last
10
+ @where_within_or.last << hash
11
+ else
12
+ @where ||= []
13
+ @where << hash_to_sql(hash, @table_name)
14
+ end
15
+ self
16
+ end
17
+
18
+ # Set that all conditions added in this block should be joined using OR
19
+ # rather than AND.
20
+ def or(&block)
21
+ @where_within_or ||= []
22
+ # Start by making an array within the OR block for this calling
23
+ @where_within_or << []
24
+ # Execute the block. All queries to 'where' will be added to the last
25
+ # array in the chain (created above)
26
+ block.call
27
+ ensure
28
+ # Start work on a full array of SQL fragments for all OR queries
29
+ @where_within_or_sql ||= []
30
+ # After each OR call, store up the SQL fragment for all where queries
31
+ # executed within the block.
32
+ if w = @where_within_or.pop
33
+ @where_within_or_sql << w.map do |w|
34
+ hash_to_sql(w, @table_name)
35
+ end.join(' OR ')
36
+ end
37
+
38
+ # When there are no fragments in the chain left, add it to the main
39
+ # where chain for the query.
40
+ if @where_within_or.empty?
41
+ @where ||= []
42
+ @where << "(#{@where_within_or_sql.flatten.join(' OR ')})"
43
+ @where_within_or_sql = nil
44
+ end
45
+ self
46
+ end
47
+
48
+ private
49
+
50
+ def hash_to_sql(hash, table, joiner = ' AND ')
51
+ sql = hash.map do |key, value|
52
+ if key.is_a?(Hash)
53
+ table = key.first[0]
54
+ key = key.first[1]
55
+ end
56
+
57
+ key = escape_and_join(table, key)
58
+
59
+ if value.is_a?(Array)
60
+ escaped_values = value.map { |v| value_escape(v) }.join(', ')
61
+ "#{key} IN (#{escaped_values})"
62
+ elsif value.is_a?(Hash)
63
+ sql = []
64
+ value.each do |operator, value|
65
+ case operator
66
+ when :not_equal
67
+ if value.nil?
68
+ sql << "#{key} IS NOT NULL"
69
+ else
70
+ sql << "#{key} != #{value_escape(value)}"
71
+ end
72
+ when :equal
73
+ if value.nil?
74
+ sql << "#{key} IS NULL"
75
+ else
76
+ sql << "#{key} = #{value_escape(value)}"
77
+ end
78
+ when :less_than
79
+ sql << "#{key} < #{value_escape(value)}"
80
+ when :greater_than
81
+ sql << "#{key} > #{value_escape(value)}"
82
+ when :less_than_or_equal_to
83
+ sql << "#{key} <= #{value_escape(value)}"
84
+ when :greater_than_or_equal_to
85
+ sql << "#{key} >= #{value_escape(value)}"
86
+ when :in, :not_in
87
+ escaped_values = value.map { |v| value_escape(v) }.join(', ')
88
+ op = operator == :in ? "IN" : "NOT IN"
89
+ sql << "#{key} #{op} (#{escaped_values})"
90
+ when :like
91
+ sql << "#{key} LIKE #{value_escape(value)}"
92
+ when :not_like
93
+ sql << "#{key} NOT LIKE #{value_escape(value)}"
94
+ else
95
+ raise InvalidOperatorError, "Invalid operator '#{operator}'"
96
+ end
97
+ end
98
+ sql.empty? ? "1=0" : sql.join(joiner)
99
+ elsif value == nil
100
+ "#{key} IS NULL"
101
+ else
102
+ "#{key} = #{value_escape(value)}"
103
+ end
104
+ end.join(joiner)
105
+ "(#{sql})"
106
+ end
107
+
108
+ end
109
+ end
@@ -0,0 +1,17 @@
1
+ module SQB
2
+ module Grouping
3
+
4
+ # Add a grouping
5
+ #
6
+ # @param column [String, Symbol, Hash]
7
+ # @return [Query]
8
+ def group_by(column)
9
+ @groups ||= []
10
+ with_table_and_column(column) do |table, column|
11
+ @groups << escape_and_join(table, column)
12
+ end
13
+ self
14
+ end
15
+
16
+ end
17
+ end
data/lib/sqb/insert.rb ADDED
@@ -0,0 +1,43 @@
1
+ require 'sqb/base'
2
+
3
+ module SQB
4
+ class Insert < Base
5
+
6
+ def to_sql
7
+ [].tap do |query|
8
+ query << "INSERT INTO"
9
+ query << escape_and_join(@options[:database_name], @table_name)
10
+ if @values.nil? || @values.empty?
11
+ raise NoValuesError, "No values have been specified. Use `value` to add values to the query."
12
+ end
13
+ query << "(#{columns.join(', ')})"
14
+ query << "VALUES"
15
+ query << "(#{values.join(', ')})"
16
+ end.join(' ')
17
+ end
18
+
19
+ # Set a value to be inserted
20
+ #
21
+ # @param key [String]
22
+ # @param value [String, nil]
23
+ def value(hash)
24
+ @values ||= {}
25
+ hash.each do |key, value|
26
+ @values[key] = value
27
+ end
28
+ self
29
+ end
30
+ alias_method :values, :value
31
+
32
+ private
33
+
34
+ def columns
35
+ @values.keys.map { |k| escape(k) }
36
+ end
37
+
38
+ def values
39
+ @values.values.map { |v| value_escape(v) }
40
+ end
41
+
42
+ end
43
+ end
data/lib/sqb/joins.rb ADDED
@@ -0,0 +1,55 @@
1
+ module SQB
2
+ module Joins
3
+
4
+ # Add a join
5
+ #
6
+ # @param table_name [String, Symbol]
7
+ # @param foreign_key [String, Symbol]
8
+ # @option options [Hash] :where
9
+ # @option options [Array] :select
10
+ # @return [Query]
11
+ def join(table_name, foreign_key, options = {})
12
+ @joins ||= []
13
+ @joins_name_mapping ||= {}
14
+
15
+ if options[:name]
16
+ join_name = options[:name]
17
+ else
18
+ @joins_name_mapping[table_name] ||= 0
19
+ join_name= "#{table_name}_#{@joins_name_mapping[table_name]}"
20
+ @joins_name_mapping[table_name] += 1
21
+ end
22
+
23
+ @joins << [].tap do |query|
24
+ query << "INNER JOIN"
25
+ query << escape_and_join(@options[:database_name], table_name)
26
+ query << "AS"
27
+ query << escape(join_name)
28
+ query << "ON"
29
+ query << escape_and_join(@table_name, 'id')
30
+ query << "="
31
+ query << escape_and_join(join_name, foreign_key)
32
+ end.join(' ')
33
+
34
+ if options[:where]
35
+ join_where = options[:where].each_with_object({}) do |(column, value), hash|
36
+ hash[{join_name => column}] = value
37
+ end
38
+ where(join_where)
39
+ end
40
+
41
+ if columns = options[:columns]
42
+ for field in columns
43
+ column({join_name => field}, :as => "#{join_name}_#{field}")
44
+ end
45
+ end
46
+
47
+ if g = options[:group_by]
48
+ group_by(join_name => g.is_a?(Symbol) ? g : :id)
49
+ end
50
+
51
+ self
52
+ end
53
+
54
+ end
55
+ end
@@ -0,0 +1,14 @@
1
+ module SQB
2
+ module Limiting
3
+
4
+ # Limit the number of records return
5
+ #
6
+ # @param number [Integer]
7
+ # @return [Query]
8
+ def limit(number)
9
+ @limit = number&.to_i
10
+ self
11
+ end
12
+
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ module SQB
2
+ module Offsetting
3
+
4
+ # Set the offset
5
+ #
6
+ # @param number [Integer]
7
+ # @return [Query]
8
+ def offset(number)
9
+ @offset = number&.to_i
10
+ self
11
+ end
12
+
13
+ end
14
+ end
@@ -0,0 +1,39 @@
1
+ module SQB
2
+ module Ordering
3
+
4
+ VALID_ORDERS = ['ASC', 'DESC']
5
+
6
+ # Add an order column
7
+ #
8
+ # @param column [String, Symbol, Hash]
9
+ # @param direction [String] 'ASC' or 'DESC' (default 'ASC')
10
+ # @return [Query]
11
+ def order(column, direction = nil)
12
+ direction = direction ? direction.to_s.upcase : 'ASC'
13
+
14
+ unless VALID_ORDERS.include?(direction)
15
+ raise InvalidOrderDirectionError, "Invalid order direction #{direction}"
16
+ end
17
+
18
+ @orders ||= []
19
+
20
+ with_table_and_column(column) do |table, column|
21
+ @orders << [escape_and_join(table, column), direction].join(' ')
22
+ end
23
+
24
+ self
25
+ end
26
+
27
+ # Add an order replacing all previous ones
28
+ def order!(*args)
29
+ @orders = []
30
+ order(*args)
31
+ end
32
+
33
+ # Remove all ordering for this query
34
+ def no_order!
35
+ @orders = []
36
+ end
37
+
38
+ end
39
+ end
data/lib/sqb/query.rb CHANGED
@@ -1,375 +1,6 @@
1
- require 'sqb/error'
1
+ require 'sqb/select'
2
2
 
3
3
  module SQB
4
- class Query
5
-
6
- attr_reader :prepared_arguments
7
-
8
- VALID_ORDERS = ['ASC', 'DESC']
9
-
10
- def initialize(table_name, options = {}, &escape_block)
11
- @table_name = table_name
12
- @columns = []
13
- @joins = []
14
- @joins_name_mapping = {}
15
- @where = []
16
- @orders = []
17
- @groups = []
18
- @limit = nil
19
- @offset = nil
20
- @distinct = false
21
- @where_within_or = []
22
- @options = options
23
- @prepared_arguments = []
24
-
25
- if @options[:prepared] == false && escape_block.nil?
26
- raise EscapeBlockMissingError, "An escape block must be provided if prepared statements are disabled."
27
- else
28
- @escape_block = escape_block
29
- end
30
- end
31
-
32
- # Generate the full SQL query for this query.
33
- #
34
- # @return [String]
35
- def to_sql
36
- [].tap do |query|
37
- query << "SELECT"
38
- query << "DISTINCT" if @distinct
39
- if @columns.empty?
40
- query << escape_and_join(@table_name, '*')
41
- else
42
- query << @columns.join(', ')
43
- end
44
- query << "FROM"
45
- query << escape_and_join(@options[:database_name], @table_name)
46
-
47
- unless @joins.empty?
48
- query << @joins.join(' ')
49
- end
50
-
51
- unless @where.empty?
52
- query << "WHERE"
53
- query << @where.join(' AND ')
54
- end
55
-
56
- unless @groups.empty?
57
- query << "GROUP BY"
58
- query << @groups.join(', ')
59
- end
60
-
61
- unless @orders.empty?
62
- query << "ORDER BY"
63
- query << @orders.join(', ')
64
- end
65
-
66
- if @limit
67
- query << "LIMIT #{@limit.to_i}"
68
- end
69
-
70
- if @offset
71
- query << "OFFSET #{@offset.to_i}"
72
- end
73
- end.join(' ')
74
- end
75
-
76
- # Add a column to the query
77
- #
78
- # @param column [String, Symbol, Hash] the column name (or a hash with table & column name)
79
- # @option options [String] :function a function to wrap around the column
80
- # @options options [String] :as the name to return this column as
81
- # @return [Query] returns the query
82
- def column(column, options = {})
83
- with_table_and_column(column) do |table, column|
84
- @columns << [].tap do |query|
85
- if options[:function]
86
- query << "#{escape_function(options[:function])}("
87
- end
88
- query << escape_and_join(table, column)
89
- if options[:function]
90
- query << ")"
91
- end
92
- if options[:as]
93
- query << "AS"
94
- query << escape(options[:as])
95
- end
96
- end.join(' ')
97
- end
98
- self
99
- end
100
-
101
- # Replace all existing columns with the given column
102
- def column!(*args)
103
- @columns = []
104
- column(*args)
105
- end
106
-
107
- # Add a condition to the query by providing a hash of keys and values.
108
- #
109
- # @param hash [Hash]
110
- # @return [Query]
111
- def where(hash)
112
- if @where_within_or.last
113
- @where_within_or.last << hash
114
- else
115
- @where << hash_to_sql(hash, @table_name)
116
- end
117
- self
118
- end
119
-
120
- # Set that all conditions added in this block should be joined using OR
121
- # rather than AND.
122
- def or(&block)
123
- # Start by making an array within the OR block for this calling
124
- @where_within_or << []
125
- # Execute the block. All queries to 'where' will be added to the last
126
- # array in the chain (created above)
127
- block.call
128
- ensure
129
- # Start work on a full array of SQL fragments for all OR queries
130
- @where_within_or_sql ||= []
131
- # After each OR call, store up the SQL fragment for all where queries
132
- # executed within the block.
133
- if w = @where_within_or.pop
134
- @where_within_or_sql << w.map do |w|
135
- hash_to_sql(w, @table_name)
136
- end.join(' OR ')
137
- end
138
-
139
- # When there are no fragments in the chain left, add it to the main
140
- # where chain for the query.
141
- if @where_within_or.empty?
142
- @where << "(#{@where_within_or_sql.flatten.join(' OR ')})"
143
- @where_within_or_sql = nil
144
- end
145
- self
146
- end
147
-
148
- # Limit the number of records return
149
- #
150
- # @param number [Integer]
151
- # @return [Query]
152
- def limit(number)
153
- @limit = number&.to_i
154
- self
155
- end
156
-
157
- # Set the offset
158
- #
159
- # @param number [Integer]
160
- # @return [Query]
161
- def offset(number)
162
- @offset = number&.to_i
163
- self
164
- end
165
-
166
- # Add an order column
167
- #
168
- # @param column [String, Symbol, Hash]
169
- # @param direction [String] 'ASC' or 'DESC' (default 'ASC')
170
- # @return [Query]
171
- def order(column, direction = nil)
172
- direction = direction ? direction.to_s.upcase : 'ASC'
173
-
174
- unless VALID_ORDERS.include?(direction)
175
- raise InvalidOrderDirectionError, "Invalid order direction #{direction}"
176
- end
177
-
178
- with_table_and_column(column) do |table, column|
179
- @orders << [escape_and_join(table, column), direction].join(' ')
180
- end
181
-
182
- self
183
- end
184
-
185
- # Add an order replacing all previous ones
186
- def order!(*args)
187
- @orders = []
188
- order(*args)
189
- end
190
-
191
- # Remove all ordering for this query
192
- def no_order!
193
- @orders = []
194
- end
195
-
196
- # Add a grouping
197
- #
198
- # @param column [String, Symbol, Hash]
199
- # @return [Query]
200
- def group_by(column)
201
- with_table_and_column(column) do |table, column|
202
- @groups << escape_and_join(table, column)
203
- end
204
- self
205
- end
206
-
207
- # Add a join
208
- #
209
- # @param table_name [String, Symbol]
210
- # @param foreign_key [String, Symbol]
211
- # @option options [Hash] :where
212
- # @option options [Array] :select
213
- # @return [Query]
214
- def join(table_name, foreign_key, options = {})
215
-
216
- if options[:name]
217
- join_name = options[:name]
218
- else
219
- @joins_name_mapping[table_name] ||= 0
220
- join_name= "#{table_name}_#{@joins_name_mapping[table_name]}"
221
- @joins_name_mapping[table_name] += 1
222
- end
223
-
224
- @joins << [].tap do |query|
225
- query << "INNER JOIN"
226
- query << escape_and_join(@options[:database_name], table_name)
227
- query << "AS"
228
- query << escape(join_name)
229
- query << "ON"
230
- query << escape_and_join(@table_name, 'id')
231
- query << "="
232
- query << escape_and_join(join_name, foreign_key)
233
- end.join(' ')
234
-
235
- if options[:where]
236
- join_where = options[:where].each_with_object({}) do |(column, value), hash|
237
- hash[{join_name => column}] = value
238
- end
239
- where(join_where)
240
- end
241
-
242
- if columns = options[:columns]
243
- for field in columns
244
- column({join_name => field}, :as => "#{join_name}_#{field}")
245
- end
246
- end
247
-
248
- if g = options[:group_by]
249
- group_by(join_name => g.is_a?(Symbol) ? g : :id)
250
- end
251
-
252
- self
253
- end
254
-
255
- def distinct
256
- @distinct = true
257
- self
258
- end
259
-
260
- private
261
-
262
- def hash_to_sql(hash, table, joiner = ' AND ')
263
- sql = hash.map do |key, value|
264
- if key.is_a?(Hash)
265
- table = key.first[0]
266
- key = key.first[1]
267
- end
268
-
269
- key = escape_and_join(table, key)
270
-
271
- if value.is_a?(Array)
272
- escaped_values = value.map { |v| value_escape(v) }.join(', ')
273
- "#{key} IN (#{escaped_values})"
274
- elsif value.is_a?(Hash)
275
- sql = []
276
- value.each do |operator, value|
277
- case operator
278
- when :not_equal
279
- if value.nil?
280
- sql << "#{key} IS NOT NULL"
281
- else
282
- sql << "#{key} != #{value_escape(value)}"
283
- end
284
- when :equal
285
- if value.nil?
286
- sql << "#{key} IS NULL"
287
- else
288
- sql << "#{key} = #{value_escape(value)}"
289
- end
290
- when :less_than
291
- sql << "#{key} < #{value_escape(value)}"
292
- when :greater_than
293
- sql << "#{key} > #{value_escape(value)}"
294
- when :less_than_or_equal_to
295
- sql << "#{key} <= #{value_escape(value)}"
296
- when :greater_than_or_equal_to
297
- sql << "#{key} >= #{value_escape(value)}"
298
- when :in, :not_in
299
- escaped_values = value.map { |v| value_escape(v) }.join(', ')
300
- op = operator == :in ? "IN" : "NOT IN"
301
- sql << "#{key} #{op} (#{escaped_values})"
302
- when :like
303
- sql << "#{key} LIKE #{value_escape(value)}"
304
- when :not_like
305
- sql << "#{key} NOT LIKE #{value_escape(value)}"
306
- else
307
- raise InvalidOperatorError, "Invalid operator '#{operator}'"
308
- end
309
- end
310
- sql.empty? ? "1=0" : sql.join(joiner)
311
- elsif value == nil
312
- "#{key} IS NULL"
313
- else
314
- "#{key} = #{value_escape(value)}"
315
- end
316
- end.join(joiner)
317
- "(#{sql})"
318
- end
319
-
320
- def escape(name)
321
- if name.is_a?(SafeString)
322
- name
323
- else
324
- "`#{name.to_s.gsub('`', '``')}`"
325
- end
326
- end
327
-
328
- def escape_function(name)
329
- if name.is_a?(SafeString)
330
- name
331
- else
332
- name.to_s.gsub(/[^a-z0-9\_]/i, '').upcase
333
- end
334
- end
335
-
336
- def value_escape(value)
337
- if value == true
338
- 1
339
- elsif value == false
340
- 0
341
- elsif value.nil?
342
- 'NULL'
343
- elsif value.is_a?(Integer)
344
- value.to_i
345
- else
346
- if @options[:prepared] == false
347
- escaped_value = @escape_block ? @escape_block.call(value.to_s) : value.to_s
348
- "'" + escaped_value + "'"
349
- else
350
- @prepared_arguments << value.to_s
351
- '?'
352
- end
353
- end
354
- end
355
-
356
- def with_table_and_column(input, &block)
357
- if input.is_a?(Hash)
358
- input.each { |table, column| block.call(table, column) }
359
- else
360
- block.call(@table_name, input)
361
- end
362
- end
363
-
364
- def escape_and_join(*parts)
365
- if parts.last.is_a?(SafeString)
366
- # If a safe string is provided as a column name, we'll
367
- # always use this even if a table name is provided too.
368
- parts.last
369
- else
370
- parts.compact.map { |part| escape(part) }.join('.')
371
- end
372
- end
373
-
4
+ class Query < Select
374
5
  end
375
6
  end
@@ -1,5 +1,4 @@
1
1
  module SQB
2
2
  class SafeString < String
3
-
4
3
  end
5
4
  end
data/lib/sqb/select.rb ADDED
@@ -0,0 +1,66 @@
1
+ require 'sqb/base'
2
+ require 'sqb/distinct'
3
+ require 'sqb/columns'
4
+ require 'sqb/filtering'
5
+ require 'sqb/joins'
6
+ require 'sqb/ordering'
7
+ require 'sqb/grouping'
8
+ require 'sqb/limiting'
9
+ require 'sqb/offsetting'
10
+
11
+ module SQB
12
+ class Select < Base
13
+
14
+ include SQB::Distinct
15
+ include SQB::Columns
16
+ include SQB::Filtering
17
+ include SQB::Joins
18
+ include SQB::Ordering
19
+ include SQB::Grouping
20
+ include SQB::Limiting
21
+ include SQB::Offsetting
22
+
23
+ def to_sql
24
+ [].tap do |query|
25
+ query << "SELECT"
26
+ query << "DISTINCT" if @distinct
27
+ if @columns.nil? || @columns.empty?
28
+ query << escape_and_join(@table_name, '*')
29
+ else
30
+ query << @columns.join(', ')
31
+ end
32
+
33
+ query << "FROM"
34
+ query << escape_and_join(@options[:database_name], @table_name)
35
+
36
+ if @joins && !@joins.empty?
37
+ query << @joins.join(' ')
38
+ end
39
+
40
+ if @where && !@where.empty?
41
+ query << "WHERE"
42
+ query << @where.join(' AND ')
43
+ end
44
+
45
+ if @groups && !@groups.empty?
46
+ query << "GROUP BY"
47
+ query << @groups.join(', ')
48
+ end
49
+
50
+ if @orders && !@orders.empty?
51
+ query << "ORDER BY"
52
+ query << @orders.join(', ')
53
+ end
54
+
55
+ if @limit
56
+ query << "LIMIT #{@limit.to_i}"
57
+ end
58
+
59
+ if @offset
60
+ query << "OFFSET #{@offset.to_i}"
61
+ end
62
+ end.join(' ')
63
+ end
64
+
65
+ end
66
+ end
data/lib/sqb/update.rb ADDED
@@ -0,0 +1,52 @@
1
+ require 'sqb/base'
2
+ require 'sqb/filtering'
3
+ require 'sqb/limiting'
4
+
5
+ module SQB
6
+ class Update < Base
7
+
8
+ include SQB::Filtering
9
+ include SQB::Limiting
10
+
11
+ def to_sql
12
+ [].tap do |query|
13
+ query << "UPDATE"
14
+ query << escape_and_join(@options[:database_name], @table_name)
15
+ query << "SET"
16
+ if @sets && !@sets.empty?
17
+ query << @sets.map do |key, value|
18
+ "#{escape_and_join(@table_name, key)} = #{value_escape(value)}"
19
+ end.join(', ')
20
+ else
21
+ raise NoValuesError, "No values have been updated. Use `set` to set the values to update."
22
+ end
23
+
24
+ if @where && !@where.empty?
25
+ query << "WHERE"
26
+ query << @where.join(' AND ')
27
+ end
28
+
29
+ if @limit
30
+ query << "LIMIT #{@limit.to_i}"
31
+ end
32
+
33
+ if @offset
34
+ query << "OFFSET #{@offset.to_i}"
35
+ end
36
+ end.join(' ')
37
+ end
38
+
39
+ # Set a value to be updated
40
+ #
41
+ # @param key [String]
42
+ # @param value [String, nil]
43
+ def set(hash)
44
+ @sets ||= {}
45
+ hash.each do |key, value|
46
+ @sets[key] = value
47
+ end
48
+ self
49
+ end
50
+
51
+ end
52
+ end
data/lib/sqb/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module SQB
2
- VERSION = '1.0.2'
2
+ VERSION = '1.0.3'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sqb
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.2
4
+ version: 1.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Adam Cooke
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-02-27 00:00:00.000000000 Z
11
+ date: 2018-02-28 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: A friendly SQL builder for MySQL.
14
14
  email:
@@ -18,9 +18,23 @@ extensions: []
18
18
  extra_rdoc_files: []
19
19
  files:
20
20
  - lib/sqb.rb
21
+ - lib/sqb/base.rb
22
+ - lib/sqb/columns.rb
23
+ - lib/sqb/delete.rb
24
+ - lib/sqb/distinct.rb
21
25
  - lib/sqb/error.rb
26
+ - lib/sqb/escaping.rb
27
+ - lib/sqb/filtering.rb
28
+ - lib/sqb/grouping.rb
29
+ - lib/sqb/insert.rb
30
+ - lib/sqb/joins.rb
31
+ - lib/sqb/limiting.rb
32
+ - lib/sqb/offsetting.rb
33
+ - lib/sqb/ordering.rb
22
34
  - lib/sqb/query.rb
23
35
  - lib/sqb/safe_string.rb
36
+ - lib/sqb/select.rb
37
+ - lib/sqb/update.rb
24
38
  - lib/sqb/version.rb
25
39
  homepage: https://github.com/adamcooke/sqb
26
40
  licenses: