mirror of
https://github.com/psycopg/psycopg2.git
synced 2025-02-14 16:20:33 +03:00
Merge branch 'fix-512'
This commit is contained in:
commit
451e1e2e73
1
NEWS
1
NEWS
|
@ -47,6 +47,7 @@ Bug fixes:
|
||||||
|
|
||||||
- Fixed error caused by missing decoding `~psycopg2.extras.LoggingConnection`
|
- Fixed error caused by missing decoding `~psycopg2.extras.LoggingConnection`
|
||||||
(:ticket:`#483`).
|
(:ticket:`#483`).
|
||||||
|
- Fixed integer overflow in :sql:`interval` seconds (:ticket:`#512`).
|
||||||
|
|
||||||
Other changes:
|
Other changes:
|
||||||
|
|
||||||
|
|
|
@ -60,6 +60,6 @@ please check the `project homepage`__.
|
||||||
:target: https://travis-ci.org/psycopg/psycopg2
|
:target: https://travis-ci.org/psycopg/psycopg2
|
||||||
:alt: Linux and OSX build status
|
:alt: Linux and OSX build status
|
||||||
|
|
||||||
.. |appveyor| image:: https://ci.appveyor.com/api/projects/status/github/psycopg/psycopg2?svg=true
|
.. |appveyor| image:: https://ci.appveyor.com/api/projects/status/github/psycopg/psycopg2?branch=master&svg=true
|
||||||
:target: https://ci.appveyor.com/project/psycopg/psycopg2
|
:target: https://ci.appveyor.com/project/psycopg/psycopg2
|
||||||
:alt: Windows build status
|
:alt: Windows build status
|
||||||
|
|
|
@ -220,11 +220,9 @@ typecast_PYTIME_cast(const char *str, Py_ssize_t len, PyObject *curs)
|
||||||
static PyObject *
|
static PyObject *
|
||||||
typecast_PYINTERVAL_cast(const char *str, Py_ssize_t len, PyObject *curs)
|
typecast_PYINTERVAL_cast(const char *str, Py_ssize_t len, PyObject *curs)
|
||||||
{
|
{
|
||||||
long years = 0, months = 0, days = 0;
|
long v = 0, years = 0, months = 0, hours = 0, minutes = 0, micros = 0;
|
||||||
double hours = 0.0, minutes = 0.0, seconds = 0.0, hundredths = 0.0;
|
PY_LONG_LONG days = 0, seconds = 0;
|
||||||
double v = 0.0, sign = 1.0, denominator = 1.0;
|
int sign = 1, denom = 1, part = 0;
|
||||||
int part = 0, sec;
|
|
||||||
double micro;
|
|
||||||
|
|
||||||
if (str == NULL) { Py_RETURN_NONE; }
|
if (str == NULL) { Py_RETURN_NONE; }
|
||||||
|
|
||||||
|
@ -234,56 +232,68 @@ typecast_PYINTERVAL_cast(const char *str, Py_ssize_t len, PyObject *curs)
|
||||||
switch (*str) {
|
switch (*str) {
|
||||||
|
|
||||||
case '-':
|
case '-':
|
||||||
sign = -1.0;
|
sign = -1;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case '0': case '1': case '2': case '3': case '4':
|
case '0': case '1': case '2': case '3': case '4':
|
||||||
case '5': case '6': case '7': case '8': case '9':
|
case '5': case '6': case '7': case '8': case '9':
|
||||||
v = v * 10.0 + (double)(*str - '0');
|
{
|
||||||
if (part == 6){
|
long v1;
|
||||||
denominator *= 10;
|
v1 = v * 10 + (*str - '0');
|
||||||
|
/* detect either a rollover, happening if v is really too short,
|
||||||
|
* or too big value. On Win where long == int the 2nd check
|
||||||
|
* is useless. */
|
||||||
|
if (v1 < v || v1 > (long)INT_MAX) {
|
||||||
|
PyErr_SetString(
|
||||||
|
PyExc_OverflowError, "interval component too big");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
v = v1;
|
||||||
|
}
|
||||||
|
if (part == 6) {
|
||||||
|
denom *= 10;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'y':
|
case 'y':
|
||||||
if (part == 0) {
|
if (part == 0) {
|
||||||
years = (long)(v*sign);
|
years = v * sign;
|
||||||
|
v = 0; sign = 1; part = 1;
|
||||||
str = skip_until_space2(str, &len);
|
str = skip_until_space2(str, &len);
|
||||||
v = 0.0; sign = 1.0; part = 1;
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'm':
|
case 'm':
|
||||||
if (part <= 1) {
|
if (part <= 1) {
|
||||||
months = (long)(v*sign);
|
months = v * sign;
|
||||||
|
v = 0; sign = 1; part = 2;
|
||||||
str = skip_until_space2(str, &len);
|
str = skip_until_space2(str, &len);
|
||||||
v = 0.0; sign = 1.0; part = 2;
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'd':
|
case 'd':
|
||||||
if (part <= 2) {
|
if (part <= 2) {
|
||||||
days = (long)(v*sign);
|
days = v * sign;
|
||||||
|
v = 0; sign = 1; part = 3;
|
||||||
str = skip_until_space2(str, &len);
|
str = skip_until_space2(str, &len);
|
||||||
v = 0.0; sign = 1.0; part = 3;
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ':':
|
case ':':
|
||||||
if (part <= 3) {
|
if (part <= 3) {
|
||||||
hours = v;
|
hours = v;
|
||||||
v = 0.0; part = 4;
|
v = 0; part = 4;
|
||||||
}
|
}
|
||||||
else if (part == 4) {
|
else if (part == 4) {
|
||||||
minutes = v;
|
minutes = v;
|
||||||
v = 0.0; part = 5;
|
v = 0; part = 5;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case '.':
|
case '.':
|
||||||
if (part == 5) {
|
if (part == 5) {
|
||||||
seconds = v;
|
seconds = v;
|
||||||
v = 0.0; part = 6;
|
v = 0; part = 6;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -294,7 +304,7 @@ typecast_PYINTERVAL_cast(const char *str, Py_ssize_t len, PyObject *curs)
|
||||||
str++;
|
str++;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* manage last value, be it minutes or seconds or hundredths of a second */
|
/* manage last value, be it minutes or seconds or microseconds */
|
||||||
if (part == 4) {
|
if (part == 4) {
|
||||||
minutes = v;
|
minutes = v;
|
||||||
}
|
}
|
||||||
|
@ -302,25 +312,30 @@ typecast_PYINTERVAL_cast(const char *str, Py_ssize_t len, PyObject *curs)
|
||||||
seconds = v;
|
seconds = v;
|
||||||
}
|
}
|
||||||
else if (part == 6) {
|
else if (part == 6) {
|
||||||
hundredths = v;
|
micros = v;
|
||||||
hundredths = hundredths/denominator;
|
if (denom < 1000000L) {
|
||||||
|
do {
|
||||||
|
micros *= 10;
|
||||||
|
denom *= 10;
|
||||||
|
} while (denom < 1000000L);
|
||||||
|
}
|
||||||
|
else if (denom > 1000000L) {
|
||||||
|
micros = (long)round((double)micros / denom * 1000000.0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* calculates seconds */
|
/* add hour, minutes, seconds together and include the sign */
|
||||||
if (sign < 0.0) {
|
seconds += 60 * (PY_LONG_LONG)minutes + 3600 * (PY_LONG_LONG)hours;
|
||||||
seconds = - (hundredths + seconds + minutes*60 + hours*3600);
|
if (sign < 0) {
|
||||||
}
|
seconds = -seconds;
|
||||||
else {
|
micros = -micros;
|
||||||
seconds += hundredths + minutes*60 + hours*3600;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* calculates days */
|
/* add the days, months years together - they already include a sign */
|
||||||
days += years*365 + months*30;
|
days += 30 * (PY_LONG_LONG)months + 365 * (PY_LONG_LONG)years;
|
||||||
|
|
||||||
micro = (seconds - floor(seconds)) * 1000000.0;
|
return PyObject_CallFunction((PyObject*)PyDateTimeAPI->DeltaType, "LLl",
|
||||||
sec = (int)floor(seconds);
|
days, seconds, micros);
|
||||||
return PyObject_CallFunction((PyObject*)PyDateTimeAPI->DeltaType, "iii",
|
|
||||||
days, sec, (int)round(micro));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* psycopg defaults to using python datetime types */
|
/* psycopg defaults to using python datetime types */
|
||||||
|
|
|
@ -28,6 +28,11 @@ from psycopg2.tz import FixedOffsetTimezone, ZERO
|
||||||
from testutils import unittest, ConnectingTestCase, skip_before_postgres
|
from testutils import unittest, ConnectingTestCase, skip_before_postgres
|
||||||
|
|
||||||
|
|
||||||
|
def total_seconds(d):
|
||||||
|
"""Return total number of seconds of a timedelta as a float."""
|
||||||
|
return d.days * 24 * 60 * 60 + d.seconds + d.microseconds / 1000000.0
|
||||||
|
|
||||||
|
|
||||||
class CommonDatetimeTestsMixin:
|
class CommonDatetimeTestsMixin:
|
||||||
|
|
||||||
def execute(self, *args):
|
def execute(self, *args):
|
||||||
|
@ -333,6 +338,59 @@ class DatetimeTests(ConnectingTestCase, CommonDatetimeTestsMixin):
|
||||||
t = self.execute("select '24:00+05:30'::timetz;")
|
t = self.execute("select '24:00+05:30'::timetz;")
|
||||||
self.assertEqual(t, time(0, 0, tzinfo=FixedOffsetTimezone(330)))
|
self.assertEqual(t, time(0, 0, tzinfo=FixedOffsetTimezone(330)))
|
||||||
|
|
||||||
|
def test_large_interval(self):
|
||||||
|
t = self.execute("select '999999:00:00'::interval")
|
||||||
|
self.assertEqual(total_seconds(t), 999999 * 60 * 60)
|
||||||
|
|
||||||
|
t = self.execute("select '-999999:00:00'::interval")
|
||||||
|
self.assertEqual(total_seconds(t), -999999 * 60 * 60)
|
||||||
|
|
||||||
|
t = self.execute("select '999999:00:00.1'::interval")
|
||||||
|
self.assertEqual(total_seconds(t), 999999 * 60 * 60 + 0.1)
|
||||||
|
|
||||||
|
t = self.execute("select '999999:00:00.9'::interval")
|
||||||
|
self.assertEqual(total_seconds(t), 999999 * 60 * 60 + 0.9)
|
||||||
|
|
||||||
|
t = self.execute("select '-999999:00:00.1'::interval")
|
||||||
|
self.assertEqual(total_seconds(t), -999999 * 60 * 60 - 0.1)
|
||||||
|
|
||||||
|
t = self.execute("select '-999999:00:00.9'::interval")
|
||||||
|
self.assertEqual(total_seconds(t), -999999 * 60 * 60 - 0.9)
|
||||||
|
|
||||||
|
def test_micros_rounding(self):
|
||||||
|
t = self.execute("select '0.1'::interval")
|
||||||
|
self.assertEqual(total_seconds(t), 0.1)
|
||||||
|
|
||||||
|
t = self.execute("select '0.01'::interval")
|
||||||
|
self.assertEqual(total_seconds(t), 0.01)
|
||||||
|
|
||||||
|
t = self.execute("select '0.000001'::interval")
|
||||||
|
self.assertEqual(total_seconds(t), 1e-6)
|
||||||
|
|
||||||
|
t = self.execute("select '0.0000004'::interval")
|
||||||
|
self.assertEqual(total_seconds(t), 0)
|
||||||
|
|
||||||
|
t = self.execute("select '0.0000006'::interval")
|
||||||
|
self.assertEqual(total_seconds(t), 1e-6)
|
||||||
|
|
||||||
|
def test_interval_overflow(self):
|
||||||
|
cur = self.conn.cursor()
|
||||||
|
# hack a cursor to receive values too extreme to be represented
|
||||||
|
# but still I want an error, not a random number
|
||||||
|
psycopg2.extensions.register_type(
|
||||||
|
psycopg2.extensions.new_type(
|
||||||
|
psycopg2.STRING.values, 'WAT', psycopg2.extensions.INTERVAL),
|
||||||
|
cur)
|
||||||
|
|
||||||
|
def f(val):
|
||||||
|
cur.execute("select '%s'::text" % val)
|
||||||
|
return cur.fetchone()[0]
|
||||||
|
|
||||||
|
self.assertRaises(OverflowError, f, '100000000000000000:00:00')
|
||||||
|
self.assertRaises(OverflowError, f, '00:100000000000000000:00:00')
|
||||||
|
self.assertRaises(OverflowError, f, '00:00:100000000000000000:00')
|
||||||
|
self.assertRaises(OverflowError, f, '00:00:00.100000000000000000')
|
||||||
|
|
||||||
|
|
||||||
# Only run the datetime tests if psycopg was compiled with support.
|
# Only run the datetime tests if psycopg was compiled with support.
|
||||||
if not hasattr(psycopg2.extensions, 'PYDATETIME'):
|
if not hasattr(psycopg2.extensions, 'PYDATETIME'):
|
||||||
|
|
Loading…
Reference in New Issue
Block a user