sequel 3.34.1 → 3.35.0

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.
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