sequel 3.32.0 → 3.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 (58) hide show
  1. data/CHANGELOG +42 -0
  2. data/Rakefile +3 -2
  3. data/doc/migration.rdoc +41 -14
  4. data/doc/opening_databases.rdoc +2 -0
  5. data/doc/release_notes/3.29.0.txt +1 -1
  6. data/doc/release_notes/3.33.0.txt +157 -0
  7. data/doc/sharding.rdoc +93 -7
  8. data/doc/testing.rdoc +1 -1
  9. data/lib/sequel/adapters/do.rb +1 -0
  10. data/lib/sequel/adapters/do/mysql.rb +6 -0
  11. data/lib/sequel/adapters/jdbc.rb +1 -0
  12. data/lib/sequel/adapters/jdbc/mysql.rb +0 -5
  13. data/lib/sequel/adapters/mock.rb +10 -5
  14. data/lib/sequel/adapters/mysql.rb +23 -1
  15. data/lib/sequel/adapters/mysql2.rb +16 -2
  16. data/lib/sequel/adapters/postgres.rb +22 -4
  17. data/lib/sequel/adapters/shared/mysql.rb +51 -10
  18. data/lib/sequel/adapters/shared/postgres.rb +101 -63
  19. data/lib/sequel/adapters/shared/sqlite.rb +19 -0
  20. data/lib/sequel/adapters/sqlite.rb +71 -16
  21. data/lib/sequel/adapters/swift.rb +1 -0
  22. data/lib/sequel/connection_pool.rb +1 -1
  23. data/lib/sequel/connection_pool/sharded_single.rb +6 -1
  24. data/lib/sequel/connection_pool/sharded_threaded.rb +6 -1
  25. data/lib/sequel/connection_pool/threaded.rb +12 -11
  26. data/lib/sequel/database/connecting.rb +2 -0
  27. data/lib/sequel/database/misc.rb +6 -0
  28. data/lib/sequel/database/query.rb +1 -1
  29. data/lib/sequel/extensions/arbitrary_servers.rb +108 -0
  30. data/lib/sequel/extensions/migration.rb +45 -7
  31. data/lib/sequel/extensions/server_block.rb +139 -0
  32. data/lib/sequel/model/associations.rb +9 -9
  33. data/lib/sequel/model/inflections.rb +1 -1
  34. data/lib/sequel/plugins/instance_hooks.rb +1 -1
  35. data/lib/sequel/plugins/json_serializer.rb +1 -1
  36. data/lib/sequel/plugins/list.rb +12 -2
  37. data/lib/sequel/version.rb +1 -1
  38. data/spec/adapters/mysql_spec.rb +64 -8
  39. data/spec/adapters/postgres_spec.rb +139 -78
  40. data/spec/adapters/sqlite_spec.rb +87 -0
  41. data/spec/core/connection_pool_spec.rb +14 -0
  42. data/spec/core/database_spec.rb +5 -0
  43. data/spec/core/mock_adapter_spec.rb +21 -9
  44. data/spec/extensions/arbitrary_servers_spec.rb +114 -0
  45. data/spec/extensions/instance_hooks_spec.rb +19 -0
  46. data/spec/extensions/list_spec.rb +31 -0
  47. data/spec/extensions/migration_spec.rb +61 -4
  48. data/spec/extensions/server_block_spec.rb +90 -0
  49. data/spec/extensions/spec_helper.rb +1 -1
  50. data/spec/files/transaction_migrations/001_create_alt_basic.rb +3 -0
  51. data/spec/files/transaction_migrations/002_create_basic.rb +3 -0
  52. data/spec/files/transactionless_migrations/001_create_alt_basic.rb +4 -0
  53. data/spec/files/transactionless_migrations/002_create_basic.rb +4 -0
  54. data/spec/integration/dataset_test.rb +2 -2
  55. data/spec/integration/plugin_test.rb +9 -9
  56. data/spec/integration/schema_test.rb +3 -1
  57. data/spec/integration/transaction_test.rb +2 -2
  58. metadata +12 -2
@@ -0,0 +1,139 @@
1
+ # The server_block extension adds the Database#with_server method, which takes a shard
2
+ # argument and a block, and makes it so that access inside the block will use the
3
+ # specified shard by default.
4
+ #
5
+ # First, you need to enable it on the database object:
6
+ #
7
+ # Sequel.extension :server_block
8
+ # DB.extend Sequel::ServerBlock
9
+ #
10
+ # Then you can call with_server:
11
+ #
12
+ # DB.with_server(:shard1) do
13
+ # DB[:a].all # Uses shard1
14
+ # DB[:a].server(:shard2).all # Uses shard2
15
+ # end
16
+ # DB[:a].all # Uses default
17
+ #
18
+ # You can even nest calls to with_server:
19
+ #
20
+ # DB.with_server(:shard1) do
21
+ # DB[:a].all # Uses shard1
22
+ # DB.with_server(:shard2) do
23
+ # DB[:a].all # Uses shard2
24
+ # end
25
+ # DB[:a].all # Uses shard1
26
+ # end
27
+ # DB[:a].all # Uses default
28
+ #
29
+ # Note this this extension assumes the following shard names should use the
30
+ # server/shard passed to with_server: :default, nil, :read_only. All other
31
+ # shard names will cause the standard behavior to be used.
32
+
33
+ module Sequel
34
+ module ServerBlock
35
+ # Enable the server block on the connection pool, choosing the correct
36
+ # extension depending on whether the connection pool is threaded or not.
37
+ # Also defines the with_server method on the receiver for easy use.
38
+ def self.extended(db)
39
+ pool = db.pool
40
+ if defined?(ShardedThreadedConnectionPool) && pool.is_a?(ShardedThreadedConnectionPool)
41
+ pool.extend(ThreadedServerBlock)
42
+ pool.instance_variable_set(:@default_servers, {})
43
+ else
44
+ pool.extend(UnthreadedServerBlock)
45
+ pool.instance_variable_set(:@default_servers, [])
46
+ end
47
+ end
48
+
49
+ # Delegate to the connection pool
50
+ def with_server(server, &block)
51
+ pool.with_server(server, &block)
52
+ end
53
+ end
54
+
55
+ # Adds with_server support for the sharded single connection pool.
56
+ module UnthreadedServerBlock
57
+ # Set a default server/shard to use inside the block.
58
+ def with_server(server)
59
+ begin
60
+ set_default_server(server)
61
+ yield
62
+ ensure
63
+ clear_default_server
64
+ end
65
+ end
66
+
67
+ private
68
+
69
+ # Make the given server the new default server.
70
+ def set_default_server(server)
71
+ @default_servers << server
72
+ end
73
+
74
+ # Remove the current default server, restoring the
75
+ # previous default server.
76
+ def clear_default_server
77
+ @default_servers.pop
78
+ end
79
+
80
+ # Use the server given to with_server if appropriate.
81
+ def pick_server(server)
82
+ if @default_servers.empty?
83
+ super
84
+ else
85
+ case server
86
+ when :default, nil, :read_only
87
+ @default_servers.last
88
+ else
89
+ super
90
+ end
91
+ end
92
+ end
93
+ end
94
+
95
+ # Adds with_server support for the sharded threaded connection pool.
96
+ module ThreadedServerBlock
97
+ # Set a default server/shard to use inside the block for the current
98
+ # thread.
99
+ def with_server(server)
100
+ begin
101
+ set_default_server(server)
102
+ yield
103
+ ensure
104
+ clear_default_server
105
+ end
106
+ end
107
+
108
+ private
109
+
110
+ # Make the given server the new default server for the current thread.
111
+ def set_default_server(server)
112
+ sync{(@default_servers[Thread.current] ||= [])} << server
113
+ end
114
+
115
+ # Remove the current default server for the current thread, restoring the
116
+ # previous default server.
117
+ def clear_default_server
118
+ t = Thread.current
119
+ a = sync{@default_servers[t]}
120
+ a.pop
121
+ sync{@default_servers.delete(t)} if a.empty?
122
+ end
123
+
124
+ # Use the server given to with_server for the given thread, if appropriate.
125
+ def pick_server(server)
126
+ a = sync{@default_servers[Thread.current]}
127
+ if !a || a.empty?
128
+ super
129
+ else
130
+ case server
131
+ when :default, nil, :read_only
132
+ a.last
133
+ else
134
+ super
135
+ end
136
+ end
137
+ end
138
+ end
139
+ end
@@ -704,7 +704,7 @@ module Sequel
704
704
  # singularized unless the type is :many_to_one or :one_to_one)
705
705
  # :clone :: Merge the current options and block into the options and block used in defining
706
706
  # the given association. Can be used to DRY up a bunch of similar associations that
707
- # all share the same options such as :class and :key, while changing the order and block used.
707
+ # all share the same options such as :class and :key, while changing the order and block used.
708
708
  # :conditions :: The conditions to use to filter the association, can be any argument passed to filter.
709
709
  # :dataset :: A proc that is instance_evaled to get the base dataset
710
710
  # to use for the _dataset method (before the other options are applied).
@@ -809,9 +809,9 @@ module Sequel
809
809
  # Defaults to primary key of the current table. Can use an
810
810
  # array of symbols for a composite key association.
811
811
  # :primary_key_column :: Similar to, and usually identical to, :primary_key, but :primary_key refers
812
- # to the model method call, where :primary_key_column refers to the underlying column.
813
- # Should only be used if the the model method differs from the primary key column, in
814
- # conjunction with defining a model alias method for the primary key column.
812
+ # to the model method call, where :primary_key_column refers to the underlying column.
813
+ # Should only be used if the the model method differs from the primary key column, in
814
+ # conjunction with defining a model alias method for the primary key column.
815
815
  # === :many_to_many
816
816
  # :graph_join_table_block :: The block to pass to +join_table+ for
817
817
  # the join table when eagerly loading the association via +eager_graph+.
@@ -833,17 +833,17 @@ module Sequel
833
833
  # :join_table_block :: proc that can be used to modify the dataset used in the add/remove/remove_all
834
834
  # methods. Should accept a dataset argument and return a modified dataset if present.
835
835
  # :left_key :: foreign key in join table that points to current model's
836
- # primary key, as a symbol. Defaults to :"#{self.name.underscore}_id".
836
+ # primary key, as a symbol. Defaults to :"#{self.name.underscore}_id".
837
837
  # Can use an array of symbols for a composite key association.
838
838
  # :left_primary_key :: column in current table that :left_key points to, as a symbol.
839
839
  # Defaults to primary key of current table. Can use an
840
840
  # array of symbols for a composite key association.
841
841
  # :left_primary_key_column :: Similar to, and usually identical to, :left_primary_key, but :left_primary_key refers to
842
- # the model method to call, where :left_primary_key_column refers to the underlying column. Should only
843
- # be used if the model method differs from the left primary key column, in conjunction
844
- # with defining a model alias method for the left primary key column.
842
+ # the model method to call, where :left_primary_key_column refers to the underlying column. Should only
843
+ # be used if the model method differs from the left primary key column, in conjunction
844
+ # with defining a model alias method for the left primary key column.
845
845
  # :right_key :: foreign key in join table that points to associated
846
- # model's primary key, as a symbol. Defaults to Defaults to :"#{name.to_s.singularize}_id".
846
+ # model's primary key, as a symbol. Defaults to :"#{name.to_s.singularize}_id".
847
847
  # Can use an array of symbols for a composite key association.
848
848
  # :right_primary_key :: column in associated table that :right_key points to, as a symbol.
849
849
  # Defaults to primary key of the associated table. Can use an
@@ -121,7 +121,7 @@ module Sequel
121
121
  def constantize(s)
122
122
  s = s.to_s
123
123
  return s.constantize if s.respond_to?(:constantize)
124
- raise(NameError, "#{inspect} is not a valid constant name!") unless m = VALID_CONSTANT_NAME_REGEXP.match(s.to_s)
124
+ raise(NameError, "#{s.inspect} is not a valid constant name!") unless m = VALID_CONSTANT_NAME_REGEXP.match(s)
125
125
  Object.module_eval("::#{m[1]}", __FILE__, __LINE__)
126
126
  end
127
127
 
@@ -29,7 +29,7 @@ module Sequel
29
29
  BEFORE_HOOKS = Sequel::Model::BEFORE_HOOKS
30
30
  AFTER_HOOKS = Sequel::Model::AFTER_HOOKS - [:after_initialize]
31
31
  HOOKS = BEFORE_HOOKS + AFTER_HOOKS
32
- HOOKS.each{|h| class_eval("def #{h}_hook(&block); add_instance_hook(:#{h}, &block) end", __FILE__, __LINE__)}
32
+ HOOKS.each{|h| class_eval("def #{h}_hook(&block); add_instance_hook(:#{h}, &block); self end", __FILE__, __LINE__)}
33
33
 
34
34
  BEFORE_HOOKS.each{|h| class_eval("def #{h}; run_before_instance_hooks(:#{h}) == false ? false : super end", __FILE__, __LINE__)}
35
35
  AFTER_HOOKS.each{|h| class_eval(<<-END, __FILE__, __LINE__ + 1)}
@@ -149,7 +149,7 @@ module Sequel
149
149
  # to include in the JSON output. Using a nested
150
150
  # hash, you can pass options to associations
151
151
  # to affect the JSON used for associated objects.
152
- # :naked :: Not to add the JSON.create_id key to the JSON
152
+ # :naked :: Not to add the JSON.create_id (json_class) key to the JSON
153
153
  # output hash, so when the JSON is parsed, it
154
154
  # will yield a hash instead of a model object.
155
155
  # :only :: Symbol or Array of Symbols of columns to only
@@ -39,9 +39,11 @@ module Sequel
39
39
  # Item.plugin :list, :scope=>:user_id
40
40
  #
41
41
  # Note that using this plugin modifies the order of the model's dataset to
42
- # sort by the position and scope fields.
42
+ # sort by the position and scope fields. Also note that this plugin is subject to
43
+ # race conditions, and is not safe when concurrent modifications are made
44
+ # to the same list.
43
45
  #
44
- # Also note that unlike ruby arrays, the list plugin assumes that the
46
+ # Additionally, note that unlike ruby arrays, the list plugin assumes that the
45
47
  # first entry in the list has position 1, not position 0.
46
48
  #
47
49
  # Copyright (c) 2007-2010 Sharon Rosner, Wayne E. Seguin, Aman Gupta, Adrian Madrid, Jeremy Evans
@@ -90,6 +92,14 @@ module Sequel
90
92
  list_dataset.first(position_field => p)
91
93
  end
92
94
 
95
+ # Set the value of the position_field to the maximum value plus 1 unless the
96
+ # position field already has a value.
97
+ def before_create
98
+ unless send(position_field)
99
+ send("#{position_field}=", list_dataset.max(position_field).to_i+1)
100
+ end
101
+ end
102
+
93
103
  # Find the last position in the list containing this instance.
94
104
  def last_position
95
105
  list_dataset.max(position_field).to_i
@@ -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 = 32
6
+ MINOR = 33
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
@@ -434,6 +434,7 @@ describe "A MySQL database" do
434
434
 
435
435
  specify "should support add_foreign_key" do
436
436
  @db.alter_table :test2 do
437
+ add_index :value, :unique=>true
437
438
  add_foreign_key :value2, :test2, :key=>:value
438
439
  end
439
440
  @db[:test2].columns.should == [:name, :value, :zyx, :ert, :xyz, :value2]
@@ -485,6 +486,7 @@ describe "A MySQL database" do
485
486
  end
486
487
  after do
487
488
  @db.drop_table(:items) rescue nil
489
+ @db.drop_table(:users) rescue nil
488
490
  end
489
491
 
490
492
  specify "should support defaults for boolean columns" do
@@ -493,14 +495,18 @@ describe "A MySQL database" do
493
495
  end
494
496
 
495
497
  specify "should correctly format CREATE TABLE statements with foreign keys" do
496
- @db.create_table(:items){Integer :id; foreign_key :p_id, :items, :key => :id, :null => false, :on_delete => :cascade}
497
- @db.sqls.should == ["CREATE TABLE `items` (`id` integer, `p_id` integer NOT NULL, FOREIGN KEY (`p_id`) REFERENCES `items`(`id`) ON DELETE CASCADE)"]
498
+ @db.create_table(:items){primary_key :id; foreign_key :p_id, :items, :key => :id, :null => false, :on_delete => :cascade}
499
+ @db.sqls.should == ["CREATE TABLE `items` (`id` integer PRIMARY KEY AUTO_INCREMENT, `p_id` integer NOT NULL, UNIQUE (`id`), FOREIGN KEY (`p_id`) REFERENCES `items`(`id`) ON DELETE CASCADE)"]
498
500
  end
499
501
 
500
502
  specify "should correctly format ALTER TABLE statements with foreign keys" do
501
503
  @db.create_table(:items){Integer :id}
504
+ @db.create_table(:users){primary_key :id}
502
505
  @db.alter_table(:items){add_foreign_key :p_id, :users, :key => :id, :null => false, :on_delete => :cascade}
503
- @db.sqls.should == ["CREATE TABLE `items` (`id` integer)", "ALTER TABLE `items` ADD COLUMN `p_id` integer NOT NULL", "ALTER TABLE `items` ADD FOREIGN KEY (`p_id`) REFERENCES `users`(`id`) ON DELETE CASCADE"]
506
+ @db.sqls.should == ["CREATE TABLE `items` (`id` integer)",
507
+ "CREATE TABLE `users` (`id` integer PRIMARY KEY AUTO_INCREMENT)",
508
+ "ALTER TABLE `items` ADD COLUMN `p_id` integer NOT NULL",
509
+ "ALTER TABLE `items` ADD FOREIGN KEY (`p_id`) REFERENCES `users`(`id`) ON DELETE CASCADE"]
504
510
  end
505
511
 
506
512
  specify "should have rename_column support keep existing options" do
@@ -593,9 +599,59 @@ describe "A MySQL database" do
593
599
  end
594
600
  end
595
601
 
602
+ describe "MySQL foreign key support" do
603
+ after do
604
+ MYSQL_DB.drop_table(:testfk) rescue nil
605
+ MYSQL_DB.drop_table(:testpk) rescue nil
606
+ end
607
+
608
+ specify "should create table without :key" do
609
+ MYSQL_DB.create_table!(:testpk){primary_key :id}
610
+ MYSQL_DB.create_table!(:testfk){foreign_key :fk, :testpk}
611
+ end
612
+
613
+ specify "should create table with composite keys without :key" do
614
+ MYSQL_DB.create_table!(:testpk){Integer :id; Integer :id2; primary_key([:id, :id2])}
615
+ MYSQL_DB.create_table!(:testfk){Integer :fk; Integer :fk2; foreign_key([:fk, :fk2], :testpk)}
616
+ end
617
+
618
+ specify "should create table with self referential without :key" do
619
+ MYSQL_DB.create_table!(:testfk){primary_key :id; foreign_key :fk, :testfk}
620
+ end
621
+
622
+ specify "should create table with self referential with composite keys without :key" do
623
+ MYSQL_DB.create_table!(:testfk){Integer :id; Integer :id2; Integer :fk; Integer :fk2; primary_key([:id, :id2]); foreign_key([:fk, :fk2], :testfk)}
624
+ end
625
+
626
+ specify "should alter table without :key" do
627
+ MYSQL_DB.create_table!(:testpk){primary_key :id}
628
+ MYSQL_DB.create_table!(:testfk){Integer :id}
629
+ MYSQL_DB.alter_table(:testfk){add_foreign_key :fk, :testpk}
630
+ end
631
+
632
+ specify "should alter table with composite keys without :key" do
633
+ MYSQL_DB.create_table!(:testpk){Integer :id; Integer :id2; primary_key([:id, :id2])}
634
+ MYSQL_DB.create_table!(:testfk){Integer :fk; Integer :fk2}
635
+ MYSQL_DB.alter_table(:testfk){add_foreign_key([:fk, :fk2], :testpk)}
636
+ end
637
+
638
+ specify "should alter table with self referential without :key" do
639
+ MYSQL_DB.create_table!(:testfk){primary_key :id}
640
+ MYSQL_DB.alter_table(:testfk){add_foreign_key :fk, :testfk}
641
+ end
642
+
643
+ specify "should alter table with self referential with composite keys without :key" do
644
+ MYSQL_DB.create_table!(:testfk){Integer :id; Integer :id2; Integer :fk; Integer :fk2; primary_key([:id, :id2])}
645
+ MYSQL_DB.alter_table(:testfk){add_foreign_key [:fk, :fk2], :testfk}
646
+ end
647
+ end
648
+
596
649
  describe "A grouped MySQL dataset" do
597
650
  before do
598
- MYSQL_DB[:test2].delete
651
+ MYSQL_DB.create_table! :test2 do
652
+ text :name
653
+ integer :value
654
+ end
599
655
  MYSQL_DB[:test2] << {:name => '11', :value => 10}
600
656
  MYSQL_DB[:test2] << {:name => '11', :value => 20}
601
657
  MYSQL_DB[:test2] << {:name => '11', :value => 30}
@@ -626,9 +682,9 @@ describe "A MySQL database" do
626
682
  end
627
683
 
628
684
  specify "should support fulltext indexes and full_text_search" do
629
- @db.create_table(:posts){text :title; text :body; full_text_index :title; full_text_index [:title, :body]}
685
+ @db.create_table(:posts, :engine=>:MyISAM){text :title; text :body; full_text_index :title; full_text_index [:title, :body]}
630
686
  @db.sqls.should == [
631
- "CREATE TABLE `posts` (`title` text, `body` text)",
687
+ "CREATE TABLE `posts` (`title` text, `body` text) ENGINE=MyISAM",
632
688
  "CREATE FULLTEXT INDEX `posts_title_index` ON `posts` (`title`)",
633
689
  "CREATE FULLTEXT INDEX `posts_title_body_index` ON `posts` (`title`, `body`)"
634
690
  ]
@@ -651,9 +707,9 @@ describe "A MySQL database" do
651
707
  end
652
708
 
653
709
  specify "should support spatial indexes" do
654
- @db.create_table(:posts){point :geom, :null=>false; spatial_index [:geom]}
710
+ @db.create_table(:posts, :engine=>:MyISAM){point :geom, :null=>false; spatial_index [:geom]}
655
711
  @db.sqls.should == [
656
- "CREATE TABLE `posts` (`geom` point NOT NULL)",
712
+ "CREATE TABLE `posts` (`geom` point NOT NULL) ENGINE=MyISAM",
657
713
  "CREATE SPATIAL INDEX `posts_geom_index` ON `posts` (`geom`)"
658
714
  ]
659
715
  end
@@ -37,7 +37,7 @@ describe "A PostgreSQL database" do
37
37
  before do
38
38
  @db = POSTGRES_DB
39
39
  end
40
-
40
+
41
41
  specify "should provide the server version" do
42
42
  @db.server_version.should > 70000
43
43
  end
@@ -59,18 +59,18 @@ describe "A PostgreSQL dataset" do
59
59
  @d = POSTGRES_DB[:test]
60
60
  @d.delete # remove all records
61
61
  end
62
-
62
+
63
63
  specify "should quote columns and tables using double quotes if quoting identifiers" do
64
64
  @d.quote_identifiers = true
65
65
  @d.select(:name).sql.should == \
66
66
  'SELECT "name" FROM "test"'
67
-
67
+
68
68
  @d.select('COUNT(*)'.lit).sql.should == \
69
69
  'SELECT COUNT(*) FROM "test"'
70
70
 
71
71
  @d.select(:max.sql_function(:value)).sql.should == \
72
72
  'SELECT max("value") FROM "test"'
73
-
73
+
74
74
  @d.select(:NOW.sql_function).sql.should == \
75
75
  'SELECT NOW() FROM "test"'
76
76
 
@@ -82,13 +82,13 @@ describe "A PostgreSQL dataset" do
82
82
 
83
83
  @d.select('test.name AS item_name'.lit).sql.should == \
84
84
  'SELECT test.name AS item_name FROM "test"'
85
-
85
+
86
86
  @d.select('"name"'.lit).sql.should == \
87
87
  'SELECT "name" FROM "test"'
88
88
 
89
89
  @d.select('max(test."name") AS "max_name"'.lit).sql.should == \
90
90
  'SELECT max(test."name") AS "max_name" FROM "test"'
91
-
91
+
92
92
  @d.select(:test.sql_function(:abc, 'hello')).sql.should == \
93
93
  "SELECT test(\"abc\", 'hello') FROM \"test\""
94
94
 
@@ -107,7 +107,7 @@ describe "A PostgreSQL dataset" do
107
107
  @d.disable_insert_returning.insert_sql(:value => 333).should =~ \
108
108
  /\AINSERT INTO "test" \("value"\) VALUES \(333\)\z/
109
109
  end
110
-
110
+
111
111
  specify "should quote fields correctly when reversing the order if quoting identifiers" do
112
112
  @d.quote_identifiers = true
113
113
  @d.reverse_order(:name).sql.should == \
@@ -129,7 +129,7 @@ describe "A PostgreSQL dataset" do
129
129
  @d.filter(:name => /bc/).count.should == 2
130
130
  @d.filter(:name => /^bc/).count.should == 1
131
131
  end
132
-
132
+
133
133
  specify "should support NULLS FIRST and NULLS LAST" do
134
134
  @d << {:name => 'abc'}
135
135
  @d << {:name => 'bcd'}
@@ -138,20 +138,20 @@ describe "A PostgreSQL dataset" do
138
138
  @d.order(:value.asc(:nulls=>:last), :name).select_map(:name).should == %w[bcd abc bcd]
139
139
  @d.order(:value.asc(:nulls=>:first), :name).reverse.select_map(:name).should == %w[bcd bcd abc]
140
140
  end
141
-
141
+
142
142
  specify "#lock should lock tables and yield if a block is given" do
143
143
  @d.lock('EXCLUSIVE'){@d.insert(:name=>'a')}
144
144
  end
145
-
145
+
146
146
  specify "#lock should lock table if inside a transaction" do
147
147
  POSTGRES_DB.transaction{@d.lock('EXCLUSIVE'); @d.insert(:name=>'a')}
148
148
  end
149
-
149
+
150
150
  specify "#lock should return nil" do
151
151
  @d.lock('EXCLUSIVE'){@d.insert(:name=>'a')}.should == nil
152
152
  POSTGRES_DB.transaction{@d.lock('EXCLUSIVE').should == nil; @d.insert(:name=>'a')}
153
153
  end
154
-
154
+
155
155
  specify "should raise an error if attempting to update a joined dataset with a single FROM table" do
156
156
  proc{POSTGRES_DB[:test].join(:test2, [:name]).update(:name=>'a')}.should raise_error(Sequel::Error, 'Need multiple FROM tables if updating/deleting a dataset with JOINs')
157
157
  end
@@ -169,7 +169,7 @@ describe "Dataset#distinct" do
169
169
  after do
170
170
  @db.drop_table(:a)
171
171
  end
172
-
172
+
173
173
  it "#distinct with arguments should return results distinct on those arguments" do
174
174
  @ds.insert(20, 10)
175
175
  @ds.insert(30, 10)
@@ -194,7 +194,7 @@ if POSTGRES_DB.pool.respond_to?(:max_size) and POSTGRES_DB.pool.max_size > 1
194
194
  POSTGRES_DB.drop_table(:items)
195
195
  POSTGRES_DB.disconnect
196
196
  end
197
-
197
+
198
198
  specify "should handle FOR UPDATE" do
199
199
  @ds.insert(:number=>20)
200
200
  c, t = nil, nil
@@ -216,7 +216,7 @@ if POSTGRES_DB.pool.respond_to?(:max_size) and POSTGRES_DB.pool.max_size > 1
216
216
  t.join
217
217
  c.should == {:id=>1, :number=>30, :name=>'Jim'}
218
218
  end
219
-
219
+
220
220
  specify "should handle FOR SHARE" do
221
221
  @ds.insert(:number=>20)
222
222
  c, t = nil
@@ -285,12 +285,12 @@ describe "A PostgreSQL database" do
285
285
  @db[:test2].columns.should == [:name, :value, :xyz]
286
286
  @db[:test2] << {:name => 'mmm', :value => 111}
287
287
  @db[:test2].first[:xyz].should == '000'
288
-
288
+
289
289
  @db[:test2].columns.should == [:name, :value, :xyz]
290
290
  @db.drop_column :test2, :xyz
291
-
291
+
292
292
  @db[:test2].columns.should == [:name, :value]
293
-
293
+
294
294
  @db[:test2].delete
295
295
  @db.add_column :test2, :xyz, :text, :default => '000'
296
296
  @db[:test2] << {:name => 'mmm', :value => 111, :xyz => 'qqqq'}
@@ -299,20 +299,20 @@ describe "A PostgreSQL database" do
299
299
  @db.rename_column :test2, :xyz, :zyx
300
300
  @db[:test2].columns.should == [:name, :value, :zyx]
301
301
  @db[:test2].first[:zyx].should == 'qqqq'
302
-
302
+
303
303
  @db.add_column :test2, :xyz, :float
304
304
  @db[:test2].delete
305
305
  @db[:test2] << {:name => 'mmm', :value => 111, :xyz => 56.78}
306
306
  @db.set_column_type :test2, :xyz, :integer
307
-
307
+
308
308
  @db[:test2].first[:xyz].should == 57
309
309
  end
310
-
310
+
311
311
  specify "#locks should be a dataset returning database locks " do
312
312
  @db.locks.should be_a_kind_of(Sequel::Dataset)
313
313
  @db.locks.all.should be_a_kind_of(Array)
314
314
  end
315
- end
315
+ end
316
316
 
317
317
  describe "A PostgreSQL database" do
318
318
  before do
@@ -323,7 +323,7 @@ describe "A PostgreSQL database" do
323
323
  after do
324
324
  @db.drop_table(:posts) rescue nil
325
325
  end
326
-
326
+
327
327
  specify "should support resetting the primary key sequence" do
328
328
  @db.create_table(:posts){primary_key :a}
329
329
  @db[:posts].insert(:a=>20).should == 20
@@ -334,7 +334,7 @@ describe "A PostgreSQL database" do
334
334
  @db[:posts].insert.should == 21
335
335
  @db[:posts].order(:a).map(:a).should == [1, 2, 10, 20, 21]
336
336
  end
337
-
337
+
338
338
  specify "should support specifying Integer/Bignum/Fixnum types in primary keys and have them be auto incrementing" do
339
339
  @db.create_table(:posts){primary_key :a, :type=>Integer}
340
340
  @db[:posts].insert.should == 1
@@ -351,7 +351,7 @@ describe "A PostgreSQL database" do
351
351
  @db.create_table(:posts){Integer :a}
352
352
  @db.reset_primary_key_sequence(:posts).should == nil
353
353
  end
354
-
354
+
355
355
  specify "should support opclass specification" do
356
356
  @db.create_table(:posts){text :title; text :body; integer :user_id; index(:user_id, :opclass => :int4_ops, :type => :btree)}
357
357
  @db.sqls.should == [
@@ -392,7 +392,7 @@ describe "A PostgreSQL database" do
392
392
  'CREATE INDEX "posts_geom_index" ON "posts" USING gist ("geom")'
393
393
  ]
394
394
  end
395
-
395
+
396
396
  specify "should support indexes with index type" do
397
397
  @db.create_table(:posts){varchar :title, :size => 5; index :title, :type => 'hash'}
398
398
  @db.sqls.should == [
@@ -400,7 +400,7 @@ describe "A PostgreSQL database" do
400
400
  'CREATE INDEX "posts_title_index" ON "posts" USING hash ("title")'
401
401
  ]
402
402
  end
403
-
403
+
404
404
  specify "should support unique indexes with index type" do
405
405
  @db.create_table(:posts){varchar :title, :size => 5; index :title, :type => 'btree', :unique => true}
406
406
  @db.sqls.should == [
@@ -408,7 +408,7 @@ describe "A PostgreSQL database" do
408
408
  'CREATE UNIQUE INDEX "posts_title_index" ON "posts" USING btree ("title")'
409
409
  ]
410
410
  end
411
-
411
+
412
412
  specify "should support partial indexes" do
413
413
  @db.create_table(:posts){varchar :title, :size => 5; index :title, :where => {:title => '5'}}
414
414
  @db.sqls.should == [
@@ -416,7 +416,7 @@ describe "A PostgreSQL database" do
416
416
  'CREATE INDEX "posts_title_index" ON "posts" ("title") WHERE ("title" = \'5\')'
417
417
  ]
418
418
  end
419
-
419
+
420
420
  specify "should support identifiers for table names in indicies" do
421
421
  @db.create_table(Sequel::SQL::Identifier.new(:posts)){varchar :title, :size => 5; index :title, :where => {:title => '5'}}
422
422
  @db.sqls.should == [
@@ -424,7 +424,7 @@ describe "A PostgreSQL database" do
424
424
  'CREATE INDEX "posts_title_index" ON "posts" ("title") WHERE ("title" = \'5\')'
425
425
  ]
426
426
  end
427
-
427
+
428
428
  specify "should support renaming tables" do
429
429
  @db.create_table!(:posts1){primary_key :a}
430
430
  @db.rename_table(:posts1, :posts)
@@ -441,45 +441,45 @@ describe "Postgres::Dataset#import" do
441
441
  after do
442
442
  @db.drop_table(:test) rescue nil
443
443
  end
444
-
444
+
445
445
  specify "#import should return separate insert statements if server_version < 80200" do
446
446
  @ds.meta_def(:server_version){80199}
447
447
  @ds.import([:x, :y], [[1, 2], [3, 4]])
448
448
  @db.sqls.should == ['BEGIN', 'INSERT INTO "test" ("x", "y") VALUES (1, 2)', 'INSERT INTO "test" ("x", "y") VALUES (3, 4)', 'COMMIT']
449
449
  @ds.all.should == [{:x=>1, :y=>2}, {:x=>3, :y=>4}]
450
450
  end
451
-
451
+
452
452
  specify "#import should a single insert statement if server_version >= 80200" do
453
453
  @ds.meta_def(:server_version){80200}
454
454
  @ds.import([:x, :y], [[1, 2], [3, 4]])
455
455
  @db.sqls.should == ['BEGIN', 'INSERT INTO "test" ("x", "y") VALUES (1, 2), (3, 4)', 'COMMIT']
456
456
  @ds.all.should == [{:x=>1, :y=>2}, {:x=>3, :y=>4}]
457
457
  end
458
-
458
+
459
459
  specify "#import should work correctly when returning primary keys for server_version < 80200" do
460
460
  @ds.meta_def(:server_version){80199}
461
461
  @ds.import([:x, :y], [[1, 2], [3, 4]], :return=>:primary_key).should == [1, 3]
462
462
  @ds.all.should == [{:x=>1, :y=>2}, {:x=>3, :y=>4}]
463
463
  end
464
-
464
+
465
465
  specify "#import should work correctly when returning primary keys for server_version >= 80200" do
466
466
  @ds.meta_def(:server_version){80200}
467
467
  @ds.import([:x, :y], [[1, 2], [3, 4]], :return=>:primary_key).should == [1, 3]
468
468
  @ds.all.should == [{:x=>1, :y=>2}, {:x=>3, :y=>4}]
469
469
  end
470
-
470
+
471
471
  specify "#import should work correctly when returning primary keys with :slice option for server_version < 80200" do
472
472
  @ds.meta_def(:server_version){80199}
473
473
  @ds.import([:x, :y], [[1, 2], [3, 4]], :return=>:primary_key, :slice=>1).should == [1, 3]
474
474
  @ds.all.should == [{:x=>1, :y=>2}, {:x=>3, :y=>4}]
475
475
  end
476
-
476
+
477
477
  specify "#import should work correctly when returning primary keys with :slice option for server_version >= 80200" do
478
478
  @ds.meta_def(:server_version){80200}
479
479
  @ds.import([:x, :y], [[1, 2], [3, 4]], :return=>:primary_key, :slice=>1).should == [1, 3]
480
480
  @ds.all.should == [{:x=>1, :y=>2}, {:x=>3, :y=>4}]
481
481
  end
482
-
482
+
483
483
  specify "#import should work correctly with an arbitrary returning value" do
484
484
  @ds.meta_def(:server_version){80200}
485
485
  @ds.returning(:y, :x).import([:x, :y], [[1, 2], [3, 4]]).should == [{:y=>2, :x=>1}, {:y=>4, :x=>3}]
@@ -509,7 +509,7 @@ describe "Postgres::Dataset#insert" do
509
509
  @ds.disable_insert_returning.insert(:value=>20).should == 2
510
510
  @ds.meta_def(:server_version){80100}
511
511
  @ds.insert(:value=>13).should == 3
512
-
512
+
513
513
  @db.sqls.reject{|x| x =~ /pg_class/}.should == [
514
514
  'INSERT INTO "test5" ("value") VALUES (10) RETURNING "xid"',
515
515
  'INSERT INTO "test5" ("value") VALUES (20)',
@@ -519,7 +519,7 @@ describe "Postgres::Dataset#insert" do
519
519
  ]
520
520
  @ds.all.should == [{:xid=>1, :value=>10}, {:xid=>2, :value=>20}, {:xid=>3, :value=>13}]
521
521
  end
522
-
522
+
523
523
  specify "should insert correctly if server_version < 80200" do
524
524
  @ds.meta_def(:server_version){80100}
525
525
  @ds.insert(:value=>10).should == 1
@@ -586,7 +586,7 @@ describe "Postgres::Database schema qualified tables" do
586
586
  POSTGRES_DB << "DROP SCHEMA schema_test CASCADE"
587
587
  POSTGRES_DB.default_schema = nil
588
588
  end
589
-
589
+
590
590
  specify "should be able to create, drop, select and insert into tables in a given schema" do
591
591
  POSTGRES_DB.create_table(:schema_test__schema_test){primary_key :i}
592
592
  POSTGRES_DB[:schema_test__schema_test].first.should == nil
@@ -599,19 +599,19 @@ describe "Postgres::Database schema qualified tables" do
599
599
  POSTGRES_DB.from('schema_test.schema_test'.lit).first.should == nil
600
600
  POSTGRES_DB.drop_table(:schema_test.qualify(:schema_test))
601
601
  end
602
-
602
+
603
603
  specify "#tables should not include tables in a default non-public schema" do
604
604
  POSTGRES_DB.create_table(:schema_test__schema_test){integer :i}
605
605
  POSTGRES_DB.tables.should include(:schema_test)
606
606
  POSTGRES_DB.tables.should_not include(:pg_am)
607
607
  POSTGRES_DB.tables.should_not include(:domain_udt_usage)
608
608
  end
609
-
609
+
610
610
  specify "#tables should return tables in the schema provided by the :schema argument" do
611
611
  POSTGRES_DB.create_table(:schema_test__schema_test){integer :i}
612
612
  POSTGRES_DB.tables(:schema=>:schema_test).should == [:schema_test]
613
613
  end
614
-
614
+
615
615
  specify "#schema should not include columns from tables in a default non-public schema" do
616
616
  POSTGRES_DB.create_table(:schema_test__domains){integer :i}
617
617
  sch = POSTGRES_DB.schema(:domains)
@@ -619,7 +619,7 @@ describe "Postgres::Database schema qualified tables" do
619
619
  cs.should include(:i)
620
620
  cs.should_not include(:data_type)
621
621
  end
622
-
622
+
623
623
  specify "#schema should only include columns from the table in the given :schema argument" do
624
624
  POSTGRES_DB.create_table!(:domains){integer :d}
625
625
  POSTGRES_DB.create_table(:schema_test__domains){integer :i}
@@ -629,7 +629,7 @@ describe "Postgres::Database schema qualified tables" do
629
629
  cs.should_not include(:d)
630
630
  POSTGRES_DB.drop_table(:domains)
631
631
  end
632
-
632
+
633
633
  specify "#schema should raise an exception if columns from tables in two separate schema are returned" do
634
634
  POSTGRES_DB.create_table!(:public__domains){integer :d}
635
635
  POSTGRES_DB.create_table(:schema_test__domains){integer :i}
@@ -641,41 +641,41 @@ describe "Postgres::Database schema qualified tables" do
641
641
  POSTGRES_DB.drop_table(:public__domains)
642
642
  end
643
643
  end
644
-
644
+
645
645
  specify "#table_exists? should see if the table is in a given schema" do
646
646
  POSTGRES_DB.create_table(:schema_test__schema_test){integer :i}
647
647
  POSTGRES_DB.table_exists?(:schema_test__schema_test).should == true
648
648
  end
649
-
649
+
650
650
  specify "should be able to get primary keys for tables in a given schema" do
651
651
  POSTGRES_DB.create_table(:schema_test__schema_test){primary_key :i}
652
652
  POSTGRES_DB.primary_key(:schema_test__schema_test).should == 'i'
653
653
  end
654
-
654
+
655
655
  specify "should be able to get serial sequences for tables in a given schema" do
656
656
  POSTGRES_DB.create_table(:schema_test__schema_test){primary_key :i}
657
657
  POSTGRES_DB.primary_key_sequence(:schema_test__schema_test).should == '"schema_test".schema_test_i_seq'
658
658
  end
659
-
659
+
660
660
  specify "should be able to get serial sequences for tables that have spaces in the name in a given schema" do
661
661
  POSTGRES_DB.quote_identifiers = true
662
662
  POSTGRES_DB.create_table(:"schema_test__schema test"){primary_key :i}
663
663
  POSTGRES_DB.primary_key_sequence(:"schema_test__schema test").should == '"schema_test"."schema test_i_seq"'
664
664
  end
665
-
665
+
666
666
  specify "should be able to get custom sequences for tables in a given schema" do
667
667
  POSTGRES_DB << "CREATE SEQUENCE schema_test.kseq"
668
668
  POSTGRES_DB.create_table(:schema_test__schema_test){integer :j; primary_key :k, :type=>:integer, :default=>"nextval('schema_test.kseq'::regclass)".lit}
669
669
  POSTGRES_DB.primary_key_sequence(:schema_test__schema_test).should == '"schema_test".kseq'
670
670
  end
671
-
671
+
672
672
  specify "should be able to get custom sequences for tables that have spaces in the name in a given schema" do
673
673
  POSTGRES_DB.quote_identifiers = true
674
674
  POSTGRES_DB << "CREATE SEQUENCE schema_test.\"ks eq\""
675
675
  POSTGRES_DB.create_table(:"schema_test__schema test"){integer :j; primary_key :k, :type=>:integer, :default=>"nextval('schema_test.\"ks eq\"'::regclass)".lit}
676
676
  POSTGRES_DB.primary_key_sequence(:"schema_test__schema test").should == '"schema_test"."ks eq"'
677
677
  end
678
-
678
+
679
679
  specify "#default_schema= should change the default schema used from public" do
680
680
  POSTGRES_DB.create_table(:schema_test__schema_test){primary_key :i}
681
681
  POSTGRES_DB.default_schema = :schema_test
@@ -692,12 +692,12 @@ describe "Postgres::Database schema qualified tables and eager graphing" do
692
692
  @db.run "DROP SCHEMA s CASCADE" rescue nil
693
693
  @db.run "CREATE SCHEMA s"
694
694
  @db.quote_identifiers = true
695
-
695
+
696
696
  @db.create_table(:s__bands){primary_key :id; String :name}
697
697
  @db.create_table(:s__albums){primary_key :id; String :name; foreign_key :band_id, :s__bands}
698
698
  @db.create_table(:s__tracks){primary_key :id; String :name; foreign_key :album_id, :s__albums}
699
699
  @db.create_table(:s__members){primary_key :id; String :name; foreign_key :band_id, :s__bands}
700
-
700
+
701
701
  @Band = Class.new(Sequel::Model(:s__bands))
702
702
  @Album = Class.new(Sequel::Model(:s__albums))
703
703
  @Track = Class.new(Sequel::Model(:s__tracks))
@@ -706,17 +706,17 @@ describe "Postgres::Database schema qualified tables and eager graphing" do
706
706
  def @Album.name; :Album; end
707
707
  def @Track.name; :Track; end
708
708
  def @Member.name; :Member; end
709
-
709
+
710
710
  @Band.one_to_many :albums, :class=>@Album, :order=>:name
711
711
  @Band.one_to_many :members, :class=>@Member, :order=>:name
712
712
  @Album.many_to_one :band, :class=>@Band, :order=>:name
713
713
  @Album.one_to_many :tracks, :class=>@Track, :order=>:name
714
714
  @Track.many_to_one :album, :class=>@Album, :order=>:name
715
715
  @Member.many_to_one :band, :class=>@Band, :order=>:name
716
-
716
+
717
717
  @Member.many_to_many :members, :class=>@Member, :join_table=>:s__bands, :right_key=>:id, :left_key=>:id, :left_primary_key=>:band_id, :right_primary_key=>:band_id, :order=>:name
718
718
  @Band.many_to_many :tracks, :class=>@Track, :join_table=>:s__albums, :right_key=>:id, :right_primary_key=>:album_id, :order=>:name
719
-
719
+
720
720
  @b1 = @Band.create(:name=>"BM")
721
721
  @b2 = @Band.create(:name=>"J")
722
722
  @a1 = @Album.create(:name=>"BM1", :band=>@b1)
@@ -736,7 +736,7 @@ describe "Postgres::Database schema qualified tables and eager graphing" do
736
736
  @db.quote_identifiers = false
737
737
  @db.run "DROP SCHEMA s CASCADE"
738
738
  end
739
-
739
+
740
740
  specify "should return all eager graphs correctly" do
741
741
  bands = @Band.order(:bands__name).eager_graph(:albums).all
742
742
  bands.should == [@b1, @b2]
@@ -918,7 +918,7 @@ if POSTGRES_DB.dataset.supports_window_functions?
918
918
  after do
919
919
  @db.drop_table(:i1)
920
920
  end
921
-
921
+
922
922
  specify "should give correct results for window functions" do
923
923
  @ds.window(:win, :partition=>:group_id, :order=>:id).select(:id){sum(:over, :args=>amount, :window=>win){}}.all.should ==
924
924
  [{:sum=>1, :id=>1}, {:sum=>11, :id=>2}, {:sum=>111, :id=>3}, {:sum=>1000, :id=>4}, {:sum=>11000, :id=>5}, {:sum=>111000, :id=>6}]
@@ -932,7 +932,7 @@ if POSTGRES_DB.dataset.supports_window_functions?
932
932
  end
933
933
  end
934
934
 
935
- describe "Postgres::Database functions, languages, and triggers" do
935
+ describe "Postgres::Database functions, languages, schemas, and triggers" do
936
936
  before do
937
937
  @d = POSTGRES_DB
938
938
  end
@@ -940,6 +940,7 @@ describe "Postgres::Database functions, languages, and triggers" do
940
940
  @d.drop_function('tf', :if_exists=>true, :cascade=>true)
941
941
  @d.drop_function('tf', :if_exists=>true, :cascade=>true, :args=>%w'integer integer')
942
942
  @d.drop_language(:plpgsql, :if_exists=>true, :cascade=>true) if @d.server_version < 90000
943
+ @d.drop_schema(:sequel, :if_exists=>true, :cascade=>true)
943
944
  @d.drop_table(:test) rescue nil
944
945
  end
945
946
 
@@ -953,7 +954,7 @@ describe "Postgres::Database functions, languages, and triggers" do
953
954
  @d.drop_function('tf')
954
955
  proc{@d['SELECT tf()'].all}.should raise_error(Sequel::DatabaseError)
955
956
  end
956
-
957
+
957
958
  specify "#create_function and #drop_function should support options" do
958
959
  args = ['tf', 'SELECT $1 + $2', {:args=>[[:integer, :a], :integer], :replace=>true, :returns=>:integer, :language=>'SQL', :behavior=>:immutable, :strict=>true, :security_definer=>true, :cost=>2, :set=>{:search_path => 'public'}}]
959
960
  @d.send(:create_function_sql,*args).should =~ /\A\s*CREATE OR REPLACE FUNCTION tf\(a integer, integer\)\s+RETURNS integer\s+LANGUAGE SQL\s+IMMUTABLE\s+STRICT\s+SECURITY DEFINER\s+COST 2\s+SET search_path = public\s+AS 'SELECT \$1 \+ \$2'\s*\z/
@@ -967,7 +968,7 @@ describe "Postgres::Database functions, languages, and triggers" do
967
968
  # Make sure if exists works
968
969
  @d.drop_function(*args)
969
970
  end
970
-
971
+
971
972
  specify "#create_language and #drop_language should create and drop languages" do
972
973
  @d.send(:create_language_sql, :plpgsql).should == 'CREATE LANGUAGE plpgsql'
973
974
  @d.create_language(:plpgsql, :replace=>true) if @d.server_version < 90000
@@ -980,7 +981,20 @@ describe "Postgres::Database functions, languages, and triggers" do
980
981
  # Make sure if exists works
981
982
  @d.drop_language(:plpgsql, :if_exists=>true, :cascade=>true) if @d.server_version < 90000
982
983
  end
983
-
984
+
985
+ specify "#create_schema and #drop_schema should create and drop schemas" do
986
+ @d.send(:create_schema_sql, :sequel).should == 'CREATE SCHEMA sequel'
987
+ @d.send(:drop_schema_sql, :sequel).should == 'DROP SCHEMA sequel'
988
+ @d.send(:drop_schema_sql, :sequel, :if_exists=>true, :cascade=>true).should == 'DROP SCHEMA IF EXISTS sequel CASCADE'
989
+ @d.create_schema(:sequel)
990
+ @d.create_table(:sequel__test){Integer :a}
991
+ if @d.server_version >= 80200
992
+ @d.drop_schema(:sequel, :if_exists=>true, :cascade=>true)
993
+ else
994
+ @d.drop_schema(:sequel, :cascade=>true)
995
+ end
996
+ end
997
+
984
998
  specify "#create_trigger and #drop_trigger should create and drop triggers" do
985
999
  @d.create_language(:plpgsql) if @d.server_version < 90000
986
1000
  @d.create_function(:tf, 'BEGIN IF NEW.value IS NULL THEN RAISE EXCEPTION \'Blah\'; END IF; RETURN NEW; END;', :language=>:plpgsql, :returns=>:trigger)
@@ -1014,11 +1028,11 @@ if POSTGRES_DB.adapter_scheme == :postgres
1014
1028
  after(:all) do
1015
1029
  @db.drop_table(:test_cursor) rescue nil
1016
1030
  end
1017
-
1031
+
1018
1032
  specify "should return the same results as the non-cursor use" do
1019
1033
  @ds.all.should == @ds.use_cursor.all
1020
1034
  end
1021
-
1035
+
1022
1036
  specify "should respect the :rows_per_fetch option" do
1023
1037
  @db.sqls.clear
1024
1038
  @ds.use_cursor.all
@@ -1027,7 +1041,7 @@ if POSTGRES_DB.adapter_scheme == :postgres
1027
1041
  @ds.use_cursor(:rows_per_fetch=>100).all
1028
1042
  @db.sqls.length.should == 15
1029
1043
  end
1030
-
1044
+
1031
1045
  specify "should handle returning inside block" do
1032
1046
  def @ds.check_return
1033
1047
  use_cursor.each{|r| return}
@@ -1044,7 +1058,7 @@ if POSTGRES_DB.adapter_scheme == :postgres
1044
1058
  @db.instance_eval do
1045
1059
  disconnect
1046
1060
  @conversion_procs = nil
1047
- end
1061
+ end
1048
1062
  end
1049
1063
  after do
1050
1064
  Sequel::Postgres::PG_NAMED_TYPES.delete(:interval)
@@ -1075,7 +1089,7 @@ if POSTGRES_DB.adapter_scheme == :postgres && SEQUEL_POSTGRES_USES_PG && POSTGRE
1075
1089
  after(:all) do
1076
1090
  @db.drop_table(:test_copy) rescue nil
1077
1091
  end
1078
-
1092
+
1079
1093
  specify "without a block or options should return a text version of the table as a single string" do
1080
1094
  @db.copy_table(:test_copy).should == "1\t2\n3\t4\n"
1081
1095
  end
@@ -1083,35 +1097,35 @@ if POSTGRES_DB.adapter_scheme == :postgres && SEQUEL_POSTGRES_USES_PG && POSTGRE
1083
1097
  specify "without a block and with :format=>:csv should return a csv version of the table as a single string" do
1084
1098
  @db.copy_table(:test_copy, :format=>:csv).should == "1,2\n3,4\n"
1085
1099
  end
1086
-
1100
+
1087
1101
  specify "should treat string as SQL code" do
1088
1102
  @db.copy_table('COPY "test_copy" TO STDOUT').should == "1\t2\n3\t4\n"
1089
1103
  end
1090
-
1104
+
1091
1105
  specify "should respect given :options options" do
1092
1106
  @db.copy_table(:test_copy, :options=>"FORMAT csv, HEADER TRUE").should == "x,y\n1,2\n3,4\n"
1093
1107
  end
1094
-
1108
+
1095
1109
  specify "should respect given :options options when :format is used" do
1096
1110
  @db.copy_table(:test_copy, :format=>:csv, :options=>"QUOTE '''', FORCE_QUOTE *").should == "'1','2'\n'3','4'\n"
1097
1111
  end
1098
-
1112
+
1099
1113
  specify "should accept dataset as first argument" do
1100
1114
  @db.copy_table(@db[:test_copy].cross_join(:test_copy___tc).order(1, 2, 3, 4)).should == "1\t2\t1\t2\n1\t2\t3\t4\n3\t4\t1\t2\n3\t4\t3\t4\n"
1101
1115
  end
1102
-
1116
+
1103
1117
  specify "with a block and no options should yield each row as a string in text format" do
1104
1118
  buf = []
1105
1119
  @db.copy_table(:test_copy){|b| buf << b}
1106
1120
  buf.should == ["1\t2\n", "3\t4\n"]
1107
1121
  end
1108
-
1122
+
1109
1123
  specify "with a block and :format=>:csv should yield each row as a string in csv format" do
1110
1124
  buf = []
1111
1125
  @db.copy_table(:test_copy, :format=>:csv){|b| buf << b}
1112
1126
  buf.should == ["1,2\n", "3,4\n"]
1113
1127
  end
1114
-
1128
+
1115
1129
  specify "should work fine when using a block that is terminated early with a following copy_table" do
1116
1130
  buf = []
1117
1131
  proc{@db.copy_table(:test_copy, :format=>:csv){|b| buf << b; break}}.should raise_error(Sequel::DatabaseDisconnectError)
@@ -1123,7 +1137,7 @@ if POSTGRES_DB.adapter_scheme == :postgres && SEQUEL_POSTGRES_USES_PG && POSTGRE
1123
1137
  @db.copy_table(:test_copy){|b| buf << b}
1124
1138
  buf.should == ["1\t2\n", "3\t4\n"]
1125
1139
  end
1126
-
1140
+
1127
1141
  specify "should work fine when using a block that is terminated early with a following regular query" do
1128
1142
  buf = []
1129
1143
  proc{@db.copy_table(:test_copy, :format=>:csv){|b| buf << b; break}}.should raise_error(Sequel::DatabaseDisconnectError)
@@ -1133,13 +1147,13 @@ if POSTGRES_DB.adapter_scheme == :postgres && SEQUEL_POSTGRES_USES_PG && POSTGRE
1133
1147
  buf.should == ["1,2\n"]
1134
1148
  @db[:test_copy].select_order_map(:x).should == [1, 3]
1135
1149
  end
1136
- end
1150
+ end
1137
1151
 
1138
1152
  describe "Postgres::Database LISTEN/NOTIFY" do
1139
1153
  before(:all) do
1140
1154
  @db = POSTGRES_DB
1141
1155
  end
1142
-
1156
+
1143
1157
  specify "should support listen and notify" do
1144
1158
  notify_pid = @db.synchronize{|conn| conn.backend_pid}
1145
1159
 
@@ -1196,3 +1210,50 @@ if POSTGRES_DB.adapter_scheme == :postgres && SEQUEL_POSTGRES_USES_PG && POSTGRE
1196
1210
  end
1197
1211
  end
1198
1212
  end
1213
+
1214
+ describe 'POSTGRES special float handling' do
1215
+ before do
1216
+ @db = POSTGRES_DB
1217
+ @db.create_table!(:test5){Float :value}
1218
+ @db.sqls.clear
1219
+ @ds = @db[:test5]
1220
+ end
1221
+ after do
1222
+ @db.drop_table(:test5) rescue nil
1223
+ end
1224
+
1225
+ specify 'should quote NaN' do
1226
+ nan = 0.0/0.0
1227
+ @ds.insert_sql(:value => nan).should == %q{INSERT INTO test5 (value) VALUES ('NaN')}
1228
+ end
1229
+
1230
+ specify 'should quote +Infinity' do
1231
+ inf = 1.0/0.0
1232
+ @ds.insert_sql(:value => inf).should == %q{INSERT INTO test5 (value) VALUES ('Infinity')}
1233
+ end
1234
+
1235
+ specify 'should quote -Infinity' do
1236
+ inf = -1.0/0.0
1237
+ @ds.insert_sql(:value => inf).should == %q{INSERT INTO test5 (value) VALUES ('-Infinity')}
1238
+ end
1239
+
1240
+ if POSTGRES_DB.adapter_scheme == :postgres
1241
+ specify 'inserts NaN' do
1242
+ nan = 0.0/0.0
1243
+ @ds.insert(:value=>nan)
1244
+ @ds.all[0][:value].nan?.should be_true
1245
+ end
1246
+
1247
+ specify 'inserts +Infinity' do
1248
+ inf = 1.0/0.0
1249
+ @ds.insert(:value=>inf)
1250
+ @ds.all[0][:value].infinite?.should > 0
1251
+ end
1252
+
1253
+ specify 'inserts -Infinity' do
1254
+ inf = -1.0/0.0
1255
+ @ds.insert(:value=>inf)
1256
+ @ds.all[0][:value].infinite?.should < 0
1257
+ end
1258
+ end
1259
+ end