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