root / env / lib / python2.7 / site-packages / south / db / firebird.py @ d1a4905f
History | View | Annotate | Download (12.7 KB)
1 |
# firebird
|
---|---|
2 |
|
3 |
import datetime |
4 |
|
5 |
from django.db import connection, models |
6 |
from django.core.management.color import no_style |
7 |
from django.db.utils import DatabaseError |
8 |
|
9 |
from south.db import generic |
10 |
|
11 |
class DatabaseOperations(generic.DatabaseOperations): |
12 |
backend_name = 'firebird'
|
13 |
alter_string_set_type = 'ALTER %(column)s TYPE %(type)s'
|
14 |
alter_string_set_default = 'ALTER %(column)s SET DEFAULT %(default)s;'
|
15 |
alter_string_drop_null = ''
|
16 |
add_column_string = 'ALTER TABLE %s ADD %s;'
|
17 |
delete_column_string = 'ALTER TABLE %s DROP %s;'
|
18 |
allows_combined_alters = False
|
19 |
|
20 |
def _fill_constraint_cache(self, db_name, table_name): |
21 |
self._constraint_cache.setdefault(db_name, {})
|
22 |
self._constraint_cache[db_name][table_name] = {}
|
23 |
|
24 |
rows = self.execute(""" |
25 |
SELECT
|
26 |
rc.RDB$CONSTRAINT_NAME,
|
27 |
rc.RDB$CONSTRAINT_TYPE,
|
28 |
cc.RDB$TRIGGER_NAME
|
29 |
FROM rdb$relation_constraints rc
|
30 |
JOIN rdb$check_constraints cc
|
31 |
ON rc.rdb$constraint_name = cc.rdb$constraint_name
|
32 |
WHERE rc.rdb$constraint_type = 'NOT NULL'
|
33 |
AND rc.rdb$relation_name = '%s'
|
34 |
""" % table_name)
|
35 |
|
36 |
for constraint, kind, column in rows: |
37 |
self._constraint_cache[db_name][table_name].setdefault(column, set()) |
38 |
self._constraint_cache[db_name][table_name][column].add((kind, constraint))
|
39 |
return
|
40 |
|
41 |
def _alter_column_set_null(self, table_name, column_name, is_null): |
42 |
sql = """
|
43 |
UPDATE RDB$RELATION_FIELDS SET RDB$NULL_FLAG = %(null_flag)s
|
44 |
WHERE RDB$FIELD_NAME = '%(column)s'
|
45 |
AND RDB$RELATION_NAME = '%(table_name)s'
|
46 |
"""
|
47 |
null_flag = 'NULL' if is_null else '1' |
48 |
return sql % {
|
49 |
'null_flag': null_flag,
|
50 |
'column': column_name.upper(),
|
51 |
'table_name': table_name.upper()
|
52 |
} |
53 |
|
54 |
def _column_has_default(self, params): |
55 |
sql = """
|
56 |
SELECT a.RDB$DEFAULT_VALUE
|
57 |
FROM RDB$RELATION_FIELDS a
|
58 |
WHERE a.RDB$FIELD_NAME = '%(column)s'
|
59 |
AND a.RDB$RELATION_NAME = '%(table_name)s'
|
60 |
"""
|
61 |
value = self.execute(sql % params)
|
62 |
return True if value else False |
63 |
|
64 |
|
65 |
def _alter_set_defaults(self, field, name, params, sqls): |
66 |
"Subcommand of alter_column that sets default values (overrideable)"
|
67 |
# Next, set any default
|
68 |
if not field.null and field.has_default(): |
69 |
default = field.get_default() |
70 |
sqls.append(('ALTER COLUMN %s SET DEFAULT %%s ' % (self.quote_name(name),), [default])) |
71 |
elif self._column_has_default(params): |
72 |
sqls.append(('ALTER COLUMN %s DROP DEFAULT' % (self.quote_name(name),), [])) |
73 |
|
74 |
|
75 |
@generic.invalidate_table_constraints
|
76 |
def create_table(self, table_name, fields): |
77 |
qn = self.quote_name(table_name)
|
78 |
columns = [] |
79 |
autoinc_sql = ''
|
80 |
|
81 |
for field_name, field in fields: |
82 |
col = self.column_sql(table_name, field_name, field)
|
83 |
if not col: |
84 |
continue
|
85 |
#col = self.adj_column_sql(col)
|
86 |
|
87 |
columns.append(col) |
88 |
if isinstance(field, models.AutoField): |
89 |
field_name = field.db_column or field.column
|
90 |
autoinc_sql = connection.ops.autoinc_sql(table_name, field_name) |
91 |
|
92 |
sql = 'CREATE TABLE %s (%s);' % (qn, ', '.join([col for col in columns])) |
93 |
self.execute(sql)
|
94 |
if autoinc_sql:
|
95 |
self.execute(autoinc_sql[0]) |
96 |
self.execute(autoinc_sql[1]) |
97 |
|
98 |
|
99 |
def column_sql(self, table_name, field_name, field, tablespace='', with_name=True, field_prepared=False): |
100 |
"""
|
101 |
Creates the SQL snippet for a column. Used by add_column and add_table.
|
102 |
"""
|
103 |
|
104 |
# If the field hasn't already been told its attribute name, do so.
|
105 |
if not field_prepared: |
106 |
field.set_attributes_from_name(field_name) |
107 |
|
108 |
# hook for the field to do any resolution prior to it's attributes being queried
|
109 |
if hasattr(field, 'south_init'): |
110 |
field.south_init() |
111 |
|
112 |
# Possible hook to fiddle with the fields (e.g. defaults & TEXT on MySQL)
|
113 |
field = self._field_sanity(field)
|
114 |
|
115 |
try:
|
116 |
sql = field.db_type(connection=self._get_connection())
|
117 |
except TypeError: |
118 |
sql = field.db_type() |
119 |
|
120 |
if sql:
|
121 |
# Some callers, like the sqlite stuff, just want the extended type.
|
122 |
if with_name:
|
123 |
field_output = [self.quote_name(field.column), sql]
|
124 |
else:
|
125 |
field_output = [sql] |
126 |
|
127 |
if field.primary_key:
|
128 |
field_output.append('NOT NULL PRIMARY KEY')
|
129 |
elif field.unique:
|
130 |
# Just use UNIQUE (no indexes any more, we have delete_unique)
|
131 |
field_output.append('UNIQUE')
|
132 |
|
133 |
sql = ' '.join(field_output)
|
134 |
sqlparams = () |
135 |
|
136 |
# if the field is "NOT NULL" and a default value is provided, create the column with it
|
137 |
# this allows the addition of a NOT NULL field to a table with existing rows
|
138 |
if not getattr(field, '_suppress_default', False): |
139 |
if field.has_default():
|
140 |
default = field.get_default() |
141 |
# If the default is actually None, don't add a default term
|
142 |
if default is not None: |
143 |
# If the default is a callable, then call it!
|
144 |
if callable(default): |
145 |
default = default() |
146 |
# Now do some very cheap quoting. TODO: Redesign return values to avoid this.
|
147 |
if isinstance(default, basestring): |
148 |
default = "'%s'" % default.replace("'", "''") |
149 |
elif isinstance(default, (datetime.date, datetime.time, datetime.datetime)): |
150 |
default = "'%s'" % default
|
151 |
elif isinstance(default, bool): |
152 |
default = int(default)
|
153 |
# Escape any % signs in the output (bug #317)
|
154 |
if isinstance(default, basestring): |
155 |
default = default.replace("%", "%%") |
156 |
# Add it in
|
157 |
sql += " DEFAULT %s"
|
158 |
sqlparams = (default) |
159 |
elif (not field.null and field.blank) or (field.get_default() == ''): |
160 |
if field.empty_strings_allowed and self._get_connection().features.interprets_empty_strings_as_nulls: |
161 |
sql += " DEFAULT ''"
|
162 |
# Error here would be nice, but doesn't seem to play fair.
|
163 |
#else:
|
164 |
# raise ValueError("Attempting to add a non null column that isn't character based without an explicit default value.")
|
165 |
|
166 |
# Firebird need set not null after of default value keyword
|
167 |
if not field.primary_key and not field.null: |
168 |
sql += ' NOT NULL'
|
169 |
|
170 |
if field.rel and self.supports_foreign_keys: |
171 |
self.add_deferred_sql(
|
172 |
self.foreign_key_sql(
|
173 |
table_name, |
174 |
field.column, |
175 |
field.rel.to._meta.db_table, |
176 |
field.rel.to._meta.get_field(field.rel.field_name).column |
177 |
) |
178 |
) |
179 |
|
180 |
# Things like the contrib.gis module fields have this in 1.1 and below
|
181 |
if hasattr(field, 'post_create_sql'): |
182 |
for stmt in field.post_create_sql(no_style(), table_name): |
183 |
self.add_deferred_sql(stmt)
|
184 |
|
185 |
# In 1.2 and above, you have to ask the DatabaseCreation stuff for it.
|
186 |
# This also creates normal indexes in 1.1.
|
187 |
if hasattr(self._get_connection().creation, "sql_indexes_for_field"): |
188 |
# Make a fake model to pass in, with only db_table
|
189 |
model = self.mock_model("FakeModelForGISCreation", table_name) |
190 |
for stmt in self._get_connection().creation.sql_indexes_for_field(model, field, no_style()): |
191 |
self.add_deferred_sql(stmt)
|
192 |
|
193 |
if sql:
|
194 |
return sql % sqlparams
|
195 |
else:
|
196 |
return None |
197 |
|
198 |
|
199 |
def _drop_constraints(self, table_name, name, field): |
200 |
if self.has_check_constraints: |
201 |
check_constraints = self._constraints_affecting_columns(table_name, [name], "CHECK") |
202 |
for constraint in check_constraints: |
203 |
self.execute(self.delete_check_sql % { |
204 |
'table': self.quote_name(table_name), |
205 |
'constraint': self.quote_name(constraint), |
206 |
}) |
207 |
|
208 |
# Drop or add UNIQUE constraint
|
209 |
unique_constraint = list(self._constraints_affecting_columns(table_name, [name], "UNIQUE")) |
210 |
if field.unique and not unique_constraint: |
211 |
self.create_unique(table_name, [name])
|
212 |
elif not field.unique and unique_constraint: |
213 |
self.delete_unique(table_name, [name])
|
214 |
|
215 |
# Drop all foreign key constraints
|
216 |
try:
|
217 |
self.delete_foreign_key(table_name, name)
|
218 |
except ValueError: |
219 |
# There weren't any
|
220 |
pass
|
221 |
|
222 |
|
223 |
@generic.invalidate_table_constraints
|
224 |
def alter_column(self, table_name, name, field, explicit_name=True, ignore_constraints=False): |
225 |
"""
|
226 |
Alters the given column name so it will match the given field.
|
227 |
Note that conversion between the two by the database must be possible.
|
228 |
Will not automatically add _id by default; to have this behavour, pass
|
229 |
explicit_name=False.
|
230 |
|
231 |
@param table_name: The name of the table to add the column to
|
232 |
@param name: The name of the column to alter
|
233 |
@param field: The new field definition to use
|
234 |
"""
|
235 |
|
236 |
if self.dry_run: |
237 |
return
|
238 |
|
239 |
# hook for the field to do any resolution prior to it's attributes being queried
|
240 |
if hasattr(field, 'south_init'): |
241 |
field.south_init() |
242 |
|
243 |
# Add _id or whatever if we need to
|
244 |
field.set_attributes_from_name(name) |
245 |
if not explicit_name: |
246 |
name = field.column |
247 |
else:
|
248 |
field.column = name |
249 |
|
250 |
if not ignore_constraints: |
251 |
# Drop all check constraints. Note that constraints will be added back
|
252 |
# with self.alter_string_set_type and self.alter_string_drop_null.
|
253 |
self._drop_constraints(table_name, name, field)
|
254 |
|
255 |
# First, change the type
|
256 |
params = { |
257 |
"column": self.quote_name(name), |
258 |
"type": self._db_type_for_alter_column(field), |
259 |
"table_name": table_name
|
260 |
} |
261 |
|
262 |
# SQLs is a list of (SQL, values) pairs.
|
263 |
sqls = [] |
264 |
sqls_extra = [] |
265 |
|
266 |
# Only alter the column if it has a type (Geometry ones sometimes don't)
|
267 |
if params["type"] is not None: |
268 |
sqls.append((self.alter_string_set_type % params, []))
|
269 |
|
270 |
# Add any field- and backend- specific modifications
|
271 |
self._alter_add_column_mods(field, name, params, sqls)
|
272 |
|
273 |
# Next, nullity: modified, firebird doesn't support DROP NOT NULL
|
274 |
sqls_extra.append(self._alter_column_set_null(table_name, name, field.null))
|
275 |
|
276 |
# Next, set any default
|
277 |
self._alter_set_defaults(field, name, params, sqls)
|
278 |
|
279 |
# Finally, actually change the column
|
280 |
if self.allows_combined_alters: |
281 |
sqls, values = zip(*sqls)
|
282 |
self.execute(
|
283 |
"ALTER TABLE %s %s;" % (self.quote_name(table_name), ", ".join(sqls)), |
284 |
generic.flatten(values), |
285 |
) |
286 |
else:
|
287 |
# Databases like e.g. MySQL don't like more than one alter at once.
|
288 |
for sql, values in sqls: |
289 |
try:
|
290 |
self.execute("ALTER TABLE %s %s;" % (self.quote_name(table_name), sql), values) |
291 |
except DatabaseError, e:
|
292 |
print e
|
293 |
|
294 |
|
295 |
# Execute extra sql, which don't need ALTER TABLE statement
|
296 |
for sql in sqls_extra: |
297 |
self.execute(sql)
|
298 |
|
299 |
if not ignore_constraints: |
300 |
# Add back FK constraints if needed
|
301 |
if field.rel and self.supports_foreign_keys: |
302 |
self.execute(
|
303 |
self.foreign_key_sql(
|
304 |
table_name, |
305 |
field.column, |
306 |
field.rel.to._meta.db_table, |
307 |
field.rel.to._meta.get_field(field.rel.field_name).column |
308 |
) |
309 |
) |
310 |
|
311 |
@generic.copy_column_constraints
|
312 |
@generic.delete_column_constraints
|
313 |
def rename_column(self, table_name, old, new): |
314 |
if old == new:
|
315 |
# Short-circuit out
|
316 |
return []
|
317 |
|
318 |
self.execute('ALTER TABLE %s ALTER %s TO %s;' % ( |
319 |
self.quote_name(table_name),
|
320 |
self.quote_name(old),
|
321 |
self.quote_name(new),
|
322 |
)) |