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,28 +0,0 @@
|
|
1
|
-
module Enumerable
|
2
|
-
def empty?
|
3
|
-
each { return false }
|
4
|
-
true
|
5
|
-
end
|
6
|
-
|
7
|
-
def one?
|
8
|
-
return one? { |entry| entry } unless block_given?
|
9
|
-
|
10
|
-
matches = 0
|
11
|
-
each do |entry|
|
12
|
-
matches += 1 if yield(entry)
|
13
|
-
return false if matches > 1
|
14
|
-
end
|
15
|
-
matches == 1
|
16
|
-
end
|
17
|
-
|
18
|
-
def first
|
19
|
-
each { |entry| return entry }
|
20
|
-
nil
|
21
|
-
end
|
22
|
-
|
23
|
-
def size
|
24
|
-
size = 0
|
25
|
-
each { size += 1 }
|
26
|
-
size
|
27
|
-
end
|
28
|
-
end
|
data/lib/dm-core/migrations.rb
DELETED
@@ -1,1427 +0,0 @@
|
|
1
|
-
# TODO: move to dm-more/dm-migrations
|
2
|
-
|
3
|
-
module DataMapper
|
4
|
-
module Migrations
|
5
|
-
module SingletonMethods
|
6
|
-
# destructively migrates the repository upwards to match model definitions
|
7
|
-
#
|
8
|
-
# @param [Symbol] name repository to act on, :default is the default
|
9
|
-
#
|
10
|
-
# @api public
|
11
|
-
def migrate!(repository_name = nil)
|
12
|
-
repository(repository_name).migrate!
|
13
|
-
end
|
14
|
-
|
15
|
-
# drops and recreates the repository upwards to match model definitions
|
16
|
-
#
|
17
|
-
# @param [Symbol] name repository to act on, :default is the default
|
18
|
-
#
|
19
|
-
# @api public
|
20
|
-
def auto_migrate!(repository_name = nil)
|
21
|
-
auto_migrate_down!(repository_name)
|
22
|
-
auto_migrate_up!(repository_name)
|
23
|
-
end
|
24
|
-
|
25
|
-
# @api public
|
26
|
-
def auto_upgrade!(repository_name = nil)
|
27
|
-
repository_execute(:auto_upgrade!, repository_name)
|
28
|
-
end
|
29
|
-
|
30
|
-
private
|
31
|
-
|
32
|
-
# @api private
|
33
|
-
def auto_migrate_down!(repository_name)
|
34
|
-
repository_execute(:auto_migrate_down!, repository_name)
|
35
|
-
end
|
36
|
-
|
37
|
-
# @api private
|
38
|
-
def auto_migrate_up!(repository_name)
|
39
|
-
repository_execute(:auto_migrate_up!, repository_name)
|
40
|
-
end
|
41
|
-
|
42
|
-
# @api private
|
43
|
-
def repository_execute(method, repository_name)
|
44
|
-
DataMapper::Model.descendants.each do |model|
|
45
|
-
model.send(method, repository_name || model.default_repository_name)
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
module DataObjectsAdapter
|
51
|
-
# @api private
|
52
|
-
def self.included(base)
|
53
|
-
base.extend ClassMethods
|
54
|
-
|
55
|
-
DataMapper.extend(Migrations::SingletonMethods)
|
56
|
-
|
57
|
-
[ :Repository, :Model ].each do |name|
|
58
|
-
DataMapper.const_get(name).send(:include, Migrations.const_get(name))
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
# Returns whether the storage_name exists.
|
63
|
-
#
|
64
|
-
# @param [String] storage_name
|
65
|
-
# a String defining the name of a storage, for example a table name.
|
66
|
-
#
|
67
|
-
# @return [Boolean]
|
68
|
-
# true if the storage exists
|
69
|
-
#
|
70
|
-
# @api semipublic
|
71
|
-
def storage_exists?(storage_name)
|
72
|
-
statement = <<-SQL.compress_lines
|
73
|
-
SELECT COUNT(*)
|
74
|
-
FROM "information_schema"."tables"
|
75
|
-
WHERE "table_type" = 'BASE TABLE'
|
76
|
-
AND "table_schema" = ?
|
77
|
-
AND "table_name" = ?
|
78
|
-
SQL
|
79
|
-
|
80
|
-
select(statement, schema_name, storage_name).first > 0
|
81
|
-
end
|
82
|
-
|
83
|
-
# Returns whether the field exists.
|
84
|
-
#
|
85
|
-
# @param [String] storage_name
|
86
|
-
# a String defining the name of a storage, for example a table name.
|
87
|
-
# @param [String] field
|
88
|
-
# a String defining the name of a field, for example a column name.
|
89
|
-
#
|
90
|
-
# @return [Boolean]
|
91
|
-
# true if the field exists.
|
92
|
-
#
|
93
|
-
# @api semipublic
|
94
|
-
def field_exists?(storage_name, column_name)
|
95
|
-
statement = <<-SQL.compress_lines
|
96
|
-
SELECT COUNT(*)
|
97
|
-
FROM "information_schema"."columns"
|
98
|
-
WHERE "table_schema" = ?
|
99
|
-
AND "table_name" = ?
|
100
|
-
AND "column_name" = ?
|
101
|
-
SQL
|
102
|
-
|
103
|
-
select(statement, schema_name, storage_name, column_name).first > 0
|
104
|
-
end
|
105
|
-
|
106
|
-
# @api semipublic
|
107
|
-
def upgrade_model_storage(model)
|
108
|
-
name = self.name
|
109
|
-
properties = model.properties_with_subclasses(name)
|
110
|
-
|
111
|
-
if success = create_model_storage(model)
|
112
|
-
return properties
|
113
|
-
end
|
114
|
-
|
115
|
-
table_name = model.storage_name(name)
|
116
|
-
|
117
|
-
with_connection do |connection|
|
118
|
-
properties.map do |property|
|
119
|
-
schema_hash = property_schema_hash(property)
|
120
|
-
next if field_exists?(table_name, schema_hash[:name])
|
121
|
-
|
122
|
-
statement = alter_table_add_column_statement(connection, table_name, schema_hash)
|
123
|
-
command = connection.create_command(statement)
|
124
|
-
command.execute_non_query
|
125
|
-
|
126
|
-
property
|
127
|
-
end.compact
|
128
|
-
end
|
129
|
-
end
|
130
|
-
|
131
|
-
# @api semipublic
|
132
|
-
def create_model_storage(model)
|
133
|
-
name = self.name
|
134
|
-
properties = model.properties_with_subclasses(name)
|
135
|
-
|
136
|
-
return false if storage_exists?(model.storage_name(name))
|
137
|
-
return false if properties.empty?
|
138
|
-
|
139
|
-
with_connection do |connection|
|
140
|
-
statements = [ create_table_statement(connection, model, properties) ]
|
141
|
-
statements.concat(create_index_statements(model))
|
142
|
-
statements.concat(create_unique_index_statements(model))
|
143
|
-
|
144
|
-
statements.each do |statement|
|
145
|
-
command = connection.create_command(statement)
|
146
|
-
command.execute_non_query
|
147
|
-
end
|
148
|
-
end
|
149
|
-
|
150
|
-
true
|
151
|
-
end
|
152
|
-
|
153
|
-
# @api semipublic
|
154
|
-
def destroy_model_storage(model)
|
155
|
-
return true unless supports_drop_table_if_exists? || storage_exists?(model.storage_name(name))
|
156
|
-
execute(drop_table_statement(model))
|
157
|
-
true
|
158
|
-
end
|
159
|
-
|
160
|
-
module SQL #:nodoc:
|
161
|
-
# private ## This cannot be private for current migrations
|
162
|
-
|
163
|
-
# Adapters that support AUTO INCREMENT fields for CREATE TABLE
|
164
|
-
# statements should overwrite this to return true
|
165
|
-
#
|
166
|
-
# @api private
|
167
|
-
def supports_serial?
|
168
|
-
false
|
169
|
-
end
|
170
|
-
|
171
|
-
# @api private
|
172
|
-
def supports_drop_table_if_exists?
|
173
|
-
false
|
174
|
-
end
|
175
|
-
|
176
|
-
# @api private
|
177
|
-
def schema_name
|
178
|
-
raise NotImplementedError, "#{self.class}#schema_name not implemented"
|
179
|
-
end
|
180
|
-
|
181
|
-
# @api private
|
182
|
-
def alter_table_add_column_statement(connection, table_name, schema_hash)
|
183
|
-
"ALTER TABLE #{quote_name(table_name)} ADD COLUMN #{property_schema_statement(connection, schema_hash)}"
|
184
|
-
end
|
185
|
-
|
186
|
-
# @api private
|
187
|
-
def create_table_statement(connection, model, properties)
|
188
|
-
statement = <<-SQL.compress_lines
|
189
|
-
CREATE TABLE #{quote_name(model.storage_name(name))}
|
190
|
-
(#{properties.map { |property| property_schema_statement(connection, property_schema_hash(property)) }.join(', ')},
|
191
|
-
PRIMARY KEY(#{ properties.key.map { |property| quote_name(property.field) }.join(', ')}))
|
192
|
-
SQL
|
193
|
-
|
194
|
-
statement
|
195
|
-
end
|
196
|
-
|
197
|
-
# @api private
|
198
|
-
def drop_table_statement(model)
|
199
|
-
table_name = quote_name(model.storage_name(name))
|
200
|
-
if supports_drop_table_if_exists?
|
201
|
-
"DROP TABLE IF EXISTS #{table_name}"
|
202
|
-
else
|
203
|
-
"DROP TABLE #{table_name}"
|
204
|
-
end
|
205
|
-
end
|
206
|
-
|
207
|
-
# @api private
|
208
|
-
def create_index_statements(model)
|
209
|
-
name = self.name
|
210
|
-
table_name = model.storage_name(name)
|
211
|
-
model.properties(name).indexes.map do |index_name, fields|
|
212
|
-
<<-SQL.compress_lines
|
213
|
-
CREATE INDEX #{quote_name("index_#{table_name}_#{index_name}")} ON
|
214
|
-
#{quote_name(table_name)} (#{fields.map { |field| quote_name(field) }.join(', ')})
|
215
|
-
SQL
|
216
|
-
end
|
217
|
-
end
|
218
|
-
|
219
|
-
# @api private
|
220
|
-
def create_unique_index_statements(model)
|
221
|
-
name = self.name
|
222
|
-
table_name = model.storage_name(name)
|
223
|
-
model.properties(name).unique_indexes.map do |index_name, fields|
|
224
|
-
<<-SQL.compress_lines
|
225
|
-
CREATE UNIQUE INDEX #{quote_name("unique_#{table_name}_#{index_name}")} ON
|
226
|
-
#{quote_name(table_name)} (#{fields.map { |field| quote_name(field) }.join(', ')})
|
227
|
-
SQL
|
228
|
-
end
|
229
|
-
end
|
230
|
-
|
231
|
-
# @api private
|
232
|
-
def property_schema_hash(property)
|
233
|
-
primitive = property.primitive
|
234
|
-
type = property.type
|
235
|
-
type_map = self.class.type_map
|
236
|
-
|
237
|
-
schema = (type_map[type] || type_map[primitive]).merge(:name => property.field)
|
238
|
-
|
239
|
-
schema_primitive = schema[:primitive]
|
240
|
-
|
241
|
-
if primitive == String && schema_primitive != 'TEXT' && schema_primitive != 'CLOB' && schema_primitive != 'NVARCHAR'
|
242
|
-
schema[:length] = property.length
|
243
|
-
elsif primitive == BigDecimal || primitive == Float
|
244
|
-
schema[:precision] = property.precision
|
245
|
-
schema[:scale] = property.scale
|
246
|
-
end
|
247
|
-
|
248
|
-
schema[:allow_nil] = property.allow_nil?
|
249
|
-
schema[:serial] = property.serial?
|
250
|
-
|
251
|
-
default = property.default
|
252
|
-
|
253
|
-
if default.nil? || default.respond_to?(:call)
|
254
|
-
# remove the default if the property does not allow nil
|
255
|
-
schema.delete(:default) unless schema[:allow_nil]
|
256
|
-
else
|
257
|
-
schema[:default] = if type.respond_to?(:dump)
|
258
|
-
type.dump(default, property)
|
259
|
-
else
|
260
|
-
default
|
261
|
-
end
|
262
|
-
end
|
263
|
-
|
264
|
-
schema
|
265
|
-
end
|
266
|
-
|
267
|
-
# @api private
|
268
|
-
def property_schema_statement(connection, schema)
|
269
|
-
statement = quote_name(schema[:name])
|
270
|
-
statement << " #{schema[:primitive]}"
|
271
|
-
|
272
|
-
length = schema[:length]
|
273
|
-
|
274
|
-
if schema[:precision] && schema[:scale]
|
275
|
-
statement << "(#{[ :precision, :scale ].map { |key| connection.quote_value(schema[key]) }.join(', ')})"
|
276
|
-
elsif length == 'max'
|
277
|
-
statement << '(max)'
|
278
|
-
elsif length
|
279
|
-
statement << "(#{connection.quote_value(length)})"
|
280
|
-
end
|
281
|
-
|
282
|
-
statement << " DEFAULT #{connection.quote_value(schema[:default])}" if schema.key?(:default)
|
283
|
-
statement << ' NOT NULL' unless schema[:allow_nil]
|
284
|
-
statement
|
285
|
-
end
|
286
|
-
end # module SQL
|
287
|
-
|
288
|
-
include SQL
|
289
|
-
|
290
|
-
module ClassMethods
|
291
|
-
# Default types for all data object based adapters.
|
292
|
-
#
|
293
|
-
# @return [Hash] default types for data objects adapters.
|
294
|
-
#
|
295
|
-
# @api private
|
296
|
-
def type_map
|
297
|
-
length = Property::DEFAULT_LENGTH
|
298
|
-
precision = Property::DEFAULT_PRECISION
|
299
|
-
scale = Property::DEFAULT_SCALE_BIGDECIMAL
|
300
|
-
|
301
|
-
@type_map ||= {
|
302
|
-
Integer => { :primitive => 'INTEGER' },
|
303
|
-
String => { :primitive => 'VARCHAR', :length => length },
|
304
|
-
Class => { :primitive => 'VARCHAR', :length => length },
|
305
|
-
BigDecimal => { :primitive => 'DECIMAL', :precision => precision, :scale => scale },
|
306
|
-
Float => { :primitive => 'FLOAT', :precision => precision },
|
307
|
-
DateTime => { :primitive => 'TIMESTAMP' },
|
308
|
-
Date => { :primitive => 'DATE' },
|
309
|
-
Time => { :primitive => 'TIMESTAMP' },
|
310
|
-
TrueClass => { :primitive => 'BOOLEAN' },
|
311
|
-
Types::Text => { :primitive => 'TEXT' },
|
312
|
-
}.freeze
|
313
|
-
end
|
314
|
-
end # module ClassMethods
|
315
|
-
end # module DataObjectsAdapter
|
316
|
-
|
317
|
-
module MysqlAdapter
|
318
|
-
DEFAULT_ENGINE = 'InnoDB'.freeze
|
319
|
-
DEFAULT_CHARACTER_SET = 'utf8'.freeze
|
320
|
-
DEFAULT_COLLATION = 'utf8_unicode_ci'.freeze
|
321
|
-
|
322
|
-
# @api private
|
323
|
-
def self.included(base)
|
324
|
-
base.extend ClassMethods
|
325
|
-
end
|
326
|
-
|
327
|
-
# @api semipublic
|
328
|
-
def storage_exists?(storage_name)
|
329
|
-
select('SHOW TABLES LIKE ?', storage_name).first == storage_name
|
330
|
-
end
|
331
|
-
|
332
|
-
# @api semipublic
|
333
|
-
def field_exists?(storage_name, field)
|
334
|
-
result = select("SHOW COLUMNS FROM #{quote_name(storage_name)} LIKE ?", field).first
|
335
|
-
result ? result.field == field : false
|
336
|
-
end
|
337
|
-
|
338
|
-
module SQL #:nodoc:
|
339
|
-
# private ## This cannot be private for current migrations
|
340
|
-
|
341
|
-
# @api private
|
342
|
-
def supports_serial?
|
343
|
-
true
|
344
|
-
end
|
345
|
-
|
346
|
-
# @api private
|
347
|
-
def supports_drop_table_if_exists?
|
348
|
-
true
|
349
|
-
end
|
350
|
-
|
351
|
-
# @api private
|
352
|
-
def schema_name
|
353
|
-
# TODO: is there a cleaner way to find out the current DB we are connected to?
|
354
|
-
normalized_uri.path.split('/').last
|
355
|
-
end
|
356
|
-
|
357
|
-
# TODO: update dkubb/dm-more/dm-migrations to use schema_name and remove this
|
358
|
-
alias db_name schema_name
|
359
|
-
|
360
|
-
# @api private
|
361
|
-
def create_table_statement(connection, model, properties)
|
362
|
-
"#{super} ENGINE = #{DEFAULT_ENGINE} CHARACTER SET #{character_set} COLLATE #{collation}"
|
363
|
-
end
|
364
|
-
|
365
|
-
# @api private
|
366
|
-
def property_schema_hash(property)
|
367
|
-
schema = super
|
368
|
-
|
369
|
-
if schema[:primitive] == 'TEXT'
|
370
|
-
schema[:primitive] = text_column_statement(property.length)
|
371
|
-
schema.delete(:default)
|
372
|
-
end
|
373
|
-
|
374
|
-
min = property.min
|
375
|
-
max = property.max
|
376
|
-
|
377
|
-
if property.primitive == Integer && min && max
|
378
|
-
schema[:primitive] = integer_column_statement(min..max)
|
379
|
-
end
|
380
|
-
|
381
|
-
schema
|
382
|
-
end
|
383
|
-
|
384
|
-
# @api private
|
385
|
-
def property_schema_statement(connection, schema)
|
386
|
-
statement = super
|
387
|
-
|
388
|
-
if supports_serial? && schema[:serial]
|
389
|
-
statement << ' AUTO_INCREMENT'
|
390
|
-
end
|
391
|
-
|
392
|
-
statement
|
393
|
-
end
|
394
|
-
|
395
|
-
# @api private
|
396
|
-
def character_set
|
397
|
-
@character_set ||= show_variable('character_set_connection') || DEFAULT_CHARACTER_SET
|
398
|
-
end
|
399
|
-
|
400
|
-
# @api private
|
401
|
-
def collation
|
402
|
-
@collation ||= show_variable('collation_connection') || DEFAULT_COLLATION
|
403
|
-
end
|
404
|
-
|
405
|
-
# @api private
|
406
|
-
def show_variable(name)
|
407
|
-
result = select('SHOW VARIABLES LIKE ?', name).first
|
408
|
-
result ? result.value.freeze : nil
|
409
|
-
end
|
410
|
-
|
411
|
-
private
|
412
|
-
|
413
|
-
# Return SQL statement for the text column
|
414
|
-
#
|
415
|
-
# @param [Integer] length
|
416
|
-
# the max allowed length
|
417
|
-
#
|
418
|
-
# @return [String]
|
419
|
-
# the statement to create the text column
|
420
|
-
#
|
421
|
-
# @api private
|
422
|
-
def text_column_statement(length)
|
423
|
-
if length < 2**8 then 'TINYTEXT'
|
424
|
-
elsif length < 2**16 then 'TEXT'
|
425
|
-
elsif length < 2**24 then 'MEDIUMTEXT'
|
426
|
-
elsif length < 2**32 then 'LONGTEXT'
|
427
|
-
|
428
|
-
# http://www.postgresql.org/files/documentation/books/aw_pgsql/node90.html
|
429
|
-
# Implies that PostgreSQL doesn't have a size limit on text
|
430
|
-
# fields, so this param validation happens here instead of
|
431
|
-
# DM::Property#initialize.
|
432
|
-
else
|
433
|
-
raise ArgumentError, "length of #{length} exceeds maximum size supported"
|
434
|
-
end
|
435
|
-
end
|
436
|
-
|
437
|
-
# Return SQL statement for the integer column
|
438
|
-
#
|
439
|
-
# @param [Range] range
|
440
|
-
# the min/max allowed integers
|
441
|
-
#
|
442
|
-
# @return [String]
|
443
|
-
# the statement to create the integer column
|
444
|
-
#
|
445
|
-
# @api private
|
446
|
-
def integer_column_statement(range)
|
447
|
-
'%s(%d)%s' % [
|
448
|
-
integer_column_type(range),
|
449
|
-
integer_display_size(range),
|
450
|
-
integer_statement_sign(range),
|
451
|
-
]
|
452
|
-
end
|
453
|
-
|
454
|
-
# Return the integer column type
|
455
|
-
#
|
456
|
-
# Use the smallest available column type that will satisfy the
|
457
|
-
# allowable range of numbers
|
458
|
-
#
|
459
|
-
# @param [Range] range
|
460
|
-
# the min/max allowed integers
|
461
|
-
#
|
462
|
-
# @return [String]
|
463
|
-
# the column type
|
464
|
-
#
|
465
|
-
# @api private
|
466
|
-
def integer_column_type(range)
|
467
|
-
if range.first < 0
|
468
|
-
signed_integer_column_type(range)
|
469
|
-
else
|
470
|
-
unsigned_integer_column_type(range)
|
471
|
-
end
|
472
|
-
end
|
473
|
-
|
474
|
-
# Return the signed integer column type
|
475
|
-
#
|
476
|
-
# @param [Range] range
|
477
|
-
# the min/max allowed integers
|
478
|
-
#
|
479
|
-
# @return [String]
|
480
|
-
#
|
481
|
-
# @api private
|
482
|
-
def signed_integer_column_type(range)
|
483
|
-
min = range.first
|
484
|
-
max = range.last
|
485
|
-
|
486
|
-
tinyint = 2**7
|
487
|
-
smallint = 2**15
|
488
|
-
integer = 2**31
|
489
|
-
mediumint = 2**23
|
490
|
-
bigint = 2**63
|
491
|
-
|
492
|
-
if min >= -tinyint && max < tinyint then 'TINYINT'
|
493
|
-
elsif min >= -smallint && max < smallint then 'SMALLINT'
|
494
|
-
elsif min >= -mediumint && max < mediumint then 'MEDIUMINT'
|
495
|
-
elsif min >= -integer && max < integer then 'INT'
|
496
|
-
elsif min >= -bigint && max < bigint then 'BIGINT'
|
497
|
-
else
|
498
|
-
raise ArgumentError, "min #{min} and max #{max} exceeds supported range"
|
499
|
-
end
|
500
|
-
end
|
501
|
-
|
502
|
-
# Return the unsigned integer column type
|
503
|
-
#
|
504
|
-
# @param [Range] range
|
505
|
-
# the min/max allowed integers
|
506
|
-
#
|
507
|
-
# @return [String]
|
508
|
-
#
|
509
|
-
# @api private
|
510
|
-
def unsigned_integer_column_type(range)
|
511
|
-
max = range.last
|
512
|
-
|
513
|
-
if max < 2**8 then 'TINYINT'
|
514
|
-
elsif max < 2**16 then 'SMALLINT'
|
515
|
-
elsif max < 2**24 then 'MEDIUMINT'
|
516
|
-
elsif max < 2**32 then 'INT'
|
517
|
-
elsif max < 2**64 then 'BIGINT'
|
518
|
-
else
|
519
|
-
raise ArgumentError, "min #{range.first} and max #{max} exceeds supported range"
|
520
|
-
end
|
521
|
-
end
|
522
|
-
|
523
|
-
# Return the integer column display size
|
524
|
-
#
|
525
|
-
# Adjust the display size to match the maximum number of
|
526
|
-
# expected digits. This is more for documentation purposes
|
527
|
-
# and does not affect what can actually be stored in a
|
528
|
-
# specific column
|
529
|
-
#
|
530
|
-
# @param [Range] range
|
531
|
-
# the min/max allowed integers
|
532
|
-
#
|
533
|
-
# @return [Integer]
|
534
|
-
# the display size for the integer
|
535
|
-
#
|
536
|
-
# @api private
|
537
|
-
def integer_display_size(range)
|
538
|
-
[ range.first.to_s.length, range.last.to_s.length ].max
|
539
|
-
end
|
540
|
-
|
541
|
-
# Return the integer sign statement
|
542
|
-
#
|
543
|
-
# @param [Range] range
|
544
|
-
# the min/max allowed integers
|
545
|
-
#
|
546
|
-
# @return [String, nil]
|
547
|
-
# statement if unsigned, nil if signed
|
548
|
-
#
|
549
|
-
# @api private
|
550
|
-
def integer_statement_sign(range)
|
551
|
-
' UNSIGNED' unless range.first < 0
|
552
|
-
end
|
553
|
-
end # module SQL
|
554
|
-
|
555
|
-
include SQL
|
556
|
-
|
557
|
-
module ClassMethods
|
558
|
-
# Types for MySQL databases.
|
559
|
-
#
|
560
|
-
# @return [Hash] types for MySQL databases.
|
561
|
-
#
|
562
|
-
# @api private
|
563
|
-
def type_map
|
564
|
-
@type_map ||= super.merge(
|
565
|
-
DateTime => { :primitive => 'DATETIME' },
|
566
|
-
Time => { :primitive => 'DATETIME' }
|
567
|
-
).freeze
|
568
|
-
end
|
569
|
-
end # module ClassMethods
|
570
|
-
end # module MysqlAdapter
|
571
|
-
|
572
|
-
module PostgresAdapter
|
573
|
-
# @api private
|
574
|
-
def self.included(base)
|
575
|
-
base.extend ClassMethods
|
576
|
-
end
|
577
|
-
|
578
|
-
# @api semipublic
|
579
|
-
def upgrade_model_storage(model)
|
580
|
-
without_notices { super }
|
581
|
-
end
|
582
|
-
|
583
|
-
# @api semipublic
|
584
|
-
def create_model_storage(model)
|
585
|
-
without_notices { super }
|
586
|
-
end
|
587
|
-
|
588
|
-
# @api semipublic
|
589
|
-
def destroy_model_storage(model)
|
590
|
-
if supports_drop_table_if_exists?
|
591
|
-
without_notices { super }
|
592
|
-
else
|
593
|
-
super
|
594
|
-
end
|
595
|
-
end
|
596
|
-
|
597
|
-
module SQL #:nodoc:
|
598
|
-
# private ## This cannot be private for current migrations
|
599
|
-
|
600
|
-
# @api private
|
601
|
-
def supports_drop_table_if_exists?
|
602
|
-
@supports_drop_table_if_exists ||= postgres_version >= '8.2'
|
603
|
-
end
|
604
|
-
|
605
|
-
# @api private
|
606
|
-
def schema_name
|
607
|
-
@schema_name ||= select('SELECT current_schema()').first.freeze
|
608
|
-
end
|
609
|
-
|
610
|
-
# @api private
|
611
|
-
def postgres_version
|
612
|
-
@postgres_version ||= select('SELECT version()').first.split[1].freeze
|
613
|
-
end
|
614
|
-
|
615
|
-
# @api private
|
616
|
-
def without_notices
|
617
|
-
# execute the block with NOTICE messages disabled
|
618
|
-
begin
|
619
|
-
execute('SET client_min_messages = warning')
|
620
|
-
yield
|
621
|
-
ensure
|
622
|
-
execute('RESET client_min_messages')
|
623
|
-
end
|
624
|
-
end
|
625
|
-
|
626
|
-
# @api private
|
627
|
-
def property_schema_hash(property)
|
628
|
-
schema = super
|
629
|
-
|
630
|
-
primitive = property.primitive
|
631
|
-
|
632
|
-
# Postgres does not support precision and scale for Float
|
633
|
-
if primitive == Float
|
634
|
-
schema.delete(:precision)
|
635
|
-
schema.delete(:scale)
|
636
|
-
end
|
637
|
-
|
638
|
-
min = property.min
|
639
|
-
max = property.max
|
640
|
-
|
641
|
-
if primitive == Integer && min && max
|
642
|
-
schema[:primitive] = integer_column_statement(min..max)
|
643
|
-
end
|
644
|
-
|
645
|
-
if schema[:serial]
|
646
|
-
schema[:primitive] = serial_column_statement(min..max)
|
647
|
-
end
|
648
|
-
|
649
|
-
schema
|
650
|
-
end
|
651
|
-
|
652
|
-
private
|
653
|
-
|
654
|
-
# Return SQL statement for the integer column
|
655
|
-
#
|
656
|
-
# @param [Range] range
|
657
|
-
# the min/max allowed integers
|
658
|
-
#
|
659
|
-
# @return [String]
|
660
|
-
# the statement to create the integer column
|
661
|
-
#
|
662
|
-
# @api private
|
663
|
-
def integer_column_statement(range)
|
664
|
-
min = range.first
|
665
|
-
max = range.last
|
666
|
-
|
667
|
-
smallint = 2**15
|
668
|
-
integer = 2**31
|
669
|
-
bigint = 2**63
|
670
|
-
|
671
|
-
if min >= -smallint && max < smallint then 'SMALLINT'
|
672
|
-
elsif min >= -integer && max < integer then 'INTEGER'
|
673
|
-
elsif min >= -bigint && max < bigint then 'BIGINT'
|
674
|
-
else
|
675
|
-
raise ArgumentError, "min #{min} and max #{max} exceeds supported range"
|
676
|
-
end
|
677
|
-
end
|
678
|
-
|
679
|
-
# Return SQL statement for the serial column
|
680
|
-
#
|
681
|
-
# @param [Integer] max
|
682
|
-
# the max allowed integer
|
683
|
-
#
|
684
|
-
# @return [String]
|
685
|
-
# the statement to create the serial column
|
686
|
-
#
|
687
|
-
# @api private
|
688
|
-
def serial_column_statement(range)
|
689
|
-
max = range.last
|
690
|
-
|
691
|
-
if max.nil? || max < 2**31 then 'SERIAL'
|
692
|
-
elsif max < 2**63 then 'BIGSERIAL'
|
693
|
-
else
|
694
|
-
raise ArgumentError, "min #{range.first} and max #{max} exceeds supported range"
|
695
|
-
end
|
696
|
-
end
|
697
|
-
end # module SQL
|
698
|
-
|
699
|
-
include SQL
|
700
|
-
|
701
|
-
module ClassMethods
|
702
|
-
# Types for PostgreSQL databases.
|
703
|
-
#
|
704
|
-
# @return [Hash] types for PostgreSQL databases.
|
705
|
-
#
|
706
|
-
# @api private
|
707
|
-
def type_map
|
708
|
-
precision = Property::DEFAULT_PRECISION
|
709
|
-
scale = Property::DEFAULT_SCALE_BIGDECIMAL
|
710
|
-
|
711
|
-
@type_map ||= super.merge(
|
712
|
-
BigDecimal => { :primitive => 'NUMERIC', :precision => precision, :scale => scale },
|
713
|
-
Float => { :primitive => 'DOUBLE PRECISION' }
|
714
|
-
).freeze
|
715
|
-
end
|
716
|
-
end # module ClassMethods
|
717
|
-
end # module PostgresAdapter
|
718
|
-
|
719
|
-
module Sqlite3Adapter
|
720
|
-
# @api private
|
721
|
-
def self.included(base)
|
722
|
-
base.extend ClassMethods
|
723
|
-
end
|
724
|
-
|
725
|
-
# @api semipublic
|
726
|
-
def storage_exists?(storage_name)
|
727
|
-
table_info(storage_name).any?
|
728
|
-
end
|
729
|
-
|
730
|
-
# @api semipublic
|
731
|
-
def field_exists?(storage_name, column_name)
|
732
|
-
table_info(storage_name).any? do |row|
|
733
|
-
row.name == column_name
|
734
|
-
end
|
735
|
-
end
|
736
|
-
|
737
|
-
module SQL #:nodoc:
|
738
|
-
# private ## This cannot be private for current migrations
|
739
|
-
|
740
|
-
# @api private
|
741
|
-
def supports_serial?
|
742
|
-
@supports_serial ||= sqlite_version >= '3.1.0'
|
743
|
-
end
|
744
|
-
|
745
|
-
# @api private
|
746
|
-
def supports_drop_table_if_exists?
|
747
|
-
@supports_drop_table_if_exists ||= sqlite_version >= '3.3.0'
|
748
|
-
end
|
749
|
-
|
750
|
-
# @api private
|
751
|
-
def table_info(table_name)
|
752
|
-
select("PRAGMA table_info(#{quote_name(table_name)})")
|
753
|
-
end
|
754
|
-
|
755
|
-
# @api private
|
756
|
-
def create_table_statement(connection, model, properties)
|
757
|
-
statement = <<-SQL.compress_lines
|
758
|
-
CREATE TABLE #{quote_name(model.storage_name(name))}
|
759
|
-
(#{properties.map { |property| property_schema_statement(connection, property_schema_hash(property)) }.join(', ')}
|
760
|
-
SQL
|
761
|
-
|
762
|
-
# skip adding the primary key if one of the columns is serial. In
|
763
|
-
# SQLite the serial column must be the primary key, so it has already
|
764
|
-
# been defined
|
765
|
-
unless properties.any? { |property| property.serial? }
|
766
|
-
statement << ", PRIMARY KEY(#{properties.key.map { |property| quote_name(property.field) }.join(', ')})"
|
767
|
-
end
|
768
|
-
|
769
|
-
statement << ')'
|
770
|
-
statement
|
771
|
-
end
|
772
|
-
|
773
|
-
# @api private
|
774
|
-
def property_schema_statement(connection, schema)
|
775
|
-
statement = super
|
776
|
-
|
777
|
-
if supports_serial? && schema[:serial]
|
778
|
-
statement << ' PRIMARY KEY AUTOINCREMENT'
|
779
|
-
end
|
780
|
-
|
781
|
-
statement
|
782
|
-
end
|
783
|
-
|
784
|
-
# @api private
|
785
|
-
def sqlite_version
|
786
|
-
@sqlite_version ||= select('SELECT sqlite_version(*)').first.freeze
|
787
|
-
end
|
788
|
-
end # module SQL
|
789
|
-
|
790
|
-
include SQL
|
791
|
-
|
792
|
-
module ClassMethods
|
793
|
-
# Types for SQLite 3 databases.
|
794
|
-
#
|
795
|
-
# @return [Hash] types for SQLite 3 databases.
|
796
|
-
#
|
797
|
-
# @api private
|
798
|
-
def type_map
|
799
|
-
@type_map ||= super.merge(Class => { :primitive => 'VARCHAR' }).freeze
|
800
|
-
end
|
801
|
-
end # module ClassMethods
|
802
|
-
end # module Sqlite3Adapter
|
803
|
-
|
804
|
-
module OracleAdapter
|
805
|
-
# @api private
|
806
|
-
def self.included(base)
|
807
|
-
base.extend ClassMethods
|
808
|
-
end
|
809
|
-
|
810
|
-
# @api semipublic
|
811
|
-
def storage_exists?(storage_name)
|
812
|
-
statement = <<-SQL.compress_lines
|
813
|
-
SELECT COUNT(*)
|
814
|
-
FROM all_tables
|
815
|
-
WHERE owner = ?
|
816
|
-
AND table_name = ?
|
817
|
-
SQL
|
818
|
-
|
819
|
-
select(statement, schema_name, oracle_upcase(storage_name)).first > 0
|
820
|
-
end
|
821
|
-
|
822
|
-
# @api semipublic
|
823
|
-
def sequence_exists?(sequence_name)
|
824
|
-
return false unless sequence_name
|
825
|
-
statement = <<-SQL.compress_lines
|
826
|
-
SELECT COUNT(*)
|
827
|
-
FROM all_sequences
|
828
|
-
WHERE sequence_owner = ?
|
829
|
-
AND sequence_name = ?
|
830
|
-
SQL
|
831
|
-
|
832
|
-
select(statement, schema_name, oracle_upcase(sequence_name)).first > 0
|
833
|
-
end
|
834
|
-
|
835
|
-
# @api semipublic
|
836
|
-
def field_exists?(storage_name, field_name)
|
837
|
-
statement = <<-SQL.compress_lines
|
838
|
-
SELECT COUNT(*)
|
839
|
-
FROM all_tab_columns
|
840
|
-
WHERE owner = ?
|
841
|
-
AND table_name = ?
|
842
|
-
AND column_name = ?
|
843
|
-
SQL
|
844
|
-
|
845
|
-
select(statement, schema_name, oracle_upcase(storage_name), oracle_upcase(field_name)).first > 0
|
846
|
-
end
|
847
|
-
|
848
|
-
# @api semipublic
|
849
|
-
def storage_fields(storage_name)
|
850
|
-
statement = <<-SQL.compress_lines
|
851
|
-
SELECT column_name
|
852
|
-
FROM all_tab_columns
|
853
|
-
WHERE owner = ?
|
854
|
-
AND table_name = ?
|
855
|
-
SQL
|
856
|
-
|
857
|
-
select(statement, schema_name, oracle_upcase(storage_name))
|
858
|
-
end
|
859
|
-
|
860
|
-
# @api semipublic
|
861
|
-
def create_model_storage(model)
|
862
|
-
name = self.name
|
863
|
-
properties = model.properties_with_subclasses(name)
|
864
|
-
table_name = model.storage_name(name)
|
865
|
-
truncate_or_delete = self.class.auto_migrate_with
|
866
|
-
table_is_truncated = truncate_or_delete && @truncated_tables && @truncated_tables[table_name]
|
867
|
-
|
868
|
-
return false if storage_exists?(table_name) && !table_is_truncated
|
869
|
-
return false if properties.empty?
|
870
|
-
|
871
|
-
with_connection do |connection|
|
872
|
-
# if table was truncated then check if all columns for properties are present
|
873
|
-
# TODO: check all other column definition options
|
874
|
-
if table_is_truncated && storage_has_all_fields?(table_name, properties)
|
875
|
-
@truncated_tables[table_name] = nil
|
876
|
-
else
|
877
|
-
# forced drop of table if properties are different
|
878
|
-
if truncate_or_delete
|
879
|
-
destroy_model_storage(model, true)
|
880
|
-
end
|
881
|
-
|
882
|
-
statements = [ create_table_statement(connection, model, properties) ]
|
883
|
-
statements.concat(create_index_statements(model))
|
884
|
-
statements.concat(create_unique_index_statements(model))
|
885
|
-
statements.concat(create_sequence_statements(model))
|
886
|
-
|
887
|
-
statements.each do |statement|
|
888
|
-
command = connection.create_command(statement)
|
889
|
-
command.execute_non_query
|
890
|
-
end
|
891
|
-
end
|
892
|
-
|
893
|
-
end
|
894
|
-
|
895
|
-
true
|
896
|
-
end
|
897
|
-
|
898
|
-
# @api semipublic
|
899
|
-
def destroy_model_storage(model, forced = false)
|
900
|
-
table_name = model.storage_name(name)
|
901
|
-
klass = self.class
|
902
|
-
truncate_or_delete = klass.auto_migrate_with
|
903
|
-
if storage_exists?(table_name)
|
904
|
-
if truncate_or_delete && !forced
|
905
|
-
case truncate_or_delete
|
906
|
-
when :truncate
|
907
|
-
execute(truncate_table_statement(model))
|
908
|
-
when :delete
|
909
|
-
execute(delete_table_statement(model))
|
910
|
-
else
|
911
|
-
raise ArgumentError, "Unsupported auto_migrate_with option"
|
912
|
-
end
|
913
|
-
@truncated_tables ||= {}
|
914
|
-
@truncated_tables[table_name] = true
|
915
|
-
else
|
916
|
-
execute(drop_table_statement(model))
|
917
|
-
@truncated_tables[table_name] = nil if @truncated_tables
|
918
|
-
end
|
919
|
-
end
|
920
|
-
# added destroy of sequences
|
921
|
-
reset_sequences = klass.auto_migrate_reset_sequences
|
922
|
-
table_is_truncated = @truncated_tables && @truncated_tables[table_name]
|
923
|
-
unless truncate_or_delete && !reset_sequences && !forced
|
924
|
-
if sequence_exists?(model_sequence_name(model))
|
925
|
-
statement = if table_is_truncated && !forced
|
926
|
-
reset_sequence_statement(model)
|
927
|
-
else
|
928
|
-
drop_sequence_statement(model)
|
929
|
-
end
|
930
|
-
execute(statement) if statement
|
931
|
-
end
|
932
|
-
end
|
933
|
-
true
|
934
|
-
end
|
935
|
-
|
936
|
-
private
|
937
|
-
|
938
|
-
def storage_has_all_fields?(table_name, properties)
|
939
|
-
properties.map { |property| oracle_upcase(property.field) }.sort == storage_fields(table_name).sort
|
940
|
-
end
|
941
|
-
|
942
|
-
# If table or column name contains just lowercase characters then do uppercase
|
943
|
-
# as uppercase version will be used in Oracle data dictionary tables
|
944
|
-
def oracle_upcase(name)
|
945
|
-
name =~ /[A-Z]/ ? name : name.upcase
|
946
|
-
end
|
947
|
-
|
948
|
-
module SQL #:nodoc:
|
949
|
-
# private ## This cannot be private for current migrations
|
950
|
-
|
951
|
-
# @api private
|
952
|
-
def schema_name
|
953
|
-
@schema_name ||= select("SELECT SYS_CONTEXT('userenv','current_schema') FROM dual").first.freeze
|
954
|
-
end
|
955
|
-
|
956
|
-
# @api private
|
957
|
-
def create_sequence_statements(model)
|
958
|
-
name = self.name
|
959
|
-
table_name = model.storage_name(name)
|
960
|
-
serial = model.serial(name)
|
961
|
-
|
962
|
-
statements = []
|
963
|
-
if sequence_name = model_sequence_name(model)
|
964
|
-
sequence_name = quote_name(sequence_name)
|
965
|
-
column_name = quote_name(serial.field)
|
966
|
-
|
967
|
-
statements << <<-SQL.compress_lines
|
968
|
-
CREATE SEQUENCE #{sequence_name} NOCACHE
|
969
|
-
SQL
|
970
|
-
|
971
|
-
# create trigger only if custom sequence name was not specified
|
972
|
-
unless serial.options[:sequence]
|
973
|
-
statements << <<-SQL.compress_lines
|
974
|
-
CREATE OR REPLACE TRIGGER #{quote_name(default_trigger_name(table_name))}
|
975
|
-
BEFORE INSERT ON #{quote_name(table_name)} FOR EACH ROW
|
976
|
-
BEGIN
|
977
|
-
IF inserting THEN
|
978
|
-
IF :new.#{column_name} IS NULL THEN
|
979
|
-
SELECT #{sequence_name}.NEXTVAL INTO :new.#{column_name} FROM dual;
|
980
|
-
END IF;
|
981
|
-
END IF;
|
982
|
-
END;
|
983
|
-
SQL
|
984
|
-
end
|
985
|
-
end
|
986
|
-
|
987
|
-
statements
|
988
|
-
end
|
989
|
-
|
990
|
-
# @api private
|
991
|
-
def drop_sequence_statement(model)
|
992
|
-
if sequence_name = model_sequence_name(model)
|
993
|
-
"DROP SEQUENCE #{quote_name(sequence_name)}"
|
994
|
-
else
|
995
|
-
nil
|
996
|
-
end
|
997
|
-
end
|
998
|
-
|
999
|
-
# @api private
|
1000
|
-
def reset_sequence_statement(model)
|
1001
|
-
if sequence_name = model_sequence_name(model)
|
1002
|
-
sequence_name = quote_name(sequence_name)
|
1003
|
-
<<-SQL.compress_lines
|
1004
|
-
DECLARE
|
1005
|
-
cval INTEGER;
|
1006
|
-
BEGIN
|
1007
|
-
SELECT #{sequence_name}.NEXTVAL INTO cval FROM dual;
|
1008
|
-
EXECUTE IMMEDIATE 'ALTER SEQUENCE #{sequence_name} INCREMENT BY -' || cval || ' MINVALUE 0';
|
1009
|
-
SELECT #{sequence_name}.NEXTVAL INTO cval FROM dual;
|
1010
|
-
EXECUTE IMMEDIATE 'ALTER SEQUENCE #{sequence_name} INCREMENT BY 1';
|
1011
|
-
END;
|
1012
|
-
SQL
|
1013
|
-
else
|
1014
|
-
nil
|
1015
|
-
end
|
1016
|
-
|
1017
|
-
end
|
1018
|
-
|
1019
|
-
# @api private
|
1020
|
-
def truncate_table_statement(model)
|
1021
|
-
"TRUNCATE TABLE #{quote_name(model.storage_name(name))}"
|
1022
|
-
end
|
1023
|
-
|
1024
|
-
# @api private
|
1025
|
-
def delete_table_statement(model)
|
1026
|
-
"DELETE FROM #{quote_name(model.storage_name(name))}"
|
1027
|
-
end
|
1028
|
-
|
1029
|
-
private
|
1030
|
-
|
1031
|
-
def model_sequence_name(model)
|
1032
|
-
name = self.name
|
1033
|
-
table_name = model.storage_name(name)
|
1034
|
-
serial = model.serial(name)
|
1035
|
-
|
1036
|
-
if serial
|
1037
|
-
serial.options[:sequence] || default_sequence_name(table_name)
|
1038
|
-
else
|
1039
|
-
nil
|
1040
|
-
end
|
1041
|
-
end
|
1042
|
-
|
1043
|
-
def default_sequence_name(table_name)
|
1044
|
-
# truncate table name if necessary to fit in max length of identifier
|
1045
|
-
"#{table_name[0,self.class::IDENTIFIER_MAX_LENGTH-4]}_seq"
|
1046
|
-
end
|
1047
|
-
|
1048
|
-
def default_trigger_name(table_name)
|
1049
|
-
# truncate table name if necessary to fit in max length of identifier
|
1050
|
-
"#{table_name[0,self.class::IDENTIFIER_MAX_LENGTH-4]}_pkt"
|
1051
|
-
end
|
1052
|
-
|
1053
|
-
end # module SQL
|
1054
|
-
|
1055
|
-
include SQL
|
1056
|
-
|
1057
|
-
module ClassMethods
|
1058
|
-
# Types for Oracle databases.
|
1059
|
-
#
|
1060
|
-
# @return [Hash] types for Oracle databases.
|
1061
|
-
#
|
1062
|
-
# @api private
|
1063
|
-
def type_map
|
1064
|
-
length = Property::DEFAULT_LENGTH
|
1065
|
-
precision = Property::DEFAULT_PRECISION
|
1066
|
-
scale = Property::DEFAULT_SCALE_BIGDECIMAL
|
1067
|
-
|
1068
|
-
@type_map ||= {
|
1069
|
-
Integer => { :primitive => 'NUMBER', :precision => precision, :scale => 0 },
|
1070
|
-
String => { :primitive => 'VARCHAR2', :length => length },
|
1071
|
-
Class => { :primitive => 'VARCHAR2', :length => length },
|
1072
|
-
BigDecimal => { :primitive => 'NUMBER', :precision => precision, :scale => nil },
|
1073
|
-
Float => { :primitive => 'BINARY_FLOAT', },
|
1074
|
-
DateTime => { :primitive => 'DATE' },
|
1075
|
-
Date => { :primitive => 'DATE' },
|
1076
|
-
Time => { :primitive => 'DATE' },
|
1077
|
-
TrueClass => { :primitive => 'NUMBER', :precision => 1, :scale => 0 },
|
1078
|
-
Types::Text => { :primitive => 'CLOB' },
|
1079
|
-
}.freeze
|
1080
|
-
end
|
1081
|
-
|
1082
|
-
# Use table truncate or delete for auto_migrate! to speed up test execution
|
1083
|
-
#
|
1084
|
-
# @param [Symbol] :truncate, :delete or :drop_and_create (or nil)
|
1085
|
-
# do not specify parameter to return current value
|
1086
|
-
#
|
1087
|
-
# @return [Symbol] current value of auto_migrate_with option (nil returned for :drop_and_create)
|
1088
|
-
#
|
1089
|
-
# @api semipublic
|
1090
|
-
def auto_migrate_with(value = :not_specified)
|
1091
|
-
return @auto_migrate_with if value == :not_specified
|
1092
|
-
value = nil if value == :drop_and_create
|
1093
|
-
raise ArgumentError unless [nil, :truncate, :delete].include?(value)
|
1094
|
-
@auto_migrate_with = value
|
1095
|
-
end
|
1096
|
-
|
1097
|
-
# Set if sequences will or will not be reset during auto_migrate!
|
1098
|
-
#
|
1099
|
-
# @param [TrueClass, FalseClass] reset sequences?
|
1100
|
-
# do not specify parameter to return current value
|
1101
|
-
#
|
1102
|
-
# @return [Symbol] current value of auto_migrate_reset_sequences option (default value is true)
|
1103
|
-
#
|
1104
|
-
# @api semipublic
|
1105
|
-
def auto_migrate_reset_sequences(value = :not_specified)
|
1106
|
-
return @auto_migrate_reset_sequences.nil? ? true : @auto_migrate_reset_sequences if value == :not_specified
|
1107
|
-
raise ArgumentError unless [true, false].include?(value)
|
1108
|
-
@auto_migrate_reset_sequences = value
|
1109
|
-
end
|
1110
|
-
|
1111
|
-
end # module ClassMethods
|
1112
|
-
end # module PostgresAdapter
|
1113
|
-
|
1114
|
-
module SqlserverAdapter
|
1115
|
-
DEFAULT_CHARACTER_SET = 'utf8'.freeze
|
1116
|
-
|
1117
|
-
# @api private
|
1118
|
-
def self.included(base)
|
1119
|
-
base.extend ClassMethods
|
1120
|
-
end
|
1121
|
-
|
1122
|
-
# @api semipublic
|
1123
|
-
def storage_exists?(storage_name)
|
1124
|
-
select("SELECT name FROM sysobjects WHERE name LIKE ?", storage_name).first == storage_name
|
1125
|
-
end
|
1126
|
-
|
1127
|
-
# @api semipublic
|
1128
|
-
def field_exists?(storage_name, field_name)
|
1129
|
-
result = select("SELECT c.name FROM sysobjects as o JOIN syscolumns AS c ON o.id = c.id WHERE o.name = #{quote_name(storage_name)} AND c.name LIKE ?", field_name).first
|
1130
|
-
result ? result.field == field_name : false
|
1131
|
-
end
|
1132
|
-
|
1133
|
-
module SQL #:nodoc:
|
1134
|
-
# private ## This cannot be private for current migrations
|
1135
|
-
|
1136
|
-
# @api private
|
1137
|
-
def supports_serial?
|
1138
|
-
true
|
1139
|
-
end
|
1140
|
-
|
1141
|
-
# @api private
|
1142
|
-
def supports_drop_table_if_exists?
|
1143
|
-
false
|
1144
|
-
end
|
1145
|
-
|
1146
|
-
# @api private
|
1147
|
-
def schema_name
|
1148
|
-
# TODO: is there a cleaner way to find out the current DB we are connected to?
|
1149
|
-
@options[:path].split('/').last
|
1150
|
-
end
|
1151
|
-
|
1152
|
-
# TODO: update dkubb/dm-more/dm-migrations to use schema_name and remove this
|
1153
|
-
|
1154
|
-
alias db_name schema_name
|
1155
|
-
|
1156
|
-
# @api private
|
1157
|
-
def create_table_statement(connection, model, properties)
|
1158
|
-
statement = <<-SQL.compress_lines
|
1159
|
-
CREATE TABLE #{quote_name(model.storage_name(name))}
|
1160
|
-
(#{properties.map { |property| property_schema_statement(connection, property_schema_hash(property)) }.join(', ')}
|
1161
|
-
SQL
|
1162
|
-
|
1163
|
-
unless properties.any? { |property| property.serial? }
|
1164
|
-
statement << ", PRIMARY KEY(#{properties.key.map { |property| quote_name(property.field) }.join(', ')})"
|
1165
|
-
end
|
1166
|
-
|
1167
|
-
statement << ')'
|
1168
|
-
statement
|
1169
|
-
end
|
1170
|
-
|
1171
|
-
# @api private
|
1172
|
-
def property_schema_hash(property)
|
1173
|
-
schema = super
|
1174
|
-
|
1175
|
-
min = property.min
|
1176
|
-
max = property.max
|
1177
|
-
|
1178
|
-
if property.primitive == Integer && min && max
|
1179
|
-
schema[:primitive] = integer_column_statement(min..max)
|
1180
|
-
end
|
1181
|
-
|
1182
|
-
if schema[:primitive] == 'TEXT'
|
1183
|
-
schema.delete(:default)
|
1184
|
-
end
|
1185
|
-
|
1186
|
-
schema
|
1187
|
-
end
|
1188
|
-
|
1189
|
-
# @api private
|
1190
|
-
def property_schema_statement(connection, schema)
|
1191
|
-
if supports_serial? && schema[:serial]
|
1192
|
-
statement = quote_name(schema[:name])
|
1193
|
-
statement << " #{schema[:primitive]}"
|
1194
|
-
|
1195
|
-
length = schema[:length]
|
1196
|
-
|
1197
|
-
if schema[:precision] && schema[:scale]
|
1198
|
-
statement << "(#{[ :precision, :scale ].map { |key| connection.quote_value(schema[key]) }.join(', ')})"
|
1199
|
-
elsif length
|
1200
|
-
statement << "(#{connection.quote_value(length)})"
|
1201
|
-
end
|
1202
|
-
|
1203
|
-
statement << ' IDENTITY'
|
1204
|
-
else
|
1205
|
-
statement = super
|
1206
|
-
end
|
1207
|
-
|
1208
|
-
statement
|
1209
|
-
end
|
1210
|
-
|
1211
|
-
# @api private
|
1212
|
-
def character_set
|
1213
|
-
@character_set ||= show_variable('character_set_connection') || DEFAULT_CHARACTER_SET
|
1214
|
-
end
|
1215
|
-
|
1216
|
-
# @api private
|
1217
|
-
def collation
|
1218
|
-
@collation ||= show_variable('collation_connection') || DEFAULT_COLLATION
|
1219
|
-
end
|
1220
|
-
|
1221
|
-
# @api private
|
1222
|
-
def show_variable(name)
|
1223
|
-
raise "SqlserverAdapter#show_variable: Not implemented"
|
1224
|
-
end
|
1225
|
-
|
1226
|
-
private
|
1227
|
-
|
1228
|
-
# Return SQL statement for the integer column
|
1229
|
-
#
|
1230
|
-
# @param [Range] range
|
1231
|
-
# the min/max allowed integers
|
1232
|
-
#
|
1233
|
-
# @return [String]
|
1234
|
-
# the statement to create the integer column
|
1235
|
-
#
|
1236
|
-
# @api private
|
1237
|
-
def integer_column_statement(range)
|
1238
|
-
min = range.first
|
1239
|
-
max = range.last
|
1240
|
-
|
1241
|
-
smallint = 2**15
|
1242
|
-
integer = 2**31
|
1243
|
-
bigint = 2**63
|
1244
|
-
|
1245
|
-
if min >= 0 && max < 2**8 then 'TINYINT'
|
1246
|
-
elsif min >= -smallint && max < smallint then 'SMALLINT'
|
1247
|
-
elsif min >= -integer && max < integer then 'INT'
|
1248
|
-
elsif min >= -bigint && max < bigint then 'BIGINT'
|
1249
|
-
else
|
1250
|
-
raise ArgumentError, "min #{min} and max #{max} exceeds supported range"
|
1251
|
-
end
|
1252
|
-
end
|
1253
|
-
|
1254
|
-
end # module SQL
|
1255
|
-
|
1256
|
-
include SQL
|
1257
|
-
|
1258
|
-
module ClassMethods
|
1259
|
-
# Types for Sqlserver databases.
|
1260
|
-
#
|
1261
|
-
# @return [Hash] types for Sqlserver databases.
|
1262
|
-
#
|
1263
|
-
# @api private
|
1264
|
-
def type_map
|
1265
|
-
length = Property::DEFAULT_LENGTH
|
1266
|
-
precision = Property::DEFAULT_PRECISION
|
1267
|
-
scale = Property::DEFAULT_SCALE_BIGDECIMAL
|
1268
|
-
|
1269
|
-
@type_map ||= super.merge(
|
1270
|
-
DateTime => { :primitive => 'DATETIME' },
|
1271
|
-
Date => { :primitive => 'SMALLDATETIME' },
|
1272
|
-
Time => { :primitive => 'SMALLDATETIME' },
|
1273
|
-
TrueClass => { :primitive => 'BIT', },
|
1274
|
-
Types::Text => { :primitive => 'NVARCHAR', :length => 'max' }
|
1275
|
-
).freeze
|
1276
|
-
end
|
1277
|
-
end # module ClassMethods
|
1278
|
-
end # module SqlserverAdapter
|
1279
|
-
|
1280
|
-
|
1281
|
-
module Repository
|
1282
|
-
# Determine whether a particular named storage exists in this repository
|
1283
|
-
#
|
1284
|
-
# @param [String]
|
1285
|
-
# storage_name name of the storage to test for
|
1286
|
-
#
|
1287
|
-
# @return [Boolean]
|
1288
|
-
# true if the data-store +storage_name+ exists
|
1289
|
-
#
|
1290
|
-
# @api semipublic
|
1291
|
-
def storage_exists?(storage_name)
|
1292
|
-
adapter = self.adapter
|
1293
|
-
if adapter.respond_to?(:storage_exists?)
|
1294
|
-
adapter.storage_exists?(storage_name)
|
1295
|
-
end
|
1296
|
-
end
|
1297
|
-
|
1298
|
-
# @api semipublic
|
1299
|
-
def upgrade_model_storage(model)
|
1300
|
-
adapter = self.adapter
|
1301
|
-
if adapter.respond_to?(:upgrade_model_storage)
|
1302
|
-
adapter.upgrade_model_storage(model)
|
1303
|
-
end
|
1304
|
-
end
|
1305
|
-
|
1306
|
-
# @api semipublic
|
1307
|
-
def create_model_storage(model)
|
1308
|
-
adapter = self.adapter
|
1309
|
-
if adapter.respond_to?(:create_model_storage)
|
1310
|
-
adapter.create_model_storage(model)
|
1311
|
-
end
|
1312
|
-
end
|
1313
|
-
|
1314
|
-
# @api semipublic
|
1315
|
-
def destroy_model_storage(model)
|
1316
|
-
adapter = self.adapter
|
1317
|
-
if adapter.respond_to?(:destroy_model_storage)
|
1318
|
-
adapter.destroy_model_storage(model)
|
1319
|
-
end
|
1320
|
-
end
|
1321
|
-
|
1322
|
-
# Destructively automigrates the data-store to match the model.
|
1323
|
-
# First migrates all models down and then up.
|
1324
|
-
# REPEAT: THIS IS DESTRUCTIVE
|
1325
|
-
#
|
1326
|
-
# @api public
|
1327
|
-
def auto_migrate!
|
1328
|
-
DataMapper.auto_migrate!(name)
|
1329
|
-
end
|
1330
|
-
|
1331
|
-
# Safely migrates the data-store to match the model
|
1332
|
-
# preserving data already in the data-store
|
1333
|
-
#
|
1334
|
-
# @api public
|
1335
|
-
def auto_upgrade!
|
1336
|
-
DataMapper.auto_upgrade!(name)
|
1337
|
-
end
|
1338
|
-
end # module Repository
|
1339
|
-
|
1340
|
-
module Model
|
1341
|
-
# @api private
|
1342
|
-
def self.included(mod)
|
1343
|
-
mod.descendants.each { |model| model.extend self }
|
1344
|
-
end
|
1345
|
-
|
1346
|
-
# @api semipublic
|
1347
|
-
def storage_exists?(repository_name = default_repository_name)
|
1348
|
-
repository(repository_name).storage_exists?(storage_name(repository_name))
|
1349
|
-
end
|
1350
|
-
|
1351
|
-
# Destructively automigrates the data-store to match the model
|
1352
|
-
# REPEAT: THIS IS DESTRUCTIVE
|
1353
|
-
#
|
1354
|
-
# @param Symbol repository_name the repository to be migrated
|
1355
|
-
#
|
1356
|
-
# @api public
|
1357
|
-
def auto_migrate!(repository_name = self.repository_name)
|
1358
|
-
assert_valid
|
1359
|
-
auto_migrate_down!(repository_name)
|
1360
|
-
auto_migrate_up!(repository_name)
|
1361
|
-
end
|
1362
|
-
|
1363
|
-
# Safely migrates the data-store to match the model
|
1364
|
-
# preserving data already in the data-store
|
1365
|
-
#
|
1366
|
-
# @param Symbol repository_name the repository to be migrated
|
1367
|
-
#
|
1368
|
-
# @api public
|
1369
|
-
def auto_upgrade!(repository_name = self.repository_name)
|
1370
|
-
assert_valid
|
1371
|
-
base_model = self.base_model
|
1372
|
-
if base_model == self
|
1373
|
-
repository(repository_name).upgrade_model_storage(self)
|
1374
|
-
else
|
1375
|
-
base_model.auto_upgrade!(repository_name)
|
1376
|
-
end
|
1377
|
-
end
|
1378
|
-
|
1379
|
-
# Destructively migrates the data-store down, which basically
|
1380
|
-
# deletes all the models.
|
1381
|
-
# REPEAT: THIS IS DESTRUCTIVE
|
1382
|
-
#
|
1383
|
-
# @param Symbol repository_name the repository to be migrated
|
1384
|
-
#
|
1385
|
-
# @api private
|
1386
|
-
def auto_migrate_down!(repository_name = self.repository_name)
|
1387
|
-
assert_valid
|
1388
|
-
base_model = self.base_model
|
1389
|
-
if base_model == self
|
1390
|
-
repository(repository_name).destroy_model_storage(self)
|
1391
|
-
else
|
1392
|
-
base_model.auto_migrate_down!(repository_name)
|
1393
|
-
end
|
1394
|
-
end
|
1395
|
-
|
1396
|
-
# Auto migrates the data-store to match the model
|
1397
|
-
#
|
1398
|
-
# @param Symbol repository_name the repository to be migrated
|
1399
|
-
#
|
1400
|
-
# @api private
|
1401
|
-
def auto_migrate_up!(repository_name = self.repository_name)
|
1402
|
-
assert_valid
|
1403
|
-
base_model = self.base_model
|
1404
|
-
if base_model == self
|
1405
|
-
repository(repository_name).create_model_storage(self)
|
1406
|
-
else
|
1407
|
-
base_model.auto_migrate_up!(repository_name)
|
1408
|
-
end
|
1409
|
-
end
|
1410
|
-
end # module Model
|
1411
|
-
end
|
1412
|
-
|
1413
|
-
module Adapters
|
1414
|
-
extendable do
|
1415
|
-
|
1416
|
-
# @api private
|
1417
|
-
def const_added(const_name)
|
1418
|
-
if DataMapper::Migrations.const_defined?(const_name)
|
1419
|
-
adapter = const_get(const_name)
|
1420
|
-
adapter.send(:include, DataMapper::Migrations.const_get(const_name))
|
1421
|
-
end
|
1422
|
-
|
1423
|
-
super
|
1424
|
-
end
|
1425
|
-
end
|
1426
|
-
end # module Adapters
|
1427
|
-
end # module DataMapper
|