sequel 4.15.0 → 4.16.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG +22 -0
- data/doc/association_basics.rdoc +1 -1
- data/doc/migration.rdoc +1 -1
- data/doc/release_notes/4.16.0.txt +36 -0
- data/lib/sequel/adapters/ado/access.rb +1 -1
- data/lib/sequel/adapters/cubrid.rb +0 -1
- data/lib/sequel/adapters/db2.rb +3 -4
- data/lib/sequel/adapters/jdbc.rb +2 -3
- data/lib/sequel/adapters/jdbc/mysql.rb +2 -2
- data/lib/sequel/adapters/mysql.rb +3 -3
- data/lib/sequel/adapters/odbc.rb +1 -1
- data/lib/sequel/adapters/oracle.rb +0 -1
- data/lib/sequel/adapters/postgres.rb +8 -1
- data/lib/sequel/adapters/shared/mssql.rb +1 -1
- data/lib/sequel/adapters/shared/mysql.rb +0 -1
- data/lib/sequel/adapters/shared/postgres.rb +5 -0
- data/lib/sequel/database/schema_methods.rb +6 -4
- data/lib/sequel/dataset/query.rb +9 -7
- data/lib/sequel/extensions/migration.rb +4 -4
- data/lib/sequel/model/associations.rb +7 -1
- data/lib/sequel/model/base.rb +37 -13
- data/lib/sequel/plugins/lazy_attributes.rb +1 -1
- data/lib/sequel/plugins/validation_helpers.rb +4 -1
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/postgres_spec.rb +4 -3
- data/spec/core/dataset_spec.rb +5 -0
- data/spec/core/schema_spec.rb +24 -2
- data/spec/extensions/migration_spec.rb +3 -3
- data/spec/extensions/validation_helpers_spec.rb +14 -0
- data/spec/model/association_reflection_spec.rb +29 -13
- data/spec/model/record_spec.rb +41 -2
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 505bc666a3de9a394dd0bb31e07afff6feca86da
|
4
|
+
data.tar.gz: 2082ac9b3731827b5c4e0c39859e8471e706a99d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f728df92423ad2e207a2db67d89cc7c79819351973c348845978d7ba93826f33caf21b4251dec609c5fcc48c0c13b55460ac2800f7cd36b328d87463c804cc3a
|
7
|
+
data.tar.gz: 836ab1fb7bc23c595070f15dff4cf300b80d4b4eaf3e024c305d073a84a3a84c19782bef11359e9e96c0cb739206995ac8a4828ece1a5057c66a8c50fee0f87b
|
data/CHANGELOG
CHANGED
@@ -1,3 +1,25 @@
|
|
1
|
+
=== 4.16.0 (2014-11-01)
|
2
|
+
|
3
|
+
* Make Database#create_table? and #create_join_table? not use IF NOT EXISTS if indexes are being added (jeremyevans) (#904)
|
4
|
+
|
5
|
+
* Dataset#distinct now accepts virtual row blocks (chanks) (#901)
|
6
|
+
|
7
|
+
* Recognize disconnect errors in the postgres adapter when SSL is used (jeremyevans) (#900)
|
8
|
+
|
9
|
+
* Stop converting '' default values to nil default values on MySQL (jeremyevans)
|
10
|
+
|
11
|
+
* Add Model#qualified_pk_hash, for returning a hash with qualified pk keys (jeremyevans)
|
12
|
+
|
13
|
+
* Make validates_unique use a qualified primary key if the dataset is joined (jeremyevans) (#895)
|
14
|
+
|
15
|
+
* Make Sequel::Model.cache_associations = false skip the database's schema cache when loading the schema (jeremyevans)
|
16
|
+
|
17
|
+
* Make Database#foreign_key_list work on Microsoft SQL Server 2005 (jeremyevans)
|
18
|
+
|
19
|
+
* Make create_table with :foreign option reversible on PostgreSQL (jeremyevans)
|
20
|
+
|
21
|
+
* Make drop_table with :foreign option on PostgreSQL drop a foreign table (johnnyt) (#892)
|
22
|
+
|
1
23
|
=== 4.15.0 (2014-10-01)
|
2
24
|
|
3
25
|
* Make AssociationReflection#reciprocal not raise error if associated class contains association with invalid associated class (jeremyevans)
|
data/doc/association_basics.rdoc
CHANGED
@@ -191,7 +191,7 @@ specify an explicit :key option:
|
|
191
191
|
many_to_one :artist, :key=>:artistid
|
192
192
|
end
|
193
193
|
class Artist
|
194
|
-
one_to_many :
|
194
|
+
one_to_many :albums, :key=>:artistid
|
195
195
|
end
|
196
196
|
|
197
197
|
For many_to_many associations, the :left_key and :right_key options can be
|
data/doc/migration.rdoc
CHANGED
@@ -358,7 +358,7 @@ you should give it some thought before using it.
|
|
358
358
|
|
359
359
|
== Ignoring missing migrations
|
360
360
|
|
361
|
-
In some cases, you may want to allow a migration in the database that does not exist in the filesystem (deploying to an older version of code without running a down migration when deploy auto-migrates, for example). If required, you can pass <tt>:allow_missing_migration_files => true
|
361
|
+
In some cases, you may want to allow a migration in the database that does not exist in the filesystem (deploying to an older version of code without running a down migration when deploy auto-migrates, for example). If required, you can pass <tt>:allow_missing_migration_files => true</tt> as an option. This will stop errors from being raised if there are migrations in the database that do not exist in the filesystem.
|
362
362
|
|
363
363
|
== Modifying existing migrations
|
364
364
|
|
@@ -0,0 +1,36 @@
|
|
1
|
+
= New Features
|
2
|
+
|
3
|
+
* Model#qualified_pk_hash has been added, which is similar to
|
4
|
+
Model#pk_hash, but uses qualified keys.
|
5
|
+
|
6
|
+
* Dataset#distinct now accepts a virtual row block.
|
7
|
+
|
8
|
+
* Database#drop_view with :foreign=>true option now drops foreign
|
9
|
+
tables on PostgreSQL. Database#create_table with :foreign option
|
10
|
+
is now reversible on PostgreSQL.
|
11
|
+
|
12
|
+
= Other Improvements
|
13
|
+
|
14
|
+
* Sequel::Model.cache_associations = false now skips the database's
|
15
|
+
schema cache when loading the schema for a model. This fixes
|
16
|
+
some issues in environments that use code reloading.
|
17
|
+
|
18
|
+
* Database#create_table? and #create_join_table? no longer use
|
19
|
+
IF NOT EXISTS if indexes are being created.
|
20
|
+
|
21
|
+
* Model.primary_key_hash and .qualified_primary_key_hash have been
|
22
|
+
optimized.
|
23
|
+
|
24
|
+
* validates_unique in the validation_helpers plugin now uses a
|
25
|
+
qualified primary key if the model's dataset is joined. This fixes
|
26
|
+
a case when the auto_validations and class_table_inheritance
|
27
|
+
plugins are used together.
|
28
|
+
|
29
|
+
* Disconnect errors are now recognized in the postgres adapter when
|
30
|
+
SSL is used for connecting.
|
31
|
+
|
32
|
+
* Empty string default values are no longer converted to nil default
|
33
|
+
values on MySQL.
|
34
|
+
|
35
|
+
* Database#foreign_key_list now works correctly on Microsoft SQL
|
36
|
+
Server 2005.
|
@@ -109,7 +109,7 @@ module Sequel
|
|
109
109
|
def execute_insert(sql, opts=OPTS)
|
110
110
|
synchronize(opts[:server]) do |conn|
|
111
111
|
begin
|
112
|
-
|
112
|
+
log_yield(sql){conn.Execute(sql)}
|
113
113
|
res = log_yield(LAST_INSERT_ID){conn.Execute(LAST_INSERT_ID)}
|
114
114
|
res.getRows.transpose.each{|r| return r.shift}
|
115
115
|
rescue ::WIN32OLERuntimeError => e
|
@@ -124,7 +124,6 @@ module Sequel
|
|
124
124
|
def fetch_rows(sql)
|
125
125
|
execute(sql) do |stmt|
|
126
126
|
begin
|
127
|
-
procs =
|
128
127
|
cols = stmt.column_info.map{|c| [output_identifier(c[COLUMN_INFO_NAME]), CUBRID_TYPE_PROCS[c[COLUMN_INFO_TYPE]]]}
|
129
128
|
@columns = cols.map{|c| c.first}
|
130
129
|
stmt.each do |r|
|
data/lib/sequel/adapters/db2.rb
CHANGED
@@ -38,7 +38,7 @@ module Sequel
|
|
38
38
|
set_adapter_scheme :db2
|
39
39
|
|
40
40
|
TEMPORARY = 'GLOBAL TEMPORARY '.freeze
|
41
|
-
|
41
|
+
_, NullHandle = DB2CLI.SQLAllocHandle(DB2CLI::SQL_HANDLE_ENV, DB2CLI::SQL_NULL_HANDLE)
|
42
42
|
|
43
43
|
# Hash of connection procs for converting
|
44
44
|
attr_reader :conversion_procs
|
@@ -64,7 +64,7 @@ module Sequel
|
|
64
64
|
log_connection_execute(conn, sql)
|
65
65
|
sql = "SELECT IDENTITY_VAL_LOCAL() FROM SYSIBM.SYSDUMMY1"
|
66
66
|
log_connection_execute(conn, sql) do |sth|
|
67
|
-
|
67
|
+
_, _, datatype, size, _, _ = checked_error("Could not describe column"){DB2CLI.SQLDescribeCol(sth, 1, 256)}
|
68
68
|
if DB2CLI.SQLFetch(sth) != DB2CLI::SQL_NO_DATA_FOUND
|
69
69
|
v, _ = checked_error("Could not get data"){DB2CLI.SQLGetData(sth, 1, datatype, size)}
|
70
70
|
if v.is_a?(String)
|
@@ -180,7 +180,6 @@ module Sequel
|
|
180
180
|
def fetch_rows(sql)
|
181
181
|
execute(sql) do |sth|
|
182
182
|
db = @db
|
183
|
-
i = 1
|
184
183
|
column_info = get_column_info(sth)
|
185
184
|
cols = column_info.map{|c| c.at(1)}
|
186
185
|
@columns = cols
|
@@ -213,7 +212,7 @@ module Sequel
|
|
213
212
|
cps = db.conversion_procs
|
214
213
|
|
215
214
|
(1..column_count).map do |i|
|
216
|
-
name,
|
215
|
+
name, _, datatype, size, digits, _ = db.checked_error("Could not describe column"){DB2CLI.SQLDescribeCol(sth, i, MAX_COL_SIZE)}
|
217
216
|
pr = if datatype == DB2CLI::SQL_SMALLINT && convert && size <= 5 && digits <= 1
|
218
217
|
cps[:boolean]
|
219
218
|
elsif datatype == DB2CLI::SQL_CLOB && Sequel::DB2.use_clob_as_blob
|
data/lib/sequel/adapters/jdbc.rb
CHANGED
@@ -595,7 +595,6 @@ module Sequel
|
|
595
595
|
# Parse the table schema for the given table.
|
596
596
|
def schema_parse_table(table, opts=OPTS)
|
597
597
|
m = output_identifier_meth(opts[:dataset])
|
598
|
-
ds = dataset
|
599
598
|
schema, table = metadata_schema_and_table(table, opts)
|
600
599
|
pks, ts = [], []
|
601
600
|
metadata(:getPrimaryKeys, nil, schema, table) do |h|
|
@@ -781,8 +780,8 @@ module Sequel
|
|
781
780
|
|
782
781
|
while result.next
|
783
782
|
row = {}
|
784
|
-
cols.each do |n,
|
785
|
-
row[n] = pr.call(result,
|
783
|
+
cols.each do |n, j, pr|
|
784
|
+
row[n] = pr.call(result, j)
|
786
785
|
end
|
787
786
|
yield row
|
788
787
|
end
|
@@ -133,12 +133,12 @@ module Sequel
|
|
133
133
|
# depending on the value given.
|
134
134
|
def convert_invalid_date_time=(v)
|
135
135
|
m0 = ::Sequel.method(:string_to_time)
|
136
|
-
@conversion_procs[11] = (v != false) ? lambda{|
|
136
|
+
@conversion_procs[11] = (v != false) ? lambda{|val| convert_date_time(val, &m0)} : m0
|
137
137
|
m1 = ::Sequel.method(:string_to_date)
|
138
|
-
m = (v != false) ? lambda{|
|
138
|
+
m = (v != false) ? lambda{|val| convert_date_time(val, &m1)} : m1
|
139
139
|
[10, 14].each{|i| @conversion_procs[i] = m}
|
140
140
|
m2 = method(:to_application_timestamp)
|
141
|
-
m = (v != false) ? lambda{|
|
141
|
+
m = (v != false) ? lambda{|val| convert_date_time(val, &m2)} : m2
|
142
142
|
[7, 12].each{|i| @conversion_procs[i] = m}
|
143
143
|
@convert_invalid_date_time = v
|
144
144
|
end
|
data/lib/sequel/adapters/odbc.rb
CHANGED
@@ -111,7 +111,6 @@ module Sequel
|
|
111
111
|
'decimal'.freeze=>Float, 'date'.freeze=>Time, 'datetime'.freeze=>Time,
|
112
112
|
'time'.freeze=>Time, 'boolean'.freeze=>String, 'blob'.freeze=>OCI8::BLOB}
|
113
113
|
def cursor_bind_params(conn, cursor, args)
|
114
|
-
cursor
|
115
114
|
i = 0
|
116
115
|
args.map do |arg, type|
|
117
116
|
i += 1
|
@@ -108,7 +108,14 @@ module Sequel
|
|
108
108
|
# PGconn subclass for connection specific methods used with the
|
109
109
|
# pg, postgres, or postgres-pr driver.
|
110
110
|
class Adapter < ::PGconn
|
111
|
-
|
111
|
+
disconnect_errors = [
|
112
|
+
'could not receive data from server',
|
113
|
+
'no connection to the server',
|
114
|
+
'connection not open',
|
115
|
+
'terminating connection due to administrator command',
|
116
|
+
'PQconsumeInput() SSL SYSCALL error'
|
117
|
+
]
|
118
|
+
DISCONNECT_ERROR_RE = /\A#{Regexp.union(disconnect_errors)}/
|
112
119
|
|
113
120
|
self.translate_results = false if respond_to?(:translate_results=)
|
114
121
|
|
@@ -144,7 +144,7 @@ module Sequel
|
|
144
144
|
:rc__name___referenced_column,
|
145
145
|
object_schema_name(:fk__referenced_object_id).as(:schema),
|
146
146
|
object_name(:fk__referenced_object_id).as(:table)]}.
|
147
|
-
order(:
|
147
|
+
order(:fk__name, :fkc__constraint_column_id)
|
148
148
|
h = {}
|
149
149
|
ds.each do |row|
|
150
150
|
if r = h[row[:name]]
|
@@ -464,7 +464,6 @@ module Sequel
|
|
464
464
|
row[:allow_null] = row.delete(:Null) == 'YES'
|
465
465
|
row[:default] = row.delete(:Default)
|
466
466
|
row[:primary_key] = row.delete(:Key) == 'PRI'
|
467
|
-
row[:default] = nil if blank_object?(row[:default])
|
468
467
|
row[:db_type] = row.delete(:Type)
|
469
468
|
row[:type] = schema_column_type(row[:db_type])
|
470
469
|
[m.call(row.delete(:Field)), row]
|
@@ -906,6 +906,11 @@ module Sequel
|
|
906
906
|
"DROP TRIGGER#{' IF EXISTS' if opts[:if_exists]} #{name} ON #{quote_schema_table(table)}#{' CASCADE' if opts[:cascade]}"
|
907
907
|
end
|
908
908
|
|
909
|
+
# Support :foreign tables
|
910
|
+
def drop_table_sql(name, options)
|
911
|
+
"DROP#{' FOREIGN' if options[:foreign]} TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_schema_table(name)}#{' CASCADE' if options[:cascade]}"
|
912
|
+
end
|
913
|
+
|
909
914
|
# SQL for dropping a view from the database.
|
910
915
|
def drop_view_sql(name, opts=OPTS)
|
911
916
|
"DROP #{'MATERIALIZED ' if opts[:materialized]}VIEW#{' IF EXISTS' if opts[:if_exists]} #{quote_schema_table(name)}#{' CASCADE' if opts[:cascade]}"
|
@@ -142,7 +142,7 @@ module Sequel
|
|
142
142
|
|
143
143
|
# Creates the join table unless it already exists.
|
144
144
|
def create_join_table?(hash, options=OPTS)
|
145
|
-
if supports_create_table_if_not_exists?
|
145
|
+
if supports_create_table_if_not_exists? && options[:no_index]
|
146
146
|
create_join_table(hash, options.merge(:if_not_exists=>true))
|
147
147
|
elsif !table_exists?(join_table_name(hash, options))
|
148
148
|
create_join_table(hash, options)
|
@@ -215,10 +215,12 @@ module Sequel
|
|
215
215
|
# # SELECT NULL FROM a LIMIT 1 -- check existence
|
216
216
|
# # CREATE TABLE a (a integer) -- if it doesn't already exist
|
217
217
|
def create_table?(name, options=OPTS, &block)
|
218
|
-
|
219
|
-
|
218
|
+
options = options.dup
|
219
|
+
generator = options[:generator] ||= create_table_generator(&block)
|
220
|
+
if generator.indexes.empty? && supports_create_table_if_not_exists?
|
221
|
+
create_table(name, options.merge!(:if_not_exists=>true))
|
220
222
|
elsif !table_exists?(name)
|
221
|
-
create_table(name, options
|
223
|
+
create_table(name, options)
|
222
224
|
end
|
223
225
|
end
|
224
226
|
|
data/lib/sequel/dataset/query.rb
CHANGED
@@ -82,16 +82,18 @@ module Sequel
|
|
82
82
|
c
|
83
83
|
end
|
84
84
|
|
85
|
-
# Returns a copy of the dataset with the SQL DISTINCT clause.
|
86
|
-
#
|
87
|
-
#
|
88
|
-
#
|
89
|
-
#
|
90
|
-
# are given and DISTINCT ON is not supported.
|
85
|
+
# Returns a copy of the dataset with the SQL DISTINCT clause. The DISTINCT
|
86
|
+
# clause is used to remove duplicate rows from the output. If arguments
|
87
|
+
# are provided, uses a DISTINCT ON clause, in which case it will only be
|
88
|
+
# distinct on those columns, instead of all returned columns. If a block
|
89
|
+
# is given, it is treated as a virtual row block, similar to +where+.
|
90
|
+
# Raises an error if arguments are given and DISTINCT ON is not supported.
|
91
91
|
#
|
92
92
|
# DB[:items].distinct # SQL: SELECT DISTINCT * FROM items
|
93
93
|
# DB[:items].order(:id).distinct(:id) # SQL: SELECT DISTINCT ON (id) * FROM items ORDER BY id
|
94
|
-
|
94
|
+
# DB[:items].order(:id).distinct{func(:id)} # SQL: SELECT DISTINCT ON (func(id)) * FROM items ORDER BY id
|
95
|
+
def distinct(*args, &block)
|
96
|
+
virtual_row_columns(args, block)
|
95
97
|
raise(InvalidOperation, "DISTINCT ON not supported") if !args.empty? && !supports_distinct_on?
|
96
98
|
clone(:distinct => args)
|
97
99
|
end
|
@@ -203,12 +203,12 @@ module Sequel
|
|
203
203
|
@actions << [:drop_join_table, *args]
|
204
204
|
end
|
205
205
|
|
206
|
-
def create_table(
|
207
|
-
@actions << [:drop_table,
|
206
|
+
def create_table(name, opts=OPTS)
|
207
|
+
@actions << [:drop_table, name, opts]
|
208
208
|
end
|
209
209
|
|
210
|
-
def create_view(name, _,
|
211
|
-
@actions << [:drop_view, name,
|
210
|
+
def create_view(name, _, opts=OPTS)
|
211
|
+
@actions << [:drop_view, name, opts]
|
212
212
|
end
|
213
213
|
|
214
214
|
def rename_column(table, name, new_name)
|
@@ -259,7 +259,7 @@ module Sequel
|
|
259
259
|
ds.send(:post_load, objects)
|
260
260
|
else
|
261
261
|
loader = placeholder_eager_loader
|
262
|
-
loader = loader.with_dataset{|
|
262
|
+
loader = loader.with_dataset{|dataset| dataset.eager(cascade)} if cascade
|
263
263
|
objects = loader.all(eo[:id_map].keys)
|
264
264
|
end
|
265
265
|
|
@@ -2036,6 +2036,12 @@ module Sequel
|
|
2036
2036
|
end
|
2037
2037
|
ds
|
2038
2038
|
end
|
2039
|
+
|
2040
|
+
# If not caching associations, reload the database schema by default,
|
2041
|
+
# ignoring any cached values.
|
2042
|
+
def reload_db_schema?
|
2043
|
+
!@cache_associations
|
2044
|
+
end
|
2039
2045
|
end
|
2040
2046
|
|
2041
2047
|
# Instance methods used to implement the associations support.
|
data/lib/sequel/model/base.rb
CHANGED
@@ -580,14 +580,15 @@ module Sequel
|
|
580
580
|
# Artist.primary_key_hash(1) # => {:id=>1}
|
581
581
|
# Artist.primary_key_hash([1, 2]) # => {:id1=>1, :id2=>2}
|
582
582
|
def primary_key_hash(value)
|
583
|
-
|
584
|
-
|
583
|
+
case key = @primary_key
|
584
|
+
when Symbol
|
585
|
+
{key => value}
|
585
586
|
when Array
|
586
587
|
hash = {}
|
587
|
-
key.
|
588
|
+
key.zip(Array(value)){|k,v| hash[k] = v}
|
588
589
|
hash
|
589
590
|
else
|
590
|
-
{
|
591
|
+
raise(Error, "#{self} does not have a primary key")
|
591
592
|
end
|
592
593
|
end
|
593
594
|
|
@@ -599,9 +600,16 @@ module Sequel
|
|
599
600
|
# Artist.filter(Artist.qualified_primary_key_hash(1))
|
600
601
|
# # SELECT * FROM artists WHERE (artists.id = 1)
|
601
602
|
def qualified_primary_key_hash(value, qualifier=table_name)
|
602
|
-
|
603
|
-
|
604
|
-
|
603
|
+
case key = @primary_key
|
604
|
+
when Symbol
|
605
|
+
{SQL::QualifiedIdentifier.new(qualifier, key) => value}
|
606
|
+
when Array
|
607
|
+
hash = {}
|
608
|
+
key.zip(Array(value)){|k,v| hash[SQL::QualifiedIdentifier.new(qualifier, k)] = v}
|
609
|
+
hash
|
610
|
+
else
|
611
|
+
raise(Error, "#{self} does not have a primary key")
|
612
|
+
end
|
605
613
|
end
|
606
614
|
|
607
615
|
# Similar to finder, but uses a prepared statement instead of a placeholder
|
@@ -891,7 +899,7 @@ module Sequel
|
|
891
899
|
# Get the schema from the database, fall back on checking the columns
|
892
900
|
# via the database if that will return inaccurate results or if
|
893
901
|
# it raises an error.
|
894
|
-
def get_db_schema(reload =
|
902
|
+
def get_db_schema(reload = reload_db_schema?)
|
895
903
|
set_columns(nil)
|
896
904
|
return nil unless @dataset
|
897
905
|
schema_hash = {}
|
@@ -933,7 +941,7 @@ module Sequel
|
|
933
941
|
end
|
934
942
|
schema_hash
|
935
943
|
end
|
936
|
-
|
944
|
+
|
937
945
|
# Uncached version of setter_methods, to be overridden by plugins
|
938
946
|
# that want to modify the methods used.
|
939
947
|
def get_setter_methods
|
@@ -1045,6 +1053,11 @@ module Sequel
|
|
1045
1053
|
end
|
1046
1054
|
end
|
1047
1055
|
|
1056
|
+
# Whether to reload the database schema by default, ignoring any cached value.
|
1057
|
+
def reload_db_schema?
|
1058
|
+
false
|
1059
|
+
end
|
1060
|
+
|
1048
1061
|
# Reset the cached fast primary lookup SQL if a simple table and primary key
|
1049
1062
|
# are used, or set it to nil if not used.
|
1050
1063
|
def reset_fast_pk_lookup_sql
|
@@ -1445,6 +1458,16 @@ module Sequel
|
|
1445
1458
|
model.primary_key_hash(pk)
|
1446
1459
|
end
|
1447
1460
|
|
1461
|
+
# Returns a hash mapping the receivers primary key column(s) to their values.
|
1462
|
+
#
|
1463
|
+
# Artist[1].qualified_pk_hash
|
1464
|
+
# # => {Sequel.qualify(:artists, :id)=>1}
|
1465
|
+
# Artist[[1, 2]].qualified_pk_hash
|
1466
|
+
# # => {Sequel.qualify(:artists, :id1)=>1, Sequel.qualify(:artists, :id2)=>2}
|
1467
|
+
def qualified_pk_hash(qualifier=model.table_name)
|
1468
|
+
model.qualified_primary_key_hash(pk, qualifier)
|
1469
|
+
end
|
1470
|
+
|
1448
1471
|
# Reloads attributes from database and returns self. Also clears all
|
1449
1472
|
# changed_columns information. Raises an +Error+ if the record no longer
|
1450
1473
|
# exists in the database.
|
@@ -1631,7 +1654,7 @@ module Sequel
|
|
1631
1654
|
raise Error, "No dataset for model #{model}" unless ds = model.instance_dataset
|
1632
1655
|
|
1633
1656
|
cond = if ds.joined_dataset?
|
1634
|
-
|
1657
|
+
qualified_pk_hash
|
1635
1658
|
else
|
1636
1659
|
pk_hash
|
1637
1660
|
end
|
@@ -2283,11 +2306,12 @@ module Sequel
|
|
2283
2306
|
# value. If no records matches, returns nil.
|
2284
2307
|
#
|
2285
2308
|
# # Single primary key
|
2286
|
-
# Artist.dataset.with_pk(1)
|
2309
|
+
# Artist.dataset.with_pk(1)
|
2310
|
+
# # SELECT * FROM artists WHERE (artists.id = 1) LIMIT 1
|
2287
2311
|
#
|
2288
2312
|
# # Composite primary key
|
2289
|
-
# Artist.dataset.with_pk([1, 2])
|
2290
|
-
#
|
2313
|
+
# Artist.dataset.with_pk([1, 2])
|
2314
|
+
# # SELECT * FROM artists WHERE ((artists.id1 = 1) AND (artists.id2 = 2)) LIMIT 1
|
2291
2315
|
def with_pk(pk)
|
2292
2316
|
first(model.qualified_primary_key_hash(pk))
|
2293
2317
|
end
|
@@ -241,7 +241,10 @@ module Sequel
|
|
241
241
|
ds.where(arr.zip(vals))
|
242
242
|
end
|
243
243
|
ds = yield(ds) if block_given?
|
244
|
-
|
244
|
+
unless new?
|
245
|
+
h = ds.joined_dataset? ? qualified_pk_hash : pk_hash
|
246
|
+
ds = ds.exclude(h)
|
247
|
+
end
|
245
248
|
errors.add(a, message) unless ds.count == 0
|
246
249
|
end
|
247
250
|
end
|
data/lib/sequel/version.rb
CHANGED
@@ -3,7 +3,7 @@ module Sequel
|
|
3
3
|
MAJOR = 4
|
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 = 16
|
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
|
@@ -277,10 +277,11 @@ describe "A PostgreSQL database" do
|
|
277
277
|
a.should == "WARNING: foo\n"
|
278
278
|
end if DB.adapter_scheme == :postgres && SEQUEL_POSTGRES_USES_PG && DB.server_version >= 90000
|
279
279
|
|
280
|
-
|
281
|
-
|
282
|
-
|
280
|
+
# These only test the SQL created, because a true test using file_fdw or postgres_fdw
|
281
|
+
# requires superuser permissions, and you should not be running the tests as a superuser.
|
282
|
+
specify "should support creating and dropping foreign tables" do
|
283
283
|
DB.send(:create_table_sql, :t, DB.create_table_generator{Integer :a}, :foreign=>:f, :options=>{:o=>1}).should == 'CREATE FOREIGN TABLE "t" ("a" integer) SERVER "f" OPTIONS (o \'1\')'
|
284
|
+
DB.send(:drop_table_sql, :t, :foreign=>true).should == 'DROP FOREIGN TABLE "t"'
|
284
285
|
end
|
285
286
|
end
|
286
287
|
|
data/spec/core/dataset_spec.rb
CHANGED
@@ -1820,6 +1820,11 @@ describe "Dataset#distinct" do
|
|
1820
1820
|
@dataset.distinct(Sequel.cast(:stamp, :integer), :node_id=>nil).sql.should == 'SELECT DISTINCT ON (CAST(stamp AS integer), (node_id IS NULL)) name FROM test'
|
1821
1821
|
end
|
1822
1822
|
|
1823
|
+
specify "should use DISTINCT ON if columns are given in a virtual row block and DISTINCT ON is supported" do
|
1824
|
+
meta_def(@dataset, :supports_distinct_on?){true}
|
1825
|
+
@dataset.distinct{func(:id)}.sql.should == 'SELECT DISTINCT ON (func(id)) name FROM test'
|
1826
|
+
end
|
1827
|
+
|
1823
1828
|
specify "should do a subselect for count" do
|
1824
1829
|
@dataset.distinct.count
|
1825
1830
|
@db.sqls.should == ['SELECT count(*) AS count FROM (SELECT DISTINCT name FROM test) AS t1 LIMIT 1']
|
data/spec/core/schema_spec.rb
CHANGED
@@ -731,6 +731,17 @@ describe "DB#create_table?" do
|
|
731
731
|
@db.create_table?(:cats){|*a|}
|
732
732
|
@db.sqls.should == ['CREATE TABLE IF NOT EXISTS cats ()']
|
733
733
|
end
|
734
|
+
|
735
|
+
specify "should not use IF NOT EXISTS if the indexes are created" do
|
736
|
+
meta_def(@db, :table_exists?){|a| false}
|
737
|
+
meta_def(@db, :supports_create_table_if_not_exists?){true}
|
738
|
+
@db.create_table?(:cats){|*a| Integer :a, :index=>true}
|
739
|
+
@db.sqls.should == ['CREATE TABLE cats (a integer)', 'CREATE INDEX cats_a_index ON cats (a)']
|
740
|
+
|
741
|
+
meta_def(@db, :table_exists?){|a| true}
|
742
|
+
@db.create_table?(:cats){|*a| Integer :a, :index=>true}
|
743
|
+
@db.sqls.should == []
|
744
|
+
end
|
734
745
|
end
|
735
746
|
|
736
747
|
describe "DB#create_join_table" do
|
@@ -798,10 +809,21 @@ describe "DB#create_join_table?" do
|
|
798
809
|
@db.sqls.should == []
|
799
810
|
end
|
800
811
|
|
801
|
-
specify "should use IF NOT EXISTS
|
812
|
+
specify "should not use IF NOT EXISTS" do
|
813
|
+
meta_def(@db, :table_exists?){|a| false}
|
802
814
|
meta_def(@db, :supports_create_table_if_not_exists?){true}
|
803
815
|
@db.create_join_table?(:cat_id=>:cats, :dog_id=>:dogs)
|
804
|
-
@db.sqls.should == ['CREATE TABLE
|
816
|
+
@db.sqls.should == ['CREATE TABLE cats_dogs (cat_id integer NOT NULL REFERENCES cats, dog_id integer NOT NULL REFERENCES dogs, PRIMARY KEY (cat_id, dog_id))', 'CREATE INDEX cats_dogs_dog_id_cat_id_index ON cats_dogs (dog_id, cat_id)']
|
817
|
+
|
818
|
+
meta_def(@db, :table_exists?){|a| true}
|
819
|
+
@db.create_join_table?(:cat_id=>:cats, :dog_id=>:dogs)
|
820
|
+
@db.sqls.should == []
|
821
|
+
end
|
822
|
+
|
823
|
+
specify "should not use IF NOT EXISTS if no_index is used" do
|
824
|
+
meta_def(@db, :supports_create_table_if_not_exists?){true}
|
825
|
+
@db.create_join_table?({:cat_id=>:cats, :dog_id=>:dogs}, :no_index=>true)
|
826
|
+
@db.sqls.should == ['CREATE TABLE IF NOT EXISTS cats_dogs (cat_id integer NOT NULL REFERENCES cats, dog_id integer NOT NULL REFERENCES dogs, PRIMARY KEY (cat_id, dog_id))']
|
805
827
|
end
|
806
828
|
end
|
807
829
|
|
@@ -121,7 +121,7 @@ describe "Reversible Migrations with Sequel.migration{change{}}" do
|
|
121
121
|
end
|
122
122
|
@db = @c.new
|
123
123
|
@p = Proc.new do
|
124
|
-
create_table(:a){Integer :a}
|
124
|
+
create_table(:a, :foo=>:bar){Integer :a}
|
125
125
|
add_column :a, :b, String
|
126
126
|
add_index :a, :b
|
127
127
|
rename_column :a, :b, :c
|
@@ -146,7 +146,7 @@ describe "Reversible Migrations with Sequel.migration{change{}}" do
|
|
146
146
|
specify "should apply up with normal actions in normal order" do
|
147
147
|
p = @p
|
148
148
|
Sequel.migration{change(&p)}.apply(@db, :up)
|
149
|
-
@db.actions.should == [[:create_table, :a],
|
149
|
+
@db.actions.should == [[:create_table, :a, {:foo=>:bar}],
|
150
150
|
[:add_column, :a, :b, String],
|
151
151
|
[:add_index, :a, :b],
|
152
152
|
[:rename_column, :a, :b, :c],
|
@@ -189,7 +189,7 @@ describe "Reversible Migrations with Sequel.migration{change{}}" do
|
|
189
189
|
[:rename_column, :a, :c, :b],
|
190
190
|
[:drop_index, :a, :b],
|
191
191
|
[:drop_column, :a, :b],
|
192
|
-
[:drop_table, :a]]
|
192
|
+
[:drop_table, :a, {:foo=>:bar}]]
|
193
193
|
end
|
194
194
|
|
195
195
|
specify "should raise in the down direction if migration uses unsupported method" do
|
@@ -477,6 +477,20 @@ describe "Sequel::Plugins::ValidationHelpers" do
|
|
477
477
|
"SELECT count(*) AS count FROM items WHERE ((a IN (1, 2, 3)) AND (username = '0records') AND (id != 3)) LIMIT 1"]
|
478
478
|
end
|
479
479
|
|
480
|
+
it "should use qualified primary keys for validates_unique when the dataset is joined" do
|
481
|
+
@c.columns(:id, :username, :password)
|
482
|
+
@c.set_dataset DB[:items]
|
483
|
+
c = @c
|
484
|
+
@c.set_validations{validates_unique(:username, :dataset=>c.cross_join(:a))}
|
485
|
+
@c.dataset._fetch = {:v=>0}
|
486
|
+
|
487
|
+
DB.reset
|
488
|
+
@c.new(:username => "0records", :password => "anothertest").should be_valid
|
489
|
+
@c.load(:id=>3, :username => "0records", :password => "anothertest").should be_valid
|
490
|
+
DB.sqls.should == ["SELECT count(*) AS count FROM items CROSS JOIN a WHERE (username = '0records') LIMIT 1",
|
491
|
+
"SELECT count(*) AS count FROM items CROSS JOIN a WHERE ((username = '0records') AND (items.id != 3)) LIMIT 1"]
|
492
|
+
end
|
493
|
+
|
480
494
|
it "should support :only_if_modified option for validates_unique, and not check uniqueness for existing records if values haven't changed" do
|
481
495
|
@c.columns(:id, :username, :password)
|
482
496
|
@c.set_dataset DB[:items]
|
@@ -479,22 +479,38 @@ end
|
|
479
479
|
|
480
480
|
describe Sequel::Model::Associations::AssociationReflection, "with caching disabled" do
|
481
481
|
before do
|
482
|
-
@
|
483
|
-
|
484
|
-
|
485
|
-
|
482
|
+
@db = Sequel.mock
|
483
|
+
@c = Class.new(Sequel::Model)
|
484
|
+
@c.dataset = @db[:foo]
|
485
|
+
@c.cache_associations = false
|
486
486
|
end
|
487
487
|
|
488
488
|
it "should not cache metadata" do
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
489
|
+
begin
|
490
|
+
class ::ParParent < Sequel::Model; end
|
491
|
+
c = ParParent
|
492
|
+
@c.many_to_one :c, :class=>:ParParent
|
493
|
+
@c.association_reflection(:c).associated_class.should == c
|
494
|
+
Object.send(:remove_const, :ParParent)
|
495
|
+
class ::ParParent < Sequel::Model; end
|
496
|
+
c = ParParent
|
497
|
+
@c.association_reflection(:c).associated_class.should == c
|
498
|
+
ensure
|
499
|
+
Object.send(:remove_const, :ParParent)
|
500
|
+
end
|
501
|
+
end
|
502
|
+
|
503
|
+
it "should not used cached schema" do
|
504
|
+
def @db.supports_schema_parsing?; true end
|
505
|
+
def @db.schema(table, opts={})
|
506
|
+
[[opts[:reload] ? :reload : :id, {}]]
|
507
|
+
end
|
508
|
+
@c.dataset = @db[:items]
|
509
|
+
@c.columns.should == [:reload]
|
510
|
+
|
511
|
+
@c.cache_associations = true
|
512
|
+
@c.dataset = @db[:items]
|
513
|
+
@c.columns.should == [:id]
|
498
514
|
end
|
499
515
|
end
|
500
516
|
|
data/spec/model/record_spec.rb
CHANGED
@@ -873,11 +873,50 @@ describe "Model#pk_hash" do
|
|
873
873
|
it "should raise if no primary key" do
|
874
874
|
@m.set_primary_key nil
|
875
875
|
m = @m.new(:id => 111, :x => 2, :y => 3)
|
876
|
-
proc
|
876
|
+
proc{m.pk_hash}.should raise_error(Sequel::Error)
|
877
877
|
|
878
878
|
@m.no_primary_key
|
879
879
|
m = @m.new(:id => 111, :x => 2, :y => 3)
|
880
|
-
proc
|
880
|
+
proc{m.pk_hash}.should raise_error(Sequel::Error)
|
881
|
+
end
|
882
|
+
end
|
883
|
+
|
884
|
+
describe "Model#qualified_pk_hash" do
|
885
|
+
before do
|
886
|
+
@m = Class.new(Sequel::Model(:items))
|
887
|
+
@m.columns :id, :x, :y
|
888
|
+
end
|
889
|
+
|
890
|
+
it "should by default return a hash with the value of the :id column" do
|
891
|
+
m = @m.load(:id => 111, :x => 2, :y => 3)
|
892
|
+
m.qualified_pk_hash.should == {Sequel.qualify(:items, :id) => 111}
|
893
|
+
end
|
894
|
+
|
895
|
+
it "should accept a custom qualifier" do
|
896
|
+
m = @m.load(:id => 111, :x => 2, :y => 3)
|
897
|
+
m.qualified_pk_hash(:foo).should == {Sequel.qualify(:foo, :id) => 111}
|
898
|
+
end
|
899
|
+
|
900
|
+
it "should return a hash with the primary key value for custom primary key" do
|
901
|
+
@m.set_primary_key :x
|
902
|
+
m = @m.load(:id => 111, :x => 2, :y => 3)
|
903
|
+
m.qualified_pk_hash.should == {Sequel.qualify(:items, :x) => 2}
|
904
|
+
end
|
905
|
+
|
906
|
+
it "should return a hash with the primary key values for composite primary key" do
|
907
|
+
@m.set_primary_key [:y, :x]
|
908
|
+
m = @m.load(:id => 111, :x => 2, :y => 3)
|
909
|
+
m.qualified_pk_hash.should == {Sequel.qualify(:items, :y) => 3, Sequel.qualify(:items, :x) => 2}
|
910
|
+
end
|
911
|
+
|
912
|
+
it "should raise if no primary key" do
|
913
|
+
@m.set_primary_key nil
|
914
|
+
m = @m.new(:id => 111, :x => 2, :y => 3)
|
915
|
+
proc{m.qualified_pk_hash}.should raise_error(Sequel::Error)
|
916
|
+
|
917
|
+
@m.no_primary_key
|
918
|
+
m = @m.new(:id => 111, :x => 2, :y => 3)
|
919
|
+
proc{m.qualified_pk_hash}.should raise_error(Sequel::Error)
|
881
920
|
end
|
882
921
|
end
|
883
922
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sequel
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 4.
|
4
|
+
version: 4.16.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jeremy Evans
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-11-01 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: The Database Toolkit for Ruby
|
14
14
|
email: code@jeremyevans.net
|
@@ -132,6 +132,7 @@ extra_rdoc_files:
|
|
132
132
|
- doc/release_notes/4.13.0.txt
|
133
133
|
- doc/release_notes/4.14.0.txt
|
134
134
|
- doc/release_notes/4.15.0.txt
|
135
|
+
- doc/release_notes/4.16.0.txt
|
135
136
|
files:
|
136
137
|
- CHANGELOG
|
137
138
|
- MIT-LICENSE
|
@@ -234,6 +235,7 @@ files:
|
|
234
235
|
- doc/release_notes/4.13.0.txt
|
235
236
|
- doc/release_notes/4.14.0.txt
|
236
237
|
- doc/release_notes/4.15.0.txt
|
238
|
+
- doc/release_notes/4.16.0.txt
|
237
239
|
- doc/release_notes/4.2.0.txt
|
238
240
|
- doc/release_notes/4.3.0.txt
|
239
241
|
- doc/release_notes/4.4.0.txt
|