Bug#862734: unblock: python-vertica/0.7.1-1
Package: release.debian.org
Severity: normal
User: release.debian.org@packages.debian.org
Usertags: unblock
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512
Please unblock package python-vertica
Dear release team,
python-vertica version 0.7.1 has just been released.
Amongst other changes, it fixes a bug about named parameters support with python3.
Bug details can be found on [1]
All tests now pass for both python 2.7 & 3.5, though I had to explicitly disable them in the package because they require a running Vertica backend.
[1]: https://github.com/uber/vertica-python/issues/112
diff -Nru python-vertica-0.6.8/debian/changelog python-vertica-0.7.1/debian/changelog
- --- python-vertica-0.6.8/debian/changelog 2016-10-24 11:40:15.000000000 +0200
+++ python-vertica-0.7.1/debian/changelog 2017-05-16 09:33:39.000000000 +0200
@@ -1,3 +1,27 @@
+python-vertica (0.7.1-1) unstable; urgency=medium
+
+ * Import python-vertica_0.7.1.orig.tar.gz
+
+ -- Jean Baptiste Favre <debian@jbfavre.org> Tue, 16 May 2017 09:33:39 +0200
+
+python-vertica (0.6.14-1) unstable; urgency=medium
+
+ * Import python-vertica_0.6.14.orig.tar.gz
+
+ -- Jean Baptiste Favre <debian@jbfavre.org> Sat, 25 Mar 2017 14:47:34 +0100
+
+python-vertica (0.6.13-1) unstable; urgency=medium
+
+ * Import python-vertica_0.6.13.orig.tar.gz
+
+ -- Jean Baptiste Favre <debian@jbfavre.org> Sun, 05 Mar 2017 12:35:21 +0100
+
+python-vertica (0.6.12-1) unstable; urgency=medium
+
+ * Import python-vertica_0.6.12.orig.tar.gz
+
+ -- Jean Baptiste Favre <debian@jbfavre.org> Mon, 13 Feb 2017 09:59:57 +0100
+
python-vertica (0.6.8-2) unstable; urgency=medium
* Update compat version
diff -Nru python-vertica-0.6.8/debian/.git-dpm python-vertica-0.7.1/debian/.git-dpm
- --- python-vertica-0.6.8/debian/.git-dpm 2016-10-24 11:37:58.000000000 +0200
+++ python-vertica-0.7.1/debian/.git-dpm 2017-05-16 09:32:26.000000000 +0200
@@ -1,11 +1,11 @@
# see git-dpm(1) from git-dpm package
- -9058172d19195ba5de74a6ad69d64d07c2629682
- -9058172d19195ba5de74a6ad69d64d07c2629682
- -9058172d19195ba5de74a6ad69d64d07c2629682
- -9058172d19195ba5de74a6ad69d64d07c2629682
- -python-vertica_0.6.8.orig.tar.gz
- -ff6d554fc104801836b55d9291194ff3b8e61c35
- -24542
+8a1cd3b55ec2c7acd314c271aacd37f880bad859
+8a1cd3b55ec2c7acd314c271aacd37f880bad859
+8a1cd3b55ec2c7acd314c271aacd37f880bad859
+8a1cd3b55ec2c7acd314c271aacd37f880bad859
+python-vertica_0.7.1.orig.tar.gz
+9020980df05fe56565f2c67856707d5d1fd0a55c
+28533
debianTag="debian/%e%v"
patchedTag="patched/%e%v"
upstreamTag="upstream/%e%u"
diff -Nru python-vertica-0.6.8/debian/rules python-vertica-0.7.1/debian/rules
- --- python-vertica-0.6.8/debian/rules 2016-10-24 11:37:58.000000000 +0200
+++ python-vertica-0.7.1/debian/rules 2017-05-16 09:33:39.000000000 +0200
@@ -6,3 +6,5 @@
%:
dh $@ --with python2,python3 --buildsystem=pybuild
+
+override_dh_auto_test:
diff -Nru python-vertica-0.6.8/.gitignore python-vertica-0.7.1/.gitignore
- --- python-vertica-0.6.8/.gitignore 2016-10-06 23:30:16.000000000 +0200
+++ python-vertica-0.7.1/.gitignore 2017-05-14 00:40:53.000000000 +0200
@@ -40,7 +40,7 @@
.vagrant
# pycharm
- -/.idea/
+.idea
# default virtual environment
/env/
diff -Nru python-vertica-0.6.8/README.md python-vertica-0.7.1/README.md
- --- python-vertica-0.6.8/README.md 2016-10-06 23:30:16.000000000 +0200
+++ python-vertica-0.7.1/README.md 2017-05-14 00:40:53.000000000 +0200
@@ -24,6 +24,10 @@
pip install --pre pytz
+If you're using pip >= 1.4 and you don't already have python-dateutil installed:
+
+ pip install --pre python-dateutil
+
To install vertica-python with pip:
pip install vertica-python
@@ -40,20 +44,24 @@
## Run unit tests
- -To run the tests, you must have access to a Vertica database. You can
- -spin one up with Vagrant that uses the default credentials using
- -`vagrant up`. If you want to run it against an existing database
- -instead; you can set the environment variables seen in
- -`tests/test_commons.py`.
+To run the tests, you must have access to a Vertica database. Heres one way to go about it:
+
+Download docker kitematic:
+https://kitematic.com/
+
+Spin up a vertica container (i use sumitchawla/vertica)
- -Assuming you have [tox](http://tox.readthedocs.io/) installed, all you
- -have to do is run `tox`. It will run the unit tests using both python 2 and 3.
+Edit the port number in `tests/test_commons.py` to match the container.
- -If you run into an error like:
- -```ERROR: InterpreterNotFound: python3.4```
+Install tox:
+http://tox.readthedocs.io
- -Edit the envlist property of tox.ini to use the version of python you have installed (eg py35)
+Edit `tox.ini` envlist property to list the version(s) of python you have installed
+Run tox:
+```bash
+tox
+```
## Usage
@@ -73,7 +81,9 @@
# default throw error on invalid UTF-8 results
'unicode_error': 'strict',
# SSL is disabled by default
- - 'ssl': False}
+ 'ssl': False,
+ 'connection_timeout': 5
+ # connection timeout is not enabled by default}
# simple connection, with manual close
connection = vertica_python.connect(**conn_info)
diff -Nru python-vertica-0.6.8/setup.py python-vertica-0.7.1/setup.py
- --- python-vertica-0.6.8/setup.py 2016-10-06 23:30:16.000000000 +0200
+++ python-vertica-0.7.1/setup.py 2017-05-14 00:40:53.000000000 +0200
@@ -10,7 +10,7 @@
# version should use the format 'x.x.x' (instead of 'vx.x.x')
setup(
name='vertica-python',
- - version='0.6.8',
+ version='0.7.1',
description='A native Python client for the Vertica database.',
author='Justin Berka, Alex Kim',
author_email='justin.berka@gmail.com, alex.kim@uber.com',
diff -Nru python-vertica-0.6.8/tox.ini python-vertica-0.7.1/tox.ini
- --- python-vertica-0.6.8/tox.ini 2016-10-06 23:30:16.000000000 +0200
+++ python-vertica-0.7.1/tox.ini 2017-05-14 00:40:53.000000000 +0200
@@ -1,7 +1,8 @@
[tox]
- -envlist = py27,py34
+envlist = py27,py34,py35,py36
[testenv]
+passenv = *
commands =
nosetests
deps =
diff -Nru python-vertica-0.6.8/vertica_python/compat.py python-vertica-0.7.1/vertica_python/compat.py
- --- python-vertica-0.6.8/vertica_python/compat.py 1970-01-01 01:00:00.000000000 +0100
+++ python-vertica-0.7.1/vertica_python/compat.py 2017-05-14 00:40:53.000000000 +0200
@@ -0,0 +1,100 @@
+# Copyright 2015 The TensorFlow Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# ==============================================================================
+
+"""Functions for Python 2 vs. 3 compatibility.
+## Conversion routines
+In addition to the functions below, `as_str` converts an object to a `str`.
+@@as_bytes
+@@as_text
+@@as_str_any
+## Types
+The compatibility module also provides the following types:
+* `bytes_or_text_types`
+* `complex_types`
+* `integral_types`
+* `real_types`
+"""
+
+from __future__ import absolute_import
+from __future__ import division
+from __future__ import print_function
+
+import six as _six
+
+
+def as_bytes(bytes_or_text, encoding='utf-8'):
+ """Converts either bytes or unicode to `bytes`, using utf-8 encoding for text.
+ Args:
+ bytes_or_text: A `bytes`, `str`, or `unicode` object.
+ encoding: A string indicating the charset for encoding unicode.
+ Returns:
+ A `bytes` object.
+ Raises:
+ TypeError: If `bytes_or_text` is not a binary or unicode string.
+ """
+ if isinstance(bytes_or_text, _six.text_type):
+ return bytes_or_text.encode(encoding)
+ elif isinstance(bytes_or_text, bytes):
+ return bytes_or_text
+ else:
+ raise TypeError('Expected binary or unicode string, got %r' %
+ (bytes_or_text,))
+
+
+def as_text(bytes_or_text, encoding='utf-8'):
+ """Returns the given argument as a unicode string.
+ Args:
+ bytes_or_text: A `bytes`, `str, or `unicode` object.
+ encoding: A string indicating the charset for decoding unicode.
+ Returns:
+ A `unicode` (Python 2) or `str` (Python 3) object.
+ Raises:
+ TypeError: If `bytes_or_text` is not a binary or unicode string.
+ """
+ if isinstance(bytes_or_text, _six.text_type):
+ return bytes_or_text
+ elif isinstance(bytes_or_text, bytes):
+ return bytes_or_text.decode(encoding)
+ else:
+ raise TypeError('Expected binary or unicode string, got %r' % bytes_or_text)
+
+
+# Convert an object to a `str` in both Python 2 and 3.
+if _six.PY2:
+ as_str = as_bytes
+else:
+ as_str = as_text
+
+
+def as_str_any(value):
+ """Converts to `str` as `str(value)`, but use `as_str` for `bytes`.
+ Args:
+ value: A object that can be converted to `str`.
+ Returns:
+ A `str` object.
+ """
+ if isinstance(value, bytes):
+ return as_str(value)
+ else:
+ return str(value)
+
+
+# Either bytes or text.
+bytes_or_text_types = (bytes, _six.text_type)
+
+_allowed_symbols = [
+ 'as_str',
+ 'bytes_or_text_types',
+]
diff -Nru python-vertica-0.6.8/vertica_python/datatypes.py python-vertica-0.7.1/vertica_python/datatypes.py
- --- python-vertica-0.6.8/vertica_python/datatypes.py 2016-10-06 23:30:16.000000000 +0200
+++ python-vertica-0.7.1/vertica_python/datatypes.py 2017-05-14 00:40:53.000000000 +0200
@@ -1,33 +1,38 @@
+from __future__ import print_function, division, absolute_import
- -
- -from datetime import datetime
- -from datetime import timedelta
+from datetime import date, datetime, time
+# noinspection PyPep8Naming
def Date(year, month, day):
- - return datetime.date(year, month, day)
+ return date(year, month, day)
+# noinspection PyPep8Naming
def Time(hour, minute, second):
- - return datetime.time(hour, minute, second)
+ return time(hour, minute, second)
+# noinspection PyPep8Naming
def Timestamp(year, month, day, hour, minute, second):
- - return datetime.datetime(year, month, day, hour, minute, second)
+ return datetime(year, month, day, hour, minute, second)
+# noinspection PyPep8Naming
def DateFromTicks(ticks):
- - d = datetime(1970, 1, 1) + timedelta(seconds=ticks)
+ d = datetime.utcfromtimestamp(ticks)
return d.date()
+# noinspection PyPep8Naming
def TimeFromTicks(ticks):
- - d = datetime(1970, 1, 1) + timedelta(seconds=ticks)
+ d = datetime.utcfromtimestamp(ticks)
return d.time()
+# noinspection PyPep8Naming
def TimestampFromTicks(ticks):
- - d = datetime(1970, 1, 1) + timedelta(seconds=ticks)
+ d = datetime.utcfromtimestamp(ticks)
return d.time()
@@ -35,9 +40,11 @@
pass
+# noinspection PyPep8Naming
def Binary(string):
return Bytea(string)
+
# vertica doesnt have a binary or row_id type i think
STRING = 9
BINARY = 10000
diff -Nru python-vertica-0.6.8/vertica_python/errors.py python-vertica-0.7.1/vertica_python/errors.py
- --- python-vertica-0.6.8/vertica_python/errors.py 2016-10-06 23:30:16.000000000 +0200
+++ python-vertica-0.7.1/vertica_python/errors.py 2017-05-14 00:40:53.000000000 +0200
@@ -1,15 +1,16 @@
+from __future__ import print_function, division, absolute_import
- -
- -import sys
- -if sys.version_info < (3,):
- - import exceptions
import re
+#############################################
+# dbapi errors
+#############################################
class Error(Exception):
pass
+# noinspection PyShadowingBuiltins
class Warning(Exception):
pass
@@ -75,9 +76,9 @@
def __init__(self, error_response, sql):
self.error_response = error_response
self.sql = sql
- - super(QueryError, self).__init__("{0}, SQL: {1}".format(
- - error_response.error_message(), repr(self.one_line_sql()))
- - )
+ ProgrammingError.__init__(self,
+ "{0}, SQL: {1}".format(error_response.error_message(),
+ repr(self.one_line_sql())))
def one_line_sql(self):
if self.sql:
@@ -137,6 +138,14 @@
pass
+class QueryCanceled(QueryError):
+ pass
+
+
+class ConnectionFailure(QueryError):
+ pass
+
+
QUERY_ERROR_CLASSES = {
b'55V03': LockFailure,
b'53000': InsufficientResources,
@@ -148,5 +157,7 @@
b'22V04': CopyRejected,
b'42501': PermissionDenied,
b'22007': InvalidDatetimeFormat,
- - b'42710': DuplicateObject
+ b'42710': DuplicateObject,
+ b'57014': QueryCanceled,
+ b'08006': ConnectionFailure
}
diff -Nru python-vertica-0.6.8/vertica_python/__init__.py python-vertica-0.7.1/vertica_python/__init__.py
- --- python-vertica-0.6.8/vertica_python/__init__.py 2016-10-06 23:30:16.000000000 +0200
+++ python-vertica-0.7.1/vertica_python/__init__.py 2017-05-14 00:40:53.000000000 +0200
@@ -1,30 +1,32 @@
+from __future__ import print_function, division, absolute_import
+from .vertica.connection import Connection, connect
- -from vertica_python.vertica.connection import Connection
- -
+# Importing exceptions for compatibility with dbapi 2.0.
+# See: PEP 249 - Python Database API 2.0
+# https://www.python.org/dev/peps/pep-0249/#exceptions
+from . import errors
+from .errors import (
+ Error, Warning, DataError, DatabaseError, IntegrityError, InterfaceError,
+ InternalError, NotSupportedError, OperationalError, ProgrammingError)
# Main module for this library.
- -
- -# The version number of this library.
- -version_info = (0, 6, 8)
- -
- -__version__ = '.'.join(map(str, version_info))
- -
__author__ = 'Uber Technologies, Inc'
__copyright__ = 'Copyright 2013, Uber Technologies, Inc.'
__license__ = 'MIT'
+__all__ = ['Connection', 'PROTOCOL_VERSION', 'version_info', 'apilevel', 'threadsafety',
+ 'paramstyle', 'connect', 'Error', 'Warning', 'DataError', 'DatabaseError',
+ 'IntegrityError', 'InterfaceError', 'InternalError', 'NotSupportedError',
+ 'OperationalError', 'ProgrammingError']
+
+# The version number of this library.
+version_info = (0, 7, 1)
+__version__ = '.'.join(map(str, version_info))
+
# The protocol version (3.0.0) implemented in this library.
PROTOCOL_VERSION = 3 << 16
- -
apilevel = 2.0
- -
- -# Threads may share the module, but not connections!
- -threadsafety = 1
+threadsafety = 1 # Threads may share the module, but not connections!
paramstyle = 'named' # WHERE name=:name
- -
- -
- -def connect(**kwargs):
- - """Opens a new connection to a Vertica database."""
- - return Connection(kwargs)
diff -Nru python-vertica-0.6.8/vertica_python/tests/base.py python-vertica-0.7.1/vertica_python/tests/base.py
- --- python-vertica-0.6.8/vertica_python/tests/base.py 1970-01-01 01:00:00.000000000 +0100
+++ python-vertica-0.7.1/vertica_python/tests/base.py 2017-05-14 00:40:53.000000000 +0200
@@ -0,0 +1,103 @@
+from __future__ import print_function, division, absolute_import
+
+import os
+import unittest
+
+from six import string_types
+
+from .. import *
+from ..compat import as_text, as_str, as_bytes
+
+DEFAULT_VP_TEST_HOST = '127.0.0.1'
+DEFAULT_VP_TEST_PORT = 5433
+DEFAULT_VP_TEST_USER = 'dbadmin'
+DEFAULT_VP_TEST_PASSWD = ''
+DEFAULT_VP_TEST_DB = 'docker'
+DEFAULT_VP_TEST_TABLE = 'vertica_python_unit_test'
+
+
+class VerticaPythonTestCase(unittest.TestCase):
+ """Base class for tests that query Vertica."""
+
+ @classmethod
+ def setUpClass(cls):
+ cls._host = os.getenv('VP_TEST_HOST', DEFAULT_VP_TEST_HOST)
+ cls._port = int(os.getenv('VP_TEST_PORT', DEFAULT_VP_TEST_PORT))
+ cls._user = os.getenv('VP_TEST_USER', DEFAULT_VP_TEST_USER)
+ cls._password = os.getenv('VP_TEST_PASSWD', DEFAULT_VP_TEST_PASSWD)
+ cls._database = os.getenv('VP_TEST_DB', DEFAULT_VP_TEST_DB)
+ cls._table = os.getenv('VP_TEST_TABLE', DEFAULT_VP_TEST_TABLE)
+
+ cls._conn_info = {
+ 'host': cls._host,
+ 'port': cls._port,
+ 'database': cls._database,
+ 'user': cls._user,
+ 'password': cls._password,
+ }
+
+ @classmethod
+ def tearDownClass(cls):
+ with cls._connect() as conn:
+ cur = conn.cursor()
+ cur.execute("DROP TABLE IF EXISTS {0}".format(cls._table))
+
+ @classmethod
+ def _connect(cls):
+ """Connects to vertica.
+
+ :return: a connection to vertica.
+ """
+ return connect(**cls._conn_info)
+
+ def _query_and_fetchall(self, query):
+ """Creates a new connection, executes a query and fetches all the results.
+
+ :param query: query to execute
+ :return: all fetched results as returned by cursor.fetchall()
+ """
+ with self._connect() as conn:
+ cur = conn.cursor()
+ cur.execute(query)
+ results = cur.fetchall()
+
+ return results
+
+ def _query_and_fetchone(self, query):
+ """Creates a new connection, executes a query and fetches one result.
+
+ :param query: query to execute
+ :return: the first result fetched by cursor.fetchone()
+ """
+ with self._connect() as conn:
+ cur = conn.cursor()
+ cur.execute(query)
+ result = cur.fetchone()
+
+ return result
+
+ def assertTextEqual(self, first, second, msg=None):
+ first_text = as_text(first)
+ second_text = as_text(second)
+ self.assertEqual(first=first_text, second=second_text, msg=msg)
+
+ def assertStrEqual(self, first, second, msg=None):
+ first_str = as_str(first)
+ second_str = as_str(second)
+ self.assertEqual(first=first_str, second=second_str, msg=msg)
+
+ def assertBytesEqual(self, first, second, msg=None):
+ first_bytes = as_bytes(first)
+ second_bytes = as_bytes(second)
+ self.assertEqual(first=first_bytes, second=second_bytes, msg=msg)
+
+ def assertResultEqual(self, value, result, msg=None):
+ if isinstance(value, string_types):
+ self.assertTextEqual(first=value, second=result, msg=msg)
+ else:
+ self.assertEqual(first=value, second=result, msg=msg)
+
+ def assertListOfListsEqual(self, list1, list2, msg=None):
+ self.assertEqual(len(list1), len(list2), msg=msg)
+ for l1, l2 in zip(list1, list2):
+ self.assertListEqual(l1, l2, msg=msg)
diff -Nru python-vertica-0.6.8/vertica_python/tests/basic_tests.py python-vertica-0.7.1/vertica_python/tests/basic_tests.py
- --- python-vertica-0.6.8/vertica_python/tests/basic_tests.py 2016-10-06 23:30:16.000000000 +0200
+++ python-vertica-0.7.1/vertica_python/tests/basic_tests.py 1970-01-01 01:00:00.000000000 +0100
@@ -1,411 +0,0 @@
- -import unittest
- -import logging
- -import tempfile
- -
- -from .test_commons import conn_info
- -
- -import vertica_python
- -from vertica_python import errors
- -
- -logger = logging.getLogger('vertica')
- -
- -
- -def init_table(cur):
- - # clean old table
- - cur.execute('DROP TABLE IF EXISTS vertica_python_unit_test;')
- -
- - # create test table
- - cur.execute("""CREATE TABLE vertica_python_unit_test (
- - a int,
- - b varchar(32)
- - ) ;
- - """)
- -
- -
- -class TestVerticaPython(unittest.TestCase):
- -
- - def test_inline_commit(self):
- -
- - conn = vertica_python.connect(**conn_info)
- - cur = conn.cursor()
- - init_table(cur)
- -
- - cur.execute(""" INSERT INTO vertica_python_unit_test (a, b) VALUES (1, 'aa'); commit; """)
- - cur.execute("SELECT a, b from vertica_python_unit_test WHERE a = 1")
- -
- - # unknown rowcount
- - assert cur.rowcount == -1
- -
- - res = cur.fetchall()
- - assert 1 == len(res)
- - assert 1 == res[0][0]
- - assert 'aa' == res[0][1]
- - assert cur.rowcount == 1
- -
- - def test_multi_inserts_and_transaction(self):
- -
- - conn = vertica_python.connect(**conn_info)
- - cur = conn.cursor()
- - init_table(cur)
- -
- - conn2 = vertica_python.connect(**conn_info)
- - cur2 = conn2.cursor()
- -
- - # insert data without a commit
- - cur.execute(""" INSERT INTO vertica_python_unit_test (a, b) VALUES (2, 'bb') """)
- -
- - # verify we can see it from this cursor
- - cur.execute("SELECT a, b from vertica_python_unit_test WHERE a = 2")
- - res = cur.fetchall()
- - assert 1 == len(res)
- - assert 2 == res[0][0]
- - assert 'bb' == res[0][1]
- -
- - # verify we cant see it from other cursor
- - cur2.execute("SELECT a, b from vertica_python_unit_test WHERE a = 2")
- - res = cur2.fetchall()
- - assert 0 == len(res)
- -
- - # insert more data then commit
- - cur.execute(""" INSERT INTO vertica_python_unit_test (a, b) VALUES (3, 'cc') """)
- - cur.execute(""" commit; """)
- -
- - # verify we can see it from this cursor
- - cur.execute("SELECT a, b from vertica_python_unit_test WHERE a = 2 or a = 3")
- - res = cur.fetchall()
- - assert 2 == len(res)
- -
- - # verify we can see it from other cursor
- - cur2.execute("SELECT a, b from vertica_python_unit_test WHERE a = 2 or a = 3")
- - res = cur2.fetchall()
- - assert 2 == len(res)
- -
- - def test_conn_commit(self):
- -
- - conn = vertica_python.connect(**conn_info)
- - cur = conn.cursor()
- - init_table(cur)
- -
- - cur.execute(""" INSERT INTO vertica_python_unit_test (a, b) VALUES (5, 'cc') """)
- - conn.commit()
- - cur.execute("SELECT a, b from vertica_python_unit_test WHERE a = 5")
- - res = cur.fetchall()
- - assert 1 == len(res)
- -
- -
- - def test_delete(self):
- -
- - conn = vertica_python.connect(**conn_info)
- - cur = conn.cursor()
- - init_table(cur)
- -
- - cur.execute(""" INSERT INTO vertica_python_unit_test (a, b) VALUES (5, 'cc') """)
- - conn.commit()
- -
- - cur.execute(""" DELETE from vertica_python_unit_test WHERE a = 5 """)
- -
- - # validate delete count
- - assert cur.rowcount == -1
- - res = cur.fetchone()
- - assert 1 == len(res)
- - assert 1 == res[0]
- -
- - conn.commit()
- -
- - cur.execute("SELECT a, b from vertica_python_unit_test WHERE a = 5")
- - res = cur.fetchall()
- - assert 0 == len(res)
- -
- -
- - def test_update(self):
- -
- - conn = vertica_python.connect(**conn_info)
- - cur = conn.cursor()
- - init_table(cur)
- -
- - cur.execute(""" INSERT INTO vertica_python_unit_test (a, b) VALUES (5, 'cc') """)
- -
- - # validate insert count
- - res = cur.fetchone()
- - assert 1 == len(res)
- - assert 1 == res[0]
- -
- - conn.commit()
- -
- - cur.execute(""" UPDATE vertica_python_unit_test SET b = 'ff' WHERE a = 5 """)
- -
- - # validate update count
- - assert cur.rowcount == -1
- - res = cur.fetchone()
- - assert 1 == len(res)
- - assert 1 == res[0]
- -
- - conn.commit()
- -
- - cur.execute("SELECT a, b from vertica_python_unit_test WHERE a = 5")
- - res = cur.fetchall()
- - assert 1 == len(res)
- - assert 5 == res[0][0]
- - assert 'ff' == res[0][1]
- -
- - def test_copy_with_string(self):
- -
- - conn = vertica_python.connect(**conn_info)
- - cur = conn.cursor()
- - init_table(cur)
- -
- - conn2 = vertica_python.connect(**conn_info)
- - cur2 = conn.cursor()
- -
- - cur.copy(""" COPY vertica_python_unit_test (a, b) from stdin DELIMITER ',' """, "1,foo\n2,bar")
- - # no commit necessary for copy
- -
- - # verify this cursor can see copy data
- - cur.execute("SELECT a, b from vertica_python_unit_test WHERE a = 1")
- - res = cur.fetchall()
- - assert 1 == len(res)
- - assert 1 == res[0][0]
- - assert 'foo' == res[0][1]
- -
- - # verify other cursor can see copy data
- - cur2.execute("SELECT a, b from vertica_python_unit_test WHERE a = 2")
- - res = cur2.fetchall()
- - assert 1 == len(res)
- - assert 2 == res[0][0]
- - assert 'bar' == res[0][1]
- -
- - def test_copy_with_file(self):
- -
- - conn = vertica_python.connect(**conn_info)
- - cur = conn.cursor()
- - init_table(cur)
- -
- - conn2 = vertica_python.connect(**conn_info)
- - cur2 = conn.cursor()
- -
- - f = tempfile.TemporaryFile()
- - f.write(b"1,foo\n2,bar")
- - # move rw pointer to top of file
- - f.seek(0)
- - cur.copy(""" COPY vertica_python_unit_test (a, b) from stdin DELIMITER ',' """, f)
- - f.close()
- -
- - # verify this cursor can see copy data
- - cur.execute("SELECT a, b from vertica_python_unit_test WHERE a = 1")
- - res = cur.fetchall()
- - assert 1 == len(res)
- - assert 1 == res[0][0]
- - assert 'foo' == res[0][1]
- -
- - # verify other cursor can see copy data
- - cur2.execute("SELECT a, b from vertica_python_unit_test WHERE a = 2")
- - res = cur2.fetchall()
- - assert 1 == len(res)
- - assert 2 == res[0][0]
- - assert 'bar' == res[0][1]
- -
- - def test_with_conn(self):
- -
- - with vertica_python.connect(**conn_info) as conn:
- - cur = conn.cursor()
- - init_table(cur)
- -
- - cur.execute(""" INSERT INTO vertica_python_unit_test (a, b) VALUES (1, 'aa'); commit; """)
- - cur.execute("SELECT a, b from vertica_python_unit_test WHERE a = 1")
- - res = cur.fetchall()
- - assert 1 == len(res)
- -
- - def test_iterator(self):
- -
- - with vertica_python.connect(**conn_info) as conn:
- - cur = conn.cursor()
- - init_table(cur)
- -
- - cur.execute(""" INSERT INTO vertica_python_unit_test (a, b) VALUES (1, 'aa') """)
- - cur.execute(""" INSERT INTO vertica_python_unit_test (a, b) VALUES (2, 'bb') """)
- - cur.execute(""" INSERT INTO vertica_python_unit_test (a, b) VALUES (3, 'cc') """)
- - conn.commit()
- -
- - cur.execute("SELECT a, b from vertica_python_unit_test ORDER BY a ASC")
- -
- - i = 0;
- - for row in cur.iterate():
- - if i == 0:
- - assert 1 == row[0]
- - assert 'aa' == row[1]
- - if i == 1:
- - assert 2 == row[0]
- - assert 'bb' == row[1]
- - if i == 2:
- - assert 3 == row[0]
- - assert 'cc' == row[1]
- - i = i + 1
- -
- -
- - def test_mid_iterator_execution(self):
- -
- - with vertica_python.connect(**conn_info) as conn:
- - cur = conn.cursor()
- - init_table(cur)
- -
- - cur.execute(""" INSERT INTO vertica_python_unit_test (a, b) VALUES (1, 'aa') """)
- - cur.execute(""" INSERT INTO vertica_python_unit_test (a, b) VALUES (2, 'bb') """)
- - cur.execute(""" INSERT INTO vertica_python_unit_test (a, b) VALUES (3, 'cc') """)
- - conn.commit()
- -
- - cur.execute("SELECT a, b from vertica_python_unit_test ORDER BY a ASC")
- -
- - # don't finish iterating
- - for row in cur.iterate():
- - break;
- -
- - # make new query and verify result
- - cur.execute(""" SELECT COUNT(*) FROM vertica_python_unit_test """)
- - res = cur.fetchall()
- - assert 1 == len(res)
- - assert 3 == res[0][0]
- -
- -
- - def test_query_errors(self):
- - conn = vertica_python.connect(**conn_info)
- - cur = conn.cursor()
- - init_table(cur)
- -
- - failed = False;
- - # create table syntax error
- - try:
- - failed = False;
- - cur.execute("""CREATE TABLE vertica_python_unit_test_fail (
- - a int,
- - b varchar(32),,,
- - ) ;
- - """)
- - except errors.VerticaSyntaxError:
- - failed = True;
- - assert True == failed
- -
- - # select table not found error
- - try:
- - failed = False;
- - cur.execute(""" INSERT INTO vertica_python_unit_test (a, b) VALUES (1, 'aa') """)
- - cur.execute(""" SELECT * from vertica_python_unit_test_fail """)
- - #conn.commit()
- - except errors.QueryError:
- - failed = True;
- - assert True == failed
- -
- - # verify cursor still useable after errors
- - cur.execute("SELECT a, b from vertica_python_unit_test WHERE a = 1")
- - res = cur.fetchall()
- - assert 1 == len(res)
- - assert 1 == res[0][0]
- - assert 'aa' == res[0][1]
- -
- - def test_cursor_close_and_reuse(self):
- -
- - conn = vertica_python.connect(**conn_info)
- - cur = conn.cursor()
- - init_table(cur)
- -
- - # insert data
- - cur.execute(""" INSERT INTO vertica_python_unit_test (a, b) VALUES (2, 'bb'); commit; """)
- - #conn.commit()
- -
- - # query
- - cur.execute("SELECT a, b from vertica_python_unit_test WHERE a = 2")
- - res = cur.fetchall()
- - assert 1 == len(res)
- -
- - # close and reopen cursor
- - cur.close()
- - cur = conn.cursor()
- -
- - cur.execute("SELECT a, b from vertica_python_unit_test")
- - res = cur.fetchall()
- - assert 1 == len(res)
- -
- - # unit test for #78
- - def test_copy_with_data_in_buffer(self):
- -
- - conn = vertica_python.connect(**conn_info)
- - cur = conn.cursor()
- - init_table(cur)
- -
- - cur.execute("select 1;")
- - cur.fetchall()
- -
- - # Current status: CommandComplete
- -
- - copy_sql = """COPY vertica_python_unit_test (a, b)
- - FROM STDIN
- - DELIMITER '|'
- - NULL AS 'None'"""
- -
- - data = """1|name1
- - 2|name2"""
- -
- - cur.copy(copy_sql, data)
- - cur.execute("select 1;") # will raise QueryError here
- -
- - conn.close()
- -
- - # unit test for #74
- - def test_nextset(self):
- -
- - conn = vertica_python.connect(**conn_info)
- - cur = conn.cursor()
- - init_table(cur)
- -
- - cur.execute("select 1; select 2;")
- - res = cur.fetchall()
- -
- - assert 1 == len(res)
- - assert 1 == res[0][0]
- - assert cur.fetchone() is None
- -
- - assert cur.nextset() == True
- -
- - res = cur.fetchall()
- - assert 1 == len(res)
- - assert 2 == res[0][0]
- - assert cur.fetchone() is None
- -
- - assert cur.nextset() is None
- -
- - # unit test for #74
- - def test_nextset_with_delete(self):
- -
- - conn = vertica_python.connect(**conn_info)
- - cur = conn.cursor()
- - init_table(cur)
- -
- - # insert data
- - cur.execute(""" INSERT INTO vertica_python_unit_test (a, b) VALUES (1, 'aa') """)
- - cur.execute(""" INSERT INTO vertica_python_unit_test (a, b) VALUES (2, 'bb') """)
- - conn.commit()
- -
- - cur.execute("""select * from vertica_python_unit_test;
- - delete from vertica_python_unit_test;
- - select * from vertica_python_unit_test;
- - """)
- -
- - # check first select results
- - res = cur.fetchall()
- - assert 2 == len(res)
- - assert cur.fetchone() is None
- -
- - # check delete results
- - assert cur.nextset() == True
- - res = cur.fetchall()
- - assert 1 == len(res)
- - assert 2 == res[0][0]
- - assert cur.fetchone() is None
- -
- - # check second select results
- - assert cur.nextset() == True
- - res = cur.fetchall()
- - assert 0 == len(res)
- - assert cur.fetchone() is None
- -
- - # no more data sets
- - assert cur.nextset() is None
diff -Nru python-vertica-0.6.8/vertica_python/tests/column_tests.py python-vertica-0.7.1/vertica_python/tests/column_tests.py
- --- python-vertica-0.6.8/vertica_python/tests/column_tests.py 2016-10-06 23:30:16.000000000 +0200
+++ python-vertica-0.7.1/vertica_python/tests/column_tests.py 1970-01-01 01:00:00.000000000 +0100
@@ -1,21 +0,0 @@
- -from .test_commons import conn_info, VerticaTestCase
- -from .. import connect
- -
- -
- -class ColumnTestCase(VerticaTestCase):
- - def test_column_names_query(self):
- - column_0 = 'isocode'
- - column_1 = 'name'
- - query = """
- - select 'US' as {column_0}, 'United States' as {column_1}
- - union all
- - select 'CA', 'Canada'
- - union all
- - select 'MX', 'Mexico'
- - """.format(column_0=column_0, column_1=column_1)
- - with connect(**conn_info) as conn:
- - cur = conn.cursor()
- - cur.execute(query)
- - description = cur.description
- - assert description[0].name == column_0
- - assert description[1].name == column_1
diff -Nru python-vertica-0.6.8/vertica_python/tests/date_tests.py python-vertica-0.7.1/vertica_python/tests/date_tests.py
- --- python-vertica-0.6.8/vertica_python/tests/date_tests.py 2016-10-06 23:30:16.000000000 +0200
+++ python-vertica-0.7.1/vertica_python/tests/date_tests.py 1970-01-01 01:00:00.000000000 +0100
@@ -1,73 +0,0 @@
- -from datetime import date, datetime
- -from .test_commons import *
- -from vertica_python import errors
- -from vertica_python.vertica.column import timestamp_parse
- -
- -
- -class DateParsingTestCase(VerticaTestCase):
- - """
- - Testing DATE type parsing with focus on AD/BC and lack of support for dates Before Christ.
- -
- - Note: the 'BC' or 'AD' era indicators in Vertica's date format seem to make Vertica behave as follows:
- -
- - - Both 'BC' and 'AD' are simply a flags that tell Vertica: include era indicator if the date is Before
- - Christ
- - - Dates in AD will never include era indicator
- - """
- - def _query_to_date(self, expression, pattern):
- - return self.query_and_fetchall("SELECT TO_DATE('%(expression)s', '%(pattern)s')" % locals())
- -
- - def _assert_date(self, expression, pattern, expected):
- - res = self._query_to_date(expression, pattern)
- -
- - if len(res) == 0:
- - self.fail("Expected that query '%(query)s' would return one row with one column. Got nothing." % locals())
- -
- - elif len(res[0]) == 0:
- - self.fail("Expected that query '%(query)s' would return one row and one column. Got one row and no column."
- - % locals())
- -
- - self.assertEqual(expected, res[0][0], "Expected date '%s' but got: '%s'" % (str(expected), str(res[0][0])))
- -
- - def test_after_christ(self):
- - self._assert_date('2000-01-01 AD', 'YYYY-MM-DD BC', date(2000, 1, 1))
- - self._assert_date('2000-01-01 AD', 'YYYY-MM-DD AD', date(2000, 1, 1))
- - self._assert_date('2000-01-01', 'YYYY-MM-DD', date(2000, 1, 1))
- -
- - def test_before_christ_bc_indicator(self):
- - try:
- - res = self._query_to_date('2000-01-01 BC', 'YYYY-MM-DD BC')
- -
- - self.fail("Expected to see NotSupportedError when Before Christ date is encountered. Got: " + str(res))
- - except errors.NotSupportedError:
- - pass
- -
- - def test_before_christ_ad_indicator(self):
- - try:
- - res = self._query_to_date('2000-01-01 BC', 'YYYY-MM-DD AD')
- -
- - self.fail("Expected to see NotSupportedError when Before Christ date is encountered. Got: " + str(res))
- - except errors.NotSupportedError:
- - pass
- -
- -
- -class TimestampParsingTestCase(VerticaTestCase):
- - """Verify timestamp parsing works properly."""
- -
- - def test_timestamp_parser(self):
- - test_timestamp = '1841-05-05 22:07:58'.encode(encoding='utf-8', errors='strict')
- - parsed_timestamp = timestamp_parse(test_timestamp)
- - # Assert parser default to strptime
- - self.assertEqual(datetime(year=1841, month=5, day=5, hour=22, minute=7, second=58), parsed_timestamp)
- -
- - def test_timestamp_with_year_over_9999(self):
- - test_timestamp = '44841-05-05 22:07:58'.encode(encoding='utf-8', errors='strict')
- - parsed_timestamp = timestamp_parse(test_timestamp)
- - # Assert year was truncated properly
- - self.assertEqual(datetime(year=9999, month=5, day=5, hour=22, minute=7, second=58), parsed_timestamp)
- -
- - def test_timestamp_with_year_over_9999_and_ms(self):
- - test_timestamp = '124841-05-05 22:07:58.000003'.encode(encoding='utf-8', errors='strict')
- - parsed_timestamp = timestamp_parse(test_timestamp)
- - # Assert year was truncated properly
- - self.assertEqual(datetime(year=9999, month=5, day=5, hour=22, minute=7, second=58, microsecond=3), parsed_timestamp)
diff -Nru python-vertica-0.6.8/vertica_python/tests/error_tests.py python-vertica-0.7.1/vertica_python/tests/error_tests.py
- --- python-vertica-0.6.8/vertica_python/tests/error_tests.py 2016-10-06 23:30:16.000000000 +0200
+++ python-vertica-0.7.1/vertica_python/tests/error_tests.py 1970-01-01 01:00:00.000000000 +0100
@@ -1,62 +0,0 @@
- -from .test_commons import conn_info, VerticaTestCase
- -from .. import connect
- -from .. import errors
- -
- -
- -class ErrorTestCase(VerticaTestCase):
- -
- - def test_missing_schema(self):
- -
- - query = "SELECT 1 FROM missing_schema.table"
- -
- - with connect(**conn_info) as conn:
- - cur = conn.cursor()
- -
- - failed = False
- -
- - try:
- - cur.execute(query)
- - except errors.MissingSchema:
- - failed = True
- -
- - assert failed is True
- -
- - def test_missing_relation(self):
- -
- - query = "SELECT 1 FROM missing_table"
- -
- - with connect(**conn_info) as conn:
- - cur = conn.cursor()
- -
- - failed = False
- -
- - try:
- - cur.execute(query)
- - except errors.MissingRelation:
- - failed = True
- -
- - assert failed is True
- -
- - def test_duplicate_object(self):
- -
- - create = "CREATE TABLE test_table (a BOOLEAN)"
- - drop = "DROP TABLE test_table"
- -
- - with connect(**conn_info) as conn:
- - cur = conn.cursor()
- -
- - failed = False
- -
- - cur.execute(create)
- -
- - try:
- - cur.execute(create)
- - except errors.DuplicateObject:
- - failed = True
- - finally:
- - try:
- - cur.execute(drop)
- - except errors.MissingRelation:
- - pass
- -
- - assert failed is True
diff -Nru python-vertica-0.6.8/vertica_python/tests/test_column.py python-vertica-0.7.1/vertica_python/tests/test_column.py
- --- python-vertica-0.6.8/vertica_python/tests/test_column.py 1970-01-01 01:00:00.000000000 +0100
+++ python-vertica-0.7.1/vertica_python/tests/test_column.py 2017-05-14 00:40:53.000000000 +0200
@@ -0,0 +1,18 @@
+from __future__ import print_function, division, absolute_import
+
+from .base import VerticaPythonTestCase
+
+
+class ColumnTestCase(VerticaPythonTestCase):
+ def test_column_names_query(self):
+ columns = ['isocode', 'name']
+
+ with self._connect() as conn:
+ cur = conn.cursor()
+ cur.execute("""
+ SELECT 'US' AS {0}, 'United States' AS {1}
+ UNION ALL SELECT 'CA', 'Canada'
+ UNION ALL SELECT 'MX', 'Mexico' """.format(*columns))
+ description = cur.description
+
+ self.assertListEqual([d.name for d in description], columns)
diff -Nru python-vertica-0.6.8/vertica_python/tests/test_commons.py python-vertica-0.7.1/vertica_python/tests/test_commons.py
- --- python-vertica-0.6.8/vertica_python/tests/test_commons.py 2016-10-06 23:30:16.000000000 +0200
+++ python-vertica-0.7.1/vertica_python/tests/test_commons.py 1970-01-01 01:00:00.000000000 +0100
@@ -1,28 +0,0 @@
- -import unittest
- -import os
- -import vertica_python
- -
- -conn_info = {'host': os.getenv('VP_TEST_HOST', '127.0.0.1'),
- - 'port': int(os.getenv('VP_TEST_PORT', 5433)),
- - 'user': os.getenv('VP_TEST_USER', 'dbadmin'),
- - 'password': os.getenv('VP_TEST_PASSWD', ''),
- - 'database': os.getenv('VP_TEST_DB', 'docker')}
- -
- -
- -class VerticaTestCase(unittest.TestCase):
- - """
- - Base class for tests that query Vertica.
- -
- - Implements a couple of functions for mindless repetetive tasks.
- - """
- - def query_and_fetchall(self, query):
- - """
- - Creates new connection to vertica, executes query, returns all fetched results. Closes connection.
- - :param query: query to execute
- - :return: all fetched results as returned by cursor.fetchall()
- - """
- - with vertica_python.connect(**conn_info) as conn:
- - cur = conn.cursor()
- - cur.execute(query)
- -
- - return cur.fetchall()
diff -Nru python-vertica-0.6.8/vertica_python/tests/test_cursor.py python-vertica-0.7.1/vertica_python/tests/test_cursor.py
- --- python-vertica-0.6.8/vertica_python/tests/test_cursor.py 1970-01-01 01:00:00.000000000 +0100
+++ python-vertica-0.7.1/vertica_python/tests/test_cursor.py 2017-05-14 00:40:53.000000000 +0200
@@ -0,0 +1,353 @@
+from __future__ import print_function, division, absolute_import
+
+import logging
+import tempfile
+
+from .base import VerticaPythonTestCase
+from .. import errors
+
+logger = logging.getLogger('vertica')
+
+
+class CursorTestCase(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_inline_commit(self):
+ with self._connect() as conn:
+ cur = conn.cursor()
+ cur.execute(
+ "INSERT INTO {0} (a, b) VALUES (1, 'aa'); COMMIT;".format(self._table))
+ cur.execute("SELECT a, b FROM {0} WHERE a = 1".format(self._table))
+
+ # unknown rowcount
+ self.assertEqual(cur.rowcount, -1)
+
+ res = cur.fetchall()
+ self.assertEqual(cur.rowcount, 1)
+
+ self.assertListOfListsEqual(res, [[1, 'aa']])
+
+ def test_multi_inserts_and_transaction(self):
+ with self._connect() as conn1, self._connect() as conn2:
+ cur1 = conn1.cursor()
+ cur2 = conn2.cursor()
+
+ # insert data without a commit
+ cur1.execute("INSERT INTO {0} (a, b) VALUES (2, 'bb')".format(self._table))
+
+ # verify we can see it from this cursor
+ cur1.execute("SELECT a, b FROM {0} WHERE a = 2".format(self._table))
+ res_from_cur_1_before_commit = cur1.fetchall()
+ self.assertListOfListsEqual(res_from_cur_1_before_commit, [[2, 'bb']])
+
+ # verify we cant see it from other cursor
+ cur2.execute("SELECT a, b FROM {0} WHERE a = 2".format(self._table))
+
+ res_from_cur2_before_commit = cur2.fetchall()
+ self.assertListOfListsEqual(res_from_cur2_before_commit, [])
+
+ # insert more data then commit
+ cur1.execute("INSERT INTO {0} (a, b) VALUES (3, 'cc')".format(self._table))
+ cur1.execute("COMMIT")
+
+ # verify we can see it from this cursor
+ cur1.execute(
+ "SELECT a, b FROM {0} WHERE a = 2 OR a = 3 ORDER BY a".format(self._table))
+ res_from_cur1_after_commit = cur1.fetchall()
+ self.assertListOfListsEqual(res_from_cur1_after_commit, [[2, 'bb'], [3, 'cc']])
+
+ # verify we can see it from other cursor
+ cur2.execute(
+ "SELECT a, b FROM {0} WHERE a = 2 OR a = 3 ORDER BY a".format(self._table))
+ res_from_cur2_after_commit = cur2.fetchall()
+ self.assertListOfListsEqual(res_from_cur2_after_commit, [[2, 'bb'], [3, 'cc']])
+
+ def test_conn_commit(self):
+ with self._connect() as conn:
+ cur = conn.cursor()
+ cur.execute("INSERT INTO {0} (a, b) VALUES (5, 'cc')".format(self._table))
+ conn.commit()
+
+ with self._connect() as conn:
+ cur = conn.cursor()
+ cur.execute("SELECT a, b FROM {0} WHERE a = 5".format(self._table))
+ res = cur.fetchall()
+
+ self.assertListOfListsEqual(res, [[5, 'cc']])
+
+ def test_delete(self):
+ with self._connect() as conn:
+ cur = conn.cursor()
+
+ cur.execute("INSERT INTO {0} (a, b) VALUES (5, 'cc')".format(self._table))
+ self.assertEqual(cur.rowcount, -1)
+ update_res = cur.fetchall()
+ self.assertListOfListsEqual(update_res, [[1]])
+ conn.commit()
+
+ # validate delete count
+ cur.execute("DELETE FROM {0} WHERE a = 5".format(self._table))
+ self.assertEqual(cur.rowcount, -1)
+ delete_res = cur.fetchall()
+ self.assertListOfListsEqual(delete_res, [[1]])
+ conn.commit()
+
+ # validate deleted
+ cur.execute("SELECT a, b FROM {0} WHERE a = 5".format(self._table))
+ res = cur.fetchall()
+ self.assertListOfListsEqual(res, [])
+
+ def test_update(self):
+ with self._connect() as conn:
+ cur = conn.cursor()
+
+ cur.execute("INSERT INTO {0} (a, b) VALUES (5, 'cc')".format(self._table))
+ # validate insert count
+ insert_res = cur.fetchall()
+ self.assertListOfListsEqual(insert_res, [[1]], msg='Bad INSERT response')
+ conn.commit()
+
+ cur.execute("UPDATE {0} SET b = 'ff' WHERE a = 5".format(self._table))
+ # validate update count
+ assert cur.rowcount == -1
+ update_res = cur.fetchall()
+ self.assertListOfListsEqual(update_res, [[1]], msg='Bad UPDATE response')
+ conn.commit()
+
+ cur.execute("SELECT a, b FROM {0} WHERE a = 5".format(self._table))
+ res = cur.fetchall()
+ self.assertListOfListsEqual(res, [[5, 'ff']])
+
+ def test_copy_with_string(self):
+ with self._connect() as conn1, self._connect() as conn2:
+ cur1 = conn1.cursor()
+ cur2 = conn2.cursor()
+
+ cur1.copy("COPY {0} (a, b) FROM STDIN DELIMITER ','".format(self._table),
+ "1,foo\n2,bar")
+ # no commit necessary for copy
+ cur1.execute("SELECT a, b FROM {0} WHERE a = 1".format(self._table))
+ res_from_cur1 = cur1.fetchall()
+ self.assertListOfListsEqual(res_from_cur1, [[1, 'foo']])
+
+ cur2.execute("SELECT a, b FROM {0} WHERE a = 2".format(self._table))
+ res_from_cur2 = cur2.fetchall()
+ self.assertListOfListsEqual(res_from_cur2, [[2, 'bar']])
+
+ def test_copy_with_file(self):
+ f = tempfile.TemporaryFile()
+ f.write(b"1,foo\n2,bar")
+ # move rw pointer to top of file
+ f.seek(0)
+
+ with self._connect() as conn1, self._connect() as conn2:
+ cur1 = conn1.cursor()
+ cur2 = conn2.cursor()
+
+ cur1.copy("COPY {0} (a, b) FROM STDIN DELIMITER ','".format(self._table),
+ f)
+ # no commit necessary for copy
+ cur1.execute("SELECT a, b FROM {0} WHERE a = 1".format(self._table))
+ res_from_cur1 = cur1.fetchall()
+ self.assertListOfListsEqual(res_from_cur1, [[1, 'foo']])
+
+ cur2.execute("SELECT a, b FROM {0} WHERE a = 2".format(self._table))
+ res_from_cur2 = cur2.fetchall()
+ self.assertListOfListsEqual(res_from_cur2, [[2, 'bar']])
+
+ # unit test for #78
+ def test_copy_with_data_in_buffer(self):
+ with self._connect() as conn:
+ cur = conn.cursor()
+
+ cur.execute("SELECT 1;")
+ res = cur.fetchall()
+ self.assertListOfListsEqual(res, [[1]])
+
+ cur.copy("COPY {0} (a, b) FROM STDIN DELIMITER ','".format(self._table),
+ "1,foo\n2,bar")
+
+ cur.execute("SELECT 1;")
+ res = cur.fetchall()
+ self.assertListOfListsEqual(res, [[1]])
+
+ def test_with_conn(self):
+ with self._connect() as conn:
+ cur = conn.cursor()
+
+ cur.execute("INSERT INTO {0} (a, b) VALUES (1, 'aa'); COMMIT;".format(self._table))
+ cur.execute("SELECT a, b FROM {0} WHERE a = 1".format(self._table))
+ res = cur.fetchall()
+ self.assertListOfListsEqual(res, [[1, 'aa']])
+
+ def test_iterator(self):
+ with self._connect() as conn:
+ cur = conn.cursor()
+ values = [[1, 'aa'], [2, 'bb'], [3, 'cc']]
+
+ for n, s in values:
+ cur.execute("INSERT INTO {0} (a, b) VALUES (:n, :s)".format(self._table),
+ {'n': n, 's': s})
+ conn.commit()
+
+ cur.execute("SELECT a, b FROM {0} ORDER BY a ASC".format(self._table))
+
+ for val, res in zip(sorted(values), cur.iterate()):
+ self.assertListEqual(res, val)
+
+ remaining = cur.fetchall()
+ self.assertListOfListsEqual(remaining, [])
+
+ def test_mid_iterator_execution(self):
+ with self._connect() as conn:
+ cur = conn.cursor()
+ values = [[1, 'aa'], [2, 'bb'], [3, 'cc']]
+
+ for n, s in values:
+ cur.execute("INSERT INTO {0} (a, b) VALUES (:n, :s)".format(self._table),
+ {'n': n, 's': s})
+ conn.commit()
+
+ cur.execute("SELECT a, b FROM {0} ORDER BY a ASC".format(self._table))
+
+ for val, res in zip(sorted(values), cur.iterate()):
+ self.assertListEqual(res, val)
+ break # stop after one comparison
+
+ # make new query and verify result
+ cur.execute("SELECT COUNT(*) FROM {0}".format(self._table))
+ res = cur.fetchall()
+ self.assertListOfListsEqual(res, [[3]])
+
+ def test_query_errors(self):
+ with self._connect() as conn:
+ cur = conn.cursor()
+
+ # create table syntax error
+ with self.assertRaises(errors.VerticaSyntaxError):
+ cur.execute("""CREATE TABLE {0}_fail (
+ a INT,
+ b VARCHAR(32),,,
+ );
+ """.format(self._table))
+
+ # select table not found error
+ cur.execute("INSERT INTO {0} (a, b) VALUES (1, 'aa'); COMMIT;".format(self._table))
+ with self.assertRaises(errors.QueryError):
+ cur.execute("SELECT * FROM {0}_fail".format(self._table))
+
+ # verify cursor still usable after errors
+ cur.execute("SELECT a, b FROM {0} WHERE a = 1".format(self._table))
+ res = cur.fetchall()
+ self.assertListOfListsEqual(res, [[1, 'aa']])
+
+ def test_cursor_close_and_reuse(self):
+ with self._connect() as conn:
+ cur = conn.cursor()
+
+ # insert data
+ cur.execute("INSERT INTO {0} (a, b) VALUES (2, 'bb'); COMMIT;".format(self._table))
+
+ # (query -> close -> reopen) * 3 times
+ for _ in range(3):
+ cur.execute("SELECT a, b FROM {0} WHERE a = 2".format(self._table))
+ res = cur.fetchall()
+ self.assertListOfListsEqual(res, [[2, 'bb']])
+
+ # close and reopen cursor
+ cur.close()
+ cur = conn.cursor()
+
+ # unit test for #74
+ def test_nextset(self):
+ with self._connect() as conn:
+ cur = conn.cursor()
+
+ cur.execute("SELECT 1; SELECT 2;")
+
+ res1 = cur.fetchall()
+ self.assertListOfListsEqual(res1, [[1]])
+ self.assertIsNone(cur.fetchone())
+ self.assertTrue(cur.nextset())
+
+ res2 = cur.fetchall()
+ self.assertListOfListsEqual(res2, [[2]])
+ self.assertIsNone(cur.fetchone())
+ self.assertFalse(cur.nextset())
+
+ # unit test for #74
+ def test_nextset_with_delete(self):
+ with self._connect() as conn:
+ cur = conn.cursor()
+
+ # insert data
+ cur.execute("INSERT INTO {0} (a, b) VALUES (1, 'aa')".format(self._table))
+ cur.execute("INSERT INTO {0} (a, b) VALUES (2, 'bb')".format(self._table))
+ conn.commit()
+
+ cur.execute("""
+ SELECT * FROM {0} ORDER BY a ASC;
+ DELETE FROM {0};
+ SELECT * FROM {0} ORDER BY a ASC;
+ """.format(self._table))
+
+ # check first select results
+ res1 = cur.fetchall()
+ self.assertListOfListsEqual(res1, [[1, 'aa'], [2, 'bb']])
+ self.assertIsNone(cur.fetchone())
+ self.assertTrue(cur.nextset())
+
+ # check delete results
+ res2 = cur.fetchall()
+ self.assertListOfListsEqual(res2, [[2]])
+ self.assertIsNone(cur.fetchone())
+ self.assertTrue(cur.nextset())
+
+ # check second select results
+ res3 = cur.fetchall()
+ self.assertListOfListsEqual(res3, [])
+ self.assertIsNone(cur.fetchone())
+ self.assertFalse(cur.nextset())
+
+ # unit test for #124
+ def test_nextset_with_error(self):
+ with self._connect() as conn:
+ cur = conn.cursor()
+
+ cur.execute("SELECT 1; SELECT a; SELECT 2")
+
+ # verify data from first query
+ res1 = cur.fetchall()
+ self.assertListOfListsEqual(res1, [[1]])
+ self.assertIsNone(cur.fetchone())
+
+ # second statement results in a query error
+ with self.assertRaises(errors.MissingColumn):
+ cur.nextset()
+
+ # unit test for #144
+ def test_empty_query(self):
+ with self._connect() as conn:
+ cur = conn.cursor()
+
+ cur.execute("")
+ res = cur.fetchall()
+ self.assertListOfListsEqual(res, [])
diff -Nru python-vertica-0.6.8/vertica_python/tests/test_datatypes.py python-vertica-0.7.1/vertica_python/tests/test_datatypes.py
- --- python-vertica-0.6.8/vertica_python/tests/test_datatypes.py 1970-01-01 01:00:00.000000000 +0100
+++ python-vertica-0.7.1/vertica_python/tests/test_datatypes.py 2017-05-14 00:40:53.000000000 +0200
@@ -0,0 +1,25 @@
+from __future__ import print_function, division, absolute_import
+
+from decimal import Decimal
+
+from .base import VerticaPythonTestCase
+
+
+class TypeTestCase(VerticaPythonTestCase):
+ def test_decimal_query(self):
+ value = Decimal(0.42)
+ query = "SELECT {0}::numeric".format(value)
+ res = self._query_and_fetchone(query)
+ self.assertAlmostEqual(res[0], value)
+
+ def test_boolean_query__true(self):
+ value = True
+ query = "SELECT {0}::boolean".format(value)
+ res = self._query_and_fetchone(query)
+ self.assertEqual(res[0], value)
+
+ def test_boolean_query__false(self):
+ value = False
+ query = "SELECT {0}::boolean".format(value)
+ res = self._query_and_fetchone(query)
+ self.assertEqual(res[0], value)
diff -Nru python-vertica-0.6.8/vertica_python/tests/test_dates.py python-vertica-0.7.1/vertica_python/tests/test_dates.py
- --- python-vertica-0.6.8/vertica_python/tests/test_dates.py 1970-01-01 01:00:00.000000000 +0100
+++ python-vertica-0.7.1/vertica_python/tests/test_dates.py 2017-05-14 00:40:53.000000000 +0200
@@ -0,0 +1,238 @@
+from __future__ import print_function, division, absolute_import
+
+from collections import namedtuple
+from datetime import date, datetime
+
+from .base import VerticaPythonTestCase
+from .. import errors
+from ..vertica.column import timestamp_parse
+
+DateTestingCase = namedtuple("DateTestingCase", ["string", "template", "date"])
+TimestampTestingCase = namedtuple("TimestampTestingCase", ["string", "timestamp"])
+
+
+class DateParsingTestCase(VerticaPythonTestCase):
+ """Testing DATE type parsing with focus on 'AD'/'BC'.
+
+ Note: the 'BC' or 'AD' era indicators in Vertica's date format seem to make Vertica behave as
+ follows:
+ 1. Both 'BC' and 'AD' are simply a flags that tell Vertica: include era indicator if the
+ date is Before Christ
+ 2. Dates in 'AD' will never include era indicator
+ """
+
+ def _test_dates(self, test_cases, msg=None):
+ with self._connect() as conn:
+ cur = conn.cursor()
+ for tc in test_cases:
+ cur.execute("SELECT TO_DATE('{0}', '{1}')".format(tc.string, tc.template))
+ res = cur.fetchall()
+ self.assertListOfListsEqual(res, [[tc.date]], msg=msg)
+
+ def _test_not_supported(self, test_cases, msg=None):
+ with self._connect() as conn:
+ cur = conn.cursor()
+ for tc in test_cases:
+ with self.assertRaises(errors.NotSupportedError, msg=msg):
+ cur.execute("SELECT TO_DATE('{0}', '{1}')".format(tc.string, tc.template))
+ res = cur.fetchall()
+ self.assertListOfListsEqual(res, [[tc.date]])
+
+ def test_no_to_no(self):
+ test_cases = [
+ DateTestingCase('1985-10-25', 'YYYY-MM-DD', date(1985, 10, 25)),
+ DateTestingCase('1955-11-12', 'YYYY-MM-DD', date(1955, 11, 12)),
+ DateTestingCase('1885-01-01', 'YYYY-MM-DD', date(1885, 1, 1)),
+ DateTestingCase('2015-10-21', 'YYYY-MM-DD', date(2015, 10, 21)),
+ ]
+ self._test_dates(test_cases=test_cases, msg='no indicator -> no indicator')
+
+ def test_ad_to_no(self):
+ test_cases = [
+ DateTestingCase('1985-10-25 AD', 'YYYY-MM-DD', date(1985, 10, 25)),
+ DateTestingCase('1955-11-12 AD', 'YYYY-MM-DD', date(1955, 11, 12)),
+ DateTestingCase('1885-01-01 AD', 'YYYY-MM-DD', date(1885, 1, 1)),
+ DateTestingCase('2015-10-21 AD', 'YYYY-MM-DD', date(2015, 10, 21)),
+ ]
+ self._test_dates(test_cases=test_cases, msg='AD indicator -> no indicator')
+
+ def test_bc_to_no(self):
+ test_cases = [
+ DateTestingCase('1985-10-25 BC', 'YYYY-MM-DD', date(1985, 10, 25)),
+ DateTestingCase('1955-11-12 BC', 'YYYY-MM-DD', date(1955, 11, 12)),
+ DateTestingCase('1885-01-01 BC', 'YYYY-MM-DD', date(1885, 1, 1)),
+ DateTestingCase('2015-10-21 BC', 'YYYY-MM-DD', date(2015, 10, 21)),
+ ]
+ self._test_dates(test_cases=test_cases, msg='BC indicator -> no indicator')
+
+ def test_no_to_ad(self):
+ test_cases = [
+ DateTestingCase('1985-10-25', 'YYYY-MM-DD AD', date(1985, 10, 25)),
+ DateTestingCase('1955-11-12', 'YYYY-MM-DD AD', date(1955, 11, 12)),
+ DateTestingCase('1885-01-01', 'YYYY-MM-DD AD', date(1885, 1, 1)),
+ DateTestingCase('2015-10-21', 'YYYY-MM-DD AD', date(2015, 10, 21)),
+ ]
+ self._test_dates(test_cases=test_cases, msg='no indicator -> AD indicator')
+
+ def test_ad_to_ad(self):
+ test_cases = [
+ DateTestingCase('1985-10-25 AD', 'YYYY-MM-DD AD', date(1985, 10, 25)),
+ DateTestingCase('1955-11-12 AD', 'YYYY-MM-DD AD', date(1955, 11, 12)),
+ DateTestingCase('1885-01-01 AD', 'YYYY-MM-DD AD', date(1885, 1, 1)),
+ DateTestingCase('2015-10-21 AD', 'YYYY-MM-DD AD', date(2015, 10, 21)),
+ ]
+ self._test_dates(test_cases=test_cases, msg='AD indicator -> AD indicator')
+
+ def test_bc_to_ad(self):
+ test_cases = [
+ DateTestingCase('1985-10-25 BC', 'YYYY-MM-DD AD', date(1985, 10, 25)),
+ DateTestingCase('1955-11-12 BC', 'YYYY-MM-DD AD', date(1955, 11, 12)),
+ DateTestingCase('1885-01-01 BC', 'YYYY-MM-DD AD', date(1885, 1, 1)),
+ DateTestingCase('2015-10-21 BC', 'YYYY-MM-DD AD', date(2015, 10, 21)),
+ ]
+ self._test_not_supported(test_cases=test_cases, msg='BC indicator -> AD indicator')
+
+ def test_no_to_bc(self):
+ test_cases = [
+ DateTestingCase('1985-10-25', 'YYYY-MM-DD BC', date(1985, 10, 25)),
+ DateTestingCase('1955-11-12', 'YYYY-MM-DD BC', date(1955, 11, 12)),
+ DateTestingCase('1885-01-01', 'YYYY-MM-DD BC', date(1885, 1, 1)),
+ DateTestingCase('2015-10-21', 'YYYY-MM-DD BC', date(2015, 10, 21)),
+ ]
+ self._test_dates(test_cases=test_cases, msg='no indicator -> BC indicator')
+
+ def test_ad_to_bc(self):
+ test_cases = [
+ DateTestingCase('1985-10-25 AD', 'YYYY-MM-DD BC', date(1985, 10, 25)),
+ DateTestingCase('1955-11-12 AD', 'YYYY-MM-DD BC', date(1955, 11, 12)),
+ DateTestingCase('1885-01-01 AD', 'YYYY-MM-DD BC', date(1885, 1, 1)),
+ DateTestingCase('2015-10-21 AD', 'YYYY-MM-DD BC', date(2015, 10, 21)),
+ ]
+ self._test_dates(test_cases=test_cases, msg='AD indicator -> BC indicator')
+
+ def test_bc_to_bc(self):
+ test_cases = [
+ DateTestingCase('1985-10-25 BC', 'YYYY-MM-DD BC', date(1985, 10, 25)),
+ DateTestingCase('1955-11-12 BC', 'YYYY-MM-DD BC', date(1955, 11, 12)),
+ DateTestingCase('1885-01-01 BC', 'YYYY-MM-DD BC', date(1885, 1, 1)),
+ DateTestingCase('2015-10-21 BC', 'YYYY-MM-DD BC', date(2015, 10, 21)),
+ ]
+ self._test_not_supported(test_cases=test_cases, msg='BC indicator -> BC indicator')
+
+
+class TimestampParsingTestCase(VerticaPythonTestCase):
+ def _test_timestamps(self, test_cases, msg=None):
+ for tc in test_cases:
+ self.assertEqual(timestamp_parse(tc.string), tc.timestamp, msg=msg)
+
+ def test_timestamp_second_resolution(self):
+ test_cases = [ # back to the future dates
+ TimestampTestingCase(
+ '1985-10-26 01:25:01',
+ datetime(year=1985, month=10, day=26, hour=1, minute=25, second=1)
+ ),
+ TimestampTestingCase(
+ '1955-11-12 22:55:02',
+ datetime(year=1955, month=11, day=12, hour=22, minute=55, second=2)
+ ),
+ TimestampTestingCase(
+ '2015-10-21 11:12:03',
+ datetime(year=2015, month=10, day=21, hour=11, minute=12, second=3)
+ ),
+ TimestampTestingCase(
+ '1885-01-01 01:02:04',
+ datetime(year=1885, month=1, day=1, hour=1, minute=2, second=4)
+ ),
+ TimestampTestingCase(
+ '1885-09-02 02:03:05',
+ datetime(year=1885, month=9, day=2, hour=2, minute=3, second=5)
+ ),
+ ]
+ self._test_timestamps(test_cases=test_cases, msg='timestamp second resolution')
+
+ def test_timestamp_microsecond_resolution(self):
+ test_cases = [ # back to the future dates
+ TimestampTestingCase(
+ '1985-10-26 01:25:01.1',
+ datetime(year=1985, month=10, day=26, hour=1, minute=25, second=1,
+ microsecond=100000)
+ ),
+ TimestampTestingCase(
+ '1955-11-12 22:55:02.01',
+ datetime(year=1955, month=11, day=12, hour=22, minute=55, second=2,
+ microsecond=10000)
+ ),
+ TimestampTestingCase(
+ '2015-10-21 11:12:03.001',
+ datetime(year=2015, month=10, day=21, hour=11, minute=12, second=3,
+ microsecond=1000)
+ ),
+ TimestampTestingCase(
+ '1885-01-01 01:02:04.000001',
+ datetime(year=1885, month=1, day=1, hour=1, minute=2, second=4,
+ microsecond=1)
+ ),
+ TimestampTestingCase(
+ '1885-09-02 02:03:05.002343',
+ datetime(year=1885, month=9, day=2, hour=2, minute=3, second=5,
+ microsecond=2343)
+ ),
+ ]
+ self._test_timestamps(test_cases=test_cases, msg='timestamp microsecond resolution')
+
+ def test_timestamp_year_over_9999_second_resolution(self):
+ """Asserts that years over 9999 are truncated to 9999"""
+ test_cases = [
+ TimestampTestingCase(
+ '19850-10-26 01:25:01',
+ datetime(year=9999, month=10, day=26, hour=1, minute=25, second=1)
+ ),
+ TimestampTestingCase(
+ '10000-11-12 22:55:02',
+ datetime(year=9999, month=11, day=12, hour=22, minute=55, second=2)
+ ),
+ TimestampTestingCase(
+ '9999-10-21 11:12:03',
+ datetime(year=9999, month=10, day=21, hour=11, minute=12, second=3)
+ ),
+ TimestampTestingCase(
+ '18850-01-01 01:02:04',
+ datetime(year=9999, month=1, day=1, hour=1, minute=2, second=4)
+ ),
+ TimestampTestingCase(
+ '18850-09-02 02:03:05',
+ datetime(year=9999, month=9, day=2, hour=2, minute=3, second=5)
+ ),
+ ]
+ self._test_timestamps(test_cases=test_cases, msg='timestamp past 9999 second resolution')
+
+ def test_timestamp_year_over_9999_microsecond_resolution(self):
+ test_cases = [
+ TimestampTestingCase(
+ '19850-10-26 01:25:01.1',
+ datetime(year=9999, month=10, day=26, hour=1, minute=25, second=1,
+ microsecond=100000)
+ ),
+ TimestampTestingCase(
+ '10000-11-12 22:55:02.01',
+ datetime(year=9999, month=11, day=12, hour=22, minute=55, second=2,
+ microsecond=10000)
+ ),
+ TimestampTestingCase(
+ '9999-10-21 11:12:03.001',
+ datetime(year=9999, month=10, day=21, hour=11, minute=12, second=3,
+ microsecond=1000)
+ ),
+ TimestampTestingCase(
+ '18850-01-01 01:02:04.000001',
+ datetime(year=9999, month=1, day=1, hour=1, minute=2, second=4,
+ microsecond=1)
+ ),
+ TimestampTestingCase(
+ '18850-09-02 02:03:05.002343',
+ datetime(year=9999, month=9, day=2, hour=2, minute=3, second=5,
+ microsecond=2343)
+ ),
+ ]
+ self._test_timestamps(test_cases=test_cases,
+ msg='timestamp past 9999 microsecond resolution')
diff -Nru python-vertica-0.6.8/vertica_python/tests/test_errors.py python-vertica-0.7.1/vertica_python/tests/test_errors.py
- --- python-vertica-0.6.8/vertica_python/tests/test_errors.py 1970-01-01 01:00:00.000000000 +0100
+++ python-vertica-0.7.1/vertica_python/tests/test_errors.py 2017-05-14 00:40:53.000000000 +0200
@@ -0,0 +1,31 @@
+from __future__ import print_function, division, absolute_import
+
+from .base import VerticaPythonTestCase
+
+from .. import errors
+
+
+class ErrorTestCase(VerticaPythonTestCase):
+ def setUp(self):
+ with self._connect() as conn:
+ cur = conn.cursor()
+ cur.execute("DROP TABLE IF EXISTS {0}".format(self._table))
+
+ def test_missing_schema(self):
+ with self._connect() as conn:
+ cur = conn.cursor()
+ with self.assertRaises(errors.MissingSchema):
+ cur.execute("SELECT 1 FROM missing_schema.table")
+
+ def test_missing_relation(self):
+ with self._connect() as conn:
+ cur = conn.cursor()
+ with self.assertRaises(errors.MissingRelation):
+ cur.execute("SELECT 1 FROM missing_table")
+
+ def test_duplicate_object(self):
+ with self._connect() as conn:
+ cur = conn.cursor()
+ cur.execute("CREATE TABLE {0} (a BOOLEAN)".format(self._table))
+ with self.assertRaises(errors.DuplicateObject):
+ cur.execute("CREATE TABLE {0} (a BOOLEAN)".format(self._table))
diff -Nru python-vertica-0.6.8/vertica_python/tests/test_timezones.py python-vertica-0.7.1/vertica_python/tests/test_timezones.py
- --- python-vertica-0.6.8/vertica_python/tests/test_timezones.py 1970-01-01 01:00:00.000000000 +0100
+++ python-vertica-0.7.1/vertica_python/tests/test_timezones.py 2017-05-14 00:40:53.000000000 +0200
@@ -0,0 +1,52 @@
+from __future__ import print_function, division, absolute_import
+
+from collections import namedtuple
+from datetime import datetime
+import pytz
+
+from .base import VerticaPythonTestCase
+
+TimeZoneTestingCase = namedtuple("TimeZoneTestingCase", ["string", "template", "timestamp"])
+
+
+class TimeZoneTestCase(VerticaPythonTestCase):
+ def _test_ts(self, test_cases):
+ with self._connect() as conn:
+ cur = conn.cursor()
+ for tc in test_cases:
+ cur.execute("SELECT TO_TIMESTAMP('{0}', '{1}')".format(tc.string, tc.template))
+ res = cur.fetchone()
+ self.assertEqual(tc.timestamp.toordinal(), res[0].toordinal())
+
+ def test_simple_ts_query(self):
+ template = 'YYYY-MM-DD HH:MI:SS.MS'
+ test_cases = [
+ TimeZoneTestingCase(
+ string='2016-05-15 13:15:17.789', template=template,
+ timestamp=datetime(year=2016, month=5, day=15, hour=13, minute=15, second=17,
+ microsecond=789000)
+ ),
+ ]
+ self._test_ts(test_cases=test_cases)
+
+ def test_simple_ts_with_tz_query(self):
+ template = 'YYYY-MM-DD HH:MI:SS.MS TZ'
+ test_cases = [
+ TimeZoneTestingCase(
+ string='2016-05-15 13:15:17.789 UTC', template=template,
+ timestamp=datetime(year=2016, month=5, day=15, hour=13, minute=15, second=17,
+ microsecond=789000, tzinfo=pytz.utc)
+ ),
+ ]
+ self._test_ts(test_cases=test_cases)
+
+ def test_simple_ts_with_offset_query(self):
+ template = 'YYYY-MM-DD HH:MI:SS.MS+00'
+ test_cases = [
+ TimeZoneTestingCase(
+ string='2016-05-15 13:15:17.789 UTC', template=template,
+ timestamp=datetime(year=2016, month=5, day=15, hour=13, minute=15, second=17,
+ microsecond=789000, tzinfo=pytz.utc)
+ ),
+ ]
+ self._test_ts(test_cases=test_cases)
diff -Nru python-vertica-0.6.8/vertica_python/tests/test_unicode.py python-vertica-0.7.1/vertica_python/tests/test_unicode.py
- --- python-vertica-0.6.8/vertica_python/tests/test_unicode.py 1970-01-01 01:00:00.000000000 +0100
+++ python-vertica-0.7.1/vertica_python/tests/test_unicode.py 2017-05-14 00:40:53.000000000 +0200
@@ -0,0 +1,65 @@
+from __future__ import print_function, division, absolute_import
+
+from .base import VerticaPythonTestCase
+
+
+class UnicodeTestCase(VerticaPythonTestCase):
+ def test_unicode_query(self):
+ value = u'\u16a0'
+ query = u"SELECT '{0}'".format(value)
+
+ with self._connect() as conn:
+ cur = conn.cursor()
+ cur.execute(query)
+ res = cur.fetchone()
+
+ self.assertResultEqual(value, res[0])
+
+ def test_unicode_list_parameter(self):
+ values = [u'\u00f1', 'foo', 3]
+ 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)
+
+ def test_unicode_named_parameter_binding(self):
+ values = [u'\u16b1', 'foo', 3]
+ keys = [u'\u16a0', 'foo', 3]
+
+ query = u"SELECT {0}".format(", ".join([u":{0}".format(key) for key in keys]))
+
+ with self._connect() as conn:
+ cur = conn.cursor()
+ cur.execute(query, dict(zip(keys, values)))
+ results = cur.fetchone()
+
+ for val, res in zip(values, results):
+ self.assertResultEqual(val, res)
+
+ def test_string_query(self):
+ value = u'test'
+ query = u"SELECT '{0}'".format(value)
+
+ with self._connect() as conn:
+ cur = conn.cursor()
+ cur.execute(query)
+ res = cur.fetchone()
+
+ self.assertEqual(value, res[0])
+
+ def test_string_named_parameter_binding(self):
+ key = u'test'
+ value = u'value'
+ 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])
diff -Nru python-vertica-0.6.8/vertica_python/tests/timezone_tests.py python-vertica-0.7.1/vertica_python/tests/timezone_tests.py
- --- python-vertica-0.6.8/vertica_python/tests/timezone_tests.py 2016-10-06 23:30:16.000000000 +0200
+++ python-vertica-0.7.1/vertica_python/tests/timezone_tests.py 1970-01-01 01:00:00.000000000 +0100
@@ -1,48 +0,0 @@
- -from datetime import date, datetime
- -import pytz
- -from .test_commons import conn_info, VerticaTestCase
- -from .. import connect
- -
- -class TimeZoneTestCase(VerticaTestCase):
- -
- - def test_simple_ts_query(self):
- - query = """
- - select
- - to_timestamp('2016-05-15 13:15:17.789', 'YYYY-MM-DD HH:MI:SS.MS')
- - ;
- - """
- - value = datetime(year=2016, month=5, day=15, hour=13, minute=15, second=17, microsecond=789000)
- -
- - with connect(**conn_info) as conn:
- - cur = conn.cursor()
- - cur.execute(query)
- - res = cur.fetchall()
- - assert res[0][0] == value
- -
- - def test_simple_ts_with_tz_query(self):
- - query = """
- - select
- - to_timestamp_tz('2016-05-15 13:15:17.789 UTC', 'YYYY-MM-DD HH:MI:SS.MS TZ')
- - ;
- - """
- - value = datetime(year=2016, month=5, day=15, hour=13, minute=15, second=17, microsecond=789000, tzinfo=pytz.utc)
- -
- - with connect(**conn_info) as conn:
- - cur = conn.cursor()
- - cur.execute(query)
- - res = cur.fetchall()
- - assert res[0][0] == value
- -
- - def test_simple_ts_with_offset_query(self):
- - query = """
- - select
- - timestamp '2016-05-15 13:15:17.789+00'
- - ;
- - """
- - value = datetime(year=2016, month=5, day=15, hour=13, minute=15, second=17, microsecond=789000, tzinfo=pytz.utc)
- -
- - with connect(**conn_info) as conn:
- - cur = conn.cursor()
- - cur.execute(query)
- - res = cur.fetchall()
- - assert res[0][0] == value
diff -Nru python-vertica-0.6.8/vertica_python/tests/type_tests.py python-vertica-0.7.1/vertica_python/tests/type_tests.py
- --- python-vertica-0.6.8/vertica_python/tests/type_tests.py 2016-10-06 23:30:16.000000000 +0200
+++ python-vertica-0.7.1/vertica_python/tests/type_tests.py 1970-01-01 01:00:00.000000000 +0100
@@ -1,15 +0,0 @@
- -from .test_commons import conn_info, VerticaTestCase
- -from .. import connect
- -from decimal import Decimal
- -
- -
- -class TypeTestCase(VerticaTestCase):
- - def test_decimal_query(self):
- - value = Decimal(0.42)
- - query = "SELECT {}::numeric".format(value)
- -
- - with connect(**conn_info) as conn:
- - cur = conn.cursor()
- - cur.execute(query)
- - res = cur.fetchone()
- - self.assertAlmostEqual(res[0], value)
diff -Nru python-vertica-0.6.8/vertica_python/tests/unicode_tests.py python-vertica-0.7.1/vertica_python/tests/unicode_tests.py
- --- python-vertica-0.6.8/vertica_python/tests/unicode_tests.py 2016-10-06 23:30:16.000000000 +0200
+++ python-vertica-0.7.1/vertica_python/tests/unicode_tests.py 1970-01-01 01:00:00.000000000 +0100
@@ -1,75 +0,0 @@
- -from .test_commons import conn_info, VerticaTestCase
- -from .. import connect
- -
- -
- -class UnicodeTestCase(VerticaTestCase):
- - def test_unicode_query(self):
- - value = u'\u16a0'
- - query = u"SELECT '{}'".format(value)
- -
- - with connect(**conn_info) as conn:
- - cur = conn.cursor()
- - cur.execute(query)
- - res = cur.fetchone()
- -
- - assert res[0] == value
- -
- - # this test is broken on python3: see issue #112
- - def test_unicode_list_parameter(self):
- - v1 = u'\u00f1'
- - v2 = 'foo'
- - v3 = 3
- - query = u"SELECT %s, %s, %s"
- -
- - with connect(**conn_info) as conn:
- - cur = conn.cursor()
- - cur.execute(query, (v1, v2, v3))
- - res = cur.fetchone()
- -
- - assert res[0] == v1
- - assert res[1] == v2
- - assert res[2] == v3
- -
- - # this test is broken on python3: see issue #112
- - def test_unicode_named_parameter_binding(self):
- - k1 = u'\u16a0'
- - k2 = 'foo'
- - k3 = 3
- -
- - v1 = u'\u16b1'
- - v2 = 'foo'
- - v3 = 3
- -
- - query = u"SELECT :{}, :{}, :{}".format(k1, k2, k3)
- -
- - with connect(**conn_info) as conn:
- - cur = conn.cursor()
- - cur.execute(query, {k1: v1, k2: v2, k3: v3})
- - res = cur.fetchone()
- -
- - assert res[0] == v1
- - assert res[1] == v2
- - assert res[2] == v3
- -
- - def test_string_query(self):
- - value = u'test'
- - query = u"SELECT '{}'".format(value)
- -
- - with connect(**conn_info) as conn:
- - cur = conn.cursor()
- - cur.execute(query)
- - res = cur.fetchone()
- -
- - assert res[0] == value
- -
- - def test_string_named_parameter_binding(self):
- - key = u'test'
- - value = u'value'
- - query = u"SELECT :{}".format(key)
- -
- - with connect(**conn_info) as conn:
- - cur = conn.cursor()
- - cur.execute(query, {key: value})
- - res = cur.fetchone()
- -
- - assert res[0] == value
diff -Nru python-vertica-0.6.8/vertica_python/vertica/column.py python-vertica-0.7.1/vertica_python/vertica/column.py
- --- python-vertica-0.6.8/vertica_python/vertica/column.py 2016-10-06 23:30:16.000000000 +0200
+++ python-vertica-0.7.1/vertica_python/vertica/column.py 2017-05-14 00:40:53.000000000 +0200
@@ -1,18 +1,21 @@
+from __future__ import print_function, division, absolute_import
- -
- -from collections import namedtuple
import re
+from collections import namedtuple
+from datetime import date, datetime
+from decimal import Decimal
+import pytz
+# noinspection PyCompatibility,PyUnresolvedReferences
from builtins import str
- -from decimal import Decimal
- -from datetime import date
- -from datetime import datetime
from dateutil import parser
- -from vertica_python import errors
- -import pytz
+from .. import errors
+from ..compat import as_str, as_text
- -years_re = re.compile(r'^([0-9]+)-')
+YEARS_RE = re.compile(r"^([0-9]+)-")
+
+UTF_8 = 'utf-8'
# these methods are bad...
@@ -34,12 +37,12 @@
# timestamptz type stores: 2013-01-01 05:00:00.01+00
# select t AT TIMEZONE 'America/New_York' returns: 2012-12-31 19:00:00.01
def timestamp_parse(s):
- - s = str(s, 'utf-8')
+ s = as_str(s)
try:
dt = _timestamp_parse(s)
except ValueError:
# Value error, year might be over 9999
- - year_match = years_re.match(s)
+ year_match = YEARS_RE.match(s)
if year_match:
year = year_match.groups()[0]
dt = _timestamp_parse_without_year(s[len(year) + 1:])
@@ -63,11 +66,11 @@
def timestamp_tz_parse(s):
- - s = str(s, 'utf-8')
- - # if timezome is simply UTC...
+ s = as_str(s)
+ # if timezone is simply UTC...
if s.endswith('+00'):
# remove time zone
- - ts = timestamp_parse(s[:-3].encode(encoding='utf-8', errors='strict'))
+ ts = timestamp_parse(s[:-3].encode(encoding=UTF_8, errors='strict'))
ts = ts.replace(tzinfo=pytz.UTC)
return ts
# other wise do a real parse (slower)
@@ -81,50 +84,19 @@
:return: an instance of datetime.date
:raises NotSupportedError when a date Before Christ is encountered
"""
- - if s.endswith(b' BC'):
- - raise errors.NotSupportedError('Dates Before Christ are not supported. Got: ' + str(s, 'utf-8'))
+ s = as_str(s)
+ if s.endswith(' BC'):
+ raise errors.NotSupportedError('Dates Before Christ are not supported. Got: {0}'.format(s))
- - return date(*map(lambda x: int(x), s.split(b'-')))
+ # Value error, year might be over 9999
+ return date(*map(lambda x: min(int(x), 9999), s.split('-')))
- -ColumnTuple = namedtuple(
- - 'Column',
- - ['name', 'type_code', 'display_size', 'internal_size',
- - 'precision', 'scale', 'null_ok']
- -)
+ColumnTuple = namedtuple('Column', ['name', 'type_code', 'display_size', 'internal_size',
+ 'precision', 'scale', 'null_ok'])
- -class Column(object):
- -
- - @classmethod
- - def data_type_conversions(cls, unicode_error=None):
- - if unicode_error is None:
- - unicode_error = 'strict'
- - return [
- - ('unspecified', None),
- - ('tuple', None),
- - ('pos', None),
- - ('record', None),
- - ('unknown', None),
- - ('bool', lambda s: s == 't'),
- - ('integer', lambda s: int(s)),
- - ('float', lambda s: float(s)),
- - ('char', lambda s: str(s, 'utf-8', unicode_error)),
- - ('varchar', lambda s: str(s, 'utf-8', unicode_error)),
- - ('date', date_parse),
- - ('time', None),
- - ('timestamp', timestamp_parse),
- - ('timestamp_tz', timestamp_tz_parse),
- - ('interval', None),
- - ('time_tz', None),
- - ('numeric', lambda s: Decimal(str(s, 'utf-8', unicode_error))),
- - ('bytea', None),
- - ('rle_tuple', None),
- - ]
- -
- - @property
- - def data_types():
- - return map(lambda x: x[0], Column.data_type_conversions())
+class Column(object):
def __init__(self, col, unicode_error=None):
self.name = col['name'].decode()
self.type_code = col['data_type_oid']
@@ -134,7 +106,7 @@
self.scale = None
self.null_ok = None
self.unicode_error = unicode_error
- - self.data_type_conversions = Column.data_type_conversions(unicode_error=self.unicode_error)
+ self.data_type_conversions = Column._data_type_conversions(unicode_error=self.unicode_error)
# WORKAROUND: Treat LONGVARCHAR as VARCHAR
if self.type_code == 115:
@@ -144,20 +116,53 @@
if self.type_code >= len(self.data_type_conversions):
self.type_code = 0
- - #self.props = ColumnTuple(col['name'], col['data_type_oid'], None, col['data_type_size'], None, None, None)
- - self.props = ColumnTuple(self.name, self.type_code, None, col['data_type_size'], None, None, None)
+ # self.props = ColumnTuple(col['name'], col['data_type_oid'], None, col['data_type_size'],
+ # None, None, None)
+ self.props = ColumnTuple(self.name, self.type_code, None, col['data_type_size'], None, None,
+ None)
- - #self.converter = self.data_type_conversions[col['data_type_oid']][1]
+ # self.converter = self.data_type_conversions[col['data_type_oid']][1]
self.converter = self.data_type_conversions[self.type_code][1]
# things that are actually sent
- -# self.name = col['name']
- -# self.data_type = self.data_type_conversions[col['data_type_oid']][0]
- -# self.type_modifier = col['type_modifier']
- -# self.format = 'text' if col['format_code'] == 0 else 'binary'
- -# self.table_oid = col['table_oid']
- -# self.attribute_number = col['attribute_number']
- -# self.size = col['data_type_size']
+ # self.name = col['name']
+ # self.data_type = self.data_type_conversions[col['data_type_oid']][0]
+ # self.type_modifier = col['type_modifier']
+ # self.format = 'text' if col['format_code'] == 0 else 'binary'
+ # self.table_oid = col['table_oid']
+ # self.attribute_number = col['attribute_number']
+ # self.size = col['data_type_size']
+
+ @classmethod
+ def _data_type_conversions(cls, unicode_error=None):
+ if unicode_error is None:
+ unicode_error = 'strict'
+ return [
+ ('unspecified', None),
+ ('tuple', None),
+ ('pos', None),
+ ('record', None),
+ ('unknown', None),
+ ('bool', lambda s: 't' == str(s, encoding=UTF_8, errors=unicode_error)),
+ ('integer', lambda s: int(s)),
+ ('float', lambda s: float(s)),
+ ('char', lambda s: str(s, encoding=UTF_8, errors=unicode_error)),
+ ('varchar', lambda s: str(s, encoding=UTF_8, errors=unicode_error)),
+ ('date', date_parse),
+ ('time', None),
+ ('timestamp', timestamp_parse),
+ ('timestamp_tz', timestamp_tz_parse),
+ ('interval', None),
+ ('time_tz', None),
+ ('numeric',
+ lambda s: Decimal(str(s, encoding=UTF_8, errors=unicode_error))),
+ ('bytea', None),
+ ('rle_tuple', None),
+ ]
+
+ @classmethod
+ def data_types(cls):
+ return tuple([name for name, value in cls._data_type_conversions()])
def convert(self, s):
if s is None:
@@ -165,13 +170,13 @@
return self.converter(s) if self.converter is not None else s
def __str__(self):
- - return self.props.__str__()
+ return as_str(str(self.props))
def __unicode__(self):
- - return str(self.props.__str__())
+ return as_text(str(self.props))
def __repr__(self):
- - return self.props.__str__()
+ return as_str(str(self.props))
def __iter__(self):
for prop in self.props:
diff -Nru python-vertica-0.6.8/vertica_python/vertica/connection.py python-vertica-0.7.1/vertica_python/vertica/connection.py
- --- python-vertica-0.6.8/vertica_python/vertica/connection.py 2016-10-06 23:30:16.000000000 +0200
+++ python-vertica-0.7.1/vertica_python/vertica/connection.py 2017-05-14 00:40:53.000000000 +0200
@@ -1,34 +1,41 @@
- -
+from __future__ import print_function, division, absolute_import
import logging
- -import select
import socket
import ssl
- -
from struct import unpack
+# noinspection PyCompatibility,PyUnresolvedReferences
from builtins import str
+from six import raise_from
- -import vertica_python.errors as errors
- -import vertica_python.vertica.messages as messages
+from .. import errors
+from ..vertica import messages
+from ..vertica.cursor import Cursor
+from ..vertica.messages.message import BackendMessage, FrontendMessage
+from ..vertica.messages.frontend_messages import CancelRequest
- -from vertica_python.vertica.messages.message import BackendMessage
+logger = logging.getLogger('vertica')
- -from vertica_python.vertica.cursor import Cursor
- -from vertica_python.errors import SSLNotSupported
- -import collections
+ASCII = 'ascii'
- -logger = logging.getLogger('vertica')
+
+def connect(**kwargs):
+ """Opens a new connection to a Vertica database."""
+ return Connection(kwargs)
class Connection(object):
def __init__(self, options=None):
- - self.reset_values()
+ self.parameters = {}
+ self.session_id = None
+ self.backend_pid = None
+ self.backend_key = None
+ self.transaction_status = None
+ self.socket = None
options = options or {}
- - self.options = dict(
- - (key, value) for key, value in options.items() if value is not None
- - )
+ self.options = {key: value for key, value in options.items() if value is not None}
# we only support one cursor per connection
self.options.setdefault('unicode_error', None)
@@ -53,16 +60,21 @@
finally:
self.close()
- - #
- - # dbApi methods
- - #
- -
+ #############################################
+ # dbapi methods
+ #############################################
def close(self):
try:
self.write(messages.Terminate())
finally:
self.close_socket()
+ def cancel(self):
+ if self.closed():
+ raise errors.ConnectionError('Connection is closed')
+
+ self.write(CancelRequest(backend_pid=self.backend_pid, backend_key=self.backend_key))
+
def commit(self):
if self.closed():
raise errors.ConnectionError('Connection is closed')
@@ -88,9 +100,9 @@
self._cursor.cursor_type = cursor_type
return self._cursor
- - #
- - # Internal
- - #
+ #############################################
+ # internal
+ #############################################
def reset_values(self):
self.parameters = {}
self.session_id = None
@@ -105,14 +117,17 @@
host = self.options.get('host')
port = self.options.get('port')
+ connection_timeout = self.options.get('connection_timeout')
raw_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
raw_socket.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
+ if connection_timeout is not None:
+ raw_socket.settimeout(connection_timeout)
raw_socket.connect((host, port))
ssl_options = self.options.get('ssl')
if ssl_options is not None and ssl_options is not False:
from ssl import CertificateError, SSLError
- - raw_socket.sendall(messages.SslRequest().to_bytes())
+ raw_socket.sendall(messages.SslRequest().get_message())
response = raw_socket.recv(1)
if response in ('S', b'S'):
try:
@@ -121,11 +136,11 @@
else:
raw_socket = ssl.wrap_socket(raw_socket)
except CertificateError as e:
- - raise errors.ConnectionError('SSL: ' + e.message)
+ raise_from(errors.ConnectionError, e)
except SSLError as e:
- - raise errors.ConnectionError('SSL: ' + e.reason)
+ raise_from(errors.ConnectionError, e)
else:
- - raise SSLNotSupported("SSL requested but not supported by server")
+ raise errors.SSLNotSupported("SSL requested but not supported by server")
self.socket = raw_socket
return self.socket
@@ -142,32 +157,27 @@
return not self.opened()
def write(self, message):
- -
- - is_stream = hasattr(message, "read_bytes")
- -
- - if (hasattr(message, 'to_bytes') is False or isinstance(getattr(message, 'to_bytes'), collections.Callable) is False) and not is_stream:
+ if not isinstance(message, FrontendMessage):
raise TypeError("invalid message: ({0})".format(message))
logger.debug('=> %s', message)
- - try:
- -
- - if not is_stream:
- - self._socket().sendall(message.to_bytes())
- - else:
- - # read to end in chunks
- - while True:
- - data = message.read_bytes()
- - if len(data) == 0:
- - break
+ try:
+ for data in message.fetch_message():
+ try:
self._socket().sendall(data)
+ except Exception:
+ logger.error("couldn't send message")
+ raise
except Exception as e:
self.close_socket()
if str(e) == 'unsupported authentication method: 9':
- - raise errors.ConnectionError('Error during authentication. Your password might be expired.')
+ raise errors.ConnectionError(
+ 'Error during authentication. Your password might be expired.')
else:
- - raise errors.ConnectionError(str(e))
+ # noinspection PyTypeChecker
+ raise_from(errors.ConnectionError, e)
def close_socket(self):
try:
@@ -186,15 +196,14 @@
size = unpack('!I', self.read_bytes(4))[0]
if size < 4:
- - raise errors.MessageError(
- - "Bad message size: {0}".format(size)
- - )
- - message = BackendMessage.factory(type_, self.read_bytes(size - 4))
+ raise errors.MessageError("Bad message size: {0}".format(size))
+ message = BackendMessage.from_type(type_, self.read_bytes(size - 4))
logger.debug('<= %s', message)
return message
except (SystemError, IOError) as e:
self.close_socket()
- - raise errors.ConnectionError(str(e))
+ # noinspection PyTypeChecker
+ raise_from(errors.ConnectionError, e)
def process_message(self, message):
if isinstance(message, messages.ErrorResponse):
@@ -210,10 +219,12 @@
elif isinstance(message, messages.ReadyForQuery):
self.transaction_status = message.transaction_status
elif isinstance(message, messages.CommandComplete):
- - # TODO: im not ever seeing this actually returned by vertica...
+ # TODO: I'm not ever seeing this actually returned by vertica...
# if vertica returns a row count, set the rowcount attribute in cursor
- - #if hasattr(message, 'rows'):
- - # self.cursor.rowcount = message.rows
+ # if hasattr(message, 'rows'):
+ # self.cursor.rowcount = message.rows
+ pass
+ elif isinstance(message, messages.EmptyQueryResponse):
pass
elif isinstance(message, messages.CopyInResponse):
pass
@@ -224,17 +235,13 @@
self._cursor._message = message
def __str__(self):
- - safe_options = dict(
- - (key, value) for key, value in self.options.items() if key != 'password'
- - )
+ safe_options = {key: value for key, value in self.options.items() if key != 'password'}
+
s1 = "<Vertica.Connection:{0} parameters={1} backend_pid={2}, ".format(
- - id(self), self.parameters, self.backend_pid
- - )
+ id(self), self.parameters, self.backend_pid)
s2 = "backend_key={0}, transaction_status={1}, socket={2}, options={3}>".format(
- - self.backend_key, self.transaction_status, self.socket,
- - safe_options,
- - )
- - return s1 + s2
+ self.backend_key, self.transaction_status, self.socket, safe_options)
+ return ''.join([s1, s2])
def read_bytes(self, n):
results = bytes()
@@ -242,14 +249,14 @@
bytes_ = self._socket().recv(n - len(results))
if not bytes_:
raise errors.ConnectionError("Connection closed by Vertica")
- - results = results + bytes_
+ results += bytes_
return results
def startup_connection(self):
# This doesn't handle Unicode usernames or passwords
- - user = self.options['user'].encode('ascii')
- - database = self.options['database'].encode('ascii')
- - password = self.options['password'].encode('ascii')
+ user = self.options['user'].encode(ASCII)
+ database = self.options['database'].encode(ASCII)
+ password = self.options['password'].encode(ASCII)
self.write(messages.Startup(user, database))
@@ -259,9 +266,9 @@
if isinstance(message, messages.Authentication):
# Password message isn't right format ("incomplete message from client")
if message.code != messages.Authentication.OK:
- - self.write(messages.Password(password, message.code, {
- - 'user': user, 'salt': getattr(message, 'salt', None)
- - }))
+ self.write(messages.Password(password, message.code,
+ {'user': user,
+ 'salt': getattr(message, 'salt', None)}))
else:
self.process_message(message)
diff -Nru python-vertica-0.6.8/vertica_python/vertica/cursor.py python-vertica-0.7.1/vertica_python/vertica/cursor.py
- --- python-vertica-0.6.8/vertica_python/vertica/cursor.py 2016-10-06 23:30:16.000000000 +0200
+++ python-vertica-0.7.1/vertica_python/vertica/cursor.py 2017-05-14 00:40:53.000000000 +0200
@@ -1,132 +1,178 @@
+from __future__ import print_function, division, absolute_import
- -
- -import re
import logging
+import re
- -from collections import OrderedDict
- -from builtins import str
+try:
+ from collections import OrderedDict # python 2.7+ / 3
+except ImportError:
+ from ordereddict import OrderedDict # python 2.6
- -import vertica_python.errors as errors
+from io import IOBase
- -import vertica_python.vertica.messages as messages
- -from vertica_python.vertica.column import Column
+import six
+# noinspection PyUnresolvedReferences,PyCompatibility
+from builtins import str
+from six import binary_type, text_type, string_types, BytesIO, StringIO
+
+try:
+ from psycopg2.extensions import QuotedString
+except ImportError:
+ class QuotedString(object):
+ def __init__(self, s):
+ raise ImportError("couldn't import psycopg2.extensions.QuotedString")
+
+from .. import errors
+from ..compat import as_text
+from ..vertica import messages
+from ..vertica.column import Column
logger = logging.getLogger('vertica')
+UTF_8 = 'utf-8'
+
+if six.PY2:
+ # noinspection PyUnresolvedReferences
+ file_type = (IOBase, file)
+elif six.PY3:
+ file_type = (IOBase,)
+
+
class Cursor(object):
- - def __init__(self, connection, cursor_type=None, unicode_error='strict'):
+ # 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)
+
+ def __init__(self, connection, cursor_type=None, unicode_error=None):
self.connection = connection
self.cursor_type = cursor_type
- - self.unicode_error = unicode_error
+ self.unicode_error = unicode_error if unicode_error is not None else 'strict'
self._closed = False
self._message = None
+ self.operation = None
self.error = None
#
- - # dbApi properties
+ # dbapi properties
#
self.description = None
self.rowcount = -1
self.arraysize = 1
+ #############################################
+ # supporting `with` statements
+ #############################################
def __enter__(self):
return self
def __exit__(self, type_, value, traceback):
self.close()
- - #
- - # dbApi methods
- - #
- -
+ #############################################
+ # dbapi methods
+ #############################################
+ # noinspection PyMethodMayBeStatic
def callproc(self, procname, parameters=None):
raise errors.NotSupportedError('Cursor.callproc() is not implemented')
def close(self):
self._closed = True
+ def cancel(self):
+ if self.closed():
+ raise errors.Error('Cursor is closed')
+
+ self.connection.close()
+
def execute(self, operation, parameters=None):
+ self.operation = as_text(operation)
+
if self.closed():
raise errors.Error('Cursor is closed')
self.flush_to_query_ready()
if parameters:
- - # # optional requirement
- - import six
- - from psycopg2.extensions import adapt
- -
- - if isinstance(parameters, dict):
- - for key in parameters:
- - param = parameters[key]
- - # Make sure adapt() behaves properly
- - if self.is_stringy(param) and six.PY2:
- - param = param.encode('utf8')
- - v = adapt(param).getquoted()
- -
- - # Using a regex with word boundary to correctly handle params with similar names
- - # such as :s and :start
- - match_str = u':%s\\b' % str(key)
- - operation = re.sub(match_str, v.decode('utf-8'), operation, flags=re.UNICODE)
- - elif isinstance(parameters, tuple):
- - tlist = []
- - for param in parameters:
- - if self.is_stringy(param) and six.PY2:
- - param = param.encode('utf8')
- - v = adapt(param).getquoted()
- - tlist.append(v.decode('utf-8'))
- -
- - operation = operation % tuple(tlist)
- - else:
- - raise errors.Error("Argument 'parameters' must be dict or tuple")
+ # TODO: quote = True for backward compatibility. see if should be False.
+ operation = self.format_operation_with_parameters(operation, parameters, quote=True)
self.rowcount = -1
self.connection.write(messages.Query(operation))
# read messages until we hit an Error, DataRow or ReadyForQuery
+ self._message = self.connection.read_message()
while True:
- - message = self.connection.read_message()
- - # save the message because there's no way to undo the read
- - self._message = message
- - if isinstance(message, messages.ErrorResponse):
- - raise errors.QueryError.from_error_response(message, operation)
- - elif isinstance(message, messages.RowDescription):
- - self.description = list(map(lambda fd: Column(fd, self.unicode_error), message.fields))
- - elif isinstance(message, messages.DataRow):
+ if isinstance(self._message, messages.ErrorResponse):
+ raise errors.QueryError.from_error_response(self._message, operation)
+ elif isinstance(self._message, messages.RowDescription):
+ self.description = [Column(fd, self.unicode_error) for fd in self._message.fields]
+ elif isinstance(self._message, messages.DataRow):
+ break
+ elif isinstance(self._message, messages.ReadyForQuery):
break
- - elif isinstance(message, messages.ReadyForQuery):
+ elif isinstance(self._message, messages.CommandComplete):
break
else:
- - self.connection.process_message(message)
+ self.connection.process_message(self._message)
+ self._message = self.connection.read_message()
+
+ def executemany(self, operation, seq_of_parameters):
+ operation = as_text(operation)
+
+ if not isinstance(seq_of_parameters, (list, tuple)):
+ raise TypeError("seq_of_parameters should be list/tuple")
+
+ 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)
+
+ variables = ",".join([variable.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)
+ 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)
- - def is_stringy(self, s):
- - try:
- - # python 2 case
- - return isinstance(s, basestring)
- - except NameError:
- - # python 3 case
- - return isinstance(s, str)
+ self.copy(copy_statement, data)
+
+ else:
+ raise NotImplementedError(
+ "executemany is implemented for simple INSERT statements only")
def fetchone(self):
- - if isinstance(self._message, messages.DataRow):
- - if self.rowcount == -1:
- - self.rowcount = 1
+ while True:
+ if isinstance(self._message, messages.DataRow):
+ if self.rowcount == -1:
+ self.rowcount = 1
+ else:
+ self.rowcount += 1
+ row = self.row_formatter(self._message)
+ # fetch next message
+ self._message = self.connection.read_message()
+ return row
+ elif isinstance(self._message, messages.ReadyForQuery):
+ return None
+ elif isinstance(self._message, messages.CommandComplete):
+ return None
else:
- - self.rowcount += 1
+ self.connection.process_message(self._message)
- - row = self.row_formatter(self._message)
- - # fetch next message
self._message = self.connection.read_message()
- - return row
- - elif isinstance(self._message, messages.ReadyForQuery):
- - return None
- - elif isinstance(self._message, messages.CommandComplete):
- - return None
- - else:
- - self.connection.process_message(self._message)
def iterate(self):
row = self.fetchone()
@@ -155,7 +201,7 @@
self.flush_to_command_complete()
if self._message is None:
- - return None
+ return False
elif isinstance(self._message, messages.CommandComplete):
# there might be another set, read next message to find out
self._message = self.connection.read_message()
@@ -164,29 +210,31 @@
self._message = self.connection.read_message()
return True
elif isinstance(self._message, messages.ReadyForQuery):
- - return None
+ return False
+ elif isinstance(self._message, messages.ErrorResponse):
+ raise errors.QueryError.from_error_response(self._message, self.operation)
else:
- - raise errors.Error('Unexpected nextset() state after CommandComplete: ' + str(self._message))
+ raise errors.Error(
+ 'Unexpected nextset() state after CommandComplete: {0}'.format(self._message))
elif isinstance(self._message, messages.ReadyForQuery):
# no more sets left to be read
- - return None
+ return False
else:
- - raise errors.Error('Unexpected nextset() state: ' + str(self._message))
- -
+ raise errors.Error('Unexpected nextset() state: {0}'.format(self._message))
- - def setinputsizes(self):
+ def setinputsizes(self, sizes):
pass
def setoutputsize(self, size, column=None):
pass
- - #
- - # Non dbApi methods
- - #
+ #############################################
+ # non-dbapi methods
+ #############################################
def flush_to_query_ready(self):
- - # if the last message isnt empty or ReadyForQuery, read all remaining messages
- - if(self._message is None
- - or isinstance(self._message, messages.ReadyForQuery)):
+ # if the last message isn't empty or ReadyForQuery, read all remaining messages
+ if self._message is None \
+ or isinstance(self._message, messages.ReadyForQuery):
return
while True:
@@ -197,10 +245,9 @@
break
def flush_to_command_complete(self):
- - # if the last message isnt empty or CommandComplete, read messages until it is
- - if(self._message is None
- - or isinstance(self._message, messages.ReadyForQuery)
- - or isinstance(self._message, messages.CommandComplete)):
+ # if the last message isn't empty or CommandComplete, read messages until it is
+ if self._message is None or isinstance(self._message, (messages.ReadyForQuery,
+ messages.CommandComplete)):
return
while True:
@@ -209,20 +256,31 @@
self._message = message
break
- -
- - # example:
- - #
- - # with open("/tmp/file.csv", "rb") as fs:
- - # cursor.copy("COPY table(field1,field2) FROM STDIN DELIMITER ',' ENCLOSED BY '\"'", fs, buffer_size=65536)
- - #
- -
def copy(self, sql, data, **kwargs):
+ """
+
+ EXAMPLE:
+ >> with open("/tmp/file.csv", "rb") as fs:
+ >> cursor.copy("COPY table(field1,field2) FROM STDIN DELIMITER ',' ENCLOSED BY '\"'",
+ >> fs, buffer_size=65536)
+
+ """
+ sql = as_text(sql)
if self.closed():
raise errors.Error('Cursor is closed')
self.flush_to_query_ready()
+ if isinstance(data, binary_type):
+ stream = BytesIO(data)
+ elif isinstance(data, text_type):
+ stream = StringIO(data)
+ elif isinstance(data, file_type):
+ stream = data
+ else:
+ raise TypeError("Not valid type of data {0}".format(type(data)))
+
self.connection.write(messages.Query(sql))
while True:
@@ -232,29 +290,22 @@
raise errors.QueryError.from_error_response(message, sql)
self.connection.process_message(message=message)
+
if isinstance(message, messages.ReadyForQuery):
break
elif isinstance(message, messages.CopyInResponse):
- -
- - #write stuff
- - if not hasattr(data, "read"):
- - self.connection.write(messages.CopyData(data))
- - else:
- - # treat data as stream
- - self.connection.write(messages.CopyStream(data, **kwargs))
- -
+ self.connection.write(messages.CopyStream(stream, **kwargs))
self.connection.write(messages.CopyDone())
if self.error is not None:
raise self.error
- - #
- - # Internal
- - #
- -
def closed(self):
return self._closed or self.connection.closed()
+ #############################################
+ # internal
+ #############################################
def row_formatter(self, row_data):
if self.cursor_type is None:
return self.format_row_as_array(row_data)
@@ -263,7 +314,7 @@
elif self.cursor_type in (dict, 'dict'):
return self.format_row_as_dict(row_data)
else:
- - raise Exception('Unrecognized cursor_type: %r' % self.cursor_type)
+ raise TypeError('Unrecognized cursor_type: {0}'.format(self.cursor_type))
def format_row_as_dict(self, row_data):
return OrderedDict(
@@ -274,3 +325,49 @@
def format_row_as_array(self, row_data):
return [self.description[idx].convert(value)
for idx, value in enumerate(row_data.values)]
+
+ # noinspection PyArgumentList
+ def format_operation_with_parameters(self, operation, parameters, quote=True):
+ operation = as_text(operation)
+
+ if isinstance(parameters, dict):
+ for key, param in six.iteritems(parameters):
+ if not isinstance(key, string_types):
+ key = str(key)
+ key = as_text(key)
+
+ if isinstance(param, string_types):
+ param = as_text(param)
+ if quote:
+ param = self.format_quote(param)
+ else:
+ param = str(param)
+ value = as_text(param)
+
+ # Using a regex with word boundary to correctly handle params with similar names
+ # such as :s and :start
+ match_str = u":{0}\\b".format(key)
+ operation = re.sub(match_str, value, operation, flags=re.U)
+
+ elif isinstance(parameters, (tuple, list)):
+ tlist = []
+ for param in parameters:
+ if isinstance(param, string_types):
+ param = as_text(param)
+ if quote:
+ param = self.format_quote(param)
+ else:
+ param = str(param)
+ value = as_text(param)
+
+ tlist.append(value)
+
+ operation = operation % tuple(tlist)
+ else:
+ raise errors.Error("Argument 'parameters' must be dict or tuple")
+
+ return operation
+
+ def format_quote(self, param):
+ # TODO Make sure adapt() behaves properly
+ return QuotedString(param.encode(UTF_8, self.unicode_error)).getquoted()
diff -Nru python-vertica-0.6.8/vertica_python/vertica/messages/backend_messages/authentication.py python-vertica-0.7.1/vertica_python/vertica/messages/backend_messages/authentication.py
- --- python-vertica-0.6.8/vertica_python/vertica/messages/backend_messages/authentication.py 2016-10-06 23:30:16.000000000 +0200
+++ python-vertica-0.7.1/vertica_python/vertica/messages/backend_messages/authentication.py 2017-05-14 00:40:53.000000000 +0200
@@ -1,11 +1,12 @@
- -
+from __future__ import print_function, division, absolute_import
from struct import unpack
- -from vertica_python.vertica.messages.message import BackendMessage
+from ..message import BackendMessage
class Authentication(BackendMessage):
+ message_id = b'R'
OK = 0
KERBEROS_V5 = 2
@@ -18,6 +19,7 @@
SSPI = 9
def __init__(self, data):
+ BackendMessage.__init__(self)
unpacked = unpack('!I{0}s'.format(len(data) - 4), data)
self.code = unpacked[0]
other = unpacked[1::][0]
@@ -27,4 +29,4 @@
self.auth_data = other
- -Authentication._message_id(b'R')
+BackendMessage.register(Authentication)
diff -Nru python-vertica-0.6.8/vertica_python/vertica/messages/backend_messages/backend_key_data.py python-vertica-0.7.1/vertica_python/vertica/messages/backend_messages/backend_key_data.py
- --- python-vertica-0.6.8/vertica_python/vertica/messages/backend_messages/backend_key_data.py 2016-10-06 23:30:16.000000000 +0200
+++ python-vertica-0.7.1/vertica_python/vertica/messages/backend_messages/backend_key_data.py 2017-05-14 00:40:53.000000000 +0200
@@ -1,16 +1,18 @@
- -
+from __future__ import print_function, division, absolute_import
from struct import unpack
- -from vertica_python.vertica.messages.message import BackendMessage
+from ..message import BackendMessage
class BackendKeyData(BackendMessage):
+ message_id = b'K'
def __init__(self, data):
+ BackendMessage.__init__(self)
unpacked = unpack('!2I', data)
self.pid = unpacked[0]
self.key = unpacked[1]
- -BackendKeyData._message_id(b'K')
+BackendMessage.register(BackendKeyData)
diff -Nru python-vertica-0.6.8/vertica_python/vertica/messages/backend_messages/bind_complete.py python-vertica-0.7.1/vertica_python/vertica/messages/backend_messages/bind_complete.py
- --- python-vertica-0.6.8/vertica_python/vertica/messages/backend_messages/bind_complete.py 2016-10-06 23:30:16.000000000 +0200
+++ python-vertica-0.7.1/vertica_python/vertica/messages/backend_messages/bind_complete.py 2017-05-14 00:40:53.000000000 +0200
@@ -1,10 +1,10 @@
+from __future__ import print_function, division, absolute_import
- -
- -from vertica_python.vertica.messages.message import BackendMessage
+from ..message import BackendMessage
class BindComplete(BackendMessage):
- - pass
+ message_id = b'2'
- -BindComplete._message_id(b'2')
+BackendMessage.register(BindComplete)
diff -Nru python-vertica-0.6.8/vertica_python/vertica/messages/backend_messages/close_complete.py python-vertica-0.7.1/vertica_python/vertica/messages/backend_messages/close_complete.py
- --- python-vertica-0.6.8/vertica_python/vertica/messages/backend_messages/close_complete.py 2016-10-06 23:30:16.000000000 +0200
+++ python-vertica-0.7.1/vertica_python/vertica/messages/backend_messages/close_complete.py 2017-05-14 00:40:53.000000000 +0200
@@ -1,10 +1,10 @@
+from __future__ import print_function, division, absolute_import
- -
- -from vertica_python.vertica.messages.message import BackendMessage
+from ..message import BackendMessage
class CloseComplete(BackendMessage):
- - pass
+ message_id = b'3'
- -CloseComplete._message_id(b'3')
+BackendMessage.register(CloseComplete)
diff -Nru python-vertica-0.6.8/vertica_python/vertica/messages/backend_messages/command_complete.py python-vertica-0.7.1/vertica_python/vertica/messages/backend_messages/command_complete.py
- --- python-vertica-0.6.8/vertica_python/vertica/messages/backend_messages/command_complete.py 2016-10-06 23:30:16.000000000 +0200
+++ python-vertica-0.7.1/vertica_python/vertica/messages/backend_messages/command_complete.py 2017-05-14 00:40:53.000000000 +0200
@@ -1,16 +1,17 @@
- -
+from __future__ import print_function, division, absolute_import
import re
from struct import unpack
- -from vertica_python.vertica.messages.message import BackendMessage
+from ..message import BackendMessage
class CommandComplete(BackendMessage):
+ message_id = b'C'
def __init__(self, data):
- -
+ BackendMessage.__init__(self)
data = unpack('{0}sx'.format(len(data) - 1), data)[0]
if re.match(b"INSERT", data) is not None:
@@ -29,4 +30,4 @@
self.tag = data
- -CommandComplete._message_id(b'C')
+BackendMessage.register(CommandComplete)
diff -Nru python-vertica-0.6.8/vertica_python/vertica/messages/backend_messages/copy_in_response.py python-vertica-0.7.1/vertica_python/vertica/messages/backend_messages/copy_in_response.py
- --- python-vertica-0.6.8/vertica_python/vertica/messages/backend_messages/copy_in_response.py 2016-10-06 23:30:16.000000000 +0200
+++ python-vertica-0.7.1/vertica_python/vertica/messages/backend_messages/copy_in_response.py 2017-05-14 00:40:53.000000000 +0200
@@ -1,16 +1,18 @@
- -
+from __future__ import print_function, division, absolute_import
from struct import unpack
- -from vertica_python.vertica.messages.message import BackendMessage
+from ..message import BackendMessage
class CopyInResponse(BackendMessage):
+ message_id = b'G'
def __init__(self, data):
- - values = unpack('!B{0}H'.format((len(data) - 1)//2), data)
+ BackendMessage.__init__(self)
+ values = unpack('!B{0}H'.format((len(data) - 1) // 2), data)
self.format = values[0]
self.column_formats = values[2::]
- -CopyInResponse._message_id(b'G')
+BackendMessage.register(CopyInResponse)
diff -Nru python-vertica-0.6.8/vertica_python/vertica/messages/backend_messages/data_row.py python-vertica-0.7.1/vertica_python/vertica/messages/backend_messages/data_row.py
- --- python-vertica-0.6.8/vertica_python/vertica/messages/backend_messages/data_row.py 2016-10-06 23:30:16.000000000 +0200
+++ python-vertica-0.7.1/vertica_python/vertica/messages/backend_messages/data_row.py 2017-05-14 00:40:53.000000000 +0200
@@ -1,14 +1,17 @@
+from __future__ import print_function, division, absolute_import
- -
- -from builtins import range
from struct import unpack, unpack_from
- -from vertica_python.vertica.messages.message import BackendMessage
+from six.moves import range
+
+from ..message import BackendMessage
class DataRow(BackendMessage):
+ message_id = b'D'
def __init__(self, data):
+ BackendMessage.__init__(self)
self.values = []
field_count = unpack('!H', data[0:2])[0]
pos = 2
@@ -26,4 +29,4 @@
pos += (4 + max(size, 0))
- -DataRow._message_id(b'D')
+BackendMessage.register(DataRow)
diff -Nru python-vertica-0.6.8/vertica_python/vertica/messages/backend_messages/empty_query_response.py python-vertica-0.7.1/vertica_python/vertica/messages/backend_messages/empty_query_response.py
- --- python-vertica-0.6.8/vertica_python/vertica/messages/backend_messages/empty_query_response.py 2016-10-06 23:30:16.000000000 +0200
+++ python-vertica-0.7.1/vertica_python/vertica/messages/backend_messages/empty_query_response.py 2017-05-14 00:40:53.000000000 +0200
@@ -1,10 +1,14 @@
+from __future__ import print_function, division, absolute_import
- -
- -from vertica_python.vertica.messages.message import BackendMessage
+from ..message import BackendMessage
class EmptyQueryResponse(BackendMessage):
- - pass
+ message_id = b'I'
+
+ def __init__(self, data=None):
+ BackendMessage.__init__(self)
+ self.data = data
- -EmptyQueryResponse._message_id(b'I')
+BackendMessage.register(EmptyQueryResponse)
diff -Nru python-vertica-0.6.8/vertica_python/vertica/messages/backend_messages/error_response.py python-vertica-0.7.1/vertica_python/vertica/messages/backend_messages/error_response.py
- --- python-vertica-0.6.8/vertica_python/vertica/messages/backend_messages/error_response.py 2016-10-06 23:30:16.000000000 +0200
+++ python-vertica-0.7.1/vertica_python/vertica/messages/backend_messages/error_response.py 2017-05-14 00:40:53.000000000 +0200
@@ -1,11 +1,11 @@
+from __future__ import print_function, division, absolute_import
- -
- -from vertica_python.vertica.messages.message import BackendMessage
+from ..message import BackendMessage
from vertica_python.vertica.messages.backend_messages.notice_response import NoticeResponse
class ErrorResponse(NoticeResponse, BackendMessage):
- - pass
+ message_id = b'E'
- -ErrorResponse._message_id(b'E')
+BackendMessage.register(ErrorResponse)
diff -Nru python-vertica-0.6.8/vertica_python/vertica/messages/backend_messages/__init__.py python-vertica-0.7.1/vertica_python/vertica/messages/backend_messages/__init__.py
- --- python-vertica-0.6.8/vertica_python/vertica/messages/backend_messages/__init__.py 2016-10-06 23:30:16.000000000 +0200
+++ python-vertica-0.7.1/vertica_python/vertica/messages/backend_messages/__init__.py 2017-05-14 00:40:53.000000000 +0200
@@ -0,0 +1,25 @@
+from __future__ import print_function, division, absolute_import
+
+from .authentication import Authentication
+from .backend_key_data import BackendKeyData
+from .bind_complete import BindComplete
+from .close_complete import CloseComplete
+from .command_complete import CommandComplete
+from .copy_in_response import CopyInResponse
+from .data_row import DataRow
+from .empty_query_response import EmptyQueryResponse
+from .error_response import ErrorResponse
+from .no_data import NoData
+from .notice_response import NoticeResponse
+from .parameter_description import ParameterDescription
+from .parameter_status import ParameterStatus
+from .parse_complete import ParseComplete
+from .portal_suspended import PortalSuspended
+from .ready_for_query import ReadyForQuery
+from .row_description import RowDescription
+from .unknown import Unknown
+
+__all__ = ['RowDescription', 'ReadyForQuery', 'PortalSuspended', 'ParseComplete', 'ParameterStatus',
+ 'NoticeResponse', 'NoData', 'ErrorResponse', 'EmptyQueryResponse', 'DataRow',
+ 'CopyInResponse', 'CommandComplete', 'CloseComplete', 'BindComplete', 'Authentication',
+ 'BackendKeyData', 'ParameterDescription', 'Unknown']
diff -Nru python-vertica-0.6.8/vertica_python/vertica/messages/backend_messages/no_data.py python-vertica-0.7.1/vertica_python/vertica/messages/backend_messages/no_data.py
- --- python-vertica-0.6.8/vertica_python/vertica/messages/backend_messages/no_data.py 2016-10-06 23:30:16.000000000 +0200
+++ python-vertica-0.7.1/vertica_python/vertica/messages/backend_messages/no_data.py 2017-05-14 00:40:53.000000000 +0200
@@ -1,10 +1,10 @@
+from __future__ import print_function, division, absolute_import
- -
- -from vertica_python.vertica.messages.message import BackendMessage
+from ..message import BackendMessage
class NoData(BackendMessage):
- - pass
+ message_id = b'n'
- -NoData._message_id(b'n')
+BackendMessage.register(NoData)
diff -Nru python-vertica-0.6.8/vertica_python/vertica/messages/backend_messages/notice_response.py python-vertica-0.7.1/vertica_python/vertica/messages/backend_messages/notice_response.py
- --- python-vertica-0.6.8/vertica_python/vertica/messages/backend_messages/notice_response.py 2016-10-06 23:30:16.000000000 +0200
+++ python-vertica-0.7.1/vertica_python/vertica/messages/backend_messages/notice_response.py 2017-05-14 00:40:53.000000000 +0200
@@ -1,11 +1,12 @@
- -
+from __future__ import print_function, division, absolute_import
from struct import unpack_from
- -from vertica_python.vertica.messages.message import BackendMessage
+from ..message import BackendMessage
class NoticeResponse(BackendMessage):
+ message_id = b'N'
FIELDS_DEFINITIONS = [
{'type': b'q', 'name': "Internal Query", 'method': 'internal_query'},
@@ -22,13 +23,8 @@
{'type': b'L', 'name': "Line", 'method': 'line'}
]
- - def FIELDS(self):
- - pairs = []
- - for field in self.FIELDS_DEFINITIONS:
- - pairs.append((field['type'], field['name']))
- - return dict(pairs)
- -
def __init__(self, data):
+ BackendMessage.__init__(self)
self.values = {}
pos = 0
@@ -40,7 +36,7 @@
key = unpacked[0]
value = unpacked[1]
- - self.values[self.FIELDS()[key]] = value
+ self.values[self.fields()[key]] = value
pos += (len(value) + 2)
# May want to break out into a function at some point
@@ -48,6 +44,14 @@
if self.values.get(field_def['name'], None) is not None:
setattr(self, field_def['method'], self.values[field_def['name']])
+ def fields(self):
+ # was FIELDS before
+ # TODO verify that it doesn't break anything
+ pairs = []
+ for field in self.FIELDS_DEFINITIONS:
+ pairs.append((field['type'], field['name']))
+ return dict(pairs)
+
def error_message(self):
ordered = []
for field in self.FIELDS_DEFINITIONS:
@@ -56,4 +60,4 @@
return ', '.join(ordered)
- -NoticeResponse._message_id(b'N')
+BackendMessage.register(NoticeResponse)
diff -Nru python-vertica-0.6.8/vertica_python/vertica/messages/backend_messages/parameter_description.py python-vertica-0.7.1/vertica_python/vertica/messages/backend_messages/parameter_description.py
- --- python-vertica-0.6.8/vertica_python/vertica/messages/backend_messages/parameter_description.py 2016-10-06 23:30:16.000000000 +0200
+++ python-vertica-0.7.1/vertica_python/vertica/messages/backend_messages/parameter_description.py 2017-05-14 00:40:53.000000000 +0200
@@ -1,17 +1,19 @@
- -
+from __future__ import print_function, division, absolute_import
from struct import unpack, unpack_from
- -from vertica_python.vertica.messages.message import BackendMessage
+from ..message import BackendMessage
from vertica_python.vertica.column import Column
class ParameterDescription(BackendMessage):
+ message_id = b't'
def __init__(self, data):
+ BackendMessage.__init__(self)
parameter_count = unpack('!H', data)[0]
parameter_type_ids = unpack_from("!{0}N".format(parameter_count), data, 2)
- - self.parameter_types = [Column.data_types[dtid] for dtid in parameter_type_ids]
+ self.parameter_types = [Column.data_types()[dtid] for dtid in parameter_type_ids]
- -ParameterDescription._message_id(b't')
+BackendMessage.register(ParameterDescription)
diff -Nru python-vertica-0.6.8/vertica_python/vertica/messages/backend_messages/parameter_status.py python-vertica-0.7.1/vertica_python/vertica/messages/backend_messages/parameter_status.py
- --- python-vertica-0.6.8/vertica_python/vertica/messages/backend_messages/parameter_status.py 2016-10-06 23:30:16.000000000 +0200
+++ python-vertica-0.7.1/vertica_python/vertica/messages/backend_messages/parameter_status.py 2017-05-14 00:40:53.000000000 +0200
@@ -1,17 +1,19 @@
- -
+from __future__ import print_function, division, absolute_import
from struct import unpack
- -from vertica_python.vertica.messages.message import BackendMessage
+from ..message import BackendMessage
class ParameterStatus(BackendMessage):
+ message_id = b'S'
def __init__(self, data):
+ BackendMessage.__init__(self)
null_byte = data.find(b'\x00')
unpacked = unpack('{0}sx{1}sx'.format(null_byte - 1, len(data) - null_byte - 1), data)
self.name = unpacked[0]
self.value = unpacked[1]
- -ParameterStatus._message_id(b'S')
+BackendMessage.register(ParameterStatus)
diff -Nru python-vertica-0.6.8/vertica_python/vertica/messages/backend_messages/parse_complete.py python-vertica-0.7.1/vertica_python/vertica/messages/backend_messages/parse_complete.py
- --- python-vertica-0.6.8/vertica_python/vertica/messages/backend_messages/parse_complete.py 2016-10-06 23:30:16.000000000 +0200
+++ python-vertica-0.7.1/vertica_python/vertica/messages/backend_messages/parse_complete.py 2017-05-14 00:40:53.000000000 +0200
@@ -1,10 +1,10 @@
+from __future__ import print_function, division, absolute_import
- -
- -from vertica_python.vertica.messages.message import BackendMessage
+from ..message import BackendMessage
class ParseComplete(BackendMessage):
- - pass
+ message_id = b'1'
- -ParseComplete._message_id(b'1')
+BackendMessage.register(ParseComplete)
diff -Nru python-vertica-0.6.8/vertica_python/vertica/messages/backend_messages/portal_suspended.py python-vertica-0.7.1/vertica_python/vertica/messages/backend_messages/portal_suspended.py
- --- python-vertica-0.6.8/vertica_python/vertica/messages/backend_messages/portal_suspended.py 2016-10-06 23:30:16.000000000 +0200
+++ python-vertica-0.7.1/vertica_python/vertica/messages/backend_messages/portal_suspended.py 2017-05-14 00:40:53.000000000 +0200
@@ -1,10 +1,10 @@
+from __future__ import print_function, division, absolute_import
- -
- -from vertica_python.vertica.messages.message import BackendMessage
+from ..message import BackendMessage
class PortalSuspended(BackendMessage):
- - pass
+ message_id = b's'
- -PortalSuspended._message_id(b's')
+BackendMessage.register(PortalSuspended)
diff -Nru python-vertica-0.6.8/vertica_python/vertica/messages/backend_messages/ready_for_query.py python-vertica-0.7.1/vertica_python/vertica/messages/backend_messages/ready_for_query.py
- --- python-vertica-0.6.8/vertica_python/vertica/messages/backend_messages/ready_for_query.py 2016-10-06 23:30:16.000000000 +0200
+++ python-vertica-0.7.1/vertica_python/vertica/messages/backend_messages/ready_for_query.py 2017-05-14 00:40:53.000000000 +0200
@@ -1,11 +1,12 @@
- -
+from __future__ import print_function, division, absolute_import
from struct import unpack
- -from vertica_python.vertica.messages.message import BackendMessage
+from ..message import BackendMessage
class ReadyForQuery(BackendMessage):
+ message_id = b'Z'
STATUSES = {
b'I': 'no_transaction',
@@ -14,7 +15,8 @@
}
def __init__(self, data):
+ BackendMessage.__init__(self)
self.transaction_status = self.STATUSES[unpack('c', data)[0]]
- -ReadyForQuery._message_id(b'Z')
+BackendMessage.register(ReadyForQuery)
diff -Nru python-vertica-0.6.8/vertica_python/vertica/messages/backend_messages/row_description.py python-vertica-0.7.1/vertica_python/vertica/messages/backend_messages/row_description.py
- --- python-vertica-0.6.8/vertica_python/vertica/messages/backend_messages/row_description.py 2016-10-06 23:30:16.000000000 +0200
+++ python-vertica-0.7.1/vertica_python/vertica/messages/backend_messages/row_description.py 2017-05-14 00:40:53.000000000 +0200
@@ -1,20 +1,24 @@
- -
+from __future__ import print_function, division, absolute_import
from struct import unpack, unpack_from
- -from vertica_python.vertica.messages.message import BackendMessage
+from six.moves import range
+
+from ..message import BackendMessage
class RowDescription(BackendMessage):
+ message_id = b'T'
def __init__(self, data):
- -
+ BackendMessage.__init__(self)
self.fields = []
field_count = unpack('!H', data[0:2])[0]
pos = 2
for i in range(field_count):
- - field_info = unpack_from("!{0}sxIHIHIH".format(data.find(b'\x00', pos) - pos), data, pos)
+ field_info = unpack_from("!{0}sxIHIHIH".format(data.find(b'\x00', pos) - pos), data,
+ pos)
self.fields.append({
'name': field_info[0],
'table_oid': field_info[1],
@@ -28,4 +32,4 @@
pos += 19 + len(field_info[0])
- -RowDescription._message_id(b'T')
+BackendMessage.register(RowDescription)
diff -Nru python-vertica-0.6.8/vertica_python/vertica/messages/backend_messages/unknown.py python-vertica-0.7.1/vertica_python/vertica/messages/backend_messages/unknown.py
- --- python-vertica-0.6.8/vertica_python/vertica/messages/backend_messages/unknown.py 2016-10-06 23:30:16.000000000 +0200
+++ python-vertica-0.7.1/vertica_python/vertica/messages/backend_messages/unknown.py 2017-05-14 00:40:53.000000000 +0200
@@ -1,10 +1,14 @@
+from __future__ import print_function, division, absolute_import
- -
- -from vertica_python.vertica.messages.message import BackendMessage
+from ..message import BackendMessage
class Unknown(BackendMessage):
- -
def __init__(self, message_id, data):
- - self.message_id = message_id
+ BackendMessage.__init__(self)
+ self._message_id = message_id
self.data = data
+
+ @property
+ def message_id(self):
+ return self._message_id
diff -Nru python-vertica-0.6.8/vertica_python/vertica/messages/frontend_messages/bind.py python-vertica-0.7.1/vertica_python/vertica/messages/frontend_messages/bind.py
- --- python-vertica-0.6.8/vertica_python/vertica/messages/frontend_messages/bind.py 2016-10-06 23:30:16.000000000 +0200
+++ python-vertica-0.7.1/vertica_python/vertica/messages/frontend_messages/bind.py 2017-05-14 00:40:53.000000000 +0200
@@ -1,24 +1,29 @@
+from __future__ import print_function, division, absolute_import
+
from struct import pack
- -from vertica_python.vertica.messages.message import FrontendMessage
+from ..message import BulkFrontendMessage
- -class Bind(FrontendMessage):
+class Bind(BulkFrontendMessage):
+ message_id = b'B'
def __init__(self, portal_name, prepared_statement_name, parameter_values):
- - self.portal_name = portal_name
- - self.prepared_statement_name = prepared_statement_name
- - self.parameter_values = parameter_values
- -
- - def to_bytes(self):
- - bytes_ = pack('!{0}sx{1}sxHH'.format(len(self.portal_name), len(self.prepared_statement_name)), self.portal_name, self.prepared_statement_name, 0, len(self.parameter_values))
- - for val in self.parameter_values.values():
+ BulkFrontendMessage.__init__(self)
+ self._portal_name = portal_name
+ self._prepared_statement_name = prepared_statement_name
+ self._parameter_values = parameter_values
+
+ def read_bytes(self):
+ bytes_ = pack('!{0}sx{1}sxHH'.format(
+ len(self._portal_name), len(self._prepared_statement_name)),
+ self._portal_name, self._prepared_statement_name, 0, len(self._parameter_values))
+
+ for val in self._parameter_values.values():
if val is None:
bytes_ += pack('!I', [-1])
else:
bytes_ += pack('!I{0}s'.format(len(val)), len(val), val)
bytes_ += pack('!H', [0])
- - return self.message_string(bytes_)
- -
- -Bind._message_id(b'B')
+ return bytes_
diff -Nru python-vertica-0.6.8/vertica_python/vertica/messages/frontend_messages/cancel_request.py python-vertica-0.7.1/vertica_python/vertica/messages/frontend_messages/cancel_request.py
- --- python-vertica-0.6.8/vertica_python/vertica/messages/frontend_messages/cancel_request.py 2016-10-06 23:30:16.000000000 +0200
+++ python-vertica-0.7.1/vertica_python/vertica/messages/frontend_messages/cancel_request.py 2017-05-14 00:40:53.000000000 +0200
@@ -1,18 +1,18 @@
- -
+from __future__ import print_function, division, absolute_import
from struct import pack
- -from vertica_python.vertica.messages.message import FrontendMessage
+from ..message import BulkFrontendMessage
- -class CancelRequest(FrontendMessage):
+class CancelRequest(BulkFrontendMessage):
+ message_id = None
def __init__(self, backend_pid, backend_key):
- - self.backend_pid = backend_pid
- - self.backend_key = backend_key
- -
- - def to_bytes(self):
- - return self.message_string(pack('!3I', 80877102, self.backend_pid, self.backend_key))
- -
- -
- -CancelRequest._message_id(None)
+ BulkFrontendMessage.__init__(self)
+ self._backend_pid = backend_pid
+ self._backend_key = backend_key
+
+ def read_bytes(self):
+ bytes_ = pack('!3I', 80877102, self._backend_pid, self._backend_key)
+ return bytes_
diff -Nru python-vertica-0.6.8/vertica_python/vertica/messages/frontend_messages/close.py python-vertica-0.7.1/vertica_python/vertica/messages/frontend_messages/close.py
- --- python-vertica-0.6.8/vertica_python/vertica/messages/frontend_messages/close.py 2016-10-06 23:30:16.000000000 +0200
+++ python-vertica-0.7.1/vertica_python/vertica/messages/frontend_messages/close.py 2017-05-14 00:40:53.000000000 +0200
@@ -1,24 +1,26 @@
- -
+from __future__ import print_function, division, absolute_import
from struct import pack
- -from vertica_python.vertica.messages.message import FrontendMessage
+from ..message import BulkFrontendMessage
- -class Close(FrontendMessage):
+class Close(BulkFrontendMessage):
+ message_id = b'C'
def __init__(self, close_type, close_name):
- - self.close_name = close_name
+ BulkFrontendMessage.__init__(self)
+
+ self._close_name = close_name
if close_type == 'portal':
- - self.close_type = 'P'
+ self._close_type = 'P'
elif close_type == 'prepared_statement':
- - self.close_type = 'S'
+ self._close_type = 'S'
else:
- - raise ValueError("{0} is not a valid close_type. Must be either portal or prepared_statement".format(close_type))
- -
- - def to_bytes(self):
- - return self.message_string(pack('c{0}sx'.format(len(self.close_name)), self.close_type, self.close_name))
- -
+ raise ValueError("{0} is not a valid close_type. "
+ "Must be either portal or prepared_statement".format(close_type))
- -Close._message_id(b'C')
+ def read_bytes(self):
+ bytes_ = pack('c{0}sx'.format(len(self._close_name)), self._close_type, self._close_name)
+ return bytes_
diff -Nru python-vertica-0.6.8/vertica_python/vertica/messages/frontend_messages/copy_data.py python-vertica-0.7.1/vertica_python/vertica/messages/frontend_messages/copy_data.py
- --- python-vertica-0.6.8/vertica_python/vertica/messages/frontend_messages/copy_data.py 2016-10-06 23:30:16.000000000 +0200
+++ python-vertica-0.7.1/vertica_python/vertica/messages/frontend_messages/copy_data.py 2017-05-14 00:40:53.000000000 +0200
@@ -1,19 +1,26 @@
+from __future__ import print_function, division, absolute_import
+from six import text_type, binary_type
- -from struct import pack
+from ..message import BulkFrontendMessage
- -from vertica_python.vertica.messages.message import FrontendMessage
+UTF_8 = 'utf-8'
- -class CopyData(FrontendMessage):
+class CopyData(BulkFrontendMessage):
+ message_id = b'd'
- - def __init__(self, data):
- - self.data = data
+ def __init__(self, data, unicode_error='strict'):
+ BulkFrontendMessage.__init__(self)
+ self._unicode_error = unicode_error
+ if isinstance(data, text_type):
+ self._data = self._data.encode(encoding=UTF_8, errors=self._unicode_error)
+ elif isinstance(data, binary_type):
+ self._data = data
+ else:
+ raise TypeError("should be string or bytes")
- - def to_bytes(self):
+ def read_bytes(self):
# to deal with unicode strings
- - encoded = self.data.encode('utf-8')
- - return self.message_string(pack('{0}s'.format(len(encoded)), encoded))
- -
- -
- -CopyData._message_id(b'd')
+ bytes_ = self._data
+ return bytes_
diff -Nru python-vertica-0.6.8/vertica_python/vertica/messages/frontend_messages/copy_done.py python-vertica-0.7.1/vertica_python/vertica/messages/frontend_messages/copy_done.py
- --- python-vertica-0.6.8/vertica_python/vertica/messages/frontend_messages/copy_done.py 2016-10-06 23:30:16.000000000 +0200
+++ python-vertica-0.7.1/vertica_python/vertica/messages/frontend_messages/copy_done.py 2017-05-14 00:40:53.000000000 +0200
@@ -1,10 +1,8 @@
+from __future__ import print_function, division, absolute_import
+from ..message import BulkFrontendMessage
- -from vertica_python.vertica.messages.message import FrontendMessage
+class CopyDone(BulkFrontendMessage):
+ message_id = b'c'
- -class CopyDone(FrontendMessage):
- - pass
- -
- -
- -CopyDone._message_id(b'c')
diff -Nru python-vertica-0.6.8/vertica_python/vertica/messages/frontend_messages/copy_fail.py python-vertica-0.7.1/vertica_python/vertica/messages/frontend_messages/copy_fail.py
- --- python-vertica-0.6.8/vertica_python/vertica/messages/frontend_messages/copy_fail.py 2016-10-06 23:30:16.000000000 +0200
+++ python-vertica-0.7.1/vertica_python/vertica/messages/frontend_messages/copy_fail.py 2017-05-14 00:40:53.000000000 +0200
@@ -1,17 +1,17 @@
- -
+from __future__ import print_function, division, absolute_import
from struct import pack
- -from vertica_python.vertica.messages.message import FrontendMessage
+from ..message import BulkFrontendMessage
- -class CopyFail(FrontendMessage):
+class CopyFail(BulkFrontendMessage):
+ message_id = b'f'
def __init__(self, error_message):
- - self.error_message = error_message
- -
- - def to_bytes(self):
- - return self.message_string(pack('{0}sx'.format(len(self.error_message)), self.error_message))
- -
+ BulkFrontendMessage.__init__(self)
+ self._error_message = error_message
- -CopyFail._message_id(b'f')
+ def read_bytes(self):
+ bytes_ = pack('{0}sx'.format(len(self._error_message)), self._error_message)
+ return bytes_
diff -Nru python-vertica-0.6.8/vertica_python/vertica/messages/frontend_messages/copy_stream.py python-vertica-0.7.1/vertica_python/vertica/messages/frontend_messages/copy_stream.py
- --- python-vertica-0.6.8/vertica_python/vertica/messages/frontend_messages/copy_stream.py 2016-10-06 23:30:16.000000000 +0200
+++ python-vertica-0.7.1/vertica_python/vertica/messages/frontend_messages/copy_stream.py 2017-05-14 00:40:53.000000000 +0200
@@ -1,22 +1,34 @@
+from __future__ import print_function, division, absolute_import
+from six import text_type, binary_type
- -from struct import pack
+from ..message import StreamFrontendMessage
- -from vertica_python.vertica.messages.message import FrontendMessage
+DEFAULT_BUFFER_SIZE = 131072
- -class CopyStream(FrontendMessage):
+UTF_8 = 'utf-8'
- - def __init__(self, stream, buffer_size=131072):
- - self.stream = stream
- - self.bufsize = buffer_size
- - def read_bytes(self):
+class CopyStream(StreamFrontendMessage):
+ message_id = b'd'
- - data = self.stream.read(self.bufsize)
+ def __init__(self, stream, buffer_size=DEFAULT_BUFFER_SIZE, unicode_error='strict'):
+ StreamFrontendMessage.__init__(self)
+ self._stream = stream
+ self._unicode_error = unicode_error
+ self._buffer_size = buffer_size
- - if len(data) == 0:
- - return data
+ def stream_bytes(self):
+ while True:
+ chunk = self._stream.read(self._buffer_size)
+ if isinstance(chunk, text_type):
+ bytes_ = chunk.encode(encoding=UTF_8, errors=self._unicode_error)
+ elif isinstance(chunk, binary_type):
+ bytes_ = chunk
+ else:
+ raise TypeError("should be string or bytes")
- - return self.message_string(data)
+ if not chunk:
+ break
- -CopyStream._message_id(b'd')
+ yield bytes_
diff -Nru python-vertica-0.6.8/vertica_python/vertica/messages/frontend_messages/crypt_windows.py python-vertica-0.7.1/vertica_python/vertica/messages/frontend_messages/crypt_windows.py
- --- python-vertica-0.6.8/vertica_python/vertica/messages/frontend_messages/crypt_windows.py 2016-10-06 23:30:16.000000000 +0200
+++ python-vertica-0.7.1/vertica_python/vertica/messages/frontend_messages/crypt_windows.py 2017-05-14 00:40:53.000000000 +0200
@@ -1,4 +1,7 @@
#!/usr/bin/env python
+from __future__ import print_function, division, absolute_import
+
+from six.moves import range
# Initial permutation
IP = (
@@ -6,7 +9,7 @@
60, 52, 44, 36, 28, 20, 12, 4,
62, 54, 46, 38, 30, 22, 14, 6,
64, 56, 48, 40, 32, 24, 16, 8,
- - 57, 49, 41, 33, 25, 17, 9, 1,
+ 57, 49, 41, 33, 25, 17, 9, 1,
59, 51, 43, 35, 27, 19, 11, 3,
61, 53, 45, 37, 29, 21, 13, 5,
63, 55, 47, 39, 31, 23, 15, 7,
@@ -21,31 +24,31 @@
36, 4, 44, 12, 52, 20, 60, 28,
35, 3, 43, 11, 51, 19, 59, 27,
34, 2, 42, 10, 50, 18, 58, 26,
- - 33, 1, 41, 9, 49, 17, 57, 25,
+ 33, 1, 41, 9, 49, 17, 57, 25,
)
# Permuted-choice 1 from the key bits to yield C and D.
# Note that bits 8,16... are left out: They are intended for a parity check.
PC1_C = (
- - 57, 49, 41, 33, 25, 17, 9,
+ 57, 49, 41, 33, 25, 17, 9,
1, 58, 50, 42, 34, 26, 18,
- - 10, 2, 59, 51, 43, 35, 27,
- - 19, 11, 3, 60, 52, 44, 36,
+ 10, 2, 59, 51, 43, 35, 27,
+ 19, 11, 3, 60, 52, 44, 36,
)
PC1_D = (
63, 55, 47, 39, 31, 23, 15,
- - 7, 62, 54, 46, 38, 30, 22,
- - 14, 6, 61, 53, 45, 37, 29,
- - 21, 13, 5, 28, 20, 12, 4,
+ 7, 62, 54, 46, 38, 30, 22,
+ 14, 6, 61, 53, 45, 37, 29,
+ 21, 13, 5, 28, 20, 12, 4,
)
# Permuted-choice 2, to pick out the bits from the CD array that generate the
# key schedule.
PC2_C = (
- - 14, 17, 11, 24, 1, 5,
- - 3, 28, 15, 6, 21, 10,
- - 23, 19, 12, 4, 26, 8,
- - 16, 7, 27, 20, 13, 2,
+ 14, 17, 11, 24, 1, 5,
+ 3, 28, 15, 6, 21, 10,
+ 23, 19, 12, 4, 26, 8,
+ 16, 7, 27, 20, 13, 2,
)
PC2_D = (
41, 52, 31, 37, 47, 55,
@@ -64,78 +67,78 @@
# The E bit-selection table.
E = [0] * 48
e2 = (
- - 32, 1, 2, 3, 4, 5,
- - 4, 5, 6, 7, 8, 9,
- - 8, 9, 10, 11, 12, 13,
+ 32, 1, 2, 3, 4, 5,
+ 4, 5, 6, 7, 8, 9,
+ 8, 9, 10, 11, 12, 13,
12, 13, 14, 15, 16, 17,
16, 17, 18, 19, 20, 21,
20, 21, 22, 23, 24, 25,
24, 25, 26, 27, 28, 29,
- - 28, 29, 30, 31, 32, 1,
+ 28, 29, 30, 31, 32, 1,
)
# S-boxes.
S = (
(
- - 14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7,
- - 0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8,
- - 4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0,
- - 15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13
+ 14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7,
+ 0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8,
+ 4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0,
+ 15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13
),
(
- - 15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10,
- - 3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5,
- - 0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15,
- - 13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9
+ 15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10,
+ 3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5,
+ 0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15,
+ 13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9
),
(
- - 10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8,
- - 13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1,
- - 13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7,
- - 1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12
+ 10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8,
+ 13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1,
+ 13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7,
+ 1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12
),
(
- - 7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15,
- - 13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9,
- - 10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4,
- - 3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14
+ 7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15,
+ 13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9,
+ 10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4,
+ 3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14
),
(
- - 2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9,
- - 14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6,
- - 4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14,
- - 11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3
+ 2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9,
+ 14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6,
+ 4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14,
+ 11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3
),
(
- - 12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11,
- - 10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8,
- - 9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6,
- - 4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13
+ 12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11,
+ 10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8,
+ 9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6,
+ 4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13
),
(
- - 4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1,
- - 13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6,
- - 1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2,
- - 6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12
+ 4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1,
+ 13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6,
+ 1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2,
+ 6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12
),
(
- - 13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7,
- - 1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2,
- - 7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8,
- - 2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11
+ 13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7,
+ 1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2,
+ 7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8,
+ 2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11
)
)
# P is a permutation on the selected combination of the current L and key.
P = (
- - 16, 7, 20, 21,
+ 16, 7, 20, 21,
29, 12, 28, 17,
- - 1, 15, 23, 26,
- - 5, 18, 31, 10,
- - 2, 8, 24, 14,
- - 32, 27, 3, 9,
- - 19, 13, 30, 6,
- - 22, 11, 4, 25,
+ 1, 15, 23, 26,
+ 5, 18, 31, 10,
+ 2, 8, 24, 14,
+ 32, 27, 3, 9,
+ 19, 13, 30, 6,
+ 22, 11, 4, 25,
)
# The combination of the key and the input, before selection.
@@ -180,10 +183,11 @@
for i in range(48):
E[i] = e2[i]
+
def __encrypt(block):
global preS
- - left, right = [], [] # block in two halves
+ left, right = [], [] # block in two halves
f = [0] * 32
# First, permute the bits in the input
@@ -244,6 +248,7 @@
return block
+
def crypt(pw, salt):
iobuf = []
diff -Nru python-vertica-0.6.8/vertica_python/vertica/messages/frontend_messages/describe.py python-vertica-0.7.1/vertica_python/vertica/messages/frontend_messages/describe.py
- --- python-vertica-0.6.8/vertica_python/vertica/messages/frontend_messages/describe.py 2016-10-06 23:30:16.000000000 +0200
+++ python-vertica-0.7.1/vertica_python/vertica/messages/frontend_messages/describe.py 2017-05-14 00:40:53.000000000 +0200
@@ -1,24 +1,27 @@
- -
+from __future__ import print_function, division, absolute_import
from struct import pack
- -from vertica_python.vertica.messages.message import FrontendMessage
+from ..message import BulkFrontendMessage
- -class Describe(FrontendMessage):
+class Describe(BulkFrontendMessage):
+ message_id = b'D'
def __init__(self, describe_type, describe_name):
- - self.describe_name = describe_name
+ BulkFrontendMessage.__init__(self)
+
+ self._describe_name = describe_name
if describe_type == 'portal':
- - self.describe_type = 'P'
+ self._describe_type = 'P'
elif describe_type == 'prepared_statement':
- - self.describe_type = 'S'
+ self._describe_type = 'S'
else:
- - raise ValueError("{0} is not a valid describe_type. Must be either portal or prepared_statement".format(describe_type))
- -
- - def to_bytes(self):
- - return self.message_string(pack('c{0}sx'.format(len(self.describe_name)), self.describe_type, self.describe_name))
- -
+ raise ValueError("{0} is not a valid describe_type. "
+ "Must be either portal or prepared_statement".format(describe_type))
- -Describe._message_id(b'D')
+ def read_bytes(self):
+ bytes_ = pack('c{0}sx'.format(len(self._describe_name)), self._describe_type,
+ self._describe_name)
+ return bytes_
diff -Nru python-vertica-0.6.8/vertica_python/vertica/messages/frontend_messages/execute.py python-vertica-0.7.1/vertica_python/vertica/messages/frontend_messages/execute.py
- --- python-vertica-0.6.8/vertica_python/vertica/messages/frontend_messages/execute.py 2016-10-06 23:30:16.000000000 +0200
+++ python-vertica-0.7.1/vertica_python/vertica/messages/frontend_messages/execute.py 2017-05-14 00:40:53.000000000 +0200
@@ -1,18 +1,18 @@
- -
+from __future__ import print_function, division, absolute_import
from struct import pack
- -from vertica_python.vertica.messages.message import FrontendMessage
+from ..message import BulkFrontendMessage
- -class Execute(FrontendMessage):
+class Execute(BulkFrontendMessage):
+ message_id = b'E'
def __init__(self, portal_name, max_rows):
- - self.portal_name = portal_name
- - self.max_rows = max_rows
- -
- - def to_bytes(self):
- - return self.message_string(pack('!{0}sxI'.format(len(self.portal_name)), self.portal_name, self.max_rows))
- -
- -
- -Execute._message_id(b'E')
+ BulkFrontendMessage.__init__(self)
+ self._portal_name = portal_name
+ self._max_rows = max_rows
+
+ def read_bytes(self):
+ bytes_ = pack('!{0}sxI'.format(len(self._portal_name)), self._portal_name, self._max_rows)
+ return bytes_
diff -Nru python-vertica-0.6.8/vertica_python/vertica/messages/frontend_messages/flush.py python-vertica-0.7.1/vertica_python/vertica/messages/frontend_messages/flush.py
- --- python-vertica-0.6.8/vertica_python/vertica/messages/frontend_messages/flush.py 2016-10-06 23:30:16.000000000 +0200
+++ python-vertica-0.7.1/vertica_python/vertica/messages/frontend_messages/flush.py 2017-05-14 00:40:53.000000000 +0200
@@ -1,10 +1,7 @@
+from __future__ import print_function, division, absolute_import
+from ..message import BulkFrontendMessage
- -from vertica_python.vertica.messages.message import FrontendMessage
- -
- -class Flush(FrontendMessage):
- - pass
- -
- -
- -Flush._message_id(b'H')
+class Flush(BulkFrontendMessage):
+ message_id = b'H'
diff -Nru python-vertica-0.6.8/vertica_python/vertica/messages/frontend_messages/__init__.py python-vertica-0.7.1/vertica_python/vertica/messages/frontend_messages/__init__.py
- --- python-vertica-0.6.8/vertica_python/vertica/messages/frontend_messages/__init__.py 2016-10-06 23:30:16.000000000 +0200
+++ python-vertica-0.7.1/vertica_python/vertica/messages/frontend_messages/__init__.py 2017-05-14 00:40:53.000000000 +0200
@@ -0,0 +1,23 @@
+from __future__ import print_function, division, absolute_import
+
+from .bind import Bind
+from .cancel_request import CancelRequest
+from .close import Close
+from .copy_data import CopyData
+from .copy_stream import CopyStream
+from .copy_done import CopyDone
+from .copy_fail import CopyFail
+from .describe import Describe
+from .execute import Execute
+from .flush import Flush
+from .parse import Parse
+from .password import Password
+from .query import Query
+from .ssl_request import SslRequest
+from .startup import Startup
+from .sync import Sync
+from .terminate import Terminate
+
+__all__ = ['Bind', 'Query', 'CancelRequest', 'Close', 'CopyData', 'CopyDone', 'CopyFail',
+ 'CopyStream', 'Describe', 'Execute', 'Flush', 'Parse', 'Terminate', 'Password',
+ 'SslRequest', 'Startup', 'Sync']
diff -Nru python-vertica-0.6.8/vertica_python/vertica/messages/frontend_messages/parse.py python-vertica-0.7.1/vertica_python/vertica/messages/frontend_messages/parse.py
- --- python-vertica-0.6.8/vertica_python/vertica/messages/frontend_messages/parse.py 2016-10-06 23:30:16.000000000 +0200
+++ python-vertica-0.7.1/vertica_python/vertica/messages/frontend_messages/parse.py 2017-05-14 00:40:53.000000000 +0200
@@ -1,23 +1,26 @@
- -
+from __future__ import print_function, division, absolute_import
from struct import pack
- -from vertica_python.vertica.messages.message import FrontendMessage
+from ..message import BulkFrontendMessage
- -class Parse(FrontendMessage):
+class Parse(BulkFrontendMessage):
+ message_id = b'P'
def __init__(self, name, query, param_types):
- - self.name = name
- - self.query = query
- - self.param_types = param_types
+ BulkFrontendMessage.__init__(self)
+
+ self._name = name
+ self._query = query
+ self._param_types = param_types
- - def to_bytes(self):
+ def read_bytes(self):
params = ""
- - for param in self.param_types:
+ for param in self._param_types:
params = params + param
- - return self.message_string(pack('!{0}sx{1}sxH{2}I'.format(len(self.name), len(self.query), len(self.param_types)), self.name, self.query, len(self.param_types), params))
- -
- -
- -Parse._message_id(b'P')
+ bytes_ = pack('!{0}sx{1}sxH{2}I'.format(len(self._name), len(self._query),
+ len(self._param_types)),
+ self._name, self._query, len(self._param_types), params)
+ return bytes_
diff -Nru python-vertica-0.6.8/vertica_python/vertica/messages/frontend_messages/password.py python-vertica-0.7.1/vertica_python/vertica/messages/frontend_messages/password.py
- --- python-vertica-0.6.8/vertica_python/vertica/messages/frontend_messages/password.py 2016-10-06 23:30:16.000000000 +0200
+++ python-vertica-0.7.1/vertica_python/vertica/messages/frontend_messages/password.py 2017-05-14 00:40:53.000000000 +0200
@@ -1,59 +1,62 @@
- -
+from __future__ import print_function, division, absolute_import
import os
+import hashlib
+from struct import pack
+
+import six
+
+from ..message import BulkFrontendMessage
+from ..backend_messages.authentication import Authentication
if os.name == 'nt':
from . import crypt_windows as crypt
else:
import crypt
- -import hashlib
- -from struct import pack
- -
- -import six
- -from vertica_python.vertica.messages.message import FrontendMessage
- -from vertica_python.vertica.messages.backend_messages.authentication import Authentication
+ASCII = 'ascii'
- -class Password(FrontendMessage):
+class Password(BulkFrontendMessage):
+ message_id = b'p'
def __init__(self, password, auth_method=None, options=None):
- - self.password = password
- - self.options = options or {}
+ BulkFrontendMessage.__init__(self)
+
+ self._password = password
+ self._options = options or {}
if auth_method is not None:
- - self.auth_method = auth_method
+ self._auth_method = auth_method
else:
- - self.auth_method = Authentication.CLEARTEXT_PASSWORD
+ self._auth_method = Authentication.CLEARTEXT_PASSWORD
def encoded_password(self):
- - if self.auth_method == Authentication.CLEARTEXT_PASSWORD:
- - return self.password
- - elif self.auth_method == Authentication.CRYPT_PASSWORD:
- - return crypt.crypt(self.password, self.options['salt'])
- - elif self.auth_method == Authentication.MD5_PASSWORD:
+ if self._auth_method == Authentication.CLEARTEXT_PASSWORD:
+ return self._password
+ elif self._auth_method == Authentication.CRYPT_PASSWORD:
+ return crypt.crypt(self._password, self._options['salt'])
+ elif self._auth_method == Authentication.MD5_PASSWORD:
for key in 'user', 'salt':
m = hashlib.md5()
- - m.update(self.password + self.options[key])
+ m.update(self._password + self._options[key])
hexdigest = m.hexdigest()
if six.PY3:
# In python3 the output of m.hexdigest() is a unicode string,
# so has to be converted to bytes before concat'ing with
# the password bytes.
- - hexdigest = bytes(hexdigest, 'ascii')
- - self.password = hexdigest
+ hexdigest = bytes(hexdigest, ASCII)
+ self._password = hexdigest
prefix = 'md5'
if six.PY3:
# Same workaround for bytes here.
- - prefix = bytes(prefix, 'ascii')
- - return prefix + self.password
+ prefix = bytes(prefix, ASCII)
+ return prefix + self._password
else:
- - raise ValueError("unsupported authentication method: {0}".format(self.auth_method))
+ raise ValueError("unsupported authentication method: {0}".format(self._auth_method))
- - def to_bytes(self):
+ def read_bytes(self):
encoded_pw = self.encoded_password()
- - return self.message_string(pack('{0}sx'.format(len(encoded_pw)), encoded_pw))
- -
- -
- -Password._message_id(b'p')
+ bytes_ = pack('{0}sx'.format(len(encoded_pw)), encoded_pw)
+ return bytes_
diff -Nru python-vertica-0.6.8/vertica_python/vertica/messages/frontend_messages/query.py python-vertica-0.7.1/vertica_python/vertica/messages/frontend_messages/query.py
- --- python-vertica-0.6.8/vertica_python/vertica/messages/frontend_messages/query.py 2016-10-06 23:30:16.000000000 +0200
+++ python-vertica-0.7.1/vertica_python/vertica/messages/frontend_messages/query.py 2017-05-14 00:40:53.000000000 +0200
@@ -1,18 +1,20 @@
- -
+from __future__ import print_function, division, absolute_import
from struct import pack
- -from vertica_python.vertica.messages.message import FrontendMessage
- -
+from ..message import BulkFrontendMessage
- -class Query(FrontendMessage):
+UTF_8 = 'utf-8'
- - def __init__(self, query_string):
- - self.query_string = query_string
- - def to_bytes(self):
- - encoded = self.query_string.encode('utf-8')
- - return self.message_string(pack('{0}sx'.format(len(encoded)), encoded))
+class Query(BulkFrontendMessage):
+ message_id = b'Q'
+ def __init__(self, query_string):
+ BulkFrontendMessage.__init__(self)
+ self._query_string = query_string
- -Query._message_id(b'Q')
+ def read_bytes(self):
+ encoded = self._query_string.encode(UTF_8)
+ bytes_ = pack('{0}sx'.format(len(encoded)), encoded)
+ return bytes_
diff -Nru python-vertica-0.6.8/vertica_python/vertica/messages/frontend_messages/ssl_request.py python-vertica-0.7.1/vertica_python/vertica/messages/frontend_messages/ssl_request.py
- --- python-vertica-0.6.8/vertica_python/vertica/messages/frontend_messages/ssl_request.py 2016-10-06 23:30:16.000000000 +0200
+++ python-vertica-0.7.1/vertica_python/vertica/messages/frontend_messages/ssl_request.py 2017-05-14 00:40:53.000000000 +0200
@@ -1,14 +1,14 @@
- -
+from __future__ import print_function, division, absolute_import
from struct import pack
- -from vertica_python.vertica.messages.message import FrontendMessage
- -
- -
- -class SslRequest(FrontendMessage):
+from ..message import BulkFrontendMessage
- - def to_bytes(self):
- - return self.message_string(pack('!I', 80877103))
+class SslRequest(BulkFrontendMessage):
+ message_id = None
+ SSL_REQUEST = 80877103
- -SslRequest._message_id(None)
+ def read_bytes(self):
+ bytes_ = pack('!I', self.SSL_REQUEST)
+ return bytes_
diff -Nru python-vertica-0.6.8/vertica_python/vertica/messages/frontend_messages/startup.py python-vertica-0.7.1/vertica_python/vertica/messages/frontend_messages/startup.py
- --- python-vertica-0.6.8/vertica_python/vertica/messages/frontend_messages/startup.py 2016-10-06 23:30:16.000000000 +0200
+++ python-vertica-0.7.1/vertica_python/vertica/messages/frontend_messages/startup.py 2017-05-14 00:40:53.000000000 +0200
@@ -1,28 +1,47 @@
+from __future__ import print_function, division, absolute_import
- -
+import platform
+import os
+import uuid
from struct import pack
+# noinspection PyUnresolvedReferences,PyCompatibility
+from builtins import str
+
import vertica_python
- -from vertica_python.vertica.messages.message import FrontendMessage
+from ..message import BulkFrontendMessage
+
+ASCII = 'ascii'
- -class Startup(FrontendMessage):
+class Startup(BulkFrontendMessage):
+ message_id = None
def __init__(self, user, database, options=None):
- - self.user = user
- - self.database = database
- - self.options = options
- -
- - def to_bytes(self):
- - startstr = pack('!I', vertica_python.PROTOCOL_VERSION)
- - if self.user is not None:
- - startstr = startstr + pack('4sx{0}sx'.format(len(self.user)), b'user', self.user)
- - if self.database is not None:
- - startstr = startstr + pack('8sx{0}sx'.format(len(self.database)), b'database', self.database)
- - if self.options is not None:
- - startstr = startstr + pack('7sx{0}sx'.format(len(self.options)), b'options', self.options)
- - startstr = startstr + pack('x')
- - return self.message_string(startstr)
+ BulkFrontendMessage.__init__(self)
+ self._user = user
+ self._database = database
+ self._options = options
+ self._type = b'vertica-python'
+ self._version = vertica_python.__version__.encode(ASCII)
+ self._platform = platform.platform().encode(ASCII)
+ self._pid = '{0}'.format(os.getpid()).encode(ASCII)
+ self._label = self._type + b'-' + self._version + b'-' + str(uuid.uuid1()).encode(ASCII)
+
+ def read_bytes(self):
+ bytes_ = pack('!I', vertica_python.PROTOCOL_VERSION)
+ if self._user is not None:
+ bytes_ += pack('4sx{0}sx'.format(len(self._user)), b'user', self._user)
+ if self._database is not None:
+ bytes_ += pack('8sx{0}sx'.format(len(self._database)), b'database', self._database)
+ if self._options is not None:
+ bytes_ += pack('7sx{0}sx'.format(len(self._options)), b'options', self._options)
+ bytes_ += pack('12sx{0}sx'.format(len(self._label)), b'client_label', self._label)
+ bytes_ += pack('11sx{0}sx'.format(len(self._type)), b'client_type', self._type)
+ bytes_ += pack('14sx{0}sx'.format(len(self._version)), b'client_version', self._version)
+ bytes_ += pack('9sx{0}sx'.format(len(self._platform)), b'client_os', self._platform)
+ bytes_ += pack('10sx{0}sx'.format(len(self._pid)), b'client_pid', self._pid)
+ bytes_ += pack('x')
- -Startup._message_id(None)
+ return bytes_
diff -Nru python-vertica-0.6.8/vertica_python/vertica/messages/frontend_messages/sync.py python-vertica-0.7.1/vertica_python/vertica/messages/frontend_messages/sync.py
- --- python-vertica-0.6.8/vertica_python/vertica/messages/frontend_messages/sync.py 2016-10-06 23:30:16.000000000 +0200
+++ python-vertica-0.7.1/vertica_python/vertica/messages/frontend_messages/sync.py 2017-05-14 00:40:53.000000000 +0200
@@ -1,10 +1,7 @@
+from __future__ import print_function, division, absolute_import
+from ..message import BulkFrontendMessage
- -from vertica_python.vertica.messages.message import FrontendMessage
- -
- -class Sync(FrontendMessage):
- - pass
- -
- -
- -Sync._message_id(b'S')
+class Sync(BulkFrontendMessage):
+ message_id = b'S'
diff -Nru python-vertica-0.6.8/vertica_python/vertica/messages/frontend_messages/terminate.py python-vertica-0.7.1/vertica_python/vertica/messages/frontend_messages/terminate.py
- --- python-vertica-0.6.8/vertica_python/vertica/messages/frontend_messages/terminate.py 2016-10-06 23:30:16.000000000 +0200
+++ python-vertica-0.7.1/vertica_python/vertica/messages/frontend_messages/terminate.py 2017-05-14 00:40:53.000000000 +0200
@@ -1,10 +1,7 @@
+from __future__ import print_function, division, absolute_import
+from ..message import BulkFrontendMessage
- -from vertica_python.vertica.messages.message import FrontendMessage
- -
- -class Terminate(FrontendMessage):
- - pass
- -
- -
- -Terminate._message_id(b'X')
+class Terminate(BulkFrontendMessage):
+ message_id = b'X'
diff -Nru python-vertica-0.6.8/vertica_python/vertica/messages/__init__.py python-vertica-0.7.1/vertica_python/vertica/messages/__init__.py
- --- python-vertica-0.6.8/vertica_python/vertica/messages/__init__.py 2016-10-06 23:30:16.000000000 +0200
+++ python-vertica-0.7.1/vertica_python/vertica/messages/__init__.py 2017-05-14 00:40:53.000000000 +0200
@@ -1,44 +1,9 @@
- -from .backend_messages.authentication import Authentication
- -from .backend_messages.backend_key_data import BackendKeyData
- -from .backend_messages.bind_complete import BindComplete
- -from .backend_messages.close_complete import CloseComplete
- -from .backend_messages.command_complete import CommandComplete
- -from .backend_messages.copy_in_response import CopyInResponse
- -from .backend_messages.data_row import DataRow
- -from .backend_messages.empty_query_response import EmptyQueryResponse
- -from .backend_messages.error_response import ErrorResponse
- -from .backend_messages.no_data import NoData
- -from .backend_messages.notice_response import NoticeResponse
- -from .backend_messages.parameter_description import ParameterDescription
- -from .backend_messages.parameter_status import ParameterStatus
- -from .backend_messages.parse_complete import ParseComplete
- -from .backend_messages.portal_suspended import PortalSuspended
- -from .backend_messages.ready_for_query import ReadyForQuery
- -from .backend_messages.row_description import RowDescription
- -from .backend_messages.unknown import Unknown
+from __future__ import print_function, division, absolute_import
- -from .frontend_messages.bind import Bind
- -from .frontend_messages.cancel_request import CancelRequest
- -from .frontend_messages.close import Close
- -from .frontend_messages.copy_data import CopyData
- -from .frontend_messages.copy_stream import CopyStream
- -from .frontend_messages.copy_done import CopyDone
- -from .frontend_messages.copy_fail import CopyFail
- -from .frontend_messages.describe import Describe
- -from .frontend_messages.execute import Execute
- -from .frontend_messages.flush import Flush
- -from .frontend_messages.parse import Parse
- -from .frontend_messages.password import Password
- -from .frontend_messages.query import Query
- -from .frontend_messages.ssl_request import SslRequest
- -from .frontend_messages.startup import Startup
- -from .frontend_messages.sync import Sync
- -from .frontend_messages.terminate import Terminate
+from ..messages import backend_messages
+from ..messages.backend_messages import *
- -__all__ = ["Authentication", "BackendKeyData", "BindComplete", "CloseComplete", "CommandComplete",
- - "CopyInResponse", "DataRow", "EmptyQueryResponse", "ErrorResponse", "NoData", "NoticeResponse",
- - "ParameterDescription", "ParameterStatus", "ParseComplete", "PortalSuspended",
- - "ReadyForQuery", "RowDescription", "Unknown", "Bind", "CancelRequest", "Close", "CopyData", "CopyDone",
- - "CopyFail", "Describe", "Execute", "Flush", "Parse", "Password", "Query", "SslRequest", "Startup", "Sync",
- - "Terminate",
- - ]
+from ..messages import frontend_messages
+from ..messages.frontend_messages import *
+
+__all__ = backend_messages.__all__ + frontend_messages.__all__
diff -Nru python-vertica-0.6.8/vertica_python/vertica/messages/message.py python-vertica-0.7.1/vertica_python/vertica/messages/message.py
- --- python-vertica-0.6.8/vertica_python/vertica/messages/message.py 2016-10-06 23:30:16.000000000 +0200
+++ python-vertica-0.7.1/vertica_python/vertica/messages/message.py 2017-05-14 00:40:53.000000000 +0200
@@ -1,23 +1,22 @@
+from __future__ import print_function, division, absolute_import
- -
- -import types
- -
+from abc import ABCMeta
from struct import pack
- -from vertica_python.vertica.messages import *
+from ..messages import *
class Message(object):
+ __metaclass__ = ABCMeta
- - @classmethod
- - def _message_id(cls, message_id):
- - instance_message_id = message_id
+ def __init__(self):
+ pass
- - def message_id(self):
- - return instance_message_id
- - setattr(cls, 'message_id', types.MethodType(message_id, cls))
+ @property
+ def message_id(self):
+ raise NotImplementedError("no default message_id")
- - def message_string(self, msg):
+ def _bytes_to_message(self, msg):
if isinstance(msg, list):
msg = ''.join(msg)
@@ -28,31 +27,73 @@
bytesize = len(msg) + 4
message_size = pack('!I', bytesize)
- - if self.message_id() is not None:
- - msg_with_size = self.message_id() + message_size + msg
+ if self.message_id is not None:
+ msg_with_size = self.message_id + message_size + msg
else:
msg_with_size = message_size + msg
return msg_with_size
+# noinspection PyAbstractClass
class BackendMessage(Message):
- - MessageIdMap = {}
+ __metaclass__ = ABCMeta
+ _message_id_map = {}
@classmethod
- - def factory(cls, type_, data):
- - klass = cls.MessageIdMap[type_]
+ def from_type(cls, type_, data):
+ klass = cls._message_id_map.get(type_)
if klass is not None:
return klass(data)
else:
- - return messages.Unknown(type_, data)
+ return Unknown(type_, data)
- - @classmethod
- - def _message_id(cls, message_id):
- - super(BackendMessage, cls)
- - cls.MessageIdMap[message_id] = cls
+ @staticmethod
+ def register(cls):
+ # TODO replace _message_id() with that
+ assert issubclass(cls, BackendMessage), \
+ "{0} is not subclass of BackendMessage".format(cls.__name__)
+ assert cls.message_id not in BackendMessage._message_id_map, \
+ "can't write the same key twice: {0}".format(cls.message_id)
+ BackendMessage._message_id_map[cls.message_id] = cls
+
+# noinspection PyAbstractClass
class FrontendMessage(Message):
- - def to_bytes(self):
- - return self.message_string(b'')
+ __metaclass__ = ABCMeta
+
+ def fetch_message(self):
+ """Generator for getting the message's content"""
+ raise NotImplementedError("fetch_bytes has no default implementation")
+
+
+# noinspection PyAbstractClass
+class BulkFrontendMessage(FrontendMessage):
+ __metaclass__ = ABCMeta
+
+ def read_bytes(self):
+ return b''
+
+ def get_message(self):
+ bytes_ = self.read_bytes()
+ return self._bytes_to_message(bytes_)
+
+ def fetch_message(self):
+ yield self.get_message()
+
+
+# noinspection PyAbstractClass
+class StreamFrontendMessage(FrontendMessage):
+ __metaclass__ = ABCMeta
+
+ def stream_bytes(self):
+ raise NotImplementedError("stream_bytes has no default implementation")
+
+ def stream_message(self):
+ for bytes_ in self.stream_bytes():
+ yield self._bytes_to_message(bytes_)
+
+ def fetch_message(self):
+ for message in self.stream_message():
+ yield message
unblock python-vertica/0.7.1-1
- -- System Information:
Debian Release: 9.0
APT prefers stable-updates
APT policy: (500, 'stable-updates'), (500, 'oldstable-updates'), (500, 'unstable'), (500, 'testing'), (500, 'stable'), (500, 'oldstable'), (1, 'experimental')
Architecture: amd64
(x86_64)
Kernel: Linux 4.9.0-3-amd64 (SMP w/4 CPU cores)
Locale: LANG=fr_FR.UTF-8, LC_CTYPE=fr_FR.UTF-8 (charmap=UTF-8)
Shell: /bin/sh linked to /bin/dash
Init: systemd (via /run/systemd/system)
-----BEGIN PGP SIGNATURE-----
iQKTBAEBCgB9FiEEToRbojDLTUSJBphHtN1Tas99hzcFAlka2JhfFIAAAAAALgAo
aXNzdWVyLWZwckBub3RhdGlvbnMub3BlbnBncC5maWZ0aGhvcnNlbWFuLm5ldDRF
ODQ1QkEyMzBDQjRENDQ4OTA2OTg0N0I0REQ1MzZBQ0Y3RDg3MzcACgkQtN1Tas99
hzdXPBAA1bIKF4YAvXp1U8ICc6gpFAvA+WqVS8Lmt4EFbDcVpC93d+0XiRns8oD+
dnVK8mIEtMNxIXPJaZGMvze+QWcjeBE88jEhLv2MNT31p82c08f5Zyz9U+jtoLEJ
LN8PJqLE2sJ9TJ8xnpnA599aqBwX4C+my4wy5WCS1tHirZhkz5PlKYoKOeKMFvVi
5ianPDyqkL6H22ZX7cBE0zt5fvBZ1byCPFJTD0r4u6By8T728Mqx6ySthu9WUxuQ
k0GOjGYx5PYmQ1augVQz4eYq891Xb99QCZ4oN4eyUaG0in5T41U60RQ6mSrs2L8v
FpS9wdczkhL098kqaHx+Hz+JS3Wuee9a99DHzmIig3EcVo8kw3d8LRJm6SEcNiRA
kB0WOq6CgPivRXVwwyhQU8TNXTGnytvd8EGtweh628vw+6hfcXLc7N3hKFs/qEpP
1e4U1rXg7Lhrpz9LVIZ9Wh+CqVQzzGNY22YKNaI06b9B+K5iFEF0x8LMrRvo0abD
xgMqoMPLnpzJTwmTE1vvzyxRLmE+T7Ag0oKY2TF0d4y/W+y79EEEryxuqkSUCqa7
UIROctVgb8atGnoyqQ3GGalXSXaw61aIZUlgfkdzQ/fMSJnvyP5ixKwxRRCGmW+f
HEPYHCZ/gfTy06NkfUIZndt2hST18Jcnjz7QGKtZBrsXq7AcHA0=
=2BGZ
-----END PGP SIGNATURE-----
Reply to: