sequel 3.34.0 → 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 (102) hide show
  1. data/CHANGELOG +56 -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/postgres.rb +1 -52
  13. data/lib/sequel/adapters/do/sqlite.rb +0 -5
  14. data/lib/sequel/adapters/do.rb +2 -2
  15. data/lib/sequel/adapters/firebird.rb +1 -1
  16. data/lib/sequel/adapters/ibmdb.rb +2 -2
  17. data/lib/sequel/adapters/jdbc/db2.rb +0 -5
  18. data/lib/sequel/adapters/jdbc/derby.rb +29 -2
  19. data/lib/sequel/adapters/jdbc/firebird.rb +0 -5
  20. data/lib/sequel/adapters/jdbc/h2.rb +1 -1
  21. data/lib/sequel/adapters/jdbc/hsqldb.rb +7 -0
  22. data/lib/sequel/adapters/jdbc/informix.rb +0 -5
  23. data/lib/sequel/adapters/jdbc/jtds.rb +0 -5
  24. data/lib/sequel/adapters/jdbc/mysql.rb +0 -5
  25. data/lib/sequel/adapters/jdbc/postgresql.rb +4 -35
  26. data/lib/sequel/adapters/jdbc/sqlite.rb +0 -5
  27. data/lib/sequel/adapters/jdbc/sqlserver.rb +0 -5
  28. data/lib/sequel/adapters/jdbc/transactions.rb +4 -4
  29. data/lib/sequel/adapters/jdbc.rb +23 -19
  30. data/lib/sequel/adapters/mysql2.rb +1 -1
  31. data/lib/sequel/adapters/odbc/mssql.rb +14 -1
  32. data/lib/sequel/adapters/odbc.rb +3 -3
  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/postgres.rb +1 -71
  43. data/lib/sequel/adapters/swift.rb +1 -1
  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/associations.rb +95 -70
  59. data/lib/sequel/model/base.rb +19 -20
  60. data/lib/sequel/model.rb +2 -2
  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 +1 -1
  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/single_table_inheritance_spec.rb +5 -0
  91. data/spec/extensions/xml_serializer_spec.rb +13 -0
  92. data/spec/files/{transactionless_migrations → transaction_specified_migrations}/001_create_alt_basic.rb +1 -1
  93. data/spec/integration/associations_test.rb +5 -7
  94. data/spec/integration/dataset_test.rb +25 -7
  95. data/spec/integration/plugin_test.rb +1 -1
  96. data/spec/integration/schema_test.rb +16 -1
  97. data/spec/model/associations_spec.rb +2 -2
  98. metadata +14 -9
  99. data/lib/sequel/adapters/odbc/db2.rb +0 -17
  100. /data/spec/files/{transactionless_migrations → transaction_specified_migrations}/002_create_basic.rb +0 -0
  101. /data/spec/files/{transaction_migrations → transaction_unspecified_migrations}/001_create_alt_basic.rb +0 -0
  102. /data/spec/files/{transaction_migrations → transaction_unspecified_migrations}/002_create_basic.rb +0 -0
@@ -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
@@ -41,7 +41,7 @@ module Sequel
41
41
  # arguments.
42
42
  def execute_prepared_statement(ps_name, opts, &block)
43
43
  args = opts[:arguments]
44
- ps = prepared_statements[ps_name]
44
+ ps = prepared_statement(ps_name)
45
45
  sql = ps.prepared_sql
46
46
  synchronize(opts[:server]) do |conn|
47
47
  unless conn.prepared_statements[ps_name] == sql
@@ -137,7 +137,7 @@ module Sequel
137
137
  ps.extend(PreparedStatementMethods)
138
138
  if name
139
139
  ps.prepared_statement_name = name
140
- db.prepared_statements[name] = ps
140
+ db.set_prepared_statement(name, ps)
141
141
  end
142
142
  ps
143
143
  end
@@ -217,6 +217,8 @@ module Sequel
217
217
  sql << complex_expression_arg_pairs(args){|a, b| "(#{literal(a)} * power(2, #{literal b}))"}
218
218
  when :>>
219
219
  sql << complex_expression_arg_pairs(args){|a, b| "(#{literal(a)} / power(2, #{literal b}))"}
220
+ when :%
221
+ sql << complex_expression_arg_pairs(args){|a, b| "MOD(#{literal(a)}, #{literal(b)})"}
220
222
  when :ILIKE, :'NOT ILIKE'
221
223
  sql << ILIKE_0
222
224
  literal_append(sql, args.at(0))