From 191068a286e8dbdd18ec149a6a36d6c3ae152159 Mon Sep 17 00:00:00 2001 From: Andrey Klychkov Date: Thu, 22 Aug 2019 09:53:29 +0300 Subject: [PATCH] Issue 59955: add array handling to postgresql_query module (#60559) * Issue 59955: add array handling to postgresql_query module * Issue 59955: add array handling to postgresql_query module, improvements * Issue 59955: add array handling to postgresql_query module, fix example comment --- ...59-postgresql_query_add_array_handling.yml | 2 + .../database/postgresql/postgresql_query.py | 62 ++++++++++ .../postgresql/tasks/postgresql_query.yml | 108 ++++++++++++++++++ 3 files changed, 172 insertions(+) create mode 100644 changelogs/fragments/60559-postgresql_query_add_array_handling.yml diff --git a/changelogs/fragments/60559-postgresql_query_add_array_handling.yml b/changelogs/fragments/60559-postgresql_query_add_array_handling.yml new file mode 100644 index 00000000000..055b11241d2 --- /dev/null +++ b/changelogs/fragments/60559-postgresql_query_add_array_handling.yml @@ -0,0 +1,2 @@ +minor_changes: +- postgresql_query - Add array handling for positional_args and named_args parameters (https://github.com/ansible/ansible/issues/59955). diff --git a/lib/ansible/modules/database/postgresql/postgresql_query.py b/lib/ansible/modules/database/postgresql/postgresql_query.py index fda07f494da..60365a7b789 100644 --- a/lib/ansible/modules/database/postgresql/postgresql_query.py +++ b/lib/ansible/modules/database/postgresql/postgresql_query.py @@ -30,11 +30,13 @@ options: positional_args: description: - List of values to be passed as positional arguments to the query. + When the value is a list, it will be converted to PostgreSQL array. - Mutually exclusive with I(named_args). type: list named_args: description: - Dictionary of key-value arguments to pass to the query. + When the value is a list, it will be converted to PostgreSQL array. - Mutually exclusive with I(positional_args). type: dict path_to_script: @@ -119,6 +121,22 @@ EXAMPLES = r''' query: INSERT INTO test_table (array_column) VALUES (%s) positional_args: - '{1,2,3}' + +# Pass list and string vars as positional_args +- name: Set vars + set_fact: + my_list: + - 1 + - 2 + - 3 + my_arr: '{1, 2, 3}' + +- name: Select from test table by passing positional_args as arrays + postgresql_query: + query: SELECT * FROM test_array_table WHERE arr_col1 = %s AND arr_col2 = %s + positional_args: + - '{{ my_list }}' + - '{{ my_arr|string }}' ''' RETURN = r''' @@ -161,12 +179,50 @@ from ansible.module_utils.postgres import ( postgres_common_argument_spec, ) from ansible.module_utils._text import to_native +from ansible.module_utils.six import iteritems # =========================================== # Module execution. # +def list_to_pg_array(elem): + """Convert the passed list to PostgreSQL array + represented as a string. + + Args: + elem (list): List that needs to be converted. + + Returns: + elem (str): String representation of PostgreSQL array. + """ + elem = str(elem).strip('[]') + elem = '{' + elem + '}' + return elem + + +def convert_elements_to_pg_arrays(obj): + """Convert list elements of the passed object + to PostgreSQL arrays represented as strings. + + Args: + obj (dict or list): Object whose elements need to be converted. + + Returns: + obj (dict or list): Object with converted elements. + """ + if isinstance(obj, dict): + for (key, elem) in iteritems(obj): + if isinstance(elem, list): + obj[key] = list_to_pg_array(elem) + + elif isinstance(obj, list): + for i, elem in enumerate(obj): + if isinstance(elem, list): + obj[i] = list_to_pg_array(elem) + + return obj + def main(): argument_spec = postgres_common_argument_spec() @@ -201,6 +257,12 @@ def main(): if path_to_script and query: module.fail_json(msg="path_to_script is mutually exclusive with query") + if positional_args: + positional_args = convert_elements_to_pg_arrays(positional_args) + + elif named_args: + named_args = convert_elements_to_pg_arrays(named_args) + if path_to_script: try: query = open(path_to_script, 'r').read() diff --git a/test/integration/targets/postgresql/tasks/postgresql_query.yml b/test/integration/targets/postgresql/tasks/postgresql_query.yml index b86598fbc3e..aca07442e20 100644 --- a/test/integration/targets/postgresql/tasks/postgresql_query.yml +++ b/test/integration/targets/postgresql/tasks/postgresql_query.yml @@ -360,3 +360,111 @@ - result.rowcount == 0 - result.statusmessage == 'VACUUM' - result.query_result == {} + +# +# Issue 59955 +# +- name: postgresql_query - create test table for issue 59955 + become_user: "{{ pg_user }}" + become: yes + postgresql_table: + login_user: "{{ pg_user }}" + login_db: postgres + name: test_array_table + columns: + - arr_col int[] + when: postgres_version_resp.stdout is version('9.4', '>=') + +- set_fact: + my_list: + - 1 + - 2 + - 3 + my_arr: '{1, 2, 3}' + when: postgres_version_resp.stdout is version('9.4', '>=') + +- name: postgresql_query - insert array into test table by positional args + become_user: "{{ pg_user }}" + become: yes + postgresql_query: + login_user: "{{ pg_user }}" + login_db: postgres + query: INSERT INTO test_array_table (arr_col) VALUES (%s) + positional_args: + - '{{ my_list }}' + register: result + when: postgres_version_resp.stdout is version('9.4', '>=') + +- assert: + that: + - result is changed + - result.query == "INSERT INTO test_array_table (arr_col) VALUES ('{1, 2, 3}')" + when: postgres_version_resp.stdout is version('9.4', '>=') + +- name: postgresql_query - select array from test table by passing positional_args + become_user: "{{ pg_user }}" + become: yes + postgresql_query: + login_user: "{{ pg_user }}" + login_db: postgres + query: SELECT * FROM test_array_table WHERE arr_col = %s + positional_args: + - '{{ my_list }}' + register: result + when: postgres_version_resp.stdout is version('9.4', '>=') + +- assert: + that: + - result is not changed + - result.query == "SELECT * FROM test_array_table WHERE arr_col = '{1, 2, 3}'" + - result.rowcount == 1 + when: postgres_version_resp.stdout is version('9.4', '>=') + +- name: postgresql_query - select array from test table by passing named_args + become_user: "{{ pg_user }}" + become: yes + postgresql_query: + login_user: "{{ pg_user }}" + login_db: postgres + query: SELECT * FROM test_array_table WHERE arr_col = %(arr_val)s + named_args: + arr_val: + - '{{ my_list }}' + register: result + when: postgres_version_resp.stdout is version('9.4', '>=') + +- assert: + that: + - result is not changed + - result.query == "SELECT * FROM test_array_table WHERE arr_col = '{1, 2, 3}'" + - result.rowcount == 1 + when: postgres_version_resp.stdout is version('9.4', '>=') + +- name: postgresql_query - select array from test table by passing positional_args as a string + become_user: "{{ pg_user }}" + become: yes + postgresql_query: + login_user: "{{ pg_user }}" + login_db: postgres + query: SELECT * FROM test_array_table WHERE arr_col = %s + positional_args: + - '{{ my_arr|string }}' + register: result + when: postgres_version_resp.stdout is version('9.4', '>=') + +- assert: + that: + - result is not changed + - result.query == "SELECT * FROM test_array_table WHERE arr_col = '{1, 2, 3}'" + - result.rowcount == 1 + when: postgres_version_resp.stdout is version('9.4', '>=') + +- name: postgresql_query - clean up + become_user: "{{ pg_user }}" + become: yes + postgresql_table: + login_user: "{{ pg_user }}" + login_db: postgres + name: test_array_table + state: absent + when: postgres_version_resp.stdout is version('9.4', '>=')