sequel 3.30.0 → 3.31.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 +40 -0
- data/Rakefile +12 -2
- data/doc/association_basics.rdoc +28 -0
- data/doc/dataset_filtering.rdoc +8 -0
- data/doc/opening_databases.rdoc +1 -0
- data/doc/release_notes/3.31.0.txt +146 -0
- data/lib/sequel/adapters/jdbc.rb +7 -6
- data/lib/sequel/adapters/jdbc/derby.rb +5 -0
- data/lib/sequel/adapters/jdbc/h2.rb +6 -1
- data/lib/sequel/adapters/mock.rb +21 -2
- data/lib/sequel/adapters/shared/db2.rb +10 -0
- data/lib/sequel/adapters/shared/mssql.rb +40 -5
- data/lib/sequel/adapters/shared/mysql.rb +19 -2
- data/lib/sequel/adapters/shared/oracle.rb +13 -1
- data/lib/sequel/adapters/shared/postgres.rb +52 -8
- data/lib/sequel/adapters/shared/sqlite.rb +4 -3
- data/lib/sequel/adapters/utils/stored_procedures.rb +1 -11
- data/lib/sequel/database/schema_generator.rb +9 -2
- data/lib/sequel/dataset/actions.rb +37 -19
- data/lib/sequel/dataset/features.rb +10 -0
- data/lib/sequel/dataset/prepared_statements.rb +0 -10
- data/lib/sequel/dataset/query.rb +13 -1
- data/lib/sequel/dataset/sql.rb +6 -1
- data/lib/sequel/model/associations.rb +14 -4
- data/lib/sequel/model/base.rb +10 -0
- data/lib/sequel/plugins/serialization.rb +82 -43
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/mssql_spec.rb +46 -0
- data/spec/adapters/mysql_spec.rb +3 -0
- data/spec/adapters/postgres_spec.rb +61 -24
- data/spec/core/database_spec.rb +31 -18
- data/spec/core/dataset_spec.rb +90 -13
- data/spec/core/mock_adapter_spec.rb +37 -0
- data/spec/extensions/instance_filters_spec.rb +1 -0
- data/spec/extensions/nested_attributes_spec.rb +1 -1
- data/spec/extensions/serialization_spec.rb +49 -5
- data/spec/extensions/sharding_spec.rb +1 -1
- data/spec/integration/associations_test.rb +15 -0
- data/spec/integration/dataset_test.rb +71 -0
- data/spec/integration/prepared_statement_test.rb +8 -0
- data/spec/model/association_reflection_spec.rb +27 -0
- data/spec/model/associations_spec.rb +18 -3
- data/spec/model/base_spec.rb +20 -0
- data/spec/model/eager_loading_spec.rb +21 -0
- metadata +4 -2
data/lib/sequel/dataset/sql.rb
CHANGED
@@ -20,7 +20,7 @@ module Sequel
|
|
20
20
|
# DB.select(1).where(DB[:items].exists)
|
21
21
|
# # SELECT 1 WHERE (EXISTS (SELECT * FROM items))
|
22
22
|
def exists
|
23
|
-
|
23
|
+
SQL::PlaceholderLiteralString.new("EXISTS ?", [self], true)
|
24
24
|
end
|
25
25
|
|
26
26
|
# Returns an INSERT SQL query string. See +insert+.
|
@@ -1196,7 +1196,12 @@ module Sequel
|
|
1196
1196
|
def select_group_sql(sql)
|
1197
1197
|
if group = @opts[:group]
|
1198
1198
|
sql << GROUP_BY
|
1199
|
+
if go = @opts[:group_options]
|
1200
|
+
sql << go.to_s.upcase
|
1201
|
+
sql << PAREN_OPEN
|
1202
|
+
end
|
1199
1203
|
expression_list_append(sql, group)
|
1204
|
+
sql << PAREN_CLOSE if go
|
1200
1205
|
end
|
1201
1206
|
end
|
1202
1207
|
|
@@ -761,6 +761,10 @@ module Sequel
|
|
761
761
|
# :key :: foreign key in current model's table that references
|
762
762
|
# associated model's primary key, as a symbol. Defaults to :"#{name}_id". Can use an
|
763
763
|
# array of symbols for a composite key association.
|
764
|
+
# :key_column :: Similar to, and usually identical to, :key, but :key refers to the model method
|
765
|
+
# to call, where :key_column refers to the underlying column. Should only be
|
766
|
+
# used if the association has the same name as the foreign key column, in conjunction
|
767
|
+
# with defining a model alias method for the key column.
|
764
768
|
# :primary_key :: column in the associated table that :key option references, as a symbol.
|
765
769
|
# Defaults to the primary key of the associated table. Can use an
|
766
770
|
# array of symbols for a composite key association.
|
@@ -1121,8 +1125,13 @@ module Sequel
|
|
1121
1125
|
name = opts[:name]
|
1122
1126
|
model = self
|
1123
1127
|
opts[:key] = opts.default_key unless opts.include?(:key)
|
1124
|
-
key = opts[:key]
|
1125
|
-
cks = opts[:keys] = Array(
|
1128
|
+
key_column = key = opts[:key]
|
1129
|
+
cks = opts[:graph_keys] = opts[:keys] = Array(key)
|
1130
|
+
if opts[:key_column]
|
1131
|
+
key_column = opts[:key_column]
|
1132
|
+
opts[:eager_loader_key] ||= key_column
|
1133
|
+
opts[:graph_keys] = Array(key_column)
|
1134
|
+
end
|
1126
1135
|
opts[:qualified_key] = opts.qualify_cur(key)
|
1127
1136
|
if opts[:primary_key]
|
1128
1137
|
cpks = Array(opts[:primary_key])
|
@@ -1136,7 +1145,7 @@ module Sequel
|
|
1136
1145
|
klass.filter(Array(opts.qualified_primary_key).zip(cks.map{|k| send(k)}))
|
1137
1146
|
end
|
1138
1147
|
opts[:eager_loader] ||= proc do |eo|
|
1139
|
-
h = eo[:key_hash][
|
1148
|
+
h = eo[:key_hash][key_column]
|
1140
1149
|
keys = h.keys
|
1141
1150
|
# Default the cached association to nil, so any object that doesn't have it
|
1142
1151
|
# populated will have cached the negative lookup.
|
@@ -1158,9 +1167,10 @@ module Sequel
|
|
1158
1167
|
only_conditions = opts[:graph_only_conditions]
|
1159
1168
|
conditions = opts[:graph_conditions]
|
1160
1169
|
graph_block = opts[:graph_block]
|
1170
|
+
graph_cks = opts[:graph_keys]
|
1161
1171
|
opts[:eager_grapher] ||= proc do |eo|
|
1162
1172
|
ds = eo[:self]
|
1163
|
-
ds.graph(eager_graph_dataset(opts, eo), use_only_conditions ? only_conditions : opts.primary_keys.zip(
|
1173
|
+
ds.graph(eager_graph_dataset(opts, eo), use_only_conditions ? only_conditions : opts.primary_keys.zip(graph_cks) + conditions, eo.merge(:select=>select, :join_type=>join_type, :from_self_alias=>ds.opts[:eager_graph][:master]), &graph_block)
|
1164
1174
|
end
|
1165
1175
|
|
1166
1176
|
def_association_dataset_methods(opts)
|
data/lib/sequel/model/base.rb
CHANGED
@@ -234,6 +234,16 @@ module Sequel
|
|
234
234
|
@db_schema ||= get_db_schema
|
235
235
|
end
|
236
236
|
|
237
|
+
# Create a column alias, where the column methods have one name, but the underlying storage uses a
|
238
|
+
# different name.
|
239
|
+
def def_column_alias(meth, column)
|
240
|
+
clear_setter_methods_cache
|
241
|
+
overridable_methods_module.module_eval do
|
242
|
+
define_method(meth){self[column]}
|
243
|
+
define_method("#{meth}="){|v| self[column] = v}
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
237
247
|
# If a block is given, define a method on the dataset (if the model currently has an dataset) with the given argument name using
|
238
248
|
# the given block. Also define a class method on the model that calls the
|
239
249
|
# dataset method. Stores the method name and block so that it can be reapplied if the model's
|
@@ -11,8 +11,17 @@ module Sequel
|
|
11
11
|
# set the @deserialized_values entry. This plugin adds a before_save hook
|
12
12
|
# that serializes all @deserialized_values to @values.
|
13
13
|
#
|
14
|
-
# You can
|
15
|
-
#
|
14
|
+
# You can specify the serialization format as a pair of serializer/deserializer
|
15
|
+
# callable objects. You can also specify the serialization format as a single
|
16
|
+
# symbol, if such a symbol has a registered serializer/deserializer pair in the
|
17
|
+
# plugin. By default, the plugin registers the :marshal, :yaml, and :json
|
18
|
+
# serialization formats. To register your own serialization formats, use
|
19
|
+
# Sequel::Plugins::Serialization.register_format.
|
20
|
+
# If you use yaml or json format, you need to require the libraries, Sequel
|
21
|
+
# does not do the requiring for you.
|
22
|
+
#
|
23
|
+
# You can specify the columns to serialize when loading the plugin, or later
|
24
|
+
# using the serialize_attributes class method.
|
16
25
|
#
|
17
26
|
# Because of how this plugin works, it must be used inside each model class
|
18
27
|
# that needs serialization, after any set_dataset method calls in that class.
|
@@ -22,56 +31,96 @@ module Sequel
|
|
22
31
|
# == Example
|
23
32
|
#
|
24
33
|
# require 'sequel'
|
34
|
+
# # Require json, as the plugin doesn't require it for you.
|
25
35
|
# require 'json'
|
36
|
+
#
|
37
|
+
# # Register custom serializer/deserializer pair
|
38
|
+
# Sequel::Plugins::Serialization.register_format(:reverse,
|
39
|
+
# lambda{|v| v.reverse},
|
40
|
+
# lambda{|v| v.reverse})
|
41
|
+
#
|
26
42
|
# class User < Sequel::Model
|
43
|
+
# # Built-in format support when loading the plugin
|
27
44
|
# plugin :serialization, :json, :permissions
|
28
|
-
#
|
45
|
+
#
|
46
|
+
# # Built-in format support after loading the plugin using serialize_attributes
|
29
47
|
# plugin :serialization
|
30
|
-
# serialize_attributes :marshal, :permissions
|
48
|
+
# serialize_attributes :marshal, :permissions
|
49
|
+
#
|
50
|
+
# # Use custom registered serialization format just like built-in format
|
51
|
+
# serialize_attributes :reverse, :password
|
52
|
+
#
|
53
|
+
# # Use a custom serializer/deserializer pair without registering
|
54
|
+
# serialize_attributes [lambda{|v| v.reverse}, lambda{|v| v.reverse}], :password
|
31
55
|
# end
|
32
56
|
# user = User.create
|
33
57
|
# user.permissions = { :global => 'read-only' }
|
34
58
|
# user.save
|
35
59
|
module Serialization
|
60
|
+
# The default serializers supported by the serialization module.
|
61
|
+
# Use register_format to add serializers to this hash.
|
62
|
+
REGISTERED_FORMATS = {}
|
63
|
+
|
36
64
|
# Set up the column readers to do deserialization and the column writers
|
37
65
|
# to save the value in deserialized_values.
|
38
66
|
def self.apply(model, *args)
|
39
|
-
model.instance_eval
|
67
|
+
model.instance_eval do
|
68
|
+
@deserialization_map = {}
|
69
|
+
@serialization_map = {}
|
70
|
+
end
|
40
71
|
end
|
41
72
|
|
73
|
+
# Automatically call serialize_attributes with the format and columns unless
|
74
|
+
# no columns were provided.
|
42
75
|
def self.configure(model, format=nil, *columns)
|
43
76
|
model.serialize_attributes(format, *columns) unless columns.empty?
|
44
77
|
end
|
45
78
|
|
79
|
+
# Register a serializer/deserializer pair with a format symbol, to allow
|
80
|
+
# models to pick this format by name. Both serializer and deserializer
|
81
|
+
# should be callable objects.
|
82
|
+
def self.register_format(format, serializer, deserializer)
|
83
|
+
REGISTERED_FORMATS[format] = [serializer, deserializer]
|
84
|
+
end
|
85
|
+
register_format(:marshal, lambda{|v| [Marshal.dump(v)].pack('m')}, lambda{|v| Marshal.load(v.unpack('m')[0]) rescue Marshal.load(v)})
|
86
|
+
register_format(:yaml, lambda{|v| v.to_yaml}, lambda{|v| YAML.load(v)})
|
87
|
+
register_format(:json, lambda{|v| v.to_json}, lambda{|v| JSON.parse(v)})
|
88
|
+
|
46
89
|
module ClassMethods
|
47
|
-
# A
|
48
|
-
#
|
90
|
+
# A hash with column name symbols and callable values, with the value
|
91
|
+
# called to deserialize the column.
|
92
|
+
attr_reader :deserialization_map
|
93
|
+
|
94
|
+
# A hash with column name symbols and callable values, with the value
|
95
|
+
# called to serialize the column.
|
49
96
|
attr_reader :serialization_map
|
50
97
|
|
51
98
|
# Module to store the serialized column accessor methods, so they can
|
52
99
|
# call be overridden and call super to get the serialization behavior
|
53
100
|
attr_accessor :serialization_module
|
54
101
|
|
55
|
-
# Copy the
|
102
|
+
# Copy the serialization_map and deserialization map into the subclass.
|
56
103
|
def inherited(subclass)
|
57
104
|
super
|
58
105
|
sm = serialization_map.dup
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
def serialization_format
|
65
|
-
serialization_map.values.first
|
106
|
+
dsm = deserialization_map.dup
|
107
|
+
subclass.instance_eval do
|
108
|
+
@deserialization_map = dsm
|
109
|
+
@serialization_map = sm
|
110
|
+
end
|
66
111
|
end
|
67
112
|
|
68
113
|
# Create instance level reader that deserializes column values on request,
|
69
|
-
# and instance level writer that stores new deserialized
|
70
|
-
# columns
|
114
|
+
# and instance level writer that stores new deserialized values.
|
71
115
|
def serialize_attributes(format, *columns)
|
72
|
-
|
116
|
+
if format.is_a?(Symbol)
|
117
|
+
unless format = REGISTERED_FORMATS[format]
|
118
|
+
raise(Error, "Unsupported serialization format: #{format} (valid formats: #{REGISTERED_FORMATS.keys.map{|k| k.inspect}.join})")
|
119
|
+
end
|
120
|
+
end
|
121
|
+
serializer, deserializer = format
|
73
122
|
raise(Error, "No columns given. The serialization plugin requires you specify which columns to serialize") if columns.empty?
|
74
|
-
define_serialized_attribute_accessor(
|
123
|
+
define_serialized_attribute_accessor(serializer, deserializer, *columns)
|
75
124
|
end
|
76
125
|
|
77
126
|
# The columns that will be serialized. This is only for
|
@@ -83,12 +132,13 @@ module Sequel
|
|
83
132
|
private
|
84
133
|
|
85
134
|
# Add serializated attribute acessor methods to the serialization_module
|
86
|
-
def define_serialized_attribute_accessor(
|
135
|
+
def define_serialized_attribute_accessor(serializer, deserializer, *columns)
|
87
136
|
m = self
|
88
137
|
include(self.serialization_module ||= Module.new) unless serialization_module
|
89
138
|
serialization_module.class_eval do
|
90
139
|
columns.each do |column|
|
91
|
-
m.serialization_map[column] =
|
140
|
+
m.serialization_map[column] = serializer
|
141
|
+
m.deserialization_map[column] = deserializer
|
92
142
|
define_method(column) do
|
93
143
|
if deserialized_values.has_key?(column)
|
94
144
|
deserialized_values[column]
|
@@ -127,6 +177,7 @@ module Sequel
|
|
127
177
|
super
|
128
178
|
end
|
129
179
|
|
180
|
+
# Initialization the deserialized values for objects retrieved from the database.
|
130
181
|
def set_values(*)
|
131
182
|
@deserialized_values ||= {}
|
132
183
|
super
|
@@ -134,33 +185,21 @@ module Sequel
|
|
134
185
|
|
135
186
|
private
|
136
187
|
|
137
|
-
# Deserialize the column
|
188
|
+
# Deserialize the column value. Called when the model column accessor is called to
|
189
|
+
# return a deserialized value.
|
138
190
|
def deserialize_value(column, v)
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
Marshal.load(v.unpack('m')[0]) rescue Marshal.load(v)
|
143
|
-
when :yaml
|
144
|
-
YAML.load v if v
|
145
|
-
when :json
|
146
|
-
JSON.parse v if v
|
147
|
-
else
|
148
|
-
raise Error, "Bad serialization format (#{model.serialization_map[column].inspect}) for column #{column.inspect}"
|
191
|
+
unless v.nil?
|
192
|
+
raise Sequel::Error, "no entry in deserialization_map for #{column.inspect}" unless callable = model.deserialization_map[column]
|
193
|
+
callable.call(v)
|
149
194
|
end
|
150
195
|
end
|
151
196
|
|
152
|
-
# Serialize the column to
|
197
|
+
# Serialize the column value. Called before saving to ensure the serialized value
|
198
|
+
# is saved in the database.
|
153
199
|
def serialize_value(column, v)
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
[Marshal.dump(v)].pack('m')
|
158
|
-
when :yaml
|
159
|
-
v.to_yaml
|
160
|
-
when :json
|
161
|
-
v.to_json
|
162
|
-
else
|
163
|
-
raise Error, "Bad serialization format (#{model.serialization_map[column].inspect}) for column #{column.inspect}"
|
200
|
+
unless v.nil?
|
201
|
+
raise Sequel::Error, "no entry in serialization_map for #{column.inspect}" unless callable = model.serialization_map[column]
|
202
|
+
callable.call(v)
|
164
203
|
end
|
165
204
|
end
|
166
205
|
end
|
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 = 31
|
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/mssql_spec.rb
CHANGED
@@ -60,6 +60,35 @@ describe "A MSSQL database" do
|
|
60
60
|
end
|
61
61
|
end
|
62
62
|
|
63
|
+
# This spec is currently disabled as the SQL Server 2008 R2 Express doesn't support
|
64
|
+
# full text searching. Even if full text searching is supported,
|
65
|
+
# you may need to create a full text catalog on the database first via:
|
66
|
+
# CREATE FULLTEXT CATALOG ftscd AS DEFAULT
|
67
|
+
describe "MSSQL full_text_search" do
|
68
|
+
before do
|
69
|
+
@db = MSSQL_DB
|
70
|
+
@db.drop_table(:posts) rescue nil
|
71
|
+
end
|
72
|
+
after do
|
73
|
+
@db.drop_table(:posts) rescue nil
|
74
|
+
end
|
75
|
+
|
76
|
+
specify "should support fulltext indexes and full_text_search" do
|
77
|
+
log do
|
78
|
+
@db.create_table(:posts){Integer :id, :null=>false; String :title; String :body; index :id, :name=>:fts_id_idx, :unique=>true; full_text_index :title, :key_index=>:fts_id_idx; full_text_index [:title, :body], :key_index=>:fts_id_idx}
|
79
|
+
@db[:posts].insert(:title=>'ruby rails', :body=>'y')
|
80
|
+
@db[:posts].insert(:title=>'sequel', :body=>'ruby')
|
81
|
+
@db[:posts].insert(:title=>'ruby scooby', :body=>'x')
|
82
|
+
|
83
|
+
@db[:posts].full_text_search(:title, 'rails').all.should == [{:title=>'ruby rails', :body=>'y'}]
|
84
|
+
@db[:posts].full_text_search([:title, :body], ['sequel', 'ruby']).all.should == [{:title=>'sequel', :body=>'ruby'}]
|
85
|
+
|
86
|
+
@db[:posts].full_text_search(:title, :$n).call(:select, :n=>'rails').should == [{:title=>'ruby rails', :body=>'y'}]
|
87
|
+
@db[:posts].full_text_search(:title, :$n).prepare(:select, :fts_select).call(:n=>'rails').should == [{:title=>'ruby rails', :body=>'y'}]
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end if false
|
91
|
+
|
63
92
|
describe "MSSQL Dataset#join_table" do
|
64
93
|
specify "should emulate the USING clause with ON" do
|
65
94
|
MSSQL_DB[:items].join(:categories, [:id]).sql.should ==
|
@@ -219,6 +248,23 @@ describe "MSSQL dataset" do
|
|
219
248
|
end
|
220
249
|
end
|
221
250
|
|
251
|
+
describe "MSSQL::Dataset#import" do
|
252
|
+
before do
|
253
|
+
@db = MSSQL_DB
|
254
|
+
@db.create_table!(:test){primary_key :x; Integer :y}
|
255
|
+
@db.sqls.clear
|
256
|
+
@ds = @db[:test]
|
257
|
+
end
|
258
|
+
after do
|
259
|
+
@db.drop_table(:test) rescue nil
|
260
|
+
end
|
261
|
+
|
262
|
+
specify "#import should work correctly with an arbitrary output value" do
|
263
|
+
@ds.output(nil, [:inserted__y, :inserted__x]).import([:y], [[3], [4]]).should == [{:y=>3, :x=>1}, {:y=>4, :x=>2}]
|
264
|
+
@ds.all.should == [{:x=>1, :y=>3}, {:x=>2, :y=>4}]
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
222
268
|
describe "MSSQL joined datasets" do
|
223
269
|
before do
|
224
270
|
@db = MSSQL_DB
|
data/spec/adapters/mysql_spec.rb
CHANGED
@@ -645,6 +645,9 @@ describe "A MySQL database" do
|
|
645
645
|
"SELECT * FROM `posts` WHERE (MATCH (`title`) AGAINST ('rails'))",
|
646
646
|
"SELECT * FROM `posts` WHERE (MATCH (`title`, `body`) AGAINST ('sequel ruby'))",
|
647
647
|
"SELECT * FROM `posts` WHERE (MATCH (`title`) AGAINST ('+ruby -rails' IN BOOLEAN MODE))"]
|
648
|
+
|
649
|
+
@db[:posts].full_text_search(:title, :$n).call(:select, :n=>'rails').should == [{:title=>'ruby rails', :body=>'y'}]
|
650
|
+
@db[:posts].full_text_search(:title, :$n).prepare(:select, :fts_select).call(:n=>'rails').should == [{:title=>'ruby rails', :body=>'y'}]
|
648
651
|
end
|
649
652
|
|
650
653
|
specify "should support spatial indexes" do
|
@@ -380,6 +380,9 @@ describe "A PostgreSQL database" do
|
|
380
380
|
%{SELECT * FROM "posts" WHERE (to_tsvector('simple', (COALESCE("title", ''))) @@ to_tsquery('simple', 'rails'))},
|
381
381
|
%{SELECT * FROM "posts" WHERE (to_tsvector('simple', (COALESCE("title", '') || ' ' || COALESCE("body", ''))) @@ to_tsquery('simple', 'yowsa | rails'))},
|
382
382
|
%{SELECT * FROM "posts" WHERE (to_tsvector('french', (COALESCE("title", ''))) @@ to_tsquery('french', 'scooby'))}]
|
383
|
+
|
384
|
+
@db[:posts].full_text_search(:title, :$n).call(:select, :n=>'rails').should == [{:title=>'ruby rails', :body=>'yowsa'}]
|
385
|
+
@db[:posts].full_text_search(:title, :$n).prepare(:select, :fts_select).call(:n=>'rails').should == [{:title=>'ruby rails', :body=>'yowsa'}]
|
383
386
|
end
|
384
387
|
|
385
388
|
specify "should support spatial indexes" do
|
@@ -431,7 +434,7 @@ end
|
|
431
434
|
describe "Postgres::Dataset#import" do
|
432
435
|
before do
|
433
436
|
@db = POSTGRES_DB
|
434
|
-
@db.create_table!(:test){
|
437
|
+
@db.create_table!(:test){primary_key :x; Integer :y}
|
435
438
|
@db.sqls.clear
|
436
439
|
@ds = @db[:test]
|
437
440
|
end
|
@@ -441,28 +444,45 @@ describe "Postgres::Dataset#import" do
|
|
441
444
|
|
442
445
|
specify "#import should return separate insert statements if server_version < 80200" do
|
443
446
|
@ds.meta_def(:server_version){80199}
|
444
|
-
|
445
447
|
@ds.import([:x, :y], [[1, 2], [3, 4]])
|
446
|
-
|
447
|
-
@db.sqls.should == [
|
448
|
-
'BEGIN',
|
449
|
-
'INSERT INTO "test" ("x", "y") VALUES (1, 2)',
|
450
|
-
'INSERT INTO "test" ("x", "y") VALUES (3, 4)',
|
451
|
-
'COMMIT'
|
452
|
-
]
|
448
|
+
@db.sqls.should == ['BEGIN', 'INSERT INTO "test" ("x", "y") VALUES (1, 2)', 'INSERT INTO "test" ("x", "y") VALUES (3, 4)', 'COMMIT']
|
453
449
|
@ds.all.should == [{:x=>1, :y=>2}, {:x=>3, :y=>4}]
|
454
450
|
end
|
455
451
|
|
456
452
|
specify "#import should a single insert statement if server_version >= 80200" do
|
457
453
|
@ds.meta_def(:server_version){80200}
|
458
|
-
|
459
454
|
@ds.import([:x, :y], [[1, 2], [3, 4]])
|
460
|
-
|
461
|
-
@
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
455
|
+
@db.sqls.should == ['BEGIN', 'INSERT INTO "test" ("x", "y") VALUES (1, 2), (3, 4)', 'COMMIT']
|
456
|
+
@ds.all.should == [{:x=>1, :y=>2}, {:x=>3, :y=>4}]
|
457
|
+
end
|
458
|
+
|
459
|
+
specify "#import should work correctly when returning primary keys for server_version < 80200" do
|
460
|
+
@ds.meta_def(:server_version){80199}
|
461
|
+
@ds.import([:x, :y], [[1, 2], [3, 4]], :return=>:primary_key).should == [1, 3]
|
462
|
+
@ds.all.should == [{:x=>1, :y=>2}, {:x=>3, :y=>4}]
|
463
|
+
end
|
464
|
+
|
465
|
+
specify "#import should work correctly when returning primary keys for server_version >= 80200" do
|
466
|
+
@ds.meta_def(:server_version){80200}
|
467
|
+
@ds.import([:x, :y], [[1, 2], [3, 4]], :return=>:primary_key).should == [1, 3]
|
468
|
+
@ds.all.should == [{:x=>1, :y=>2}, {:x=>3, :y=>4}]
|
469
|
+
end
|
470
|
+
|
471
|
+
specify "#import should work correctly when returning primary keys with :slice option for server_version < 80200" do
|
472
|
+
@ds.meta_def(:server_version){80199}
|
473
|
+
@ds.import([:x, :y], [[1, 2], [3, 4]], :return=>:primary_key, :slice=>1).should == [1, 3]
|
474
|
+
@ds.all.should == [{:x=>1, :y=>2}, {:x=>3, :y=>4}]
|
475
|
+
end
|
476
|
+
|
477
|
+
specify "#import should work correctly when returning primary keys with :slice option for server_version >= 80200" do
|
478
|
+
@ds.meta_def(:server_version){80200}
|
479
|
+
@ds.import([:x, :y], [[1, 2], [3, 4]], :return=>:primary_key, :slice=>1).should == [1, 3]
|
480
|
+
@ds.all.should == [{:x=>1, :y=>2}, {:x=>3, :y=>4}]
|
481
|
+
end
|
482
|
+
|
483
|
+
specify "#import should work correctly with an arbitrary returning value" do
|
484
|
+
@ds.meta_def(:server_version){80200}
|
485
|
+
@ds.returning(:y, :x).import([:x, :y], [[1, 2], [3, 4]]).should == [{:y=>2, :x=>1}, {:y=>4, :x=>3}]
|
466
486
|
@ds.all.should == [{:x=>1, :y=>2}, {:x=>3, :y=>4}]
|
467
487
|
end
|
468
488
|
end
|
@@ -500,21 +520,26 @@ describe "Postgres::Dataset#insert" do
|
|
500
520
|
@ds.all.should == [{:xid=>1, :value=>10}, {:xid=>2, :value=>20}, {:xid=>3, :value=>13}]
|
501
521
|
end
|
502
522
|
|
503
|
-
specify "should
|
523
|
+
specify "should insert correctly if server_version < 80200" do
|
504
524
|
@ds.meta_def(:server_version){80100}
|
505
|
-
@ds.
|
506
|
-
@ds.
|
525
|
+
@ds.insert(:value=>10).should == 1
|
526
|
+
@ds.all.should == [{:xid=>1, :value=>10}]
|
507
527
|
end
|
508
528
|
|
509
|
-
specify "should
|
510
|
-
@ds.disable_insert_returning
|
511
|
-
@ds.
|
512
|
-
|
529
|
+
specify "should insert correctly if disabling insert returning" do
|
530
|
+
@ds.disable_insert_returning.insert(:value=>10).should == 1
|
531
|
+
@ds.all.should == [{:xid=>1, :value=>10}]
|
532
|
+
end
|
533
|
+
|
534
|
+
specify "should insert correctly if using a column array and a value array and server_version < 80200" do
|
535
|
+
@ds.meta_def(:server_version){80100}
|
536
|
+
@ds.insert([:value], [10]).should == 1
|
537
|
+
@ds.all.should == [{:xid=>1, :value=>10}]
|
513
538
|
end
|
514
539
|
|
515
540
|
specify "should use INSERT RETURNING if server_version >= 80200" do
|
516
541
|
@ds.meta_def(:server_version){80201}
|
517
|
-
@ds.insert(:value=>10)
|
542
|
+
@ds.insert(:value=>10).should == 1
|
518
543
|
@db.sqls.last.should == 'INSERT INTO "test5" ("value") VALUES (10) RETURNING "xid"'
|
519
544
|
end
|
520
545
|
|
@@ -605,6 +630,18 @@ describe "Postgres::Database schema qualified tables" do
|
|
605
630
|
POSTGRES_DB.drop_table(:domains)
|
606
631
|
end
|
607
632
|
|
633
|
+
specify "#schema should raise an exception if columns from tables in two separate schema are returned" do
|
634
|
+
POSTGRES_DB.create_table!(:public__domains){integer :d}
|
635
|
+
POSTGRES_DB.create_table(:schema_test__domains){integer :i}
|
636
|
+
begin
|
637
|
+
proc{POSTGRES_DB.schema(:domains)}.should raise_error(Sequel::Error)
|
638
|
+
POSTGRES_DB.schema(:public__domains).map{|x| x.first}.should == [:d]
|
639
|
+
POSTGRES_DB.schema(:schema_test__domains).map{|x| x.first}.should == [:i]
|
640
|
+
ensure
|
641
|
+
POSTGRES_DB.drop_table(:public__domains)
|
642
|
+
end
|
643
|
+
end
|
644
|
+
|
608
645
|
specify "#table_exists? should see if the table is in a given schema" do
|
609
646
|
POSTGRES_DB.create_table(:schema_test__schema_test){integer :i}
|
610
647
|
POSTGRES_DB.table_exists?(:schema_test__schema_test).should == true
|