mssql_script: allow non-returning SQL statements (#6457)

* feat: Allow non-returning SQL statements

- The current implementation fails out when certain statements or
  batches do not have resultsets - this limits the usefulness of the
  module
- Instead, it is known that statements without resultsets return then
  OperationalError exception with text "Statement not executed or
  executed statement has no resultset". We will utilize these facts to
  accept these statements
- The implementation also assumes that users will always use best-
  practices for the script syntax; that is, "GO" will always be
  capitalized but this is not strictly required -- update to allow "GO"
  to be any mixed-case

Signed-off-by: Lesley Kimmel <lesley.j.kimmel@gmail.com>

* feat: Add changelog fragment for change

- Add changelog fragment for PR 6192

Signed-off-by: Lesley Kimmel <lesley.j.kimmel@gmail.com>

* feat: Improve batching

- Previous batching had shortcomings like making strict assumptions
  about the format of the incoming script and did not handle Windows-
  based scripts (e.g. \r characters). It also did not handle cases where
  there were trailing or leading whitespace characters round the 'GO'
- Added a special case for removing the Byte Order Mark (BOM) character
  that may come as part of a script when slurped from some hosts.

Signed-off-by: Lesley Kimmel <lesley.j.kimmel@gmail.com>

* feat: Use str.splitlines()

- Use of this method is cleaner

Signed-off-by: Lesley Kimmel <lesley.j.kimmel@gmail.com>

* Update changelogs/fragments/6192-allow-empty-resultsets.yml

Co-authored-by: Felix Fontein <felix@fontein.de>

* fix: Update transcribing errors

- Replace local namespace with project namespace
- Remove 'return' statement from the module.fail_json call

Signed-off-by: Lesley Kimmel <lesley.j.kimmel@gmail.com>

---------

Signed-off-by: Lesley Kimmel <lesley.j.kimmel@gmail.com>
Co-authored-by: Lesley Kimmel <lesleyk@vmware.com>
Co-authored-by: Felix Fontein <felix@fontein.de>
This commit is contained in:
ljkimmel 2023-05-07 14:58:38 -05:00 committed by GitHub
parent 61a0dc4370
commit 165182cdbf
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 73 additions and 5 deletions

View file

@ -280,14 +280,32 @@ def run_module():
cursor = conn.cursor(as_dict=True)
query_results_key = 'query_results_dict'
queries = script.split('\nGO\n')
# Process the script into batches
queries = []
current_batch = []
for statement in script.splitlines(keepends=True):
# Ignore the Byte Order Mark, if found
if statement.strip() == '\uFEFF':
continue
# Assume each 'GO' is on its own line but may have leading/trailing whitespace
# and be of mixed-case
if statement.strip().upper() != 'GO':
current_batch.append(statement)
else:
queries.append(''.join(current_batch))
current_batch = []
if len(current_batch) > 0:
queries.append(''.join(current_batch))
result['changed'] = True
if module.check_mode:
module.exit_json(**result)
query_results = []
try:
for query in queries:
for query in queries:
# Catch and exit on any bad query errors
try:
cursor.execute(query, sql_params)
qry_result = []
rows = cursor.fetchall()
@ -295,8 +313,17 @@ def run_module():
qry_result.append(rows)
rows = cursor.fetchall()
query_results.append(qry_result)
except Exception as e:
return module.fail_json(msg="query failed", query=query, error=str(e), **result)
except Exception as e:
# We know we executed the statement so this error just means we have no resultset
# which is ok (eg UPDATE/INSERT)
if (
type(e).__name__ == 'OperationalError' and
str(e) == 'Statement not executed or executed statement has no resultset'
):
query_results.append([])
else:
error_msg = '%s: %s' % (type(e).__name__, str(e))
module.fail_json(msg="query failed", query=query, error=error_msg, **result)
# ensure that the result is json serializable
qry_results = json.loads(json.dumps(query_results, default=clean_output))