sbf-dm-migrations 1.3.0.beta

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.
Files changed (62) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +38 -0
  3. data/.rspec +1 -0
  4. data/.rubocop.yml +468 -0
  5. data/.travis.yml +52 -0
  6. data/Gemfile +61 -0
  7. data/LICENSE +20 -0
  8. data/README.rdoc +39 -0
  9. data/Rakefile +4 -0
  10. data/db/migrations/1_create_people_table.rb +12 -0
  11. data/db/migrations/2_add_dob_to_people.rb +13 -0
  12. data/db/migrations/config.rb +4 -0
  13. data/dm-migrations.gemspec +20 -0
  14. data/examples/Rakefile +149 -0
  15. data/examples/sample_migration.rb +58 -0
  16. data/examples/sample_migration_spec.rb +46 -0
  17. data/lib/dm-migrations/adapters/dm-do-adapter.rb +304 -0
  18. data/lib/dm-migrations/adapters/dm-mysql-adapter.rb +306 -0
  19. data/lib/dm-migrations/adapters/dm-oracle-adapter.rb +339 -0
  20. data/lib/dm-migrations/adapters/dm-postgres-adapter.rb +152 -0
  21. data/lib/dm-migrations/adapters/dm-sqlite-adapter.rb +88 -0
  22. data/lib/dm-migrations/adapters/dm-sqlserver-adapter.rb +184 -0
  23. data/lib/dm-migrations/adapters/dm-yaml-adapter.rb +21 -0
  24. data/lib/dm-migrations/auto_migration.rb +227 -0
  25. data/lib/dm-migrations/exceptions/duplicate_migration.rb +6 -0
  26. data/lib/dm-migrations/migration.rb +323 -0
  27. data/lib/dm-migrations/migration_runner.rb +76 -0
  28. data/lib/dm-migrations/sql/column.rb +5 -0
  29. data/lib/dm-migrations/sql/mysql.rb +84 -0
  30. data/lib/dm-migrations/sql/oracle.rb +9 -0
  31. data/lib/dm-migrations/sql/postgres.rb +89 -0
  32. data/lib/dm-migrations/sql/sqlite.rb +59 -0
  33. data/lib/dm-migrations/sql/sqlserver.rb +9 -0
  34. data/lib/dm-migrations/sql/table.rb +15 -0
  35. data/lib/dm-migrations/sql/table_creator.rb +105 -0
  36. data/lib/dm-migrations/sql/table_modifier.rb +57 -0
  37. data/lib/dm-migrations/sql.rb +7 -0
  38. data/lib/dm-migrations/version.rb +5 -0
  39. data/lib/dm-migrations.rb +3 -0
  40. data/lib/spec/example/migration_example_group.rb +69 -0
  41. data/lib/spec/matchers/migration_matchers.rb +96 -0
  42. data/spec/integration/auto_migration_spec.rb +590 -0
  43. data/spec/integration/auto_upgrade_spec.rb +41 -0
  44. data/spec/integration/migration_runner_spec.rb +84 -0
  45. data/spec/integration/migration_spec.rb +156 -0
  46. data/spec/integration/sql_spec.rb +290 -0
  47. data/spec/isolated/require_after_setup_spec.rb +24 -0
  48. data/spec/isolated/require_before_setup_spec.rb +24 -0
  49. data/spec/isolated/require_spec.rb +23 -0
  50. data/spec/spec_helper.rb +16 -0
  51. data/spec/unit/migration_spec.rb +501 -0
  52. data/spec/unit/sql/column_spec.rb +14 -0
  53. data/spec/unit/sql/postgres_spec.rb +90 -0
  54. data/spec/unit/sql/sqlite_extensions_spec.rb +103 -0
  55. data/spec/unit/sql/table_creator_spec.rb +91 -0
  56. data/spec/unit/sql/table_modifier_spec.rb +47 -0
  57. data/spec/unit/sql/table_spec.rb +26 -0
  58. data/spec/unit/sql_spec.rb +7 -0
  59. data/tasks/spec.rake +21 -0
  60. data/tasks/yard.rake +9 -0
  61. data/tasks/yardstick.rake +19 -0
  62. metadata +120 -0
@@ -0,0 +1,323 @@
1
+ require 'dm-migrations/exceptions/duplicate_migration'
2
+ require 'dm-migrations/sql'
3
+
4
+ require 'benchmark'
5
+
6
+ module DataMapper
7
+ class Migration
8
+ include SQL
9
+
10
+ # The position or version the migration belongs to
11
+ attr_reader :position
12
+
13
+ # The name of the migration
14
+ attr_reader :name
15
+
16
+ # The repository the migration operates on
17
+ attr_reader :repository
18
+
19
+ #
20
+ # Creates a new migration.
21
+ #
22
+ # @param [Symbol, String, Integer] position
23
+ # The position or version the migration belongs to.
24
+ #
25
+ # @param [Symbol] name
26
+ # The name of the migration.
27
+ #
28
+ # @param [Hash] options
29
+ # Additional options for the migration.
30
+ #
31
+ # @option options [Boolean] :verbose (true)
32
+ # Enables or disables verbose output.
33
+ #
34
+ # @option options [Symbol] :repository (:default)
35
+ # The DataMapper repository the migration will operate on.
36
+ #
37
+ def initialize(position, name, options = {}, &block)
38
+ @position = position
39
+ @name = name
40
+ @options = options
41
+ @verbose = options.fetch(:verbose, true)
42
+ @up_action = nil
43
+ @down_action = nil
44
+
45
+ @repository = if options.key?(:database)
46
+ warn 'Using the :database option with migrations is deprecated, use :repository instead'
47
+ options[:database]
48
+ else
49
+ options.fetch(:repository, :default)
50
+ end
51
+
52
+ instance_eval(&block)
53
+ end
54
+
55
+ #
56
+ # The repository the migration will operate on.
57
+ #
58
+ # @return [Symbol, nil]
59
+ # The name of the DataMapper repository the migration will run against.
60
+ #
61
+ # @deprecated Use {#repository} instead.
62
+ #
63
+ # @since 1.0.1.
64
+ #
65
+ def database
66
+ warn 'Using the DataMapper::Migration#database method is deprecated, use #repository instead'
67
+ @repository
68
+ end
69
+
70
+ #
71
+ # The adapter the migration will use.
72
+ #
73
+ # @return [DataMapper::Adapter]
74
+ # The adapter the migration will operate on.
75
+ #
76
+ # @since 1.0.1
77
+ #
78
+ def adapter
79
+ setup! unless setup?
80
+
81
+ @adapter
82
+ end
83
+
84
+ # define the actions that should be performed on an up migration
85
+ def up(&block)
86
+ @up_action = block
87
+ end
88
+
89
+ # define the actions that should be performed on a down migration
90
+ def down(&block)
91
+ @down_action = block
92
+ end
93
+
94
+ # perform the migration by running the code in the #up block
95
+ def perform_up
96
+ result = nil
97
+
98
+ if needs_up?
99
+ # TODO: fix this so it only does transactions for databases that support create/drop
100
+ # database.transaction.commit do
101
+ if @up_action
102
+ say_with_time "== Performing Up Migration ##{position}: #{name}", 0 do
103
+ result = @up_action&.call
104
+ end
105
+ end
106
+
107
+ update_migration_info(:up)
108
+ # end
109
+ end
110
+
111
+ result
112
+ end
113
+
114
+ # un-do the migration by running the code in the #down block
115
+ def perform_down
116
+ result = nil
117
+
118
+ if needs_down?
119
+ # TODO: fix this so it only does transactions for databases that support create/drop
120
+ # database.transaction.commit do
121
+ if @down_action
122
+ say_with_time "== Performing Down Migration ##{position}: #{name}", 0 do
123
+ result = @down_action&.call
124
+ end
125
+ end
126
+
127
+ update_migration_info(:down)
128
+ # end
129
+ end
130
+
131
+ result
132
+ end
133
+
134
+ # execute raw SQL
135
+ def execute(sql, *bind_values)
136
+ say_with_time(sql) do
137
+ adapter.execute(sql, *bind_values)
138
+ end
139
+ end
140
+
141
+ #
142
+ # Execute raw SQL and return a result set.
143
+ #
144
+ # @param [String] sql
145
+ # The raw SQL statement.
146
+ #
147
+ # @param [Array] bind_values
148
+ # Additional values to bind to the statement.
149
+ #
150
+ # @return [Array<Struct>]
151
+ # The result set.
152
+ #
153
+ # @since 1.3.0
154
+ #
155
+ def select(sql, *bind_values)
156
+ say_with_time(sql) do
157
+ adapter.select(sql, *bind_values)
158
+ end
159
+ end
160
+
161
+ def create_table(table_name, opts = {}, &block)
162
+ execute TableCreator.new(adapter, table_name, opts, &block).to_sql
163
+ end
164
+
165
+ def drop_table(table_name, _opts = {})
166
+ execute "DROP TABLE #{adapter.send(:quote_name, table_name.to_s)}"
167
+ end
168
+
169
+ def modify_table(table_name, opts = {}, &block)
170
+ TableModifier.new(adapter, table_name, opts, &block).statements.each do |sql|
171
+ execute(sql)
172
+ end
173
+ end
174
+
175
+ def create_index(table_name, *columns_and_options)
176
+ opts = if columns_and_options.last.is_a?(Hash)
177
+ columns_and_options.pop
178
+ else
179
+ {}
180
+ end
181
+ columns = columns_and_options.flatten
182
+
183
+ opts[:name] ||= "#{opts[:unique] ? 'unique_' : ''}index_#{table_name}_#{columns.join('_')}"
184
+
185
+ execute DataMapper::Ext::String.compress_lines(<<-SQL)
186
+ CREATE #{opts[:unique] ? 'UNIQUE ' : ''}INDEX #{quote_column_name(opts[:name])} ON
187
+ #{quote_table_name(table_name)} (#{columns.map { |c| quote_column_name(c) }.join(', ')})
188
+ SQL
189
+ end
190
+
191
+ # Orders migrations by position, so we know what order to run them in.
192
+ # First order by position, then by name, so at least the order is predictable.
193
+ def <=>(other)
194
+ if position == other.position
195
+ name.to_s <=> other.name.to_s
196
+ else
197
+ position <=> other.position
198
+ end
199
+ end
200
+
201
+ # Output some text. Optional indent level
202
+ def say(message, indent = 4)
203
+ write "#{' ' * indent} #{message}"
204
+ end
205
+
206
+ # Time how long the block takes to run, and output it with the message.
207
+ def say_with_time(message, indent = 2)
208
+ say(message, indent)
209
+ result = nil
210
+ time = Benchmark.measure { result = yield }
211
+ say('-> %.4fs' % time.real, indent)
212
+ result
213
+ end
214
+
215
+ # output the given text, but only if verbose mode is on
216
+ def write(text = '')
217
+ puts text if @verbose
218
+ end
219
+
220
+ # Inserts or removes a row into the `migration_info` table, so we can mark this migration as run, or un-done
221
+ def update_migration_info(direction)
222
+ save = @verbose
223
+ @verbose = false
224
+
225
+ create_migration_info_table_if_needed
226
+
227
+ if direction.to_sym == :up
228
+ execute("INSERT INTO #{migration_info_table} (#{migration_name_column}) VALUES (#{quoted_name})")
229
+ elsif direction.to_sym == :down
230
+ execute("DELETE FROM #{migration_info_table} WHERE #{migration_name_column} = #{quoted_name}")
231
+ end
232
+ @verbose = save
233
+ end
234
+
235
+ def create_migration_info_table_if_needed
236
+ save = @verbose
237
+ @verbose = false
238
+ execute("CREATE TABLE #{migration_info_table} (#{migration_name_column} VARCHAR(255) UNIQUE)") unless migration_info_table_exists?
239
+ @verbose = save
240
+ end
241
+
242
+ # Quote the name of the migration for use in SQL
243
+ def quoted_name
244
+ "'#{name}'"
245
+ end
246
+
247
+ def migration_info_table_exists?
248
+ adapter.storage_exists?('migration_info')
249
+ end
250
+
251
+ # Fetch the record for this migration out of the migration_info table
252
+ def migration_record
253
+ return [] unless migration_info_table_exists?
254
+
255
+ adapter.select("SELECT #{migration_name_column} FROM #{migration_info_table} WHERE #{migration_name_column} = #{quoted_name}")
256
+ end
257
+
258
+ # True if the migration needs to be run
259
+ def needs_up?
260
+ return true unless migration_info_table_exists?
261
+
262
+ migration_record.empty?
263
+ end
264
+
265
+ # True if the migration has already been run
266
+ def needs_down?
267
+ return false unless migration_info_table_exists?
268
+
269
+ !migration_record.empty?
270
+ end
271
+
272
+ # Quoted table name, for the adapter
273
+ def migration_info_table
274
+ @migration_info_table ||= quote_table_name('migration_info')
275
+ end
276
+
277
+ # Quoted `migration_name` column, for the adapter
278
+ def migration_name_column
279
+ @migration_name_column ||= quote_column_name('migration_name')
280
+ end
281
+
282
+ def quote_table_name(table_name)
283
+ # TODO: Fix this for 1.9 - can't use this hack to access a private method
284
+ adapter.send(:quote_name, table_name.to_s)
285
+ end
286
+
287
+ def quote_column_name(column_name)
288
+ # TODO: Fix this for 1.9 - can't use this hack to access a private method
289
+ adapter.send(:quote_name, column_name.to_s)
290
+ end
291
+
292
+ #
293
+ # Determines whether the migration has been setup.
294
+ #
295
+ # @return [Boolean]
296
+ # Specifies whether the migration has been setup.
297
+ #
298
+ # @since 1.0.1
299
+ #
300
+ protected def setup?
301
+ !@adapter.nil?
302
+ end
303
+
304
+ #
305
+ # Sets up the migration.
306
+ #
307
+ # @since 1.0.1
308
+ #
309
+ protected def setup!
310
+ @adapter = DataMapper.repository(@repository)&.adapter
311
+
312
+ case @adapter.class.name
313
+ when /Sqlite/ then @adapter.extend(SQL::Sqlite)
314
+ when /Mysql/ then @adapter.extend(SQL::Mysql)
315
+ when /Postgres/ then @adapter.extend(SQL::Postgres)
316
+ when /Sqlserver/ then @adapter.extend(SQL::Sqlserver)
317
+ when /Oracle/ then @adapter.extend(SQL::Oracle)
318
+ else
319
+ raise(RuntimeError, "Unsupported Migration Adapter #{@adapter.class}", caller)
320
+ end
321
+ end
322
+ end
323
+ end
@@ -0,0 +1,76 @@
1
+ require 'dm-migrations/migration'
2
+
3
+ module DataMapper
4
+ module MigrationRunner
5
+ # Creates a new migration, and adds it to the list of migrations to be run.
6
+ # Migrations can be defined in any order, they will be sorted and run in the
7
+ # correct order.
8
+ #
9
+ # The order that migrations are run in is set by the first argument. It is not
10
+ # necessary that this be unique; migrations with the same version number are
11
+ # expected to be able to be run in any order.
12
+ #
13
+ # The second argument is the name of the migration. This name is used internally
14
+ # to track if the migration has been run. It is required that this name be unique
15
+ # across all migrations.
16
+ #
17
+ # Additionally, it accepts a number of options:
18
+ # * <tt>:database</tt> If you defined several DataMapper::database instances use this
19
+ # to choose which one to run the migration against. Defaults to <tt>:default</tt>.
20
+ # Migrations are tracked individually per database.
21
+ # * <tt>:verbose</tt> true/false, defaults to true. Determines if the migration should
22
+ # output its status messages when it runs.
23
+ #
24
+ # Example of a simple migration:
25
+ #
26
+ # migration( 1, :create_people_table ) do
27
+ # up do
28
+ # create_table :people do
29
+ # column :id, Integer, :serial => true
30
+ # column :name, String, :size => 50
31
+ # column :age, Integer
32
+ # end
33
+ # end
34
+ # down do
35
+ # drop_table :people
36
+ # end
37
+ # end
38
+ #
39
+ # Its recommended that you stick with raw SQL for migrations that manipulate data. If
40
+ # you write a migration using a model, then later change the model, there's a
41
+ # possibility the migration will no longer work. Using SQL will always work.
42
+ def migration(number, name, opts = {}, &block)
43
+ raise "Migration name conflict: '#{name}'" if migrations.map(&:name).include?(name.to_s)
44
+
45
+ migrations << DataMapper::Migration.new(number, name.to_s, opts, &block)
46
+ end
47
+
48
+ # Run all migrations that need to be run. In most cases, this would be called by a
49
+ # rake task as part of a larger project, but this provides the ability to run them
50
+ # in a script or test.
51
+ #
52
+ # has an optional argument 'level' which if supplied, only performs the migrations
53
+ # with a position less than or equal to the level.
54
+ def migrate_up!(level = nil)
55
+ migrations.sort.each do |migration|
56
+ migration.perform_up if level.nil? || migration.position <= level.to_i
57
+ end
58
+ end
59
+
60
+ # Run all the down steps for the migrations that have already been run.
61
+ #
62
+ # has an optional argument 'level' which, if supplied, only performs the
63
+ # down migrations with a position greater than the level.
64
+ def migrate_down!(level = nil)
65
+ migrations.sort.reverse.each do |migration|
66
+ migration.perform_down if level.nil? || migration.position > level.to_i
67
+ end
68
+ end
69
+
70
+ def migrations
71
+ @@migrations ||= []
72
+ end
73
+ end
74
+ end
75
+
76
+ include DataMapper::MigrationRunner
@@ -0,0 +1,5 @@
1
+ module SQL
2
+ class Column
3
+ attr_accessor :name, :type, :not_null, :default_value, :primary_key, :unique
4
+ end
5
+ end
@@ -0,0 +1,84 @@
1
+ require 'dm-migrations/sql/table'
2
+
3
+ module SQL
4
+ module Mysql
5
+ def supports_schema_transactions?
6
+ false
7
+ end
8
+
9
+ def table(table_name)
10
+ SQL::Mysql::Table.new(self, table_name)
11
+ end
12
+
13
+ def recreate_database
14
+ execute "DROP DATABASE #{schema_name}"
15
+ execute "CREATE DATABASE #{schema_name}"
16
+ execute "USE #{schema_name}"
17
+ end
18
+
19
+ def supports_serial?
20
+ true
21
+ end
22
+
23
+ def table_options(opts)
24
+ opt_engine = opts[:storage_engine] || storage_engine
25
+ opt_char_set = opts[:character_set] || character_set
26
+ opt_collation = opts[:collation] || collation
27
+
28
+ " ENGINE = #{opt_engine} CHARACTER SET #{opt_char_set} COLLATE #{opt_collation}"
29
+ end
30
+
31
+ def property_schema_statement(connection, schema)
32
+ if supports_serial? && schema[:serial]
33
+ "#{schema[:quote_column_name]} SERIAL PRIMARY KEY"
34
+ else
35
+ super
36
+ end
37
+ end
38
+
39
+ def change_column_type_statement(name, column)
40
+ "ALTER TABLE #{quote_name(name)} MODIFY COLUMN #{column.to_sql}"
41
+ end
42
+
43
+ def rename_column_type_statement(table_name, old_col, new_col)
44
+ table = quote_name(table_name)
45
+ column_info = select("SHOW COLUMNS FROM #{table} LIKE ?", old_col).first
46
+
47
+ column_options = {
48
+ name: column_info.field,
49
+ primitive: column_info.type,
50
+ serial: column_info.extra == 'auto_increment',
51
+ allow_nil: column_info.null == 'YES',
52
+ default: column_info.default
53
+ }
54
+
55
+ column = with_connection do |connection|
56
+ property_schema_statement(connection, column_options)
57
+ end
58
+
59
+ column_definition = column.split(' ', 2).last
60
+
61
+ "ALTER TABLE #{table} CHANGE #{quote_name(old_col)} #{quote_name(new_col)} #{column_definition}"
62
+ end
63
+
64
+ class Table
65
+ def initialize(adapter, table_name)
66
+ @columns = []
67
+ adapter.table_info(table_name).each do |col_struct|
68
+ @columns << SQL::Mysql::Column.new(col_struct)
69
+ end
70
+ end
71
+ end
72
+
73
+ class Column
74
+ def initialize(col_struct)
75
+ @name = col_struct.name
76
+ @type = col_struct.type
77
+ @default_value = col_struct.dflt_value
78
+ @primary_key = col_struct.pk
79
+
80
+ @not_null = col_struct.notnull == 0
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,9 @@
1
+ require 'dm-migrations/sql/table'
2
+
3
+ module SQL
4
+ module Oracle
5
+ def change_column_type_statement(name, column)
6
+ "ALTER TABLE #{quote_name(name)} MODIFY ( #{column.to_sql} )"
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,89 @@
1
+ module SQL
2
+ module Postgres
3
+ def supports_schema_transactions?
4
+ true
5
+ end
6
+
7
+ def table(table_name)
8
+ SQL::Postgres::Table.new(self, table_name)
9
+ end
10
+
11
+ def recreate_database
12
+ execute 'DROP SCHEMA IF EXISTS test CASCADE'
13
+ execute 'CREATE SCHEMA test'
14
+ execute 'SET search_path TO test'
15
+ end
16
+
17
+ def supports_serial?
18
+ true
19
+ end
20
+
21
+ def property_schema_statement(connection, schema)
22
+ if supports_serial? && schema[:serial]
23
+ statement = "#{schema[:quote_column_name]} SERIAL PRIMARY KEY"
24
+ else
25
+ statement = super
26
+ statement << " DEFAULT nextval('#{schema[:sequence_name]}') NOT NULL" if schema.key?(:sequence_name)
27
+ statement
28
+ end
29
+ statement
30
+ end
31
+
32
+ def table_options(_opts)
33
+ ''
34
+ end
35
+
36
+ def change_column_type_statement(name, column)
37
+ "ALTER TABLE #{quote_name(name)} ALTER COLUMN #{column.to_sql}"
38
+ end
39
+
40
+ def rename_column_type_statement(table_name, old_col, new_col)
41
+ "ALTER TABLE #{quote_name(table_name)} RENAME COLUMN #{quote_name(old_col)} TO #{quote_name(new_col)}"
42
+ end
43
+
44
+ class Table < SQL::Table
45
+ def initialize(adapter, table_name)
46
+ super()
47
+ @adapter = adapter
48
+ @name = table_name
49
+ @columns = []
50
+ adapter.query_table(table_name).each do |col_struct|
51
+ @columns << SQL::Postgres::Column.new(col_struct)
52
+ end
53
+
54
+ query_column_constraints
55
+ end
56
+
57
+ def query_column_constraints
58
+ @adapter.select(
59
+ "SELECT * FROM information_schema.table_constraints WHERE table_name='#{@name}' AND table_schema=current_schema()"
60
+ ).each do |table_constraint|
61
+ @adapter.select(
62
+ "SELECT * FROM information_schema.constraint_column_usage WHERE constraint_name='#{table_constraint.constraint_name}' " \
63
+ 'AND table_schema=current_schema()'
64
+ ).each do |constrained_column|
65
+ @columns.each do |column|
66
+ next unless column.name == constrained_column.column_name
67
+
68
+ case table_constraint.constraint_type
69
+ when 'UNIQUE' then column.unique = true
70
+ when 'PRIMARY KEY' then column.primary_key = true
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
77
+
78
+ class Column < SQL::Column
79
+ def initialize(col_struct)
80
+ super()
81
+ @name = col_struct.column_name
82
+ @type = col_struct.data_type
83
+ @default_value = col_struct.column_default
84
+
85
+ @not_null = col_struct.is_nullable != 'YES'
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,59 @@
1
+ require 'dm-migrations/sql/table'
2
+
3
+ require 'fileutils'
4
+
5
+ module SQL
6
+ module Sqlite
7
+ def supports_schema_transactions?
8
+ true
9
+ end
10
+
11
+ def table(table_name)
12
+ SQL::Sqlite::Table.new(self, table_name)
13
+ end
14
+
15
+ def recreate_database
16
+ DataMapper.logger.info "Dropping #{@uri.path}"
17
+ FileUtils.rm_f(@uri.path)
18
+ # do nothing, sqlite will automatically create the database file
19
+ end
20
+
21
+ def table_options(_opts)
22
+ ''
23
+ end
24
+
25
+ def supports_serial?
26
+ true
27
+ end
28
+
29
+ def change_column_type_statement(*)
30
+ raise NotImplementedError
31
+ end
32
+
33
+ def rename_column_type_statement(*)
34
+ raise NotImplementedError
35
+ end
36
+
37
+ class Table < SQL::Table
38
+ def initialize(adapter, table_name)
39
+ super()
40
+ @columns = []
41
+ adapter.table_info(table_name).each do |col_struct|
42
+ @columns << SQL::Sqlite::Column.new(col_struct)
43
+ end
44
+ end
45
+ end
46
+
47
+ class Column < SQL::Column
48
+ def initialize(col_struct)
49
+ super()
50
+ @name = col_struct.name
51
+ @type = col_struct.type
52
+ @default_value = col_struct.dflt_value
53
+ @primary_key = col_struct.pk
54
+
55
+ @not_null = col_struct.notnull == 0
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,9 @@
1
+ require 'dm-migrations/sql/table'
2
+
3
+ module SQL
4
+ module Sqlserver
5
+ def change_column_type_statement(name, column)
6
+ "ALTER TABLE #{quote_name(name)} ALTER COLUMN #{column.to_sql}"
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,15 @@
1
+ require 'dm-migrations/sql/column'
2
+
3
+ module SQL
4
+ class Table
5
+ attr_accessor :name, :columns
6
+
7
+ def to_s
8
+ name
9
+ end
10
+
11
+ def column(column_name)
12
+ @columns.select { |c| c.name == column_name.to_s }.first
13
+ end
14
+ end
15
+ end