sequel 4.2.0 → 4.3.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.
- checksums.yaml +4 -4
- data/CHANGELOG +28 -0
- data/doc/extensions.rdoc +84 -0
- data/doc/model_plugins.rdoc +270 -0
- data/doc/release_notes/4.3.0.txt +40 -0
- data/doc/testing.rdoc +3 -0
- data/lib/sequel/adapters/jdbc/as400.rb +4 -0
- data/lib/sequel/adapters/shared/mysql.rb +6 -1
- data/lib/sequel/adapters/shared/postgres.rb +2 -0
- data/lib/sequel/ast_transformer.rb +2 -0
- data/lib/sequel/extensions/error_sql.rb +71 -0
- data/lib/sequel/extensions/migration.rb +0 -1
- data/lib/sequel/extensions/pagination.rb +6 -2
- data/lib/sequel/extensions/pg_array.rb +12 -5
- data/lib/sequel/extensions/pg_hstore.rb +5 -3
- data/lib/sequel/extensions/pg_inet.rb +3 -3
- data/lib/sequel/extensions/pg_interval.rb +3 -3
- data/lib/sequel/extensions/pg_json.rb +3 -3
- data/lib/sequel/extensions/pg_range.rb +3 -3
- data/lib/sequel/extensions/pg_row.rb +3 -3
- data/lib/sequel/extensions/server_block.rb +11 -3
- data/lib/sequel/plugins/rcte_tree.rb +59 -39
- data/lib/sequel/plugins/tree.rb +13 -6
- data/lib/sequel/sql.rb +1 -1
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/postgres_spec.rb +17 -0
- data/spec/core/dataset_spec.rb +14 -0
- data/spec/core/schema_spec.rb +1 -0
- data/spec/extensions/error_sql_spec.rb +20 -0
- data/spec/extensions/migration_spec.rb +15 -0
- data/spec/extensions/pagination_spec.rb +19 -0
- data/spec/extensions/pg_array_spec.rb +3 -2
- data/spec/extensions/rcte_tree_spec.rb +135 -0
- data/spec/extensions/tree_spec.rb +130 -0
- data/spec/integration/database_test.rb +5 -0
- data/spec/integration/dataset_test.rb +4 -0
- data/spec/integration/plugin_test.rb +163 -177
- data/spec/integration/spec_helper.rb +4 -0
- metadata +10 -2
@@ -31,6 +31,10 @@ module Sequel
|
|
31
31
|
|
32
32
|
private
|
33
33
|
|
34
|
+
def disconnect_error?(exception, opts)
|
35
|
+
super || exception.message =~ /\AThe connection does not exist\./
|
36
|
+
end
|
37
|
+
|
34
38
|
# Use JDBC connection's setAutoCommit to false to start transactions
|
35
39
|
def begin_transaction(conn, opts=OPTS)
|
36
40
|
set_transaction_isolation(conn, opts)
|
@@ -564,6 +564,7 @@ module Sequel
|
|
564
564
|
BACKSLASH_RE = /\\/.freeze
|
565
565
|
QUAD_BACKSLASH = "\\\\\\\\".freeze
|
566
566
|
BLOB_START = "0x".freeze
|
567
|
+
EMPTY_BLOB = "''".freeze
|
567
568
|
HSTAR = "H*".freeze
|
568
569
|
|
569
570
|
include Sequel::Dataset::Replace
|
@@ -873,7 +874,11 @@ module Sequel
|
|
873
874
|
|
874
875
|
# MySQL uses a preceding X for hex escaping strings
|
875
876
|
def literal_blob_append(sql, v)
|
876
|
-
|
877
|
+
if v.empty?
|
878
|
+
sql << EMPTY_BLOB
|
879
|
+
else
|
880
|
+
sql << BLOB_START << v.unpack(HSTAR).first
|
881
|
+
end
|
877
882
|
end
|
878
883
|
|
879
884
|
# Use 0 for false on MySQL
|
@@ -66,6 +66,8 @@ module Sequel
|
|
66
66
|
SQL::JoinUsingClause.new(v(o.using), o.join_type, v(o.table), v(o.table_alias))
|
67
67
|
when SQL::JoinClause
|
68
68
|
SQL::JoinClause.new(o.join_type, v(o.table), v(o.table_alias))
|
69
|
+
when SQL::DelayedEvaluation
|
70
|
+
SQL::DelayedEvaluation.new(lambda{v(o.callable.call)})
|
69
71
|
when SQL::Wrapper
|
70
72
|
SQL::Wrapper.new(v(o.value))
|
71
73
|
else
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# The error_sql extension adds a DatabaseError#sql method
|
2
|
+
# that you can use to get the sql that caused the error
|
3
|
+
# to be raised.
|
4
|
+
#
|
5
|
+
# begin
|
6
|
+
# DB.run "Invalid SQL"
|
7
|
+
# rescue => e
|
8
|
+
# puts e.sql # "Invalid SQL"
|
9
|
+
# end
|
10
|
+
#
|
11
|
+
# On some databases, the error message contains part or all
|
12
|
+
# of the SQL used, but on other databases, none of the SQL
|
13
|
+
# used is displayed in the error message, so it can be
|
14
|
+
# difficult to track down what is causing the error without
|
15
|
+
# using a logger. This extension should hopefully make
|
16
|
+
# debugging easier on databases that have bad error
|
17
|
+
# messages.
|
18
|
+
#
|
19
|
+
# This extension may not work correctly in the following cases:
|
20
|
+
#
|
21
|
+
# * log_yield is not used when executing the query.
|
22
|
+
# * The underlying exception is frozen or reused.
|
23
|
+
# * The underlying exception doesn't correctly record instance
|
24
|
+
# variables set on it (seems to happen on JRuby when underlying
|
25
|
+
# exception objects are Java exceptions).
|
26
|
+
#
|
27
|
+
# To load the extension into the database:
|
28
|
+
#
|
29
|
+
# DB.extension :error_sql
|
30
|
+
|
31
|
+
module Sequel
|
32
|
+
class DatabaseError
|
33
|
+
# Get the SQL code that caused this error to be raised.
|
34
|
+
def sql
|
35
|
+
# We store the error SQL in the wrapped exception instead of the
|
36
|
+
# current exception, since when the error SQL is originally associated
|
37
|
+
# with the wrapped exception, the current exception doesn't exist. It's
|
38
|
+
# possible to copy the error SQL into the current exception, but there
|
39
|
+
# doesn't seem to be a reason to do that.
|
40
|
+
wrapped_exception.instance_variable_get(:@sequel_error_sql) if wrapped_exception
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
module ErrorSQL
|
45
|
+
# Store the SQL related to the exception with the exception, so it
|
46
|
+
# is available for DatabaseError#sql later.
|
47
|
+
def log_exception(exception, message)
|
48
|
+
exception.instance_variable_set(:@sequel_error_sql, message)
|
49
|
+
super
|
50
|
+
end
|
51
|
+
|
52
|
+
# If there are no loggers for this database and an exception is raised
|
53
|
+
# store the SQL related to the exception with the exception, so it
|
54
|
+
# is available for DatabaseError#sql later.
|
55
|
+
def log_yield(sql, args=nil)
|
56
|
+
if @loggers.empty?
|
57
|
+
begin
|
58
|
+
yield
|
59
|
+
rescue => e
|
60
|
+
sql = "#{sql}; #{args.inspect}" if args
|
61
|
+
e.instance_variable_set(:@sequel_error_sql, sql)
|
62
|
+
raise
|
63
|
+
end
|
64
|
+
else
|
65
|
+
super
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
Database.register_extension(:error_sql, ErrorSQL)
|
71
|
+
end
|
@@ -404,7 +404,6 @@ module Sequel
|
|
404
404
|
self
|
405
405
|
end
|
406
406
|
end
|
407
|
-
private_class_method :migrator_class
|
408
407
|
|
409
408
|
# The column to use to hold the migration version number for integer migrations or
|
410
409
|
# filename for timestamp migrations (defaults to :version for integer migrations and
|
@@ -28,9 +28,11 @@ module Sequel
|
|
28
28
|
end
|
29
29
|
|
30
30
|
# Yields a paginated dataset for each page and returns the receiver. Does
|
31
|
-
# a count to find the total number of records for this dataset.
|
31
|
+
# a count to find the total number of records for this dataset. Returns
|
32
|
+
# an enumerator if no block is given.
|
32
33
|
def each_page(page_size)
|
33
34
|
raise(Error, "You cannot paginate a dataset that already has a limit") if @opts[:limit]
|
35
|
+
return to_enum(:each_page, page_size) unless block_given?
|
34
36
|
record_count = count
|
35
37
|
total_pages = (record_count / page_size.to_f).ceil
|
36
38
|
(1..total_pages).each{|page_no| yield paginate(page_no, page_size, record_count)}
|
@@ -47,7 +49,8 @@ module Sequel
|
|
47
49
|
attr_accessor :page_size
|
48
50
|
|
49
51
|
# The number of pages in the dataset before pagination, of which
|
50
|
-
# this paginated dataset is one.
|
52
|
+
# this paginated dataset is one. Empty datasets are considered
|
53
|
+
# to have a single page.
|
51
54
|
attr_accessor :page_count
|
52
55
|
|
53
56
|
# The current page of the dataset, starting at 1 and not 0.
|
@@ -107,6 +110,7 @@ module Sequel
|
|
107
110
|
@page_size = page_size
|
108
111
|
@pagination_record_count = record_count
|
109
112
|
@page_count = (record_count / page_size.to_f).ceil
|
113
|
+
@page_count = 1 if @page_count == 0
|
110
114
|
self
|
111
115
|
end
|
112
116
|
end
|
@@ -38,8 +38,9 @@
|
|
38
38
|
#
|
39
39
|
# If you are not using the native postgres adapter and are using array
|
40
40
|
# types as model column values you probably should use the
|
41
|
-
#
|
42
|
-
# array
|
41
|
+
# typecast_on_load plugin if the column values are returned as a
|
42
|
+
# regular array, and the pg_typecast_on_load plugin if the column
|
43
|
+
# values are returned as a string.
|
43
44
|
#
|
44
45
|
# This extension by default includes handlers for array types for
|
45
46
|
# all scalar types that the native postgres adapter handles. It
|
@@ -109,6 +110,7 @@ module Sequel
|
|
109
110
|
|
110
111
|
ARRAY = "ARRAY".freeze
|
111
112
|
DOUBLE_COLON = '::'.freeze
|
113
|
+
EMPTY_ARRAY = "'{}'".freeze
|
112
114
|
EMPTY_BRACKET = '[]'.freeze
|
113
115
|
OPEN_BRACKET = '['.freeze
|
114
116
|
CLOSE_BRACKET = ']'.freeze
|
@@ -517,9 +519,14 @@ module Sequel
|
|
517
519
|
# If the receiver has a type, add a cast to the
|
518
520
|
# database array type.
|
519
521
|
def sql_literal_append(ds, sql)
|
520
|
-
|
521
|
-
|
522
|
-
|
522
|
+
at = array_type
|
523
|
+
if empty? && at
|
524
|
+
sql << EMPTY_ARRAY
|
525
|
+
else
|
526
|
+
sql << ARRAY
|
527
|
+
_literal_append(sql, ds, to_a)
|
528
|
+
end
|
529
|
+
if at
|
523
530
|
sql << DOUBLE_COLON << at.to_s << EMPTY_BRACKET
|
524
531
|
end
|
525
532
|
end
|
@@ -75,9 +75,11 @@
|
|
75
75
|
#
|
76
76
|
# DB.extension :pg_hstore
|
77
77
|
#
|
78
|
-
# If you are not using the native postgres adapter
|
79
|
-
#
|
80
|
-
#
|
78
|
+
# If you are not using the native postgres adapter and are using hstore
|
79
|
+
# types as model column values you probably should use the
|
80
|
+
# typecast_on_load plugin if the column values are returned as a
|
81
|
+
# hash, and the pg_typecast_on_load plugin if the column
|
82
|
+
# values are returned as a string.
|
81
83
|
#
|
82
84
|
# This extension requires the delegate and strscan libraries.
|
83
85
|
|
@@ -10,9 +10,9 @@
|
|
10
10
|
#
|
11
11
|
# DB.extension :pg_inet
|
12
12
|
#
|
13
|
-
# If you are not using the native postgres adapter
|
14
|
-
#
|
15
|
-
#
|
13
|
+
# If you are not using the native postgres adapter and are using inet/cidr
|
14
|
+
# types as model column values you probably should use the
|
15
|
+
# pg_typecast_on_load plugin if the column values are returned as a string.
|
16
16
|
#
|
17
17
|
# This extension integrates with the pg_array extension. If you plan
|
18
18
|
# to use the inet[] or cidr[] types, load the pg_array extension before
|
@@ -15,9 +15,9 @@
|
|
15
15
|
#
|
16
16
|
# DB.extension :pg_interval
|
17
17
|
#
|
18
|
-
# If you are not using the native postgres adapter
|
19
|
-
#
|
20
|
-
#
|
18
|
+
# If you are not using the native postgres adapter and are using interval
|
19
|
+
# types as model column values you probably should use the
|
20
|
+
# pg_typecast_on_load plugin if the column values are returned as a string.
|
21
21
|
#
|
22
22
|
# This extension integrates with the pg_array extension. If you plan
|
23
23
|
# to use arrays of interval types, load the pg_array extension before the
|
@@ -42,9 +42,9 @@
|
|
42
42
|
#
|
43
43
|
# DB.extension :pg_json
|
44
44
|
#
|
45
|
-
# If you are not using the native postgres adapter
|
46
|
-
#
|
47
|
-
#
|
45
|
+
# If you are not using the native postgres adapter and are using json
|
46
|
+
# types as model column values you probably should use the
|
47
|
+
# pg_typecast_on_load plugin if the column values are returned as a string.
|
48
48
|
#
|
49
49
|
# This extension integrates with the pg_array extension. If you plan
|
50
50
|
# to use the json[] type, load the pg_array extension before the
|
@@ -45,9 +45,9 @@
|
|
45
45
|
#
|
46
46
|
# DB.extension :pg_range
|
47
47
|
#
|
48
|
-
# If you are not using the native postgres adapter
|
49
|
-
#
|
50
|
-
#
|
48
|
+
# If you are not using the native postgres adapter and are using range
|
49
|
+
# types as model column values you probably should use the
|
50
|
+
# pg_typecast_on_load plugin if the column values are returned as a string.
|
51
51
|
#
|
52
52
|
# This extension integrates with the pg_array extension. If you plan
|
53
53
|
# to use arrays of range types, load the pg_array extension before the
|
@@ -74,9 +74,9 @@
|
|
74
74
|
# DB.conversion_procs.select{|k,v| v.is_a?(Sequel::Postgres::PGRow::Parser) && \
|
75
75
|
# v.converter && (v.converter.name.nil? || v.converter.name == '') }.map{|k,v| v}
|
76
76
|
#
|
77
|
-
# If you are not using the native postgres adapter
|
78
|
-
#
|
79
|
-
#
|
77
|
+
# If you are not using the native postgres adapter and are using composite types
|
78
|
+
# types as model column values you probably should use the
|
79
|
+
# pg_typecast_on_load plugin if the column values are returned as a string.
|
80
80
|
#
|
81
81
|
# This extension requires both the strscan and delegate libraries.
|
82
82
|
|
@@ -25,9 +25,17 @@
|
|
25
25
|
# end
|
26
26
|
# DB[:a].all # Uses default
|
27
27
|
#
|
28
|
-
# Note
|
29
|
-
# server
|
30
|
-
#
|
28
|
+
# Note that if you pass the nil, :default, or :read_only server/shard
|
29
|
+
# names to Dataset#server inside a with_server block, they will be
|
30
|
+
# ignored and the server/shard given to with_server will be used:
|
31
|
+
#
|
32
|
+
# DB.with_server(:shard1) do
|
33
|
+
# DB[:a].all # Uses shard1
|
34
|
+
# DB[:a].server(:shard2).all # Uses shard2
|
35
|
+
# DB[:a].server(nil).all # Uses shard1
|
36
|
+
# DB[:a].server(:default).all # Uses shard1
|
37
|
+
# DB[:a].server(:read_only).all # Uses shard1
|
38
|
+
# end
|
31
39
|
|
32
40
|
module Sequel
|
33
41
|
module ServerBlock
|
@@ -102,13 +102,8 @@ module Sequel
|
|
102
102
|
|
103
103
|
key = opts[:key] ||= :parent_id
|
104
104
|
prkey = opts[:primary_key] ||= model.primary_key
|
105
|
-
|
106
|
-
parent = opts.merge(opts.fetch(:parent, {})).fetch(:name, :parent)
|
107
|
-
childrena = opts.merge(opts.fetch(:children, {})).fetch(:name, :children)
|
108
|
-
|
109
105
|
ka = opts[:key_alias] ||= :x_root_x
|
110
106
|
t = opts[:cte_name] ||= :t
|
111
|
-
opts[:reciprocal] = nil
|
112
107
|
c_all = if model.dataset.recursive_cte_requires_column_aliases?
|
113
108
|
# Work around Oracle/ruby-oci8 bug that returns integers as BigDecimals in recursive queries.
|
114
109
|
conv_bd = model.db.database_type == :oracle
|
@@ -119,13 +114,42 @@ module Sequel
|
|
119
114
|
[SQL::ColumnAll.new(model.table_name)]
|
120
115
|
end
|
121
116
|
|
117
|
+
bd_conv = lambda{|v| conv_bd && v.is_a?(BigDecimal) ? v.to_i : v}
|
118
|
+
|
119
|
+
key_array = Array(key)
|
120
|
+
prkey_array = Array(prkey)
|
121
|
+
if key.is_a?(Array)
|
122
|
+
key_conv = lambda{|m| key_array.map{|k| m[k]}}
|
123
|
+
key_present = lambda{|m| key_conv[m].all?}
|
124
|
+
prkey_conv = lambda{|m| prkey_array.map{|k| m[k]}}
|
125
|
+
key_aliases = (0...key_array.length).map{|i| :"#{ka}_#{i}"}
|
126
|
+
ka_conv = lambda{|m| key_aliases.map{|k| m[k]}}
|
127
|
+
ancestor_base_case_columns = prkey_array.zip(key_aliases).map{|k, ka_| SQL::AliasedExpression.new(k, ka_)} + c_all
|
128
|
+
descendant_base_case_columns = key_array.zip(key_aliases).map{|k, ka_| SQL::AliasedExpression.new(k, ka_)} + c_all
|
129
|
+
recursive_case_columns = prkey_array.zip(key_aliases).map{|k, ka_| SQL::QualifiedIdentifier.new(t, ka_)} + c_all
|
130
|
+
extract_key_alias = lambda{|m| key_aliases.map{|ka_| bd_conv[m.values.delete(ka_)]}}
|
131
|
+
else
|
132
|
+
key_present = key_conv = lambda{|m| m[key]}
|
133
|
+
prkey_conv = lambda{|m| m[prkey]}
|
134
|
+
key_aliases = [ka]
|
135
|
+
ka_conv = lambda{|m| m[ka]}
|
136
|
+
ancestor_base_case_columns = [SQL::AliasedExpression.new(prkey, ka)] + c_all
|
137
|
+
descendant_base_case_columns = [SQL::AliasedExpression.new(key, ka)] + c_all
|
138
|
+
recursive_case_columns = [SQL::QualifiedIdentifier.new(t, ka)] + c_all
|
139
|
+
extract_key_alias = lambda{|m| bd_conv[m.values.delete(ka)]}
|
140
|
+
end
|
141
|
+
|
142
|
+
parent = opts.merge(opts.fetch(:parent, {})).fetch(:name, :parent)
|
143
|
+
childrena = opts.merge(opts.fetch(:children, {})).fetch(:name, :children)
|
144
|
+
|
145
|
+
opts[:reciprocal] = nil
|
122
146
|
a = opts.merge(opts.fetch(:ancestors, {}))
|
123
147
|
ancestors = a.fetch(:name, :ancestors)
|
124
148
|
a[:read_only] = true unless a.has_key?(:read_only)
|
125
149
|
a[:eager_loader_key] = key
|
126
150
|
a[:dataset] ||= proc do
|
127
|
-
base_ds = model.filter(
|
128
|
-
recursive_ds = model.join(t,
|
151
|
+
base_ds = model.filter(prkey_array.zip(key_array.map{|k| send(k)}))
|
152
|
+
recursive_ds = model.join(t, key_array.zip(prkey_array))
|
129
153
|
if c = a[:conditions]
|
130
154
|
(base_ds, recursive_ds) = [base_ds, recursive_ds].collect do |ds|
|
131
155
|
(c.is_a?(Array) && !Sequel.condition_specifier?(c)) ? ds.filter(*c) : ds.filter(c)
|
@@ -140,14 +164,14 @@ module Sequel
|
|
140
164
|
aal = Array(a[:after_load])
|
141
165
|
aal << proc do |m, ancs|
|
142
166
|
unless m.associations.has_key?(parent)
|
143
|
-
parent_map = {m
|
167
|
+
parent_map = {prkey_conv[m]=>m}
|
144
168
|
child_map = {}
|
145
|
-
child_map[m
|
169
|
+
child_map[key_conv[m]] = m if key_present[m]
|
146
170
|
m.associations[parent] = nil
|
147
171
|
ancs.each do |obj|
|
148
172
|
obj.associations[parent] = nil
|
149
|
-
parent_map[obj
|
150
|
-
if ok = obj
|
173
|
+
parent_map[prkey_conv[obj]] = obj
|
174
|
+
if ok = key_conv[obj]
|
151
175
|
child_map[ok] = obj
|
152
176
|
end
|
153
177
|
end
|
@@ -164,16 +188,16 @@ module Sequel
|
|
164
188
|
parent_map = {}
|
165
189
|
children_map = {}
|
166
190
|
eo[:rows].each do |obj|
|
167
|
-
parent_map[obj
|
168
|
-
(children_map[obj
|
191
|
+
parent_map[prkey_conv[obj]] = obj
|
192
|
+
(children_map[key_conv[obj]] ||= []) << obj
|
169
193
|
obj.associations[ancestors] = []
|
170
194
|
obj.associations[parent] = nil
|
171
195
|
end
|
172
196
|
r = model.association_reflection(ancestors)
|
173
197
|
base_case = model.filter(prkey=>id_map.keys).
|
174
|
-
select(
|
175
|
-
recursive_case = model.join(t,
|
176
|
-
select(
|
198
|
+
select(*ancestor_base_case_columns)
|
199
|
+
recursive_case = model.join(t, key_array.zip(prkey_array)).
|
200
|
+
select(*recursive_case_columns)
|
177
201
|
if c = r[:conditions]
|
178
202
|
(base_case, recursive_case) = [base_case, recursive_case].collect do |ds|
|
179
203
|
(c.is_a?(Array) && !Sequel.condition_specifier?(c)) ? ds.filter(*c) : ds.filter(c)
|
@@ -184,26 +208,24 @@ module Sequel
|
|
184
208
|
model.from(SQL::AliasedExpression.new(t, table_alias)).
|
185
209
|
with_recursive(t, base_case,
|
186
210
|
recursive_case,
|
187
|
-
:args=>((
|
211
|
+
:args=>((key_aliases + col_aliases) if col_aliases)),
|
188
212
|
r.select,
|
189
213
|
eo[:associations], eo)
|
190
214
|
elds = elds.select_append(ka) unless elds.opts[:select] == nil
|
191
215
|
elds.all do |obj|
|
192
|
-
opk = obj
|
216
|
+
opk = prkey_conv[obj]
|
193
217
|
if parent_map.has_key?(opk)
|
194
218
|
if idm_obj = parent_map[opk]
|
195
|
-
idm_obj.values[
|
219
|
+
key_aliases.each{|ka_| idm_obj.values[ka_] = obj.values[ka_]}
|
196
220
|
obj = idm_obj
|
197
221
|
end
|
198
222
|
else
|
199
223
|
obj.associations[parent] = nil
|
200
224
|
parent_map[opk] = obj
|
201
|
-
(children_map[obj
|
225
|
+
(children_map[key_conv[obj]] ||= []) << obj
|
202
226
|
end
|
203
227
|
|
204
|
-
|
205
|
-
kv = kv.to_i if conv_bd && kv.is_a?(BigDecimal)
|
206
|
-
if roots = id_map[kv]
|
228
|
+
if roots = id_map[extract_key_alias[obj]]
|
207
229
|
roots.each do |root|
|
208
230
|
root.associations[ancestors] << obj
|
209
231
|
end
|
@@ -224,8 +246,8 @@ module Sequel
|
|
224
246
|
d[:read_only] = true unless d.has_key?(:read_only)
|
225
247
|
la = d[:level_alias] ||= :x_level_x
|
226
248
|
d[:dataset] ||= proc do
|
227
|
-
base_ds = model.filter(
|
228
|
-
recursive_ds = model.join(t,
|
249
|
+
base_ds = model.filter(key_array.zip(prkey_array.map{|k| send(k)}))
|
250
|
+
recursive_ds = model.join(t, prkey_array.zip(key_array))
|
229
251
|
if c = d[:conditions]
|
230
252
|
(base_ds, recursive_ds) = [base_ds, recursive_ds].collect do |ds|
|
231
253
|
(c.is_a?(Array) && !Sequel.condition_specifier?(c)) ? ds.filter(*c) : ds.filter(c)
|
@@ -240,15 +262,15 @@ module Sequel
|
|
240
262
|
dal = Array(d[:after_load])
|
241
263
|
dal << proc do |m, descs|
|
242
264
|
unless m.associations.has_key?(childrena)
|
243
|
-
parent_map = {m
|
265
|
+
parent_map = {prkey_conv[m]=>m}
|
244
266
|
children_map = {}
|
245
267
|
m.associations[childrena] = []
|
246
268
|
descs.each do |obj|
|
247
269
|
obj.associations[childrena] = []
|
248
|
-
if opk = obj
|
270
|
+
if opk = prkey_conv[obj]
|
249
271
|
parent_map[opk] = obj
|
250
272
|
end
|
251
|
-
if ok = obj
|
273
|
+
if ok = key_conv[obj]
|
252
274
|
(children_map[ok] ||= []) << obj
|
253
275
|
end
|
254
276
|
end
|
@@ -264,15 +286,15 @@ module Sequel
|
|
264
286
|
parent_map = {}
|
265
287
|
children_map = {}
|
266
288
|
eo[:rows].each do |obj|
|
267
|
-
parent_map[obj
|
289
|
+
parent_map[prkey_conv[obj]] = obj
|
268
290
|
obj.associations[descendants] = []
|
269
291
|
obj.associations[childrena] = []
|
270
292
|
end
|
271
293
|
r = model.association_reflection(descendants)
|
272
294
|
base_case = model.filter(key=>id_map.keys).
|
273
|
-
select(
|
274
|
-
recursive_case = model.join(t,
|
275
|
-
select(
|
295
|
+
select(*descendant_base_case_columns)
|
296
|
+
recursive_case = model.join(t, prkey_array.zip(key_array)).
|
297
|
+
select(*recursive_case_columns)
|
276
298
|
if c = r[:conditions]
|
277
299
|
(base_case, recursive_case) = [base_case, recursive_case].collect do |ds|
|
278
300
|
(c.is_a?(Array) && !Sequel.condition_specifier?(c)) ? ds.filter(*c) : ds.filter(c)
|
@@ -288,7 +310,7 @@ module Sequel
|
|
288
310
|
table_alias = model.dataset.schema_and_table(model.table_name)[1].to_sym
|
289
311
|
elds = model.eager_loading_dataset(r,
|
290
312
|
model.from(SQL::AliasedExpression.new(t, table_alias)).with_recursive(t, base_case, recursive_case,
|
291
|
-
:args=>((
|
313
|
+
:args=>((key_aliases + col_aliases + (level ? [la] : [])) if col_aliases)),
|
292
314
|
r.select,
|
293
315
|
associations, eo)
|
294
316
|
elds = elds.select_append(ka) unless elds.opts[:select] == nil
|
@@ -297,10 +319,10 @@ module Sequel
|
|
297
319
|
no_cache = no_cache_level == obj.values.delete(la)
|
298
320
|
end
|
299
321
|
|
300
|
-
opk = obj
|
322
|
+
opk = prkey_conv[obj]
|
301
323
|
if parent_map.has_key?(opk)
|
302
324
|
if idm_obj = parent_map[opk]
|
303
|
-
idm_obj.values[
|
325
|
+
key_aliases.each{|ka_| idm_obj.values[ka_] = obj.values[ka_]}
|
304
326
|
obj = idm_obj
|
305
327
|
end
|
306
328
|
else
|
@@ -308,13 +330,11 @@ module Sequel
|
|
308
330
|
parent_map[opk] = obj
|
309
331
|
end
|
310
332
|
|
311
|
-
|
312
|
-
kv = kv.to_i if conv_bd && kv.is_a?(BigDecimal)
|
313
|
-
if root = id_map[kv].first
|
333
|
+
if root = id_map[extract_key_alias[obj]].first
|
314
334
|
root.associations[descendants] << obj
|
315
335
|
end
|
316
336
|
|
317
|
-
(children_map[obj
|
337
|
+
(children_map[key_conv[obj]] ||= []) << obj
|
318
338
|
end
|
319
339
|
children_map.each do |parent_id, objs|
|
320
340
|
parent_map[parent_id].associations[childrena] = objs.uniq
|