| 1 |
import sys |
|---|
| 2 |
import time |
|---|
| 3 |
try: |
|---|
| 4 |
set |
|---|
| 5 |
except NameError: |
|---|
| 6 |
# Python 2.3 compat |
|---|
| 7 |
from sets import Set as set |
|---|
| 8 |
|
|---|
| 9 |
from django.conf import settings |
|---|
| 10 |
from django.core.management import call_command |
|---|
| 11 |
|
|---|
| 12 |
# The prefix to put on the default database name when creating |
|---|
| 13 |
# the test database. |
|---|
| 14 |
TEST_DATABASE_PREFIX = 'test_' |
|---|
| 15 |
|
|---|
| 16 |
class BaseDatabaseCreation(object): |
|---|
| 17 |
""" |
|---|
| 18 |
This class encapsulates all backend-specific differences that pertain to |
|---|
| 19 |
database *creation*, such as the column types to use for particular Django |
|---|
| 20 |
Fields, the SQL used to create and destroy tables, and the creation and |
|---|
| 21 |
destruction of test databases. |
|---|
| 22 |
""" |
|---|
| 23 |
data_types = {} |
|---|
| 24 |
|
|---|
| 25 |
def __init__(self, connection): |
|---|
| 26 |
self.connection = connection |
|---|
| 27 |
|
|---|
| 28 |
def sql_create_model(self, model, style, known_models=set()): |
|---|
| 29 |
""" |
|---|
| 30 |
Returns the SQL required to create a single model, as a tuple of: |
|---|
| 31 |
(list_of_sql, pending_references_dict) |
|---|
| 32 |
""" |
|---|
| 33 |
from django.db import models |
|---|
| 34 |
|
|---|
| 35 |
opts = model._meta |
|---|
| 36 |
final_output = [] |
|---|
| 37 |
table_output = [] |
|---|
| 38 |
pending_references = {} |
|---|
| 39 |
qn = self.connection.ops.quote_name |
|---|
| 40 |
for f in opts.local_fields: |
|---|
| 41 |
col_type = f.db_type() |
|---|
| 42 |
tablespace = f.db_tablespace or opts.db_tablespace |
|---|
| 43 |
if col_type is None: |
|---|
| 44 |
# Skip ManyToManyFields, because they're not represented as |
|---|
| 45 |
# database columns in this table. |
|---|
| 46 |
continue |
|---|
| 47 |
# Make the definition (e.g. 'foo VARCHAR(30)') for this field. |
|---|
| 48 |
field_output = [style.SQL_FIELD(qn(f.column)), |
|---|
| 49 |
style.SQL_COLTYPE(col_type)] |
|---|
| 50 |
field_output.append(style.SQL_KEYWORD('%sNULL' % (not f.null and 'NOT ' or ''))) |
|---|
| 51 |
if f.primary_key: |
|---|
| 52 |
field_output.append(style.SQL_KEYWORD('PRIMARY KEY')) |
|---|
| 53 |
elif f.unique: |
|---|
| 54 |
field_output.append(style.SQL_KEYWORD('UNIQUE')) |
|---|
| 55 |
if tablespace and f.unique: |
|---|
| 56 |
# We must specify the index tablespace inline, because we |
|---|
| 57 |
# won't be generating a CREATE INDEX statement for this field. |
|---|
| 58 |
field_output.append(self.connection.ops.tablespace_sql(tablespace, inline=True)) |
|---|
| 59 |
if f.rel: |
|---|
| 60 |
ref_output, pending = self.sql_for_inline_foreign_key_references(f, known_models, style) |
|---|
| 61 |
if pending: |
|---|
| 62 |
pr = pending_references.setdefault(f.rel.to, []).append((model, f)) |
|---|
| 63 |
else: |
|---|
| 64 |
field_output.extend(ref_output) |
|---|
| 65 |
table_output.append(' '.join(field_output)) |
|---|
| 66 |
if opts.order_with_respect_to: |
|---|
| 67 |
table_output.append(style.SQL_FIELD(qn('_order')) + ' ' + \ |
|---|
| 68 |
style.SQL_COLTYPE(models.IntegerField().db_type()) + ' ' + \ |
|---|
| 69 |
style.SQL_KEYWORD('NULL')) |
|---|
| 70 |
for field_constraints in opts.unique_together: |
|---|
| 71 |
table_output.append(style.SQL_KEYWORD('UNIQUE') + ' (%s)' % \ |
|---|
| 72 |
", ".join([style.SQL_FIELD(qn(opts.get_field(f).column)) for f in field_constraints])) |
|---|
| 73 |
|
|---|
| 74 |
full_statement = [style.SQL_KEYWORD('CREATE TABLE') + ' ' + style.SQL_TABLE(qn(opts.db_table)) + ' ('] |
|---|
| 75 |
for i, line in enumerate(table_output): # Combine and add commas. |
|---|
| 76 |
full_statement.append(' %s%s' % (line, i < len(table_output)-1 and ',' or '')) |
|---|
| 77 |
full_statement.append(')') |
|---|
| 78 |
if opts.db_tablespace: |
|---|
| 79 |
full_statement.append(self.connection.ops.tablespace_sql(opts.db_tablespace)) |
|---|
| 80 |
full_statement.append(';') |
|---|
| 81 |
final_output.append('\n'.join(full_statement)) |
|---|
| 82 |
|
|---|
| 83 |
if opts.has_auto_field: |
|---|
| 84 |
# Add any extra SQL needed to support auto-incrementing primary keys. |
|---|
| 85 |
auto_column = opts.auto_field.db_column or opts.auto_field.name |
|---|
| 86 |
autoinc_sql = self.connection.ops.autoinc_sql(opts.db_table, auto_column) |
|---|
| 87 |
if autoinc_sql: |
|---|
| 88 |
for stmt in autoinc_sql: |
|---|
| 89 |
final_output.append(stmt) |
|---|
| 90 |
|
|---|
| 91 |
return final_output, pending_references |
|---|
| 92 |
|
|---|
| 93 |
def sql_for_inline_foreign_key_references(self, field, known_models, style): |
|---|
| 94 |
"Return the SQL snippet defining the foreign key reference for a field" |
|---|
| 95 |
qn = self.connection.ops.quote_name |
|---|
| 96 |
if field.rel.to in known_models: |
|---|
| 97 |
output = [style.SQL_KEYWORD('REFERENCES') + ' ' + \ |
|---|
| 98 |
style.SQL_TABLE(qn(field.rel.to._meta.db_table)) + ' (' + \ |
|---|
| 99 |
style.SQL_FIELD(qn(field.rel.to._meta.get_field(field.rel.field_name).column)) + ')' + |
|---|
| 100 |
self.connection.ops.deferrable_sql() |
|---|
| 101 |
] |
|---|
| 102 |
pending = False |
|---|
| 103 |
else: |
|---|
| 104 |
# We haven't yet created the table to which this field |
|---|
| 105 |
# is related, so save it for later. |
|---|
| 106 |
output = [] |
|---|
| 107 |
pending = True |
|---|
| 108 |
|
|---|
| 109 |
return output, pending |
|---|
| 110 |
|
|---|
| 111 |
def sql_for_pending_references(self, model, style, pending_references): |
|---|
| 112 |
"Returns any ALTER TABLE statements to add constraints after the fact." |
|---|
| 113 |
from django.db.backends.util import truncate_name |
|---|
| 114 |
|
|---|
| 115 |
qn = self.connection.ops.quote_name |
|---|
| 116 |
final_output = [] |
|---|
| 117 |
opts = model._meta |
|---|
| 118 |
if model in pending_references: |
|---|
| 119 |
for rel_class, f in pending_references[model]: |
|---|
| 120 |
rel_opts = rel_class._meta |
|---|
| 121 |
r_table = rel_opts.db_table |
|---|
| 122 |
r_col = f.column |
|---|
| 123 |
table = opts.db_table |
|---|
| 124 |
col = opts.get_field(f.rel.field_name).column |
|---|
| 125 |
# For MySQL, r_name must be unique in the first 64 characters. |
|---|
| 126 |
# So we are careful with character usage here. |
|---|
| 127 |
r_name = '%s_refs_%s_%x' % (r_col, col, abs(hash((r_table, table)))) |
|---|
| 128 |
final_output.append(style.SQL_KEYWORD('ALTER TABLE') + ' %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s)%s;' % \ |
|---|
| 129 |
(qn(r_table), truncate_name(r_name, self.connection.ops.max_name_length()), |
|---|
| 130 |
qn(r_col), qn(table), qn(col), |
|---|
| 131 |
self.connection.ops.deferrable_sql())) |
|---|
| 132 |
del pending_references[model] |
|---|
| 133 |
return final_output |
|---|
| 134 |
|
|---|
| 135 |
def sql_for_many_to_many(self, model, style): |
|---|
| 136 |
"Return the CREATE TABLE statments for all the many-to-many tables defined on a model" |
|---|
| 137 |
output = [] |
|---|
| 138 |
for f in model._meta.local_many_to_many: |
|---|
| 139 |
output.extend(self.sql_for_many_to_many_field(model, f, style)) |
|---|
| 140 |
return output |
|---|
| 141 |
|
|---|
| 142 |
def sql_for_many_to_many_field(self, model, f, style): |
|---|
| 143 |
"Return the CREATE TABLE statements for a single m2m field" |
|---|
| 144 |
from django.db import models |
|---|
| 145 |
from django.db.backends.util import truncate_name |
|---|
| 146 |
|
|---|
| 147 |
output = [] |
|---|
| 148 |
if f.creates_table: |
|---|
| 149 |
opts = model._meta |
|---|
| 150 |
qn = self.connection.ops.quote_name |
|---|
| 151 |
tablespace = f.db_tablespace or opts.db_tablespace |
|---|
| 152 |
if tablespace: |
|---|
| 153 |
sql = self.connection.ops.tablespace_sql(tablespace, inline=True) |
|---|
| 154 |
if sql: |
|---|
| 155 |
tablespace_sql = ' ' + sql |
|---|
| 156 |
else: |
|---|
| 157 |
tablespace_sql = '' |
|---|
| 158 |
else: |
|---|
| 159 |
tablespace_sql = '' |
|---|
| 160 |
table_output = [style.SQL_KEYWORD('CREATE TABLE') + ' ' + \ |
|---|
| 161 |
style.SQL_TABLE(qn(f.m2m_db_table())) + ' ('] |
|---|
| 162 |
table_output.append(' %s %s %s%s,' % |
|---|
| 163 |
(style.SQL_FIELD(qn('id')), |
|---|
| 164 |
style.SQL_COLTYPE(models.AutoField(primary_key=True).db_type()), |
|---|
| 165 |
style.SQL_KEYWORD('NOT NULL PRIMARY KEY'), |
|---|
| 166 |
tablespace_sql)) |
|---|
| 167 |
|
|---|
| 168 |
deferred = [] |
|---|
| 169 |
inline_output, deferred = self.sql_for_inline_many_to_many_references(model, f, style) |
|---|
| 170 |
table_output.extend(inline_output) |
|---|
| 171 |
|
|---|
| 172 |
table_output.append(' %s (%s, %s)%s' % |
|---|
| 173 |
(style.SQL_KEYWORD('UNIQUE'), |
|---|
| 174 |
style.SQL_FIELD(qn(f.m2m_column_name())), |
|---|
| 175 |
style.SQL_FIELD(qn(f.m2m_reverse_name())), |
|---|
| 176 |
tablespace_sql)) |
|---|
| 177 |
table_output.append(')') |
|---|
| 178 |
if opts.db_tablespace: |
|---|
| 179 |
# f.db_tablespace is only for indices, so ignore its value here. |
|---|
| 180 |
table_output.append(self.connection.ops.tablespace_sql(opts.db_tablespace)) |
|---|
| 181 |
table_output.append(';') |
|---|
| 182 |
output.append('\n'.join(table_output)) |
|---|
| 183 |
|
|---|
| 184 |
for r_table, r_col, table, col in deferred: |
|---|
| 185 |
r_name = '%s_refs_%s_%x' % (r_col, col, |
|---|
| 186 |
abs(hash((r_table, table)))) |
|---|
| 187 |
output.append(style.SQL_KEYWORD('ALTER TABLE') + ' %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s)%s;' % |
|---|
| 188 |
(qn(r_table), |
|---|
| 189 |
truncate_name(r_name, self.connection.ops.max_name_length()), |
|---|
| 190 |
qn(r_col), qn(table), qn(col), |
|---|
| 191 |
self.connection.ops.deferrable_sql())) |
|---|
| 192 |
|
|---|
| 193 |
# Add any extra SQL needed to support auto-incrementing PKs |
|---|
| 194 |
autoinc_sql = self.connection.ops.autoinc_sql(f.m2m_db_table(), 'id') |
|---|
| 195 |
if autoinc_sql: |
|---|
| 196 |
for stmt in autoinc_sql: |
|---|
| 197 |
output.append(stmt) |
|---|
| 198 |
return output |
|---|
| 199 |
|
|---|
| 200 |
def sql_for_inline_many_to_many_references(self, model, field, style): |
|---|
| 201 |
"Create the references to other tables required by a many-to-many table" |
|---|
| 202 |
from django.db import models |
|---|
| 203 |
opts = model._meta |
|---|
| 204 |
qn = self.connection.ops.quote_name |
|---|
| 205 |
|
|---|
| 206 |
table_output = [ |
|---|
| 207 |
' %s %s %s %s (%s)%s,' % |
|---|
| 208 |
(style.SQL_FIELD(qn(field.m2m_column_name())), |
|---|
| 209 |
style.SQL_COLTYPE(models.ForeignKey(model).db_type()), |
|---|
| 210 |
style.SQL_KEYWORD('NOT NULL REFERENCES'), |
|---|
| 211 |
style.SQL_TABLE(qn(opts.db_table)), |
|---|
| 212 |
style.SQL_FIELD(qn(opts.pk.column)), |
|---|
| 213 |
self.connection.ops.deferrable_sql()), |
|---|
| 214 |
' %s %s %s %s (%s)%s,' % |
|---|
| 215 |
(style.SQL_FIELD(qn(field.m2m_reverse_name())), |
|---|
| 216 |
style.SQL_COLTYPE(models.ForeignKey(field.rel.to).db_type()), |
|---|
| 217 |
style.SQL_KEYWORD('NOT NULL REFERENCES'), |
|---|
| 218 |
style.SQL_TABLE(qn(field.rel.to._meta.db_table)), |
|---|
| 219 |
style.SQL_FIELD(qn(field.rel.to._meta.pk.column)), |
|---|
| 220 |
self.connection.ops.deferrable_sql()) |
|---|
| 221 |
] |
|---|
| 222 |
deferred = [] |
|---|
| 223 |
|
|---|
| 224 |
return table_output, deferred |
|---|
| 225 |
|
|---|
| 226 |
def sql_indexes_for_model(self, model, style): |
|---|
| 227 |
"Returns the CREATE INDEX SQL statements for a single model" |
|---|
| 228 |
output = [] |
|---|
| 229 |
for f in model._meta.local_fields: |
|---|
| 230 |
output.extend(self.sql_indexes_for_field(model, f, style)) |
|---|
| 231 |
return output |
|---|
| 232 |
|
|---|
| 233 |
def sql_indexes_for_field(self, model, f, style): |
|---|
| 234 |
"Return the CREATE INDEX SQL statements for a single model field" |
|---|
| 235 |
if f.db_index and not f.unique: |
|---|
| 236 |
qn = self.connection.ops.quote_name |
|---|
| 237 |
tablespace = f.db_tablespace or model._meta.db_tablespace |
|---|
| 238 |
if tablespace: |
|---|
| 239 |
sql = self.connection.ops.tablespace_sql(tablespace) |
|---|
| 240 |
if sql: |
|---|
| 241 |
tablespace_sql = ' ' + sql |
|---|
| 242 |
else: |
|---|
| 243 |
tablespace_sql = '' |
|---|
| 244 |
else: |
|---|
| 245 |
tablespace_sql = '' |
|---|
| 246 |
output = [style.SQL_KEYWORD('CREATE INDEX') + ' ' + |
|---|
| 247 |
style.SQL_TABLE(qn('%s_%s' % (model._meta.db_table, f.column))) + ' ' + |
|---|
| 248 |
style.SQL_KEYWORD('ON') + ' ' + |
|---|
| 249 |
style.SQL_TABLE(qn(model._meta.db_table)) + ' ' + |
|---|
| 250 |
"(%s)" % style.SQL_FIELD(qn(f.column)) + |
|---|
| 251 |
"%s;" % tablespace_sql] |
|---|
| 252 |
else: |
|---|
| 253 |
output = [] |
|---|
| 254 |
return output |
|---|
| 255 |
|
|---|
| 256 |
def sql_destroy_model(self, model, references_to_delete, style): |
|---|
| 257 |
"Return the DROP TABLE and restraint dropping statements for a single model" |
|---|
| 258 |
# Drop the table now |
|---|
| 259 |
qn = self.connection.ops.quote_name |
|---|
| 260 |
output = ['%s %s;' % (style.SQL_KEYWORD('DROP TABLE'), |
|---|
| 261 |
style.SQL_TABLE(qn(model._meta.db_table)))] |
|---|
| 262 |
if model in references_to_delete: |
|---|
| 263 |
output.extend(self.sql_remove_table_constraints(model, references_to_delete, style)) |
|---|
| 264 |
|
|---|
| 265 |
if model._meta.has_auto_field: |
|---|
| 266 |
ds = self.connection.ops.drop_sequence_sql(model._meta.db_table) |
|---|
| 267 |
if ds: |
|---|
| 268 |
output.append(ds) |
|---|
| 269 |
return output |
|---|
| 270 |
|
|---|
| 271 |
def sql_remove_table_constraints(self, model, references_to_delete, style): |
|---|
| 272 |
from django.db.backends.util import truncate_name |
|---|
| 273 |
|
|---|
| 274 |
output = [] |
|---|
| 275 |
qn = self.connection.ops.quote_name |
|---|
| 276 |
for rel_class, f in references_to_delete[model]: |
|---|
| 277 |
table = rel_class._meta.db_table |
|---|
| 278 |
col = f.column |
|---|
| 279 |
r_table = model._meta.db_table |
|---|
| 280 |
r_col = model._meta.get_field(f.rel.field_name).column |
|---|
| 281 |
r_name = '%s_refs_%s_%x' % (col, r_col, abs(hash((table, r_table)))) |
|---|
| 282 |
output.append('%s %s %s %s;' % \ |
|---|
| 283 |
(style.SQL_KEYWORD('ALTER TABLE'), |
|---|
| 284 |
style.SQL_TABLE(qn(table)), |
|---|
| 285 |
style.SQL_KEYWORD(self.connection.ops.drop_foreignkey_sql()), |
|---|
| 286 |
style.SQL_FIELD(truncate_name(r_name, self.connection.ops.max_name_length())))) |
|---|
| 287 |
del references_to_delete[model] |
|---|
| 288 |
return output |
|---|
| 289 |
|
|---|
| 290 |
def sql_destroy_many_to_many(self, model, f, style): |
|---|
| 291 |
"Returns the DROP TABLE statements for a single m2m field" |
|---|
| 292 |
qn = self.connection.ops.quote_name |
|---|
| 293 |
output = [] |
|---|
| 294 |
if f.creates_table: |
|---|
| 295 |
output.append("%s %s;" % (style.SQL_KEYWORD('DROP TABLE'), |
|---|
| 296 |
style.SQL_TABLE(qn(f.m2m_db_table())))) |
|---|
| 297 |
ds = self.connection.ops.drop_sequence_sql("%s_%s" % (model._meta.db_table, f.column)) |
|---|
| 298 |
if ds: |
|---|
| 299 |
output.append(ds) |
|---|
| 300 |
return output |
|---|
| 301 |
|
|---|
| 302 |
def create_test_db(self, verbosity=1, autoclobber=False): |
|---|
| 303 |
""" |
|---|
| 304 |
Creates a test database, prompting the user for confirmation if the |
|---|
| 305 |
database already exists. Returns the name of the test database created. |
|---|
| 306 |
""" |
|---|
| 307 |
if verbosity >= 1: |
|---|
| 308 |
print "Creating test database..." |
|---|
| 309 |
|
|---|
| 310 |
test_database_name = self._create_test_db(verbosity, autoclobber) |
|---|
| 311 |
|
|---|
| 312 |
self.connection.close() |
|---|
| 313 |
settings.DATABASE_NAME = test_database_name |
|---|
| 314 |
|
|---|
| 315 |
call_command('syncdb', verbosity=verbosity, interactive=False) |
|---|
| 316 |
|
|---|
| 317 |
if settings.CACHE_BACKEND.startswith('db://'): |
|---|
| 318 |
cache_name = settings.CACHE_BACKEND[len('db://'):] |
|---|
| 319 |
call_command('createcachetable', cache_name) |
|---|
| 320 |
|
|---|
| 321 |
# Get a cursor (even though we don't need one yet). This has |
|---|
| 322 |
# the side effect of initializing the test database. |
|---|
| 323 |
cursor = self.connection.cursor() |
|---|
| 324 |
|
|---|
| 325 |
return test_database_name |
|---|
| 326 |
|
|---|
| 327 |
def _create_test_db(self, verbosity, autoclobber): |
|---|
| 328 |
"Internal implementation - creates the test db tables." |
|---|
| 329 |
suffix = self.sql_table_creation_suffix() |
|---|
| 330 |
|
|---|
| 331 |
if settings.TEST_DATABASE_NAME: |
|---|
| 332 |
test_database_name = settings.TEST_DATABASE_NAME |
|---|
| 333 |
else: |
|---|
| 334 |
test_database_name = TEST_DATABASE_PREFIX + settings.DATABASE_NAME |
|---|
| 335 |
|
|---|
| 336 |
qn = self.connection.ops.quote_name |
|---|
| 337 |
|
|---|
| 338 |
# Create the test database and connect to it. We need to autocommit |
|---|
| 339 |
# if the database supports it because PostgreSQL doesn't allow |
|---|
| 340 |
# CREATE/DROP DATABASE statements within transactions. |
|---|
| 341 |
cursor = self.connection.cursor() |
|---|
| 342 |
self.set_autocommit() |
|---|
| 343 |
try: |
|---|
| 344 |
cursor.execute("CREATE DATABASE %s %s" % (qn(test_database_name), suffix)) |
|---|
| 345 |
except Exception, e: |
|---|
| 346 |
sys.stderr.write("Got an error creating the test database: %s\n" % e) |
|---|
| 347 |
if not autoclobber: |
|---|
| 348 |
confirm = raw_input("Type 'yes' if you would like to try deleting the test database '%s', or 'no' to cancel: " % test_database_name) |
|---|
| 349 |
if autoclobber or confirm == 'yes': |
|---|
| 350 |
try: |
|---|
| 351 |
if verbosity >= 1: |
|---|
| 352 |
print "Destroying old test database..." |
|---|
| 353 |
cursor.execute("DROP DATABASE %s" % qn(test_database_name)) |
|---|
| 354 |
if verbosity >= 1: |
|---|
| 355 |
print "Creating test database..." |
|---|
| 356 |
cursor.execute("CREATE DATABASE %s %s" % (qn(test_database_name), suffix)) |
|---|
| 357 |
except Exception, e: |
|---|
| 358 |
sys.stderr.write("Got an error recreating the test database: %s\n" % e) |
|---|
| 359 |
sys.exit(2) |
|---|
| 360 |
else: |
|---|
| 361 |
print "Tests cancelled." |
|---|
| 362 |
sys.exit(1) |
|---|
| 363 |
|
|---|
| 364 |
return test_database_name |
|---|
| 365 |
|
|---|
| 366 |
def destroy_test_db(self, old_database_name, verbosity=1): |
|---|
| 367 |
""" |
|---|
| 368 |
Destroy a test database, prompting the user for confirmation if the |
|---|
| 369 |
database already exists. Returns the name of the test database created. |
|---|
| 370 |
""" |
|---|
| 371 |
if verbosity >= 1: |
|---|
| 372 |
print "Destroying test database..." |
|---|
| 373 |
self.connection.close() |
|---|
| 374 |
test_database_name = settings.DATABASE_NAME |
|---|
| 375 |
settings.DATABASE_NAME = old_database_name |
|---|
| 376 |
|
|---|
| 377 |
self._destroy_test_db(test_database_name, verbosity) |
|---|
| 378 |
|
|---|
| 379 |
def _destroy_test_db(self, test_database_name, verbosity): |
|---|
| 380 |
"Internal implementation - remove the test db tables." |
|---|
| 381 |
# Remove the test database to clean up after |
|---|
| 382 |
# ourselves. Connect to the previous database (not the test database) |
|---|
| 383 |
# to do so, because it's not allowed to delete a database while being |
|---|
| 384 |
# connected to it. |
|---|
| 385 |
cursor = self.connection.cursor() |
|---|
| 386 |
self.set_autocommit() |
|---|
| 387 |
time.sleep(1) # To avoid "database is being accessed by other users" errors. |
|---|
| 388 |
cursor.execute("DROP DATABASE %s" % self.connection.ops.quote_name(test_database_name)) |
|---|
| 389 |
self.connection.close() |
|---|
| 390 |
|
|---|
| 391 |
def set_autocommit(self): |
|---|
| 392 |
"Make sure a connection is in autocommit mode." |
|---|
| 393 |
if hasattr(self.connection.connection, "autocommit"): |
|---|
| 394 |
if callable(self.connection.connection.autocommit): |
|---|
| 395 |
self.connection.connection.autocommit(True) |
|---|
| 396 |
else: |
|---|
| 397 |
self.connection.connection.autocommit = True |
|---|
| 398 |
elif hasattr(self.connection.connection, "set_isolation_level"): |
|---|
| 399 |
self.connection.connection.set_isolation_level(0) |
|---|
| 400 |
|
|---|
| 401 |
def sql_table_creation_suffix(self): |
|---|
| 402 |
"SQL to append to the end of the test table creation statements" |
|---|
| 403 |
return '' |
|---|