2007-11-11 13:40:12 +03:00
|
|
|
#!/usr/bin/env python
|
2011-01-07 04:44:19 +03:00
|
|
|
|
|
|
|
# test_quote.py - unit test for strings quoting
|
|
|
|
#
|
|
|
|
# Copyright (C) 2007-2011 Daniele Varrazzo <daniele.varrazzo@gmail.com>
|
|
|
|
#
|
|
|
|
# psycopg2 is free software: you can redistribute it and/or modify it
|
|
|
|
# under the terms of the GNU Lesser General Public License as published
|
|
|
|
# by the Free Software Foundation, either version 3 of the License, or
|
|
|
|
# (at your option) any later version.
|
|
|
|
#
|
|
|
|
# In addition, as a special exception, the copyright holders give
|
|
|
|
# permission to link this program with the OpenSSL library (or with
|
|
|
|
# modified versions of OpenSSL that use the same license as OpenSSL),
|
|
|
|
# and distribute linked combinations including the two.
|
|
|
|
#
|
|
|
|
# You must obey the GNU Lesser General Public License in all respects for
|
|
|
|
# all of the code used other than OpenSSL.
|
|
|
|
#
|
|
|
|
# psycopg2 is distributed in the hope that it will be useful, but WITHOUT
|
|
|
|
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
|
|
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
|
|
|
# License for more details.
|
|
|
|
|
2010-12-28 15:47:29 +03:00
|
|
|
import sys
|
2015-10-15 12:52:18 +03:00
|
|
|
from testutils import unittest, ConnectingTestCase, skip_before_libpq
|
2008-04-21 03:19:42 +04:00
|
|
|
|
2007-11-11 11:53:44 +03:00
|
|
|
import psycopg2
|
|
|
|
import psycopg2.extensions
|
2010-12-29 05:47:29 +03:00
|
|
|
from psycopg2.extensions import b
|
2007-11-11 11:53:44 +03:00
|
|
|
|
2013-04-07 03:23:30 +04:00
|
|
|
class QuotingTestCase(ConnectingTestCase):
|
2007-11-11 11:53:44 +03:00
|
|
|
r"""Checks the correct quoting of strings and binary objects.
|
|
|
|
|
|
|
|
Since ver. 8.1, PostgreSQL is moving towards SQL standard conforming
|
|
|
|
strings, where the backslash (\) is treated as literal character,
|
|
|
|
not as escape. To treat the backslash as a C-style escapes, PG supports
|
|
|
|
the E'' quotes.
|
|
|
|
|
|
|
|
This test case checks that the E'' quotes are used whenever they are
|
|
|
|
needed. The tests are expected to pass with all PostgreSQL server versions
|
|
|
|
(currently tested with 7.4 <= PG <= 8.3beta) and with any
|
|
|
|
'standard_conforming_strings' server parameter value.
|
|
|
|
The tests also check that no warning is raised ('escape_string_warning'
|
|
|
|
should be on).
|
|
|
|
|
2012-02-28 20:28:07 +04:00
|
|
|
http://www.postgresql.org/docs/current/static/sql-syntax-lexical.html#SQL-SYNTAX-STRINGS
|
|
|
|
http://www.postgresql.org/docs/current/static/runtime-config-compatible.html
|
2007-11-11 11:53:44 +03:00
|
|
|
"""
|
|
|
|
def test_string(self):
|
|
|
|
data = """some data with \t chars
|
|
|
|
to escape into, 'quotes' and \\ a backslash too.
|
|
|
|
"""
|
|
|
|
data += "".join(map(chr, range(1,127)))
|
|
|
|
|
|
|
|
curs = self.conn.cursor()
|
|
|
|
curs.execute("SELECT %s;", (data,))
|
|
|
|
res = curs.fetchone()[0]
|
|
|
|
|
|
|
|
self.assertEqual(res, data)
|
|
|
|
self.assert_(not self.conn.notices)
|
|
|
|
|
|
|
|
def test_binary(self):
|
2010-12-29 05:47:29 +03:00
|
|
|
data = b("""some data with \000\013 binary
|
2007-11-11 11:53:44 +03:00
|
|
|
stuff into, 'quotes' and \\ a backslash too.
|
2010-12-29 05:47:29 +03:00
|
|
|
""")
|
|
|
|
if sys.version_info[0] < 3:
|
|
|
|
data += "".join(map(chr, range(256)))
|
|
|
|
else:
|
|
|
|
data += bytes(range(256))
|
2007-11-11 11:53:44 +03:00
|
|
|
|
|
|
|
curs = self.conn.cursor()
|
|
|
|
curs.execute("SELECT %s::bytea;", (psycopg2.Binary(data),))
|
2010-12-29 05:47:29 +03:00
|
|
|
if sys.version_info[0] < 3:
|
|
|
|
res = str(curs.fetchone()[0])
|
|
|
|
else:
|
|
|
|
res = curs.fetchone()[0].tobytes()
|
2007-11-11 11:53:44 +03:00
|
|
|
|
2011-02-23 17:04:27 +03:00
|
|
|
if res[0] in (b('x'), ord(b('x'))) and self.conn.server_version >= 90000:
|
|
|
|
return self.skipTest(
|
|
|
|
"bytea broken with server >= 9.0, libpq < 9")
|
|
|
|
|
2007-11-11 11:53:44 +03:00
|
|
|
self.assertEqual(res, data)
|
|
|
|
self.assert_(not self.conn.notices)
|
|
|
|
|
|
|
|
def test_unicode(self):
|
2008-04-21 03:19:42 +04:00
|
|
|
curs = self.conn.cursor()
|
|
|
|
curs.execute("SHOW server_encoding")
|
|
|
|
server_encoding = curs.fetchone()[0]
|
|
|
|
if server_encoding != "UTF8":
|
2010-11-19 06:55:37 +03:00
|
|
|
return self.skipTest(
|
|
|
|
"Unicode test skipped since server encoding is %s"
|
|
|
|
% server_encoding)
|
2008-04-21 03:19:42 +04:00
|
|
|
|
2007-11-11 11:53:44 +03:00
|
|
|
data = u"""some data with \t chars
|
|
|
|
to escape into, 'quotes', \u20ac euro sign and \\ a backslash too.
|
|
|
|
"""
|
|
|
|
data += u"".join(map(unichr, [ u for u in range(1,65536)
|
|
|
|
if not 0xD800 <= u <= 0xDFFF ])) # surrogate area
|
|
|
|
self.conn.set_client_encoding('UNICODE')
|
|
|
|
|
2011-01-03 19:29:04 +03:00
|
|
|
psycopg2.extensions.register_type(psycopg2.extensions.UNICODE, self.conn)
|
2007-11-11 11:53:44 +03:00
|
|
|
curs.execute("SELECT %s::text;", (data,))
|
|
|
|
res = curs.fetchone()[0]
|
|
|
|
|
|
|
|
self.assertEqual(res, data)
|
|
|
|
self.assert_(not self.conn.notices)
|
|
|
|
|
2010-12-28 15:47:29 +03:00
|
|
|
def test_latin1(self):
|
|
|
|
self.conn.set_client_encoding('LATIN1')
|
|
|
|
curs = self.conn.cursor()
|
|
|
|
if sys.version_info[0] < 3:
|
|
|
|
data = ''.join(map(chr, range(32, 127) + range(160, 256)))
|
|
|
|
else:
|
|
|
|
data = bytes(range(32, 127) + range(160, 256)).decode('latin1')
|
|
|
|
|
|
|
|
# as string
|
|
|
|
curs.execute("SELECT %s::text;", (data,))
|
|
|
|
res = curs.fetchone()[0]
|
|
|
|
self.assertEqual(res, data)
|
|
|
|
self.assert_(not self.conn.notices)
|
|
|
|
|
|
|
|
# as unicode
|
|
|
|
if sys.version_info[0] < 3:
|
|
|
|
psycopg2.extensions.register_type(psycopg2.extensions.UNICODE, self.conn)
|
|
|
|
data = data.decode('latin1')
|
|
|
|
|
|
|
|
curs.execute("SELECT %s::text;", (data,))
|
|
|
|
res = curs.fetchone()[0]
|
|
|
|
self.assertEqual(res, data)
|
|
|
|
self.assert_(not self.conn.notices)
|
|
|
|
|
|
|
|
def test_koi8(self):
|
|
|
|
self.conn.set_client_encoding('KOI8')
|
|
|
|
curs = self.conn.cursor()
|
|
|
|
if sys.version_info[0] < 3:
|
|
|
|
data = ''.join(map(chr, range(32, 127) + range(128, 256)))
|
|
|
|
else:
|
|
|
|
data = bytes(range(32, 127) + range(128, 256)).decode('koi8_r')
|
|
|
|
|
|
|
|
# as string
|
|
|
|
curs.execute("SELECT %s::text;", (data,))
|
|
|
|
res = curs.fetchone()[0]
|
|
|
|
self.assertEqual(res, data)
|
|
|
|
self.assert_(not self.conn.notices)
|
|
|
|
|
|
|
|
# as unicode
|
|
|
|
if sys.version_info[0] < 3:
|
|
|
|
psycopg2.extensions.register_type(psycopg2.extensions.UNICODE, self.conn)
|
|
|
|
data = data.decode('koi8_r')
|
|
|
|
|
|
|
|
curs.execute("SELECT %s::text;", (data,))
|
|
|
|
res = curs.fetchone()[0]
|
|
|
|
self.assertEqual(res, data)
|
|
|
|
self.assert_(not self.conn.notices)
|
|
|
|
|
|
|
|
|
2013-04-07 03:23:30 +04:00
|
|
|
class TestQuotedString(ConnectingTestCase):
|
2013-04-05 03:00:42 +04:00
|
|
|
def test_encoding(self):
|
|
|
|
q = psycopg2.extensions.QuotedString('hi')
|
|
|
|
self.assertEqual(q.encoding, 'latin1')
|
|
|
|
|
|
|
|
self.conn.set_client_encoding('utf_8')
|
|
|
|
q.prepare(self.conn)
|
|
|
|
self.assertEqual(q.encoding, 'utf_8')
|
|
|
|
|
|
|
|
|
2015-10-13 18:29:55 +03:00
|
|
|
class TestQuotedIdentifier(ConnectingTestCase):
|
2015-10-15 12:52:18 +03:00
|
|
|
@skip_before_libpq(9, 0)
|
2015-10-13 18:29:55 +03:00
|
|
|
def test_identifier(self):
|
|
|
|
from psycopg2.extensions import quote_ident
|
|
|
|
self.assertEqual(quote_ident('blah-blah', self.conn), '"blah-blah"')
|
|
|
|
self.assertEqual(quote_ident('quote"inside', self.conn), '"quote""inside"')
|
|
|
|
|
2015-10-15 12:52:18 +03:00
|
|
|
@skip_before_libpq(9, 0)
|
|
|
|
def test_unicode_ident(self):
|
|
|
|
from psycopg2.extensions import quote_ident
|
|
|
|
snowman = u"\u2603"
|
|
|
|
quoted = '"' + snowman + '"'
|
|
|
|
if sys.version_info[0] < 3:
|
|
|
|
self.assertEqual(quote_ident(snowman, self.conn), quoted.encode('utf8'))
|
|
|
|
else:
|
|
|
|
self.assertEqual(quote_ident(snowman, self.conn), quoted)
|
|
|
|
|
2015-10-13 18:29:55 +03:00
|
|
|
|
2007-11-11 11:53:44 +03:00
|
|
|
def test_suite():
|
|
|
|
return unittest.TestLoader().loadTestsFromName(__name__)
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
unittest.main()
|
|
|
|
|