import collections import os import re from nose.tools import eq_ try: from nose.tools import assert_raises_regexp except ImportError: from ansible.compat.six import string_types # Python < 2.7 def assert_raises_regexp(expected, regexp, callable, *a, **kw): try: callable(*a, **kw) except expected as e: if isinstance(regexp, string_types): regexp = re.compile(regexp) if not regexp.search(str(e)): raise Exception('"%s" does not match "%s"' % (regexp.pattern, str(e))) else: if hasattr(expected,'__name__'): excName = expected.__name__ else: excName = str(expected) raise AssertionError("%s not raised" % excName) from ansible.compat.tests import mock from ansible.module_utils.database import ( pg_quote_identifier, SQLParseError, ) # Note: Using nose's generator test cases here so we can't inherit from # unittest.TestCase class TestQuotePgIdentifier(object): # These are all valid strings # The results are based on interpreting the identifier as a table name valid = { # User quoted '"public.table"': '"public.table"', '"public"."table"': '"public"."table"', '"schema test"."table test"': '"schema test"."table test"', # We quote part 'public.table': '"public"."table"', '"public".table': '"public"."table"', 'public."table"': '"public"."table"', 'schema test.table test': '"schema test"."table test"', '"schema test".table test': '"schema test"."table test"', 'schema test."table test"': '"schema test"."table test"', # Embedded double quotes 'table "test"': '"table ""test"""', 'public."table ""test"""': '"public"."table ""test"""', 'public.table "test"': '"public"."table ""test"""', 'schema "test".table': '"schema ""test"""."table"', '"schema ""test""".table': '"schema ""test"""."table"', '"""wat"""."""test"""': '"""wat"""."""test"""', # Sigh, handle these as well: '"no end quote': '"""no end quote"', 'schema."table': '"schema"."""table"', '"schema.table': '"""schema"."table"', 'schema."table.something': '"schema"."""table"."something"', # Embedded dots '"schema.test"."table.test"': '"schema.test"."table.test"', '"schema.".table': '"schema."."table"', '"schema."."table"': '"schema."."table"', 'schema.".table"': '"schema".".table"', '"schema".".table"': '"schema".".table"', '"schema.".".table"': '"schema.".".table"', # These are valid but maybe not what the user intended '."table"': '".""table"""', 'table.': '"table."', } invalid = { ('test.too.many.dots', 'table'): 'PostgreSQL does not support table with more than 3 dots', ('"test.too".many.dots', 'database'): 'PostgreSQL does not support database with more than 1 dots', ('test.too."many.dots"', 'database'): 'PostgreSQL does not support database with more than 1 dots', ('"test"."too"."many"."dots"', 'database'): "PostgreSQL does not support database with more than 1 dots", ('"test"."too"."many"."dots"', 'schema'): "PostgreSQL does not support schema with more than 2 dots", ('"test"."too"."many"."dots"', 'table'): "PostgreSQL does not support table with more than 3 dots", ('"test"."too"."many"."dots"."for"."column"', 'column'): "PostgreSQL does not support column with more than 4 dots", ('"table "invalid" double quote"', 'table'): 'User escaped identifiers must escape extra quotes', ('"schema "invalid"""."table "invalid"', 'table'): 'User escaped identifiers must escape extra quotes', ('"schema."table"','table'): 'User escaped identifiers must escape extra quotes', ('"schema".', 'table'): 'Identifier name unspecified or unquoted trailing dot', } def check_valid_quotes(self, identifier, quoted_identifier): eq_(pg_quote_identifier(identifier, 'table'), quoted_identifier) def test_valid_quotes(self): for identifier in self.valid: yield self.check_valid_quotes, identifier, self.valid[identifier] def check_invalid_quotes(self, identifier, id_type, msg): assert_raises_regexp(SQLParseError, msg, pg_quote_identifier, *(identifier, id_type)) def test_invalid_quotes(self): for test in self.invalid: yield self.check_invalid_quotes, test[0], test[1], self.invalid[test] def test_how_many_dots(self): eq_(pg_quote_identifier('role', 'role'), '"role"') assert_raises_regexp(SQLParseError, "PostgreSQL does not support role with more than 1 dots", pg_quote_identifier, *('role.more', 'role')) eq_(pg_quote_identifier('db', 'database'), '"db"') assert_raises_regexp(SQLParseError, "PostgreSQL does not support database with more than 1 dots", pg_quote_identifier, *('db.more', 'database')) eq_(pg_quote_identifier('db.schema', 'schema'), '"db"."schema"') assert_raises_regexp(SQLParseError, "PostgreSQL does not support schema with more than 2 dots", pg_quote_identifier, *('db.schema.more', 'schema')) eq_(pg_quote_identifier('db.schema.table', 'table'), '"db"."schema"."table"') assert_raises_regexp(SQLParseError, "PostgreSQL does not support table with more than 3 dots", pg_quote_identifier, *('db.schema.table.more', 'table')) eq_(pg_quote_identifier('db.schema.table.column', 'column'), '"db"."schema"."table"."column"') assert_raises_regexp(SQLParseError, "PostgreSQL does not support column with more than 4 dots", pg_quote_identifier, *('db.schema.table.column.more', 'column'))