sequel 4.32.0 → 4.33.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +30 -0
  3. data/doc/active_record.rdoc +16 -0
  4. data/doc/release_notes/4.32.0.txt +2 -2
  5. data/doc/release_notes/4.33.0.txt +88 -0
  6. data/lib/sequel/adapters/shared/mssql.rb +2 -1
  7. data/lib/sequel/adapters/shared/oracle.rb +7 -1
  8. data/lib/sequel/adapters/shared/postgres.rb +2 -3
  9. data/lib/sequel/database/connecting.rb +1 -1
  10. data/lib/sequel/database/query.rb +1 -5
  11. data/lib/sequel/database/schema_generator.rb +1 -3
  12. data/lib/sequel/database/transactions.rb +5 -0
  13. data/lib/sequel/extensions/graph_each.rb +11 -2
  14. data/lib/sequel/extensions/schema_dumper.rb +0 -3
  15. data/lib/sequel/model.rb +2 -1
  16. data/lib/sequel/model/associations.rb +2 -0
  17. data/lib/sequel/model/base.rb +29 -7
  18. data/lib/sequel/model/exceptions.rb +2 -2
  19. data/lib/sequel/plugins/boolean_readers.rb +3 -2
  20. data/lib/sequel/plugins/boolean_subsets.rb +3 -2
  21. data/lib/sequel/plugins/class_table_inheritance.rb +1 -1
  22. data/lib/sequel/version.rb +1 -1
  23. data/spec/adapters/mssql_spec.rb +27 -0
  24. data/spec/adapters/postgres_spec.rb +27 -18
  25. data/spec/core/database_spec.rb +15 -0
  26. data/spec/core/schema_generator_spec.rb +11 -0
  27. data/spec/extensions/boolean_readers_spec.rb +1 -1
  28. data/spec/extensions/boolean_subsets_spec.rb +1 -1
  29. data/spec/extensions/defaults_setter_spec.rb +2 -1
  30. data/spec/extensions/graph_each_spec.rb +10 -0
  31. data/spec/extensions/looser_typecasting_spec.rb +1 -1
  32. data/spec/extensions/schema_dumper_spec.rb +7 -1
  33. data/spec/integration/model_test.rb +4 -0
  34. data/spec/model/associations_spec.rb +2 -0
  35. data/spec/model/model_spec.rb +69 -3
  36. metadata +4 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d56404078cfe75cf79b2fe4b804e205d8a2e48d3
4
- data.tar.gz: 587618022698a1b6f7076571e01361b080f3ea77
3
+ metadata.gz: 3187d95f59e9b0abc9502193d8843e11fc1c46f6
4
+ data.tar.gz: 7437eeaaa3c1a9f2016ef5d0fbe70a13b6f92a54
5
5
  SHA512:
6
- metadata.gz: 97590c69f6bef7d22b856c9e331256357cdb4bc5804a65747242c14e3e9c40149f134503f303ce410941181e7574253205e73482f5fe5501ce2d6bebaa94c6cc
7
- data.tar.gz: cb19067f82ec892008af6e1064bfb107edce01df096e377606adade354dbfa59dad90aa55dd2a33dc1d7ede4b9c42f16e5f5850c864979e85dfbb63adbfceca6
6
+ metadata.gz: 7a6239f275f8d99e78fddfbdce4ee36f28cd00b1d79a895b5b1c1f2a6529de35b437905d20bd9a60fb9e8aff6d2dd00b4a89d5bbbeb9f5ea52b897b0cbe72830
7
+ data.tar.gz: d123e46cfeb494f9ea8ecb4ee62793bb4873b6df240cec31ea7c290fe8e6c0f24f64f0f419729ee1bf7aedf724d69ad86497c248458c6818af70ccf44d65d525
data/CHANGELOG CHANGED
@@ -1,3 +1,33 @@
1
+ === 4.33.0 (2016-04-01)
2
+
3
+ * Handle arbitrary objects passed as arguments to the association method (jeremyevans) (#1166)
4
+
5
+ * Handle array with multiple columns as Dataset#insert_conflict :target value on PostgreSQL (chanks) (#1165)
6
+
7
+ * Add Database#transaction :savepoint=>:only option, for only creating a savepoint if already inside a transaction (jeremyevans)
8
+
9
+ * Make Database#sequence_for_table on Oracle handle cases where the schema for a table cannot be determined (jeremyevans)
10
+
11
+ * The boolean_readers, boolean_subsets, and class_table_inheritance plugins no longer do blind rescues (jeremyevans) (#1162)
12
+
13
+ * Add Model.require_valid_table setting, if set to true doesn't swallow any errors for invalid tables (jeremyevans)
14
+
15
+ * Creating model classes inside a transaction when the table doesn't exist no longer rolls back the transaction on PostgreSQL (jeremyevans) (#1160)
16
+
17
+ * Sequel::Model no longer swallows many errors when subclassing or setting datasets (jeremyevans) (#1160)
18
+
19
+ * Handle altering column NULL settings for varchar(max) and text columns on MSSQL (Ilja Resch)
20
+
21
+ * Remove Sequel.firebird and Sequel.informix adapter methods (jeremyevans)
22
+
23
+ * Make graph_each extension handle result set splitting when using Dataset#first (jeremyevans)
24
+
25
+ * Allow raising Sequel::ValidationFailed and Sequel::HookFailed without an argument (jeremyevans)
26
+
27
+ * Allow schema_dumper to handle :qualify=>true option on PostgreSQL (jeremyevans)
28
+
29
+ * Allow foreign_key schema method to handle SQL::Identifier and SQL::QualifiedIdentifier as 2nd argument (jeremyevans)
30
+
1
31
  === 4.32.0 (2016-03-01)
2
32
 
3
33
  * Use mutex for synchronizing access to association reflection cache on MRI (jeremyevans)
@@ -626,6 +626,22 @@ For anything more complex, you can use +dataset_module+:
626
626
  end
627
627
  end
628
628
 
629
+ === +none+
630
+
631
+ If you want to return an empty dataset, but still preserve chaining, you can use the +null_dataset+ extension:
632
+
633
+ DB.extension(:null_dataset)
634
+
635
+ class User < Sequel::Model
636
+ def self.nullify
637
+ dataset.nullify
638
+ end
639
+ end
640
+
641
+ user = User.create(name: 'John Doe')
642
+ User.nullify.where(name: 'John Doe').all
643
+ # => []
644
+
629
645
  ==== +reset_column_information+
630
646
 
631
647
  If you want to completely reload the schema for the table:
@@ -1,6 +1,6 @@
1
1
  = New Features
2
2
 
3
- * A no_auto_string_literals plugin has been added, which removes the
3
+ * A no_auto_literal_strings extension has been added, which removes the
4
4
  automatic usage of strings in filter arguments as literal SQL code.
5
5
  By default, if you do:
6
6
 
@@ -22,7 +22,7 @@
22
22
  input as literal SQL code is probably the most common SQL injection
23
23
  vector in applications using Sequel.
24
24
 
25
- With the no_auto_string_literals extension, passing a plain string
25
+ With the no_auto_literal_strings extension, passing a plain string
26
26
  as the first or only argument to a filter method raises an
27
27
  exception. If you want to use literal SQL code, you have to do so
28
28
  explicitly:
@@ -0,0 +1,88 @@
1
+ = New Features
2
+
3
+ * A Sequel::Model.require_valid_table accessor has been added. This
4
+ setting is false for backwards compatibility, but if set to true,
5
+ will raise an error you try to create a model class where an
6
+ invalid table name is used or the schema or columns cannot be
7
+ determined. This makes it easier to catch bugs, as things will
8
+ fail fast, but it means that you must change code like:
9
+
10
+ class Foo < Sequel::Model
11
+ set_dataset :my_foos
12
+ end
13
+
14
+ to:
15
+
16
+ class Foo < Sequel::Model(:my_foos)
17
+ end
18
+
19
+ as otherwise Foo will attempt to use the foos table by default
20
+ when creating the class, which will raise an error as it is not
21
+ the correct table name.
22
+
23
+ * Sequel::Database#transaction now supports a :savepoint=>:only
24
+ option, which will create a savepoint if already inside a
25
+ transaction, but will yield without creating a transaction if
26
+ not inside a transaction. The use case for this is when you
27
+ are running code that may raise an exception, and you don't
28
+ want to invalidate the current transaction state.
29
+
30
+ = Other Improvements
31
+
32
+ * The graph_each extension now splits results into subhashes when
33
+ using Sequel::Dataset#first, as it did before Sequel 4.27.0.
34
+
35
+ * On PostgreSQL, Dataset#insert_conflict now accepts an array of
36
+ columns as the value for the :target option.
37
+
38
+ * You can now pass a Sequel::SQL::Identifier or a
39
+ Sequel::SQL::QualifiedIdentifer as the table argument when creating
40
+ a foreign key. Previously, only symbols were supported, and using
41
+ other values required specifying the :table option. So this will
42
+ now work to reference a table that includes a double underscore:
43
+
44
+ foreign_key :foo_id, Sequel.identifier(:fo__oo)
45
+
46
+ * Creating model classes inside a transaction on PostgreSQL where
47
+ the implicit table name isn't correct no longer causes the
48
+ transaction to fail.
49
+
50
+ Similar issues were also fixed in the boolean_readers,
51
+ boolean_subsets, and class_table_inheritance plugins.
52
+
53
+ * On PostgreSQL, You can now use the :qualify=>true option in the
54
+ schema dumper, to dump using schema-qualified table names.
55
+
56
+ * On Microsoft SQL Server, the set_column_allow_null and
57
+ set_column_not_null alter table methods now work on varchar(max),
58
+ text, and similar columns.
59
+
60
+ * On Oracle, Sequel::Database#sequence_for_table now returns nil if
61
+ given a table that doesn't exist or that the user does not have
62
+ access to.
63
+
64
+ * Passing arbitrary objects to a model association method now
65
+ indicates that the association should be reloaded, which was
66
+ used to work but was broken in Sequel 4.32.0.
67
+
68
+ * It is now possible to raise Sequel::ValidationFailed and
69
+ Sequel::HookFailed without an argument.
70
+
71
+ = Backwards Compatibility
72
+
73
+ * Sequel::Model no longer swallows many errors when subclassing or
74
+ setting datasets. While this should hopefully not affect backwards
75
+ compatibility, it may break things where the methods were raising
76
+ exceptions. If this does break backwards compatibility, it is
77
+ most likely because it is no longer hiding another bug that should
78
+ be fixed. Specific changes include:
79
+
80
+ * Model.inherited no longer rescues exceptions raised by set_dataset
81
+ * When subclassing a model that has a dataset, the columns and
82
+ schema are just copied from the superclass
83
+ * Only Sequel::Error is rescued in calls to columns and schema,
84
+ before it would rescue StandardError.
85
+
86
+ * The Sequel.firebird and Sequel.informix adapter methods have been
87
+ removed, they are no longer needed as the firebird and informix
88
+ adapters were removed a few versions back.
@@ -283,7 +283,8 @@ module Sequel
283
283
  when :set_column_null
284
284
  sch = schema(table).find{|k,v| k.to_s == op[:name].to_s}.last
285
285
  type = sch[:db_type]
286
- if [:string, :decimal].include?(sch[:type]) and size = (sch[:max_chars] || sch[:column_size])
286
+ if [:string, :decimal].include?(sch[:type]) && !["text", "ntext"].include?(type) && (size = (sch[:max_chars] || sch[:column_size]))
287
+ size = "MAX" if size == -1
287
288
  type += "(#{size}#{", #{sch[:scale]}" if sch[:scale] && sch[:scale].to_i > 0})"
288
289
  end
289
290
  "ALTER TABLE #{quote_schema_table(table)} ALTER COLUMN #{quote_identifier(op[:name])} #{type_literal(:type=>type)} #{'NOT ' unless op[:null]}NULL"
@@ -219,7 +219,13 @@ module Sequel
219
219
  def sequence_for_table(table)
220
220
  return nil unless autosequence
221
221
  @primary_key_sequences.fetch(table) do |key|
222
- pk = schema(table).select{|k, v| v[:primary_key]}
222
+ begin
223
+ sch = schema(table)
224
+ rescue Sequel::Error
225
+ return nil
226
+ end
227
+
228
+ pk = sch.select{|k, v| v[:primary_key]}
223
229
  @primary_key_sequences[table] = if pk.length == 1
224
230
  seq = "seq_#{table}_#{pk.first.first}"
225
231
  seq.to_sym unless from(:user_sequences).filter(:sequence_name=>input_identifier_meth.call(seq)).empty?
@@ -1595,9 +1595,8 @@ module Sequel
1595
1595
  sql << " ON CONSTRAINT "
1596
1596
  identifier_append(sql, target)
1597
1597
  elsif target = opts[:target]
1598
- sql << ' ('
1599
- identifier_append(sql, target)
1600
- sql << ')'
1598
+ sql << ' '
1599
+ identifier_append(sql, Array(target))
1601
1600
  end
1602
1601
 
1603
1602
  if values = opts[:update]
@@ -8,7 +8,7 @@ module Sequel
8
8
  # ---------------------
9
9
 
10
10
  # Array of supported database adapters
11
- ADAPTERS = %w'ado amalgalite cubrid do firebird ibmdb informix jdbc mock mysql mysql2 odbc oracle postgres sqlanywhere sqlite swift tinytds'.collect(&:to_sym)
11
+ ADAPTERS = %w'ado amalgalite cubrid do ibmdb jdbc mock mysql mysql2 odbc oracle postgres sqlanywhere sqlite swift tinytds'.collect(&:to_sym)
12
12
 
13
13
  @single_threaded = false
14
14
 
@@ -194,11 +194,7 @@ module Sequel
194
194
  sch, table_name = schema_and_table(name)
195
195
  name = SQL::QualifiedIdentifier.new(sch, table_name) if sch
196
196
  ds = from(name)
197
- if in_transaction? && supports_savepoints?
198
- transaction(:savepoint=>true){_table_exists?(ds)}
199
- else
200
- _table_exists?(ds)
201
- end
197
+ transaction(:savepoint=>:only){_table_exists?(ds)}
202
198
  true
203
199
  rescue DatabaseError
204
200
  false
@@ -156,12 +156,10 @@ module Sequel
156
156
  opts = case table
157
157
  when Hash
158
158
  table.merge(opts)
159
- when Symbol
160
- opts.merge(:table=>table)
161
159
  when NilClass
162
160
  opts
163
161
  else
164
- raise(Error, "The second argument to foreign_key should be a Hash, Symbol, or nil")
162
+ opts.merge(:table=>table)
165
163
  end
166
164
  return composite_foreign_key(name, opts) if name.is_a?(Array)
167
165
  column(name, Integer, opts)
@@ -69,6 +69,8 @@ module Sequel
69
69
  # default Sequel will reuse an existing transaction, so if you want to
70
70
  # use a savepoint you must use this option. If the surrounding transaction
71
71
  # uses :auto_savepoint, you can set this to false to not use a savepoint.
72
+ # If the value given for this option is :only, it will only create a
73
+ # savepoint if it is inside a transacation.
72
74
  #
73
75
  # PostgreSQL specific options:
74
76
  #
@@ -97,6 +99,9 @@ module Sequel
97
99
  end
98
100
  else
99
101
  synchronize(opts[:server]) do |conn|
102
+ if opts[:savepoint] == :only && supports_savepoints? && !_trans(conn)
103
+ return yield(conn)
104
+ end
100
105
  if already_in_transaction?(conn, opts)
101
106
  if opts[:savepoint] != false && (stack = _trans(conn)[:savepoints]) && stack.last
102
107
  _transaction(conn, Hash[opts].merge!(:savepoint=>true), &block)
@@ -31,12 +31,21 @@ module Sequel
31
31
  end
32
32
  end
33
33
 
34
+ # Call graph_each for graphed datasets that are not being eager graphed.
35
+ def with_sql_each(sql)
36
+ if @opts[:graph] && !@opts[:eager_graph]
37
+ graph_each(sql){|r| yield r}
38
+ else
39
+ super
40
+ end
41
+ end
42
+
34
43
  private
35
44
 
36
45
  # Fetch the rows, split them into component table parts,
37
46
  # tranform and run the row_proc on each part (if applicable),
38
47
  # and yield a hash of the parts.
39
- def graph_each
48
+ def graph_each(sql=select_sql)
40
49
  # Reject tables with nil datasets, as they are excluded from
41
50
  # the result set
42
51
  datasets = @opts[:graph][:table_aliases].to_a.reject{|ta,ds| ds.nil?}
@@ -48,7 +57,7 @@ module Sequel
48
57
  # Use the manually set graph aliases, if any, otherwise
49
58
  # use the ones automatically created by .graph
50
59
  column_aliases = @opts[:graph_aliases] || @opts[:graph][:column_aliases]
51
- fetch_rows(select_sql) do |r|
60
+ fetch_rows(sql) do |r|
52
61
  graph = {}
53
62
  # Create the sub hashes, one per table
54
63
  table_aliases.each{|ta| graph[ta]={}}
@@ -144,7 +144,6 @@ END_MIG
144
144
  # Return a string with a create table block that will recreate the given
145
145
  # table's schema. Takes the same options as dump_schema_migration.
146
146
  def dump_table_schema(table, options=OPTS)
147
- table = table.value.to_s if table.is_a?(SQL::Identifier)
148
147
  gen = dump_table_generator(table, options)
149
148
  commands = [gen.dump_columns, gen.dump_constraints, gen.dump_indexes].reject{|x| x == ''}.join("\n\n")
150
149
  "create_table(#{table.inspect}#{', :ignore_index_errors=>true' if !options[:same_db] && options[:indexes] != false && !gen.indexes.empty?}) do\n#{commands.gsub(/^/o, ' ')}\nend"
@@ -244,8 +243,6 @@ END_MIG
244
243
  # Return a Schema::Generator object that will recreate the
245
244
  # table's schema. Takes the same options as dump_schema_migration.
246
245
  def dump_table_generator(table, options=OPTS)
247
- table = table.value.to_s if table.is_a?(SQL::Identifier)
248
- raise(Error, "must provide table as a Symbol, String, or Sequel::SQL::Identifier") unless [String, Symbol].any?{|c| table.is_a?(c)}
249
246
  s = schema(table).dup
250
247
  pks = s.find_all{|x| x.last[:primary_key] == true}.map(&:first)
251
248
  options = options.merge(:single_pk=>true) if pks.length == 1
@@ -125,7 +125,7 @@ module Sequel
125
125
  :@raise_on_typecast_failure=>nil, :@plugins=>:dup, :@setter_methods=>nil,
126
126
  :@use_after_commit_rollback=>nil, :@fast_pk_lookup_sql=>nil,
127
127
  :@fast_instance_delete_sql=>nil, :@finders=>:dup, :@finder_loaders=>:dup,
128
- :@db=>nil, :@default_set_fields_options=>:dup}
128
+ :@db=>nil, :@default_set_fields_options=>:dup, :@require_valid_table=>nil}
129
129
 
130
130
  # Regular expression that determines if a method name is normal in the sense that
131
131
  # it could be used literally in ruby code without using send. Used to
@@ -153,6 +153,7 @@ module Sequel
153
153
  @raise_on_save_failure = true
154
154
  @raise_on_typecast_failure = false
155
155
  @require_modification = nil
156
+ @require_valid_table = false
156
157
  @restrict_primary_key = true
157
158
  @restricted_columns = nil
158
159
  @setter_methods = nil
@@ -2272,6 +2272,8 @@ module Sequel
2272
2272
  else
2273
2273
  if dynamic_opts.respond_to?(:call)
2274
2274
  {:callback=>dynamic_opts}
2275
+ else
2276
+ {:reload=>true}
2275
2277
  end
2276
2278
  end
2277
2279
 
@@ -60,6 +60,26 @@ module Sequel
60
60
  # Sequel will not check the number of rows modified (default: true).
61
61
  attr_accessor :require_modification
62
62
 
63
+ # Requires that all models have valid tables, raising exceptions if creating a model
64
+ # without a valid table backing it. Enabling this will break code like:
65
+ #
66
+ # class Foo < Sequel::Model
67
+ # set_dataset :my_foo
68
+ # end
69
+ #
70
+ # As when Sequel::Model is subclassed, before set_dataset is executed, it will try to
71
+ # get the schema for the foos table, which will raise an exception. You would need to
72
+ # switch to using:
73
+ #
74
+ # class Foo < Sequel::Model(:my_foo)
75
+ # end
76
+ #
77
+ # or:
78
+ #
79
+ # Foo = Sequel::Model()
80
+ # Foo.set_dataset :my_foo
81
+ attr_accessor :require_valid_table
82
+
63
83
  # Should be the literal primary key column name if this Model's table has a simple primary key, or
64
84
  # nil if the model has a compound primary key or no primary key.
65
85
  attr_reader :simple_pk
@@ -513,10 +533,10 @@ module Sequel
513
533
 
514
534
  unless ivs.include?("@dataset")
515
535
  if @dataset && self != Model
516
- subclass.set_dataset(@dataset.clone, :inherited=>true) rescue nil
536
+ subclass.set_dataset(@dataset.clone, :inherited=>true)
517
537
  elsif (n = subclass.name) && !n.to_s.empty?
518
538
  db
519
- subclass.set_dataset(subclass.implicit_table_name) rescue nil
539
+ subclass.set_dataset(subclass.implicit_table_name)
520
540
  end
521
541
  end
522
542
  end
@@ -689,12 +709,14 @@ module Sequel
689
709
  @require_modification = Sequel::Model.require_modification.nil? ? @dataset.provides_accurate_rows_matched? : Sequel::Model.require_modification
690
710
  if inherited
691
711
  self.simple_table = superclass.simple_table
692
- @columns = @dataset.columns rescue nil
712
+ @columns = superclass.instance_variable_get(:@columns)
713
+ @db_schema = superclass.instance_variable_get(:@db_schema)
693
714
  else
694
715
  @dataset_method_modules.each{|m| @dataset.extend(m)} if @dataset_method_modules
716
+ @db_schema = get_db_schema
695
717
  end
718
+
696
719
  @dataset.model = self if @dataset.respond_to?(:model=)
697
- check_non_connection_error{@db_schema = (inherited ? superclass.db_schema : get_db_schema)}
698
720
  reset_instance_dataset
699
721
  self
700
722
  end
@@ -798,11 +820,11 @@ module Sequel
798
820
  # Yield to the passed block and swallow all errors other than DatabaseConnectionErrors.
799
821
  def check_non_connection_error
800
822
  begin
801
- yield
823
+ db.transaction(:savepoint=>:only){yield}
802
824
  rescue Sequel::DatabaseConnectionError
803
825
  raise
804
- rescue
805
- nil
826
+ rescue Sequel::Error
827
+ raise if require_valid_table
806
828
  end
807
829
  end
808
830
 
@@ -7,7 +7,7 @@ module Sequel
7
7
  # The Sequel::Model instance related to this error.
8
8
  attr_reader :model
9
9
 
10
- def initialize(message, model=nil)
10
+ def initialize(message=nil, model=nil)
11
11
  @model = model
12
12
  super(message)
13
13
  end
@@ -35,7 +35,7 @@ module Sequel
35
35
  # The Sequel::Model::Errors object related to this exception.
36
36
  attr_reader :errors
37
37
 
38
- def initialize(errors)
38
+ def initialize(errors=nil)
39
39
  if errors.is_a?(Sequel::Model)
40
40
  @model = errors
41
41
  errors = @model.errors
@@ -49,8 +49,9 @@ module Sequel
49
49
  # Add attribute? methods for all of the boolean attributes for this model.
50
50
  def create_boolean_readers
51
51
  im = instance_methods.collect(&:to_s)
52
- cs = columns rescue return
53
- cs.each{|c| create_boolean_reader(c) if boolean_attribute?(c) && !im.include?("#{c}?")}
52
+ if cs = check_non_connection_error{columns}
53
+ cs.each{|c| create_boolean_reader(c) if boolean_attribute?(c) && !im.include?("#{c}?")}
54
+ end
54
55
  end
55
56
  end
56
57
  end
@@ -49,8 +49,9 @@ module Sequel
49
49
 
50
50
  # Add subset methods for all of the boolean columns in this model.
51
51
  def create_boolean_subsets
52
- cs = columns rescue return
53
- cs.each{|c| subset(*boolean_subset_args(c)) if db_schema[c][:type] == :boolean}
52
+ if cs = check_non_connection_error{columns}
53
+ cs.each{|c| subset(*boolean_subset_args(c)) if db_schema[c][:type] == :boolean}
54
+ end
54
55
  end
55
56
  end
56
57
  end
@@ -245,7 +245,7 @@ module Sequel
245
245
  columns = db.from(table).columns
246
246
  else
247
247
  table = subclass.implicit_table_name
248
- columns = db.from(table).columns rescue nil
248
+ columns = check_non_connection_error{db.from(table).columns}
249
249
  table = nil if !columns || columns.empty?
250
250
  end
251
251
  end
@@ -5,7 +5,7 @@ module Sequel
5
5
  MAJOR = 4
6
6
  # The minor version of Sequel. Bumped for every non-patch level
7
7
  # release, generally around once a month.
8
- MINOR = 32
8
+ MINOR = 33
9
9
  # The tiny version of Sequel. Usually 0, only bumped for bugfix
10
10
  # releases that fix regressions from previous versions.
11
11
  TINY = 0
@@ -518,6 +518,33 @@ describe "A MSSQL database adds index with include" do
518
518
  end
519
519
  end
520
520
 
521
+ describe "MSSQL set_column_allow_null" do
522
+ before do
523
+ @db = DB
524
+ end
525
+ after do
526
+ @db.drop_table?(:test3)
527
+ end
528
+
529
+ it "should work with nvarchar(MAX) columns" do
530
+ @db.create_table!(:test3) do
531
+ column :t, 'nvarchar(MAX)'
532
+ end
533
+ @db.alter_table(:test3) do
534
+ set_column_not_null :t
535
+ end
536
+ end
537
+
538
+ it "should work with text columns" do
539
+ @db.create_table!(:test3) do
540
+ column :t, 'text'
541
+ end
542
+ @db.alter_table(:test3) do
543
+ set_column_not_null :t
544
+ end
545
+ end
546
+ end
547
+
521
548
  describe "MSSQL::Database#drop_column with a schema" do
522
549
  before do
523
550
  DB.run "create schema test" rescue nil
@@ -184,7 +184,7 @@ end
184
184
  describe "PostgreSQL", 'INSERT ON CONFLICT' do
185
185
  before(:all) do
186
186
  @db = DB
187
- @db.create_table!(:ic_test){Integer :a; Integer :b; unique :a, :name=>:ic_test_a_uidx}
187
+ @db.create_table!(:ic_test){Integer :a; Integer :b; Integer :c; unique :a, :name=>:ic_test_a_uidx; unique [:b, :c], :name=>:ic_test_b_c_uidx}
188
188
  @ds = @db[:ic_test]
189
189
  end
190
190
  before do
@@ -194,26 +194,35 @@ describe "PostgreSQL", 'INSERT ON CONFLICT' do
194
194
  @db.drop_table?(:ic_test)
195
195
  end
196
196
 
197
- it "Dataset#insert_ignore and insert_constraint should ignore uniqueness violations" do
198
- @ds.insert(1, 2)
199
- proc{@ds.insert(1, 3)}.must_raise Sequel::UniqueConstraintViolation
200
- @ds.insert_ignore.insert(1, 3).must_equal nil
201
- @ds.insert_conflict.insert(1, 3).must_equal nil
202
- @ds.insert_conflict(:target=>:a).insert(1, 3).must_equal nil
203
- @ds.insert_conflict(:constraint=>:ic_test_a_uidx).insert(1, 3).must_equal nil
204
- @ds.all.must_equal [{:a=>1, :b=>2}]
197
+ it "Dataset#insert_ignore and insert_conflict should ignore uniqueness violations" do
198
+ @ds.insert(1, 2, 3)
199
+ proc{@ds.insert(1, 3, 4)}.must_raise Sequel::UniqueConstraintViolation
200
+ @ds.insert_ignore.insert(1, 3, 4).must_equal nil
201
+ @ds.insert_conflict.insert(1, 3, 4).must_equal nil
202
+ @ds.insert_conflict(:target=>:a).insert(1, 3, 4).must_equal nil
203
+ @ds.insert_conflict(:constraint=>:ic_test_a_uidx).insert(1, 3, 4).must_equal nil
204
+ @ds.all.must_equal [{:a=>1, :b=>2, :c=>3}]
205
205
  end
206
206
 
207
- it "Dataset#insert_constraint should handle upserts" do
208
- @ds.insert(1, 2)
209
- @ds.insert_conflict(:target=>:a, :update=>{:b=>3}).insert(1, 3).must_equal nil
210
- @ds.all.must_equal [{:a=>1, :b=>3}]
207
+ it "Dataset#insert_conflict should handle upserts" do
208
+ @ds.insert(1, 2, 3)
209
+ @ds.insert_conflict(:target=>:a, :update=>{:b=>3}).insert(1, 3, 4).must_equal nil
210
+ @ds.all.must_equal [{:a=>1, :b=>3, :c=>3}]
211
+ @ds.insert_conflict(:target=>[:b, :c], :update=>{:c=>5}).insert(5, 3, 3).must_equal nil
212
+ @ds.all.must_equal [{:a=>1, :b=>3, :c=>5}]
211
213
  @ds.insert_conflict(:constraint=>:ic_test_a_uidx, :update=>{:b=>4}).insert(1, 3).must_equal nil
212
- @ds.all.must_equal [{:a=>1, :b=>4}]
213
- @ds.insert_conflict(:constraint=>:ic_test_a_uidx, :update=>{:b=>5}, :update_where=>{:ic_test__b=>4}).insert(1, 3).must_equal nil
214
- @ds.all.must_equal [{:a=>1, :b=>5}]
215
- @ds.insert_conflict(:constraint=>:ic_test_a_uidx, :update=>{:b=>6}, :update_where=>{:ic_test__b=>4}).insert(1, 3).must_equal nil
216
- @ds.all.must_equal [{:a=>1, :b=>5}]
214
+ @ds.all.must_equal [{:a=>1, :b=>4, :c=>5}]
215
+ @ds.insert_conflict(:constraint=>:ic_test_a_uidx, :update=>{:b=>5}, :update_where=>{:ic_test__b=>4}).insert(1, 3, 4).must_equal nil
216
+ @ds.all.must_equal [{:a=>1, :b=>5, :c=>5}]
217
+ @ds.insert_conflict(:constraint=>:ic_test_a_uidx, :update=>{:b=>6}, :update_where=>{:ic_test__b=>4}).insert(1, 3, 4).must_equal nil
218
+ @ds.all.must_equal [{:a=>1, :b=>5, :c=>5}]
219
+ end
220
+
221
+ it "Dataset#insert_conflict should respect expressions in the target argument" do
222
+ @ds.insert_conflict(:target=>:a).insert_sql(1, 2, 3).must_equal "INSERT INTO \"ic_test\" VALUES (1, 2, 3) ON CONFLICT (\"a\") DO NOTHING"
223
+ @ds.insert_conflict(:target=>[:b, :c]).insert_sql(1, 2, 3).must_equal "INSERT INTO \"ic_test\" VALUES (1, 2, 3) ON CONFLICT (\"b\", \"c\") DO NOTHING"
224
+ @ds.insert_conflict(:target=>[:b, Sequel.function(:round, :c)]).insert_sql(1, 2, 3).must_equal "INSERT INTO \"ic_test\" VALUES (1, 2, 3) ON CONFLICT (\"b\", round(\"c\")) DO NOTHING"
225
+ @ds.insert_conflict(:target=>[:b, Sequel.virtual_row{|o| o.round(:c)}]).insert_sql(1, 2, 3).must_equal "INSERT INTO \"ic_test\" VALUES (1, 2, 3) ON CONFLICT (\"b\", round(\"c\")) DO NOTHING"
217
226
  end
218
227
  end if DB.server_version >= 90500
219
228
 
@@ -1033,6 +1033,16 @@ describe "Database#transaction with savepoint support" do
1033
1033
  proc{@db.transaction(:prepare=>'XYZ'){@db.transaction(:savepoint=>true){@db.after_rollback{@db.execute('foo')}}}}.must_raise(Sequel::Error)
1034
1034
  @db.sqls.must_equal ['BEGIN', 'SAVEPOINT autopoint_1','ROLLBACK TO SAVEPOINT autopoint_1', 'ROLLBACK']
1035
1035
  end
1036
+
1037
+ it "should create savepoint if inside a transaction when :savepoint=>:only is used" do
1038
+ @db.transaction{@db.transaction(:savepoint=>:only){}}
1039
+ @db.sqls.must_equal ['BEGIN', 'SAVEPOINT autopoint_1','RELEASE SAVEPOINT autopoint_1', 'COMMIT']
1040
+ end
1041
+
1042
+ it "should not create transaction if not inside a transaction when :savepoint=>:only is used" do
1043
+ @db.transaction(:savepoint=>:only){}
1044
+ @db.sqls.must_equal []
1045
+ end
1036
1046
  end
1037
1047
 
1038
1048
  describe "Database#transaction without savepoint support" do
@@ -1041,6 +1051,11 @@ describe "Database#transaction without savepoint support" do
1041
1051
  meta_def(@db, :supports_savepoints?){false}
1042
1052
  end
1043
1053
 
1054
+ it "should not create savepoint if inside a transaction when :savepoint=>:only is used" do
1055
+ @db.transaction{@db.transaction(:savepoint=>:only){}}
1056
+ @db.sqls.must_equal ['BEGIN', 'COMMIT']
1057
+ end
1058
+
1044
1059
  include DatabaseTransactionSpecs
1045
1060
  end
1046
1061
 
@@ -43,6 +43,17 @@ describe Sequel::Schema::Generator do
43
43
  columns.first[:primary_key].must_equal nil
44
44
  end
45
45
 
46
+ it "should handle SQL::Identifier and SQL::QualifiedIdentifier as foreign_key arguments" do
47
+ generator = Sequel::Schema::Generator.new(Sequel.mock) do
48
+ foreign_key :a_id, Sequel.identifier(:as)
49
+ foreign_key :b_id, Sequel.qualify(:c, :b)
50
+ end
51
+
52
+ columns = generator.columns
53
+ columns.first.values_at(:name, :table).must_equal [:a_id, Sequel.identifier(:as)]
54
+ columns.last.values_at(:name, :table).must_equal [:b_id, Sequel.qualify(:c, :b)]
55
+ end
56
+
46
57
  it "counts definitions correctly" do
47
58
  @columns.size.must_equal 6
48
59
  @indexes.size.must_equal 2
@@ -2,7 +2,7 @@ require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
2
2
 
3
3
  describe Sequel::Model, "BooleanReaders plugin" do
4
4
  before do
5
- @db = Sequel::Database.new
5
+ @db = Sequel.mock
6
6
  def @db.supports_schema_parsing?() true end
7
7
  def @db.schema(*args)
8
8
  [[:id, {}], [:z, {:type=>:integer, :db_type=>'tinyint(1)'}], [:b, {:type=>:boolean, :db_type=>'boolean'}]]
@@ -2,7 +2,7 @@ require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
2
2
 
3
3
  describe "boolean_subsets plugin" do
4
4
  before do
5
- @db = Sequel::Database.new
5
+ @db = Sequel.mock
6
6
  def @db.supports_schema_parsing?() true end
7
7
  def @db.schema(*args)
8
8
  [[:asdaf9898as, {}], [:active, {:type=>:boolean}]]
@@ -4,11 +4,12 @@ describe "Sequel::Plugins::DefaultsSetter" do
4
4
  before do
5
5
  @db = db = Sequel.mock
6
6
  def db.supports_schema_parsing?() true end
7
+ def db.schema(*) [] end
7
8
  @c = c = Class.new(Sequel::Model(db[:foo]))
8
9
  @c.instance_variable_set(:@db_schema, {:a=>{}})
9
10
  @c.plugin :defaults_setter
10
11
  @c.columns :a
11
- @pr = proc{|x| db.meta_def(:schema){|*| [[:a, {:ruby_default => x}]]}; c.dataset = c.dataset; c}
12
+ @pr = proc{|x| db.meta_def(:schema){|*| [[:id, {:primary_key=>true}], [:a, {:ruby_default => x, :primary_key=>false}]]}; c.dataset = c.dataset; c}
12
13
  end
13
14
  after do
14
15
  Sequel.datetime_class = Time
@@ -51,6 +51,16 @@ describe Sequel::Dataset, " graphing" do
51
51
  @ds1.graph(@ds2, :x=>:id).graph(@ds2, {:y=>:points__id}, :table_alias=>:graph).all.must_equal [{:points=>{:id=>1, :x=>2, :y=>3}, :lines=>{:id=>4, :x=>5, :y=>6, :graph_id=>7}, :graph=>{:id=>8, :x=>9, :y=>10, :graph_id=>11}}]
52
52
  end
53
53
 
54
+ it "#graph_each should split the result set into component tables when using first" do
55
+ @db.fetch = [[{:id=>1,:x=>2,:y=>3,:lines_id=>4,:lines_x=>5,:lines_y=>6,:graph_id=>7}],
56
+ [{:id=>1,:x=>2,:y=>3,:lines_id=>4,:lines_x=>5,:lines_y=>6,:graph_id=>7, :graphs_id=>8, :name=>9, :graphs_x=>10, :graphs_y=>11, :graphs_lines_x=>12}],
57
+ [{:id=>1,:x=>2,:y=>3,:lines_id=>4,:lines_x=>5,:lines_y=>6,:graph_id=>7, :graph_id_0=>8, :graph_x=>9, :graph_y=>10, :graph_graph_id=>11}]]
58
+
59
+ @ds1.graph(@ds2, :x=>:id).first.must_equal(:points=>{:id=>1, :x=>2, :y=>3}, :lines=>{:id=>4, :x=>5, :y=>6, :graph_id=>7})
60
+ @ds1.graph(@ds2, :x=>:id).graph(@ds3, :id=>:graph_id).first.must_equal(:points=>{:id=>1, :x=>2, :y=>3}, :lines=>{:id=>4, :x=>5, :y=>6, :graph_id=>7}, :graphs=>{:id=>8, :name=>9, :x=>10, :y=>11, :lines_x=>12})
61
+ @ds1.graph(@ds2, :x=>:id).graph(@ds2, {:y=>:points__id}, :table_alias=>:graph).first.must_equal(:points=>{:id=>1, :x=>2, :y=>3}, :lines=>{:id=>4, :x=>5, :y=>6, :graph_id=>7}, :graph=>{:id=>8, :x=>9, :y=>10, :graph_id=>11})
62
+ end
63
+
54
64
  it "#graph_each should give a nil value instead of a hash when all values for a table are nil" do
55
65
  @db.fetch = [[{:id=>1,:x=>2,:y=>3,:lines_id=>nil,:lines_x=>nil,:lines_y=>nil,:graph_id=>nil}],
56
66
  [{:id=>1,:x=>2,:y=>3,:lines_id=>4,:lines_x=>5,:lines_y=>6,:graph_id=>7, :graphs_id=>nil, :name=>nil, :graphs_x=>nil, :graphs_y=>nil, :graphs_lines_x=>nil},
@@ -2,7 +2,7 @@ require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
2
2
 
3
3
  describe "LooserTypecasting Extension" do
4
4
  before do
5
- @db = Sequel::Database.new
5
+ @db = Sequel.mock
6
6
  def @db.supports_schema_parsing?() true end
7
7
  def @db.schema(*args)
8
8
  [[:id, {}], [:z, {:type=>:float}], [:b, {:type=>:integer}], [:d, {:type=>:decimal}], [:s, {:type=>:string}]]
@@ -102,7 +102,7 @@ describe "Sequel::Database dump methods" do
102
102
  end
103
103
 
104
104
  it "should support dumping table schemas when given an identifier" do
105
- @d.dump_table_schema(Sequel.identifier(:t__t1)).must_equal "create_table(\"t__t1\") do\n primary_key :c1\n String :c2, :size=>20\nend"
105
+ @d.dump_table_schema(Sequel.identifier(:t__t1)).must_equal "create_table(Sequel::SQL::Identifier.new(:t__t1)) do\n primary_key :c1\n String :c2, :size=>20\nend"
106
106
  end
107
107
 
108
108
  it "should dump non-Integer primary key columns with explicit :type" do
@@ -804,4 +804,10 @@ END_MIG
804
804
  ["create_table(:t4) do\n primary_key :c1, :table=>:t3, :key=>[:c1]\nend",
805
805
  "create_table(:t4) do\n primary_key :c1, :key=>[:c1], :table=>:t3\nend"].must_include(@d.dump_table_schema(:t4))
806
806
  end
807
+
808
+ it "should handle dumping on PostgreSQL using qualified tables" do
809
+ @d = Sequel.connect('mock://postgres').extension(:schema_dumper)
810
+ @d.meta_def(:schema){|*s| [[:c1, {:db_type=>'timestamp', :primary_key=>true, :allow_null=>true}]]}
811
+ @d.dump_table_schema(Sequel.qualify(:foo, :bar), :same_db=>true).must_equal "create_table(Sequel::SQL::QualifiedIdentifier.new(:foo, :bar)) do\n column :c1, \"timestamp\"\n \n primary_key [:c1]\nend"
812
+ end
807
813
  end
@@ -222,5 +222,9 @@ describe "Sequel::Model with no existing table" do
222
222
  db.drop_table?(:items)
223
223
  class ::Item < Sequel::Model(db); end; Object.send(:remove_const, :Item)
224
224
  c = Class.new(Sequel::Model); c.set_dataset(db[:items])
225
+ db.transaction do
226
+ c = Class.new(Sequel::Model(db[:items]))
227
+ db.get(Sequel.cast(1, Integer)).must_equal 1
228
+ end
225
229
  end
226
230
  end
@@ -450,6 +450,8 @@ describe Sequel::Model, "many_to_one" do
450
450
  DB.sqls.must_equal ["SELECT * FROM nodes WHERE id = 234"]
451
451
  d.parent(:reload=>true).wont_equal 42
452
452
  DB.sqls.must_equal ["SELECT * FROM nodes WHERE id = 234"]
453
+ d.parent(Object.new).wont_equal 42
454
+ DB.sqls.must_equal ["SELECT * FROM nodes WHERE id = 234"]
453
455
  end
454
456
 
455
457
  it "should use a callback if given one as the argument" do
@@ -151,6 +151,7 @@ end
151
151
  describe Sequel::Model do
152
152
  before do
153
153
  @model = Class.new(Sequel::Model(:items))
154
+ DB.reset
154
155
  end
155
156
 
156
157
  it "has table_name return name of table" do
@@ -242,8 +243,9 @@ describe Sequel::Model do
242
243
  end
243
244
 
244
245
  it "doesn't raise an error on set_dataset if there is an error raised getting the schema" do
245
- def @model.get_db_schema(*) raise Sequel::Error end
246
- @model.set_dataset(DB[:foo])
246
+ db = Sequel.mock
247
+ def db.schema(*) raise Sequel::Error; end
248
+ @model.set_dataset(db[:foo])
247
249
  end
248
250
 
249
251
  it "reload_db_schema? should be false by default" do
@@ -253,10 +255,21 @@ describe Sequel::Model do
253
255
  end
254
256
 
255
257
  it "doesn't raise an error on inherited if there is an error setting the dataset" do
256
- def @model.set_dataset(*) raise Sequel::Error end
258
+ db = Sequel.mock
259
+ def db.schema(*) raise Sequel::Error; end
260
+ @model.db = db
257
261
  Class.new(@model)
258
262
  end
259
263
 
264
+ it "uses a savepoint if inside a transaction when getting the columns" do
265
+ db = Sequel.mock
266
+ def db.supports_savepoints?; true end
267
+ Sequel::Model(db[:table])
268
+ db.sqls.must_equal ["SELECT * FROM table LIMIT 1"]
269
+ c = db.transaction{Sequel::Model(db[:table])}
270
+ db.sqls.must_equal ["BEGIN", "SAVEPOINT autopoint_1", "SELECT * FROM table LIMIT 1", "RELEASE SAVEPOINT autopoint_1", "COMMIT"]
271
+ end
272
+
260
273
  it "should raise if bad inherited instance variable value is used" do
261
274
  def @model.inherited_instance_variables() super.merge(:@a=>:foo) end
262
275
  @model.instance_eval{@a=1}
@@ -277,6 +290,59 @@ describe Sequel::Model do
277
290
  end
278
291
  end
279
292
 
293
+ describe Sequel::Model, ".require_valid_table = true" do
294
+ before do
295
+ @db = Sequel.mock
296
+ @db.columns = proc do |sql|
297
+ raise Sequel::Error if sql =~ /foos/
298
+ [:id]
299
+ end
300
+ def @db.supports_schema_parsing?; true end
301
+ def @db.schema(t, *) t.first_source == :foos ? (raise Sequel::Error) : [[:id, {}]] end
302
+ Sequel::Model.db = @db
303
+ Sequel::Model.require_valid_table = true
304
+ end
305
+ after do
306
+ Sequel::Model.require_valid_table = false
307
+ Sequel::Model.db = DB
308
+ if Object.const_defined?(:Bar)
309
+ Object.send(:remove_const, :Bar)
310
+ end
311
+ if Object.const_defined?(:Foo)
312
+ Object.send(:remove_const, :Foo)
313
+ end
314
+ end
315
+
316
+ it "should raise an exception when creating a model with an invalid implicit table" do
317
+ proc{class ::Foo < Sequel::Model; end}.must_raise Sequel::Error
318
+ end
319
+
320
+ it "should not raise an exception when creating a model with a valid implicit table" do
321
+ class ::Bar < Sequel::Model; end
322
+ Bar.columns.must_equal [:id]
323
+ end
324
+
325
+ it "should raise an exception when creating a model with an invalid explicit table" do
326
+ proc{Sequel::Model(@db[:foos])}.must_raise Sequel::Error
327
+ end
328
+
329
+ it "should not raise an exception when creating a model with a valid explicit table" do
330
+ c = Sequel::Model(@db[:bars])
331
+ c.columns.must_equal [:id]
332
+ end
333
+
334
+ it "should raise an exception when calling set_dataset with an invalid table" do
335
+ c = Class.new(Sequel::Model)
336
+ proc{c.set_dataset @db[:foos]}.must_raise Sequel::Error
337
+ end
338
+
339
+ it "should not raise an exception when calling set_dataset with an valid table" do
340
+ c = Class.new(Sequel::Model)
341
+ c.set_dataset @db[:bars]
342
+ c.columns.must_equal [:id]
343
+ end
344
+ end
345
+
280
346
  describe Sequel::Model, "constructors" do
281
347
  before do
282
348
  @m = Class.new(Sequel::Model)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sequel
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.32.0
4
+ version: 4.33.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeremy Evans
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-03-01 00:00:00.000000000 Z
11
+ date: 2016-04-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: minitest
@@ -233,6 +233,7 @@ extra_rdoc_files:
233
233
  - doc/release_notes/4.30.0.txt
234
234
  - doc/release_notes/4.31.0.txt
235
235
  - doc/release_notes/4.32.0.txt
236
+ - doc/release_notes/4.33.0.txt
236
237
  files:
237
238
  - CHANGELOG
238
239
  - MIT-LICENSE
@@ -354,6 +355,7 @@ files:
354
355
  - doc/release_notes/4.30.0.txt
355
356
  - doc/release_notes/4.31.0.txt
356
357
  - doc/release_notes/4.32.0.txt
358
+ - doc/release_notes/4.33.0.txt
357
359
  - doc/release_notes/4.4.0.txt
358
360
  - doc/release_notes/4.5.0.txt
359
361
  - doc/release_notes/4.6.0.txt