mirror of
https://github.com/psycopg/psycopg2.git
synced 2024-11-22 08:56:34 +03:00
Merge branch 'fix-558'
This commit is contained in:
commit
2b5e131831
1
NEWS
1
NEWS
|
@ -21,6 +21,7 @@ What's new in psycopg 2.7.2
|
|||
- Fixed `~psycopg2.extras.ReplicationCursor.consume_stream()`
|
||||
*keepalive_interval* argument (:ticket:`#547`).
|
||||
- Fixed random `!SystemError` upon receiving abort signal (:ticket:`#551`).
|
||||
- Parse intervals returned as microseconds from Redshift (:ticket:`#558`).
|
||||
- Added `~psycopg2.extras.Json` `!prepare()` method to consider connection
|
||||
params when adapting (:ticket:`#562`).
|
||||
- `~psycopg2.errorcodes` map updated to PostgreSQL 10 beta 1.
|
||||
|
|
|
@ -276,6 +276,44 @@ typecast_PYTIME_cast(const char *str, Py_ssize_t len, PyObject *curs)
|
|||
return obj;
|
||||
}
|
||||
|
||||
|
||||
/* Attempt parsing a number as microseconds
|
||||
* Redshift is reported returning this stuff, see #558
|
||||
*
|
||||
* Return a new `timedelta()` object in case of success or NULL and set an error
|
||||
*/
|
||||
static PyObject *
|
||||
interval_from_usecs(const char *str)
|
||||
{
|
||||
PyObject *us = NULL;
|
||||
char *pend;
|
||||
PyObject *rv = NULL;
|
||||
|
||||
Dprintf("interval_from_usecs: %s", str);
|
||||
|
||||
if (!(us = PyLong_FromString((char *)str, &pend, 0))) {
|
||||
Dprintf("interval_from_usecs: parsing long failed");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (*pend != '\0') {
|
||||
/* there are trailing chars, it's not just micros. Barf. */
|
||||
Dprintf("interval_from_usecs: spurious chars %s", pend);
|
||||
PyErr_Format(PyExc_ValueError,
|
||||
"expected number of microseconds, got %s", str);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
rv = PyObject_CallFunction(
|
||||
(PyObject*)PyDateTimeAPI->DeltaType, "LLO",
|
||||
0L, 0L, us);
|
||||
|
||||
exit:
|
||||
Py_XDECREF(us);
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
/** INTERVAL - parse an interval into a timedelta object **/
|
||||
|
||||
static PyObject *
|
||||
|
@ -284,6 +322,7 @@ typecast_PYINTERVAL_cast(const char *str, Py_ssize_t len, PyObject *curs)
|
|||
long v = 0, years = 0, months = 0, hours = 0, minutes = 0, micros = 0;
|
||||
PY_LONG_LONG days = 0, seconds = 0;
|
||||
int sign = 1, denom = 1, part = 0;
|
||||
const char *orig = str;
|
||||
|
||||
if (str == NULL) { Py_RETURN_NONE; }
|
||||
|
||||
|
@ -305,6 +344,16 @@ typecast_PYINTERVAL_cast(const char *str, Py_ssize_t len, PyObject *curs)
|
|||
* or too big value. On Win where long == int the 2nd check
|
||||
* is useless. */
|
||||
if (v1 < v || v1 > (long)INT_MAX) {
|
||||
/* uhm, oops... but before giving up, maybe it's redshift
|
||||
* returning microseconds? See #558 */
|
||||
PyObject *rv;
|
||||
if ((rv = interval_from_usecs(orig))) {
|
||||
return rv;
|
||||
}
|
||||
else {
|
||||
PyErr_Clear();
|
||||
}
|
||||
|
||||
PyErr_SetString(
|
||||
PyExc_OverflowError, "interval component too big");
|
||||
return NULL;
|
||||
|
@ -384,6 +433,10 @@ typecast_PYINTERVAL_cast(const char *str, Py_ssize_t len, PyObject *curs)
|
|||
micros = (long)round((double)micros / denom * 1000000.0);
|
||||
}
|
||||
}
|
||||
else if (part == 0) {
|
||||
/* Parsing failed, maybe it's just an integer? Assume usecs */
|
||||
return interval_from_usecs(orig);
|
||||
}
|
||||
|
||||
/* add hour, minutes, seconds together and include the sign */
|
||||
seconds += 60 * (PY_LONG_LONG)minutes + 3600 * (PY_LONG_LONG)hours;
|
||||
|
|
|
@ -411,6 +411,27 @@ class DatetimeTests(ConnectingTestCase, CommonDatetimeTestsMixin):
|
|||
self.assert_(t.tzinfo is not None)
|
||||
self.assert_(t < datetime(1000, 1, 1, tzinfo=FixedOffsetTimezone()))
|
||||
|
||||
def test_redshift_day(self):
|
||||
# Redshift is reported returning 1 day interval as microsec (bug #558)
|
||||
cur = self.conn.cursor()
|
||||
psycopg2.extensions.register_type(
|
||||
psycopg2.extensions.new_type(
|
||||
psycopg2.STRING.values, 'WAT', psycopg2.extensions.INTERVAL),
|
||||
cur)
|
||||
|
||||
from datetime import timedelta
|
||||
for s, v in [
|
||||
('0', timedelta(0)),
|
||||
('1', timedelta(microseconds=1)),
|
||||
('-1', timedelta(microseconds=-1)),
|
||||
('1000000', timedelta(seconds=1)),
|
||||
('86400000000', timedelta(days=1)),
|
||||
('-86400000000', timedelta(days=-1)),
|
||||
]:
|
||||
cur.execute("select %s::text", (s,))
|
||||
r = cur.fetchone()[0]
|
||||
self.assertEqual(r, v, "%s -> %s != %s" % (s, r, v))
|
||||
|
||||
|
||||
# Only run the datetime tests if psycopg was compiled with support.
|
||||
if not hasattr(psycopg2.extensions, 'PYDATETIME'):
|
||||
|
|
Loading…
Reference in New Issue
Block a user