sequel 3.32.0 → 3.33.0

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