dm-core 0.10.2 → 1.0.0.rc1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +10 -1
- data/Gemfile +143 -0
- data/Rakefile +9 -5
- data/VERSION +1 -1
- data/dm-core.gemspec +160 -57
- data/lib/dm-core.rb +131 -56
- data/lib/dm-core/adapters.rb +98 -14
- data/lib/dm-core/adapters/abstract_adapter.rb +24 -4
- data/lib/dm-core/adapters/in_memory_adapter.rb +7 -2
- data/lib/dm-core/associations/many_to_many.rb +19 -30
- data/lib/dm-core/associations/many_to_one.rb +58 -42
- data/lib/dm-core/associations/one_to_many.rb +33 -23
- data/lib/dm-core/associations/one_to_one.rb +27 -11
- data/lib/dm-core/associations/relationship.rb +4 -4
- data/lib/dm-core/collection.rb +23 -16
- data/lib/dm-core/core_ext/array.rb +36 -0
- data/lib/dm-core/core_ext/hash.rb +30 -0
- data/lib/dm-core/core_ext/module.rb +46 -0
- data/lib/dm-core/core_ext/object.rb +31 -0
- data/lib/dm-core/core_ext/pathname.rb +20 -0
- data/lib/dm-core/core_ext/string.rb +22 -0
- data/lib/dm-core/core_ext/try_dup.rb +44 -0
- data/lib/dm-core/model.rb +88 -27
- data/lib/dm-core/model/hook.rb +75 -18
- data/lib/dm-core/model/property.rb +50 -9
- data/lib/dm-core/model/relationship.rb +31 -31
- data/lib/dm-core/model/scope.rb +3 -3
- data/lib/dm-core/property.rb +196 -516
- data/lib/dm-core/property/binary.rb +7 -0
- data/lib/dm-core/property/boolean.rb +35 -0
- data/lib/dm-core/property/class.rb +24 -0
- data/lib/dm-core/property/date.rb +47 -0
- data/lib/dm-core/property/date_time.rb +48 -0
- data/lib/dm-core/property/decimal.rb +43 -0
- data/lib/dm-core/property/discriminator.rb +48 -0
- data/lib/dm-core/property/float.rb +24 -0
- data/lib/dm-core/property/integer.rb +32 -0
- data/lib/dm-core/property/numeric.rb +43 -0
- data/lib/dm-core/property/object.rb +32 -0
- data/lib/dm-core/property/serial.rb +8 -0
- data/lib/dm-core/property/string.rb +49 -0
- data/lib/dm-core/property/text.rb +12 -0
- data/lib/dm-core/property/time.rb +48 -0
- data/lib/dm-core/property/typecast/numeric.rb +32 -0
- data/lib/dm-core/property/typecast/time.rb +28 -0
- data/lib/dm-core/property_set.rb +10 -4
- data/lib/dm-core/query.rb +14 -37
- data/lib/dm-core/query/conditions/comparison.rb +8 -6
- data/lib/dm-core/query/conditions/operation.rb +33 -2
- data/lib/dm-core/query/operator.rb +2 -5
- data/lib/dm-core/query/path.rb +4 -6
- data/lib/dm-core/repository.rb +21 -6
- data/lib/dm-core/resource.rb +316 -133
- data/lib/dm-core/resource/state.rb +79 -0
- data/lib/dm-core/resource/state/clean.rb +40 -0
- data/lib/dm-core/resource/state/deleted.rb +30 -0
- data/lib/dm-core/resource/state/dirty.rb +86 -0
- data/lib/dm-core/resource/state/immutable.rb +34 -0
- data/lib/dm-core/resource/state/persisted.rb +29 -0
- data/lib/dm-core/resource/state/transient.rb +70 -0
- data/lib/dm-core/spec/lib/adapter_helpers.rb +52 -0
- data/lib/dm-core/spec/lib/collection_helpers.rb +20 -0
- data/{spec → lib/dm-core/spec}/lib/counter_adapter.rb +5 -1
- data/lib/dm-core/spec/lib/pending_helpers.rb +50 -0
- data/lib/dm-core/spec/lib/spec_helper.rb +68 -0
- data/lib/dm-core/spec/setup.rb +165 -0
- data/lib/dm-core/spec/{adapter_shared_spec.rb → shared/adapter_spec.rb} +21 -7
- data/{spec/public/shared/resource_shared_spec.rb → lib/dm-core/spec/shared/resource_spec.rb} +120 -83
- data/{spec/public/shared/sel_shared_spec.rb → lib/dm-core/spec/shared/sel_spec.rb} +5 -6
- data/lib/dm-core/support/assertions.rb +8 -0
- data/lib/dm-core/support/equalizer.rb +1 -0
- data/lib/dm-core/support/hook.rb +420 -0
- data/lib/dm-core/support/lazy_array.rb +453 -0
- data/lib/dm-core/support/local_object_space.rb +12 -0
- data/lib/dm-core/support/logger.rb +193 -6
- data/lib/dm-core/support/naming_conventions.rb +8 -8
- data/lib/dm-core/support/subject.rb +33 -0
- data/lib/dm-core/type.rb +4 -0
- data/lib/dm-core/types/boolean.rb +2 -0
- data/lib/dm-core/types/decimal.rb +9 -0
- data/lib/dm-core/types/discriminator.rb +2 -0
- data/lib/dm-core/types/object.rb +3 -0
- data/lib/dm-core/types/serial.rb +2 -0
- data/lib/dm-core/types/text.rb +2 -0
- data/lib/dm-core/version.rb +1 -1
- data/spec/public/associations/many_to_many/read_multiple_join_spec.rb +67 -0
- data/spec/public/model/hook_spec.rb +209 -0
- data/spec/public/model/property_spec.rb +35 -0
- data/spec/public/model/relationship_spec.rb +33 -20
- data/spec/public/model_spec.rb +142 -10
- data/spec/public/property/binary_spec.rb +14 -0
- data/spec/public/property/boolean_spec.rb +14 -0
- data/spec/public/property/class_spec.rb +20 -0
- data/spec/public/property/date_spec.rb +14 -0
- data/spec/public/property/date_time_spec.rb +14 -0
- data/spec/public/property/decimal_spec.rb +14 -0
- data/spec/public/{types → property}/discriminator_spec.rb +2 -12
- data/spec/public/property/float_spec.rb +14 -0
- data/spec/public/property/integer_spec.rb +14 -0
- data/spec/public/property/object_spec.rb +9 -17
- data/spec/public/property/serial_spec.rb +14 -0
- data/spec/public/property/string_spec.rb +14 -0
- data/spec/public/property/text_spec.rb +52 -0
- data/spec/public/property/time_spec.rb +14 -0
- data/spec/public/property_spec.rb +28 -87
- data/spec/public/resource_spec.rb +101 -0
- data/spec/public/sel_spec.rb +5 -15
- data/spec/public/shared/collection_shared_spec.rb +16 -30
- data/spec/public/shared/finder_shared_spec.rb +2 -4
- data/spec/public/shared/property_shared_spec.rb +176 -0
- data/spec/semipublic/adapters/abstract_adapter_spec.rb +1 -1
- data/spec/semipublic/adapters/in_memory_adapter_spec.rb +2 -2
- data/spec/semipublic/associations/many_to_many_spec.rb +89 -0
- data/spec/semipublic/associations/many_to_one_spec.rb +24 -1
- data/spec/semipublic/associations/one_to_many_spec.rb +51 -0
- data/spec/semipublic/associations/one_to_one_spec.rb +49 -0
- data/spec/semipublic/associations/relationship_spec.rb +3 -3
- data/spec/semipublic/associations_spec.rb +1 -1
- data/spec/semipublic/property/binary_spec.rb +13 -0
- data/spec/semipublic/property/boolean_spec.rb +65 -0
- data/spec/semipublic/property/class_spec.rb +33 -0
- data/spec/semipublic/property/date_spec.rb +43 -0
- data/spec/semipublic/property/date_time_spec.rb +46 -0
- data/spec/semipublic/property/decimal_spec.rb +82 -0
- data/spec/semipublic/property/discriminator_spec.rb +19 -0
- data/spec/semipublic/property/float_spec.rb +82 -0
- data/spec/semipublic/property/integer_spec.rb +82 -0
- data/spec/semipublic/property/serial_spec.rb +13 -0
- data/spec/semipublic/property/string_spec.rb +13 -0
- data/spec/semipublic/property/text_spec.rb +31 -0
- data/spec/semipublic/property/time_spec.rb +50 -0
- data/spec/semipublic/property_spec.rb +2 -532
- data/spec/semipublic/query/conditions/comparison_spec.rb +171 -169
- data/spec/semipublic/query/conditions/operation_spec.rb +53 -51
- data/spec/semipublic/query/path_spec.rb +17 -17
- data/spec/semipublic/query_spec.rb +47 -78
- data/spec/semipublic/resource/state/clean_spec.rb +88 -0
- data/spec/semipublic/resource/state/deleted_spec.rb +78 -0
- data/spec/semipublic/resource/state/dirty_spec.rb +133 -0
- data/spec/semipublic/resource/state/immutable_spec.rb +99 -0
- data/spec/semipublic/resource/state/transient_spec.rb +128 -0
- data/spec/semipublic/resource/state_spec.rb +226 -0
- data/spec/semipublic/shared/property_shared_spec.rb +143 -0
- data/spec/semipublic/shared/resource_shared_spec.rb +16 -15
- data/spec/semipublic/shared/resource_state_shared_spec.rb +78 -0
- data/spec/semipublic/shared/subject_shared_spec.rb +79 -0
- data/spec/spec_helper.rb +21 -97
- data/spec/support/types/huge_integer.rb +17 -0
- data/spec/unit/array_spec.rb +48 -0
- data/spec/unit/hash_spec.rb +35 -0
- data/spec/unit/hook_spec.rb +1234 -0
- data/spec/unit/lazy_array_spec.rb +1959 -0
- data/spec/unit/module_spec.rb +70 -0
- data/spec/unit/object_spec.rb +37 -0
- data/spec/unit/try_dup_spec.rb +45 -0
- data/tasks/local_gemfile.rake +18 -0
- data/tasks/spec.rake +0 -3
- metadata +197 -71
- data/deps.rip +0 -2
- data/lib/dm-core/adapters/data_objects_adapter.rb +0 -712
- data/lib/dm-core/adapters/mysql_adapter.rb +0 -42
- data/lib/dm-core/adapters/oracle_adapter.rb +0 -229
- data/lib/dm-core/adapters/postgres_adapter.rb +0 -22
- data/lib/dm-core/adapters/sqlite3_adapter.rb +0 -17
- data/lib/dm-core/adapters/sqlserver_adapter.rb +0 -114
- data/lib/dm-core/adapters/yaml_adapter.rb +0 -111
- data/lib/dm-core/core_ext/enumerable.rb +0 -28
- data/lib/dm-core/migrations.rb +0 -1427
- data/lib/dm-core/spec/data_objects_adapter_shared_spec.rb +0 -366
- data/lib/dm-core/transaction.rb +0 -508
- data/lib/dm-core/types/paranoid_boolean.rb +0 -42
- data/lib/dm-core/types/paranoid_datetime.rb +0 -41
- data/spec/lib/adapter_helpers.rb +0 -105
- data/spec/lib/collection_helpers.rb +0 -18
- data/spec/lib/pending_helpers.rb +0 -46
- data/spec/public/migrations_spec.rb +0 -503
- data/spec/public/transaction_spec.rb +0 -153
- data/spec/semipublic/adapters/mysql_adapter_spec.rb +0 -17
- data/spec/semipublic/adapters/oracle_adapter_spec.rb +0 -194
- data/spec/semipublic/adapters/postgres_adapter_spec.rb +0 -17
- data/spec/semipublic/adapters/sqlite3_adapter_spec.rb +0 -17
- data/spec/semipublic/adapters/sqlserver_adapter_spec.rb +0 -17
- data/spec/semipublic/adapters/yaml_adapter_spec.rb +0 -12
@@ -1,42 +0,0 @@
|
|
1
|
-
require DataMapper.root / 'lib' / 'dm-core' / 'adapters' / 'data_objects_adapter'
|
2
|
-
|
3
|
-
require 'do_mysql'
|
4
|
-
|
5
|
-
module DataMapper
|
6
|
-
module Adapters
|
7
|
-
class MysqlAdapter < DataObjectsAdapter
|
8
|
-
module SQL #:nodoc:
|
9
|
-
IDENTIFIER_MAX_LENGTH = 64
|
10
|
-
|
11
|
-
private
|
12
|
-
|
13
|
-
# @api private
|
14
|
-
def supports_default_values? #:nodoc:
|
15
|
-
false
|
16
|
-
end
|
17
|
-
|
18
|
-
# @api private
|
19
|
-
def supports_subquery?(query, source_key, target_key, qualify)
|
20
|
-
# TODO: renable once query does not include target_model for deletes and updates
|
21
|
-
# query.limit.nil?
|
22
|
-
|
23
|
-
false
|
24
|
-
end
|
25
|
-
|
26
|
-
# @api private
|
27
|
-
def regexp_operator(operand)
|
28
|
-
'REGEXP'
|
29
|
-
end
|
30
|
-
|
31
|
-
# @api private
|
32
|
-
def quote_name(name)
|
33
|
-
"`#{name[0, self.class::IDENTIFIER_MAX_LENGTH].gsub('`', '``')}`"
|
34
|
-
end
|
35
|
-
end #module SQL
|
36
|
-
|
37
|
-
include SQL
|
38
|
-
end # class MysqlAdapter
|
39
|
-
|
40
|
-
const_added(:MysqlAdapter)
|
41
|
-
end # module Adapters
|
42
|
-
end # module DataMapper
|
@@ -1,229 +0,0 @@
|
|
1
|
-
require DataMapper.root / 'lib' / 'dm-core' / 'adapters' / 'data_objects_adapter'
|
2
|
-
|
3
|
-
require 'do_oracle'
|
4
|
-
|
5
|
-
module DataMapper
|
6
|
-
|
7
|
-
class Property
|
8
|
-
# for custom sequence names
|
9
|
-
OPTIONS << :sequence
|
10
|
-
end
|
11
|
-
|
12
|
-
module Adapters
|
13
|
-
class OracleAdapter < DataObjectsAdapter
|
14
|
-
module SQL #:nodoc:
|
15
|
-
IDENTIFIER_MAX_LENGTH = 30
|
16
|
-
|
17
|
-
private
|
18
|
-
|
19
|
-
# Constructs INSERT statement for given query,
|
20
|
-
#
|
21
|
-
# @return [String] INSERT statement as a string
|
22
|
-
#
|
23
|
-
# @api private
|
24
|
-
def insert_statement(model, properties, serial)
|
25
|
-
statement = "INSERT INTO #{quote_name(model.storage_name(name))} "
|
26
|
-
|
27
|
-
no_properties = properties.empty?
|
28
|
-
custom_sequence = serial && serial.options[:sequence]
|
29
|
-
serial_field = serial && quote_name(serial.field)
|
30
|
-
|
31
|
-
if supports_default_values? && no_properties && !custom_sequence
|
32
|
-
statement << "(#{serial_field}) " if serial
|
33
|
-
statement << default_values_clause
|
34
|
-
else
|
35
|
-
# do not use custom sequence if identity field was assigned a value
|
36
|
-
if custom_sequence && properties.include?(serial)
|
37
|
-
custom_sequence = nil
|
38
|
-
end
|
39
|
-
statement << "("
|
40
|
-
if custom_sequence
|
41
|
-
statement << "#{serial_field}"
|
42
|
-
statement << ", " unless no_properties
|
43
|
-
end
|
44
|
-
statement << "#{properties.map { |property| quote_name(property.field) }.join(', ')}) "
|
45
|
-
statement << "VALUES ("
|
46
|
-
if custom_sequence
|
47
|
-
statement << "#{quote_name(custom_sequence)}.NEXTVAL"
|
48
|
-
statement << ", " unless no_properties
|
49
|
-
end
|
50
|
-
statement << "#{(['?'] * properties.size).join(', ')})"
|
51
|
-
end
|
52
|
-
|
53
|
-
if supports_returning? && serial
|
54
|
-
statement << returning_clause(serial)
|
55
|
-
end
|
56
|
-
|
57
|
-
statement
|
58
|
-
end
|
59
|
-
|
60
|
-
# Oracle syntax for inserting default values
|
61
|
-
def default_values_clause
|
62
|
-
'VALUES (DEFAULT)'
|
63
|
-
end
|
64
|
-
|
65
|
-
# @api private
|
66
|
-
def supports_returning?
|
67
|
-
true
|
68
|
-
end
|
69
|
-
|
70
|
-
# INTO :insert_id is recognized by Oracle DataObjects driver
|
71
|
-
def returning_clause(serial)
|
72
|
-
" RETURNING #{quote_name(serial.field)} INTO :insert_id"
|
73
|
-
end
|
74
|
-
|
75
|
-
# Constructs SELECT statement for given query,
|
76
|
-
# Overrides DataObjects adapter implementation with using subquery instead of GROUP BY to get unique records
|
77
|
-
#
|
78
|
-
# @return [String] SELECT statement as a string
|
79
|
-
#
|
80
|
-
# @api private
|
81
|
-
def select_statement(query)
|
82
|
-
name = self.name
|
83
|
-
model = query.model
|
84
|
-
fields = query.fields
|
85
|
-
conditions = query.conditions
|
86
|
-
limit = query.limit
|
87
|
-
offset = query.offset
|
88
|
-
order = query.order
|
89
|
-
group_by = nil
|
90
|
-
|
91
|
-
# FIXME: using a boolean for qualify does not work in some cases,
|
92
|
-
# such as when you have a self-referrential many to many association.
|
93
|
-
# if you don't qualfiy the columns with a unique alias, then the
|
94
|
-
# SQL query will fail. This may mean though, that it might not
|
95
|
-
# be enough to pass in a Property, but we may need to know the
|
96
|
-
# table and the alias we should use for the column.
|
97
|
-
|
98
|
-
qualify = query.links.any?
|
99
|
-
|
100
|
-
if query.unique?
|
101
|
-
group_by = fields.select { |property| property.kind_of?(Property) }
|
102
|
-
end
|
103
|
-
|
104
|
-
# create subquery to find all valid keys and then use these keys to retrive all other columns
|
105
|
-
use_subquery = qualify
|
106
|
-
no_group_by = group_by.blank?
|
107
|
-
no_order = order.blank?
|
108
|
-
|
109
|
-
# when we can include ROWNUM condition in main WHERE clause
|
110
|
-
use_simple_rownum_limit = limit && (offset||0 == 0) && no_group_by && no_order
|
111
|
-
|
112
|
-
unless (limit && limit > 1) || offset > 0 || qualify
|
113
|
-
# TODO: move this method to Query, so that it walks the conditions
|
114
|
-
# and finds an OR operator
|
115
|
-
|
116
|
-
# TODO: handle cases where two or more properties need to be
|
117
|
-
# used together to be unique
|
118
|
-
|
119
|
-
# if a unique property is used, and there is no OR operator, then an ORDER
|
120
|
-
# and LIMIT are unecessary because it should only return a single row
|
121
|
-
if conditions.respond_to?(:slug) && conditions.slug == :and &&
|
122
|
-
conditions.any? { |operand| operand.respond_to?(:slug) && operand.slug == :eql && operand.subject.respond_to?(:unique?) && operand.subject.unique? } &&
|
123
|
-
!conditions.any? { |operand| operand.respond_to?(:slug) && operand.slug == :or }
|
124
|
-
order = nil
|
125
|
-
no_order = true
|
126
|
-
limit = nil
|
127
|
-
end
|
128
|
-
end
|
129
|
-
|
130
|
-
conditions_statement, bind_values = conditions_statement(conditions, qualify)
|
131
|
-
|
132
|
-
model_key_column = columns_statement(model.key(name), qualify)
|
133
|
-
from_statement = " FROM #{quote_name(model.storage_name(name))}"
|
134
|
-
|
135
|
-
statement = "SELECT #{columns_statement(fields, qualify)}"
|
136
|
-
if use_subquery
|
137
|
-
statement << from_statement
|
138
|
-
statement << " WHERE (#{model_key_column}) IN"
|
139
|
-
statement << " (SELECT DISTINCT #{model_key_column}"
|
140
|
-
# do not need to do group by for uniqueness as just one row per primary key will be returned
|
141
|
-
no_group_by = true
|
142
|
-
end
|
143
|
-
statement << from_statement
|
144
|
-
statement << join_statement(query, qualify) if qualify
|
145
|
-
statement << " WHERE (#{conditions_statement})" unless conditions_statement.blank?
|
146
|
-
if use_subquery
|
147
|
-
statement << ")"
|
148
|
-
end
|
149
|
-
if use_simple_rownum_limit
|
150
|
-
statement << " AND rownum <= ?"
|
151
|
-
bind_values << limit
|
152
|
-
end
|
153
|
-
statement << " GROUP BY #{columns_statement(group_by, qualify)}" unless no_group_by
|
154
|
-
statement << " ORDER BY #{order_statement(order, qualify)}" unless no_order
|
155
|
-
|
156
|
-
add_limit_offset!(statement, limit, offset, bind_values) unless use_simple_rownum_limit
|
157
|
-
|
158
|
-
return statement, bind_values
|
159
|
-
end
|
160
|
-
|
161
|
-
# Oracle does not support LIMIT and OFFSET
|
162
|
-
# Functionality is mimiced through the use of nested selects.
|
163
|
-
# See http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:127412348064
|
164
|
-
def add_limit_offset!(statement, limit, offset, bind_values)
|
165
|
-
positive_offset = offset > 0
|
166
|
-
|
167
|
-
if limit && positive_offset
|
168
|
-
statement.replace "select * from (select raw_sql_.*, rownum raw_rnum_ from (#{statement}) raw_sql_ where rownum <= ?) where raw_rnum_ > ?"
|
169
|
-
bind_values << offset + limit << offset
|
170
|
-
elsif limit
|
171
|
-
statement.replace "select raw_sql_.* from (#{statement}) raw_sql_ where rownum <= ?"
|
172
|
-
bind_values << limit
|
173
|
-
elsif positive_offset
|
174
|
-
statement.replace "select * from (select raw_sql_.*, rownum raw_rnum_ from (#{statement}) raw_sql_) where raw_rnum_ > ?"
|
175
|
-
bind_values << offset
|
176
|
-
end
|
177
|
-
end
|
178
|
-
|
179
|
-
# @api private
|
180
|
-
# Oracle does not allow " in table or column names therefore substitute them with underscore
|
181
|
-
def quote_name(name)
|
182
|
-
"\"#{oracle_upcase(name)[0, self.class::IDENTIFIER_MAX_LENGTH].gsub('"', '_')}\""
|
183
|
-
end
|
184
|
-
|
185
|
-
# If table or column name contains just lowercase characters then do uppercase
|
186
|
-
# as uppercase version will be used in Oracle data dictionary tables
|
187
|
-
def oracle_upcase(name)
|
188
|
-
name =~ /[A-Z]/ ? name : name.upcase
|
189
|
-
end
|
190
|
-
|
191
|
-
# CLOB value should be compared using DBMS_LOB.SUBSTR function
|
192
|
-
# NOTE: just first 32767 bytes will be compared!
|
193
|
-
# @api private
|
194
|
-
def equality_operator(property, operand)
|
195
|
-
if operand.nil?
|
196
|
-
'IS'
|
197
|
-
elsif property.type == Types::Text
|
198
|
-
'DBMS_LOB.SUBSTR(%s) = ?'
|
199
|
-
else
|
200
|
-
'='
|
201
|
-
end
|
202
|
-
end
|
203
|
-
|
204
|
-
# @api private
|
205
|
-
def include_operator(property, operand)
|
206
|
-
operator = case operand
|
207
|
-
when Array then 'IN'
|
208
|
-
when Range then 'BETWEEN'
|
209
|
-
end
|
210
|
-
if property.type == Types::Text
|
211
|
-
"DBMS_LOB.SUBSTR(%s) #{operator} ?"
|
212
|
-
else
|
213
|
-
operator
|
214
|
-
end
|
215
|
-
end
|
216
|
-
|
217
|
-
# @api private
|
218
|
-
def regexp_operator(operand)
|
219
|
-
'REGEXP_LIKE(%s, ?)'
|
220
|
-
end
|
221
|
-
|
222
|
-
end #module SQL
|
223
|
-
|
224
|
-
include SQL
|
225
|
-
end # class PostgresAdapter
|
226
|
-
|
227
|
-
const_added(:OracleAdapter)
|
228
|
-
end # module Adapters
|
229
|
-
end # module DataMapper
|
@@ -1,22 +0,0 @@
|
|
1
|
-
require DataMapper.root / 'lib' / 'dm-core' / 'adapters' / 'data_objects_adapter'
|
2
|
-
|
3
|
-
require 'do_postgres'
|
4
|
-
|
5
|
-
module DataMapper
|
6
|
-
module Adapters
|
7
|
-
class PostgresAdapter < DataObjectsAdapter
|
8
|
-
module SQL #:nodoc:
|
9
|
-
private
|
10
|
-
|
11
|
-
# @api private
|
12
|
-
def supports_returning?
|
13
|
-
true
|
14
|
-
end
|
15
|
-
end #module SQL
|
16
|
-
|
17
|
-
include SQL
|
18
|
-
end # class PostgresAdapter
|
19
|
-
|
20
|
-
const_added(:PostgresAdapter)
|
21
|
-
end # module Adapters
|
22
|
-
end # module DataMapper
|
@@ -1,17 +0,0 @@
|
|
1
|
-
require DataMapper.root / 'lib' / 'dm-core' / 'adapters' / 'data_objects_adapter'
|
2
|
-
|
3
|
-
require 'do_sqlite3'
|
4
|
-
|
5
|
-
module DataMapper
|
6
|
-
module Adapters
|
7
|
-
class Sqlite3Adapter < DataObjectsAdapter
|
8
|
-
# @api private
|
9
|
-
def supports_subquery?(query, source_key, target_key, qualify)
|
10
|
-
# SQLite3 cannot match a subquery against more than one column
|
11
|
-
source_key.size == 1 && target_key.size == 1
|
12
|
-
end
|
13
|
-
end # class Sqlite3Adapter
|
14
|
-
|
15
|
-
const_added(:Sqlite3Adapter)
|
16
|
-
end # module Adapters
|
17
|
-
end # module DataMapper
|
@@ -1,114 +0,0 @@
|
|
1
|
-
require DataMapper.root / 'lib' / 'dm-core' / 'adapters' / 'data_objects_adapter'
|
2
|
-
|
3
|
-
require 'do_sqlserver'
|
4
|
-
|
5
|
-
DataObjects::Sqlserver = DataObjects::SqlServer
|
6
|
-
|
7
|
-
module DataMapper
|
8
|
-
module Adapters
|
9
|
-
class SqlserverAdapter < DataObjectsAdapter
|
10
|
-
module SQL #:nodoc:
|
11
|
-
private
|
12
|
-
|
13
|
-
# Constructs INSERT statement for given query,
|
14
|
-
#
|
15
|
-
# @return [String] INSERT statement as a string
|
16
|
-
#
|
17
|
-
# @api private
|
18
|
-
def insert_statement(model, properties, serial)
|
19
|
-
statement = ""
|
20
|
-
# Check if there is a serial property being set directly
|
21
|
-
require_identity_insert = !properties.empty? && properties.any? { |property| property.serial? }
|
22
|
-
set_identity_insert(model, statement, true) if require_identity_insert
|
23
|
-
statement << super
|
24
|
-
set_identity_insert(model, statement, false) if require_identity_insert
|
25
|
-
statement
|
26
|
-
end
|
27
|
-
|
28
|
-
def set_identity_insert(model, statement, enable = true)
|
29
|
-
statement << " SET IDENTITY_INSERT #{quote_name(model.storage_name(name))} #{enable ? 'ON' : 'OFF'} "
|
30
|
-
end
|
31
|
-
|
32
|
-
def select_statement(query)
|
33
|
-
name = self.name
|
34
|
-
qualify = query.links.any?
|
35
|
-
fields = query.fields
|
36
|
-
offset = query.offset
|
37
|
-
limit = query.limit
|
38
|
-
order_by = query.order
|
39
|
-
group_by = if qualify || query.unique?
|
40
|
-
fields.select { |property| property.kind_of?(Property) }
|
41
|
-
end
|
42
|
-
|
43
|
-
conditions_statement, bind_values = conditions_statement(query.conditions, qualify)
|
44
|
-
|
45
|
-
use_limit_offset_subquery = limit && offset > 0
|
46
|
-
|
47
|
-
columns_statement = columns_statement(fields, qualify)
|
48
|
-
from_statement = " FROM #{quote_name(query.model.storage_name(name))}"
|
49
|
-
where_statement = " WHERE #{conditions_statement}" unless conditions_statement.blank?
|
50
|
-
join_statement = join_statement(query, qualify)
|
51
|
-
order_statement = order_statement(order_by, qualify)
|
52
|
-
no_group_by = group_by ? group_by.empty? : true
|
53
|
-
no_order_by = order_by ? order_by.empty? : true
|
54
|
-
|
55
|
-
if use_limit_offset_subquery
|
56
|
-
# If using qualifiers, we must qualify elements outside the subquery
|
57
|
-
# with 'RowResults' -- this is a different scope to the subquery.
|
58
|
-
# Otherwise, we hit upon "multi-part identifier cannot be bound"
|
59
|
-
# error from SQL Server.
|
60
|
-
statement = "SELECT #{columns_statement(fields, qualify, 'RowResults')}"
|
61
|
-
statement << " FROM ( SELECT Row_Number() OVER (ORDER BY #{order_statement}) AS RowID,"
|
62
|
-
statement << " #{columns_statement}"
|
63
|
-
statement << from_statement
|
64
|
-
statement << join_statement if qualify
|
65
|
-
statement << where_statement if where_statement
|
66
|
-
statement << ") AS RowResults"
|
67
|
-
statement << " WHERE RowId > #{offset} AND RowId <= #{offset + limit}"
|
68
|
-
statement << " GROUP BY #{columns_statement(group_by, qualify, 'RowResults')}" unless no_group_by
|
69
|
-
statement << " ORDER BY #{order_statement(order_by, qualify, 'RowResults')}" unless no_order_by
|
70
|
-
else
|
71
|
-
statement = "SELECT #{columns_statement}"
|
72
|
-
statement << from_statement
|
73
|
-
statement << join_statement if qualify
|
74
|
-
statement << where_statement if where_statement
|
75
|
-
statement << " GROUP BY #{columns_statement(group_by, qualify)}" unless no_group_by
|
76
|
-
statement << " ORDER BY #{order_statement}" unless no_order_by
|
77
|
-
end
|
78
|
-
|
79
|
-
add_limit_offset!(statement, limit, offset, bind_values) unless use_limit_offset_subquery
|
80
|
-
|
81
|
-
return statement, bind_values
|
82
|
-
end
|
83
|
-
|
84
|
-
# SQL Server does not support LIMIT and OFFSET
|
85
|
-
# Functionality therefore must be mimicked through the use of nested selects.
|
86
|
-
# See also:
|
87
|
-
# - http://stackoverflow.com/questions/2840/paging-sql-server-2005-results
|
88
|
-
# - http://stackoverflow.com/questions/216673/emulate-mysql-limit-clause-in-microsoft-sql-server-2000
|
89
|
-
#
|
90
|
-
def add_limit_offset!(statement, limit, offset, bind_values)
|
91
|
-
# Limit and offset is handled by subqueries (see #select_statement).
|
92
|
-
if limit
|
93
|
-
# If there is just a limit on rows to return, but no offset, then we
|
94
|
-
# can use TOP clause.
|
95
|
-
statement.sub!(/^\s*SELECT(\s+DISTINCT)?/i) { "SELECT#{$1} TOP #{limit}" }
|
96
|
-
# bind_values << limit
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|
100
|
-
# @api private
|
101
|
-
# TODO: Not actually supported out of the box. Is theoretically possible
|
102
|
-
# via CLR integration, custom functions.
|
103
|
-
def regexp_operator(operand)
|
104
|
-
'REGEXP'
|
105
|
-
end
|
106
|
-
|
107
|
-
end #module SQL
|
108
|
-
|
109
|
-
include SQL
|
110
|
-
end # class SqlserverAdapter
|
111
|
-
|
112
|
-
const_added(:SqlserverAdapter)
|
113
|
-
end # module Adapters
|
114
|
-
end # module DataMapper
|
@@ -1,111 +0,0 @@
|
|
1
|
-
require 'pathname'
|
2
|
-
require 'yaml'
|
3
|
-
|
4
|
-
module DataMapper
|
5
|
-
module Adapters
|
6
|
-
class YamlAdapter < AbstractAdapter
|
7
|
-
# @api semipublic
|
8
|
-
def create(resources)
|
9
|
-
update_records(resources.first.model) do |records|
|
10
|
-
resources.each do |resource|
|
11
|
-
initialize_serial(resource, records.size.succ)
|
12
|
-
records << resource.attributes(:field)
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
# @api semipublic
|
18
|
-
def read(query)
|
19
|
-
query.filter_records(records_for(query.model).dup)
|
20
|
-
end
|
21
|
-
|
22
|
-
# @api semipublic
|
23
|
-
def update(attributes, collection)
|
24
|
-
attributes = attributes_as_fields(attributes)
|
25
|
-
|
26
|
-
update_records(collection.model) do |records|
|
27
|
-
records_to_update = collection.query.filter_records(records.dup)
|
28
|
-
records_to_update.each { |resource| resource.update(attributes) }.size
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
# @api semipublic
|
33
|
-
def delete(collection)
|
34
|
-
update_records(collection.model) do |records|
|
35
|
-
records_to_delete = collection.query.filter_records(records.dup)
|
36
|
-
records.replace(records - records_to_delete)
|
37
|
-
records_to_delete.size
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
private
|
42
|
-
|
43
|
-
# @api semipublic
|
44
|
-
def initialize(name, options = {})
|
45
|
-
super
|
46
|
-
(@path = Pathname(@options[:path]).freeze).mkpath
|
47
|
-
end
|
48
|
-
|
49
|
-
# Retrieves all records for a model and yeilds them to a block.
|
50
|
-
#
|
51
|
-
# The block should make any changes to the records in-place. After
|
52
|
-
# the block executes all the records are dumped back to the file.
|
53
|
-
#
|
54
|
-
# @param [Model, #to_s] model
|
55
|
-
# Used to determine which file to read/write to
|
56
|
-
#
|
57
|
-
# @yieldparam [Hash]
|
58
|
-
# A hash of record.key => record pairs retrieved from the file
|
59
|
-
#
|
60
|
-
# @api private
|
61
|
-
def update_records(model)
|
62
|
-
records = records_for(model)
|
63
|
-
result = yield records
|
64
|
-
write_records(model, records)
|
65
|
-
result
|
66
|
-
end
|
67
|
-
|
68
|
-
# Read all records from a file for a model
|
69
|
-
#
|
70
|
-
# @param [#storage_name] model
|
71
|
-
# The model/name to retieve records for
|
72
|
-
#
|
73
|
-
# @api private
|
74
|
-
def records_for(model)
|
75
|
-
file = yaml_file(model)
|
76
|
-
file.readable? && YAML.load_file(file) || []
|
77
|
-
end
|
78
|
-
|
79
|
-
# Writes all records to a file
|
80
|
-
#
|
81
|
-
# @param [#storage_name] model
|
82
|
-
# The model/name to write the records for
|
83
|
-
#
|
84
|
-
# @param [Hash] records
|
85
|
-
# A hash of record.key => record pairs to be written
|
86
|
-
#
|
87
|
-
# @api private
|
88
|
-
def write_records(model, records)
|
89
|
-
yaml_file(model).open('w') do |fh|
|
90
|
-
YAML.dump(records, fh)
|
91
|
-
end
|
92
|
-
end
|
93
|
-
|
94
|
-
# Given a model, gives the filename to be used for record storage
|
95
|
-
#
|
96
|
-
# @example
|
97
|
-
# yaml_file(Article) #=> "/path/to/files/articles.yml"
|
98
|
-
#
|
99
|
-
# @param [#storage_name] model
|
100
|
-
# The model to be used to determine the file name.
|
101
|
-
#
|
102
|
-
# @api private
|
103
|
-
def yaml_file(model)
|
104
|
-
@path / "#{model.storage_name(name)}.yml"
|
105
|
-
end
|
106
|
-
|
107
|
-
end # class YamlAdapter
|
108
|
-
|
109
|
-
const_added(:YamlAdapter)
|
110
|
-
end # module Adapters
|
111
|
-
end # module DataMapper
|