Merge pull request #2446 from b6d/postgresql_db-module-extension

postgresql_db: add args lc_collate, lc_ctype
pull/2476/merge
Michael DeHaan 12 years ago
commit 3263356bf4

@ -64,6 +64,16 @@ options:
- Encoding of the database - Encoding of the database
required: false required: false
default: null default: null
lc_collate:
description:
- Collation order (LC_COLLATE) to use in the database. Must match collation order of template database unless C(template0) is used as template.
required: false
default: null
lc_ctype:
description:
- Character classification (LC_CTYPE) to use in the database (e.g. lower, upper, ...) Must match LC_CTYPE of template database unless C(template0) is used as template.
required: false
default: null
state: state:
description: description:
- The database state - The database state
@ -73,6 +83,8 @@ options:
examples: examples:
- code: "postgresql_db: db=acme" - code: "postgresql_db: db=acme"
description: Create a new database with name C(acme) description: Create a new database with name C(acme)
- code: "postgresql_db: db=acme encoding='UTF-8' lc_collate='de_DE.UTF-8' lc_ctype='de_DE.UTF-8' template='template0'"
description: Create a new database with name C(acme) and specific encoding and locale settings. If a template different from C(template0) is specified, encoding and locale settings must match those of the template.
notes: notes:
- The default authentication assumes that you are either logging in as or sudo'ing to the C(postgres) account on the host. - The default authentication assumes that you are either logging in as or sudo'ing to the C(postgres) account on the host.
- This module uses I(psycopg2), a Python PostgreSQL database adapter. You must ensure that psycopg2 is installed on - This module uses I(psycopg2), a Python PostgreSQL database adapter. You must ensure that psycopg2 is installed on
@ -83,11 +95,16 @@ author: Lorin Hochstein
try: try:
import psycopg2 import psycopg2
import psycopg2.extras
except ImportError: except ImportError:
postgresqldb_found = False postgresqldb_found = False
else: else:
postgresqldb_found = True postgresqldb_found = True
class NotSupportedError(Exception):
pass
# =========================================== # ===========================================
# PostgreSQL module specific support methods. # PostgreSQL module specific support methods.
# #
@ -97,11 +114,21 @@ def set_owner(cursor, db, owner):
cursor.execute(query) cursor.execute(query)
return True return True
def db_owned_by(cursor, db, user): def get_encoding_id(cursor, encoding):
query = """SELECT * FROM pg_database JOIN pg_user ON datdba = usesysid query = "SELECT pg_char_to_encoding(%(encoding)s) AS encoding_id;"
WHERE usename = %(user)s and datname = %(db)s""" cursor.execute(query, {'encoding': encoding})
cursor.execute(query, {'db':db, 'user':user}) return cursor.fetchone()['encoding_id']
return cursor.rowcount == 1
def get_db_info(cursor, db):
query = """
SELECT usename AS owner,
pg_encoding_to_char(encoding) AS encoding, encoding AS encoding_id,
datcollate AS lc_collate, datctype AS lc_ctype
FROM pg_database JOIN pg_user ON pg_user.usesysid = pg_database.datdba
WHERE datname = %(db)s
"""
cursor.execute(query, {'db':db})
return cursor.fetchone()
def db_exists(cursor, db): def db_exists(cursor, db):
query = "SELECT * FROM pg_database WHERE datname=%(db)s" query = "SELECT * FROM pg_database WHERE datname=%(db)s"
@ -116,7 +143,7 @@ def db_delete(cursor, db):
else: else:
return False return False
def db_create(cursor, db, owner, template, encoding): def db_create(cursor, db, owner, template, encoding, lc_collate, lc_ctype):
if not db_exists(cursor, db): if not db_exists(cursor, db):
if owner: if owner:
owner = " OWNER \"%s\"" % owner owner = " OWNER \"%s\"" % owner
@ -124,13 +151,37 @@ def db_create(cursor, db, owner, template, encoding):
template = " TEMPLATE \"%s\"" % template template = " TEMPLATE \"%s\"" % template
if encoding: if encoding:
encoding = " ENCODING '%s'" % encoding encoding = " ENCODING '%s'" % encoding
query = "CREATE DATABASE \"%s\"%s%s%s" % (db, owner, template, encoding) if lc_collate:
lc_collate = " LC_COLLATE '%s'" % lc_collate
if lc_ctype:
lc_ctype = " LC_CTYPE '%s'" % lc_ctype
query = 'CREATE DATABASE "%s"%s%s%s%s%s' % (db, owner,
template, encoding,
lc_collate, lc_ctype)
cursor.execute(query) cursor.execute(query)
return True return True
elif owner and not db_owned_by(cursor, db, owner):
return set_owner(cursor, db, owner)
else: else:
return False db_info = get_db_info(cursor, db)
if (encoding and
get_encoding_id(cursor, encoding) != db_info['encoding_id']):
raise NotSupportedError(
'Changing database encoding is not supported. '
'Current encoding: %s' % db_info['encoding']
)
elif lc_collate and lc_collate != db_info['lc_collate']:
raise NotSupportedError(
'Changing LC_COLLATE is not supported. '
'Current LC_COLLATE: %s' % db_info['lc_collate']
)
elif lc_ctype and lc_ctype != db_info['lc_ctype']:
raise NotSupportedError(
'Changing LC_CTYPE is not supported.'
'Current LC_CTYPE: %s' % db_info['lc_ctype']
)
elif owner and owner != db_info['owner']:
return set_owner(cursor, db, owner)
else:
return False
# =========================================== # ===========================================
# Module execution. # Module execution.
@ -147,6 +198,8 @@ def main():
owner=dict(default=""), owner=dict(default=""),
template=dict(default=""), template=dict(default=""),
encoding=dict(default=""), encoding=dict(default=""),
lc_collate=dict(default=""),
lc_ctype=dict(default=""),
state=dict(default="present", choices=["absent", "present"]), state=dict(default="present", choices=["absent", "present"]),
), ),
supports_check_mode = True supports_check_mode = True
@ -160,6 +213,8 @@ def main():
owner = module.params["owner"] owner = module.params["owner"]
template = module.params["template"] template = module.params["template"]
encoding = module.params["encoding"] encoding = module.params["encoding"]
lc_collate = module.params["lc_collate"]
lc_ctype = module.params["lc_ctype"]
state = module.params["state"] state = module.params["state"]
changed = False changed = False
@ -183,7 +238,8 @@ def main():
db_connection.set_isolation_level(psycopg2 db_connection.set_isolation_level(psycopg2
.extensions .extensions
.ISOLATION_LEVEL_AUTOCOMMIT) .ISOLATION_LEVEL_AUTOCOMMIT)
cursor = db_connection.cursor() cursor = db_connection.cursor(
cursor_factory=psycopg2.extras.DictCursor)
except Exception, e: except Exception, e:
module.fail_json(msg="unable to connect to database: %s" % e) module.fail_json(msg="unable to connect to database: %s" % e)
@ -195,8 +251,10 @@ def main():
changed = db_delete(cursor, db) changed = db_delete(cursor, db)
elif state == "present": elif state == "present":
changed = db_create(cursor, db, owner, template, encoding) changed = db_create(cursor, db, owner, template, encoding,
lc_collate, lc_ctype)
except NotSupportedError, e:
module.fail_json(msg=str(e))
except Exception, e: except Exception, e:
module.fail_json(msg="Database query failed: %s" % e) module.fail_json(msg="Database query failed: %s" % e)

Loading…
Cancel
Save