Class: ActiveRecord::ConnectionAdapters::SQLServerAdapter

Inherits:
AbstractAdapter
  • Object
show all
Defined in:
lib/connection_adapters/sqlserver_adapter.rb

Instance Method Summary collapse

Instance Method Details

#constraints(table_name, name = nil) ⇒ Object

SQL Server allows you to duplicate table names & constraint names in a single database, provided that each constraint belongs to a different schema.

The nice trickery in Rails and DrySQL that relies on retrieving info from the information_schema views will get confused if you create > 1 table with the same name in the same database or > 1 constraint with the same name.

A future solution to this problem would be to have the ability to specify the desired schema in the database connection properties and have information_schema queries select only rows from the desired schema. Until this is implemented, do not duplicate table names or constraint names inside any DB.



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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
# File 'lib/connection_adapters/sqlserver_adapter.rb', line 61

def constraints(table_name, name = nil)#:nodoc:
  constraints = [] 
  
  # May 15/2007 - Constraints query re-written by Clifford Heath
	    sql = %Q{
		-- Get fields of all unique indexes:
		SELECT	user_name(o.uid) as constraint_schema,
			i.name AS constraint_name,
			o.name AS table_name,
			CASE (SELECT p.xtype FROM sysconstraints t, sysobjects p
  WHERE t.id = o.id
    AND t.constid = p.id
    AND i.name = p.name
 )
 WHEN 'PK' THEN 'PRIMARY KEY'
 WHEN 'UQ' THEN 'UNIQUE' -- unique constraint
 ELSE 'UNIQUE'		-- unique index
			END AS constraint_type,
			c.name AS column_name,
			NULL AS foreign_constraint_name,
			NULL AS referenced_constraint_name,
			NULL AS update_rule,
			NULL AS delete_rule,
			NULL AS foreign_table_name,
			NULL AS foreign_column_name
		FROM	sysobjects AS o,
			sysindexes AS i,
			sysindexkeys AS k,
			syscolumns AS c
		WHERE	('#{table_name}' = ''	-- All tables
			OR '#{table_name}' = o.name)
		  AND	o.type = 'U'		-- Tables
		  AND	o.status >= 0		-- exclude system tables
		  AND	o.id = i.id		-- indexes for this table
		  AND	o.id = k.id		-- indexkeys for table
		  AND	i.indid = k.indid	-- indexkey for index
		  AND	k.colid = c.colid	-- indexkey for column
		  AND	o.id = c.id		-- column for table
		  AND	(i.status&2) <> 0	-- unique
		  AND	i.indid NOT IN (0, 255)	-- not base table or text
		-- ORDER BY o.name, i.indid, k.keyno	-- Can't do this with UNION

		UNION ALL   -- Don't bother with distinct union!

		-- Get field pairs of all FK constraints to and from this table
		SELECT	user_name(foreign_key.uid) AS constraint_schema,
			foreign_key.name AS constraint_name,
			from_table.name AS table_name,
			'FOREIGN KEY' AS constraint_type,
			from_column.name AS column_name,
			foreign_key.name AS foreign_constraint_name,
			i.name AS referenced_constraint_name,
			CASE WHEN (ObjectProperty(f.constid, 'CnstIsUpdateCascade')=1)
 THEN 'CASCADE'
 ELSE 'NO ACTION'
			END AS update_rule,
			CASE WHEN (ObjectProperty(f.constid, 'CnstIsDeleteCascade')=1)
 THEN 'CASCADE'
 ELSE 'NO ACTION'
			END AS delete_rule,
			to_table.name AS foreign_table_name,
			to_column.name AS foreign_column_name
		FROM    sysobjects AS from_table,
			sysforeignkeys AS f,
			sysobjects AS to_table,
			sysobjects AS foreign_key,
			syscolumns AS from_column,
			syscolumns AS to_column,
			sysreferences AS r,
			sysindexes AS i
		WHERE   ('#{table_name}' = ''		-- FK's for all tables
			OR from_table.name = '#{table_name}'
			OR to_table.name = '#{table_name}')
		  AND	from_table.type = 'U'   	-- All user tables
		  AND   from_table.status >= 0
		  AND   from_table.id = f.fkeyid        -- All fk's from the table
		  AND   f.constid = foreign_key.id      -- Get the sysobject from fk
		  AND   f.rkeyid = to_table.id  	-- Get referenced table
		  AND   f.fkey = from_column.colid      -- Get source table's column
		  AND   from_column.id = from_table.id
		  AND   f.rkey = to_column.colid        -- And referenced's table's col
		  AND   to_column.id = to_table.id
		  AND	foreign_key.id = r.constid
		  AND	r.rkeyid = i.id
		  AND	r.rkeyindid = i.indid
	    }

  results = select_all(sql, name)
  constraint_name_hash = {}
  results.each do |row| 
    constraint_name = row['constraint_name']
    foreign_constraint_name = row['foreign_constraint_name']
    column_name = row['column_name']
    # Process constraints local to this table
    if !(current_constraint = constraint_name_hash[constraint_name])
      current_constraint = SQLServerConstraint.new(row['constraint_schema'], row['table_name'], column_name, constraint_name, 
      row['constraint_type'], row['referenced_constraint_name'], row['foreign_table_name'], row['foreign_column_name'] )
      constraints << current_constraint
      constraint_name_hash[constraint_name] = current_constraint 
    # This key is a composite
    else
      current_constraint.column_names << column_name unless current_constraint.column_names.include?(column_name)
      referenced_column_name = row['foreign_column_name']
      if referenced_column_name
        current_constraint.referenced_column_names << referenced_column_name unless current_constraint.referenced_column_names.include?(referenced_column_name)
      end
    end
  end
  constraints
end