Repository: ansible-collections/community.general

Issue:
postgresql_privs fails with schema name which contain '-' (dash) character 
_From @vosmax on Jul 15, 2020 14:44_

##### SUMMARY
<!--- Explain the problem briefly below -->
Task with module **postgresql_privs** fails with error when it's trying to apply default privileges to schema with name, which contain '-' (dash) character.  

##### ISSUE TYPE
- Bug Report

##### COMPONENT NAME
postgresql_privs

##### ANSIBLE VERSION
<!--- Paste verbatim output from "ansible --version" between quotes -->
```
ansible 2.9.10
  config file = None
  configured module search path = ['/Users/ilon.mask/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/ansible
  executable location = /Library/Frameworks/Python.framework/Versions/3.8/bin/ansible
  python version = 3.8.3 (v3.8.3:6f8c8320e9, May 13 2020, 16:29:34) [Clang 6.0 (clang-600.0.57)]
```

##### CONFIGURATION
<!--- Paste verbatim output from "ansible-config dump --only-changed" between quotes -->
```paste below
empty
```

##### OS / ENVIRONMENT
<!--- Provide all relevant information below, e.g. target OS versions, network device firmware, etc. -->
MacOS Catalina 10.15.5

##### STEPS TO REPRODUCE
<!--- Describe exactly how to reproduce the problem, using a minimal test-case -->

<!--- Paste example playbooks or commands between quotes below -->
```yaml
vars:
  postgres_db:
     host: localhost
     user: test
     password: test 
     db: test 
     schema: "test-schema"
     role: "my-role"

- name: Set default privs 
  postgresql_privs:
     login_host: "{{ postgres_db.host }}"
     login_user: "{{ postgres_db.user }}"
     login_password: "{{ postgres_db.password }}"
     login_db: "{{ postgres_db.db  }}"
     schema: "{{ postgres_db.schema | quote }}"
     role: "{{ postgres_db.role }}"
     type: default_privs
     obj: TABLES
     privs: all
```

<!--- HINT: You can paste gist.github.com links for larger files -->

##### EXPECTED RESULTS
<!--- Describe what you expected to happen when running the steps above -->
Default privileges are applied to the schema 

##### ACTUAL RESULTS
<!--- Describe what actually happened. If possible run with extra verbosity (-vvvv) -->

<!--- Paste verbatim command output between quotes -->
```paste below
"msg": "syntax error at or near \"-\"\nLINE 1: ALTER DEFAULT PRIVILEGES IN SCHEMA test-schema REVOKE A...\n
```


_Copied from original issue: ansible/ansible#70654_


Discussion:
Files identified in the description:
* [`plugins/modules/database/postgresql/postgresql_privs.py`](https://github.com/ansible-collections/community.general/blob/master/plugins/modules/database/postgresql/postgresql_privs.py)

If these files are inaccurate, please update the `component name` section of the description or use the `!component` bot command.

[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: components_banner --->
cc @Andersson007 @Dorn- @amenonsen @andytom @b6d @jbscalia @kostiantyn-nemchenko @matburt @nerzhul @sebasmannem @tcraxs
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->
@vosmax hey, thanks for reporting this!
1. Did it happen before 2.9.10 ? I mean did it start appearing after upgrade, i.e. the same playbook works before that?
2. Could you run the playbook with `-vvv` option and put the output here (starting from traceback would be enough)?
Thanks!
@Andersson007 Hey! 
1. Have no idea. Just started to work on this 
2. -vvv output
```
<127.0.0.1> ESTABLISH LOCAL CONNECTION FOR USER: elon.mask
<127.0.0.1> EXEC /bin/sh -c 'echo ~elon.mask && sleep 0'
<127.0.0.1> EXEC /bin/sh -c '( umask 77 && mkdir -p "` echo /Users/elon.mask/.ansible/tmp `"&& mkdir /Users/elon.mask/.ansible/tmp/ansible-tmp-1594839010.8667378-31015-208658247846105 && echo ansible-tmp-1594839010.8667378-31015-208658247846105="` echo /Users/elon.mask/.ansible/tmp/ansible-tmp-1594839010.8667378-31015-208658247846105 `" ) && sleep 0'
Using module file /Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/ansible/modules/database/postgresql/postgresql_privs.py
<127.0.0.1> PUT /Users/elon.mask/.ansible/tmp/ansible-local-31011zwxv9rk9/tmp4x3_xqzs TO /Users/elon.mask/.ansible/tmp/ansible-tmp-1594839010.8667378-31015-208658247846105/AnsiballZ_postgresql_privs.py
<127.0.0.1> EXEC /bin/sh -c 'chmod u+x /Users/elon.mask/.ansible/tmp/ansible-tmp-1594839010.8667378-31015-208658247846105/ /Users/elon.mask/.ansible/tmp/ansible-tmp-1594839010.8667378-31015-208658247846105/AnsiballZ_postgresql_privs.py && sleep 0'
<127.0.0.1> EXEC /bin/sh -c '/usr/local/bin/python /Users/elon.mask/.ansible/tmp/ansible-tmp-1594839010.8667378-31015-208658247846105/AnsiballZ_postgresql_privs.py && sleep 0'
<127.0.0.1> EXEC /bin/sh -c 'rm -f -r /Users/elon.mask/.ansible/tmp/ansible-tmp-1594839010.8667378-31015-208658247846105/ > /dev/null 2>&1 && sleep 0'
The full traceback is:
WARNING: The below traceback may *not* be related to the actual failure.
  File "/var/folders/42/_4n082tj24g8m439th2g10640000gp/T/ansible_********ql_privs_payload_AfAHMb/ansible_********ql_privs_payload.zip/ansible/modules/database/********ql/********ql_privs.py", line 1046, in main
  File "/var/folders/42/_4n082tj24g8m439th2g10640000gp/T/ansible_********ql_privs_payload_AfAHMb/ansible_********ql_privs_payload.zip/ansible/modules/database/********ql/********ql_privs.py", line 750, in manipulate_privs
fatal: [localhost]: FAILED! => {
    "changed": false,
    "invocation": {
        "module_args": {
            "ca_cert": null,
            "database": "test-db",
            "fail_on_role": true,
            "grant_option": null,
            "host": "localhost",
            "login": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER",
            "login_db": "test-db",
            "login_host": "localhost",
            "login_password": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER",
            "login_unix_socket": "",
            "login_user": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER",
            "obj": "TABLES",
            "objs": "TABLES",
            "password": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER",
            "port": 5432,
            "privs": "all",
            "role": "test-user",
            "roles": "test-user",
            "schema": "test-schema",
            "session_role": null,
            "ssl_mode": "prefer",
            "state": "present",
            "target_roles": null,
            "type": "default_privs",
            "unix_socket": ""
        }
    },
    "msg": "syntax error at or near \"-\"\nLINE 1: ALTER DEFAULT PRIVILEGES IN SCHEMA test-schema REVOKE...\n                                                   ^\n"
}

PLAY RECAP ******************************************************************************************************************************************************
localhost                  : ok=0    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0  
```
IMHO the root of the issue is here [postgresql_privs.py#L871](https://github.com/ansible-collections/community.general/blob/main/plugins/modules/database/postgresql/postgresql_privs.py#L871)

According documentation names with dash/hyphens must be in double quotes. In the code, which builds SQL query, and output we can see that there are no double quotes. 


 
@vosmax thanks for the info!
yes, probably that's the reason. Will try to look ASAP (hope tomorrow).

Resolving patch:
diff --git a/plugins/modules/database/postgresql/postgresql_privs.py b/plugins/modules/database/postgresql/postgresql_privs.py
--- a/plugins/modules/database/postgresql/postgresql_privs.py
+++ b/plugins/modules/database/postgresql/postgresql_privs.py
@@ -766,6 +766,9 @@ def manipulate_privs(self, obj_type, privs, objs, roles, target_roles,
         if target_roles:
             as_who = ','.join('"%s"' % r for r in target_roles)
 
+        if schema_qualifier:
+            schema_qualifier = '"%s"' % schema_qualifier
+
         status_before = get_status(objs)
 
         query = QueryBuilder(state) \


Test patch:
diff --git a/tests/integration/targets/postgresql_privs/defaults/main.yml b/tests/integration/targets/postgresql_privs/defaults/main.yml
--- a/tests/integration/targets/postgresql_privs/defaults/main.yml
+++ b/tests/integration/targets/postgresql_privs/defaults/main.yml
@@ -4,6 +4,9 @@ db_user2: ansible_db_user2
 db_user3: ansible_db_user3
 db_user_with_dots1: role.with.dots1
 db_user_with_dots2: role.with.dots2
+db_name_with_hyphens: ansible-db
+db_user_with_hyphens: ansible-db-user
+db_schema_with_hyphens: ansible-db-schema
 db_session_role1: session_role1
 db_session_role2: session_role2
 dangerous_name: 'curious.anonymous"; SELECT * FROM information_schema.tables; --'
diff --git a/tests/integration/targets/postgresql_privs/tasks/postgresql_privs_general.yml b/tests/integration/targets/postgresql_privs/tasks/postgresql_privs_general.yml
--- a/tests/integration/targets/postgresql_privs/tasks/postgresql_privs_general.yml
+++ b/tests/integration/targets/postgresql_privs/tasks/postgresql_privs_general.yml
@@ -27,6 +27,125 @@
     db: "{{ db_name }}"
     login_user: "{{ pg_user }}"
 
+#############################
+# Test of solving bug 656 #
+#############################
+- name: Create DB with hyphen in the name
+  become_user: "{{ pg_user }}"
+  become: yes
+  postgresql_db:
+    state: present
+    name: "{{ db_name_with_hyphens }}"
+    login_user: "{{ pg_user }}"
+  register: result
+
+- assert:
+    that:
+    - result is changed
+
+- name: Create a user with hyphen in the name
+  postgresql_user:
+    name: "{{ db_user_with_hyphens }}"
+    state: present
+    encrypted: yes
+    password: password
+    role_attr_flags: CREATEDB,LOGIN
+    db: "{{ db_name_with_hyphens }}"
+    login_user: "{{ pg_user }}"
+  register: result
+
+- assert:
+    that:
+    - result is changed
+
+- name: Create schema with hyphen in the name
+  postgresql_schema: 
+     login_user: "{{ pg_user }}"
+     login_password: password
+     db: "{{ db_name_with_hyphens }}"
+     name: "{{ db_schema_with_hyphens }}" 
+     state: present
+  register: result
+
+- assert:
+    that:
+    - result is changed
+
+- name: Set table default privs on the schema with hyphen in the name 
+  postgresql_privs:
+     login_user: "{{ pg_user }}"
+     password: password
+     db: "{{ db_name_with_hyphens }}"
+     schema: "{{ db_schema_with_hyphens }}" 
+     role: "{{ db_user_with_hyphens }}"
+     type: default_privs
+     obj: TABLES
+     privs: all
+     state: present
+  register: result
+
+- assert:
+    that:
+    - result is changed
+
+- name: Delete table default privs on the schema with hyphen in the name
+  postgresql_privs:
+     login_user: "{{ pg_user }}"
+     password: password
+     db: "{{ db_name_with_hyphens }}"
+     schema: "{{ db_schema_with_hyphens }}"
+     role: "{{ db_user_with_hyphens }}"
+     type: default_privs
+     obj: TABLES
+     privs: all
+     state: absent
+  register: result
+
+- assert:
+    that:
+    - result is changed
+
+- name: Delete schema with hyphen in the name
+  postgresql_schema:
+     login_user: "{{ pg_user }}"
+     login_password: password
+     db: "{{ db_name_with_hyphens }}"
+     name: "{{ db_schema_with_hyphens }}"
+     state: absent
+  register: result
+
+- assert:
+    that:
+    - result is changed
+
+- name: Delete a user with hyphen in the name
+  postgresql_user:
+    name: "{{ db_user_with_hyphens }}"
+    state: absent
+    encrypted: yes
+    password: password
+    role_attr_flags: CREATEDB,LOGIN
+    db: "{{ db_name_with_hyphens }}"
+    login_user: "{{ pg_user }}"
+  register: result
+
+- assert:
+    that:
+    - result is changed
+
+- name: Delete DB with hyphen in the name
+  become_user: "{{ pg_user }}"
+  become: yes
+  postgresql_db:
+    state: absent
+    name: "{{ db_name_with_hyphens }}"
+    login_user: "{{ pg_user }}"
+  register: result
+
+- assert:
+    that:
+    - result is changed
+
 #############################
 # Test of solving bug 27327 #
 #############################