Skip to content

Commit 39ee995

Browse files
authored
Merge pull request #96 from microsoft/dev
Prepare for 1.1.2 release
2 parents 9d7abdf + 7ef1108 commit 39ee995

18 files changed

+420
-237
lines changed

mssql/functions.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,7 @@ def bulk_update_with_default(self, objs, fields, batch_size=None, default=0):
238238
if any(f.primary_key for f in fields):
239239
raise ValueError('bulk_update() cannot be used with primary key fields.')
240240
if not objs:
241-
return
241+
return 0
242242
# PK is used twice in the resulting update query, once in the filter
243243
# and once in the WHEN. Each field will also have one CAST.
244244
max_batch_size = connections[self.db].ops.bulk_batch_size(['pk', 'pk'] + fields, objs)
@@ -266,9 +266,11 @@ def bulk_update_with_default(self, objs, fields, batch_size=None, default=0):
266266
case_statement = Cast(case_statement, output_field=field)
267267
update_kwargs[field.attname] = case_statement
268268
updates.append(([obj.pk for obj in batch_objs], update_kwargs))
269+
rows_updated = 0
269270
with transaction.atomic(using=self.db, savepoint=False):
270271
for pks, update_kwargs in updates:
271-
self.filter(pk__in=pks).update(**update_kwargs)
272+
rows_updated += self.filter(pk__in=pks).update(**update_kwargs)
273+
return rows_updated
272274

273275
ATan2.as_microsoft = sqlserver_atan2
274276
In.split_parameter_list_as_sql = split_parameter_list_as_sql

mssql/introspection.py

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
SQL_BIGAUTOFIELD = -777444
1515

1616
def get_schema_name():
17-
return getattr(settings, 'SCHEMA_TO_INSPECT', 'dbo')
17+
return getattr(settings, 'SCHEMA_TO_INSPECT', 'SCHEMA_NAME()')
1818

1919
class DatabaseIntrospection(BaseDatabaseIntrospection):
2020
# Map type codes to Django Field types.
@@ -66,7 +66,7 @@ def get_table_list(self, cursor):
6666
"""
6767
Returns a list of table and view names in the current database.
6868
"""
69-
sql = 'SELECT TABLE_NAME, TABLE_TYPE FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = ' + "'" + get_schema_name() + "'"
69+
sql = f'SELECT TABLE_NAME, TABLE_TYPE FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = {get_schema_name()}'
7070
cursor.execute(sql)
7171
types = {'BASE TABLE': 't', 'VIEW': 'v'}
7272
return [TableInfo(row[0], types.get(row[1]))
@@ -129,10 +129,10 @@ def get_table_description(self, cursor, table_name, identity_check=True):
129129
return items
130130

131131
def get_sequences(self, cursor, table_name, table_fields=()):
132-
cursor.execute("""
132+
cursor.execute(f"""
133133
SELECT c.name FROM sys.columns c
134134
INNER JOIN sys.tables t ON c.object_id = t.object_id
135-
WHERE t.schema_id = SCHEMA_ID(""" + "'" + get_schema_name() + "'" + """) AND t.name = %s AND c.is_identity = 1""",
135+
WHERE t.schema_id = SCHEMA_ID({get_schema_name()}) AND t.name = %s AND c.is_identity = 1""",
136136
[table_name])
137137
# SQL Server allows only one identity column per table
138138
# https://docs.microsoft.com/en-us/sql/t-sql/statements/create-table-transact-sql-identity-property
@@ -148,7 +148,7 @@ def get_relations(self, cursor, table_name):
148148
# CONSTRAINT_TABLE_USAGE: http://msdn2.microsoft.com/en-us/library/ms179883.aspx
149149
# REFERENTIAL_CONSTRAINTS: http://msdn2.microsoft.com/en-us/library/ms179987.aspx
150150
# TABLE_CONSTRAINTS: http://msdn2.microsoft.com/en-us/library/ms181757.aspx
151-
sql = """
151+
sql = f"""
152152
SELECT e.COLUMN_NAME AS column_name,
153153
c.TABLE_NAME AS referenced_table_name,
154154
d.COLUMN_NAME AS referenced_column_name
@@ -161,7 +161,7 @@ def get_relations(self, cursor, table_name):
161161
ON c.CONSTRAINT_NAME = d.CONSTRAINT_NAME AND c.CONSTRAINT_SCHEMA = d.CONSTRAINT_SCHEMA
162162
INNER JOIN INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE AS e
163163
ON a.CONSTRAINT_NAME = e.CONSTRAINT_NAME AND a.TABLE_SCHEMA = e.TABLE_SCHEMA
164-
WHERE a.TABLE_SCHEMA = """ + "'" + get_schema_name() + "'" + """ AND a.TABLE_NAME = %s AND a.CONSTRAINT_TYPE = 'FOREIGN KEY'"""
164+
WHERE a.TABLE_SCHEMA = {get_schema_name()} AND a.TABLE_NAME = %s AND a.CONSTRAINT_TYPE = 'FOREIGN KEY'"""
165165
cursor.execute(sql, (table_name,))
166166
return dict([[item[0], (item[2], item[1])] for item in cursor.fetchall()])
167167

@@ -171,14 +171,14 @@ def get_key_columns(self, cursor, table_name):
171171
key columns in given table.
172172
"""
173173
key_columns = []
174-
cursor.execute("""
174+
cursor.execute(f"""
175175
SELECT c.name AS column_name, rt.name AS referenced_table_name, rc.name AS referenced_column_name
176176
FROM sys.foreign_key_columns fk
177177
INNER JOIN sys.tables t ON t.object_id = fk.parent_object_id
178178
INNER JOIN sys.columns c ON c.object_id = t.object_id AND c.column_id = fk.parent_column_id
179179
INNER JOIN sys.tables rt ON rt.object_id = fk.referenced_object_id
180180
INNER JOIN sys.columns rc ON rc.object_id = rt.object_id AND rc.column_id = fk.referenced_column_id
181-
WHERE t.schema_id = SCHEMA_ID(""" + "'" + get_schema_name() + "'" + """) AND t.name = %s""", [table_name])
181+
WHERE t.schema_id = SCHEMA_ID({get_schema_name()}) AND t.name = %s""", [table_name])
182182
key_columns.extend([tuple(row) for row in cursor.fetchall()])
183183
return key_columns
184184

@@ -201,7 +201,7 @@ def get_constraints(self, cursor, table_name):
201201
constraints = {}
202202
# Loop over the key table, collecting things as constraints
203203
# This will get PKs, FKs, and uniques, but not CHECK
204-
cursor.execute("""
204+
cursor.execute(f"""
205205
SELECT
206206
kc.constraint_name,
207207
kc.column_name,
@@ -241,9 +241,7 @@ def get_constraints(self, cursor, table_name):
241241
kc.table_name = fk.table_name AND
242242
kc.column_name = fk.column_name
243243
WHERE
244-
kc.table_schema = """
245-
+ "'" + get_schema_name() + "'" +
246-
""" AND
244+
kc.table_schema = {get_schema_name()} AND
247245
kc.table_name = %s
248246
ORDER BY
249247
kc.constraint_name ASC,
@@ -263,7 +261,7 @@ def get_constraints(self, cursor, table_name):
263261
# Record the details
264262
constraints[constraint]['columns'].append(column)
265263
# Now get CHECK constraint columns
266-
cursor.execute("""
264+
cursor.execute(f"""
267265
SELECT kc.constraint_name, kc.column_name
268266
FROM INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE AS kc
269267
JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS AS c ON
@@ -272,7 +270,7 @@ def get_constraints(self, cursor, table_name):
272270
kc.constraint_name = c.constraint_name
273271
WHERE
274272
c.constraint_type = 'CHECK' AND
275-
kc.table_schema = """ + "'" + get_schema_name() + "'" + """ AND
273+
kc.table_schema = {get_schema_name()} AND
276274
kc.table_name = %s
277275
""", [table_name])
278276
for constraint, column in cursor.fetchall():
@@ -289,7 +287,7 @@ def get_constraints(self, cursor, table_name):
289287
# Record the details
290288
constraints[constraint]['columns'].append(column)
291289
# Now get indexes
292-
cursor.execute("""
290+
cursor.execute(f"""
293291
SELECT
294292
i.name AS index_name,
295293
i.is_unique,
@@ -311,7 +309,7 @@ def get_constraints(self, cursor, table_name):
311309
ic.object_id = c.object_id AND
312310
ic.column_id = c.column_id
313311
WHERE
314-
t.schema_id = SCHEMA_ID(""" + "'" + get_schema_name() + "'" + """) AND
312+
t.schema_id = SCHEMA_ID({get_schema_name()}) AND
315313
t.name = %s
316314
ORDER BY
317315
i.index_id ASC,

mssql/management/commands/inspectdb.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,5 @@ def add_arguments(self, parser):
1515

1616
def handle(self, *args, **options):
1717
if options["schema"]:
18-
settings.SCHEMA_TO_INSPECT = options["schema"]
18+
settings.SCHEMA_TO_INSPECT = "'" + options["schema"] + "'"
1919
return super().handle(*args, **options)

mssql/schema.py

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -225,14 +225,6 @@ def _model_indexes_sql(self, model):
225225
output.append(index.create_sql(model, self))
226226
return output
227227

228-
def _alter_many_to_many(self, model, old_field, new_field, strict):
229-
"""Alter M2Ms to repoint their to= endpoints."""
230-
231-
for idx in self._constraint_names(old_field.remote_field.through, index=True, unique=True):
232-
self.execute(self.sql_delete_index % {'name': idx, 'table': old_field.remote_field.through._meta.db_table})
233-
234-
return super()._alter_many_to_many(model, old_field, new_field, strict)
235-
236228
def _db_table_constraint_names(self, db_table, column_names=None, unique=None,
237229
primary_key=None, index=None, foreign_key=None,
238230
check=None, type_=None, exclude=None):
@@ -442,9 +434,17 @@ def _alter_field(self, model, old_field, new_field, old_type, new_type,
442434
# Drop unique constraint, SQL Server requires explicit deletion
443435
self._delete_unique_constraints(model, old_field, new_field, strict)
444436
# Drop indexes, SQL Server requires explicit deletion
445-
self._delete_indexes(model, old_field, new_field)
446-
if not new_field.get_internal_type() in ("JSONField", "TextField") and not (old_field.db_index and new_field.db_index):
447-
post_actions.append((self._create_index_sql(model, [new_field]), ()))
437+
indexes_dropped = self._delete_indexes(model, old_field, new_field)
438+
if (
439+
new_field.get_internal_type() not in ("JSONField", "TextField") and
440+
(old_field.db_index or not new_field.db_index) and
441+
new_field.db_index or
442+
(indexes_dropped and sorted(indexes_dropped) == sorted(
443+
[index.name for index in model._meta.indexes]))
444+
):
445+
create_index_sql_statement = self._create_index_sql(model, [new_field])
446+
if create_index_sql_statement.__str__() not in [sql.__str__() for sql in self.deferred_sql]:
447+
post_actions.append((create_index_sql_statement, ()))
448448
# Only if we have a default and there is a change from NULL to NOT NULL
449449
four_way_default_alteration = (
450450
new_field.has_default() and
@@ -566,7 +566,9 @@ def _alter_field(self, model, old_field, new_field, old_type, new_type,
566566
if index_columns:
567567
for columns in index_columns:
568568
create_index_sql_statement = self._create_index_sql(model, columns)
569-
if create_index_sql_statement.__str__() not in [sql.__str__() for sql in self.deferred_sql]:
569+
if (create_index_sql_statement.__str__()
570+
not in [sql.__str__() for sql in self.deferred_sql] + [statement[0].__str__() for statement in post_actions]
571+
):
570572
self.execute(create_index_sql_statement)
571573

572574
# Type alteration on primary key? Then we need to alter the column
@@ -653,6 +655,7 @@ def _alter_field(self, model, old_field, new_field, old_type, new_type,
653655

654656
def _delete_indexes(self, model, old_field, new_field):
655657
index_columns = []
658+
index_names = []
656659
if old_field.db_index and new_field.db_index:
657660
index_columns.append([old_field.column])
658661
elif old_field.null != new_field.null:
@@ -671,6 +674,7 @@ def _delete_indexes(self, model, old_field, new_field):
671674
index_names = self._constraint_names(model, columns, index=True)
672675
for index_name in index_names:
673676
self.execute(self._delete_constraint_sql(self.sql_delete_index, model, index_name))
677+
return index_names
674678

675679
def _delete_unique_constraints(self, model, old_field, new_field, strict=False):
676680
unique_columns = []

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828

2929
setup(
3030
name='mssql-django',
31-
version='1.1.1',
31+
version='1.1.2',
3232
description='Django backend for Microsoft SQL Server',
3333
long_description=long_description,
3434
long_description_content_type='text/markdown',

testapp/migrations/0002_test_unique_nullable_part1.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ class Migration(migrations.Migration):
88
]
99

1010
operations = [
11-
# Issue #38 test prep
11+
# Prep test for issue https://github.com/ESSolutions/django-mssql-backend/issues/38
1212
# Create with a field that is unique *and* nullable so it is implemented with a filtered unique index.
1313
migrations.CreateModel(
1414
name='TestUniqueNullableModel',

testapp/migrations/0003_test_unique_nullable_part2.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ class Migration(migrations.Migration):
88
]
99

1010
operations = [
11-
# Issue #38 test
11+
# Run test for issue https://github.com/ESSolutions/django-mssql-backend/issues/38
1212
# Now remove the null=True to check this transition is correctly handled.
1313
migrations.AlterField(
1414
model_name='testuniquenullablemodel',

testapp/migrations/0004_test_issue45_unique_type_change_part1.py renamed to testapp/migrations/0004_test_unique_type_change_part1.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ class Migration(migrations.Migration):
77
('testapp', '0003_test_unique_nullable_part2'),
88
]
99

10-
# Issue #45 test prep
10+
# Prep test for issue https://github.com/ESSolutions/django-mssql-backend/issues/45
1111
operations = [
1212
# for case 1:
1313
migrations.AddField(

testapp/migrations/0005_test_issue45_unique_type_change_part2.py renamed to testapp/migrations/0005_test_unique_type_change_part2.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@
44
class Migration(migrations.Migration):
55

66
dependencies = [
7-
('testapp', '0004_test_issue45_unique_type_change_part1'),
7+
('testapp', '0004_test_unique_type_change_part1'),
88
]
99

10-
# Issue #45 test
10+
# Run test for issue https://github.com/ESSolutions/django-mssql-backend/issues/45
1111
operations = [
1212
# Case 1: changing max_length changes the column type - the filtered UNIQUE INDEX which implements
1313
# the nullable unique constraint, should be correctly reinstated after this change of column type

testapp/migrations/0006_test_remove_onetoone_field_part1.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
class Migration(migrations.Migration):
88

99
dependencies = [
10-
('testapp', '0005_test_issue45_unique_type_change_part2'),
10+
('testapp', '0005_test_unique_type_change_part2'),
1111
]
1212

1313
operations = [

0 commit comments

Comments
 (0)