sequel_core 1.5.1 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (68) hide show
  1. data/CHANGELOG +116 -0
  2. data/COPYING +19 -19
  3. data/README +83 -32
  4. data/Rakefile +9 -20
  5. data/bin/sequel +43 -112
  6. data/doc/cheat_sheet.rdoc +225 -0
  7. data/doc/dataset_filtering.rdoc +257 -0
  8. data/lib/sequel_core/adapters/adapter_skeleton.rb +4 -2
  9. data/lib/sequel_core/adapters/ado.rb +3 -1
  10. data/lib/sequel_core/adapters/db2.rb +4 -2
  11. data/lib/sequel_core/adapters/dbi.rb +127 -113
  12. data/lib/sequel_core/adapters/informix.rb +4 -2
  13. data/lib/sequel_core/adapters/jdbc.rb +5 -3
  14. data/lib/sequel_core/adapters/mysql.rb +112 -46
  15. data/lib/sequel_core/adapters/odbc.rb +5 -7
  16. data/lib/sequel_core/adapters/odbc_mssql.rb +12 -3
  17. data/lib/sequel_core/adapters/openbase.rb +3 -1
  18. data/lib/sequel_core/adapters/oracle.rb +11 -9
  19. data/lib/sequel_core/adapters/postgres.rb +261 -262
  20. data/lib/sequel_core/adapters/sqlite.rb +72 -22
  21. data/lib/sequel_core/connection_pool.rb +140 -73
  22. data/lib/sequel_core/core_ext.rb +201 -66
  23. data/lib/sequel_core/core_sql.rb +123 -153
  24. data/lib/sequel_core/database/schema.rb +156 -0
  25. data/lib/sequel_core/database.rb +321 -338
  26. data/lib/sequel_core/dataset/callback.rb +11 -12
  27. data/lib/sequel_core/dataset/convenience.rb +213 -240
  28. data/lib/sequel_core/dataset/pagination.rb +58 -43
  29. data/lib/sequel_core/dataset/parse_tree_sequelizer.rb +331 -0
  30. data/lib/sequel_core/dataset/query.rb +41 -0
  31. data/lib/sequel_core/dataset/schema.rb +15 -0
  32. data/lib/sequel_core/dataset/sequelizer.rb +41 -373
  33. data/lib/sequel_core/dataset/sql.rb +741 -632
  34. data/lib/sequel_core/dataset.rb +183 -168
  35. data/lib/sequel_core/deprecated.rb +1 -169
  36. data/lib/sequel_core/exceptions.rb +24 -19
  37. data/lib/sequel_core/migration.rb +44 -52
  38. data/lib/sequel_core/object_graph.rb +43 -42
  39. data/lib/sequel_core/pretty_table.rb +71 -76
  40. data/lib/sequel_core/schema/generator.rb +163 -105
  41. data/lib/sequel_core/schema/sql.rb +250 -93
  42. data/lib/sequel_core/schema.rb +2 -8
  43. data/lib/sequel_core/sql.rb +394 -0
  44. data/lib/sequel_core/worker.rb +37 -27
  45. data/lib/sequel_core.rb +99 -45
  46. data/spec/adapters/informix_spec.rb +0 -1
  47. data/spec/adapters/mysql_spec.rb +177 -124
  48. data/spec/adapters/oracle_spec.rb +0 -1
  49. data/spec/adapters/postgres_spec.rb +98 -58
  50. data/spec/adapters/sqlite_spec.rb +45 -4
  51. data/spec/blockless_filters_spec.rb +269 -0
  52. data/spec/connection_pool_spec.rb +21 -18
  53. data/spec/core_ext_spec.rb +169 -19
  54. data/spec/core_sql_spec.rb +56 -49
  55. data/spec/database_spec.rb +78 -17
  56. data/spec/dataset_spec.rb +300 -428
  57. data/spec/migration_spec.rb +1 -1
  58. data/spec/object_graph_spec.rb +5 -11
  59. data/spec/rcov.opts +1 -1
  60. data/spec/schema_generator_spec.rb +16 -4
  61. data/spec/schema_spec.rb +89 -10
  62. data/spec/sequelizer_spec.rb +56 -56
  63. data/spec/spec.opts +0 -5
  64. data/spec/spec_config.rb +7 -0
  65. data/spec/spec_config.rb.example +5 -5
  66. data/spec/spec_helper.rb +6 -0
  67. data/spec/worker_spec.rb +1 -1
  68. metadata +78 -63
@@ -1,113 +1,127 @@
1
- require 'dbi'
2
-
3
- module Sequel
4
- module DBI
5
- class Database < Sequel::Database
6
- set_adapter_scheme :dbi
7
-
8
- DBI_ADAPTERS = {
9
- :ado => "ADO",
10
- :db2 => "DB2",
11
- :frontbase => "FrontBase",
12
- :interbase => "InterBase",
13
- :msql => "Msql",
14
- :mysql => "Mysql",
15
- :odbc => "ODBC",
16
- :oracle => "Oracle",
17
- :pg => "Pg",
18
- :proxy => "Proxy",
19
- :sqlite => "SQLite",
20
- :sqlrelay => "SQLRelay"
21
- }
22
-
23
- # Converts a uri to an options hash. These options are then passed
24
- # to a newly created database object.
25
- def self.uri_to_options(uri)
26
- database = (uri.path =~ /\/(.*)/) && ($1)
27
- if uri.scheme =~ /dbi-(.+)/
28
- adapter = DBI_ADAPTERS[$1.to_sym] || $1
29
- database = "#{adapter}:#{database}"
30
- end
31
- {
32
- :user => uri.user,
33
- :password => uri.password,
34
- :host => uri.host,
35
- :port => uri.port,
36
- :database => database
37
- }
38
- end
39
-
40
-
41
- def connect
42
- dbname = @opts[:database]
43
- dbname = 'DBI:' + dbname unless dbname =~ /^DBI:/
44
- ::DBI.connect(dbname, @opts[:user], @opts[:password])
45
- end
46
-
47
- def disconnect
48
- @pool.disconnect {|c| c.disconnect}
49
- end
50
-
51
- def dataset(opts = nil)
52
- DBI::Dataset.new(self, opts)
53
- end
54
-
55
- def execute(sql)
56
- @logger.info(sql) if @logger
57
- @pool.hold do |conn|
58
- conn.execute(sql)
59
- end
60
- end
61
-
62
- def do(sql)
63
- @logger.info(sql) if @logger
64
- @pool.hold do |conn|
65
- conn.do(sql)
66
- end
67
- end
68
- end
69
-
70
- class Dataset < Sequel::Dataset
71
- def literal(v)
72
- case v
73
- when Time
74
- literal(v.iso8601)
75
- else
76
- super
77
- end
78
- end
79
-
80
- def fetch_rows(sql, &block)
81
- @db.synchronize do
82
- s = @db.execute sql
83
- begin
84
- @columns = s.column_names.map {|c| c.to_sym}
85
- s.fetch {|r| yield hash_row(s, r)}
86
- ensure
87
- s.finish rescue nil
88
- end
89
- end
90
- self
91
- end
92
-
93
- def hash_row(stmt, row)
94
- @columns.inject({}) do |m, c|
95
- m[c] = row.shift
96
- m
97
- end
98
- end
99
-
100
- def insert(*values)
101
- @db.do insert_sql(*values)
102
- end
103
-
104
- def update(*args, &block)
105
- @db.do update_sql(*args, &block)
106
- end
107
-
108
- def delete(opts = nil)
109
- @db.do delete_sql(opts)
110
- end
111
- end
112
- end
113
- end
1
+ require 'dbi'
2
+
3
+ module Sequel
4
+ module DBI
5
+ class Database < Sequel::Database
6
+ attr_writer :lowercase
7
+
8
+ set_adapter_scheme :dbi
9
+
10
+ DBI_ADAPTERS = {
11
+ :ado => "ADO",
12
+ :db2 => "DB2",
13
+ :frontbase => "FrontBase",
14
+ :interbase => "InterBase",
15
+ :msql => "Msql",
16
+ :mysql => "Mysql",
17
+ :odbc => "ODBC",
18
+ :oracle => "Oracle",
19
+ :pg => "pg",
20
+ :proxy => "Proxy",
21
+ :sqlite => "SQLite",
22
+ :sqlrelay => "SQLRelay"
23
+ }
24
+
25
+ # Converts a uri to an options hash. These options are then passed
26
+ # to a newly created database object.
27
+ def self.uri_to_options(uri)
28
+ database = (m = /\/(.*)/.match(uri.path)) && (m[1])
29
+ if m = /dbi-(.+)/.match(uri.scheme)
30
+ adapter = DBI_ADAPTERS[m[1].to_sym] || m[1]
31
+ database = "#{adapter}:dbname=#{database}"
32
+ end
33
+ {
34
+ :user => uri.user,
35
+ :password => uri.password,
36
+ :host => uri.host,
37
+ :port => uri.port,
38
+ :database => database
39
+ }
40
+ end
41
+
42
+
43
+ def connect
44
+ dbname = @opts[:database]
45
+ if dbname !~ /^DBI:/ then
46
+ dbname = "DBI:#{dbname}"
47
+ [:host, :port].each{|sym| dbname += ";#{sym}=#{@opts[sym]}" unless @opts[sym].blank?}
48
+ end
49
+ ::DBI.connect(dbname, @opts[:user], @opts[:password])
50
+ end
51
+
52
+ def disconnect
53
+ @pool.disconnect {|c| c.disconnect}
54
+ end
55
+
56
+ def dataset(opts = nil)
57
+ DBI::Dataset.new(self, opts)
58
+ end
59
+
60
+ def execute(sql)
61
+ log_info(sql)
62
+ @pool.hold do |conn|
63
+ conn.execute(sql)
64
+ end
65
+ end
66
+
67
+ def do(sql)
68
+ log_info(sql)
69
+ @pool.hold do |conn|
70
+ conn.do(sql)
71
+ end
72
+ end
73
+
74
+ # Converts all column names to lowercase
75
+ def lowercase
76
+ @lowercase ||= false
77
+ end
78
+ end
79
+
80
+ class Dataset < Sequel::Dataset
81
+ def literal(v)
82
+ case v
83
+ when Time
84
+ literal(v.iso8601)
85
+ when Date, DateTime
86
+ literal(v.to_s)
87
+ else
88
+ super
89
+ end
90
+ end
91
+
92
+ def fetch_rows(sql, &block)
93
+ @db.synchronize do
94
+ s = @db.execute sql
95
+ begin
96
+ @columns = s.column_names.map do |c|
97
+ @db.lowercase ? c.downcase.to_sym : c.to_sym
98
+ end
99
+ s.fetch {|r| yield hash_row(s, r)}
100
+ ensure
101
+ s.finish rescue nil
102
+ end
103
+ end
104
+ self
105
+ end
106
+
107
+ def hash_row(stmt, row)
108
+ @columns.inject({}) do |m, c|
109
+ m[c] = row.shift
110
+ m
111
+ end
112
+ end
113
+
114
+ def insert(*values)
115
+ @db.do insert_sql(*values)
116
+ end
117
+
118
+ def update(*args, &block)
119
+ @db.do update_sql(*args, &block)
120
+ end
121
+
122
+ def delete(opts = nil)
123
+ @db.do delete_sql(opts)
124
+ end
125
+ end
126
+ end
127
+ end
@@ -25,13 +25,13 @@ module Sequel
25
25
 
26
26
  # Returns number of rows affected
27
27
  def execute(sql)
28
- @logger.info(sql) if @logger
28
+ log_info(sql)
29
29
  @pool.hold {|c| c.immediate(sql)}
30
30
  end
31
31
  alias_method :do, :execute
32
32
 
33
33
  def query(sql, &block)
34
- @logger.info(sql) if @logger
34
+ log_info(sql)
35
35
  @pool.hold {|c| block[c.cursor(sql)]}
36
36
  end
37
37
  end
@@ -41,6 +41,8 @@ module Sequel
41
41
  case v
42
42
  when Time
43
43
  literal(v.iso8601)
44
+ when Date, DateTime
45
+ literal(v.to_s)
44
46
  else
45
47
  super
46
48
  end
@@ -38,7 +38,7 @@ module Sequel
38
38
  end
39
39
 
40
40
  def execute_and_forget(sql)
41
- @logger.info(sql) if @logger
41
+ log_info(sql)
42
42
  @pool.hold do |conn|
43
43
  stmt = conn.createStatement
44
44
  begin
@@ -50,7 +50,7 @@ module Sequel
50
50
  end
51
51
 
52
52
  def execute(sql)
53
- @logger.info(sql) if @logger
53
+ log_info(sql)
54
54
  @pool.hold do |conn|
55
55
  stmt = conn.createStatement
56
56
  begin
@@ -67,6 +67,8 @@ module Sequel
67
67
  case v
68
68
  when Time
69
69
  literal(v.iso8601)
70
+ when Date, DateTime
71
+ literal(v.to_s)
70
72
  else
71
73
  super
72
74
  end
@@ -105,4 +107,4 @@ module Sequel
105
107
  end
106
108
  end
107
109
  end
108
- end
110
+ end
@@ -4,7 +4,7 @@ require 'mysql'
4
4
  class Mysql::Result
5
5
  MYSQL_TYPES = {
6
6
  0 => :to_d, # MYSQL_TYPE_DECIMAL
7
- 1 => :to_i, # MYSQL_TYPE_TINY
7
+ #1 => :to_i, # MYSQL_TYPE_TINY
8
8
  2 => :to_i, # MYSQL_TYPE_SHORT
9
9
  3 => :to_i, # MYSQL_TYPE_LONG
10
10
  4 => :to_f, # MYSQL_TYPE_FLOAT
@@ -33,7 +33,17 @@ class Mysql::Result
33
33
  }
34
34
 
35
35
  def convert_type(v, type)
36
- v ? ((t = MYSQL_TYPES[type]) ? v.send(t) : v) : nil
36
+ if v
37
+ if type == 1
38
+ # We special case tinyint here to avoid adding
39
+ # a method to an ancestor of Fixnum
40
+ v.to_i == 0 ? false : true
41
+ else
42
+ (t = MYSQL_TYPES[type]) ? v.send(t) : v
43
+ end
44
+ else
45
+ nil
46
+ end
37
47
  end
38
48
 
39
49
  def columns(with_table = nil)
@@ -55,7 +65,6 @@ class Mysql::Result
55
65
  row[i] = v.send(t)
56
66
  end
57
67
  end
58
- row.keys = c
59
68
  yield row
60
69
  end
61
70
  end
@@ -80,8 +89,8 @@ module Sequel
80
89
  if conn.respond_to?(:server_version)
81
90
  pool.hold {|c| c.server_version}
82
91
  else
83
- get(:version[]) =~ /(\d+)\.(\d+)\.(\d+)/
84
- ($1.to_i * 10000) + ($2.to_i * 100) + $3.to_i
92
+ m = /(\d+)\.(\d+)\.(\d+)/.match(get(:version[]))
93
+ (m[1].to_i * 10000) + (m[2].to_i * 100) + m[3].to_i
85
94
  end
86
95
  end
87
96
  end
@@ -114,6 +123,8 @@ module Sequel
114
123
  if encoding = @opts[:encoding] || @opts[:charset]
115
124
  conn.query("set character_set_connection = '#{encoding}'")
116
125
  conn.query("set character_set_client = '#{encoding}'")
126
+ conn.query("set character_set_database = '#{encoding}'")
127
+ conn.query("set character_set_server = '#{encoding}'")
117
128
  conn.query("set character_set_results = '#{encoding}'")
118
129
  end
119
130
  conn.reconnect = true
@@ -135,10 +146,14 @@ module Sequel
135
146
  end
136
147
 
137
148
  def execute(sql, &block)
138
- @logger.info(sql) if @logger
139
- @pool.hold do |conn|
140
- conn.query(sql)
141
- block[conn] if block
149
+ begin
150
+ log_info(sql)
151
+ @pool.hold do |conn|
152
+ conn.query(sql)
153
+ block[conn] if block
154
+ end
155
+ rescue Mysql::Error => e
156
+ raise Error.new(e.message)
142
157
  end
143
158
  end
144
159
 
@@ -175,7 +190,7 @@ module Sequel
175
190
  sql = "#{literal(column[:name].to_sym)} #{TYPES[column[:type]]}"
176
191
  column[:size] ||= 255 if column[:type] == :varchar
177
192
  elements = column[:size] || column[:elements]
178
- sql << "(#{literal(elements)})" if elements
193
+ sql << literal(Array(elements)) if elements
179
194
  sql << UNSIGNED if column[:unsigned]
180
195
  sql << UNIQUE if column[:unique]
181
196
  sql << NOT_NULL if column[:null] == false
@@ -185,7 +200,7 @@ module Sequel
185
200
  sql << " #{auto_increment_sql}" if column[:auto_increment]
186
201
  if column[:table]
187
202
  sql << ", FOREIGN KEY (#{literal(column[:name].to_sym)}) REFERENCES #{column[:table]}"
188
- sql << "(#{literal(column[:key])})" if column[:key]
203
+ sql << literal(Array(column[:key])) if column[:key]
189
204
  sql << " ON DELETE #{on_delete_clause(column[:on_delete])}" if column[:on_delete]
190
205
  end
191
206
  sql
@@ -196,13 +211,13 @@ module Sequel
196
211
  unique = "UNIQUE " if index[:unique]
197
212
  case index[:type]
198
213
  when :full_text
199
- "CREATE FULLTEXT INDEX #{index_name} ON #{table_name} (#{literal(index[:columns])})"
214
+ "CREATE FULLTEXT INDEX #{index_name} ON #{table_name} #{literal(index[:columns])}"
200
215
  when :spatial
201
- "CREATE SPATIAL INDEX #{index_name} ON #{table_name} (#{literal(index[:columns])})"
216
+ "CREATE SPATIAL INDEX #{index_name} ON #{table_name} #{literal(index[:columns])}"
202
217
  when nil
203
- "CREATE #{unique}INDEX #{index_name} ON #{table_name} (#{literal(index[:columns])})"
218
+ "CREATE #{unique}INDEX #{index_name} ON #{table_name} #{literal(index[:columns])}"
204
219
  else
205
- "CREATE #{unique}INDEX #{index_name} ON #{table_name} (#{literal(index[:columns])}) USING #{index[:type]}"
220
+ "CREATE #{unique}INDEX #{index_name} ON #{table_name} #{literal(index[:columns])} USING #{index[:type]}"
206
221
  end
207
222
  end
208
223
 
@@ -212,16 +227,20 @@ module Sequel
212
227
  if @transactions.include? Thread.current
213
228
  return yield(conn)
214
229
  end
230
+ log_info(SQL_BEGIN)
215
231
  conn.query(SQL_BEGIN)
216
232
  begin
217
233
  @transactions << Thread.current
218
- result = yield(conn)
219
- conn.query(SQL_COMMIT)
220
- result
221
- rescue => e
234
+ yield(conn)
235
+ rescue ::Exception => e
236
+ log_info(SQL_ROLLBACK)
222
237
  conn.query(SQL_ROLLBACK)
223
- raise e unless Error::Rollback === e
238
+ raise (Mysql::Error === e ? Error.new(e.message) : e) unless Error::Rollback === e
224
239
  ensure
240
+ unless e
241
+ log_info(SQL_COMMIT)
242
+ conn.query(SQL_COMMIT)
243
+ end
225
244
  @transactions.delete(Thread.current)
226
245
  end
227
246
  end
@@ -231,12 +250,37 @@ module Sequel
231
250
  def use(db_name)
232
251
  disconnect
233
252
  @opts[:database] = db_name if self << "USE #{db_name}"
253
+ @schemas = nil
234
254
  self
235
255
  end
256
+
257
+ private
258
+ def connection_pool_default_options
259
+ super.merge(:pool_reuse_connections=>:last_resort, :pool_convert_exceptions=>false)
260
+ end
261
+
262
+ def schema_ds_dataset
263
+ ds = schema_utility_dataset.clone
264
+ ds.quote_identifiers = true
265
+ ds
266
+ end
267
+
268
+ def schema_ds_filter(table_name, opts)
269
+ filt = super
270
+ # Restrict it to the given or current database, unless specifically requesting :database = nil
271
+ filt = SQL::ComplexExpression.new(:AND, filt, {:c__table_schema=>opts[:database] || self.opts[:database]}) if opts[:database] || !opts.include?(:database)
272
+ filt
273
+ end
274
+
275
+ def schema_ds_join(table_name, opts)
276
+ [:information_schema__columns, {:table_schema => :table_schema, :table_name => :table_name}, :c]
277
+ end
236
278
  end
237
279
 
238
280
  class Dataset < Sequel::Dataset
239
- def quote_column_ref(c); "`#{c}`"; end
281
+ def quoted_identifier(c)
282
+ "`#{c}`"
283
+ end
240
284
 
241
285
  TRUE = '1'
242
286
  FALSE = '0'
@@ -268,7 +312,7 @@ module Sequel
268
312
  when LiteralString
269
313
  v
270
314
  when String
271
- "'#{v.gsub(/'|\\/, '\&\&')}'"
315
+ "'#{::Mysql.quote(v)}'"
272
316
  when true
273
317
  TRUE
274
318
  when false
@@ -293,27 +337,44 @@ module Sequel
293
337
  #
294
338
  # === Example
295
339
  # @ds = MYSQL_DB[:nodes]
296
- # @ds.join_expr(:natural_left_outer, :nodes)
297
- # # 'NATURAL LEFT OUTER JOIN nodes'
298
- #
299
- def join_expr(type, table, expr = nil, options = {})
300
- raise Error::InvalidJoinType, "Invalid join type: #{type}" unless join_type = JOIN_TYPES[type || :inner]
301
-
302
- server_version = @opts[:server_version] ||= @db.server_version
303
- type = :inner if type == :cross && !expr.nil?
304
-
305
- if (server_version >= 50014) && /\Anatural|cross|straight\z/.match(type.to_s)
306
- table = "( #{literal(table)} )" if table.is_a?(Array)
307
- "#{join_type} #{table}"
340
+ # @ds.join_table(:natural_left_outer, :nodes)
341
+ # # join SQL is 'NATURAL LEFT OUTER JOIN nodes'
342
+ def join_table(type, table, expr=nil, table_alias=nil)
343
+ raise(Error::InvalidJoinType, "Invalid join type: #{type}") unless join_type = JOIN_TYPES[type || :inner]
344
+
345
+ server_version = (@opts[:server_version] ||= @db.server_version)
346
+ type = :inner if (type == :cross) && !expr.nil?
347
+ return super(type, table, expr, table_alias) unless (server_version >= 50014) && /natural|cross|straight/.match(type.to_s)
348
+
349
+ table = if Array === table
350
+ "( #{table.collect{|t| quote_identifier(t)}.join(', ')} )"
308
351
  else
309
- super
352
+ quote_identifier(table)
310
353
  end
354
+ clone(:join => "#{@opts[:join]} #{join_type} #{table}")
311
355
  end
312
356
 
313
357
  def insert_default_values_sql
314
358
  "INSERT INTO #{source_list(@opts[:from])} () VALUES ()"
315
359
  end
316
360
 
361
+ def complex_expression_sql(op, args)
362
+ case op
363
+ when :~, :'!~'
364
+ "#{'NOT ' if op == :'!~'}(#{literal(args.at(0))} REGEXP BINARY #{literal(args.at(1))})"
365
+ when :'~*', :'!~*'
366
+ "#{'NOT ' if op == :'!~*'}(#{literal(args.at(0))} REGEXP #{literal(args.at(1))})"
367
+ when :'||'
368
+ if args.length > 1
369
+ "CONCAT(#{args.collect{|a| literal(a)}.join(', ')})"
370
+ else
371
+ literal(args.at(0))
372
+ end
373
+ else
374
+ super(op, args)
375
+ end
376
+ end
377
+
317
378
  def match_expr(l, r)
318
379
  case r
319
380
  when Regexp
@@ -352,7 +413,7 @@ module Sequel
352
413
  end
353
414
 
354
415
  if where = opts[:where]
355
- sql << " WHERE #{where}"
416
+ sql << " WHERE #{literal(where)}"
356
417
  end
357
418
 
358
419
  if group = opts[:group]
@@ -360,7 +421,7 @@ module Sequel
360
421
  end
361
422
 
362
423
  if having = opts[:having]
363
- sql << " HAVING #{having}"
424
+ sql << " HAVING #{literal(having)}"
364
425
  end
365
426
 
366
427
  if order = opts[:order]
@@ -391,13 +452,22 @@ module Sequel
391
452
 
392
453
  def full_text_search(cols, terms, opts = {})
393
454
  mode = opts[:boolean] ? " IN BOOLEAN MODE" : ""
394
- filter("MATCH (#{literal(cols)}) AGAINST (#{literal(terms)}#{mode})")
455
+ s = if Array === terms
456
+ if mode.blank?
457
+ "MATCH #{literal(Array(cols))} AGAINST #{literal(terms)}"
458
+ else
459
+ "MATCH #{literal(Array(cols))} AGAINST (#{literal(terms)[1...-1]}#{mode})"
460
+ end
461
+ else
462
+ "MATCH #{literal(Array(cols))} AGAINST (#{literal(terms)}#{mode})"
463
+ end
464
+ filter(s)
395
465
  end
396
466
 
397
467
  # MySQL allows HAVING clause on ungrouped datasets.
398
468
  def having(*cond, &block)
399
469
  @opts[:having] = {}
400
- filter(*cond, &block)
470
+ x = filter(*cond, &block)
401
471
  end
402
472
 
403
473
  # MySQL supports ORDER and LIMIT clauses in UPDATE statements.
@@ -431,12 +501,8 @@ module Sequel
431
501
  when Array
432
502
  if values.empty?
433
503
  "REPLACE INTO #{from} DEFAULT VALUES"
434
- elsif values.keys
435
- fl = values.keys.map {|f| literal(f.is_a?(String) ? f.to_sym : f)}
436
- vl = values.values.map {|v| literal(v)}
437
- "REPLACE INTO #{from} (#{fl.join(COMMA_SEPARATOR)}) VALUES (#{vl.join(COMMA_SEPARATOR)})"
438
504
  else
439
- "REPLACE INTO #{from} VALUES (#{literal(values)})"
505
+ "REPLACE INTO #{from} VALUES #{literal(values)}"
440
506
  end
441
507
  when Hash
442
508
  if values.empty?
@@ -498,8 +564,8 @@ module Sequel
498
564
  end
499
565
 
500
566
  def multi_insert_sql(columns, values)
501
- columns = literal(columns)
502
- values = values.map {|r| "(#{literal(r)})"}.join(COMMA_SEPARATOR)
567
+ columns = column_list(columns)
568
+ values = values.map {|r| literal(Array(r))}.join(COMMA_SEPARATOR)
503
569
  ["INSERT INTO #{source_list(@opts[:from])} (#{columns}) VALUES #{values}"]
504
570
  end
505
571
  end
@@ -46,14 +46,14 @@ module Sequel
46
46
  # fetch_rows method source code for an example of how to drop
47
47
  # the statements.
48
48
  def execute(sql)
49
- @logger.info(sql) if @logger
49
+ log_info(sql)
50
50
  @pool.hold do |conn|
51
51
  conn.run(sql)
52
52
  end
53
53
  end
54
54
 
55
55
  def do(sql)
56
- @logger.info(sql) if @logger
56
+ log_info(sql)
57
57
  @pool.hold do |conn|
58
58
  conn.do(sql)
59
59
  end
@@ -74,12 +74,10 @@ module Sequel
74
74
  BOOL_TRUE
75
75
  when false
76
76
  BOOL_FALSE
77
- when Time
77
+ when Time, DateTime
78
78
  formatted = v.strftime(ODBC_TIMESTAMP_FORMAT)
79
- if v.usec >= 1000
80
- msec = ( v.usec.to_f / 1000 ).round
81
- formatted.insert ODBC_TIMESTAMP_AFTER_SECONDS, ".#{msec}"
82
- end
79
+ usec = (Time === v ? v.usec : (v.sec_fraction * 86400000000))
80
+ formatted.insert(ODBC_TIMESTAMP_AFTER_SECONDS, ".#{(usec.to_f/1000).round}") if usec >= 1000
83
81
  formatted
84
82
  when Date
85
83
  v.strftime(ODBC_DATE_FORMAT)
@@ -58,7 +58,7 @@ module Sequel
58
58
  end
59
59
 
60
60
  if where = opts[:where]
61
- sql << " WHERE #{where}"
61
+ sql << " WHERE #{literal(where)}"
62
62
  end
63
63
 
64
64
  if group = opts[:group]
@@ -70,7 +70,7 @@ module Sequel
70
70
  end
71
71
 
72
72
  if having = opts[:having]
73
- sql << " HAVING #{having}"
73
+ sql << " HAVING #{literal(having)}"
74
74
  end
75
75
 
76
76
  if union = opts[:union]
@@ -91,7 +91,16 @@ module Sequel
91
91
  def full_text_search(cols, terms, opts = {})
92
92
  filter("CONTAINS (#{literal(cols)}, #{literal(terms)})")
93
93
  end
94
+
95
+ def complex_expression_sql(op, args)
96
+ case op
97
+ when :'||'
98
+ super(:+, args)
99
+ else
100
+ super(op, args)
101
+ end
102
+ end
94
103
  end
95
104
  end
96
105
  end
97
- end
106
+ end