@@ -136,21 +136,34 @@ def __db_connection(self):
136
136
137
137
return self .__db_conn
138
138
139
- def __execute (self , statement , * args ):
139
+ def __execute_dml (self , statement , * args ):
140
140
logger .debug (statement , args )
141
- c = self .__connection ()
141
+ c = self .__db_connection ()
142
142
# If timeout is set, then apply it to the connection. PyODBC will then assign that value to the Cursor created during execute()
143
143
if self .timeout :
144
144
c .timeout = self .timeout
145
- return c .execute (statement , * args )
146
-
147
- def __db_execute (self , statement , * args ):
145
+ cur = c .execute (statement , * args )
146
+ # If the SQL query causes multiple messages to come back (either extra row counts from triggers, or PRINT statements),
147
+ # then we need to keep running nextset() for PyODBC to get the query to run to completion
148
+ while cur .nextset ():
149
+ pass
150
+ return cur
151
+
152
+ def __execute_ddl (self , statement , * args ):
148
153
logger .debug (statement , args )
149
154
c = self .__db_connection ()
150
155
# If timeout is set, then apply it to the connection. PyODBC will then assign that value to the Cursor created during execute()
151
156
if self .timeout :
152
157
c .timeout = self .timeout
153
158
return c .execute (statement , * args )
159
+
160
+ def __execute_server (self , statement , * args ):
161
+ logger .debug (statement , args )
162
+ c = self .__connection ()
163
+ # If timeout is set, then apply it to the connection. PyODBC will then assign that value to the Cursor created during execute()
164
+ if self .timeout :
165
+ c .timeout = self .timeout
166
+ return c .execute (statement , * args )
154
167
155
168
def __get_path (self , filepath ):
156
169
if "\\ " in filepath :
@@ -169,7 +182,7 @@ def __get_default_datafolder(self):
169
182
checking the model db seems like a good 'boring' solution
170
183
:return: Default data directory e.g. "C:\\ DATA"
171
184
"""
172
- datafile = self .__execute (
185
+ datafile = self .__execute_server (
173
186
"""
174
187
SELECT physical_name
175
188
FROM sys.master_files mf
@@ -187,7 +200,7 @@ def __get_default_logfolder(self):
187
200
__get_default_datafolder: see for more info
188
201
:return:
189
202
"""
190
- logfile = self .__execute (
203
+ logfile = self .__execute_server (
191
204
"""
192
205
SELECT physical_name
193
206
FROM sys.master_files mf
@@ -207,7 +220,7 @@ def __get_file_moves(self, input_path):
207
220
datadir = self .__get_default_datafolder ()
208
221
logdir = self .__get_default_logfolder ()
209
222
210
- filelist = self .__execute (
223
+ filelist = self .__execute_server (
211
224
f"RESTORE FILELISTONLY FROM DISK = ?;" , input_path
212
225
).fetchall ()
213
226
@@ -245,7 +258,7 @@ def __run_scripts(self, script_list, title=""):
245
258
246
259
for i , script in enumerate (script_list ):
247
260
logger .info (f'Running { title } script #{ i } "{ script [:50 ]} "' )
248
- cursor = self .__db_execute (script )
261
+ cursor = self .__execute_dml (script )
249
262
results = None
250
263
try :
251
264
results = cursor .fetchall ()
@@ -262,10 +275,10 @@ def __create_seed_table(self, qualifier_map):
262
275
SEED_TABLE_NAME , "," .join (seed_column_lines )
263
276
)
264
277
265
- self .__db_execute (create_statement )
278
+ self .__execute_ddl (create_statement )
266
279
267
280
def __drop_seed_table (self ):
268
- self .__db_execute ("DROP TABLE IF EXISTS [{}];" .format (SEED_TABLE_NAME ))
281
+ self .__execute_ddl ("DROP TABLE IF EXISTS [{}];" .format (SEED_TABLE_NAME ))
269
282
270
283
def __insert_seed_row (self , qualifier_map ):
271
284
column_list = "," .join (
@@ -279,7 +292,7 @@ def __insert_seed_row(self, qualifier_map):
279
292
statement = "INSERT INTO [{}]({}) VALUES ({});" .format (
280
293
SEED_TABLE_NAME , column_list , substitution_list
281
294
)
282
- self .__db_execute (statement , value_list )
295
+ self .__execute_dml (statement , value_list )
283
296
284
297
def __seed (self , qualifier_map ):
285
298
for i in self .progress (
@@ -312,10 +325,10 @@ def create_database(self):
312
325
313
326
def drop_database (self ):
314
327
# force connection close so we can always drop the db: sometimes timing makes a normal drop impossible.
315
- self .__execute (
328
+ self .__execute_server (
316
329
f"ALTER DATABASE [{ self .db_name } ] SET SINGLE_USER WITH ROLLBACK IMMEDIATE;"
317
330
)
318
- self .__execute (f"DROP DATABASE IF EXISTS [{ self .db_name } ];" )
331
+ self .__execute_server (f"DROP DATABASE IF EXISTS [{ self .db_name } ];" )
319
332
320
333
def anonymize_database (self , database_strategy , db_workers ):
321
334
qualifier_map = database_strategy .fake_update_qualifier_map
@@ -343,13 +356,13 @@ def anonymize_table(progressbar, table_strategy: TableStrategy):
343
356
344
357
if table_strategy .strategy_type == TableStrategyTypes .TRUNCATE :
345
358
progressbar .set_description ("Truncating {}" .format (table_name ))
346
- self .__db_execute (
359
+ self .__execute_dml (
347
360
"TRUNCATE TABLE {}[{}];" .format (schema_prefix , table_name )
348
361
)
349
362
350
363
elif table_strategy .strategy_type == TableStrategyTypes .DELETE :
351
364
progressbar .set_description ("Deleting {}" .format (table_name ))
352
- self .__db_execute (
365
+ self .__execute_dml (
353
366
"DELETE FROM {}[{}];" .format (schema_prefix , table_name )
354
367
)
355
368
@@ -386,7 +399,7 @@ def anonymize_table(progressbar, table_strategy: TableStrategy):
386
399
387
400
# set ansi warnings off because otherwise we run into lots of little incompatibilities between the seed data nd the columns
388
401
# e.g. string or binary data would be truncated (when the data is too long)
389
- self .__db_execute (
402
+ self .__execute_dml (
390
403
f"{ ansi_warnings_prefix } UPDATE { schema_prefix } [{ table_name } ] SET { column_assignments } { where_clause } ; { ansi_warnings_suffix } "
391
404
)
392
405
@@ -426,7 +439,7 @@ def restore_database(self, input_path):
426
439
move_clauses = ", " .join (["MOVE ? TO ?" ] * len (move_files ))
427
440
move_clause_params = [item for pair in move_files .items () for item in pair ]
428
441
429
- restore_cursor = self .__execute (
442
+ restore_cursor = self .__execute_server (
430
443
f"RESTORE DATABASE ? FROM DISK = ? WITH { move_clauses } , STATS = ?;" ,
431
444
[self .db_name , input_path , * move_clause_params , self .__STATS ],
432
445
)
@@ -442,7 +455,7 @@ def dump_database(self, output_path):
442
455
"," .join (with_options ) + ", " if len (with_options ) > 0 else ""
443
456
)
444
457
445
- dump_cursor = self .__execute (
458
+ dump_cursor = self .__execute_server (
446
459
f"BACKUP DATABASE ? TO DISK = ? WITH { with_options_str } STATS = ?;" ,
447
460
[self .db_name , output_path , self .__STATS ],
448
461
)
0 commit comments