datamapper 0.1.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +65 -0
- data/README +193 -1
- data/do_performance.rb +153 -0
- data/environment.rb +45 -0
- data/example.rb +119 -22
- data/lib/data_mapper.rb +36 -16
- data/lib/data_mapper/adapters/abstract_adapter.rb +8 -0
- data/lib/data_mapper/adapters/data_object_adapter.rb +360 -0
- data/lib/data_mapper/adapters/mysql_adapter.rb +30 -179
- data/lib/data_mapper/adapters/postgresql_adapter.rb +90 -199
- data/lib/data_mapper/adapters/sql/coersion.rb +32 -3
- data/lib/data_mapper/adapters/sql/commands/conditions.rb +97 -128
- data/lib/data_mapper/adapters/sql/commands/load_command.rb +234 -231
- data/lib/data_mapper/adapters/sql/commands/loader.rb +99 -0
- data/lib/data_mapper/adapters/sql/mappings/associations_set.rb +30 -0
- data/lib/data_mapper/adapters/sql/mappings/column.rb +68 -6
- data/lib/data_mapper/adapters/sql/mappings/schema.rb +6 -3
- data/lib/data_mapper/adapters/sql/mappings/table.rb +71 -42
- data/lib/data_mapper/adapters/sql/quoting.rb +8 -2
- data/lib/data_mapper/adapters/sqlite3_adapter.rb +32 -201
- data/lib/data_mapper/associations.rb +21 -7
- data/lib/data_mapper/associations/belongs_to_association.rb +96 -80
- data/lib/data_mapper/associations/has_and_belongs_to_many_association.rb +158 -67
- data/lib/data_mapper/associations/has_many_association.rb +96 -78
- data/lib/data_mapper/associations/has_n_association.rb +64 -0
- data/lib/data_mapper/associations/has_one_association.rb +49 -79
- data/lib/data_mapper/associations/reference.rb +47 -0
- data/lib/data_mapper/base.rb +216 -50
- data/lib/data_mapper/callbacks.rb +71 -24
- data/lib/data_mapper/{session.rb → context.rb} +20 -8
- data/lib/data_mapper/database.rb +176 -45
- data/lib/data_mapper/embedded_value.rb +65 -0
- data/lib/data_mapper/identity_map.rb +12 -4
- data/lib/data_mapper/support/active_record_impersonation.rb +12 -8
- data/lib/data_mapper/support/enumerable.rb +8 -0
- data/lib/data_mapper/support/serialization.rb +13 -0
- data/lib/data_mapper/support/string.rb +1 -12
- data/lib/data_mapper/support/symbol.rb +3 -0
- data/lib/data_mapper/validations/unique_validator.rb +1 -2
- data/lib/data_mapper/validations/validation_helper.rb +18 -1
- data/performance.rb +109 -34
- data/plugins/can_has_sphinx/LICENSE +23 -0
- data/plugins/can_has_sphinx/README +4 -0
- data/plugins/can_has_sphinx/REVISION +1 -0
- data/plugins/can_has_sphinx/Rakefile +22 -0
- data/plugins/can_has_sphinx/init.rb +1 -0
- data/plugins/can_has_sphinx/install.rb +1 -0
- data/plugins/can_has_sphinx/lib/acts_as_sphinx.rb +123 -0
- data/plugins/can_has_sphinx/lib/sphinx.rb +460 -0
- data/plugins/can_has_sphinx/scripts/sphinx.sh +47 -0
- data/plugins/can_has_sphinx/tasks/acts_as_sphinx_tasks.rake +41 -0
- data/plugins/dataobjects/REVISION +1 -0
- data/plugins/dataobjects/Rakefile +7 -0
- data/plugins/dataobjects/do.rb +246 -0
- data/plugins/dataobjects/do_mysql.rb +179 -0
- data/plugins/dataobjects/do_postgres.rb +181 -0
- data/plugins/dataobjects/do_sqlite3.rb +153 -0
- data/plugins/dataobjects/spec/do_spec.rb +150 -0
- data/plugins/dataobjects/spec/spec_helper.rb +81 -0
- data/plugins/dataobjects/swig_mysql/do_mysql.bundle +0 -0
- data/plugins/dataobjects/swig_mysql/extconf.rb +33 -0
- data/plugins/dataobjects/swig_mysql/mysql_c.c +18800 -0
- data/plugins/dataobjects/swig_mysql/mysql_c.i +8 -0
- data/plugins/dataobjects/swig_mysql/mysql_supp.i +46 -0
- data/plugins/dataobjects/swig_postgres/Makefile +146 -0
- data/plugins/dataobjects/swig_postgres/extconf.rb +29 -0
- data/plugins/dataobjects/swig_postgres/postgres_c.bundle +0 -0
- data/plugins/dataobjects/swig_postgres/postgres_c.c +8185 -0
- data/plugins/dataobjects/swig_postgres/postgres_c.i +73 -0
- data/plugins/dataobjects/swig_sqlite/db +0 -0
- data/plugins/dataobjects/swig_sqlite/extconf.rb +9 -0
- data/plugins/dataobjects/swig_sqlite/sqlite3_c.c +4725 -0
- data/plugins/dataobjects/swig_sqlite/sqlite_c.i +168 -0
- data/rakefile.rb +45 -23
- data/spec/acts_as_tree_spec.rb +39 -0
- data/spec/associations_spec.rb +220 -0
- data/spec/attributes_spec.rb +15 -0
- data/spec/base_spec.rb +44 -0
- data/spec/callbacks_spec.rb +45 -0
- data/spec/can_has_sphinx.rb +6 -0
- data/spec/coersion_spec.rb +34 -0
- data/spec/conditions_spec.rb +49 -0
- data/spec/conversions_to_yaml_spec.rb +17 -0
- data/spec/count_command_spec.rb +11 -0
- data/spec/delete_command_spec.rb +1 -1
- data/spec/embedded_value_spec.rb +23 -0
- data/spec/fixtures/animals_exhibits.yaml +2 -0
- data/spec/fixtures/people.yaml +18 -1
- data/spec/{legacy.rb → legacy_spec.rb} +3 -3
- data/spec/load_command_spec.rb +157 -20
- data/spec/magic_columns_spec.rb +9 -0
- data/spec/mock_adapter.rb +20 -0
- data/spec/models/animal.rb +1 -1
- data/spec/models/animals_exhibit.rb +6 -0
- data/spec/models/exhibit.rb +2 -0
- data/spec/models/person.rb +26 -1
- data/spec/models/project.rb +19 -0
- data/spec/models/sales_person.rb +1 -0
- data/spec/models/section.rb +6 -0
- data/spec/models/zoo.rb +3 -1
- data/spec/query_spec.rb +9 -0
- data/spec/save_command_spec.rb +65 -1
- data/spec/schema_spec.rb +89 -0
- data/spec/single_table_inheritance_spec.rb +27 -0
- data/spec/spec_helper.rb +9 -55
- data/spec/{symbolic_operators.rb → symbolic_operators_spec.rb} +9 -5
- data/spec/{validates_confirmation_of.rb → validates_confirmation_of_spec.rb} +4 -3
- data/spec/{validates_format_of.rb → validates_format_of_spec.rb} +5 -4
- data/spec/{validates_length_of.rb → validates_length_of_spec.rb} +8 -7
- data/spec/{validates_uniqueness_of.rb → validates_uniqueness_of_spec.rb} +7 -10
- data/spec/{validations.rb → validations_spec.rb} +24 -6
- data/tasks/drivers.rb +20 -0
- data/tasks/fixtures.rb +42 -0
- metadata +181 -42
- data/lib/data_mapper/adapters/sql/commands/advanced_load_command.rb +0 -140
- data/lib/data_mapper/adapters/sql/commands/delete_command.rb +0 -113
- data/lib/data_mapper/adapters/sql/commands/save_command.rb +0 -141
- data/lib/data_mapper/adapters/sql/commands/table_exists_command.rb +0 -33
- data/lib/data_mapper/adapters/sql_adapter.rb +0 -163
- data/lib/data_mapper/associations/advanced_has_many_association.rb +0 -55
- data/lib/data_mapper/support/blank_slate.rb +0 -3
- data/lib/data_mapper/support/proc.rb +0 -69
- data/lib/data_mapper/support/struct.rb +0 -26
- data/lib/data_mapper/unit_of_work.rb +0 -38
- data/spec/basic_finder.rb +0 -67
- data/spec/belongs_to.rb +0 -47
- data/spec/has_and_belongs_to_many.rb +0 -25
- data/spec/has_many.rb +0 -34
- data/spec/new_record.rb +0 -24
- data/spec/sub_select.rb +0 -16
- data/spec/support/string_spec.rb +0 -7
@@ -1,3 +1,6 @@
|
|
1
|
+
require 'bigdecimal'
|
2
|
+
require 'bigdecimal/util'
|
3
|
+
|
1
4
|
module DataMapper
|
2
5
|
module Adapters
|
3
6
|
module Sql
|
@@ -8,8 +11,11 @@ module DataMapper
|
|
8
11
|
# it.
|
9
12
|
module Coersion
|
10
13
|
|
11
|
-
|
12
|
-
|
14
|
+
class CoersionError < StandardError
|
15
|
+
end
|
16
|
+
|
17
|
+
TRUE_ALIASES = ['true'.freeze, 'TRUE'.freeze, '1'.freeze]
|
18
|
+
FALSE_ALIASES = [nil, '0'.freeze]
|
13
19
|
|
14
20
|
def self.included(base)
|
15
21
|
base.const_set('TRUE_ALIASES', TRUE_ALIASES.dup)
|
@@ -47,6 +53,18 @@ module DataMapper
|
|
47
53
|
nil
|
48
54
|
end
|
49
55
|
|
56
|
+
def type_cast_decimal(raw_value)
|
57
|
+
return nil if raw_value.blank?
|
58
|
+
raw_value.to_d
|
59
|
+
rescue ArgumentError
|
60
|
+
nil
|
61
|
+
end
|
62
|
+
|
63
|
+
def type_cast_float(raw_value)
|
64
|
+
return nil if raw_value.blank?
|
65
|
+
raw_value.to_f
|
66
|
+
end
|
67
|
+
|
50
68
|
def type_cast_datetime(raw_value)
|
51
69
|
return nil if raw_value.blank?
|
52
70
|
|
@@ -54,7 +72,18 @@ module DataMapper
|
|
54
72
|
when DateTime then raw_value
|
55
73
|
when Date then DateTime.new(raw_value)
|
56
74
|
when String then DateTime::parse(raw_value)
|
57
|
-
else "Can't type-cast #{raw_value.inspect} to a datetime"
|
75
|
+
else raise CoersionError.new("Can't type-cast #{raw_value.inspect} to a datetime")
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def type_cast_date(raw_value)
|
80
|
+
return nil if raw_value.blank?
|
81
|
+
|
82
|
+
case raw_value
|
83
|
+
when Date then raw_value
|
84
|
+
when DateTime, Time then Date::civil(raw_value.year, raw_value.month, raw_value.day)
|
85
|
+
when String then Date::parse(raw_value)
|
86
|
+
else raise CoersionError.new("Can't type-cast #{raw_value.inspect} to a date")
|
58
87
|
end
|
59
88
|
end
|
60
89
|
|
@@ -5,154 +5,123 @@ module DataMapper
|
|
5
5
|
|
6
6
|
class Conditions
|
7
7
|
|
8
|
-
def initialize(adapter, loader)
|
8
|
+
def initialize(adapter, loader, conditions_hash)
|
9
9
|
@adapter, @loader = adapter, loader
|
10
|
-
@
|
10
|
+
@conditions = parse_conditions(conditions_hash)
|
11
11
|
end
|
12
12
|
|
13
|
-
|
14
|
-
|
15
|
-
attr_reader :inner_error
|
16
|
-
|
17
|
-
def initialize(clause, inner_error = nil)
|
18
|
-
@clause = clause
|
19
|
-
@inner_error = inner_error
|
20
|
-
|
21
|
-
message = "Failed to normalize clause: #{clause.inspect}"
|
22
|
-
message << ", Error: #{inner_error.inspect}" unless inner_error.nil?
|
23
|
-
|
24
|
-
super(message)
|
25
|
-
end
|
26
|
-
|
27
|
-
end
|
28
|
-
|
29
|
-
def normalize(clause, collector)
|
30
|
-
case clause
|
31
|
-
when Hash then
|
32
|
-
clause.each_pair do |k,v|
|
33
|
-
if k.kind_of?(Symbol::Operator)
|
34
|
-
if k.type == :select
|
35
|
-
k.options[:class] ||= @loader.klass
|
36
|
-
|
37
|
-
k.options[:select] ||= if k.value.to_s == @adapter[k.options[:class]].default_foreign_key
|
38
|
-
@adapter[k.options[:class]].key.column_name
|
39
|
-
else
|
40
|
-
k.value
|
41
|
-
end
|
42
|
-
|
43
|
-
sub_select = @adapter.select_statement(k.options.merge(v))
|
44
|
-
normalize(["#{@adapter[@loader.klass][k.value.to_sym].to_sql} IN ?", sub_select], collector)
|
45
|
-
else
|
46
|
-
@has_id = true if k.value == :id
|
47
|
-
op = case k.type
|
48
|
-
when :gt then '>'
|
49
|
-
when :gte then '>='
|
50
|
-
when :lt then '<'
|
51
|
-
when :lte then '<='
|
52
|
-
when :not then v.nil? ? 'IS NOT' : (v.kind_of?(Array) ? 'NOT IN' : '<>')
|
53
|
-
when :eql then v.nil? ? 'IS' : (v.kind_of?(Array) ? 'IN' : '=')
|
54
|
-
when :like then 'LIKE'
|
55
|
-
when :in then 'IN'
|
56
|
-
else raise ArgumentError.new('Operator type not supported')
|
57
|
-
end
|
58
|
-
normalize(["#{@adapter[@loader.klass][k.value.to_sym].to_sql} #{op} ?", v], collector)
|
59
|
-
end
|
60
|
-
else
|
61
|
-
@has_id = true if k == :id
|
62
|
-
case v
|
63
|
-
when Array then
|
64
|
-
normalize(["#{@adapter[@loader.klass][k.to_sym].to_sql} IN ?", v], collector)
|
65
|
-
when LoadCommand then
|
66
|
-
normalize(["#{@adapter[@loader.klass][k.to_sym].to_sql} IN ?", v], collector)
|
67
|
-
else
|
68
|
-
normalize(["#{@adapter[@loader.klass][k.to_sym].to_sql} = ?", v], collector)
|
69
|
-
end
|
70
|
-
end
|
71
|
-
end
|
72
|
-
when Array then
|
73
|
-
return collector if clause.empty?
|
74
|
-
@has_id = true if clause.first =~ /(^|\s|\`)id(\`|\s|\=|\<)/ && !clause[1].kind_of?(LoadCommand)
|
75
|
-
collector << escape(clause)
|
76
|
-
when String then
|
77
|
-
@has_id = true if clause =~ /(^|\s|\`)id(\`|\s|\=|\<)/
|
78
|
-
collector << clause
|
79
|
-
else raise NormalizationError.new(clause)
|
80
|
-
end
|
81
|
-
|
82
|
-
return collector
|
13
|
+
def empty?
|
14
|
+
@conditions.empty?
|
83
15
|
end
|
84
16
|
|
85
|
-
def
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
case
|
17
|
+
def to_parameterized_sql
|
18
|
+
sql = []
|
19
|
+
parameters = []
|
20
|
+
|
21
|
+
@conditions.each do |condition|
|
22
|
+
case condition
|
23
|
+
when String then sql << condition
|
91
24
|
when Array then
|
92
|
-
|
93
|
-
|
94
|
-
'(' << conditions.shift.to_sql << ')'
|
25
|
+
sql << condition.shift
|
26
|
+
parameters += condition
|
95
27
|
else
|
96
|
-
|
28
|
+
raise "Unable to parse condition: #{condition.inspect}" if condition
|
97
29
|
end
|
98
30
|
end
|
31
|
+
|
32
|
+
parameters.unshift("(#{sql.join(') AND (')})")
|
99
33
|
end
|
100
|
-
|
101
|
-
def has_id?
|
102
|
-
normalized_conditions
|
103
|
-
@has_id
|
104
|
-
end
|
105
|
-
|
106
|
-
def normalized_conditions
|
107
|
-
|
108
|
-
if @normalized_conditions.nil?
|
109
|
-
@normalized_conditions = []
|
110
|
-
|
111
|
-
normalize(implicits, @normalized_conditions)
|
112
34
|
|
113
|
-
|
114
|
-
|
35
|
+
private
|
36
|
+
|
37
|
+
class ConditionsError < StandardError
|
38
|
+
|
39
|
+
attr_reader :inner_error
|
40
|
+
|
41
|
+
def initialize(clause, value, inner_error)
|
42
|
+
@clause, @value, @inner_error = clause, value, inner_error
|
115
43
|
end
|
116
|
-
|
117
|
-
end
|
118
|
-
|
119
|
-
return @normalized_conditions
|
120
|
-
end
|
121
|
-
|
122
|
-
def table
|
123
|
-
@table || (@table = @adapter[@loader.klass])
|
124
|
-
end
|
125
|
-
|
126
|
-
def implicits
|
127
|
-
@implicits || @implicits = begin
|
128
44
|
|
129
|
-
|
45
|
+
def message
|
46
|
+
"Conditions (:clause => #{@clause.inspect}, :value => #{@value.inspect}) failed: #{@inner_error}"
|
47
|
+
end
|
130
48
|
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
49
|
+
def backtrace
|
50
|
+
@inner_error.backtrace
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
def expression_to_sql(clause, value, collector)
|
56
|
+
qualify_columns = @loader.qualify_columns?
|
57
|
+
|
58
|
+
case clause
|
59
|
+
when Symbol::Operator then
|
60
|
+
operator = case clause.type
|
61
|
+
when :gt then '>'
|
62
|
+
when :gte then '>='
|
63
|
+
when :lt then '<'
|
64
|
+
when :lte then '<='
|
65
|
+
when :not then inequality_operator(value)
|
66
|
+
when :eql then equality_operator(value)
|
67
|
+
when :like then equality_operator(value, 'LIKE')
|
68
|
+
when :in then equality_operator(value)
|
69
|
+
else raise ArgumentError.new('Operator type not supported')
|
70
|
+
end
|
71
|
+
collector << ["#{primary_class_table[clause].to_sql(qualify_columns)} #{operator} ?", value]
|
72
|
+
when Symbol then
|
73
|
+
collector << ["#{primary_class_table[clause].to_sql(qualify_columns)} #{equality_operator(value)} ?", value]
|
74
|
+
when String then
|
75
|
+
collector << [clause, value]
|
76
|
+
when Mappings::Column then
|
77
|
+
collector << ["#{clause.to_sql(qualify_columns)} #{equality_operator(value)} ?", value]
|
78
|
+
else raise "CAN HAS CRASH? #{clause.inspect}"
|
135
79
|
end
|
80
|
+
rescue => e
|
81
|
+
raise ConditionsError.new(clause, value, e)
|
82
|
+
end
|
83
|
+
|
84
|
+
def equality_operator(value, default = '=')
|
85
|
+
case value
|
86
|
+
when NilClass then 'IS'
|
87
|
+
when Array then 'IN'
|
88
|
+
else default
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def inequality_operator(value, default = '<>')
|
93
|
+
case value
|
94
|
+
when NilClass then 'IS NOT'
|
95
|
+
when Array then 'NOT IN'
|
96
|
+
else default
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def parse_conditions(conditions_hash)
|
101
|
+
collection = []
|
136
102
|
|
137
|
-
|
138
|
-
|
139
|
-
|
103
|
+
case x = conditions_hash.delete(:conditions)
|
104
|
+
when Array then
|
105
|
+
clause = x.shift
|
106
|
+
expression_to_sql(clause, x, collection)
|
107
|
+
when Hash then
|
108
|
+
x.each_pair do |key,value|
|
109
|
+
expression_to_sql(key, value, collection)
|
140
110
|
end
|
141
|
-
|
142
|
-
raise "Invalid options: #{invalid_keys.inspect}" unless invalid_keys.nil?
|
143
111
|
else
|
144
|
-
|
112
|
+
raise "Unable to parse conditions: #{x.inspect}" if x
|
113
|
+
end
|
114
|
+
|
115
|
+
conditions_hash.each_pair do |key,value|
|
116
|
+
expression_to_sql(key, value, collection)
|
145
117
|
end
|
118
|
+
|
119
|
+
collection
|
120
|
+
end
|
121
|
+
|
122
|
+
def primary_class_table
|
123
|
+
@primary_class_table || (@primary_class_table = @loader.send(:primary_class_table))
|
146
124
|
end
|
147
|
-
end
|
148
|
-
|
149
|
-
def empty?
|
150
|
-
!@loader.options.has_key?(:conditions) && implicits.empty?
|
151
|
-
end
|
152
|
-
|
153
|
-
def to_a
|
154
|
-
normalized_conditions
|
155
|
-
end
|
156
125
|
end
|
157
126
|
|
158
127
|
end
|
@@ -1,4 +1,5 @@
|
|
1
|
-
require
|
1
|
+
require 'data_mapper/adapters/sql/commands/conditions'
|
2
|
+
require 'data_mapper/adapters/sql/commands/loader'
|
2
3
|
|
3
4
|
module DataMapper
|
4
5
|
module Adapters
|
@@ -7,287 +8,289 @@ module DataMapper
|
|
7
8
|
|
8
9
|
class LoadCommand
|
9
10
|
|
10
|
-
attr_reader :
|
11
|
+
attr_reader :conditions, :session, :options
|
11
12
|
|
12
|
-
def initialize(adapter, session,
|
13
|
-
@adapter, @session, @
|
13
|
+
def initialize(adapter, session, primary_class, options = {})
|
14
|
+
@adapter, @session, @primary_class = adapter, session, primary_class
|
15
|
+
|
16
|
+
@options, conditions_hash = partition_options(options)
|
14
17
|
|
15
18
|
@order = @options[:order]
|
16
19
|
@limit = @options[:limit]
|
20
|
+
@offset = @options[:offset]
|
17
21
|
@reload = @options[:reload]
|
18
|
-
@instance_id =
|
19
|
-
@conditions = Conditions.new(@adapter, self)
|
22
|
+
@instance_id = conditions_hash[:id]
|
23
|
+
@conditions = Conditions.new(@adapter, self, conditions_hash)
|
24
|
+
@loaders = Hash.new { |h,k| h[k] = Loader.new(self, k) }
|
25
|
+
end
|
26
|
+
|
27
|
+
# Display an overview of load options at a glance.
|
28
|
+
def inspect
|
29
|
+
<<-EOS.compress_lines % (object_id * 2)
|
30
|
+
#<#{self.class.name}:0x%x
|
31
|
+
@database=#{@adapter.name}
|
32
|
+
@reload=#{@reload.inspect}
|
33
|
+
@order=#{@order.inspect}
|
34
|
+
@limit=#{@limit.inspect}
|
35
|
+
@offset=#{@offset.inspect}
|
36
|
+
@options=#{@options.inspect}>
|
37
|
+
EOS
|
38
|
+
end
|
39
|
+
|
40
|
+
# Access the Conditions instance
|
41
|
+
def conditions
|
42
|
+
@conditions
|
20
43
|
end
|
21
44
|
|
45
|
+
# If +true+ then force the command to reload any objects
|
46
|
+
# already existing in the IdentityMap when executing.
|
22
47
|
def reload?
|
23
48
|
@reload
|
24
49
|
end
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
def
|
31
|
-
@
|
50
|
+
|
51
|
+
# Determine if there is a limitation on the number of
|
52
|
+
# instances returned in the results. If +nil+, no limit
|
53
|
+
# is set. Can be used in conjunction with #offset for
|
54
|
+
# paging through a set of results.
|
55
|
+
def limit
|
56
|
+
@limit
|
32
57
|
end
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
58
|
+
|
59
|
+
# Used in conjunction with #limit to page through a set
|
60
|
+
# of results.
|
61
|
+
def offset
|
62
|
+
@offset
|
37
63
|
end
|
38
|
-
|
39
|
-
def
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
64
|
+
|
65
|
+
def call
|
66
|
+
|
67
|
+
# Check to see if the query is for a specific id and return if found
|
68
|
+
#
|
69
|
+
# NOTE: If the :id option is an Array:
|
70
|
+
# We could search for loaded instance ids and reject from
|
71
|
+
# the Array for already loaded instances, but working under the
|
72
|
+
# assumption that we'll probably have to issue a query to find
|
73
|
+
# at-least some of the instances we're looking for, it's faster to
|
74
|
+
# just skip that and go straight for the query.
|
75
|
+
unless reload? || @instance_id.blank? || @instance_id.is_a?(Array)
|
76
|
+
# If the id is for only a single record, attempt to find it.
|
77
|
+
if instance = @session.identity_map.get(@primary_class, @instance_id)
|
78
|
+
return instance
|
79
|
+
end
|
44
80
|
end
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
select_columns.map { |column| @adapter.quote_column_name(column.to_s) }
|
81
|
+
|
82
|
+
results = []
|
83
|
+
|
84
|
+
# Execute the statement and load the objects.
|
85
|
+
@adapter.execute(*to_parameterized_sql) do |reader, num_rows|
|
86
|
+
if @options.has_key?(:intercept_load)
|
87
|
+
load(reader, &@options[:intercept_load])
|
53
88
|
else
|
54
|
-
|
55
|
-
include?(column.name) || !column.lazy?
|
56
|
-
end.map { |column| column.to_sql }
|
89
|
+
load(reader)
|
57
90
|
end
|
58
91
|
end
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
@
|
63
|
-
|
92
|
+
|
93
|
+
results += @loaders[@primary_class].loaded_set
|
94
|
+
|
95
|
+
if @limit == 1 || (@instance_id && !@instance_id.is_a?(Array))
|
96
|
+
results.first
|
64
97
|
else
|
65
|
-
|
98
|
+
results
|
66
99
|
end
|
67
100
|
end
|
68
101
|
|
69
|
-
def
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
102
|
+
def load(reader)
|
103
|
+
# The following blocks are identical aside from the yield.
|
104
|
+
# It's written this way to avoid a conditional within each
|
105
|
+
# iterator, and to take advantage of the performance of
|
106
|
+
# yield vs. Proc#call.
|
107
|
+
if block_given?
|
108
|
+
reader.each do
|
109
|
+
@loaders.each_pair do |klass,loader|
|
110
|
+
row = reader.current_row
|
111
|
+
yield(loader.materialize(row), @columns, row)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
else
|
115
|
+
reader.each do
|
116
|
+
@loaders.each_pair do |klass,loader|
|
117
|
+
loader.materialize(reader.current_row)
|
118
|
+
end
|
119
|
+
end
|
86
120
|
end
|
87
|
-
|
88
|
-
return sql
|
89
121
|
end
|
90
122
|
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
return instance unless instance.nil?
|
102
|
-
end
|
123
|
+
# Generate a select statement based on the initialization
|
124
|
+
# arguments.
|
125
|
+
def to_parameterized_sql
|
126
|
+
parameters = []
|
127
|
+
|
128
|
+
sql = 'SELECT ' << columns_for_select.join(', ')
|
129
|
+
sql << ' FROM ' << from_table_name
|
130
|
+
|
131
|
+
included_associations.each do |association|
|
132
|
+
sql << ' ' << association.to_sql
|
103
133
|
end
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
results = if eof?(reader)
|
108
|
-
nil
|
109
|
-
elsif limit == 1 || ( instance_id && !instance_id.kind_of?(Array) )
|
110
|
-
fetch_one(reader)
|
111
|
-
else
|
112
|
-
fetch_all(reader)
|
134
|
+
|
135
|
+
shallow_included_associations.each do |association|
|
136
|
+
sql << ' ' << association.to_shallow_sql
|
113
137
|
end
|
114
138
|
|
115
|
-
|
139
|
+
unless conditions.empty?
|
140
|
+
where_clause, *parameters = conditions.to_parameterized_sql
|
141
|
+
sql << ' WHERE ' << where_clause
|
142
|
+
end
|
116
143
|
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
def load(hash, set = [])
|
121
|
-
|
122
|
-
instance_class = unless hash['type'].nil?
|
123
|
-
Kernel::const_get(hash['type'])
|
124
|
-
else
|
125
|
-
klass
|
144
|
+
unless @order.nil?
|
145
|
+
sql << ' ORDER BY ' << @order.to_s
|
126
146
|
end
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
instance_id = mapping.key.type_cast_value(hash['id'])
|
131
|
-
instance = @session.identity_map.get(instance_class, instance_id)
|
132
|
-
|
133
|
-
if instance.nil? || reload?
|
134
|
-
instance ||= instance_class.new
|
135
|
-
instance.class.callbacks.execute(:before_materialize, instance)
|
136
|
-
|
137
|
-
instance.instance_variable_set(:@new_record, false)
|
138
|
-
hash.each_pair do |name_as_string,raw_value|
|
139
|
-
name = name_as_string.to_sym
|
140
|
-
if column = mapping.find_by_column_name(name)
|
141
|
-
value = column.type_cast_value(raw_value)
|
142
|
-
instance.instance_variable_set(column.instance_variable_name, value)
|
143
|
-
else
|
144
|
-
instance.instance_variable_set("@#{name}", value)
|
145
|
-
end
|
146
|
-
instance.original_hashes[name] = value.hash
|
147
|
-
end
|
148
|
-
|
149
|
-
instance.instance_variable_set(:@__key, instance_id)
|
150
|
-
|
151
|
-
instance.class.callbacks.execute(:after_materialize, instance)
|
152
|
-
|
153
|
-
@session.identity_map.set(instance)
|
147
|
+
|
148
|
+
unless @limit.nil?
|
149
|
+
sql << ' LIMIT ' << @limit.to_s
|
154
150
|
end
|
155
|
-
|
156
|
-
instance.instance_variable_set(:@loaded_set, set)
|
157
|
-
instance.session = @session
|
158
|
-
set << instance
|
159
|
-
return instance
|
160
|
-
end
|
161
|
-
|
162
|
-
def load_instances(fields, rows)
|
163
|
-
table = @adapter[klass]
|
164
151
|
|
165
|
-
|
166
|
-
|
167
|
-
key_ordinal = nil
|
168
|
-
key_column = table.key
|
169
|
-
type_ordinal = nil
|
170
|
-
type_column = nil
|
171
|
-
|
172
|
-
fields.each_with_index do |field, i|
|
173
|
-
column = table.find_by_column_name(field.to_sym)
|
174
|
-
key_ordinal = i if column.key?
|
175
|
-
type_ordinal, type_column = i, column if column.name == :type
|
176
|
-
columns[column] = i
|
152
|
+
unless @offset.nil?
|
153
|
+
sql << ' OFFSET ' << @offset.to_s
|
177
154
|
end
|
178
155
|
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
156
|
+
parameters.unshift(sql)
|
157
|
+
end
|
158
|
+
|
159
|
+
def qualify_columns?
|
160
|
+
return @qualify_columns unless @qualify_columns.nil?
|
161
|
+
@qualify_columns = !(included_associations.empty? && shallow_included_associations.empty?)
|
162
|
+
end
|
163
|
+
|
164
|
+
private
|
165
|
+
# Return the Sql-escaped columns names to be selected in the results.
|
166
|
+
def columns_for_select
|
167
|
+
@columns_for_select || begin
|
168
|
+
qualify_columns = qualify_columns?
|
169
|
+
@columns_for_select = []
|
186
170
|
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
171
|
+
columns.each_with_index do |column,i|
|
172
|
+
class_for_loader = column.table.klass
|
173
|
+
@loaders[class_for_loader].add_column(column, i) if class_for_loader
|
174
|
+
@columns_for_select << column.to_sql(qualify_columns)
|
191
175
|
end
|
192
176
|
|
193
|
-
|
177
|
+
@columns_for_select
|
194
178
|
end
|
195
179
|
|
196
|
-
|
197
|
-
|
180
|
+
end
|
181
|
+
|
182
|
+
# Returns the DataMapper::Adapters::Sql::Mappings::Column instances to
|
183
|
+
# be selected in the results.
|
184
|
+
def columns
|
185
|
+
@columns || begin
|
186
|
+
@columns = primary_class_columns
|
187
|
+
@columns += included_columns
|
198
188
|
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
end
|
209
|
-
else
|
210
|
-
rows.each do |row|
|
211
|
-
load_instance(
|
212
|
-
create_instance(
|
213
|
-
klass,
|
214
|
-
key_column.type_cast_value(row[key_ordinal])
|
215
|
-
),
|
216
|
-
columns,
|
217
|
-
row,
|
218
|
-
set
|
219
|
-
)
|
189
|
+
included_associations.each do |assoc|
|
190
|
+
@columns += assoc.association_columns
|
191
|
+
end
|
192
|
+
|
193
|
+
shallow_included_associations.each do |assoc|
|
194
|
+
@columns += assoc.join_columns
|
195
|
+
end
|
196
|
+
|
197
|
+
@columns
|
220
198
|
end
|
221
199
|
end
|
222
200
|
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
instance.instance_variable_set(:@__key, instance_id)
|
239
|
-
instance.instance_variable_set(:@new_record, false)
|
240
|
-
@session.identity_map.set(instance)
|
201
|
+
# Returns the default columns for the primary_class_table,
|
202
|
+
# or maps symbols specified in a +:select+ option to columns
|
203
|
+
# in the primary_class_table.
|
204
|
+
def primary_class_columns
|
205
|
+
@primary_class_columns || @primary_class_columns = begin
|
206
|
+
if @options.has_key?(:select)
|
207
|
+
case x = @options[:select]
|
208
|
+
when Array then x
|
209
|
+
when Symbol then [x]
|
210
|
+
else raise ':select option must be a Symbol, or an Array of Symbols'
|
211
|
+
end.map { |name| primary_class_table[name] }
|
212
|
+
else
|
213
|
+
primary_class_table.columns.reject { |column| column.lazy? }
|
214
|
+
end
|
215
|
+
end
|
241
216
|
end
|
242
217
|
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
218
|
+
def included_associations
|
219
|
+
@included_associations || @included_associations = begin
|
220
|
+
associations = primary_class_table.associations
|
221
|
+
include_options.map do |name|
|
222
|
+
associations[name]
|
223
|
+
end.compact
|
224
|
+
end
|
225
|
+
end
|
249
226
|
|
250
|
-
|
227
|
+
def shallow_included_associations
|
228
|
+
@shallow_included_associations || @shallow_included_associations = begin
|
229
|
+
associations = primary_class_table.associations
|
230
|
+
shallow_include_options.map do |name|
|
231
|
+
associations[name]
|
232
|
+
end.compact
|
233
|
+
end
|
234
|
+
end
|
251
235
|
|
252
|
-
|
236
|
+
def included_columns
|
237
|
+
@included_columns || @included_columns = begin
|
238
|
+
include_options.map do |name|
|
239
|
+
primary_class_table[name]
|
240
|
+
end.compact
|
241
|
+
end
|
242
|
+
end
|
253
243
|
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
244
|
+
def include_options
|
245
|
+
@include_options || @include_options = begin
|
246
|
+
case x = @options[:include]
|
247
|
+
when Array then x
|
248
|
+
when Symbol then [x]
|
249
|
+
else []
|
250
|
+
end
|
251
|
+
end
|
259
252
|
end
|
260
253
|
|
261
|
-
|
254
|
+
def shallow_include_options
|
255
|
+
@shallow_include_options || @shallow_include_options = begin
|
256
|
+
case x = @options[:shallow_include]
|
257
|
+
when Array then x
|
258
|
+
when Symbol then [x]
|
259
|
+
else []
|
260
|
+
end
|
261
|
+
end
|
262
|
+
end
|
262
263
|
|
263
|
-
|
264
|
-
|
264
|
+
# Determine if a Column should be included based on the
|
265
|
+
# value of the +:include+ option.
|
266
|
+
def include_column?(name)
|
267
|
+
!primary_class_table[name].lazy? || include_options.includes?(name)
|
268
|
+
end
|
269
|
+
|
270
|
+
# Return the Sql-escaped table name of the +primary_class+.
|
271
|
+
def from_table_name
|
272
|
+
@from_table_name || (@from_table_name = @adapter.table(@primary_class).to_sql)
|
273
|
+
end
|
265
274
|
|
266
|
-
|
275
|
+
# Returns the DataMapper::Adapters::Sql::Mappings::Table for the +primary_class+.
|
276
|
+
def primary_class_table
|
277
|
+
@primary_class_table || (@primary_class_table = @adapter.table(@primary_class))
|
278
|
+
end
|
267
279
|
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
end
|
283
|
-
|
284
|
-
def fetch_one(reader)
|
285
|
-
raise NotImplementedError.new
|
286
|
-
end
|
287
|
-
|
288
|
-
def fetch_all(reader)
|
289
|
-
raise NotImplementedError.new
|
290
|
-
end
|
280
|
+
def partition_options(options)
|
281
|
+
find_options = @adapter.class::FIND_OPTIONS
|
282
|
+
conditions_hash = {}
|
283
|
+
options_hash = {}
|
284
|
+
options.each do |key,value|
|
285
|
+
if key != :conditions && find_options.include?(key)
|
286
|
+
options_hash[key] = value
|
287
|
+
else
|
288
|
+
conditions_hash[key] = value
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
[ options_hash, conditions_hash ]
|
293
|
+
end
|
291
294
|
|
292
295
|
end # class LoadCommand
|
293
296
|
end # module Commands
|