sequel 3.32.0 → 3.33.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|