drysql 0.2.1 → 0.2.2
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/associations.rb +24 -16
- data/lib/base.rb +16 -12
- data/lib/connection_adapters/abstract/schema_definitions.rb +5 -10
- data/lib/connection_adapters/abstract_adapter.rb +4 -12
- data/lib/connection_adapters/ibm_db_adapter.rb +216 -0
- data/lib/connection_adapters/mysql_adapter.rb +38 -36
- data/lib/connection_adapters/oracle_adapter.rb +137 -156
- data/lib/connection_adapters/postgresql_adapter.rb +37 -26
- data/lib/connection_adapters/sqlserver_adapter.rb +101 -105
- data/lib/dependencies.rb +11 -7
- data/lib/drysql.rb +2 -2
- data/lib/validations.rb +24 -19
- metadata +9 -9
- data/lib/connection_adapters/ibm_db2_adapter.rb +0 -432
@@ -1,157 +1,138 @@
|
|
1
|
-
module ActiveRecord
|
2
|
-
|
3
|
-
module ConnectionAdapters
|
4
|
-
|
5
|
-
class OracleColumn < Column
|
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
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
if !foreign_constraint_name.nil? && !constraint_name_hash.has_key?(foreign_constraint_name)
|
139
|
-
current_foreign_constraint = OracleConstraint.new(foreign_constraint_name, OracleConstraint::FOREIGN_KEY_TYPE,
|
140
|
-
row['foreign_table_name'], row['foreign_column_name'], constraint_name,
|
141
|
-
row['table_name'], row['column_name'], row['foreign_delete_rule'])
|
142
|
-
constraints << current_foreign_constraint
|
143
|
-
constraint_name_hash[foreign_constraint_name] = current_foreign_constraint
|
144
|
-
# Composite FKs are currently not supported
|
145
|
-
# else
|
146
|
-
# constraint_name_hash[foreign_constraint_name].column_name.add(row['foreign_column_name'])
|
147
|
-
end
|
148
|
-
end
|
149
|
-
constraints
|
150
|
-
end
|
151
|
-
|
152
|
-
|
153
|
-
end
|
154
|
-
|
155
|
-
end
|
156
|
-
|
1
|
+
module ActiveRecord
|
2
|
+
|
3
|
+
module ConnectionAdapters
|
4
|
+
|
5
|
+
class OracleColumn < Column
|
6
|
+
|
7
|
+
# The default initialize method must be overridden here in order to retrieve the
|
8
|
+
# original default value. The standard Rails adapter nils out the default value
|
9
|
+
# if it is not of a recognized type, so it then appears as if no default value
|
10
|
+
# was specified. DrySQL grabs the default value before it gets nil'd out
|
11
|
+
def initialize(name, default, sql_type = nil, null = true)
|
12
|
+
if !(default.nil? || default.blank?) then @default_specified = true end
|
13
|
+
super(name, default, sql_type, null)
|
14
|
+
end
|
15
|
+
|
16
|
+
def is_nullable?
|
17
|
+
@null || @default_specified
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
class OracleConstraint < AbstractTableConstraint
|
23
|
+
|
24
|
+
PRIMARY_KEY_TYPE = "P"
|
25
|
+
FOREIGN_KEY_TYPE = "R"
|
26
|
+
UNIQUE_KEY_TYPE = "U"
|
27
|
+
CHECK_CONSTRAINT_TYPE = "C"
|
28
|
+
|
29
|
+
def initialize(constraint_name, constraint_type, table_name, column_name,
|
30
|
+
referenced_constraint_name, referenced_table_name, referenced_column_name, delete_rule)
|
31
|
+
@constraint_name = constraint_name
|
32
|
+
@table_schema = table_schema
|
33
|
+
@table_name = table_name
|
34
|
+
@constraint_type = constraint_type
|
35
|
+
@column_names = [column_name]
|
36
|
+
@referenced_table_name = referenced_table_name
|
37
|
+
@referenced_column_names = [ referenced_column_name ] if referenced_column_name
|
38
|
+
@delete_rule = delete_rule
|
39
|
+
end
|
40
|
+
|
41
|
+
def is_foreign_constraint?(table_name)
|
42
|
+
@table_name.upcase != table_name.upcase
|
43
|
+
end
|
44
|
+
|
45
|
+
def primary_key?
|
46
|
+
constraint_type == PRIMARY_KEY_TYPE
|
47
|
+
end
|
48
|
+
|
49
|
+
def foreign_key?
|
50
|
+
constraint_type == FOREIGN_KEY_TYPE
|
51
|
+
end
|
52
|
+
|
53
|
+
def unique_key?
|
54
|
+
constraint_type == UNIQUE_KEY_TYPE
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
|
60
|
+
class OracleAdapter < AbstractAdapter
|
61
|
+
|
62
|
+
def constraints(table_name, name = nil)#:nodoc:
|
63
|
+
constraints = []
|
64
|
+
upcase_table_name = table_name.upcase
|
65
|
+
sql = %Q{
|
66
|
+
select UC.constraint_name, UC.constraint_type, UC.table_name, COL.column_name,
|
67
|
+
REF.r_constraint_name as referenced_constraint_name, REF.constraint_name as foreign_constraint_name,
|
68
|
+
REF.delete_rule as foreign_delete_rule,
|
69
|
+
COLREF.table_name as foreign_table_name,
|
70
|
+
COLREF.column_name as foreign_column_name
|
71
|
+
from (select owner, constraint_name, constraint_type, table_name, r_owner, r_constraint_name
|
72
|
+
from all_constraints
|
73
|
+
where table_name='#{upcase_table_name}') UC
|
74
|
+
inner join (select constraint_name, table_name, column_name from all_cons_columns where table_name='#{upcase_table_name}') COL
|
75
|
+
on (COL.constraint_name = UC.constraint_name)
|
76
|
+
left join all_constraints REF
|
77
|
+
on (UC.constraint_name=REF.constraint_name OR UC.constraint_name=REF.r_constraint_name)
|
78
|
+
left join all_cons_columns COLREF
|
79
|
+
on (not COLREF.table_name='#{upcase_table_name}'
|
80
|
+
AND (REF.constraint_name=COLREF.constraint_name OR REF.r_constraint_name=COLREF.constraint_name))
|
81
|
+
}
|
82
|
+
|
83
|
+
results = select_all(sql, name)
|
84
|
+
constraint_name_hash = {}
|
85
|
+
results.each do |row|
|
86
|
+
# The author(s) of the Oracle adapter chose to selectively downcase column names for
|
87
|
+
# "cleanliness" within our Rails code. I had to follow suit with constraint column & table names
|
88
|
+
# so that model objects could look up their data values using the generated column accessor methods
|
89
|
+
unless row['column_name'].nil? then row['column_name'] = oracle_downcase(row['column_name']) end
|
90
|
+
unless row['foreign_column_name'].nil? then row['foreign_column_name'] = oracle_downcase(row['foreign_column_name']) end
|
91
|
+
unless row['table_name'].nil? then row['table_name'] = oracle_downcase(row['table_name']) end
|
92
|
+
unless row['foreign_table_name'].nil? then row['foreign_table_name'] = oracle_downcase(row['foreign_table_name']) end
|
93
|
+
constraint_name = row['constraint_name']
|
94
|
+
foreign_constraint_name = row['foreign_constraint_name']
|
95
|
+
column_name = row['column_name']
|
96
|
+
|
97
|
+
# Process constraints local to this table
|
98
|
+
if !(current_constraint = constraint_name_hash[constraint_name])
|
99
|
+
current_constraint = OracleConstraint.new(constraint_name, row['constraint_type'], row['table_name'],
|
100
|
+
column_name, row['referenced_constraint_name'], row['foreign_table_name'],
|
101
|
+
row['foreign_column_name'], row['foreign_delete_rule'])
|
102
|
+
constraints << current_constraint
|
103
|
+
constraint_name_hash[constraint_name] = current_constraint
|
104
|
+
# This key is a composite
|
105
|
+
else
|
106
|
+
current_constraint.column_names << column_name unless current_constraint.column_names.include?(column_name)
|
107
|
+
referenced_column_name = row['foreign_column_name']
|
108
|
+
if referenced_column_name
|
109
|
+
current_constraint.referenced_column_names << referenced_column_name unless current_constraint.referenced_column_names.include?(referenced_column_name)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
# Process constraints that reference this table's local constraints
|
114
|
+
if foreign_constraint_name && foreign_constraint_name != constraint_name
|
115
|
+
if !(current_foreign_constraint = constraint_name_hash[foreign_constraint_name])
|
116
|
+
current_foreign_constraint = OracleConstraint.new(foreign_constraint_name, OracleConstraint::FOREIGN_KEY_TYPE,
|
117
|
+
row['foreign_table_name'], row['foreign_column_name'], constraint_name,
|
118
|
+
row['table_name'], row['column_name'], row['foreign_delete_rule'])
|
119
|
+
constraints << current_foreign_constraint
|
120
|
+
constraint_name_hash[foreign_constraint_name] = current_foreign_constraint
|
121
|
+
# This key is a composite
|
122
|
+
else
|
123
|
+
referenced_column_name = row['foreign_column_name']
|
124
|
+
if referenced_column_name
|
125
|
+
current_foreign_constraint.column_names << referenced_column_name unless current_foreign_constraint.column_names.include?(referenced_column_name)
|
126
|
+
end
|
127
|
+
current_foreign_constraint.referenced_column_names << column_name unless current_foreign_constraint.referenced_column_names.include?(column_name)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
constraints
|
132
|
+
end
|
133
|
+
|
134
|
+
end
|
135
|
+
|
136
|
+
end
|
137
|
+
|
157
138
|
end
|
@@ -5,11 +5,9 @@ module ActiveRecord
|
|
5
5
|
class PostgreSQLColumn < Column #:nodoc:
|
6
6
|
attr_accessor :generated, :default_specified
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
@default_specified
|
12
|
-
end
|
8
|
+
def is_nullable?
|
9
|
+
@null || @default_specified || @generated
|
10
|
+
end
|
13
11
|
|
14
12
|
end
|
15
13
|
|
@@ -22,12 +20,12 @@ module ActiveRecord
|
|
22
20
|
@table_catalog = table_catalog
|
23
21
|
@table_schema = table_schema
|
24
22
|
@table_name = table_name
|
25
|
-
@
|
23
|
+
@column_names = [column_name]
|
26
24
|
@constraint_name = constraint_name
|
27
25
|
@constraint_type = constraint_type
|
28
26
|
@referenced_constraint_name = referenced_constraint_name
|
29
27
|
@referenced_table_name = referenced_table_name
|
30
|
-
@
|
28
|
+
@referenced_column_names = [referenced_column_name] if referenced_column_name
|
31
29
|
@update_rule = update_rule
|
32
30
|
@delete_rule = delete_rule
|
33
31
|
end
|
@@ -62,30 +60,43 @@ module ActiveRecord
|
|
62
60
|
def constraints(table_name, name = nil)#:nodoc:
|
63
61
|
constraints = []
|
64
62
|
|
65
|
-
sql =
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
63
|
+
sql = %Q{
|
64
|
+
select t1.*, KCU.column_name, REF2.unique_constraint_name, KCU2.table_name as referenced_table_name,
|
65
|
+
KCU2.column_name as referenced_column_name, REF2.delete_rule, REF2.update_rule
|
66
|
+
from (select constraint_name, table_catalog, table_schema, table_name, constraint_type
|
67
|
+
from information_schema.table_constraints
|
68
|
+
where table_name='#{table_name.downcase}'
|
69
|
+
or constraint_name in
|
70
|
+
(select REF.constraint_name from information_schema.referential_constraints as REF
|
71
|
+
where unique_constraint_name in
|
72
|
+
(select constraint_name from information_schema.table_constraints
|
73
|
+
where table_name='#{table_name.downcase}'))) as t1
|
74
|
+
inner join information_schema.key_column_usage as KCU
|
75
|
+
on (t1.constraint_name=KCU.constraint_name)
|
76
|
+
left join information_schema.referential_constraints as REF2
|
77
|
+
on (REF2.constraint_name=t1.constraint_name)
|
78
|
+
left join information_schema.key_column_usage as KCU2
|
79
|
+
on (REF2.unique_constraint_name=KCU2.constraint_name)
|
80
|
+
}
|
75
81
|
|
76
82
|
results = query(sql, name)
|
77
83
|
constraint_name_hash = {}
|
78
84
|
results.each do |row|
|
79
|
-
constraints << PostgreSQLConstraint.new(row[0], row[1], row[2], row[3], row[4], row[5], row[6], row[7], row[8], row[9], row[10])
|
80
85
|
comparable_constraint_name = row[0].upcase
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
86
|
+
referenced_column_name = row[8]
|
87
|
+
column_name = row[5]
|
88
|
+
existing_constraint = constraint_name_hash[comparable_constraint_name]
|
89
|
+
if !existing_constraint
|
90
|
+
new_constraint = PostgreSQLConstraint.new(row[0], row[1], row[2], row[3], row[4], column_name, row[6], row[7],
|
91
|
+
referenced_column_name, row[9], row[10])
|
92
|
+
constraints << new_constraint
|
93
|
+
constraint_name_hash[comparable_constraint_name] = new_constraint
|
94
|
+
else
|
95
|
+
existing_constraint.column_names << column_name unless existing_constraint.column_names.include?(column_name)
|
96
|
+
if referenced_column_name
|
97
|
+
existing_constraint.referenced_column_names << referenced_column_name unless existing_constraint.referenced_column_names.include?(referenced_column_name)
|
98
|
+
end
|
99
|
+
end
|
89
100
|
end
|
90
101
|
constraints
|
91
102
|
end
|
@@ -3,14 +3,12 @@ module ActiveRecord
|
|
3
3
|
module ConnectionAdapters
|
4
4
|
|
5
5
|
class SQLServerColumn < Column# :nodoc:
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
def default_specified?
|
11
|
-
@default_specified
|
6
|
+
|
7
|
+
def is_nullable?
|
8
|
+
@null || @default_specified || @identity
|
12
9
|
end
|
13
10
|
|
11
|
+
|
14
12
|
# Borrowed verbatim from standard Rails sqlserver_adapter.
|
15
13
|
# Had to re-implement a bit of functionality here to avoid
|
16
14
|
# a dependency on SQLServerColumn.identity accessor
|
@@ -20,10 +18,6 @@ module ActiveRecord
|
|
20
18
|
def initialize(name, default, sql_type = nil, identity = false, null = true)
|
21
19
|
if !(default.nil? || default.blank?) then @default_specified = true end
|
22
20
|
super(name, default, sql_type, null)
|
23
|
-
@identity = identity
|
24
|
-
@is_special = sql_type =~ /text|ntext|image/i
|
25
|
-
# SQL Server only supports limits on *char and float types
|
26
|
-
@limit = nil unless @type == :float or @type == :string
|
27
21
|
end
|
28
22
|
|
29
23
|
end
|
@@ -34,75 +28,25 @@ module ActiveRecord
|
|
34
28
|
referenced_constraint_name, referenced_table_name, referenced_column_name)
|
35
29
|
@table_schema = table_schema
|
36
30
|
@table_name = table_name
|
37
|
-
@
|
31
|
+
@column_names = [column_name]
|
38
32
|
@referenced_table_name = referenced_table_name
|
39
|
-
@
|
33
|
+
@referenced_column_names = [referenced_column_name] if referenced_column_name
|
40
34
|
@constraint_schema = constraint_schema
|
41
|
-
@referenced_constraint_name = referenced_constraint_name
|
35
|
+
@referenced_constraint_name = referenced_constraint_name
|
42
36
|
@constraint_name = constraint_name
|
43
37
|
@constraint_type = constraint_type
|
44
38
|
end
|
45
39
|
|
46
40
|
|
47
|
-
def is_member_of_composite?
|
48
|
-
@column_name.size > 1
|
49
|
-
end
|
50
|
-
|
51
41
|
def is_foreign_constraint?(table_name)
|
52
42
|
@table_name.upcase != table_name.upcase
|
53
43
|
end
|
54
44
|
|
55
45
|
end
|
46
|
+
|
56
47
|
|
57
48
|
class SQLServerAdapter < AbstractAdapter
|
58
49
|
|
59
|
-
# Borrowed verbatim from Rails sqlserver_adapter.
|
60
|
-
#
|
61
|
-
# Since we can't create a dependency from ActiveRecord to DrySQL,
|
62
|
-
# we need to instantiate SQLServer columns in this class extension.
|
63
|
-
#
|
64
|
-
# I'll be able to remove this code once the ActiveRecord gem is
|
65
|
-
# released with the new version of the sqlserver_adapter that defines
|
66
|
-
# the SQLServerColumn class
|
67
|
-
def columns(table_name, name = nil)
|
68
|
-
return [] if table_name.blank?
|
69
|
-
table_name = table_name.to_s if table_name.is_a?(Symbol)
|
70
|
-
table_name = table_name.split('.')[-1] unless table_name.nil?
|
71
|
-
sql = %Q{
|
72
|
-
SELECT
|
73
|
-
cols.COLUMN_NAME as ColName,
|
74
|
-
cols.COLUMN_DEFAULT as DefaultValue,
|
75
|
-
cols.NUMERIC_SCALE as numeric_scale,
|
76
|
-
cols.NUMERIC_PRECISION as numeric_precision,
|
77
|
-
cols.DATA_TYPE as ColType,
|
78
|
-
cols.IS_NULLABLE As IsNullable,
|
79
|
-
COL_LENGTH(cols.TABLE_NAME, cols.COLUMN_NAME) as Length,
|
80
|
-
COLUMNPROPERTY(OBJECT_ID(cols.TABLE_NAME), cols.COLUMN_NAME, 'IsIdentity') as IsIdentity,
|
81
|
-
cols.NUMERIC_SCALE as Scale
|
82
|
-
FROM INFORMATION_SCHEMA.COLUMNS cols
|
83
|
-
WHERE cols.TABLE_NAME = '#{table_name}'
|
84
|
-
}
|
85
|
-
# Comment out if you want to have the Columns select statment logged.
|
86
|
-
# Personally, I think it adds unnecessary bloat to the log.
|
87
|
-
# If you do comment it out, make sure to un-comment the "result" line that follows
|
88
|
-
result = log(sql, name) { @connection.select_all(sql) }
|
89
|
-
#result = @connection.select_all(sql)
|
90
|
-
columns = []
|
91
|
-
result.each do |field|
|
92
|
-
default = field[:DefaultValue].to_s.gsub!(/[()\']/,"") =~ /null/ ? nil : field[:DefaultValue]
|
93
|
-
if field[:ColType] =~ /numeric|decimal/i
|
94
|
-
type = "#{field[:ColType]}(#{field[:numeric_precision]},#{field[:numeric_scale]})"
|
95
|
-
else
|
96
|
-
type = "#{field[:ColType]}(#{field[:Length]})"
|
97
|
-
end
|
98
|
-
is_identity = field[:IsIdentity] == 1
|
99
|
-
is_nullable = field[:IsNullable] == 'YES'
|
100
|
-
columns << SQLServerColumn.new(field[:ColName], default, type, is_identity, is_nullable)
|
101
|
-
end
|
102
|
-
columns
|
103
|
-
end
|
104
|
-
|
105
|
-
|
106
50
|
# SQL Server allows you to duplicate table names & constraint names in a single database,
|
107
51
|
# provided that each constraint belongs to a different schema.
|
108
52
|
#
|
@@ -114,60 +58,112 @@ module ActiveRecord
|
|
114
58
|
# in the database connection properties and have information_schema queries select only
|
115
59
|
# rows from the desired schema. Until this is implemented, do not duplicate table names
|
116
60
|
# or constraint names inside any DB.
|
117
|
-
#
|
118
|
-
# Constraints Query Optimization:
|
119
|
-
# -------------------------------
|
120
|
-
# - The current query seems to be optimal for the strategy of retrieving local table constraints
|
121
|
-
# as well as foreign keys into the current table in a single query
|
122
|
-
# - Since 4 tables need to be joined to return information about FKs into the current table
|
123
|
-
# the strategy is to join maximally reduced subsets of each table (i.e. rather than
|
124
|
-
# join the entire table, select minimum number of columns necessary and the minimum number of
|
125
|
-
# rows necessary from each table and then join the results)
|
126
|
-
# - Due to the layout of the information_schema views, the database will always need to scan every
|
127
|
-
# row in REFERENTIAL_CONSTRAINTS in order to stitch together table constraints and the FKs from
|
128
|
-
# other tables that reference these table constraints, which means that performance is inversely
|
129
|
-
# related to the number of FKs defined in the schema. Deleting unnecessary tables & FKs, and keeping
|
130
|
-
# logically separate groups of tables in separate schemas will improve performance
|
131
61
|
def constraints(table_name, name = nil)#:nodoc:
|
132
62
|
constraints = []
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
63
|
+
|
64
|
+
# May 15/2007 - Constraints query re-written by Clifford Heath
|
65
|
+
sql = %Q{
|
66
|
+
-- Get fields of all unique indexes:
|
67
|
+
SELECT user_name(o.uid) as constraint_schema,
|
68
|
+
i.name AS constraint_name,
|
69
|
+
o.name AS table_name,
|
70
|
+
CASE (SELECT p.xtype FROM sysconstraints t, sysobjects p
|
71
|
+
WHERE t.id = o.id
|
72
|
+
AND t.constid = p.id
|
73
|
+
AND i.name = p.name
|
74
|
+
)
|
75
|
+
WHEN 'PK' THEN 'PRIMARY KEY'
|
76
|
+
WHEN 'UQ' THEN 'UNIQUE' -- unique constraint
|
77
|
+
ELSE 'UNIQUE' -- unique index
|
78
|
+
END AS constraint_type,
|
79
|
+
c.name AS column_name,
|
80
|
+
NULL AS foreign_constraint_name,
|
81
|
+
NULL AS referenced_constraint_name,
|
82
|
+
NULL AS update_rule,
|
83
|
+
NULL AS delete_rule,
|
84
|
+
NULL AS foreign_table_name,
|
85
|
+
NULL AS foreign_column_name
|
86
|
+
FROM sysobjects AS o,
|
87
|
+
sysindexes AS i,
|
88
|
+
sysindexkeys AS k,
|
89
|
+
syscolumns AS c
|
90
|
+
WHERE ('#{table_name}' = '' -- All tables
|
91
|
+
OR '#{table_name}' = o.name)
|
92
|
+
AND o.type = 'U' -- Tables
|
93
|
+
AND o.status >= 0 -- exclude system tables
|
94
|
+
AND o.id = i.id -- indexes for this table
|
95
|
+
AND o.id = k.id -- indexkeys for table
|
96
|
+
AND i.indid = k.indid -- indexkey for index
|
97
|
+
AND k.colid = c.colid -- indexkey for column
|
98
|
+
AND o.id = c.id -- column for table
|
99
|
+
AND (i.status&2) <> 0 -- unique
|
100
|
+
AND i.indid NOT IN (0, 255) -- not base table or text
|
101
|
+
-- ORDER BY o.name, i.indid, k.keyno -- Can't do this with UNION
|
102
|
+
|
103
|
+
UNION ALL -- Don't bother with distinct union!
|
104
|
+
|
105
|
+
-- Get field pairs of all FK constraints to and from this table
|
106
|
+
SELECT user_name(foreign_key.uid) AS constraint_schema,
|
107
|
+
foreign_key.name AS constraint_name,
|
108
|
+
from_table.name AS table_name,
|
109
|
+
'FOREIGN KEY' AS constraint_type,
|
110
|
+
from_column.name AS column_name,
|
111
|
+
foreign_key.name AS foreign_constraint_name,
|
112
|
+
i.name AS referenced_constraint_name,
|
113
|
+
CASE WHEN (ObjectProperty(f.constid, 'CnstIsUpdateCascade')=1)
|
114
|
+
THEN 'CASCADE'
|
115
|
+
ELSE 'NO ACTION'
|
116
|
+
END AS update_rule,
|
117
|
+
CASE WHEN (ObjectProperty(f.constid, 'CnstIsDeleteCascade')=1)
|
118
|
+
THEN 'CASCADE'
|
119
|
+
ELSE 'NO ACTION'
|
120
|
+
END AS delete_rule,
|
121
|
+
to_table.name AS foreign_table_name,
|
122
|
+
to_column.name AS foreign_column_name
|
123
|
+
FROM sysobjects AS from_table,
|
124
|
+
sysforeignkeys AS f,
|
125
|
+
sysobjects AS to_table,
|
126
|
+
sysobjects AS foreign_key,
|
127
|
+
syscolumns AS from_column,
|
128
|
+
syscolumns AS to_column,
|
129
|
+
sysreferences AS r,
|
130
|
+
sysindexes AS i
|
131
|
+
WHERE ('#{table_name}' = '' -- FK's for all tables
|
132
|
+
OR from_table.name = '#{table_name}'
|
133
|
+
OR to_table.name = '#{table_name}')
|
134
|
+
AND from_table.type = 'U' -- All user tables
|
135
|
+
AND from_table.status >= 0
|
136
|
+
AND from_table.id = f.fkeyid -- All fk's from the table
|
137
|
+
AND f.constid = foreign_key.id -- Get the sysobject from fk
|
138
|
+
AND f.rkeyid = to_table.id -- Get referenced table
|
139
|
+
AND f.fkey = from_column.colid -- Get source table's column
|
140
|
+
AND from_column.id = from_table.id
|
141
|
+
AND f.rkey = to_column.colid -- And referenced's table's col
|
142
|
+
AND to_column.id = to_table.id
|
143
|
+
AND foreign_key.id = r.constid
|
144
|
+
AND r.rkeyid = i.id
|
145
|
+
AND r.rkeyindid = i.indid
|
146
|
+
}
|
142
147
|
|
143
148
|
results = select_all(sql, name)
|
144
149
|
constraint_name_hash = {}
|
145
150
|
results.each do |row|
|
146
151
|
constraint_name = row['constraint_name']
|
147
152
|
foreign_constraint_name = row['foreign_constraint_name']
|
148
|
-
|
153
|
+
column_name = row['column_name']
|
149
154
|
# Process constraints local to this table
|
150
|
-
if !constraint_name_hash
|
151
|
-
current_constraint = SQLServerConstraint.new(row['constraint_schema'], row['table_name'],
|
155
|
+
if !(current_constraint = constraint_name_hash[constraint_name])
|
156
|
+
current_constraint = SQLServerConstraint.new(row['constraint_schema'], row['table_name'], column_name, constraint_name,
|
152
157
|
row['constraint_type'], row['referenced_constraint_name'], row['foreign_table_name'], row['foreign_column_name'] )
|
153
158
|
constraints << current_constraint
|
154
|
-
constraint_name_hash[constraint_name] = current_constraint
|
159
|
+
constraint_name_hash[constraint_name] = current_constraint
|
155
160
|
# This key is a composite
|
156
161
|
else
|
157
|
-
current_constraint
|
158
|
-
|
159
|
-
if
|
160
|
-
|
161
|
-
|
162
|
-
# Process constraints that reference this table's local constraints
|
163
|
-
if !foreign_constraint_name.nil? && !constraint_name_hash.has_key?(foreign_constraint_name)
|
164
|
-
current_foreign_constraint = SQLServerConstraint.new(row['constraint_schema'], row['foreign_table_name'], row['foreign_column_name'],
|
165
|
-
foreign_constraint_name, SQLServerConstraint::FOREIGN_KEY_TYPE, constraint_name, row['table_name'], row['column_name'] )
|
166
|
-
constraints << current_foreign_constraint
|
167
|
-
constraint_name_hash[foreign_constraint_name] = current_foreign_constraint
|
168
|
-
# Composite FKs are currently not supported
|
169
|
-
# else
|
170
|
-
# constraint_name_hash[foreign_constraint_name].column_name.add(row['foreign_column_name'])
|
162
|
+
current_constraint.column_names << column_name unless current_constraint.column_names.include?(column_name)
|
163
|
+
referenced_column_name = row['foreign_column_name']
|
164
|
+
if referenced_column_name
|
165
|
+
current_constraint.referenced_column_names << referenced_column_name unless current_constraint.referenced_column_names.include?(referenced_column_name)
|
166
|
+
end
|
171
167
|
end
|
172
168
|
end
|
173
169
|
constraints
|