sequel 3.32.0 → 3.33.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +42 -0
- data/Rakefile +3 -2
- data/doc/migration.rdoc +41 -14
- data/doc/opening_databases.rdoc +2 -0
- data/doc/release_notes/3.29.0.txt +1 -1
- data/doc/release_notes/3.33.0.txt +157 -0
- data/doc/sharding.rdoc +93 -7
- data/doc/testing.rdoc +1 -1
- data/lib/sequel/adapters/do.rb +1 -0
- data/lib/sequel/adapters/do/mysql.rb +6 -0
- data/lib/sequel/adapters/jdbc.rb +1 -0
- data/lib/sequel/adapters/jdbc/mysql.rb +0 -5
- data/lib/sequel/adapters/mock.rb +10 -5
- data/lib/sequel/adapters/mysql.rb +23 -1
- data/lib/sequel/adapters/mysql2.rb +16 -2
- data/lib/sequel/adapters/postgres.rb +22 -4
- data/lib/sequel/adapters/shared/mysql.rb +51 -10
- data/lib/sequel/adapters/shared/postgres.rb +101 -63
- data/lib/sequel/adapters/shared/sqlite.rb +19 -0
- data/lib/sequel/adapters/sqlite.rb +71 -16
- data/lib/sequel/adapters/swift.rb +1 -0
- data/lib/sequel/connection_pool.rb +1 -1
- data/lib/sequel/connection_pool/sharded_single.rb +6 -1
- data/lib/sequel/connection_pool/sharded_threaded.rb +6 -1
- data/lib/sequel/connection_pool/threaded.rb +12 -11
- data/lib/sequel/database/connecting.rb +2 -0
- data/lib/sequel/database/misc.rb +6 -0
- data/lib/sequel/database/query.rb +1 -1
- data/lib/sequel/extensions/arbitrary_servers.rb +108 -0
- data/lib/sequel/extensions/migration.rb +45 -7
- data/lib/sequel/extensions/server_block.rb +139 -0
- data/lib/sequel/model/associations.rb +9 -9
- data/lib/sequel/model/inflections.rb +1 -1
- data/lib/sequel/plugins/instance_hooks.rb +1 -1
- data/lib/sequel/plugins/json_serializer.rb +1 -1
- data/lib/sequel/plugins/list.rb +12 -2
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/mysql_spec.rb +64 -8
- data/spec/adapters/postgres_spec.rb +139 -78
- data/spec/adapters/sqlite_spec.rb +87 -0
- data/spec/core/connection_pool_spec.rb +14 -0
- data/spec/core/database_spec.rb +5 -0
- data/spec/core/mock_adapter_spec.rb +21 -9
- data/spec/extensions/arbitrary_servers_spec.rb +114 -0
- data/spec/extensions/instance_hooks_spec.rb +19 -0
- data/spec/extensions/list_spec.rb +31 -0
- data/spec/extensions/migration_spec.rb +61 -4
- data/spec/extensions/server_block_spec.rb +90 -0
- data/spec/extensions/spec_helper.rb +1 -1
- data/spec/files/transaction_migrations/001_create_alt_basic.rb +3 -0
- data/spec/files/transaction_migrations/002_create_basic.rb +3 -0
- data/spec/files/transactionless_migrations/001_create_alt_basic.rb +4 -0
- data/spec/files/transactionless_migrations/002_create_basic.rb +4 -0
- data/spec/integration/dataset_test.rb +2 -2
- data/spec/integration/plugin_test.rb +9 -9
- data/spec/integration/schema_test.rb +3 -1
- data/spec/integration/transaction_test.rb +2 -2
- 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
|
-
#
|
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
|
-
#
|
813
|
-
#
|
814
|
-
#
|
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
|
-
#
|
843
|
-
#
|
844
|
-
#
|
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
|
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
|
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
|
data/lib/sequel/plugins/list.rb
CHANGED
@@ -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
|
-
#
|
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
|
data/lib/sequel/version.rb
CHANGED
@@ -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 =
|
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
|
data/spec/adapters/mysql_spec.rb
CHANGED
@@ -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){
|
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)",
|
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
|
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
|