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,45 @@
|
|
1
|
+
require 'dm-migrations/sql/table'
|
2
|
+
|
3
|
+
module SQL
|
4
|
+
module Sqlite
|
5
|
+
|
6
|
+
def supports_schema_transactions?
|
7
|
+
true
|
8
|
+
end
|
9
|
+
|
10
|
+
def table(table_name)
|
11
|
+
SQL::Sqlite::Table.new(self, table_name)
|
12
|
+
end
|
13
|
+
|
14
|
+
def recreate_database
|
15
|
+
DataMapper.logger.info "Dropping #{@uri.path}"
|
16
|
+
system "rm #{@uri.path}"
|
17
|
+
# do nothing, sqlite will automatically create the database file
|
18
|
+
end
|
19
|
+
|
20
|
+
def table_options
|
21
|
+
''
|
22
|
+
end
|
23
|
+
|
24
|
+
def supports_serial?
|
25
|
+
true
|
26
|
+
end
|
27
|
+
|
28
|
+
class Table < SQL::Table
|
29
|
+
def initialize(adapter, table_name)
|
30
|
+
@columns = []
|
31
|
+
adapter.table_info(table_name).each do |col_struct|
|
32
|
+
@columns << SQL::Sqlite::Column.new(col_struct)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
class Column < SQL::Column
|
38
|
+
def initialize(col_struct)
|
39
|
+
@name, @type, @default_value, @primary_key = col_struct.name, col_struct.type, col_struct.dflt_value, col_struct.pk
|
40
|
+
|
41
|
+
@not_null = col_struct.notnull == 0
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
module SQL
|
2
|
+
class TableCreator
|
3
|
+
attr_accessor :table_name, :opts
|
4
|
+
|
5
|
+
def initialize(adapter, table_name, opts = {}, &block)
|
6
|
+
@adapter = adapter
|
7
|
+
@table_name = table_name.to_s
|
8
|
+
@opts = opts
|
9
|
+
|
10
|
+
@columns = []
|
11
|
+
|
12
|
+
self.instance_eval &block
|
13
|
+
end
|
14
|
+
|
15
|
+
def quoted_table_name
|
16
|
+
@adapter.send(:quote_name, table_name)
|
17
|
+
end
|
18
|
+
|
19
|
+
def column(name, type, opts = {})
|
20
|
+
@columns << Column.new(@adapter, name, type, opts)
|
21
|
+
end
|
22
|
+
|
23
|
+
def to_sql
|
24
|
+
"CREATE TABLE #{quoted_table_name} (#{@columns.map{ |c| c.to_sql }.join(', ')})#{@adapter.table_options}"
|
25
|
+
end
|
26
|
+
|
27
|
+
# A helper for using the native NOW() SQL function in a default
|
28
|
+
def now
|
29
|
+
SqlExpr.new('NOW()')
|
30
|
+
end
|
31
|
+
|
32
|
+
# A helper for using the native UUID() SQL function in a default
|
33
|
+
def uuid
|
34
|
+
SqlExpr.new('UUID()')
|
35
|
+
end
|
36
|
+
|
37
|
+
class SqlExpr
|
38
|
+
attr_accessor :sql
|
39
|
+
def initialize(sql)
|
40
|
+
@sql = sql
|
41
|
+
end
|
42
|
+
|
43
|
+
def to_s
|
44
|
+
@sql.to_s
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
class Column
|
49
|
+
attr_accessor :name, :type
|
50
|
+
|
51
|
+
def initialize(adapter, name, type, opts = {})
|
52
|
+
@adapter = adapter
|
53
|
+
@name = name.to_s
|
54
|
+
@opts = opts
|
55
|
+
@type = build_type(type)
|
56
|
+
end
|
57
|
+
|
58
|
+
def to_sql
|
59
|
+
type
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
def build_type(type_class)
|
65
|
+
schema = { :name => @name, :quote_column_name => quoted_name }.merge(@opts)
|
66
|
+
|
67
|
+
[ :nullable, :nullable? ].each do |option|
|
68
|
+
next if (value = schema.delete(option)).nil?
|
69
|
+
warn "#{option.inspect} is deprecated, use :allow_nil instead"
|
70
|
+
schema[:allow_nil] = value unless schema.key?(:allow_nil)
|
71
|
+
end
|
72
|
+
|
73
|
+
unless schema.key?(:allow_nil)
|
74
|
+
schema[:allow_nil] = !schema[:not_null]
|
75
|
+
end
|
76
|
+
|
77
|
+
schema[:length] ||= schema.delete(:size) if schema.key?(:size)
|
78
|
+
|
79
|
+
if type_class.kind_of?(String)
|
80
|
+
schema[:primitive] = type_class
|
81
|
+
else
|
82
|
+
primitive = type_class.respond_to?(:primitive) ? type_class.primitive : type_class
|
83
|
+
options = @adapter.class.type_map[primitive].dup
|
84
|
+
|
85
|
+
if type_class.respond_to?(:options) && type_class.options.kind_of?(options.class)
|
86
|
+
options.update(type_class.options)
|
87
|
+
end
|
88
|
+
|
89
|
+
schema = options.update(schema)
|
90
|
+
end
|
91
|
+
|
92
|
+
@adapter.send(:with_connection) do |connection|
|
93
|
+
@adapter.property_schema_statement(connection, schema)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def quoted_name
|
98
|
+
@adapter.send(:quote_name, name)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module SQL
|
2
|
+
class TableModifier
|
3
|
+
attr_accessor :table_name, :opts, :statements, :adapter
|
4
|
+
|
5
|
+
def initialize(adapter, table_name, opts = {}, &block)
|
6
|
+
@adapter = adapter
|
7
|
+
@table_name = table_name.to_s
|
8
|
+
@opts = (opts)
|
9
|
+
|
10
|
+
@statements = []
|
11
|
+
|
12
|
+
self.instance_eval &block
|
13
|
+
end
|
14
|
+
|
15
|
+
def add_column(name, type, opts = {})
|
16
|
+
column = SQL::TableCreator::Column.new(@adapter, name, type, opts)
|
17
|
+
@statements << "ALTER TABLE #{quoted_table_name} ADD COLUMN #{column.to_sql}"
|
18
|
+
end
|
19
|
+
|
20
|
+
def drop_column(name)
|
21
|
+
# raise NotImplemented for SQLite3. Can't ALTER TABLE, need to copy table.
|
22
|
+
# We'd have to inspect it, and we can't, since we aren't executing any queries yet.
|
23
|
+
# TODO instead of building the SQL queries when executing the block, create AddColumn,
|
24
|
+
# AlterColumn and DropColumn objects that get #to_sql'd
|
25
|
+
if name.is_a?(Array)
|
26
|
+
name.each{ |n| drop_column(n) }
|
27
|
+
else
|
28
|
+
@statements << "ALTER TABLE #{quoted_table_name} DROP COLUMN #{quote_column_name(name)}"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
alias drop_columns drop_column
|
32
|
+
|
33
|
+
def rename_column(name, new_name, opts = {})
|
34
|
+
# raise NotImplemented for SQLite3
|
35
|
+
@statements << "ALTER TABLE #{quoted_table_name} RENAME COLUMN #{quote_column_name(name)} TO #{quote_column_name(new_name)}"
|
36
|
+
end
|
37
|
+
|
38
|
+
def change_column(name, type, opts = {})
|
39
|
+
# raise NotImplemented for SQLite3
|
40
|
+
@statements << "ALTER TABLE #{quoted_table_name} ALTER COLUMN #{quote_column_name(name)} TYPE #{type}"
|
41
|
+
end
|
42
|
+
|
43
|
+
def quote_column_name(name)
|
44
|
+
@adapter.send(:quote_name, name.to_s)
|
45
|
+
end
|
46
|
+
|
47
|
+
def quoted_table_name
|
48
|
+
@adapter.send(:quote_name, table_name)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'spec/matchers/migration_matchers'
|
2
|
+
|
3
|
+
require 'spec'
|
4
|
+
|
5
|
+
module Spec
|
6
|
+
module Example
|
7
|
+
class MigrationExampleGroup < Spec::Example::ExampleGroup
|
8
|
+
include Spec::Matchers::Migration
|
9
|
+
|
10
|
+
before(:all) do
|
11
|
+
if this_migration.adapter.supports_schema_transactions?
|
12
|
+
run_prereq_migrations
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
before(:each) do
|
17
|
+
if ! this_migration.adapter.supports_schema_transactions?
|
18
|
+
run_prereq_migrations
|
19
|
+
else
|
20
|
+
this_migration.adapter.begin_transaction
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
after(:each) do
|
25
|
+
if this_migration.adapter.supports_schema_transactions?
|
26
|
+
this_migration.adapter.rollback_transaction
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
after(:all) do
|
31
|
+
this_migration.adapter.recreate_database
|
32
|
+
end
|
33
|
+
|
34
|
+
def run_prereq_migrations
|
35
|
+
"running n-1 migrations"
|
36
|
+
all_databases.each do |db|
|
37
|
+
db.adapter.recreate_database
|
38
|
+
end
|
39
|
+
@@migrations.sort.each do |migration|
|
40
|
+
break if migration.name.to_s == migration_name.to_s
|
41
|
+
migration.perform_up
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def run_migration
|
46
|
+
this_migration.perform_up
|
47
|
+
end
|
48
|
+
|
49
|
+
def migration_name
|
50
|
+
@migration_name ||= self.class.instance_variable_get("@description_text").to_s
|
51
|
+
end
|
52
|
+
|
53
|
+
def all_databases
|
54
|
+
@@migrations.map { |m| m.database }.uniq
|
55
|
+
end
|
56
|
+
|
57
|
+
def this_migration
|
58
|
+
@@migrations.select { |m| m.name.to_s == migration_name }.first
|
59
|
+
end
|
60
|
+
|
61
|
+
def select(sql)
|
62
|
+
this_migration.adapter.select(sql)
|
63
|
+
end
|
64
|
+
|
65
|
+
def table(table_name)
|
66
|
+
this_migration.adapter.table(table_name)
|
67
|
+
end
|
68
|
+
|
69
|
+
Spec::Example::ExampleGroupFactory.register(:migration, self)
|
70
|
+
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
module Spec
|
2
|
+
module Matchers
|
3
|
+
module Migration
|
4
|
+
|
5
|
+
def have_table(table_name)
|
6
|
+
HaveTableMatcher.new(table_name)
|
7
|
+
end
|
8
|
+
|
9
|
+
def have_column(column_name)
|
10
|
+
HaveColumnMatcher.new(column_name)
|
11
|
+
end
|
12
|
+
|
13
|
+
def permit_null
|
14
|
+
NullableColumnMatcher.new
|
15
|
+
end
|
16
|
+
|
17
|
+
def be_primary_key
|
18
|
+
PrimaryKeyMatcher.new
|
19
|
+
end
|
20
|
+
|
21
|
+
class HaveTableMatcher
|
22
|
+
|
23
|
+
attr_accessor :table_name, :repository
|
24
|
+
|
25
|
+
def initialize(table_name)
|
26
|
+
@table_name = table_name
|
27
|
+
end
|
28
|
+
|
29
|
+
def matches?(repository)
|
30
|
+
repository.adapter.storage_exists?(table_name)
|
31
|
+
end
|
32
|
+
|
33
|
+
def failure_message
|
34
|
+
%(expected #{repository} to have table '#{table_name}')
|
35
|
+
end
|
36
|
+
|
37
|
+
def negative_failure_message
|
38
|
+
%(expected #{repository} to not have table '#{table_name}')
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
class HaveColumnMatcher
|
44
|
+
|
45
|
+
attr_accessor :table, :column_name
|
46
|
+
|
47
|
+
def initialize(column_name)
|
48
|
+
@column_name = column_name
|
49
|
+
end
|
50
|
+
|
51
|
+
def matches?(table)
|
52
|
+
@table = table
|
53
|
+
table.columns.map { |c| c.name }.include?(column_name.to_s)
|
54
|
+
end
|
55
|
+
|
56
|
+
def failure_message
|
57
|
+
%(expected #{table} to have column '#{column_name}')
|
58
|
+
end
|
59
|
+
|
60
|
+
def negative_failure_message
|
61
|
+
%(expected #{table} to not have column '#{column_name}')
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
|
66
|
+
class NullableColumnMatcher
|
67
|
+
|
68
|
+
attr_accessor :column
|
69
|
+
|
70
|
+
def matches?(column)
|
71
|
+
@column = column
|
72
|
+
! column.not_null
|
73
|
+
end
|
74
|
+
|
75
|
+
def failure_message
|
76
|
+
%(expected #{column.name} to permit NULL)
|
77
|
+
end
|
78
|
+
|
79
|
+
def negative_failure_message
|
80
|
+
%(expected #{column.name} to be NOT NULL)
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
|
85
|
+
class PrimaryKeyMatcher
|
86
|
+
|
87
|
+
attr_accessor :column
|
88
|
+
|
89
|
+
def matches?(column)
|
90
|
+
@column = column
|
91
|
+
column.primary_key
|
92
|
+
end
|
93
|
+
|
94
|
+
def failure_message
|
95
|
+
%(expected #{column.name} to be PRIMARY KEY)
|
96
|
+
end
|
97
|
+
|
98
|
+
def negative_failure_message
|
99
|
+
%(expected #{column.name} to not be PRIMARY KEY)
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,506 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
require 'dm-migrations/auto_migration'
|
4
|
+
|
5
|
+
describe DataMapper::Migrations do
|
6
|
+
def capture_log(mod)
|
7
|
+
original, mod.logger = mod.logger, DataObjects::Logger.new(@log = StringIO.new, :debug)
|
8
|
+
yield
|
9
|
+
ensure
|
10
|
+
@log.rewind
|
11
|
+
@output = @log.readlines.map do |line|
|
12
|
+
line.chomp.gsub(/\A.+?~ \(\d+\.?\d*\)\s+/, '')
|
13
|
+
end
|
14
|
+
|
15
|
+
mod.logger = original
|
16
|
+
end
|
17
|
+
|
18
|
+
supported_by :mysql do
|
19
|
+
before :all do
|
20
|
+
module ::Blog
|
21
|
+
class Article
|
22
|
+
include DataMapper::Resource
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
@model = ::Blog::Article
|
27
|
+
end
|
28
|
+
|
29
|
+
describe '#auto_migrate' do
|
30
|
+
describe 'Integer property' do
|
31
|
+
[
|
32
|
+
[ 0, 1, 'TINYINT(1) UNSIGNED' ],
|
33
|
+
[ 0, 9, 'TINYINT(1) UNSIGNED' ],
|
34
|
+
[ 0, 10, 'TINYINT(2) UNSIGNED' ],
|
35
|
+
[ 0, 99, 'TINYINT(2) UNSIGNED' ],
|
36
|
+
[ 0, 100, 'TINYINT(3) UNSIGNED' ],
|
37
|
+
[ 0, 255, 'TINYINT(3) UNSIGNED' ],
|
38
|
+
[ 0, 256, 'SMALLINT(3) UNSIGNED' ],
|
39
|
+
[ 0, 999, 'SMALLINT(3) UNSIGNED' ],
|
40
|
+
[ 0, 1000, 'SMALLINT(4) UNSIGNED' ],
|
41
|
+
[ 0, 9999, 'SMALLINT(4) UNSIGNED' ],
|
42
|
+
[ 0, 10000, 'SMALLINT(5) UNSIGNED' ],
|
43
|
+
[ 0, 65535, 'SMALLINT(5) UNSIGNED' ],
|
44
|
+
[ 0, 65536, 'MEDIUMINT(5) UNSIGNED' ],
|
45
|
+
[ 0, 99999, 'MEDIUMINT(5) UNSIGNED' ],
|
46
|
+
[ 0, 100000, 'MEDIUMINT(6) UNSIGNED' ],
|
47
|
+
[ 0, 999999, 'MEDIUMINT(6) UNSIGNED' ],
|
48
|
+
[ 0, 1000000, 'MEDIUMINT(7) UNSIGNED' ],
|
49
|
+
[ 0, 9999999, 'MEDIUMINT(7) UNSIGNED' ],
|
50
|
+
[ 0, 10000000, 'MEDIUMINT(8) UNSIGNED' ],
|
51
|
+
[ 0, 16777215, 'MEDIUMINT(8) UNSIGNED' ],
|
52
|
+
[ 0, 16777216, 'INT(8) UNSIGNED' ],
|
53
|
+
[ 0, 99999999, 'INT(8) UNSIGNED' ],
|
54
|
+
[ 0, 100000000, 'INT(9) UNSIGNED' ],
|
55
|
+
[ 0, 999999999, 'INT(9) UNSIGNED' ],
|
56
|
+
[ 0, 1000000000, 'INT(10) UNSIGNED' ],
|
57
|
+
[ 0, 4294967295, 'INT(10) UNSIGNED' ],
|
58
|
+
[ 0, 4294967296, 'BIGINT(10) UNSIGNED' ],
|
59
|
+
[ 0, 9999999999, 'BIGINT(10) UNSIGNED' ],
|
60
|
+
[ 0, 10000000000, 'BIGINT(11) UNSIGNED' ],
|
61
|
+
[ 0, 99999999999, 'BIGINT(11) UNSIGNED' ],
|
62
|
+
[ 0, 100000000000, 'BIGINT(12) UNSIGNED' ],
|
63
|
+
[ 0, 999999999999, 'BIGINT(12) UNSIGNED' ],
|
64
|
+
[ 0, 1000000000000, 'BIGINT(13) UNSIGNED' ],
|
65
|
+
[ 0, 9999999999999, 'BIGINT(13) UNSIGNED' ],
|
66
|
+
[ 0, 10000000000000, 'BIGINT(14) UNSIGNED' ],
|
67
|
+
[ 0, 99999999999999, 'BIGINT(14) UNSIGNED' ],
|
68
|
+
[ 0, 100000000000000, 'BIGINT(15) UNSIGNED' ],
|
69
|
+
[ 0, 999999999999999, 'BIGINT(15) UNSIGNED' ],
|
70
|
+
[ 0, 1000000000000000, 'BIGINT(16) UNSIGNED' ],
|
71
|
+
[ 0, 9999999999999999, 'BIGINT(16) UNSIGNED' ],
|
72
|
+
[ 0, 10000000000000000, 'BIGINT(17) UNSIGNED' ],
|
73
|
+
[ 0, 99999999999999999, 'BIGINT(17) UNSIGNED' ],
|
74
|
+
[ 0, 100000000000000000, 'BIGINT(18) UNSIGNED' ],
|
75
|
+
[ 0, 999999999999999999, 'BIGINT(18) UNSIGNED' ],
|
76
|
+
[ 0, 1000000000000000000, 'BIGINT(19) UNSIGNED' ],
|
77
|
+
[ 0, 9999999999999999999, 'BIGINT(19) UNSIGNED' ],
|
78
|
+
[ 0, 10000000000000000000, 'BIGINT(20) UNSIGNED' ],
|
79
|
+
[ 0, 18446744073709551615, 'BIGINT(20) UNSIGNED' ],
|
80
|
+
|
81
|
+
[ -1, 0, 'TINYINT(2)' ],
|
82
|
+
[ -1, 9, 'TINYINT(2)' ],
|
83
|
+
[ -1, 10, 'TINYINT(2)' ],
|
84
|
+
[ -1, 99, 'TINYINT(2)' ],
|
85
|
+
[ -1, 100, 'TINYINT(3)' ],
|
86
|
+
[ -1, 127, 'TINYINT(3)' ],
|
87
|
+
[ -1, 128, 'SMALLINT(3)' ],
|
88
|
+
[ -1, 999, 'SMALLINT(3)' ],
|
89
|
+
[ -1, 1000, 'SMALLINT(4)' ],
|
90
|
+
[ -1, 9999, 'SMALLINT(4)' ],
|
91
|
+
[ -1, 10000, 'SMALLINT(5)' ],
|
92
|
+
[ -1, 32767, 'SMALLINT(5)' ],
|
93
|
+
[ -1, 32768, 'MEDIUMINT(5)' ],
|
94
|
+
[ -1, 99999, 'MEDIUMINT(5)' ],
|
95
|
+
[ -1, 100000, 'MEDIUMINT(6)' ],
|
96
|
+
[ -1, 999999, 'MEDIUMINT(6)' ],
|
97
|
+
[ -1, 1000000, 'MEDIUMINT(7)' ],
|
98
|
+
[ -1, 8388607, 'MEDIUMINT(7)' ],
|
99
|
+
[ -1, 8388608, 'INT(7)' ],
|
100
|
+
[ -1, 9999999, 'INT(7)' ],
|
101
|
+
[ -1, 10000000, 'INT(8)' ],
|
102
|
+
[ -1, 99999999, 'INT(8)' ],
|
103
|
+
[ -1, 100000000, 'INT(9)' ],
|
104
|
+
[ -1, 999999999, 'INT(9)' ],
|
105
|
+
[ -1, 1000000000, 'INT(10)' ],
|
106
|
+
[ -1, 2147483647, 'INT(10)' ],
|
107
|
+
[ -1, 2147483648, 'BIGINT(10)' ],
|
108
|
+
[ -1, 9999999999, 'BIGINT(10)' ],
|
109
|
+
[ -1, 10000000000, 'BIGINT(11)' ],
|
110
|
+
[ -1, 99999999999, 'BIGINT(11)' ],
|
111
|
+
[ -1, 100000000000, 'BIGINT(12)' ],
|
112
|
+
[ -1, 999999999999, 'BIGINT(12)' ],
|
113
|
+
[ -1, 1000000000000, 'BIGINT(13)' ],
|
114
|
+
[ -1, 9999999999999, 'BIGINT(13)' ],
|
115
|
+
[ -1, 10000000000000, 'BIGINT(14)' ],
|
116
|
+
[ -1, 99999999999999, 'BIGINT(14)' ],
|
117
|
+
[ -1, 100000000000000, 'BIGINT(15)' ],
|
118
|
+
[ -1, 999999999999999, 'BIGINT(15)' ],
|
119
|
+
[ -1, 1000000000000000, 'BIGINT(16)' ],
|
120
|
+
[ -1, 9999999999999999, 'BIGINT(16)' ],
|
121
|
+
[ -1, 10000000000000000, 'BIGINT(17)' ],
|
122
|
+
[ -1, 99999999999999999, 'BIGINT(17)' ],
|
123
|
+
[ -1, 100000000000000000, 'BIGINT(18)' ],
|
124
|
+
[ -1, 999999999999999999, 'BIGINT(18)' ],
|
125
|
+
[ -1, 1000000000000000000, 'BIGINT(19)' ],
|
126
|
+
[ -1, 9223372036854775807, 'BIGINT(19)' ],
|
127
|
+
|
128
|
+
[ -1, 0, 'TINYINT(2)' ],
|
129
|
+
[ -9, 0, 'TINYINT(2)' ],
|
130
|
+
[ -10, 0, 'TINYINT(3)' ],
|
131
|
+
[ -99, 0, 'TINYINT(3)' ],
|
132
|
+
[ -100, 0, 'TINYINT(4)' ],
|
133
|
+
[ -128, 0, 'TINYINT(4)' ],
|
134
|
+
[ -129, 0, 'SMALLINT(4)' ],
|
135
|
+
[ -999, 0, 'SMALLINT(4)' ],
|
136
|
+
[ -1000, 0, 'SMALLINT(5)' ],
|
137
|
+
[ -9999, 0, 'SMALLINT(5)' ],
|
138
|
+
[ -10000, 0, 'SMALLINT(6)' ],
|
139
|
+
[ -32768, 0, 'SMALLINT(6)' ],
|
140
|
+
[ -32769, 0, 'MEDIUMINT(6)' ],
|
141
|
+
[ -99999, 0, 'MEDIUMINT(6)' ],
|
142
|
+
[ -100000, 0, 'MEDIUMINT(7)' ],
|
143
|
+
[ -999999, 0, 'MEDIUMINT(7)' ],
|
144
|
+
[ -1000000, 0, 'MEDIUMINT(8)' ],
|
145
|
+
[ -8388608, 0, 'MEDIUMINT(8)' ],
|
146
|
+
[ -8388609, 0, 'INT(8)' ],
|
147
|
+
[ -9999999, 0, 'INT(8)' ],
|
148
|
+
[ -10000000, 0, 'INT(9)' ],
|
149
|
+
[ -99999999, 0, 'INT(9)' ],
|
150
|
+
[ -100000000, 0, 'INT(10)' ],
|
151
|
+
[ -999999999, 0, 'INT(10)' ],
|
152
|
+
[ -1000000000, 0, 'INT(11)' ],
|
153
|
+
[ -2147483648, 0, 'INT(11)' ],
|
154
|
+
[ -2147483649, 0, 'BIGINT(11)' ],
|
155
|
+
[ -9999999999, 0, 'BIGINT(11)' ],
|
156
|
+
[ -10000000000, 0, 'BIGINT(12)' ],
|
157
|
+
[ -99999999999, 0, 'BIGINT(12)' ],
|
158
|
+
[ -100000000000, 0, 'BIGINT(13)' ],
|
159
|
+
[ -999999999999, 0, 'BIGINT(13)' ],
|
160
|
+
[ -1000000000000, 0, 'BIGINT(14)' ],
|
161
|
+
[ -9999999999999, 0, 'BIGINT(14)' ],
|
162
|
+
[ -10000000000000, 0, 'BIGINT(15)' ],
|
163
|
+
[ -99999999999999, 0, 'BIGINT(15)' ],
|
164
|
+
[ -100000000000000, 0, 'BIGINT(16)' ],
|
165
|
+
[ -999999999999999, 0, 'BIGINT(16)' ],
|
166
|
+
[ -1000000000000000, 0, 'BIGINT(17)' ],
|
167
|
+
[ -9999999999999999, 0, 'BIGINT(17)' ],
|
168
|
+
[ -10000000000000000, 0, 'BIGINT(18)' ],
|
169
|
+
[ -99999999999999999, 0, 'BIGINT(18)' ],
|
170
|
+
[ -100000000000000000, 0, 'BIGINT(19)' ],
|
171
|
+
[ -999999999999999999, 0, 'BIGINT(19)' ],
|
172
|
+
[ -1000000000000000000, 0, 'BIGINT(20)' ],
|
173
|
+
[ -9223372036854775808, 0, 'BIGINT(20)' ],
|
174
|
+
|
175
|
+
[ nil, 2147483647, 'INT(10) UNSIGNED' ],
|
176
|
+
[ 0, nil, 'INT(10) UNSIGNED' ],
|
177
|
+
[ nil, nil, 'INTEGER' ],
|
178
|
+
].each do |min, max, statement|
|
179
|
+
options = { :key => true }
|
180
|
+
options[:min] = min if min
|
181
|
+
options[:max] = max if max
|
182
|
+
|
183
|
+
describe "with a min of #{min} and a max of #{max}" do
|
184
|
+
before :all do
|
185
|
+
@property = @model.property(:id, Integer, options)
|
186
|
+
|
187
|
+
@response = capture_log(DataObjects::Mysql) { @model.auto_migrate! }
|
188
|
+
end
|
189
|
+
|
190
|
+
it 'should return true' do
|
191
|
+
@response.should be(true)
|
192
|
+
end
|
193
|
+
|
194
|
+
it "should create a #{statement} column" do
|
195
|
+
@output.last.should =~ %r{\ACREATE TABLE `blog_articles` \(`id` #{Regexp.escape(statement)} NOT NULL, PRIMARY KEY\(`id`\)\) ENGINE = InnoDB CHARACTER SET [a-z\d]+ COLLATE (?:[a-z\d](?:_?[a-z\d]+)*)\z}
|
196
|
+
end
|
197
|
+
|
198
|
+
options.only(:min, :max).each do |key, value|
|
199
|
+
it "should allow the #{key} value #{value} to be stored" do
|
200
|
+
lambda {
|
201
|
+
resource = @model.create(@property => value)
|
202
|
+
@model.first(@property => value).should eql(resource)
|
203
|
+
}.should_not raise_error
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
describe 'Text property' do
|
211
|
+
before :all do
|
212
|
+
@model.property(:id, DataMapper::Property::Serial)
|
213
|
+
end
|
214
|
+
|
215
|
+
[
|
216
|
+
[ 0, 'TINYTEXT' ],
|
217
|
+
[ 1, 'TINYTEXT' ],
|
218
|
+
[ 255, 'TINYTEXT' ],
|
219
|
+
[ 256, 'TEXT' ],
|
220
|
+
[ 65535, 'TEXT' ],
|
221
|
+
[ 65536, 'MEDIUMTEXT' ],
|
222
|
+
[ 16777215, 'MEDIUMTEXT' ],
|
223
|
+
[ 16777216, 'LONGTEXT' ],
|
224
|
+
[ 4294967295, 'LONGTEXT' ],
|
225
|
+
|
226
|
+
[ nil, 'TEXT' ],
|
227
|
+
].each do |length, statement|
|
228
|
+
options = {}
|
229
|
+
options[:length] = length if length
|
230
|
+
|
231
|
+
describe "with a length of #{length}" do
|
232
|
+
before :all do
|
233
|
+
@property = @model.property(:body, DataMapper::Types::Text, options)
|
234
|
+
|
235
|
+
@response = capture_log(DataObjects::Mysql) { @model.auto_migrate! }
|
236
|
+
end
|
237
|
+
|
238
|
+
it 'should return true' do
|
239
|
+
@response.should be(true)
|
240
|
+
end
|
241
|
+
|
242
|
+
it "should create a #{statement} column" do
|
243
|
+
@output.last.should =~ %r{\ACREATE TABLE `blog_articles` \(`id` INT\(10\) UNSIGNED NOT NULL AUTO_INCREMENT, `body` #{Regexp.escape(statement)}, PRIMARY KEY\(`id`\)\) ENGINE = InnoDB CHARACTER SET [a-z\d]+ COLLATE (?:[a-z\d](?:_?[a-z\d]+)*)\z}
|
244
|
+
end
|
245
|
+
end
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
describe 'String property' do
|
250
|
+
before :all do
|
251
|
+
@model.property(:id, DataMapper::Property::Serial)
|
252
|
+
end
|
253
|
+
|
254
|
+
[
|
255
|
+
[ 1, 'VARCHAR(1)' ],
|
256
|
+
[ 50, 'VARCHAR(50)' ],
|
257
|
+
[ 255, 'VARCHAR(255)' ],
|
258
|
+
[ nil, 'VARCHAR(50)' ],
|
259
|
+
].each do |length, statement|
|
260
|
+
options = {}
|
261
|
+
options[:length] = length if length
|
262
|
+
|
263
|
+
describe "with a length of #{length}" do
|
264
|
+
before :all do
|
265
|
+
@property = @model.property(:title, String, options)
|
266
|
+
|
267
|
+
@response = capture_log(DataObjects::Mysql) { @model.auto_migrate! }
|
268
|
+
end
|
269
|
+
|
270
|
+
it 'should return true' do
|
271
|
+
@response.should be(true)
|
272
|
+
end
|
273
|
+
|
274
|
+
it "should create a #{statement} column" do
|
275
|
+
@output.last.should =~ %r{\ACREATE TABLE `blog_articles` \(`id` INT\(10\) UNSIGNED NOT NULL AUTO_INCREMENT, `title` #{Regexp.escape(statement)}, PRIMARY KEY\(`id`\)\) ENGINE = InnoDB CHARACTER SET [a-z\d]+ COLLATE (?:[a-z\d](?:_?[a-z\d]+)*)\z}
|
276
|
+
end
|
277
|
+
end
|
278
|
+
end
|
279
|
+
end
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
supported_by :postgres do
|
284
|
+
before :all do
|
285
|
+
module ::Blog
|
286
|
+
class Article
|
287
|
+
include DataMapper::Resource
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
@model = ::Blog::Article
|
292
|
+
end
|
293
|
+
|
294
|
+
describe '#auto_migrate' do
|
295
|
+
describe 'Integer property' do
|
296
|
+
[
|
297
|
+
[ 0, 1, 'SMALLINT' ],
|
298
|
+
[ 0, 32767, 'SMALLINT' ],
|
299
|
+
[ 0, 32768, 'INTEGER' ],
|
300
|
+
[ 0, 2147483647, 'INTEGER' ],
|
301
|
+
[ 0, 2147483648, 'BIGINT' ],
|
302
|
+
[ 0, 9223372036854775807, 'BIGINT' ],
|
303
|
+
|
304
|
+
[ -1, 1, 'SMALLINT' ],
|
305
|
+
[ -1, 32767, 'SMALLINT' ],
|
306
|
+
[ -1, 32768, 'INTEGER' ],
|
307
|
+
[ -1, 2147483647, 'INTEGER' ],
|
308
|
+
[ -1, 2147483648, 'BIGINT' ],
|
309
|
+
[ -1, 9223372036854775807, 'BIGINT' ],
|
310
|
+
|
311
|
+
[ -1, 0, 'SMALLINT' ],
|
312
|
+
[ -32768, 0, 'SMALLINT' ],
|
313
|
+
[ -32769, 0, 'INTEGER' ],
|
314
|
+
[ -2147483648, 0, 'INTEGER' ],
|
315
|
+
[ -2147483649, 0, 'BIGINT' ],
|
316
|
+
[ -9223372036854775808, 0, 'BIGINT' ],
|
317
|
+
|
318
|
+
[ nil, 2147483647, 'INTEGER' ],
|
319
|
+
[ 0, nil, 'INTEGER' ],
|
320
|
+
[ nil, nil, 'INTEGER' ],
|
321
|
+
].each do |min, max, statement|
|
322
|
+
options = { :key => true }
|
323
|
+
options[:min] = min if min
|
324
|
+
options[:max] = max if max
|
325
|
+
|
326
|
+
describe "with a min of #{min} and a max of #{max}" do
|
327
|
+
before :all do
|
328
|
+
@property = @model.property(:id, Integer, options)
|
329
|
+
|
330
|
+
@response = capture_log(DataObjects::Postgres) { @model.auto_migrate! }
|
331
|
+
end
|
332
|
+
|
333
|
+
it 'should return true' do
|
334
|
+
@response.should be(true)
|
335
|
+
end
|
336
|
+
|
337
|
+
it "should create a #{statement} column" do
|
338
|
+
@output[-2].should == "CREATE TABLE \"blog_articles\" (\"id\" #{statement} NOT NULL, PRIMARY KEY(\"id\"))"
|
339
|
+
end
|
340
|
+
|
341
|
+
options.only(:min, :max).each do |key, value|
|
342
|
+
it "should allow the #{key} value #{value} to be stored" do
|
343
|
+
lambda {
|
344
|
+
resource = @model.create(@property => value)
|
345
|
+
@model.first(@property => value).should eql(resource)
|
346
|
+
}.should_not raise_error
|
347
|
+
end
|
348
|
+
end
|
349
|
+
end
|
350
|
+
end
|
351
|
+
end
|
352
|
+
|
353
|
+
describe 'Serial property' do
|
354
|
+
[
|
355
|
+
[ 1, 'SERIAL' ],
|
356
|
+
[ 2147483647, 'SERIAL' ],
|
357
|
+
[ 2147483648, 'BIGSERIAL' ],
|
358
|
+
[ 9223372036854775807, 'BIGSERIAL' ],
|
359
|
+
|
360
|
+
[ nil, 'SERIAL' ],
|
361
|
+
].each do |max, statement|
|
362
|
+
options = {}
|
363
|
+
options[:max] = max if max
|
364
|
+
|
365
|
+
describe "with a max of #{max}" do
|
366
|
+
before :all do
|
367
|
+
@property = @model.property(:id, DataMapper::Property::Serial, options)
|
368
|
+
|
369
|
+
@response = capture_log(DataObjects::Postgres) { @model.auto_migrate! }
|
370
|
+
end
|
371
|
+
|
372
|
+
it 'should return true' do
|
373
|
+
@response.should be(true)
|
374
|
+
end
|
375
|
+
|
376
|
+
it "should create a #{statement} column" do
|
377
|
+
@output[-2].should == "CREATE TABLE \"blog_articles\" (\"id\" #{statement} NOT NULL, PRIMARY KEY(\"id\"))"
|
378
|
+
end
|
379
|
+
|
380
|
+
options.only(:min, :max).each do |key, value|
|
381
|
+
it "should allow the #{key} value #{value} to be stored" do
|
382
|
+
lambda {
|
383
|
+
resource = @model.create(@property => value)
|
384
|
+
@model.first(@property => value).should eql(resource)
|
385
|
+
}.should_not raise_error
|
386
|
+
end
|
387
|
+
end
|
388
|
+
end
|
389
|
+
end
|
390
|
+
end
|
391
|
+
|
392
|
+
describe 'String property' do
|
393
|
+
before :all do
|
394
|
+
@model.property(:id, DataMapper::Property::Serial)
|
395
|
+
end
|
396
|
+
|
397
|
+
[
|
398
|
+
[ 1, 'VARCHAR(1)' ],
|
399
|
+
[ 50, 'VARCHAR(50)' ],
|
400
|
+
[ 255, 'VARCHAR(255)' ],
|
401
|
+
[ nil, 'VARCHAR(50)' ],
|
402
|
+
].each do |length, statement|
|
403
|
+
options = {}
|
404
|
+
options[:length] = length if length
|
405
|
+
|
406
|
+
describe "with a length of #{length}" do
|
407
|
+
before :all do
|
408
|
+
@property = @model.property(:title, String, options)
|
409
|
+
|
410
|
+
@response = capture_log(DataObjects::Postgres) { @model.auto_migrate! }
|
411
|
+
end
|
412
|
+
|
413
|
+
it 'should return true' do
|
414
|
+
@response.should be(true)
|
415
|
+
end
|
416
|
+
|
417
|
+
it "should create a #{statement} column" do
|
418
|
+
@output[-2].should == "CREATE TABLE \"blog_articles\" (\"id\" SERIAL NOT NULL, \"title\" #{statement}, PRIMARY KEY(\"id\"))"
|
419
|
+
end
|
420
|
+
end
|
421
|
+
end
|
422
|
+
end
|
423
|
+
end
|
424
|
+
end
|
425
|
+
|
426
|
+
supported_by :sqlserver do
|
427
|
+
before :all do
|
428
|
+
module ::Blog
|
429
|
+
class Article
|
430
|
+
include DataMapper::Resource
|
431
|
+
end
|
432
|
+
end
|
433
|
+
|
434
|
+
@model = ::Blog::Article
|
435
|
+
end
|
436
|
+
|
437
|
+
describe '#auto_migrate' do
|
438
|
+
describe 'Integer property' do
|
439
|
+
[
|
440
|
+
[ 0, 1, 'TINYINT' ],
|
441
|
+
[ 0, 255, 'TINYINT' ],
|
442
|
+
[ 0, 256, 'SMALLINT' ],
|
443
|
+
[ 0, 32767, 'SMALLINT' ],
|
444
|
+
[ 0, 32768, 'INT' ],
|
445
|
+
[ 0, 2147483647, 'INT' ],
|
446
|
+
[ 0, 2147483648, 'BIGINT' ],
|
447
|
+
[ 0, 9223372036854775807, 'BIGINT' ],
|
448
|
+
|
449
|
+
[ -1, 1, 'SMALLINT' ],
|
450
|
+
[ -1, 255, 'SMALLINT' ],
|
451
|
+
[ -1, 256, 'SMALLINT' ],
|
452
|
+
[ -1, 32767, 'SMALLINT' ],
|
453
|
+
[ -1, 32768, 'INT' ],
|
454
|
+
[ -1, 2147483647, 'INT' ],
|
455
|
+
[ -1, 2147483648, 'BIGINT' ],
|
456
|
+
[ -1, 9223372036854775807, 'BIGINT' ],
|
457
|
+
|
458
|
+
[ -1, 0, 'SMALLINT' ],
|
459
|
+
[ -32768, 0, 'SMALLINT' ],
|
460
|
+
[ -32769, 0, 'INT' ],
|
461
|
+
[ -2147483648, 0, 'INT' ],
|
462
|
+
[ -2147483649, 0, 'BIGINT' ],
|
463
|
+
[ -9223372036854775808, 0, 'BIGINT' ],
|
464
|
+
|
465
|
+
[ nil, 2147483647, 'INT' ],
|
466
|
+
[ 0, nil, 'INT' ],
|
467
|
+
[ nil, nil, 'INTEGER' ],
|
468
|
+
].each do |min, max, statement|
|
469
|
+
options = { :key => true }
|
470
|
+
options[:min] = min if min
|
471
|
+
options[:max] = max if max
|
472
|
+
|
473
|
+
describe "with a min of #{min} and a max of #{max}" do
|
474
|
+
before :all do
|
475
|
+
@property = @model.property(:id, Integer, options)
|
476
|
+
|
477
|
+
@response = capture_log(DataObjects::Sqlserver) { @model.auto_migrate! }
|
478
|
+
end
|
479
|
+
|
480
|
+
it 'should return true' do
|
481
|
+
@response.should be(true)
|
482
|
+
end
|
483
|
+
|
484
|
+
it "should create a #{statement} column" do
|
485
|
+
@output.last.should == "CREATE TABLE \"blog_articles\" (\"id\" #{statement} NOT NULL, PRIMARY KEY(\"id\"))"
|
486
|
+
end
|
487
|
+
|
488
|
+
options.only(:min, :max).each do |key, value|
|
489
|
+
it "should allow the #{key} value #{value} to be stored" do
|
490
|
+
lambda {
|
491
|
+
resource = @model.create(@property => value)
|
492
|
+
@model.first(@property => value).should eql(resource)
|
493
|
+
}.should_not raise_error
|
494
|
+
end
|
495
|
+
end
|
496
|
+
end
|
497
|
+
end
|
498
|
+
end
|
499
|
+
|
500
|
+
describe 'String property' do
|
501
|
+
it 'needs specs'
|
502
|
+
end
|
503
|
+
end
|
504
|
+
end
|
505
|
+
|
506
|
+
end
|