77
88from django .conf import settings
99from django .db .backends .base .operations import BaseDatabaseOperations
10+ from django .db .models .expressions import Exists , ExpressionWrapper , RawSQL
11+ from django .db .models .sql .where import WhereNode
1012from django .utils import timezone
1113from django .utils .encoding import force_str
12-
14+ from django import VERSION as django_version
1315import pytz
1416
1517
@@ -315,7 +317,33 @@ def savepoint_rollback_sql(self, sid):
315317 """
316318 return "ROLLBACK TRANSACTION %s" % sid
317319
318- def sql_flush (self , style , tables , sequences , allow_cascade = False ):
320+ def _build_sequences (self , sequences , cursor ):
321+ seqs = []
322+ for seq in sequences :
323+ cursor .execute ("SELECT COUNT(*) FROM %s" % self .quote_name (seq ["table" ]))
324+ rowcnt = cursor .fetchone ()[0 ]
325+ elem = {}
326+ if rowcnt :
327+ elem ['start_id' ] = 0
328+ else :
329+ elem ['start_id' ] = 1
330+ elem .update (seq )
331+ seqs .append (elem )
332+ return seqs
333+
334+ def _sql_flush_new (self , style , tables , * , reset_sequences = False , allow_cascade = False ):
335+ if reset_sequences :
336+ return [
337+ sequence
338+ for sequence in self .connection .introspection .sequence_list ()
339+ ]
340+
341+ return []
342+
343+ def _sql_flush_old (self , style , tables , sequences , allow_cascade = False ):
344+ return sequences
345+
346+ def sql_flush (self , style , tables , * args , ** kwargs ):
319347 """
320348 Returns a list of SQL statements required to remove all data from
321349 the given database tables (without actually removing the tables
@@ -330,56 +358,50 @@ def sql_flush(self, style, tables, sequences, allow_cascade=False):
330358 The `allow_cascade` argument determines whether truncation may cascade
331359 to tables with foreign keys pointing the tables being truncated.
332360 """
333- if tables :
334- # Cannot use TRUNCATE on tables that are referenced by a FOREIGN KEY
335- # So must use the much slower DELETE
336- from django .db import connections
337- cursor = connections [self .connection .alias ].cursor ()
338- # Try to minimize the risks of the braindeaded inconsistency in
339- # DBCC CHEKIDENT(table, RESEED, n) behavior.
340- seqs = []
341- for seq in sequences :
342- cursor .execute ("SELECT COUNT(*) FROM %s" % self .quote_name (seq ["table" ]))
343- rowcnt = cursor .fetchone ()[0 ]
344- elem = {}
345- if rowcnt :
346- elem ['start_id' ] = 0
347- else :
348- elem ['start_id' ] = 1
349- elem .update (seq )
350- seqs .append (elem )
351- COLUMNS = "TABLE_NAME, CONSTRAINT_NAME"
352- WHERE = "CONSTRAINT_TYPE not in ('PRIMARY KEY','UNIQUE')"
353- cursor .execute (
354- "SELECT {} FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE {}" .format (COLUMNS , WHERE ))
355- fks = cursor .fetchall ()
356- sql_list = ['ALTER TABLE %s NOCHECK CONSTRAINT %s;' %
357- (self .quote_name (fk [0 ]), self .quote_name (fk [1 ])) for fk in fks ]
358- sql_list .extend (['%s %s %s;' % (style .SQL_KEYWORD ('DELETE' ), style .SQL_KEYWORD ('FROM' ),
359- style .SQL_FIELD (self .quote_name (table ))) for table in tables ])
360-
361- if self .connection .to_azure_sql_db and self .connection .sql_server_version < 2014 :
362- warnings .warn ("Resetting identity columns is not supported "
363- "on this versios of Azure SQL Database." ,
364- RuntimeWarning )
365- else :
366- # Then reset the counters on each table.
367- sql_list .extend (['%s %s (%s, %s, %s) %s %s;' % (
368- style .SQL_KEYWORD ('DBCC' ),
369- style .SQL_KEYWORD ('CHECKIDENT' ),
370- style .SQL_FIELD (self .quote_name (seq ["table" ])),
371- style .SQL_KEYWORD ('RESEED' ),
372- style .SQL_FIELD ('%d' % seq ['start_id' ]),
373- style .SQL_KEYWORD ('WITH' ),
374- style .SQL_KEYWORD ('NO_INFOMSGS' ),
375- ) for seq in seqs ])
376-
377- sql_list .extend (['ALTER TABLE %s CHECK CONSTRAINT %s;' %
378- (self .quote_name (fk [0 ]), self .quote_name (fk [1 ])) for fk in fks ])
379- return sql_list
380- else :
361+
362+ if not tables :
381363 return []
382364
365+ if django_version >= (3 , 1 ):
366+ sequences = self ._sql_flush_new (style , tables , * args , ** kwargs )
367+ else :
368+ sequences = self ._sql_flush_old (style , tables , * args , ** kwargs )
369+
370+ from django .db import connections
371+ cursor = connections [self .connection .alias ].cursor ()
372+
373+ seqs = self ._build_sequences (sequences , cursor )
374+
375+ COLUMNS = "TABLE_NAME, CONSTRAINT_NAME"
376+ WHERE = "CONSTRAINT_TYPE not in ('PRIMARY KEY','UNIQUE')"
377+ cursor .execute (
378+ "SELECT {} FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE {}" .format (COLUMNS , WHERE ))
379+ fks = cursor .fetchall ()
380+ sql_list = ['ALTER TABLE %s NOCHECK CONSTRAINT %s;' %
381+ (self .quote_name (fk [0 ]), self .quote_name (fk [1 ])) for fk in fks ]
382+ sql_list .extend (['%s %s %s;' % (style .SQL_KEYWORD ('DELETE' ), style .SQL_KEYWORD ('FROM' ),
383+ style .SQL_FIELD (self .quote_name (table ))) for table in tables ])
384+
385+ if self .connection .to_azure_sql_db and self .connection .sql_server_version < 2014 :
386+ warnings .warn ("Resetting identity columns is not supported "
387+ "on this versios of Azure SQL Database." ,
388+ RuntimeWarning )
389+ else :
390+ # Then reset the counters on each table.
391+ sql_list .extend (['%s %s (%s, %s, %s) %s %s;' % (
392+ style .SQL_KEYWORD ('DBCC' ),
393+ style .SQL_KEYWORD ('CHECKIDENT' ),
394+ style .SQL_FIELD (self .quote_name (seq ["table" ])),
395+ style .SQL_KEYWORD ('RESEED' ),
396+ style .SQL_FIELD ('%d' % seq ['start_id' ]),
397+ style .SQL_KEYWORD ('WITH' ),
398+ style .SQL_KEYWORD ('NO_INFOMSGS' ),
399+ ) for seq in seqs ])
400+
401+ sql_list .extend (['ALTER TABLE %s CHECK CONSTRAINT %s;' %
402+ (self .quote_name (fk [0 ]), self .quote_name (fk [1 ])) for fk in fks ])
403+ return sql_list
404+
383405 def start_transaction_sql (self ):
384406 """
385407 Returns the SQL statement required to start a transaction.
@@ -445,3 +467,16 @@ def time_trunc_sql(self, lookup_type, field_name):
445467 elif lookup_type == 'second' :
446468 sql = "CONVERT(time, SUBSTRING(CONVERT(varchar, %s, 114), 0, 9))" % field_name
447469 return sql
470+
471+ def conditional_expression_supported_in_where_clause (self , expression ):
472+ """
473+ Following "Moved conditional expression wrapping to the Exact lookup" in django 3.1
474+ https://github.com/django/django/commit/37e6c5b79bd0529a3c85b8c478e4002fd33a2a1d
475+ """
476+ if isinstance (expression , (Exists , WhereNode )):
477+ return True
478+ if isinstance (expression , ExpressionWrapper ) and expression .conditional :
479+ return self .conditional_expression_supported_in_where_clause (expression .expression )
480+ if isinstance (expression , RawSQL ) and expression .conditional :
481+ return True
482+ return False
0 commit comments