sequel 4.32.0 → 4.33.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 (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