dm-hibernate-migrations 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/lib/dm-migrations.rb +3 -0
- data/lib/dm-migrations/adapters/dm-do-adapter.rb +284 -0
- data/lib/dm-migrations/adapters/dm-mysql-adapter.rb +283 -0
- data/lib/dm-migrations/adapters/dm-oracle-adapter.rb +321 -0
- data/lib/dm-migrations/adapters/dm-postgres-adapter.rb +159 -0
- data/lib/dm-migrations/adapters/dm-sqlite-adapter.rb +96 -0
- data/lib/dm-migrations/adapters/dm-sqlserver-adapter.rb +177 -0
- data/lib/dm-migrations/adapters/dm-yaml-adapter.rb +23 -0
- data/lib/dm-migrations/auto_migration.rb +237 -0
- data/lib/dm-migrations/migration.rb +217 -0
- data/lib/dm-migrations/migration_runner.rb +85 -0
- data/lib/dm-migrations/sql.rb +5 -0
- data/lib/dm-migrations/sql/column.rb +5 -0
- data/lib/dm-migrations/sql/mysql.rb +53 -0
- data/lib/dm-migrations/sql/postgres.rb +78 -0
- data/lib/dm-migrations/sql/sqlite.rb +45 -0
- data/lib/dm-migrations/sql/table.rb +15 -0
- data/lib/dm-migrations/sql/table_creator.rb +102 -0
- data/lib/dm-migrations/sql/table_modifier.rb +51 -0
- data/lib/spec/example/migration_example_group.rb +73 -0
- data/lib/spec/matchers/migration_matchers.rb +106 -0
- data/spec/integration/auto_migration_spec.rb +506 -0
- data/spec/integration/migration_runner_spec.rb +89 -0
- data/spec/integration/migration_spec.rb +138 -0
- data/spec/integration/sql_spec.rb +190 -0
- data/spec/isolated/require_after_setup_spec.rb +30 -0
- data/spec/isolated/require_before_setup_spec.rb +30 -0
- data/spec/isolated/require_spec.rb +25 -0
- data/spec/rcov.opts +6 -0
- data/spec/spec.opts +4 -0
- data/spec/spec_helper.rb +16 -0
- data/spec/unit/migration_spec.rb +453 -0
- data/spec/unit/sql/column_spec.rb +14 -0
- data/spec/unit/sql/postgres_spec.rb +97 -0
- data/spec/unit/sql/sqlite_extensions_spec.rb +108 -0
- data/spec/unit/sql/table_creator_spec.rb +94 -0
- data/spec/unit/sql/table_modifier_spec.rb +49 -0
- data/spec/unit/sql/table_spec.rb +28 -0
- data/spec/unit/sql_spec.rb +7 -0
- metadata +157 -0
@@ -0,0 +1,284 @@
|
|
1
|
+
require 'dm-migrations/auto_migration'
|
2
|
+
|
3
|
+
module DataMapper
|
4
|
+
module Migrations
|
5
|
+
|
6
|
+
module DataObjectsAdapter
|
7
|
+
|
8
|
+
def dialect
|
9
|
+
self.class.to_s.sub(/.*::/, '').sub(/Adapter$/,'')
|
10
|
+
end
|
11
|
+
|
12
|
+
# Returns whether the storage_name exists.
|
13
|
+
#
|
14
|
+
# @param [String] storage_name
|
15
|
+
# a String defining the name of a storage, for example a table name.
|
16
|
+
#
|
17
|
+
# @return [Boolean]
|
18
|
+
# true if the storage exists
|
19
|
+
#
|
20
|
+
# @api semipublic
|
21
|
+
def storage_exists?(storage_name)
|
22
|
+
statement = <<-SQL.compress_lines
|
23
|
+
SELECT COUNT(*)
|
24
|
+
FROM "information_schema"."tables"
|
25
|
+
WHERE "table_type" = 'BASE TABLE'
|
26
|
+
AND "table_schema" = ?
|
27
|
+
AND "table_name" = ?
|
28
|
+
SQL
|
29
|
+
|
30
|
+
select(statement, schema_name, storage_name).first > 0
|
31
|
+
end
|
32
|
+
|
33
|
+
# Returns whether the field exists.
|
34
|
+
#
|
35
|
+
# @param [String] storage_name
|
36
|
+
# a String defining the name of a storage, for example a table name.
|
37
|
+
# @param [String] field
|
38
|
+
# a String defining the name of a field, for example a column name.
|
39
|
+
#
|
40
|
+
# @return [Boolean]
|
41
|
+
# true if the field exists.
|
42
|
+
#
|
43
|
+
# @api semipublic
|
44
|
+
def field_exists?(storage_name, column_name)
|
45
|
+
statement = <<-SQL.compress_lines
|
46
|
+
SELECT COUNT(*)
|
47
|
+
FROM "information_schema"."columns"
|
48
|
+
WHERE "table_schema" = ?
|
49
|
+
AND "table_name" = ?
|
50
|
+
AND "column_name" = ?
|
51
|
+
SQL
|
52
|
+
|
53
|
+
select(statement, schema_name, storage_name, column_name).first > 0
|
54
|
+
end
|
55
|
+
|
56
|
+
# @api semipublic
|
57
|
+
def upgrade_model_storage(model)
|
58
|
+
name = self.name
|
59
|
+
properties = model.properties_with_subclasses(name)
|
60
|
+
|
61
|
+
if success = create_model_storage(model)
|
62
|
+
return properties
|
63
|
+
end
|
64
|
+
|
65
|
+
table_name = model.storage_name(name)
|
66
|
+
|
67
|
+
with_connection do |connection|
|
68
|
+
properties.map do |property|
|
69
|
+
schema_hash = property_schema_hash(property)
|
70
|
+
next if field_exists?(table_name, schema_hash[:name])
|
71
|
+
|
72
|
+
statement = alter_table_add_column_statement(connection, table_name, schema_hash)
|
73
|
+
command = connection.create_command(statement)
|
74
|
+
command.execute_non_query
|
75
|
+
|
76
|
+
property
|
77
|
+
end.compact
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
# @api semipublic
|
82
|
+
def create_model_storage(model)
|
83
|
+
name = self.name
|
84
|
+
properties = model.properties_with_subclasses(name)
|
85
|
+
|
86
|
+
return false if storage_exists?(model.storage_name(name))
|
87
|
+
return false if properties.empty?
|
88
|
+
|
89
|
+
with_connection do |connection|
|
90
|
+
statements = [ create_table_statement(connection, model, properties) ]
|
91
|
+
statements.concat(create_index_statements(model))
|
92
|
+
statements.concat(create_unique_index_statements(model))
|
93
|
+
|
94
|
+
statements.each do |statement|
|
95
|
+
command = connection.create_command(statement)
|
96
|
+
command.execute_non_query
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
true
|
101
|
+
end
|
102
|
+
|
103
|
+
# @api semipublic
|
104
|
+
def destroy_model_storage(model)
|
105
|
+
return true unless supports_drop_table_if_exists? || storage_exists?(model.storage_name(name))
|
106
|
+
execute(drop_table_statement(model))
|
107
|
+
true
|
108
|
+
end
|
109
|
+
|
110
|
+
module SQL #:nodoc:
|
111
|
+
# private ## This cannot be private for current migrations
|
112
|
+
|
113
|
+
# Adapters that support AUTO INCREMENT fields for CREATE TABLE
|
114
|
+
# statements should overwrite this to return true
|
115
|
+
#
|
116
|
+
# @api private
|
117
|
+
def supports_serial?
|
118
|
+
false
|
119
|
+
end
|
120
|
+
|
121
|
+
# @api private
|
122
|
+
def supports_drop_table_if_exists?
|
123
|
+
false
|
124
|
+
end
|
125
|
+
|
126
|
+
# @api private
|
127
|
+
def schema_name
|
128
|
+
raise NotImplementedError, "#{self.class}#schema_name not implemented"
|
129
|
+
end
|
130
|
+
|
131
|
+
# @api private
|
132
|
+
def alter_table_add_column_statement(connection, table_name, schema_hash)
|
133
|
+
"ALTER TABLE #{quote_name(table_name)} ADD COLUMN #{property_schema_statement(connection, schema_hash)}"
|
134
|
+
end
|
135
|
+
|
136
|
+
# @api private
|
137
|
+
def create_table_statement(connection, model, properties)
|
138
|
+
statement = <<-SQL.compress_lines
|
139
|
+
CREATE TABLE #{quote_name(model.storage_name(name))}
|
140
|
+
(#{properties.map { |property| property_schema_statement(connection, property_schema_hash(property)) }.join(', ')},
|
141
|
+
PRIMARY KEY(#{ properties.key.map { |property| quote_name(property.field) }.join(', ')}))
|
142
|
+
SQL
|
143
|
+
|
144
|
+
statement
|
145
|
+
end
|
146
|
+
|
147
|
+
# @api private
|
148
|
+
def drop_table_statement(model)
|
149
|
+
table_name = quote_name(model.storage_name(name))
|
150
|
+
if supports_drop_table_if_exists?
|
151
|
+
"DROP TABLE IF EXISTS #{table_name}"
|
152
|
+
else
|
153
|
+
"DROP TABLE #{table_name}"
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
# @api private
|
158
|
+
def create_index_statements(model)
|
159
|
+
name = self.name
|
160
|
+
table_name = model.storage_name(name)
|
161
|
+
|
162
|
+
indexes(model).map do |index_name, fields|
|
163
|
+
<<-SQL.compress_lines
|
164
|
+
CREATE INDEX #{quote_name("index_#{table_name}_#{index_name}")} ON
|
165
|
+
#{quote_name(table_name)} (#{fields.map { |field| quote_name(field) }.join(', ')})
|
166
|
+
SQL
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
# @api private
|
171
|
+
def create_unique_index_statements(model)
|
172
|
+
name = self.name
|
173
|
+
table_name = model.storage_name(name)
|
174
|
+
key = model.key(name).map { |property| property.field }
|
175
|
+
unique_indexes = unique_indexes(model).reject { |index_name, fields| fields == key }
|
176
|
+
|
177
|
+
unique_indexes.map do |index_name, fields|
|
178
|
+
<<-SQL.compress_lines
|
179
|
+
CREATE UNIQUE INDEX #{quote_name("unique_#{table_name}_#{index_name}")} ON
|
180
|
+
#{quote_name(table_name)} (#{fields.map { |field| quote_name(field) }.join(', ')})
|
181
|
+
SQL
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
# @api private
|
186
|
+
def property_schema_hash(property)
|
187
|
+
primitive = property.primitive
|
188
|
+
type = property.type
|
189
|
+
type_map = self.class.type_map
|
190
|
+
|
191
|
+
schema = (type_map[property.class] || type_map[primitive] || type_map[type]).merge(:name => property.field)
|
192
|
+
|
193
|
+
schema_primitive = schema[:primitive]
|
194
|
+
|
195
|
+
if primitive == String && schema_primitive != 'TEXT' && schema_primitive != 'CLOB' && schema_primitive != 'NVARCHAR'
|
196
|
+
schema[:length] = property.length
|
197
|
+
elsif primitive == BigDecimal || primitive == Float
|
198
|
+
schema[:precision] = property.precision
|
199
|
+
schema[:scale] = property.scale
|
200
|
+
end
|
201
|
+
|
202
|
+
schema[:allow_nil] = property.allow_nil?
|
203
|
+
schema[:serial] = property.serial?
|
204
|
+
|
205
|
+
default = property.default
|
206
|
+
|
207
|
+
if default.nil? || default.respond_to?(:call)
|
208
|
+
# remove the default if the property does not allow nil
|
209
|
+
schema.delete(:default) unless schema[:allow_nil]
|
210
|
+
else
|
211
|
+
schema[:default] = if type.respond_to?(:dump)
|
212
|
+
type.dump(default, property)
|
213
|
+
else
|
214
|
+
default
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
schema
|
219
|
+
end
|
220
|
+
|
221
|
+
# @api private
|
222
|
+
def property_schema_statement(connection, schema)
|
223
|
+
statement = quote_name(schema[:name])
|
224
|
+
statement << " #{schema[:primitive]}"
|
225
|
+
|
226
|
+
length = schema[:length]
|
227
|
+
|
228
|
+
if schema[:precision] && schema[:scale]
|
229
|
+
statement << "(#{[ :precision, :scale ].map { |key| connection.quote_value(schema[key]) }.join(', ')})"
|
230
|
+
elsif length == 'max'
|
231
|
+
statement << '(max)'
|
232
|
+
elsif length
|
233
|
+
statement << "(#{connection.quote_value(length)})"
|
234
|
+
end
|
235
|
+
|
236
|
+
statement << " DEFAULT #{connection.quote_value(schema[:default])}" if schema.key?(:default)
|
237
|
+
statement << ' NOT NULL' unless schema[:allow_nil]
|
238
|
+
statement
|
239
|
+
end
|
240
|
+
|
241
|
+
# @api private
|
242
|
+
def indexes(model)
|
243
|
+
model.properties(name).indexes
|
244
|
+
end
|
245
|
+
|
246
|
+
# @api private
|
247
|
+
def unique_indexes(model)
|
248
|
+
model.properties(name).unique_indexes
|
249
|
+
end
|
250
|
+
end # module SQL
|
251
|
+
|
252
|
+
include SQL
|
253
|
+
|
254
|
+
module ClassMethods
|
255
|
+
# Default types for all data object based adapters.
|
256
|
+
#
|
257
|
+
# @return [Hash] default types for data objects adapters.
|
258
|
+
#
|
259
|
+
# @api private
|
260
|
+
def type_map
|
261
|
+
length = Property::String::DEFAULT_LENGTH
|
262
|
+
precision = Property::Numeric::DEFAULT_PRECISION
|
263
|
+
scale = Property::Decimal::DEFAULT_SCALE
|
264
|
+
|
265
|
+
@type_map ||= {
|
266
|
+
Property::Binary => { :primitive => 'BLOB' },
|
267
|
+
Object => { :primitive => 'TEXT' },
|
268
|
+
Integer => { :primitive => 'INTEGER' },
|
269
|
+
String => { :primitive => 'VARCHAR', :length => length },
|
270
|
+
Class => { :primitive => 'VARCHAR', :length => length },
|
271
|
+
BigDecimal => { :primitive => 'DECIMAL', :precision => precision, :scale => scale },
|
272
|
+
Float => { :primitive => 'FLOAT', :precision => precision },
|
273
|
+
DateTime => { :primitive => 'TIMESTAMP' },
|
274
|
+
Date => { :primitive => 'DATE' },
|
275
|
+
Time => { :primitive => 'TIMESTAMP' },
|
276
|
+
TrueClass => { :primitive => 'BOOLEAN' },
|
277
|
+
Property::Text => { :primitive => 'TEXT' },
|
278
|
+
}.freeze
|
279
|
+
end
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
end
|
284
|
+
end
|
@@ -0,0 +1,283 @@
|
|
1
|
+
require 'dm-migrations/auto_migration'
|
2
|
+
require 'dm-migrations/adapters/dm-do-adapter'
|
3
|
+
|
4
|
+
module DataMapper
|
5
|
+
module Migrations
|
6
|
+
module MysqlAdapter
|
7
|
+
|
8
|
+
DEFAULT_ENGINE = 'InnoDB'.freeze
|
9
|
+
DEFAULT_CHARACTER_SET = 'utf8'.freeze
|
10
|
+
DEFAULT_COLLATION = 'utf8_unicode_ci'.freeze
|
11
|
+
|
12
|
+
include DataObjectsAdapter
|
13
|
+
|
14
|
+
# @api private
|
15
|
+
def self.included(base)
|
16
|
+
base.extend DataObjectsAdapter::ClassMethods
|
17
|
+
base.extend ClassMethods
|
18
|
+
end
|
19
|
+
|
20
|
+
# @api semipublic
|
21
|
+
def storage_exists?(storage_name)
|
22
|
+
select('SHOW TABLES LIKE ?', storage_name).first == storage_name
|
23
|
+
end
|
24
|
+
|
25
|
+
# @api semipublic
|
26
|
+
def field_exists?(storage_name, field)
|
27
|
+
result = select("SHOW COLUMNS FROM #{quote_name(storage_name)} LIKE ?", field).first
|
28
|
+
result ? result.field == field : false
|
29
|
+
end
|
30
|
+
|
31
|
+
module SQL #:nodoc:
|
32
|
+
# private ## This cannot be private for current migrations
|
33
|
+
|
34
|
+
# @api private
|
35
|
+
def supports_serial?
|
36
|
+
true
|
37
|
+
end
|
38
|
+
|
39
|
+
# @api private
|
40
|
+
def supports_drop_table_if_exists?
|
41
|
+
true
|
42
|
+
end
|
43
|
+
|
44
|
+
# @api private
|
45
|
+
def schema_name
|
46
|
+
# TODO: is there a cleaner way to find out the current DB we are connected to?
|
47
|
+
normalized_uri.path.split('/').last
|
48
|
+
end
|
49
|
+
|
50
|
+
# @api private
|
51
|
+
def create_table_statement(connection, model, properties)
|
52
|
+
"#{super} ENGINE = #{DEFAULT_ENGINE} CHARACTER SET #{character_set} COLLATE #{collation}"
|
53
|
+
end
|
54
|
+
|
55
|
+
# @api private
|
56
|
+
def property_schema_hash(property)
|
57
|
+
schema = super
|
58
|
+
|
59
|
+
if property.kind_of?(Property::Text)
|
60
|
+
schema[:primitive] = text_column_statement(property.length)
|
61
|
+
schema.delete(:default)
|
62
|
+
end
|
63
|
+
|
64
|
+
if property.kind_of?(Property::Integer)
|
65
|
+
min = property.min
|
66
|
+
max = property.max
|
67
|
+
|
68
|
+
schema[:primitive] = integer_column_statement(min..max) if min && max
|
69
|
+
end
|
70
|
+
|
71
|
+
schema
|
72
|
+
end
|
73
|
+
|
74
|
+
# @api private
|
75
|
+
def property_schema_statement(connection, schema)
|
76
|
+
statement = super
|
77
|
+
|
78
|
+
if supports_serial? && schema[:serial]
|
79
|
+
statement << ' AUTO_INCREMENT'
|
80
|
+
end
|
81
|
+
|
82
|
+
statement
|
83
|
+
end
|
84
|
+
|
85
|
+
# @api private
|
86
|
+
def character_set
|
87
|
+
@character_set ||= show_variable('character_set_connection') || DEFAULT_CHARACTER_SET
|
88
|
+
end
|
89
|
+
|
90
|
+
# @api private
|
91
|
+
def collation
|
92
|
+
@collation ||= show_variable('collation_connection') || DEFAULT_COLLATION
|
93
|
+
end
|
94
|
+
|
95
|
+
# @api private
|
96
|
+
def show_variable(name)
|
97
|
+
result = select('SHOW VARIABLES LIKE ?', name).first
|
98
|
+
result ? result.value.freeze : nil
|
99
|
+
end
|
100
|
+
|
101
|
+
private
|
102
|
+
|
103
|
+
# Return SQL statement for the text column
|
104
|
+
#
|
105
|
+
# @param [Integer] length
|
106
|
+
# the max allowed length
|
107
|
+
#
|
108
|
+
# @return [String]
|
109
|
+
# the statement to create the text column
|
110
|
+
#
|
111
|
+
# @api private
|
112
|
+
def text_column_statement(length)
|
113
|
+
if length < 2**8 then 'TINYTEXT'
|
114
|
+
elsif length < 2**16 then 'TEXT'
|
115
|
+
elsif length < 2**24 then 'MEDIUMTEXT'
|
116
|
+
elsif length < 2**32 then 'LONGTEXT'
|
117
|
+
|
118
|
+
# http://www.postgresql.org/files/documentation/books/aw_pgsql/node90.html
|
119
|
+
# Implies that PostgreSQL doesn't have a size limit on text
|
120
|
+
# fields, so this param validation happens here instead of
|
121
|
+
# DM::Property#initialize.
|
122
|
+
else
|
123
|
+
raise ArgumentError, "length of #{length} exceeds maximum size supported"
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
# Return SQL statement for the integer column
|
128
|
+
#
|
129
|
+
# @param [Range] range
|
130
|
+
# the min/max allowed integers
|
131
|
+
#
|
132
|
+
# @return [String]
|
133
|
+
# the statement to create the integer column
|
134
|
+
#
|
135
|
+
# @api private
|
136
|
+
def integer_column_statement(range)
|
137
|
+
'%s(%d)%s' % [
|
138
|
+
integer_column_type(range),
|
139
|
+
integer_display_size(range),
|
140
|
+
integer_statement_sign(range),
|
141
|
+
]
|
142
|
+
end
|
143
|
+
|
144
|
+
# Return the integer column type
|
145
|
+
#
|
146
|
+
# Use the smallest available column type that will satisfy the
|
147
|
+
# allowable range of numbers
|
148
|
+
#
|
149
|
+
# @param [Range] range
|
150
|
+
# the min/max allowed integers
|
151
|
+
#
|
152
|
+
# @return [String]
|
153
|
+
# the column type
|
154
|
+
#
|
155
|
+
# @api private
|
156
|
+
def integer_column_type(range)
|
157
|
+
if range.first < 0
|
158
|
+
signed_integer_column_type(range)
|
159
|
+
else
|
160
|
+
unsigned_integer_column_type(range)
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
# Return the signed integer column type
|
165
|
+
#
|
166
|
+
# @param [Range] range
|
167
|
+
# the min/max allowed integers
|
168
|
+
#
|
169
|
+
# @return [String]
|
170
|
+
#
|
171
|
+
# @api private
|
172
|
+
def signed_integer_column_type(range)
|
173
|
+
min = range.first
|
174
|
+
max = range.last
|
175
|
+
|
176
|
+
tinyint = 2**7
|
177
|
+
smallint = 2**15
|
178
|
+
integer = 2**31
|
179
|
+
mediumint = 2**23
|
180
|
+
bigint = 2**63
|
181
|
+
|
182
|
+
if min >= -tinyint && max < tinyint then 'TINYINT'
|
183
|
+
elsif min >= -smallint && max < smallint then 'SMALLINT'
|
184
|
+
elsif min >= -mediumint && max < mediumint then 'MEDIUMINT'
|
185
|
+
elsif min >= -integer && max < integer then 'INT'
|
186
|
+
elsif min >= -bigint && max < bigint then 'BIGINT'
|
187
|
+
else
|
188
|
+
raise ArgumentError, "min #{min} and max #{max} exceeds supported range"
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
# Return the unsigned integer column type
|
193
|
+
#
|
194
|
+
# @param [Range] range
|
195
|
+
# the min/max allowed integers
|
196
|
+
#
|
197
|
+
# @return [String]
|
198
|
+
#
|
199
|
+
# @api private
|
200
|
+
def unsigned_integer_column_type(range)
|
201
|
+
max = range.last
|
202
|
+
|
203
|
+
if max < 2**8 then 'TINYINT'
|
204
|
+
elsif max < 2**16 then 'SMALLINT'
|
205
|
+
elsif max < 2**24 then 'MEDIUMINT'
|
206
|
+
elsif max < 2**32 then 'INT'
|
207
|
+
elsif max < 2**64 then 'BIGINT'
|
208
|
+
else
|
209
|
+
raise ArgumentError, "min #{range.first} and max #{max} exceeds supported range"
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
# Return the integer column display size
|
214
|
+
#
|
215
|
+
# Adjust the display size to match the maximum number of
|
216
|
+
# expected digits. This is more for documentation purposes
|
217
|
+
# and does not affect what can actually be stored in a
|
218
|
+
# specific column
|
219
|
+
#
|
220
|
+
# @param [Range] range
|
221
|
+
# the min/max allowed integers
|
222
|
+
#
|
223
|
+
# @return [Integer]
|
224
|
+
# the display size for the integer
|
225
|
+
#
|
226
|
+
# @api private
|
227
|
+
def integer_display_size(range)
|
228
|
+
[ range.first.to_s.length, range.last.to_s.length ].max
|
229
|
+
end
|
230
|
+
|
231
|
+
# Return the integer sign statement
|
232
|
+
#
|
233
|
+
# @param [Range] range
|
234
|
+
# the min/max allowed integers
|
235
|
+
#
|
236
|
+
# @return [String, nil]
|
237
|
+
# statement if unsigned, nil if signed
|
238
|
+
#
|
239
|
+
# @api private
|
240
|
+
def integer_statement_sign(range)
|
241
|
+
' UNSIGNED' unless range.first < 0
|
242
|
+
end
|
243
|
+
|
244
|
+
# @api private
|
245
|
+
def indexes(model)
|
246
|
+
filter_indexes(model, super)
|
247
|
+
end
|
248
|
+
|
249
|
+
# @api private
|
250
|
+
def unique_indexes(model)
|
251
|
+
filter_indexes(model, super)
|
252
|
+
end
|
253
|
+
|
254
|
+
# Filter out any indexes with an unindexable column in MySQL
|
255
|
+
#
|
256
|
+
# @api private
|
257
|
+
def filter_indexes(model, indexes)
|
258
|
+
field_map = model.properties(name).field_map
|
259
|
+
indexes.select do |index_name, fields|
|
260
|
+
fields.all? { |field| !field_map[field].kind_of?(Property::Text) }
|
261
|
+
end
|
262
|
+
end
|
263
|
+
end # module SQL
|
264
|
+
|
265
|
+
include SQL
|
266
|
+
|
267
|
+
module ClassMethods
|
268
|
+
# Types for MySQL databases.
|
269
|
+
#
|
270
|
+
# @return [Hash] types for MySQL databases.
|
271
|
+
#
|
272
|
+
# @api private
|
273
|
+
def type_map
|
274
|
+
@type_map ||= super.merge(
|
275
|
+
DateTime => { :primitive => 'DATETIME' },
|
276
|
+
Time => { :primitive => 'DATETIME' }
|
277
|
+
).freeze
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
281
|
+
end
|
282
|
+
end
|
283
|
+
end
|