dm-core 0.10.2 → 1.0.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|