1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
|
require 'active_support/core_ext/string/strip'
module ActiveRecord
module ConnectionAdapters
class AbstractAdapter
class SchemaCreation # :nodoc:
def initialize(conn)
@conn = conn
@cache = {}
end
def accept(o)
m = @cache[o.class] ||= "visit_#{o.class.name.split('::').last}"
send m, o
end
def visit_AddColumn(o)
"ADD #{accept(o)}"
end
private
def visit_AlterTable(o)
sql = "ALTER TABLE #{quote_table_name(o.name)} "
sql << o.adds.map { |col| visit_AddColumn col }.join(' ')
sql << o.foreign_key_adds.map { |fk| visit_AddForeignKey fk }.join(' ')
sql << o.foreign_key_drops.map { |fk| visit_DropForeignKey fk }.join(' ')
end
def visit_ColumnDefinition(o)
sql_type = type_to_sql(o.type, o.limit, o.precision, o.scale)
column_sql = "#{quote_column_name(o.name)} #{sql_type}"
add_column_options!(column_sql, column_options(o)) unless o.type == :primary_key
column_sql
end
def visit_TableDefinition(o)
create_sql = "CREATE#{' TEMPORARY' if o.temporary} TABLE "
create_sql << "#{quote_table_name(o.name)} "
create_sql << "(#{o.columns.map { |c| accept c }.join(', ')}) " unless o.as
create_sql << "#{o.options}"
create_sql << " AS #{@conn.to_sql(o.as)}" if o.as
create_sql
end
def visit_AddForeignKey(o)
sql = <<-SQL.strip_heredoc
ADD CONSTRAINT #{quote_column_name(o.name)}
FOREIGN KEY (#{quote_column_name(o.column)})
REFERENCES #{quote_table_name(o.to_table)} (#{quote_column_name(o.primary_key)})
SQL
sql << " #{action_sql('DELETE', o.on_delete)}" if o.on_delete
sql << " #{action_sql('UPDATE', o.on_update)}" if o.on_update
sql
end
def visit_DropForeignKey(name)
"DROP CONSTRAINT #{quote_column_name(name)}"
end
def column_options(o)
column_options = {}
column_options[:null] = o.null unless o.null.nil?
column_options[:default] = o.default unless o.default.nil?
column_options[:column] = o
column_options[:first] = o.first
column_options[:after] = o.after
column_options[:auto_increment] = o.auto_increment
column_options[:primary_key] = o.primary_key
column_options
end
def quote_column_name(name)
@conn.quote_column_name name
end
def quote_table_name(name)
@conn.quote_table_name name
end
def type_to_sql(type, limit, precision, scale)
@conn.type_to_sql type.to_sym, limit, precision, scale
end
def add_column_options!(sql, options)
sql << " DEFAULT #{quote_default_expression(options[:default], options[:column])}" if options_include_default?(options)
# must explicitly check for :null to allow change_column to work on migrations
if options[:null] == false
sql << " NOT NULL"
end
if options[:auto_increment] == true
sql << " AUTO_INCREMENT"
end
if options[:primary_key] == true
sql << " PRIMARY KEY"
end
sql
end
def quote_default_expression(value, column)
column.sql_type ||= type_to_sql(column.type, column.limit, column.precision, column.scale)
value = type_for_column(column).type_cast_for_database(value)
@conn.quote(value)
end
def options_include_default?(options)
options.include?(:default) && !(options[:null] == false && options[:default].nil?)
end
def action_sql(action, dependency)
case dependency
when :nullify then "ON #{action} SET NULL"
when :cascade then "ON #{action} CASCADE"
when :restrict then "ON #{action} RESTRICT"
else
raise ArgumentError, <<-MSG.strip_heredoc
'#{dependency}' is not supported for :on_update or :on_delete.
Supported values are: :nullify, :cascade, :restrict
MSG
end
end
def type_for_column(column)
@conn.lookup_cast_type(column.sql_type)
end
end
end
end
end
|