mirror of
https://github.com/psycopg/psycopg2.git
synced 2025-03-06 08:55:46 +03:00
Explicit cast of the SQL representation of time-related objects.
Allow the objects to be recognized as the proper type by Postgres in not strong contexts: problem reported by Peter Eisentraut. Added tests to check the types are respected in a complete Py -> PG -> Py roundtrip without context.
This commit is contained in:
parent
e8c2a14362
commit
bf1c76b792
|
@ -2,6 +2,9 @@
|
||||||
|
|
||||||
* Added typecasters for arrays of specific MX/Py time-related types.
|
* Added typecasters for arrays of specific MX/Py time-related types.
|
||||||
|
|
||||||
|
* psycopg/adapter_[mx]datetime.c: Explicit cast of the SQL representation
|
||||||
|
of time-related objects.
|
||||||
|
|
||||||
2010-05-03 Daniele Varrazzo <daniele.varrazzo@gmail.com>
|
2010-05-03 Daniele Varrazzo <daniele.varrazzo@gmail.com>
|
||||||
|
|
||||||
* psycopg/adapter_binary.c: Adapt buffer objects using an explicit cast on
|
* psycopg/adapter_binary.c: Adapt buffer objects using an explicit cast on
|
||||||
|
|
|
@ -57,10 +57,25 @@ static PyObject *
|
||||||
pydatetime_str(pydatetimeObject *self)
|
pydatetime_str(pydatetimeObject *self)
|
||||||
{
|
{
|
||||||
if (self->type <= PSYCO_DATETIME_TIMESTAMP) {
|
if (self->type <= PSYCO_DATETIME_TIMESTAMP) {
|
||||||
|
|
||||||
|
/* Select the right PG type to cast into. */
|
||||||
|
char *fmt = NULL;
|
||||||
|
switch (self->type) {
|
||||||
|
case PSYCO_DATETIME_TIME:
|
||||||
|
fmt = "'%s'::time";
|
||||||
|
break;
|
||||||
|
case PSYCO_DATETIME_DATE:
|
||||||
|
fmt = "'%s'::date";
|
||||||
|
break;
|
||||||
|
case PSYCO_DATETIME_TIMESTAMP:
|
||||||
|
fmt = "'%s'::timestamp";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
PyObject *res = NULL;
|
PyObject *res = NULL;
|
||||||
PyObject *iso = PyObject_CallMethod(self->wrapped, "isoformat", NULL);
|
PyObject *iso = PyObject_CallMethod(self->wrapped, "isoformat", NULL);
|
||||||
if (iso) {
|
if (iso) {
|
||||||
res = PyString_FromFormat("'%s'", PyString_AsString(iso));
|
res = PyString_FromFormat(fmt, PyString_AsString(iso));
|
||||||
Py_DECREF(iso);
|
Py_DECREF(iso);
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
|
@ -78,7 +93,7 @@ pydatetime_str(pydatetimeObject *self)
|
||||||
}
|
}
|
||||||
buffer[6] = '\0';
|
buffer[6] = '\0';
|
||||||
|
|
||||||
return PyString_FromFormat("'%d days %d.%s seconds'",
|
return PyString_FromFormat("'%d days %d.%s seconds'::interval",
|
||||||
obj->days, obj->seconds, buffer);
|
obj->days, obj->seconds, buffer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,10 +56,10 @@ mxdatetime_str(mxdatetimeObject *self)
|
||||||
case PSYCO_MXDATETIME_DATE:
|
case PSYCO_MXDATETIME_DATE:
|
||||||
dt = (mxDateTimeObject *)self->wrapped;
|
dt = (mxDateTimeObject *)self->wrapped;
|
||||||
if (dt->year >= 1)
|
if (dt->year >= 1)
|
||||||
PyOS_snprintf(buf, sizeof(buf) - 1, "'%04ld-%02d-%02d'",
|
PyOS_snprintf(buf, sizeof(buf) - 1, "'%04ld-%02d-%02d'::date",
|
||||||
dt->year, (int)dt->month, (int)dt->day);
|
dt->year, (int)dt->month, (int)dt->day);
|
||||||
else
|
else
|
||||||
PyOS_snprintf(buf, sizeof(buf) - 1, "'%04ld-%02d-%02d BC'",
|
PyOS_snprintf(buf, sizeof(buf) - 1, "'%04ld-%02d-%02d BC'::date",
|
||||||
1 - dt->year, (int)dt->month, (int)dt->day);
|
1 - dt->year, (int)dt->month, (int)dt->day);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -67,12 +67,12 @@ mxdatetime_str(mxdatetimeObject *self)
|
||||||
dt = (mxDateTimeObject *)self->wrapped;
|
dt = (mxDateTimeObject *)self->wrapped;
|
||||||
if (dt->year >= 1)
|
if (dt->year >= 1)
|
||||||
PyOS_snprintf(buf, sizeof(buf) - 1,
|
PyOS_snprintf(buf, sizeof(buf) - 1,
|
||||||
"'%04ld-%02d-%02dT%02d:%02d:%09.6f'",
|
"'%04ld-%02d-%02dT%02d:%02d:%09.6f'::timestamp",
|
||||||
dt->year, (int)dt->month, (int)dt->day,
|
dt->year, (int)dt->month, (int)dt->day,
|
||||||
(int)dt->hour, (int)dt->minute, dt->second);
|
(int)dt->hour, (int)dt->minute, dt->second);
|
||||||
else
|
else
|
||||||
PyOS_snprintf(buf, sizeof(buf) - 1,
|
PyOS_snprintf(buf, sizeof(buf) - 1,
|
||||||
"'%04ld-%02d-%02dT%02d:%02d:%09.6f BC'",
|
"'%04ld-%02d-%02dT%02d:%02d:%09.6f BC'::timestamp",
|
||||||
1 - dt->year, (int)dt->month, (int)dt->day,
|
1 - dt->year, (int)dt->month, (int)dt->day,
|
||||||
(int)dt->hour, (int)dt->minute, dt->second);
|
(int)dt->hour, (int)dt->minute, dt->second);
|
||||||
break;
|
break;
|
||||||
|
@ -85,16 +85,16 @@ mxdatetime_str(mxdatetimeObject *self)
|
||||||
time */
|
time */
|
||||||
dtd = (mxDateTimeDeltaObject *)self->wrapped;
|
dtd = (mxDateTimeDeltaObject *)self->wrapped;
|
||||||
if (0 <= dtd->seconds && dtd->seconds < 24*3600) {
|
if (0 <= dtd->seconds && dtd->seconds < 24*3600) {
|
||||||
PyOS_snprintf(buf, sizeof(buf) - 1, "'%02d:%02d:%09.6f'",
|
PyOS_snprintf(buf, sizeof(buf) - 1, "'%02d:%02d:%09.6f'::time",
|
||||||
(int)dtd->hour, (int)dtd->minute, dtd->second);
|
(int)dtd->hour, (int)dtd->minute, dtd->second);
|
||||||
} else {
|
} else {
|
||||||
double ss = dtd->hour*3600.0 + dtd->minute*60.0 + dtd->second;
|
double ss = dtd->hour*3600.0 + dtd->minute*60.0 + dtd->second;
|
||||||
|
|
||||||
if (dtd->seconds >= 0)
|
if (dtd->seconds >= 0)
|
||||||
PyOS_snprintf(buf, sizeof(buf) - 1, "'%ld days %.6f seconds'",
|
PyOS_snprintf(buf, sizeof(buf) - 1, "'%ld days %.6f seconds'::interval",
|
||||||
dtd->day, ss);
|
dtd->day, ss);
|
||||||
else
|
else
|
||||||
PyOS_snprintf(buf, sizeof(buf) - 1, "'-%ld days -%.6f seconds'",
|
PyOS_snprintf(buf, sizeof(buf) - 1, "'-%ld days -%.6f seconds'::interval",
|
||||||
dtd->day, ss);
|
dtd->day, ss);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -247,6 +247,47 @@ class DatetimeTests(unittest.TestCase, CommonDatetimeTestsMixin):
|
||||||
self.assertEqual(seconds, -3583504)
|
self.assertEqual(seconds, -3583504)
|
||||||
self.assertEqual(int(round((value - seconds) * 1000000)), 123456)
|
self.assertEqual(int(round((value - seconds) * 1000000)), 123456)
|
||||||
|
|
||||||
|
def _test_type_roundtrip(self, o1):
|
||||||
|
o2 = self.execute("select %s;", (o1,))
|
||||||
|
self.assertEqual(type(o1), type(o2))
|
||||||
|
|
||||||
|
def _test_type_roundtrip_array(self, o1):
|
||||||
|
o1 = [o1]
|
||||||
|
o2 = self.execute("select %s;", (o1,))
|
||||||
|
self.assertEqual(type(o1[0]), type(o2[0]))
|
||||||
|
|
||||||
|
def test_type_roundtrip_date(self):
|
||||||
|
from datetime import date
|
||||||
|
self._test_type_roundtrip(date(2010,05,03))
|
||||||
|
|
||||||
|
def test_type_roundtrip_datetime(self):
|
||||||
|
from datetime import datetime
|
||||||
|
self._test_type_roundtrip(datetime(2010,05,03,10,20,30))
|
||||||
|
|
||||||
|
def test_type_roundtrip_time(self):
|
||||||
|
from datetime import time
|
||||||
|
self._test_type_roundtrip(time(10,20,30))
|
||||||
|
|
||||||
|
def test_type_roundtrip_interval(self):
|
||||||
|
from datetime import timedelta
|
||||||
|
self._test_type_roundtrip(timedelta(seconds=30))
|
||||||
|
|
||||||
|
def test_type_roundtrip_date_array(self):
|
||||||
|
from datetime import date
|
||||||
|
self._test_type_roundtrip_array(date(2010,05,03))
|
||||||
|
|
||||||
|
def test_type_roundtrip_datetime_array(self):
|
||||||
|
from datetime import datetime
|
||||||
|
self._test_type_roundtrip_array(datetime(2010,05,03,10,20,30))
|
||||||
|
|
||||||
|
def test_type_roundtrip_time_array(self):
|
||||||
|
from datetime import time
|
||||||
|
self._test_type_roundtrip_array(time(10,20,30))
|
||||||
|
|
||||||
|
def test_type_roundtrip_interval_array(self):
|
||||||
|
from datetime import timedelta
|
||||||
|
self._test_type_roundtrip_array(timedelta(seconds=30))
|
||||||
|
|
||||||
|
|
||||||
# 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._psycopg, 'PYDATETIME'):
|
if not hasattr(psycopg2._psycopg, 'PYDATETIME'):
|
||||||
|
@ -264,6 +305,15 @@ class mxDateTimeTests(unittest.TestCase, CommonDatetimeTestsMixin):
|
||||||
self.DATETIME = psycopg2._psycopg.MXDATETIME
|
self.DATETIME = psycopg2._psycopg.MXDATETIME
|
||||||
self.INTERVAL = psycopg2._psycopg.MXINTERVAL
|
self.INTERVAL = psycopg2._psycopg.MXINTERVAL
|
||||||
|
|
||||||
|
psycopg2.extensions.register_type(self.DATE, self.conn)
|
||||||
|
psycopg2.extensions.register_type(self.TIME, self.conn)
|
||||||
|
psycopg2.extensions.register_type(self.DATETIME, self.conn)
|
||||||
|
psycopg2.extensions.register_type(self.INTERVAL, self.conn)
|
||||||
|
psycopg2.extensions.register_type(psycopg2.extensions.MXDATEARRAY, self.conn)
|
||||||
|
psycopg2.extensions.register_type(psycopg2.extensions.MXTIMEARRAY, self.conn)
|
||||||
|
psycopg2.extensions.register_type(psycopg2.extensions.MXDATETIMEARRAY, self.conn)
|
||||||
|
psycopg2.extensions.register_type(psycopg2.extensions.MXINTERVALARRAY, self.conn)
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
self.conn.close()
|
self.conn.close()
|
||||||
|
|
||||||
|
@ -370,6 +420,47 @@ class mxDateTimeTests(unittest.TestCase, CommonDatetimeTestsMixin):
|
||||||
self.assertEqual(seconds, -3583504)
|
self.assertEqual(seconds, -3583504)
|
||||||
self.assertEqual(int(round((value - seconds) * 1000000)), 123456)
|
self.assertEqual(int(round((value - seconds) * 1000000)), 123456)
|
||||||
|
|
||||||
|
def _test_type_roundtrip(self, o1):
|
||||||
|
o2 = self.execute("select %s;", (o1,))
|
||||||
|
self.assertEqual(type(o1), type(o2))
|
||||||
|
|
||||||
|
def _test_type_roundtrip_array(self, o1):
|
||||||
|
o1 = [o1]
|
||||||
|
o2 = self.execute("select %s;", (o1,))
|
||||||
|
self.assertEqual(type(o1[0]), type(o2[0]))
|
||||||
|
|
||||||
|
def test_type_roundtrip_date(self):
|
||||||
|
from mx.DateTime import Date
|
||||||
|
self._test_type_roundtrip(Date(2010,05,03))
|
||||||
|
|
||||||
|
def test_type_roundtrip_datetime(self):
|
||||||
|
from mx.DateTime import DateTime
|
||||||
|
self._test_type_roundtrip(DateTime(2010,05,03,10,20,30))
|
||||||
|
|
||||||
|
def test_type_roundtrip_time(self):
|
||||||
|
from mx.DateTime import Time
|
||||||
|
self._test_type_roundtrip(Time(10,20,30))
|
||||||
|
|
||||||
|
def test_type_roundtrip_interval(self):
|
||||||
|
from mx.DateTime import DateTimeDeltaFrom
|
||||||
|
self._test_type_roundtrip(DateTimeDeltaFrom(seconds=30))
|
||||||
|
|
||||||
|
def test_type_roundtrip_date_array(self):
|
||||||
|
from mx.DateTime import Date
|
||||||
|
self._test_type_roundtrip_array(Date(2010,05,03))
|
||||||
|
|
||||||
|
def test_type_roundtrip_datetime_array(self):
|
||||||
|
from mx.DateTime import DateTime
|
||||||
|
self._test_type_roundtrip_array(DateTime(2010,05,03,10,20,30))
|
||||||
|
|
||||||
|
def test_type_roundtrip_time_array(self):
|
||||||
|
from mx.DateTime import Time
|
||||||
|
self._test_type_roundtrip_array(Time(10,20,30))
|
||||||
|
|
||||||
|
def test_type_roundtrip_interval_array(self):
|
||||||
|
from mx.DateTime import DateTimeDeltaFrom
|
||||||
|
self._test_type_roundtrip_array(DateTimeDeltaFrom(seconds=30))
|
||||||
|
|
||||||
|
|
||||||
# Only run the mx.DateTime tests if psycopg was compiled with support.
|
# Only run the mx.DateTime tests if psycopg was compiled with support.
|
||||||
if not hasattr(psycopg2._psycopg, 'MXDATETIME'):
|
if not hasattr(psycopg2._psycopg, 'MXDATETIME'):
|
||||||
|
|
Loading…
Reference in New Issue
Block a user