sequel 3.34.1 → 3.35.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (101) hide show
  1. data/CHANGELOG +52 -0
  2. data/README.rdoc +3 -1
  3. data/Rakefile +2 -10
  4. data/doc/active_record.rdoc +1 -0
  5. data/doc/migration.rdoc +18 -7
  6. data/doc/model_hooks.rdoc +6 -0
  7. data/doc/opening_databases.rdoc +3 -0
  8. data/doc/prepared_statements.rdoc +0 -1
  9. data/doc/release_notes/3.35.0.txt +144 -0
  10. data/doc/schema_modification.rdoc +16 -1
  11. data/doc/thread_safety.rdoc +17 -0
  12. data/lib/sequel/adapters/do.rb +2 -2
  13. data/lib/sequel/adapters/do/postgres.rb +1 -52
  14. data/lib/sequel/adapters/do/sqlite.rb +0 -5
  15. data/lib/sequel/adapters/firebird.rb +1 -1
  16. data/lib/sequel/adapters/ibmdb.rb +2 -2
  17. data/lib/sequel/adapters/jdbc.rb +23 -19
  18. data/lib/sequel/adapters/jdbc/db2.rb +0 -5
  19. data/lib/sequel/adapters/jdbc/derby.rb +29 -2
  20. data/lib/sequel/adapters/jdbc/firebird.rb +0 -5
  21. data/lib/sequel/adapters/jdbc/h2.rb +1 -1
  22. data/lib/sequel/adapters/jdbc/hsqldb.rb +7 -0
  23. data/lib/sequel/adapters/jdbc/informix.rb +0 -5
  24. data/lib/sequel/adapters/jdbc/jtds.rb +0 -5
  25. data/lib/sequel/adapters/jdbc/mysql.rb +0 -5
  26. data/lib/sequel/adapters/jdbc/postgresql.rb +4 -35
  27. data/lib/sequel/adapters/jdbc/sqlite.rb +0 -5
  28. data/lib/sequel/adapters/jdbc/sqlserver.rb +0 -5
  29. data/lib/sequel/adapters/jdbc/transactions.rb +4 -4
  30. data/lib/sequel/adapters/mysql2.rb +1 -1
  31. data/lib/sequel/adapters/odbc.rb +3 -3
  32. data/lib/sequel/adapters/odbc/mssql.rb +14 -1
  33. data/lib/sequel/adapters/oracle.rb +6 -18
  34. data/lib/sequel/adapters/postgres.rb +36 -53
  35. data/lib/sequel/adapters/shared/db2.rb +16 -2
  36. data/lib/sequel/adapters/shared/mssql.rb +40 -9
  37. data/lib/sequel/adapters/shared/mysql.rb +16 -4
  38. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +2 -2
  39. data/lib/sequel/adapters/shared/oracle.rb +2 -0
  40. data/lib/sequel/adapters/shared/postgres.rb +135 -211
  41. data/lib/sequel/adapters/sqlite.rb +2 -2
  42. data/lib/sequel/adapters/swift.rb +1 -1
  43. data/lib/sequel/adapters/swift/postgres.rb +1 -71
  44. data/lib/sequel/adapters/tinytds.rb +3 -3
  45. data/lib/sequel/core.rb +27 -4
  46. data/lib/sequel/database/connecting.rb +7 -8
  47. data/lib/sequel/database/logging.rb +6 -1
  48. data/lib/sequel/database/misc.rb +20 -4
  49. data/lib/sequel/database/query.rb +38 -18
  50. data/lib/sequel/database/schema_generator.rb +5 -2
  51. data/lib/sequel/database/schema_methods.rb +34 -8
  52. data/lib/sequel/dataset/prepared_statements.rb +1 -1
  53. data/lib/sequel/dataset/sql.rb +18 -24
  54. data/lib/sequel/extensions/core_extensions.rb +0 -23
  55. data/lib/sequel/extensions/migration.rb +22 -8
  56. data/lib/sequel/extensions/pg_auto_parameterize.rb +4 -0
  57. data/lib/sequel/extensions/schema_dumper.rb +1 -1
  58. data/lib/sequel/model.rb +2 -2
  59. data/lib/sequel/model/associations.rb +95 -70
  60. data/lib/sequel/model/base.rb +16 -18
  61. data/lib/sequel/plugins/dirty.rb +214 -0
  62. data/lib/sequel/plugins/identity_map.rb +1 -1
  63. data/lib/sequel/plugins/json_serializer.rb +16 -1
  64. data/lib/sequel/plugins/many_through_many.rb +22 -32
  65. data/lib/sequel/plugins/many_to_one_pk_lookup.rb +2 -2
  66. data/lib/sequel/plugins/prepared_statements.rb +22 -8
  67. data/lib/sequel/plugins/prepared_statements_associations.rb +2 -3
  68. data/lib/sequel/plugins/prepared_statements_with_pk.rb +1 -1
  69. data/lib/sequel/plugins/single_table_inheritance.rb +1 -1
  70. data/lib/sequel/plugins/subclasses.rb +10 -2
  71. data/lib/sequel/plugins/timestamps.rb +1 -1
  72. data/lib/sequel/plugins/xml_serializer.rb +12 -1
  73. data/lib/sequel/sql.rb +1 -1
  74. data/lib/sequel/version.rb +2 -2
  75. data/spec/adapters/postgres_spec.rb +30 -79
  76. data/spec/core/database_spec.rb +46 -2
  77. data/spec/core/dataset_spec.rb +28 -22
  78. data/spec/core/schema_generator_spec.rb +1 -1
  79. data/spec/core/schema_spec.rb +51 -0
  80. data/spec/extensions/arbitrary_servers_spec.rb +0 -4
  81. data/spec/extensions/association_autoreloading_spec.rb +17 -0
  82. data/spec/extensions/association_proxies_spec.rb +4 -4
  83. data/spec/extensions/core_extensions_spec.rb +1 -24
  84. data/spec/extensions/dirty_spec.rb +155 -0
  85. data/spec/extensions/json_serializer_spec.rb +13 -0
  86. data/spec/extensions/migration_spec.rb +28 -15
  87. data/spec/extensions/named_timezones_spec.rb +6 -8
  88. data/spec/extensions/pg_auto_parameterize_spec.rb +6 -5
  89. data/spec/extensions/schema_dumper_spec.rb +3 -1
  90. data/spec/extensions/xml_serializer_spec.rb +13 -0
  91. data/spec/files/{transactionless_migrations → transaction_specified_migrations}/001_create_alt_basic.rb +1 -1
  92. data/spec/files/{transactionless_migrations → transaction_specified_migrations}/002_create_basic.rb +0 -0
  93. data/spec/files/{transaction_migrations → transaction_unspecified_migrations}/001_create_alt_basic.rb +0 -0
  94. data/spec/files/{transaction_migrations → transaction_unspecified_migrations}/002_create_basic.rb +0 -0
  95. data/spec/integration/associations_test.rb +5 -7
  96. data/spec/integration/dataset_test.rb +25 -7
  97. data/spec/integration/plugin_test.rb +1 -1
  98. data/spec/integration/schema_test.rb +16 -1
  99. data/spec/model/associations_spec.rb +2 -2
  100. metadata +14 -9
  101. data/lib/sequel/adapters/odbc/db2.rb +0 -17
@@ -28,9 +28,22 @@ module Sequel
28
28
  end
29
29
  end
30
30
  end
31
-
32
31
  class Dataset < ODBC::Dataset
33
32
  include Sequel::MSSQL::DatasetMethods
33
+
34
+ private
35
+
36
+ # Use ODBC format, not Microsoft format, as the ODBC layer does
37
+ # some translation.
38
+ def default_timestamp_format
39
+ TIMESTAMP_FORMAT
40
+ end
41
+
42
+ # Use ODBC format, not Microsoft format, as the ODBC layer does
43
+ # some translation.
44
+ def literal_date(v)
45
+ v.strftime(ODBC_DATE_FORMAT)
46
+ end
34
47
  end
35
48
  end
36
49
  end
@@ -126,7 +126,7 @@ module Sequel
126
126
  end
127
127
 
128
128
  def execute_prepared_statement(conn, type, name, opts)
129
- ps = prepared_statements[name]
129
+ ps = prepared_statement(name)
130
130
  sql = ps.prepared_sql
131
131
  if cursora = conn.prepared_statements[name]
132
132
  cursor, cursor_sql = cursora
@@ -243,7 +243,7 @@ module Sequel
243
243
 
244
244
  # Default values
245
245
  defaults = begin
246
- metadata_dataset.from(:dba_tab_cols).
246
+ metadata_dataset.from(:user_tab_cols).
247
247
  where(:table_name=>im.call(table)).
248
248
  to_hash(:column_name, :data_default)
249
249
  rescue DatabaseError
@@ -370,19 +370,6 @@ module Sequel
370
370
  ps.call(bind_vars, &block)
371
371
  end
372
372
 
373
- # Prepare the given type of query with the given name and store
374
- # it in the database. Note that a new native prepared statement is
375
- # created on each call to this prepared statement.
376
- def prepare(type, name=nil, *values)
377
- ps = to_prepared_statement(type, values)
378
- ps.extend(PreparedStatementMethods)
379
- if name
380
- ps.prepared_statement_name = name
381
- db.prepared_statements[name] = ps
382
- end
383
- ps
384
- end
385
-
386
373
  def fetch_rows(sql)
387
374
  execute(sql) do |cursor|
388
375
  offset = @opts[:offset]
@@ -403,14 +390,15 @@ module Sequel
403
390
  self
404
391
  end
405
392
 
406
- # Create a named prepared statement that is stored in the
407
- # database (and connection) for reuse.
393
+ # Prepare the given type of query with the given name and store
394
+ # it in the database. Note that a new native prepared statement is
395
+ # created on each call to this prepared statement.
408
396
  def prepare(type, name=nil, *values)
409
397
  ps = to_prepared_statement(type, values)
410
398
  ps.extend(PreparedStatementMethods)
411
399
  if name
412
400
  ps.prepared_statement_name = name
413
- db.prepared_statements[name] = ps
401
+ db.set_prepared_statement(name, ps)
414
402
  end
415
403
  ps
416
404
  end
@@ -12,7 +12,7 @@ rescue LoadError => e
12
12
  class PGconn
13
13
  unless method_defined?(:escape_string)
14
14
  if self.respond_to?(:escape)
15
- # If there is no escape_string instead method, but there is an
15
+ # If there is no escape_string instance method, but there is an
16
16
  # escape class method, use that instead.
17
17
  def escape_string(str)
18
18
  Sequel::Postgres.force_standard_strings ? str.gsub("'", "''") : self.class.escape(str)
@@ -91,24 +91,29 @@ module Sequel
91
91
  NAN = 0.0/0.0
92
92
  PLUS_INFINITY = 1.0/0.0
93
93
  MINUS_INFINITY = -1.0/0.0
94
+ NAN_STR = 'NaN'.freeze
95
+ PLUS_INFINITY_STR = 'Infinity'.freeze
96
+ MINUS_INFINITY_STR = '-Infinity'.freeze
97
+ TRUE_STR = 't'.freeze
98
+ DASH_STR = '-'.freeze
94
99
 
95
100
  TYPE_TRANSLATOR = tt = Class.new do
96
- def boolean(s) s == 't' end
101
+ def boolean(s) s == TRUE_STR end
97
102
  def bytea(s) ::Sequel::SQL::Blob.new(Adapter.unescape_bytea(s)) end
98
103
  def integer(s) s.to_i end
99
104
  def float(s)
100
105
  case s
101
- when 'NaN'
106
+ when NAN_STR
102
107
  NAN
103
- when 'Infinity'
108
+ when PLUS_INFINITY_STR
104
109
  PLUS_INFINITY
105
- when '-Infinity'
110
+ when MINUS_INFINITY_STR
106
111
  MINUS_INFINITY
107
112
  else
108
113
  s.to_f
109
114
  end
110
115
  end
111
- def date(s) ::Date.new(*s.split("-").map{|x| x.to_i}) end
116
+ def date(s) ::Date.new(*s.split(DASH_STR).map{|x| x.to_i}) end
112
117
  end.new
113
118
 
114
119
  # Hash with type name strings/symbols and callable values for converting PostgreSQL types.
@@ -149,8 +154,6 @@ module Sequel
149
154
  class Adapter < ::PGconn
150
155
  DISCONNECT_ERROR_RE = /\Acould not receive data from server: Software caused connection abort/
151
156
 
152
- include Sequel::Postgres::AdapterMethods
153
-
154
157
  self.translate_results = false if respond_to?(:translate_results=)
155
158
 
156
159
  # Hash of prepared statements for this connection. Keys are
@@ -158,18 +161,6 @@ module Sequel
158
161
  # are SQL strings.
159
162
  attr_reader(:prepared_statements) if SEQUEL_POSTGRES_USES_PG
160
163
 
161
- # Apply connection settings for this connection. Current sets
162
- # the date style to ISO in order make Date object creation in ruby faster,
163
- # if Postgres.use_iso_date_format is true.
164
- def apply_connection_settings
165
- super
166
- if Postgres.use_iso_date_format
167
- sql = "SET DateStyle = 'ISO'"
168
- execute(sql)
169
- end
170
- @prepared_statements = {} if SEQUEL_POSTGRES_USES_PG
171
- end
172
-
173
164
  # Raise a Sequel::DatabaseDisconnectError if a PGError is raised and
174
165
  # the connection status cannot be determined or it is not OK.
175
166
  def check_disconnect_errors
@@ -210,11 +201,6 @@ module Sequel
210
201
  def execute_query(sql, args)
211
202
  @db.log_yield(sql, args){args ? async_exec(sql, args) : async_exec(sql)}
212
203
  end
213
-
214
- # Return the requested values for the given row.
215
- def single_value(r)
216
- r.getvalue(0, 0) unless r.nil? || (r.ntuples == 0)
217
- end
218
204
  end
219
205
 
220
206
  # Database class for PostgreSQL databases used with Sequel and the
@@ -223,7 +209,7 @@ module Sequel
223
209
  include Sequel::Postgres::DatabaseMethods
224
210
 
225
211
  INFINITE_TIMESTAMP_STRINGS = ['infinity'.freeze, '-infinity'.freeze].freeze
226
- INFINITE_DATETIME_VALUES = ([1.0/0.0, -1.0/0.0] + INFINITE_TIMESTAMP_STRINGS).freeze
212
+ INFINITE_DATETIME_VALUES = ([PLUS_INFINITY, MINUS_INFINITY] + INFINITE_TIMESTAMP_STRINGS).freeze
227
213
 
228
214
  set_adapter_scheme :postgres
229
215
 
@@ -299,8 +285,9 @@ module Sequel
299
285
  conn.async_exec("set client_encoding to '#{encoding}'")
300
286
  end
301
287
  end
302
- conn.db = self
303
- conn.apply_connection_settings
288
+ conn.instance_variable_set(:@db, self)
289
+ conn.instance_variable_set(:@prepared_statements, {}) if SEQUEL_POSTGRES_USES_PG
290
+ connection_configuration_sqls.each{|sql| conn.execute(sql)}
304
291
  @conversion_procs ||= get_conversion_procs(conn)
305
292
  conn
306
293
  end
@@ -312,19 +299,7 @@ module Sequel
312
299
  synchronize(opts[:server]){|conn| conn.execute(sql, opts[:arguments], &block)}
313
300
  end
314
301
  end
315
-
316
- # Insert the values into the table and return the primary key (if
317
- # automatically generated).
318
- def execute_insert(sql, opts={})
319
- return execute(sql, opts) if Symbol === sql
320
- check_database_errors do
321
- synchronize(opts[:server]) do |conn|
322
- conn.execute(sql, opts[:arguments])
323
- insert_result(conn, opts[:table], opts[:values])
324
- end
325
- end
326
- end
327
-
302
+
328
303
  if SEQUEL_POSTGRES_USES_PG
329
304
  # +copy_table+ uses PostgreSQL's +COPY+ SQL statement to return formatted
330
305
  # results directly to the caller. This method is only supported if pg is the
@@ -470,6 +445,13 @@ module Sequel
470
445
  end
471
446
  end
472
447
 
448
+ # Set the DateStyle to ISO if configured, for faster date parsing.
449
+ def connection_configuration_sqls
450
+ sqls = super
451
+ sqls << "SET DateStyle = 'ISO'" if Postgres.use_iso_date_format
452
+ sqls
453
+ end
454
+
473
455
  # Disconnect given connection
474
456
  def disconnect_connection(conn)
475
457
  begin
@@ -487,7 +469,7 @@ module Sequel
487
469
  # of rows changed. If the :insert option is passed, return the value
488
470
  # of the primary key for the last inserted row.
489
471
  def execute_prepared_statement(name, opts={})
490
- ps = prepared_statements[name]
472
+ ps = prepared_statement(name)
491
473
  sql = ps.prepared_sql
492
474
  ps_name = name.to_s
493
475
  synchronize(opts[:server]) do |conn|
@@ -511,14 +493,10 @@ module Sequel
511
493
  end
512
494
 
513
495
  q = conn.check_disconnect_errors{log_yield(log_sql, args){conn.exec_prepared(ps_name, args)}}
514
- if opts[:table] && opts[:values]
515
- insert_result(conn, opts[:table], opts[:values])
516
- else
517
- begin
518
- block_given? ? yield(q) : q.cmd_tuples
519
- ensure
520
- q.clear
521
- end
496
+ begin
497
+ block_given? ? yield(q) : q.cmd_tuples
498
+ ensure
499
+ q.clear
522
500
  end
523
501
  end
524
502
  end
@@ -549,6 +527,10 @@ module Sequel
549
527
  end
550
528
  end
551
529
 
530
+ # Don't log, since logging is done by the underlying connection.
531
+ def log_connection_execute(conn, sql)
532
+ conn.execute(sql)
533
+ end
552
534
 
553
535
  # If the value is an infinite value (either an infinite float or a string returned by
554
536
  # by PostgreSQL for an infinite timestamp), return it without converting it if
@@ -573,6 +555,7 @@ module Sequel
573
555
  include Sequel::Postgres::DatasetMethods
574
556
 
575
557
  Database::DatasetClass = self
558
+ APOS = Sequel::Dataset::APOS
576
559
 
577
560
  # Yield all rows returned by executing the given SQL and converting
578
561
  # the types.
@@ -708,7 +691,7 @@ module Sequel
708
691
  ps.extend(PreparedStatementMethods)
709
692
  if name
710
693
  ps.prepared_statement_name = name
711
- db.prepared_statements[name] = ps
694
+ db.set_prepared_statement(name, ps)
712
695
  end
713
696
  ps
714
697
  end
@@ -766,12 +749,12 @@ module Sequel
766
749
 
767
750
  # Use the driver's escape_bytea
768
751
  def literal_blob_append(sql, v)
769
- sql << "'" << db.synchronize{|c| c.escape_bytea(v)} << "'"
752
+ sql << APOS << db.synchronize{|c| c.escape_bytea(v)} << APOS
770
753
  end
771
754
 
772
755
  # Use the driver's escape_string
773
756
  def literal_string_append(sql, v)
774
- sql << "'" << db.synchronize{|c| c.escape_string(v)} << "'"
757
+ sql << APOS << db.synchronize{|c| c.escape_string(v)} << APOS
775
758
  end
776
759
 
777
760
  # For each row in the result set, yield a hash with column name symbol
@@ -131,12 +131,24 @@ module Sequel
131
131
  super
132
132
  end
133
133
 
134
+ # Insert data from the current table into the new table after
135
+ # creating the table, since it is not possible to do it in one step.
136
+ def create_table_as(name, sql, options)
137
+ super
138
+ from(name).insert(sql.is_a?(Dataset) ? sql : dataset.with_sql(sql))
139
+ end
140
+
141
+ # DB2 requires parens around the SELECT, and DEFINITION ONLY at the end.
142
+ def create_table_as_sql(name, sql, options)
143
+ "#{create_table_prefix_sql(name, options)} AS (#{sql}) DEFINITION ONLY"
144
+ end
145
+
134
146
  # Here we use DGTT which has most backward compatibility, which uses
135
147
  # DECLARE instead of CREATE. CGTT can only be used after version 9.7.
136
148
  # http://www.ibm.com/developerworks/data/library/techarticle/dm-0912globaltemptable/
137
- def create_table_sql(name, generator, options)
149
+ def create_table_prefix_sql(name, options)
138
150
  if options[:temp]
139
- "DECLARE GLOBAL TEMPORARY TABLE #{quote_identifier(name)} (#{column_list_sql(generator)})"
151
+ "DECLARE GLOBAL TEMPORARY TABLE #{quote_identifier(name)}"
140
152
  else
141
153
  super
142
154
  end
@@ -221,6 +233,8 @@ module Sequel
221
233
  sql << complex_expression_arg_pairs(args){|a, b| "(#{literal(a)} * POWER(2, #{literal(b)}))"}
222
234
  when :>>
223
235
  sql << complex_expression_arg_pairs(args){|a, b| "(#{literal(a)} / POWER(2, #{literal(b)}))"}
236
+ when :%
237
+ sql << complex_expression_arg_pairs(args){|a, b| "MOD(#{literal(a)}, #{literal(b)})"}
224
238
  when :'B~'
225
239
  literal_append(sql, SQL::Function.new(:BITNOT, *args))
226
240
  when :extract
@@ -72,6 +72,11 @@ module Sequel
72
72
  true
73
73
  end
74
74
 
75
+ # MSSQL supports transaction DDL statements.
76
+ def supports_transactional_ddl?
77
+ true
78
+ end
79
+
75
80
  # Microsoft SQL Server supports using the INFORMATION_SCHEMA to get
76
81
  # information on tables.
77
82
  def tables(opts={})
@@ -141,7 +146,7 @@ module Sequel
141
146
  # Commit the active transaction on the connection, does not commit/release
142
147
  # savepoints.
143
148
  def commit_transaction(conn, opts={})
144
- log_connection_execute(conn, commit_transaction_sql) unless @transactions[conn][:savepoint_level] > 1
149
+ log_connection_execute(conn, commit_transaction_sql) unless _trans(conn)[:savepoint_level] > 1
145
150
  end
146
151
 
147
152
  # SQL to COMMIT a transaction.
@@ -152,10 +157,19 @@ module Sequel
152
157
  # MSSQL uses the name of the table to decide the difference between
153
158
  # a regular and temporary table, with temporary table names starting with
154
159
  # a #.
155
- def create_table_sql(name, generator, options)
156
- "CREATE TABLE #{quote_schema_table(options[:temp] ? "##{name}" : name)} (#{column_list_sql(generator)})"
160
+ def create_table_prefix_sql(name, options)
161
+ "CREATE TABLE #{quote_schema_table(options[:temp] ? "##{name}" : name)}"
157
162
  end
158
163
 
164
+ # MSSQL doesn't support CREATE TABLE AS, it only supports SELECT INTO.
165
+ # Emulating CREATE TABLE AS using SELECT INTO is only possible if a dataset
166
+ # is given as the argument, it can't work with a string, so raise an
167
+ # Error if a string is given.
168
+ def create_table_as(name, ds, options)
169
+ raise(Error, "must provide dataset instance as value of create_table :as option on MSSQL") unless ds.is_a?(Sequel::Dataset)
170
+ run(ds.into(name).sql)
171
+ end
172
+
159
173
  # The name of the constraint for setting the default value on the table and column.
160
174
  def default_constraint_name(table, column)
161
175
  from(:sysobjects___c_obj).
@@ -325,12 +339,16 @@ module Sequel
325
339
  OUTPUT_INSERTED = " OUTPUT INSERTED.*".freeze
326
340
  HEX_START = '0x'.freeze
327
341
  UNICODE_STRING_START = "N'".freeze
342
+ BACKSLASH_CRLF_RE = /\\((?:\r\n)|\n)/.freeze
343
+ BACKSLASH_CRLF_REPLACE = '\\\\\\\\\\1\\1'.freeze
328
344
  TOP_PAREN = " TOP (".freeze
329
345
  TOP = " TOP ".freeze
330
346
  OUTPUT = " OUTPUT ".freeze
331
347
  HSTAR = "H*".freeze
332
348
  CASE_SENSITIVE_COLLATION = 'Latin1_General_CS_AS'.freeze
333
349
  CASE_INSENSITIVE_COLLATION = 'Latin1_General_CI_AS'.freeze
350
+ DEFAULT_TIMESTAMP_FORMAT = "'%Y-%m-%dT%H:%M:%S%N%z'".freeze
351
+ FORMAT_DATE = "'%Y%m%d'".freeze
334
352
 
335
353
  # Allow overriding of the mssql_unicode_strings option at the dataset level.
336
354
  attr_accessor :mssql_unicode_strings
@@ -557,6 +575,13 @@ module Sequel
557
575
  server_version >= 10000000
558
576
  end
559
577
 
578
+ # Use strict ISO-8601 format with T between date and time,
579
+ # since that is the format that is multilanguage and not
580
+ # DATEFORMAT dependent.
581
+ def default_timestamp_format
582
+ DEFAULT_TIMESTAMP_FORMAT
583
+ end
584
+
560
585
  # MSSQL supports the OUTPUT clause for DELETE statements.
561
586
  # It also allows prepending a WITH clause.
562
587
  def delete_clause_methods
@@ -612,18 +637,24 @@ module Sequel
612
637
  sql << HEX_START << v.unpack(HSTAR).first
613
638
  end
614
639
 
615
- # Optionally use unicode string syntax for all strings. Don't double
616
- # backslashes.
617
- def literal_string_append(sql, v)
618
- sql << (mssql_unicode_strings ? UNICODE_STRING_START : APOS)
619
- sql << v.gsub(APOS_RE, DOUBLE_APOS) << APOS
640
+ # Use YYYYmmdd format, since that's the only want that is
641
+ # multilanguage and not DATEFORMAT dependent.
642
+ def literal_date(v)
643
+ v.strftime(FORMAT_DATE)
620
644
  end
621
-
645
+
622
646
  # Use 0 for false on MSSQL
623
647
  def literal_false
624
648
  BOOL_FALSE
625
649
  end
626
650
 
651
+ # Optionally use unicode string syntax for all strings. Don't double
652
+ # backslashes.
653
+ def literal_string_append(sql, v)
654
+ sql << (mssql_unicode_strings ? UNICODE_STRING_START : APOS)
655
+ sql << v.gsub(APOS_RE, DOUBLE_APOS).gsub(BACKSLASH_CRLF_RE, BACKSLASH_CRLF_REPLACE) << APOS
656
+ end
657
+
627
658
  # Use 1 for true on MSSQL
628
659
  def literal_true
629
660
  BOOL_TRUE
@@ -223,7 +223,7 @@ module Sequel
223
223
  # Use XA START to start a new prepared transaction if the :prepare
224
224
  # option is given.
225
225
  def begin_transaction(conn, opts={})
226
- if (s = opts[:prepare]) && (th = @transactions[conn])[:savepoint_level] == 0
226
+ if (s = opts[:prepare]) && (th = _trans(conn))[:savepoint_level] == 0
227
227
  log_connection_execute(conn, "XA START #{literal(s)}")
228
228
  th[:savepoint_level] += 1
229
229
  else
@@ -246,7 +246,7 @@ module Sequel
246
246
  # Prepare the XA transaction for a two-phase commit if the
247
247
  # :prepare option is given.
248
248
  def commit_transaction(conn, opts={})
249
- if (s = opts[:prepare]) && @transactions[conn][:savepoint_level] <= 1
249
+ if (s = opts[:prepare]) && _trans(conn)[:savepoint_level] <= 1
250
250
  log_connection_execute(conn, "XA END #{literal(s)}")
251
251
  log_connection_execute(conn, "XA PREPARE #{literal(s)}")
252
252
  else
@@ -335,7 +335,7 @@ module Sequel
335
335
 
336
336
  # Rollback the currently open XA transaction
337
337
  def rollback_transaction(conn, opts={})
338
- if (s = opts[:prepare]) && @transactions[conn][:savepoint_level] <= 1
338
+ if (s = opts[:prepare]) && _trans(conn)[:savepoint_level] <= 1
339
339
  log_connection_execute(conn, "XA END #{literal(s)}")
340
340
  log_connection_execute(conn, "XA PREPARE #{literal(s)}")
341
341
  log_connection_execute(conn, "XA ROLLBACK #{literal(s)}")
@@ -353,7 +353,9 @@ module Sequel
353
353
  def schema_parse_table(table_name, opts)
354
354
  m = output_identifier_meth(opts[:dataset])
355
355
  im = input_identifier_meth(opts[:dataset])
356
- metadata_dataset.with_sql("DESCRIBE ?", SQL::Identifier.new(im.call(table_name))).map do |row|
356
+ table = SQL::Identifier.new(im.call(table_name))
357
+ table = SQL::QualifiedIdentifier.new(im.call(opts[:schema]), table) if opts[:schema]
358
+ metadata_dataset.with_sql("DESCRIBE ?", table).map do |row|
357
359
  row[:auto_increment] = true if row.delete(:Extra).to_s =~ /auto_increment/io
358
360
  row[:allow_null] = row.delete(:Null) == 'YES'
359
361
  row[:default] = row.delete(:Default)
@@ -414,6 +416,9 @@ module Sequel
414
416
  INSERT_CLAUSE_METHODS = Dataset.clause_methods(:insert, %w'insert ignore into columns values on_duplicate_key_update')
415
417
  SELECT_CLAUSE_METHODS = Dataset.clause_methods(:select, %w'select distinct calc_found_rows columns from join where group having compounds order limit lock')
416
418
  UPDATE_CLAUSE_METHODS = Dataset.clause_methods(:update, %w'update ignore table set where order limit')
419
+ APOS = Dataset::APOS
420
+ APOS_RE = Dataset::APOS_RE
421
+ DOUBLE_APOS = Dataset::DOUBLE_APOS
417
422
  SPACE = Dataset::SPACE
418
423
  PAREN_OPEN = Dataset::PAREN_OPEN
419
424
  PAREN_CLOSE = Dataset::PAREN_CLOSE
@@ -446,6 +451,8 @@ module Sequel
446
451
  MATCH_AGAINST_BOOLEAN = ["(MATCH ".freeze, " AGAINST (".freeze, " IN BOOLEAN MODE))".freeze].freeze
447
452
  EXPLAIN = 'EXPLAIN '.freeze
448
453
  EXPLAIN_EXTENDED = 'EXPLAIN EXTENDED '.freeze
454
+ BACKSLASH_RE = /\\/.freeze
455
+ QUAD_BACKSLASH = "\\\\\\\\".freeze
449
456
 
450
457
  # MySQL specific syntax for LIKE/REGEXP searches, as well as
451
458
  # string concatenation.
@@ -772,6 +779,11 @@ module Sequel
772
779
  BOOL_FALSE
773
780
  end
774
781
 
782
+ # SQL fragment for String. Doubles \ and ' by default.
783
+ def literal_string_append(sql, v)
784
+ sql << APOS << v.gsub(BACKSLASH_RE, QUAD_BACKSLASH).gsub(APOS_RE, DOUBLE_APOS) << APOS
785
+ end
786
+
775
787
  # Use 1 for true on MySQL
776
788
  def literal_true
777
789
  BOOL_TRUE