[Python-modules-commits] [python-vertica] 01/03: Import python-vertica_0.7.3.orig.tar.gz

Jean Baptiste Favre jbfavre-guest at moszumanska.debian.org
Wed Jun 21 06:29:49 UTC 2017


This is an automated email from the git hooks/post-receive script.

jbfavre-guest pushed a commit to branch master
in repository python-vertica.

commit f5f20028a530f0eb6b52bfc831c4785cdca49e4c
Author: Jean Baptiste Favre <debian at jbfavre.org>
Date:   Wed Jun 21 08:09:48 2017 +0200

    Import python-vertica_0.7.3.orig.tar.gz
---
 setup.py                                   |  2 +-
 vertica_python/__init__.py                 |  2 +-
 vertica_python/tests/test_cursor.py        | 45 +++++++++++++++++++++++
 vertica_python/tests/test_unicode.py       | 26 +++++++++++++
 vertica_python/vertica/cursor.py           | 59 ++++++++++++++++--------------
 vertica_python/vertica/messages/message.py |  1 +
 6 files changed, 106 insertions(+), 29 deletions(-)

diff --git a/setup.py b/setup.py
index 4823d12..1c5b734 100644
--- a/setup.py
+++ b/setup.py
@@ -10,7 +10,7 @@ opts = ReqOpts(None, 'git')
 # version should use the format 'x.x.x' (instead of 'vx.x.x')
 setup(
     name='vertica-python',
-    version='0.7.1',
+    version='0.7.3',
     description='A native Python client for the Vertica database.',
     author='Justin Berka, Alex Kim',
     author_email='justin.berka at gmail.com, alex.kim at uber.com',
diff --git a/vertica_python/__init__.py b/vertica_python/__init__.py
index f90fcd0..57e1df6 100644
--- a/vertica_python/__init__.py
+++ b/vertica_python/__init__.py
@@ -21,7 +21,7 @@ __all__ = ['Connection', 'PROTOCOL_VERSION', 'version_info', 'apilevel', 'thread
            'OperationalError', 'ProgrammingError']
 
 # The version number of this library.
-version_info = (0, 7, 1)
+version_info = (0, 7, 3)
 __version__ = '.'.join(map(str, version_info))
 
 # The protocol version (3.0.0) implemented in this library.
diff --git a/vertica_python/tests/test_cursor.py b/vertica_python/tests/test_cursor.py
index d76c0b8..8d7dd6c 100644
--- a/vertica_python/tests/test_cursor.py
+++ b/vertica_python/tests/test_cursor.py
@@ -351,3 +351,48 @@ class CursorTestCase(VerticaPythonTestCase):
             cur.execute("")
             res = cur.fetchall()
             self.assertListOfListsEqual(res, [])
+
+
+class TestExecutemany(VerticaPythonTestCase):
+    def setUp(self):
+        self._init_table()
+
+    def tearDown(self):
+        # self._init_table()
+        pass
+
+    def _init_table(self):
+        with self._connect() as conn:
+            cur = conn.cursor()
+            # clean old table
+            cur.execute("DROP TABLE IF EXISTS {0}".format(self._table))
+
+            # create test table
+            cur.execute("""CREATE TABLE {0} (
+                                a INT,
+                                b VARCHAR(32)
+                           )
+                        """.format(self._table))
+
+    def _test_executemany(self, table, seq_of_values):
+        with self._connect() as conn:
+            cur = conn.cursor()
+
+            cur.executemany("INSERT INTO {0} (a, b) VALUES (%s, %s)".format(table),
+                            seq_of_values)
+            conn.commit()
+
+            cur.execute("SELECT * FROM {0} ORDER BY a ASC, b ASC".format(table))
+
+            # check first select results
+            res1 = cur.fetchall()
+            seq_of_values_to_compare = sorted([list(values) for values in seq_of_values])
+            self.assertListOfListsEqual(res1, seq_of_values_to_compare)
+            self.assertIsNone(cur.fetchone())
+
+    def test_executemany(self):
+        self._test_executemany(self._table, [(1, 'aa'), (2, 'bb')])
+
+    def test_executemany_quoted_path(self):
+        table = '.'.join(['"{}"'.format(s.strip('"')) for s in self._table.split('.')])
+        self._test_executemany(table, [(1, 'aa'), (2, 'bb')])
diff --git a/vertica_python/tests/test_unicode.py b/vertica_python/tests/test_unicode.py
index 9f01ee1..09cd2ce 100644
--- a/vertica_python/tests/test_unicode.py
+++ b/vertica_python/tests/test_unicode.py
@@ -63,3 +63,29 @@ class UnicodeTestCase(VerticaPythonTestCase):
             res = cur.fetchone()
 
         self.assertResultEqual(value, res[0])
+
+    # unit test for issue #160
+    def test_null_named_parameter_binding(self):
+        key = u'test'
+        value = None
+        query = u"SELECT :{0}".format(key)
+
+        with self._connect() as conn:
+            cur = conn.cursor()
+            cur.execute(query, {key: value})
+            res = cur.fetchone()
+
+        self.assertResultEqual(value, res[0])
+
+    # unit test for issue #160
+    def test_null_list_parameter(self):
+        values = [u'\u00f1', 'foo', None]
+        query = u"SELECT {0}".format(", ".join(["%s"] * len(values)))
+
+        with self._connect() as conn:
+            cur = conn.cursor()
+            cur.execute(query, tuple(values))
+            results = cur.fetchone()
+
+        for val, res in zip(values, results):
+            self.assertResultEqual(val, res)
diff --git a/vertica_python/vertica/cursor.py b/vertica_python/vertica/cursor.py
index e9ae88f..d2b9be3 100644
--- a/vertica_python/vertica/cursor.py
+++ b/vertica_python/vertica/cursor.py
@@ -37,14 +37,19 @@ if six.PY2:
 elif six.PY3:
     file_type = (IOBase,)
 
+NULL = "NULL"
+
+RE_NAME_BASE = u"[a-zA-Z_][\\w\\d\\$_]*"
+RE_NAME = u'(("{0}")|({0}))'.format(RE_NAME_BASE)
+RE_BASIC_INSERT_STAT = (
+    u"INSERT\\s+INTO\\s+(?P<target>({0}\\.)?{0})"
+    u"\\s*\\(\\s*(?P<variables>{0}(\\s*,\\s*{0})*)\\s*\\)"
+    u"\\s+VALUES\\s*\\(\\s*(?P<values>.*)\\s*\\)").format(RE_NAME)
+
 
 class Cursor(object):
     # NOTE: this is used in executemany and is here for pandas compatibility
-    _insert_statement = re.compile(
-        u"INSERT\\s+INTO"
-        "\\s+((?P<schema>{id})\\.)?(?P<table>{id})"
-        "\\s*\\(\\s*(?P<variables>({id}(\\s*,\\s*{id})*)?\\s*)\\)"
-        "\\s+VALUES\\s*\\(\\s*(?P<values>.*)\\)".format(id=u"[a-zA-Z_][\\w\\d\\$_]*"), re.U | re.I)
+    _insert_statement = re.compile(RE_BASIC_INSERT_STAT, re.U | re.I)
 
     def __init__(self, connection, cursor_type=None, unicode_error=None):
         self.connection = connection
@@ -98,7 +103,7 @@ class Cursor(object):
 
         if parameters:
             # TODO: quote = True for backward compatibility. see if should be False.
-            operation = self.format_operation_with_parameters(operation, parameters, quote=True)
+            operation = self.format_operation_with_parameters(operation, parameters)
 
         self.rowcount = -1
 
@@ -130,23 +135,20 @@ class Cursor(object):
 
         m = self._insert_statement.match(operation)
         if m:
-            schema = as_text(m.group('schema'))
-            table = as_text(m.group('table'))
-            variables = as_text(m.group('variables'))
-            values = as_text(m.group('values'))
-            if schema is not None:
-                table = "%s.%s" % (schema, table)
+            target = as_text(m.group('target'))
 
-            variables = ",".join([variable.strip() for variable in variables.split(",")])
+            variables = as_text(m.group('variables'))
+            variables = ",".join([variable.strip().strip('"') for variable in variables.split(",")])
 
-            values = ",".join([value.strip() for value in values.split(",")])
-            seq_of_values = [self.format_operation_with_parameters(values, parameters)
+            values = as_text(m.group('values'))
+            values = ",".join([value.strip().strip('"') for value in values.split(",")])
+            seq_of_values = [self.format_operation_with_parameters(values, parameters, is_csv=True)
                              for parameters in seq_of_parameters]
             data = "\n".join(seq_of_values)
 
             copy_statement = (
-                "COPY {table} ({variables}) FROM STDIN DELIMITER ',' ENCLOSED BY '\"' "
-                "ENFORCELENGTH ABORT ON ERROR").format(table=table, variables=variables)
+                u"COPY {0} ({1}) FROM STDIN DELIMITER ',' ENCLOSED BY '\"' "
+                u"ENFORCELENGTH ABORT ON ERROR").format(target, variables)
 
             self.copy(copy_statement, data)
 
@@ -261,7 +263,7 @@ class Cursor(object):
         
         EXAMPLE:
         >> with open("/tmp/file.csv", "rb") as fs:
-        >>     cursor.copy("COPY table(field1,field2) FROM STDIN DELIMITER ',' ENCLOSED BY '\"'",
+        >>     cursor.copy("COPY table(field1,field2) FROM STDIN DELIMITER ',' ENCLOSED BY ''''",
         >>                 fs, buffer_size=65536)
 
         """
@@ -327,7 +329,7 @@ class Cursor(object):
                 for idx, value in enumerate(row_data.values)]
 
     # noinspection PyArgumentList
-    def format_operation_with_parameters(self, operation, parameters, quote=True):
+    def format_operation_with_parameters(self, operation, parameters, is_csv=False):
         operation = as_text(operation)
 
         if isinstance(parameters, dict):
@@ -337,9 +339,9 @@ class Cursor(object):
                 key = as_text(key)
 
                 if isinstance(param, string_types):
-                    param = as_text(param)
-                    if quote:
-                        param = self.format_quote(param)
+                    param = self.format_quote(as_text(param), is_csv)
+                elif param is None:
+                    param = NULL
                 else:
                     param = str(param)
                 value = as_text(param)
@@ -353,9 +355,9 @@ class Cursor(object):
             tlist = []
             for param in parameters:
                 if isinstance(param, string_types):
-                    param = as_text(param)
-                    if quote:
-                        param = self.format_quote(param)
+                    param = self.format_quote(as_text(param), is_csv)
+                elif param is None:
+                    param = NULL
                 else:
                     param = str(param)
                 value = as_text(param)
@@ -368,6 +370,9 @@ class Cursor(object):
 
         return operation
 
-    def format_quote(self, param):
+    def format_quote(self, param, is_csv):
         # TODO Make sure adapt() behaves properly
-        return QuotedString(param.encode(UTF_8, self.unicode_error)).getquoted()
+        if is_csv:
+            return '"{0}"'.format(re.escape(param))
+        else:
+            return QuotedString(param.encode(UTF_8, self.unicode_error)).getquoted()
diff --git a/vertica_python/vertica/messages/message.py b/vertica_python/vertica/messages/message.py
index a72cdb9..5601c08 100644
--- a/vertica_python/vertica/messages/message.py
+++ b/vertica_python/vertica/messages/message.py
@@ -46,6 +46,7 @@ class BackendMessage(Message):
         if klass is not None:
             return klass(data)
         else:
+            from .backend_messages import Unknown
             return Unknown(type_, data)
 
     @staticmethod

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/python-vertica.git



More information about the Python-modules-commits mailing list