sequel 3.42.0 → 3.43.0

Sign up to get free protection for your applications and to get access to all the features.
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
+