summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJames Henstridge <james@jamesh.id.au>2008-03-17 04:06:44 +0000
committerJames Henstridge <james@jamesh.id.au>2008-03-17 04:06:44 +0000
commitcceaa7331b82294952aa18beba63616187f2fd93 (patch)
tree4f84aaadcaba613687d7f70e04210e658ec69828
parent1ea0cd498000ebd7ba1c3843d28d7df4ab6a68c7 (diff)
downloadpsycopg2-cceaa7331b82294952aa18beba63616187f2fd93.tar.gz
* psycopg/typecast.c (typecast_parse_time): give the correct
return value for partially parsed time values. * psycopg/typecast_mxdatetime.c (typecast_MXDATE_cast): return NULL after setting DataError. Also, don't treat it as an error if typecast_parse_time() returns 0 (as might happen if the remainder of the string is " BC"). * psycopg/typecast_datetime.c (typecast_PYDATE_cast): return NULL after setting DataError. (typecast_PYDATETIME_cast): same here. (typecast_PYTIME_cast): same here. * tests/test_dates.py (CommonDatetimeTestsMixin.test_parse_incomplete_date): test that parsing incomplete date values results in DataError. (CommonDatetimeTestsMixin.test_parse_incomplete_time): same for times. (CommonDatetimeTestsMixin.test_parse_incomplete_time): same for datetimes.
-rw-r--r--ChangeLog23
-rw-r--r--psycopg/typecast.c4
-rw-r--r--psycopg/typecast_datetime.c8
-rw-r--r--psycopg/typecast_mxdatetime.c4
-rw-r--r--tests/test_dates.py252
5 files changed, 54 insertions, 237 deletions
diff --git a/ChangeLog b/ChangeLog
index d94fe6b..60018ff 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,26 @@
+2008-03-17 James Henstridge <james@jamesh.id.au>
+
+ * psycopg/typecast.c (typecast_parse_time): give the correct
+ return value for partially parsed time values.
+
+ * psycopg/typecast_mxdatetime.c (typecast_MXDATE_cast): return
+ NULL after setting DataError. Also, don't treat it as an error if
+ typecast_parse_time() returns 0 (as might happen if the remainder
+ of the string is " BC").
+
+ * psycopg/typecast_datetime.c (typecast_PYDATE_cast): return NULL
+ after setting DataError.
+ (typecast_PYDATETIME_cast): same here.
+ (typecast_PYTIME_cast): same here.
+
+ * tests/test_dates.py
+ (CommonDatetimeTestsMixin.test_parse_incomplete_date): test that
+ parsing incomplete date values results in DataError.
+ (CommonDatetimeTestsMixin.test_parse_incomplete_time): same for
+ times.
+ (CommonDatetimeTestsMixin.test_parse_incomplete_time): same for
+ datetimes.
+
2008-03-07 Jason Erickson <jerickso@stickpeople.com>
* psycopg/pqpath.c (pq_raise): if PSYCOPG_EXTENSIONS is not
diff --git a/psycopg/typecast.c b/psycopg/typecast.c
index 6ed90a8..c7474e7 100644
--- a/psycopg/typecast.c
+++ b/psycopg/typecast.c
@@ -146,7 +146,9 @@ typecast_parse_time(const char* s, const char** t, Py_ssize_t* len,
}
if (acc != -1) {
- if (cz == 2) { *ss = acc; cz += 1; }
+ if (cz == 0) { *hh = acc; cz += 1; }
+ else if (cz == 1) { *mm = acc; cz += 1; }
+ else if (cz == 2) { *ss = acc; cz += 1; }
else if (cz == 3) { *us = acc; cz += 1; }
else if (cz == 4) { tzhh = acc; cz += 1; }
else if (cz == 5) tzmm = acc;
diff --git a/psycopg/typecast_datetime.c b/psycopg/typecast_datetime.c
index 99f4b60..8144850 100644
--- a/psycopg/typecast_datetime.c
+++ b/psycopg/typecast_datetime.c
@@ -58,9 +58,10 @@ typecast_PYDATE_cast(const char *str, Py_ssize_t len, PyObject *curs)
n, len, y, m, d);
if (n != 3) {
PyErr_SetString(DataError, "unable to parse date");
+ return NULL;
}
else {
- if (y > 9999) y = 9999;
+ if (y > 9999) y = 9999;
obj = PyObject_CallFunction(pyDateTypeP, "iii", y, m, d);
}
}
@@ -98,6 +99,7 @@ typecast_PYDATETIME_cast(const char *str, Py_ssize_t len, PyObject *curs)
tp, n, len, y, m, d);
if (n != 3) {
PyErr_SetString(DataError, "unable to parse date");
+ return NULL;
}
if (len > 0) {
@@ -108,6 +110,7 @@ typecast_PYDATETIME_cast(const char *str, Py_ssize_t len, PyObject *curs)
n, len, hh, mm, ss, us, tz);
if (n < 3 || n > 5) {
PyErr_SetString(DataError, "unable to parse time");
+ return NULL;
}
}
@@ -116,7 +119,7 @@ typecast_PYDATETIME_cast(const char *str, Py_ssize_t len, PyObject *curs)
ss -= 60;
}
if (y > 9999)
- y = 9999;
+ y = 9999;
if (n == 5 && ((cursorObject*)curs)->tzinfo_factory != Py_None) {
/* we have a time zone, calculate minutes and create
@@ -158,6 +161,7 @@ typecast_PYTIME_cast(const char *str, Py_ssize_t len, PyObject *curs)
if (n < 3 || n > 5) {
PyErr_SetString(DataError, "unable to parse time");
+ return NULL;
}
else {
if (ss > 59) {
diff --git a/psycopg/typecast_mxdatetime.c b/psycopg/typecast_mxdatetime.c
index a6e9509..e030091 100644
--- a/psycopg/typecast_mxdatetime.c
+++ b/psycopg/typecast_mxdatetime.c
@@ -54,6 +54,7 @@ typecast_MXDATE_cast(const char *str, Py_ssize_t len, PyObject *curs)
" y = %d, m = %d, d = %d", tp, n, len, y, m, d);
if (n != 3) {
PyErr_SetString(DataError, "unable to parse date");
+ return NULL;
}
if (len > 0) {
@@ -62,8 +63,9 @@ typecast_MXDATE_cast(const char *str, Py_ssize_t len, PyObject *curs)
" len = " FORMAT_CODE_PY_SSIZE_T ","
" hh = %d, mm = %d, ss = %d, us = %d, tz = %d",
n, len, hh, mm, ss, us, tz);
- if (n < 3 || n > 5) {
+ if (n != 0 && (n < 3 || n > 5)) {
PyErr_SetString(DataError, "unable to parse time");
+ return NULL;
}
}
diff --git a/tests/test_dates.py b/tests/test_dates.py
index 376659f..303391b 100644
--- a/tests/test_dates.py
+++ b/tests/test_dates.py
@@ -25,239 +25,9 @@ class CommonDatetimeTestsMixin:
value = self.DATE(None, None)
self.assertEqual(value, None)
- def test_parse_time(self):
- value = self.TIME('13:30:29', None)
- self.assertNotEqual(value, None)
- self.assertEqual(value.hour, 13)
- self.assertEqual(value.minute, 30)
- self.assertEqual(value.second, 29)
-
- def test_parse_null_time(self):
- value = self.TIME(None, None)
- self.assertEqual(value, None)
-
- def test_parse_datetime(self):
- value = self.DATETIME('2007-01-01 13:30:29', None)
- self.assertNotEqual(value, None)
- self.assertEqual(value.year, 2007)
- self.assertEqual(value.month, 1)
- self.assertEqual(value.day, 1)
- self.assertEqual(value.hour, 13)
- self.assertEqual(value.minute, 30)
- self.assertEqual(value.second, 29)
-
- def test_parse_null_datetime(self):
- value = self.DATETIME(None, None)
- self.assertEqual(value, None)
-
- def test_parse_null_interval(self):
- value = self.INTERVAL(None, None)
- self.assertEqual(value, None)
-
-
-class DatetimeTests(unittest.TestCase, CommonDatetimeTestsMixin):
- """Tests for the datetime based date handling in psycopg2."""
-
- def setUp(self):
- self.DATE = psycopg2._psycopg.PYDATE
- self.TIME = psycopg2._psycopg.PYTIME
- self.DATETIME = psycopg2._psycopg.PYDATETIME
- self.INTERVAL = psycopg2._psycopg.PYINTERVAL
-
- def test_parse_bc_date(self):
- # datetime does not support BC dates
- self.assertRaises(ValueError, self.DATE, '00042-01-01 BC', None)
-
- def test_parse_bc_datetime(self):
- # datetime does not support BC dates
- self.assertRaises(ValueError, self.DATETIME,
- '00042-01-01 13:30:29 BC', None)
-
- def test_parse_time_microseconds(self):
- value = self.TIME('13:30:29.123456', None)
- self.assertEqual(value.second, 29)
- self.assertEqual(value.microsecond, 123456)
-
- def test_parse_datetime_microseconds(self):
- value = self.DATETIME('2007-01-01 13:30:29.123456', None)
- self.assertEqual(value.second, 29)
- self.assertEqual(value.microsecond, 123456)
-
- def test_parse_interval(self):
- value = self.INTERVAL('42 days 12:34:56.123456', None)
- self.assertNotEqual(value, None)
- self.assertEqual(value.days, 42)
- self.assertEqual(value.seconds, 45296)
- self.assertEqual(value.microseconds, 123456)
-
- def test_parse_negative_interval(self):
- value = self.INTERVAL('-42 days -12:34:56.123456', None)
- self.assertNotEqual(value, None)
- self.assertEqual(value.days, -43)
- self.assertEqual(value.seconds, 41103)
- self.assertEqual(value.microseconds, 876544)
-
- def test_adapt_date(self):
- from datetime import date
- value = self.execute('select (%s)::date::text',
- [date(2007, 1, 1)])
- self.assertEqual(value, '2007-01-01')
-
- def test_adapt_time(self):
- from datetime import time
- value = self.execute('select (%s)::time::text',
- [time(13, 30, 29)])
- self.assertEqual(value, '13:30:29')
-
- def test_adapt_datetime(self):
- from datetime import datetime
- value = self.execute('select (%s)::timestamp::text',
- [datetime(2007, 1, 1, 13, 30, 29)])
- self.assertEqual(value, '2007-01-01 13:30:29')
-
- def test_adapt_timedelta(self):
- from datetime import timedelta
- value = self.execute('select extract(epoch from (%s)::interval)',
- [timedelta(days=42, seconds=45296,
- microseconds=123456)])
- seconds = math.floor(value)
- self.assertEqual(seconds, 3674096)
- self.assertEqual(int(round((value - seconds) * 1000000)), 123456)
-
- def test_adapt_megative_timedelta(self):
- from datetime import timedelta
- value = self.execute('select extract(epoch from (%s)::interval)',
- [timedelta(days=-42, seconds=45296,
- microseconds=123456)])
- seconds = math.floor(value)
- self.assertEqual(seconds, -3583504)
- self.assertEqual(int(round((value - seconds) * 1000000)), 123456)
-
-
-# Only run the datetime tests if psycopg was compiled with support.
-if not hasattr(psycopg2._psycopg, 'PYDATETIME'):
- del DatetimeTests
-
-
-class mxDateTimeTests(unittest.TestCase, CommonDatetimeTestsMixin):
- """Tests for the mx.DateTime based date handling in psycopg2."""
-
- def setUp(self):
- self.DATE = psycopg2._psycopg.MXDATE
- self.TIME = psycopg2._psycopg.MXTIME
- self.DATETIME = psycopg2._psycopg.MXDATETIME
- self.INTERVAL = psycopg2._psycopg.MXINTERVAL
-
- def test_parse_bc_date(self):
- value = self.DATE('00042-01-01 BC', None)
- self.assertNotEqual(value, None)
- # mx.DateTime numbers BC dates from 0 rather than 1.
- self.assertEqual(value.year, -41)
- self.assertEqual(value.month, 1)
- self.assertEqual(value.day, 1)
-
- def test_parse_bc_datetime(self):
- value = self.DATETIME('00042-01-01 13:30:29 BC', None)
- self.assertNotEqual(value, None)
- # mx.DateTime numbers BC dates from 0 rather than 1.
- self.assertEqual(value.year, -41)
- self.assertEqual(value.month, 1)
- self.assertEqual(value.day, 1)
- self.assertEqual(value.hour, 13)
- self.assertEqual(value.minute, 30)
- self.assertEqual(value.second, 29)
-
- def test_parse_time_microseconds(self):
- value = self.TIME('13:30:29.123456', None)
- self.assertEqual(math.floor(value.second), 29)
- self.assertEqual(
- int((value.second - math.floor(value.second)) * 1000000), 123456)
-
- def test_parse_datetime_microseconds(self):
- value = self.DATETIME('2007-01-01 13:30:29.123456', None)
- self.assertEqual(math.floor(value.second), 29)
- self.assertEqual(
- int((value.second - math.floor(value.second)) * 1000000), 123456)
-
- def test_parse_interval(self):
- value = self.INTERVAL('42 days 05:50:05', None)
- self.assertNotEqual(value, None)
- self.assertEqual(value.day, 42)
- self.assertEqual(value.hour, 5)
- self.assertEqual(value.minute, 50)
- self.assertEqual(value.second, 5)
-
- def test_adapt_time(self):
- from mx.DateTime import Time
- value = self.execute('select (%s)::time::text',
- [Time(13, 30, 29)])
- self.assertEqual(value, '13:30:29')
-
- def test_adapt_datetime(self):
- from mx.DateTime import DateTime
- value = self.execute('select (%s)::timestamp::text',
- [DateTime(2007, 1, 1, 13, 30, 29.123456)])
- self.assertEqual(value, '2007-01-01 13:30:29.123456')
-
- def test_adapt_bc_datetime(self):
- from mx.DateTime import DateTime
- value = self.execute('select (%s)::timestamp::text',
- [DateTime(-41, 1, 1, 13, 30, 29.123456)])
- self.assertEqual(value, '0042-01-01 13:30:29.123456 BC')
-
- def test_adapt_timedelta(self):
- from mx.DateTime import DateTimeDeltaFrom
- value = self.execute('select extract(epoch from (%s)::interval)',
- [DateTimeDeltaFrom(days=42,
- seconds=45296.123456)])
- seconds = math.floor(value)
- self.assertEqual(seconds, 3674096)
- self.assertEqual(int(round((value - seconds) * 1000000)), 123456)
-
- def test_adapt_megative_timedelta(self):
- from mx.DateTime import DateTimeDeltaFrom
- value = self.execute('select extract(epoch from (%s)::interval)',
- [DateTimeDeltaFrom(days=-42,
- seconds=45296.123456)])
- seconds = math.floor(value)
- self.assertEqual(seconds, -3583504)
- self.assertEqual(int(round((value - seconds) * 1000000)), 123456)
-
-
-# Only run the mx.DateTime tests if psycopg was compiled with support.
-if not hasattr(psycopg2._psycopg, 'MXDATETIME'):
- del mxDateTimeTests
-
-
-def test_suite():
- return unittest.TestLoader().loadTestsFromName(__name__)
-
-#!/usr/bin/env python
-import math
-import unittest
-
-import psycopg2
-import tests
-
-
-class CommonDatetimeTestsMixin:
-
- def execute(self, *args):
- conn = psycopg2.connect("dbname=%s" % tests.dbname)
- curs = conn.cursor()
- curs.execute(*args)
- return curs.fetchone()[0]
-
- def test_parse_date(self):
- value = self.DATE('2007-01-01', None)
- self.assertNotEqual(value, None)
- self.assertEqual(value.year, 2007)
- self.assertEqual(value.month, 1)
- self.assertEqual(value.day, 1)
-
- def test_parse_null_date(self):
- value = self.DATE(None, None)
- self.assertEqual(value, None)
+ def test_parse_incomplete_date(self):
+ self.assertRaises(psycopg2.DataError, self.DATE, '2007', None)
+ self.assertRaises(psycopg2.DataError, self.DATE, '2007-01', None)
def test_parse_time(self):
value = self.TIME('13:30:29', None)
@@ -270,6 +40,10 @@ class CommonDatetimeTestsMixin:
value = self.TIME(None, None)
self.assertEqual(value, None)
+ def test_parse_incomplete_time(self):
+ self.assertRaises(psycopg2.DataError, self.TIME, '13', None)
+ self.assertRaises(psycopg2.DataError, self.TIME, '13:30', None)
+
def test_parse_datetime(self):
value = self.DATETIME('2007-01-01 13:30:29', None)
self.assertNotEqual(value, None)
@@ -284,6 +58,18 @@ class CommonDatetimeTestsMixin:
value = self.DATETIME(None, None)
self.assertEqual(value, None)
+ def test_parse_incomplete_time(self):
+ self.assertRaises(psycopg2.DataError,
+ self.DATETIME, '2007', None)
+ self.assertRaises(psycopg2.DataError,
+ self.DATETIME, '2007-01', None)
+ self.assertRaises(psycopg2.DataError,
+ self.DATETIME, '2007-01-01 13', None)
+ self.assertRaises(psycopg2.DataError,
+ self.DATETIME, '2007-01-01 13:30', None)
+ self.assertRaises(psycopg2.DataError,
+ self.DATETIME, '2007-01-01 13:30:29+00:10:50', None)
+
def test_parse_null_interval(self):
value = self.INTERVAL(None, None)
self.assertEqual(value, None)