sequel 3.42.0 → 3.43.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 (80) hide show
  1. data/CHANGELOG +40 -0
  2. data/MIT-LICENSE +1 -1
  3. data/Rakefile +1 -1
  4. data/doc/opening_databases.rdoc +2 -2
  5. data/doc/prepared_statements.rdoc +7 -0
  6. data/doc/release_notes/3.43.0.txt +105 -0
  7. data/doc/schema_modification.rdoc +19 -0
  8. data/lib/sequel/adapters/do/mysql.rb +1 -1
  9. data/lib/sequel/adapters/jdbc.rb +13 -8
  10. data/lib/sequel/adapters/jdbc/hsqldb.rb +5 -0
  11. data/lib/sequel/adapters/jdbc/mysql.rb +1 -1
  12. data/lib/sequel/adapters/jdbc/oracle.rb +6 -0
  13. data/lib/sequel/adapters/jdbc/postgresql.rb +9 -3
  14. data/lib/sequel/adapters/mysql.rb +1 -1
  15. data/lib/sequel/adapters/mysql2.rb +1 -1
  16. data/lib/sequel/adapters/oracle.rb +1 -1
  17. data/lib/sequel/adapters/postgres.rb +4 -2
  18. data/lib/sequel/adapters/shared/db2.rb +12 -0
  19. data/lib/sequel/adapters/shared/mssql.rb +9 -5
  20. data/lib/sequel/adapters/shared/postgres.rb +2 -0
  21. data/lib/sequel/adapters/swift/mysql.rb +1 -1
  22. data/lib/sequel/core.rb +2 -2
  23. data/lib/sequel/database.rb +0 -2
  24. data/lib/sequel/database/query.rb +20 -5
  25. data/lib/sequel/database/schema_generator.rb +5 -0
  26. data/lib/sequel/database/schema_methods.rb +5 -0
  27. data/lib/sequel/dataset.rb +0 -2
  28. data/lib/sequel/dataset/actions.rb +25 -2
  29. data/lib/sequel/dataset/misc.rb +1 -1
  30. data/lib/sequel/dataset/sql.rb +28 -6
  31. data/lib/sequel/extensions/core_refinements.rb +221 -0
  32. data/lib/sequel/extensions/date_arithmetic.rb +194 -0
  33. data/lib/sequel/extensions/meta_def.rb +30 -0
  34. data/lib/sequel/extensions/migration.rb +5 -0
  35. data/lib/sequel/extensions/null_dataset.rb +2 -0
  36. data/lib/sequel/extensions/pagination.rb +2 -0
  37. data/lib/sequel/extensions/pg_array.rb +12 -1
  38. data/lib/sequel/extensions/pg_array_ops.rb +10 -1
  39. data/lib/sequel/extensions/pg_hstore.rb +12 -1
  40. data/lib/sequel/extensions/pg_hstore_ops.rb +10 -1
  41. data/lib/sequel/extensions/pg_json.rb +18 -1
  42. data/lib/sequel/extensions/pg_range.rb +12 -1
  43. data/lib/sequel/extensions/pg_range_ops.rb +10 -1
  44. data/lib/sequel/extensions/pg_row.rb +18 -2
  45. data/lib/sequel/extensions/pg_row_ops.rb +10 -1
  46. data/lib/sequel/extensions/query.rb +2 -0
  47. data/lib/sequel/model/associations.rb +5 -13
  48. data/lib/sequel/model/base.rb +4 -6
  49. data/lib/sequel/plugins/boolean_readers.rb +4 -2
  50. data/lib/sequel/plugins/many_through_many.rb +23 -0
  51. data/lib/sequel/plugins/string_stripper.rb +53 -3
  52. data/lib/sequel/plugins/validation_class_methods.rb +5 -0
  53. data/lib/sequel/sql.rb +3 -3
  54. data/lib/sequel/version.rb +1 -1
  55. data/spec/adapters/db2_spec.rb +19 -8
  56. data/spec/adapters/mssql_spec.rb +1 -2
  57. data/spec/adapters/mysql_spec.rb +2 -2
  58. data/spec/adapters/postgres_spec.rb +29 -3
  59. data/spec/core/dataset_spec.rb +107 -0
  60. data/spec/core/expression_filters_spec.rb +5 -0
  61. data/spec/core/schema_spec.rb +14 -3
  62. data/spec/core/spec_helper.rb +2 -0
  63. data/spec/extensions/core_refinements_spec.rb +551 -0
  64. data/spec/extensions/date_arithmetic_spec.rb +150 -0
  65. data/spec/extensions/force_encoding_spec.rb +1 -1
  66. data/spec/extensions/meta_def_spec.rb +21 -0
  67. data/spec/extensions/spec_helper.rb +5 -0
  68. data/spec/extensions/string_stripper_spec.rb +44 -2
  69. data/spec/integration/associations_test.rb +2 -2
  70. data/spec/integration/plugin_test.rb +90 -0
  71. data/spec/integration/schema_test.rb +1 -1
  72. data/spec/model/association_reflection_spec.rb +4 -4
  73. data/spec/model/associations_spec.rb +2 -2
  74. data/spec/model/base_spec.rb +2 -2
  75. data/spec/model/eager_loading_spec.rb +5 -5
  76. data/spec/model/hooks_spec.rb +4 -4
  77. data/spec/model/model_spec.rb +9 -9
  78. data/spec/model/record_spec.rb +15 -18
  79. metadata +12 -5
  80. data/lib/sequel/metaprogramming.rb +0 -13
@@ -20,7 +20,8 @@
20
20
  # r = Sequel.expr(:row_column).pg_row
21
21
  #
22
22
  # If you have loaded the {core_extensions extension}[link:files/doc/core_extensions_rdoc.html]),
23
- # you can also call Symbol#pg_row:
23
+ # or you have loaded the {core_refinements extension}[link:files/doc/core_refinements_rdoc.html])
24
+ # and have activated refinements for the file, you can also use Symbol#pg_row:
24
25
  #
25
26
  # r = :row_column.pg_row
26
27
  #
@@ -180,3 +181,11 @@ if Sequel.core_extensions?
180
181
  include Sequel::Postgres::PGRowOp::ExpressionMethods
181
182
  end
182
183
  end
184
+
185
+ if defined?(Sequel::CoreRefinements)
186
+ module Sequel::CoreRefinements
187
+ refine Symbol do
188
+ include Sequel::Postgres::PGRowOp::ExpressionMethods
189
+ end
190
+ end
191
+ end
@@ -5,6 +5,8 @@
5
5
  # To load the extension, do:
6
6
  #
7
7
  # Sequel.extension :query
8
+ #
9
+ # This extension uses Object#extend at runtime, which can hurt performance.
8
10
 
9
11
  module Sequel
10
12
  class Database
@@ -69,11 +69,6 @@ module Sequel
69
69
  :"#{self[:name]}_dataset"
70
70
  end
71
71
 
72
- # Name symbol for the _helper internal association method
73
- def dataset_helper_method
74
- :"_#{self[:name]}_dataset_helper"
75
- end
76
-
77
72
  # Whether the dataset needs a primary key to function, true by default.
78
73
  def dataset_need_primary_key?
79
74
  true
@@ -1096,9 +1091,6 @@ module Sequel
1096
1091
 
1097
1092
  # Adds the association dataset methods to the association methods module.
1098
1093
  def def_association_dataset_methods(opts)
1099
- # If a block is given, define a helper method for it, because it takes
1100
- # an argument. This is unnecessary in Ruby 1.9, as that has instance_exec.
1101
- association_module_private_def(opts.dataset_helper_method, opts, &opts[:block]) if opts[:block]
1102
1094
  association_module_private_def(opts._dataset_method, opts, &opts[:dataset])
1103
1095
  association_module_def(opts.dataset_method, opts){_dataset(opts)}
1104
1096
  def_association_method(opts)
@@ -1122,10 +1114,10 @@ module Sequel
1122
1114
  lcpks = opts[:left_primary_keys] = Array(left_pk)
1123
1115
  lpkc = opts[:left_primary_key_column] ||= left_pk
1124
1116
  lpkcs = opts[:left_primary_key_columns] ||= Array(lpkc)
1125
- raise(Error, "mismatched number of left composite keys: #{lcks.inspect} vs #{lcpks.inspect}") unless lcks.length == lcpks.length
1117
+ raise(Error, "mismatched number of left keys: #{lcks.inspect} vs #{lcpks.inspect}") unless lcks.length == lcpks.length
1126
1118
  if opts[:right_primary_key]
1127
1119
  rcpks = Array(opts[:right_primary_key])
1128
- raise(Error, "mismatched number of right composite keys: #{rcks.inspect} vs #{rcpks.inspect}") unless rcks.length == rcpks.length
1120
+ raise(Error, "mismatched number of right keys: #{rcks.inspect} vs #{rcpks.inspect}") unless rcks.length == rcpks.length
1129
1121
  end
1130
1122
  uses_lcks = opts[:uses_left_composite_keys] = lcks.length > 1
1131
1123
  uses_rcks = opts[:uses_right_composite_keys] = rcks.length > 1
@@ -1221,7 +1213,7 @@ module Sequel
1221
1213
  opts[:qualified_key] = opts.qualify_cur(key)
1222
1214
  if opts[:primary_key]
1223
1215
  cpks = Array(opts[:primary_key])
1224
- raise(Error, "mismatched number of composite keys: #{cks.inspect} vs #{cpks.inspect}") unless cks.length == cpks.length
1216
+ raise(Error, "mismatched number of keys: #{cks.inspect} vs #{cpks.inspect}") unless cks.length == cpks.length
1225
1217
  end
1226
1218
  uses_cks = opts[:uses_composite_keys] = cks.length > 1
1227
1219
  qualify = opts[:qualify] != false
@@ -1281,7 +1273,7 @@ module Sequel
1281
1273
  cpks = opts[:primary_keys] = Array(primary_key)
1282
1274
  pkc = opts[:primary_key_column] ||= primary_key
1283
1275
  pkcs = opts[:primary_key_columns] ||= Array(pkc)
1284
- raise(Error, "mismatched number of composite keys: #{cks.inspect} vs #{cpks.inspect}") unless cks.length == cpks.length
1276
+ raise(Error, "mismatched number of keys: #{cks.inspect} vs #{cpks.inspect}") unless cks.length == cpks.length
1285
1277
  uses_cks = opts[:uses_composite_keys] = cks.length > 1
1286
1278
  opts[:dataset] ||= proc do
1287
1279
  opts.associated_class.filter(opts.predicate_keys.zip(cpks.map{|k| send(k)}))
@@ -1452,7 +1444,7 @@ module Sequel
1452
1444
  ds = ds.eager(*opts[:eager]) if opts[:eager]
1453
1445
  ds = ds.distinct if opts[:distinct]
1454
1446
  ds = ds.eager_graph(opts[:eager_graph]) if opts[:eager_graph] && opts.eager_graph_lazy_dataset?
1455
- ds = send(opts.dataset_helper_method, ds) if opts[:block]
1447
+ ds = instance_exec(ds, &opts[:block]) if opts[:block]
1456
1448
  ds
1457
1449
  end
1458
1450
 
@@ -2,8 +2,6 @@ module Sequel
2
2
  class Model
3
3
  extend Enumerable
4
4
  extend Inflections
5
- extend Metaprogramming
6
- include Metaprogramming
7
5
 
8
6
  # Class methods for Sequel::Model that implement basic model functionality.
9
7
  #
@@ -686,7 +684,7 @@ module Sequel
686
684
  if meth.to_s =~ NORMAL_METHOD_NAME_REGEXP
687
685
  instance_eval("def #{meth}(*args, &block); dataset.#{meth}(*args, &block) end", __FILE__, __LINE__)
688
686
  else
689
- meta_def(meth){|*args, &block| dataset.send(meth, *args, &block)}
687
+ (class << self; self; end).send(:define_method, meth){|*args, &block| dataset.send(meth, *args, &block)}
690
688
  end
691
689
  end
692
690
 
@@ -1085,11 +1083,11 @@ module Sequel
1085
1083
  def hash
1086
1084
  case primary_key
1087
1085
  when Array
1088
- [model, !pk.all? ? @values.sort_by{|k,v| k.to_s} : pk].hash
1086
+ [model, !pk.all? ? @values : pk].hash
1089
1087
  when Symbol
1090
- [model, pk.nil? ? @values.sort_by{|k,v| k.to_s} : pk].hash
1088
+ [model, pk.nil? ? @values : pk].hash
1091
1089
  else
1092
- [model, @values.sort_by{|k,v| k.to_s}].hash
1090
+ [model, @values].hash
1093
1091
  end
1094
1092
  end
1095
1093
 
@@ -26,8 +26,10 @@ module Sequel
26
26
  # Add the boolean_attribute? class method to the model, and create
27
27
  # attribute? boolean reader methods for the class's columns if the class has a dataset.
28
28
  def self.configure(model, &block)
29
- model.meta_def(:boolean_attribute?, &(block || DEFAULT_BOOLEAN_ATTRIBUTE_PROC))
30
- model.instance_eval{send(:create_boolean_readers) if @dataset}
29
+ model.instance_eval do
30
+ (class << self; self; end).send(:define_method, :boolean_attribute?, &(block || DEFAULT_BOOLEAN_ATTRIBUTE_PROC))
31
+ send(:create_boolean_readers) if @dataset
32
+ end
31
33
  end
32
34
 
33
35
  module ClassMethods
@@ -13,6 +13,29 @@ module Sequel
13
13
  #
14
14
  # Which will give you the tags for all of the artist's albums.
15
15
  #
16
+ # Let's break down the 2nd argument of the many_through_many call:
17
+ #
18
+ # [[:albums_artists, :artist_id, :album_id],
19
+ # [:albums, :id, :id],
20
+ # [:albums_tags, :album_id, :tag_id]]
21
+ #
22
+ # This argument is an array of arrays with three elements. Each entry in the main array represents a JOIN in SQL:
23
+ #
24
+ # * The first element in each array represents the name of the table to join.
25
+ # * The second element in each array represents the column used to join to the previous table.
26
+ # * The third element in each array represents the column used to join to the next table.
27
+ #
28
+ # So the "Artist.many_through_many :tags" is translated into something similar to:
29
+ #
30
+ # FROM artists
31
+ # JOIN albums_artists ON (artists.id = albums_artists.artist_id)
32
+ # JOIN albums ON (albums_artists.album_id = albums.id)
33
+ # JOIN albums_tags ON (albums.id = albums_tag.album_id)
34
+ # JOIN tags ON (albums_tags.tag_id = tags.id)
35
+ #
36
+ # The "artists.id" and "tags.id" criteria come from other association options (defaulting to the primary keys of the current and
37
+ # associated tables), but hopefully you can see how each argument in the array is used in the JOIN clauses.
38
+ #
16
39
  # Here are some more examples:
17
40
  #
18
41
  # # Same as Artist.many_to_many :albums
@@ -1,10 +1,18 @@
1
1
  module Sequel
2
2
  module Plugins
3
- # StringStripper is a very simple plugin that strips all input strings
3
+ # StringStripper is a plugin that strips all input strings
4
4
  # when assigning to the model's values. Example:
5
5
  #
6
6
  # album = Album.new(:name=>' A ')
7
7
  # album.name # => 'A'
8
+ #
9
+ # SQL::Blob instances and all non-strings are not modified by
10
+ # this plugin. Additionally, strings passed to a blob column
11
+ # setter are also not modified. You can explicitly set
12
+ # other columns to skip the stripping:
13
+ #
14
+ # Album.skip_string_stripping :foo
15
+ # Album.new(:foo=>' A ').foo # => ' A '
8
16
  #
9
17
  # Usage:
10
18
  #
@@ -14,11 +22,53 @@ module Sequel
14
22
  # # Make the Album class strip strings
15
23
  # Album.plugin :string_stripper
16
24
  module StringStripper
25
+ # Set blob columns as skipping stripping when plugin is loaded.
26
+ def self.configure(model)
27
+ model.instance_variable_set(:@skipped_string_stripping_columns, [])
28
+ model.send(:set_skipped_string_stripping_columns)
29
+ end
30
+
31
+ module ClassMethods
32
+ # Copy skipped stripping columns from superclass into subclass.
33
+ def inherited(subclass)
34
+ subclass.instance_variable_set(:@skipped_string_stripping_columns, @skipped_string_stripping_columns.dup)
35
+ super
36
+ end
37
+
38
+ # Set blob columns as skipping stripping when plugin is loaded.
39
+ def set_dataset(*)
40
+ super
41
+ set_skipped_string_stripping_columns
42
+ end
43
+
44
+ # Skip stripping for the given columns.
45
+ def skip_string_stripping(*columns)
46
+ @skipped_string_stripping_columns.concat(columns).uniq!
47
+ end
48
+
49
+ # Return true if the column should not have values stripped.
50
+ def skip_string_stripping?(column)
51
+ @skipped_string_stripping_columns.include?(column)
52
+ end
53
+
54
+ private
55
+
56
+ # Automatically skip stripping of blob columns
57
+ def set_skipped_string_stripping_columns
58
+ if @db_schema
59
+ blob_columns = @db_schema.map{|k,v| k if v[:type] == :blob}.compact
60
+ skip_string_stripping(*blob_columns)
61
+ end
62
+ end
63
+ end
64
+
17
65
  module InstanceMethods
18
- # Strip value if it is a string, before attempting to assign
66
+ # Strip value if it is a non-blob string and the model hasn't been set
67
+ # to skip stripping for the column, before attempting to assign
19
68
  # it to the model's values.
20
69
  def []=(k, v)
21
- v.is_a?(String) ? super(k, v.strip) : super
70
+ v = v.strip if v.is_a?(String) && !v.is_a?(SQL::Blob) && !model.skip_string_stripping?(k)
71
+ super(k, v)
22
72
  end
23
73
  end
24
74
  end
@@ -51,6 +51,11 @@ module Sequel
51
51
  def method_missing(m, *args, &block)
52
52
  @receiver.send(:"validates_#{m}", *args, &block)
53
53
  end
54
+
55
+ # This object responds to all validates_* methods the model responds to.
56
+ def respond_to_missing?(meth, include_private)
57
+ @receiver.respond_to?(:"validates_#{meth}", include_private)
58
+ end
54
59
  end
55
60
 
56
61
  # Returns true if validations are defined.
data/lib/sequel/sql.rb CHANGED
@@ -6,7 +6,7 @@ module Sequel
6
6
  class BasicObject
7
7
  # The instance methods to not remove from the class when removing
8
8
  # other methods.
9
- KEEP_METHODS = %w"__id__ __send__ __metaclass__ instance_eval == equal? initialize method_missing"
9
+ KEEP_METHODS = %w"__id__ __send__ __metaclass__ instance_eval instance_exec == equal? initialize method_missing"
10
10
 
11
11
  # Remove all but the most basic instance methods from the class. A separate
12
12
  # method so that it can be called again if necessary if you load libraries
@@ -655,7 +655,7 @@ module Sequel
655
655
  # Sequel.function(:func).cast_numeric # CAST(func() AS integer)
656
656
  # Sequel.function(:func).cast_numeric(Float) # CAST(func() AS double precision)
657
657
  def cast_numeric(sql_type = nil)
658
- cast(sql_type || Integer).sql_number
658
+ Cast.new(self, sql_type || Integer).sql_number
659
659
  end
660
660
 
661
661
  # Cast the reciever to the given SQL type (or the database's default String type if none given),
@@ -665,7 +665,7 @@ module Sequel
665
665
  # Sequel.function(:func).cast_string # CAST(func() AS varchar(255))
666
666
  # Sequel.function(:func).cast_string(:text) # CAST(func() AS text)
667
667
  def cast_string(sql_type = nil)
668
- cast(sql_type || String).sql_string
668
+ Cast.new(self, sql_type || String).sql_string
669
669
  end
670
670
  end
671
671
 
@@ -3,7 +3,7 @@ module Sequel
3
3
  MAJOR = 3
4
4
  # The minor version of Sequel. Bumped for every non-patch level
5
5
  # release, generally around once a month.
6
- MINOR = 42
6
+ MINOR = 43
7
7
  # The tiny version of Sequel. Usually 0, only bumped for bugfix
8
8
  # releases that fix regressions from previous versions.
9
9
  TINY = 0
@@ -1,5 +1,4 @@
1
1
  #!/usr/bin/env ruby
2
- # coding: utf-8
3
2
  #Author: Roy L Zuo (roylzuo at gmail dot com)
4
3
  #Description:
5
4
 
@@ -39,21 +38,33 @@ describe Sequel::Database do
39
38
  end
40
39
 
41
40
  describe "Simple Dataset operations" do
42
- before do
41
+ before(:all) do
42
+ Sequel::DB2.use_clob_as_blob = false
43
43
  DB2_DB.create_table!(:items) do
44
44
  Integer :id, :primary_key => true
45
45
  Integer :number
46
+ column :bin_string, 'varchar(20) for bit data'
47
+ column :bin_blob, 'blob'
46
48
  end
47
49
  @ds = DB2_DB[:items]
48
- @ds.insert(:number=>10, :id => 1 )
49
50
  end
50
- after do
51
+ after(:each) do
52
+ @ds.delete
53
+ end
54
+ after(:all) do
55
+ Sequel::DB2.use_clob_as_blob = true
51
56
  DB2_DB.drop_table(:items)
52
57
  end
53
- cspecify "should insert with a primary key specified", :mssql do
54
- @ds.insert(:id=>100, :number=>20)
55
- @ds.count.should == 2
56
- @ds.order(:id).all.should == [{:id=>1, :number=>10}, {:id=>100, :number=>20}]
58
+
59
+ specify "should insert with a primary key specified" do
60
+ @ds.insert(:id => 1, :number => 10)
61
+ @ds.insert(:id => 100, :number => 20)
62
+ @ds.select_hash(:id, :number).should == {1 => 10, 100 => 20}
63
+ end
64
+
65
+ specify "should insert into binary columns" do
66
+ @ds.insert(:id => 1, :bin_string => Sequel.blob("\1"), :bin_blob => Sequel.blob("\2"))
67
+ @ds.select(:bin_string, :bin_blob).first.should == {:bin_string => "\1", :bin_blob => "\2"}
57
68
  end
58
69
  end
59
70
 
@@ -1,4 +1,3 @@
1
- # encoding: utf-8
2
1
  require File.join(File.dirname(File.expand_path(__FILE__)), 'spec_helper.rb')
3
2
 
4
3
  require ENV['SEQUEL_MSSQL_SPEC_REQUIRE'] if ENV['SEQUEL_MSSQL_SPEC_REQUIRE']
@@ -391,7 +390,7 @@ describe "MSSSQL::Dataset#insert" do
391
390
  end
392
391
 
393
392
  specify "should have insert_select return nil if the server version is not 2005+" do
394
- @ds.meta_def(:server_version){8000760}
393
+ def @ds.server_version() 8000760 end
395
394
  @ds.insert_select(:value=>10).should == nil
396
395
  end
397
396
 
@@ -150,8 +150,8 @@ if [:mysql, :mysql2].include?(MYSQL_DB.adapter_scheme)
150
150
  specify "should allow disabling the conversion on a per-dataset basis" do
151
151
  @db.convert_tinyint_to_bool = true
152
152
  ds = @ds.clone
153
- ds.meta_def(:cast_tinyint_integer?){|f| true} # mysql
154
- ds.meta_def(:convert_tinyint_to_bool?){false} # mysql2
153
+ def ds.cast_tinyint_integer?(f) true end #mysql
154
+ def ds.convert_tinyint_to_bool?() false end #mysql2
155
155
  ds.delete
156
156
  ds << {:b=>true, :i=>10}
157
157
  ds.all.should == [{:b=>1, :i=>10}]
@@ -539,6 +539,25 @@ describe "A PostgreSQL database" do
539
539
  @db[:posts].insert.should == 21
540
540
  @db[:posts].order(:a).map(:a).should == [1, 2, 10, 20, 21]
541
541
  end
542
+
543
+ specify "should support resetting the primary key sequence with default_schema" do
544
+ begin
545
+ @db.run("DROP SCHEMA p") rescue nil
546
+ @db.run("CREATE SCHEMA p")
547
+ @db.default_schema = :p
548
+ @db.create_table(:posts){primary_key :a}
549
+ @db[:p__posts].insert(:a=>20).should == 20
550
+ @db[:p__posts].insert.should == 1
551
+ @db[:p__posts].insert.should == 2
552
+ @db[:p__posts].insert(:a=>10).should == 10
553
+ @db.reset_primary_key_sequence(:posts).should == 21
554
+ @db[:p__posts].insert.should == 21
555
+ @db[:p__posts].order(:a).map(:a).should == [1, 2, 10, 20, 21]
556
+ ensure
557
+ @db.default_schema = nil
558
+ @db.run("DROP SCHEMA p CASCADE")
559
+ end
560
+ end
542
561
 
543
562
  specify "should support specifying Integer/Bignum/Fixnum types in primary keys and have them be auto incrementing" do
544
563
  @db.create_table(:posts){primary_key :a, :type=>Integer}
@@ -1295,6 +1314,13 @@ if ((POSTGRES_DB.adapter_scheme == :postgres && SEQUEL_POSTGRES_USES_PG) || POST
1295
1314
  end
1296
1315
  end
1297
1316
 
1317
+ specify "should handle database errors with a rollback of copied data and still have a usable connection" do
1318
+ 2.times do
1319
+ proc{@db.copy_into(:test_copy, :data=>["1\t2\n", "3\ta\n"])}.should raise_error(Sequel::DatabaseError)
1320
+ @ds.select_map([:x, :y]).should == []
1321
+ end
1322
+ end
1323
+
1298
1324
  specify "should raise an Error if both :data and a block are provided" do
1299
1325
  proc{@db.copy_into(:test_copy, :data=>["1\t2\n", "3\t4\n"]){}}.should raise_error(Sequel::Error)
1300
1326
  end
@@ -2524,7 +2550,7 @@ describe 'PostgreSQL interval types' do
2524
2550
  v.should == ActiveSupport::Duration.new(31557600 + 2*86400*30 + 3*86400*7 + 4*86400 + 5*3600 + 6*60 + 7, [[:years, 1], [:months, 2], [:days, 25], [:seconds, 18367]])
2525
2551
  v.parts.sort_by{|k,v| k.to_s}.should == [[:years, 1], [:months, 2], [:days, 25], [:seconds, 18367]].sort_by{|k,v| k.to_s}
2526
2552
  end
2527
- end if ((require 'active_support/duration'; require 'active_support/inflector'; require 'active_support/core_ext/string/inflections'; true) rescue false)
2553
+ end if (begin require 'active_support/duration'; require 'active_support/inflector'; require 'active_support/core_ext/string/inflections'; true; rescue LoadError; false end)
2528
2554
 
2529
2555
  describe 'PostgreSQL row-valued/composite types' do
2530
2556
  before(:all) do
@@ -2547,8 +2573,8 @@ describe 'PostgreSQL row-valued/composite types' do
2547
2573
  column :employees, 'person[]'
2548
2574
  end
2549
2575
  @db.register_row_type(:address)
2550
- @db.register_row_type(:person)
2551
- @db.register_row_type(:company)
2576
+ @db.register_row_type(Sequel.qualify(:public, :person))
2577
+ @db.register_row_type(:public__company)
2552
2578
 
2553
2579
  @native = POSTGRES_DB.adapter_scheme == :postgres
2554
2580
  end
@@ -2686,6 +2686,40 @@ describe "Dataset#get" do
2686
2686
  @d.get(false).should == "SELECT 'f' FROM test LIMIT 1"
2687
2687
  @d.get(nil).should == "SELECT NULL FROM test LIMIT 1"
2688
2688
  end
2689
+
2690
+ specify "should support an array of expressions to get an array of results" do
2691
+ @d._fetch = {:name=>1, :abc=>2}
2692
+ @d.get([:name, :abc]).should == [1, 2]
2693
+ @d.db.sqls.should == ['SELECT name, abc FROM test LIMIT 1']
2694
+ end
2695
+
2696
+ specify "should support an array with a single expression" do
2697
+ @d.get([:name]).should == ['SELECT name FROM test LIMIT 1']
2698
+ end
2699
+
2700
+ specify "should handle an array with aliases" do
2701
+ @d._fetch = {:name=>1, :abc=>2}
2702
+ @d.get([:n___name, Sequel.as(:a, :abc)]).should == [1, 2]
2703
+ @d.db.sqls.should == ['SELECT n AS name, a AS abc FROM test LIMIT 1']
2704
+ end
2705
+
2706
+ specify "should raise an Error if an alias cannot be determined" do
2707
+ @d._fetch = {:name=>1, :abc=>2}
2708
+ proc{@d.get([Sequel.+(:a, 1), :a])}.should raise_error(Sequel::Error)
2709
+ end
2710
+
2711
+ specify "should support an array of expressions in a virtual row" do
2712
+ @d._fetch = {:name=>1, :abc=>2}
2713
+ @d.get{[name, n__abc]}.should == [1, 2]
2714
+ @d.db.sqls.should == ['SELECT name, n.abc FROM test LIMIT 1']
2715
+ end
2716
+
2717
+ specify "should work with static SQL" do
2718
+ @d.with_sql('SELECT foo').get(:name).should == "SELECT foo"
2719
+ @d._fetch = {:name=>1, :abc=>2}
2720
+ @d.with_sql('SELECT foo').get{[name, n__abc]}.should == [1, 2]
2721
+ @d.db.sqls.should == ['SELECT foo'] * 2
2722
+ end
2689
2723
  end
2690
2724
 
2691
2725
  describe "Dataset#set_row_proc" do
@@ -4414,3 +4448,76 @@ describe "Dataset extensions" do
4414
4448
  proc{@ds.extension(:foo2)}.should raise_error(Sequel::Error)
4415
4449
  end
4416
4450
  end
4451
+
4452
+ describe "Dataset#schema_and_table" do
4453
+ before do
4454
+ @ds = Sequel.mock[:test]
4455
+ end
4456
+
4457
+ it "should correctly handle symbols" do
4458
+ @ds.schema_and_table(:s).should == [nil, 's']
4459
+ @ds.schema_and_table(:s___a).should == [nil, 's']
4460
+ @ds.schema_and_table(:t__s).should == ['t', 's']
4461
+ @ds.schema_and_table(:t__s___a).should == ['t', 's']
4462
+ end
4463
+
4464
+ it "should correctly handle strings" do
4465
+ @ds.schema_and_table('s').should == [nil, 's']
4466
+ end
4467
+
4468
+ it "should correctly handle identifiers" do
4469
+ @ds.schema_and_table(Sequel.identifier(:s)).should == [nil, 's']
4470
+ end
4471
+
4472
+ it "should correctly handle qualified identifiers" do
4473
+ @ds.schema_and_table(Sequel.qualify(:t, :s)).should == ['t', 's']
4474
+ end
4475
+
4476
+ it "should respect default_schema" do
4477
+ @ds.db.default_schema = :foo
4478
+ @ds.schema_and_table(:s).should == ['foo', 's']
4479
+ @ds.schema_and_table(:s, nil).should == [nil, 's']
4480
+ end
4481
+ end
4482
+
4483
+ describe "Dataset#split_qualifiers" do
4484
+ before do
4485
+ @ds = Sequel.mock[:test]
4486
+ end
4487
+
4488
+ it "should correctly handle symbols" do
4489
+ @ds.split_qualifiers(:s).should == ['s']
4490
+ @ds.split_qualifiers(:s___a).should == ['s']
4491
+ @ds.split_qualifiers(:t__s).should == ['t', 's']
4492
+ @ds.split_qualifiers(:t__s___a).should == ['t', 's']
4493
+ end
4494
+
4495
+ it "should correctly handle strings" do
4496
+ @ds.split_qualifiers('s').should == ['s']
4497
+ end
4498
+
4499
+ it "should correctly handle identifiers" do
4500
+ @ds.split_qualifiers(Sequel.identifier(:s)).should == ['s']
4501
+ end
4502
+
4503
+ it "should correctly handle simple qualified identifiers" do
4504
+ @ds.split_qualifiers(Sequel.qualify(:t, :s)).should == ['t', 's']
4505
+ end
4506
+
4507
+ it "should correctly handle complex qualified identifiers" do
4508
+ @ds.split_qualifiers(Sequel.qualify(:d__t, :s)).should == ['d', 't', 's']
4509
+ @ds.split_qualifiers(Sequel.qualify(Sequel.qualify(:d, :t), :s)).should == ['d', 't', 's']
4510
+ @ds.split_qualifiers(Sequel.qualify(:d, :t__s)).should == ['d', 't', 's']
4511
+ @ds.split_qualifiers(Sequel.qualify(:d, Sequel.qualify(:t, :s))).should == ['d', 't', 's']
4512
+ @ds.split_qualifiers(Sequel.qualify(:d__t, :s__s2)).should == ['d', 't', 's', 's2']
4513
+ @ds.split_qualifiers(Sequel.qualify(Sequel.qualify(:d, :t), Sequel.qualify(:s, :s2))).should == ['d', 't', 's', 's2']
4514
+ end
4515
+
4516
+ it "should respect default_schema" do
4517
+ @ds.db.default_schema = :foo
4518
+ @ds.split_qualifiers(:s).should == ['foo', 's']
4519
+ @ds.split_qualifiers(:s, nil).should == ['s']
4520
+ @ds.split_qualifiers(Sequel.qualify(:d__t, :s)).should == ['d', 't', 's']
4521
+ end
4522
+ end
4523
+